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.  *      http://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. package org.apache.commons.geometry.euclidean.twod;

  18. import java.util.Arrays;
  19. import java.util.Comparator;
  20. import java.util.Iterator;
  21. import java.util.function.UnaryOperator;

  22. import org.apache.commons.geometry.core.internal.DoubleFunction2N;
  23. import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
  24. import org.apache.commons.geometry.euclidean.EuclideanVectorSum;
  25. import org.apache.commons.geometry.euclidean.MultiDimensionalEuclideanVector;
  26. import org.apache.commons.geometry.euclidean.internal.Vectors;
  27. import org.apache.commons.numbers.core.Precision;

  28. /** This class represents vectors and points in two-dimensional Euclidean space.
  29.  * Instances of this class are guaranteed to be immutable.
  30.  */
  31. public class Vector2D extends MultiDimensionalEuclideanVector<Vector2D> {

  32.     /** Zero vector (coordinates: 0, 0). */
  33.     public static final Vector2D ZERO = new Vector2D(0, 0);

  34.     /** A vector with all coordinates set to NaN. */
  35.     public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);

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

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

  42.     /** Comparator that sorts vectors in component-wise ascending order.
  43.      * Vectors are only considered equal if their coordinates match exactly.
  44.      * Null arguments are evaluated as being greater than non-null arguments.
  45.      */
  46.     public static final Comparator<Vector2D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
  47.         int cmp = 0;

  48.         if (a != null && b != null) {
  49.             cmp = Double.compare(a.getX(), b.getX());
  50.             if (cmp == 0) {
  51.                 cmp = Double.compare(a.getY(), b.getY());
  52.             }
  53.         } else if (a != null) {
  54.             cmp = -1;
  55.         } else if (b != null) {
  56.             cmp = 1;
  57.         }

  58.         return cmp;
  59.     };

  60.     /** Abscissa (first coordinate). */
  61.     private final double x;

  62.     /** Ordinate (second coordinate). */
  63.     private final double y;

  64.     /** Simple constructor.
  65.      * @param x abscissa (first coordinate)
  66.      * @param y ordinate (second coordinate)
  67.      */
  68.     private Vector2D(final double x, final double y) {
  69.         this.x = x;
  70.         this.y = y;
  71.     }

  72.     /** Returns the abscissa (first coordinate value) of the instance.
  73.      * @return the abscissa
  74.      */
  75.     public double getX() {
  76.         return x;
  77.     }

  78.     /** Returns the ordinate (second coordinate value) of the instance.
  79.      * @return the ordinate
  80.      */
  81.     public double getY() {
  82.         return y;
  83.     }

  84.     /** Get the coordinates for this instance as a dimension 2 array.
  85.      * @return coordinates for this instance
  86.      */
  87.     public double[] toArray() {
  88.         return new double[]{x, y};
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public int getDimension() {
  93.         return 2;
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public boolean isNaN() {
  98.         return Double.isNaN(x) || Double.isNaN(y);
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public boolean isInfinite() {
  103.         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
  104.     }

  105.     /** {@inheritDoc} */
  106.     @Override
  107.     public boolean isFinite() {
  108.         return Double.isFinite(x) && Double.isFinite(y);
  109.     }

  110.     /** {@inheritDoc} */
  111.     @Override
  112.     public Vector2D vectorTo(final Vector2D v) {
  113.         return v.subtract(this);
  114.     }

  115.     /** {@inheritDoc} */
  116.     @Override
  117.     public Unit directionTo(final Vector2D v) {
  118.         return vectorTo(v).normalize();
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public Vector2D lerp(final Vector2D p, final double t) {
  123.         return Sum.create()
  124.                 .addScaled(1.0 - t, this)
  125.                 .addScaled(t, p).get();
  126.     }

  127.     /** {@inheritDoc} */
  128.     @Override
  129.     public Vector2D getZero() {
  130.         return ZERO;
  131.     }

  132.     /** {@inheritDoc} */
  133.     @Override
  134.     public double norm() {
  135.         return Vectors.norm(x, y);
  136.     }

  137.     /** {@inheritDoc} */
  138.     @Override
  139.     public double normSq() {
  140.         return Vectors.normSq(x, y);
  141.     }

  142.     /** {@inheritDoc} */
  143.     @Override
  144.     public Vector2D withNorm(final double magnitude) {
  145.         final double invNorm = 1.0 / getCheckedNorm();

  146.         return new Vector2D(
  147.                     magnitude * x * invNorm,
  148.                     magnitude * y * invNorm
  149.                 );
  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     public Vector2D add(final Vector2D v) {
  154.         return new Vector2D(x + v.x, y + v.y);
  155.     }

  156.     /** {@inheritDoc} */
  157.     @Override
  158.     public Vector2D add(final double factor, final Vector2D v) {
  159.         return new Vector2D(x + (factor * v.x), y + (factor * v.y));
  160.     }

  161.     /** {@inheritDoc} */
  162.     @Override
  163.     public Vector2D subtract(final Vector2D v) {
  164.         return new Vector2D(x - v.x, y - v.y);
  165.     }

  166.     /** {@inheritDoc} */
  167.     @Override
  168.     public Vector2D subtract(final double factor, final Vector2D v) {
  169.         return new Vector2D(x - (factor * v.x), y - (factor * v.y));
  170.     }

  171.     /** {@inheritDoc} */
  172.     @Override
  173.     public Vector2D negate() {
  174.         return new Vector2D(-x, -y);
  175.     }

  176.     /** {@inheritDoc} */
  177.     @Override
  178.     public Unit normalize() {
  179.         return Unit.from(x, y);
  180.     }

  181.     /** {@inheritDoc} */
  182.     @Override
  183.     public Unit normalizeOrNull() {
  184.         return Unit.tryCreateNormalized(x, y, false);
  185.     }

  186.     /** {@inheritDoc} */
  187.     @Override
  188.     public Vector2D multiply(final double a) {
  189.         return new Vector2D(a * x, a * y);
  190.     }

  191.     /** {@inheritDoc} */
  192.     @Override
  193.     public double distance(final Vector2D v) {
  194.         return Vectors.norm(x - v.x, y - v.y);
  195.     }

  196.     /** {@inheritDoc} */
  197.     @Override
  198.     public double distanceSq(final Vector2D v) {
  199.         return Vectors.normSq(x - v.x, y - v.y);
  200.     }

  201.     /** {@inheritDoc} */
  202.     @Override
  203.     public double dot(final Vector2D v) {
  204.         return Vectors.linearCombination(x, v.x, y, v.y);
  205.     }

  206.     /** {@inheritDoc}
  207.      * <p>This method computes the angular separation between the two
  208.      * vectors using the dot product for well separated vectors and the
  209.      * cross product for almost aligned vectors. This allows to have a
  210.      * good accuracy in all cases, even for vectors very close to each
  211.      * other.</p>
  212.      */
  213.     @Override
  214.     public double angle(final Vector2D v) {
  215.         final double normProduct = getCheckedNorm() * v.getCheckedNorm();

  216.         final double dot = dot(v);
  217.         final double threshold = normProduct * 0.9999;
  218.         if ((dot < -threshold) || (dot > threshold)) {
  219.             // the vectors are almost aligned, compute using the sine
  220.             final double n = Math.abs(Vectors.linearCombination(x, v.y, -y, v.x));
  221.             if (dot >= 0) {
  222.                 return Math.asin(n / normProduct);
  223.             }
  224.             return Math.PI - Math.asin(n / normProduct);
  225.         }

  226.         // the vectors are sufficiently separated to use the cosine
  227.         return Math.acos(dot / normProduct);
  228.     }

  229.     /** {@inheritDoc} */
  230.     @Override
  231.     public Vector2D project(final Vector2D base) {
  232.         return getComponent(base, false, Vector2D::new);
  233.     }

  234.     /** {@inheritDoc} */
  235.     @Override
  236.     public Vector2D reject(final Vector2D base) {
  237.         return getComponent(base, true, Vector2D::new);
  238.     }

  239.     /** {@inheritDoc}
  240.      * The returned vector is computed by rotating the current instance {@code pi/2} radians
  241.      * counterclockwise around the origin and normalizing. For example, if this method is
  242.      * called on a vector pointing along the positive x-axis, then a unit vector representing
  243.      * the positive y-axis is returned.
  244.      * @return a unit vector orthogonal to the current instance
  245.      * @throws IllegalArgumentException if the norm of the current instance is zero, NaN, or infinite
  246.      */
  247.     @Override
  248.     public Vector2D.Unit orthogonal() {
  249.         return Unit.from(-y, x);
  250.     }

  251.     /** {@inheritDoc} */
  252.     @Override
  253.     public Vector2D.Unit orthogonal(final Vector2D dir) {
  254.         return dir.getComponent(this, true, Vector2D.Unit::from);
  255.     }

  256.     /** Compute the signed area of the parallelogram with sides formed by this instance
  257.      * and the given vector.
  258.      *
  259.      * <p>The parallelogram in question can be visualized by taking the current instance as the
  260.      * first side and placing {@code v} at the end of it to create the second. The other sides
  261.      * are formed by lines parallel to these two vectors. If {@code v} points to the <em>left</em> of
  262.      * the current instance (ie, the parallelogram is wound counter-clockwise), then the
  263.      * returned area is positive. If {@code v} points to the <em>right</em> of the current instance,
  264.      * (ie, the parallelogram is wound clockwise), then the returned area is negative. If
  265.      * the vectors are collinear (ie, they lie on the same line), then 0 is returned. The area of
  266.      * the triangle formed by the two vectors is exactly half of the returned value.
  267.      * @param v vector representing the second side of the constructed parallelogram
  268.      * @return the signed area of the parallelogram formed by this instance and the given vector
  269.      */
  270.     public double signedArea(final Vector2D v) {
  271.         return Vectors.linearCombination(
  272.                 x, v.y,
  273.                 -y, v.x);
  274.     }

  275.     /** Convenience method to apply a function to this vector. This
  276.      * can be used to transform the vector inline with other methods.
  277.      * @param fn the function to apply
  278.      * @return the transformed vector
  279.      */
  280.     public Vector2D transform(final UnaryOperator<Vector2D> fn) {
  281.         return fn.apply(this);
  282.     }

  283.     /** {@inheritDoc} */
  284.     @Override
  285.     public boolean eq(final Vector2D vec, final Precision.DoubleEquivalence precision) {
  286.         return precision.eq(x, vec.x) &&
  287.                 precision.eq(y, vec.y);
  288.     }

  289.     /**
  290.      * Get a hashCode for the 2D coordinates.
  291.      * <p>
  292.      * All NaN values have the same hash code.</p>
  293.      *
  294.      * @return a hash code value for this object
  295.      */
  296.     @Override
  297.     public int hashCode() {
  298.         if (isNaN()) {
  299.             return 542;
  300.         }
  301.         return 122 * (76 * Double.hashCode(x) + Double.hashCode(y));
  302.     }

  303.     /**
  304.      * Test for the equality of two vector instances.
  305.      * <p>
  306.      * If all coordinates of two vectors are exactly the same, and none are
  307.      * <code>Double.NaN</code>, the two instances are considered to be equal.
  308.      * </p>
  309.      * <p>
  310.      * <code>NaN</code> coordinates are considered to globally affect the vector
  311.      * and be equal to each other - i.e, if either (or all) coordinates of the
  312.      * vector are equal to <code>Double.NaN</code>, the vector is equal to
  313.      * {@link #NaN}.
  314.      * </p>
  315.      *
  316.      * @param other Object to test for equality to this
  317.      * @return true if two Vector2D objects are equal, false if
  318.      *         object is null, not an instance of Vector2D, or
  319.      *         not equal to this Vector2D instance
  320.      *
  321.      */
  322.     @Override
  323.     public boolean equals(final Object other) {
  324.         if (this == other) {
  325.             return true;
  326.         }
  327.         if (other instanceof Vector2D) {
  328.             final Vector2D rhs = (Vector2D) other;
  329.             if (rhs.isNaN()) {
  330.                 return this.isNaN();
  331.             }

  332.             return Double.compare(x, rhs.x) == 0 &&
  333.                     Double.compare(y, rhs.y) == 0;
  334.         }
  335.         return false;
  336.     }

  337.     /** {@inheritDoc} */
  338.     @Override
  339.     public String toString() {
  340.         return SimpleTupleFormat.getDefault().format(x, y);
  341.     }

  342.     /** Returns a component of the current instance relative to the given base
  343.      * vector. If {@code reject} is true, the vector rejection is returned; otherwise,
  344.      * the projection is returned.
  345.      * @param base The base vector
  346.      * @param reject If true, the rejection of this instance from {@code base} is
  347.      *      returned. If false, the projection of this instance onto {@code base}
  348.      *      is returned.
  349.      * @param factory factory function used to build the final vector
  350.      * @param <T> Vector implementation type
  351.      * @return The projection or rejection of this instance relative to {@code base},
  352.      *      depending on the value of {@code reject}.
  353.      * @throws IllegalArgumentException if {@code base} has a zero, NaN, or infinite norm
  354.      */
  355.     private <T extends Vector2D> T getComponent(final Vector2D base, final boolean reject,
  356.             final DoubleFunction2N<T> factory) {
  357.         final double aDotB = dot(base);

  358.         // We need to check the norm value here to ensure that it's legal. However, we don't
  359.         // want to incur the cost or floating point error of getting the actual norm and then
  360.         // multiplying it again to get the square norm. So, we'll just check the squared norm
  361.         // directly. This will produce the same error result as checking the actual norm since
  362.         // Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and
  363.         // Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY.
  364.         final double baseMagSq = Vectors.checkedNorm(base.normSq());

  365.         final double scale = aDotB / baseMagSq;

  366.         final double projX = scale * base.x;
  367.         final double projY = scale * base.y;

  368.         if (reject) {
  369.             return factory.apply(x - projX, y - projY);
  370.         }

  371.         return factory.apply(projX, projY);
  372.     }

  373.     /** Returns a vector with the given coordinate values.
  374.      * @param x abscissa (first coordinate value)
  375.      * @param y abscissa (second coordinate value)
  376.      * @return vector instance
  377.      */
  378.     public static Vector2D of(final double x, final double y) {
  379.         return new Vector2D(x, y);
  380.     }

  381.     /** Creates a vector from the coordinates in the given 2-element array.
  382.      * @param v coordinates array
  383.      * @return new vector
  384.      * @exception IllegalArgumentException if the array does not have 2 elements
  385.      */
  386.     public static Vector2D of(final double[] v) {
  387.         if (v.length != 2) {
  388.             throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 2");
  389.         }
  390.         return new Vector2D(v[0], v[1]);
  391.     }

  392.     /** Parses the given string and returns a new vector instance. The expected string
  393.      * format is the same as that returned by {@link #toString()}.
  394.      * @param str the string to parse
  395.      * @return vector instance represented by the string
  396.      * @throws IllegalArgumentException if the given string has an invalid format
  397.      */
  398.     public static Vector2D parse(final String str) {
  399.         return SimpleTupleFormat.getDefault().parse(str, Vector2D::new);
  400.     }

  401.     /** Return a vector containing the maximum component values from all input vectors.
  402.      * @param first first vector
  403.      * @param more additional vectors
  404.      * @return a vector containing the maximum component values from all input vectors
  405.      */
  406.     public static Vector2D max(final Vector2D first, final Vector2D... more) {
  407.         return computeMax(first, Arrays.asList(more).iterator());
  408.     }

  409.     /** Return a vector containing the maximum component values from all input vectors.
  410.      * @param vecs input vectors
  411.      * @return a vector containing the maximum component values from all input vectors
  412.      * @throws IllegalArgumentException if the argument does not contain any vectors
  413.      */
  414.     public static Vector2D max(final Iterable<Vector2D> vecs) {
  415.         final Iterator<Vector2D> it = vecs.iterator();
  416.         if (!it.hasNext()) {
  417.             throw new IllegalArgumentException("Cannot compute vector max: no vectors given");
  418.         }

  419.         return computeMax(it.next(), it);
  420.     }

  421.     /** Internal method for computing a max vector.
  422.      * @param first first vector
  423.      * @param more iterator with additional vectors
  424.      * @return vector containing the maximum component values of all input vectors
  425.      */
  426.     private static Vector2D computeMax(final Vector2D first, final Iterator<? extends Vector2D> more) {
  427.         double x = first.getX();
  428.         double y = first.getY();

  429.         Vector2D vec;
  430.         while (more.hasNext()) {
  431.             vec = more.next();

  432.             x = Math.max(x, vec.getX());
  433.             y = Math.max(y, vec.getY());
  434.         }

  435.         return Vector2D.of(x, y);
  436.     }

  437.     /** Return a vector containing the minimum component values from all input vectors.
  438.      * @param first first vector
  439.      * @param more more vectors
  440.      * @return a vector containing the minimum component values from all input vectors
  441.      */
  442.     public static Vector2D min(final Vector2D first, final Vector2D... more) {
  443.         return computeMin(first, Arrays.asList(more).iterator());
  444.     }

  445.     /** Return a vector containing the minimum component values from all input vectors.
  446.      * @param vecs input vectors
  447.      * @return a vector containing the minimum component values from all input vectors
  448.      * @throws IllegalArgumentException if the argument does not contain any vectors
  449.      */
  450.     public static Vector2D min(final Iterable<Vector2D> vecs) {
  451.         final Iterator<Vector2D> it = vecs.iterator();
  452.         if (!it.hasNext()) {
  453.             throw new IllegalArgumentException("Cannot compute vector min: no vectors given");
  454.         }

  455.         return computeMin(it.next(), it);
  456.     }

  457.     /** Internal method for computing a min vector.
  458.      * @param first first vector
  459.      * @param more iterator with additional vectors
  460.      * @return vector containing the minimum component values of all input vectors
  461.      */
  462.     private static Vector2D computeMin(final Vector2D first, final Iterator<? extends Vector2D> more) {
  463.         double x = first.getX();
  464.         double y = first.getY();

  465.         Vector2D vec;
  466.         while (more.hasNext()) {
  467.             vec = more.next();

  468.             x = Math.min(x, vec.getX());
  469.             y = Math.min(y, vec.getY());
  470.         }

  471.         return Vector2D.of(x, y);
  472.     }

  473.     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
  474.      * of points.
  475.      * @param first first point
  476.      * @param more additional points
  477.      * @return the centroid of the given points
  478.      */
  479.     public static Vector2D centroid(final Vector2D first, final Vector2D... more) {
  480.         return computeCentroid(first, Arrays.asList(more).iterator());
  481.     }

  482.     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
  483.      * of points.
  484.      * @param pts the points to compute the centroid of
  485.      * @return the centroid of the given points
  486.      * @throws IllegalArgumentException if the argument contains no points
  487.      */
  488.     public static Vector2D centroid(final Iterable<Vector2D> pts) {
  489.         final Iterator<Vector2D> it = pts.iterator();
  490.         if (!it.hasNext()) {
  491.             throw new IllegalArgumentException("Cannot compute centroid: no points given");
  492.         }

  493.         return computeCentroid(it.next(), it);
  494.     }

  495.     /** Internal method for computing the centroid of a set of points.
  496.      * @param first first point
  497.      * @param more iterator with additional points
  498.      * @return the centroid of the point set
  499.      */
  500.     private static Vector2D computeCentroid(final Vector2D first, final Iterator<? extends Vector2D> more) {
  501.         final Sum sum = Sum.of(first);
  502.         int count = 1;

  503.         while (more.hasNext()) {
  504.             sum.add(more.next());
  505.             ++count;
  506.         }

  507.         return sum.get().multiply(1.0 / count);
  508.     }

  509.     /**
  510.      * Represents unit vectors.
  511.      * This allows optimizations for certain operations.
  512.      */
  513.     public static final class Unit extends Vector2D {
  514.         /** Unit vector (coordinates: 1, 0). */
  515.         public static final Unit PLUS_X  = new Unit(1d, 0d);
  516.         /** Negation of unit vector (coordinates: -1, 0). */
  517.         public static final Unit MINUS_X = new Unit(-1d, 0d);
  518.         /** Unit vector (coordinates: 0, 1). */
  519.         public static final Unit PLUS_Y  = new Unit(0d, 1d);
  520.         /** Negation of unit vector (coordinates: 0, -1). */
  521.         public static final Unit MINUS_Y = new Unit(0d, -1d);

  522.         /** Maximum coordinate value for computing normalized vectors
  523.          * with raw, unscaled values.
  524.          */
  525.         private static final double UNSCALED_MAX = 0x1.0p+500;

  526.         /** Factor used to scale up coordinate values in order to produce
  527.          * normalized coordinates without overflow or underflow.
  528.          */
  529.         private static final double SCALE_UP_FACTOR = 0x1.0p+600;

  530.         /** Factor used to scale down coordinate values in order to produce
  531.          * normalized coordinates without overflow or underflow.
  532.          */
  533.         private static final double SCALE_DOWN_FACTOR = 0x1.0p-600;

  534.         /** Simple constructor. Callers are responsible for ensuring that the given
  535.          * values represent a normalized vector.
  536.          * @param x abscissa (first coordinate value)
  537.          * @param y abscissa (second coordinate value)
  538.          */
  539.         private Unit(final double x, final double y) {
  540.             super(x, y);
  541.         }

  542.         /** {@inheritDoc} */
  543.         @Override
  544.         public double norm() {
  545.             return 1;
  546.         }

  547.         /** {@inheritDoc} */
  548.         @Override
  549.         public double normSq() {
  550.             return 1;
  551.         }

  552.         /** {@inheritDoc} */
  553.         @Override
  554.         public Unit normalize() {
  555.             return this;
  556.         }

  557.         /** {@inheritDoc} */
  558.         @Override
  559.         public Unit normalizeOrNull() {
  560.             return this;
  561.         }

  562.         /** {@inheritDoc} */
  563.         @Override
  564.         public Vector2D.Unit orthogonal() {
  565.             return new Unit(-getY(), getX());
  566.         }

  567.         /** {@inheritDoc} */
  568.         @Override
  569.         public Vector2D withNorm(final double mag) {
  570.             return multiply(mag);
  571.         }

  572.         /** {@inheritDoc} */
  573.         @Override
  574.         public Unit negate() {
  575.             return new Unit(-getX(), -getY());
  576.         }

  577.         /** Create a normalized vector.
  578.          * @param x Vector coordinate.
  579.          * @param y Vector coordinate.
  580.          * @return a vector whose norm is 1.
  581.          * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
  582.          *      or infinite
  583.          */
  584.         public static Unit from(final double x, final double y) {
  585.             return tryCreateNormalized(x, y, true);
  586.         }

  587.         /** Create a normalized vector.
  588.          * @param v Vector.
  589.          * @return a vector whose norm is 1.
  590.          * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
  591.          *      or infinite
  592.          */
  593.         public static Unit from(final Vector2D v) {
  594.             return v instanceof Unit ?
  595.                 (Unit) v :
  596.                 from(v.getX(), v.getY());
  597.         }

  598.         /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure}
  599.          * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null
  600.          * is returned.
  601.          * @param x x coordinate
  602.          * @param y y coordinate
  603.          * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created
  604.          * @return normalized vector or null if one cannot be created and {@code throwOnFailure}
  605.          *      is false
  606.          * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite
  607.          */
  608.         private static Unit tryCreateNormalized(final double x, final double y, final boolean throwOnFailure) {

  609.             // Compute the inverse norm directly. If the result is a non-zero real number,
  610.             // then we can go ahead and construct the unit vector immediately. If not,
  611.             // we'll do some extra work for edge cases.
  612.             final double norm = Vectors.norm(x, y);
  613.             final double normInv = 1.0 / norm;
  614.             if (Vectors.isRealNonZero(normInv)) {
  615.                 return new Unit(
  616.                         x * normInv,
  617.                         y * normInv);
  618.             }

  619.             // Direct computation did not work. Try scaled versions of the coordinates
  620.             // to handle overflow and underflow.
  621.             final double scaledX;
  622.             final double scaledY;

  623.             final double maxCoord = Math.max(Math.abs(x), Math.abs(y));
  624.             if (maxCoord > UNSCALED_MAX) {
  625.                 scaledX = x * SCALE_DOWN_FACTOR;
  626.                 scaledY = y * SCALE_DOWN_FACTOR;
  627.             } else {
  628.                 scaledX = x * SCALE_UP_FACTOR;
  629.                 scaledY = y * SCALE_UP_FACTOR;
  630.             }

  631.             final double scaledNormInv = 1.0 / Vectors.norm(scaledX, scaledY);

  632.             if (Vectors.isRealNonZero(scaledNormInv)) {
  633.                 return new Unit(
  634.                         scaledX * scaledNormInv,
  635.                         scaledY * scaledNormInv);
  636.             } else if (throwOnFailure) {
  637.                 throw Vectors.illegalNorm(norm);
  638.             }
  639.             return null;
  640.         }
  641.     }

  642.     /** Class used to create high-accuracy sums of vectors. Each vector component is
  643.      * summed using an instance of {@link org.apache.commons.numbers.core.Sum}.
  644.      *
  645.      * <p>This class is mutable and not thread-safe.
  646.      * @see org.apache.commons.numbers.core.Sum
  647.      */
  648.     public static final class Sum extends EuclideanVectorSum<Vector2D> {
  649.         /** X component sum. */
  650.         private final org.apache.commons.numbers.core.Sum xsum;
  651.         /** Y component sum. */
  652.         private final org.apache.commons.numbers.core.Sum ysum;

  653.         /** Construct a new instance with the given initial value.
  654.          * @param initial initial value
  655.          */
  656.         Sum(final Vector2D initial) {
  657.             this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x);
  658.             this.ysum = org.apache.commons.numbers.core.Sum.of(initial.y);
  659.         }

  660.         /** {@inheritDoc} */
  661.         @Override
  662.         public Sum add(final Vector2D vec) {
  663.             xsum.add(vec.x);
  664.             ysum.add(vec.y);
  665.             return this;
  666.         }

  667.         /** {@inheritDoc} */
  668.         @Override
  669.         public Sum addScaled(final double scale, final Vector2D vec) {
  670.             xsum.addProduct(scale, vec.x);
  671.             ysum.addProduct(scale, vec.y);
  672.             return this;
  673.         }

  674.         /** {@inheritDoc} */
  675.         @Override
  676.         public Vector2D get() {
  677.             return Vector2D.of(
  678.                     xsum.getAsDouble(),
  679.                     ysum.getAsDouble());
  680.         }

  681.         /** Create a new instance with an initial value set to the {@link Vector2D#ZERO zero vector}.
  682.          * @return new instance set to zero
  683.          */
  684.         public static Sum create() {
  685.             return new Sum(Vector2D.ZERO);
  686.         }

  687.         /** Construct a new instance with an initial value set to the argument.
  688.          * @param initial initial sum value
  689.          * @return new instance
  690.          */
  691.         public static Sum of(final Vector2D initial) {
  692.             return new Sum(initial);
  693.         }

  694.         /** Construct a new instance from multiple values.
  695.          * @param first first vector
  696.          * @param more additional vectors
  697.          * @return new instance
  698.          */
  699.         public static Sum of(final Vector2D first, final Vector2D... more) {
  700.             final Sum s = new Sum(first);
  701.             for (final Vector2D v : more) {
  702.                 s.add(v);
  703.             }
  704.             return s;
  705.         }
  706.     }
  707. }