# HG changeset patch # User Rik # Date 1544655681 28800 # Node ID 69bd0cfbd1239284abeebc97f173ea77b92b8435 # Parent da75dfccf14ba88161b2a1b34c9cfc71ecba95d6 Add color and marker cycling and (bug #39036). * line.m: Rewrite documentation to make calling forms clearer and how they differ. * __line__.m: Rename variable 'h' to 'hp' (parent handle). Completely overhaul input validation. More quickly separate out low-level calling forms and plot them. Use cellindexmat rather than cellfun for increased performance. When there are multiple lines call __next_line_style__ and __next_line_color__ to implement color and marker cycling. diff -r da75dfccf14b -r 69bd0cfbd123 scripts/plot/draw/line.m --- a/scripts/plot/draw/line.m Wed Dec 12 11:25:38 2018 -0800 +++ b/scripts/plot/draw/line.m Wed Dec 12 15:01:21 2018 -0800 @@ -19,18 +19,32 @@ ## -*- texinfo -*- ## @deftypefn {} {} line () ## @deftypefnx {} {} line (@var{x}, @var{y}) -## @deftypefnx {} {} line (@var{x}, @var{y}, @var{property}, @var{value}, @dots{}) ## @deftypefnx {} {} line (@var{x}, @var{y}, @var{z}) -## @deftypefnx {} {} line (@var{x}, @var{y}, @var{z}, @var{property}, @var{value}, @dots{}) -## @deftypefnx {} {} line (@var{property}, @var{value}, @dots{}) +## @deftypefnx {} {} line ("xdata", @var{x}, "ydata", @var{y}) +## @deftypefnx {} {} line ("xdata", @var{x}, "ydata", @var{y}, "zdata", @var{z}) +## @deftypefnx {} {} line (@dots{}, @var{property}, @var{value}) ## @deftypefnx {} {} line (@var{hax}, @dots{}) ## @deftypefnx {} {@var{h} =} line (@dots{}) -## Create line object from @var{x} and @var{y} (and possibly @var{z}) and -## insert in the current axes. +## Create a line object from @var{x} and @var{y} (and possibly @var{z}) and +## insert it in the current axes. +## +## In the standard calling form the data @var{x}, @var{y}, and @var{z} may be +## scalars, vectors, or matrices. In the case of matrix inputs, @code{line} +## will attempt to orient scalars and vectors so the results can be plotted. +## This requires that one of the dimensions of the vector match either the +## number of rows or the number of columns of the matrix. +## +## In the low-level calling form (50% higher performance) where the data is +## specified by name (@code{line ("xdata", @var{x}, @dots{})}) the data must be +## vectors. If no data is specified (@code{line ()}) then +## @w{@code{@var{x} == @var{y} = [0, 1]}}. ## ## Multiple property-value pairs may be specified for the line object, but they ## must appear in pairs. ## +## If called with only @var{property}/@var{value} pairs then any unspecified +## properties use their default values as specified on the root object. +## ## If the first argument @var{hax} is an axes handle, then plot into this axes, ## rather than the current axes returned by @code{gca}. ## @@ -39,6 +53,9 @@ ## ## Programming Note: The full list of properties is documented at ## @ref{Line Properties,,Line Properties}. +## +## The function @code{line} differs from @code{plot} in that line objects are +## inserted in to the current axes without first clearing the plot. ## @seealso{image, patch, rectangle, surface, text} ## @end deftypefn diff -r da75dfccf14b -r 69bd0cfbd123 scripts/plot/draw/private/__line__.m --- a/scripts/plot/draw/private/__line__.m Wed Dec 12 11:25:38 2018 -0800 +++ b/scripts/plot/draw/private/__line__.m Wed Dec 12 15:01:21 2018 -0800 @@ -17,20 +17,22 @@ ## . ## -*- texinfo -*- -## @deftypefn {} {@var{h} =} __line__ (@var{p}, @dots{}) -## Undocumented internal function. +## @deftypefn {} {@var{h} =} __line__ (@var{parent}, @dots{}) +## Create line object with parent @var{parent}. +## +## Return handle @var{h} to created line objects. ## @end deftypefn -## __line__ (p, x, y, z) +## __line__ (hp, x, y, z) ## Create line object from x, y, and z with parent p. ## Return handle to line object. ## Author: jwe -function h = __line__ (p, varargin) +function h = __line__ (hp, varargin) - nvargs = numel (varargin); - + nvargs = nargin - 1; # remove argument 'hp' + have_data_prop = false; if (nvargs > 1 && ! ischar (varargin{1}) && ! ischar (varargin{2})) if (nvargs > 2 && ! ischar (varargin{3})) num_data_args = 3; @@ -39,19 +41,9 @@ endif else num_data_args = 0; - endif - - if (num_data_args > 0 && ! size_equal (varargin{1:num_data_args})) - n = 1:num_data_args; - m = cellfun (@numel, varargin(1:num_data_args)); - [~, m] = max (m); - b = ones (size (varargin{m(1)})); - try - varargin(n) = cellfun (@(x) bsxfun (@times, b, x), varargin(n), - "uniformoutput", false); - catch - error ("line: number of X, Y, and Z points must be equal"); - end_try_catch + if (any (strcmpi ("xdata", varargin(1:2:end)))) + have_data_prop = true; + endif endif if (rem (nvargs - num_data_args, 2) != 0) @@ -63,64 +55,99 @@ other_args = varargin(num_data_args+1:end); endif - nlines = 0; - nvecpts = 0; - ismat = false (1, 3); - for i = 1:num_data_args - tmp = varargin{i}(:,:); - if (isvector (tmp)) - nlines = max (1, nlines); - if (! isscalar (tmp)) - if (nvecpts == 0) - nvecpts = numel (tmp); - elseif (nvecpts != numel (tmp)) - error ("line: data size mismatch"); + if (num_data_args == 0) + if (have_data_prop) + ## Low-level calling form with a single line + handles(1) = __go_line__ (hp, other_args{:}); + else + ## line called without any data, use default data. + handles(1) = __go_line__ (hp, "xdata", [0, 1], "ydata", [0, 1], + other_args{:}); + endif + else + ## Normal case, extract and validate input data args + ismat = false (1,3); + + ## Initialize loop variables + tmpdata = varargin{1}(:,:); # N-D arrays -> 2-D arrays + if (isvector (tmpdata)) + tmpdata = tmpdata(:); # Use column vectors by default + else + ismat(1) = true; + endif + [nr, nc] = size (tmpdata); + if (islogical (tmpdata)) + tmpdata = uint8 (tmpdata); + elseif (iscomplex (tmpdata)) + tmpdata = real (tmpdata); + endif + varargin{1} = tmpdata; + reorient_done = false; + + for i = 2:num_data_args + tmpdata = varargin{i}(:,:); # N-D arrays -> 2-D arrays + + if (isvector (tmpdata)) + tmpdata = tmpdata(:); # Use column vectors by default + [r, c] = size (tmpdata); + if (nr == r) + ## Do nothing, properly oriented + elseif (nc == r && nc > 1 && ! reorient_done) + ## Re-orient first matrix + varargin{i-1} = varargin{i-1}.'; + [nr, nc] = deal (nc, nr); # swap rows and columns. + reorient_done = true; + else + error ("line: number of X, Y, and Z points must be equal"); + endif + else + ismat(i) = true; + [r, c] = size (tmpdata); + if (nr == r && (nc == c || nc == 1)) + ## Do nothing, properly oriented + nc = max (nc, c); + elseif (nr == c) + tmpdata = tmpdata.'; + [nr, nc] = deal (c, r); # swap rows and columns. + else + error ("line: number of X, Y, and Z points must be equal"); endif endif - else - ismat(i) = true; - nlines = max (columns (tmp), nlines); - endif - varargin{i} = tmp; - endfor - if (num_data_args == 0) - varargin = {[0, 1], [0, 1]}; - num_data_args = 2; - nlines = 1; - endif + ## Convert logical or complex inputs + if (islogical (tmpdata)) + tmpdata = uint8 (tmpdata); + elseif (iscomplex (tmpdata)) + tmpdata = real (tmpdata); + endif + varargin{i} = tmpdata; + + endfor + + nlines = nc; - handles = zeros (nlines, 1); - - data = cell (1, 3); - - if (num_data_args > 1) + data = cell (1, 3); data(1:num_data_args) = varargin(1:num_data_args); - for i = 1:num_data_args - if (islogical (data{i})) - data(i) = double (data{i}); - elseif (iscomplex (data{i})) - data(i) = real (data{i}); - endif + data_args = {"xdata", data{1}, "ydata", data{2}, "zdata", data{3}}; + mask = [false, ismat(1), false, ismat(2), false, ismat(3)]; + + handles = zeros (nlines, 1); + for i = 1:nlines + data_args(mask) = cellindexmat (data(ismat), ":", i); + + ## FIXME: Technically, it may not be the right thing to do to rotate + ## the style if the options in other_args specify a color + ## or linestyle. The plot will be made correctly, but the next + ## call to line may not use the correct value. + [linestyle, marker] = __next_line_style__ (); + color = __next_line_color__ (); + + handles(i) = __go_line__ (hp, data_args{:}, + "color", color, "linestyle", linestyle, + "marker", marker, other_args{:}); endfor endif - data_args = reshape ({"xdata", "ydata", "zdata"; data{:}}, [1, 6]); - mask = reshape ([false(1,3); ismat], [1, 6]); - - for i = 1:nlines - tmp = data(ismat); - if (! size_equal (tmp) - || (nvecpts != 0 && any (nvecpts != cellfun ("size", tmp, 1)))) - error ("line: data size_mismatch"); - endif - data_args(mask) = cellfun (@(x) x(:,i), data(ismat), - "uniformoutput", false); - - handles(i) = __go_line__ (p, data_args{:}, other_args{:}); - - endfor - if (nargout > 0) h = handles; endif