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