Vector3D.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.threed;

  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.DoubleFunction3N;
  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 three-dimensional Euclidean space.
  29.  * Instances of this class are guaranteed to be immutable.
  30.  */
  31. public class Vector3D extends MultiDimensionalEuclideanVector<Vector3D> {

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

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

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

  39.     /** A vector with all coordinates set to negative infinity. */
  40.     public static final Vector3D NEGATIVE_INFINITY =
  41.         new Vector3D(Double.NEGATIVE_INFINITY, 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<Vector3D> 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.                 if (cmp == 0) {
  53.                     cmp = Double.compare(a.getZ(), b.getZ());
  54.                 }
  55.             }
  56.         } else if (a != null) {
  57.             cmp = -1;
  58.         } else if (b != null) {
  59.             cmp = 1;
  60.         }

  61.         return cmp;
  62.     };

  63.     /** X coordinate value (abscissa). */
  64.     private final double x;

  65.     /** Y coordinate value (ordinate). */
  66.     private final double y;

  67.     /** Z coordinate value (height). */
  68.     private final double z;

  69.     /** Simple constructor.
  70.      * Build a vector from its coordinates
  71.      * @param x x coordinate value
  72.      * @param y y coordinate value
  73.      * @param z z coordinate value
  74.      */
  75.     private Vector3D(final double x, final double y, final double z) {
  76.         this.x = x;
  77.         this.y = y;
  78.         this.z = z;
  79.     }

  80.     /** Return the x coordinate value (abscissa) of the instance.
  81.      * @return the x coordinate value
  82.      */
  83.     public double getX() {
  84.         return x;
  85.     }

  86.     /** Return the y coordinate value (ordinate) of the instance.
  87.      * @return the y coordinate value
  88.      */
  89.     public double getY() {
  90.         return y;
  91.     }

  92.     /** Returns the z coordinate value (height) of the instance.
  93.      * @return the z coordinate value
  94.      */
  95.     public double getZ() {
  96.         return z;
  97.     }

  98.     /** Get the coordinates for this instance as a dimension 3 array.
  99.      * @return the coordinates for this instance
  100.      */
  101.     public double[] toArray() {
  102.         return new double[]{x, y, z};
  103.     }

  104.     /** {@inheritDoc} */
  105.     @Override
  106.     public int getDimension() {
  107.         return 3;
  108.     }

  109.     /** {@inheritDoc} */
  110.     @Override
  111.     public boolean isNaN() {
  112.         return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z);
  113.     }

  114.     /** {@inheritDoc} */
  115.     @Override
  116.     public boolean isInfinite() {
  117.         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
  118.     }

  119.     /** {@inheritDoc} */
  120.     @Override
  121.     public boolean isFinite() {
  122.         return Double.isFinite(x) && Double.isFinite(y) && Double.isFinite(z);
  123.     }

  124.     /** {@inheritDoc} */
  125.     @Override
  126.     public Vector3D getZero() {
  127.         return ZERO;
  128.     }

  129.     /** {@inheritDoc} */
  130.     @Override
  131.     public Vector3D vectorTo(final Vector3D v) {
  132.         return v.subtract(this);
  133.     }

  134.     /** {@inheritDoc} */
  135.     @Override
  136.     public Unit directionTo(final Vector3D v) {
  137.         return vectorTo(v).normalize();
  138.     }

  139.     /** {@inheritDoc} */
  140.     @Override
  141.     public Vector3D lerp(final Vector3D p, final double t) {
  142.         return Sum.create()
  143.                 .addScaled(1.0 - t, this)
  144.                 .addScaled(t, p).get();
  145.     }

  146.     /** {@inheritDoc} */
  147.     @Override
  148.     public double norm() {
  149.         return Vectors.norm(x, y, z);
  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     public double normSq() {
  154.         return Vectors.normSq(x, y, z);
  155.     }

  156.     /** {@inheritDoc} */
  157.     @Override
  158.     public Vector3D withNorm(final double magnitude) {
  159.         final double m = magnitude / getCheckedNorm();

  160.         return new Vector3D(
  161.                     m * x,
  162.                     m * y,
  163.                     m * z
  164.                 );
  165.     }

  166.     /** {@inheritDoc} */
  167.     @Override
  168.     public Vector3D add(final Vector3D v) {
  169.         return new Vector3D(
  170.                     x + v.x,
  171.                     y + v.y,
  172.                     z + v.z
  173.                 );
  174.     }

  175.     /** {@inheritDoc} */
  176.     @Override
  177.     public Vector3D add(final double factor, final Vector3D v) {
  178.         return new Vector3D(
  179.                     x + (factor * v.x),
  180.                     y + (factor * v.y),
  181.                     z + (factor * v.z)
  182.                 );
  183.     }

  184.     /** {@inheritDoc} */
  185.     @Override
  186.     public Vector3D subtract(final Vector3D v) {
  187.         return new Vector3D(
  188.                     x - v.x,
  189.                     y - v.y,
  190.                     z - v.z
  191.                 );
  192.     }

  193.     /** {@inheritDoc} */
  194.     @Override
  195.     public Vector3D subtract(final double factor, final Vector3D v) {
  196.         return new Vector3D(
  197.                     x - (factor * v.x),
  198.                     y - (factor * v.y),
  199.                     z - (factor * v.z)
  200.                 );
  201.     }

  202.     /** {@inheritDoc} */
  203.     @Override
  204.     public Vector3D negate() {
  205.         return new Vector3D(-x, -y, -z);
  206.     }

  207.     /** {@inheritDoc} */
  208.     @Override
  209.     public Unit normalize() {
  210.         return Unit.from(x, y, z);
  211.     }

  212.     /** {@inheritDoc} */
  213.     @Override
  214.     public Unit normalizeOrNull() {
  215.         return Unit.tryCreateNormalized(x, y, z, false);
  216.     }

  217.     /** {@inheritDoc} */
  218.     @Override
  219.     public Vector3D multiply(final double a) {
  220.         return new Vector3D(a * x, a * y, a * z);
  221.     }

  222.     /** {@inheritDoc} */
  223.     @Override
  224.     public double distance(final Vector3D v) {
  225.         return Vectors.norm(
  226.                 x - v.x,
  227.                 y - v.y,
  228.                 z - v.z
  229.             );
  230.     }

  231.     /** {@inheritDoc} */
  232.     @Override
  233.     public double distanceSq(final Vector3D v) {
  234.         return Vectors.normSq(
  235.                 x - v.x,
  236.                 y - v.y,
  237.                 z - v.z
  238.             );
  239.     }

  240.     /** {@inheritDoc}
  241.      * <p>
  242.      * The implementation uses specific multiplication and addition
  243.      * algorithms to preserve accuracy and reduce cancellation effects.
  244.      * It should be very accurate even for nearly orthogonal vectors.
  245.      * </p>
  246.      * @see org.apache.commons.numbers.core.Sum
  247.      */
  248.     @Override
  249.     public double dot(final Vector3D v) {
  250.         return Vectors.linearCombination(
  251.                 x, v.x,
  252.                 y, v.y,
  253.                 z, v.z);
  254.     }

  255.     /** {@inheritDoc}
  256.      * <p>This method computes the angular separation between two
  257.      * vectors using the dot product for well separated vectors and the
  258.      * cross product for almost aligned vectors. This allows to have a
  259.      * good accuracy in all cases, even for vectors very close to each
  260.      * other.</p>
  261.      */
  262.     @Override
  263.     public double angle(final Vector3D v) {
  264.         final double normProduct = getCheckedNorm() * v.getCheckedNorm();

  265.         final double dot = dot(v);
  266.         final double threshold = normProduct * 0.99;
  267.         if ((dot < -threshold) || (dot > threshold)) {
  268.             // the vectors are almost aligned, compute using the sine
  269.             final Vector3D cross = cross(v);
  270.             if (dot >= 0) {
  271.                 return Math.asin(cross.norm() / normProduct);
  272.             }
  273.             return Math.PI - Math.asin(cross.norm() / normProduct);
  274.         }

  275.         // the vectors are sufficiently separated to use the cosine
  276.         return Math.acos(dot / normProduct);
  277.     }

  278.     /** {@inheritDoc} */
  279.     @Override
  280.     public Vector3D project(final Vector3D base) {
  281.         return getComponent(base, false, Vector3D::new);
  282.     }

  283.     /** {@inheritDoc} */
  284.     @Override
  285.     public Vector3D reject(final Vector3D base) {
  286.         return getComponent(base, true, Vector3D::new);
  287.     }

  288.     /** {@inheritDoc}
  289.      * <p>There are an infinite number of normalized vectors orthogonal
  290.      * to the instance. This method picks up one of them almost
  291.      * arbitrarily. It is useful when one needs to compute a reference
  292.      * frame with one of the axes in a predefined direction. The
  293.      * following example shows how to build a frame having the k axis
  294.      * aligned with the known vector u :
  295.      * <pre><code>
  296.      *   Vector3D k = u.normalize();
  297.      *   Vector3D i = k.orthogonal();
  298.      *   Vector3D j = k.cross(i);
  299.      * </code></pre>
  300.      * @return a unit vector orthogonal to the instance
  301.      * @throws IllegalArgumentException if the norm of the instance
  302.      *      is zero, NaN, or infinite
  303.      */
  304.     @Override
  305.     public Vector3D.Unit orthogonal() {
  306.         final double threshold = 0.6 * getCheckedNorm();

  307.         final double inverse;
  308.         if (Math.abs(x) <= threshold) {
  309.             inverse  = 1 / Vectors.norm(y, z);
  310.             return new Unit(0, inverse * z, -inverse * y);
  311.         } else if (Math.abs(y) <= threshold) {
  312.             inverse  = 1 / Vectors.norm(x, z);
  313.             return new Unit(-inverse * z, 0, inverse * x);
  314.         }
  315.         inverse  = 1 / Vectors.norm(x, y);
  316.         return new Unit(inverse * y, -inverse * x, 0);
  317.     }

  318.     /** {@inheritDoc} */
  319.     @Override
  320.     public Vector3D.Unit orthogonal(final Vector3D dir) {
  321.         return dir.getComponent(this, true, Vector3D.Unit::from);
  322.     }

  323.     /** Compute the cross-product of the instance with another vector.
  324.      * @param v other vector
  325.      * @return the cross product this ^ v as a new Vector3D
  326.      */
  327.     public Vector3D cross(final Vector3D v) {
  328.         return new Vector3D(Vectors.linearCombination(y, v.z, -z, v.y),
  329.                             Vectors.linearCombination(z, v.x, -x, v.z),
  330.                             Vectors.linearCombination(x, v.y, -y, v.x));
  331.     }

  332.     /** Convenience method to apply a function to this vector. This
  333.      * can be used to transform the vector inline with other methods.
  334.      * @param fn the function to apply
  335.      * @return the transformed vector
  336.      */
  337.     public Vector3D transform(final UnaryOperator<Vector3D> fn) {
  338.         return fn.apply(this);
  339.     }

  340.     /** {@inheritDoc} */
  341.     @Override
  342.     public boolean eq(final Vector3D vec, final Precision.DoubleEquivalence precision) {
  343.         return precision.eq(x, vec.x) &&
  344.                 precision.eq(y, vec.y) &&
  345.                 precision.eq(z, vec.z);
  346.     }

  347.     /**
  348.      * Get a hashCode for the vector.
  349.      * <p>All NaN values have the same hash code.</p>
  350.      *
  351.      * @return a hash code value for this object
  352.      */
  353.     @Override
  354.     public int hashCode() {
  355.         if (isNaN()) {
  356.             return 642;
  357.         }
  358.         return 643 * (164 * Double.hashCode(x) + 3 * Double.hashCode(y) + Double.hashCode(z));
  359.     }

  360.     /**d
  361.      * Test for the equality of two vector instances.
  362.      * <p>
  363.      * If all coordinates of two vectors are exactly the same, and none are
  364.      * <code>Double.NaN</code>, the two instances are considered to be equal.
  365.      * </p>
  366.      * <p>
  367.      * <code>NaN</code> coordinates are considered to globally affect the vector
  368.      * and be equal to each other - i.e, if either (or all) coordinates of the
  369.      * vector are equal to <code>Double.NaN</code>, the vector is equal to
  370.      * {@link #NaN}.
  371.      * </p>
  372.      *
  373.      * @param other Object to test for equality to this
  374.      * @return true if two Vector3D objects are equal, false if
  375.      *         object is null, not an instance of Vector3D, or
  376.      *         not equal to this Vector3D instance
  377.      *
  378.      */
  379.     @Override
  380.     public boolean equals(final Object other) {
  381.         if (this == other) {
  382.             return true;
  383.         }
  384.         if (other instanceof Vector3D) {
  385.             final Vector3D rhs = (Vector3D) other;
  386.             if (rhs.isNaN()) {
  387.                 return this.isNaN();
  388.             }

  389.             return Double.compare(x, rhs.x) == 0 &&
  390.                     Double.compare(y, rhs.y) == 0 &&
  391.                     Double.compare(z, rhs.z) == 0;
  392.         }
  393.         return false;
  394.     }

  395.     /** {@inheritDoc} */
  396.     @Override
  397.     public String toString() {
  398.         return SimpleTupleFormat.getDefault().format(x, y, z);
  399.     }

  400.     /** Returns a component of the current instance relative to the given base
  401.      * vector. If {@code reject} is true, the vector rejection is returned; otherwise,
  402.      * the projection is returned.
  403.      * @param base The base vector
  404.      * @param reject If true, the rejection of this instance from {@code base} is
  405.      *      returned. If false, the projection of this instance onto {@code base}
  406.      *      is returned.
  407.      * @param factory factory function used to build the final vector
  408.      * @param <V> Vector implementation type
  409.      * @return The projection or rejection of this instance relative to {@code base},
  410.      *      depending on the value of {@code reject}.
  411.      * @throws IllegalArgumentException if {@code base} has a zero, NaN,
  412.      *      or infinite norm
  413.      */
  414.     private <V extends Vector3D> V getComponent(final Vector3D base, final boolean reject,
  415.                                                 final DoubleFunction3N<V> factory) {
  416.         final double aDotB = dot(base);

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

  424.         final double scale = aDotB / baseMagSq;

  425.         final double projX = scale * base.x;
  426.         final double projY = scale * base.y;
  427.         final double projZ = scale * base.z;

  428.         if (reject) {
  429.             return factory.apply(x - projX, y - projY, z - projZ);
  430.         }

  431.         return factory.apply(projX, projY, projZ);
  432.     }

  433.     /** Returns a vector with the given coordinate values.
  434.      * @param x x coordinate value
  435.      * @param y y coordinate value
  436.      * @param z z coordinate value
  437.      * @return vector instance
  438.      */
  439.     public static Vector3D of(final double x, final double y, final double z) {
  440.         return new Vector3D(x, y, z);
  441.     }

  442.     /** Creates a vector from the coordinates in the given 3-element array.
  443.      * @param v coordinates array
  444.      * @return new vector
  445.      * @exception IllegalArgumentException if the array does not have 3 elements
  446.      */
  447.     public static Vector3D of(final double[] v) {
  448.         if (v.length != 3) {
  449.             throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 3");
  450.         }
  451.         return new Vector3D(v[0], v[1], v[2]);
  452.     }

  453.     /** Parses the given string and returns a new vector instance. The expected string
  454.      * format is the same as that returned by {@link #toString()}.
  455.      * @param str the string to parse
  456.      * @return vector instance represented by the string
  457.      * @throws IllegalArgumentException if the given string has an invalid format
  458.      */
  459.     public static Vector3D parse(final String str) {
  460.         return SimpleTupleFormat.getDefault().parse(str, Vector3D::new);
  461.     }

  462.     /** Return a vector containing the maximum component values from all input vectors.
  463.      * @param first first vector
  464.      * @param more additional vectors
  465.      * @return a vector containing the maximum component values from all input vectors
  466.      */
  467.     public static Vector3D max(final Vector3D first, final Vector3D... more) {
  468.         return computeMax(first, Arrays.asList(more).iterator());
  469.     }

  470.     /** Return a vector containing the maximum component values from all input vectors.
  471.      * @param vecs input vectors
  472.      * @return a vector containing the maximum component values from all input vectors
  473.      * @throws IllegalArgumentException if the argument does not contain any vectors
  474.      */
  475.     public static Vector3D max(final Iterable<Vector3D> vecs) {
  476.         final Iterator<Vector3D> it = vecs.iterator();
  477.         if (!it.hasNext()) {
  478.             throw new IllegalArgumentException("Cannot compute vector max: no vectors given");
  479.         }

  480.         return computeMax(it.next(), it);
  481.     }

  482.     /** Internal method for computing a max vector.
  483.      * @param first first vector
  484.      * @param more iterator with additional vectors
  485.      * @return vector containing the maximum component values of all input vectors
  486.      */
  487.     private static Vector3D computeMax(final Vector3D first, final Iterator<? extends Vector3D> more) {
  488.         double x = first.getX();
  489.         double y = first.getY();
  490.         double z = first.getZ();

  491.         Vector3D vec;
  492.         while (more.hasNext()) {
  493.             vec = more.next();

  494.             x = Math.max(x, vec.getX());
  495.             y = Math.max(y, vec.getY());
  496.             z = Math.max(z, vec.getZ());
  497.         }

  498.         return Vector3D.of(x, y, z);
  499.     }

  500.     /** Return a vector containing the minimum component values from all input vectors.
  501.      * @param first first vector
  502.      * @param more additional vectors
  503.      * @return a vector containing the minimum component values from all input vectors
  504.      */
  505.     public static Vector3D min(final Vector3D first, final Vector3D... more) {
  506.         return computeMin(first, Arrays.asList(more).iterator());
  507.     }

  508.     /** Return a vector containing the minimum component values from all input vectors.
  509.      * @param vecs input vectors
  510.      * @return a vector containing the minimum component values from all input vectors
  511.      * @throws IllegalArgumentException if the argument does not contain any vectors
  512.      */
  513.     public static Vector3D min(final Iterable<Vector3D> vecs) {
  514.         final Iterator<Vector3D> it = vecs.iterator();
  515.         if (!it.hasNext()) {
  516.             throw new IllegalArgumentException("Cannot compute vector min: no vectors given");
  517.         }

  518.         return computeMin(it.next(), it);
  519.     }

  520.     /** Internal method for computing a min vector.
  521.      * @param first first vector
  522.      * @param more iterator with additional vectors
  523.      * @return vector containing the minimum component values of all input vectors
  524.      */
  525.     private static Vector3D computeMin(final Vector3D first, final Iterator<? extends Vector3D> more) {
  526.         double x = first.getX();
  527.         double y = first.getY();
  528.         double z = first.getZ();

  529.         Vector3D vec;
  530.         while (more.hasNext()) {
  531.             vec = more.next();

  532.             x = Math.min(x, vec.getX());
  533.             y = Math.min(y, vec.getY());
  534.             z = Math.min(z, vec.getZ());
  535.         }

  536.         return Vector3D.of(x, y, z);
  537.     }

  538.     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
  539.      * of points.
  540.      * @param first first point
  541.      * @param more additional points
  542.      * @return the centroid of the given points
  543.      */
  544.     public static Vector3D centroid(final Vector3D first, final Vector3D... more) {
  545.         return computeCentroid(first, Arrays.asList(more).iterator());
  546.     }

  547.     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
  548.      * of points.
  549.      * @param pts the points to compute the centroid of
  550.      * @return the centroid of the given points
  551.      * @throws IllegalArgumentException if the argument contains no points
  552.      */
  553.     public static Vector3D centroid(final Iterable<Vector3D> pts) {
  554.         final Iterator<Vector3D> it = pts.iterator();
  555.         if (!it.hasNext()) {
  556.             throw new IllegalArgumentException("Cannot compute centroid: no points given");
  557.         }

  558.         return computeCentroid(it.next(), it);
  559.     }

  560.     /** Internal method for computing the centroid of a set of points.
  561.      * @param first first point
  562.      * @param more iterator with additional points
  563.      * @return the centroid of the point set
  564.      */
  565.     private static Vector3D computeCentroid(final Vector3D first, final Iterator<? extends Vector3D> more) {
  566.         final Sum sum = Sum.of(first);
  567.         int count = 1;

  568.         while (more.hasNext()) {
  569.             sum.add(more.next());
  570.             ++count;
  571.         }

  572.         return sum.get().multiply(1.0 / count);
  573.     }

  574.     /**
  575.      * Represents unit vectors.
  576.      * This allows optimizations for certain operations.
  577.      */
  578.     public static final class Unit extends Vector3D {
  579.         /** Unit vector (coordinates: 1, 0, 0). */
  580.         public static final Unit PLUS_X  = new Unit(1d, 0d, 0d);
  581.         /** Negation of unit vector (coordinates: -1, 0, 0). */
  582.         public static final Unit MINUS_X = new Unit(-1d, 0d, 0d);
  583.         /** Unit vector (coordinates: 0, 1, 0). */
  584.         public static final Unit PLUS_Y  = new Unit(0d, 1d, 0d);
  585.         /** Negation of unit vector (coordinates: 0, -1, 0). */
  586.         public static final Unit MINUS_Y = new Unit(0d, -1d, 0d);
  587.         /** Unit vector (coordinates: 0, 0, 1). */
  588.         public static final Unit PLUS_Z  = new Unit(0d, 0d, 1d);
  589.         /** Negation of unit vector (coordinates: 0, 0, -1). */
  590.         public static final Unit MINUS_Z = new Unit(0d, 0d, -1d);

  591.         /** Maximum coordinate value for computing normalized vectors
  592.          * with raw, unscaled values.
  593.          */
  594.         private static final double UNSCALED_MAX = 0x1.0p+500;

  595.         /** Factor used to scale up coordinate values in order to produce
  596.          * normalized coordinates without overflow or underflow.
  597.          */
  598.         private static final double SCALE_UP_FACTOR = 0x1.0p+600;

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

  603.         /** Simple constructor. Callers are responsible for ensuring that the given
  604.          * values represent a normalized vector.
  605.          * @param x x coordinate value
  606.          * @param y x coordinate value
  607.          * @param z x coordinate value
  608.          */
  609.         private Unit(final double x, final double y, final double z) {
  610.             super(x, y, z);
  611.         }

  612.         /** {@inheritDoc} */
  613.         @Override
  614.         public double norm() {
  615.             return 1;
  616.         }

  617.         /** {@inheritDoc} */
  618.         @Override
  619.         public double normSq() {
  620.             return 1;
  621.         }

  622.         /** {@inheritDoc} */
  623.         @Override
  624.         public Unit normalize() {
  625.             return this;
  626.         }

  627.         /** {@inheritDoc} */
  628.         @Override
  629.         public Unit normalizeOrNull() {
  630.             return this;
  631.         }

  632.         /** {@inheritDoc} */
  633.         @Override
  634.         public Vector3D withNorm(final double mag) {
  635.             return multiply(mag);
  636.         }

  637.         /** {@inheritDoc} */
  638.         @Override
  639.         public Unit negate() {
  640.             return new Unit(-getX(), -getY(), -getZ());
  641.         }

  642.         /** Create a normalized vector.
  643.          * @param x Vector coordinate.
  644.          * @param y Vector coordinate.
  645.          * @param z Vector coordinate.
  646.          * @return a vector whose norm is 1.
  647.          * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
  648.          *      or infinite
  649.          */
  650.         public static Unit from(final double x, final double y, final double z) {
  651.             return tryCreateNormalized(x, y, z, true);
  652.         }

  653.         /** Create a normalized vector.
  654.          * @param v Vector.
  655.          * @return a vector whose norm is 1.
  656.          * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
  657.          *      or infinite
  658.          */
  659.         public static Unit from(final Vector3D v) {
  660.             return v instanceof Unit ?
  661.                 (Unit) v :
  662.                 from(v.getX(), v.getY(), v.getZ());
  663.         }

  664.         /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure}
  665.          * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null
  666.          * is returned.
  667.          * @param x x coordinate
  668.          * @param y y coordinate
  669.          * @param z z coordinate
  670.          * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created
  671.          * @return normalized vector or null if one cannot be created and {@code throwOnFailure}
  672.          *      is false
  673.          * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite
  674.          */
  675.         private static Unit tryCreateNormalized(final double x, final double y, final double z,
  676.                 final boolean throwOnFailure) {

  677.             // Compute the inverse norm directly. If the result is a non-zero real number,
  678.             // then we can go ahead and construct the unit vector immediately. If not,
  679.             // we'll do some extra work for edge cases.
  680.             final double norm = Math.sqrt((x * x) + (y * y) + (z * z));
  681.             final double normInv = 1.0 / norm;
  682.             if (Vectors.isRealNonZero(normInv)) {
  683.                 return new Unit(
  684.                         x * normInv,
  685.                         y * normInv,
  686.                         z * normInv);
  687.             }

  688.             // Direct computation did not work. Try scaled versions of the coordinates
  689.             // to handle overflow and underflow.
  690.             final double scaledX;
  691.             final double scaledY;
  692.             final double scaledZ;

  693.             final double maxCoord = Math.max(Math.max(Math.abs(x), Math.abs(y)), Math.abs(z));
  694.             if (maxCoord > UNSCALED_MAX) {
  695.                 scaledX = x * SCALE_DOWN_FACTOR;
  696.                 scaledY = y * SCALE_DOWN_FACTOR;
  697.                 scaledZ = z * SCALE_DOWN_FACTOR;
  698.             } else {
  699.                 scaledX = x * SCALE_UP_FACTOR;
  700.                 scaledY = y * SCALE_UP_FACTOR;
  701.                 scaledZ = z * SCALE_UP_FACTOR;
  702.             }

  703.             final double scaledNormInv = 1.0 / Math.sqrt(
  704.                     (scaledX * scaledX) +
  705.                     (scaledY * scaledY) +
  706.                     (scaledZ * scaledZ));

  707.             if (Vectors.isRealNonZero(scaledNormInv)) {
  708.                 return new Unit(
  709.                         scaledX * scaledNormInv,
  710.                         scaledY * scaledNormInv,
  711.                         scaledZ * scaledNormInv);
  712.             } else if (throwOnFailure) {
  713.                 throw Vectors.illegalNorm(norm);
  714.             }
  715.             return null;
  716.         }
  717.     }

  718.     /** Class used to create high-accuracy sums of vectors. Each vector component is
  719.      * summed using an instance of {@link org.apache.commons.numbers.core.Sum}.
  720.      *
  721.      * <p>This class is mutable and not thread-safe.
  722.      * @see org.apache.commons.numbers.core.Sum
  723.      */
  724.     public static final class Sum extends EuclideanVectorSum<Vector3D> {
  725.         /** X component sum. */
  726.         private final org.apache.commons.numbers.core.Sum xsum;
  727.         /** Y component sum. */
  728.         private final org.apache.commons.numbers.core.Sum ysum;
  729.         /** Z component sum. */
  730.         private final org.apache.commons.numbers.core.Sum zsum;

  731.         /** Construct a new instance with the given initial value.
  732.          * @param initial initial value
  733.          */
  734.         Sum(final Vector3D initial) {
  735.             this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x);
  736.             this.ysum = org.apache.commons.numbers.core.Sum.of(initial.y);
  737.             this.zsum = org.apache.commons.numbers.core.Sum.of(initial.z);
  738.         }

  739.         /** {@inheritDoc} */
  740.         @Override
  741.         public Sum add(final Vector3D vec) {
  742.             xsum.add(vec.x);
  743.             ysum.add(vec.y);
  744.             zsum.add(vec.z);
  745.             return this;
  746.         }

  747.         /** {@inheritDoc} */
  748.         @Override
  749.         public Sum addScaled(final double scale, final Vector3D vec) {
  750.             xsum.addProduct(scale, vec.x);
  751.             ysum.addProduct(scale, vec.y);
  752.             zsum.addProduct(scale, vec.z);
  753.             return this;
  754.         }

  755.         /** {@inheritDoc} */
  756.         @Override
  757.         public Vector3D get() {
  758.             return Vector3D.of(
  759.                     xsum.getAsDouble(),
  760.                     ysum.getAsDouble(),
  761.                     zsum.getAsDouble());
  762.         }

  763.         /** Create a new instance with an initial value set to the {@link Vector3D#ZERO zero vector}.
  764.          * @return new instance set to zero
  765.          */
  766.         public static Sum create() {
  767.             return new Sum(Vector3D.ZERO);
  768.         }

  769.         /** Construct a new instance with an initial value set to the argument.
  770.          * @param initial initial sum value
  771.          * @return new instance
  772.          */
  773.         public static Sum of(final Vector3D initial) {
  774.             return new Sum(initial);
  775.         }

  776.         /** Construct a new instance from multiple values.
  777.          * @param first first vector
  778.          * @param more additional vectors
  779.          * @return new instance
  780.          */
  781.         public static Sum of(final Vector3D first, final Vector3D... more) {
  782.             final Sum s = new Sum(first);
  783.             for (final Vector3D v : more) {
  784.                 s.add(v);
  785.             }
  786.             return s;
  787.         }
  788.     }
  789. }