changeset 27810:f2b89a2e20b6

overhaul streamXXX.m series of functions. * stream-euler.cc (__streameuler3d__, __streameuler2d__): Use @code{} macro in docstring. * stream2.m, stream3.m, streamline.m, streamtube.m: Match names in documentation with variable names in code. Don't use double quotes around "options" input which is not a string input. Use numel() in preference to length(). Don't surround condition of case statement with parentheses. Use size_equal() function to simplify input validation. Avoid unnecessary use of logical(). Use two newlines after "endfunction" and start of %!demo blocks. Add BIST tests for input validation.
author Rik <rik@octave.org>
date Thu, 12 Dec 2019 14:50:17 -0800
parents b6db1c7c4595
children 392c03df4565
files libinterp/corefcn/stream-euler.cc scripts/plot/draw/stream2.m scripts/plot/draw/stream3.m scripts/plot/draw/streamline.m scripts/plot/draw/streamtube.m
diffstat 5 files changed, 263 insertions(+), 173 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/stream-euler.cc	Thu Dec 12 20:35:09 2019 +0100
+++ b/libinterp/corefcn/stream-euler.cc	Thu Dec 12 14:50:17 2019 -0800
@@ -57,7 +57,7 @@
   double x, y;
 } Vector2;
 
-// The integer- and the fractional value from a point in C-Space.
+// The integer value and the fractional value from a point in C-Space.
 // Equivalent to the cell index the point is located in and the local
 // coordinates of the point in the cell.
 
@@ -503,10 +503,11 @@
 DEFUN (__streameuler2d__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {} __streameuler2d__ (@var{U}, @var{V}, @var{TX}, @var{TY}, @var{ZETA}, @var{XI}, @var{H}, @var{MAXNVERTS})
-Calculates the streamline in a vector field [@var{U}, @var{V}] starting from a
-seed point at position [@var{ZETA}, @var{XI}].  The integrator used is
-Heun's Scheme.  The step size can be controlled by @var{H}.  The Jacobian
-matrix can be defined for each grid cell by [@var{TX}, @var{TY}].
+Calculates the streamline in a vector field @code{[@var{U}, @var{V}]} starting
+from a seed point at position @code{[@var{ZETA}, @var{XI}]}.  The integrator
+used is Heun's Scheme.  The step size can be controlled by @var{H}.  The
+Jacobian matrix can be defined for each grid cell by
+@code{[@var{TX}, @var{TY}]}.
 
 @seealso{streamline, stream2, stream3, __streameuler3d__}
 @end deftypefn */)
@@ -517,11 +518,11 @@
 DEFUN (__streameuler3d__, args, ,
        doc: /* -*- texinfo -*-
 @deftypefn {} {} __streameuler3d__ (@var{U}, @var{V}, @var{W}, @var{TX}, @var{TY}, @var{TZ}, @var{ZETA}, @var{XI}, @var{RHO}, @var{H}, @var{MAXNVERTS})
-Calculates the streamline in a vector field [@var{U}, @var{V}, @var{W}]
-starting from a seed point at position [@var{ZETA}, @var{XI}, @var{RHO}].  The
-integrator used is Heun's Scheme.  The step size can be controlled by @var{H}.
- The Jacobian matrix can be defined for each grid cell by [@var{TX}, @var{TY},
-@var{TZ}].
+Calculates the streamline in a vector field @code{[@var{U}, @var{V}, @var{W}]}
+starting from a seed point at position
+@code{[@var{ZETA}, @var{XI}, @var{RHO}]}.  The integrator used is Heun's
+Scheme.  The step size can be controlled by @var{H}.  The Jacobian matrix can
+be defined for each grid cell by @code{[@var{TX}, @var{TY}, @var{TZ}]}.
 
 @seealso{streamline, stream2, stream3, __streameuler2d__}
 @end deftypefn */)
--- a/scripts/plot/draw/stream2.m	Thu Dec 12 20:35:09 2019 +0100
+++ b/scripts/plot/draw/stream2.m	Thu Dec 12 14:50:17 2019 -0800
@@ -19,7 +19,7 @@
 ## -*- texinfo -*-
 ## @deftypefn  {} {@var{xy} =} stream2 (@var{x}, @var{y}, @var{u}, @var{v}, @var{sx}, @var{sy})
 ## @deftypefnx {} {@var{xy} =} stream2 (@var{u}, @var{v}, @var{sx}, @var{sy})
-## @deftypefnx {} {@var{xy} =} stream2 (@dots{}, "@var{options}")
+## @deftypefnx {} {@var{xy} =} stream2 (@dots{}, @var{options})
 ## Compute 2-D streamline data.
 ##
 ## Calculates streamlines of a vector field given by @code{[@var{u}, @var{v}]}.
@@ -30,9 +30,9 @@
 ## @code{[]} is returned.
 ##
 ## The input parameter @var{options} is a 2-D vector of the form
-## @code{[@var{stepsize}, @var{maxnumbervertices}]}.  The first parameter
-## specifies the step size used for trajectory integration (default 0.1).  It
-## is allowed to set a negative value to control the direction of integration.
+## @code{[@var{stepsize}, @var{max_vertices}]}.  The first parameter
+## specifies the step size used for trajectory integration (default 0.1).  A
+## negative value is allowed which will reverse the direction of integration.
 ## The second parameter specifies the maximum number of segments used to
 ## create a streamline (default 10,000).
 ##
@@ -51,7 +51,6 @@
 ## @end example
 ##
 ## @seealso{streamline, stream3}
-##
 ## @end deftypefn
 
 ## References:
@@ -75,52 +74,53 @@
 function xy = stream2 (varargin)
 
   options = [];
-  switch (length (varargin))
-    case (0)
+  switch (numel (varargin))
+    case 0
       print_usage ();
     case {4,5}
-      if (length (varargin) == 4)
+      if (numel (varargin) == 4)
         [u, v, spx, spy] = varargin{:};
       else
         [u, v, spx, spy, options] = varargin{:};
       endif
       [m, n] = size (u);
       [x, y] = meshgrid (1:n, 1:m);
-    case (6)
+    case 6
       [x, y, u, v, spx, spy] = varargin{:};
-    case (7)
+    case 7
       [x, y, u, v, spx, spy, options] = varargin{:};
     otherwise
-      error ("stream2: unknown input parameter count");
+      error ("stream2: invalid number of inputs");
   endswitch
 
-  h = 0.1;
-  maxnverts = 10000;
+  stepsize = 0.1;
+  max_vertices = 10_000;
   if (! isempty (options))
-    switch (length (options))
-      case (1)
-        h = options(1);
-      case (2)
-        h = options(1);
-        maxnverts = options(2);
+    switch (numel (options))
+      case 1
+        stepsize = options(1);
+      case 2
+        stepsize = options(1);
+        max_vertices = options(2);
       otherwise
-        error ("stream2: wrong options length");
+        error ("stream2: invalid number of OPTIONS elements");
     endswitch
+
+    if (! isreal (stepsize) || stepsize == 0)
+      error ("stream2: STEPSIZE must be a real scalar != 0");
+    endif
+    if (! isreal (max_vertices) || max_vertices < 1)
+      error ("stream2: MAX_VERTICES must be an integer > 0");
+    endif
+    max_vertices = fix (max_vertices);
   endif
 
-  if ((! isnumeric (h)) || (h == 0))
-    error ("stream2: step size error");
-  endif
-  if ((! isnumeric (maxnverts)) || (maxnverts < 1))
-    error ("stream2: max num vertices error");
-  endif
-  if (! (isequal (size (u), size (v), size (x), size (y)) && ...
-         isequal (size (spx), size (spy))) )
+  if (! (size_equal (u, v, x, y) && size_equal (spx, spy)))
     error ("stream2: matrix dimensions must match");
   endif
-  if (iscomplex (u) || iscomplex (v) || iscomplex (x) || ...
-      iscomplex (y) || iscomplex (spx) || iscomplex (spy))
-    error ("stream2: input must be real valued");
+  if (iscomplex (u) || iscomplex (v) || iscomplex (x) || iscomplex (y)
+      || iscomplex (spx) || iscomplex (spy))
+    error ("stream2: all inputs must be real-valued");
   endif
 
   gx = x(1,:);
@@ -130,12 +130,12 @@
   dx = diff (gx);
   dy = diff (gy);
   ## "<" used to check if the mesh is ascending
-  if (any (dx <= 0) || any (dy <= 0) || ...
-      any (isnan (dx)) || any (isnan (dy)))
-    error ("stream2: ill shaped elements in mesh");
+  if (any (dx <= 0) || any (dy <= 0)
+      || any (isnan (dx)) || any (isnan (dy)))
+    error ("stream2: non-monotonically increasing or NaN values found in mesh");
   endif
-  tx = 1./dx;
-  ty = 1./dy;
+  tx = 1 ./ dx;
+  ty = 1 ./ dy;
   ## "Don't cares" used for handling points located on the border
   tx(end + 1) = 0;
   ty(end + 1) = 0;
@@ -145,15 +145,15 @@
   px = spx(:);
   py = spy(:);
 
-  for nseed = 1:length (px)
+  for nseed = 1 : numel (px)
 
     xp = px(nseed);
     yp = py(nseed);
-    idx = find (logical (diff (gx <= xp)), 1);
+    idx = find (diff (gx <= xp), 1);
     if (gx(end) == xp)
       idx = numel (gx);
     endif
-    idy = find (logical (diff (gy <= yp)), 1);
+    idy = find (diff (gy <= yp), 1);
     if (gy(end) == yp)
       idy = numel (gy);
     endif
@@ -165,7 +165,7 @@
       zeta = (idx - 1) + (xp - gx(idx)) * tx(idx);
       xi = (idy - 1) + (yp - gy(idy)) * ty(idy);
 
-      C = __streameuler2d__ (u, v, tx, ty, zeta, xi, h, maxnverts);
+      C = __streameuler2d__ (u, v, tx, ty, zeta, xi, stepsize, max_vertices);
 
       ## Transform from C coordinates to P coordinates
       idu = floor (C(:,1));
@@ -178,6 +178,7 @@
 
 endfunction
 
+
 %!demo
 %! clf;
 %! [x, y] = meshgrid (-5:5, -4:4);
@@ -195,5 +196,34 @@
 %! axis equal;
 
 %!test
-%! xy = stream2([1,1,1;2,2,2;3,3,3], [1,1,1;2,2,2;3,3,3], 1, 1, [0.01,5]);
+%! xy = stream2 ([1,1,1;2,2,2;3,3,3], [1,1,1;2,2,2;3,3,3], 1, 1, [0.01,5]);
 %! assert (numel (xy{:}), 10);
+
+## Test input validation
+%!error stream2 ()
+%!error <invalid number of inputs> stream2 (1)
+%!error <invalid number of inputs> stream2 (1,2)
+%!error <invalid number of inputs> stream2 (1,2,3)
+%!error <invalid number of OPTIONS> stream2 (1,2,3,4, [1,2,3])
+%!error <STEPSIZE must be a real scalar != 0> stream2 (1,2,3,4, [1i])
+%!error <STEPSIZE must be a real scalar != 0> stream2 (1,2,3,4, [0])
+%!error <MAX_VERTICES must be an integer> stream2 (1,2,3,4, [1, 1i])
+%!error <MAX_VERTICES must be an integer> stream2 (1,2,3,4, [1, 0])
+%!error <matrix dimensions must match> stream2 ([1 1],2,3,4)
+%!error <matrix dimensions must match> stream2 (1,[2 2],3,4)
+%!error <matrix dimensions must match> stream2 (1,2,[3 3],4)
+%!error <matrix dimensions must match> stream2 (1,2,3,[4 4])
+%!error <all inputs must be real-valued> stream2 (1i,2,3,4)
+%!error <all inputs must be real-valued> stream2 (1,2i,3,4)
+%!error <all inputs must be real-valued> stream2 (1,2,3i,4)
+%!error <all inputs must be real-valued> stream2 (1,2,3,4i)
+%!error <non-monotonically increasing or NaN values found in mesh>
+%! stream2 ([2 1], [1 2], [1 1], [2 2], [3 3], [4 4]);
+%!error <non-monotonically increasing or NaN values found in mesh>
+%! stream2 ([1 NaN], [1 2], [1 1], [2 2], [3 3], [4 4]);
+## FIXME: vectors representing x, y mesh are not accepted.
+%#!error <non-monotonically increasing or NaN values found in mesh>
+%! stream2 ([1 2], [2 1], [1 1], [2 2], [3 3], [4 4]);
+%#!error <non-monotonically increasing or NaN values found in mesh>
+%! stream2 ([1 2], [1 NaN], [1 1], [2 2], [3 3], [4 4]);
+
--- a/scripts/plot/draw/stream3.m	Thu Dec 12 20:35:09 2019 +0100
+++ b/scripts/plot/draw/stream3.m	Thu Dec 12 14:50:17 2019 -0800
@@ -19,7 +19,7 @@
 ## -*- texinfo -*-
 ## @deftypefn  {} {@var{xyz} =} stream3 (@var{x}, @var{y}, @var{z}, @var{u}, @var{v}, @var{w}, @var{sx}, @var{sy}, @var{sz})
 ## @deftypefnx {} {@var{xyz} =} stream3 (@var{u}, @var{v}, @var{w}, @var{sx}, @var{sy}, @var{sz})
-## @deftypefnx {} {@var{xyz} =} stream3 (@dots{}, "@var{options}")
+## @deftypefnx {} {@var{xyz} =} stream3 (@dots{}, @var{options})
 ## Compute 3-D streamline data.
 ##
 ## Calculate streamlines of a vector field given by @code{[@var{u}, @var{v},
@@ -30,9 +30,9 @@
 ## the vector field, @code{[]} is returned.
 ##
 ## The input parameter @var{options} is a 2-D vector of the form
-## @code{[@var{stepsize}, @var{maxnumbervertices}]}.  The first parameter
-## specifies the step size used for trajectory integration (default 0.1).  It
-## is allowed to set a negative value to control the direction of integration.
+## @code{[@var{stepsize}, @var{max_vertices}]}.  The first parameter
+## specifies the step size used for trajectory integration (default 0.1).  A
+## negative value is allowed which will reverse the direction of integration.
 ## The second parameter specifies the maximum number of segments used to
 ## create a streamline (default 10,000).
 ##
@@ -52,7 +52,6 @@
 ## @end example
 ##
 ## @seealso{stream2, streamline, streamtube}
-##
 ## @end deftypefn
 
 ## References:
@@ -76,53 +75,54 @@
 function xyz = stream3 (varargin)
 
   options = [];
-  switch (length (varargin))
-    case (0)
+  switch (numel (varargin))
+    case 0
       print_usage ();
     case {6,7}
-      if (length (varargin) == 6)
+      if (numel (varargin) == 6)
         [u, v, w, spx, spy, spz] = varargin{:};
       else
         [u, v, w, spx, spy, spz, options] = varargin{:};
       endif
       [m, n, p] = size (u);
       [x, y, z] = meshgrid (1:n, 1:m, 1:p);
-    case (9)
+    case 9
       [x, y, z, u, v, w, spx, spy, spz] = varargin{:};
-    case (10)
+    case 10
       [x, y, z, u, v, w, spx, spy, spz, options] = varargin{:};
     otherwise
-      error ("stream3: unknown input parameter count");
+      error ("stream3: invalid number of inputs");
   endswitch
 
-  h = 0.1;
-  maxnverts = 10000;
+  stepsize = 0.1;
+  max_vertices = 10_000;
   if (! isempty (options))
-    switch (length (options))
-      case (1)
-        h = options(1);
-      case (2)
-        h = options(1);
-        maxnverts = options(2);
+    switch (numel (options))
+      case 1
+        stepsize = options(1);
+      case 2
+        stepsize = options(1);
+        max_vertices = options(2);
       otherwise
-        error ("stream3: wrong options length");
+        error ("stream3: invalid number of OPTIONS elements");
     endswitch
+
+    if (! isreal (stepsize) || stepsize == 0)
+      error ("stream2: STEPSIZE must be a real scalar != 0");
+    endif
+    if (! isreal (max_vertices) || max_vertices < 1)
+      error ("stream2: MAX_VERTICES must be an integer > 0");
+    endif
+    max_vertices = fix (max_vertices);
   endif
 
-  if ((! isnumeric (h)) || (h == 0))
-    error ("stream3: step size error");
-  endif
-  if ((! isnumeric (maxnverts)) || (maxnverts < 1))
-    error ("stream3: max num vertices error");
-  endif
-  if (! (isequal (size (u), size (v), size (w), size (x), size (y), size (z)) && ...
-         isequal (size (spx), size (spy))) )
+  if (! (size_equal (u, v, w, x, y, z) && size_equal (spx, spy, spz)))
     error ("stream3: matrix dimensions must match");
   endif
-  if (iscomplex (u) || iscomplex (v) || iscomplex (w) || ...
-      iscomplex (x) || iscomplex (y) || iscomplex (z) || ...
-      iscomplex (spx) || iscomplex (spy) || iscomplex (spz))
-    error ("stream3: input must be real valued");
+  if (iscomplex (u) || iscomplex (v) || iscomplex (w)
+      || iscomplex (x) || iscomplex (y) || iscomplex (z)
+      || iscomplex (spx) || iscomplex (spy) || iscomplex (spz))
+    error ("stream3: all inputs must be real-valued");
   endif
 
   gx = x(1, :, 1);
@@ -135,13 +135,13 @@
   dy = diff (gy);
   dz = diff (gz);
   ## "<" used to check if the mesh is ascending
-  if (any (dx <= 0) || any (dy <= 0) || any (dz <= 0) || ...
-      any (isnan (dx)) || any (isnan (dy)) || any (isnan (dz)))
-    error ("stream3: ill shaped elements in mesh");
+  if (any (dx <= 0) || any (dy <= 0) || any (dz <= 0)
+      || any (isnan (dx)) || any (isnan (dy)) || any (isnan (dz)))
+    error ("stream3: non-monotonically increasing or NaN values found in mesh");
   endif
-  tx = 1./dx;
-  ty = 1./dy;
-  tz = 1./dz;
+  tx = 1 ./ dx;
+  ty = 1 ./ dy;
+  tz = 1 ./ dz;
   ## "Don't cares" used for handling points located on the border
   tx(end + 1) = 0;
   ty(end + 1) = 0;
@@ -154,20 +154,20 @@
   py = spy(:);
   pz = spz(:);
 
-  for nseed = 1:length (px)
+  for nseed = 1 : numel (px)
 
     xp = px(nseed);
     yp = py(nseed);
     zp = pz(nseed);
-    idx = find (logical (diff (gx <= xp)), 1);
+    idx = find (diff (gx <= xp), 1);
     if (gx(end) == xp)
       idx = numel (gx);
     endif
-    idy = find (logical (diff (gy <= yp)), 1);
+    idy = find (diff (gy <= yp), 1);
     if (gy(end) == yp)
       idy = numel (gy);
     endif
-    idz = find (logical (diff (gz <= zp)), 1);
+    idz = find (diff (gz <= zp), 1);
     if (gz(end) == zp)
       idz = numel (gz);
     endif
@@ -180,7 +180,8 @@
       xi = (idy - 1) + (yp - gy(idy)) * ty(idy);
       rho = (idz - 1) + (zp - gz(idz)) * tz(idz);
 
-      C = __streameuler3d__ (u, v, w, tx, ty, tz, zeta, xi, rho, h, maxnverts);
+      C = __streameuler3d__ (u, v, w, tx, ty, tz, zeta, xi, rho, ...
+                             stepsize, max_vertices);
 
       ## Transform from C coordinates to P coordinates
       idu = floor (C(:, 1));
@@ -195,6 +196,7 @@
 
 endfunction
 
+
 %!demo
 %! clf;
 %! [x, y, z] = meshgrid (-30:1:30, -30:1:30, 0:1:50);
@@ -220,3 +222,42 @@
 %! [u, v, w] = meshgrid (0:3, 0:3, 0:3);
 %! xyz = stream3 (u, v, w, 2, 2, 2, [0.01,5]);
 %! assert (numel (xyz{:}), 15);
+
+## Test input validation
+%!error stream3 ()
+%!error <invalid number of inputs> stream3 (1)
+%!error <invalid number of inputs> stream3 (1,2)
+%!error <invalid number of inputs> stream3 (1,2,3)
+%!error <invalid number of inputs> stream3 (1,2,3,4)
+%!error <invalid number of inputs> stream3 (1,2,3,4,5)
+%!error <invalid number of inputs> stream3 (1,2,3,4,5,6,7,8)
+%!error <invalid number of OPTIONS> stream3 (1,2,3,4,5,6, [1,2,3])
+%!error <STEPSIZE must be a real scalar != 0> stream3 (1,2,3,4,5,6, [1i])
+%!error <STEPSIZE must be a real scalar != 0> stream3 (1,2,3,4,5,6, [0])
+%!error <MAX_VERTICES must be an integer> stream3 (1,2,3,4,5,6, [1, 1i])
+%!error <MAX_VERTICES must be an integer> stream3 (1,2,3,4,5,6, [1, 0])
+%!error <matrix dimensions must match> stream3 ([1 1],2,3,4,5,6)
+%!error <matrix dimensions must match> stream3 (1,[2 2],3,4,5,6)
+%!error <matrix dimensions must match> stream3 (1,2,[3 3],4,5,6)
+%!error <matrix dimensions must match> stream3 (1,2,3,[4 4],5,6)
+%!error <matrix dimensions must match> stream3 (1,2,3,4,[5 5],6)
+%!error <matrix dimensions must match> stream3 (1,2,3,4,5,[6 6])
+%!error <all inputs must be real-valued> stream3 (1i,2,3,4,5,6)
+%!error <all inputs must be real-valued> stream3 (1,2i,3,4,5,6)
+%!error <all inputs must be real-valued> stream3 (1,2,3i,4,5,6)
+%!error <all inputs must be real-valued> stream3 (1,2,3,4i,5,6)
+%!error <all inputs must be real-valued> stream3 (1,2,3,4,5i,6)
+%!error <all inputs must be real-valued> stream3 (1,2,3,4,5,6i)
+%!error <non-monotonically increasing or NaN values found in mesh>
+%! stream3 ([2 1], [1 2], [3 3], [4 4], [5 5], [6 6], [7 7], [8 8], [9 9]);
+%!error <non-monotonically increasing or NaN values found in mesh>
+%! stream3 ([1 NaN], [1 2], [3 3], [4 4], [5 5], [6 6], [7 7], [8 8], [9 9]);
+## FIXME: vectors representing x, y, z mesh are not accepted.
+%#!error <non-monotonically increasing or NaN values found in mesh>
+%! stream3 ([1 2], [2 1], [3 3], [4 4], [5 5], [6 6], [7 7], [8 8], [9 9]);
+%#!error <non-monotonically increasing or NaN values found in mesh>
+%! stream3 ([1 2], [1 NaN], [3 3], [4 4], [5 5], [6 6], [7 7], [8 8], [9 9]);
+%#!error <non-monotonically increasing or NaN values found in mesh>
+%! stream3 ([1 2], [1 2], [2 1], [4 4], [5 5], [6 6], [7 7], [8 8], [9 9]);
+%#!error <non-monotonically increasing or NaN values found in mesh>
+%! stream3 ([1 2], [1 2], [1 NaN], [4 4], [5 5], [6 6], [7 7], [8 8], [9 9]);
--- a/scripts/plot/draw/streamline.m	Thu Dec 12 20:35:09 2019 +0100
+++ b/scripts/plot/draw/streamline.m	Thu Dec 12 14:50:17 2019 -0800
@@ -19,7 +19,7 @@
 ## -*- texinfo -*-
 ## @deftypefn  {} {} streamline (@var{x}, @var{y}, @var{z}, @var{u}, @var{v}, @var{w}, @var{sx}, @var{sy}, @var{sz})
 ## @deftypefnx {} {} streamline (@var{u}, @var{v}, @var{w}, @var{sx}, @var{sy}, @var{sz})
-## @deftypefnx {} {} streamline (@dots{}, "@var{options}")
+## @deftypefnx {} {} streamline (@dots{}, @var{options})
 ## @deftypefnx {} {} streamline (@var{hax}, @dots{})
 ## @deftypefnx {} {@var{h} =} streamline (@dots{})
 ## Plot streamlines of 2-D or 3-D vector fields.
@@ -31,9 +31,9 @@
 ## points @code{[@var{sx}, @var{sy}]} or @code{[@var{sx}, @var{sy}, @var{sz}]}.
 ##
 ## The input parameter @var{options} is a 2-D vector of the form
-## @code{[@var{stepsize}, @var{maxnumbervertices}]}.  The first parameter
-## specifies the step size used for trajectory integration (default 0.1).  It
-## is allowed to set a negative value to control the direction of integration.
+## @code{[@var{stepsize}, @var{max_vertices}]}.  The first parameter
+## specifies the step size used for trajectory integration (default 0.1).  A
+## negative value is allowed which will reverse the direction of integration.
 ## The second parameter specifies the maximum number of segments used to
 ## create a streamline (default 10,000).
 ##
@@ -55,16 +55,20 @@
 ## @end example
 ##
 ## @seealso{stream2, stream3, streamtube}
-##
 ## @end deftypefn
 
 function h = streamline (varargin)
 
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("streamline", varargin{:});
+
   if (nargin == 0)
     print_usage ();
   endif
 
-  [hax, varargin] = __plt_get_axis_arg__ ("streamline", varargin{:});
+  nd = ndims (varargin{1});
+  if (nd > 3)
+    error ("streamline: input data must be 2-D or 3-D");
+  endif
 
   if (isempty (hax))
     hax = gca ();
@@ -73,33 +77,31 @@
   endif
 
   h = [];
-  argval = varargin(1);
-  switch (numel (size (argval{:})))
-    case (2)
-      xy = stream2 (varargin{:});
-      for i = 1:length (xy)
-        sl = xy{i};
-        if (! isempty (sl))
-          tmp = line (hax, "xdata", sl(:, 1), "ydata", sl(:, 2), "color", "b");
-          h = [h; tmp];
-        endif
-      endfor
-    case (3)
-      xyz = stream3 (varargin{:});
-      for i = 1:length (xyz)
-        sl = xyz{i};
-        if (! isempty (sl))
-          tmp = line (hax, "xdata", sl(:, 1), "ydata", sl(:, 2), "zdata", sl(:, 3), ...
-                      "color", "b");
-          h = [h; tmp];
-        endif
-      endfor
-    otherwise
-      error ("streamline: 2D or 3D input data only");
-  endswitch
+  if (nd == 2)
+    xy = stream2 (varargin{:});
+    for i = 1 : length (xy)
+      sl = xy{i};
+      if (! isempty (sl))
+        htmp = line (hax, "xdata", sl(:, 1), "ydata", sl(:, 2), "color", "b");
+        h = [h; htmp];
+      endif
+    endfor
+  else
+    xyz = stream3 (varargin{:});
+    for i = 1 : length (xyz)
+      sl = xyz{i};
+      if (! isempty (sl))
+        htmp = line (hax,
+                     "xdata", sl(:, 1), "ydata", sl(:, 2), "zdata", sl(:, 3),
+                     "color", "b");
+        h = [h; htmp];
+      endif
+    endfor
+  endif
 
 endfunction
 
+
 %!demo
 %! clf;
 %! [x, y] = meshgrid (-2:0.5:2);
@@ -157,4 +159,14 @@
 %! grid on;
 %! axis equal tight;
 
+## Test input validation
 %!error streamline ()
+%!error <Invalid call to streamline>
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   hax = axes ();
+%!   streamline (hax);
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
+%!error <input data must be 2-D or 3-D> streamline (ones (2,2,2,2))
--- a/scripts/plot/draw/streamtube.m	Thu Dec 12 20:35:09 2019 +0100
+++ b/scripts/plot/draw/streamtube.m	Thu Dec 12 14:50:17 2019 -0800
@@ -20,7 +20,7 @@
 ## @deftypefn  {} {} streamtube (@var{x}, @var{y}, @var{z}, @var{u}, @var{v}, @var{w}, @var{sx}, @var{sy}, @var{sz})
 ## @deftypefnx {} {} streamtube (@var{u}, @var{v}, @var{w}, @var{sx}, @var{sy}, @var{sz})
 ## @deftypefnx {} {} streamtube (@var{vertices}, @var{x}, @var{y}, @var{z}, @var{u}, @var{v}, @var{w})
-## @deftypefnx {} {} streamtube (@dots{}, "@var{options}")
+## @deftypefnx {} {} streamtube (@dots{}, @var{options})
 ## @deftypefnx {} {} streamtube (@var{hax}, @dots{})
 ## @deftypefnx {} {@var{h} =} streamtube (@dots{})
 ## Calculate and display streamtubes.
@@ -34,14 +34,14 @@
 ## The streamtubes start at the seed points
 ## @code{[@var{sx}, @var{sy}, @var{sz}]}.
 ##
-## The tubes are colored depending on the local vector field strength.
+## The tubes are colored based on the local vector field strength.
 ##
 ## The input parameter @var{options} is a 2-D vector of the form
 ## @code{[@var{scale}, @var{n}]}.  The first parameter scales the start radius
 ## of the streamtubes (default 1).  The second parameter specifies the number
 ## of patches used for the streamtube circumference (default 20).
 ##
-## @code{streamtube} can be called with a cell array containing precomputed
+## @code{streamtube} can be called with a cell array containing pre-computed
 ## streamline data.  To do this, @var{vertices} must be created with the
 ## @code{stream3} function.  This option is useful if you need to alter the
 ## integrator step size or the maximum number of vertices of the streamline.
@@ -65,7 +65,6 @@
 ## @end example
 ##
 ## @seealso{stream3, streamline}
-##
 ## @end deftypefn
 
 ## References:
@@ -78,28 +77,18 @@
 
 function h = streamtube (varargin)
 
-  if (nargin == 0)
-    print_usage ();
-  endif
-
-  [hax, varargin] = __plt_get_axis_arg__ ("streamtube", varargin{:});
-
-  if (isempty (hax))
-    hax = gca ();
-  else
-    hax = hax(1);
-  endif
+  [hax, varargin, nargin] = __plt_get_axis_arg__ ("streamtube", varargin{:});
 
   options = [];
   xyz = [];
-  switch (length (varargin))
-    case (0)
+  switch (nargin)
+    case 0
       print_usage ();
-    case (6)
+    case 6
       [u, v, w, spx, spy, spz] = varargin{:};
       [m, n, p] = size (u);
       [x, y, z] = meshgrid (1:n, 1:m, 1:p);
-    case (7)
+    case 7
       if (iscell (varargin{1}))
         [xyz, x, y, z, u, v, w] = varargin{:};
       else
@@ -107,57 +96,64 @@
         [m, n, p] = size (u);
         [x, y, z] = meshgrid (1:n, 1:m, 1:p);
       endif
-    case (8)
+    case 8
       [xyz, x, y, z, u, v, w, options] = varargin{:};
-    case (9)
+    case 9
       [x, y, z, u, v, w, spx, spy, spz] = varargin{:};
-    case (10)
+    case 10
       [x, y, z, u, v, w, spx, spy, spz, options] = varargin{:};
     otherwise
-      error ("streamtube: unknown input parameter count");
+      error ("streamtube: invalid number of inputs");
   endswitch
 
   scale = 1;
   n = 20;
   if (! isempty (options))
-    switch (length (options))
-      case (1)
+    switch (numel (options))
+      case 1
         scale = options(1);
-      case (2)
+      case 2
         scale = options(1);
         n = options(2);
       otherwise
-        error ("streamtube: wrong options length");
+        error ("streamtube: invalid number of OPTIONS elements");
     endswitch
+
+    if (! isreal (scale) || scale <= 0)
+      error ("streamtube: SCALE must be a real scalar > 0");
+    endif
+    if (! isreal (n) || n < 3)
+      error ("streamtube: number of polygons N must be greater than 2");
+    endif
+    n = fix (n);
   endif
 
-  if ((! isnumeric (scale)) || (scale <= 0))
-    error ("streamtube: scale error");
+  if (isempty (hax))
+    hax = gca ();
+  else
+    hax = hax(1);
   endif
-  if ((! isnumeric (n)) || (n < 3))
-    error ("streamtube: number of polygons for tube circumference too small");
-  endif
+
   if isempty (xyz)
     xyz = stream3 (x, y, z, u, v, w, spx, spy, spz, 0.5);
   endif
 
   div = divergence (x, y, z, u, v, w);
   vn = sqrt (u.*u + v.*v + w.*w);
-  vmax = max (max (max (vn)));
-  vmin = min (min (min (vn)));
+  vmax = max (vn(:));
+  vmin = min (vn(:));
 
   ## Radius estimator
   [ny, nx, nz] = size (x);
-  dx = (max (max (max (x))) - min (min (min (x)))) / nx;
-  dy = (max (max (max (y))) - min (min (min (y)))) / ny;
-  dz = (max (max (max (z))) - min (min (min (z)))) / nz;
-  r0 = scale * sqrt (dx * dx + dy * dy + dz * dz);
+  dx = (max (x(:)) - min (x(:))) / nx;
+  dy = (max (y(:)) - min (y(:))) / ny;
+  dz = (max (z(:)) - min (z(:))) / nz;
+  r0 = scale * sqrt (dx*dx + dy*dy + dz*dz);
 
   h = [];
-  for i = 1:length (xyz)
-
+  for i = 1 : length (xyz)
     sl = xyz{i};
-    [nverts, ~] = size(sl);
+    nverts = rows (sl);
     if (! isempty (sl)) && (nverts > 2)
 
       divsl = interp3 (x, y, z, div, sl(:, 1), sl(:, 2), sl(:, 3));
@@ -166,11 +162,10 @@
       wsl = interp3 (x, y, z, w, sl(:, 1), sl(:, 2), sl(:, 3));
       vv = sqrt (usl.*usl + vsl.*vsl + wsl.*wsl);
 
-      tmp = plottube (hax, sl, divsl, vv, vmax, vmin, r0, n);
-      h = [h, tmp];
+      htmp = plottube (hax, sl, divsl, vv, vmax, vmin, r0, n);
+      h = [h; htmp];
 
     endif
-
   endfor
 
 endfunction
@@ -184,7 +179,7 @@
     maxnverts = length (sl);
   endif
   if (maxnverts < 3)
-    error ("streamtube: too less data to show");
+    error ("streamtube: too little data to plot");
   endif
 
   if (vmax == vmin)
@@ -193,7 +188,7 @@
     colscale = 1.0 / (vmax - vmin);
   endif
 
-  phi = linspace (0, 2*pi (), npoly);
+  phi = linspace (0, 2*pi, npoly);
 
   X0 = sl(1, :);
   X1 = sl(2, :);
@@ -265,7 +260,7 @@
     N = [S(3), S(3), - S(1) - S(2)];
   endif
 
-  N = N / norm(N);
+  N /= norm (N);
 
 endfunction
 
@@ -273,7 +268,7 @@
 ## from starting point XS to ending point XE
 function [px, py, pz] = segment_patch_data (XS, XE)
 
-  [~, npoly] = size (XS);
+  npoly = columns (XS);
 
   px = zeros (4, npoly);
   py = zeros (4, npoly);
@@ -324,6 +319,7 @@
 
 endfunction
 
+
 %!demo
 %! clf;
 %! [x, y, z] = meshgrid (-1:0.1:1, -1:0.1:1, -3.5:0.1:0);
@@ -368,5 +364,15 @@
 %! set (gca, "cameraviewanglemode", "manual");
 %! title ("Integration Towards Sink");
 
+## Test input validation
 %!error streamtube ()
-
+%!error <invalid number of inputs> streamtube (1)
+%!error <invalid number of inputs> streamtube (1,2)
+%!error <invalid number of inputs> streamtube (1,2,3)
+%!error <invalid number of inputs> streamtube (1,2,3,4)
+%!error <invalid number of inputs> streamtube (1,2,3,4,5)
+%!error <invalid number of OPTIONS> streamtube (1,2,3,4,5,6, [1,2,3])
+%!error <SCALE must be a real scalar . 0> streamtube (1,2,3,4,5,6, [1i])
+%!error <SCALE must be a real scalar . 0> streamtube (1,2,3,4,5,6, [0])
+%!error <N must be greater than 2> streamtube (1,2,3,4,5,6, [1, 1i])
+%!error <N must be greater than 2> streamtube (1,2,3,4,5,6, [1, 2])