changeset 17038:08dd9458684a

Overhaul __plt_get_axis_arg__ and newplot functions to avoid creating unnecessary axes. * scripts/plot/__plt_get_axis_arg__.m: Only return an axis handle if found in argument list. Do not create any figures or axes. * scripts/plot/newplot.m: Overhaul to allow specifying an axis handle input. Prepare figure and axes according to Matlab conventions. * scripts/plot/line.m: Never call newplot() for a core graphic object. Always plot into gca. * scripts/plot/plot.m: Update to use new __plt_get_axis_arg__ and newplot functions.
author Rik <rik@octave.org>
date Wed, 24 Jul 2013 23:05:37 -0700
parents 08f0e372d006
children f694f791709b
files scripts/plot/__plt_get_axis_arg__.m scripts/plot/line.m scripts/plot/newplot.m scripts/plot/plot.m
diffstat 4 files changed, 169 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/__plt_get_axis_arg__.m
+++ b/scripts/plot/__plt_get_axis_arg__.m
@@ -25,17 +25,7 @@
 
 function [h, varargin, narg] = __plt_get_axis_arg__ (caller, varargin)
 
-  ## First argument can be a boolean which determines whether a new
-  ## axis will be created if none exists.
-  if (islogical (caller))
-    nogca = caller;
-    caller = varargin{1};
-    varargin(1) = [];
-  else
-    nogca = false;
-  endif
-
-  ## Search for parent property
+  h = [];
   parent = find (strcmpi (varargin, "parent"), 1);
   
   ## Look for numeric scalar which is a graphics handle but not the
@@ -65,28 +55,6 @@
     else
       error ("%s: expecting parent value to be axes handle", caller);
     endif
-  else
-    ## No axis specified.  Use current axis or create one as necessary.
-    f = get (0, "currentfigure");
-    if (isempty (f))
-      h = [];
-    else
-      h = get (f, "currentaxes");
-    endif
-    if (isempty (h))
-      if (nogca)
-        h = NaN;
-      else
-        h = gca ();
-      endif
-    endif
-    if (nargin < 2)
-      varargin = {};
-    endif
-  endif
-
-  if (ishandle (h) && strcmp (get (h, "nextplot"), "new"))
-    h = axes ();
   endif
 
   narg = length (varargin);
--- a/scripts/plot/line.m
+++ b/scripts/plot/line.m
@@ -40,9 +40,13 @@
 
 function h = line (varargin)
 
-  ## Get any axis argument which may be in a 'parent' PROP/VAL pair
+  ## Get axis argument which may be in a 'parent' PROP/VAL pair
   [hax, varargin] = __plt_get_axis_arg__ ("line", varargin{:});
 
+  if (isempty (hax))
+    hax = gca ();
+  endif
+
   htmp = __line__ (hax, varargin{:});
 
   if (nargout > 0)
--- a/scripts/plot/newplot.m
+++ b/scripts/plot/newplot.m
@@ -18,56 +18,165 @@
 
 ## -*- texinfo -*-
 ## @deftypefn  {Function File} {} newplot ()
-## @deftypefnx {Function File} {@var{h} =} newplot ()
-## Prepare graphics engine to produce a new plot.  This function is
-## called at the beginning of all high-level plotting functions.
-## It is not normally required in user programs.
+## @deftypefnx {Function File} {} newplot (@var{hfig})
+## @deftypefnx {Function File} {} newplot (@var{hax})
+## @deftypefnx {Function File} {@var{hax} =} newplot (@dots{})
+## Prepare graphics engine to produce a new plot.
+##
+## This function is called at the beginning of all high-level plotting
+## functions.  It is not normally required in user programs.  @code{newplot}
+## queries the "NextPlot" field of the current figure and axis to determine
+## what to do.
+##
+## @multitable @columnfractions .25 .75
+## @headitem Figure NextPlot @tab Action
+## @item "new" @tab Create a new figure and make it the current figure.
+##
+## @item "add" (default) @tab Add new graphic objects to the current figure.
+##
+## @item "replacechildren" @tab Delete child objects whose HandleVisibility is
+## set to "on".  Set NextPlot property to "add".  This typically clears a
+## figure, but leaves in place hidden objects such as menubars.  This is
+## equivalent to @code{clf}.
+##
+## @item "replace" @tab Delete all child objects of the figure and reset all
+## figure properties to their defaults.  However, the following four properties
+## are not reset: Position, Units, PaperPosition, PaperUnits.  This is
+## equivalent to @code{clf reset}.
 ##
-## The optional return value @var{h} is a graphics handle to the created
-## axes (not figure).
+## @end multitable    
+##
+## @multitable @columnfractions .25 .75
+## @headitem Axis NextPlot @tab Action
+## @item "add" @tab Add new graphic objects to the current axes.  This is
+## equivalent to @code{hold on}.
+##
+## @item "replacechildren" @tab Delete child objects whose HandleVisibility is
+## set to "on", but leave axis properties unmodified.  This typically clears a
+## plot, but preserves special settings such as log scaling for axes.
+## This is equivalent to @code{cla}.
+##
+## @item "replace" (default) @tab Delete all child objects of the axis and reset all axis
+## properties to their defaults.  However, the following properties
+## are not reset: Position, Units.  This is equivalent to @code{cla reset}.
+##
+## @end multitable    
+##
+## If the optional input @var{hfig} or @var{hax} is given then prepare the
+## specified figure or axes rather than the current figure and axes.
+##
+## The optional return value @var{hax} is a graphics handle to the created
+## axes object (not figure).
+##
+## @strong{Caution:} Calling @code{newplot} may change the current figure and
+## current axis.
 ## @end deftypefn
 
-function h = newplot ()
+## FIXME: The Matlab function takes an optional list of file handles, hsave,
+##        which are not deleted when the figure and axes are prepared.
+##        I'm sure there is a good reason for that, but coding such
+##        compatibility is really tricky and doesn't serve much purpose since
+##        newplot is nearly exclusively used by Octave internal plotting
+##        functions.  In Octave's case the argument is almost always null,
+##        or occasionally the axis handle to plot into.
+
+function hax = newplot (hsave = [])
+
+  if (nargin > 1)
+    print_usage ();
+  endif
 
-  if (nargin == 0)
+  cf = [];
+  ca = [];
+
+  if (! isempty (hsave))
+    ## Find the first valid axes 
+    ca = ancestor (hsave, "axes", "toplevel"); 
+    ca = ca(find (ca, 1));
+    ## Next, find the figure associated with any axis found
+    if (! isempty (ca))
+      cf = ancestor (ca, "figure", "toplevel");
+    else
+      cf = ancestor (hsave, "figure", "toplevel"); 
+      cf = cf(find (cf, 1));
+    endif
+  endif
+
+  if (isempty (cf))
+    ## get current figure, or create a new one if necessary
     cf = gcf ();
-    fnp = get (cf, "nextplot");
-    switch (fnp)
-      ## FIXME -- probably we should do more than validate the nextplot
-      ## property value...
-      case "new"
-      case "add"
-      case "replacechildren"
-        delete (get (cf, "children"));
-      case "replace"
-      otherwise
-        error ("newplot: unrecognized nextplot property for current figure");
-    endswitch
+  else
+    ## switch to figure provided without causing other updates
+    set (0, "currentfigure", cf);
+  endif
+
+  fnp = get (cf, "nextplot");
+  switch (fnp)
+    case "add"
+      ## Default case.  Doesn't require action.
+    case "new"
+      ## Ordinarily, create a new figure to hold plot.
+      ## But, if user has requested preparing a specific axis, then
+      ## use the existing figure to hold the requested axis.
+      if (isempty (ca))
+        cf = figure ();
+      endif
+    case "replacechildren"
+      kids = get (cf, "children");
+      if (! isempty (ca))
+        kids(kids == ca) = [];
+      endif
+      delete (kids);
+    case "replace"
+      kids = allchild (cf);
+      if (! isempty (ca))
+        kids(kids == ca) = [];
+      endif
+      delete (kids);
+      reset (cf);
+  endswitch
+  set (cf, "nextplot", "add");  # Matlab compatibility
+
+  if (isempty (ca))
     ca = gca ();
-    anp = get (ca, "nextplot");
-    if (strcmp (get (ca, "__hold_all__"), "off"))
-      __next_line_color__ (true);
-      __next_line_style__ (true);
-    else
-      __next_line_color__ (false);
-      __next_line_style__ (false);
-    endif
-    switch (anp)
-      case "new"
-      case "add"
-      case "replacechildren"
-        delete (get (ca, "children"));
-      case "replace"
-        __go_axes_init__ (ca, "replace");
-        __request_drawnow__ ();
-      otherwise
-        error ("newplot: unrecognized nextplot property for current axes");
-    endswitch
-    if (nargout > 0)
-      h = ca;
-    endif
+  else
+    set (cf, "currentaxes", ca);
+  endif
+
+  ## FIXME: Is this necessary anymore?
+  ##        It seems like a kluge that belongs somewhere else.
+  if (strcmp (get (ca, "__hold_all__"), "off"))
+    __next_line_color__ (true);
+    __next_line_style__ (true);
   else
-    print_usage ();
+    __next_line_color__ (false);
+    __next_line_style__ (false);
+  endif
+
+  anp = get (ca, "nextplot");
+  switch (anp)
+    case "add"
+      ## Default case.  Doesn't require action.
+    case "replacechildren"
+      delete (get (ca, "children"));
+    case "replace"
+      __go_axes_init__ (ca, "replace");
+      __request_drawnow__ ();
+      ## FIXME: The code above should perform the following:
+      ###########################
+      ## delete (allchild (ca));
+      ## reset (ca);
+      ###########################
+      ## Actually, __go_axes_init__ does both less and more.
+      ## It doesn't really remove all children since it re-instantiates
+      ## xlabel, ylabel, zlabel, and title text objects.
+      ## Also it preserves font properties like fontsize.
+      ## For the time being, in order to have axis labels and title work,
+      ## the above code is is required.
+  endswitch
+
+  if (nargout > 0)
+    hax = ca;
   endif
 
 endfunction
@@ -77,8 +186,8 @@
 %! hf = figure ("visible", "off");
 %! unwind_protect
 %!   p = plot ([0, 1]);
-%!   ha = newplot ();
-%!   assert (ha, gca);
+%!   hax = newplot ();
+%!   assert (hax, gca);
 %!   assert (isempty (get (gca, "children")));
 %! unwind_protect_cleanup
 %!   close (hf);
--- a/scripts/plot/plot.m
+++ b/scripts/plot/plot.m
@@ -181,25 +181,26 @@
 
 ## Author: jwe
 
-function retval = plot (varargin)
+function h = plot (varargin)
 
-  [h, varargin, nargs] = __plt_get_axis_arg__ ("plot", varargin{:});
+  [hax, varargin, nargs] = __plt_get_axis_arg__ ("plot", varargin{:});
 
   if (nargs < 1)
     print_usage ();
   endif
 
-  oldh = gca ();
+  oldfig = ifelse (isempty (hax), [], get (0, "currentfigure"));
   unwind_protect
-    axes (h);
-    newplot ();
-    tmp = __plt__ ("plot", h, varargin{:});
+    hax = newplot (hax);
+    htmp = __plt__ ("plot", hax, varargin{:});
   unwind_protect_cleanup
-    axes (oldh);
+    if (! isempty (oldfig))
+      set (0, "currentfigure", oldfig);
+    endif
   end_unwind_protect
 
   if (nargout > 0)
-    retval = tmp;
+    h = htmp;
   endif
 
 endfunction