Mercurial > hg > octave-lojdl
view scripts/plot/subplot.m @ 17451:9304514b566c
subplot.m: Don't clear axes when switching to existing subplot (most of a fix for bug #39874).
* scripts/plot/subplot.m: Use outerposition for comparing whether axes has
moved and whether it should be deleted. Redo documentation string to add
explanations of Matlab compatible options which are available. Remove
cruft related to "outerpositiontight". Use double quotes rather than single
quotes where possible.
author | Rik <rik@octave.org> |
---|---|
date | Mon, 16 Sep 2013 15:45:45 -0700 |
parents | 1c89599167a6 |
children |
line wrap: on
line source
## Copyright (C) 1995-2012 John W. Eaton ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## <http://www.gnu.org/licenses/>. ## -*- texinfo -*- ## @deftypefn {Function File} {} subplot (@var{rows}, @var{cols}, @var{index}) ## @deftypefnx {Function File} {} subplot (@var{rcn}) ## @deftypefnx {Function File} {} subplot (@var{hax}) ## @deftypefnx {Function File} {} subplot (@dots{}, "align") ## @deftypefnx {Function File} {} subplot (@dots{}, "replace") ## @deftypefnx {Function File} {} subplot (@dots{}, "position", @var{pos}) ## @deftypefnx {Function File} {} subplot (@dots{}, @var{prop}, @var{val}, @dots{}) ## @deftypefnx {Function File} {@var{hax} =} subplot (@dots{}) ## Set up a plot grid with @var{rows} by @var{cols} subwindows and set the ## current axes for plotting (@code{gca}) to the location given by @var{index}. ## ## If only one numeric argument is supplied, then it must be a three digit ## value specifying the the number of rows in in digit 1, the number of ## columns in digit 2, and the plot index in digit 3. ## ## The plot index runs row-wise; First, all columns in a row are numbered ## and then the next row is filled. ## ## For example, a plot with 2x3 grid will have plot indices running as follows: ## @tex ## \vskip 10pt ## \hfil\vbox{\offinterlineskip\hrule ## \halign{\vrule#&&\qquad\hfil#\hfil\qquad\vrule\cr ## height13pt&1&2&3\cr height12pt&&&\cr\noalign{\hrule} ## height13pt&4&5&6\cr height12pt&&&\cr\noalign{\hrule}}} ## \hfil ## \vskip 10pt ## @end tex ## @ifnottex ## ## @example ## @group ## +-----+-----+-----+ ## | 1 | 2 | 3 | ## +-----+-----+-----+ ## | 4 | 5 | 6 | ## +-----+-----+-----+ ## @end group ## @end example ## ## @end ifnottex ## ## @var{index} may also be a vector. In this case, the new axis will enclose ## the grid locations specified. The first demo illustrates this: ## ## @example ## demo ("subplot", 1) ## @end example ## ## The index of the subplot to make active may also be specified by its axes ## handle, @var{hax}, returned from a previous @code{subplot} command. ## ## If the option @qcode{"align"} is given then the plot boxes of the subwindows ## will align, but this may leave no room for axis tick marks or labels. ## ## If the option @qcode{"replace"} is given then the subplot axis will be ## reset, rather than just switching the current axis for plotting to the ## requested subplot. ## ## The @qcode{"position"} property can be used to exactly position the subplot ## axes within the current figure. The option @var{pos} is a 4-element vector ## [x, y, width, height] that determines the location and size of the axes. ## The values in @var{pos} are normalized in the range [0,1]. ## ## Any property/value pairs are passed directly to the underlying axes object. ## ## If the output @var{hax} is requested, subplot returns the axis handle for ## the subplot. This is useful for modifying the properties of a subplot ## using @code{set}. ## @seealso{axes, plot, gca, set} ## @end deftypefn ## Author: Vinayak Dutt <Dutt.Vinayak@mayo.EDU> ## Adapted-By: jwe function h = subplot (varargin) align_axes = false; replace_axes = false; have_position = false; initial_args_decoded = false; if (nargin >= 3) ## R, C, N? arg1 = varargin{1}; arg2 = varargin{2}; arg3 = varargin{3}; if ( isnumeric (arg1) && isscalar (arg1) && isnumeric (arg2) && isscalar (arg2) && isnumeric (arg3)) rows = arg1; cols = arg2; index = arg3; varargin(1:3) = []; initial_args_decoded = true; endif endif if (! initial_args_decoded && nargin > 1) ## check for "position", pos, ... if (strcmpi (varargin{1}, "position")) arg = varargin{2}; if (isnumeric (arg) && numel (arg) == 4) pos = arg; varargin(1:2) = []; have_position = true; initial_args_decoded = true; else error ("subplot: POSITION must be a 4-element numeric array"); endif endif endif if (! initial_args_decoded && nargin > 0) arg = varargin{1}; if (nargin == 1 && isaxes (arg)) ## Axes handle axes (arg); cf = get (0, "currentfigure"); set (cf, "nextplot", "add"); return; elseif (isscalar (arg) && arg >= 0) ## RCN? index = rem (arg, 10); arg = (arg - index) / 10; cols = rem (arg, 10); arg = (arg - cols) / 10; rows = rem (arg, 10); varargin(1) = []; initial_args_decoded = true; else error ("subplot: expecting axes handle or RCN argument"); endif endif if (! initial_args_decoded) print_usage (); endif if (! have_position) cols = round (cols); rows = round (rows); index = round (index); if (any (index < 1) || any (index > rows*cols)) error ("subplot: INDEX value must be >= 1 and <= ROWS*COLS"); endif if (rows < 1 || cols < 1 || index < 1) error ("subplot: ROWS, COLS, and INDEX must be be positive"); endif endif ## Process "align" and "replace" options idx = strcmpi (varargin, "align"); if (any (idx)) align_axes = true; varargin(idx) = []; endif idx = strcmpi (varargin, "replace"); if (any (idx)) replace_axes = true; varargin(idx) = []; endif axesunits = get (0, "defaultaxesunits"); cf = gcf (); figureunits = get (cf, "units"); unwind_protect set (0, "defaultaxesunits", "normalized"); set (cf, "units", "pixels"); ## FIXME: At the moment we force gnuplot to use the aligned mode ## which will set "activepositionproperty" to "position". ## Τhis can yield to text overlap between labels and titles ## see bug #31610 if (strcmp (get (cf, "__graphics_toolkit__"), "gnuplot")) align_axes = true; endif if (! have_position) pos = subplot_position (rows, cols, index, "position"); outerpos = subplot_position (rows, cols, index, "outerposition"); box = [pos(1:2), pos(1:2)+pos(3:4)]; outerbox = [outerpos(1:2), outerpos(1:2)+outerpos(3:4)]; looseinset = [box(1:2)-outerbox(1:2), outerbox(3:4)-box(3:4)]; if (align_axes) activepositionproperty = "position"; else activepositionproperty = "outerposition"; endif endif set (cf, "nextplot", "add"); found = false; kids = get (cf, "children"); for child = kids(:)' ## Check whether this child is still valid; this might not be the ## case anymore due to the deletion of previous children (due to ## "deletefcn" callback or for legends/colorbars that are deleted ## with their corresponding axes). if (! ishandle (child)) continue; endif if (strcmp (get (child, "type"), "axes")) ## Skip legend and colorbar objects. if (any (strcmp (get (child, "tag"), {"legend", "colorbar"}))) continue; endif objpos = get (child, "outerposition"); if (all (abs (objpos - outerpos) < eps) && ! replace_axes) ## If the new axes are in exactly the same position ## as an existing axes object, use the existing axes. found = true; hsubplot = child; else ## If the new axes overlap an old axes object, delete the old axes. if (align_axes) objpos = get (child, "position"); endif x0 = pos(1); x1 = x0 + pos(3); y0 = pos(2); y1 = y0 + pos(4); objx0 = objpos(1); objx1 = objx0 + objpos(3); objy0 = objpos(2); objy1 = objy0 + objpos(4); if (! (x0 >= objx1 || x1 <= objx0 || y0 >= objy1 || y1 <= objy0)) delete (child); endif endif endif endfor if (found) ## Switch to existing subplot set (cf, "currentaxes", hsubplot); else hsubplot = axes ("box", "off", "position", pos, "looseinset", looseinset, "activepositionproperty", activepositionproperty, varargin{:}); addproperty ("subplot_align", hsubplot, "boolean", true); addlistener (hsubplot, "position", @subplot_align); if (! align_axes) set (hsubplot, "subplot_align", false) subplot_align (hsubplot) endif endif unwind_protect_cleanup set (0, "defaultaxesunits", axesunits); set (cf, "units", figureunits); end_unwind_protect if (nargout > 0) h = hsubplot; endif endfunction function pos = subplot_position (rows, cols, index, position_property) if (rows == 1 && cols == 1) ## Trivial result for subplot (1,1,1) if (strcmpi (position_property, "position")) pos = get (0, "defaultaxesposition"); else pos = get (0, "defaultaxesouterposition"); endif return; endif if (strcmp (position_property, "outerposition")) margins.left = 0.05; margins.bottom = 0.05; margins.right = 0.05; margins.top = 0.05; margins.column = 0.04 / cols; margins.row = 0.04 / rows; width = 1 - margins.left - margins.right - (cols-1)*margins.column; width = width / cols; height = 1 - margins.top - margins.bottom - (rows-1)*margins.row; height = height / rows; else defaultaxesposition = get (0, "defaultaxesposition"); ## The outer margins surrounding all subplot "positions" are independent ## of the number of rows and/or columns margins.left = defaultaxesposition(1); margins.bottom = defaultaxesposition(2); margins.right = 1.0 - margins.left - defaultaxesposition(3); margins.top = 1.0 - margins.bottom - defaultaxesposition(4); ## Fit from Matlab experiments pc = 1 ./ [0.1860, (margins.left + margins.right - 1)]; margins.column = 1 ./ polyval (pc , cols); pr = 1 ./ [0.2282, (margins.top + margins.bottom - 1)]; margins.row = 1 ./ polyval (pr , rows); ## Calculate the width/height of the subplot axes "position". ## This is also consistent with Matlab width = 1 - margins.left - margins.right - (cols-1)*margins.column; width = width / cols; height = 1 - margins.top - margins.bottom - (rows-1)*margins.row; height = height / rows; endif ## Index offsets from the lower left subplot yi = fix ((index(:)-1)/cols); xi = index(:) - yi*cols - 1; yi = (rows - 1) - yi; ## Lower left corner of the subplot, i.e., position(1:2) x0 = xi .* (width + margins.column) + margins.left; y0 = yi .* (height + margins.row) + margins.bottom; if (numel (x0) > 1) ## subplot (row, col, m:n) x1 = max (x0(:)) + width; y1 = max (y0(:)) + height; x0 = min (x0(:)); y0 = min (y0(:)); pos = [x0, y0, x1-x0, y1-y0]; else ## subplot (row, col, num) pos = [x0, y0, width, height]; endif endfunction function subplot_align (h, varargin) persistent updating = false; if (! updating) unwind_protect updating = true; hfig = ancestor (h, "figure"); hsubplots = findall (hfig, "type", "axes", "subplot_align", "off"); if (! isempty (hsubplots)) tightinset = get (hsubplots, "tightinset"); if (iscell (tightinset)) tightinset = max (cell2mat (tightinset)); endif looseinset = get (hsubplots, "looseinset"); if (iscell (looseinset)) looseinset = max (cell2mat (looseinset)); endif looseinset = max (tightinset, looseinset); set (hsubplots, "looseinset", looseinset); endif unwind_protect_cleanup updating = false; end_unwind_protect endif endfunction %!demo %! clf; %! r = 3; %! c = 3; %! fmt = {'horizontalalignment', 'center', 'verticalalignment', 'middle'}; %! for n = 1 : r*c %! subplot (r, c, n); %! xlabel (sprintf ('xlabel #%d', n)); %! ylabel (sprintf ('ylabel #%d', n)); %! title (sprintf ('title #%d', n)); %! text (0.5, 0.5, sprintf ('subplot(%d,%d,%d)', r, c, n), fmt{:}); %! axis ([0 1 0 1]); %! end %! subplot (r, c, 1:3); %! xlabel (sprintf ('xlabel #%d:%d', 1, 3)); %! ylabel (sprintf ('ylabel #%d:%d', 1, 3)); %! title (sprintf ('title #%d:%d', 1, 3)); %! text (0.5, 0.5, sprintf ('subplot(%d,%d,%d:%d)', r, c, 1, 3), fmt{:}); %! axis ([0 1 0 1]); %!demo %! clf; %! x = 0:1; %! for n = 1:4 %! subplot (2,2,n, 'align'); %! plot (x, x); %! xlabel (sprintf ('xlabel (2,2,%d)', n)); %! ylabel (sprintf ('ylabel (2,2,%d)', n)); %! title (sprintf ('title (2,2,%d)', n)); %! end %! subplot (1,2,1, 'align'); %! plot (x, x); %! xlabel ('xlabel (1,2,1)'); %! ylabel ('ylabel (1,2,1)'); %! title ('title (1,2,1)'); %!demo %! clf; %! x = 0:10; %! ax(1) = subplot (221); %! set (ax(1), 'tag', '1'); %! plot (x, rand (3, 11)) %! title ('x & y labels & ticklabels'); %! xlabel xlabel %! ylabel ylabel %! ax(2) = subplot (222); %! set (ax(2), 'tag', '2'); %! plot (x, rand (3, 11)) %! title ('no labels'); %! axis ('nolabel','tic') %! ax(3) = subplot (223); %! set (ax(3), 'tag', '3'); %! plot (x, rand (3, 11)) %! title ('no labels'); %! axis ('nolabel','tic') %! ax(4) = subplot (224); %! set (ax(4), 'tag', '4'); %! plot (x, rand (3, 11)) %! title ('x & y labels & ticklabels'); %! xlabel xlabel %! ylabel ylabel %!demo %! x = 0:10; %! subplot (221); %! plot (x, rand (3, 11)) %! ylim ([0, 1]); %! text (0.5, 0.5, '{x,y}labels & {x,y}ticklabels', ... %! 'horizontalalignment', 'center', ... %! 'units', 'normalized'); %! xlabel xlabel %! ylabel ylabel %! title title %! subplot (222); %! plot (x, rand (3, 11)) %! axis ('labely'); %! ylabel ylabel %! text (0.5, 0.5, 'no xlabels, xticklabels', ... %! 'horizontalalignment', 'center', ... %! 'units', 'normalized'); %! subplot (223); %! plot (x, rand (3, 11)) %! axis ('labelx'); %! text (0.5, 0.5, 'no ylabels, yticklabels', ... %! 'horizontalalignment', 'center', ... %! 'units', 'normalized'); %! xlabel xlabel %! title title %! subplot (224); %! plot (x, rand (3, 11)) %! axis ('nolabel','tic'); %! text (0.5, 0.5, 'no {x,y}labels, {x,y}ticklabels', ... %! 'horizontalalignment', 'center', ... %! 'units', 'normalized');