001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.euclidean.threed;
018
019import java.util.Arrays;
020import java.util.Comparator;
021import java.util.Iterator;
022import java.util.function.UnaryOperator;
023
024import org.apache.commons.geometry.core.internal.DoubleFunction3N;
025import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
026import org.apache.commons.geometry.euclidean.EuclideanVectorSum;
027import org.apache.commons.geometry.euclidean.MultiDimensionalEuclideanVector;
028import org.apache.commons.geometry.euclidean.internal.Vectors;
029import org.apache.commons.numbers.core.Precision;
030
031/** This class represents vectors and points in three-dimensional Euclidean space.
032 * Instances of this class are guaranteed to be immutable.
033 */
034public class Vector3D extends MultiDimensionalEuclideanVector<Vector3D> {
035
036    /** Zero (null) vector (coordinates: 0, 0, 0). */
037    public static final Vector3D ZERO = new Vector3D(0, 0, 0);
038
039    /** A vector with all coordinates set to NaN. */
040    public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
041
042    /** A vector with all coordinates set to positive infinity. */
043    public static final Vector3D POSITIVE_INFINITY =
044        new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
045
046    /** A vector with all coordinates set to negative infinity. */
047    public static final Vector3D NEGATIVE_INFINITY =
048        new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
049
050    /** Comparator that sorts vectors in component-wise ascending order.
051     * Vectors are only considered equal if their coordinates match exactly.
052     * Null arguments are evaluated as being greater than non-null arguments.
053     */
054    public static final Comparator<Vector3D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
055        int cmp = 0;
056
057        if (a != null && b != null) {
058            cmp = Double.compare(a.getX(), b.getX());
059            if (cmp == 0) {
060                cmp = Double.compare(a.getY(), b.getY());
061                if (cmp == 0) {
062                    cmp = Double.compare(a.getZ(), b.getZ());
063                }
064            }
065        } else if (a != null) {
066            cmp = -1;
067        } else if (b != null) {
068            cmp = 1;
069        }
070
071        return cmp;
072    };
073
074    /** X coordinate value (abscissa). */
075    private final double x;
076
077    /** Y coordinate value (ordinate). */
078    private final double y;
079
080    /** Z coordinate value (height). */
081    private final double z;
082
083    /** Simple constructor.
084     * Build a vector from its coordinates
085     * @param x x coordinate value
086     * @param y y coordinate value
087     * @param z z coordinate value
088     */
089    private Vector3D(final double x, final double y, final double z) {
090        this.x = x;
091        this.y = y;
092        this.z = z;
093    }
094
095    /** Return the x coordinate value (abscissa) of the instance.
096     * @return the x coordinate value
097     */
098    public double getX() {
099        return x;
100    }
101
102    /** Return the y coordinate value (ordinate) of the instance.
103     * @return the y coordinate value
104     */
105    public double getY() {
106        return y;
107    }
108
109    /** Returns the z coordinate value (height) of the instance.
110     * @return the z coordinate value
111     */
112    public double getZ() {
113        return z;
114    }
115
116    /** Get the coordinates for this instance as a dimension 3 array.
117     * @return the coordinates for this instance
118     */
119    public double[] toArray() {
120        return new double[]{x, y, z};
121    }
122
123    /** {@inheritDoc} */
124    @Override
125    public int getDimension() {
126        return 3;
127    }
128
129    /** {@inheritDoc} */
130    @Override
131    public boolean isNaN() {
132        return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z);
133    }
134
135    /** {@inheritDoc} */
136    @Override
137    public boolean isInfinite() {
138        return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
139    }
140
141    /** {@inheritDoc} */
142    @Override
143    public boolean isFinite() {
144        return Double.isFinite(x) && Double.isFinite(y) && Double.isFinite(z);
145    }
146
147    /** {@inheritDoc} */
148    @Override
149    public Vector3D getZero() {
150        return ZERO;
151    }
152
153    /** {@inheritDoc} */
154    @Override
155    public Vector3D vectorTo(final Vector3D v) {
156        return v.subtract(this);
157    }
158
159    /** {@inheritDoc} */
160    @Override
161    public Unit directionTo(final Vector3D v) {
162        return vectorTo(v).normalize();
163    }
164
165    /** {@inheritDoc} */
166    @Override
167    public Vector3D lerp(final Vector3D p, final double t) {
168        return Sum.create()
169                .addScaled(1.0 - t, this)
170                .addScaled(t, p).get();
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 Unit normalizeOrNull() {
252        return Unit.tryCreateNormalized(x, y, z, false);
253    }
254
255    /** {@inheritDoc} */
256    @Override
257    public Vector3D multiply(final double a) {
258        return new Vector3D(a * x, a * y, a * z);
259    }
260
261    /** {@inheritDoc} */
262    @Override
263    public double distance(final Vector3D v) {
264        return Vectors.norm(
265                x - v.x,
266                y - v.y,
267                z - v.z
268            );
269    }
270
271    /** {@inheritDoc} */
272    @Override
273    public double distanceSq(final Vector3D v) {
274        return Vectors.normSq(
275                x - v.x,
276                y - v.y,
277                z - v.z
278            );
279    }
280
281    /** {@inheritDoc}
282     * <p>
283     * The implementation uses specific multiplication and addition
284     * algorithms to preserve accuracy and reduce cancellation effects.
285     * It should be very accurate even for nearly orthogonal vectors.
286     * </p>
287     * @see org.apache.commons.numbers.core.Sum
288     */
289    @Override
290    public double dot(final Vector3D v) {
291        return Vectors.linearCombination(
292                x, v.x,
293                y, v.y,
294                z, v.z);
295    }
296
297    /** {@inheritDoc}
298     * <p>This method computes the angular separation between two
299     * vectors using the dot product for well separated vectors and the
300     * cross product for almost aligned vectors. This allows to have a
301     * good accuracy in all cases, even for vectors very close to each
302     * other.</p>
303     */
304    @Override
305    public double angle(final Vector3D v) {
306        final double normProduct = getCheckedNorm() * v.getCheckedNorm();
307
308        final double dot = dot(v);
309        final double threshold = normProduct * 0.99;
310        if ((dot < -threshold) || (dot > threshold)) {
311            // the vectors are almost aligned, compute using the sine
312            final Vector3D cross = cross(v);
313            if (dot >= 0) {
314                return Math.asin(cross.norm() / normProduct);
315            }
316            return Math.PI - Math.asin(cross.norm() / normProduct);
317        }
318
319        // the vectors are sufficiently separated to use the cosine
320        return Math.acos(dot / normProduct);
321    }
322
323    /** {@inheritDoc} */
324    @Override
325    public Vector3D project(final Vector3D base) {
326        return getComponent(base, false, Vector3D::new);
327    }
328
329    /** {@inheritDoc} */
330    @Override
331    public Vector3D reject(final Vector3D base) {
332        return getComponent(base, true, Vector3D::new);
333    }
334
335    /** {@inheritDoc}
336     * <p>There are an infinite number of normalized vectors orthogonal
337     * to the instance. This method picks up one of them almost
338     * arbitrarily. It is useful when one needs to compute a reference
339     * frame with one of the axes in a predefined direction. The
340     * following example shows how to build a frame having the k axis
341     * aligned with the known vector u :
342     * <pre><code>
343     *   Vector3D k = u.normalize();
344     *   Vector3D i = k.orthogonal();
345     *   Vector3D j = k.cross(i);
346     * </code></pre>
347     * @return a unit vector orthogonal to the instance
348     * @throws IllegalArgumentException if the norm of the instance
349     *      is zero, NaN, or infinite
350     */
351    @Override
352    public Vector3D.Unit orthogonal() {
353        final double threshold = 0.6 * getCheckedNorm();
354
355        final double inverse;
356        if (Math.abs(x) <= threshold) {
357            inverse  = 1 / Vectors.norm(y, z);
358            return new Unit(0, inverse * z, -inverse * y);
359        } else if (Math.abs(y) <= threshold) {
360            inverse  = 1 / Vectors.norm(x, z);
361            return new Unit(-inverse * z, 0, inverse * x);
362        }
363        inverse  = 1 / Vectors.norm(x, y);
364        return new Unit(inverse * y, -inverse * x, 0);
365    }
366
367    /** {@inheritDoc} */
368    @Override
369    public Vector3D.Unit orthogonal(final Vector3D dir) {
370        return dir.getComponent(this, true, Vector3D.Unit::from);
371    }
372
373    /** Compute the cross-product of the instance with another vector.
374     * @param v other vector
375     * @return the cross product this ^ v as a new Vector3D
376     */
377    public Vector3D cross(final Vector3D v) {
378        return new Vector3D(Vectors.linearCombination(y, v.z, -z, v.y),
379                            Vectors.linearCombination(z, v.x, -x, v.z),
380                            Vectors.linearCombination(x, v.y, -y, v.x));
381    }
382
383    /** Convenience method to apply a function to this vector. This
384     * can be used to transform the vector inline with other methods.
385     * @param fn the function to apply
386     * @return the transformed vector
387     */
388    public Vector3D transform(final UnaryOperator<Vector3D> fn) {
389        return fn.apply(this);
390    }
391
392    /** {@inheritDoc} */
393    @Override
394    public boolean eq(final Vector3D vec, final Precision.DoubleEquivalence precision) {
395        return precision.eq(x, vec.x) &&
396                precision.eq(y, vec.y) &&
397                precision.eq(z, vec.z);
398    }
399
400    /**
401     * Get a hashCode for the vector.
402     * <p>All NaN values have the same hash code.</p>
403     *
404     * @return a hash code value for this object
405     */
406    @Override
407    public int hashCode() {
408        if (isNaN()) {
409            return 642;
410        }
411        return 643 * (164 * Double.hashCode(x) + 3 * Double.hashCode(y) + Double.hashCode(z));
412    }
413
414    /**d
415     * Test for the equality of two vector instances.
416     * <p>
417     * If all coordinates of two vectors are exactly the same, and none are
418     * <code>Double.NaN</code>, the two instances are considered to be equal.
419     * </p>
420     * <p>
421     * <code>NaN</code> coordinates are considered to globally affect the vector
422     * and be equal to each other - i.e, if either (or all) coordinates of the
423     * vector are equal to <code>Double.NaN</code>, the vector is equal to
424     * {@link #NaN}.
425     * </p>
426     *
427     * @param other Object to test for equality to this
428     * @return true if two Vector3D objects are equal, false if
429     *         object is null, not an instance of Vector3D, or
430     *         not equal to this Vector3D instance
431     *
432     */
433    @Override
434    public boolean equals(final Object other) {
435        if (this == other) {
436            return true;
437        }
438        if (other instanceof Vector3D) {
439            final Vector3D rhs = (Vector3D) other;
440            if (rhs.isNaN()) {
441                return this.isNaN();
442            }
443
444            return Double.compare(x, rhs.x) == 0 &&
445                    Double.compare(y, rhs.y) == 0 &&
446                    Double.compare(z, rhs.z) == 0;
447        }
448        return false;
449    }
450
451    /** {@inheritDoc} */
452    @Override
453    public String toString() {
454        return SimpleTupleFormat.getDefault().format(x, y, z);
455    }
456
457    /** Returns a component of the current instance relative to the given base
458     * vector. If {@code reject} is true, the vector rejection is returned; otherwise,
459     * the projection is returned.
460     * @param base The base vector
461     * @param reject If true, the rejection of this instance from {@code base} is
462     *      returned. If false, the projection of this instance onto {@code base}
463     *      is returned.
464     * @param factory factory function used to build the final vector
465     * @param <V> Vector implementation type
466     * @return The projection or rejection of this instance relative to {@code base},
467     *      depending on the value of {@code reject}.
468     * @throws IllegalArgumentException if {@code base} has a zero, NaN,
469     *      or infinite norm
470     */
471    private <V extends Vector3D> V getComponent(final Vector3D base, final boolean reject,
472                                                final DoubleFunction3N<V> factory) {
473        final double aDotB = dot(base);
474
475        // We need to check the norm value here to ensure that it's legal. However, we don't
476        // want to incur the cost or floating point error of getting the actual norm and then
477        // multiplying it again to get the square norm. So, we'll just check the squared norm
478        // directly. This will produce the same error result as checking the actual norm since
479        // Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and
480        // Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY.
481        final double baseMagSq = Vectors.checkedNorm(base.normSq());
482
483        final double scale = aDotB / baseMagSq;
484
485        final double projX = scale * base.x;
486        final double projY = scale * base.y;
487        final double projZ = scale * base.z;
488
489        if (reject) {
490            return factory.apply(x - projX, y - projY, z - projZ);
491        }
492
493        return factory.apply(projX, projY, projZ);
494    }
495
496    /** Returns a vector with the given coordinate values.
497     * @param x x coordinate value
498     * @param y y coordinate value
499     * @param z z coordinate value
500     * @return vector instance
501     */
502    public static Vector3D of(final double x, final double y, final double z) {
503        return new Vector3D(x, y, z);
504    }
505
506    /** Creates a vector from the coordinates in the given 3-element array.
507     * @param v coordinates array
508     * @return new vector
509     * @exception IllegalArgumentException if the array does not have 3 elements
510     */
511    public static Vector3D of(final double[] v) {
512        if (v.length != 3) {
513            throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 3");
514        }
515        return new Vector3D(v[0], v[1], v[2]);
516    }
517
518    /** Parses the given string and returns a new vector instance. The expected string
519     * format is the same as that returned by {@link #toString()}.
520     * @param str the string to parse
521     * @return vector instance represented by the string
522     * @throws IllegalArgumentException if the given string has an invalid format
523     */
524    public static Vector3D parse(final String str) {
525        return SimpleTupleFormat.getDefault().parse(str, Vector3D::new);
526    }
527
528    /** Return a vector containing the maximum component values from all input vectors.
529     * @param first first vector
530     * @param more additional vectors
531     * @return a vector containing the maximum component values from all input vectors
532     */
533    public static Vector3D max(final Vector3D first, final Vector3D... more) {
534        return computeMax(first, Arrays.asList(more).iterator());
535    }
536
537    /** Return a vector containing the maximum component values from all input vectors.
538     * @param vecs input vectors
539     * @return a vector containing the maximum component values from all input vectors
540     * @throws IllegalArgumentException if the argument does not contain any vectors
541     */
542    public static Vector3D max(final Iterable<Vector3D> vecs) {
543        final Iterator<Vector3D> it = vecs.iterator();
544        if (!it.hasNext()) {
545            throw new IllegalArgumentException("Cannot compute vector max: no vectors given");
546        }
547
548        return computeMax(it.next(), it);
549    }
550
551    /** Internal method for computing a max vector.
552     * @param first first vector
553     * @param more iterator with additional vectors
554     * @return vector containing the maximum component values of all input vectors
555     */
556    private static Vector3D computeMax(final Vector3D first, final Iterator<? extends Vector3D> more) {
557        double x = first.getX();
558        double y = first.getY();
559        double z = first.getZ();
560
561        Vector3D vec;
562        while (more.hasNext()) {
563            vec = more.next();
564
565            x = Math.max(x, vec.getX());
566            y = Math.max(y, vec.getY());
567            z = Math.max(z, vec.getZ());
568        }
569
570        return Vector3D.of(x, y, z);
571    }
572
573    /** Return a vector containing the minimum component values from all input vectors.
574     * @param first first vector
575     * @param more additional vectors
576     * @return a vector containing the minimum component values from all input vectors
577     */
578    public static Vector3D min(final Vector3D first, final Vector3D... more) {
579        return computeMin(first, Arrays.asList(more).iterator());
580    }
581
582    /** Return a vector containing the minimum component values from all input vectors.
583     * @param vecs input vectors
584     * @return a vector containing the minimum component values from all input vectors
585     * @throws IllegalArgumentException if the argument does not contain any vectors
586     */
587    public static Vector3D min(final Iterable<Vector3D> vecs) {
588        final Iterator<Vector3D> it = vecs.iterator();
589        if (!it.hasNext()) {
590            throw new IllegalArgumentException("Cannot compute vector min: no vectors given");
591        }
592
593        return computeMin(it.next(), it);
594    }
595
596    /** Internal method for computing a min vector.
597     * @param first first vector
598     * @param more iterator with additional vectors
599     * @return vector containing the minimum component values of all input vectors
600     */
601    private static Vector3D computeMin(final Vector3D first, final Iterator<? extends Vector3D> more) {
602        double x = first.getX();
603        double y = first.getY();
604        double z = first.getZ();
605
606        Vector3D vec;
607        while (more.hasNext()) {
608            vec = more.next();
609
610            x = Math.min(x, vec.getX());
611            y = Math.min(y, vec.getY());
612            z = Math.min(z, vec.getZ());
613        }
614
615        return Vector3D.of(x, y, z);
616    }
617
618    /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
619     * of points.
620     * @param first first point
621     * @param more additional points
622     * @return the centroid of the given points
623     */
624    public static Vector3D centroid(final Vector3D first, final Vector3D... more) {
625        return computeCentroid(first, Arrays.asList(more).iterator());
626    }
627
628    /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
629     * of points.
630     * @param pts the points to compute the centroid of
631     * @return the centroid of the given points
632     * @throws IllegalArgumentException if the argument contains no points
633     */
634    public static Vector3D centroid(final Iterable<Vector3D> pts) {
635        final Iterator<Vector3D> it = pts.iterator();
636        if (!it.hasNext()) {
637            throw new IllegalArgumentException("Cannot compute centroid: no points given");
638        }
639
640        return computeCentroid(it.next(), it);
641    }
642
643    /** Internal method for computing the centroid of a set of points.
644     * @param first first point
645     * @param more iterator with additional points
646     * @return the centroid of the point set
647     */
648    private static Vector3D computeCentroid(final Vector3D first, final Iterator<? extends Vector3D> more) {
649        final Sum sum = Sum.of(first);
650        int count = 1;
651
652        while (more.hasNext()) {
653            sum.add(more.next());
654            ++count;
655        }
656
657        return sum.get().multiply(1.0 / count);
658    }
659
660    /**
661     * Represents unit vectors.
662     * This allows optimizations for certain operations.
663     */
664    public static final class Unit extends Vector3D {
665        /** Unit vector (coordinates: 1, 0, 0). */
666        public static final Unit PLUS_X  = new Unit(1d, 0d, 0d);
667        /** Negation of unit vector (coordinates: -1, 0, 0). */
668        public static final Unit MINUS_X = new Unit(-1d, 0d, 0d);
669        /** Unit vector (coordinates: 0, 1, 0). */
670        public static final Unit PLUS_Y  = new Unit(0d, 1d, 0d);
671        /** Negation of unit vector (coordinates: 0, -1, 0). */
672        public static final Unit MINUS_Y = new Unit(0d, -1d, 0d);
673        /** Unit vector (coordinates: 0, 0, 1). */
674        public static final Unit PLUS_Z  = new Unit(0d, 0d, 1d);
675        /** Negation of unit vector (coordinates: 0, 0, -1). */
676        public static final Unit MINUS_Z = new Unit(0d, 0d, -1d);
677
678        /** Maximum coordinate value for computing normalized vectors
679         * with raw, unscaled values.
680         */
681        private static final double UNSCALED_MAX = 0x1.0p+500;
682
683        /** Factor used to scale up coordinate values in order to produce
684         * normalized coordinates without overflow or underflow.
685         */
686        private static final double SCALE_UP_FACTOR = 0x1.0p+600;
687
688        /** Factor used to scale down coordinate values in order to produce
689         * normalized coordinates without overflow or underflow.
690         */
691        private static final double SCALE_DOWN_FACTOR = 0x1.0p-600;
692
693        /** Simple constructor. Callers are responsible for ensuring that the given
694         * values represent a normalized vector.
695         * @param x x coordinate value
696         * @param y x coordinate value
697         * @param z x coordinate value
698         */
699        private Unit(final double x, final double y, final double z) {
700            super(x, y, z);
701        }
702
703        /** {@inheritDoc} */
704        @Override
705        public double norm() {
706            return 1;
707        }
708
709        /** {@inheritDoc} */
710        @Override
711        public double normSq() {
712            return 1;
713        }
714
715        /** {@inheritDoc} */
716        @Override
717        public Unit normalize() {
718            return this;
719        }
720
721        /** {@inheritDoc} */
722        @Override
723        public Unit normalizeOrNull() {
724            return this;
725        }
726
727        /** {@inheritDoc} */
728        @Override
729        public Vector3D withNorm(final double mag) {
730            return multiply(mag);
731        }
732
733        /** {@inheritDoc} */
734        @Override
735        public Unit negate() {
736            return new Unit(-getX(), -getY(), -getZ());
737        }
738
739        /** Create a normalized vector.
740         * @param x Vector coordinate.
741         * @param y Vector coordinate.
742         * @param z Vector coordinate.
743         * @return a vector whose norm is 1.
744         * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
745         *      or infinite
746         */
747        public static Unit from(final double x, final double y, final double z) {
748            return tryCreateNormalized(x, y, z, true);
749        }
750
751        /** Create a normalized vector.
752         * @param v Vector.
753         * @return a vector whose norm is 1.
754         * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
755         *      or infinite
756         */
757        public static Unit from(final Vector3D v) {
758            return v instanceof Unit ?
759                (Unit) v :
760                from(v.getX(), v.getY(), v.getZ());
761        }
762
763        /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure}
764         * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null
765         * is returned.
766         * @param x x coordinate
767         * @param y y coordinate
768         * @param z z coordinate
769         * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created
770         * @return normalized vector or null if one cannot be created and {@code throwOnFailure}
771         *      is false
772         * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite
773         */
774        private static Unit tryCreateNormalized(final double x, final double y, final double z,
775                final boolean throwOnFailure) {
776
777            // Compute the inverse norm directly. If the result is a non-zero real number,
778            // then we can go ahead and construct the unit vector immediately. If not,
779            // we'll do some extra work for edge cases.
780            final double norm = Math.sqrt((x * x) + (y * y) + (z * z));
781            final double normInv = 1.0 / norm;
782            if (Vectors.isRealNonZero(normInv)) {
783                return new Unit(
784                        x * normInv,
785                        y * normInv,
786                        z * normInv);
787            }
788
789            // Direct computation did not work. Try scaled versions of the coordinates
790            // to handle overflow and underflow.
791            final double scaledX;
792            final double scaledY;
793            final double scaledZ;
794
795            final double maxCoord = Math.max(Math.max(Math.abs(x), Math.abs(y)), Math.abs(z));
796            if (maxCoord > UNSCALED_MAX) {
797                scaledX = x * SCALE_DOWN_FACTOR;
798                scaledY = y * SCALE_DOWN_FACTOR;
799                scaledZ = z * SCALE_DOWN_FACTOR;
800            } else {
801                scaledX = x * SCALE_UP_FACTOR;
802                scaledY = y * SCALE_UP_FACTOR;
803                scaledZ = z * SCALE_UP_FACTOR;
804            }
805
806            final double scaledNormInv = 1.0 / Math.sqrt(
807                    (scaledX * scaledX) +
808                    (scaledY * scaledY) +
809                    (scaledZ * scaledZ));
810
811            if (Vectors.isRealNonZero(scaledNormInv)) {
812                return new Unit(
813                        scaledX * scaledNormInv,
814                        scaledY * scaledNormInv,
815                        scaledZ * scaledNormInv);
816            } else if (throwOnFailure) {
817                throw Vectors.illegalNorm(norm);
818            }
819            return null;
820        }
821    }
822
823    /** Class used to create high-accuracy sums of vectors. Each vector component is
824     * summed using an instance of {@link org.apache.commons.numbers.core.Sum}.
825     *
826     * <p>This class is mutable and not thread-safe.
827     * @see org.apache.commons.numbers.core.Sum
828     */
829    public static final class Sum extends EuclideanVectorSum<Vector3D> {
830        /** X component sum. */
831        private final org.apache.commons.numbers.core.Sum xsum;
832        /** Y component sum. */
833        private final org.apache.commons.numbers.core.Sum ysum;
834        /** Z component sum. */
835        private final org.apache.commons.numbers.core.Sum zsum;
836
837        /** Construct a new instance with the given initial value.
838         * @param initial initial value
839         */
840        Sum(final Vector3D initial) {
841            this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x);
842            this.ysum = org.apache.commons.numbers.core.Sum.of(initial.y);
843            this.zsum = org.apache.commons.numbers.core.Sum.of(initial.z);
844        }
845
846        /** {@inheritDoc} */
847        @Override
848        public Sum add(final Vector3D vec) {
849            xsum.add(vec.x);
850            ysum.add(vec.y);
851            zsum.add(vec.z);
852            return this;
853        }
854
855        /** {@inheritDoc} */
856        @Override
857        public Sum addScaled(final double scale, final Vector3D vec) {
858            xsum.addProduct(scale, vec.x);
859            ysum.addProduct(scale, vec.y);
860            zsum.addProduct(scale, vec.z);
861            return this;
862        }
863
864        /** {@inheritDoc} */
865        @Override
866        public Vector3D get() {
867            return Vector3D.of(
868                    xsum.getAsDouble(),
869                    ysum.getAsDouble(),
870                    zsum.getAsDouble());
871        }
872
873        /** Create a new instance with an initial value set to the {@link Vector3D#ZERO zero vector}.
874         * @return new instance set to zero
875         */
876        public static Sum create() {
877            return new Sum(Vector3D.ZERO);
878        }
879
880        /** Construct a new instance with an initial value set to the argument.
881         * @param initial initial sum value
882         * @return new instance
883         */
884        public static Sum of(final Vector3D initial) {
885            return new Sum(initial);
886        }
887
888        /** Construct a new instance from multiple values.
889         * @param first first vector
890         * @param more additional vectors
891         * @return new instance
892         */
893        public static Sum of(final Vector3D first, final Vector3D... more) {
894            final Sum s = new Sum(first);
895            for (final Vector3D v : more) {
896                s.add(v);
897            }
898            return s;
899        }
900    }
901}