View Javadoc

    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
    19  import java.util.Arrays;
    20  import java.util.Comparator;
    21  import java.util.Iterator;
    22  import java.util.function.UnaryOperator;
    23
    24  import org.apache.commons.geometry.core.internal.DoubleFunction3N;
    25  import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
    26  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
    27  import org.apache.commons.geometry.euclidean.MultiDimensionalEuclideanVector;
    28  import org.apache.commons.geometry.euclidean.internal.Vectors;
    29  import org.apache.commons.numbers.arrays.LinearCombination;
    30
    31  /** This class represents vectors and points in three-dimensional Euclidean space.
    32   * Instances of this class are guaranteed to be immutable.
    33   */
    34  public class Vector3D extends MultiDimensionalEuclideanVector<Vector3D> {
    35
    36      /** Zero (null) vector (coordinates: 0, 0, 0). */
    37      public static final Vector3D ZERO = new Vector3D(0, 0, 0);
    38
    39      // CHECKSTYLE: stop ConstantName
    40      /** A vector with all coordinates set to NaN. */
    41      public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
    42      // CHECKSTYLE: resume ConstantName
    43
    44      /** A vector with all coordinates set to positive infinity. */
    45      public static final Vector3D POSITIVE_INFINITY =
    46          new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
    47
    48      /** A vector with all coordinates set to negative infinity. */
    49      public static final Vector3D NEGATIVE_INFINITY =
    50          new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
    51
    52      /** Comparator that sorts vectors in component-wise ascending order.
    53       * Vectors are only considered equal if their coordinates match exactly.
    54       * Null arguments are evaluated as being greater than non-null arguments.
    55       */
    56      public static final Comparator<Vector3D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
    57          int cmp = 0;
    58
    59          if (a != null && b != null) {
    60              cmp = Double.compare(a.getX(), b.getX());
    61              if (cmp == 0) {
    62                  cmp = Double.compare(a.getY(), b.getY());
    63                  if (cmp == 0) {
    64                      cmp = Double.compare(a.getZ(), b.getZ());
    65                  }
    66              }
    67          } else if (a != null) {
    68              cmp = -1;
    69          } else if (b != null) {
    70              cmp = 1;
    71          }
    72
    73          return cmp;
    74      };
    75
    76      /** X coordinate value (abscissa). */
    77      private final double x;
    78
    79      /** Y coordinate value (ordinate). */
    80      private final double y;
    81
    82      /** Z coordinate value (height). */
    83      private final double z;
    84
    85      /** Simple constructor.
    86       * Build a vector from its coordinates
    87       * @param x x coordinate value
    88       * @param y y coordinate value
    89       * @param z z coordinate value
    90       */
    91      private Vector3D(final double x, final double y, final double z) {
    92          this.x = x;
    93          this.y = y;
    94          this.z = z;
    95      }
    96
    97      /** Return the x coordinate value (abscissa) of the instance.
    98       * @return the x coordinate value
    99       */
    100     public double getX() {
    101         return x;
    102     }
    103
    104     /** Return the y coordinate value (ordinate) of the instance.
    105      * @return the y coordinate value
    106      */
    107     public double getY() {
    108         return y;
    109     }
    110
    111     /** Returns the z coordinate value (height) of the instance.
    112      * @return the z coordinate value
    113      */
    114     public double getZ() {
    115         return z;
    116     }
    117
    118     /** Get the coordinates for this instance as a dimension 3 array.
    119      * @return the coordinates for this instance
    120      */
    121     public double[] toArray() {
    122         return new double[]{x, y, z};
    123     }
    124
    125     /** {@inheritDoc} */
    126     @Override
    127     public int getDimension() {
    128         return 3;
    129     }
    130
    131     /** {@inheritDoc} */
    132     @Override
    133     public boolean isNaN() {
    134         return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z);
    135     }
    136
    137     /** {@inheritDoc} */
    138     @Override
    139     public boolean isInfinite() {
    140         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
    141     }
    142
    143     /** {@inheritDoc} */
    144     @Override
    145     public boolean isFinite() {
    146         return Double.isFinite(x) && Double.isFinite(y) && Double.isFinite(z);
    147     }
    148
    149     /** {@inheritDoc} */
    150     @Override
    151     public Vector3D getZero() {
    152         return ZERO;
    153     }
    154
    155     /** {@inheritDoc} */
    156     @Override
    157     public Vector3D vectorTo(final Vector3D v) {
    158         return v.subtract(this);
    159     }
    160
    161     /** {@inheritDoc} */
    162     @Override
    163     public Unit directionTo(final Vector3D v) {
    164         return vectorTo(v).normalize();
    165     }
    166
    167     /** {@inheritDoc} */
    168     @Override
    169     public Vector3D lerp(final Vector3D p, final double t) {
    170         return linearCombination(1.0 - t, this, t, p);
    171     }
    172
    173     /** {@inheritDoc} */
    174     @Override
    175     public double norm() {
    176         return Vectors.norm(x, y, z);
    177     }
    178
    179     /** {@inheritDoc} */
    180     @Override
    181     public double normSq() {
    182         return Vectors.normSq(x, y, z);
    183     }
    184
    185     /** {@inheritDoc} */
    186     @Override
    187     public Vector3D withNorm(final double magnitude) {
    188         final double m = magnitude / getCheckedNorm();
    189
    190         return new Vector3D(
    191                     m * x,
    192                     m * y,
    193                     m * z
    194                 );
    195     }
    196
    197     /** {@inheritDoc} */
    198     @Override
    199     public Vector3D add(final Vector3D v) {
    200         return new Vector3D(
    201                     x + v.x,
    202                     y + v.y,
    203                     z + v.z
    204                 );
    205     }
    206
    207     /** {@inheritDoc} */
    208     @Override
    209     public Vector3D add(final double factor, final Vector3D v) {
    210         return new Vector3D(
    211                     x + (factor * v.x),
    212                     y + (factor * v.y),
    213                     z + (factor * v.z)
    214                 );
    215     }
    216
    217     /** {@inheritDoc} */
    218     @Override
    219     public Vector3D subtract(final Vector3D v) {
    220         return new Vector3D(
    221                     x - v.x,
    222                     y - v.y,
    223                     z - v.z
    224                 );
    225     }
    226
    227     /** {@inheritDoc} */
    228     @Override
    229     public Vector3D subtract(final double factor, final Vector3D v) {
    230         return new Vector3D(
    231                     x - (factor * v.x),
    232                     y - (factor * v.y),
    233                     z - (factor * v.z)
    234                 );
    235     }
    236
    237     /** {@inheritDoc} */
    238     @Override
    239     public Vector3D negate() {
    240         return new Vector3D(-x, -y, -z);
    241     }
    242
    243     /** {@inheritDoc} */
    244     @Override
    245     public Unit normalize() {
    246         return Unit.from(x, y, z);
    247     }
    248
    249     /** {@inheritDoc} */
    250     @Override
    251     public Vector3D multiply(final double a) {
    252         return new Vector3D(a * x, a * y, a * z);
    253     }
    254
    255     /** {@inheritDoc} */
    256     @Override
    257     public double distance(final Vector3D v) {
    258         return Vectors.norm(
    259                 x - v.x,
    260                 y - v.y,
    261                 z - v.z
    262             );
    263     }
    264
    265     /** {@inheritDoc} */
    266     @Override
    267     public double distanceSq(final Vector3D v) {
    268         return Vectors.normSq(
    269                 x - v.x,
    270                 y - v.y,
    271                 z - v.z
    272             );
    273     }
    274
    275     /** {@inheritDoc}
    276      * <p>
    277      * The implementation uses specific multiplication and addition
    278      * algorithms to preserve accuracy and reduce cancellation effects.
    279      * It should be very accurate even for nearly orthogonal vectors.
    280      * </p>
    281      * @see LinearCombination#value(double, double, double, double, double, double)
    282      */
    283     @Override
    284     public double dot(final Vector3D v) {
    285         return LinearCombination.value(x, v.x, y, v.y, z, v.z);
    286     }
    287
    288     /** {@inheritDoc}
    289      * <p>This method computes the angular separation between two
    290      * vectors using the dot product for well separated vectors and the
    291      * cross product for almost aligned vectors. This allows to have a
    292      * good accuracy in all cases, even for vectors very close to each
    293      * other.</p>
    294      */
    295     @Override
    296     public double angle(final Vector3D v) {
    297         final double normProduct = getCheckedNorm() * v.getCheckedNorm();
    298
    299         final double dot = dot(v);
    300         final double threshold = normProduct * 0.99;
    301         if ((dot < -threshold) || (dot > threshold)) {
    302             // the vectors are almost aligned, compute using the sine
    303             final Vector3D cross = cross(v);
    304             if (dot >= 0) {
    305                 return Math.asin(cross.norm() / normProduct);
    306             }
    307             return Math.PI - Math.asin(cross.norm() / normProduct);
    308         }
    309
    310         // the vectors are sufficiently separated to use the cosine
    311         return Math.acos(dot / normProduct);
    312     }
    313
    314     /** {@inheritDoc} */
    315     @Override
    316     public Vector3D project(final Vector3D base) {
    317         return getComponent(base, false, Vector3D::new);
    318     }
    319
    320     /** {@inheritDoc} */
    321     @Override
    322     public Vector3D reject(final Vector3D base) {
    323         return getComponent(base, true, Vector3D::new);
    324     }
    325
    326     /** {@inheritDoc}
    327      * <p>There are an infinite number of normalized vectors orthogonal
    328      * to the instance. This method picks up one of them almost
    329      * arbitrarily. It is useful when one needs to compute a reference
    330      * frame with one of the axes in a predefined direction. The
    331      * following example shows how to build a frame having the k axis
    332      * aligned with the known vector u :
    333      * <pre><code>
    334      *   Vector3D k = u.normalize();
    335      *   Vector3D i = k.orthogonal();
    336      *   Vector3D j = k.cross(i);
    337      * </code></pre>
    338      * @return a unit vector orthogonal to the instance
    339      * @throws IllegalArgumentException if the norm of the instance
    340      *      is zero, NaN, or infinite
    341      */
    342     @Override
    343     public Vector3D.Unit orthogonal() {
    344         final double threshold = 0.6 * getCheckedNorm();
    345
    346         final double inverse;
    347         if (Math.abs(x) <= threshold) {
    348             inverse  = 1 / Vectors.norm(y, z);
    349             return new Unit(0, inverse * z, -inverse * y);
    350         } else if (Math.abs(y) <= threshold) {
    351             inverse  = 1 / Vectors.norm(x, z);
    352             return new Unit(-inverse * z, 0, inverse * x);
    353         }
    354         inverse  = 1 / Vectors.norm(x, y);
    355         return new Unit(inverse * y, -inverse * x, 0);
    356     }
    357
    358     /** {@inheritDoc} */
    359     @Override
    360     public Vector3D.Unit orthogonal(final Vector3D dir) {
    361         return dir.getComponent(this, true, Vector3D.Unit::from);
    362     }
    363
    364     /** Compute the cross-product of the instance with another vector.
    365      * @param v other vector
    366      * @return the cross product this ^ v as a new Vector3D
    367      */
    368     public Vector3D cross(final Vector3D v) {
    369         return new Vector3D(LinearCombination.value(y, v.z, -z, v.y),
    370                             LinearCombination.value(z, v.x, -x, v.z),
    371                             LinearCombination.value(x, v.y, -y, v.x));
    372     }
    373
    374     /** Convenience method to apply a function to this vector. This
    375      * can be used to transform the vector inline with other methods.
    376      * @param fn the function to apply
    377      * @return the transformed vector
    378      */
    379     public Vector3D transform(final UnaryOperator<Vector3D> fn) {
    380         return fn.apply(this);
    381     }
    382
    383     /** {@inheritDoc} */
    384     @Override
    385     public boolean eq(final Vector3D vec, final DoublePrecisionContext precision) {
    386         return precision.eq(x, vec.x) &&
    387                 precision.eq(y, vec.y) &&
    388                 precision.eq(z, vec.z);
    389     }
    390
    391     /**
    392      * Get a hashCode for the vector.
    393      * <p>All NaN values have the same hash code.</p>
    394      *
    395      * @return a hash code value for this object
    396      */
    397     @Override
    398     public int hashCode() {
    399         if (isNaN()) {
    400             return 642;
    401         }
    402         return 643 * (164 * Double.hashCode(x) + 3 * Double.hashCode(y) + Double.hashCode(z));
    403     }
    404
    405     /**d
    406      * Test for the equality of two vector instances.
    407      * <p>
    408      * If all coordinates of two vectors are exactly the same, and none are
    409      * <code>Double.NaN</code>, the two instances are considered to be equal.
    410      * </p>
    411      * <p>
    412      * <code>NaN</code> coordinates are considered to globally affect the vector
    413      * and be equal to each other - i.e, if either (or all) coordinates of the
    414      * vector are equal to <code>Double.NaN</code>, the vector is equal to
    415      * {@link #NaN}.
    416      * </p>
    417      *
    418      * @param other Object to test for equality to this
    419      * @return true if two Vector3D objects are equal, false if
    420      *         object is null, not an instance of Vector3D, or
    421      *         not equal to this Vector3D instance
    422      *
    423      */
    424     @Override
    425     public boolean equals(final Object other) {
    426         if (this == other) {
    427             return true;
    428         }
    429         if (other instanceof Vector3D) {
    430             final Vector3D rhs = (Vector3D) other;
    431             if (rhs.isNaN()) {
    432                 return this.isNaN();
    433             }
    434
    435             return Double.compare(x, rhs.x) == 0 &&
    436                     Double.compare(y, rhs.y) == 0 &&
    437                     Double.compare(z, rhs.z) == 0;
    438         }
    439         return false;
    440     }
    441
    442     /** {@inheritDoc} */
    443     @Override
    444     public String toString() {
    445         return SimpleTupleFormat.getDefault().format(x, y, z);
    446     }
    447
    448     /** Returns a component of the current instance relative to the given base
    449      * vector. If {@code reject} is true, the vector rejection is returned; otherwise,
    450      * the projection is returned.
    451      * @param base The base vector
    452      * @param reject If true, the rejection of this instance from {@code base} is
    453      *      returned. If false, the projection of this instance onto {@code base}
    454      *      is returned.
    455      * @param factory factory function used to build the final vector
    456      * @param <V> Vector implementation type
    457      * @return The projection or rejection of this instance relative to {@code base},
    458      *      depending on the value of {@code reject}.
    459      * @throws IllegalArgumentException if {@code base} has a zero, NaN,
    460      *      or infinite norm
    461      */
    462     private <V extends Vector3D> V getComponent(final Vector3D base, final boolean reject,
    463                                                 final DoubleFunction3N<V> factory) {
    464         final double aDotB = dot(base);
    465
    466         // We need to check the norm value here to ensure that it's legal. However, we don't
    467         // want to incur the cost or floating point error of getting the actual norm and then
    468         // multiplying it again to get the square norm. So, we'll just check the squared norm
    469         // directly. This will produce the same error result as checking the actual norm since
    470         // Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and
    471         // Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY.
    472         final double baseMagSq = Vectors.checkedNorm(base.normSq());
    473
    474         final double scale = aDotB / baseMagSq;
    475
    476         final double projX = scale * base.x;
    477         final double projY = scale * base.y;
    478         final double projZ = scale * base.z;
    479
    480         if (reject) {
    481             return factory.apply(x - projX, y - projY, z - projZ);
    482         }
    483
    484         return factory.apply(projX, projY, projZ);
    485     }
    486
    487     /** Returns a vector with the given coordinate values.
    488      * @param x x coordinate value
    489      * @param y y coordinate value
    490      * @param z z coordinate value
    491      * @return vector instance
    492      */
    493     public static Vector3D of(final double x, final double y, final double z) {
    494         return new Vector3D(x, y, z);
    495     }
    496
    497     /** Creates a vector from the coordinates in the given 3-element array.
    498      * @param v coordinates array
    499      * @return new vector
    500      * @exception IllegalArgumentException if the array does not have 3 elements
    501      */
    502     public static Vector3D of(final double[] v) {
    503         if (v.length != 3) {
    504             throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 3");
    505         }
    506         return new Vector3D(v[0], v[1], v[2]);
    507     }
    508
    509     /** Parses the given string and returns a new vector instance. The expected string
    510      * format is the same as that returned by {@link #toString()}.
    511      * @param str the string to parse
    512      * @return vector instance represented by the string
    513      * @throws IllegalArgumentException if the given string has an invalid format
    514      */
    515     public static Vector3D parse(final String str) {
    516         return SimpleTupleFormat.getDefault().parse(str, Vector3D::new);
    517     }
    518
    519     /** Return a vector containing the maximum component values from all input vectors.
    520      * @param first first vector
    521      * @param more additional vectors
    522      * @return a vector containing the maximum component values from all input vectors
    523      */
    524     public static Vector3D max(final Vector3D first, final Vector3D... more) {
    525         return computeMax(first, Arrays.asList(more).iterator());
    526     }
    527
    528     /** Return a vector containing the maximum component values from all input vectors.
    529      * @param vecs input vectors
    530      * @return a vector containing the maximum component values from all input vectors
    531      * @throws IllegalArgumentException if the argument does not contain any vectors
    532      */
    533     public static Vector3D max(final Iterable<Vector3D> vecs) {
    534         final Iterator<Vector3D> it = vecs.iterator();
    535         if (!it.hasNext()) {
    536             throw new IllegalArgumentException("Cannot compute vector max: no vectors given");
    537         }
    538
    539         return computeMax(it.next(), it);
    540     }
    541
    542     /** Internal method for computing a max vector.
    543      * @param first first vector
    544      * @param more iterator with additional vectors
    545      * @return vector containing the maximum component values of all input vectors
    546      */
    547     private static Vector3D computeMax(final Vector3D first, final Iterator<Vector3D> more) {
    548         double x = first.getX();
    549         double y = first.getY();
    550         double z = first.getZ();
    551
    552         Vector3D vec;
    553         while (more.hasNext()) {
    554             vec = more.next();
    555
    556             x = Math.max(x, vec.getX());
    557             y = Math.max(y, vec.getY());
    558             z = Math.max(z, vec.getZ());
    559         }
    560
    561         return Vector3D.of(x, y, z);
    562     }
    563
    564     /** Return a vector containing the minimum component values from all input vectors.
    565      * @param first first vector
    566      * @param more additional vectors
    567      * @return a vector containing the minimum component values from all input vectors
    568      */
    569     public static Vector3D min(final Vector3D first, final Vector3D... more) {
    570         return computeMin(first, Arrays.asList(more).iterator());
    571     }
    572
    573     /** Return a vector containing the minimum component values from all input vectors.
    574      * @param vecs input vectors
    575      * @return a vector containing the minimum component values from all input vectors
    576      * @throws IllegalArgumentException if the argument does not contain any vectors
    577      */
    578     public static Vector3D min(final Iterable<Vector3D> vecs) {
    579         final Iterator<Vector3D> it = vecs.iterator();
    580         if (!it.hasNext()) {
    581             throw new IllegalArgumentException("Cannot compute vector min: no vectors given");
    582         }
    583
    584         return computeMin(it.next(), it);
    585     }
    586
    587     /** Internal method for computing a min vector.
    588      * @param first first vector
    589      * @param more iterator with additional vectors
    590      * @return vector containing the minimum component values of all input vectors
    591      */
    592     private static Vector3D computeMin(final Vector3D first, final Iterator<Vector3D> more) {
    593         double x = first.getX();
    594         double y = first.getY();
    595         double z = first.getZ();
    596
    597         Vector3D vec;
    598         while (more.hasNext()) {
    599             vec = more.next();
    600
    601             x = Math.min(x, vec.getX());
    602             y = Math.min(y, vec.getY());
    603             z = Math.min(z, vec.getZ());
    604         }
    605
    606         return Vector3D.of(x, y, z);
    607     }
    608
    609     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
    610      * of points.
    611      * @param first first point
    612      * @param more additional points
    613      * @return the centroid of the given points
    614      */
    615     public static Vector3D centroid(final Vector3D first, final Vector3D... more) {
    616         return computeCentroid(first, Arrays.asList(more).iterator());
    617     }
    618
    619     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
    620      * of points.
    621      * @param pts the points to compute the centroid of
    622      * @return the centroid of the given points
    623      * @throws IllegalArgumentException if the argument contains no points
    624      */
    625     public static Vector3D centroid(final Iterable<Vector3D> pts) {
    626         final Iterator<Vector3D> it = pts.iterator();
    627         if (!it.hasNext()) {
    628             throw new IllegalArgumentException("Cannot compute centroid: no points given");
    629         }
    630
    631         return computeCentroid(it.next(), it);
    632     }
    633
    634     /** Internal method for computing the centroid of a set of points.
    635      * @param first first point
    636      * @param more iterator with additional points
    637      * @return the centroid of the point set
    638      */
    639     private static Vector3D computeCentroid(final Vector3D first, final Iterator<Vector3D> more) {
    640         double x = first.getX();
    641         double y = first.getY();
    642         double z = first.getZ();
    643
    644         int count = 1;
    645
    646         Vector3D pt;
    647         while (more.hasNext()) {
    648             pt = more.next();
    649
    650             x += pt.getX();
    651             y += pt.getY();
    652             z += pt.getZ();
    653
    654             ++count;
    655         }
    656
    657         final double invCount = 1.0 / count;
    658
    659         return new Vector3D(invCount * x, invCount * y, invCount * z);
    660     }
    661
    662     /** Returns a vector consisting of the linear combination of the inputs.
    663      * <p>
    664      * A linear combination is the sum of all of the inputs multiplied by their
    665      * corresponding scale factors.
    666      * </p>
    667      *
    668      * @param a scale factor for first vector
    669      * @param c first vector
    670      * @return vector calculated by {@code a * c}
    671      */
    672     public static Vector3D linearCombination(final double a, final Vector3D c) {
    673         return c.multiply(a);
    674     }
    675
    676     /** Returns a vector consisting of the linear combination of the inputs.
    677      * <p>
    678      * A linear combination is the sum of all of the inputs multiplied by their
    679      * corresponding scale factors.
    680      * </p>
    681      *
    682      * @param a1 scale factor for first vector
    683      * @param v1 first vector
    684      * @param a2 scale factor for second vector
    685      * @param v2 second vector
    686      * @return vector calculated by {@code (a1 * v1) + (a2 * v2)}
    687      */
    688     public static Vector3D linearCombination(final double a1, final Vector3D v1,
    689             final double a2, final Vector3D v2) {
    690         return new Vector3D(
    691                 LinearCombination.value(a1, v1.x, a2, v2.x),
    692                 LinearCombination.value(a1, v1.y, a2, v2.y),
    693                 LinearCombination.value(a1, v1.z, a2, v2.z));
    694     }
    695
    696     /** Returns a vector consisting of the linear combination of the inputs.
    697      * <p>
    698      * A linear combination is the sum of all of the inputs multiplied by their
    699      * corresponding scale factors.
    700      * </p>
    701      *
    702      * @param a1 scale factor for first vector
    703      * @param v1 first vector
    704      * @param a2 scale factor for second vector
    705      * @param v2 second vector
    706      * @param a3 scale factor for third vector
    707      * @param v3 third vector
    708      * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3)}
    709      */
    710     public static Vector3D linearCombination(final double a1, final Vector3D v1,
    711             final double a2, final Vector3D v2,
    712             final double a3, final Vector3D v3) {
    713         return new Vector3D(
    714                 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x),
    715                 LinearCombination.value(a1, v1.y, a2, v2.y, a3, v3.y),
    716                 LinearCombination.value(a1, v1.z, a2, v2.z, a3, v3.z));
    717     }
    718
    719     /** Returns a vector consisting of the linear combination of the inputs.
    720      * <p>
    721      * A linear combination is the sum of all of the inputs multiplied by their
    722      * corresponding scale factors.
    723      * </p>
    724      *
    725      * @param a1 scale factor for first vector
    726      * @param v1 first vector
    727      * @param a2 scale factor for second vector
    728      * @param v2 second vector
    729      * @param a3 scale factor for third vector
    730      * @param v3 third vector
    731      * @param a4 scale factor for fourth vector
    732      * @param v4 fourth vector
    733      * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3) + (a4 * v4)}
    734      */
    735     public static Vector3D linearCombination(final double a1, final Vector3D v1,
    736             final double a2, final Vector3D v2,
    737             final double a3, final Vector3D v3,
    738             final double a4, final Vector3D v4) {
    739         return new Vector3D(
    740                 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x, a4, v4.x),
    741                 LinearCombination.value(a1, v1.y, a2, v2.y, a3, v3.y, a4, v4.y),
    742                 LinearCombination.value(a1, v1.z, a2, v2.z, a3, v3.z, a4, v4.z));
    743     }
    744
    745     /**
    746      * Represents unit vectors.
    747      * This allows optimizations for certain operations.
    748      */
    749     public static final class Unit extends Vector3D {
    750         /** Unit vector (coordinates: 1, 0, 0). */
    751         public static final Unit PLUS_X  = new Unit(1d, 0d, 0d);
    752         /** Negation of unit vector (coordinates: -1, 0, 0). */
    753         public static final Unit MINUS_X = new Unit(-1d, 0d, 0d);
    754         /** Unit vector (coordinates: 0, 1, 0). */
    755         public static final Unit PLUS_Y  = new Unit(0d, 1d, 0d);
    756         /** Negation of unit vector (coordinates: 0, -1, 0). */
    757         public static final Unit MINUS_Y = new Unit(0d, -1d, 0d);
    758         /** Unit vector (coordinates: 0, 0, 1). */
    759         public static final Unit PLUS_Z  = new Unit(0d, 0d, 1d);
    760         /** Negation of unit vector (coordinates: 0, 0, -1). */
    761         public static final Unit MINUS_Z = new Unit(0d, 0d, -1d);
    762
    763         /** Simple constructor. Callers are responsible for ensuring that the given
    764          * values represent a normalized vector.
    765          * @param x x coordinate value
    766          * @param y x coordinate value
    767          * @param z x coordinate value
    768          */
    769         private Unit(final double x, final double y, final double z) {
    770             super(x, y, z);
    771         }
    772
    773         /**
    774          * Creates a normalized vector.
    775          *
    776          * @param x Vector coordinate.
    777          * @param y Vector coordinate.
    778          * @param z Vector coordinate.
    779          * @return a vector whose norm is 1.
    780          * @throws IllegalArgumentException if the norm of the given value
    781          *      is zero, NaN, or infinite
    782          */
    783         public static Unit from(final double x, final double y, final double z) {
    784             final double invNorm = 1 / Vectors.checkedNorm(Vectors.norm(x, y, z));
    785             return new Unit(x * invNorm, y * invNorm, z * invNorm);
    786         }
    787
    788         /**
    789          * Creates a normalized vector.
    790          *
    791          * @param v Vector.
    792          * @return a vector whose norm is 1.
    793          * @throws IllegalArgumentException if the norm of the given
    794          *      value is zero, NaN, or infinite
    795          */
    796         public static Unit from(final Vector3D v) {
    797             return v instanceof Unit ?
    798                 (Unit) v :
    799                 from(v.getX(), v.getY(), v.getZ());
    800         }
    801
    802         /** {@inheritDoc} */
    803         @Override
    804         public double norm() {
    805             return 1;
    806         }
    807
    808         /** {@inheritDoc} */
    809         @Override
    810         public double normSq() {
    811             return 1;
    812         }
    813
    814         /** {@inheritDoc} */
    815         @Override
    816         public Unit normalize() {
    817             return this;
    818         }
    819
    820         /** {@inheritDoc} */
    821         @Override
    822         public Vector3D withNorm(final double mag) {
    823             return multiply(mag);
    824         }
    825
    826         /** {@inheritDoc} */
    827         @Override
    828         public Unit negate() {
    829             return new Unit(-getX(), -getY(), -getZ());
    830         }
    831     }
    832 }