import numpy as np
[docs]
class Vector:
    """
    A class for representing Cartesian vectors with ``x``, ``y`` and ``z``
    components that are either ``float`` or ``numpy.array`` objects of
    identical size.
    Attributes
    ----------
    x : float or numpy.ndarray
        The x component(s)
    y : float or numpy.ndarray
        The y component(s)
    z : float or numpy.ndarray
        The z component(s)
    """
[docs]
    def __init__(self, x, y, z):
        """
        A class for representing Cartesian vectors with ``x``, ``y`` and ``z``
        components that are either ``float`` or ``numpy.array`` objects of
        identical size.
        Parameters
        ----------
        x : float or numpy.ndarray
            The x component(s)
        y : float or numpy.ndarray
            The y component(s)
        z : float or numpy.ndarray
            The z component(s)
        """
        self.x = x
        self.y = y
        self.z = z 
    def angular_distance(self, other):
        """
        Compute angular distance between points on the sphere, following:
        https://en.wikipedia.org/wiki/Great-circle_distance
        Parameters
        ----------
        other : mpas_tools.vector.Vector
            The vector to compute the angular distance to
        Returns
        -------
        angularDistance : numpy.ndarray
            The angular distance (in radians) between segments of the transect.
        """
        angular_distance = np.arctan2(self.cross(other).mag(), self.dot(other))
        return angular_distance
    @staticmethod
    def intersects(a1, a2, b1, b2):
        """
        Based on https://stackoverflow.com/a/26669130/7728169
        Determine if the great circle arc from ``a1`` to ``a2`` intersects that
        from ``b1`` to ``b2``.
        Parameters
        ----------
        a1 : mpas_tools.vector.Vector
            Cartesian coordinates of the end point of a great circle arc.
            The types of the attributes ``x``, ``y``, and ``z`` must either be
            ``numpy.arrays`` of identical size for all 4 vectors (in which case
            intersections are found element-wise), or scalars for
            at least one of either ``a1`` and ``a2`` or ``b1`` and ``b2``.
        a2 : mpas_tools.vector.Vector
            Cartesian coordinates of the other end point of a great circle arc.
        b1 : mpas_tools.vector.Vector
            Cartesian coordinates of an end point of a second great circle arc.
        b2 : mpas_tools.vector.Vector
            Cartesian coordinates of the other end point of the second great
            circle arc.
        Returns
        -------
        intersect : numpy.ndarray
            A boolean array of the same size as ``a1`` and ``a2`` or ``b1`` and
            ``b2``, whichever is greater, indicating if the particular pair of
            arcs intersects
        """
        return np.logical_and(Vector.straddles(a1, a2, b1, b2),
                              Vector.straddles(b1, b2, a1, a2))
    @staticmethod
    def intersection(a1, a2, b1, b2):
        """
        Based on https://stackoverflow.com/a/26669130/7728169
        Find the intersection point as a unit vector between great circle arc
        from ``a1`` to ``a2`` and from ``b1`` to ``b2``.  The arcs should have
        already have been found to intersect by calling ``intersects()``
        Parameters
        ----------
        a1 : mpas_tools.vector.Vector
            Cartesian coordinates of the end point of a great circle arc.
            The types of the attributes ``x``, ``y``, and ``z`` must either be
            ``numpy.arrays`` of identical size for all 4 vectors (in which case
            intersections are found element-wise), or scalars for
            at least one of either ``a1`` and ``a2`` or ``b1`` and ``b2``.
        a2 : mpas_tools.vector.Vector
            Cartesian coordinates of the other end point of a great circle arc.
        b1 : mpas_tools.vector.Vector
            Cartesian coordinates of an end point of a second great circle arc.
        b2 : mpas_tools.vector.Vector
            Cartesian coordinates of the other end point of the second great
            circle arc.
        Returns
        -------
        points : mpas_tools.vector.Vector
            An array of Cartesian points *on the unit sphere* indicating where
            the arcs intersect
        """
        points = (a1.cross(a2)).cross(b1.cross(b2))
        s = np.sign(Vector.det(a1, b1, b2))/points.mag()
        points = Vector(s*points.x,  s*points.y, s*points.z)
        return points
    @staticmethod
    def straddles(a1, a2, b1, b2):
        """
        Based on https://stackoverflow.com/a/26669130/7728169
        Determines if the great circle segment determined by (a1, a2)
        straddles the great circle determined by (b1, b2)
        Parameters
        ----------
        a1 : mpas_tools.vector.Vector
            Cartesian coordinates of first end point of first great circle arc.
            The types of the attributes ``x``, ``y``, and ``z`` must either be
            ``numpy.arrays`` of identical size for all 4 vectors (in which case
            intersections are found element-wise), or scalars for
            at least one of either the a's or the b's.
        a2 : mpas_tools.vector.Vector
            Second end point of first great circle arc.
        b1 : mpas_tools.vector.Vector
            First end point of second great circle arc.
        b2 : mpas_tools.vector.Vector
            Second end point of second great circle arc.
        Returns
        -------
        straddle : numpy.ndarray
            A boolean array of the same size as the a's or the b's, whichever
            is greater, indicating if the great circle segment determined by
            (a1, a2) straddles the great circle determined by (b1, b2)
        """
        return Vector.det(a1, b1, b2) * Vector.det(a2, b1, b2) < 0
    def dot(self, other):
        """
        Compute the dot product between this vector and ``other``.
        Parameters
        ----------
        other : mpas_tools.vector.Vector
            The other vector
        Returns
        -------
        dot_product : numpy.ndarray
            The dot product
        """
        return self.x * other.x + self.y * other.y + self.z * other.z
    def cross(self, other):
        """
        Compute the dot product between this vector and ``other``.
        Parameters
        ----------
        other : mpas_tools.vector.Vector
            The other vector
        Returns
        -------
        cross_product : mpas_tools.vector.Vector
            The cross product
        """
        return Vector(self.y * other.z - self.z * other.y,
                      self.z * other.x - self.x * other.z,
                      self.x * other.y - self.y * other.x)
    @staticmethod
    def det(v1, v2, v3):
        """
        The determinant of the matrix defined by the three ``Vector`` objects
        Parameters
        ----------
        v1 : mpas_tools.vector.Vector
            First row of the matrix
        v2 : mpas_tools.vector.Vector
            Second row
        v3 : mpas_tools.vector.Vector
            Third row
        Returns
        -------
        determinant : numpy.ndarray
            The determinant of the matrix
        """
        return v1.dot(v2.cross(v3))
    def mag(self):
        """
        The magnitude of the vector
        Returns
        -------
        magnitude : numpy.ndarray
            The magnitude of the vector
        """
        return np.sqrt(self.dot(self))