function getfigure() % This function adds a new menu option to copy the contents of a figure (axes, % lines, or even the whole figure) into the current figure % % first call adds the menu, second call deletes it % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - % V5.6 - 2023 % Eduard Clotet & J. Palacín % http://robotica.udl.cat hmenu = findobj(gcf,'Tag','getfigure'); if (isempty(hmenu)) % Initialize menu options h_get = uimenu(gcf,'label','Getfigure','Tag','getfigure'); uimenu(h_get,'label','Get a figure','callback',{@copy, 'Figure', 'Original'},'TAG','GET'); uimenu(h_get,'label','Get a figure (replicate figure size)','callback',{@copy, 'Figure', 'Exact'},'TAG','GET'); uimenu(h_get,'label','Get a specific Axes','callback',{@copy, 'Axes', 'Resize'},'TAG','GET','separator','on'); uimenu(h_get,'label','Get a specific Axes (replicate axes size)','callback',{@copy, 'Axes', 'Original'},'TAG','GET'); %uimenu(h_get,'label','Copy all the content of a specific Axes (and keep its original size)','separator','on','callback',{@copy,'Axes','OriginalFigure'},'TAG','GET'); uimenu(h_get,'label','Copy the content of specific Axes','separator','on','callback',{@merge,'NoResize'},'TAG','GET'); uimenu(h_get,'label','Copy the content of specific Axes (replicate figure/axes size)','callback',{@merge,'ResizeFigAndAx'},'TAG','GET'); uimenu(h_get,'label','Copy one line','separator','on','callback',{@copy, 'Line','Original'},'TAG','GET'); uimenu(h_get,'label','Copy one line (subsampled)','callback',{@copy, 'Line','Subsampled'},'TAG','GET'); undoButton = uimenu(h_get,'label','Undo last Get','callback',@undo_copy,'separator','on','enable','off','TAG','GET'); uimenu(h_get,'label','Cancel','callback',@cancelCopy,'separator','on','enable','off','Tag','UNDO'); moveToMenu = uimenu(h_get,'label','Reorder visual stacking','separator','on','TAG','GET'); uimenu(moveToMenu,'label','Send to front','callback',{@changeLayer,'Front'}); uimenu(moveToMenu,'label','Send to back','callback',{@changeLayer,'Back'}); uimenu(moveToMenu,'label','Move forward','callback',{@changeLayer,'Forward'}); uimenu(moveToMenu,'label','Move backwards','callback',{@changeLayer,'Backwards'}); uimenu(h_get,'label','Select one object and call inspector(gco)','callback',@openInspect,'separator','on','TAG','GET'); setColorsMenu = uimenu(h_get,'label','Select axes and change properties of','separator','on','TAG','GET'); uimenu(h_get,'label','Remove Getfigure from the menu','callback','delete(findobj(gcf,''Tag'',''getfigure''));','separator','on'); props = createPropertiesList(); numElements = length(props); for elemID = 1:numElements label = props{elemID}.label; propsList = props{elemID}.properties; menu_h = uimenu(setColorsMenu,'label',label); numProps = length(propsList); for propID = 1:numProps propName = propsList{propID}; uimenu(menu_h,'label',propName,'Callback',{@changeProp,label,propName}); end end %**************** % UNDO STRUCTURE %**************** % numActions: Number of actions listed % currentAction: ID of the last action % actionsList: List of objects involved in each action undoStructure.numActions = 0; undoStructure.currentAction = 0; undoStructure.actionsList = {}; undoButton.UserData = undoStructure; disp('''getfigure'' menu has been added to the menu'); % Set foccus on figure figure(gcf); else delete(hmenu); disp('''getfigure'' menu has been deleted'); end end function changeLayer(~, ~, direction) ginput(1); switch direction case 'Front' uistack(gco,'top'); case 'Back' uistack(gco,'bottom'); case 'Forward' uistack(gco,'up'); case 'Backwards' uistack(gco,'down'); end end function openInspect(~,~) if isempty(findobj(gcf,'Type','axes')) inspect(gcf); else ginput(1); inspect(gco); end end function props = createPropertiesList() props = cell(1,1); lineID = 1; quiverID = 2; props{lineID}.label = 'Lines'; props{lineID}.properties = {'Color','LineStyle','LineWidth','Marker','MarkerSize','MarkerFaceColor','MarkerEdgeColor'}; props{quiverID}.label = 'Quivers'; props{quiverID}.properties = {'Color','LineStyle','LineWidth','Marker','MarkerSize','MarkerFaceColor','MarkerEdgeColor','MaxHeadSize','AutoScale'}; end function cancelCopy(src, ~) src.Enable = 'off'; end function changeProp(~, ~, elementType, property) ginput(1); switch lower(elementType) case 'lines' axID = gca(); newVal = lineProperties(property); if isempty(newVal) return; end childList = axID.Children; numChildren = length(childList); for childID = 1:numChildren switch class(childList(childID)) case 'matlab.graphics.chart.primitive.Line' childList(childID).(property) = newVal; end end case 'quivers' axID = gca(); newVal = quiverProperties(property); childList = axID.Children; numChildren = length(childList); for childID = 1:numChildren switch class(childList(childID)) case 'matlab.graphics.chart.primitive.Quiver' childList(childID).(property) = newVal; end end end end function newVal = lineProperties(property) newVal = []; switch property case 'Color' newVal = uisetcolor(); case 'Marker' optionsList = {'+','o','*','.','x','square','diamond',... 'v','^','>','<','pentagram','hexagram','none'}; selectedVal = createPopupMenu(optionsList); newVal = optionsList{selectedVal}; case 'LineStyle' optionsList = {'-','--',':','-.','none'}; selectedVal = createPopupMenu(optionsList); newVal = optionsList{selectedVal}; case 'LineWidth' prompt = {'New line width:'}; dlg_title = 'Line Width'; num_lines = 1; defaultans = {'1'}; newVal = str2double(inputdlg(prompt,dlg_title,num_lines,defaultans)); if isnan(newVal) || newVal <= 0 return; end case 'MarkerSize' prompt = {'New marker size:'}; dlg_title = 'marker Size'; num_lines = 1; defaultans = {'6'}; newVal = str2double(inputdlg(prompt,dlg_title,num_lines,defaultans)); if isnan(newVal) || newVal <= 0 return; end case 'MarkerFaceColor' newVal = uisetcolor(); case 'MarkerEdgeColor' newVal = uisetcolor(); end end function newVal = quiverProperties(property) newVal = []; switch property case 'Color' newVal = uisetcolor(); case 'Marker' optionsList = {'+','o','*','.','x','square','diamond',... 'v','^','>','<','pentagram','hexagram','none'}; selectedVal = createPopupMenu(optionsList); newVal = optionsList{selectedVal}; case 'LineStyle' optionsList = {'-','--',':','-.','none'}; selectedVal = createPopupMenu(optionsList); newVal = optionsList{selectedVal}; case 'LineWidth' prompt = {'New line width:'}; dlg_title = 'Line Width'; num_lines = 1; defaultans = {'1'}; newVal = str2double(inputdlg(prompt,dlg_title,num_lines,defaultans)); if isnan(newVal) || newVal <= 0 return; end case 'MarkerSize' prompt = {'New marker size:'}; dlg_title = 'marker Size'; num_lines = 1; defaultans = {'6'}; newVal = str2double(inputdlg(prompt,dlg_title,num_lines,defaultans)); if isnan(newVal) || newVal <= 0 return; end case 'MarkerFaceColor' newVal = uisetcolor(); case 'MarkerEdgeColor' newVal = uisetcolor(); case 'MaxHeadSize' prompt = {'New Max Head Size:'}; dlg_title = 'Max Head Size'; num_lines = 1; defaultans = {'0.2'}; newVal = str2double(inputdlg(prompt,dlg_title,num_lines,defaultans)); if isnan(newVal) || newVal <= 0 return; end case 'AutoScale' optionsList = {'on','off'}; selectedVal = createPopupMenu(optionsList); newVal = optionsList{selectedVal}; end end function selectedVal = createPopupMenu(optionsList) selectedVal = -1; popWindow = figure('Units','Normalized','Position',[0.4,0.4,0.2,0.2],... 'MenuBar','none'); popList = uicontrol(popWindow, 'style','pop',... 'unit','normalized',... 'position',[0.2,0.45,0.6,0.1],... 'fontsize',12,... 'fontweight','bold',... 'string',optionsList,... 'value',1); uicontrol(popWindow, 'style','pushbutton',... 'unit','normalized',... 'position',[0.2,0.2,0.2,0.1],... 'fontsize',12,... 'fontweight','bold',... 'string','Accept','Callback',{@acceptCallback,popList}); waitfor(popWindow); function acceptCallback(src,~, popList) selectedVal = popList.Value; delete(src.Parent); end end function copy(src, ~, target, targetSize) persistent initial increment addlast if isempty(initial) % initialize persistent subsampling values initial = 1; increment = 6; addlast = 'no'; end % Enable cancel button % Retrieve undo actions list btnID = getButtonID(src.Parent,'Cancel'); if isempty(btnID) disp('Cancel button not found'); return; end % Hide other options hcf = gcf; set(findobj(hcf,'TAG','GET'),'Visible','off'); cancelBtn = src.Parent.Children(btnID); cancelBtn.Enable = 'on'; % Get destination figure dstFigure = gcf; % Get the current type of element initialObject = gco; switch target case 'Axes' expectedSelectionClass = 'matlab.graphics.axis.Axes'; initialObject = get(dstFigure,'CurrentAxes'); case 'Figure' expectedSelectionClass = 'matlab.ui.Figure'; initialObject = gcf; case 'Line' expectedSelectionClass = 'matlab.graphics.chart.primitive.Line'; initialObject = gco; end % Set targetDectected flag to 0 targetDetected = 0; % Wait untill the user selects an object that matches the target class while ~targetDetected && strcmp(cancelBtn.Enable,'on') if strcmp(target,'Figure') currentElm = gcf; elseif strcmp(target,'Axes') currentElm = get(gcf,'CurrentAxes'); else currentElm = gco; end % Check if the user selected an object matching the target class % and its different from the initial object if isa(currentElm, expectedSelectionClass) && ~isequal(initialObject, currentElm) targetDetected = 1; else refresh(); drawnow(); end end % Show the other menus set(findobj(hcf,'TAG','GET'),'Visible','on'); % Check if user cancelled the action if strcmp(cancelBtn.Enable, 'off') disp('Copy action canceled'); return; end % Get the list of elements to copy and the number of elements on that % list [cpyElmsList, numCpyItems] = getTargetElms(currentElm); % Initialize list of newly added objects newlyAddedObjects = {}; % Copy elements on cpyElmsList for i = 1:numCpyItems switch class(cpyElmsList{i}) case 'matlab.graphics.illustration.Legend' la = copyobj([cpyElmsList{i}, cpyElmsList{i}.Axes], dstFigure); case 'matlab.graphics.axis.Axes' la = copyobj(cpyElmsList{i}, dstFigure); case 'matlab.graphics.chart.primitive.Line' dstChildsList = dstFigure.Children; numChilds = length(dstChildsList); targetChild = []; for childID = 1:numChilds if isa(dstChildsList(childID), 'matlab.graphics.axis.Axes') targetChild = dstChildsList(childID); break; end end if isempty(targetChild) figure(dstFigure); targetChild = axes(); end la = copyobj(cpyElmsList{i}, targetChild); if strcmpi(targetSize,'subsampled') answer=inputdlg({'Initial value of the subsampled line (initial)','Subsampling value (increment)','Force the inclusion of the last value (yes/no)'},'line([initial:increment:end])',1,{num2str(initial),num2str(increment),addlast}); drawnow; if(isempty(answer)) delete(la) return; end initial = str2double(char(answer(1))); increment = str2double(char(answer(2))); addlast = lower(char(answer(3))); switch addlast(1) case 'y' addlast = 'yes'; otherwise addlast = 'no'; end numElms = length(la.XData); if mod(numElms, increment) == 0 || addlast(1) == 'n' % No need to add the last element la.XData = la.XData(initial:increment:end); la.YData = la.YData(initial:increment:end); if ~isempty(la.ZData) la.ZData = la.ZData(initial:increment:end); end else lastElm.x = la.XData(end); lastElm.y = la.YData(end); if ~isempty(la.ZData) lastElm.z = la.ZData(end); end la.XData = [la.XData(initial:increment:end),lastElm.x]; la.YData = [la.YData(initial:increment:end),lastElm.y]; if ~isempty(la.ZData) la.ZData = [la.ZData(initial:increment:end),lastElm.z]; end end end end if length(la) == 1 newlyAddedObjects{end+1} = la; %#ok Total number of elements is unknown else numAddedElems = length(la); for elemID = 1:numAddedElems newlyAddedObjects{end+1} = la(elemID); %#ok Total number of elements is unknown end end end if strcmp(targetSize, 'Resize') la.Position = [0.1300 0.1100 0.7750 0.8150]; end if strcmp(target,'Figure') && strcmp(targetSize,'Exact') dstFigure.Position(3:4) = currentElm.Position(3:4); end if strcmp(target,'Axes') && strcmp(targetSize,'OriginalFigure') dstFigure.Position(3:4) = currentElm.Parent.Position(3:4); end % Retrieve undo actions list btnID = getButtonID(src.Parent,'Undo last Get'); if isempty(btnID) disp('Button not found'); end undoButton = src.Parent.Children(btnID); undoButton.Enable = 'on'; % Update undo actions list undoStructure = undoButton.UserData; undoStructure.numActions = undoStructure.numActions + 1; undoStructure.currentAction = undoStructure.currentAction + 1; undoStructure.actionsList{undoStructure.currentAction} = newlyAddedObjects; undoButton.UserData = undoStructure; cancelBtn.Enable = 'off'; end function btnID = getButtonID(parentID,targetName) childsList = parentID.Children; numChilds = length(childsList); btnID = []; for childID = 1:numChilds currentChild = childsList(childID); if strcmpi(currentChild.Label,targetName) btnID = childID; end end if isempty(btnID) return; end end function merge(src, ~, targetSize) btnID = getButtonID(src.Parent,'Cancel'); if isempty(btnID) disp('Cancel button not found'); return; end % Hide other options hcf = gcf; set(findobj(hcf,'TAG','GET'),'Visible','off'); cancelBtn = src.Parent.Children(btnID); cancelBtn.Enable = 'on'; % % % get destination axes (creates an axes if no axes in the figure) % dstAx = gca; expectedSelectionClass = 'matlab.graphics.axis.Axes'; initialObject = gca; targetDetected = 0; while ~targetDetected && strcmp(cancelBtn.Enable,'on') currentElm = gca; if isa(currentElm, expectedSelectionClass) && ~isequal(initialObject, currentElm) targetDetected = 1; else refresh(); drawnow(); end end % Show other options set(findobj(hcf,'TAG','GET'),'Visible','on'); % Check if user cancelled the action if strcmp(cancelBtn.Enable, 'off') disp('Exit') return; end [cpyElmsList, numCpyItems] = getTargetElms(currentElm); newlyAddedObjects = {}; % get destination axes (creates an axes if no axes in the figure) figure(hcf); dstAx = gca; for i = 1:numCpyItems switch class(cpyElmsList{i}) case 'matlab.graphics.illustration.Legend' axesObj = cpyElmsList{i}.Axes; case 'matlab.graphics.axis.Axes' axesObj = cpyElmsList{i}; end childList = axesObj.Children; numChildren = length(childList); for childID = numChildren:-1:1 newlyAddedObjects{end+1} = copyobj(childList(childID), dstAx); %#ok unknown total number of elms uistack(newlyAddedObjects{end},'top'); end end if strcmp(targetSize,'ResizeFigAndAx') dstAx.Position = currentElm.Position; dstAx.Parent.Position(3:4) = currentElm.Parent.Position(3:4); % Revise axis limits of the destination c = axis(dstAx); c2 = axis(currentElm); if (c(1) > c2(1)) c(1) = c2(1); end if (c(2) < c2(2)) c(2) = c2(2); end if (c(3) > c2(3)) c(3) = c2(3); end if (c(4) < c2(4)) c(4) = c2(4); end if length(c2) > 4 % 3D if (length(c2) > length(c)) c(5) = c2(5); c(6) = c2(6); else if (c(5) < c2(5)) c(5) = c2(5); end if (c(6) < c2(6)) c(6) = c2(6); end end end % set the new limits axis(dstAx,c); zoom(dstAx,'reset'); end % Retrieve undo actions list btnID = getButtonID(src.Parent,'Undo last Get'); if isempty(btnID) disp('Button not found'); end undoButton = src.Parent.Children(btnID); undoButton.Enable = 'on'; % Update undo actions list undoStructure = undoButton.UserData; undoStructure.numActions = undoStructure.numActions + 1; undoStructure.currentAction = undoStructure.currentAction + 1; undoStructure.actionsList{undoStructure.currentAction} = newlyAddedObjects; undoButton.UserData = undoStructure; cancelBtn.Enable = 'off'; end function [axesList, lastAxesID] = getTargetElms(srcElm) switch class(srcElm) case 'matlab.graphics.chart.primitive.Line' axesList{1} = srcElm; lastAxesID = 1; case 'matlab.graphics.axis.Axes' childList = srcElm.Children; numChildren = length(childList); axesList = cell(1, 1); % Check if this axis is linked to a Legend object for childID = 1:numChildren if isa(childList(childID),'matlab.graphics.illustration.Legend') if isequal(childList(childID).Axes) % Axes linked to a legend, just return the legend axesList{1} = childList(childID); lastAxesID = 1; return; end end end % No legend was linked to this axes, return the axes object axesList{1} = srcElm; lastAxesID = 1; case 'matlab.ui.Figure' childList = srcElm.Children; numChildren = length(childList); legendsList = cell(1, numChildren); lastLegendID = 0; axesList = cell(1, numChildren); for childID = 1:numChildren if isa(childList(childID),'matlab.graphics.illustration.Legend') lastLegendID = lastLegendID + 1; legendsList{lastLegendID} = childList(childID); axesList{lastLegendID} = legendsList{lastLegendID}.Axes; end end legendsList = legendsList(1:lastLegendID); lastAxesID = lastLegendID; for childID = 1:numChildren if isa(childList(childID),'matlab.graphics.axis.Axes') found = 0; % check if this axes has already been detected as part of legend for legendID = 1:lastLegendID if isequal(childList(childID), axesList{legendID}) found = 1; end end if ~found lastAxesID = lastAxesID + 1; axesList{lastAxesID} = childList(childID); end end end axesList = axesList(1:lastAxesID); axesList(1:lastLegendID) = legendsList; end end function undo_copy(source,~) % UNDO function undoStructure = source.UserData; undoID = undoStructure.currentAction; deleteElems = undoStructure.actionsList{undoID}; % remove one item at a time for elemID = 1:length(deleteElems) if ishandle(deleteElems{elemID}) % Try-catch block as some elements may have been deleted % manually by the user try delete(deleteElems{elemID}); catch end end end % Update UNDO structure undoStructure.actionsList(undoID) = []; undoStructure.currentAction = undoStructure.currentAction - 1; undoStructure.numActions = undoStructure.numActions -1; source.UserData = undoStructure; % Disable undo option if undoStructure.numActions == 0 source.Enable = 'off'; end end