# HG changeset patch # User Andrej Lojdl # Date 1379624029 -7200 # Node ID 0b6f44d31edcaa214003106efcad262c80568c49 # Parent 981103844b511f20cd311697abec075345643a2a# Parent 2fee3022ac2f08a7ee332d2c8c0f69f53195fb1d Merge with main after adding Windows name fot GS. diff --git a/libinterp/corefcn/graphics.cc b/libinterp/corefcn/graphics.cc --- a/libinterp/corefcn/graphics.cc +++ b/libinterp/corefcn/graphics.cc @@ -8532,6 +8532,7 @@ properties of @qcode{\"position\"}, @qcode{\"units\"},\n\ @qcode{\"windowstyle\"} and @qcode{\"paperunits\"} and the default axes\n\ properties of @qcode{\"position\"} and @qcode{\"units\"} are not reset.\n\ +@seealso{cla, clf}\n\ @end deftypefn") { int nargin = args.length (); diff --git a/scripts/miscellaneous/fileparts.m b/scripts/miscellaneous/fileparts.m --- a/scripts/miscellaneous/fileparts.m +++ b/scripts/miscellaneous/fileparts.m @@ -25,38 +25,38 @@ function [directory, name, extension, version] = fileparts (filename) - if (nargin == 1) - if (ischar (filename)) - ds = strchr (filename, filesep ("all"), 1, "last"); - if (isempty (ds)) - ds = 0; - endif - es = rindex (filename, "."); - ## These can be the same if they are both 0 (no dir or ext). - if (es <= ds) - es = length (filename)+1; - endif - if (ds == 0) - directory = ""; - elseif (ds == 1) - directory = filename(1); - else - directory = filename(1:ds-1); - endif - name = filename(ds+1:es-1); - if (es > 0 && es <= length (filename)) - extension = filename(es:end); - else - extension = ""; - endif - version = ""; - else - error ("fileparts: expecting FILENAME argument to be a string"); - endif - else + if (nargin != 1) print_usage (); endif + if (! ischar (filename) || rows (filename) > 1) + error ("fileparts: FILENAME must be a single string"); + endif + + ds = strchr (filename, filesep ("all"), 1, "last"); + if (isempty (ds)) + ds = 0; + endif + es = rindex (filename, "."); + ## These can be the same if they are both 0 (no dir or ext). + if (es <= ds) + es = length (filename)+1; + endif + if (ds == 0) + directory = ""; + elseif (ds == 1) + directory = filename(1); + else + directory = filename(1:ds-1); + endif + name = filename(ds+1:es-1); + if (es > 0 && es <= length (filename)) + extension = filename(es:end); + else + extension = ""; + endif + version = ""; + endfunction @@ -96,3 +96,9 @@ %! [d, n, e] = fileparts (".ext"); %! assert (strcmp (d, "") && strcmp (n, char (zeros (1, 0))) && strcmp (e, ".ext")); +%% Test input validation +%!error fileparts () +%!error fileparts (1,2) +%!error fileparts (1) +%!error fileparts (["a"; "b"]) + diff --git a/scripts/plot/area.m b/scripts/plot/area.m --- a/scripts/plot/area.m +++ b/scripts/plot/area.m @@ -65,35 +65,40 @@ print_usage (); endif - idx = 1; x = y = []; bv = 0; - args = {}; - ## Check for (X) or (X,Y) arguments and possible base value. - if (nargin >= idx && ismatrix (varargin{idx})) - y = varargin{idx}; - idx++; - if (nargin >= idx) - if (isscalar (varargin{idx})) - bv = varargin{idx}; - idx++; - elseif (ismatrix (varargin{idx})) - x = y; - y = varargin{idx}; - idx++; - if (nargin >= idx && isscalar (varargin{idx})) - bv = varargin{idx}; - idx++; - endif + + num_numeric = find (cellfun ("isclass", varargin, "char"), 1) - 1; + if (isempty (num_numeric)) + num_numeric = nargin; + endif + + switch (num_numeric) + case 1 + y = varargin{1}; + case 2 + if (isscalar (varargin{2})) + y = varargin{1}; + bv = varargin{2}; + else + x = varargin{1}; + y = varargin{2}; endif - endif - else - print_usage (); + case 3 + x = varargin{1}; + y = varargin{2}; + bv = varargin{3}; + otherwise + print_usage (); + endswitch + + if (! isreal (x) || ! isreal (y)) + error ("area: X and Y must be real vectors or matrices"); endif - ## Check for additional args. - if (nargin >= idx) - args = {varargin{idx:end}}; + if (! isreal (bv) || ! isscalar (bv)) + error ("area: LVL must be a real scalar"); endif + if (isvector (y)) y = y(:); endif @@ -109,7 +114,7 @@ endif unwind_protect hax = newplot (hax); - htmp = __area__ (hax, x, y, bv, args{:}); + htmp = __area__ (hax, x, y, bv, varargin{num_numeric+1:end}); unwind_protect_cleanup if (! isempty (oldfig)) set (0, "currentfigure", oldfig); @@ -128,12 +133,16 @@ y0 = zeros (1, rows (y)); retval = []; for i = 1: columns (y); + + lc = __next_line_color__ (); + + ## Must occur after __next_line_color__ in order to work correctly. hg = hggroup (); retval = [retval; hg]; args = __add_datasource__ ("area", hg, {"x", "y"}, varargin{:}); - x1 = x(:, 1).'; - y1 = y (:, i).'; + x1 = x(:, 1)'; + y1 = y(:, i)'; addproperty ("xdata", hg, "data", x1); addproperty ("ydata", hg, "data", y1); @@ -142,11 +151,11 @@ if (i == 1) h = patch (ax, [x1(1), x1, fliplr(x1)], [bv, y1, bv*ones(1, length(y1))], - __next_line_color__ (), "parent", hg); + lc, "parent", hg); else y1 = y0 + y1; h = patch (ax, [x1(1), x1, fliplr(x1)], [y0(1), y1, fliplr(y0)], - __next_line_color__ (), "parent", hg); + lc, "parent", hg); endif y0 = y1; @@ -155,14 +164,14 @@ addlistener (hg, "basevalue", @move_baseline); addproperty ("edgecolor", hg, "patchedgecolor", get (h, "edgecolor")); - addproperty ("linewidth", hg, "patchlinewidth", get (h, "linewidth")); + addproperty ("facecolor", hg, "patchfacecolor", get (h, "facecolor")); addproperty ("linestyle", hg, "patchlinestyle", get (h, "linestyle")); - addproperty ("facecolor", hg, "patchfacecolor", get (h, "facecolor")); + addproperty ("linewidth", hg, "patchlinewidth", get (h, "linewidth")); addlistener (hg, "edgecolor", @update_props); - addlistener (hg, "linewidth", @update_props); + addlistener (hg, "facecolor", @update_props); addlistener (hg, "linestyle", @update_props); - addlistener (hg, "facecolor", @update_props); + addlistener (hg, "linewidth", @update_props); addproperty ("areagroup", hg, "data"); set (retval, "areagroup", retval); @@ -177,9 +186,9 @@ function update_props (h, d) kids = get (h, "children"); set (kids, "edgecolor", get (h, "edgecolor"), - "linewidth", get (h, "linewidth"), + "facecolor", get (h, "facecolor"), "linestyle", get (h, "linestyle"), - "facecolor", get (h, "facecolor")); + "linewidth", get (h, "linewidth")); endfunction function move_baseline (h, d) @@ -259,3 +268,13 @@ %! set (h, 'ydata', sort (get (h, 'ydata'))) %! title ('area() plot of sorted data'); +%% Test input validation +%!error area () +%!error area (1,2,3,4) +%!error area ({1}) +%!error area (1+i) +%!error area (1:2, {1, 2}) +%!error area (1:2, [1 1+i]) +%!error area (1, i) +%!error area (1, 2, ones (2,2)) + diff --git a/scripts/plot/caxis.m b/scripts/plot/caxis.m --- a/scripts/plot/caxis.m +++ b/scripts/plot/caxis.m @@ -17,21 +17,23 @@ ## . ## -*- texinfo -*- -## @deftypefn {Function File} {} caxis (@var{limits}) +## @deftypefn {Function File} {} caxis ([cmin cmax]) ## @deftypefnx {Function File} {} caxis ("auto") ## @deftypefnx {Function File} {} caxis ("manual") ## @deftypefnx {Function File} {} caxis (@var{hax}, @dots{}) ## @deftypefnx {Function File} {@var{limits} =} caxis () ## Query or set color axis limits for plots. ## -## The argument @var{limits} should be a 2-element vector specifying the +## The limits argument should be a 2-element vector specifying the ## lower and upper limits to assign to the first and last value in the -## colormap. Values outside this range are clamped to the first and last +## colormap. Data values outside this range are clamped to the first and last ## colormap entries. ## -## If @var{limits} is @qcode{"auto"}, then automatic colormap scaling is -## applied, whereas if @var{limits} is @qcode{"manual"} the colormap scaling -## is set to manual. +## If the @qcode{"auto"} option is given then automatic colormap limits are +## applied. The automatic algorithm sets @var{cmin} to the minimum data value +## and @var{cmax} to the maximum data value. If @qcode{"manual"} is specified +## then the @qcode{"climmode"} property is set to @qcode{"manual"} and the +## numeric values in the @qcode{"clim"} property are used for limits. ## ## If the first argument @var{hax} is an axes handle, then operate on ## this axis rather than the current axes returned by @code{gca}. diff --git a/scripts/plot/cla.m b/scripts/plot/cla.m --- a/scripts/plot/cla.m +++ b/scripts/plot/cla.m @@ -33,7 +33,7 @@ ## ## If the first argument @var{hax} is an axes handle, then operate on ## this axis rather than the current axes returned by @code{gca}. -## @seealso{clf} +## @seealso{clf, delete, reset} ## @end deftypefn ## Author: Ben Abbott diff --git a/scripts/plot/clf.m b/scripts/plot/clf.m --- a/scripts/plot/clf.m +++ b/scripts/plot/clf.m @@ -37,7 +37,7 @@ ## ## The optional return value @var{h} is the graphics handle of the figure ## window that was cleared. -## @seealso{cla, close, delete} +## @seealso{cla, close, delete, reset} ## @end deftypefn ## Author: jwe diff --git a/scripts/plot/close.m b/scripts/plot/close.m --- a/scripts/plot/close.m +++ b/scripts/plot/close.m @@ -23,10 +23,6 @@ ## @deftypefnx {Command} {} close all hidden ## Close figure window(s). ## -## @code{close} operates by calling the function specified by the -## @qcode{"closerequestfcn"} property for each figure. By default, the function -## @code{closereq} is used. -## ## When called with no arguments, close the current figure. This is equivalent ## to @code{close (gcf)}. If the input @var{h} is a graphic handle, or vector ## of graphics handles, then close each figure in @var{h}. @@ -37,10 +33,12 @@ ## If the argument @qcode{"all hidden"} is given then all figures, including ## hidden ones, are closed. ## -## Implementation Note: @code{close} calls a function to dispose of the figure. -## It is possible that the function will delay or abort removing the figure. -## To remove a figure without executing any callback functions use -## @code{delete}. +## Implementation Note: @code{close} operates by calling the function specified +## by the @qcode{"closerequestfcn"} property for each figure. By default, the +## function @code{closereq} is used. It is possible that the function invoked +## will delay or abort removing the figure. To remove a figure without +## executing any callback functions use @code{delete}. When writing a callback +## function to close a window do not use @code{close} to avoid recursion. ## ## @seealso{closereq, delete} ## @end deftypefn diff --git a/scripts/plot/closereq.m b/scripts/plot/closereq.m --- a/scripts/plot/closereq.m +++ b/scripts/plot/closereq.m @@ -19,6 +19,9 @@ ## -*- texinfo -*- ## @deftypefn {Function File} {} closereq () ## Close the current figure and delete all graphics objects associated with it. +## +## By default, the @qcode{"closerequestfcn"} property of a new plot figure +## points to this function. ## @seealso{close, delete} ## @end deftypefn @@ -26,18 +29,18 @@ function closereq () - if (nargin == 0) - cf = gcbf (); - if (isempty (cf)) - warning ("closereq: calling closereq from octave prompt is not supported, use 'close' instead"); - cf = get (0, "currentfigure"); - endif - if (! isempty (cf) && isfigure (cf)) - delete (cf); - endif - else + if (nargin != 0) print_usage (); endif + cf = gcbf (); + if (isempty (cf)) + warning ("closereq: calling closereq from octave prompt is not supported, use 'close' instead"); + cf = get (0, "currentfigure"); + endif + if (! isempty (cf) && isfigure (cf)) + delete (cf); + endif + endfunction diff --git a/scripts/plot/compass.m b/scripts/plot/compass.m --- a/scripts/plot/compass.m +++ b/scripts/plot/compass.m @@ -46,56 +46,58 @@ ## @end group ## @end example ## -## @seealso{polar, quiver, feather, plot} +## @seealso{polar, feather, quiver, rose, plot} ## @end deftypefn function h = compass (varargin) [hax, varargin, nargin] = __plt_get_axis_arg__ ("compass", varargin{:}); - if (nargin == 0) + if (nargin == 0 || nargin > 3) print_usage (); - elseif (nargin == 1 || (nargin == 2 && ! isnumeric (varargin{2}))) - ioff = 2; + endif + + if (nargin == 1 || (nargin == 2 && ! isnumeric (varargin{2}))) z = varargin{1}(:).'; u = real (z); v = imag (z); - elseif (nargin > 1 && isnumeric (varargin{2})) - ioff = 3; + have_line_spec = (nargin == 2); + elseif (nargin >= 2 && isnumeric (varargin{2})) u = varargin{1}(:).'; v = varargin{2}(:).'; + have_line_spec = (nargin == 3); + else + print_usage (); endif - arrowsize = 0.25; - line_spec = "b-"; - have_line_spec = false; - while (ioff <= nargin) - arg = varargin{ioff++}; - if ((ischar (arg) || iscell (arg)) && ! have_line_spec) - [linespec, valid] = __pltopt__ ("compass", arg, false); + arrowsize = 0.20; + line_spec = "-b"; + + if (have_line_spec) + arg = varargin{end}; + if (ischar (arg) || iscellstr (arg)) + [~, valid] = __pltopt__ ("compass", arg, false); if (valid) line_spec = arg; - have_line_spec = true; - break; else - error ("compass: invalid linespec"); + error ("compass: invalid linestyle STYLE"); endif else - error ("compass: unrecognized argument"); + error ("compass: invalid linestyle STYLE"); endif - endwhile + endif - ## Matlab draws compass plots, with the arrow head as one continous - ## line, and each arrow separately. This is completely different than - ## quiver and quite ugly. + ## Matlab draws compass plots with the arrow head as one continous line, + ## and each arrow separately. This is completely different than quiver + ## and quite ugly. n = length (u); xend = u; xtmp = u .* (1 - arrowsize); yend = v; ytmp = v .* (1 - arrowsize); - x = [zeros(1, n); xend; xtmp - v * arrowsize / 3; xend; ... + x = [zeros(1, n); xend; xtmp - v * arrowsize / 3; xend; ... xtmp + v * arrowsize / 3]; - y = [zeros(1, n); yend; ytmp + u * arrowsize / 3; yend; ... + y = [zeros(1, n); yend; ytmp + u * arrowsize / 3; yend; ... ytmp - u * arrowsize / 3]; [r, p] = cart2pol (x, y); diff --git a/scripts/plot/errorbar.m b/scripts/plot/errorbar.m --- a/scripts/plot/errorbar.m +++ b/scripts/plot/errorbar.m @@ -197,3 +197,24 @@ %! legend ("X-Y errbars", "X-Y errboxes"); %! title ('errorbar() with X-Y errorbars and error boxes'); +## Invisible figure used for tests +%!shared hf, hax +%! hf = figure ("visible", "off"); +%! hax = axes; + +%!error errorbar () +%!error errorbar (1) +%!error errorbar (hax, {1}, 2) +%!error errorbar (hax, 1, {2}) +%!error errorbar (hax, 1, 1:2) +%!error errorbar (hax, 1, 2, 3:4) +%!error errorbar (1,2,3,4,5,6,7) + +%!error <2 column errorplot is only valid for xerr> errorbar (1,2, "~>") +%!error <6 columns only valid for xyerr and boxxy> errorbar (1,2,3,4,5,6, "~") +%!error errorbar (1,2,3,4,5) + +## Close figure used for testing +%!test +%! close (hf); + diff --git a/scripts/plot/private/__errcomm__.m b/scripts/plot/private/__errcomm__.m --- a/scripts/plot/private/__errcomm__.m +++ b/scripts/plot/private/__errcomm__.m @@ -31,45 +31,45 @@ print_usage (caller); endif - nargs = length (varargin); retval = []; + data = cell (6,1); + nargs = numel (varargin); k = 1; - data = cell (6,1); while (k <= nargs) - a = varargin{k++}; - if (isvector (a)) - a = a(:); - elseif (ismatrix (a)) - ; - else - usage ("%s (...)", caller); + arg = varargin{k++}; + if (! ismatrix (arg)) + error ("%s: data argument %d must be numeric", caller, k-1); endif - sz = size (a); + if (isvector (arg)) + arg = arg(:); + endif + sz = size (arg); ndata = 1; - data{ndata} = a; + data{ndata} = arg; while (k <= nargs) - a = varargin{k++}; - if (ischar (a) || iscellstr (a)) - retval = [retval; __errplot__(a, hax, data{1:ndata})]; + arg = varargin{k++}; + if (ischar (arg) || iscellstr (arg)) + retval(end+1,1) = __errplot__(arg, hax, data{1:ndata}); break; - elseif (isvector (a)) - a = a(:); - elseif (ismatrix (a)) - ; - else - error ("%s: wrong argument types", caller); + endif + if (! ismatrix (arg)) + error ("%s: data argument %d must be numeric", caller, k-1); endif - if (size (a) != sz) - error ("%s: argument sizes do not match", caller); + if (isvector (arg)) + arg = arg(:); endif - data{++ndata} = a; + if (any (size (arg) != sz)) + error ("%s: size of argument %d does not match others", caller, k-1); + endif + data{++ndata} = arg; if (ndata > 6) error ("%s: too many arguments to plot", caller); endif endwhile endwhile - if (! (ischar (a) || iscellstr (a))) + ## No format code found, use yerrorbar + if (! (ischar (arg) || iscellstr (arg))) retval = [retval; __errplot__("~", hax, data{1:ndata})]; endif diff --git a/scripts/plot/private/__errplot__.m b/scripts/plot/private/__errplot__.m --- a/scripts/plot/private/__errplot__.m +++ b/scripts/plot/private/__errplot__.m @@ -27,119 +27,124 @@ function h = __errplot__ (fstr, hax, varargin) - [fmt, valid] = __pltopt__ ("__errplot__", fstr); - - [len, nplots] = size (varargin{1}); - h = []; + fmt = __pltopt__ ("__errplot__", fstr); - for i = 1:nplots - ## Set the plot type based on linestyle. - - if (strcmp (fmt.errorstyle, "~")) + ## Set the plot type based on linestyle. + switch (fmt.errorstyle) + case "~" ifmt = "yerr"; - elseif (strcmp (fmt.errorstyle, ">")) + case ">" ifmt = "xerr"; - elseif (strcmp (fmt.errorstyle, "~>")) + case "~>" ifmt = "xyerr"; - elseif (strcmp (fmt.errorstyle, "#")) + case "#" ifmt = "box"; - elseif (strcmp (fmt.errorstyle, "#~")) + case "#~" ifmt = "boxy"; - elseif (strcmp (fmt.errorstyle, "#~>")) + case "#~>" ifmt = "boxxy"; + otherwise + ifmt = "yerr"; + endswitch + + h = []; + nplots = columns (varargin{1}); + for i = 1:nplots + + if (isempty (fmt.color)) + lc = __next_line_color__ (); else - ifmt = "yerr"; + lc = fmt.color (); + endif + if (isempty (fmt.marker) && isempty (fmt.linestyle)) + [ls, mk] = __next_line_style__ (); + else + ls = fmt.linestyle; + mk = fmt.marker; endif + ## Must occur after __next_line_color__ in order to work correctly. hg = hggroup ("parent", hax); h = [h; hg]; args = __add_datasource__ ("__errplot__", hg, {"x", "y", "l", "u", "xl", "xu"}); - if (isempty (fmt.color)) - fmt.color = __next_line_color__ (); - endif - if (isempty (fmt.marker) && isempty (fmt.linestyle)) - [fmt.linestyle, fmt.marker] = __next_line_style__ (); - endif - hl = [(__line__ (hg, "linestyle", fmt.linestyle, "marker", fmt.marker, - "color", fmt.color)), - (__line__ (hg, "linestyle", "-", "marker", "none", - "color", fmt.color))]; + hl = [(__line__ (hg, "color", lc, "linestyle", ls, "marker", mk)), + (__line__ (hg, "color", lc, "linestyle", "-", "marker", "none"))]; switch (numel (varargin)) case 2 ydata = varargin{1}(:,i); xdata = 1:numel (ydata); - if (strcmp (ifmt, "xerr") || strcmp (ifmt, "box")) + if (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy")) + ldata = varargin{2}(:,i); + udata = ldata; + xldata = []; + xudata = []; + elseif (strcmp (ifmt, "xerr") || strcmp (ifmt, "box")) xldata = varargin{2}(:,i); xudata = ldata; - ldata = []; - udata = []; - elseif (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy")) - ldata = varargin{2}(:,i); - udata = ldata; - xldata = []; - xudata = []; + ldata = []; + udata = []; else error ("errorbar: 2 column errorplot is only valid for xerr or yerr"); endif case 3 - if (strcmp (ifmt, "boxxy") || strcmp (ifmt, "xyerr")) - ydata = varargin{1}(:,i); - xdata = 1:numel (ydata); + if (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy")) + xdata = varargin{1}(:,i); + ydata = varargin{2}(:,i); + ldata = varargin{3}(:,i); + udata = ldata; + xldata = []; + xudata = []; + elseif (strcmp (ifmt, "xyerr") || strcmp (ifmt, "boxxy")) + ydata = varargin{1}(:,i); + xdata = 1:numel (ydata); xldata = varargin{2}(:,i); xudata = xldata; - ldata = varargin{3}(:,i); - udata = ldata; - elseif (strcmp (ifmt, "xerr") || strcmp (ifmt, "box")) - xdata = varargin{1}(:,i); - ydata = varargin{2}(:,i); - xldata = varargin{3}(:,i); - xudata = xldata; - ldata = []; - udata = []; - else # yerr or boxy - xdata = varargin{1}(:,i); - ydata = varargin{2}(:,i); - ldata = varargin{3}(:,i); - udata = ldata; - xldata = []; - xudata = []; - endif - case 4 - if (strcmp (ifmt, "boxxy") || strcmp (ifmt, "xyerr")) - xdata = varargin{1}(:,i); - ydata = varargin{2}(:,i); + ldata = varargin{3}(:,i); + udata = ldata; + else # xerr or box + xdata = varargin{1}(:,i); + ydata = varargin{2}(:,i); xldata = varargin{3}(:,i); xudata = xldata; - ldata = varargin{4}(:,i); - udata = ldata; - elseif (strcmp (ifmt, "xerr") || strcmp (ifmt, "box")) - xdata = varargin{1}(:,i); - ydata = varargin{2}(:,i); + ldata = []; + udata = []; + endif + case 4 + if (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy")) + xdata = varargin{1}(:,i); + ydata = varargin{2}(:,i); + ldata = varargin{3}(:,i); + udata = varargin{4}(:,i); + xldata = []; + xudata = []; + elseif (strcmp (ifmt, "xyerr") || strcmp (ifmt, "boxxy")) + xdata = varargin{1}(:,i); + ydata = varargin{2}(:,i); + xldata = varargin{3}(:,i); + xudata = xldata; + ldata = varargin{4}(:,i); + udata = ldata; + else # xerr or box + xdata = varargin{1}(:,i); + ydata = varargin{2}(:,i); xldata = varargin{3}(:,i); xudata = varargin{4}(:,i); - ldata = []; - udata = []; - else # yerr or boxy - xdata = varargin{1}(:,i); - ydata = varargin{2}(:,i); - ldata = varargin{3}(:,i); - udata = varargin{4}(:,i); - xldata = []; - xudata = []; + ldata = []; + udata = []; endif - case 6 # boxxy, xyerr - if (strcmp (ifmt, "boxxy") || strcmp (ifmt, "xyerr")) - xdata = varargin{1}(:,i); - ydata = varargin{2}(:,i); + case 6 # xyerr, boxxy + if (strcmp (ifmt, "xyerr") || strcmp (ifmt, "boxxy")) + xdata = varargin{1}(:,i); + ydata = varargin{2}(:,i); xldata = varargin{3}(:,i); xudata = varargin{4}(:,i); - ldata = varargin{5}(:,i); - udata = varargin{6}(:,i); + ldata = varargin{5}(:,i); + udata = varargin{6}(:,i); else - error ("errorbar: error plot with 6 columns only valid for boxxy and xyerr"); + error ("errorbar: error plot with 6 columns only valid for xyerr and boxxy"); endif otherwise error ("errorbar: error plot requires 2, 3, 4, or 6 arguments"); @@ -154,21 +159,22 @@ addproperty ("format", hg, "string", ifmt); addproperty ("color", hg, "linecolor", get (hl(1), "color")); + addproperty ("linestyle", hg, "linelinestyle", get (hl(1), "linestyle")); addproperty ("linewidth", hg, "linelinewidth", get (hl(1), "linewidth")); - addproperty ("linestyle", hg, "linelinestyle", get (hl(1), "linestyle")); addproperty ("marker", hg, "linemarker", get (hl(1), "marker")); + addproperty ("markeredgecolor", hg, "linemarkerfacecolor", + get (hl(1), "markeredgecolor")); addproperty ("markerfacecolor", hg, "linemarkerfacecolor", get (hl(1), "markerfacecolor")); - addproperty ("markeredgecolor", hg, "linemarkerfacecolor", - get (hl(1), "markeredgecolor")); addproperty ("markersize", hg, "linemarkersize", get (hl(1), "markersize")); fcn = {@update_props, hl}; addlistener (hg, "color", fcn); + addlistener (hg, "linestyle", fcn); addlistener (hg, "linewidth", fcn); - addlistener (hg, "linestyle", fcn); addlistener (hg, "marker", fcn); + addlistener (hg, "markeredgecolor", fcn); addlistener (hg, "markerfacecolor", fcn); addlistener (hg, "markersize", fcn); @@ -250,7 +256,7 @@ ylo = ydata/ry; yhi = ydata*ry; endif - nans = NaN + xdata(:); + nans = NaN + xdata(:); # fast way to do NaN (size (xdata(:))) if (strcmp (ifmt, "yerr")) xdata = [xdata, xdata, nans, ... xlo, xhi, nans, ... @@ -289,7 +295,7 @@ ydata = [y1; y2]; return; else - error ("errorbar: valid error bar types are xerr, yerr, boxxy, and xyerr"); + error ("errorbar: valid error bar types are xerr, yerr, xyerr, box, boxy, boxxy"); endif xdata = xdata.'(:); @@ -297,17 +303,17 @@ endfunction -function update_props (hg, dummy, hl) +function update_props (hg, ~, hl) set (hl, "color", get (hg, "color"), - "linewidth", get (hg, "linewidth"));, + "linewidth", get (hg, "linewidth")); set (hl(1), "linestyle", get (hg, "linestyle"), "marker", get (hg, "marker"), - "markersize", get (hg, "markersize"), + "markeredgecolor", get (hg, "markeredgecolor"), "markerfacecolor", get (hg, "markerfacecolor"), - "markeredgecolor", get (hg, "markeredgecolor")); + "markersize", get (hg, "markersize")); endfunction -function update_data (hg, dummy, hl) +function update_data (hg, ~, hl) if (strcmp (get (hg, "type"), "axes")) hax = hg; @@ -318,10 +324,10 @@ xscale = get (hax, "xscale"); yscale = get (hax, "yscale"); - xdata = get (hg, "xdata"); - ydata = get (hg, "ydata"); - ldata = get (hg, "ldata"); - udata = get (hg, "udata"); + xdata = get (hg, "xdata"); + ydata = get (hg, "ydata"); + ldata = get (hg, "ldata"); + udata = get (hg, "udata"); xldata = get (hg, "xldata"); xudata = get (hg, "xudata"); ifmt = get (hg, "format"); diff --git a/scripts/plot/private/__next_line_color__.m b/scripts/plot/private/__next_line_color__.m --- a/scripts/plot/private/__next_line_color__.m +++ b/scripts/plot/private/__next_line_color__.m @@ -37,17 +37,17 @@ else ## Find and return the next line color ca = gca (); - colorOrder = get (ca, "ColorOrder"); + colororder = get (ca, "colororder"); if (reset_colors) color_index = 1; reset_colors = false; else ## Executed when "hold all" is active - nChildren = length (get (ca, "Children")); - nColors = rows (colorOrder); - color_index = mod (nChildren, nColors) + 1; + n_kids = length (get (ca, "children")); + n_colors = rows (colororder); + color_index = mod (n_kids, n_colors) + 1; endif - rgb = colorOrder(color_index,:); + rgb = colororder(color_index,:); endif endfunction diff --git a/scripts/plot/private/__plt__.m b/scripts/plot/private/__plt__.m --- a/scripts/plot/private/__plt__.m +++ b/scripts/plot/private/__plt__.m @@ -495,8 +495,8 @@ endif retval = line (x, y, "color", color, - "linestyle", linestyle, - "marker", marker, properties{:}); + "linestyle", linestyle, + "marker", marker, properties{:}); endfunction diff --git a/scripts/plot/private/__scatter__.m b/scripts/plot/private/__scatter__.m --- a/scripts/plot/private/__scatter__.m +++ b/scripts/plot/private/__scatter__.m @@ -75,9 +75,9 @@ if (isvector (c) && columns (c) != 3) c = c(:); endif - ## Compare only first 4 letters of "fill" as that is what Matlab uses. elseif (firstnonnumeric == istart && ischar (varargin{istart}) - && ! strncmpi (varargin{istart}, "filled", 4)) + && ! ( strcmpi (varargin{istart}, "filled") + || strcmpi (varargin{istart}, "fill"))) c = varargin{istart}; firstnonnumeric++; else @@ -91,7 +91,7 @@ iarg = firstnonnumeric; while (iarg <= nargin) arg = varargin{iarg++}; - if (ischar (arg) && strncmpi (arg, "filled", 4)) + if (ischar (arg) && (strcmpi (arg, "filled") || strcmpi (arg, "fill"))) filled = true; elseif ((ischar (arg) || iscell (arg)) && ! have_marker) [linespec, valid] = __pltopt__ (fcn, arg, false); @@ -119,6 +119,7 @@ c = __next_line_color__ (); endif + ## Must occur after __next_line_color__ in order to work correctly. hg = hggroup (); newargs = __add_datasource__ (fcn, hg, {"x", "y", "z", "c", "size"}, newargs{:}); diff --git a/scripts/plot/private/__stem__.m b/scripts/plot/private/__stem__.m --- a/scripts/plot/private/__stem__.m +++ b/scripts/plot/private/__stem__.m @@ -35,7 +35,7 @@ [hax, varargin, nargin] = __plt_get_axis_arg__ (caller, varargin{:}); [x, y, z, dofill, llc, ls, mmc, ms, varargin] = ... - check_stem_arg (have_z, varargin{:}); + check_stem_arg (have_z, varargin{:}); oldfig = []; if (! isempty (hax)) @@ -63,10 +63,6 @@ yt = [zeros(1, nx); yt; NaN(1, nx)](:); endif - hg = hggroup (); - h = [h; hg]; - args = __add_datasource__ (caller, hg, {"x", "y", "z"}, varargin{:}); - if (isempty (llc)) lc = __next_line_color__ (); else @@ -85,32 +81,34 @@ fc = "none"; endif + ## Must occur after __next_line_color__ in order to work correctly. + hg = hggroup (); + h = [h; hg]; + args = __add_datasource__ (caller, hg, {"x", "y", "z"}, varargin{:}); + if (have_z) - h_stems = plot3 (hax, xt, yt, zt, "color", lc, "linestyle", ls, - "parent", hg, x, y, z, "color", mc, - "marker", ms, "linestyle", "none", - "markerfacecolor", fc, "parent", hg); - + __line__ (hax, xt, yt, zt, "color", lc, "linestyle", ls, "parent", hg); + __line__ (hax, x, y, z, "color", mc, "linestyle", "none", + "marker", ms, "markerfacecolor", fc, "parent", hg); h_baseline = []; else - h_stems = plot (hax, xt, yt, "color", lc, "linestyle", ls, - "parent", hg, x(:,i), y(:, i), "color", mc, "marker", - ms, "linestyle", "none", "markerfacecolor", - fc, "parent", hg); - + __line__ (hax, xt, yt, "color", lc, "linestyle", ls, "parent", hg); + __line__ (hax, x(:,i), y(:, i), "color", mc, "linestyle", "none", + "marker", ms, "markerfacecolor", fc, "parent", hg); x_axis_range = get (hax, "xlim"); h_baseline = line (hax, x_axis_range, [0, 0], "color", [0, 0, 0]); - set (h_baseline, "handlevisibility", "off"); - set (h_baseline, "xliminclude", "off"); + set (h_baseline, "handlevisibility", "off", "xliminclude", "off"); addlistener (hax, "xlim", @update_xlim); - addlistener (h_baseline, "ydata", @update_baseline); - addlistener (h_baseline, "visible", @update_baseline); + addproperty ("basevalue", h_baseline, "data", 0); + addlistener (h_baseline, "basevalue", {@update_baseline, 0}); + addlistener (h_baseline, "ydata", {@update_baseline, 1}); + addlistener (h_baseline, "visible", {@update_baseline, 2}); endif ## Setup the hggroup and listeners. addproperty ("showbaseline", hg, "radio", "{on}|off"); + addproperty ("baseline", hg, "data", h_baseline); addproperty ("basevalue", hg, "data", 0); - addproperty ("baseline", hg, "data", h_baseline); if (! have_z) addlistener (hg, "showbaseline", @show_baseline); @@ -118,16 +116,18 @@ endif addproperty ("color", hg, "linecolor", lc); + addproperty ("linestyle", hg, "linelinestyle", ls); addproperty ("linewidth", hg, "linelinewidth", 0.5); - addproperty ("linestyle", hg, "linelinestyle", ls); addproperty ("marker", hg, "linemarker", ms); + addproperty ("markeredgecolor", hg, "linemarkerfacecolor", mc); addproperty ("markerfacecolor", hg, "linemarkerfacecolor", fc); addproperty ("markersize", hg, "linemarkersize", 6); addlistener (hg, "color", @update_props); + addlistener (hg, "linestyle", @update_props); addlistener (hg, "linewidth", @update_props); - addlistener (hg, "linestyle", @update_props); addlistener (hg, "marker", @update_props); + addlistener (hg, "markeredgecolor", @update_props); addlistener (hg, "markerfacecolor", @update_props); addlistener (hg, "markersize", @update_props); @@ -165,317 +165,171 @@ endfunction -function [x, y, z, dofill, lc, ls, mc, ms, newargs] = check_stem_arg (have_z, varargin) - - ## FIXME: There seems to be a lot of duplicated code in this function. - ## It seems like it should be possible to simplify things by - ## combining some of the nearly identical code sections into - ## additional subfunctions. - ## FIXME: The code is so convoluted that certain options, such as "filled", - ## are not being processed correctly. +function [x, y, z, dofill, lc, ls, mc, ms, args] = check_stem_arg (have_z, varargin) if (have_z) caller = "stem3"; else caller = "stem"; endif + nargin = nargin - 1; # account for have_z argument - ## Remove prop/val pairs from data to consider. - i = 2; - newargs = {}; - while (i < length (varargin)) - if (ischar (varargin{i}) && !(strcmpi ("fill", varargin{i}) - || strcmpi ("filled", varargin{i}))) - newargs{end + 1} = varargin{i}; - newargs{end + 1} = varargin{i + 1}; - nargin = nargin - 2; - varargin(i:i+1) = []; - else - i++; - endif - endwhile + num_numeric = find (cellfun ("isclass", varargin, "char"), 1) - 1; + if (isempty (num_numeric)) + num_numeric = nargin; + endif - ## set specifiers to default values. - [lc, ls, mc, ms] = set_default_values (); - dofill = 0; - fill_2 = 0; - linespec_2 = 0; - z = []; + if (num_numeric < 1 || num_numeric > 3) + print_usage (caller); + endif - ## Check input arguments. - if (nargin == 2) + x = y = z = []; + if (num_numeric == 1) if (have_z) z = varargin{1}; - x = 1:rows (z); - y = 1:columns (z); else y = varargin{1}; + endif + elseif (num_numeric == 2) + if (have_z) + error ("stem3: must define X, Y, and Z"); + else + x = varargin{1}; + y = varargin{2}; + endif + else # nun_numeric == 3 + if (have_z) + x = varargin{1}; + y = varargin{2}; + z = varargin{3}; + else + error ("stem: can not define Z for 2-D stem plot"); + endif + endif + + ## Validate numeric data + if (have_z) + if (isempty (x)) + [nr, nc] = size (z); + if (nr >= nc) + x = repmat ([1:nc], nr, 1); + y = repmat ([1:nr]', 1, nc); + else + x = repmat ([1:nc], nr, 1); + y = repmat ([1:nr]', 1, nc); + endif + endif + if (! (ismatrix (x) && ismatrix (y) && ismatrix (z))) + error ("stem3: X, Y, and Z must be numeric"); + endif + else + if (isempty (x)) if (isvector (y)) x = 1:length (y); elseif (ismatrix (y)) x = 1:rows (y); - else - error ("stem: Y must be a matrix"); - endif # in each case, x & y will be defined - endif - elseif (nargin == 3) - ## Several possibilities - ## - ## 1. the real y data - ## 2. 'filled' - ## 3. line spec - if (ischar (varargin{2})) - ## Only 2. or 3. possible. - if (strcmpi ("fill", varargin{2}) || strcmpi ("filled", varargin{2})) - dofill = 1; - else - ## Parse the linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{2}); - endif - if (have_z) - z = varargin{1}; - x = 1:rows (z); - y = 1:columns (z); - else - y = varargin{1}; - if (isvector (y)) - x = 1:length (y); - elseif (ismatrix (y)) - x = 1:rows (y); - else - error ("stem: Y must be a matrix"); - endif # in each case, x & y will be defined - endif - else - if (have_z) - error ("stem3: must define X, Y and Z"); - else - ## Must be the real y data. - x = varargin{1}; - y = varargin{2}; - if (! (ismatrix (x) && ismatrix (y))) - error ("stem: X and Y must be matrices"); - endif endif endif - elseif (nargin == 4) - ## Again, several possibilities: - ## - ## arg2 1. real y - ## arg2 2. 'filled' or linespec - ## arg3 1. real z - ## arg3 2. 'filled' or linespec - if (ischar (varargin{2})) - ## Only arg2 2. / arg3 1. & arg3 3. are possible. - if (strcmpi ("fill", varargin{2}) || strcmpi ("filled", varargin{2})) - dofill = 1; - fill_2 = 1; # Be sure, no second "fill" is in the arguments. - else - ## Must be a linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{2}); - linespec_2 = 1; - endif - if (have_z) - z = varargin{1}; - x = 1:rows (z); - y = 1:columns (z); - else - y = varargin{1}; - if (isvector (y)) - x = 1:length (y); - elseif (ismatrix (y)) - x = 1:rows (y); - else - error ("stem: Y must be a matrix"); - endif # in each case, x & y will be defined - endif - else - if (have_z) - x = varargin{1}; - y = varargin{2}; - z = varargin{3}; - if (! (ismatrix (x) && ismatrix (y) && ismatrix (z))) - error ("stem3: X, Y and Z must be matrices"); - endif - else - ## must be the real y data. - x = varargin{1}; - y = varargin{2}; - if (! (ismatrix (x) && ismatrix (y))) - error ("stem: X and Y must be matrices"); - endif - endif - endif # if ischar (varargin{2}) - if (! have_z) - ## varargin{3} must be char. - ## Check for "fill. - if ((strcmpi (varargin{3}, "fill") || strcmpi (varargin{3}, "filled")) - && fill_2) - error ("stem: duplicate fill argument"); - elseif (strcmpi ("fill", varargin{3}) && linespec_2) - ## Must be "fill". - dofill = 1; - fill_2 = 1; - elseif ((strcmpi (varargin{3}, "fill") || strcmpi (varargin{3}, "filled")) - && !linespec_2) - ## Must be "fill". - dofill = 1; - fill_2 = 1; - elseif (! linespec_2) - ## Must be linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{3}); - linespec_2 = 1; - endif + if (! (ismatrix (x) && ismatrix (y))) + error ("stem: X and Y must be numeric"); endif - elseif (nargin == 5) - if (have_z) - x = varargin{1}; - y = varargin{2}; - z = varargin{3}; - if (! (ismatrix (x) && ismatrix (y) && ismatrix (z))) - error ("stem3: X, Y and Z must be matrices"); - endif - else - x = varargin{1}; - y = varargin{2}; - if (! (ismatrix (x) && ismatrix (y))) - error ("stem: X and Y must be matrices"); - endif - endif - - if (! have_z) - if (strcmpi (varargin{3}, "fill") || strcmpi (varargin{3}, "filled")) - dofill = 1; - fill_2 = 1; # Be sure, no second "fill" is in the arguments. - else - ## Must be a linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{3}); - linespec_2 = 1; - endif - endif - - ## Check for "fill". - if ((strcmpi (varargin{4}, "fill") || strcmpi (varargin{4}, "filled")) - && fill_2) - error ("%s: duplicate fill argument", caller); - elseif ((strcmpi (varargin{4}, "fill") || strcmpi (varargin{4}, "filled")) - && linespec_2) - ## Must be "fill". - dofill = 1; - fill_2 = 1; - elseif (!strcmpi (varargin{4}, "fill") && !strcmpi (varargin{4}, "filled") - && !linespec_2) - ## Must be linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{4}); - linespec_2 = 1; - endif - elseif (nargin == 6 && have_z) - x = varargin{1}; - y = varargin{2}; - z = varargin{3}; - if (! (ismatrix (x) && ismatrix (y) && ismatrix (z))) - error ("stem3: X, Y and Z must be matrices"); - endif - - if (strcmpi (varargin{4}, "fill") || strcmpi (varargin{4}, "filled")) - dofill = 1; - fill_2 = 1; # be sure, no second "fill" is in the arguments - else - ## Must be a linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{4}); - linespec_2 = 1; - endif - - ## check for "fill" .. - if ((strcmpi (varargin{5}, "fill") || strcmpi (varargin{5}, "filled")) - && fill_2) - error ("stem3: duplicate fill argument"); - elseif ((strcmpi (varargin{5}, "fill") || strcmpi (varargin{5}, "filled")) - && linespec_2) - ## Must be "fill". - dofill = 1; - fill_2 = 1; - elseif (!strcmpi (varargin{5}, "fill") && !strcmpi (varargin{5}, "filled") - && !linespec_2) - ## Must be linespec. - [lc, ls, mc, ms] = stem_line_spec (caller, varargin{5}); - linespec_2 = 1; - endif - else - error ("%s: incorrect number of arguments", caller); endif ## Check sizes of x, y and z. if (have_z) - if (!size_equal (x, y, z)) - error ("stem3: inconsistent size of x, y and z"); - else - x = x(:); - y = y(:); - z = z(:); + if (! size_equal (x, y, z)) + error ("stem3: inconsistent sizes for X, Y, and Z"); endif + x = x(:); + y = y(:); + z = z(:); else if (isvector (x)) x = x(:); if (isvector (y)) if (length (x) != length (y)) - error ("stem: inconsistent size of x and y"); - else - y = y(:); + error ("stem: inconsistent sizes for X and Y"); endif + y = y(:); else if (length (x) == rows (y)) x = repmat (x(:), 1, columns (y)); else - error ("stem: inconsistent size of x and y"); + error ("stem: inconsistent sizes for X and Y"); endif endif - elseif (!size_equal (x, y)) - error ("stem: inconsistent size of x and y"); + elseif (! size_equal (x, y)) + error ("stem: inconsistent sizes for X and Y"); endif endif + dofill = false; + have_line_spec = false; + ## set specifiers to default values. + [lc, ls, mc, ms] = set_default_values (); + + args = {}; + ioff = num_numeric + 1; + while (ioff <= nargin) + arg = varargin{ioff++}; + if (ischar (arg) && any (strcmpi (arg, {"fill", "filled"}))) + dofill = true; + elseif ((ischar (arg) || iscell (arg)) && ! have_line_spec) + [linespec, valid] = __pltopt__ (caller, arg, false); + if (valid) + have_line_spec = true; + [lc, ls, mc, ms] = stem_line_spec (linespec); + else + args{end+1} = arg; + if (ioff <= nargin) + args{end+1} = varargin{ioff++}; + else + error ('%s: No value specified for property "%s"', caller, arg); + endif + endif + else + args{end+1} = arg; + if (ioff <= nargin) + args{end+1} = varargin{ioff++}; + else + error ('%s: No value specified for property "%s"', caller, arg); + endif + endif + endwhile + +endfunction + +function [lc, ls, mc, ms] = stem_line_spec (lspec) + + [lc, ls, mc, ms] = set_default_values (); + + if (! isempty (lspec.color)) + lc = mc = lspec.color; + endif + + if (! isempty (lspec.linestyle) && ! strcmp (lspec.linestyle, "none")) + ls = lspec.linestyle; + endif + + if (! isempty (lspec.marker) && ! strcmp (lspec.marker, "none")) + ms = lspec.marker; + endif + endfunction -function [lc, ls, mc, ms] = stem_line_spec (caller, str) - if (! ischar (str)) - error ("%s: expecting argument to be \"fill\" or a string of specifiers", - caller); - endif - [lc, ls, mc, ms] = set_default_values (); - ## Parse the line specifier string. - cur_props = __pltopt__ ("stem", str, false); - for i = 1:length (cur_props) - if (isfield (cur_props(i), "color") && ! isempty (cur_props(i).color)); # means line color - mc = lc = cur_props(i).color; - elseif (isfield (cur_props(i), "linestyle")) - ls = cur_props(i).linestyle; - if (isempty (ls)) - ls = __next_line_style__ (); - endif - elseif (isfield (cur_props(i), "marker") && ! strcmpi (cur_props(i).marker, "none")) - ms = cur_props(i).marker; - if (isempty (ms)) - [dummy, ms] = __next_line_style__ (); - endif - endif - endfor -endfunction - function [lc, ls, mc, ms] = set_default_values () - ## set default values mc = []; lc = []; ls = "-"; ms = "o"; endfunction -function update_xlim (h, d) +function update_xlim (h, ~) kids = get (h, "children"); xlim = get (h, "xlim"); for i = 1 : length (kids) - obj = get (kids (i)); + obj = get (kids(i)); if (strcmp (obj.type, "hggroup") && isfield (obj, "baseline")) if (any (get (obj.baseline, "xdata") != xlim)) set (obj.baseline, "xdata", xlim); @@ -484,37 +338,39 @@ endfor endfunction -function update_baseline (h, d) +function update_baseline (h, ~, src) visible = get (h, "visible"); - ydata = get (h, "ydata")(1); + if (src == 0) + basevalue = get (h, "basevalue"); + else + basevalue = get (h, "ydata")(1); + endif kids = get (get (h, "parent"), "children"); for i = 1 : length (kids) - obj = get (kids (i)); + obj = get (kids(i)); if (strcmp (obj.type, "hggroup") && isfield (obj, "baseline") && obj.baseline == h) - ## Only alter if changed to avoid recursion of the listener functions - if (! strcmpi (get (kids(i), "showbaseline"), visible)) - set (kids (i), "showbaseline", visible); + ## Avoid lots of unnecessary listener updates + if (! strcmp (get (kids(i), "showbaseline"), visible)) + set (kids(i), "showbaseline", visible); endif - if (! strcmpi (get (kids(i), "basevalue"), visible)) - set (kids (i), "basevalue", ydata); + if (get (kids(i), "basevalue") != basevalue) + set (kids(i), "basevalue", basevalue); endif endif endfor endfunction -function show_baseline (h, d) +function show_baseline (h, ~) set (get (h, "baseline"), "visible", get (h, "showbaseline")); endfunction -function move_baseline (h, d) +function move_baseline (h, ~) b0 = get (h, "basevalue"); bl = get (h, "baseline"); - if (get (bl, "ydata") != [b0, b0]) - set (bl, "ydata", [b0, b0]); - endif + set (bl, "ydata", [b0, b0]); kids = get (h, "children"); yt = get (h, "ydata")(:)'; @@ -523,18 +379,18 @@ set (kids(2), "ydata", yt); endfunction -function update_props (h, d) +function update_props (h, ~) kids = get (h, "children"); set (kids(2), "color", get (h, "color"), - "linewidth", get (h, "linewidth"), - "linestyle", get (h, "linestyle")); - set (kids(1), "color", get (h, "color"), - "marker", get (h, "marker"), - "markerfacecolor", get (h, "markerfacecolor"), - "markersize", get (h, "markersize")); + "linestyle", get (h, "linestyle"), + "linewidth", get (h, "linewidth")); + set (kids(1), "color", get (h, "markeredgecolor"), + "marker", get (h, "marker"), + "markerfacecolor", get (h, "markerfacecolor"), + "markersize", get (h, "markersize")); endfunction -function update_data (h, d) +function update_data (h, ~) x = get (h, "xdata"); y = get (h, "ydata"); z = get (h, "zdata"); diff --git a/scripts/plot/stairs.m b/scripts/plot/stairs.m --- a/scripts/plot/stairs.m +++ b/scripts/plot/stairs.m @@ -70,27 +70,28 @@ if (nargin < 1) print_usage (); - else - if (nargout > 1) - [h, xs, ys] = __stairs__ (false, varargin{:}); - else - oldfig = []; - if (! isempty (hax)) - oldfig = get (0, "currentfigure"); endif - unwind_protect - hax = newplot (hax); - [htmp, xxs, yys] = __stairs__ (true, varargin{:}); - unwind_protect_cleanup - if (! isempty (oldfig)) - set (0, "currentfigure", oldfig); - endif - end_unwind_protect - if (nargout == 1) - xs = htmp; + + if (nargout < 2) + oldfig = []; + if (! isempty (hax)) + oldfig = get (0, "currentfigure"); + endif + unwind_protect + hax = newplot (hax); + [htmp, xxs, yys] = __stairs__ (true, varargin{:}); + unwind_protect_cleanup + if (! isempty (oldfig)) + set (0, "currentfigure", oldfig); endif + end_unwind_protect + if (nargout == 1) + xs = htmp; endif + else + [~, xs, ys] = __stairs__ (false, varargin{:}); endif + endfunction function [h, xs, ys] = __stairs__ (doplot, varargin) @@ -98,42 +99,35 @@ if (nargin == 2 || ischar (varargin{2})) y = varargin{1}; varargin(1) = []; - if (ismatrix (y)) - if (isvector (y)) - y = y(:); - endif - x = 1:rows (y); + if (! ismatrix (y) || ndims (y) > 2) + error ("stairs: Y must be a numeric 2-D vector or matrix"); endif + if (isvector (y)) + y = y(:); + endif + x = 1:rows (y); else x = varargin{1}; y = varargin{2}; varargin(1:2) = []; - endif - - if (ndims (x) > 2 || ndims (y) > 2) - error ("stairs: X and Y must be 2-D objects"); + if (! ismatrix (x) || ! ismatrix (y) || ndims (x) > 2 || ndims (y) > 2) + error ("stairs: X and Y must be numeric 2-D vectors or matrices"); + endif endif vec_x = isvector (x); - if (vec_x) x = x(:); endif if (isvector (y)) y = y(:); + elseif (ismatrix (y) && vec_x) + x = repmat (x, [1, columns(y)]); endif - if (ismatrix (y)) - [nr, nc] = size (y); - if (vec_x) - x = repmat (x, [1, nc]); - else - [x_nr, x_nc] = size (x); - if (x_nr != nr || x_nc != nc) - error ("stairs: argument size mismatch"); - endif - endif + if (! size_equal (x, y)) + error ("stairs: X and Y sizes must match"); endif len = 2*nr - 1; @@ -153,9 +147,9 @@ ys(ridx,:) = y(2:nr,:); have_line_spec = false; - for i = 1 : length (varargin) + for i = 1:2:numel (varargin) arg = varargin{i}; - if ((ischar (arg) || iscell (arg)) && ! have_line_spec) + if (ischar (arg) || iscell (arg)) [linespec, valid] = __pltopt__ ("stairs", arg, false); if (valid) have_line_spec = true; @@ -170,6 +164,27 @@ hold_state = get (gca (), "nextplot"); unwind_protect for i = 1 : columns (y) + + if (have_line_spec) + lc = linespec.color; + if (isempty (lc)) + lc = __next_line_color__ (); + endif + ls = linespec.linestyle; + if (isempty (ls)) + ls = "-"; + endif + mk = linespec.marker; + if (isempty (mk)) + mk = "none"; + endif + else + lc = __next_line_color__ (); + ls = "-"; + mk = "none"; + endif + + ## Must occur after __next_line_color__ in order to work correctly. hg = hggroup (); h = [h; hg]; args = __add_datasource__ ("stairs", hg, {"x", "y"}, varargin{:}); @@ -180,32 +195,27 @@ addlistener (hg, "xdata", @update_data); addlistener (hg, "ydata", @update_data); - if (have_line_spec) - htmp = line (xs(:,i).', ys(:,i).', "color", linespec.color, - "parent", hg); - else - htmp = line (xs(:,i).', ys(:,i).', "color", __next_line_color__ (), - "parent", hg); - endif + htmp = line (xs(:,i).', ys(:,i).', "color", lc, "linestyle", ls, + "marker", mk, "parent", hg); addproperty ("color", hg, "linecolor", get (htmp, "color")); + addproperty ("linestyle", hg, "linelinestyle", get (htmp, "linestyle")); addproperty ("linewidth", hg, "linelinewidth", get (htmp, "linewidth")); - addproperty ("linestyle", hg, "linelinestyle", get (htmp, "linestyle")); addproperty ("marker", hg, "linemarker", get (htmp, "marker")); + addproperty ("markeredgecolor", hg, "linemarkeredgecolor", + get (htmp, "markeredgecolor")); addproperty ("markerfacecolor", hg, "linemarkerfacecolor", get (htmp, "markerfacecolor")); - addproperty ("markeredgecolor", hg, "linemarkeredgecolor", - get (htmp, "markeredgecolor")); addproperty ("markersize", hg, "linemarkersize", get (htmp, "markersize")); addlistener (hg, "color", @update_props); + addlistener (hg, "linestyle", @update_props); addlistener (hg, "linewidth", @update_props); - addlistener (hg, "linestyle", @update_props); addlistener (hg, "marker", @update_props); + addlistener (hg, "markeredgecolor", @update_props); addlistener (hg, "markerfacecolor", @update_props); - addlistener (hg, "markeredgecolor", @update_props); addlistener (hg, "markersize", @update_props); if (! isempty (args)) @@ -221,57 +231,18 @@ endfunction - -%!demo -%! clf; -%! x = 1:10; -%! rand_1x10_data1 = [0.073, 0.455, 0.837, 0.124, 0.426, 0.781, 0.004, 0.024, 0.519, 0.698]; -%! y = rand_1x10_data1; -%! stairs (x, y); - -%!demo -%! clf; -%! x = 1:10; -%! rand_1x10_data2 = [0.014, 0.460, 0.622, 0.394, 0.531, 0.378, 0.466, 0.788, 0.342, 0.893]; -%! y = rand_1x10_data2; -%! [xs, ys] = stairs (x, y); -%! plot (xs, ys); - -%!demo -%! clf; -%! stairs (1:9); - -%!demo -%! clf; -%! [xs, ys] = stairs (9:-1:1); -%! plot (xs, ys); - -%!demo -%! clf; -%! N = 11; -%! x = 0:(N-1); -%! y = rand (1, N); -%! hs = stairs (x(1), y(1)); -%! axis ([1, N-1 0, 1]); -%! for k=2:N -%! set (hs, 'xdata', x(1:k), 'ydata', y(1:k)); -%! drawnow (); -%! pause (0.2); -%! end - - -function update_props (h, d) +function update_props (h, ~) set (get (h, "children"), "color", get (h, "color"), + "linestyle", get (h, "linestyle"), "linewidth", get (h, "linewidth"), - "linestyle", get (h, "linestyle"), "marker", get (h, "marker"), + "markeredgecolor", get (h, "markeredgecolor"), "markerfacecolor", get (h, "markerfacecolor"), - "markeredgecolor", get (h, "markeredgecolor"), "markersize", get (h, "markersize")); endfunction -function update_data (h, d) +function update_data (h, ~) x = get (h, "xdata"); y = get (h, "ydata"); @@ -298,3 +269,62 @@ set (get (h, "children"), "xdata", xs, "ydata", ys); endfunction + +%!demo +%! clf; +%! rand_1x10_data1 = [0.073, 0.455, 0.837, 0.124, 0.426, 0.781, 0.004, 0.024, 0.519, 0.698]; +%! y = rand_1x10_data1; +%! stairs (y); +%! title ('stairs() plot of y-data'); + +%!demo +%! clf; +%! x = 1:10; +%! rand_1x10_data2 = [0.014, 0.460, 0.622, 0.394, 0.531, 0.378, 0.466, 0.788, 0.342, 0.893]; +%! y = rand_1x10_data2; +%! [xs, ys] = stairs (x, y); +%! plot (xs, ys); +%! title ('plot() of stairs() generated data'); + +%!demo +%! clf; +%! stairs (1:9, '-o'); +%! title ('stairs() plot with linespec to modify marker'); + +%!demo +%! clf; +%! stairs (9:-1:1, 'marker', 's', 'markersize', 10, 'markerfacecolor', 'm'); +%! title ('stairs() plot with prop/val pairs to modify appearance'); + +%!demo +%! clf; +%! N = 11; +%! x = 0:(N-1); +%! y = rand (1, N); +%! hs = stairs (x(1), y(1)); +%! axis ([1, N-1 0, 1]); +%! title ('stairs plot data modified through handle'); +%! for k = 2:N +%! set (hs, 'xdata', x(1:k), 'ydata', y(1:k)); +%! drawnow (); +%! pause (0.2); +%! end + +## Invisible figure used for tests +%!shared hf, hax +%! hf = figure ("visible", "off"); +%! hax = axes; + +%!error stairs () +%!error stairs (hax, {1}) +%!error stairs (ones (2,2,2)) +%!error stairs ({1}, 1) +%!error stairs (1, {1}) +%!error stairs (ones (2,2,2), 1) +%!error stairs (1, ones (2,2,2)) +%!error stairs (1:2, 1:3) + +## Close figure used for testing +%!test +%! close (hf); + diff --git a/scripts/plot/stem.m b/scripts/plot/stem.m --- a/scripts/plot/stem.m +++ b/scripts/plot/stem.m @@ -57,11 +57,13 @@ ## If the first argument @var{hax} is an axes handle, then plot into this axis, ## rather than the current axes returned by @code{gca}. ## -## The optional return value @var{h} is a vector of "stem series" graphics -## handles with one handle per column of the variable @var{y}. The -## handle regroups the elements of the stem graph together as the -## children of the "stem series" handle, allowing them to be altered -## together. For example, +## The optional return value @var{h} is a handle to a "stem series" hggroup. +## The single hggroup handle has all of the graphical elements comprising the +## plot as its children; This allows the properties of multiple graphics +## objects to be changed by modifying just a single property of the +## "stem series" hggroup. +## +## For example, ## ## @example ## @group @@ -76,6 +78,40 @@ ## @noindent ## changes the color of the second "stem series" and moves the base line ## of the first. +## +## Stem Series Properties +## +## @table @asis +## @item linestyle +## The linestyle of the stem. (Default: @qcode{"-"}) +## +## @item linewidth +## The width of the stem. (Default: 0.5) +## +## @item color +## The color of the stem, and if not separately specified, the marker. +## (Default: "b" [blue]) +## +## @item marker +## The marker symbol to use at the top of each stem. (Default: @qcode{"o"}) +## +## @item markeredgecolor +## The edge color of the marker. (Default: @qcode{"color"} property) +## +## @item markerfacecolor +## The color to use for "filling" the marker. (Default: @qcode{"none"} +## [unfilled]) +## +## @item markersize +## The size of the marker. (Default: 6) +## +## @item baseline +## The handle of the line object which implements the baseline. Use @code{set} +## with the returned handle to change graphic properties of the baseline. +## +## @item basevalue +## The y-value where the baseline is drawn. (Default: 0) +## @end table ## @seealso{stem3, bar, hist, plot, stairs} ## @end deftypefn @@ -99,38 +135,51 @@ %!demo %! clf; -%! x = 1:10; -%! stem (x); +%! y = 1:10; +%! stem (y); +%! title ('stem plot of y-values only'); %!demo %! clf; %! x = 1:10; %! y = 2*x; %! stem (x, y); +%! title ('stem plot of x and y-values'); %!demo %! clf; %! x = 1:10; %! y = 2*x; %! h = stem (x, y, 'r'); +%! title ('stem plot with modified color'); %!demo %! clf; %! x = 1:10; %! y = 2*x; %! h = stem (x, y, '-.k'); +%! title ('stem plot with modified line style and color'); %!demo %! clf; %! x = 1:10; %! y = 2*x; -%! h = stem (x, y, '-.k.'); +%! h = stem (x, y, '-.ks'); +%! title ('stem plot with modified line style, color, and marker'); %!demo %! clf; %! x = 1:10; %! y = 2*x; %! h = stem (x, y, 'filled'); +%! title ('stem plot with "filled" markers'); + +%!demo +%! clf; +%! x = 1:10; +%! y = 2*x; +%! h = stem (x, y, 'markerfacecolor', [1 0 1]); +%! title ('stem plot modified with property/value pair'); %!demo %! clf; @@ -139,6 +188,7 @@ %! h = stem (x, y); %! set (h(2), 'color', 'g'); %! set (h(1), 'basevalue', -1); +%! title ('stem plots modified through hggroup handle'); %!demo %! clf; @@ -147,9 +197,19 @@ %! y = rand (1, N); %! hs = stem (x(1), y(1)); %! set (gca (), 'xlim', [1, N-1], 'ylim', [0, 1]); +%! title ('stem plot data modified through hggroup handle'); %! for k=2:N %! set (hs, 'xdata', x(1:k), 'ydata', y(1:k)) %! drawnow (); %! pause (0.2); %! end +%!error stem () +%!error stem (1,2,3) +%!error stem ({1}) +%!error stem (1, {1}) +%!error stem (1:2, 1:3) +%!error stem (1:2, ones (3,3)) +%!error stem (ones (2,2), ones (3,3)) +%!error stem (1, "FOO") + diff --git a/scripts/plot/stem3.m b/scripts/plot/stem3.m --- a/scripts/plot/stem3.m +++ b/scripts/plot/stem3.m @@ -39,8 +39,9 @@ ## If the first argument @var{hax} is an axes handle, then plot into this axis, ## rather than the current axes returned by @code{gca}. ## -## The optional return value @var{h} is a vector with the handles of the line -## and marker objects used to draw the stems as a "stem series" object. +## The optional return value @var{h} is a handle to the "stem series" hggroup +## containing the line and marker objects used for the plot. +## @xref{XREFstem,,stem}, for a description of the "stem series" object. ## ## Example: ## @@ -60,7 +61,7 @@ function h = stem3 (varargin) - if (nargin < 1 || nargin > 4) + if (nargin < 1) print_usage (); endif @@ -79,3 +80,12 @@ %! stem3 (cos (theta), sin (theta), theta); %! title ('stem3() plot'); +%!error stem3 () +%!error stem3 (1,2) +%!error stem3 ({1}, 1, 1) +%!error stem3 (1, {1}, 1) +%!error stem3 (1, 1, {1}) +%!error stem3 (ones (2,2), 1, 1); +%!error stem3 (1, ones (2,2), 1); +%!error stem3 (1, 1, ones (2,2)); +%!error stem3 (1, "FOO") diff --git a/scripts/plot/trimesh.m b/scripts/plot/trimesh.m --- a/scripts/plot/trimesh.m +++ b/scripts/plot/trimesh.m @@ -39,7 +39,9 @@ ## change the colormap to control the appearance. ## ## Optionally, the color of the mesh can be specified independently of @var{z} -## by supplying a color matrix, @var{c}. +## by supplying a color matrix, @var{c}. If @var{z} has N elements, then +## @var{c} should be an Nx1 vector for colormap data or an Nx3 matrix for +## RGB data. ## ## Any property/value pairs are passed directly to the underlying patch object. ## @@ -55,21 +57,39 @@ endif if (nargin == 3) - triplot (tri, x, y); + htmp = triplot (tri, x, y); elseif (ischar (z)) - triplot (tri, x, y, z, varargin{:}); + htmp = triplot (tri, x, y, z, varargin{:}); else + ## Process color argument + if (nargin > 4 && isnumeric (varargin{1})) + c = varargin{1}; + varargin(1) = []; + if (isvector (c)) + if (numel (c) != numel (z)) + error ("trimesh: C must have 'numel (Z)' elements"); + endif + c = c(:); + elseif (rows (c) != numel (z) || columns (c) != 3) + error ("trimesh: TrueColor C matrix must be 'numel (Z)' rows by 3 columns"); + endif + else + c = z(:); + endif + hax = newplot (); - handle = patch ("Vertices", [x(:), y(:), z(:)], "Faces", tri, - "FaceColor", "none", "EdgeColor", __next_line_color__ (), - varargin{:}); + + htmp = patch ("Vertices", [x(:), y(:), z(:)], "Faces", tri, + "FaceVertexCdata", c, "EdgeColor", "flat", "FaceColor", "w", + varargin{:}); if (! ishold ()) - set (hax, "view", [-37.5, 30], + set (hax, "view", [-37.5, 30], "box", "off", "xgrid", "on", "ygrid", "on", "zgrid", "on"); endif - if (nargout > 0) - h = handle; - endif + endif + + if (nargout > 0) + h = htmp; endif endfunction @@ -87,3 +107,12 @@ %! tri = delaunay (x(:), y(:)); %! trimesh (tri, x(:), y(:), z(:)); +%% Test input validation +%!error trimesh () +%!error trimesh (1) +%!error trimesh (1,2) +%!error trimesh (1,2,3,4,[5 6]) +%!error trimesh (1,2,3,4,[5 6]') +%!error trimesh ([1;1],[2;2],[3;3],[4;4],zeros(3,3)) +%!error trimesh ([1;1],[2;2],[3;3],[4;4],zeros(2,2)) + diff --git a/scripts/plot/trisurf.m b/scripts/plot/trisurf.m --- a/scripts/plot/trisurf.m +++ b/scripts/plot/trisurf.m @@ -37,7 +37,9 @@ ## change the colormap to control the appearance. ## ## Optionally, the color of the mesh can be specified independently of @var{z} -## by supplying a color matrix, @var{c}. +## by supplying a color matrix, @var{c}. If @var{z} has N elements, then +## @var{c} should be an Nx1 vector for colormap data or an Nx3 matrix for +## RGB data. ## ## Any property/value pairs are passed directly to the underlying patch object. ## @@ -48,43 +50,49 @@ function h = trisurf (tri, x, y, z, varargin) - if (nargin < 3) + if (nargin < 4) print_usage (); endif - if (nargin == 3) - triplot (tri, x, y); - elseif (ischar (z)) - triplot (tri, x, y, z, varargin{:}); - else - if (nargin > 4 && isnumeric (varargin{1})) - c = varargin{1}; - varargin(1) = []; - else - c = z; - endif - if (! any (strcmpi (varargin, "FaceColor"))) - nfc = numel (varargin) + 1; - varargin(nfc+(0:1)) = {"FaceColor", "flat"}; - else - nfc = find (any (strcmpi (varargin, "FaceColor")), 1); + if (nargin > 4 && isnumeric (varargin{1})) + c = varargin{1}; + varargin(1) = []; + if (isvector (c)) + if (numel (c) != numel (z)) + error ("trisurf: C must have 'numel (Z)' elements"); + endif + c = c(:); + elseif (rows (c) != numel (z) || columns (c) != 3) + error ("trisurf: TrueColor C matrix must be 'numel (Z)' rows by 3 columns"); endif - if (! any (strcmpi (varargin, "EdgeColor")) - && strcmpi (varargin{nfc+1}, "interp")) - varargin(end+(1:2)) = {"EdgeColor", "none"}; - endif - hax = newplot (); - htmp = patch ("Faces", tri, "Vertices", [x(:), y(:), z(:)], - "FaceVertexCData", reshape (c, numel (c), 1), - varargin{:}); - if (nargout > 0) - h = htmp; - endif + else + c = z(:); + endif + ## FIXME: Is all this extra input parsing necessary? + ## Is it for Matlab compatibility? + if (! any (strcmpi (varargin, "FaceColor"))) + nfc = numel (varargin) + 1; + varargin(nfc+(0:1)) = {"FaceColor", "flat"}; + else + nfc = find (any (strcmpi (varargin, "FaceColor")), 1); + endif + if (! any (strcmpi (varargin, "EdgeColor")) + && strcmpi (varargin{nfc+1}, "interp")) + varargin(end+(1:2)) = {"EdgeColor", "none"}; + endif - if (! ishold ()) - set (hax, "view", [-37.5, 30], - "xgrid", "on", "ygrid", "on", "zgrid", "on"); - endif + hax = newplot (); + + htmp = patch ("Faces", tri, "Vertices", [x(:), y(:), z(:)], + "FaceVertexCData", c, varargin{:}); + + if (! ishold ()) + set (hax, "view", [-37.5, 30], "box", "off", + "xgrid", "on", "ygrid", "on", "zgrid", "on"); + endif + + if (nargout > 0) + h = htmp; endif endfunction @@ -154,3 +162,13 @@ %! tri = delaunay (x, y); %! trisurf (tri, x, y, z, 'facecolor', 'interp', 'edgecolor', 'k'); +%% Test input validation +%!error trisurf () +%!error trisurf (1) +%!error trisurf (1,2) +%!error trisurf (1,2,3) +%!error trisurf (1,2,3,4,[5 6]) +%!error trisurf (1,2,3,4,[5 6]') +%!error trisurf ([1;1],[2;2],[3;3],[4;4],zeros(3,3)) +%!error trisurf ([1;1],[2;2],[3;3],[4;4],zeros(2,2)) +