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.twod;
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.DoubleFunction2N;
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 two-dimensional Euclidean space.
032 * Instances of this class are guaranteed to be immutable.
033 */
034public class Vector2D extends MultiDimensionalEuclideanVector<Vector2D> {
035
036    /** Zero vector (coordinates: 0, 0). */
037    public static final Vector2D ZERO = new Vector2D(0, 0);
038
039    /** A vector with all coordinates set to NaN. */
040    public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
041
042    /** A vector with all coordinates set to positive infinity. */
043    public static final Vector2D POSITIVE_INFINITY =
044        new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
045
046    /** A vector with all coordinates set to negative infinity. */
047    public static final Vector2D NEGATIVE_INFINITY =
048        new Vector2D(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<Vector2D> 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            }
062        } else if (a != null) {
063            cmp = -1;
064        } else if (b != null) {
065            cmp = 1;
066        }
067
068        return cmp;
069    };
070
071    /** Abscissa (first coordinate). */
072    private final double x;
073
074    /** Ordinate (second coordinate). */
075    private final double y;
076
077    /** Simple constructor.
078     * @param x abscissa (first coordinate)
079     * @param y ordinate (second coordinate)
080     */
081    private Vector2D(final double x, final double y) {
082        this.x = x;
083        this.y = y;
084    }
085
086    /** Returns the abscissa (first coordinate value) of the instance.
087     * @return the abscissa
088     */
089    public double getX() {
090        return x;
091    }
092
093    /** Returns the ordinate (second coordinate value) of the instance.
094     * @return the ordinate
095     */
096    public double getY() {
097        return y;
098    }
099
100    /** Get the coordinates for this instance as a dimension 2 array.
101     * @return coordinates for this instance
102     */
103    public double[] toArray() {
104        return new double[]{x, y};
105    }
106
107    /** {@inheritDoc} */
108    @Override
109    public int getDimension() {
110        return 2;
111    }
112
113    /** {@inheritDoc} */
114    @Override
115    public boolean isNaN() {
116        return Double.isNaN(x) || Double.isNaN(y);
117    }
118
119    /** {@inheritDoc} */
120    @Override
121    public boolean isInfinite() {
122        return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
123    }
124
125    /** {@inheritDoc} */
126    @Override
127    public boolean isFinite() {
128        return Double.isFinite(x) && Double.isFinite(y);
129    }
130
131    /** {@inheritDoc} */
132    @Override
133    public Vector2D vectorTo(final Vector2D v) {
134        return v.subtract(this);
135    }
136
137    /** {@inheritDoc} */
138    @Override
139    public Unit directionTo(final Vector2D v) {
140        return vectorTo(v).normalize();
141    }
142
143    /** {@inheritDoc} */
144    @Override
145    public Vector2D lerp(final Vector2D p, final double t) {
146        return Sum.create()
147                .addScaled(1.0 - t, this)
148                .addScaled(t, p).get();
149    }
150
151    /** {@inheritDoc} */
152    @Override
153    public Vector2D getZero() {
154        return ZERO;
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    public double norm() {
160        return Vectors.norm(x, y);
161    }
162
163    /** {@inheritDoc} */
164    @Override
165    public double normSq() {
166        return Vectors.normSq(x, y);
167    }
168
169    /** {@inheritDoc} */
170    @Override
171    public Vector2D withNorm(final double magnitude) {
172        final double invNorm = 1.0 / getCheckedNorm();
173
174        return new Vector2D(
175                    magnitude * x * invNorm,
176                    magnitude * y * invNorm
177                );
178    }
179
180    /** {@inheritDoc} */
181    @Override
182    public Vector2D add(final Vector2D v) {
183        return new Vector2D(x + v.x, y + v.y);
184    }
185
186    /** {@inheritDoc} */
187    @Override
188    public Vector2D add(final double factor, final Vector2D v) {
189        return new Vector2D(x + (factor * v.x), y + (factor * v.y));
190    }
191
192    /** {@inheritDoc} */
193    @Override
194    public Vector2D subtract(final Vector2D v) {
195        return new Vector2D(x - v.x, y - v.y);
196    }
197
198    /** {@inheritDoc} */
199    @Override
200    public Vector2D subtract(final double factor, final Vector2D v) {
201        return new Vector2D(x - (factor * v.x), y - (factor * v.y));
202    }
203
204    /** {@inheritDoc} */
205    @Override
206    public Vector2D negate() {
207        return new Vector2D(-x, -y);
208    }
209
210    /** {@inheritDoc} */
211    @Override
212    public Unit normalize() {
213        return Unit.from(x, y);
214    }
215
216    /** {@inheritDoc} */
217    @Override
218    public Unit normalizeOrNull() {
219        return Unit.tryCreateNormalized(x, y, false);
220    }
221
222    /** {@inheritDoc} */
223    @Override
224    public Vector2D multiply(final double a) {
225        return new Vector2D(a * x, a * y);
226    }
227
228    /** {@inheritDoc} */
229    @Override
230    public double distance(final Vector2D v) {
231        return Vectors.norm(x - v.x, y - v.y);
232    }
233
234    /** {@inheritDoc} */
235    @Override
236    public double distanceSq(final Vector2D v) {
237        return Vectors.normSq(x - v.x, y - v.y);
238    }
239
240    /** {@inheritDoc} */
241    @Override
242    public double dot(final Vector2D v) {
243        return Vectors.linearCombination(x, v.x, y, v.y);
244    }
245
246    /** {@inheritDoc}
247     * <p>This method computes the angular separation between the two
248     * vectors using the dot product for well separated vectors and the
249     * cross product for almost aligned vectors. This allows to have a
250     * good accuracy in all cases, even for vectors very close to each
251     * other.</p>
252     */
253    @Override
254    public double angle(final Vector2D v) {
255        final double normProduct = getCheckedNorm() * v.getCheckedNorm();
256
257        final double dot = dot(v);
258        final double threshold = normProduct * 0.9999;
259        if ((dot < -threshold) || (dot > threshold)) {
260            // the vectors are almost aligned, compute using the sine
261            final double n = Math.abs(Vectors.linearCombination(x, v.y, -y, v.x));
262            if (dot >= 0) {
263                return Math.asin(n / normProduct);
264            }
265            return Math.PI - Math.asin(n / normProduct);
266        }
267
268        // the vectors are sufficiently separated to use the cosine
269        return Math.acos(dot / normProduct);
270    }
271
272    /** {@inheritDoc} */
273    @Override
274    public Vector2D project(final Vector2D base) {
275        return getComponent(base, false, Vector2D::new);
276    }
277
278    /** {@inheritDoc} */
279    @Override
280    public Vector2D reject(final Vector2D base) {
281        return getComponent(base, true, Vector2D::new);
282    }
283
284    /** {@inheritDoc}
285     * The returned vector is computed by rotating the current instance {@code pi/2} radians
286     * counterclockwise around the origin and normalizing. For example, if this method is
287     * called on a vector pointing along the positive x-axis, then a unit vector representing
288     * the positive y-axis is returned.
289     * @return a unit vector orthogonal to the current instance
290     * @throws IllegalArgumentException if the norm of the current instance is zero, NaN, or infinite
291     */
292    @Override
293    public Vector2D.Unit orthogonal() {
294        return Unit.from(-y, x);
295    }
296
297    /** {@inheritDoc} */
298    @Override
299    public Vector2D.Unit orthogonal(final Vector2D dir) {
300        return dir.getComponent(this, true, Vector2D.Unit::from);
301    }
302
303    /** Compute the signed area of the parallelogram with sides formed by this instance
304     * and the given vector.
305     *
306     * <p>The parallelogram in question can be visualized by taking the current instance as the
307     * first side and placing {@code v} at the end of it to create the second. The other sides
308     * are formed by lines parallel to these two vectors. If {@code v} points to the <em>left</em> of
309     * the current instance (ie, the parallelogram is wound counter-clockwise), then the
310     * returned area is positive. If {@code v} points to the <em>right</em> of the current instance,
311     * (ie, the parallelogram is wound clockwise), then the returned area is negative. If
312     * the vectors are collinear (ie, they lie on the same line), then 0 is returned. The area of
313     * the triangle formed by the two vectors is exactly half of the returned value.
314     * @param v vector representing the second side of the constructed parallelogram
315     * @return the signed area of the parallelogram formed by this instance and the given vector
316     */
317    public double signedArea(final Vector2D v) {
318        return Vectors.linearCombination(
319                x, v.y,
320                -y, v.x);
321    }
322
323    /** Convenience method to apply a function to this vector. This
324     * can be used to transform the vector inline with other methods.
325     * @param fn the function to apply
326     * @return the transformed vector
327     */
328    public Vector2D transform(final UnaryOperator<Vector2D> fn) {
329        return fn.apply(this);
330    }
331
332    /** {@inheritDoc} */
333    @Override
334    public boolean eq(final Vector2D vec, final Precision.DoubleEquivalence precision) {
335        return precision.eq(x, vec.x) &&
336                precision.eq(y, vec.y);
337    }
338
339    /**
340     * Get a hashCode for the 2D coordinates.
341     * <p>
342     * All NaN values have the same hash code.</p>
343     *
344     * @return a hash code value for this object
345     */
346    @Override
347    public int hashCode() {
348        if (isNaN()) {
349            return 542;
350        }
351        return 122 * (76 * Double.hashCode(x) + Double.hashCode(y));
352    }
353
354    /**
355     * Test for the equality of two vector instances.
356     * <p>
357     * If all coordinates of two vectors are exactly the same, and none are
358     * <code>Double.NaN</code>, the two instances are considered to be equal.
359     * </p>
360     * <p>
361     * <code>NaN</code> coordinates are considered to globally affect the vector
362     * and be equal to each other - i.e, if either (or all) coordinates of the
363     * vector are equal to <code>Double.NaN</code>, the vector is equal to
364     * {@link #NaN}.
365     * </p>
366     *
367     * @param other Object to test for equality to this
368     * @return true if two Vector2D objects are equal, false if
369     *         object is null, not an instance of Vector2D, or
370     *         not equal to this Vector2D instance
371     *
372     */
373    @Override
374    public boolean equals(final Object other) {
375        if (this == other) {
376            return true;
377        }
378        if (other instanceof Vector2D) {
379            final Vector2D rhs = (Vector2D) other;
380            if (rhs.isNaN()) {
381                return this.isNaN();
382            }
383
384            return Double.compare(x, rhs.x) == 0 &&
385                    Double.compare(y, rhs.y) == 0;
386        }
387        return false;
388    }
389
390    /** {@inheritDoc} */
391    @Override
392    public String toString() {
393        return SimpleTupleFormat.getDefault().format(x, y);
394    }
395
396    /** Returns a component of the current instance relative to the given base
397     * vector. If {@code reject} is true, the vector rejection is returned; otherwise,
398     * the projection is returned.
399     * @param base The base vector
400     * @param reject If true, the rejection of this instance from {@code base} is
401     *      returned. If false, the projection of this instance onto {@code base}
402     *      is returned.
403     * @param factory factory function used to build the final vector
404     * @param <T> Vector implementation type
405     * @return The projection or rejection of this instance relative to {@code base},
406     *      depending on the value of {@code reject}.
407     * @throws IllegalArgumentException if {@code base} has a zero, NaN, or infinite norm
408     */
409    private <T extends Vector2D> T getComponent(final Vector2D base, final boolean reject,
410            final DoubleFunction2N<T> factory) {
411        final double aDotB = dot(base);
412
413        // We need to check the norm value here to ensure that it's legal. However, we don't
414        // want to incur the cost or floating point error of getting the actual norm and then
415        // multiplying it again to get the square norm. So, we'll just check the squared norm
416        // directly. This will produce the same error result as checking the actual norm since
417        // Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and
418        // Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY.
419        final double baseMagSq = Vectors.checkedNorm(base.normSq());
420
421        final double scale = aDotB / baseMagSq;
422
423        final double projX = scale * base.x;
424        final double projY = scale * base.y;
425
426        if (reject) {
427            return factory.apply(x - projX, y - projY);
428        }
429
430        return factory.apply(projX, projY);
431    }
432
433    /** Returns a vector with the given coordinate values.
434     * @param x abscissa (first coordinate value)
435     * @param y abscissa (second coordinate value)
436     * @return vector instance
437     */
438    public static Vector2D of(final double x, final double y) {
439        return new Vector2D(x, y);
440    }
441
442    /** Creates a vector from the coordinates in the given 2-element array.
443     * @param v coordinates array
444     * @return new vector
445     * @exception IllegalArgumentException if the array does not have 2 elements
446     */
447    public static Vector2D of(final double[] v) {
448        if (v.length != 2) {
449            throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 2");
450        }
451        return new Vector2D(v[0], v[1]);
452    }
453
454    /** Parses the given string and returns a new vector instance. The expected string
455     * format is the same as that returned by {@link #toString()}.
456     * @param str the string to parse
457     * @return vector instance represented by the string
458     * @throws IllegalArgumentException if the given string has an invalid format
459     */
460    public static Vector2D parse(final String str) {
461        return SimpleTupleFormat.getDefault().parse(str, Vector2D::new);
462    }
463
464    /** Return a vector containing the maximum component values from all input vectors.
465     * @param first first vector
466     * @param more additional vectors
467     * @return a vector containing the maximum component values from all input vectors
468     */
469    public static Vector2D max(final Vector2D first, final Vector2D... more) {
470        return computeMax(first, Arrays.asList(more).iterator());
471    }
472
473    /** Return a vector containing the maximum component values from all input vectors.
474     * @param vecs input vectors
475     * @return a vector containing the maximum component values from all input vectors
476     * @throws IllegalArgumentException if the argument does not contain any vectors
477     */
478    public static Vector2D max(final Iterable<Vector2D> vecs) {
479        final Iterator<Vector2D> it = vecs.iterator();
480        if (!it.hasNext()) {
481            throw new IllegalArgumentException("Cannot compute vector max: no vectors given");
482        }
483
484        return computeMax(it.next(), it);
485    }
486
487    /** Internal method for computing a max vector.
488     * @param first first vector
489     * @param more iterator with additional vectors
490     * @return vector containing the maximum component values of all input vectors
491     */
492    private static Vector2D computeMax(final Vector2D first, final Iterator<? extends Vector2D> more) {
493        double x = first.getX();
494        double y = first.getY();
495
496        Vector2D vec;
497        while (more.hasNext()) {
498            vec = more.next();
499
500            x = Math.max(x, vec.getX());
501            y = Math.max(y, vec.getY());
502        }
503
504        return Vector2D.of(x, y);
505    }
506
507    /** Return a vector containing the minimum component values from all input vectors.
508     * @param first first vector
509     * @param more more vectors
510     * @return a vector containing the minimum component values from all input vectors
511     */
512    public static Vector2D min(final Vector2D first, final Vector2D... more) {
513        return computeMin(first, Arrays.asList(more).iterator());
514    }
515
516    /** Return a vector containing the minimum component values from all input vectors.
517     * @param vecs input vectors
518     * @return a vector containing the minimum component values from all input vectors
519     * @throws IllegalArgumentException if the argument does not contain any vectors
520     */
521    public static Vector2D min(final Iterable<Vector2D> vecs) {
522        final Iterator<Vector2D> it = vecs.iterator();
523        if (!it.hasNext()) {
524            throw new IllegalArgumentException("Cannot compute vector min: no vectors given");
525        }
526
527        return computeMin(it.next(), it);
528    }
529
530    /** Internal method for computing a min vector.
531     * @param first first vector
532     * @param more iterator with additional vectors
533     * @return vector containing the minimum component values of all input vectors
534     */
535    private static Vector2D computeMin(final Vector2D first, final Iterator<? extends Vector2D> more) {
536        double x = first.getX();
537        double y = first.getY();
538
539        Vector2D vec;
540        while (more.hasNext()) {
541            vec = more.next();
542
543            x = Math.min(x, vec.getX());
544            y = Math.min(y, vec.getY());
545        }
546
547        return Vector2D.of(x, y);
548    }
549
550    /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
551     * of points.
552     * @param first first point
553     * @param more additional points
554     * @return the centroid of the given points
555     */
556    public static Vector2D centroid(final Vector2D first, final Vector2D... more) {
557        return computeCentroid(first, Arrays.asList(more).iterator());
558    }
559
560    /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
561     * of points.
562     * @param pts the points to compute the centroid of
563     * @return the centroid of the given points
564     * @throws IllegalArgumentException if the argument contains no points
565     */
566    public static Vector2D centroid(final Iterable<Vector2D> pts) {
567        final Iterator<Vector2D> it = pts.iterator();
568        if (!it.hasNext()) {
569            throw new IllegalArgumentException("Cannot compute centroid: no points given");
570        }
571
572        return computeCentroid(it.next(), it);
573    }
574
575    /** Internal method for computing the centroid of a set of points.
576     * @param first first point
577     * @param more iterator with additional points
578     * @return the centroid of the point set
579     */
580    private static Vector2D computeCentroid(final Vector2D first, final Iterator<? extends Vector2D> more) {
581        final Sum sum = Sum.of(first);
582        int count = 1;
583
584        while (more.hasNext()) {
585            sum.add(more.next());
586            ++count;
587        }
588
589        return sum.get().multiply(1.0 / count);
590    }
591
592    /**
593     * Represents unit vectors.
594     * This allows optimizations for certain operations.
595     */
596    public static final class Unit extends Vector2D {
597        /** Unit vector (coordinates: 1, 0). */
598        public static final Unit PLUS_X  = new Unit(1d, 0d);
599        /** Negation of unit vector (coordinates: -1, 0). */
600        public static final Unit MINUS_X = new Unit(-1d, 0d);
601        /** Unit vector (coordinates: 0, 1). */
602        public static final Unit PLUS_Y  = new Unit(0d, 1d);
603        /** Negation of unit vector (coordinates: 0, -1). */
604        public static final Unit MINUS_Y = new Unit(0d, -1d);
605
606        /** Maximum coordinate value for computing normalized vectors
607         * with raw, unscaled values.
608         */
609        private static final double UNSCALED_MAX = 0x1.0p+500;
610
611        /** Factor used to scale up coordinate values in order to produce
612         * normalized coordinates without overflow or underflow.
613         */
614        private static final double SCALE_UP_FACTOR = 0x1.0p+600;
615
616        /** Factor used to scale down coordinate values in order to produce
617         * normalized coordinates without overflow or underflow.
618         */
619        private static final double SCALE_DOWN_FACTOR = 0x1.0p-600;
620
621        /** Simple constructor. Callers are responsible for ensuring that the given
622         * values represent a normalized vector.
623         * @param x abscissa (first coordinate value)
624         * @param y abscissa (second coordinate value)
625         */
626        private Unit(final double x, final double y) {
627            super(x, y);
628        }
629
630        /** {@inheritDoc} */
631        @Override
632        public double norm() {
633            return 1;
634        }
635
636        /** {@inheritDoc} */
637        @Override
638        public double normSq() {
639            return 1;
640        }
641
642        /** {@inheritDoc} */
643        @Override
644        public Unit normalize() {
645            return this;
646        }
647
648        /** {@inheritDoc} */
649        @Override
650        public Unit normalizeOrNull() {
651            return this;
652        }
653
654        /** {@inheritDoc} */
655        @Override
656        public Vector2D.Unit orthogonal() {
657            return new Unit(-getY(), getX());
658        }
659
660        /** {@inheritDoc} */
661        @Override
662        public Vector2D withNorm(final double mag) {
663            return multiply(mag);
664        }
665
666        /** {@inheritDoc} */
667        @Override
668        public Unit negate() {
669            return new Unit(-getX(), -getY());
670        }
671
672        /** Create a normalized vector.
673         * @param x Vector coordinate.
674         * @param y Vector coordinate.
675         * @return a vector whose norm is 1.
676         * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
677         *      or infinite
678         */
679        public static Unit from(final double x, final double y) {
680            return tryCreateNormalized(x, y, true);
681        }
682
683        /** Create a normalized vector.
684         * @param v Vector.
685         * @return a vector whose norm is 1.
686         * @throws IllegalArgumentException if the norm of the given value is zero, NaN,
687         *      or infinite
688         */
689        public static Unit from(final Vector2D v) {
690            return v instanceof Unit ?
691                (Unit) v :
692                from(v.getX(), v.getY());
693        }
694
695        /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure}
696         * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null
697         * is returned.
698         * @param x x coordinate
699         * @param y y coordinate
700         * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created
701         * @return normalized vector or null if one cannot be created and {@code throwOnFailure}
702         *      is false
703         * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite
704         */
705        private static Unit tryCreateNormalized(final double x, final double y, final boolean throwOnFailure) {
706
707            // Compute the inverse norm directly. If the result is a non-zero real number,
708            // then we can go ahead and construct the unit vector immediately. If not,
709            // we'll do some extra work for edge cases.
710            final double norm = Vectors.norm(x, y);
711            final double normInv = 1.0 / norm;
712            if (Vectors.isRealNonZero(normInv)) {
713                return new Unit(
714                        x * normInv,
715                        y * normInv);
716            }
717
718            // Direct computation did not work. Try scaled versions of the coordinates
719            // to handle overflow and underflow.
720            final double scaledX;
721            final double scaledY;
722
723            final double maxCoord = Math.max(Math.abs(x), Math.abs(y));
724            if (maxCoord > UNSCALED_MAX) {
725                scaledX = x * SCALE_DOWN_FACTOR;
726                scaledY = y * SCALE_DOWN_FACTOR;
727            } else {
728                scaledX = x * SCALE_UP_FACTOR;
729                scaledY = y * SCALE_UP_FACTOR;
730            }
731
732            final double scaledNormInv = 1.0 / Vectors.norm(scaledX, scaledY);
733
734            if (Vectors.isRealNonZero(scaledNormInv)) {
735                return new Unit(
736                        scaledX * scaledNormInv,
737                        scaledY * scaledNormInv);
738            } else if (throwOnFailure) {
739                throw Vectors.illegalNorm(norm);
740            }
741            return null;
742        }
743    }
744
745    /** Class used to create high-accuracy sums of vectors. Each vector component is
746     * summed using an instance of {@link org.apache.commons.numbers.core.Sum}.
747     *
748     * <p>This class is mutable and not thread-safe.
749     * @see org.apache.commons.numbers.core.Sum
750     */
751    public static final class Sum extends EuclideanVectorSum<Vector2D> {
752        /** X component sum. */
753        private final org.apache.commons.numbers.core.Sum xsum;
754        /** Y component sum. */
755        private final org.apache.commons.numbers.core.Sum ysum;
756
757        /** Construct a new instance with the given initial value.
758         * @param initial initial value
759         */
760        Sum(final Vector2D initial) {
761            this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x);
762            this.ysum = org.apache.commons.numbers.core.Sum.of(initial.y);
763        }
764
765        /** {@inheritDoc} */
766        @Override
767        public Sum add(final Vector2D vec) {
768            xsum.add(vec.x);
769            ysum.add(vec.y);
770            return this;
771        }
772
773        /** {@inheritDoc} */
774        @Override
775        public Sum addScaled(final double scale, final Vector2D vec) {
776            xsum.addProduct(scale, vec.x);
777            ysum.addProduct(scale, vec.y);
778            return this;
779        }
780
781        /** {@inheritDoc} */
782        @Override
783        public Vector2D get() {
784            return Vector2D.of(
785                    xsum.getAsDouble(),
786                    ysum.getAsDouble());
787        }
788
789        /** Create a new instance with an initial value set to the {@link Vector2D#ZERO zero vector}.
790         * @return new instance set to zero
791         */
792        public static Sum create() {
793            return new Sum(Vector2D.ZERO);
794        }
795
796        /** Construct a new instance with an initial value set to the argument.
797         * @param initial initial sum value
798         * @return new instance
799         */
800        public static Sum of(final Vector2D initial) {
801            return new Sum(initial);
802        }
803
804        /** Construct a new instance from multiple values.
805         * @param first first vector
806         * @param more additional vectors
807         * @return new instance
808         */
809        public static Sum of(final Vector2D first, final Vector2D... more) {
810            final Sum s = new Sum(first);
811            for (final Vector2D v : more) {
812                s.add(v);
813            }
814            return s;
815        }
816    }
817}