Mercurial > octave
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 +