Vector2D.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.geometry.euclidean.twod;

  22. import java.text.NumberFormat;

  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.exception.MathRuntimeException;
  26. import org.hipparchus.geometry.Space;
  27. import org.hipparchus.geometry.Vector;
  28. import org.hipparchus.util.FastMath;
  29. import org.hipparchus.util.MathArrays;
  30. import org.hipparchus.util.MathUtils;

  31. /** This class represents a 2D vector.
  32.  * <p>Instances of this class are guaranteed to be immutable.</p>
  33.  */
  34. public class Vector2D implements Vector<Euclidean2D, Vector2D> {

  35.     /** Origin (coordinates: 0, 0). */
  36.     public static final Vector2D ZERO   = new Vector2D(0, 0);

  37.     /** First canonical vector (coordinates: 1, 0).
  38.      * @since 1.6
  39.      */
  40.     public static final Vector2D PLUS_I = new Vector2D(1, 0);

  41.     /** Opposite of the first canonical vector (coordinates: -1, 0).
  42.      * @since 1.6
  43.      */
  44.     public static final Vector2D MINUS_I = new Vector2D(-1, 0);

  45.     /** Second canonical vector (coordinates: 0, 1).
  46.      * @since 1.6
  47.      */
  48.     public static final Vector2D PLUS_J = new Vector2D(0, 1);

  49.     /** Opposite of the second canonical vector (coordinates: 0, -1).
  50.      * @since 1.6
  51.      */
  52.     public static final Vector2D MINUS_J = new Vector2D(0, -1);

  53.     // CHECKSTYLE: stop ConstantName
  54.     /** A vector with all coordinates set to NaN. */
  55.     public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
  56.     // CHECKSTYLE: resume ConstantName

  57.     /** A vector with all coordinates set to positive infinity. */
  58.     public static final Vector2D POSITIVE_INFINITY =
  59.         new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);

  60.     /** A vector with all coordinates set to negative infinity. */
  61.     public static final Vector2D NEGATIVE_INFINITY =
  62.         new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);

  63.     /** Serializable UID. */
  64.     private static final long serialVersionUID = 266938651998679754L;

  65.     /** Abscissa. */
  66.     private final double x;

  67.     /** Ordinate. */
  68.     private final double y;

  69.     /** Simple constructor.
  70.      * Build a vector from its coordinates
  71.      * @param x abscissa
  72.      * @param y ordinate
  73.      * @see #getX()
  74.      * @see #getY()
  75.      */
  76.     public Vector2D(double x, double y) {
  77.         this.x = x;
  78.         this.y = y;
  79.     }

  80.     /** Simple constructor.
  81.      * Build a vector from its coordinates
  82.      * @param v coordinates array
  83.      * @exception MathIllegalArgumentException if array does not have 2 elements
  84.      * @see #toArray()
  85.      */
  86.     public Vector2D(double[] v) throws MathIllegalArgumentException {
  87.         if (v.length != 2) {
  88.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  89.                                                    v.length, 2);
  90.         }
  91.         this.x = v[0];
  92.         this.y = v[1];
  93.     }

  94.     /** Multiplicative constructor
  95.      * Build a vector from another one and a scale factor.
  96.      * The vector built will be a * u
  97.      * @param a scale factor
  98.      * @param u base (unscaled) vector
  99.      */
  100.     public Vector2D(double a, Vector2D u) {
  101.         this.x = a * u.x;
  102.         this.y = a * u.y;
  103.     }

  104.     /** Linear constructor
  105.      * Build a vector from two other ones and corresponding scale factors.
  106.      * The vector built will be a1 * u1 + a2 * u2
  107.      * @param a1 first scale factor
  108.      * @param u1 first base (unscaled) vector
  109.      * @param a2 second scale factor
  110.      * @param u2 second base (unscaled) vector
  111.      */
  112.     public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2) {
  113.         this.x = a1 * u1.x + a2 * u2.x;
  114.         this.y = a1 * u1.y + a2 * u2.y;
  115.     }

  116.     /** Linear constructor
  117.      * Build a vector from three other ones and corresponding scale factors.
  118.      * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
  119.      * @param a1 first scale factor
  120.      * @param u1 first base (unscaled) vector
  121.      * @param a2 second scale factor
  122.      * @param u2 second base (unscaled) vector
  123.      * @param a3 third scale factor
  124.      * @param u3 third base (unscaled) vector
  125.      */
  126.     public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
  127.                    double a3, Vector2D u3) {
  128.         this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
  129.         this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
  130.     }

  131.     /** Linear constructor
  132.      * Build a vector from four other ones and corresponding scale factors.
  133.      * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
  134.      * @param a1 first scale factor
  135.      * @param u1 first base (unscaled) vector
  136.      * @param a2 second scale factor
  137.      * @param u2 second base (unscaled) vector
  138.      * @param a3 third scale factor
  139.      * @param u3 third base (unscaled) vector
  140.      * @param a4 fourth scale factor
  141.      * @param u4 fourth base (unscaled) vector
  142.      */
  143.     public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
  144.                    double a3, Vector2D u3, double a4, Vector2D u4) {
  145.         this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
  146.         this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y;
  147.     }

  148.     /** Get the abscissa of the vector.
  149.      * @return abscissa of the vector
  150.      * @see #Vector2D(double, double)
  151.      */
  152.     public double getX() {
  153.         return x;
  154.     }

  155.     /** Get the ordinate of the vector.
  156.      * @return ordinate of the vector
  157.      * @see #Vector2D(double, double)
  158.      */
  159.     public double getY() {
  160.         return y;
  161.     }

  162.     /** Get the vector coordinates as a dimension 2 array.
  163.      * @return vector coordinates
  164.      * @see #Vector2D(double[])
  165.      */
  166.     public double[] toArray() {
  167.         return new double[] { x, y };
  168.     }

  169.     /** {@inheritDoc} */
  170.     @Override
  171.     public Space getSpace() {
  172.         return Euclidean2D.getInstance();
  173.     }

  174.     /** {@inheritDoc} */
  175.     @Override
  176.     public Vector2D getZero() {
  177.         return ZERO;
  178.     }

  179.     /** {@inheritDoc} */
  180.     @Override
  181.     public double getNorm1() {
  182.         return FastMath.abs(x) + FastMath.abs(y);
  183.     }

  184.     /** {@inheritDoc} */
  185.     @Override
  186.     public double getNorm() {
  187.         return FastMath.sqrt (x * x + y * y);
  188.     }

  189.     /** {@inheritDoc} */
  190.     @Override
  191.     public double getNormSq() {
  192.         return x * x + y * y;
  193.     }

  194.     /** {@inheritDoc} */
  195.     @Override
  196.     public double getNormInf() {
  197.         return FastMath.max(FastMath.abs(x), FastMath.abs(y));
  198.     }

  199.     /** {@inheritDoc} */
  200.     @Override
  201.     public Vector2D add(Vector2D v) {
  202.         return new Vector2D(x + v.getX(), y + v.getY());
  203.     }

  204.     /** {@inheritDoc} */
  205.     @Override
  206.     public Vector2D add(double factor, Vector2D v) {
  207.         return new Vector2D(x + factor * v.getX(), y + factor * v.getY());
  208.     }

  209.     /** {@inheritDoc} */
  210.     @Override
  211.     public Vector2D subtract(Vector2D p) {
  212.         return new Vector2D(x - p.x, y - p.y);
  213.     }

  214.     /** {@inheritDoc} */
  215.     @Override
  216.     public Vector2D subtract(double factor, Vector2D v) {
  217.         return new Vector2D(x - factor * v.getX(), y - factor * v.getY());
  218.     }

  219.     /** Compute the angular separation between two vectors.
  220.      * <p>This method computes the angular separation between two
  221.      * vectors using the dot product for well separated vectors and the
  222.      * cross product for almost aligned vectors. This allows to have a
  223.      * good accuracy in all cases, even for vectors very close to each
  224.      * other.</p>
  225.      * @param v1 first vector
  226.      * @param v2 second vector
  227.      * @return angular separation between v1 and v2
  228.      * @exception MathRuntimeException if either vector has a null norm
  229.      */
  230.     public static double angle(Vector2D v1, Vector2D v2) throws MathRuntimeException {

  231.         double normProduct = v1.getNorm() * v2.getNorm();
  232.         if (normProduct == 0) {
  233.             throw new MathRuntimeException(LocalizedCoreFormats.ZERO_NORM);
  234.         }

  235.         double dot = v1.dotProduct(v2);
  236.         double threshold = normProduct * 0.9999;
  237.         if (FastMath.abs(dot) > threshold) {
  238.             // the vectors are almost aligned, compute using the sine
  239.             final double n = FastMath.abs(MathArrays.linearCombination(v1.x, v2.y, -v1.y, v2.x));
  240.             if (dot >= 0) {
  241.                 return FastMath.asin(n / normProduct);
  242.             }
  243.             return FastMath.PI - FastMath.asin(n / normProduct);
  244.         }

  245.         // the vectors are sufficiently separated to use the cosine
  246.         return FastMath.acos(dot / normProduct);

  247.     }

  248.     /** {@inheritDoc} */
  249.     @Override
  250.     public Vector2D negate() {
  251.         return new Vector2D(-x, -y);
  252.     }

  253.     /** {@inheritDoc} */
  254.     @Override
  255.     public Vector2D scalarMultiply(double a) {
  256.         return new Vector2D(a * x, a * y);
  257.     }

  258.     /** {@inheritDoc} */
  259.     @Override
  260.     public boolean isNaN() {
  261.         return Double.isNaN(x) || Double.isNaN(y);
  262.     }

  263.     /** {@inheritDoc} */
  264.     @Override
  265.     public boolean isInfinite() {
  266.         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
  267.     }

  268.     /** {@inheritDoc} */
  269.     @Override
  270.     public double distance1(Vector2D p) {
  271.         final double dx = FastMath.abs(p.x - x);
  272.         final double dy = FastMath.abs(p.y - y);
  273.         return dx + dy;
  274.     }

  275.     /** {@inheritDoc} */
  276.     @Override
  277.     public double distance(Vector2D p) {
  278.         final double dx = p.x - x;
  279.         final double dy = p.y - y;
  280.         return FastMath.sqrt(dx * dx + dy * dy);
  281.     }

  282.     /** {@inheritDoc} */
  283.     @Override
  284.     public double distanceInf(Vector2D p) {
  285.         final double dx = FastMath.abs(p.x - x);
  286.         final double dy = FastMath.abs(p.y - y);
  287.         return FastMath.max(dx, dy);
  288.     }

  289.     /** {@inheritDoc} */
  290.     @Override
  291.     public double distanceSq(Vector2D p) {
  292.         final double dx = p.x - x;
  293.         final double dy = p.y - y;
  294.         return dx * dx + dy * dy;
  295.     }

  296.     /** {@inheritDoc} */
  297.     @Override
  298.     public double dotProduct(final Vector2D v) {
  299.         return MathArrays.linearCombination(x, v.x, y, v.y);
  300.     }

  301.     /**
  302.      * Compute the cross-product of the instance and the given points.
  303.      * <p>
  304.      * The cross product can be used to determine the location of a point
  305.      * with regard to the line formed by (p1, p2) and is calculated as:
  306.      * \[
  307.      *    P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1)
  308.      * \]
  309.      * with \(p3 = (x_3, y_3)\) being this instance.
  310.      * <p>
  311.      * If the result is 0, the points are collinear, i.e. lie on a single straight line L;
  312.      * if it is positive, this point lies to the left, otherwise to the right of the line
  313.      * formed by (p1, p2).
  314.      *
  315.      * @param p1 first point of the line
  316.      * @param p2 second point of the line
  317.      * @return the cross-product
  318.      *
  319.      * @see <a href="http://en.wikipedia.org/wiki/Cross_product">Cross product (Wikipedia)</a>
  320.      */
  321.     public double crossProduct(final Vector2D p1, final Vector2D p2) {
  322.         final double x1 = p2.getX() - p1.getX();
  323.         final double y1 = getY() - p1.getY();
  324.         final double x2 = getX() - p1.getX();
  325.         final double y2 = p2.getY() - p1.getY();
  326.         return MathArrays.linearCombination(x1, y1, -x2, y2);
  327.     }

  328.     /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
  329.      * <p>Calling this method is equivalent to calling:
  330.      * <code>p1.subtract(p2).getNorm1()</code> except that no intermediate
  331.      * vector is built</p>
  332.      * @param p1 first vector
  333.      * @param p2 second vector
  334.      * @return the distance between p1 and p2 according to the L<sub>1</sub> norm
  335.      * @since 1.6
  336.      */
  337.     public static double distance1(Vector2D p1, Vector2D p2) {
  338.         return p1.distance1(p2);
  339.     }

  340.     /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
  341.      * <p>Calling this method is equivalent to calling:
  342.      * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
  343.      * vector is built</p>
  344.      * @param p1 first vector
  345.      * @param p2 second vector
  346.      * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
  347.      */
  348.     public static double distance(Vector2D p1, Vector2D p2) {
  349.         return p1.distance(p2);
  350.     }

  351.     /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
  352.      * <p>Calling this method is equivalent to calling:
  353.      * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
  354.      * vector is built</p>
  355.      * @param p1 first vector
  356.      * @param p2 second vector
  357.      * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
  358.      */
  359.     public static double distanceInf(Vector2D p1, Vector2D p2) {
  360.         return p1.distanceInf(p2);
  361.     }

  362.     /** Compute the square of the distance between two vectors.
  363.      * <p>Calling this method is equivalent to calling:
  364.      * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
  365.      * vector is built</p>
  366.      * @param p1 first vector
  367.      * @param p2 second vector
  368.      * @return the square of the distance between p1 and p2
  369.      */
  370.     public static double distanceSq(Vector2D p1, Vector2D p2) {
  371.         return p1.distanceSq(p2);
  372.     }

  373.     /** {@inheritDoc} */
  374.     @Override
  375.     public Vector2D moveTowards(final Vector2D other, final double ratio) {
  376.         return new Vector2D(x + ratio * (other.x - x),
  377.                             y + ratio * (other.y - y));
  378.     }

  379.      /** Compute the orientation of a triplet of points.
  380.      * @param p first vector of the triplet
  381.      * @param q second vector of the triplet
  382.      * @param r third vector of the triplet
  383.      * @return a positive value if (p, q, r) defines a counterclockwise oriented
  384.      * triangle, a negative value if (p, q, r) defines a clockwise oriented
  385.      * triangle, and 0 if (p, q, r) are collinear or some points are equal
  386.      * @since 1.2
  387.      */
  388.     public static double orientation(final Vector2D p, final Vector2D q, final Vector2D r) {
  389.         return MathArrays.linearCombination(new double[] {
  390.             p.getX(), -p.getX(), q.getX(), -q.getX(), r.getX(), -r.getX()
  391.         }, new double[] {
  392.             q.getY(),  r.getY(), r.getY(),  p.getY(), p.getY(),  q.getY()
  393.         });
  394.     }

  395.     /**
  396.      * Test for the equality of two 2D vectors.
  397.      * <p>
  398.      * If all coordinates of two 2D vectors are exactly the same, and none are
  399.      * {@code Double.NaN}, the two 2D vectors are considered to be equal.
  400.      * </p>
  401.      * <p>
  402.      * {@code NaN} coordinates are considered to affect globally the vector
  403.      * and be equals to each other - i.e, if either (or all) coordinates of the
  404.      * 2D vector are equal to {@code Double.NaN}, the 2D vector is equal to
  405.      * {@link #NaN}.
  406.      * </p>
  407.      *
  408.      * @param other Object to test for equality to this
  409.      * @return true if two 2D vector objects are equal, false if
  410.      *         object is null, not an instance of Vector2D, or
  411.      *         not equal to this Vector2D instance
  412.      */
  413.     @Override
  414.     public boolean equals(Object other) {

  415.         if (this == other) {
  416.             return true;
  417.         }

  418.         if (other instanceof Vector2D) {
  419.             final Vector2D rhs = (Vector2D)other;
  420.             return x == rhs.x && y == rhs.y || isNaN() && rhs.isNaN();
  421.         }

  422.         return false;

  423.     }

  424.     /**
  425.      * Test for the equality of two 2D vectors.
  426.      * <p>
  427.      * If all coordinates of two 2D vectors are exactly the same, and none are
  428.      * {@code NaN}, the two 2D vectors are considered to be equal.
  429.      * </p>
  430.      * <p>
  431.      * In compliance with IEEE754 handling, if any coordinates of any of the
  432.      * two vectors are {@code NaN}, then the vectors are considered different.
  433.      * This implies that {@link #NaN Vector2D.NaN}.equals({@link #NaN Vector2D.NaN})
  434.      * returns {@code false} despite the instance is checked against itself.
  435.      * </p>
  436.      *
  437.      * @param other Object to test for equality to this
  438.      * @return true if two 2D vector objects are equal, false if
  439.      *         object is null, not an instance of Vector2D, or
  440.      *         not equal to this Vector2D instance
  441.      * @since 2.1
  442.      */
  443.     public boolean equalsIeee754(Object other) {

  444.         if (this == other && !isNaN()) {
  445.             return true;
  446.         }

  447.         if (other instanceof Vector2D) {
  448.             final Vector2D rhs = (Vector2D) other;
  449.             return x == rhs.x && y == rhs.y;
  450.         }
  451.         return false;
  452.     }

  453.     /**
  454.      * Get a hashCode for the 2D vector.
  455.      * <p>
  456.      * All NaN values have the same hash code.</p>
  457.      *
  458.      * @return a hash code value for this object
  459.      */
  460.     @Override
  461.     public int hashCode() {
  462.         if (isNaN()) {
  463.             return 542;
  464.         }
  465.         return 122 * (76 * MathUtils.hash(x) +  MathUtils.hash(y));
  466.     }

  467.     /** Get a string representation of this vector.
  468.      * @return a string representation of this vector
  469.      */
  470.     @Override
  471.     public String toString() {
  472.         return Vector2DFormat.getVector2DFormat().format(this);
  473.     }

  474.     /** {@inheritDoc} */
  475.     @Override
  476.     public String toString(final NumberFormat format) {
  477.         return new Vector2DFormat(format).format(this);
  478.     }

  479. }