changeset 22819:3dd91233bf07

axis.m: Overhaul internal function __axis__. * axis.m (__axis__): Switch programming strategy from recursively calling __axis__ to process one option at a time to using a for loop over varargin (35% performance improvement). Change "on"/"off" arguments to only affect visibility for Matlab compatibility. Use strncmpi to simplify testing for "auto", "tic", and "label" options. Add FIXME notes and more comments to the code. Add new BIST test for input validation.
author Rik <rik@octave.org>
date Wed, 23 Nov 2016 12:04:01 -0800
parents 3918beb3edc6
children c97b26662858
files scripts/plot/appearance/axis.m
diffstat 1 files changed, 170 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/appearance/axis.m	Wed Nov 23 11:58:23 2016 -0800
+++ b/scripts/plot/appearance/axis.m	Wed Nov 23 12:04:01 2016 -0800
@@ -162,7 +162,7 @@
 
 endfunction
 
-function limits = __axis__ (ca, ax, varargin)
+function limits = __axis__ (ca, varargin)
 
   if (nargin == 1)
     if (nargout == 0)
@@ -178,178 +178,184 @@
         limits = [xlim, ylim, zlim];
       endif
     endif
+    return;
+  endif
 
-  elseif (ischar (ax))
-    len = length (ax);
+  for arg = varargin
+    opt = arg{1};
 
-    ## 'matrix mode' to reverse the y-axis
-    if (strcmpi (ax, "ij"))
-      set (ca, "ydir", "reverse");
-    elseif (strcmpi (ax, "xy"))
-      set (ca, "ydir", "normal");
+    if (ischar (opt))
+      len = length (opt);
+
+      ## 'matrix mode' to reverse the y-axis
+      if (strcmpi (opt, "ij"))
+        set (ca, "ydir", "reverse");
+      elseif (strcmpi (opt, "xy"))
+        set (ca, "ydir", "normal");
 
       ## aspect ratio
-    elseif (strcmpi (ax, "image"))
-      __axis__ (ca, "equal");
-      set (ca, "plotboxaspectratiomode", "auto");
-      __do_tight_option__ (ca);
-    elseif (strcmpi (ax, "square"))
-      set (ca, "dataaspectratiomode", "auto",
-               "plotboxaspectratio", [1, 1, 1]);
-    elseif (strcmp (ax, "equal"))
-      ## Get position of axis in pixels
-      ca_units = get (ca, "units");
-      set (ca, "units", "pixels");
-      axis_pos = get (ca, "position");
-      set (ca, "units", ca_units);
+      elseif (strcmpi (opt, "image"))
+        __axis__ (ca, "equal");
+        set (ca, "plotboxaspectratiomode", "auto");
+        __do_tight_option__ (ca);
+      elseif (strcmpi (opt, "square"))
+        set (ca, "dataaspectratiomode", "auto",
+                 "plotboxaspectratio", [1, 1, 1]);
+      elseif (strcmp (opt, "equal"))
+        ## Get position of axis in pixels
+        ca_units = get (ca, "units");
+        set (ca, "units", "pixels");
+        axis_pos = get (ca, "position");
+        set (ca, "units", ca_units);
 
-      pbar = get (ca, "PlotBoxAspectRatio");
-      dx = diff (__get_tight_lims__ (ca, "x"));
-      dy = diff (__get_tight_lims__ (ca, "y"));
-      dz = diff (__get_tight_lims__ (ca, "z"));
-      new_pbar = [dx dy dz];
-      if (dx/pbar(1) < dy/pbar(2))
-        set (ca, "xlimmode", "auto");
-        new_pbar(1) = dy / axis_pos(4)*axis_pos(3);
-      else
-        set (ca, "ylimmode", "auto");
-        new_pbar(2) = dx / axis_pos(3)*axis_pos(4);
-      endif
-      set (ca, "dataaspectratio", [1, 1, 1],
-               "plotboxaspectratio", new_pbar);
+        pbar = get (ca, "plotboxaspectratio");
+        dx = diff (__get_tight_lims__ (ca, "x"));
+        dy = diff (__get_tight_lims__ (ca, "y"));
+        dz = diff (__get_tight_lims__ (ca, "z"));
+        new_pbar = [dx dy dz];
+        if (dx/pbar(1) < dy/pbar(2))
+          set (ca, "xlimmode", "auto");
+          new_pbar(1) = (dy / axis_pos(4)) * axis_pos(3);
+        else
+          set (ca, "ylimmode", "auto");
+          new_pbar(2) = (dx / axis_pos(3)) * axis_pos(4);
+        endif
+        set (ca, "dataaspectratio", [1, 1, 1],
+                 "plotboxaspectratio", new_pbar);
 
-    elseif (strcmpi (ax, "normal"))
-      ## Set plotboxaspectratio to something obtuse so that switching
-      ## back to "auto" will force a re-calculation.
-      set (ca, "plotboxaspectratio", [3 2 1]);
-      set (ca, "plotboxaspectratiomode", "auto",
-               "dataaspectratiomode", "auto");
+      elseif (strcmpi (opt, "normal"))
+        ## Set plotboxaspectratio to something obtuse so that switching
+        ## back to "auto" will force a re-calculation.
+        set (ca, "plotboxaspectratio", [3 2 1]);
+        set (ca, "plotboxaspectratiomode", "auto",
+                 "dataaspectratiomode", "auto");
 
       ## axis limits
-    elseif (len >= 4 && strcmpi (ax(1:4), "auto"))
-      if (len > 4)
-        if (any (ax == "x"))
-          set (ca, "xlimmode", "auto");
+      elseif (strncmpi (opt, "auto", 4))
+        if (len == 4)
+          set (ca, "xlimmode", "auto", "ylimmode", "auto", "zlimmode", "auto");
+        else
+          if (any (opt == "x"))
+            set (ca, "xlimmode", "auto");
+          endif
+          if (any (opt == "y"))
+            set (ca, "ylimmode", "auto");
+          endif
+          if (any (opt == "z"))
+            set (ca, "zlimmode", "auto");
+          endif
         endif
-        if (any (ax == "y"))
-          set (ca, "ylimmode", "auto");
+      elseif (strcmpi (opt, "manual"))
+        ## fixes the axis limits
+        set (ca, "xlimmode", "manual", "ylimmode", "manual",
+                 "zlimmode", "manual");
+      elseif (strcmpi (opt, "tight"))
+        ## sets the axis limits to the min and max of all data.
+        __do_tight_option__ (ca);
+
+      ## visibility
+      elseif (strcmpi (opt, "on"))
+        set (ca, "visible", "on");
+      elseif (strcmpi (opt, "off"))
+        set (ca, "visible", "off");
+
+      ## tick marks
+      elseif (strcmpi (opt, "tic"))
+        set (ca, "xtickmode", "auto", "ytickmode", "auto", "ztickmode", "auto",
+                 "visible", "on");
+      elseif (strncmpi (opt, "tic", 3))
+        if (any (opt == "x"))
+          set (ca, "xtickmode", "auto");
+        else
+          set (ca, "xtick", []);
+        endif
+        if (any (opt == "y"))
+          set (ca, "ytickmode", "auto");
+        else
+          set (ca, "ytick", []);
         endif
-        if (any (ax == "z"))
-          set (ca, "zlimmode", "auto");
+        if (any (opt == "z"))
+          set (ca, "ztickmode", "auto");
+        else
+          set (ca, "ztick", []);
         endif
+
+      ## labels
+      elseif (strcmpi (opt, "label"))
+        set (ca, "xticklabelmode", "auto", "yticklabelmode", "auto",
+                 "zticklabelmode", "auto");
+      elseif (strcmpi (opt, "nolabel"))
+        set (ca, "xticklabel", {}, "yticklabel", {}, "zticklabel", {})
+      elseif (strncmpi (opt, "label", 5))
+        if (any (opt == "x"))
+          set (ca, "xticklabelmode", "auto");
+        else
+          set (ca, "xticklabel", "");
+        endif
+        if (any (opt == "y"))
+          set (ca, "yticklabelmode", "auto");
+        else
+          set (ca, "yticklabel", "");
+        endif
+        if (any (opt == "z"))
+          set (ca, "zticklabelmode", "auto");
+        else
+          set (ca, "zticklabel", "");
+        endif
+
       else
-        set (ca, "xlimmode", "auto", "ylimmode", "auto", "zlimmode", "auto");
-      endif
-    elseif (strcmpi (ax, "manual"))
-      ## fixes the axis limits, like axis(axis) should;
-      set (ca, "xlimmode", "manual", "ylimmode", "manual", "zlimmode", "manual");
-    elseif (strcmpi (ax, "tight"))
-      ## sets the axis limits to the min and max of all data.
-      __do_tight_option__ (ca);
-      ## tick marks
-    elseif (strcmpi (ax, "on") || strcmpi (ax, "tic"))
-      set (ca, "xtickmode", "auto", "ytickmode", "auto", "ztickmode", "auto");
-      if (strcmpi (ax, "on"))
-        set (ca, "xticklabelmode", "auto", "yticklabelmode", "auto",
-           "zticklabelmode", "auto");
+        warning ("axis: unknown option '%s'", opt);
       endif
-      set (ca, "visible", "on");
-    elseif (strcmpi (ax, "off"))
-      set (ca, "xtick", [], "ytick", [], "ztick", []);
-      set (ca, "visible", "off");
-    elseif (len > 3 && strcmpi (ax(1:3), "tic"))
-      if (any (ax == "x"))
-        set (ca, "xtickmode", "auto");
-      else
-        set (ca, "xtick", []);
-      endif
-      if (any (ax == "y"))
-        set (ca, "ytickmode", "auto");
-      else
-        set (ca, "ytick", []);
-      endif
-      if (any (ax == "z"))
-        set (ca, "ztickmode", "auto");
-      else
-        set (ca, "ztick", []);
+
+    elseif (isnumeric (opt) && isvector (opt))
+
+      len = length (opt);
+
+      if (len != 2 && len != 4 && len != 6 && len != 8)
+        error ("axis: LIMITS vector must have 2, 4, 6, or 8 elements");
       endif
-    elseif (strcmpi (ax, "label"))
-      set (ca, "xticklabelmode", "auto", "yticklabelmode", "auto",
-           "zticklabelmode", "auto");
-    elseif (strcmpi (ax, "nolabel"))
-      set (ca, "xticklabel", "", "yticklabel", "", "zticklabel", "");
-    elseif (len > 5 && strcmpi (ax(1:5), "label"))
-      if (any (ax == "x"))
-        set (ca, "xticklabelmode", "auto");
-      else
-        set (ca, "xticklabel", "");
+
+      for i = 1:2:len
+        if (opt(i) >= opt(i+1))
+          error ("axis: LIMITS(%d) must be less than LIMITS(%d)", i, i+1);
+        endif
+      endfor
+
+      if (len > 1)
+        xlim (ca, opt(1:2));
       endif
-      if (any (ax == "y"))
-        set (ca, "yticklabelmode", "auto");
-      else
-        set (ca, "yticklabel", "");
+
+      if (len > 3)
+        ylim (ca, opt(3:4));
       endif
-      if (any (ax == "z"))
-        set (ca, "zticklabelmode", "auto");
-      else
-        set (ca, "zticklabel", "");
+
+      if (len > 5)
+        zlim (ca, opt(5:6));
+      endif
+
+      if (len > 7)
+        caxis (ca, opt(7:8));
       endif
 
     else
-      warning ("axis: unknown option '%s'", ax);
-    endif
-
-  elseif (isnumeric (ax) && isvector (ax))
-
-    len = length (ax);
-
-    if (len != 2 && len != 4 && len != 6 && len != 8)
-      error ("axis: LIMITS vector must have 2, 4, 6, or 8 elements");
-    endif
-
-    for i = 1:2:len
-      if (ax(i) >= ax(i+1))
-        error ("axis: LIMITS(%d) must be less than LIMITS(%d)", i, i+1);
-      endif
-    endfor
-
-    if (len > 1)
-      xlim (ca, ax(1:2));
-    endif
-
-    if (len > 3)
-      ylim (ca, ax(3:4));
+      error ("axis: expecting no args, or a numeric vector with 2, 4, 6, or 8 elements");
     endif
 
-    if (len > 5)
-      zlim (ca, ax(5:6));
-    endif
-
-    if (len > 7)
-      caxis (ca, ax(7:8));
-    endif
-
-  else
-    error ("axis: expecting no args, or a numeric vector with 2, 4, 6, or 8 elements");
-  endif
-
-  if (! isempty (varargin))
-    __axis__ (ca, varargin{:});
-  endif
-
+  endfor
 endfunction
 
+## Find the limits for axis ("tight").
+## AX should be one of "x", "y", or "z".
 function lims = __get_tight_lims__ (ca, ax)
 
-  ## Get the limits for axis ("tight").
-  ## AX should be one of "x", "y", or "z".
   kids = findobj (ca, "-property", [ax "data"]);
   ## The data properties for hggroups mirror their children.
-  ## Exclude the redundant hgroup values.
+  ## Exclude the redundant hggroup values.
   hg_kids = findobj (kids, "type", "hggroup");
   kids = setdiff (kids, hg_kids);
   if (isempty (kids))
     ## Return the current limits.
+    ## FIXME: Is this the correct thing to do?
     lims = get (ca, [ax "lim"]);
   else
     data = get (kids, [ax "data"]);
@@ -394,15 +400,15 @@
 
   xlim = __get_tight_lims__ (ca, "x");
   if (all (xlim == 0))
-    xlim = eps () * [-1 1];
+    xlim = [-eps, +eps];
   elseif (diff (xlim == 0))
-    xlim .*= (1 + eps () * [-1, 1]);
+    xlim .*= [1-eps, 1+eps];
   endif
   ylim = __get_tight_lims__ (ca, "y");
   if (all (ylim == 0))
-    ylim = eps () * [-1 1];
+    ylim = [-eps, +eps];
   elseif (diff (ylim == 0))
-    ylim .*= (1 + eps () * [-1, 1]);
+    ylim .*= [1-eps, 1+eps];
   endif
   set (ca, "xlim", xlim, "ylim", ylim);
   nd = __calc_dimensions__ (ca);
@@ -410,9 +416,9 @@
   if (nd > 2 && is3dview)
     zlim = __get_tight_lims__ (ca, "z");
     if (all (zlim == 0))
-      zlim = eps () * [-1 1];
+      zlim = [-eps, +eps];
     elseif (diff (zlim == 0))
-      zlim .*= (1 + eps () * [-1, 1]);
+      zlim .*= [1-eps, 1+eps];
     endif
     set (ca, "zlim", zlim);
   endif
@@ -571,6 +577,13 @@
 
 %!demo
 %! clf;
+%! loglog (1:20, "-s");
+%! axis tight;
+
+## FIXME: These demos aren't actually of the axis function.
+##        They are demos for an axes object and belong elsewhere.
+%!demo
+%! clf;
 %! x = -10:10;
 %! plot (x,x, x,-x);
 %! set (gca, "yscale", "log");
@@ -579,11 +592,6 @@
 
 %!demo
 %! clf;
-%! loglog (1:20, "-s");
-%! axis tight;
-
-%!demo
-%! clf;
 %! x = -10:0.1:10;
 %! y = sin (x)./(1 + abs (x)) + 0.1*x - 0.4;
 %! plot (x, y);
@@ -669,7 +677,6 @@
 %! end_unwind_protect
 
 ## Even on errors, axis can display a figure.
-
 %!error <LIMITS vector must have .* elements>
 %! hf = figure ("visible", "off");
 %! unwind_protect
@@ -678,6 +685,14 @@
 %!   close (hf);
 %! end_unwind_protect
 
+%!error <LIMITS\(3\) must be less than LIMITS\(4\)>
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   axis ([1 2 4 3])
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+
 %!error <expecting no args, or a numeric vector with .* elements>
 %! hf = figure ("visible", "off");
 %! unwind_protect
@@ -685,3 +700,4 @@
 %! unwind_protect_cleanup
 %!   close (hf);
 %! end_unwind_protect
+