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 * https://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
18 /*
19 * This is not the original file distributed by the Apache Software Foundation
20 * It has been modified by the Hipparchus project
21 */
22
23 package org.hipparchus.geometry.euclidean.threed;
24
25 import org.hipparchus.exception.LocalizedCoreFormats;
26 import org.hipparchus.exception.MathIllegalArgumentException;
27 import org.hipparchus.exception.MathRuntimeException;
28 import org.hipparchus.geometry.Space;
29 import org.hipparchus.geometry.Vector;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.MathArrays;
32 import org.hipparchus.util.MathUtils;
33 import org.hipparchus.util.SinCos;
34
35 import java.io.Serializable;
36 import java.text.NumberFormat;
37
38 /**
39 * This class implements vectors in a three-dimensional space.
40 * <p>Instance of this class are guaranteed to be immutable.</p>
41 */
42 public class Vector3D implements Serializable, Vector<Euclidean3D, Vector3D> {
43
44 /** Null vector (coordinates: 0, 0, 0). */
45 public static final Vector3D ZERO = new Vector3D(0, 0, 0);
46
47 /** First canonical vector (coordinates: 1, 0, 0). */
48 public static final Vector3D PLUS_I = new Vector3D(1, 0, 0);
49
50 /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */
51 public static final Vector3D MINUS_I = new Vector3D(-1, 0, 0);
52
53 /** Second canonical vector (coordinates: 0, 1, 0). */
54 public static final Vector3D PLUS_J = new Vector3D(0, 1, 0);
55
56 /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */
57 public static final Vector3D MINUS_J = new Vector3D(0, -1, 0);
58
59 /** Third canonical vector (coordinates: 0, 0, 1). */
60 public static final Vector3D PLUS_K = new Vector3D(0, 0, 1);
61
62 /** Opposite of the third canonical vector (coordinates: 0, 0, -1). */
63 public static final Vector3D MINUS_K = new Vector3D(0, 0, -1);
64
65 // CHECKSTYLE: stop ConstantName
66 /** A vector with all coordinates set to NaN. */
67 public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
68 // CHECKSTYLE: resume ConstantName
69
70 /** A vector with all coordinates set to positive infinity. */
71 public static final Vector3D POSITIVE_INFINITY =
72 new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
73
74 /** A vector with all coordinates set to negative infinity. */
75 public static final Vector3D NEGATIVE_INFINITY =
76 new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
77
78 /** Serializable version identifier. */
79 private static final long serialVersionUID = 1313493323784566947L;
80
81 /** Abscissa. */
82 private final double x;
83
84 /** Ordinate. */
85 private final double y;
86
87 /** Height. */
88 private final double z;
89
90 /** Simple constructor.
91 * Build a vector from its coordinates
92 * @param x abscissa
93 * @param y ordinate
94 * @param z height
95 * @see #getX()
96 * @see #getY()
97 * @see #getZ()
98 */
99 public Vector3D(double x, double y, double z) {
100 this.x = x;
101 this.y = y;
102 this.z = z;
103 }
104
105 /** Simple constructor.
106 * Build a vector from its coordinates
107 * @param v coordinates array
108 * @exception MathIllegalArgumentException if array does not have 3 elements
109 * @see #toArray()
110 */
111 public Vector3D(double[] v) throws MathIllegalArgumentException {
112 if (v.length != 3) {
113 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
114 v.length, 3);
115 }
116 this.x = v[0];
117 this.y = v[1];
118 this.z = v[2];
119 }
120
121 /** Simple constructor.
122 * Build a vector from its azimuthal coordinates
123 * @param alpha azimuth (α) around Z
124 * (0 is +X, π/2 is +Y, π is -X and 3π/2 is -Y)
125 * @param delta elevation (δ) above (XY) plane, from -π/2 to +π/2
126 * @see #getAlpha()
127 * @see #getDelta()
128 */
129 public Vector3D(double alpha, double delta) {
130 SinCos sinCosAlpha = FastMath.sinCos(alpha);
131 SinCos sinCosDelta = FastMath.sinCos(delta);
132 this.x = sinCosAlpha.cos() * sinCosDelta.cos();
133 this.y = sinCosAlpha.sin() * sinCosDelta.cos();
134 this.z = sinCosDelta.sin();
135 }
136
137 /** Multiplicative constructor
138 * Build a vector from another one and a scale factor.
139 * The vector built will be a * u
140 * @param a scale factor
141 * @param u base (unscaled) vector
142 */
143 public Vector3D(double a, Vector3D u) {
144 this.x = a * u.x;
145 this.y = a * u.y;
146 this.z = a * u.z;
147 }
148
149 /** Linear constructor
150 * Build a vector from two other ones and corresponding scale factors.
151 * The vector built will be a1 * u1 + a2 * u2
152 * @param a1 first scale factor
153 * @param u1 first base (unscaled) vector
154 * @param a2 second scale factor
155 * @param u2 second base (unscaled) vector
156 */
157 public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2) {
158 this.x = MathArrays.linearCombination(a1, u1.x, a2, u2.x);
159 this.y = MathArrays.linearCombination(a1, u1.y, a2, u2.y);
160 this.z = MathArrays.linearCombination(a1, u1.z, a2, u2.z);
161 }
162
163 /** Linear constructor
164 * Build a vector from three other ones and corresponding scale factors.
165 * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
166 * @param a1 first scale factor
167 * @param u1 first base (unscaled) vector
168 * @param a2 second scale factor
169 * @param u2 second base (unscaled) vector
170 * @param a3 third scale factor
171 * @param u3 third base (unscaled) vector
172 */
173 public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2,
174 double a3, Vector3D u3) {
175 this.x = MathArrays.linearCombination(a1, u1.x, a2, u2.x, a3, u3.x);
176 this.y = MathArrays.linearCombination(a1, u1.y, a2, u2.y, a3, u3.y);
177 this.z = MathArrays.linearCombination(a1, u1.z, a2, u2.z, a3, u3.z);
178 }
179
180 /** Linear constructor
181 * Build a vector from four other ones and corresponding scale factors.
182 * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
183 * @param a1 first scale factor
184 * @param u1 first base (unscaled) vector
185 * @param a2 second scale factor
186 * @param u2 second base (unscaled) vector
187 * @param a3 third scale factor
188 * @param u3 third base (unscaled) vector
189 * @param a4 fourth scale factor
190 * @param u4 fourth base (unscaled) vector
191 */
192 public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2,
193 double a3, Vector3D u3, double a4, Vector3D u4) {
194 this.x = MathArrays.linearCombination(a1, u1.x, a2, u2.x, a3, u3.x, a4, u4.x);
195 this.y = MathArrays.linearCombination(a1, u1.y, a2, u2.y, a3, u3.y, a4, u4.y);
196 this.z = MathArrays.linearCombination(a1, u1.z, a2, u2.z, a3, u3.z, a4, u4.z);
197 }
198
199 /** Get the abscissa of the vector.
200 * @return abscissa of the vector
201 * @see #Vector3D(double, double, double)
202 */
203 public double getX() {
204 return x;
205 }
206
207 /** Get the ordinate of the vector.
208 * @return ordinate of the vector
209 * @see #Vector3D(double, double, double)
210 */
211 public double getY() {
212 return y;
213 }
214
215 /** Get the height of the vector.
216 * @return height of the vector
217 * @see #Vector3D(double, double, double)
218 */
219 public double getZ() {
220 return z;
221 }
222
223 /** Get the vector coordinates as a dimension 3 array.
224 * @return vector coordinates
225 * @see #Vector3D(double[])
226 */
227 public double[] toArray() {
228 return new double[] { x, y, z };
229 }
230
231 /** {@inheritDoc} */
232 @Override
233 public Space getSpace() {
234 return Euclidean3D.getInstance();
235 }
236
237 /** {@inheritDoc} */
238 @Override
239 public Vector3D getZero() {
240 return ZERO;
241 }
242
243 /** {@inheritDoc} */
244 @Override
245 public double getNorm1() {
246 return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
247 }
248
249 /** {@inheritDoc} */
250 @Override
251 public double getNorm() {
252 // there are no cancellation problems here, so we use the straightforward formula
253 return FastMath.sqrt (x * x + y * y + z * z);
254 }
255
256 /** {@inheritDoc} */
257 @Override
258 public double getNormSq() {
259 // there are no cancellation problems here, so we use the straightforward formula
260 return x * x + y * y + z * z;
261 }
262
263 /** {@inheritDoc} */
264 @Override
265 public double getNormInf() {
266 return FastMath.max(FastMath.max(FastMath.abs(x), FastMath.abs(y)), FastMath.abs(z));
267 }
268
269 /** Get the azimuth of the vector.
270 * @return azimuth (α) of the vector, between -π and +π
271 * @see #Vector3D(double, double)
272 */
273 public double getAlpha() {
274 return FastMath.atan2(y, x);
275 }
276
277 /** Get the elevation of the vector.
278 * @return elevation (δ) of the vector, between -π/2 and +π/2
279 * @see #Vector3D(double, double)
280 */
281 public double getDelta() {
282 return FastMath.asin(z / getNorm());
283 }
284
285 /** {@inheritDoc} */
286 @Override
287 public Vector3D add(final Vector3D v) {
288 return new Vector3D(x + v.x, y + v.y, z + v.z);
289 }
290
291 /** {@inheritDoc} */
292 @Override
293 public Vector3D add(double factor, final Vector3D v) {
294 return new Vector3D(1, this, factor, v);
295 }
296
297 /** {@inheritDoc} */
298 @Override
299 public Vector3D subtract(final Vector3D v) {
300 return new Vector3D(x - v.x, y - v.y, z - v.z);
301 }
302
303 /** {@inheritDoc} */
304 @Override
305 public Vector3D subtract(final double factor, final Vector3D v) {
306 return new Vector3D(1, this, -factor, v);
307 }
308
309 /** Get a vector orthogonal to the instance.
310 * <p>There are an infinite number of normalized vectors orthogonal
311 * to the instance. This method picks up one of them almost
312 * arbitrarily. It is useful when one needs to compute a reference
313 * frame with one of the axes in a predefined direction. The
314 * following example shows how to build a frame having the k axis
315 * aligned with the known vector u :
316 * </p>
317 * <pre><code>
318 * Vector3D k = u.normalize();
319 * Vector3D i = k.orthogonal();
320 * Vector3D j = Vector3D.crossProduct(k, i);
321 * </code></pre>
322 * @return a new normalized vector orthogonal to the instance
323 * @exception MathRuntimeException if the norm of the instance is null
324 */
325 public Vector3D orthogonal() throws MathRuntimeException {
326
327 double threshold = 0.6 * getNorm();
328 if (threshold == 0) {
329 throw new MathRuntimeException(LocalizedCoreFormats.ZERO_NORM);
330 }
331
332 if (FastMath.abs(x) <= threshold) {
333 double inverse = 1 / FastMath.sqrt(y * y + z * z);
334 return new Vector3D(0, inverse * z, -inverse * y);
335 } else if (FastMath.abs(y) <= threshold) {
336 double inverse = 1 / FastMath.sqrt(x * x + z * z);
337 return new Vector3D(-inverse * z, 0, inverse * x);
338 }
339 double inverse = 1 / FastMath.sqrt(x * x + y * y);
340 return new Vector3D(inverse * y, -inverse * x, 0);
341
342 }
343
344 /** Compute the angular separation between two vectors.
345 * <p>This method computes the angular separation between two
346 * vectors using the dot product for well separated vectors and the
347 * cross product for almost aligned vectors. This allows to have a
348 * good accuracy in all cases, even for vectors very close to each
349 * other.</p>
350 * @param v1 first vector
351 * @param v2 second vector
352 * @return angular separation between v1 and v2
353 * @exception MathRuntimeException if either vector has a null norm
354 */
355 public static double angle(Vector3D v1, Vector3D v2) throws MathRuntimeException {
356
357 double normProduct = v1.getNorm() * v2.getNorm();
358 if (normProduct == 0) {
359 throw new MathRuntimeException(LocalizedCoreFormats.ZERO_NORM);
360 }
361
362 double dot = v1.dotProduct(v2);
363 double threshold = normProduct * 0.9999;
364 if ((dot < -threshold) || (dot > threshold)) {
365 // the vectors are almost aligned, compute using the sine
366 Vector3D v3 = crossProduct(v1, v2);
367 if (dot >= 0) {
368 return FastMath.asin(v3.getNorm() / normProduct);
369 }
370 return FastMath.PI - FastMath.asin(v3.getNorm() / normProduct);
371 }
372
373 // the vectors are sufficiently separated to use the cosine
374 return FastMath.acos(dot / normProduct);
375
376 }
377
378 /** {@inheritDoc} */
379 @Override
380 public Vector3D negate() {
381 return new Vector3D(-x, -y, -z);
382 }
383
384 /** {@inheritDoc} */
385 @Override
386 public Vector3D scalarMultiply(double a) {
387 return new Vector3D(a * x, a * y, a * z);
388 }
389
390 /** {@inheritDoc} */
391 @Override
392 public boolean isNaN() {
393 return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z);
394 }
395
396 /** {@inheritDoc} */
397 @Override
398 public boolean isInfinite() {
399 return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
400 }
401
402 /**
403 * Test for the equality of two 3D vectors.
404 * <p>
405 * If all coordinates of two 3D vectors are exactly the same, and none are
406 * {@code Double.NaN}, the two 3D vectors are considered to be equal.
407 * </p>
408 * <p>
409 * {@code NaN} coordinates are considered to affect globally the vector
410 * and be equals to each other - i.e, if either (or all) coordinates of the
411 * 3D vector are equal to {@code Double.NaN}, the 3D vector is equal to
412 * {@link #NaN}.
413 * </p>
414 *
415 * @param other Object to test for equality to this
416 * @return true if two 3D vector objects are equal, false if
417 * object is null, not an instance of Vector3D, or
418 * not equal to this Vector3D instance
419 *
420 */
421 @Override
422 public boolean equals(Object other) {
423
424 if (this == other) {
425 return true;
426 }
427
428 if (other instanceof Vector3D) {
429 final Vector3D rhs = (Vector3D)other;
430 return x == rhs.x && y == rhs.y && z == rhs.z || isNaN() && rhs.isNaN();
431 }
432
433 return false;
434
435 }
436
437 /**
438 * Test for the equality of two 3D vectors.
439 * <p>
440 * If all coordinates of two 3D vectors are exactly the same, and none are
441 * {@code NaN}, the two 3D vectors are considered to be equal.
442 * </p>
443 * <p>
444 * In compliance with IEEE754 handling, if any coordinates of any of the
445 * two vectors are {@code NaN}, then the vectors are considered different.
446 * This implies that {@link #NaN Vector3D.NaN}.equals({@link #NaN Vector3D.NaN})
447 * returns {@code false} despite the instance is checked against itself.
448 * </p>
449 *
450 * @param other Object to test for equality to this
451 * @return true if two 3D vector objects are equal, false if
452 * object is null, not an instance of Vector3D, or
453 * not equal to this Vector3D instance
454 * @since 2.1
455 */
456 public boolean equalsIeee754(Object other) {
457
458 if (this == other && !isNaN()) {
459 return true;
460 }
461
462 if (other instanceof Vector3D) {
463 final Vector3D rhs = (Vector3D) other;
464 return x == rhs.x && y == rhs.y && z == rhs.z;
465 }
466
467 return false;
468
469 }
470
471 /**
472 * Get a hashCode for the 3D vector.
473 * <p>
474 * All NaN values have the same hash code.</p>
475 *
476 * @return a hash code value for this object
477 */
478 @Override
479 public int hashCode() {
480 if (isNaN()) {
481 return 642;
482 }
483 return 643 * (164 * MathUtils.hash(x) + 3 * MathUtils.hash(y) + MathUtils.hash(z));
484 }
485
486 /** {@inheritDoc}
487 * <p>
488 * The implementation uses specific multiplication and addition
489 * algorithms to preserve accuracy and reduce cancellation effects.
490 * It should be very accurate even for nearly orthogonal vectors.
491 * </p>
492 * @see MathArrays#linearCombination(double, double, double, double, double, double)
493 */
494 @Override
495 public double dotProduct(final Vector3D v) {
496 return MathArrays.linearCombination(x, v.x, y, v.y, z, v.z);
497 }
498
499 /** Compute the cross-product of the instance with another vector.
500 * @param v other vector
501 * @return the cross product this ^ v as a new Vector3D
502 */
503 public Vector3D crossProduct(final Vector3D v) {
504 return new Vector3D(MathArrays.linearCombination(y, v.z, -z, v.y),
505 MathArrays.linearCombination(z, v.x, -x, v.z),
506 MathArrays.linearCombination(x, v.y, -y, v.x));
507 }
508
509 /** {@inheritDoc} */
510 @Override
511 public double distance1(Vector3D v) {
512 final double dx = FastMath.abs(v.x - x);
513 final double dy = FastMath.abs(v.y - y);
514 final double dz = FastMath.abs(v.z - z);
515 return dx + dy + dz;
516 }
517
518 /** {@inheritDoc} */
519 @Override
520 public double distance(Vector3D v) {
521 final double dx = v.x - x;
522 final double dy = v.y - y;
523 final double dz = v.z - z;
524 return FastMath.sqrt(dx * dx + dy * dy + dz * dz);
525 }
526
527 /** {@inheritDoc} */
528 @Override
529 public double distanceInf(Vector3D v) {
530 final double dx = FastMath.abs(v.x - x);
531 final double dy = FastMath.abs(v.y - y);
532 final double dz = FastMath.abs(v.z - z);
533 return FastMath.max(FastMath.max(dx, dy), dz);
534 }
535
536 /** {@inheritDoc} */
537 @Override
538 public double distanceSq(Vector3D v) {
539 final double dx = v.x - x;
540 final double dy = v.y - y;
541 final double dz = v.z - z;
542 return dx * dx + dy * dy + dz * dz;
543 }
544
545 /** Compute the dot-product of two vectors.
546 * @param v1 first vector
547 * @param v2 second vector
548 * @return the dot product v1.v2
549 */
550 public static double dotProduct(Vector3D v1, Vector3D v2) {
551 return v1.dotProduct(v2);
552 }
553
554 /** Compute the cross-product of two vectors.
555 * @param v1 first vector
556 * @param v2 second vector
557 * @return the cross product v1 ^ v2 as a new Vector
558 */
559 public static Vector3D crossProduct(final Vector3D v1, final Vector3D v2) {
560 return v1.crossProduct(v2);
561 }
562
563 /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
564 * <p>Calling this method is equivalent to calling:
565 * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
566 * vector is built</p>
567 * @param v1 first vector
568 * @param v2 second vector
569 * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
570 */
571 public static double distance1(Vector3D v1, Vector3D v2) {
572 return v1.distance1(v2);
573 }
574
575 /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
576 * <p>Calling this method is equivalent to calling:
577 * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
578 * vector is built</p>
579 * @param v1 first vector
580 * @param v2 second vector
581 * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
582 */
583 public static double distance(Vector3D v1, Vector3D v2) {
584 return v1.distance(v2);
585 }
586
587 /** Compute the distance between two vectors according to the L<sub>∞</sub> norm.
588 * <p>Calling this method is equivalent to calling:
589 * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
590 * vector is built</p>
591 * @param v1 first vector
592 * @param v2 second vector
593 * @return the distance between v1 and v2 according to the L<sub>∞</sub> norm
594 */
595 public static double distanceInf(Vector3D v1, Vector3D v2) {
596 return v1.distanceInf(v2);
597 }
598
599 /** Compute the square of the distance between two vectors.
600 * <p>Calling this method is equivalent to calling:
601 * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
602 * vector is built</p>
603 * @param v1 first vector
604 * @param v2 second vector
605 * @return the square of the distance between v1 and v2
606 */
607 public static double distanceSq(Vector3D v1, Vector3D v2) {
608 return v1.distanceSq(v2);
609 }
610
611 /** {@inheritDoc} */
612 @Override
613 public Vector3D moveTowards(final Vector3D other, final double ratio) {
614 return new Vector3D(x + ratio * (other.x - x),
615 y + ratio * (other.y - y),
616 z + ratio * (other.z - z));
617 }
618
619 /** Get a string representation of this vector.
620 * @return a string representation of this vector
621 */
622 @Override
623 public String toString() {
624 return Vector3DFormat.getVector3DFormat().format(this);
625 }
626
627 /** {@inheritDoc} */
628 @Override
629 public String toString(final NumberFormat format) {
630 return new Vector3DFormat(format).format(this);
631 }
632
633 }