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.twod;
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.DoubleFunction2N;
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 two-dimensional Euclidean space.
32   * Instances of this class are guaranteed to be immutable.
33   */
34  public class Vector2D extends MultiDimensionalEuclideanVector<Vector2D> {
35  
36      /** Zero vector (coordinates: 0, 0). */
37      public static final Vector2D ZERO = new Vector2D(0, 0);
38  
39      // CHECKSTYLE: stop ConstantName
40      /** A vector with all coordinates set to NaN. */
41      public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
42      // CHECKSTYLE: resume ConstantName
43  
44      /** A vector with all coordinates set to positive infinity. */
45      public static final Vector2D POSITIVE_INFINITY =
46          new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
47  
48      /** A vector with all coordinates set to negative infinity. */
49      public static final Vector2D NEGATIVE_INFINITY =
50          new Vector2D(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<Vector2D> 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              }
64          } else if (a != null) {
65              cmp = -1;
66          } else if (b != null) {
67              cmp = 1;
68          }
69  
70          return cmp;
71      };
72  
73      /** Abscissa (first coordinate). */
74      private final double x;
75  
76      /** Ordinate (second coordinate). */
77      private final double y;
78  
79      /** Simple constructor.
80       * @param x abscissa (first coordinate)
81       * @param y ordinate (second coordinate)
82       */
83      private Vector2D(final double x, final double y) {
84          this.x = x;
85          this.y = y;
86      }
87  
88      /** Returns the abscissa (first coordinate value) of the instance.
89       * @return the abscissa
90       */
91      public double getX() {
92          return x;
93      }
94  
95      /** Returns the ordinate (second coordinate value) of the instance.
96       * @return the ordinate
97       */
98      public double getY() {
99          return y;
100     }
101 
102     /** Get the coordinates for this instance as a dimension 2 array.
103      * @return coordinates for this instance
104      */
105     public double[] toArray() {
106         return new double[]{x, y};
107     }
108 
109     /** {@inheritDoc} */
110     @Override
111     public int getDimension() {
112         return 2;
113     }
114 
115     /** {@inheritDoc} */
116     @Override
117     public boolean isNaN() {
118         return Double.isNaN(x) || Double.isNaN(y);
119     }
120 
121     /** {@inheritDoc} */
122     @Override
123     public boolean isInfinite() {
124         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     public boolean isFinite() {
130         return Double.isFinite(x) && Double.isFinite(y);
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public Vector2D vectorTo(final Vector2D v) {
136         return v.subtract(this);
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public Unit directionTo(final Vector2D v) {
142         return vectorTo(v).normalize();
143     }
144 
145     /** {@inheritDoc} */
146     @Override
147     public Vector2D lerp(final Vector2D p, final double t) {
148         return linearCombination(1.0 - t, this, t, p);
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 Vector2D multiply(final double a) {
219         return new Vector2D(a * x, a * y);
220     }
221 
222     /** {@inheritDoc} */
223     @Override
224     public double distance(final Vector2D v) {
225         return Vectors.norm(x - v.x, y - v.y);
226     }
227 
228     /** {@inheritDoc} */
229     @Override
230     public double distanceSq(final Vector2D v) {
231         return Vectors.normSq(x - v.x, y - v.y);
232     }
233 
234     /** {@inheritDoc} */
235     @Override
236     public double dot(final Vector2D v) {
237         return LinearCombination.value(x, v.x, y, v.y);
238     }
239 
240     /** {@inheritDoc}
241      * <p>This method computes the angular separation between the two
242      * vectors using the dot product for well separated vectors and the
243      * cross product for almost aligned vectors. This allows to have a
244      * good accuracy in all cases, even for vectors very close to each
245      * other.</p>
246      */
247     @Override
248     public double angle(final Vector2D v) {
249         final double normProduct = getCheckedNorm() * v.getCheckedNorm();
250 
251         final double dot = dot(v);
252         final double threshold = normProduct * 0.9999;
253         if ((dot < -threshold) || (dot > threshold)) {
254             // the vectors are almost aligned, compute using the sine
255             final double n = Math.abs(LinearCombination.value(x, v.y, -y, v.x));
256             if (dot >= 0) {
257                 return Math.asin(n / normProduct);
258             }
259             return Math.PI - Math.asin(n / normProduct);
260         }
261 
262         // the vectors are sufficiently separated to use the cosine
263         return Math.acos(dot / normProduct);
264     }
265 
266     /** {@inheritDoc} */
267     @Override
268     public Vector2D project(final Vector2D base) {
269         return getComponent(base, false, Vector2D::new);
270     }
271 
272     /** {@inheritDoc} */
273     @Override
274     public Vector2D reject(final Vector2D base) {
275         return getComponent(base, true, Vector2D::new);
276     }
277 
278     /** {@inheritDoc}
279      * The returned vector is computed by rotating the current instance {@code pi/2} radians
280      * counterclockwise around the origin and normalizing. For example, if this method is
281      * called on a vector pointing along the positive x-axis, then a unit vector representing
282      * the positive y-axis is returned.
283      * @return a unit vector orthogonal to the current instance
284      * @throws IllegalArgumentException if the norm of the current instance is zero, NaN, or infinite
285      */
286     @Override
287     public Vector2D.Unit orthogonal() {
288         return Unit.from(-y, x);
289     }
290 
291     /** {@inheritDoc} */
292     @Override
293     public Vector2D.Unit orthogonal(final Vector2D dir) {
294         return dir.getComponent(this, true, Vector2D.Unit::from);
295     }
296 
297     /** Compute the signed area of the parallelogram with sides formed by this instance
298      * and the given vector.
299      *
300      * <p>The parallelogram in question can be visualized by taking the current instance as the
301      * first side and placing {@code v} at the end of it to create the second. The other sides
302      * are formed by lines parallel to these two vectors. If {@code v} points to the <em>left</em> of
303      * the current instance (ie, the parallelogram is wound counter-clockwise), then the
304      * returned area is positive. If {@code v} points to the <em>right</em> of the current instance,
305      * (ie, the parallelogram is wound clockwise), then the returned area is negative. If
306      * the vectors are collinear (ie, they lie on the same line), then 0 is returned. The area of
307      * the triangle formed by the two vectors is exactly half of the returned value.
308      * @param v vector representing the second side of the constructed parallelogram
309      * @return the signed area of the parallelogram formed by this instance and the given vector
310      */
311     public double signedArea(final Vector2D v) {
312         return LinearCombination.value(
313                 x, v.y,
314                 -y, v.x);
315     }
316 
317     /** Convenience method to apply a function to this vector. This
318      * can be used to transform the vector inline with other methods.
319      * @param fn the function to apply
320      * @return the transformed vector
321      */
322     public Vector2D transform(final UnaryOperator<Vector2D> fn) {
323         return fn.apply(this);
324     }
325 
326     /** {@inheritDoc} */
327     @Override
328     public boolean eq(final Vector2D vec, final DoublePrecisionContext precision) {
329         return precision.eq(x, vec.x) &&
330                 precision.eq(y, vec.y);
331     }
332 
333     /**
334      * Get a hashCode for the 2D coordinates.
335      * <p>
336      * All NaN values have the same hash code.</p>
337      *
338      * @return a hash code value for this object
339      */
340     @Override
341     public int hashCode() {
342         if (isNaN()) {
343             return 542;
344         }
345         return 122 * (76 * Double.hashCode(x) + Double.hashCode(y));
346     }
347 
348     /**
349      * Test for the equality of two vector instances.
350      * <p>
351      * If all coordinates of two vectors are exactly the same, and none are
352      * <code>Double.NaN</code>, the two instances are considered to be equal.
353      * </p>
354      * <p>
355      * <code>NaN</code> coordinates are considered to globally affect the vector
356      * and be equal to each other - i.e, if either (or all) coordinates of the
357      * vector are equal to <code>Double.NaN</code>, the vector is equal to
358      * {@link #NaN}.
359      * </p>
360      *
361      * @param other Object to test for equality to this
362      * @return true if two Vector2D objects are equal, false if
363      *         object is null, not an instance of Vector2D, or
364      *         not equal to this Vector2D instance
365      *
366      */
367     @Override
368     public boolean equals(final Object other) {
369         if (this == other) {
370             return true;
371         }
372         if (other instanceof Vector2D) {
373             final Vector2D rhs = (Vector2D) other;
374             if (rhs.isNaN()) {
375                 return this.isNaN();
376             }
377 
378             return Double.compare(x, rhs.x) == 0 &&
379                     Double.compare(y, rhs.y) == 0;
380         }
381         return false;
382     }
383 
384     /** {@inheritDoc} */
385     @Override
386     public String toString() {
387         return SimpleTupleFormat.getDefault().format(x, y);
388     }
389 
390     /** Returns a component of the current instance relative to the given base
391      * vector. If {@code reject} is true, the vector rejection is returned; otherwise,
392      * the projection is returned.
393      * @param base The base vector
394      * @param reject If true, the rejection of this instance from {@code base} is
395      *      returned. If false, the projection of this instance onto {@code base}
396      *      is returned.
397      * @param factory factory function used to build the final vector
398      * @param <T> Vector implementation type
399      * @return The projection or rejection of this instance relative to {@code base},
400      *      depending on the value of {@code reject}.
401      * @throws IllegalArgumentException if {@code base} has a zero, NaN, or infinite norm
402      */
403     private <T extends Vector2D> T getComponent(final Vector2D base, final boolean reject,
404             final DoubleFunction2N<T> factory) {
405         final double aDotB = dot(base);
406 
407         // We need to check the norm value here to ensure that it's legal. However, we don't
408         // want to incur the cost or floating point error of getting the actual norm and then
409         // multiplying it again to get the square norm. So, we'll just check the squared norm
410         // directly. This will produce the same error result as checking the actual norm since
411         // Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and
412         // Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY.
413         final double baseMagSq = Vectors.checkedNorm(base.normSq());
414 
415         final double scale = aDotB / baseMagSq;
416 
417         final double projX = scale * base.x;
418         final double projY = scale * base.y;
419 
420         if (reject) {
421             return factory.apply(x - projX, y - projY);
422         }
423 
424         return factory.apply(projX, projY);
425     }
426 
427     /** Returns a vector with the given coordinate values.
428      * @param x abscissa (first coordinate value)
429      * @param y abscissa (second coordinate value)
430      * @return vector instance
431      */
432     public static Vector2D of(final double x, final double y) {
433         return new Vector2D(x, y);
434     }
435 
436     /** Creates a vector from the coordinates in the given 2-element array.
437      * @param v coordinates array
438      * @return new vector
439      * @exception IllegalArgumentException if the array does not have 2 elements
440      */
441     public static Vector2D of(final double[] v) {
442         if (v.length != 2) {
443             throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 2");
444         }
445         return new Vector2D(v[0], v[1]);
446     }
447 
448     /** Parses the given string and returns a new vector instance. The expected string
449      * format is the same as that returned by {@link #toString()}.
450      * @param str the string to parse
451      * @return vector instance represented by the string
452      * @throws IllegalArgumentException if the given string has an invalid format
453      */
454     public static Vector2D parse(final String str) {
455         return SimpleTupleFormat.getDefault().parse(str, Vector2D::new);
456     }
457 
458     /** Return a vector containing the maximum component values from all input vectors.
459      * @param first first vector
460      * @param more additional vectors
461      * @return a vector containing the maximum component values from all input vectors
462      */
463     public static Vector2D max(final Vector2D first, final Vector2D... more) {
464         return computeMax(first, Arrays.asList(more).iterator());
465     }
466 
467     /** Return a vector containing the maximum component values from all input vectors.
468      * @param vecs input vectors
469      * @return a vector containing the maximum component values from all input vectors
470      * @throws IllegalArgumentException if the argument does not contain any vectors
471      */
472     public static Vector2D max(final Iterable<Vector2D> vecs) {
473         final Iterator<Vector2D> it = vecs.iterator();
474         if (!it.hasNext()) {
475             throw new IllegalArgumentException("Cannot compute vector max: no vectors given");
476         }
477 
478         return computeMax(it.next(), it);
479     }
480 
481     /** Internal method for computing a max vector.
482      * @param first first vector
483      * @param more iterator with additional vectors
484      * @return vector containing the maximum component values of all input vectors
485      */
486     private static Vector2D computeMax(final Vector2D first, final Iterator<Vector2D> more) {
487         double x = first.getX();
488         double y = first.getY();
489 
490         Vector2D vec;
491         while (more.hasNext()) {
492             vec = more.next();
493 
494             x = Math.max(x, vec.getX());
495             y = Math.max(y, vec.getY());
496         }
497 
498         return Vector2D.of(x, y);
499     }
500 
501     /** Return a vector containing the minimum component values from all input vectors.
502      * @param first first vector
503      * @param more more vectors
504      * @return a vector containing the minimum component values from all input vectors
505      */
506     public static Vector2D min(final Vector2D first, final Vector2D... more) {
507         return computeMin(first, Arrays.asList(more).iterator());
508     }
509 
510     /** Return a vector containing the minimum component values from all input vectors.
511      * @param vecs input vectors
512      * @return a vector containing the minimum component values from all input vectors
513      * @throws IllegalArgumentException if the argument does not contain any vectors
514      */
515     public static Vector2D min(final Iterable<Vector2D> vecs) {
516         final Iterator<Vector2D> it = vecs.iterator();
517         if (!it.hasNext()) {
518             throw new IllegalArgumentException("Cannot compute vector min: no vectors given");
519         }
520 
521         return computeMin(it.next(), it);
522     }
523 
524     /** Internal method for computing a min vector.
525      * @param first first vector
526      * @param more iterator with additional vectors
527      * @return vector containing the minimum component values of all input vectors
528      */
529     private static Vector2D computeMin(final Vector2D first, final Iterator<Vector2D> more) {
530         double x = first.getX();
531         double y = first.getY();
532 
533         Vector2D vec;
534         while (more.hasNext()) {
535             vec = more.next();
536 
537             x = Math.min(x, vec.getX());
538             y = Math.min(y, vec.getY());
539         }
540 
541         return Vector2D.of(x, y);
542     }
543 
544     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
545      * of points.
546      * @param first first point
547      * @param more additional points
548      * @return the centroid of the given points
549      */
550     public static Vector2D centroid(final Vector2D first, final Vector2D... more) {
551         return computeCentroid(first, Arrays.asList(more).iterator());
552     }
553 
554     /** Compute the centroid of the given points. The centroid is the arithmetic mean position of a set
555      * of points.
556      * @param pts the points to compute the centroid of
557      * @return the centroid of the given points
558      * @throws IllegalArgumentException if the argument contains no points
559      */
560     public static Vector2D centroid(final Iterable<Vector2D> pts) {
561         final Iterator<Vector2D> it = pts.iterator();
562         if (!it.hasNext()) {
563             throw new IllegalArgumentException("Cannot compute centroid: no points given");
564         }
565 
566         return computeCentroid(it.next(), it);
567     }
568 
569     /** Internal method for computing the centroid of a set of points.
570      * @param first first point
571      * @param more iterator with additional points
572      * @return the centroid of the point set
573      */
574     private static Vector2D computeCentroid(final Vector2D first, final Iterator<Vector2D> more) {
575         double x = first.getX();
576         double y = first.getY();
577 
578         int count = 1;
579 
580         Vector2D pt;
581         while (more.hasNext()) {
582             pt = more.next();
583 
584             x += pt.getX();
585             y += pt.getY();
586 
587             ++count;
588         }
589 
590         final double invCount = 1.0 / count;
591 
592         return new Vector2D(invCount * x, invCount * y);
593     }
594 
595     /** Returns a vector consisting of the linear combination of the inputs.
596      * <p>
597      * A linear combination is the sum of all of the inputs multiplied by their
598      * corresponding scale factors.
599      * </p>
600      *
601      * @param a scale factor for first vector
602      * @param c first vector
603      * @return vector calculated by {@code a * c}
604      */
605     public static Vector2D linearCombination(final double a, final Vector2D c) {
606         return new Vector2D(a * c.x, a * c.y);
607     }
608 
609     /** Returns a vector consisting of the linear combination of the inputs.
610      * <p>
611      * A linear combination is the sum of all of the inputs multiplied by their
612      * corresponding scale factors.
613      * </p>
614      *
615      * @param a1 scale factor for first vector
616      * @param v1 first vector
617      * @param a2 scale factor for second vector
618      * @param v2 second vector
619      * @return vector calculated by {@code (a1 * v1) + (a2 * v2)}
620      */
621     public static Vector2D linearCombination(final double a1, final Vector2D v1,
622             final double a2, final Vector2D v2) {
623         return new Vector2D(
624                 LinearCombination.value(a1, v1.x, a2, v2.x),
625                 LinearCombination.value(a1, v1.y, a2, v2.y));
626     }
627 
628     /** Returns a vector consisting of the linear combination of the inputs.
629      * <p>
630      * A linear combination is the sum of all of the inputs multiplied by their
631      * corresponding scale factors.
632      * </p>
633      *
634      * @param a1 scale factor for first vector
635      * @param v1 first vector
636      * @param a2 scale factor for second vector
637      * @param v2 second vector
638      * @param a3 scale factor for third vector
639      * @param v3 third vector
640      * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3)}
641      */
642     public static Vector2D linearCombination(final double a1, final Vector2D v1,
643             final double a2, final Vector2D v2,
644             final double a3, final Vector2D v3) {
645         return new Vector2D(
646                 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x),
647                 LinearCombination.value(a1, v1.y, a2, v2.y, a3, v3.y));
648     }
649 
650     /** Returns a vector consisting of the linear combination of the inputs.
651      * <p>
652      * A linear combination is the sum of all of the inputs multiplied by their
653      * corresponding scale factors.
654      * </p>
655      *
656      * @param a1 scale factor for first vector
657      * @param v1 first vector
658      * @param a2 scale factor for second vector
659      * @param v2 second vector
660      * @param a3 scale factor for third vector
661      * @param v3 third vector
662      * @param a4 scale factor for fourth vector
663      * @param v4 fourth vector
664      * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3) + (a4 * v4)}
665      */
666     public static Vector2D linearCombination(final double a1, final Vector2D v1,
667                                              final double a2, final Vector2D v2,
668                                              final double a3, final Vector2D v3,
669                                              final double a4, final Vector2D v4) {
670         return new Vector2D(
671                 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x, a4, v4.x),
672                 LinearCombination.value(a1, v1.y, a2, v2.y, a3, v3.y, a4, v4.y));
673     }
674 
675     /**
676      * Represents unit vectors.
677      * This allows optimizations for certain operations.
678      */
679     public static final class Unit extends Vector2D {
680         /** Unit vector (coordinates: 1, 0). */
681         public static final Unit PLUS_X  = new Unit(1d, 0d);
682         /** Negation of unit vector (coordinates: -1, 0). */
683         public static final Unit MINUS_X = new Unit(-1d, 0d);
684         /** Unit vector (coordinates: 0, 1). */
685         public static final Unit PLUS_Y  = new Unit(0d, 1d);
686         /** Negation of unit vector (coordinates: 0, -1). */
687         public static final Unit MINUS_Y = new Unit(0d, -1d);
688 
689         /** Simple constructor. Callers are responsible for ensuring that the given
690          * values represent a normalized vector.
691          * @param x abscissa (first coordinate value)
692          * @param y abscissa (second coordinate value)
693          */
694         private Unit(final double x, final double y) {
695             super(x, y);
696         }
697 
698         /**
699          * Creates a normalized vector.
700          *
701          * @param x Vector coordinate.
702          * @param y Vector coordinate.
703          * @return a vector whose norm is 1.
704          * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
705          */
706         public static Unit from(final double x, final double y) {
707             final double invNorm = 1 / Vectors.checkedNorm(Vectors.norm(x, y));
708             return new Unit(x * invNorm, y * invNorm);
709         }
710 
711         /**
712          * Creates a normalized vector.
713          *
714          * @param v Vector.
715          * @return a vector whose norm is 1.
716          * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
717          */
718         public static Unit from(final Vector2D v) {
719             return v instanceof Unit ?
720                 (Unit) v :
721                 from(v.getX(), v.getY());
722         }
723 
724         /** {@inheritDoc} */
725         @Override
726         public double norm() {
727             return 1;
728         }
729 
730         /** {@inheritDoc} */
731         @Override
732         public double normSq() {
733             return 1;
734         }
735 
736         /** {@inheritDoc} */
737         @Override
738         public Unit normalize() {
739             return this;
740         }
741 
742         /** {@inheritDoc} */
743         @Override
744         public Vector2D.Unit orthogonal() {
745             return new Unit(-getY(), getX());
746         }
747 
748         /** {@inheritDoc} */
749         @Override
750         public Vector2D withNorm(final double mag) {
751             return multiply(mag);
752         }
753 
754         /** {@inheritDoc} */
755         @Override
756         public Unit negate() {
757             return new Unit(-getX(), -getY());
758         }
759     }
760 }