View Raw SPL
/*****************************************************************************
*                                                                            *
*   CART2SPH.SPL  Copyright (C) 2022 DSP Development Corporation             *
*                               All Rights Reserved                          *
*                                                                            *
*   Author:      Randy Race                                                  *
*                                                                            *
*   Synopsis:    Spherical coordinates to XYZ                                *
*                                                                            *
*   Revisions:   15 May 2022  RRR  Creation                                  *
*                                                                            *
*****************************************************************************/

#if @HELP_CART2SPH

    CART2SPH

    Purpose: Convert Cartesian XYZ coordinates to spherical coordinates.

    Syntax:  CART2SPH(x, y, z, "degrees")

                       x - A real or series, the X coordinate.

                       y - A real or series, the Y coordinate.

                       z - A real or series, the Z coordinate.

               "degrees" - Optional. A string, if "degrees", all output angles
                           are in degrees. If "radians" or not specified, the
                           output angles are in radians.

    Alternate Syntax:

             CART2SPH(xyzcoords, "degrees")

               xyzcoords - A 3 column series, where X is the first column,
                           Y the second column and Z the third column.

               "degrees" - Optional. A string, if "degrees", all output angles
                           are in degrees. If "radians" or not specified, the
                           output angles are in radians.


    Returns: A three column series where the first column is azimuth, the
             second column is elevation and the third column is the radius.

             (az, el, r) = CART2SPH(x, y, z) returns the spherical coordinates
             as separate series.

    Example:
             W1: cart2sph(1.0, 1.0, sqrt(2))

             W1 == {{0.785398, 0.785398, 2}}

             W1 transforms the XYZ coordinate with an X of 1 unit, a
             Y of 1 unit and Z of sqrt(2) to a spherical coordinate. 
             The result is a 1x3 array where the azimuth value is pi/4
             radians, the elevation value is pi/4 radians and the
             radius is 2 units.

    Example:
             W1: cart2sph(1.0, 1.0, sqrt(2), "degrees")

             W1 == {{45, 45, 2}}

             Same as above except azimuth and elevation are in degrees.

    Example:
             W2: {{1.0, 1.0, sqrt(2)}}
             W3: cart2sph(w2, "degrees")

             W3 == {{45, 45, 2}}

             Same as above except the input Cartesian coordinates are
             contained in a single 3 column series.

    Example:
             W4: {{1.0, 1.0, sqrt(2)}}
             (az, el, r) = cart2sph(w4, "degrees");

             az == {45}
             el == {45}
             r  == {2}

             Same as above except the output spherical coordinates are
             contained in three separate variables.

    Example:
             W1: {{1.0, 1.0, sqrt(2)}}
             W2: cart2sph(w1)
             W3: sph2cart(w2)

             W3 == {{1.0, 1.0, 1.414214}}

             Demonstrates that CART2SPH and SPH2CART are inverse functions.

    Remarks:
             CART2SPH transforms Cartesian XYZ coordinates to azimuth,
             elevation and radius spherical coordinates.

             AZIMUTH (az) is measured counterclockwise from the positive X
             axis in the X-Y plane. AZIMUTH is in radians unless
             "degrees" is specified. AZIMUTH varies from 0 to 2*pi radians
             or 0 to 360 degrees.

             ELEVATION (el) is measured from the X-Y plane. ELEVATION is in
             radians unless "degrees" is specified. ELEVATION varies from
             -pi/2 to pi/2 radians or -90 to 90 degrees.

             RADIUS (r) is the length of the 3D vector.

             If "degrees" is specified, the output AZIMUTH and ELEVATION
             angles are in degrees. If "radians" or not specified, AZIMUTH
             and ELEVATION are in radians.

    See Also:
             Sph2cart
#endif



/* XYZ coords to spherical */
cart2sph(argv)
{
        local az, el, r;

        /* parse args */
        (x, y, z, dflag) = cart2sph_parse_args(__FUNC__, argv);

        /* sub-expression */
        xy2 = x*x + y*y;

        /* transformation */
        az = atan2(y, x);
        el = atan2(z, sqrt(xy2));
        r  = sqrt(xy2 + z*z);

        if (dflag == "degrees")
        {
                az *= 180 / pi;
                el *= 180 / pi;
        }

        if (outargc <= 1)
        {
                /* force series for ravel */
                az = {az};
                el = {el};
                r  = {r};
        }

        /* add comments */
        if (isarray(az))
        {
                setcomment(az, "Azimuth");
        }

        if (isarray(el))
        {
                setcomment(el, "Elevation");
        }

        if (isarray(r))
        {
                setcomment(r, "Radius");
        }

        /* output */
        if (outargc > 1)
        {
                return(az, el, r);
        }
        else
        {
                return(ravel(az, el, r));
        }
}
                

/* parse args */
cart2sph_parse_args(func, argv)
{
        local x, y, z, dflag;

        x = y = z = dflag = {};

        loop (j = 1..(argc - 1))
        {
                if (isarray(getargv(j)) || isscalar(getargv(j)))
                {
                        if (isempty(x))
                        {
                                x = getargv(j);
                        }
                        else if (isempty(y))
                        {
                                y = getargv(j);
                        }
                        else if (isempty(z))
                        {
                                z = getargv(j);
                        }
                        else
                        {
                                error(sprintf("%s - too many input arguments", func));
                        }
                }
                else if (isstring(getargv(j)))
                {
                        if (isempty(dflag))
                        {
                                dflag = tolower(getargv(j));
                        }
                }
        }

        if (isempty(x))
        {
                error(sprintf("%s - input XYZ required", func));
        }
        else if (numcols(x) == 3)
        {
                /* x, y, z three column input */
                z = col(x, 3);
                y = col(x, 2);
                x = col(x, 1);
        }

        if (isempty(dflag))
        {
                dflag = "radians";
        }

        if (not(dflag == "radians" || dflag == "degrees"))
        {
                error(sprintf('%s - unknown flag "%s"', func, dflag));
        }

        return(x, y, z, dflag);
}