%FORMATFIG is used to modify figure formatting before exportation. It is
%based on exportfig (Ben Hinkle @MathWorks) but only some features of
%exportfig have been kept. Essentially, all the aspects of figure boundings
%and figure exportation have been removed. It has been found that using an
%eps export and then a file conversion is far more reliable to obtain a
%tight figure bounding. Many features have been added, including upgrades
%related to Matlab 7.x.
%
%CALL:
%
% OPT_STRUCT = FORMATFIG( ...
% FIG_HANDLE, [ OPT_STRUCT, ] [ 'PARAM_NAME', PARAM_VALUE, ] ... )
%
% OPT_STRUCT: structure with fields .PARAM_NAME and values .PARAM_VALUE
%
% NB: - for parameters present both in structure and list, the
% values of the list will be used.
% - the only required input is the figure handle FIG_HANDLE.
% - if no parameters are given, OPT_STRUCT with default values is
% returned.
%
%PARAMETERS
%
% FIGURE SIZE
%
% width
% Figure width in figure's PaperUnits. Positive scalar. Unchanged
% or scaled: [] (default).
%
% height
% Figure width in figure's PaperUnits. Positive scalar. Unchanged
% or scaled: [] (default).
%
% refobj
% Axes handle or string evaluating to an axes handle. If given,
% 'width' and 'height' are relative to the given axes. Default:
% [].
%
% Remark on width, height and refobj:
% Specifying only one dimension sets the other dimension so that the
% new figure aspect ratio is the same as the figure's or reference
% axes' current aspect ratio. If neither dimension is specified the
% size defaults to the width and height from the screen figure's or
% reference axes' size. 'PaperPositionMode' property is set to
% 'auto', so that the figure is displayed in real size (printed and
% screen versions match).
%
% activeposition
% Conserved axes position during figure and/or font resizing:
% 'outerposition' (default), 'position' or [] (no change).
% General guidelines:
% - Use 'outerposition' to let Matlab adjust axes dimensions
% automatically. Robust, but axes alignment and aspect ratio
% will be lost.
% - Use 'position' to maintain axes alignment (multiple subplots
% or superposed axes). Might lead to label overlap if labels
% are present between axes. Use 'adaptaxes' to correct label
% clipping by figure frame.
% NB: only for 7.x versions.
%
% paperunits
% Figure's PaperUnits property. Default: 'centimeters'. If empty
% ([]), the PaperUnits property of the figure is not changed.
%
% AXES LOCKING
%
% lockaxes
% True / False (default) to lock all axes limits and ticks while
% resizing.
%
% locklimits
% True / False (default): same as lockaxes but applied only for
% limits mode.
%
% lockticks
% True / False (default): same as 'lockaxes' but applied only for
% tick mode. Mostly useful when resizing fonts of LOG axis.
%
% RESIZING CORRECTIONS
%
% Remark:
% When resizing or increasing font size, a number of problems might
% occur: clipping of axes labels, misalignment of axes, label
% overlapping, non optimal legend positionning and so on. The
% parameters given here allow a certain control on these problems.
%
% adaptaxes
% True / False (default) to adapt axes size and position to the
% new font or figure sizes. This option can be used to avoid
% label overlapping or cropping. The 6.x and 7.x versions are
% different.
%
% 6.x version:
% Titles are not dealt with specially and may cause problems. Use
% 'rmtitle' to remove them. The power of 10 label should be dealt
% with nicely. This option works if several plots are present on
% a given figure, but it DOES NOT work with:
% - 3D plots
% - superposed axes
%
% 7.x versions:
% If 'activeposition' is set to 'outerposition', Matlab should do
% the job correctly, so that this option is mostly useful for
% 'position' mode. In any case, using this option will result in
% testing that all the axes Position+TightInset are included in
% the figure frame. If not, the figure margin will be increased.
% This option has no controlled effect on the space between
% subplots for 7.x versions.
%
% Linked to: 'marginfactor', 'tight' and 'matchcolorbars'.
%
% tight
% To obtain the smallest possible figure margins (at least
% in 'position' mode). 'tight' can be one of:
%
% 'x'
% Keep figure dimension along x, adapt y to conserve
% axes proportion (at least in 'position' mode)
% 'y'
% Keep figure dimension along y, adapt x to conserve
% axes proportion (at least in 'position' mode)
% 'xy'
% Keep figure dimension along both x and y
% []
% Not tight (default).
%
% Remarks:
% - 'tight' is applied after figure resizing so that the
% dimensions that are kept are the dimensions given in 'width'
% and/or 'height'.
% - 'tight' is useful to control the exported figure width while
% maintaining small margins (i.e. in combination with the
% option 'eps_loose' of FIG2PUBLIC).
% - In 'outerposition' mode, the margins will be tighter (but not
% the tightest) and the scaling will be approximatively
% conserved. This is due to an impredictable behaviour of the
% resizing function in this mode.
% - 'tight' is less or not efficient in a number of cases:
% 3D plots, axes with equal and tight options activated, axes
% with outsided legends.
% - Linked to: 'adaptaxes' and 'marginfactor'.
% - Only for 7.x versions.
%
% marginfactor
% A positive scalar (default 1) or a 4-column vector. Depending
% on the size of 'marginfactor', the behaviour is different:
%
% Scalar:
% Used as a multiplication factor of the margin
% corrections determined in 'adaptaxes' or 'tight' mode.
% This factor might be useful to decrease or increase the
% estimated corrections.
% Vector:
% Replace the automatic margin corrections by a vector
% containing custom corrections on each side of the
% figure frame, ordered as: [ left, bottom, right, top ].
% The values correspond to translations of the figure
% sides while maintaining the axes at a constant
% location. They are expressed in centimeters and applied
% to the figure obtained after the main figure resizing
% (i.e. the figure resized to match 'width' and/or
% 'height'). The sign of the values matters: to increase
% the margins, the vector must be [ <0,<0, >0, >0 ].
% This option is useful to obtain a consistent resizing
% between similar figures (if 'activeposition' is set to
% 'position').
% Remark:
% The vectorial version is allowed only for 7.x versions.
% It may have unpredictable behaviour for special
% figures, as described in 'tight' above.
%
% matchcolorbars
% True / False (default) to match the colorbars extent with their
% related plot extent. Complementary to 'adaptaxes'. Should
% always be used with 'adaptaxes' for versions older than 7. Does
% not work for 7.x versions since the field linking colorbars and
% plots has disappeared.
%
% keeptextin
% True / False (default) to keep the comment text boxes inside
% their parent axes. Shift the boxes horizontally or vertically
% if they cross the axes boundary.
%
% movelegto
% If not empty (default), move all the legends to the given
% location (see 'help legend' for details).
%
% lockaxesaa
% Same principle as 'lockaxes', but applied during the axes or
% margin adaptation process. Mostly useful when 'tight' option
% is used, to avoid clipping of edge tick labels. Default: true.
%
% COLORS and LINE STYLES
%
% colormap
% Apply a given colormap to the figure. If a string is given, it
% must be the same input as the COLORMAP function of Matlab.
% Otherwise, the color map array must be given. Applied before
% the 'color' option in the execution of the code. Default: [].
%
% color
% One of the strings:
%
% 'bw'
% Lines and text are colored in black and all other
% objects in grayscale
% 'gray'
% All objects are converted into grayscale, with scaling
% based on the current colors, not on the current values.
% Set 'colormap' option to 'gray' beforehand if
% necessary.
% 'bwtl'
% Text and lines are converted in black, other things not
% modified
% 'rgb'
% All objects are left as given (Default).
%
% Linked to: 'colormap' and 'stylemap'.
%
% stylemap
% Specifies how to map solid line colors to styles. One of:
%
% []
% No style change (default).
% 'bw'
% Built-in mapping that maps lines with the same color to
% the same style and otherwise cycles through the
% available styles.
% mls
% Matlab line style, as a string (e.g. '-').
%
% fh
% Function handle. The target function must take as input
% a cell array of line objects and output a cell array
% of line style strings.
%
% Remark: only SOLID lines are modified by this option.
%
% FONTS, LINES and MARKERS
%
% fontmode
% 'scaled', 'fixed', [] (default). If [], the fonts are left as
% they are.
%
% fontsize
% A positive scalar. In 'scaled' mode, the font size of each text
% object is multiplied by 'fontsize'. In 'fixed' mode, all the
% text objects are set to the font size given by 'fontsize'.
% If [] in 'scaled' mode, a scaling factor is computed from the
% figure resizing.
% Default: []. Units: points (in 'fixed' mode).
%
% fontsizemin, fontsizemax
% Font size limits in 'scaled' mode.
%
% linemode, linewidth, linewidthmin, linewidthmax
% Equivalent to fonts for lines.
%
% markermode, markersize, markersizemin, markersizemax
% Equivalent to fonts for markers.
%
%
% MISCELLANEOUS
%
% customcode
% Allow inclusion of custom code within FORMATFIG at different
% places. A 2-element cell array is expected: {'location','code'}
%
% 'location'
% Specifies where the code must be executed. Choice
% between 'top', 'postHid' (default), 'bot'. 'top' places
% the code at the top of formatfig, 'postHid' at the top
% but after the handle identification section and 'bot'
% at the very end of formatfig. When using 'postHid', the
% handles are available by classes in variables like
% allLines, allText, allAxes (...), so that NO deletion
% command should be used at that place.
%
% 'code'
% Any code given as character string compatible with
% EVAL() is accepted.
%
% Example: { 'postHid', 'set( allAxes, ''color'', ''k'' )' }
%
% rmtitle
% True / False (default) to remove all the axes titles.
%
%
% Last modification: Jonathan Rossel, 15.12.2010
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Modif log: (see file)
%
% 18.04.08 J.Rossel Use a trick to avoid a bug in colorbar
% management. If a text object containing
% a string is related to a colorbar, then
% its handle is reassigned to the label
% field (x or y according to the
% orientation of the bar) before
% processing.
%
% 21.04.08 J.Rossel Added the 'keeptextin' option.
%
% 22.04.08 J.Rossel When using 'adaptaxes', the text box
% units are set to an absolute unit to
% prevent Matlab from randomly modifying
% the box positions. The font scaling is
% then applied and the units are restored
% afterwards. Weird Matlab behaviour
% anyway.
% Observable with log x-axis, data units
% (xlabel shifted downwards).
%
% 29.04.08 J.Rossel Added the 'lockticks' option.
%
% 29.10.08 J.Rossel Corrected a bug in the exponent
% detection
%
% 23.03.09 J.Rossel findall has a bug in Matlab 7.8
% Updating text units with findall looses
% legends.
% Rem of 22.04.08 can't be reproduced
% on 7.8. Combine both: remove the
% process described in 22.04.08 for
% versions from 7.0.
%
% results obtained for 'adaptaxes' with
% large fonts were rubbish for 7.0 and
% later. Use the new
% ActivePositionProperty to replace the
% whole functionality. Still colorbars
% are not dealt with properly, but only
% for figures generated on older Matlab
% versions.
%
% Bug for 'keeptextin' corrected. The
% text boxes were not related correctly
% to their parent axes.
%
% 'movelegto' option added.
%
% 'matchcolorbars' option added
%
% 14.04.09 J.Rossel "oldactpos" undefined bug corrected.
% That bug appeared in 7.x when calling
% the routines with 'adaptaxes'=false.
%
% 17.04.09 J.Rossel For 7.x, in special cases, the
% automatic resizing does not work
% properly: e.g. increasing fonts when
% colorbar present and having long
% colorbar ticklabels and colorbar text
% labels. It seems that Matlab deals
% better with figure resizing. So the
% local function Local_AdaptAxes_7x has
% been added. This also solves the
% exponent problems (power of 10 being
% clipped).
%
% 20.04.09 J.Rossel Small bug corrected (appeared for
% colorbars without labels).
%
% 10.06.09 J.Rossel Modified options for coloring. Added
% the 'colormap' option. 'stylemap'
% applies to solid lines only.
%
% 20.10.09 J.Rossel Set WindowStyle to Normal before
% formatting for version above 7.x
%
% 17.11.09 J.Rossel Restore Latex interpreter if lost
% during saving. Applied at the end of
% the formatting to avoid some bugs, but
% might therefore be problematic.
%
% 'activeposition' option. Useful if axes
% must keep their alignement while being
% resized.
%
% 'marginfactor': to be used with
% 'adaptaxes'.
%
% 'stylemap': allow unique Matlab style
% input.
%
% 11.01.10 J.Rossel bug in latex interpreter restoration
% corrected.
%
% 22.04.10 A.Pitzschke correct bug with strfind when having cell with subcells
%
% 30.06.10 J.Rossel 'locklimits' added
%
% 11.10.10 J.Rossel 'customcode' added
%
% 12.10.10 J.Rossel Use of 'activeposition' modified. Now
% it is used independently of 'adaptaxes'.
% To avoid using it, leave it empty.
%
% Use of 'marginfactor' modified. Absolute
% extensions in each direction can now be
% given to get better control on final figure
% and axes size. For 6.x, 'marginfactor' now
% helps controlling the 'adaptaxes' option.
%
% 'adaptaxes' and 'keeptextin' can now be
% used when font size is not modified.
%
% 05.11.10 J.Rossel 'adaptaxes' 7.x modified. Now works with
% Position+TightInset instead of
% OuterPosition (less margins, less risk
% of problems due to corrupted outer
% position).
% Automatic margin correction for
% 'position' mode should be better.
% 'tight' option added
%
% 08.11.10 J.Rossel 'tight' option modified to
% allow proportion conservation.
% 'marginfactor' as a 4-column vectors
% now really is the absolute correction
% of the margins (for 'position' mode).
%
% 09.11.10 J.Rossel 'adaptaxes': better margin determination
% by matching axes position with plotbox
% beforehand (2D plots only).
% Also, outsided legends are dealt with
% correctly.
% param struct returned as function output.
% defaults: -1 replaced by [] for
% consistency.
%
% 10.11.10 J.Rossel 'paperunits' option added with default
% value 'centimeters'
%
% 15.12.10 J.Rossel 'lockaxesaa' option added.
%
% 16.12.10 J.Rossel patch for Matlab bug when locking log
% axis.
function opts = formatfig(varargin);
%param structure
opts = struct( ...
'width', [], ... %figure size
'height', [], ...
'refobj', [], ...
'activeposition', 'outerposition', ...
'paperunits', 'centimeters', ...
...
'lockaxes', false, ... %locking
'locklimits', false, ...
'lockticks', false, ...
...
'adaptaxes', false, ... %margin adaptation and other size correction
'tight', [], ...
'marginfactor', 1, ...
'lockaxesaa', true, ...
'matchcolorbars', false, ...
'keeptextin', false, ...
'movelegto', [], ...
...
'colormap', [], ... %colors
'color', 'rgb', ...
'stylemap', [], ...
...
'fontmode', [], ... %object sizes
'fontsize', [], ...
'fontmin', 8, ...
'fontmax', 60, ...
'linemode', [], ...
'linewidth', [], ...
'linemin', 0.5, ...
'linemax', 100, ...
'markermode', [], ...
'markersize', [], ...
'markermin', 0.5, ...
'markermax', 30, ...
...
'customcode', {{}}, ... %misc
'rmtitle', false ...
);
if nargin == 0
%empty call, to get opts.
return
end
%figure handle
H = varargin{1};
varargin(1) = [];
%version
verstr = version;
majorver = str2num(verstr(1));
%check whether a param structure has been given
%and deal with it
if isstruct(varargin{1}),
param_values = struct2cell(varargin{1});
param_names = fieldnames(varargin{1});
param = cell(1,2*length(param_names));
param(1:2:end-1) = param_names;
param(2:2:end) = param_values;
varargin(1) = []; %remove the structure
varargin = {param{:},varargin{:}}; %add the structure as a list
end
%check the fieldnames and fill the opts structure
fieldnamesS = fieldnames(opts);
for j=1:2:length(varargin)-1
if any(strcmpi(fieldnamesS,varargin{j})),
opts = subsasgn(opts,struct('type','.','subs',lower(varargin{j})),varargin{j+1});
end
end
% make sure figure is up-to-date
drawnow;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%execute custom code if any
if ~isempty( opts.customcode ) & strncmpi( opts.customcode{1}, 't', 1 )
eval( opts.customcode{2} );
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Process format
old.objs = {}; %kept to avoid too much recoding (JR)
old.prop = {};
old.values = {};
% JR: First of all: restore the link between the colorbars and their labels (if
% any). Required for size processing in 6.x versions. Maybe useful for 7.x
% (not sure), but don't do any harm anyway.
% Note: this requires to re-do the handle attribution, as something changes
% in the figure handles when relinking the colorbar labels.
%colorbar trick
%for some reasons (bug of most of Matlab versions) the label property of a text object
%for colorbars is lost when saving the figure. ie: you can add
%a label to a colorbar, but then the label is saved as a text
%object and the ylabel field of the colorbar contains a handle
%to an empty string text object! So...
allColorbars = findall( findall(H, 'type', 'axes'), 'tag', 'Colorbar');
for ii = 1 : length( allColorbars ),
ht = findall( allColorbars(ii), 'Type', 'text' );
t = get(ht,'String');
ind = find(~strcmp('',t));
if length(ind) == 1 & length(ht) > 0,
%there is only one non-empty text string
%assume it is the label
%Re-assign the text handle to the label field
pos = get(allColorbars(ii),'Position');
if pos(3) > pos(4),
%horizontal cb
set(allColorbars(ii),'Xlabel',ht(ind));
else
%vertical cb
set(allColorbars(ii),'Ylabel',ht(ind));
end
end
end
%store the handles (2nd part)
allLines = findall(H, 'type', 'line');
allSolidLines = findall( allLines, 'LineStyle', '-' );
allText = findall(H, 'type', 'text');
allAxes = findall(H, 'type', 'axes');
allColorbars = findall(allAxes, 'tag', 'Colorbar');
allImages = findall(H, 'type', 'image');
allLights = findall(H, 'type', 'light');
allPatch = findall(H, 'type', 'patch');
allSurf = findall(H, 'type', 'surface');
allRect = findall(H, 'type', 'rectangle');
allFont = [allText; allAxes];
allColor = [allLines; allText; allAxes; allLights];
allMarker = [allLines; allPatch; allSurf];
allEdge = [allPatch; allSurf];
allCData = [allImages; allPatch; allSurf];
%JR addition
tmp = strcmp(get(allAxes,'Tag'),'legend');
allLeg = allAxes(tmp); %legends only
allAxesNoLeg = allAxes(~tmp); %axes, but not legend
if ~isempty( opts.customcode ) & strncmpi( opts.customcode{1}, 'p', 1 )
%execute custom code if any
eval( opts.customcode{2} );
end
% lock axes limits, ticks and labels if requested
if opts.lockaxes
list1 = {'X','Y','Z'};
list2 = {'TickMode','TickLabelMode','LimMode'}; %NB: setting TickLabelMode to manual for log axis results in label corruption (whether this is a bug or not is arguable).
locklist = cellstr( [ reshape( repmat( char( list1 )', length( list2 ), 1 ), [], 1 ), repmat( char( list2 ), length( list1 ), 1 ) ] )'; %XTick..., Y..., etc
set( allAxesNoLeg, locklist, repmat( { 'manual' }, length( allAxesNoLeg ), length( locklist ) ) );
end
%JR addition
%lock ticks only (very useful for log axis, otherwise only the power of 10
%is displayed...)
if opts.lockticks
list1 = {'X','Y','Z'};
list2 = {'TickMode'};
locklist = cellstr( [ reshape( repmat( char( list1 )', length( list2 ), 1 ), [], 1 ), repmat( char( list2 ), length( list1 ), 1 ) ] )'; %XTick..., Y..., etc
set( allAxesNoLeg, locklist, repmat( { 'manual' }, length( allAxesNoLeg ), length( locklist ) ) );
%Avoid a bug in Matlab: with log axis, setting 'tickmode' to 'manual' corrupts the labels.
%This can be avoided by manually resetting the ticks with their original values:
set( allAxesNoLeg, { 'XTick'; 'YTick'; 'ZTick' }, get( allAxesNoLeg, { 'XTick'; 'YTick'; 'ZTick' } ) );
end
%JR addition
%lock axes limits only
if opts.locklimits
list1 = {'X','Y','Z'};
list2 = {'LimMode'};
locklist = cellstr( [ reshape( repmat( char( list1 )', length( list2 ), 1 ), [], 1 ), repmat( char( list2 ), length( list1 ), 1 ) ] )'; %XLim..., Y..., etc
set( allAxesNoLeg, locklist, repmat( { 'manual' }, length( allAxesNoLeg ), length( locklist ) ) );
end
%remove title
%JR addition
if opts.rmtitle,
tmp = get(allAxes,'Title');
if iscell(tmp), tmp = cell2mat(tmp); end
allTitles = tmp;
set(allTitles,'String',[]);
end
if majorver >= 7
if ~isempty( opts.activeposition ),
%JR: for Matlab 7 and above, set activepositionproperty before any
%changes to let Matlab resize everything automatically. By default,
%Outerposition is used. Position can also be used if axes alignment
%must be preserved. Should be ok with adaptaxes local function, but
%labels might be cropped.
oldactpos = LocalGetAsCell( allAxesNoLeg, 'ActivePositionProperty' ); %keep for restore afterwards
oldaxesunits = LocalGetAsCell( allAxesNoLeg, 'Units' );
set( allAxesNoLeg, 'ActivePositionProperty', opts.activeposition, 'Units', 'Normalized' );
elseif isempty( opts.activeposition ) & opts.adaptaxes ...
& length( unique( LocalGetAsCell( allAxesNoLeg, 'ActivePositionProperty' ) ) ) > 1
disp( [ '!!! When using ''adaptaxes'', all the axes should have the same ''ActivePositionProperty'' ', ...
'or an ''activeposition'' option should be given as well. ''adaptaxes'' might fail. !!!' ] );
end
end
% Process figure size parameters
%NB: only the figure dimensions are modified directly.
%The axes dimensions are adapted by Matlab automatically.
if majorver >= 7,
%JR: figures must be undocked first
set( H, 'WindowStyle', 'Normal' );
end
if ~isempty( opts.paperunits )
set( H, 'PaperUnits', opts.paperunits );
figurePaperUnits = opts.paperunits;
else
figurePaperUnits = get(H, 'PaperUnits');
end
oldFigureUnits = get(H, 'Units');
oldFigPos = get(H,'Position');
set(H, 'Units', figurePaperUnits);
figPos = get(H,'Position');
refsize = figPos(3:4);
if ~isempty( opts.refobj )
oldUnits = get(opts.refobj, 'Units');
set(opts.refobj, 'Units', figurePaperUnits); %make sure to have absolute units
r = get(opts.refobj, 'Position');
refsize = r(3:4);
set(opts.refobj, 'Units', oldUnits);
end
aspectRatio = refsize(1)/refsize(2); %width over height
if isempty( opts.width ) & isempty( opts.height )
opts.width = refsize(1);
opts.height = refsize(2);
elseif isempty( opts.width )
opts.width = opts.height * aspectRatio;
elseif isempty( opts.height )
opts.height = opts.width / aspectRatio;
end
wscale = opts.width/refsize(1);
hscale = opts.height/refsize(2);
sizescale = min(wscale,hscale);
set(H, 'PaperPositionMode', 'auto'); %computer screen used as ref, implies larger eps file if the figure is larger on screen than on paper (for zbuffer or opengl)
newPos = [figPos(1) figPos(2)+figPos(4)*(1-hscale) ...
wscale*figPos(3) hscale*figPos(4)]; %newpos on screen and on paper
set(H, 'Position', newPos);
set(H, 'Units', oldFigureUnits);
drawnow; %need to be called after resizing window
% Process color
%JR: colormap option
if ~isempty( opts.colormap ),
if ischar( opts.colormap ),
opts.colormap = colormap( opts.colormap );
end
set( H, 'Colormap', opts.colormap );
end
if any(strcmp(opts.color,{'bw', 'gray'})),
%compute and set gray colormap
oldcmap = get(H,'Colormap');
newgrays = 0.30*oldcmap(:,1) + 0.59*oldcmap(:,2) + 0.11*oldcmap(:,3);
newcmap = [newgrays newgrays newgrays];
old = LocalPushOldData(old, H, 'Colormap', oldcmap);
set(H, 'Colormap', newcmap); %JR: modifies the handle name for some reasons
%JR: redefine handles
allImages = findall(H, 'type', 'image');
allPatch = findall(H, 'type', 'patch');
allSurf = findall(H, 'type', 'surface');
allCData = [allImages; allPatch; allSurf];
%compute and set ColorSpec and CData properties to gray
old = LocalUpdateColors(allColor, 'color', old);
old = LocalUpdateColors(allAxes, 'xcolor', old);
old = LocalUpdateColors(allAxes, 'ycolor', old);
old = LocalUpdateColors(allAxes, 'zcolor', old);
old = LocalUpdateColors(allMarker, 'MarkerEdgeColor', old);
old = LocalUpdateColors(allMarker, 'MarkerFaceColor', old);
old = LocalUpdateColors(allEdge, 'EdgeColor', old);
old = LocalUpdateColors(allEdge, 'FaceColor', old);
old = LocalUpdateColors(allCData, 'CData', old); %useful if absolute RGB colors given instead of relative colormap values
end
% Process font parameters
if ~isempty(opts.fontmode)
oldfonts = LocalGetAsCell(allFont,'FontSize');
oldfontunits = LocalGetAsCell(allFont,'FontUnits');
if opts.adaptaxes & majorver < 7,
%JR addition: change text units before scaling fonts to avoid
%(random) repositioning by Matlab if adaptaxes is on and Matlab version
%below 7.
oldtextunits = LocalGetAsCell(allText,'Units');
set(allText,'Units','Centimeters'); %applied only during font resizing
end
switch (opts.fontmode)
case 'fixed'
if ~isempty(opts.fontsize),
set(allFont,'FontUnits','points'); %modif: used for 'fixed' before applying size
%JR addition: assess a scale factor
tmp = get(allFont,'FontSize');
if iscell(tmp), tmp = cell2mat(tmp); end
oldfontsinpoints = tmp;
fontscale = mean(opts.fontsize ./ oldfontsinpoints);
set(allFont,'FontSize',opts.fontsize);
end
case 'scaled'
if isempty(opts.fontsize)
fontscale = sizescale;
else
fontscale = opts.fontsize;
end
newfonts = LocalScale(oldfonts,fontscale,opts.fontmin,opts.fontmax);
set(allFont,{'FontSize'},newfonts);
set(allFont,'FontUnits','points'); %modif: used for 'scaled' after applying size. Needed because of axes resizing (below).
otherwise
error('Invalid FontMode parameter');
end
end
%JR addition: adapt axes to avoid label overlapping or cropping
if opts.adaptaxes | ~isempty( opts.tight ) | length( opts.marginfactor ) > 1 | opts.marginfactor(1) ~= 1,
if majorver < 7 & ( opts.adaptaxes | opts.marginfactor(1) ~= 1 ),
%6.x
%reset text units (cf above)
if ~isempty(opts.fontmode)
set(allText,{'Units'},oldtextunits);
end
%adapt axes
adaptscale = 0;
if ~isempty(opts.fontmode)
adaptscale = fontscale;
end
adaptscale = opts.marginfactor .* max( [ adaptscale, 1./sizescale ] );
%NB: previously only fontscale was used, but it might happen that the fonts remain the same while the figure size is reduced.
%In that case, this is equivalent to a relative increase of font size equal to 1/sizescale.
Local_AdaptAxes_6x( allAxesNoLeg, adaptscale );
else
%7.x
%for 7 and above, Matlab should deal with it properly after
%correct activation of ActivePositionProperty. But... in some
%cases, what Matlab should do and what Matlab does are 2
%differents things... For example, text labels of colorbars are
%still cropped when the font size is increased and if the
%ticklabels are long.
%Workaround: Matlab seems much smarter during figure resizing
%than during font resizing. So: set absolute units, increase
%figure size to include all labels, set normalized units,
%restore figure size.
%This method also works when keeping 'position' option.
Local_AdaptAxes_7x( H, allAxesNoLeg, allLeg, opts.marginfactor, opts.tight, opts.lockaxesaa );
end
%if there is a resize function (usually when legends are present),
%call it to reposition the legends
rf = get(H,'ResizeFcn');
if ~isempty(rf),
eval(rf);
end
end %option adapt
%Match colorbars
if opts.matchcolorbars,
if majorver >= 7,
disp( 'If you know where to find the field linking a colorbar and its plot, let me know!!!')
warning( 'Setting (outer)position makes it the active one!' );
else
%match colorbars with their related plot
Local_MatchColorBars( allAxesNoLeg );
end
end
%JR addition: force text boxes to stay inside its parent axes
if opts.keeptextin,
%loop on axes (not legend)
for ii = 1:length(allAxesNoLeg),
if ~isempty(get(allAxesNoLeg(ii),'ZTickLabel')),
%3D plot
%don't do anything
continue
end
%get children text boxes handles (no legend, no labels)
inText = findobj( allAxesNoLeg(ii), 'type', 'text'); %all accessible (findall=wrong!) text box children of current axes
tmp = get(inText,'Parent');
if length(tmp) > 1, tmp = cell2mat(tmp); end
tmp = find( strncmpi(get(tmp,'Tag'),'legend',6) | strncmpi(get(inText,'Tag'),'legend',6) );
inText(tmp) = []; %don't consider legends. The autosize function can be used for them.
for jj = 1:length(inText),
oldun = get(inText(jj),'Units');
set(inText(jj),'units','normalized');
ex = get(inText(jj),'extent');
pos = get(inText(jj),'position');
%check oversized boxes
%the "too wide" or "too high" cases cannot be dealt with
%automatically, the phrase must be rewritten accordingly...
%Reposition in a way that avoid dealing with the alignment
%properties
if ex(1) < 0,
%out on the left
pos(1) = pos(1) + abs(ex(1));
end
if ex(1)+ex(3) > 1,
%out on the right
pos(1) = pos(1) - (ex(1) + ex(3) - 1);
end
if ex(2) < 0,
%out at the bottom
pos(2) = pos(2) + abs(ex(2));
end
if ex(2) + ex(4) > 1,
%out at the top
pos(2) = pos(2) - (ex(2) + ex(4) - 1);
end
%apply changes
set(inText(jj),'Position',pos);
%restore units
set(inText(jj),'Units',oldun);
end %loop inText
end %loop axes
end %option keeptextin
%JR: move legends if required
if ~isempty( opts.movelegto ),
set( allLeg, 'Location', opts.movelegto );
end
if majorver >= 7 & ~isempty( opts.activeposition ),
%restore actpos and axes units
if ~iscell(oldactpos), oldactpos = {oldactpos}; end
if ~iscell(oldaxesunits), oldaxesunits = {oldaxesunits}; end
set(allAxesNoLeg,{'ActivePositionProperty'},oldactpos,{'Units'},oldaxesunits);
end
% Process line parameters
if ~isempty(opts.linemode)
oldlines = LocalGetAsCell(allMarker,'LineWidth');
old = LocalPushOldData(old, allMarker, {'LineWidth'}, oldlines);
switch (opts.linemode)
case 'fixed'
if ~isempty(opts.linewidth)
set(allMarker,'LineWidth',opts.linewidth);
end
case 'scaled'
if isempty(opts.linewidth)
scale = sizescale;
else
scale = opts.linewidth;
end
newlines = LocalScale(oldlines, scale, opts.linemin, opts.linemax);
set(allMarker,{'LineWidth'},newlines);
end
end
% Process line-markers parameters
%JR addition
if ~isempty(opts.markermode)
oldmarkers = LocalGetAsCell(allLines,'MarkerSize');
old = LocalPushOldData(old, allLines, {'MarkerSize'}, oldmarkers);
switch (opts.markermode)
case 'fixed'
if ~isempty(opts.markersize)
set(allLines,'MarkerSize',opts.markersize);
end
case 'scaled'
if isempty(opts.markersize)
scale = sizescale;
else
scale = opts.markersize;
end
newmarkers = LocalScale(oldmarkers, scale, opts.markermin, opts.markermax);
set(allLines,{'MarkerSize'},newmarkers);
end
end
% process line-style map
%JR: Must appear here due to a strange recoloring of the legends
%when changing font size (thanks to Matlab's programmers)
if ~isempty(opts.stylemap)
if ischar(opts.stylemap),
if strcmpi(opts.stylemap,'bw')
newlstyle = LocalMapColorToStyle(allSolidLines);
else
%JR: allow uniform linestyle, include all solide lines
allSolidLines = findall(H,'linestyle','-');
newlstyle = {opts.stylemap};
end
if ~isempty(allSolidLines)
set(allSolidLines,{'LineStyle'},newlstyle);
end
else
try
newlstyle = feval(opts.stylemap,allSolidLines);
set(allSolidLines,{'LineStyle'},newlstyle);
catch
warning(['Skipping stylemap. ' lasterr]);
end
end
end
%JR: coloring option re-designed: line and text to black
%Must appear here due to a strange recoloring of the legends
%when changing font size (thanks to Matlab's programmers)
if any(strcmp(opts.color,{'bw', 'bwtl'})),
%Set lines and texts to black
set( [ allText; allLines ], 'color', 'k' );
end
%JR: Set Latex interpreter. This option is sometimes lost when saving the
%figure, especially for colorbars. This should appear at the beginning
%so that these latex strings are taken into account during axes resizing
%processes. The problem is that the latex text boxes have corrupted
%'extent' properties which causes problems during axes resizing (the axes
%outerposition is largely overestimated).
textstr = LocalGetAsCell( allText, 'string', false ); %get all the strings in cell array
for ii = 1 : length( textstr ),
% APi: eliminate subcells, otherwise strfind crashes
if iscell(textstr{ii})==1,
textstr{ii}=char(textstr{ii});
end
%make sure that all strings appear on 1 row only, otherwise strfind
%fails.
if size( textstr{ ii }, 1 ) > 1,
textstr{ ii } = textstr{ ii }( : )'; %the text itself doesn't matter. Only the presence of $ is important.
end
end
mathsym = strfind( textstr, '$' ); %cell array. Empty if symbol not found.
for jj = 1 : length( mathsym ),
if ~isempty( mathsym{ jj } ),
set( allText( jj ), 'interpreter', 'latex' );
end
end
if ~isempty( opts.customcode ) & strncmpi( opts.customcode{1}, 'b', 1 )
%execute custom code if any
eval( opts.customcode{2} );
end
%Update figure
drawnow;
%
% Local Functions
%
function outData = LocalPushOldData(inData, objs, prop, values)
outData.objs = {objs, inData.objs{:}};
outData.prop = {prop, inData.prop{:}};
outData.values = {values, inData.values{:}};
function cellArray = LocalGetAsCell(fig,prop,allowemptycell);
cellArray = get(fig,prop);
if nargin < 3
allowemptycell = 0;
end
if ~iscell(cellArray) & (allowemptycell | ~isempty(cellArray))
cellArray = {cellArray};
end
function newArray = LocalScale(inArray, scale, minv, maxv)
n = length(inArray);
newArray = cell(n,1);
for k=1:n
newArray{k} = min(maxv,max(minv,scale*inArray{k}(1)));
end
function gray = LocalMapToGray1(color)
gray = color;
if ischar(color)
switch color(1)
case 'y'
color = [1 1 0];
case 'm'
color = [1 0 1];
case 'c'
color = [0 1 1];
case 'r'
color = [1 0 0];
case 'g'
color = [0 1 0];
case 'b'
color = [0 0 1];
case 'w'
color = [1 1 1];
case 'k'
color = [0 0 0];
end
end
if ~ischar(color)
gray = 0.30*color(1) + 0.59*color(2) + 0.11*color(3);
end
function newArray = LocalMapToGray(inArray);
n = length(inArray);
newArray = cell(n,1);
for k=1:n
color = inArray{k};
if ~isempty(color)
color = LocalMapToGray1(color);
end
if isempty(color) | ischar(color)
newArray{k} = color;
else
newArray{k} = [color color color];
end
end
function newArray = LocalMapColorToStyle(inArray);
inArray = LocalGetAsCell(inArray,'Color');
n = length(inArray);
newArray = cell(n,1);
styles = {'-','-.','--',':'};
uniques = [];
nstyles = length(styles);
for k=1:n
gray = LocalMapToGray1(inArray{k});
if isempty(gray) | ischar(gray) | gray < .05
newArray{k} = '-';
else
if ~isempty(uniques) & any(gray == uniques)
ind = find(gray==uniques);
else
uniques = [uniques gray];
ind = length(uniques);
end
newArray{k} = styles{mod(ind-1,nstyles)+1};
end
end
function [ newArray, haschanged ] = LocalMapCData(inArray);
n = length(inArray);
newArray = cell(n,1);
haschanged = false;
for k=1:n
color = inArray{k};
if (ndims(color) == 3) & isa(color,'double')
haschanged = true;
gray = 0.30*color(:,:,1) + 0.59*color(:,:,2) + 0.11*color(:,:,3);
color(:,:,1) = gray;
color(:,:,2) = gray;
color(:,:,3) = gray;
end
newArray{k} = color;
end
function outData = LocalUpdateColors(inArray, prop, inData)
value = LocalGetAsCell(inArray,prop);
outData.objs = {inData.objs{:}, inArray};
outData.prop = {inData.prop{:}, {prop}};
outData.values = {inData.values{:}, value};
if (~isempty(value))
if strcmp(prop,'CData')
[ value, haschanged ] = LocalMapCData(value);
%JR modif: add haschanged test to avoid
%problems when redefining colors of patches based
%on colormaps
if ~haschanged, return; end
else
value = LocalMapToGray(value);
end
set(inArray,{prop},value);
end
function Local_AdaptAxes_6x( targAxes, fontscale );
%targAxes: considered axes
%Idea: as axes can be multiple and positioned anywhere, the
%resizing is done on the axes in a way that keeps the overall
%outside boundary nearly constant.
%The fontscale factor is used to increase the distance from the
%axes to the labels. The increase of size of the label is balanced
%by shifting both the label and the axes. The shift and the space
%increase are balanced by reducing the size of the axes.
%The power of 10 (a real pain in the ...) is approximately
%accounted for.
%
%store axes units and reset
oldunits = get(targAxes,'Units');
set(targAxes,'Units','centimeters');
%loop on axes (not legend)
for ii = 1:length(targAxes),
if ~isempty(get(targAxes(ii),'ZTickLabel')),
%3D plot
%don't do anything
continue
end
%store axes position
oldpos = get(targAxes(ii),'Position');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%x-axis
xexp = false;
if ~isempty(get(targAxes(ii),'XTickLabel')),
%there are xticklabels
ticklab = true;
%find if an exponent is used
tmp = get(targAxes(ii),'XTickLabel');
if iscell(tmp), tmp = cell2mat(tmp); end
ticklabval = str2num(tmp);
if ~isempty(ticklabval),
%values are numerical
%compare them to tick values, use mean because
%perfect agreement is not required
tv = get(targAxes(ii),'XTick');
mtv = mean(abs(tv));
mtl = mean(abs(ticklabval));
if mtv/mtl > 5 | mtl/mtv > 5,
%the value is not contained in the interval limit
%an exponent is displayed at the end of the axis
%vertically, under the last ticklabel. Always
%displayed at the bottom of the axes. (even if
%labels are at the top!).
xexp = true;
end
end
else
ticklab = false;
end
axloc = get(targAxes(ii),'XAxisLocation');
axlabel = get(targAxes(ii),'XLabel');
%change label units and alignement (initiated even if empty!)
set(axlabel,'Units','centimeters','HorizontalAlignment','center');
if strcmp(axloc,'bottom'),
set(axlabel,'VerticalAlignment','top');
else
set(axlabel,'VerticalAlignment','bottom');
end
oldposlabel = get(axlabel,'Position');
if ~isempty(get(axlabel,'String')),
%there is a label string (NB: there is always a label, even
%if the string is empty)
labelstr = true;
else
labelstr = false;
end
%init variables
newpos = oldpos;
newposlabelx = oldposlabel;
labextent = get(axlabel,'Extent');
applychanges = false;
xaxlabel = axlabel;
if ticklab | labelstr,
if labelstr,
labelshift = labextent(4)*(1-1/fontscale); %size increase wrt position
if strcmp(axloc,'bottom'),
%more room done at the bottom, so no need to do more for
%the exponent
xexp = false;
end
else
labelshift = 0;
end
%define new positions
if strcmp(axloc,'bottom'),
disttolab = abs(newposlabelx(2)) * fontscale; %new distance to label from axes
%position of label center wrt absolute position
newposlabelx(2) = -disttolab;
%position of axes
%add the diff betw the dist to lab and the label
%shift
newpos(2) = newpos(2) + disttolab - abs(oldposlabel(2)) + labelshift;
newpos(4) = newpos(4) - (newpos(2) - oldpos(2)); %resize to keep roof at same position
else %top
disttolab = (newposlabelx(2) - oldpos(4)) * fontscale; %new distance to label from axes
newpos(4) = oldpos(4) - (disttolab - (oldposlabel(2)-oldpos(4)) + labelshift);
%position of label center
newposlabelx(2) = newpos(4) + disttolab;
%more room done at the top, so no need to do more for
%the exponent
yexp = false;
end
%apply changes
applychanges = true;
end %xlabel adaptation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%y-axis
yexp = false;
if ~isempty(get(targAxes(ii),'YTickLabel')),
%there are yticklabels
ticklab = true;
%find if an exponent is used
tmp = get(targAxes(ii),'YTickLabel');
if iscell(tmp), tmp = cell2mat(tmp); end
ticklabval = str2num(tmp);
if ~isempty(ticklabval),
%values are numerical
%compare them to tick values, use mean because
%perfect agreement is not required
tv = get(targAxes(ii),'YTick');
mtv = mean(abs(tv));
mtl = mean(abs(ticklabval));
if mtv/mtl > 5 | mtl/mtv > 5,
%the value is not contained in the interval limit
%an exponent is displayed at the end of the axis
yexp = true;
end
end
else
ticklab = false;
end
axloc = get(targAxes(ii),'YAxisLocation');
axlabel = get(targAxes(ii),'YLabel');
%change label units and alignement (initiated even if empty!)
set(axlabel,'Units','centimeters','HorizontalAlignment','center');
if strcmp(axloc,'left'),
set(axlabel,'VerticalAlignment','bottom');
else
set(axlabel,'VerticalAlignment','top');
end
oldposlabel = get(axlabel,'Position');
if ~isempty(get(axlabel,'String')),
%there is a label string (NB: there is always a label, even
%if the string is empty)
labelstr = true;
else
labelstr = false;
end
%init variables
newposlabely = oldposlabel;
labextent = get(axlabel,'Extent');
yaxlabel = axlabel;
if ticklab | labelstr,
if labelstr,
labelshift = labextent(3)*(1-1/fontscale); %size increase wrt position
else
labelshift = 0;
end
%define new positions
if strcmp(axloc,'left'),
disttolab = abs(newposlabely(1)) * fontscale; %new distance to label from axes
%position of label center wrt absolute position
newposlabely(1) = -disttolab;
%position of axes
%add the diff betw the dist to lab and the label
%shift
newpos(1) = newpos(1) + disttolab - abs(oldposlabel(1)) + labelshift;
newpos(3) = newpos(3) - (newpos(1) - oldpos(1)); %resize to keep roof at same position
else %right
disttolab = (newposlabely(1) - oldpos(3)) * fontscale; %new distance to label from axes
newpos(3) = oldpos(3) - (disttolab - (oldposlabel(1)-oldpos(3)) + labelshift);
%position of label center wrt absolute position
newposlabely(1) = newpos(3) + disttolab;
end
%apply changes
applychanges = true;
end %ylabel adaptation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%apply changes
%changes due to exponents
if xexp | yexp,
%estimate width of the exponent part
newfts = get(targAxes(ii),'FontSize');
oldfts = newfts / fontscale;
additionallength = 1.5*(newfts - oldfts) * 1/72 * 2.54; %from points to cm
end
if xexp,
%here: there is an exp on the x axis and there is no
%xlabel at the bottom
%decrease height and shift upwards
newpos(2) = newpos(2) + additionallength;
newpos(4) = newpos(4) - additionallength;
newposlabelx(2) = newposlabelx(2) - additionallength;
end
if yexp,
%here: there is an exp on the y axis and the x-axis is at
%the bottom, or it is at the top without labels
%decrease height
newpos(4) = newpos(4) - additionallength;
end
if applychanges,
set(targAxes(ii),'Position',newpos);
%center labels
newposlabelx(1) = newpos(3)/2;
newposlabely(2) = newpos(4)/2;
%apply label position
set(xaxlabel,'Position',newposlabelx);
set(yaxlabel,'Position',newposlabely);
end
end %loop for adaptation
%restore axes units
if ~iscell(oldunits), oldunits = {oldunits}; end
set(targAxes,{'Units'},oldunits);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Local_AdaptAxes_7x( H, allAxesNoLeg, allLeg, marginfactor, tight, blnLockAxes, blnInnerCall );
%Idea: to complete Matlab standard (usually ok for Outerposition mode but not for the Position mode)
%treatment, make sure that all the axes Outerpositions are given within
%the figure (if a label is cropped, the outerposition will go outside
%the figure). So:
%Loop on all axes and store the required additionnal length on all
%sides of the figure. Add it with axes in absolute units, change units
%to normalized and come back to original size.
%The idea is to increase the relative size of the margins through this
%process.
%
%New mode: marginfactor as a vector of absolute corrections in cm
%to allow conservation of size between figures
%
%New (05.11.10): use position+tightinset instead of outerposition.
%According to Matlab's help, in outerposition mode, it is not
%outerposition that is used, but position+tightinset (... confusing!)
%It is sufficient to keep position+tightinset inside. It doesn't
%matter if outerposition is cropped. This choice is better,
%since it results in less margin and there is a more direct observation
%of figure corruption (in some cases, outerposition might already be
%bad on the original figure without any particular symptoms since
%tightinset+position is ok...). This results in an additional
%difficulty: just increasing the margin to fit the tightinset is not
%enough, the figure reduction factor must be taken into account
%otherwise the labels will always be cropped. This last point is
%relevant only for 'position' mode.
%
%Also new: tight option
%
%New: now ok for legends outside axes. Necessary since these legends
% are not included in tightinset...
%
%15.12.10: lock axes graduation beforehand to avoid clipping of re-appearing ticklabels at tips of axis
%init
if nargin < 7, blnInnerCall = false; end
allAxesNoLegNoOL = findall(allAxesNoLeg, 'flat', '-not', 'tag', 'scribeOverlay' ); %remove hidden axes that prevents correct correction determination
allLegNoLoc = findall( allLeg, 'flat', 'location', 'none' ); %legends with positionning not influenced by axes positionning.
legloc = strtrim( cellstr( fliplr( char( LocalGetAsCell( allLeg, 'location' ) ) ) ) ); %legend locations, flipped l-r
allLegOut = allLeg( strmatch( fliplr( 'Outside' ), legloc ) ); %legends with defined location and outside the plot.
allLegTarget = [ allLegNoLoc; allLegOut ]; %legends with defined location inside plots are not important for axes adaptation and therefore avoided.
%lock axes limits, ticks and labels to avoid modification of
%axis tips during resizing (otherwise, a new tick might appear at the end
%of an axis during the last resizing and therefore be cropped).
if blnLockAxes
list1 = { 'X', 'Y', 'Z' };
%list2 = { 'TickMode', 'TickLabelMode', 'LimMode' };
list2 = { 'TickMode', 'LimMode' }; %NB: setting TickLabelMode to manual for log axis results in label corruption (whether this is a bug or not is arguable). Not necessary here, just don't do it.
locklist = cellstr( [ reshape( repmat( char( list1 )', length( list2 ), 1 ), [], 1 ), repmat( char( list2 ), length( list1 ), 1 ) ] )'; %XTick..., Y..., etc
%oldlock = get( allAxesNoLegNoOL, locklist ); % nhandles x nProps cell array. NB: not used since restoring a posteriori results in re-calculation of ticks by Matlab and therefore recreates the problem.
set( allAxesNoLegNoOL, locklist, repmat( { 'manual' }, length( allAxesNoLegNoOL ), length( locklist ) ) );
%Avoid a bug in Matlab: with log axis, setting 'tickmode' to 'manual' corrupts the labels.
%This can be avoided by manually resetting the ticks with their original values:
set( allAxesNoLegNoOL, { 'XTick'; 'YTick'; 'ZTick' }, get( allAxesNoLegNoOL, { 'XTick'; 'YTick'; 'ZTick' } ) );
end
%store
oldaxesunits = LocalGetAsCell( allAxesNoLeg, 'Units' );
oldlegunits = LocalGetAsCell( allLegTarget, 'Units' );
oldfigunits = get( H, 'units' );
oldactpos = LocalGetAsCell( allAxesNoLeg, 'ActivePositionProperty' ); %keep for restore afterwards. Necessary because automatically changed when resetting axes position for unknown reasons.
%Set same absolute units for figure and axes (avoid axes redim when figure is resized)
set( H, 'Units', 'centimeters' );
set( allAxesNoLeg, 'Units', 'centimeters' );
set( allLegTarget, 'Units', 'centimeters' );
%store original figure position (in centimeters)
fpos = get( H, 'Position' );
%Match axes position with plotbox to avoid problem of margin determination for special plots
%Hopefully that won't create new problems. It might in case of multiple subplots
%since outerposition is modified in the process, but can't be restored
%without cancelling the axes position modification...!
%Not done for 3D plots (might be done, but need to understand how that works...)
for ii = 1 : length( allAxesNoLegNoOL )
h = allAxesNoLegNoOL( ii );
if ~isempty( get( h, 'ZTickLabel' ) ),
%3D plot
%don't do anything
continue
end
pbar = get( h, 'PlotBoxAspectRatio' ); %NB: this aspect ratio is given for absolute coordinates. OK here since h is in centimeters.
if strcmpi( get( h, 'PlotBoxAspectRatioMode' ), 'manual' ) | any( pbar( 1 : 2 ) ~= 1 )
%done only if manual or not [1,1,?]. Otherwise, the value stored in pbar don't match
%the actual plot box aspect (due to stretch-to-fill mode).
axpos = get( h, 'position' );
%axopos = get( h, 'outerposition' ); %store it. Restored after the matching to avoid possible problems with subplots. NOTE: cf below.
axarf = axpos(4) / axpos(3); %axes aspect ratio factor. Height over width.
pbarf = pbar(2) / pbar(1); %plot box aspect ratio factor.
if pbarf == axarf
%already matching
continue
end
if pbarf > axarf
%axes is wider than plot box but same height (only possibility since plot box must be contained in axes and one dimension is equal)
%Corr axes width
newwidth = axpos(4) ./ pbarf;
axpos(1) = axpos(1) + ( axpos(3) - newwidth ) ./ 2; %correct the axes positioning
axpos(3) = newwidth; %correct the axes extension
elseif pbarf < axarf
%axes is higher than plot box but same width (only possibility since plot box must be contained in axes and one dimension is equal)
%Corr axes height
newheight = axpos(3) .* pbarf;
axpos(2) = axpos(2) + ( axpos(4) - newheight ) ./ 2; %correct the axes positioning
axpos(4) = newheight; %correct the axes extension
end
set( h, 'position', axpos ); %set the new position. activepositionproperty is restored in a single call below.
%set( h, 'outerposition', axopos ); %restore outerposition. No reason to modify it here. NOTE: this resets the axes position for unknown reasons. => don't do it.
end
end
%restore active position to counteract unwanted modifications resulting from plotbox matching above
set( allAxesNoLeg, { 'ActivePositionProperty' }, oldactpos );
if ~isempty( tight )
%Tight bounding
%addpos will contain the length to add (or usually remove) on each side of the figure
%addpos(1,2) is positive to remove margin and negative to add it
%(this has to be understand as something to add to the figure
%boundary in absolute coordinates on the screen, while maintaining
%the axes at the same position)
addpos = [ inf, inf, -inf, -inf ];
else
%addpos will contain the length to add on each side of the figure
%addpos(1,2) is negative (if not zero)
addpos = zeros( 1, 4 );
end
if length( marginfactor ) > 1
%use directly marginfactor
addpos = marginfactor;
if ~isempty( tight ) & ~strcmpi( tight, 'xy' )
disp( 'Warning: ''tight'' is not empty but absolute marginfactor given.' )
disp( ' This will result in scaling conservation (at least for ''position'' mode) but not tight margins.' );
end
else
%standard mode: determines automatically the required correction
for ii = 1 : length( allAxesNoLegNoOL ),
%tightinset frame:
apos = get( allAxesNoLegNoOL( ii ), 'Position' ); %axes position
timargin = get( allAxesNoLegNoOL( ii ), 'TightInset' ); %margins for tight inset frame
tipos = apos + [ -timargin( 1 : 2 ), timargin( 1 : 2 ) + timargin( 3 : 4 ) ]; %remove margin for position and addup margins for extension
addpos = [ min( addpos(1), tipos(1) ), ...
min( addpos(2), tipos(2) ), ...
max( addpos(3), sum( tipos( [ 1, 3 ] ) ) - fpos(3) ), ...
max( addpos(4), sum( tipos( [ 2, 4 ] ) ) - fpos(4) ) ];
end
for ii = 1 : length( allLegTarget ),
%for legends, 'position' must be used, but a 0.1 additionnal margin is
%added to avoid problems related to constant extent of legends during
%resizing.
%This loop is necessary for legends that are outside axes.
apos = get( allLegTarget( ii ), 'Position' ); %legend position
apos = apos + 0.1 * [ -1, -1, 2, 2 ]; %additionnal margin.
addpos = [ min( addpos(1), apos(1) ), ...
min( addpos(2), apos(2) ), ...
max( addpos(3), sum( apos( [ 1, 3 ] ) ) - fpos(3) ), ...
max( addpos(4), sum( apos( [ 2, 4 ] ) ) - fpos(4) ) ];
end
if all( strcmpi( oldactpos, 'position' ) ) & any( addpos ~= 0 )
%Let the user know the obtained values. Useful as guidelines
%if small adjustements are necessary.
disp( [ 'Automatic margin correction results in: ', num2str( addpos, '%.3g ' ) ] );
end
end
if any( addpos ~= 0 )
%Perform modifications if necessary
if all( strcmpi( oldactpos, 'position' ) )
%For 'position' mode:
%
% - multiply addpos by a correction factor. This correction factor
% is calculated so that addpos really corresponds to
% the added length on the final figure. In other words, this
% scaling factor compensates the different figure scalings
% occuring below to modify the margins.
%
%This is not done for 'outerposition' mode since the
%scaling of the margins is impredictable in that case.
%Get distances between outest axes and figure boundaries
outestapos = [ inf, inf, 0, 0 ]; %init
outestaposSL = outestapos; %same, but with Special treatment for outsided Legends
obj = [ allAxesNoLegNoOL; allLegOut; allLegNoLoc ]; %combine axes and legends
for ii = 1 : length( obj ),
apos = get( obj( ii ), 'Position' ); %axes position
oldoutestapos = outestapos; %store previous values
%update
outestapos = [ min( outestapos(1), apos(1) ), ...
min( outestapos(2), apos(2) ), ...
max( outestapos(3), apos(1) + apos(3) ), ... %coordinate of rhs (NOT width)
max( outestapos(4), apos(2) + apos(4) ) ]; %coordinate of top (NOT height)
indmodif = find( oldoutestapos ~= outestapos ); %indices of updated values
if ~isempty( indmodif )
%outestapos has been updated
if ii > length( allAxesNoLegNoOL ) & ii <= length( [ allAxesNoLegNoOL; allLegOut ] )
%the update is related to a legend (i.e. an outsided legend) with defined location
%In 'position' mode and for legends with defined location, the plot related to the legend will not
%accommodate the space required for the legend when resizing the figure.
%To account for the fixed dimensions of legends, it is sufficient to
%include the legend extent in the desired final margin, so that
%corrfact (see below) is adjusted to maintain a constant space for the legend.
%Add or remove legend width and/or height to the current margin value
%Cases left: add legend extent and the constant distance to axes of 0.38 cm
outestaposSL( indmodif( indmodif == 1 ) ) = ...
apos( indmodif( indmodif == 1 ) ) + apos( indmodif( indmodif == 1 ) + 2 ) + 0.38;
%Cases right: remove legend extent and the constant distance to axes of 0.38 cm
outestaposSL( indmodif( indmodif == 3 ) ) = ...
apos( indmodif( indmodif == 3 ) - 2 ) - 0.38;
%Cases bottom: add legend extent and the constant distance to axes of 0.33 cm
outestaposSL( indmodif( indmodif == 2 ) ) = ...
apos( indmodif( indmodif == 2 ) ) + apos( indmodif( indmodif == 2 ) + 2 ) + 0.33;
%Cases top: remove legend extent and the constant distance to axes of 0.33 cm
outestaposSL( indmodif( indmodif == 4 ) ) = ...
apos( indmodif( indmodif == 4 ) - 2 ) - 0.33;
%NB: this 0.38 (or 0.33) value is not always very accurate. That might be a problem. If
%underestimated and gamma < 1, legend might be clipped. Same if overestimated
%and gamma > 1. The solution would be to calculate it each time, but with
%all the possible legend location, it's a nightmare. To solve this issue, addpos
%includes a 0.1 cm additional margin for legends.
elseif ii > length( [ allAxesNoLegNoOL; allLegOut ] )
%the update is related to a legend (i.e. an outsided legend) with UNdefined location
%In 'position' mode for this kind of legends, the figure resizing conserves the relative
%position of the center of the legend.
%The same technique as for legends with defined locations applies, but with respect
%to the legend center
%Cases left and bottom:
outestaposSL( indmodif( indmodif < 3 ) ) = ...
apos( indmodif( indmodif < 3 ) ) + apos( indmodif( indmodif < 3 ) + 2 ) ./ 2;
%Cases right and top:
outestaposSL( indmodif( indmodif >= 3 ) ) = ...
apos( indmodif( indmodif >= 3 ) - 2 ) + apos( indmodif( indmodif >= 3 ) ) ./ 2;
else
%Standard axes
outestaposSL( indmodif ) = outestapos( indmodif );
end
end
end
%Use the margin determination including special treatment for outsided legends
outestapos = outestaposSL;
%convert to distances
outestapos( 3 : 4 ) = fpos( 3 : 4 ) - outestapos( 3 : 4 ); %outest now containes distances from outest axes to fig bound in each directions
%Correction factors and terms
%Note: depending on number of zeros in addpos, the correction factors
%and terms are calculated differently. There are 8 cases. For simplicity,
%all the cases are calculated and the correct case is selected afterwards.
%init
figdims = fpos( [ 3, 4, 3, 4 ] ); %Order: relevant lengths for left, bottom, right, top
addposP = addpos .* [ -1, -1, 1, 1 ]; %change signs. Easier to handle here. Understood as distance added in each direction.
corrfact = nan( 1, 4 );
corrterm = nan( 1, 4 );
%Standard case: both figure dimensions are conserved
corrfact11 = ... %Case all addpos different from 0. See justification below
( figdims ...
- outestapos( [ 3, 4, 1, 2 ] ) ...
+ outestapos .* addposP( [ 3, 4, 1, 2 ] ) ./ addposP ) ...
./ ...
( figdims - outestapos( [ 3, 4, 1, 2 ] ) - outestapos ...
- addposP( [ 3, 4, 1, 2 ] ) - addposP );
corrterm11 = zeros( 1, 4 );
%this formulation is obtained analytically with the equations:
% figdims(1:2) = gamma(1:2) .* ( figdims(1:2) + addposP( [ 3, 4 ] ) .*
% corrfact11( [ 3, 4 ] ) + addposP(1:2) .*
% corrfact11(1:2) )
% outestapos + addposP = gamma .* ( outestapos + corrfact11 .*
% addposP )
% gamma is the reduction factor of the extended figure size to
% the restored (initial) figure size.
%disp('check equations')
%gamma = ( outestapos + addposP ) ...
% ./ ( outestapos + addposP .* corrfact11 );
%disp( [ figdims', ( gamma .* ( figdims + addposP( [ 3, 4, 1, 2 ] ) .* ...
% corrfact11( [ 3, 4, 1, 2 ] ) + addposP .* ...
% corrfact11 ) )' ] );
%disp( [ ( outestapos + addposP )', ( gamma .* ( outestapos + corrfact11 .* ...
% addposP ) )' ] );
corrfact00 = ones( 1, 4 ); %Case all addpos = 0.
corrterm00 = zeros( 1, 4 );
corrfact01 = [ 1, 1, ... %Case first addpos of each pair is zero, second is not. See justification below.
( figdims( 3 : 4 ) - outestapos( 1 : 2 ) ) ...
./ ( figdims( 3 : 4 ) - outestapos( 1 : 2 ) - outestapos( 3 : 4 ) - addposP( 3 : 4 ) ) ];
corrterm01 = ...
[ addposP( 3 : 4 ) .* outestapos( 1 : 2 ) ...
./ ( figdims( 3 : 4 ) - outestapos( 1 : 2 ) - outestapos( 3 : 4 ) - addposP( 3 : 4 ) ), 0, 0 ];
corrfact10 = [ ... %Case second addpos of each pair is zero, first is not. See justification below.
( figdims( 1 : 2 ) - outestapos( 3 : 4 ) ) ...
./ ( figdims( 1 : 2 ) - outestapos( 1 : 2 ) - outestapos( 3 : 4 ) - addposP( 1 : 2 ) ), 1, 1 ];
corrterm10 = [ 0, 0, ...
addposP( 1 : 2 ) .* outestapos( 3 : 4 ) ...
./ ( figdims( 1 : 2 ) - outestapos( 1 : 2 ) - outestapos( 3 : 4 ) - addposP( 1 : 2 ) ) ];
%this formulation is obtained analytically with the equations:
% figdims(1:2) = gamma(1:2) .* ( figdims(1:2) + addposP10(1:2) .* corrfact10(1:2) + corrterm10((3:4)) )
% outestapos + addposP10 = gamma .* ( outestapos + corrfact10 .* addposP10 + corrterm10 )
% gamma is the reduction factor of the extended figure size to
% the restored (initial) figure size.
%Identify case
blnaddpos1 = ( addpos ~= 0 ); %true if addpos is not zero
for ii = 1 : 2
%ii = 1: left and right
%ii = 2: bottom and top
if all( blnaddpos1( [ ii, ii + 2 ] ) )
%both non zero
corrfact( [ ii, ii + 2 ] ) = corrfact11( [ ii, ii + 2 ] );
corrterm( [ ii, ii + 2 ] ) = corrterm11( [ ii, ii + 2 ] );
elseif blnaddpos1( ii )
%first one non zero and second one zero (by deduction)
corrfact( [ ii, ii + 2 ] ) = corrfact10( [ ii, ii + 2 ] );
corrterm( [ ii, ii + 2 ] ) = corrterm10( [ ii, ii + 2 ] );
elseif blnaddpos1( ii + 2 )
%first one zero and second one non zero (by deduction)
corrfact( [ ii, ii + 2 ] ) = corrfact01( [ ii, ii + 2 ] );
corrterm( [ ii, ii + 2 ] ) = corrterm01( [ ii, ii + 2 ] );
else
%both zero
corrfact( [ ii, ii + 2 ] ) = corrfact00( [ ii, ii + 2 ] );
corrterm( [ ii, ii + 2 ] ) = corrterm00( [ ii, ii + 2 ] );
end
end
%If tight mode with conserved scaling, only
%one of the figure dimension is conserved.
%In that case, the correction factors of the modified
%dimension follow the 2nd equations above and the first
%equations are replaced by the condition of constant scale
%factor gamma between dimension x and y.
if ~isempty( tight ) & ~strcmpi( tight, 'xy' )
if strcmpi( tight, 'x' )
%Keep figure dimension along x
%Modif corrfact along y to satisfy constant scaling in both dimensions
%gamma given by scaling along x dimension
gamma = figdims(1) ./ ( figdims(1) ...
+ addposP(3) .* corrfact(3) ...
+ addposP(1) .* corrfact(1) + corrterm(1) + corrterm(3) );
%store indices of values to be modified
indaddpos1 = find( addpos ~= 0 & [ false, true, false, true ] ); %ind of non zero addpos amongst [ 2, 4 ]
indaddpos0 = find( addpos == 0 & [ false, true, false, true ] ); %ind of zero addpos amongst [ 2, 4 ]
else
%Keep figure dimension along y
%Modif corrfact along x to satisfy constant scaling in both dimensions
%gamma given by scaling along y dimension
gamma = figdims(2) ./ ( figdims(2) ...
+ addposP(4) .* corrfact(4) ...
+ addposP(2) .* corrfact(2) + corrterm(2) + corrterm(4) );
%store indices of values to be modified
indaddpos1 = find( addpos ~= 0 & [ true, false, true, false ] ); %ind of non zero addpos amongst [ 1, 3 ]
indaddpos0 = find( addpos == 0 & [ true, false, true, false ] ); %ind of zero addpos amongst [ 1, 3 ]
end
%modif corrfact and corrterm accordingly
corrfact( indaddpos1 ) = ( ( 1 - gamma ) * outestapos( indaddpos1 ) + addposP( indaddpos1 ) ) ...
./ ( gamma * addposP( indaddpos1 ) );
corrfact( indaddpos0 ) = 1;
corrterm( indaddpos1 ) = 0;
corrterm( indaddpos0 ) = ( 1 - gamma ) * outestapos( indaddpos0 ) ./ gamma;
%disp( 'Check equations' )
%disp( gamma )
%disp( ( ( outestapos + addposP ) ...
% ./ ( outestapos + addposP .* corrfact ) )' ); %all gammas must be the same
%disp( [ ( outestapos + addposP )', ( gamma .* ( outestapos + corrfact .* ...
% addposP ) )' ] );
end
%express corrterm in same sense as addpos (instead of same sense as addposP)
corrterm = corrterm .* [ -1, -1, 1, 1 ];
%corr addpos
addpos = corrfact .* addpos + corrterm;
end
if length( marginfactor ) == 1
addpos = addpos .* marginfactor; %increase the additional margins.
end
%shift all axes by exceeding distance on left and bottom
%Uniform behaviour when re-setting axes position:
%Use 'position' mode to make sure that axes behave as expected.
%Anyway, for unknown reasons, using set( axes,'position', ... ) sets
%the activeposition to 'position'. Just make it explicit here.
shiftedobj = [ allAxesNoLeg; allLegNoLoc ]; %combine all axes with the legends that have no relative positionning. The other legends are positionned automatically by Matlab.
set( shiftedobj, { 'ActivePositionProperty' }, { 'Position' } );
for ii = 1 : length( shiftedobj ),
apos = get( shiftedobj( ii ), 'position' );
apos( 1 : 2 ) = apos( 1 : 2 ) - addpos( 1 : 2 ); %NB: addpos(1:2) is negative
set( shiftedobj( ii ), 'position', apos );
end
%correct figure position
%just work on width and height for the figure frame
newpos = [ fpos( 1 : 2 ), ...
fpos(3) - addpos(1) + addpos(3), ...
fpos(4) - addpos(2) + addpos(4) ]; %NB: addpos(1:2) is negative
set( H, 'Position', newpos ); %to have all the labels displayed
drawnow %need to be called after resizing figure window
%Adapt axes sizes while restoring figure position
set( [ allAxesNoLeg; allLegTarget ], 'Units', 'Normalized' );
%restore active position before resizing the figure. Legends should always be in 'position' mode since they have no labels.
set( allAxesNoLeg, { 'ActivePositionProperty' }, oldactpos );
if ~isempty( tight ) & strcmpi( tight, 'x' )
%Tight, keep figure dimension along x, keep scaling
scale = newpos(4) / newpos(3); %height over width of intermediate figure position
fpos(4) = scale * fpos(3); %modify height to conserve scale
elseif ~isempty( tight ) & strcmpi( tight, 'y' )
%Tight, keep figure dimension along y, keep scaling
scale = newpos(4) / newpos(3); %height over width of intermediate figure position
fpos(3) = fpos(4) / scale; %modify height to conserve scale
else
%Not tight or 'xy' mode
% -> keep fpos untouched
end
set( H, 'Position', fpos );
drawnow %need to be called after resizing figure window
%Check
newfpos = get( H, 'Position' );
if any( abs( fpos( 3 : 4 ) - newfpos( 3 : 4 ) ) > 0.1 ) %1mm tolerance
disp( [ '!!! The new figure is too large or too high to fit on the screen.', ...
' Arbitrary resizing will occur. Please reduce the target width or height.' ] );
end
end %test on addpos ~= 0
%restore units
set( allAxesNoLeg, { 'Units' }, oldaxesunits );
set( allLegTarget, { 'Units' }, oldlegunits );
set( H, 'Units', oldfigunits );
if all( strcmpi( oldactpos, 'outerposition' ) ) ...
& ~isempty( tight ) & length( marginfactor ) == 1 & ~blnInnerCall
%2nd run to reduce extra margins due to outerposition mode
%(necessary since no correction factor can be determined in that case)
marginfactor = 1; %marginfactor not re-applied
Local_AdaptAxes_7x( H, allAxesNoLeg, allLeg, marginfactor, tight, blnLockAxes, true );
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Local_MatchColorBars( targAxes );
%targAxes: cb and their related axes
%harmonize colorbars and axes
allColorbars = targAxes(find(strcmpi(get(targAxes,'Tag'),'Colorbar')));
%store axes units and reset
oldunits = get(targAxes,'Units');
set(targAxes,'Units','centimeters'); %to avoid automatic resizing
for ii = 1:length(allColorbars),
%find related plot
tmp = get(allColorbars(ii),'UserData');
relatedplot = tmp.PlotHandle;
if ~isempty(get(relatedplot,'ZTickLabel')),
%3D plot
%don't do anything
continue
end
cbpos = get(allColorbars(ii),'Position');
cbxlab = get(allColorbars(ii),'Xlabel');
cbxlabpos = get(cbxlab,'Position');
cbylab = get(allColorbars(ii),'Ylabel');
cbylabpos = get(cbylab,'Position');
plpos = get(relatedplot,'Position');
plxlab = get(relatedplot,'Xlabel');
plxlabpos = get(plxlab,'Position');
plylab = get(relatedplot,'Ylabel');
plylabpos = get(plylab,'Position');
if cbpos(3) > cbpos(4),
%horizontal bar, adjust left and width
pos = [max(cbpos(1),plpos(1)),min(cbpos(1)+cbpos(3),plpos(1)+plpos(3))]; %left,right
pos(2) = pos(2)-pos(1); %left,width
%set axes position
set(allColorbars(ii),'Position',[pos(1),cbpos(2),pos(2),cbpos(3)]);
set(relatedplot,'Position',[pos(1),plpos(2),pos(2),plpos(3)]);
%set labels (centered)
set(cbxlab,'Position',[pos(2)/2,cbxlabpos(2:3)]);
set(plxlab,'Position',[pos(2)/2,plxlabpos(2:3)]);
else
%vertical bar, adjust bottom and height
pos = [max(cbpos(2),plpos(2)),min(cbpos(2)+cbpos(4),plpos(2)+plpos(4))]; %bottom,top
pos(2) = pos(2)-pos(1); %bottom,height
%set axes position
set(allColorbars(ii),'Position',[cbpos(1),pos(1),cbpos(3),pos(2)]);
set(relatedplot,'Position',[plpos(1),pos(1),plpos(3),pos(2)]);
%set labels (centered)
set(cbylab,'Position',[cbylabpos(1),pos(2)/2,cbylabpos(3)]);
set(plylab,'Position',[plylabpos(1),pos(2)/2,plylabpos(3)]);
end
end %loop on colorbars
%restore axes units
if ~iscell(oldunits), oldunits = {oldunits}; end
set(targAxes,{'Units'},oldunits);
return