Calculations/Data-Analyzer/+Analyzer/runInteractiveASDTwoGaussianFitGUI.m

231 lines
8.4 KiB
Matlab

function runInteractiveASDTwoGaussianFitGUI(spectral_analysis_results)
%% runInteractiveASDTwoGaussianFitGUI
% Author: Karthik
% Date: 2025-10-16
% Version: 1.0
%
% Description:
% Interactive viewer for raw angular spectral distributions.
% Lets user browse through individual curves and refit two-Gaussian models
% live as threshold parameters are adjusted.
%
% Controls:
% - Slider or ←/→ keys to browse curves
% - Editable boxes for thresholds (Residual, Position, Amplitude, MaxTheta)
% - "Re-fit" button triggers recomputation
%
% Dependencies:
% Requires Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution.m
%% --- Initialization ---
rawCurves.x = spectral_analysis_results.S_theta_norm_all;
rawCurves.theta = spectral_analysis_results.theta_vals;
% Handle input organization
if iscell(rawCurves.x)
Ncurves = numel(rawCurves.x);
else
Ncurves = size(rawCurves.x, 1);
tmp = arrayfun(@(i) struct('x', rawCurves.x(i, :), 'theta', rawCurves.theta(:)'), 1:Ncurves);
rawCurves = tmp(:);
end
% Default fit parameters
params.MaxTheta = pi/2;
params.ResidualThreshold = 0.15;
params.PositionThreshold = pi/15;
params.AmplitudeThreshold = 0.15;
currentIdx = 1;
%% --- Create figure ---
hFig = findobj('Type','figure','Tag','InteractiveTwoGaussianFit');
if isempty(hFig)
hFig = figure('Name','Angular Spectra Fit Viewer',...
'NumberTitle','off',...
'Position',[100 100 1250 800],...
'Color','w',...
'KeyPressFcn',@keyPressCallback,...
'Tag','InteractiveTwoGaussianFit');
else
figure(hFig); clf(hFig);
end
%% --- Axes for plot ---
hAx = axes('Parent',hFig,'Position',[0.08 0.1 0.55 0.8]);
grid(hAx,'on'); box(hAx,'on');
xlabel(hAx,'\theta (rad)','FontSize',12);
ylabel(hAx,'Normalized amplitude','FontSize',12);
%% --- Slider for curve index ---
hSlider = uicontrol('Style','slider','Min',1,'Max',Ncurves,'Value',1,...
'SliderStep',[1/(Ncurves-1), 10/(Ncurves-1)],...
'Units','normalized','Position',[0.1 0.02 0.5 0.03],...
'Callback',@(~,~) updatePlot());
%% --- Parameter definitions ---
paramNames = {'MaxTheta','ResidualThreshold','PositionThreshold','AmplitudeThreshold'};
paramLabels = {'Max Theta (rad)', 'Residual Threshold', 'Position Threshold (rad)', 'Amplitude Threshold'};
paramDescs = {'Upper limit for fitting range',...
'Maximum mean residual for valid fits',...
'Minimum peak separation required',...
'Minimum amplitude ratio threshold'};
nParams = numel(paramNames);
hEdit = gobjects(nParams,1);
baseY = 0.88;
%% --- Create parameter controls ---
for i = 1:nParams
yPos = baseY - 0.12*(i-1);
% Label
uicontrol('Style','text','Units','normalized',...
'Position',[0.68 yPos 0.25 0.04],...
'String',paramLabels{i},...
'HorizontalAlignment','left',...
'FontSize',10,'FontWeight','bold',...
'BackgroundColor','w');
% Edit box
hEdit(i) = uicontrol('Style','edit','Units','normalized',...
'Position',[0.9 yPos 0.07 0.045],...
'String',num2str(params.(paramNames{i}), '%.4f'),...
'Callback',@(src,~) applyParams());
% Description
uicontrol('Style','text','Units','normalized',...
'Position',[0.68 yPos - 0.04 0.35 0.03],...
'String',paramDescs{i},...
'HorizontalAlignment','left',...
'FontSize',8,'ForegroundColor',[0.4 0.4 0.4],...
'BackgroundColor','w');
end
%% --- Apply button ---
uicontrol('Style','pushbutton','Units','normalized',...
'Position',[0.70 0.35 0.25 0.06],...
'String','Re-fit All Curves',...
'FontSize',14,'FontWeight','bold',...
'BackgroundColor',[0.2 0.6 1],...
'ForegroundColor','w',...
'Callback',@(~,~) refitAll());
%% --- Fit info text boxes ---
infoLabels = {'Fit validity:', 'Peak positions (rad):', 'Amplitudes:'};
hInfoText = gobjects(numel(infoLabels),1);
for i = 1:numel(infoLabels)
yText = 0.22 - 0.07*(i-1);
uicontrol('Style','text','Units','normalized',...
'Position',[0.68 yText 0.25 0.05],...
'String',infoLabels{i},...
'FontSize',10,'FontWeight','bold',...
'HorizontalAlignment','left',...
'BackgroundColor','w');
hInfoText(i) = uicontrol('Style','text','Units','normalized',...
'Position',[0.88 yText 0.1 0.05],...
'String','-','FontSize',10,...
'HorizontalAlignment','left',...
'BackgroundColor','w');
end
%% --- Compute initial fits ---
[fitResults, rawData] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
spectral_analysis_results.S_theta_norm_all,...
spectral_analysis_results.theta_vals,...
'MaxTheta', params.MaxTheta,...
'ResidualThreshold', params.ResidualThreshold,...
'PositionThreshold', params.PositionThreshold,...
'AmplitudeThreshold', params.AmplitudeThreshold);
%% --- Initial plot ---
updatePlot();
%% --- Nested functions ---
function applyParams()
% Update parameters from GUI fields
for j = 1:nParams
val = str2double(hEdit(j).String);
if ~isnan(val)
params.(paramNames{j}) = val;
end
end
refitAll();
end
function refitAll()
% Recompute all fits
drawnow;
[fitResults, rawData] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
spectral_analysis_results.S_theta_norm_all,...
spectral_analysis_results.theta_vals,...
'MaxTheta', params.MaxTheta,...
'ResidualThreshold', params.ResidualThreshold,...
'PositionThreshold', params.PositionThreshold,...
'AmplitudeThreshold', params.AmplitudeThreshold);
updatePlot();
end
function updatePlot()
idx = round(get(hSlider,'Value'));
currentIdx = idx;
cla(hAx);
hold(hAx,'on');
raw = rawData(idx);
plot(hAx, raw.theta, raw.x, 'k.-', 'LineWidth', 1, 'DisplayName', 'Raw Data');
if idx <= numel(fitResults)
fit = fitResults(idx);
if isfield(fit,'isValid') && ~isempty(fit.isValid) && fit.isValid
plot(hAx, fit.thetaFine, fit.yFit, 'r-', 'LineWidth', 1.5, 'DisplayName', 'Two-Gaussian Fit');
% Extract parameters from pFit vector if available
if isfield(fit,'pFit') && numel(fit.pFit) >= 6
A1 = fit.pFit(1); mu1 = fit.pFit(2);
A2 = fit.pFit(4); mu2 = fit.pFit(5);
hInfoText(1).String = 'Valid';
hInfoText(1).ForegroundColor = [0 0.5 0];
hInfoText(2).String = sprintf('[%.3f, %.3f]', mu1, mu2);
hInfoText(3).String = sprintf('[%.2f, %.2f]', A1, A2);
else
hInfoText(1).String = 'Valid';
hInfoText(1).ForegroundColor = [0 0.5 0];
hInfoText(2).String = '-';
hInfoText(3).String = '-';
end
else
hInfoText(1).String = 'Invalid';
hInfoText(1).ForegroundColor = [0.7 0 0];
hInfoText(2).String = '-';
hInfoText(3).String = '-';
end
end
set(gca, 'YScale', 'log');
title(hAx,sprintf('Curve %d / %d', idx, Ncurves),'FontSize',16);
legend(hAx,'Location','northeast','FontSize',14);
hold(hAx,'off');
drawnow;
end
function keyPressCallback(event)
switch event.Key
case 'rightarrow'
if currentIdx < Ncurves
currentIdx = currentIdx + 1;
set(hSlider,'Value',currentIdx);
updatePlot();
end
case 'leftarrow'
if currentIdx > 1
currentIdx = currentIdx - 1;
set(hSlider,'Value',currentIdx);
updatePlot();
end
end
end
end