1 ## Copyright (C) 1995-2012 John W. Eaton
3 ## This file is part of Octave.
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ## General Public License for more details.
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING. If not, see
17 ## <http://www.gnu.org/licenses/>.
20 ## @deftypefn {Function File} {} subplot (@var{rows}, @var{cols}, @var{index})
21 ## @deftypefnx {Function File} {} subplot (@var{rcn})
22 ## Set up a plot grid with @var{rows} by @var{cols} subwindows and plot
23 ## in location given by @var{index}.
25 ## If only one argument is supplied, then it must be a three digit value
26 ## specifying the location in digits 1 (rows) and 2 (columns) and the plot
29 ## The plot index runs row-wise. First all the columns in a row are filled
30 ## and then the next row is filled.
32 ## For example, a plot with 2 by 3 grid will have plot indices running as
36 ## \hfil\vbox{\offinterlineskip\hrule
37 ## \halign{\vrule#&&\qquad\hfil#\hfil\qquad\vrule\cr
38 ## height13pt&1&2&3\cr height12pt&&&\cr\noalign{\hrule}
39 ## height13pt&4&5&6\cr height12pt&&&\cr\noalign{\hrule}}}
47 ## +-----+-----+-----+
49 ## +-----+-----+-----+
51 ## +-----+-----+-----+
55 ## @var{index} may be a vector. In which case, the new axis will enclose
56 ## the grid locations specified. The first demo illustrates an example:
59 ## demo ("subplot", 1)
63 ## @seealso{axes, plot}
66 ## Author: Vinayak Dutt <Dutt.Vinayak@mayo.EDU>
69 function h = subplot (varargin)
73 have_position = false;
74 initial_args_decoded = false;
81 if (isnumeric (arg1) && isscalar (arg1) && isnumeric (arg2)
82 && isscalar (arg2) && isnumeric (arg3))
87 initial_args_decoded = true;
91 if (! initial_args_decoded && nargin > 1)
92 ## check for 'position', pos, ...
93 if (strcmpi (varargin{1}, "position"))
95 if (isnumeric (arg) && numel (arg) == 4)
99 initial_args_decoded = true;
101 error ("expecting position to be a 4-element numeric array");
106 if (! initial_args_decoded && nargin > 0)
108 if (nargin == 1 && ishandle (arg))
111 cf = get (0, "currentfigure");
112 set (cf, "nextplot", "add");
114 elseif (isscalar (arg) && arg >= 0)
116 index = rem (arg, 10);
117 arg = (arg - index) / 10;
118 cols = rem (arg, 10);
119 arg = (arg - cols) / 10;
120 rows = rem (arg, 10);
122 initial_args_decoded = true;
124 error ("subplot: expecting axes handle or RCN argument");
128 if (! initial_args_decoded)
135 index = round (index);
137 if (any (index < 1) || any (index > rows*cols))
138 error ("subplot: INDEX value must be greater than 1 and less than ROWS*COLS");
141 if (cols < 1 || rows < 1 || index < 1)
142 error ("subplot: COLS, ROWS, and INDEX must be be positive");
146 nargs = numel (varargin);
149 if (strcmpi (arg, "align"))
151 elseif (strcmpi (arg, "replace"))
160 axesunits = get (0, "defaultaxesunits");
162 figureunits = get (cf, "units");
164 units = "normalized";
165 set (0, "defaultaxesunits", units);
166 set (cf, "units", "pixels");
168 ## FIXME: At the moment we force gnuplot to use the aligned mode
169 ## which will set "activepositionproperty" to "position".
170 ## Τhis can yield to text overlap between labels and titles
172 if (strcmp (get (cf, "__graphics_toolkit__"), "gnuplot"))
178 pos = subplot_position (rows, cols, index, "position");
179 elseif (strcmp (get (cf, "__graphics_toolkit__"), "gnuplot"))
180 pos = subplot_position (rows, cols, index, "outerpositiontight");
182 pos = subplot_position (rows, cols, index, "outerposition");
186 set (cf, "nextplot", "add");
189 kids = get (cf, "children");
190 for child = reshape (kids, 1, numel (kids))
191 ## Check whether this child is still valid; this might not be the
192 ## case anymore due to the deletion of previous children (due to
193 ## "deletefcn" callback or for legends/colorbars that are deleted
194 ## with their corresponding axes).
195 if (! ishandle (child))
198 if (strcmp (get (child, "type"), "axes"))
199 ## Skip legend and colorbar objects.
200 if (strcmp (get (child, "tag"), "legend")
201 || strcmp (get (child, "tag"), "colorbar"))
205 objpos = get (child, "position");
207 objpos = get (child, "outerposition");
209 if (all (objpos == pos) && ! replace_axes)
210 ## If the new axes are in exactly the same position as an
211 ## existing axes object, use the existing axes.
215 ## If the new axes overlap an old axes object, delete the old
222 objx1 = objx0 + objpos(3);
224 objy1 = objy0 + objpos(4);
225 if (! (x0 >= objx1 || x1 <= objx0 || y0 >= objy1 || y1 <= objy0))
233 set (cf, "currentaxes", tmp);
235 tmp = axes ("box", "off", "position", pos, varargin{:});
236 elseif (strcmp (get (cf, "__graphics_toolkit__"), "gnuplot"))
237 tmp = axes ("box", "off", "outerposition", pos, varargin{:});
239 tmp = axes ("looseinset", [0 0 0 0], "box", "off", "outerposition", pos,
240 "autopos_tag", "subplot", varargin{:});
243 unwind_protect_cleanup
244 set (0, "defaultaxesunits", axesunits);
245 set (cf, "units", figureunits);
254 function pos = subplot_position (rows, cols, index, position_property)
256 if (rows == 1 && cols == 1)
257 ## Trivial result for subplot (1,1,1)
258 if (strcmpi (position_property, "position"))
259 pos = get (0, "defaultaxesposition");
261 pos = get (0, "defaultaxesouterposition");
266 if (strcmp (position_property, "outerposition")
267 || strcmp (position_property, "outerpositiontight"))
269 margins.bottom = 0.05;
270 margins.right = 0.05;
272 if (strcmp (position_property, "outerpositiontight"))
276 margins.column = 0.04 / cols;
277 margins.row = 0.04 / rows;
279 width = 1 - margins.left - margins.right - (cols-1)*margins.column;
280 width = width / cols;
281 height = 1 - margins.top - margins.bottom - (rows-1)*margins.row;
282 height = height / rows;
284 defaultaxesposition = get (0, "defaultaxesposition");
286 ## The outer margins surrounding all subplot "positions" are independent
287 ## of the number of rows and/or columns
288 margins.left = defaultaxesposition(1);
289 margins.bottom = defaultaxesposition(2);
290 margins.right = 1.0 - margins.left - defaultaxesposition(3);
291 margins.top = 1.0 - margins.bottom - defaultaxesposition(4);
293 ## Fit from Matlab experiments
294 pc = 1 ./ [0.1860, (margins.left + margins.right - 1)];
295 margins.column = 1 ./ polyval (pc , cols);
296 pr = 1 ./ [0.2282, (margins.top + margins.bottom - 1)];
297 margins.row = 1 ./ polyval (pr , rows);
299 ## Calculate the width/height of the subplot axes "position".
300 ## This is also consistent with Matlab
301 width = 1 - margins.left - margins.right - (cols-1)*margins.column;
302 width = width / cols;
303 height = 1 - margins.top - margins.bottom - (rows-1)*margins.row;
304 height = height / rows;
307 ## Index offsets from the lower left subplot
308 yi = fix ((index(:)-1)/cols);
309 xi = index(:) - yi*cols - 1;
310 yi = (rows - 1) - yi;
312 ## Lower left corner of the subplot, i.e. position(1:2)
313 x0 = xi .* (width + margins.column) + margins.left;
314 y0 = yi .* (height + margins.row) + margins.bottom;
317 ## subplot (row, col, m:n)
318 x1 = max (x0(:)) + width;
319 y1 = max (y0(:)) + height;
322 pos = [x0, y0, x1-x0, y1-y0];
324 ## subplot (row, col, num)
325 pos = [x0, y0, width, height];
334 %! fmt = {'horizontalalignment', 'center', 'verticalalignment', 'middle'};
337 %! xlabel (sprintf ("xlabel #%d", n))
338 %! ylabel (sprintf ("ylabel #%d", n))
339 %! title (sprintf ("title #%d", n))
340 %! text (0.5, 0.5, sprintf('subplot(%d,%d,%d)', r, c, n), fmt{:})
343 %! subplot (r, c, 1:3)
344 %! xlabel (sprintf ("xlabel #%d:%d", 1, 3))
345 %! ylabel (sprintf ("ylabel #%d:%d", 1, 3))
346 %! title (sprintf ("title #%d:%d", 1, 3))
347 %! text (0.5, 0.5, sprintf('subplot(%d,%d,%d:%d)', r, c, 1, 3), fmt{:})
354 %! subplot (2, 2, n, "align")
356 %! xlabel (sprintf ("xlabel (2,2,%d)", n))
357 %! ylabel (sprintf ("ylabel (2,2,%d)", n))
358 %! title (sprintf ("title (2,2,%d)", n))
360 %! subplot (1, 2, 1, "align")
362 %! xlabel ("xlabel (1,2,1)")
363 %! ylabel ("ylabel (1,2,1)")
364 %! title ("title (1,2,1)")