From b4c8c5778b7f5a0b6f05799141f1cd55feb1b629 Mon Sep 17 00:00:00 2001 From: giuliaghirardini <giuliaghirardini2001@gmail.com> Date: Sun, 9 Feb 2025 21:44:55 +0100 Subject: [PATCH] [export-standardized-figure][utilities] Added preview function to apply other plot properties --- functions/utilities/exportFigureGUI.m | 114 +++-- .../utilities/previewStandardizedFigure.m | 438 ++++++++++++++++++ 2 files changed, 516 insertions(+), 36 deletions(-) create mode 100644 functions/utilities/previewStandardizedFigure.m diff --git a/functions/utilities/exportFigureGUI.m b/functions/utilities/exportFigureGUI.m index 949c3f4..accb4eb 100644 --- a/functions/utilities/exportFigureGUI.m +++ b/functions/utilities/exportFigureGUI.m @@ -99,7 +99,7 @@ legendOrientationDropDown = uidropdown(legendPanel, 'Items', legendOrientations, 'Position', [330 10 150 22]); %% Preview content -uiPanel = uipanel(fig, 'Title', 'Copied Figure', 'Position', [810, 100, 550, 500]); +uiPanel = uipanel(fig, 'Title', 'Export preview', 'Position', [810, 100, 550, 500]); % Find all open figures (excluding this GUI) figHandles = findFigures(fig); @@ -112,16 +112,28 @@ figSelectionDropDown = uidropdown(fig, ... 'Position', [150, 500, 200, 30], ... 'ValueChangedFcn', @(src, event) updatePreview()); +% Automatically update preview if only one figure exists +if length(figHandles) == 1 + updatePreview(); +end + +%% Buttons % Button to update the dropdown menu updateButton = uibutton(fig, 'Text', 'Update Figures', ... 'Position', [370, 500, 120, 30], ... 'ButtonPushedFcn', @(src, event) updateDropdown()); -% Automatically update preview if only one figure exists -if length(figHandles) == 1 - updatePreview(); -end +% Submit Button +submitButton = uibutton(fig, 'Text', 'Export Figure', ... + 'Position', [350 100 100 30], ... + 'ButtonPushedFcn', @(src, event) exportFigureCallback()); +% preview image to export button +previewButton = uibutton(fig, 'Text', 'Update preview', ... + 'Position', [500, 500, 120, 30], ... + 'ButtonPushedFcn', @(src, event) previewFigureCallback()); + +%% Functions % Function to update dropdown menu with new figures function updateDropdown() figHandles = findFigures(fig); @@ -137,6 +149,9 @@ end % Function to update preview function updatePreview() + % Delete any existing axes + delete(findall(uiPanel, 'Type', 'figure')); + figHandles = findFigures(fig); % Get selected figure from dropdown @@ -160,14 +175,14 @@ end end % Retrieve axes from selected figure - ax = findall(selectedFig, 'Type', 'axes'); + obj = findall(selectedFig, 'Type', 'figure'); - % Delete any existing axes in the UI panel - delete(findall(uiPanel, 'Type', 'axes')); + % Delete any existing axes + delete(findall(uiPanel, 'Type', 'figure')); % Copy the axes into the UI Panel - newAx = copyobj(ax, uiPanel); - set(newAx, 'Position', [0.1, 0.1, 0.8, 0.8]); % Adjust position in panel + newAx = copyobj(obj.Children, uiPanel); + %set(newAx, 'Position', [0.1, 0.1, 0.8, 0.8]); % Adjust position in panel hold(newAx, 'on'); % Apply user settings @@ -198,8 +213,6 @@ end daspect(newAx, [1 WHratioField.Value 1]); end - hold(newAx, 'off'); - % Ensure preview updates on checkbox change addMarkersBox.ValueChangedFcn = @(src, event) updatePreview(); changeColorsBox.ValueChangedFcn = @(src, event) updatePreview(); @@ -209,19 +222,10 @@ end satelliteMapColorsBox.ValueChangedFcn = @(src, event) updatePreview(); legendLocationDropDown.ValueChangedFcn = @(src, event) updatePreview(); legendOrientationDropDown.ValueChangedFcn = @(src, event) updatePreview(); + + hold(newAx, 'off'); end -%% Further developments -% File Path Input -% uilabel(fig, 'Text', 'Figure Save Path:', 'Position', [50 70 150 30]); -% pathField = uitextarea(fig, 'Position', [200 70 300 30]); - -%% Buttons -% Submit Button -submitButton = uibutton(fig, 'Text', 'Export Figure', ... - 'Position', [350 100 100 30], ... - 'ButtonPushedFcn', @(src, event) exportFigureCallback()); - % Callback Function for Button function exportFigureCallback() % Get values from the GUI components @@ -250,7 +254,6 @@ submitButton = uibutton(fig, 'Text', 'Export Figure', ... % Try to export figure try % Call the exportStandardizedFigure function - % figToExp = gcf; selectedIdx = figSelectionDropDown.Value; figToExp = figHandles(strcmp(figSelectionDropDown.Items, selectedIdx)); exportStandardizedFigure(figToExp, figName, percTextwidth, ... @@ -272,17 +275,56 @@ submitButton = uibutton(fig, 'Text', 'Export Figure', ... uialert(fig, ['Error exporting figure: ' ME.message], 'Error'); end end -end -function figHandles = findFigures(fig) -figHandles = findall(0, 'Type', 'figure'); -figHandles(figHandles == fig) = []; % Exclude GUI figure -end + function previewFigureCallback() + % Get values from the GUI components + percTextwidth = percTextField.Value; + addMarkers = addMarkersBox.Value; + forcedMarkers = forcedMarkersField.Value; + changeColors = changeColorsBox.Value; + changeLineStyle = changeLineStyleBox.Value; + gridOption = gridBox.Value; + legendLocation = legendLocationDropDown.Value; + legendOrientation = legendOrientationDropDown.Value; + exportPDF = exportPDFBox.Value; + exportFIG = exportFIGBox.Value; + satelliteMapColors = satelliteMapColorsBox.Value; + % figurePath = pathField.Value; + WHratio = WHratioField.Value; + overwriteFigure = overwriteFigureBox.Value; -function figNames = getFigureNames(figHandles) -if isempty(figHandles) - figNames = {'No figures open'}; -else - figNames = arrayfun(@(f) sprintf('Figure %d', f.Number), figHandles, 'UniformOutput', false); -end -end \ No newline at end of file + selectedIdx = figSelectionDropDown.Value; + figToExp = figHandles(strcmp(figSelectionDropDown.Items, selectedIdx)); + previewStandardizedFigure(figToExp, percTextwidth, ... + 'addMarkers', addMarkers, ... + 'forcedMarkers', forcedMarkers, ... + 'changeColors', changeColors, ... + 'changeLineStyle', changeLineStyle, ... + 'grid', gridOption, ... + 'legendLocation', legendLocation, ... + 'legendOrientation', legendOrientation, ... + 'exportPDF', exportPDF, ... + 'exportFIG', exportFIG, ... + 'satelliteMapColors', satelliteMapColors, ... + 'WHratio', WHratio, ... + 'overwriteFigure', overwriteFigure); + end +end + + function figHandles = findFigures(fig) + figHandles = findall(0, 'Type', 'figure'); + figHandles(figHandles == fig) = []; % Exclude GUI figure + end + + function figNames = getFigureNames(figHandles) + if isempty(figHandles) + figNames = {'No figures open'}; + else + figNames = arrayfun(@(f) sprintf('Figure %d', f.Number), figHandles, 'UniformOutput', false); + end + end + +%% Further developments +% File Path Input +% uilabel(fig, 'Text', 'Figure Save Path:', 'Position', [50 70 150 30]); +% pathField = uitextarea(fig, 'Position', [200 70 300 30]); \ No newline at end of file diff --git a/functions/utilities/previewStandardizedFigure.m b/functions/utilities/previewStandardizedFigure.m new file mode 100644 index 0000000..4859db0 --- /dev/null +++ b/functions/utilities/previewStandardizedFigure.m @@ -0,0 +1,438 @@ +function previewStandardizedFigure(fig, percTextwidth, varargin) +% exportStandardizedFigure - this function standardizes plot graphics and exports the +% figure as a .pdf file +% +% INPUTS: +% fig - figure variable +% figureName - figure name, string, (note: do not add '.pdf') +% percTextwidth - percentage of the linewitdth as indicated in LaTeX, +% (70% -> 0.7), double +% varargin - optional inputs: +% addMarkers - logic, if true every lines has a different +% marker, default: true +% changeColors - logic, if true the colors of the lines will +% be changed, default: true +% changeLineStyle - logic, if both 'changeLineStyle' and +% 'changeColors' are true the lines +% with the same color will have two +% different line styles, default: false +% WHratio - double, width/height ratio; if 'WHratio' is 0 +% the ratio will not change, +% default: 0 (current ratio) +% forcedMarkers - double, number of markers for each line. If it +% is set to 0, all points will have a +% marker; default: 0 (all points) +% grid - logic, true to show grid, false otherwise, +% default: true +% legendLocation - string, location of the legend; +% default: 'southoutside' +% legendOrientation - string, orientation of the legend; +% default: 'horizontal' +% exportPDF - logic, true to export pdf file, default: true +% exportFIG - logic, true to export fig file, defalut: false +% satelliteMapColors - logic, if true, color palette for satellite +% map is chosen, default: false +% figurePath - string, path in which the pdf file and/or the +% fig file will be saved, default: '' +% overwriteFigure - logic, if true it will overwrite the .pdf +% file and the .fig file with the same +% name, default: false +% +% --------------------------------EXAMPLE-------------------------------- +% >> exportStandardizedFigure(gcf,'nameFig',0.67, 'forcedMarkers', 6, ... +% 'WHratio', 1) +% Figure saved successfully! +% +% text to copy: +% \includegraphics[width=0.67\textwidth]{<add figure path>\nameFig.pdf} +%------------------------------------------------------------------------ +% +% VERSIONS: #0, release, Maria Teresa Cazzola +% #1, update, Riccardo Cadamuro, Maria Teresa Cazzola, +% Marco Marchesi +% some improvements +% + +%% check input validity +if percTextwidth>1 + error('figure width is larger than the page!') +end + +%% Parse input +p = inputParser; + +addParameter(p, 'addMarkers', true, @islogical); +addParameter(p, 'forcedMarkers', 0); +addParameter(p, 'changeColors', true, @islogical); +addParameter(p, 'changeLineStyle', false, @islogical); +addParameter(p, 'WHratio', 0); +addParameter(p, 'grid', true, @islogical); +addParameter(p, 'exportPDF', true, @islogical); +addParameter(p, 'exportFIG', false, @islogical); +addParameter(p, 'satelliteMapColors', false, @islogical); +addParameter(p, 'figurePath', ''); +addParameter(p, 'overwriteFigure', false, @islogical); +addParameter(p, 'legendLocation', 'southoutside'); +addParameter(p, 'legendOrientation', 'horizontal'); + +parse(p, varargin{:}); + +%% Recall data +%%% diplay plot config +addMarkers = p.Results.addMarkers; % add different markers to lines +changeColors = p.Results.changeColors; % change lines colors +changeLineStyle = p.Results.changeLineStyle; % to diversify lines of the same color; +% change colors has to be true +legendLocation = p.Results.legendLocation; +legendOrientation = p.Results.legendOrientation; + +% width\height ratio +if p.Results.WHratio == 0 + changeWHratio = false; % true = change width/height ratio; false = keep the same ratio +else + changeWHratio = true; + WHratio = p.Results.WHratio; +end +% forced markers +if p.Results.forcedMarkers == 0 + forcedMarkers = false; + nForcedMarkers = nan; +else + forcedMarkers = true; + nForcedMarkers = p.Results.forcedMarkers*ones(1,length(fig.Children)); +end + +if (~addMarkers) && (forcedMarkers) + warning('addMarker is false and forcedMarkers is true: markers are not displayed') +end + +figPath = p.Results.figurePath; +if ~strcmp(figPath,'') && ~(strcmp(figPath(end),'\') || strcmp(figPath(end),'/')) + figPath = strcat(figPath,'\'); +end + +%%% dimensions setup +mult = 1.5; % multiplier for .fig file view +textwidthCm = 16.54; % \textwidth length in centimeters + +fontsize = 9; % reference font size +fontsizeLegend = fontsize*0.8; + +%% colors and options lists +if not(p.Results.satelliteMapColors) + colors = { + '#4658A9' % SkywardBlue + '#D3212D' % AmaranthRed + '#00A86B' % Jade + '#FDAF00' % yellowHoney + '#E94196' % Pink + '#FF5A36' % PortlandOrange + '#6CC407' % AppleGreen + '#B100FF' % violet + '#88ACE0' % LightCobalt + }; +else + colors = { + '#D3212D' % AmaranthRed + '#FDAF00' % yellowHoney + '#E94196' % Pink + '#6CC407' % AppleGreen + '#B100FF' % violet + '#FF5A36' % PortlandOrange + '#4658D9' % SkywardBlue (more blue) + '#04B96D' % Jade (lighter) + '#80A9DD' % LightCobalt (darker) + }; +end + +markers = { + 'o' + '<' + 'square' + 'p' + '+' + '>' + 'h' + 'diamond' + 'v' + '+' + '*' + '^' + 'x'}; + +linestylesList = {'-' + '--'; + ':'; + '-.'}; + +nColors = length(colors); +nMarkers = length(markers); +nStyles = length(linestylesList); + +%% figure +f = fig; % figure + +widthPos = textwidthCm*percTextwidth*mult; +if ~changeWHratio + WHratio = f.Position(3)/f.Position(4); % retrieve current WHratio +end +heightPos = widthPos/WHratio; + +f.Units = "centimeters"; +f.Position(3:4) = [widthPos heightPos]; + +%% check if figure is a tiledchart +if isa(f.Children,'matlab.graphics.layout.TiledChartLayout') + f = fig.Children; +end + +%% +for k = 1:length(f.Children) % for each subfigure + + if strcmp(f.Children(k).Type, 'uicontextmenu') + continue; % To avoid errors on ContextMenu + end + + ax = f.Children(k); % axes + + % no minor grid + if isfield(ax,'MinorGridLineStyle') + ax.MinorGridLineStyle = 'none'; + end + + % interpreter + listFieldnames = fieldnames(ax); + indexInterpreter = find(contains(listFieldnames,'Interpreter')); + + if ~isempty(indexInterpreter) + for i = 1:length(indexInterpreter) + ax.(listFieldnames{indexInterpreter(i)}) = 'latex'; + end + end + + for i = 1:length(listFieldnames) + try + subfieldNames = fieldnames(ax.(listFieldnames{i})); + indexSubInterpreter = find(strcmp(subfieldNames, 'Interpreter')); + if ~isempty(indexSubInterpreter) + for j = 1:length(indexSubInterpreter) + ax.(listFieldnames{i}).(subfieldNames{indexSubInterpreter(j)}) = 'latex'; + end + end + catch + end + end + + + % fontName + indexFontName = find(contains(listFieldnames,'FontName')); + if ~isempty(indexFontName) + for i = 1:length(indexFontName) + ax.(listFieldnames{indexFontName(i)}) = 'Palatino Linotype'; + end + end + + % fontSize + indexFontSize = find(contains(listFieldnames,'FontSize')); + if ~isempty(indexFontSize) + removeInd = [find(contains(listFieldnames,'FontSizeMode')); ... + find(contains(listFieldnames, 'FontSizeMultiplier'))]; + for i = 1:length(indexFontSize) + if sum(removeInd == indexFontSize(i))==0 + ax.(listFieldnames{indexFontSize(i)}) = fontsize*mult; + end + end + end + + ax.LineWidth = 0.5; + if isa(ax,'matlab.graphics.illustration.Legend') % check if axes is a legend + leg = ax; + leg.Location = legendLocation; + leg.Orientation = legendOrientation; + leg.FontSize = fontsizeLegend*mult; + while leg.Position(3)>0.8 && leg.NumColumns>1 + leg.NumColumns = leg.NumColumns-1; + end + elseif isa(ax, 'matlab.graphics.axis.Axes') || isa(ax, 'matlab.graphics.axis.GeographicAxes') + % grid + if isa(ax, 'matlab.graphics.axis.Axes') && p.Results.grid + ax.XGrid = "on"; + ax.YGrid = "on"; + ax.ZGrid = "on"; + elseif isa(ax, 'matlab.graphics.axis.Axes') + ax.XGrid = "off"; + ax.YGrid = "off"; + ax.ZGrid = "off"; + end + + jLines = 0; + jHistograms = 0; + jSurface = 0; + jScatter = 0; + jStair = 0; + jCostantLines = 0; + countColors = 0; + countMarkers = 0; + indexLinesColors = []; + indexSurfaceColors = []; + indexScatterColors = []; + indexStairColors = []; + indexLinesMarkers = []; + indexScatterMarkers = []; + indexStairMarkers = []; + for j = 1:length(ax.Children) + if isa(ax.Children(j),'matlab.graphics.chart.primitive.Line') + jLines = jLines+1; countColors = countColors + 1; + countMarkers = countMarkers + 1; + lines(jLines) = ax.Children(j); + indexLinesColors = [indexLinesColors countColors]; + indexLinesMarkers = [indexLinesMarkers countMarkers]; + elseif isa(ax.Children(j),'matlab.graphics.chart.decoration.ConstantLine') + jCostantLines = jCostantLines+1; + ax.Children(j).LineWidth = 1*mult; + ax.Children(j).Color = [0 0 0]; + constLines(jCostantLines) = ax.Children(j); + elseif changeColors && isa(ax.Children(j),'matlab.graphics.chart.primitive.Histogram') + jHistograms = jHistograms+1; + histgrams(jHistograms) = ax.Children(j); + elseif isa(ax.Children(j), 'matlab.graphics.chart.primitive.Surface') + jSurface = jSurface + 1; countColors = countColors + 1; + surfaces(jSurface) = ax.Children(j); + indexSurfaceColors = [indexSurfaceColors countColors]; + elseif isa(ax.Children(j), 'matlab.graphics.chart.primitive.Scatter') + jScatter = jScatter + 1; countColors = countColors + 1; + countMarkers = countMarkers + 1; + scatters(jScatter) = ax.Children(j); + indexScatterColors = [indexScatterColors countColors]; + indexScatterMarkers = [indexScatterMarkers countMarkers]; + elseif isa(ax.Children(j), 'matlab.graphics.chart.primitive.Stair') + jStair = jStair + 1; countColors = countColors +1; + countMarkers = countMarkers + 1; + stairs(jStair) = ax.Children(j); + indexStairColors = [indexStairColors countColors]; + indexStairMarkers = [indexStairMarkers countMarkers]; + end + end + existLegend = isfield(ax,'Legend'); + if existLegend + leg = ax.Legend; + leg.Location = legendLocation; + leg.Orientation = legendOrientation; + leg.FontSize = fontsizeLegend*mult; + while leg.Position(3)>0.8 + leg.NumColumns = leg.NumColumns-1; + end + end + + %% change graphics + existLines = ~isempty(indexLinesColors); + existSurfaces = ~isempty(indexSurfaceColors); + existScatters = ~isempty(indexScatterColors); + existStairs = ~isempty(indexStairColors); + existConstantLines = exist("constLines", 'var'); + existHistograms = exist('histgrams','var'); + + if existConstantLines + nCLines = length(constLines); + for i = 1:nCLines + index = mod(i-1, nStyles) + 1; + constLines(nCLines+1-i).LineStyle = linestylesList{index}; + end + end + + if existLines || existSurfaces || existScatters || existStairs || existHistograms + if ~existLines + lines = []; + end + if ~existSurfaces + surfaces = []; + end + if ~existScatters + scatters = []; + end + if ~existStairs + stairs = []; + end + + Nlines = length(lines); + Nsurface = length(surfaces); + Nscatter = length(scatters); + Nstair = length(stairs); + + %%% change colors + if changeColors + nIndexColors = Nlines + Nsurface + Nscatter + Nstair; + iLines = 0; + iSurface = 0; + iScatter = 0; + iStair = 0; + for i = 1:nIndexColors + index = mod(i-1, nColors) + 1; + if any((nIndexColors+1-i)==indexLinesColors) + iLines = iLines+1; + lines(Nlines+1-iLines).Color = colors{index}; + elseif any((nIndexColors+1-i)==indexSurfaceColors) + iSurface = iSurface + 1; + surfaces(Nsurface+1-iSurface).FaceColor = colors{index}; + surfaces(Nsurface+1-iSurface).EdgeColor = colors{index}; + elseif any((nIndexColors+1-i)==indexScatterColors) + iScatter = iScatter+1; + scatters(Nscatter+1-iScatter).MarkerEdgeColor = colors{index}; + scatters(Nscatter+1-iScatter).MarkerFaceColor = 'none'; + elseif any((nIndexColors+1-i)==indexStairColors) + iStair = iStair + 1; + stairs(Nstair+1-iStair).Color = colors{index}; + end + end + + if existHistograms + Nhist = length(histgrams); + for i = 1:Nhist + index = mod(i-1, nColors) + 1; + histgrams(Nhist+1-i).FaceColor = colors{index}; + histgrams(Nhist+1-i).EdgeColor = [0 0 0]; + end + end + end + + %%% addMarkers + if addMarkers + nIndexMarkers = Nlines + Nscatter; + iLines = 0; + iScatter = 0; + for i = 1:nIndexMarkers + index = mod(i-1, nMarkers) + 1; + if any((nIndexMarkers+1-i)==indexLinesMarkers) + iLines = iLines + 1; + lines(Nlines+1-iLines).Marker = markers{index}; + lines(Nlines+1-iLines).MarkerSize = mult*3; + else + iScatter = iScatter + 1; + scatters(Nscatter+1-iScatter).Marker = markers{index}; + scatters(Nscatter+1-iScatter).LineWidth = mult*1.5; + end + end + end + + %%% lines + for i = 1:Nlines + % changeLineStyle + if changeLineStyle + index = mod(i-1, nStyles) + 1; + lines(Nlines+1-i).LineStyle = linestylesList{index}; + end + + % forcedMarkers + nElements = length(lines(Nlines+1-i).XData); + if forcedMarkers && nElements>nForcedMarkers(k) + markerIndices = linspace(1,nElements,nForcedMarkers(k)); + lines(Nlines+1-i).MarkerIndices = round(markerIndices,0); + end + lines(Nlines+1-i).LineWidth = mult*1.5; % general settings + end + end + + clear('lines', 'histgrams', 'surfaces', 'scatters', 'stairs', 'constLines'); + end +end + -- GitLab