function plotODG2withAnalysis(od_imgs, scan_parameter_values, g2_results, analysis_results, options, varargin) %% plotODG2withAnalysis % Author: Karthik % Date: 2025-09-30 % Version: 1.0 % % Description: % For each scan parameter value, produces one or more compact figures. % Each row contains: % - Col 1: OD image % - Col 2: g² correlation matrix % Overlays from analysis (boundary, ellipse, centroid, anisotropy) can be % optionally toggled on/off. % % Inputs: % od_imgs : cell array of OD images % scan_parameter_values: vector/array of scan parameters (one per image) % g2_results : struct with fields % - g2_matrices{k} % - dx_phys{k}, dy_phys{k} % analysis_results : struct with fields % - boundary_coords{k} % - ellipse_params{k} % - peak_centroid{k} % - anisotropy_vals(k) % - roi_params{k} (optional) % options : struct with imaging calibration % - pixel_size (in meters) % - magnification (unitless) % varargin : name-value pairs % - 'FontName', 'FontSize', 'SkipLivePlot', 'SkipSaveFigures', % 'SaveDirectory', 'ShowOverlays', 'RepsPerPage' % % Notes: % Requires conductCorrelationAnalysis and analyzeG2Structures outputs. % --- Parse optional name-value pairs --- p = inputParser; addParameter(p, 'FontName', 'Arial', @ischar); addParameter(p, 'FontSize', 12, @isnumeric); addParameter(p, 'SkipLivePlot', false, @islogical); addParameter(p, 'SkipSaveFigures', true, @islogical); addParameter(p, 'SaveDirectory', pwd, @ischar); addParameter(p, 'ShowOverlays', true, @islogical); addParameter(p, 'RepsPerPage', 10, @isnumeric); % pagination parse(p, varargin{:}); opts = p.Results; % --- Setup save directory if needed --- if ~opts.SkipSaveFigures saveFolder = fullfile(opts.SaveDirectory, 'Results', 'SavedFigures', 'OD_G2_withAnalysis'); if ~exist(saveFolder, 'dir') mkdir(saveFolder); end end % --- Group by parameter value --- param_vals = scan_parameter_values(:); unique_params = unique(param_vals, 'rows'); N_params = size(unique_params, 1); for pIdx = 1:N_params matches = ismember(param_vals, unique_params(pIdx, :), 'rows'); idx_list = find(matches); N_reps = numel(idx_list); % --- Pagination over repetitions --- numPages = ceil(N_reps / opts.RepsPerPage); for pageIdx = 1:numPages repStart = (pageIdx-1)*opts.RepsPerPage + 1; repEnd = min(pageIdx*opts.RepsPerPage, N_reps); repSubset = idx_list(repStart:repEnd); N_rows = numel(repSubset); % --- Create compact figure --- if ~opts.SkipLivePlot fig = figure('Color', 'w', 'Position', [100 100 400 800]); figure(fig); % ensure visible else fig = figure('Color', 'w', 'Position', [100 100 400 800], 'Visible','off'); % invisible for direct save end t = tiledlayout(fig, N_rows, 2, 'TileSpacing', 'compact', 'Padding', 'compact'); title(t, sprintf('Scan parameter: %s | Page %d/%d', ... mat2str(unique_params(pIdx, :)), pageIdx, numPages), ... 'FontSize', opts.FontSize + 2, 'FontWeight', 'bold', 'FontName', opts.FontName); for r = 1:N_rows k = repSubset(r); % --- OD image --- nexttile; [M, N] = size(od_imgs{k}); x_phys = ((1:N) - ceil(N/2)) * (options.pixel_size / options.magnification * 1e6); % µm y_phys = ((1:M) - ceil(M/2)) * (options.pixel_size / options.magnification * 1e6); % µm imagesc(x_phys, y_phys, od_imgs{k}); axis image; set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', 14); colormap(gca, Colormaps.inferno()); xlabel('x (\mum)'); ylabel('y (\mum)'); % --- g² correlation matrix --- nexttile; dx = g2_results.dx_phys{k}; dy = g2_results.dy_phys{k}; g2_matrix = g2_results.g2_matrices{k}; imagesc(dx, dy, g2_matrix); axis image; set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', 14); colormap(gca, Colormaps.coolwarm()); colorbar; xlabel('\Deltax (\mum)'); ylabel('\Deltay (\mum)'); hold on; % --- Optional overlays --- if opts.ShowOverlays boundary = analysis_results.boundary_coords{k}; ellipse = analysis_results.ellipse_params{k}; centroid = analysis_results.peak_centroid{k}; anisotropy = analysis_results.anisotropy_vals(k); theta = analysis_results.ellipse_params{k}(5); % ROI rectangle (rotated) if isfield(analysis_results, 'roi_params') roi = analysis_results.roi_params{k}; if ~isempty(roi) && all(~isnan(roi)) x0 = roi(1); y0 = roi(2); w = roi(3); h = roi(4); roi_theta = roi(5); corners = [ -w/2, -h/2; w/2, -h/2; w/2, h/2; -w/2, h/2]; R = [cos(roi_theta), -sin(roi_theta); sin(roi_theta), cos(roi_theta)]; corners_rot = (R*corners')' + [x0, y0]; corners_rot = [corners_rot; corners_rot(1,:)]; plot(corners_rot(:,1), corners_rot(:,2), 'r--', 'LineWidth',1.5); end end if all(~isnan(boundary(:))) plot(boundary(:, 1), boundary(:, 2), 'g-', 'LineWidth', 2); end if all(~isnan(centroid)) plot(centroid(1), centroid(2), 'mo', 'MarkerSize', 8, 'LineWidth', 2); x_c = centroid(1); y_c = centroid(2); else x_c = NaN; y_c = NaN; end if all(~isnan(ellipse)) && ~isnan(anisotropy) && anisotropy ~= 0 a = ellipse(3); b = ellipse(4); tEllipse = linspace(0, 2*pi, 200); ellipse_x = x_c + a*cos(tEllipse)*cos(theta) - b*sin(tEllipse)*sin(theta); ellipse_y = y_c + a*cos(tEllipse)*sin(theta) + b*sin(tEllipse)*cos(theta); if anisotropy > 1.0 plot(ellipse_x, ellipse_y, 'y--', 'LineWidth', 2); else plot(ellipse_x, ellipse_y, 'y-', 'LineWidth', 2); end end end end if ~opts.SkipLivePlot drawnow; % render figure end % --- Save figure --- if ~opts.SkipSaveFigures saveFileName = sprintf('OD_G2_analysis_param_%03d_page_%02d.png', pIdx, pageIdx); Plotter.saveFigure(fig, ... 'SaveFileName', saveFileName, ... 'SaveDirectory', saveFolder, ... 'SkipSaveFigures', opts.SkipSaveFigures); end % --- Close invisible figure to free memory --- if opts.SkipLivePlot close(fig); end end end end