view scripts/plot/util/subplot.m @ 30875:5d3faba0342e

doc: Ensure documentation lists output argument when it exists for all m-files. For new users of Octave it is best to show explicit calling forms in the documentation and to show a return argument when it exists. * bp-table.cc, shift.m, accumarray.m, accumdim.m, bincoeff.m, bitcmp.m, bitget.m, bitset.m, blkdiag.m, celldisp.m, cplxpair.m, dblquad.m, flip.m, fliplr.m, flipud.m, idivide.m, int2str.m, interpft.m, logspace.m, num2str.m, polyarea.m, postpad.m, prepad.m, randi.m, repmat.m, rng.m, rot90.m, rotdim.m, structfun.m, triplequad.m, uibuttongroup.m, uicontrol.m, uipanel.m, uipushtool.m, uitoggletool.m, uitoolbar.m, waitforbuttonpress.m, help.m, __additional_help_message__.m, hsv.m, im2double.m, im2frame.m, javachk.m, usejava.m, argnames.m, char.m, formula.m, inline.m, __vectorize__.m, findstr.m, flipdim.m, strmatch.m, vectorize.m, commutation_matrix.m, cond.m, cross.m, duplication_matrix.m, expm.m, orth.m, rank.m, rref.m, trace.m, vech.m, cast.m, compare_versions.m, delete.m, dir.m, fileattrib.m, grabcode.m, gunzip.m, inputname.m, license.m, list_primes.m, ls.m, mexext.m, movefile.m, namelengthmax.m, nargoutchk.m, nthargout.m, substruct.m, swapbytes.m, ver.m, verLessThan.m, what.m, fminunc.m, fsolve.m, fzero.m, optimget.m, __fdjac__.m, matlabroot.m, savepath.m, campos.m, camroll.m, camtarget.m, camup.m, camva.m, camzoom.m, clabel.m, diffuse.m, legend.m, orient.m, rticks.m, specular.m, thetaticks.m, xlim.m, xtickangle.m, xticklabels.m, xticks.m, ylim.m, ytickangle.m, yticklabels.m, yticks.m, zlim.m, ztickangle.m, zticklabels.m, zticks.m, ellipsoid.m, isocolors.m, isonormals.m, stairs.m, surfnorm.m, __actual_axis_position__.m, __pltopt__.m, close.m, graphics_toolkit.m, pan.m, print.m, printd.m, __ghostscript__.m, __gnuplot_print__.m, __opengl_print__.m, rotate3d.m, subplot.m, zoom.m, compan.m, conv.m, poly.m, polyaffine.m, polyder.m, polyint.m, polyout.m, polyreduce.m, polyvalm.m, roots.m, prefdir.m, prefsfile.m, profexplore.m, profexport.m, profshow.m, powerset.m, unique.m, arch_rnd.m, arma_rnd.m, autoreg_matrix.m, bartlett.m, blackman.m, detrend.m, durbinlevinson.m, fftconv.m, fftfilt.m, fftshift.m, fractdiff.m, hamming.m, hanning.m, hurst.m, ifftshift.m, rectangle_lw.m, rectangle_sw.m, triangle_lw.m, sinc.m, sinetone.m, sinewave.m, spectral_adf.m, spectral_xdf.m, spencer.m, ilu.m, __sprand__.m, sprand.m, sprandn.m, sprandsym.m, treelayout.m, beta.m, betainc.m, betaincinv.m, betaln.m, cosint.m, expint.m, factorial.m, gammainc.m, gammaincinv.m, lcm.m, nthroot.m, perms.m, reallog.m, realpow.m, realsqrt.m, sinint.m, hadamard.m, hankel.m, hilb.m, invhilb.m, magic.m, pascal.m, rosser.m, toeplitz.m, vander.m, wilkinson.m, center.m, corr.m, cov.m, discrete_cdf.m, discrete_inv.m, discrete_pdf.m, discrete_rnd.m, empirical_cdf.m, empirical_inv.m, empirical_pdf.m, empirical_rnd.m, kendall.m, kurtosis.m, mad.m, mean.m, meansq.m, median.m, mode.m, moment.m, range.m, ranks.m, run_count.m, skewness.m, spearman.m, statistics.m, std.m, base2dec.m, bin2dec.m, blanks.m, cstrcat.m, deblank.m, dec2base.m, dec2bin.m, dec2hex.m, hex2dec.m, index.m, regexptranslate.m, rindex.m, strcat.m, strjust.m, strtrim.m, strtrunc.m, substr.m, untabify.m, __have_feature__.m, __prog_output_assert__.m, __run_test_suite__.m, example.m, fail.m, asctime.m, calendar.m, ctime.m, date.m, etime.m: Add return arguments to @deftypefn macros where they were missing. Rename variables in functions (particularly generic "retval") to match documentation. Rename some return variables for (hopefully) better clarity (e.g., 'ax' to 'hax' to indicate it is a graphics handle to an axes object).
author Rik <rik@octave.org>
date Wed, 30 Mar 2022 20:40:27 -0700
parents 796f54d4ddbf
children bb9c7512e090
line wrap: on
line source

########################################################################
##
## Copyright (C) 1995-2022 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## 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
## <https://www.gnu.org/licenses/>.
##
########################################################################

## -*- texinfo -*-
## @deftypefn  {} {} subplot (@var{rows}, @var{cols}, @var{index})
## @deftypefnx {} {} subplot (@var{rows}, @var{cols}, @var{index}, @var{hax})
## @deftypefnx {} {} subplot (@var{rcn})
## @deftypefnx {} {} subplot (@var{hax})
## @deftypefnx {} {} subplot (@dots{}, "align")
## @deftypefnx {} {} subplot (@dots{}, "replace")
## @deftypefnx {} {} subplot ("position", @var{pos})
## @deftypefnx {} {} subplot (@dots{}, @var{prop}, @var{val}, @dots{})
## @deftypefnx {} {@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 an axes handle @var{hax} is provided after the (@var{rows}, @var{cols},
## @var{index}) arguments, the corresponding axes is turned into a
## subplot.
##
## If only one numeric argument is supplied, then it must be a three digit
## value specifying the number of rows 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 axes 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 axes tick marks or labels.
##
## If the option @qcode{"replace"} is given then the subplot axes will be
## reset, rather than just switching the current axes 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.
## The full list of properties is documented at @ref{Axes Properties}.
##
## Any previously existing axes that would be (partly) covered by the newly
## created axes are deleted.
##
## If the output @var{hax} is requested, subplot returns the axes handle for
## the subplot.  This is useful for modifying the properties of a subplot
## using @code{set}.
##
## Under some circumstances, @code{subplot} might not be able to identify axes
## that it could re-use and might replace them.  If @code{subplot} axes
## should be referenced repeatedly, consider creating and storing their axes
## handles beforehand instead of calling @code{subplot} repeatedly for the same
## position.
##
## Example:
##
## @example
## @group
## x = 1:10;
## y = rand (16, 10);
## for i_plot = 1:4
##   hax(i_plot) = subplot (2, 2, i_plot);
##   hold (hax(i_plot), "on");
##   grid (hax(i_plot), "on");
## endfor
## for i_loop = 1:2
##   for i_plot = 1:4
##     iy = (i_loop - 1)*4 + i_plot;
##     plotyy (hax(i_plot), x,y(iy,:), x,y(iy+1,:));
##   endfor
##  endfor
## @end group
## @end example
##
## @seealso{axes, plot, gca, set}
## @end deftypefn

function hax = subplot (varargin)

  align_axes = false;
  replace_axes = false;
  have_position = false;
  initial_args_decoded = false;
  make_subplot = false;
  hsubplot = [];

  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;
      if (nargin > 3 && isaxes (varargin{4}))
        make_subplot = true;
        hsubplot = varargin{4};
        varargin(1:4) = [];
      else
        varargin(1:3) = [];
      endif
      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: invalid 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 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 "positionconstraint" to "innerposition".
    ##        This 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)
      ## Subplots that cover more that one base subplot are not updated
      align_axes = (align_axes || (! isscalar (index)));
      ## Normal case where subplot indices have been given
      [pos, opos, li] = subplot_position (cf, rows, cols, index);
    else
      ## Position is specified by the user.
      li = zeros (1,4);
      align_axes = true;
    endif

    set (cf, "nextplot", "add");

    if (! make_subplot)
      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 (! ishghandle (child))
          continue;
        endif
        if (strcmp (get (child, "type"), "axes"))
          ## Skip legend and colorbar objects.
          if (any (strcmp (get (child, "tag"), {"legend", "colorbar"})))
            continue;
          endif

          if (! replace_axes)
            if (isappdata (child, "__subplotposition__"))
              objpos = getappdata (child, "__subplotposition__");
            else
              objpos = get (child, "position");
            endif
            if (all (abs (objpos - pos) < eps))
              ## If the new axes are in exactly the same position
              ## as an existing axes object, or if they share the same
              ## appdata "__subplotposition__", use the existing axes.
              found = true;
              hsubplot = child;
            else
              ## Check if this axes is a subplot with the same layout and
              ## index as the requested one
              rcn = getappdata (child, "__subplotrcn__");
              if (all (size (rcn) == [1 3])
                  && rcn{1} == rows && rcn{2} == cols && all (rcn{3} == index))
                found = true;
                hsubplot = child;
              endif
            endif
          endif

          if (! found)
            ## If the new axes overlap an old axes object, delete the old axes.
            objpos = get (child, "position");

            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
    else
      found = true;
    endif

    if (found && ! make_subplot)
      ## Switch to existing subplot and set requested properties
      set (cf, "currentaxes", hsubplot);
      if (! isempty (varargin))
        set (hsubplot, varargin{:});
      endif
    else
      pval = [{"positionconstraint", "innerposition", ...
               "position", pos, "looseinset", li} varargin];
      if (! make_subplot)
        hsubplot = axes (pval{:});
      else
        set (hsubplot, pval{:})
      endif

      if (! align_axes)
        ## base position (no ticks, no annotation, no cumbersome neighbor)
        setappdata (hsubplot, "__subplotposition__", pos);
        ## max outerposition
        setappdata (hsubplot, "__subplotouterposition__", opos);
        setappdata (hsubplot, "__subplotrcn__", {rows, cols, index});
        addlistener (hsubplot, "outerposition", @subplot_align);
        addlistener (hsubplot, "xaxislocation", @subplot_align);
        addlistener (hsubplot, "yaxislocation", @subplot_align);
        addlistener (hsubplot, "position", {@subplot_align, true});
        subplot_align (hsubplot);
      endif

    endif
  unwind_protect_cleanup
    set (0, "defaultaxesunits", axesunits);
    set (cf, "units", figureunits);
  end_unwind_protect

  if (nargout > 0)
    hax = hsubplot;
  endif

endfunction

function [pos, opos, li] = subplot_position (hf, nrows, ncols, idx)

  if (nrows == 1 && ncols == 1)
    ## Trivial result for subplot (1,1,1)
    pos = get (0, "defaultaxesposition");
    opos = get (0, "defaultaxesouterposition");
    li = get (0, "defaultaxeslooseinset");
    return;
  endif

  ## Row/Column inside the axes array
  row = ceil (idx / ncols);
  col = idx - (row - 1) * ncols;
  row = [min(row) max(row)];
  col = [min(col) max(col)];

  ## Minimal margins around subplots defined in points
  fig_units = get (hf, "units");
  set (hf, "units", "points");
  pts_size = get (gcf, "position")(3:4);
  xbasemargin = 6 / pts_size(1);
  ybasemargin = 6 / pts_size(2);

  ## Column/row separation
  margin.column = .2 / ncols + 2 * xbasemargin;
  margin.row = .2 / nrows + 2 * ybasemargin;

  set (hf, "units", fig_units);
  margin.left = xbasemargin;
  margin.right = xbasemargin;
  margin.bottom = ybasemargin;
  margin.top = ybasemargin;

  ## Boundary axes have default margins
  borders = get (0, "defaultaxesposition");
  if (col(1) == 1)
    margin.left = borders(1);
  else
    margin.left = margin.column - margin.right;
  endif
  if (col(2) == ncols)
    margin.right = 1 - borders(1) - borders(3);
  endif


  if (row(2) == nrows)
    margin.bottom = borders(2);
  else
    margin.bottom = margin.row - margin.top;
  endif
  if (row(1) == 1)
    margin.top = 1 - borders(2) - borders(4);
  endif


  ## Compute base width and height
  width = (borders(3) - (ncols - 1) * margin.column) / ncols;
  height = (borders(4) - (nrows - 1) * margin.row) /nrows;

  ## Position, outerposition and looseinset
  x0 = borders(1) + (col(1) - 1) * (width + margin.column);
  y0 = borders(2) + (nrows - row(2)) * (height + margin.row);
  width += diff (col) * (width + margin.column);
  height += diff (row) * (height + margin.row);

  pos = [x0 y0 width height];
  opos = [(x0 - margin.left), (y0 - margin.bottom), ...
          (width + margin.left + margin.right), ...
          (height + margin.bottom + margin.top)];
  li = [margin.left, margin.bottom, margin.right, margin.top];

endfunction

function subplot_align (h, ~, rmupdate = false)
  persistent updating = false;

  if (! updating)
    if (rmupdate)
      ## The "position" property has been changed from outside this routine.
      ## Don't update anymore.
      if (isappdata (h, "__subplotposition__"))
        rmappdata (h, "__subplotposition__");
        rmappdata (h, "__subplotouterposition__");
      endif
      return;
    endif

    unwind_protect
      updating = true;
      hf = ancestor (h, "figure");
      children = get (hf, "children");

      ## Base position of the subplot
      pos = getappdata (children, "__subplotposition__");

      if (iscell (pos))
        do_align = ! cellfun (@isempty, pos);
        pos = cell2mat (pos(do_align));
      else
        return;
      endif
      hsubplots = children(do_align);


      ## There may be mixed subplot series (e.g., 2-by-6 and 1-by-6) in
      ## the same figure.  Only subplots that have the same width and
      ## height as this one are updated.
      if (any (h == hsubplots))
        width = pos(h == hsubplots, 3);
        height = pos(h == hsubplots, 4);
        do_align = (pos(:,3) == width) & (pos(:,4) == height);
        hsubplots(! do_align) = [];
        pos(! do_align,:) = [];
      else
        return;
      endif

      ## Reset outerpositions to their default value
      opos = getappdata (hsubplots, "__subplotouterposition__");
      if (iscell (opos))
        opos = cell2mat (opos);
      endif
      for ii = 1:numel (hsubplots)
        set (hsubplots(ii), "outerposition", opos(ii,:), ...
             "positionconstraint", "innerposition");
      endfor

      ## Compare current positions to default and compute the new ones
      curpos = get (hsubplots, "position");
      if (iscell (curpos))
        curpos = cell2mat (curpos);
      endif
      dx0 = max (curpos(:,1) - pos(:,1));
      dx0(dx0<0) = 0;
      dx1 = max ((pos(:,1) + pos(:,3)) - (curpos(:,1) + curpos(:,3)));
      dx1(dx1<0) = 0;
      dy0 = max (curpos(:,2) - pos(:,2));
      dy0(dy0<0) = 0;
      dy1 = max ((pos(:,2) + pos(:,4)) - (curpos(:,2) + curpos(:,4)));
      dy1(dy1<0) = 0;

      pos(:,1) += dx0;
      pos(:,2) += dy0;
      pos(:,3) -= dx0 + dx1;
      pos(:,4) -= dy0 + dy1;

      for ii = 1:numel (hsubplots)
        set (hsubplots(ii), "position", pos(ii,:));
      endfor

    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]);
%! endfor
%! 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));
%! endfor
%! 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
%! clf;
%! 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");

## Test recognition/deletion of previous axes
## Default mode
%!test
%! hf = figure ("visible", "off");
%! unwind_protect
%!   for ii = 1:9
%!     hax(ii) = subplot (3,3,ii);
%!   endfor
%!   subplot (3,3,1);
%!   assert (gca (), hax(1));
%!   subplot (2,1,1);
%!   assert (ishghandle (hax),[false(1,6), true(1,3)]);
%! unwind_protect_cleanup
%!   delete (hf);
%! end_unwind_protect

## Position mode
%!test
%! hf = figure ("visible", "off");
%! unwind_protect
%!   h1 = subplot ("position", [0.1 0.1 0.3 0.3]);
%!   h2 = subplot ("position", [0.5 0.5 0.3 0.3]);
%!   subplot ("position", [0.1 0.1 0.3 0.3]);
%!   assert (gca (), h1);
%!   subplot ("position", [0.5 0.5 0.3 0.3]);
%!   assert (gca (), h2);
%!   subplot ("position", [0.5 0.5 0.3 0.2]);
%!   assert (! ishghandle (h2));
%! unwind_protect_cleanup
%!   delete (hf);
%! end_unwind_protect

## Align mode
%!test
%! hf = figure ("visible", "off");
%! unwind_protect
%!   h1 = subplot (3,5,1, "align");
%!   h2 = subplot (3,5,2, "align");
%!   subplot (3,5,1, "align");
%!   assert (gca (), h1);
%!   subplot (3,2,1, "align");
%!   assert (! ishghandle (h1));
%!   assert (! ishghandle (h2));
%! unwind_protect_cleanup
%!   delete (hf);
%! end_unwind_protect