diff --git a/@MyFit/MyFit.m b/@MyFit/MyFit.m index dce2434..a3f5cc4 100644 --- a/@MyFit/MyFit.m +++ b/@MyFit/MyFit.m @@ -1,214 +1,242 @@ classdef MyFit < handle properties Gui Data; Fit; Parser; fit_name='linear' init_params=[]; scale_init=[]; FitStruct; Fitdata; coeffs; enable_gui=1; + enable_plot; + plot_handle; + hline_init; end properties (Dependent=true) fit_function; fit_tex; fit_params; fit_param_names; valid_fit_names; n_params; scaled_params; + init_param_fun; end methods function this=MyFit(varargin) createFitStruct(this); createParser(this); parse(this.Parser,varargin{:}); parseInputs(this); if ismember('Data',this.Parser.UsingDefaults) &&... ~ismember('x',this.Parser.UsingDefaults) &&... ~ismember('y',this.Parser.UsingDefaults) this.Data.x=this.Parser.Results.x; this.Data.y=this.Parser.Results.y; end genInitParams(this); this.scale_init=ones(1,this.n_params); if this.enable_gui createGui(this); end end %Creates the GUI of MyFit createGui(this); function createParser(this) p=inputParser; addParameter(p,'fit_name','linear',@ischar) addParameter(p,'Data',MyTrace()); addParameter(p,'Fit',MyTrace()); addParameter(p,'x',[]); addParameter(p,'y',[]); addParameter(p,'enable_gui',1); + addParameter(p,'enable_plot',0); + addParameter(p,'plot_handle',[]); this.Parser=p; end %Sets the class variables to the inputs from the inputParser. function parseInputs(this) for i=1:length(this.Parser.Parameters) %Takes the value from the inputParser to the appropriate %property. if isprop(this,this.Parser.Parameters{i}) this.(this.Parser.Parameters{i})=... this.Parser.Results.(this.Parser.Parameters{i}); end end end function fitTrace(this) this.Fit.x=linspace(min(this.Data.x),max(this.Data.x),1e3); switch this.fit_name case 'linear' this.coeffs=polyfit(this.Data.x,this.Data.y,1); case 'quadratic' this.coeffs=polyfit(this.Data.x,this.Data.y,2); case 'exponential' this.Fitdata=fitExponential(this.Data.x,... this.Data.y,this.scaled_params); this.coeffs=coeffvalues(this.Fitdata); this.Fit.y=this.Fitdata(this.Fit.x); otherwise ft=fittype(this.fit_function); this.Fitdata=fit(this.Data.x,this.Data.y,... ft); this.Fit.y=this.Fitdata(this.Fit.x)'; this.coeffs=coeffvalues(this.Fitdata); end this.init_params=this.coeffs; this.scale_init=ones(1,this.n_params); updateGui(this); end function createFitStruct(this) %Adds fits addFit(this,'linear','a*x_b','$$ax+b$$',{'a','b'},... {'Gradient','Offset'}) addFit(this,'quadratic','a*x^2+b*x+c','$$ax^2+bx+c$$',... {'a','b','c'},{'Quadratic coeff.','Linear coeff.','Offset'}); addFit(this,'gaussian','a*exp(-((x-c)/b)^2/2)+d',... '$$ae^{-\frac{(x-c)^2}{2b^2}}+d$$',{'a','b','c','d'},... {'Amplitude','Width','Center','Offset'}); - addFit(this,'lorentzian','a/(pi)*(b/((x-c)^2+b^2)',... + addFit(this,'lorentzian','a/(pi)*(b/((x-c)^2+b^2))',... '$$\frac{a}{1+\frac{(x-c)^2}{b^2}}+d$$',{'a','b','c','d'},... {'Amplitude','Width','Center','Offset'}); addFit(this,'exponential','a*exp(b*x)+c',... '$$ae^{bx}+c$$',{'a','b','c'},... {'Amplitude','Rate','Offset'}); end function updateGui(this) %Converts the scale variable to the value between 0 and 100 %necessary for the slider slider_vals=25*log10(this.scale_init)+50; for i=1:this.n_params set(this.Gui.(sprintf('edit_%s',this.fit_params{i})),... 'String',sprintf('%3.3e',this.scaled_params(i))); set(this.Gui.(sprintf('slider_%s',this.fit_params{i})),... 'Value',slider_vals(i)); end end %Adds a fit to the list of fits function addFit(this,fit_name,fit_function,fit_tex,fit_params,... fit_param_names) this.FitStruct.(fit_name).fit_function=fit_function; this.FitStruct.(fit_name).fit_tex=fit_tex; this.FitStruct.(fit_name).fit_params=fit_params; this.FitStruct.(fit_name).fit_param_names=fit_param_names; + %Generates the anonymous fit function from the above + args=['@(x,', strjoin(fit_params,','),')']; + anon_fit_fun=str2func(vectorize([args,fit_function])); + this.FitStruct.(fit_name).anon_fit_fun=anon_fit_fun; end function genInitParams(this) switch this.fit_name case 'exponential' this.init_params=initParamExponential(this.Data.x,... this.Data.y); otherwise this.init_params=ones(1,this.n_params); end end function slider_Callback(this, param_ind, hObject, ~) %Gets the value from the slider scale=get(hObject,'Value'); %Updates the scale with a new value this.scale_init(param_ind)=10^((scale-50)/25); %Updates the edit box with the new value from the slider set(this.Gui.(sprintf('edit_%s',this.fit_params{param_ind})),... 'String',sprintf('%3.3e',this.scaled_params(param_ind))); + if this.enable_plot; plotInitFun(this); end + end function edit_Callback(this, hObject, ~) init_param=str2double(get(hObject,'String')); tag=get(hObject,'Tag'); %Finds the index where the fit_param name begins (convention is %after the underscore) - fit_param_name=tag((strfind(tag,'_')+1):end); - param_ind=strcmp(fit_param_name,this.fit_param_names); - %Updates the slider with the new value from the edit box - set(this.Gui.(sprintf('slider_%s',fit_param_name)),... - 'Value',init_param); + fit_param=tag((strfind(tag,'_')+1):end); + param_ind=strcmp(fit_param,this.fit_params); + %Updates the slider to be such that the scaling is 1 + set(this.Gui.(sprintf('slider_%s',fit_param)),... + 'Value',50); %Updates the correct initial parameter this.init_params(param_ind)=init_param; + if this.enable_plot; plotInitFun(this); end + end + + function plotInitFun(this) + %Substantially faster than any alternative - generating + %anonymous functions is very cpu intensive. + x_vec=linspace(min(this.Data.x),max(this.Data.x),1000); + input_cell=num2cell(this.scaled_params); + y_vec=feval(this.FitStruct.(this.fit_name).anon_fit_fun,x_vec,... + input_cell{:}); + if isempty(this.hline_init) + this.hline_init=plot(this.plot_handle,x_vec,y_vec); + else + set(this.hline_init,'XData',x_vec,'YData',y_vec); + end end function set.fit_name(this,fit_name) assert(ischar(fit_name),'The fit name must be a string'); this.fit_name=lower(fit_name); end function valid_fit_names=get.valid_fit_names(this) valid_fit_names=fieldnames(this.FitStruct); end function fit_function=get.fit_function(this) assert(ismember(this.fit_name,this.valid_fit_names),... '%s is not a supported fit name',this.fit_name); fit_function=this.FitStruct.(this.fit_name).fit_function; end function fit_tex=get.fit_tex(this) assert(ismember(this.fit_name,this.valid_fit_names),... '%s is not a supported fit name',this.fit_name); fit_tex=this.FitStruct.(this.fit_name).fit_tex; end function fit_params=get.fit_params(this) assert(ismember(this.fit_name,this.valid_fit_names),... '%s is not a supported fit name',this.fit_name); fit_params=this.FitStruct.(this.fit_name).fit_params; end function fit_param_names=get.fit_param_names(this) assert(ismember(this.fit_name,this.valid_fit_names),... '%s is not a supported fit name',this.fit_name); fit_param_names=this.FitStruct.(this.fit_name).fit_param_names; end function scaled_params=get.scaled_params(this) scaled_params=this.scale_init.*this.init_params; end function n_params=get.n_params(this) n_params=length(this.fit_params); end + end end \ No newline at end of file diff --git a/@MyFit/createGui.m b/@MyFit/createGui.m index 7eeba20..2a3efb7 100644 --- a/@MyFit/createGui.m +++ b/@MyFit/createGui.m @@ -1,108 +1,134 @@ function createGui(this) fit_name=[upper(this.fit_name(1)),this.fit_name(2:end)]; rgb_blue=[0.5843-0.4,0.8157-0.4,1]; rgb_white=[1,1,1]; %Width of the edit boxes in the GUI edit_width=120; %Name sets the title of the window, NumberTitle turns off the FigureN text %that would otherwise be before the title, MenuBar is the menu normally on %the figure, toolbar is the toolbar normally on the figure. %HandleVisibility refers to whether gcf, gca etc will grab this figure. this.Gui.Window = figure('Name', 'MyFit', 'NumberTitle', 'off', ... 'MenuBar', 'none', 'Toolbar', 'none', 'HandleVisibility', 'off',... 'Units','Pixels','Position',[500,500,edit_width*this.n_params,400]); %The main vertical box. The four main panes of the GUI are stacked in the %box. this.Gui.MainVbox=uix.VBox('Parent',this.Gui.Window,'BackgroundColor','w'); %The title box -this.Gui.Title=uicontrol('Parent',this.Gui.MainVbox,'Style','text',... +% this.Gui.Title=uicontrol('Parent',this.Gui.MainVbox,'Style','text',... +% 'String',fit_name,'Units','Normalized',... +% 'FontSize',14,'FontWeight','bold','BackgroundColor','w'); +this.Gui.Title=annotation(this.Gui.MainVbox,'textbox',[0.5,0.5,0.3,0.3],... 'String',fit_name,'Units','Normalized',... - 'FontSize',14,'FontWeight','bold','BackgroundColor','w'); + 'HorizontalAlignment','center','VerticalAlignment','middle',... + 'FontSize',16,'BackgroundColor',rgb_white); %Displays the fitted equation -this.Gui.Title=annotation(this.Gui.MainVbox,'textbox',[0.5,0.5,0.3,0.3],... +this.Gui.Equation=annotation(this.Gui.MainVbox,'textbox',[0.5,0.5,0.3,0.3],... 'String',this.fit_tex,... 'Units','Normalized','Interpreter','LaTeX',... 'HorizontalAlignment','center','VerticalAlignment','middle',... - 'FontSize',20,'BackgroundColor','w'); - -%Creates an HBox to put extracted parameters such as quality factor in + 'FontSize',20,'BackgroundColor',rgb_white); +%Creates an HBox for extracted parameters and user interactions with GUI this.Gui.UserHbox=uix.HBox('Parent',this.Gui.MainVbox,'BackgroundColor',rgb_white); %Creates the HBox for the fitting parameters this.Gui.FitHbox=uix.HBox('Parent',this.Gui.MainVbox); %Sets the heights and minimum heights of the four vertical boxes. -1 set(this.Gui.MainVbox,'Heights',[40,-1,-1,100],'MinimumHeights',[40,80,50,100]); this.Gui.SavePanel=uix.BoxPanel( 'Parent', this.Gui.UserHbox,... - 'Padding',0,'BackgroundColor', 'w',... - 'Title','Save Panel','TitleColor',rgb_blue); -this.Gui.SaveButton=uicomponent('Parent',this.Gui.SavePanel,... + 'Padding',0,'BackgroundColor', rgb_white,... + 'Title','Save Panel','TitleColor',rgb_blue); +this.Gui.SaveVbox=uix.VBox('Parent',this.Gui.SavePanel,'BackgroundColor',... + rgb_white); +this.Gui.SaveButton=uicontrol('Parent',this.Gui.SaveVbox,... 'style','pushbutton','Background','w','String','Save Fit'); +set(this.Gui.SaveVbox,'Heights',[25]); %Insert Switch/case block here for different fits this.Gui.UserPanel=uix.BoxPanel( 'Parent', this.Gui.UserHbox,... - 'Padding',0,'BackgroundColor', 'w',... - 'Title','Calculated parameters','TitleColor',rgb_blue); + 'Padding',0,'BackgroundColor', 'w',... + 'Title','Calculated parameters','TitleColor',rgb_blue); +this.Gui.UserVbox=uix.VBox('Parent',this.Gui.UserPanel,'BackgroundColor',... + rgb_white); +switch this.fit_name + case 'exponential' + this.Gui.QHbox=uix.HBox('Parent',this.Gui.UserVbox,... + 'BackgroundColor',rgb_white); + this.Gui.FreqLabel=annotation(this.Gui.QHbox,'textbox',[0.5,0.5,0.3,0.3],... + 'String','Frequency (MHz)',... + 'Units','Normalized','HorizontalAlignment','center','VerticalAlignment','middle',... + 'FontSize',10,'BackgroundColor',rgb_white); + + this.Gui.EditFreq=uicontrol('Parent',this.Gui.QHbox,... + 'Style','edit','String',1,'HorizontalAlignment','Right',... + 'FontSize',10,'Tag','EditFreq'); + set(this.Gui.QHbox,'Widths',[-4,-2]); + set(this.Gui.UserVbox,'Heights',[25]); +end this.Gui.UserPanelBox=uix.VBox('Parent',this.Gui.UserPanel,... 'BackGroundColor','w'); switch this.fit_name case 'exponential' this.Gui.Q_text=uicontrol('Parent',this.Gui.UserPanelBox,... 'Style','edit','String','Quality factor','FontSize',12); this.Gui.Q=uicontrol('Parent',this.Gui.UserPanelBox,... 'Style','edit','String',sprintf('%3.3e',1e6),... 'FontSize',14,'Tag','Q'); set(this.Gui.UserPanelBox,'Heights',[30,30]); end %Loops over number of parameters to create a fit panel for each one for i=1:this.n_params %Generates the string for the panel handle panel_str=sprintf('panel_%s',this.fit_params{i}); %Generates the string for the vbox handle vbox_str=sprintf('vbox_%s',this.fit_params{i}); %Generates the string for the slider handle slider_str=sprintf('slider_%s',this.fit_params{i}); %Generates string for edit panels edit_str=sprintf('edit_%s',this.fit_params{i}); - %Creates the panel + %Creates the panels this.Gui.(panel_str)=uix.BoxPanel( 'Parent', this.Gui.FitHbox ,... 'Padding',0,'BackgroundColor', 'w',... 'Title',sprintf('%s (%s)',this.fit_param_names{i},this.fit_params{i}),... 'TitleColor',rgb_blue); - %Creates the vbox inside the panel that allows stacking + %Creates the vbox inside the panel that allows stacking this.Gui.(sprintf('vbox_%s',this.fit_params{i})) =uix.VBox( 'Parent', ... this.Gui.(panel_str),'Padding',0,'BackgroundColor', 'w'); + %Generates edit box for fit parameters this.Gui.(edit_str)=uicontrol('Parent',this.Gui.(vbox_str),... 'Style','edit','String',sprintf('%3.3e',this.init_params(i)),... - 'FontSize',14,'Tag',edit_str,'Callback',... + 'FontSize',14,'Tag',edit_str,'HorizontalAlignment','Right',... + 'Callback',... @(hObject,eventdata) edit_Callback(this, hObject, eventdata)); %Generates java-based slider. Looks nicer than MATLAB slider this.Gui.(sprintf('slider_%s',this.fit_params{i}))=... uicomponent('Parent',this.Gui.(vbox_str),'style',... 'jslider','pos',[0,0,95,45],'Value',72,'Orientation',0,... - 'MajorTickSpacing',20,'MinorTickSpacing',10,... + 'MajorTickSpacing',20,'MinorTickSpacing',5,... 'Paintlabels',0,'PaintTicks',1,'Value',50,'Background',... java.awt.Color.white); - + %Sets up callbacks for the slider this.Gui.([slider_str,'_callback'])=handle(this.Gui.(slider_str),... 'CallbackProperties'); this.Gui.([slider_str,'_callback']).StateChangedCallback = .... @(hObject, eventdata) slider_Callback(this,i,hObject,eventdata); -% @(hjSlider,eventData) set(this.Gui.(sprintf('edit_%s',this.fit_params{i})),... -% 'String',(get(hjSlider,'Value'))); + % @(hjSlider,eventData) set(this.Gui.(sprintf('edit_%s',this.fit_params{i})),... + % 'String',(get(hjSlider,'Value'))); %Sets heights and minimum heights for the elements in the fit vbox set(this.Gui.(vbox_str),'Heights',[30,55],'MinimumHeights',[30,55]) end + end \ No newline at end of file