"""
Coordinates System Transformations
==================================
Provide transformations between different coordinate systems.
This module implements transformations between Cartesian coordinates and
various other coordinate systems commonly used in colour science and
computer graphics applications. The transformations preserve the spatial
relationships while expressing positions in different mathematical
representations.
The following transformations are available:
- :func:`colour.algebra.cartesian_to_spherical`: Transform from Cartesian
to spherical coordinate system.
- :func:`colour.algebra.spherical_to_cartesian`: Transform from spherical
to Cartesian coordinate system.
- :func:`colour.algebra.cartesian_to_polar`: Transform from Cartesian to
polar coordinate system.
- :func:`colour.algebra.polar_to_cartesian`: Transform from polar to
Cartesian coordinate system.
- :func:`colour.algebra.cartesian_to_cylindrical`: Transform from
Cartesian to cylindrical coordinate system.
- :func:`colour.algebra.cylindrical_to_cartesian`: Transform from
cylindrical to Cartesian coordinate system.
References
----------
- :cite:`Wikipedia2005a` : Wikipedia. (2005). ISO 31-11. Retrieved July 31,
2016, from https://en.wikipedia.org/wiki/ISO_31-11
- :cite:`Wikipedia2006` : Wikipedia. (2006). List of common coordinate
transformations. Retrieved July 18, 2014, from
http://en.wikipedia.org/wiki/List_of_common_coordinate_transformations
"""
from __future__ import annotations
import typing
import numpy as np
from colour.algebra import sdiv, sdiv_mode
if typing.TYPE_CHECKING:
from colour.hints import ArrayLike, NDArrayFloat
from colour.utilities import as_float_array, tsplit, tstack
__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"
__all__ = [
"cartesian_to_spherical",
"spherical_to_cartesian",
"cartesian_to_polar",
"polar_to_cartesian",
"cartesian_to_cylindrical",
"cylindrical_to_cartesian",
]
[docs]
def cartesian_to_spherical(a: ArrayLike) -> NDArrayFloat:
"""
Transform specified cartesian coordinates array :math:`xyz` to spherical
coordinates array :math:`\\rho\\theta\\phi` (radial distance, inclination
or elevation and azimuth).
Parameters
----------
a
Cartesian coordinates array :math:`xyz` to transform.
Returns
-------
:class:`numpy.ndarray`
Spherical coordinates array :math:`\\rho\\theta\\phi`, :math:`\\rho`
is in range [0, +inf], :math:`\\theta` is in range [0, pi] radians,
i.e., [0, 180] degrees, and :math:`\\phi` is in range [-pi, pi]
radians, i.e., [-180, 180] degrees.
References
----------
:cite:`Wikipedia2006`, :cite:`Wikipedia2005a`
Examples
--------
>>> a = np.array([3, 1, 6])
>>> cartesian_to_spherical(a) # doctest: +ELLIPSIS
array([6.7823299..., 0.4850497..., 0.3217505...])
"""
x, y, z = tsplit(a)
rho = np.linalg.norm(a, axis=-1)
with sdiv_mode():
theta = np.arccos(sdiv(z, rho))
phi = np.arctan2(y, x)
return tstack([rho, theta, phi])
[docs]
def spherical_to_cartesian(a: ArrayLike) -> NDArrayFloat:
"""
Transform specified spherical coordinates array :math:`\\rho\\theta\\phi`
(radial distance, inclination or elevation and azimuth) to cartesian
coordinates array :math:`xyz`.
Parameters
----------
a
Spherical coordinates array :math:`\\rho\\theta\\phi` to transform,
:math:`\\rho` is in range [0, +inf], :math:`\\theta` is in range
[0, pi] radians, i.e., [0, 180] degrees, and :math:`\\phi` is in
range [-pi, pi] radians, i.e., [-180, 180] degrees.
Returns
-------
:class:`numpy.ndarray`
Cartesian coordinates array :math:`xyz`.
References
----------
:cite:`Wikipedia2006`, :cite:`Wikipedia2005a`
Examples
--------
>>> a = np.array([6.78232998, 0.48504979, 0.32175055])
>>> spherical_to_cartesian(a) # doctest: +ELLIPSIS
array([3.0000000..., 0.9999999..., 5.9999999...])
"""
rho, theta, phi = tsplit(a)
x = rho * np.sin(theta) * np.cos(phi)
y = rho * np.sin(theta) * np.sin(phi)
z = rho * np.cos(theta)
return tstack([x, y, z])
[docs]
def cartesian_to_polar(a: ArrayLike) -> NDArrayFloat:
"""
Transform specified cartesian coordinates array :math:`xy` to polar
coordinates array :math:`\\rho\\phi` (radial coordinate, angular
coordinate).
Parameters
----------
a
Cartesian coordinates array :math:`xy` to transform.
Returns
-------
:class:`numpy.ndarray`
Polar coordinates array :math:`\\rho\\phi`, :math:`\\rho` is in range
[0, +inf], :math:`\\phi` is in range [-pi, pi] radians, i.e., [-180,
180] degrees.
References
----------
:cite:`Wikipedia2006`, :cite:`Wikipedia2005a`
Examples
--------
>>> a = np.array([3, 1])
>>> cartesian_to_polar(a) # doctest: +ELLIPSIS
array([3.1622776..., 0.3217505...])
"""
x, y = tsplit(a)
rho = np.hypot(x, y)
phi = np.arctan2(y, x)
return tstack([rho, phi])
[docs]
def polar_to_cartesian(a: ArrayLike) -> NDArrayFloat:
"""
Transform specified polar coordinates array :math:`\\rho\\phi` (radial
coordinate, angular coordinate) to Cartesian coordinates array :math:`xy`.
Parameters
----------
a
Polar coordinates array :math:`\\rho\\phi` to transform where
:math:`\\rho` is the radial coordinate in range [0, +inf] and
:math:`\\phi` is the angular coordinate in range [-pi, pi] radians
(i.e., [-180, 180] degrees).
Returns
-------
:class:`numpy.ndarray`
Cartesian coordinates array :math:`xy`.
References
----------
:cite:`Wikipedia2006`, :cite:`Wikipedia2005a`
Examples
--------
>>> a = np.array([3.16227766, 0.32175055])
>>> polar_to_cartesian(a) # doctest: +ELLIPSIS
array([3. , 0.9999999...])
"""
rho, phi = tsplit(a)
x = rho * np.cos(phi)
y = rho * np.sin(phi)
return tstack([x, y])
[docs]
def cartesian_to_cylindrical(a: ArrayLike) -> NDArrayFloat:
"""
Transform specified cartesian coordinates array :math:`xyz` to cylindrical
coordinates array :math:`\\rho\\phi z` (radial distance, azimuth and
height).
Parameters
----------
a
Cartesian coordinates array :math:`xyz` to transform.
Returns
-------
:class:`numpy.ndarray`
Cylindrical coordinates array :math:`\\rho\\phi z`, :math:`\\rho` is
in range [0, +inf], :math:`\\phi` is in range [-pi, pi] radians
i.e., [-180, 180] degrees, :math:`z` is in range [0, +inf].
References
----------
:cite:`Wikipedia2006`, :cite:`Wikipedia2005a`
Examples
--------
>>> a = np.array([3, 1, 6])
>>> cartesian_to_cylindrical(a) # doctest: +ELLIPSIS
array([3.1622776..., 0.3217505..., 6. ])
"""
a = as_float_array(a)
rho, phi = tsplit(cartesian_to_polar(a[..., 0:2]))
return tstack([rho, phi, a[..., -1]])
[docs]
def cylindrical_to_cartesian(a: ArrayLike) -> NDArrayFloat:
"""
Transform specified cylindrical coordinates array :math:`\\rho\\phi z`
(radial distance, azimuth and height) to cartesian coordinates array
:math:`xyz`.
Parameters
----------
a
Cylindrical coordinates array :math:`\\rho\\phi z` to transform,
where :math:`\\rho` is in range [0, +inf], :math:`\\phi` is in
range [-pi, pi] radians i.e., [-180, 180] degrees, and :math:`z`
is in range [0, +inf].
Returns
-------
:class:`numpy.ndarray`
Cartesian coordinates array :math:`xyz`.
References
----------
:cite:`Wikipedia2006`, :cite:`Wikipedia2005a`
Examples
--------
>>> a = np.array([3.16227766, 0.32175055, 6.00000000])
>>> cylindrical_to_cartesian(a) # doctest: +ELLIPSIS
array([3. , 0.9999999..., 6. ])
"""
a = as_float_array(a)
x, y = tsplit(polar_to_cartesian(a[..., 0:2]))
return tstack([x, y, a[..., -1]])