# HG changeset patch # User Rik # Date 1382025530 25200 # Node ID ddfc1600a31185f393d418328b3562c483a5300f # Parent ea9df126c9a52fd5350eff01d412cab143ac8a48 Overhaul quiver/quiver3 functions. * scripts/plot/draw/private/__quiver__.m: Scale arrowsize parameter internally from 0.2 to 1/3 to match Matlab visual results. Assume meshgridded input for 2-D inputs. For vectors, assume a square grid with side length = sqrt (numel (x)). Use __next_line_color__, rather than assuming 'k', if no colorspec is given. Ignore second input to listeners with '~'. Set multiple graphic properties at onec to reduce overhead in calling get/set. * scripts/plot/draw/quiver.m: Update docstring. Replace 'retval' with 'h' to match function documentation. Use common unwind_protect sequence with call to newplot. Add titles to %!demos. * scripts/plot/draw/quiver3.m: Update docstring. Replace 'retval' with 'h' to match function documentation. Use common unwind_protect sequence with call to newplot. Add titles to %!demos. diff -r ea9df126c9a5 -r ddfc1600a311 scripts/plot/draw/private/__quiver__.m --- a/scripts/plot/draw/private/__quiver__.m Wed Oct 16 15:24:40 2013 -0700 +++ b/scripts/plot/draw/private/__quiver__.m Thu Oct 17 08:58:50 2013 -0700 @@ -27,15 +27,19 @@ is3d = varargin{2}; autoscale = 0.9; - arrowsize = 0.2; + ## Matlab uses 0.2, but Octave's algorithm produces equivalent visual + ## results if arrowsize=0.33. Since this is just a non-dimensional + ## scaling factor we scale the arrowsize property value by 0.33/0.20 + ## in order to get equivalent visual results while keeping equivalent + ## property values. + arrowsize = 0.20; - firstnonnumeric = Inf; - for i = 3:nargin - if (! isnumeric (varargin{i})) - firstnonnumeric = i; - break; - endif - endfor + firstnonnumeric = find (! cellfun ("isnumeric", varargin(3:nargin)), 1); + if (isempty (firstnonnumeric)) + firstnonnumeric = Inf; + else + firstnonnumeric += 2; + endif ioff = 3; if (nargin < (6 + is3d) || firstnonnumeric < (6 + is3d)) @@ -88,34 +92,37 @@ [linespec, valid] = __pltopt__ ("quiver", arg, false); if (valid) have_line_spec = true; - if (strcmp (linespec.linestyle, "none")) + if (isempty (linespec.linestyle) || strcmp (linespec.linestyle, "none")) linespec.linestyle = "-"; endif else - args {end + 1} = arg; + args{end+1} = arg; if (ioff <= nargin) - args {end + 1} = varargin{ioff++}; + args{end+1} = varargin{ioff++}; endif endif else - args {end + 1} = arg; + args{end+1} = arg; if (ioff <= nargin) - args {end + 1} = varargin{ioff++}; + args{end+1} = varargin{ioff++}; endif endif endwhile + ## Normalize 0.20 to 1/3 for plotting + arrowsize /= 0.20 * 3; + if (autoscale && numel (u) > 1) ## Scale the arrows to fit in the grid if (isvector (x)) - ny = nx = length (x); + nx = ny = sqrt (length (x)); else - [nx, ny] = size (x); + [ny, nx] = size (x); # assume meshgrid fmt, x in columns, y in rows endif - dx = (max (x(:)) - min (x(:))) ./ nx; - dy = (max (y(:)) - min (y(:))) ./ ny; + dx = (max (x(:)) - min (x(:))) / nx; + dy = (max (y(:)) - min (y(:))) / ny; if (is3d) - dz = (max (z(:)) - min (z(:))) ./ max (size (z)); + dz = (max (z(:)) - min (z(:))) / max (nx, ny); len = max (sqrt (u(:).^2 + v(:).^2 + w(:).^2)); else dz = 0; @@ -124,14 +131,14 @@ if (len > 0) sd = sqrt (dx.^2 + dy.^2 + dz.^2) / len; if (sd != 0) - s = sqrt (2) * autoscale * sd; - else # special case of identical points with multiple vectors + s = autoscale * sd; + else # special case of identical points with multiple vectors s = autoscale; endif uu = s * u; vv = s * v; if (is3d) - ww = s*w; + ww = s * w; endif endif else @@ -144,6 +151,16 @@ hstate = get (h, "nextplot"); unwind_protect + + if (have_line_spec) + ls = linespec.linestyle; + lc = linespec.color; + else + ls = "-"; + lc = __next_line_color__ (); + endif + + ## Must occur after __next_line_color__ in order to work correctly. hg = hggroup (); if (is3d) args = __add_datasource__ ("quiver3", hg, @@ -183,105 +200,64 @@ zend = z + ww(:); endif + ## Draw arrow shaft as one line object + if (is3d) + h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), + [y.'; yend.'; NaN(1, length (y))](:), + [z.'; zend.'; NaN(1, length (z))](:), + "linestyle", ls, "color", lc, "parent", hg); + else + h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), + [y.'; yend.'; NaN(1, length (y))](:), + "linestyle", ls, "color", lc, "parent", hg); + endif + + xtmp = x + uu(:) * (1 - arrowsize); + ytmp = y + vv(:) * (1 - arrowsize); + + if (is3d) + xydist = sqrt (uu(:).^2 + vv(:).^2 + ww(:).^2) ./ ... + (sqrt (uu(:).^2 + vv(:).^2) + eps); + xarrw1 = xtmp + vv(:) .* xydist * arrowsize / 4; + xarrw2 = xtmp - vv(:) .* xydist * arrowsize / 4; + yarrw1 = ytmp - uu(:) .* xydist * arrowsize / 4; + yarrw2 = ytmp + uu(:) .* xydist * arrowsize / 4; + zarrw1 = zarrw2 = zend - ww(:) * arrowsize; + else + xarrw1 = xtmp + vv(:) * arrowsize / 3; + xarrw2 = xtmp - vv(:) * arrowsize / 3; + yarrw1 = ytmp - uu(:) * arrowsize / 3; + yarrw2 = ytmp + uu(:) * arrowsize / 3; + endif + + ## Draw arrowhead as one line object if (have_line_spec) - if (is3d) - h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - [z.'; zend.'; NaN(1, length (z))](:), - "linestyle", linespec.linestyle, - "color", linespec.color, "parent", hg); - else - h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - "linestyle", linespec.linestyle, - "color", linespec.color, "parent", hg); - endif - else - if (is3d) - h1 = plot3 ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - [z.'; zend.'; NaN(1, length (z))](:), - "color", "black", "parent", hg); - else - h1 = plot ([x.'; xend.'; NaN(1, length (x))](:), - [y.'; yend.'; NaN(1, length (y))](:), - "parent", hg); + if (! isempty (linespec.marker) && ! strcmp (linespec.marker, "none")) + ls = "none"; # No arrowhead drawn when marker present endif endif - xtmp = x + uu(:) .* (1 - arrowsize); - ytmp = y + vv(:) .* (1 - arrowsize); - if (is3d) - xarrw1 = xtmp + sqrt((y - yend).^2 + (z - zend).^2) * arrowsize / 3; - xarrw2 = xtmp - sqrt((y - yend).^2 + (z - zend).^2) * arrowsize / 3; - yarrw1 = ytmp - sqrt((x - xend).^2 + (z - zend).^2) * arrowsize / 3; - yarrw2 = ytmp + sqrt((x - xend).^2 + (z - zend).^2) * arrowsize / 3; - - zarrw1 = zarrw2 = zend - ww(:) * arrowsize; - else - xarrw1 = xtmp + (y - yend) * arrowsize / 3; - xarrw2 = xtmp - (y - yend) * arrowsize / 3; - yarrw1 = ytmp - (x - xend) * arrowsize / 3; - yarrw2 = ytmp + (x - xend) * arrowsize / 3; - endif - - if (have_line_spec) - if (isfield (linespec, "marker") - && ! strcmp (linespec.marker, "none")) - if (is3d) - h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), - "linestyle", "none", "parent", hg); - else - h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - "linestyle", "none", "parent", hg); - endif - else - if (is3d) - h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), - "linestyle", linespec.linestyle, - "color", linespec.color, "parent", hg); - else - h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), - [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - "linestyle", linespec.linestyle, - "color", linespec.color, "parent", hg); - endif - endif - elseif (is3d) h2 = plot3 ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:), - "color", "black", "parent", hg); + "linestyle", ls, "color", lc, "parent", hg); else h2 = plot ([xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:), [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:), - "parent", hg); + "linestyle", ls, "color", lc, "parent", hg); endif - if (! have_line_spec - || (isfield (linespec, "marker") - && strcmp (linespec.marker, "none"))) - if (is3d) - h3 = plot3 (x, y, z, "linestyle", "none", "marker", "none", - "parent", hg); - else - h3 = plot (x, y, "linestyle", "none", "marker", "none", "parent", hg); - endif + ## Draw arrow base marker as a third line object + if (! have_line_spec || isempty (linespec.marker)) + mk = "none"; else - if (is3d) - h3 = plot3 (x, y, z, "linestyle", "none", "marker", linespec.marker, - "parent", hg); - else - - h3 = plot (x, y, "linestyle", "none", "marker", linespec.marker, - "parent", hg); - endif + mk = linespec.marker; + endif + if (is3d) + h3 = plot3 (x, y, z, "linestyle", "none", "marker", mk, "parent", hg); + else + h3 = plot (x, y, "linestyle", "none", "marker", mk, "parent", hg); endif if (have_filled) ## FIXME: gnuplot doesn't respect the markerfacecolor field @@ -299,23 +275,23 @@ addlistener (hg, "autoscale", @update_data); addlistener (hg, "autoscalefactor", @update_data); - addproperty ("maxheadsize", hg, "data", arrowsize); + addproperty ("maxheadsize", hg, "data", arrowsize * .20*3); addlistener (hg, "maxheadsize", @update_data); addproperty ("showarrowhead", hg, "radio", "{on}|off", "on"); addlistener (hg, "showarrowhead", @update_props); addproperty ("color", hg, "linecolor", get (h1, "color")); + addproperty ("linestyle", hg, "linelinestyle", get (h1, "linestyle")); addproperty ("linewidth", hg, "linelinewidth", get (h1, "linewidth")); - addproperty ("linestyle", hg, "linelinestyle", get (h1, "linestyle")); addproperty ("marker", hg, "linemarker", get (h3, "marker")); addproperty ("markerfacecolor", hg, "linemarkerfacecolor", get (h3, "markerfacecolor")); addproperty ("markersize", hg, "linemarkersize", get (h3, "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, "markerfacecolor", @update_props); addlistener (hg, "markersize", @update_props); @@ -332,7 +308,8 @@ endfunction -function update_data (h, d) +function update_data (h, ~) + x = get (h, "xdata"); y = get (h, "ydata"); z = get (h, "zdata"); @@ -343,6 +320,7 @@ s = get (h, "autoscalefactor"); arrowsize = get (h, "maxheadsize"); + arrowsize /= 0.20 * 3; kids = get (h, "children"); @@ -352,17 +330,17 @@ is3d = true; endif - if (strcmpi (get (h, "autoscale"), "on") && s != 0) + if (strcmp (get (h, "autoscale"), "on") && s != 0) ## Scale the arrows to fit in the grid if (isvector (x)) - ny = nx = length (x); + nx = ny = sqrt (length (x)); else - [nx, ny] = size (x); + [ny, nx] = size (x); endif - dx = (max (x(:)) - min (x(:))) ./ nx; - dy = (max (y(:)) - min (y(:))) ./ ny; + dx = (max (x(:)) - min (x(:))) / nx; + dy = (max (y(:)) - min (y(:))) / ny; if (is3d) - dz = (max (z(:)) - min (z(:))) ./ max (size (z)); + dz = (max (z(:)) - min (z(:))) / max (nx, ny); len = max (sqrt (u(:).^2 + v(:).^2 + w(:).^2)); else dz = 0; @@ -371,12 +349,12 @@ if (len > 0) sd = sqrt (dx.^2 + dy.^2 + dz.^2) / len; if (sd != 0) - s *= sqrt (2) * sd; + s *= sd; endif u = s * u; v = s * v; if (is3d) - w = s*w; + w = s * w; endif endif endif @@ -390,59 +368,57 @@ zend = z + w(:); endif - set (kids (3), "xdata", [x.'; xend.'; NaN(1, length (x))](:)); - set (kids (3), "ydata", [y.'; yend.'; NaN(1, length (y))](:)); + set (kids(3), "xdata", [x.'; xend.'; NaN(1, length (x))](:)); + set (kids(3), "ydata", [y.'; yend.'; NaN(1, length (y))](:)); if (is3d) - set (kids (3), "zdata", [z.'; zend.'; NaN(1, length (z))](:)); - endif - - xtmp = x + u(:) .* (1 - arrowsize); - ytmp = y + v(:) .* (1 - arrowsize); - xarrw1 = xtmp + (y - yend) * arrowsize / 3; - xarrw2 = xtmp - (y - yend) * arrowsize / 3; - yarrw1 = ytmp - (x - xend) * arrowsize / 3; - yarrw2 = ytmp + (x - xend) * arrowsize / 3; - if (is3d) - zarrw1 = zarrw2 = zend - w(:) * arrowsize; + set (kids(3), "zdata", [z.'; zend.'; NaN(1, length (z))](:)); endif - set (kids (2), "xdata", [x.'; xend.'; NaN(1, length (x))](:)); - set (kids (2), "ydata", [y.'; yend.'; NaN(1, length (y))](:)); + xtmp = x + u(:) * (1 - arrowsize); + ytmp = y + v(:) * (1 - arrowsize); + if (is3d) - set (kids (2), "zdata", [z.'; zend.'; NaN(1, length (z))](:)); + xydist = sqrt (u(:).^2 + v(:).^2 + w(:).^2) ./ ... + (sqrt (u(:).^2 + v(:).^2) + eps); + xarrw1 = xtmp + v(:) .* xydist * arrowsize / 4; + xarrw2 = xtmp - v(:) .* xydist * arrowsize / 4; + yarrw1 = ytmp - u(:) .* xydist * arrowsize / 4; + yarrw2 = ytmp + u(:) .* xydist * arrowsize / 4; + zarrw1 = zarrw2 = zend - w(:) * arrowsize; + else + xarrw1 = xtmp + v(:) * arrowsize / 3; + xarrw2 = xtmp - v(:) * arrowsize / 3; + yarrw1 = ytmp - u(:) * arrowsize / 3; + yarrw2 = ytmp + u(:) * arrowsize / 3; endif - set (kids (2), "xdata", [xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:)); - set (kids (2), "ydata", [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:)); + set (kids(2), "xdata", [x.'; xend.'; NaN(1, length (x))](:)); + set (kids(2), "ydata", [y.'; yend.'; NaN(1, length (y))](:)); if (is3d) - set (kids (2), "zdata", [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:)); + set (kids(2), "zdata", [z.'; zend.'; NaN(1, length (z))](:)); endif - set (kids (1), "xdata", x); - set (kids (1), "ydata", y); + set (kids(2), "xdata", [xarrw1.'; xend.'; xarrw2.'; NaN(1, length (x))](:)); + set (kids(2), "ydata", [yarrw1.'; yend.'; yarrw2.'; NaN(1, length (y))](:)); if (is3d) - set (kids (1), "zdata", z); + set (kids(2), "zdata", [zarrw1.'; zend.'; zarrw2.'; NaN(1, length (z))](:)); + endif + + set (kids(1), "xdata", x); + set (kids(1), "ydata", y); + if (is3d) + set (kids(1), "zdata", z); endif endfunction -function update_props (h, d) +function update_props (h, ~) kids = get (h, "children"); - set (kids(3), "color", get (h, "color"), - "linewidth", get (h, "linewidth"), - "linestyle", get (h, "linestyle")); - set (kids(2), "color", get (h, "color"), - "linewidth", get (h, "linewidth"), - "linestyle", get (h, "linestyle")); - if (strcmpi (get (h, "showarrowhead"), "on")) - set (kids (2), "visible", "on"); - else - set (kids (2), "visible", "off"); - endif - set (kids(1), "color", get (h, "color"), - "marker", get (h, "marker"), - "markerfacecolor", get (h, "markerfacecolor"), - "markersize", get (h, "markersize")); + set (kids([3 2]), {"color", "linestyle", "linewidth"}, + get (h, {"color", "linestyle", "linewidth"})); + set (kids(2), "visible", get (h, "showarrowhead")); + set (kids(1), {"color", "marker", "markerfacecolor", "markersize"}, + get (h, {"color", "marker", "markerfacecolor", "markersize"})); endfunction diff -r ea9df126c9a5 -r ddfc1600a311 scripts/plot/draw/quiver.m --- a/scripts/plot/draw/quiver.m Wed Oct 16 15:24:40 2013 -0700 +++ b/scripts/plot/draw/quiver.m Thu Oct 17 08:58:50 2013 -0700 @@ -35,13 +35,13 @@ ## ## The variable @var{s} is a scalar defining a scaling factor to use for ## the arrows of the field relative to the mesh spacing. A value of 0 -## disables all scaling. The default value is 1. +## disables all scaling. The default value is 0.9. ## ## The style to use for the plot can be defined with a line style @var{style} ## of the same format as the @code{plot} command. ## If a marker is specified then markers at the grid points of the vectors are -## drawn rather than arrows. If the argument @qcode{"filled"} is given then the -## markers are filled. +## drawn rather than arrows. If the argument @qcode{"filled"} is given then +## the markers are filled. ## ## If the first argument @var{hax} is an axes handle, then plot into this axis, ## rather than the current axes returned by @code{gca}. @@ -50,6 +50,8 @@ ## A quiver object regroups the components of the quiver plot (body, arrow, ## and marker), and allows them to be changed together. ## +## Example: +## ## @example ## @group ## [x, y] = meshgrid (1:2:20); @@ -61,29 +63,29 @@ ## @seealso{quiver3, compass, feather, plot} ## @end deftypefn -function retval = quiver (varargin) +function h = quiver (varargin) [hax, varargin, nargin] = __plt_get_axis_arg__ ("quiver", varargin{:}); if (nargin < 2) print_usage (); - else + endif + oldfig = []; if (! isempty (hax)) oldfig = get (0, "currentfigure"); endif - unwind_protect - hax = newplot (hax); - htmp = __quiver__ (hax, false, varargin{:}); - unwind_protect_cleanup + unwind_protect + hax = newplot (hax); + htmp = __quiver__ (hax, false, varargin{:}); + unwind_protect_cleanup if (! isempty (oldfig)) set (0, "currentfigure", oldfig); endif - end_unwind_protect - endif + end_unwind_protect if (nargout > 0) - retval = htmp; + h = htmp; endif endfunction @@ -93,14 +95,15 @@ %! clf; %! [x,y] = meshgrid (1:2:20); %! h = quiver (x,y, sin (2*pi*x/10), sin (2*pi*y/10)); -%! set (h, 'maxheadsize', 0.33); +%! title ('quiver plot') %!demo %! clf; -%! axis ('equal'); %! x = linspace (0, 3, 80); %! y = sin (2*pi*x); %! theta = 2*pi*x + pi/2; -%! quiver (x, y, sin (theta)/10, cos (theta)/10); +%! quiver (x, y, sin (theta)/10, cos (theta)/10, 0.4); +%! axis equal tight; %! hold on; plot (x,y,'r'); hold off; +%! title ('quiver() with scaled arrows'); diff -r ea9df126c9a5 -r ddfc1600a311 scripts/plot/draw/quiver3.m --- a/scripts/plot/draw/quiver3.m Wed Oct 16 15:24:40 2013 -0700 +++ b/scripts/plot/draw/quiver3.m Thu Oct 17 08:58:50 2013 -0700 @@ -35,7 +35,7 @@ ## ## The variable @var{s} is a scalar defining a scaling factor to use for ## the arrows of the field relative to the mesh spacing. A value of 0 -## disables all scaling. The default value is 1. +## disables all scaling. The default value is 0.9. ## ## The style to use for the plot can be defined with a line style @var{style} ## of the same format as the @code{plot} command. @@ -64,34 +64,34 @@ ## @seealso{quiver, compass, feather, plot} ## @end deftypefn -function retval = quiver3 (varargin) +function h = quiver3 (varargin) [hax, varargin, nargin] = __plt_get_axis_arg__ ("quiver3", varargin{:}); if (nargin < 2) print_usage (); - else + endif + oldfig = []; if (! isempty (hax)) oldfig = get (0, "currentfigure"); endif - unwind_protect - hax = newplot (hax); - htmp = __quiver__ (hax, true, varargin{:}); + unwind_protect + hax = newplot (hax); + htmp = __quiver__ (hax, true, varargin{:}); - if (! ishold (hax)) - set (hax, "view", [-37.5, 30], "box", "off", - "xgrid", "on", "ygrid", "on", "zgrid", "on"); - endif - unwind_protect_cleanup - if (! isempty (oldfig)) - set (0, "currentfigure", oldfig); - endif - end_unwind_protect - endif + if (! ishold (hax)) + set (hax, "view", [-37.5, 30], "box", "off", + "xgrid", "on", "ygrid", "on", "zgrid", "on"); + endif + unwind_protect_cleanup + if (! isempty (oldfig)) + set (0, "currentfigure", oldfig); + endif + end_unwind_protect if (nargout > 0) - retval = htmp; + h = htmp; endif endfunction @@ -100,24 +100,14 @@ %!demo %! clf; %! colormap ('default'); -%! [x,y] = meshgrid (-1:0.1:1); -%! z = sin (2*pi * sqrt (x.^2 + y.^2)); -%! theta = 2*pi * sqrt (x.^2 + y.^2) + pi/2; -%! mesh (x, y, z); -%! hold on; -%! quiver3 (x, y, z, sin (theta), cos (theta), ones (size (z))); -%! hold off; - -%!demo -%! clf; -%! colormap ('default'); %! [x, y, z] = peaks (25); %! surf (x, y, z); %! hold on; %! [u, v, w] = surfnorm (x, y, z / 10); %! h = quiver3 (x, y, z, u, v, w); -%! set (h, 'maxheadsize', 0.33); +%! set (h, 'maxheadsize', 0.25); %! hold off; +%! title ('quiver3 of surface normals to peaks() function'); %!demo %! clf; @@ -127,7 +117,9 @@ %! hold on; %! [u, v, w] = surfnorm (x, y, z / 10); %! h = quiver3 (x, y, z, u, v, w); -%! set (h, 'maxheadsize', 0.33); +%! set (h, 'maxheadsize', 0.25); %! hold off; %! shading interp; +%! title ({'quiver3 of surface normals to peaks() function'; ... +%! 'shading "interp"'});