# HG changeset patch # User Nicholas R. Jankowski # Date 1581902345 18000 # Node ID a23da76e0693ea125290b5d3d28aa6aaa916c8ba # Parent 5e49ba5bdcc162f85715daa609865c9315a37b5a Matlab compatibility fixes for coordinate transform functions (bug #57794). * cart2pol.m, cart2sph.m, pol2cart.m, sph2cart.m: Modified to allow row or column vector inputs, remove full matrix single output argument option, and clarified coordinate definitions in help text. * lightangle.m, surfl.m: Fix existing instances where single output was used and a matrix was expected. * NEWS: Added coordinate transform changes to Matlab compatibility section. diff -r 5e49ba5bdcc1 -r a23da76e0693 NEWS --- a/NEWS Sun Mar 22 19:23:03 2020 +0100 +++ b/NEWS Sun Feb 16 20:19:05 2020 -0500 @@ -44,6 +44,15 @@ - The function `griddata` now accepts 3-D inputs by passing data directly to `griddata3`. +- Coordinate transformation functions `cart2sph`, `sph2cart`, +`cart2pol`, and `pol2cart` can now accept either row or column vectors +for coordinate inputs. A single coordinate matrix with one variable per +column can still be used as function input, but a single output variable +will now contain just the first output coordinate, and will no longer +return the full output coordinate matrix. Output size matches the +size of input vectors, or in the case of an input matrix will be column +vectors with rows corresponding to the input coordinate matrix. + ### Alphabetical list of new functions added in Octave 7 * `rng` diff -r 5e49ba5bdcc1 -r a23da76e0693 scripts/general/cart2pol.m --- a/scripts/general/cart2pol.m Sun Mar 22 19:23:03 2020 +0100 +++ b/scripts/general/cart2pol.m Sun Feb 16 20:19:05 2020 -0500 @@ -28,21 +28,47 @@ ## @deftypefnx {} {[@var{theta}, @var{r}, @var{z}] =} cart2pol (@var{x}, @var{y}, @var{z}) ## @deftypefnx {} {[@var{theta}, @var{r}] =} cart2pol (@var{C}) ## @deftypefnx {} {[@var{theta}, @var{r}, @var{z}] =} cart2pol (@var{C}) -## @deftypefnx {} {@var{P} =} cart2pol (@dots{}) ## ## Transform Cartesian coordinates to polar or cylindrical coordinates. ## ## The inputs @var{x}, @var{y} (, and @var{z}) must be the same shape, or ## scalar. If called with a single matrix argument then each row of @var{C} -## represents the Cartesian coordinate (@var{x}, @var{y} (, @var{z})). +## represents the Cartesian coordinate pair (@var{x}, @var{y}) or triplet +## (@var{x}, @var{y}, @var{z}). ## -## @var{theta} describes the angle relative to the positive x-axis. +## The outputs @var{theta}, @var{r} (, and @var{z}) match the shape of the +## inputs. For a matrix input @var{C} the outputs will be column vectors with +## rows corresponding to the rows of the input matrix. +## +## @var{theta} describes the angle relative to the positive x-axis measured in +## the xy-plane. ## ## @var{r} is the distance to the z-axis @w{(0, 0, z)}. ## -## If only a single return argument is requested then return a matrix @var{P} -## where each row represents one polar/(cylindrical) coordinate -## (@var{theta}, @var{phi} (, @var{z})). +## @var{z}, if present, is unchanged by the transformation. +## +## The coordinate transformation is computed using: +## +## @tex +## $$ \theta = \arctan \left ( {y \over x} \right ) $$ +## $$ r = \sqrt{x^2 + y^2} $$ +## $$ z = z $$ +## @end tex +## @ifnottex +## +## @example +## @group +## @var{theta} = arctan (@var{y} / @var{x}) +## @var{r} = sqrt (@var{x}^2 + @var{y}^2) +## @var{z} = @var{z} +## @end group +## @end example +## +## @end ifnottex +## +## @c FIXME: Remove this note in Octave 9.1 (two releases after 7.1). +## Note: For @sc{matlab} compatibility, this function no longer returns a full +## coordinate matrix when called with a single return argument. ## @seealso{pol2cart, cart2sph, sph2cart} ## @end deftypefn @@ -53,44 +79,54 @@ endif if (nargin == 1) - if (! (isnumeric (x) && ismatrix (x) - && (columns (x) == 2 || columns (x) == 3))) - error ("cart2pol: matrix input must have 2 or 3 columns [X, Y (, Z)]"); + if (! (isnumeric (x) && ismatrix (x))) + error ("cart2pol: matrix input must be 2-D numeric array"); endif - if (columns (x) == 3) - z = x(:,3); + if (isvector (x)) + n = numel (x); + if (n != 2 && n != 3) + error ("cart2pol: matrix input must be a 2- or 3-element vector or a 2- or 3-column array"); + endif + if (n == 3) + z = x(3); + endif + y = x(2); + x = x(1); + else + ncols = columns (x); + if (ncols != 2 && ncols != 3) + error ("cart2pol: matrix input must be a 2- or 3-element vector or a 2- or 3-column array"); + endif + + if (ncols == 3) + z = x(:,3); + endif + y = x(:,2); + x = x(:,1); endif - y = x(:,2); - x = x(:,1); + elseif (nargin == 2) - if (! isnumeric (x) || ! isnumeric (y)) - error ("cart2pol: X, Y must be numeric arrays of the same size, or scalar"); + if (! (isnumeric (x) && isnumeric (y))) + error ("cart2pol: X, Y must be numeric arrays or scalars"); endif [err, x, y] = common_size (x, y); if (err) - error ("cart2pol: X, Y must be numeric arrays of the same size, or scalar"); + error ("cart2pol: X, Y must be the same size or scalars"); endif + elseif (nargin == 3) - if (! isnumeric (x) || ! isnumeric (y) || ! isnumeric (z)) - error ("cart2pol: X, Y, Z must be numeric arrays of the same size, or scalar"); + if (! (isnumeric (x) && isnumeric (y) && isnumeric (z))) + error ("cart2pol: X, Y, Z must be numeric arrays or scalars"); endif [err, x, y, z] = common_size (x, y, z); if (err) - error ("cart2pol: X, Y, Z must be numeric arrays of the same size, or scalar"); + error ("cart2pol: X, Y, Z must be the same size or scalars"); endif endif theta = atan2 (y, x); r = sqrt (x .^ 2 + y .^ 2); - if (nargout <= 1) - if (isempty (z)) - theta = [theta(:), r(:)]; - else - theta = [theta(:), r(:), z(:)]; - endif - endif - endfunction @@ -104,9 +140,16 @@ %!test %! x = [0, 1, 2]; %! y = [0, 1, 2]; -%! P = cart2pol (x, y); -%! assert (P(:,1), [0; pi/4; pi/4], sqrt (eps)); -%! assert (P(:,2), sqrt (2)*[0; 1; 2], sqrt (eps)); +%! [t, r] = cart2pol (x, y); +%! assert (t, [0, pi/4, pi/4], eps); +%! assert (r, sqrt (2)*[0, 1, 2], eps); + +%!test +%! x = [0, 1, 2]'; +%! y = [0, 1, 2]'; +%! [t, r] = cart2pol (x, y); +%! assert (t, [0; pi/4; pi/4], eps); +%! assert (r, sqrt (2)*[0; 1; 2], eps); %!test %! x = [0, 1, 2]; @@ -146,13 +189,23 @@ %!test %! C = [0, 0; 1, 1; 2, 2]; -%! P = [0, 0; pi/4, sqrt(2); pi/4, 2*sqrt(2)]; -%! assert (cart2pol (C), P, sqrt (eps)); +%! [t, r] = cart2pol (C); +%! assert (t, [0; 1; 1]*pi/4, eps); +%! assert (r, [0; 1; 2]*sqrt(2), eps); %!test %! C = [0, 0, 0; 1, 1, 1; 2, 2, 2]; -%! P = [0, 0, 0; pi/4, sqrt(2), 1; pi/4, 2*sqrt(2), 2]; -%! assert (cart2pol (C), P, sqrt (eps)); +%! [t, r, z] = cart2pol (C); +%! assert (t, [0; 1; 1]*pi/4, eps); +%! assert (r, [0; 1; 2]*sqrt(2), eps); +%! assert (z, [0; 1; 2]); + +%!test +%! C = [0, 0, 0; 1, 1, 1; 2, 2, 2;1, 1, 1]; +%! [t, r, z] = cart2pol (C); +%! assert (t, [0; 1; 1; 1]*pi/4, eps); +%! assert (r, [0; 1; 2; 1]*sqrt(2), eps); +%! assert (z, [0; 1; 2; 1]); %!test %! x = zeros (1, 1, 1, 2); @@ -179,15 +232,17 @@ ## Test input validation %!error cart2pol () %!error cart2pol (1,2,3,4) -%!error cart2pol ({1,2,3}) -%!error cart2pol (ones (3,3,2)) -%!error cart2pol ([1]) -%!error cart2pol ([1,2,3,4]) -%!error cart2pol ({1,2,3}, [1,2,3]) -%!error cart2pol ([1,2,3], {1,2,3}) -%!error cart2pol (ones (3,3,3), ones (3,2,3)) -%!error cart2pol ({1,2,3}, [1,2,3], [1,2,3]) -%!error cart2pol ([1,2,3], {1,2,3}, [1,2,3]) -%!error cart2pol ([1,2,3], [1,2,3], {1,2,3}) -%!error cart2pol (ones (3,3,3), 1, ones (3,2,3)) -%!error cart2pol (ones (3,3,3), ones (3,2,3), 1) +%!error cart2pol ({1,2,3}) +%!error cart2pol (ones (3,3,2)) +%!error cart2pol ([1]) +%!error cart2pol ([1,2,3,4]) +%!error cart2pol ({1,2,3}, [1,2,3]) +%!error cart2pol ([1,2,3], {1,2,3}) +%!error cart2pol (ones (3,3,3), ones (3,2,3)) +%!error cart2pol ([1; 1], [2, 2]) +%!error cart2pol ([1; 1], [2, 2], [3, 3]) +%!error cart2pol ({1,2,3}, [1,2,3], [1,2,3]) +%!error cart2pol ([1,2,3], {1,2,3}, [1,2,3]) +%!error cart2pol ([1,2,3], [1,2,3], {1,2,3}) +%!error cart2pol (ones (3,3,3), 1, ones (3,2,3)) +%!error cart2pol (ones (3,3,3), ones (3,2,3), 1) diff -r 5e49ba5bdcc1 -r a23da76e0693 scripts/general/cart2sph.m --- a/scripts/general/cart2sph.m Sun Mar 22 19:23:03 2020 +0100 +++ b/scripts/general/cart2sph.m Sun Feb 16 20:19:05 2020 -0500 @@ -26,22 +26,45 @@ ## -*- texinfo -*- ## @deftypefn {} {[@var{theta}, @var{phi}, @var{r}] =} cart2sph (@var{x}, @var{y}, @var{z}) ## @deftypefnx {} {[@var{theta}, @var{phi}, @var{r}] =} cart2sph (@var{C}) -## @deftypefnx {} {@var{S} =} cart2sph (@dots{}) ## Transform Cartesian coordinates to spherical coordinates. ## ## The inputs @var{x}, @var{y}, and @var{z} must be the same shape, or scalar. -## If called with a single matrix argument then each row of @var{C} represents -## the Cartesian coordinate (@var{x}, @var{y}, @var{z}). +## If called with a single matrix argument then each row of @var{C} must +## represent a Cartesian coordinate triplet (@var{x}, @var{y}, @var{z}). ## -## @var{theta} describes the angle relative to the positive x-axis. +## The outputs @var{theta}, @var{phi}, @var{r} match the shape of the inputs. +## For a matrix input @var{C} the outputs will be column vectors with rows +## corresponding to the rows of the input matrix. ## -## @var{phi} is the angle relative to the xy-plane. +## @var{theta} describes the azimuth angle relative to the positive x-axis +## measured in the xy-plane. +## +## @var{phi} is the elevation angle measured relative to the xy-plane. ## ## @var{r} is the distance to the origin @w{(0, 0, 0)}. ## -## If only a single return argument is requested then return a matrix @var{S} -## where each row represents one spherical coordinate -## (@var{theta}, @var{phi}, @var{r}). +## The coordinate transformation is computed using: +## +## @tex +## $$ \theta = \arctan \left ({y \over x} \right ) $$ +## $$ \phi = \arctan \left ( {z \over {\sqrt{x^2+y^2}}} \right ) $$ +## $$ r = \sqrt{x^2 + y^2 + z^2} $$ +## @end tex +## @ifnottex +## +## @example +## @group +## @var{theta} = arctan (@var{y} / @var{x}) +## @var{phi} = arctan (@var{z} / sqrt (@var{x}^2 + @var{y}^2)) +## @var{r} = sqrt (@var{x}^2 + @var{y}^2 + @var{z}^2) +## @end group +## @end example +## +## @end ifnottex +## +## @c FIXME: Remove this note in Octave 9.1 (two releases after 7.1). +## Note: For @sc{matlab} compatibility, this function no longer returns a full +## coordinate matrix when called with a single return argument. ## @seealso{sph2cart, cart2pol, pol2cart} ## @end deftypefn @@ -52,19 +75,29 @@ endif if (nargin == 1) - if (! (isnumeric (x) && ismatrix (x) && columns (x) == 3)) - error ("cart2sph: matrix input must have 3 columns [X, Y, Z]"); + if (! (isnumeric (x) && ismatrix (x))) + error ("cart2sph: matrix input C must be a 2-D numeric array"); + elseif (columns (x) != 3 && numel (x) != 3) + error ("cart2sph: matrix input C must be a 3-element vector or 3-column array"); endif - z = x(:,3); - y = x(:,2); - x = x(:,1); + + if (numel (x) == 3) + z = x(3); + y = x(2); + x = x(1); + else + z = x(:,3); + y = x(:,2); + x = x(:,1); + endif + else - if (! isnumeric (x) || ! isnumeric (y) || ! isnumeric (z)) - error ("cart2sph: X, Y, Z must be numeric arrays of the same size, or scalar"); + if (! (isnumeric (x) && isnumeric (y) && isnumeric (z))) + error ("cart2sph: X, Y, Z must be numeric arrays or scalars"); endif [err, x, y, z] = common_size (x, y, z); if (err) - error ("cart2sph: X, Y, Z must be numeric arrays of the same size, or scalar"); + error ("cart2sph: X, Y, Z must be the same size or scalars"); endif endif @@ -72,10 +105,6 @@ phi = atan2 (z, sqrt (x .^ 2 + y .^ 2)); r = sqrt (x .^ 2 + y .^ 2 + z .^ 2); - if (nargout <= 1) - theta = [theta(:), phi(:), r(:)]; - endif - endfunction @@ -89,13 +118,22 @@ %! assert (r, [0, 1, 2]*sqrt (3), eps); %!test +%! x = [0; 1; 2]; +%! y = [0; 1; 2]; +%! z = [0; 1; 2]; +%! [t, p, r] = cart2sph (x, y, z); +%! assert (t, [0; pi/4; pi/4], eps); +%! assert (p, [0; 1; 1] * atan (sqrt (0.5)), eps); +%! assert (r, [0; 1; 2] * sqrt (3), eps); + +%!test %! x = 0; %! y = [0, 1, 2]; %! z = [0, 1, 2]; -%! S = cart2sph (x, y, z); -%! assert (S(:,1), [0; 1; 1] * pi/2, eps); -%! assert (S(:,2), [0; 1; 1] * pi/4, eps); -%! assert (S(:,3), [0; 1; 2] * sqrt (2), eps); +%! [t, p, r] = cart2sph (x, y, z); +%! assert (t, [0, 1, 1] * pi/2, eps); +%! assert (p, [0, 1, 1] * pi/4, eps); +%! assert (r, [0, 1, 2] * sqrt (2), eps); %!test %! x = [0, 1, 2]; @@ -103,17 +141,17 @@ %! z = [0, 1, 2]; %! [t, p, r] = cart2sph (x, y, z); %! assert (t, [0, 0, 0]); -%! assert (p, [0, 1, 1] * pi/4); -%! assert (r, [0, 1, 2] * sqrt (2)); +%! assert (p, [0, 1, 1] * pi/4, eps); +%! assert (r, [0, 1, 2] * sqrt (2), eps); %!test %! x = [0, 1, 2]; %! y = [0, 1, 2]; %! z = 0; %! [t, p, r] = cart2sph (x, y, z); -%! assert (t, [0, 1, 1] * pi/4); +%! assert (t, [0, 1, 1] * pi/4, eps); %! assert (p, [0, 0, 0]); -%! assert (r, [0, 1, 2] * sqrt (2)); +%! assert (r, [0, 1, 2] * sqrt (2), eps); %!test %! x = 0; @@ -121,13 +159,22 @@ %! z = [0, 1, 2]; %! [t, p, r] = cart2sph (x, y, z); %! assert (t, [0, 0, 0]); -%! assert (p, [0, 1, 1] * pi/2); +%! assert (p, [0, 1, 1] * pi/2, eps); %! assert (r, [0, 1, 2]); %!test %! C = [0, 0, 0; 1, 0, 1; 2, 0, 2]; -%! S = [0, 0, 0; 0, pi/4, sqrt(2); 0, pi/4, 2*sqrt(2)]; -%! assert (cart2sph (C), S, eps); +%! [t, p, r] = cart2sph (C); +%! assert (t, [0; 0; 0]); +%! assert (p, [0; 1; 1] * pi/4, eps); +%! assert (r, [0; 1; 2] * sqrt (2), eps); + +%!test +%! C = [0, 0, 0; 1, 0, 1; 2, 0, 2; 1, 0, 1]; +%! [t, p, r] = cart2sph (C); +%! assert (t, [0; 0; 0; 0]); +%! assert (p, [0; 1; 1; 1] * pi/4, eps); +%! assert (r, [0; 1; 2; 1] * sqrt (2), eps); %!test %! [x, y, z] = meshgrid ([0, 1], [0, 1], [0, 1]); @@ -145,11 +192,13 @@ %!error cart2sph () %!error cart2sph (1,2) %!error cart2sph (1,2,3,4) -%!error cart2sph ({1,2,3}) -%!error cart2sph (ones (3,3,2)) -%!error cart2sph ([1,2,3,4]) -%!error cart2sph ({1,2,3}, [1,2,3], [1,2,3]) -%!error cart2sph ([1,2,3], {1,2,3}, [1,2,3]) -%!error cart2sph ([1,2,3], [1,2,3], {1,2,3}) -%!error cart2sph (ones (3,3,3), 1, ones (3,2,3)) -%!error cart2sph (ones (3,3,3), ones (3,2,3), 1) +%!error cart2sph ({1,2,3}) +%!error cart2sph (ones (3,3,2)) +%!error cart2sph ([1,2,3,4]) +%!error cart2sph ([1,2,3,4; 1,2,3,4; 1,2,3,4]) +%!error cart2sph ({1,2,3}, [1,2,3], [1,2,3]) +%!error cart2sph ([1,2,3], {1,2,3}, [1,2,3]) +%!error cart2sph ([1,2,3], [1,2,3], {1,2,3}) +%!error cart2sph ([1,2,3], [1,2,3], [1,2,3]') +%!error cart2sph (ones (3,3,3), 1, ones (3,2,3)) +%!error cart2sph (ones (3,3,3), ones (3,2,3), 1) diff -r 5e49ba5bdcc1 -r a23da76e0693 scripts/general/pol2cart.m --- a/scripts/general/pol2cart.m Sun Mar 22 19:23:03 2020 +0100 +++ b/scripts/general/pol2cart.m Sun Feb 16 20:19:05 2020 -0500 @@ -28,21 +28,43 @@ ## @deftypefnx {} {[@var{x}, @var{y}, @var{z}] =} pol2cart (@var{theta}, @var{r}, @var{z}) ## @deftypefnx {} {[@var{x}, @var{y}] =} pol2cart (@var{P}) ## @deftypefnx {} {[@var{x}, @var{y}, @var{z}] =} pol2cart (@var{P}) -## @deftypefnx {} {@var{C} =} pol2cart (@dots{}) ## Transform polar or cylindrical coordinates to Cartesian coordinates. ## ## The inputs @var{theta}, @var{r}, (and @var{z}) must be the same shape, or ## scalar. If called with a single matrix argument then each row of @var{P} -## represents the polar/(cylindrical) coordinate (@var{theta}, @var{r} -## (, @var{z})). +## represents the polar coordinate pair (@var{theta}, @var{r}) or the +## cylindrical triplet (@var{theta}, @var{r}, @var{z}). +## +## The outputs @var{x}, @var{y} (, and @var{z}) match the shape of the inputs. +## For a matrix input @var{P} the outputs will be column vectors with rows +## corresponding to the rows of the input matrix. ## -## @var{theta} describes the angle relative to the positive x-axis. +## @var{theta} describes the angle relative to the positive x-axis measured in +## the xy-plane. +## +## @var{r} is the distance to the z-axis @w{(0, 0, z)}. +## +## @var{z}, if present, is unchanged by the transformation. +## +## The coordinate transformation is computed using: ## -## @var{r} is the distance to the z-axis (0, 0, z). +## @tex +## $$ x = r \cos \theta $$ +## $$ y = r \sin \theta $$ +## $$ z = z $$ +## @end tex +## @ifnottex ## -## If only a single return argument is requested then return a matrix @var{C} -## where each row represents one Cartesian coordinate -## (@var{x}, @var{y} (, @var{z})). +## @example +## @var{x} = @var{r} * cos (@var{theta}) +## @var{y} = @var{r} * sin (@var{theta}) +## @var{z} = @var{z} +## @end example +## +## @end ifnottex +## @c FIXME: Remove this note in Octave 9.1 (two releases after 7.1). +## Note: For @sc{matlab} compatibility, this function no longer returns a full +## coordinate matrix when called with a single return argument. ## @seealso{cart2pol, sph2cart, cart2sph} ## @end deftypefn @@ -53,44 +75,55 @@ endif if (nargin == 1) - if (! (isnumeric (theta) && ismatrix (theta) - && (columns (theta) == 2 || columns (theta) == 3))) - error ("pol2cart: matrix input must have 2 or 3 columns [THETA, R (, Z)]"); + if (! (isnumeric (theta) && ismatrix (theta))) + error ("cart2pol: matrix input P must be 2-D numeric array"); endif - if (columns (theta) == 3) - z = theta(:,3); + if (isvector (theta)) + n = numel (theta); + if (n != 2 && n != 3) + error ("cart2pol: matrix input must be a 2- or 3-element vector or a 2- or 3-column array"); + endif + if (n == 3) + z = theta(3); + endif + r = theta(2); + theta = theta(1); + + else + ncols = columns(theta); + if (ncols != 2 && ncols != 3) + error ("cart2pol: matrix input must be a 2- or 3-element vector or a 2- or 3-column array"); + endif + + if (ncols == 3) + z = theta(:,3); + endif + r = theta(:,2); + theta = theta(:,1); endif - r = theta(:,2); - theta = theta(:,1); + elseif (nargin == 2) - if (! isnumeric (theta) || ! isnumeric (r)) - error ("pol2cart: THETA, R must be numeric arrays of the same size, or scalar"); + if (! (isnumeric (theta) && isnumeric (r))) + error ("pol2cart: THETA, R must be numeric arrays or scalars"); endif [err, theta, r] = common_size (theta, r); if (err) - error ("pol2cart: THETA, R must be numeric arrays of the same size, or scalar"); + error ("pol2cart: THETA, R must be the same size or scalars"); endif + elseif (nargin == 3) - if (! isnumeric (theta) || ! isnumeric (r) || ! isnumeric (z)) - error ("pol2cart: THETA, R, Z must be numeric arrays of the same size, or scalar"); + if (! (isnumeric (theta) && isnumeric (r) && isnumeric (z))) + error ("pol2cart: THETA, R, Z must be numeric arrays or scalars"); endif [err, theta, r, z] = common_size (theta, r, z); if (err) - error ("pol2cart: THETA, R, Z must be numeric arrays of the same size, or scalar"); + error ("pol2cart: THETA, R, Z must be the same size or scalars"); endif endif x = r .* cos (theta); y = r .* sin (theta); - if (nargout <= 1) - if (isempty (z)) - x = [x(:), y(:)]; - else - x = [x(:), y(:), z(:)]; - endif - endif - endfunction @@ -98,32 +131,42 @@ %! t = [0, 0.5, 1] * pi; %! r = 1; %! [x, y] = pol2cart (t, r); -%! assert (x, [1, 0, -1], sqrt (eps)); -%! assert (y, [0, 1, 0], sqrt (eps)); +%! assert (x, [1, 0, -1], eps); +%! assert (y, [0, 1, 0], eps); %!test %! t = [0, 1, 1] * pi/4; %! r = sqrt (2) * [0, 1, 2]; -%! C = pol2cart (t, r); -%! assert (C(:,1), [0; 1; 2], sqrt (eps)); -%! assert (C(:,2), [0; 1; 2], sqrt (eps)); +%! [x, y] = pol2cart (t, r); +%! assert (x, [0, 1, 2], 2*eps); +%! assert (y, [0, 1, 2], 2*eps); %!test %! t = [0, 1, 1] * pi/4; %! r = sqrt (2) * [0, 1, 2]; %! z = [0, 1, 2]; %! [x, y, z2] = pol2cart (t, r, z); -%! assert (x, [0, 1, 2], sqrt (eps)); -%! assert (y, [0, 1, 2], sqrt (eps)); +%! assert (x, [0, 1, 2], 2*eps); +%! assert (y, [0, 1, 2], 2*eps); %! assert (z2, z); %!test +%! t = [0; 1; 1] * pi/4; +%! r = sqrt (2) * [0; 1; 2]; +%! z = [0; 1; 2]; +%! [x, y, z2] = pol2cart (t, r, z); +%! assert (x, [0; 1; 2], 2*eps); +%! assert (y, [0; 1; 2], 2*eps); +%! assert (z2, z); + + +%!test %! t = 0; %! r = [0, 1, 2]; %! z = [0, 1, 2]; %! [x, y, z2] = pol2cart (t, r, z); -%! assert (x, [0, 1, 2], sqrt (eps)); -%! assert (y, [0, 0, 0], sqrt (eps)); +%! assert (x, [0, 1, 2], eps); +%! assert (y, [0, 0, 0], eps); %! assert (z2, z); %!test @@ -146,13 +189,23 @@ %!test %! P = [0, 0; pi/4, sqrt(2); pi/4, 2*sqrt(2)]; -%! C = [0, 0; 1, 1; 2, 2]; -%! assert (pol2cart (P), C, sqrt (eps)); +%! [x, y] = pol2cart(P); +%! assert (x, [0; 1; 2], 2*eps); +%! assert (y, [0; 1; 2], 2*eps); %!test %! P = [0, 0, 0; pi/4, sqrt(2), 1; pi/4, 2*sqrt(2), 2]; -%! C = [0, 0, 0; 1, 1, 1; 2, 2, 2]; -%! assert (pol2cart (P), C, sqrt (eps)); +%! [x, y, z] = pol2cart(P); +%! assert (x, [0; 1; 2], 2*eps); +%! assert (y, [0; 1; 2], 2*eps); +%! assert (z, P(:,3), 2*eps); + +%!test +%! P = [0, 0, 0; pi/4, sqrt(2), 1; pi/4, 2*sqrt(2), 2; 0, 0, 0]; +%! [x, y, z] = pol2cart(P); +%! assert (x, [0; 1; 2; 0], 2*eps); +%! assert (y, [0; 1; 2; 0], 2*eps); +%! assert (z, P(:,3), 2*eps); %!test %! r = ones (1, 1, 1, 2); @@ -182,15 +235,17 @@ ## Test input validation %!error pol2cart () %!error pol2cart (1,2,3,4) -%!error pol2cart ({1,2,3}) -%!error pol2cart (ones (3,3,2)) -%!error pol2cart ([1]) -%!error pol2cart ([1,2,3,4]) -%!error pol2cart ({1,2,3}, [1,2,3]) -%!error pol2cart ([1,2,3], {1,2,3}) -%!error pol2cart (ones (3,3,3), ones (3,2,3)) -%!error pol2cart ({1,2,3}, [1,2,3], [1,2,3]) -%!error pol2cart ([1,2,3], {1,2,3}, [1,2,3]) -%!error pol2cart ([1,2,3], [1,2,3], {1,2,3}) -%!error pol2cart (ones (3,3,3), 1, ones (3,2,3)) -%!error pol2cart (ones (3,3,3), ones (3,2,3), 1) +%!error pol2cart ({1,2,3}) +%!error pol2cart (ones (3,3,2)) +%!error pol2cart ([1]) +%!error pol2cart ([1,2,3,4]) +%!error pol2cart ({1,2,3}, [1,2,3]) +%!error pol2cart ([1,2,3], {1,2,3}) +%!error pol2cart (ones (3,3,3), ones (3,2,3)) +%!error pol2cart ([1; 1], [2, 2]) +%!error pol2cart ([1; 1], [2, 2], [3, 3]) +%!error pol2cart ({1,2,3}, [1,2,3], [1,2,3]) +%!error pol2cart ([1,2,3], {1,2,3}, [1,2,3]) +%!error pol2cart ([1,2,3], [1,2,3], {1,2,3}) +%!error pol2cart (ones (3,3,3), 1, ones (3,2,3)) +%!error pol2cart (ones (3,3,3), ones (3,2,3), 1) diff -r 5e49ba5bdcc1 -r a23da76e0693 scripts/general/sph2cart.m --- a/scripts/general/sph2cart.m Sun Mar 22 19:23:03 2020 +0100 +++ b/scripts/general/sph2cart.m Sun Feb 16 20:19:05 2020 -0500 @@ -26,22 +26,43 @@ ## -*- texinfo -*- ## @deftypefn {} {[@var{x}, @var{y}, @var{z}] =} sph2cart (@var{theta}, @var{phi}, @var{r}) ## @deftypefnx {} {[@var{x}, @var{y}, @var{z}] =} sph2cart (@var{S}) -## @deftypefnx {} {@var{C} =} sph2cart (@dots{}) ## Transform spherical coordinates to Cartesian coordinates. ## ## The inputs @var{theta}, @var{phi}, and @var{r} must be the same shape, or ## scalar. If called with a single matrix argument then each row of @var{S} -## represents the spherical coordinate (@var{theta}, @var{phi}, @var{r}). +## must represent a spherical coordinate triplet (@var{theta}, @var{phi}, +## @var{r}). ## -## @var{theta} describes the angle relative to the positive x-axis. +## The outputs @var{x}, @var{y}, @var{z} match the shape of the inputs. For a +## matrix input @var{S} the outputs are column vectors with rows corresponding +## to the rows of the input matrix. ## -## @var{phi} is the angle relative to the xy-plane. +## @var{theta} describes the azimuth angle relative to the positive x-axis +## measured in the xy-plane. +## +## @var{phi} is the elevation angle measured relative to the xy-plane. ## ## @var{r} is the distance to the origin @w{(0, 0, 0)}. ## -## If only a single return argument is requested then return a matrix @var{C} -## where each row represents one Cartesian coordinate -## (@var{x}, @var{y}, @var{z}). +## The coordinate transformation is computed using: +## +## @tex +## $$ x = r \cos \phi \cos \theta $$ +## $$ y = r \cos \phi \sin \theta $$ +## $$ z = r \sin \phi $$ +## @end tex +## @ifnottex +## +## @example +## @var{x} = r * cos (@var{phi}) * cos (@var{theta}) +## @var{y} = r * cos (@var{phi}) * sin (@var{theta}) +## @var{z} = r * sin (@var{phi}) +## @end example +## +## @end ifnottex +## @c FIXME: Remove this note in Octave 9.1 (two releases after 7.1). +## Note: For @sc{matlab} compatibility, this function no longer returns a full +## coordinate matrix when called with a single return argument. ## @seealso{cart2sph, pol2cart, cart2pol} ## @end deftypefn @@ -52,19 +73,30 @@ endif if (nargin == 1) - if (! (isnumeric (theta) && ismatrix (theta) && columns (theta) == 3)) - error ("sph2cart: matrix input must have 3 columns [THETA, PHI, R]"); + if (! (isnumeric (theta) && ismatrix (theta))) + error ("sph2cart: matrix input must be a 2-D numeric array"); + endif + if (columns (theta) != 3 && numel (theta) != 3) + error ("sph2cart: matrix input must be a 3-element vector or 3-column array"); endif - r = theta(:,3); - phi = theta(:,2); - theta = theta(:,1); + + if (numel (theta) == 3) + r = theta(3); + phi = theta(2); + theta = theta(1); + else + r = theta(:,3); + phi = theta(:,2); + theta = theta(:,1); + endif + else - if (! isnumeric (theta) || ! isnumeric (phi) || ! isnumeric (r)) - error ("sph2cart: THETA, PHI, R must be numeric arrays of the same size, or scalar"); + if (! (isnumeric (theta) && isnumeric (phi) && isnumeric (r))) + error ("sph2cart: THETA, PHI, R must be numeric arrays or scalars"); endif [err, theta, phi, r] = common_size (theta, phi, r); if (err) - error ("sph2cart: THETA, PHI, R must be numeric arrays of the same size, or scalar"); + error ("sph2cart: THETA, PHI, R must be the same size or scalars"); endif endif @@ -72,10 +104,6 @@ y = r .* cos (phi) .* sin (theta); z = r .* sin (phi); - if (nargout <= 1) - x = [x(:), y(:), z(:)]; - endif - endfunction @@ -89,13 +117,22 @@ %! assert (z, [0, 0, 0]); %!test +%! t = [0; 0; 0]; +%! p = [0; 0; 0]; +%! r = [0; 1; 2]; +%! [x, y, z] = sph2cart (t, p, r); +%! assert (x, [0; 1; 2]); +%! assert (y, [0; 0; 0]); +%! assert (z, [0; 0; 0]); + +%!test %! t = 0; %! p = [0, 0, 0]; %! r = [0, 1, 2]; -%! C = sph2cart (t, p, r); -%! assert (C(:,1), r(:)); -%! assert (C(:,2), [0; 0; 0]); -%! assert (C(:,3), [0; 0; 0]); +%! [x, y, z] = sph2cart (t, p, r); +%! assert (x, [0, 1, 2]); +%! assert (y, [0, 0, 0]); +%! assert (z, [0, 0, 0]); %!test %! t = [0, 0, 0]; @@ -123,8 +160,18 @@ %!test %! S = [ 0, 0, 1; 0.5*pi, 0, 1; pi, 0, 1]; -%! C = [ 1, 0, 0; 0, 1, 0; -1, 0, 0]; -%! assert (sph2cart (S), C, eps); +%! [x, y, z] = sph2cart (S); +%! assert (x, [1; 0; -1], eps); +%! assert (y, [0; 1; 0], eps); +%! assert (z, [0; 0; 0], eps); + +%!test +%! S = [ 0, 0, 1; 0.5*pi, 0, 1; pi, 0, 1; pi, pi, 1]; +%! [x, y, z] = sph2cart (S); +%! assert (x, [1; 0; -1; 1], eps); +%! assert (y, [0; 1; 0; 0], eps); +%! assert (z, [0; 0; 0; 0], eps); + %!test %! [t, p, r] = meshgrid ([0, pi/2], [0, pi/2], [0, 1]); @@ -143,11 +190,13 @@ %!error sph2cart () %!error sph2cart (1,2) %!error sph2cart (1,2,3,4) -%!error sph2cart ({1,2,3}) -%!error sph2cart (ones (3,3,2)) -%!error sph2cart ([1,2,3,4]) -%!error sph2cart ({1,2,3}, [1,2,3], [1,2,3]) -%!error sph2cart ([1,2,3], {1,2,3}, [1,2,3]) -%!error sph2cart ([1,2,3], [1,2,3], {1,2,3}) -%!error sph2cart (ones (3,3,3), 1, ones (3,2,3)) -%!error sph2cart (ones (3,3,3), ones (3,2,3), 1) +%!error sph2cart ({1,2,3}) +%!error sph2cart (ones (3,3,2)) +%!error sph2cart ([1,2,3,4]) +%!error sph2cart ([1,2,3,4; 1,2,3,4; 1,2,3,4]) +%!error sph2cart ({1,2,3}, [1,2,3], [1,2,3]) +%!error sph2cart ([1,2,3], {1,2,3}, [1,2,3]) +%!error sph2cart ([1,2,3], [1,2,3], {1,2,3}) +%!error sph2cart ([1,2,3], [1,2,3], [1,2,3]') +%!error sph2cart (ones (3,3,3), 1, ones (3,2,3)) +%!error sph2cart (ones (3,3,3), ones (3,2,3), 1) diff -r 5e49ba5bdcc1 -r a23da76e0693 scripts/plot/draw/lightangle.m --- a/scripts/plot/draw/lightangle.m Sun Mar 22 19:23:03 2020 +0100 +++ b/scripts/plot/draw/lightangle.m Sun Feb 16 20:19:05 2020 -0500 @@ -122,7 +122,7 @@ pos -= get (hax, "CameraTarget"); endif - pos = sph2cart (az, el, norm (pos)); + [pos(1), pos(2), pos(3)] = sph2cart (az, el, norm (pos)); if (strcmp (get (hl, "Style"), "local")) pos += get (hax, "CameraTarget"); diff -r 5e49ba5bdcc1 -r a23da76e0693 scripts/plot/draw/surfl.m --- a/scripts/plot/draw/surfl.m Sun Mar 22 19:23:03 2020 +0100 +++ b/scripts/plot/draw/surfl.m Sun Feb 16 20:19:05 2020 -0500 @@ -156,7 +156,7 @@ ## Get view vector (vv). [az, el] = view (); - vv = sph2cart ((az - 90) * pi/180.0, el * pi/180.0, 1.0); + [vv(1), vv(2), vv(3)] = sph2cart ((az - 90) * pi/180.0, el * pi/180.0, 1.0); if (! have_lv) ## Calculate light vector (lv) from view vector.