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 }