# HG changeset patch # User Rik # Date 1641932236 28800 # Node ID 36d940c58c2ea5c71611290d06f09821e082daf4 # Parent f1cec1134dd1f6c4b4c35010418aaf26a194b557 Improve input validation for patch function (bug #61782) * patch.m: Avoid call to gca() before executing __patch__ as input validation may stop patch from being created leaving empty figure and axes objects. Add BIST tests for input validation. * __patch__.m: Remove second output of function (failed). Detect 3-input case with bad color specification and issue and emit an error. Add comments to clarify code about which input combination is being processed in long if/elseif tree. Call gca() if patch is validated but no axes has been specified. * fill.m: Re-write call to __patch__ to match new function with only one output. diff -r f1cec1134dd1 -r 36d940c58c2e scripts/plot/draw/fill.m --- a/scripts/plot/draw/fill.m Tue Jan 11 20:43:58 2022 +0100 +++ b/scripts/plot/draw/fill.m Tue Jan 11 12:17:16 2022 -0800 @@ -141,12 +141,9 @@ ## For Matlab compatibility, return 1 patch object for each column for j = 1 : columns (x) if (one_color) - [htmp, err] = __patch__ (hax, x(:,j), y(:,j), cdata, opts{:}); + htmp = __patch__ (hax, x(:,j), y(:,j), cdata, opts{:}); else - [htmp, err] = __patch__ (hax, x(:,j), y(:,j), cdata(:,j), opts{:}); - endif - if (err) - print_usage (); + htmp = __patch__ (hax, x(:,j), y(:,j), cdata(:,j), opts{:}); endif hlist(end+1, 1) = htmp; endfor diff -r f1cec1134dd1 -r 36d940c58c2e scripts/plot/draw/patch.m --- a/scripts/plot/draw/patch.m Tue Jan 11 20:43:58 2022 +0100 +++ b/scripts/plot/draw/patch.m Tue Jan 11 12:17:16 2022 -0800 @@ -28,7 +28,7 @@ ## @deftypefnx {} {} patch (@var{x}, @var{y}, @var{c}) ## @deftypefnx {} {} patch (@var{x}, @var{y}, @var{z}, @var{c}) ## @deftypefnx {} {} patch ("Faces", @var{faces}, "Vertices", @var{verts}, @dots{}) -## @deftypefnx {} {} patch (@dots{}, @var{prop}, @var{val}, @dots{}) +## @deftypefnx {} {} patch (@dots{}, "@var{prop}", @var{val}, @dots{}) ## @deftypefnx {} {} patch (@dots{}, @var{propstruct}, @dots{}) ## @deftypefnx {} {} patch (@var{hax}, @dots{}) ## @deftypefnx {} {@var{h} =} patch (@dots{}) @@ -82,17 +82,11 @@ [hax, varargin] = __plt_get_axis_arg__ ("patch", varargin{:}); - if (isempty (hax)) - hax = gca (); - else + if (! isempty (hax)) hax = hax(1); - endif + endif - [htmp, failed] = __patch__ (hax, varargin{:}); - - if (failed) - print_usage (); - endif + htmp = __patch__ (hax, varargin{:}); if (nargout > 0) h = htmp; @@ -308,3 +302,9 @@ %! unwind_protect_cleanup %! close (hf); %! end_unwind_protect + +## Test input validation +%!error patch (1, 1, 'x') +%!error patch (1, 1, rand (1,2,3)) +%!error patch (1, 1, [1, 2]) +%!error patch (1, 1, {1}) diff -r f1cec1134dd1 -r 36d940c58c2e scripts/plot/draw/private/__patch__.m --- a/scripts/plot/draw/private/__patch__.m Tue Jan 11 20:43:58 2022 +0100 +++ b/scripts/plot/draw/private/__patch__.m Tue Jan 11 12:17:16 2022 -0800 @@ -32,153 +32,154 @@ ## Create patch object from x and y with color c and parent p. ## Return handle to patch object. -function [h, failed] = __patch__ (p, varargin) +function h = __patch__ (p, varargin) h = NaN; - failed = false; + nargin = nargin - 1; is_numeric_arg = cellfun (@isnumeric, varargin); if (isempty (varargin)) - args = varargin; + args = {}; elseif (is_numeric_arg(1)) if (nargin < 3 || ! is_numeric_arg(2)) - failed = true; - else - if (nargin > 4 && all (is_numeric_arg(1:4))) - x = varargin{1}; - y = varargin{2}; + print_usage ("patch"); + endif + + if (nargin >= 4 && all (is_numeric_arg(1:4))) + x = varargin{1}; + y = varargin{2}; + z = varargin{3}; + c = varargin{4}; + iarg = 5; + elseif (nargin >= 3 && all (is_numeric_arg(1:3))) + x = varargin{1}; + y = varargin{2}; + if (nargin > 3 && iscolorspec (varargin{4})) z = varargin{3}; c = varargin{4}; iarg = 5; - elseif (nargin > 3 && all (is_numeric_arg(1:3))) - x = varargin{1}; - y = varargin{2}; - if (nargin > 4 && iscolorspec (varargin{4})) - z = varargin{3}; - c = varargin{4}; - iarg = 5; - else - z = []; - c = varargin{3}; - iarg = 4; - endif - elseif (nargin > 2 && all (is_numeric_arg(1:2))) - x = varargin{1}; - y = varargin{2}; + else z = []; - if (iscolorspec (varargin{3})) - c = varargin{3}; - iarg = 4; - else - c = []; - iarg = 3; - endif + c = varargin{3}; + iarg = 4; endif + elseif (nargin >= 3 && all (is_numeric_arg(1:2))) + x = varargin{1}; + y = varargin{2}; + z = []; + if (iscolorspec (varargin{3})) + c = varargin{3}; + iarg = 4; + elseif (nargin == 3) + error ("patch: invalid color specification C"); + else + c = []; + iarg = 3; + endif + endif - if (isvector (x)) - x = x(:); - y = y(:); - z = z(:); - if (isnumeric (c)) - if (isvector (c)) - if (isequal (size (c), [1, 3])) - ## Do nothing, this is a single RGB color specification - elseif (numel (c) == numel (x)) - c = c(:); - endif - elseif (rows (c) != numel (x) && columns (c) == numel (x)) - c = c.'; + if (isvector (x)) + x = x(:); + y = y(:); + z = z(:); + if (isnumeric (c)) + if (isvector (c)) + if (isequal (size (c), [1, 3])) + ## Do nothing, this is a single RGB color specification + elseif (numel (c) == numel (x)) + c = c(:); endif + elseif (rows (c) != numel (x) && columns (c) == numel (x)) + c = c.'; endif endif - args{1} = "xdata"; - args{2} = x; - args{3} = "ydata"; - args{4} = y; - args{5} = "zdata"; - args{6} = z; + endif + args{1} = "xdata"; + args{2} = x; + args{3} = "ydata"; + args{4} = y; + args{5} = "zdata"; + args{6} = z; - if (isnumeric (c)) + if (isnumeric (c) && ! isempty (c)) - if (ndims (c) == 3 && columns (c) == 1) - c = permute (c, [1, 3, 2]); - endif + if (ndims (c) == 3 && columns (c) == 1) + c = permute (c, [1, 3, 2]); + endif - if (isvector (c) && numel (c) == columns (x)) - if (isnan (c)) - args{7} = "facecolor"; - args{8} = [1, 1, 1]; - args{9} = "cdata"; - args{10} = c; - elseif (isnumeric (c)) - args{7} = "facecolor"; - args{8} = "flat"; - args{9} = "cdata"; - args{10} = c; - else - error ("patch: color data C must be numeric"); - endif - elseif (isrow (c) && numel (c) == 3) + if (isvector (c) && numel (c) == columns (x)) + ## One color per face + if (isnan (c)) + args{7} = "facecolor"; + args{8} = [1, 1, 1]; + else args{7} = "facecolor"; - args{8} = c; + args{8} = "flat"; + endif + args{9} = "cdata"; + args{10} = c; + elseif (isrow (c) && numel (c) == 3) + ## One RGB color + args{7} = "facecolor"; + args{8} = c; + args{9} = "cdata"; + args{10} = []; + elseif (ndims (c) == 3 && size (c, 3) == 3) + ## CDATA is specified as 3-D RGB data + if ((rows (c) == 1 && columns (c) == 1) ... + || (rows (c) == 1 && columns (c) == columns (x))) + ## Single patch color or per-face color + args{7} = "facecolor"; + args{8} = "flat"; + args{9} = "cdata"; + args{10} = c; + elseif (rows (c) == rows (x) && columns (c) == columns (x)) + ## Per-vertex color + args{7} = "facecolor"; + args{8} = "interp"; + args{9} = "cdata"; + args{10} = c; + else + error ("patch: invalid TrueColor data C"); + endif + else + ## Color vectors + if (isempty (c)) + args{7} = "facecolor"; + args{8} = "interp"; args{9} = "cdata"; args{10} = []; - elseif (ndims (c) == 3 && size (c, 3) == 3) - ## CDATA is specified as RGB data - if ((rows (c) == 1 && columns (c) == 1) ... - || (rows (c) == 1 && columns (c) == columns (x))) - ## Single patch color or per-face color - args{7} = "facecolor"; - args{8} = "flat"; - args{9} = "cdata"; - args{10} = c; - elseif (rows (c) == rows (x) && columns (c) == columns (x)) - ## Per-vertex color - args{7} = "facecolor"; - args{8} = "interp"; - args{9} = "cdata"; - args{10} = c; - else - error ("patch: Invalid TrueColor data C"); - endif + elseif (size_equal (c, x) && size_equal (c, y)) + args{7} = "facecolor"; + args{8} = "interp"; + args{9} = "cdata"; + args{10} = c; else - ## Color Vectors - if (isempty (c)) - args{7} = "facecolor"; - args{8} = "interp"; - args{9} = "cdata"; - args{10} = []; - elseif (size_equal (c, x) && size_equal (c, y)) - args{7} = "facecolor"; - args{8} = "interp"; - args{9} = "cdata"; - args{10} = c; - else - error ("patch: size of X, Y, and C must be equal"); - endif + error ("patch: size of X, Y, and C must be equal"); endif - elseif (iscolorspec (c)) - args{7} = "facecolor"; - args{8} = tolower (c); - args{9} = "cdata"; - args{10} = []; - else - args{7} = "facecolor"; - args{8} = [0, 0, 0]; - args{9} = "cdata"; - args{10} = []; endif + elseif (iscolorspec (c)) + ## Color specification is a string + args{7} = "facecolor"; + args{8} = tolower (c); + args{9} = "cdata"; + args{10} = []; + elseif (! isempty (c)) + error ("patch: invalid color specification C"); + endif - args = [args, varargin(iarg:end)]; - endif + args = [args, varargin(iarg:end)]; + else + ## "Property"/Value pair input args = varargin; endif - if (! failed) - h = __go_patch__ (p, args{:}); + if (isempty (p)) + p = gca (); endif + h = __go_patch__ (p, args{:}); endfunction