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    *      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.UnitTestUtils;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.exception.MathRuntimeException;
28  import org.hipparchus.geometry.Space;
29  import org.hipparchus.random.Well1024a;
30  import org.hipparchus.util.FastMath;
31  import org.hipparchus.util.Precision;
32  import org.junit.jupiter.api.Test;
33  
34  import java.text.DecimalFormat;
35  import java.text.DecimalFormatSymbols;
36  import java.text.NumberFormat;
37  import java.util.Locale;
38  
39  import static org.junit.jupiter.api.Assertions.assertEquals;
40  import static org.junit.jupiter.api.Assertions.assertFalse;
41  import static org.junit.jupiter.api.Assertions.assertNotEquals;
42  import static org.junit.jupiter.api.Assertions.assertThrows;
43  import static org.junit.jupiter.api.Assertions.assertTrue;
44  import static org.junit.jupiter.api.Assertions.fail;
45  
46  class Vector3DTest {
47      @Test
48      void testConstructors() throws MathIllegalArgumentException {
49          double r = FastMath.sqrt(2) /2;
50          checkVector(new Vector3D(2, new Vector3D(FastMath.PI / 3, -FastMath.PI / 4)),
51                      r, r * FastMath.sqrt(3), -2 * r);
52          checkVector(new Vector3D(2, Vector3D.PLUS_I,
53                                   -3, Vector3D.MINUS_K),
54                      2, 0, 3);
55          checkVector(new Vector3D(2, Vector3D.PLUS_I,
56                                   5, Vector3D.PLUS_J,
57                                   -3, Vector3D.MINUS_K),
58                      2, 5, 3);
59          checkVector(new Vector3D(2, Vector3D.PLUS_I,
60                                   5, Vector3D.PLUS_J,
61                                   5, Vector3D.MINUS_J,
62                                   -3, Vector3D.MINUS_K),
63                      2, 0, 3);
64          checkVector(new Vector3D(new double[] { 2,  5,  -3 }),
65                      2, 5, -3);
66      }
67  
68      @Test
69      void testSpace() {
70          Space space = new Vector3D(1, 2, 2).getSpace();
71          assertEquals(3, space.getDimension());
72          assertEquals(2, space.getSubSpace().getDimension());
73          Space deserialized = (Space) UnitTestUtils.serializeAndRecover(space);
74          assertTrue(space == deserialized);
75      }
76  
77      @Test
78      void testZero() {
79          assertEquals(0, new Vector3D(1, 2, 2).getZero().getNorm(), 1.0e-15);
80      }
81  
82      @SuppressWarnings("unlikely-arg-type")
83      @Test
84      void testEquals() {
85          Vector3D u1 = new Vector3D(1, 2, 3);
86          Vector3D u2 = new Vector3D(1, 2, 3);
87          assertEquals(u1, u1);
88          assertEquals(u1, u2);
89          assertNotEquals(u1, new Rotation(1, 0, 0, 0, false));
90          assertNotEquals(u1, new Vector3D(1, 2, 3 + 10 * Precision.EPSILON));
91          assertNotEquals(u1, new Vector3D(1, 2 + 10 * Precision.EPSILON, 3));
92          assertNotEquals(u1, new Vector3D(1 + 10 * Precision.EPSILON, 2, 3));
93          assertEquals(new Vector3D(0, Double.NaN, 0), new Vector3D(0, 0, Double.NaN));
94      }
95  
96      @Test
97      void testEqualsIeee754() {
98          Vector3D u1 = new Vector3D(1, 2, 3);
99          Vector3D u2 = new Vector3D(1, 2, 3);
100         assertTrue(u1.equalsIeee754(u1));
101         assertTrue(u1.equalsIeee754(u2));
102         assertFalse(u1.equalsIeee754(new Rotation(1, 0, 0, 0, false)));
103         assertFalse(u1.equalsIeee754(new Vector3D(1, 2, 3 + 10 * Precision.EPSILON)));
104         assertFalse(u1.equalsIeee754(new Vector3D(1, 2 + 10 * Precision.EPSILON, 3)));
105         assertFalse(u1.equalsIeee754(new Vector3D(1 + 10 * Precision.EPSILON, 2, 3)));
106         assertFalse(new Vector3D(0, Double.NaN, 0).equalsIeee754(new Vector3D(0, 0, Double.NaN)));
107         assertFalse(Vector3D.NaN.equalsIeee754(Vector3D.NaN));
108     }
109 
110     @Test
111     void testHash() {
112         assertEquals(new Vector3D(0, Double.NaN, 0).hashCode(), new Vector3D(0, 0, Double.NaN).hashCode());
113         Vector3D u = new Vector3D(1, 2, 3);
114         Vector3D v = new Vector3D(1, 2, 3 + 10 * Precision.EPSILON);
115         assertTrue(u.hashCode() != v.hashCode());
116     }
117 
118     @Test
119     void testInfinite() {
120         assertTrue(new Vector3D(1, 1, Double.NEGATIVE_INFINITY).isInfinite());
121         assertTrue(new Vector3D(1, Double.NEGATIVE_INFINITY, 1).isInfinite());
122         assertTrue(new Vector3D(Double.NEGATIVE_INFINITY, 1, 1).isInfinite());
123         assertFalse(new Vector3D(1, 1, 2).isInfinite());
124         assertFalse(new Vector3D(1, Double.NaN, Double.NEGATIVE_INFINITY).isInfinite());
125     }
126 
127     @Test
128     void testNaN() {
129         assertTrue(new Vector3D(1, 1, Double.NaN).isNaN());
130         assertTrue(new Vector3D(1, Double.NaN, 1).isNaN());
131         assertTrue(new Vector3D(Double.NaN, 1, 1).isNaN());
132         assertFalse(new Vector3D(1, 1, 2).isNaN());
133         assertFalse(new Vector3D(1, 1, Double.NEGATIVE_INFINITY).isNaN());
134     }
135 
136     @Test
137     void testToString() {
138         assertEquals("{3; 2; 1}", new Vector3D(3, 2, 1).toString());
139         NumberFormat format = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US));
140         assertEquals("{3.000; 2.000; 1.000}", new Vector3D(3, 2, 1).toString(format));
141     }
142 
143     @Test
144     void testWrongDimension() throws MathIllegalArgumentException {
145         assertThrows(MathIllegalArgumentException.class, () -> {
146             new Vector3D(new double[]{2, 5});
147         });
148     }
149 
150     @Test
151     void testCoordinates() {
152         Vector3D v = new Vector3D(1, 2, 3);
153         assertTrue(FastMath.abs(v.getX() - 1) < 1.0e-12);
154         assertTrue(FastMath.abs(v.getY() - 2) < 1.0e-12);
155         assertTrue(FastMath.abs(v.getZ() - 3) < 1.0e-12);
156         double[] coordinates = v.toArray();
157         assertTrue(FastMath.abs(coordinates[0] - 1) < 1.0e-12);
158         assertTrue(FastMath.abs(coordinates[1] - 2) < 1.0e-12);
159         assertTrue(FastMath.abs(coordinates[2] - 3) < 1.0e-12);
160     }
161 
162     @Test
163     void testNorm1() {
164         assertEquals(0.0, Vector3D.ZERO.getNorm1(), 0);
165         assertEquals(6.0, new Vector3D(1, -2, 3).getNorm1(), 0);
166     }
167 
168     @Test
169     void testNorm() {
170         assertEquals(0.0, Vector3D.ZERO.getNorm(), 0);
171         assertEquals(FastMath.sqrt(14), new Vector3D(1, 2, 3).getNorm(), 1.0e-12);
172     }
173 
174     @Test
175     void testNormSq() {
176         assertEquals(0.0, new Vector3D(0, 0, 0).getNormSq(), 0);
177         assertEquals(14, new Vector3D(1, 2, 3).getNormSq(), 1.0e-12);
178     }
179 
180     @Test
181     void testNormInf() {
182         assertEquals(0.0, Vector3D.ZERO.getNormInf(), 0);
183         assertEquals(3.0, new Vector3D(1, -2, 3).getNormInf(), 0);
184     }
185 
186     @Test
187     void testDistance1() {
188         Vector3D v1 = new Vector3D(1, -2, 3);
189         Vector3D v2 = new Vector3D(-4, 2, 0);
190         assertEquals(0.0, Vector3D.distance1(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
191         assertEquals(12.0, Vector3D.distance1(v1, v2), 1.0e-12);
192         assertEquals(v1.subtract(v2).getNorm1(), Vector3D.distance1(v1, v2), 1.0e-12);
193     }
194 
195     @Test
196     void testDistance() {
197         Vector3D v1 = new Vector3D(1, -2, 3);
198         Vector3D v2 = new Vector3D(-4, 2, 0);
199         assertEquals(0.0, Vector3D.distance(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
200         assertEquals(FastMath.sqrt(50), Vector3D.distance(v1, v2), 1.0e-12);
201         assertEquals(v1.subtract(v2).getNorm(), Vector3D.distance(v1, v2), 1.0e-12);
202     }
203 
204     @Test
205     void testDistanceSq() {
206         Vector3D v1 = new Vector3D(1, -2, 3);
207         Vector3D v2 = new Vector3D(-4, 2, 0);
208         assertEquals(0.0, Vector3D.distanceSq(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
209         assertEquals(50.0, Vector3D.distanceSq(v1, v2), 1.0e-12);
210         assertEquals(Vector3D.distance(v1, v2) * Vector3D.distance(v1, v2),
211                             Vector3D.distanceSq(v1, v2), 1.0e-12);
212   }
213 
214     @Test
215     void testDistanceInf() {
216         Vector3D v1 = new Vector3D(1, -2, 3);
217         Vector3D v2 = new Vector3D(-4, 2, 0);
218         assertEquals(0.0, Vector3D.distanceInf(Vector3D.MINUS_I, Vector3D.MINUS_I), 0);
219         assertEquals(5.0, Vector3D.distanceInf(v1, v2), 1.0e-12);
220         assertEquals(v1.subtract(v2).getNormInf(), Vector3D.distanceInf(v1, v2), 1.0e-12);
221     }
222 
223     @Test
224     void testSubtract() {
225         Vector3D v1 = new Vector3D(1, 2, 3);
226         Vector3D v2 = new Vector3D(-3, -2, -1);
227         v1 = v1.subtract(v2);
228         checkVector(v1, 4, 4, 4);
229 
230         checkVector(v2.subtract(v1), -7, -6, -5);
231         checkVector(v2.subtract(3, v1), -15, -14, -13);
232     }
233 
234     @Test
235     void testAdd() {
236         Vector3D v1 = new Vector3D(1, 2, 3);
237         Vector3D v2 = new Vector3D(-3, -2, -1);
238         v1 = v1.add(v2);
239         checkVector(v1, -2, 0, 2);
240 
241         checkVector(v2.add(v1), -5, -2, 1);
242         checkVector(v2.add(3, v1), -9, -2, 5);
243     }
244 
245     @Test
246     void testScalarProduct() {
247         Vector3D v = new Vector3D(1, 2, 3);
248         v = v.scalarMultiply(3);
249         checkVector(v, 3, 6, 9);
250 
251         checkVector(v.scalarMultiply(0.5), 1.5, 3, 4.5);
252     }
253 
254     @Test
255     void testVectorialProducts() {
256         Vector3D v1 = new Vector3D(2, 1, -4);
257         Vector3D v2 = new Vector3D(3, 1, -1);
258 
259         assertTrue(FastMath.abs(Vector3D.dotProduct(v1, v2) - 11) < 1.0e-12);
260 
261         Vector3D v3 = Vector3D.crossProduct(v1, v2);
262         checkVector(v3, 3, -10, -1);
263 
264         assertTrue(FastMath.abs(Vector3D.dotProduct(v1, v3)) < 1.0e-12);
265         assertTrue(FastMath.abs(Vector3D.dotProduct(v2, v3)) < 1.0e-12);
266     }
267 
268     @Test
269     void testCrossProductCancellation() {
270         Vector3D v1 = new Vector3D(9070467121.0, 4535233560.0, 1);
271         Vector3D v2 = new Vector3D(9070467123.0, 4535233561.0, 1);
272         checkVector(Vector3D.crossProduct(v1, v2), -1, 2, 1);
273 
274         double scale    = FastMath.scalb(1.0, 100);
275         Vector3D big1   = new Vector3D(scale, v1);
276         Vector3D small2 = new Vector3D(1 / scale, v2);
277         checkVector(Vector3D.crossProduct(big1, small2), -1, 2, 1);
278 
279     }
280 
281     @Test
282     void testAngular() {
283         assertEquals(0,           Vector3D.PLUS_I.getAlpha(), 1.0e-10);
284         assertEquals(0,           Vector3D.PLUS_I.getDelta(), 1.0e-10);
285         assertEquals(FastMath.PI / 2, Vector3D.PLUS_J.getAlpha(), 1.0e-10);
286         assertEquals(0,           Vector3D.PLUS_J.getDelta(), 1.0e-10);
287         assertEquals(0,           Vector3D.PLUS_K.getAlpha(), 1.0e-10);
288         assertEquals(FastMath.PI / 2, Vector3D.PLUS_K.getDelta(), 1.0e-10);
289 
290         Vector3D u = new Vector3D(-1, 1, -1);
291         assertEquals(3 * FastMath.PI /4, u.getAlpha(), 1.0e-10);
292         assertEquals(-1.0 / FastMath.sqrt(3), FastMath.sin(u.getDelta()), 1.0e-10);
293     }
294 
295     @Test
296     void testAngularSeparation() throws MathRuntimeException {
297         Vector3D v1 = new Vector3D(2, -1, 4);
298 
299         Vector3D  k = v1.normalize();
300         Vector3D  i = k.orthogonal();
301         Vector3D v2 = k.scalarMultiply(FastMath.cos(1.2)).add(i.scalarMultiply(FastMath.sin(1.2)));
302 
303         assertTrue(FastMath.abs(Vector3D.angle(v1, v2) - 1.2) < 1.0e-12);
304   }
305 
306     @Test
307     void testNormalize() throws MathRuntimeException {
308         assertEquals(1.0, new Vector3D(5, -4, 2).normalize().getNorm(), 1.0e-12);
309         try {
310             Vector3D.ZERO.normalize();
311             fail("an exception should have been thrown");
312         } catch (MathRuntimeException ae) {
313             // expected behavior
314         }
315     }
316 
317     @Test
318     void testNegate() {
319         checkVector(new Vector3D(0.1, 2.5, 1.3).negate(), -0.1, -2.5, -1.3);
320     }
321 
322     @Test
323     void testOrthogonal() throws MathRuntimeException {
324         Vector3D v1 = new Vector3D(0.1, 2.5, 1.3);
325         assertEquals(0.0, Vector3D.dotProduct(v1, v1.orthogonal()), 1.0e-12);
326         Vector3D v2 = new Vector3D(2.3, -0.003, 7.6);
327         assertEquals(0.0, Vector3D.dotProduct(v2, v2.orthogonal()), 1.0e-12);
328         Vector3D v3 = new Vector3D(-1.7, 1.4, 0.2);
329         assertEquals(0.0, Vector3D.dotProduct(v3, v3.orthogonal()), 1.0e-12);
330         Vector3D v4 = new Vector3D(4.2, 0.1, -1.8);
331         assertEquals(0.0, Vector3D.dotProduct(v4, v4.orthogonal()), 1.0e-12);
332         try {
333             new Vector3D(0, 0, 0).orthogonal();
334             fail("an exception should have been thrown");
335         } catch (MathRuntimeException ae) {
336             // expected behavior
337         }
338     }
339 
340     @Test
341     void testAngle() throws MathRuntimeException {
342         assertEquals(0.22572612855273393616,
343                             Vector3D.angle(new Vector3D(1, 2, 3), new Vector3D(4, 5, 6)),
344                             1.0e-12);
345         assertEquals(7.98595620686106654517199e-8,
346                             Vector3D.angle(new Vector3D(1, 2, 3), new Vector3D(2, 4, 6.000001)),
347                             1.0e-12);
348         assertEquals(3.14159257373023116985197793156,
349                             Vector3D.angle(new Vector3D(1, 2, 3), new Vector3D(-2, -4, -6.000001)),
350                             1.0e-12);
351         try {
352             Vector3D.angle(Vector3D.ZERO, Vector3D.PLUS_I);
353             fail("an exception should have been thrown");
354         } catch (MathRuntimeException ae) {
355             // expected behavior
356         }
357     }
358 
359     @Test
360     void testAccurateDotProduct() {
361         // the following two vectors are nearly but not exactly orthogonal
362         // naive dot product (i.e. computing u1.x * u2.x + u1.y * u2.y + u1.z * u2.z
363         // leads to a result of 0.0, instead of the correct -1.855129...
364         Vector3D u1 = new Vector3D(-1321008684645961.0 /  268435456.0,
365                                    -5774608829631843.0 /  268435456.0,
366                                    -7645843051051357.0 / 8589934592.0);
367         Vector3D u2 = new Vector3D(-5712344449280879.0 /    2097152.0,
368                                    -4550117129121957.0 /    2097152.0,
369                                     8846951984510141.0 /     131072.0);
370         double sNaive = u1.getX() * u2.getX() + u1.getY() * u2.getY() + u1.getZ() * u2.getZ();
371         double sAccurate = u1.dotProduct(u2);
372         assertEquals(0.0, sNaive, 1.0e-30);
373         assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate, 1.0e-15);
374     }
375 
376     @Test
377     void testDotProduct() {
378         // we compare accurate versus naive dot product implementations
379         // on regular vectors (i.e. not extreme cases like in the previous test)
380         Well1024a random = new Well1024a(553267312521321234L);
381         for (int i = 0; i < 10000; ++i) {
382             double ux = 10000 * random.nextDouble();
383             double uy = 10000 * random.nextDouble();
384             double uz = 10000 * random.nextDouble();
385             double vx = 10000 * random.nextDouble();
386             double vy = 10000 * random.nextDouble();
387             double vz = 10000 * random.nextDouble();
388             double sNaive = ux * vx + uy * vy + uz * vz;
389             double sAccurate = new Vector3D(ux, uy, uz).dotProduct(new Vector3D(vx, vy, vz));
390             assertEquals(sNaive, sAccurate, 2.5e-16 * sAccurate);
391         }
392     }
393 
394     @Test
395     void testAccurateCrossProduct() {
396         // the vectors u1 and u2 are nearly but not exactly anti-parallel
397         // (7.31e-16 degrees from 180 degrees) naive cross product (i.e.
398         // computing u1.x * u2.x + u1.y * u2.y + u1.z * u2.z
399         // leads to a result of   [0.0009765, -0.0001220, -0.0039062],
400         // instead of the correct [0.0006913, -0.0001254, -0.0007909]
401         final Vector3D u1 = new Vector3D(-1321008684645961.0 /   268435456.0,
402                                          -5774608829631843.0 /   268435456.0,
403                                          -7645843051051357.0 /  8589934592.0);
404         final Vector3D u2 = new Vector3D( 1796571811118507.0 /  2147483648.0,
405                                           7853468008299307.0 /  2147483648.0,
406                                           2599586637357461.0 / 17179869184.0);
407         final Vector3D u3 = new Vector3D(12753243807587107.0 / 18446744073709551616.0,
408                                          -2313766922703915.0 / 18446744073709551616.0,
409                                           -227970081415313.0 /   288230376151711744.0);
410         Vector3D cNaive = new Vector3D(u1.getY() * u2.getZ() - u1.getZ() * u2.getY(),
411                                        u1.getZ() * u2.getX() - u1.getX() * u2.getZ(),
412                                        u1.getX() * u2.getY() - u1.getY() * u2.getX());
413         Vector3D cAccurate = u1.crossProduct(u2);
414         assertTrue(u3.distance(cNaive) > 2.9 * u3.getNorm());
415         assertEquals(0.0, u3.distance(cAccurate), 1.0e-30 * cAccurate.getNorm());
416     }
417 
418     @Test
419     void testCrossProduct() {
420         // we compare accurate versus naive cross product implementations
421         // on regular vectors (i.e. not extreme cases like in the previous test)
422         Well1024a random = new Well1024a(885362227452043214L);
423         for (int i = 0; i < 10000; ++i) {
424             double ux = 10000 * random.nextDouble();
425             double uy = 10000 * random.nextDouble();
426             double uz = 10000 * random.nextDouble();
427             double vx = 10000 * random.nextDouble();
428             double vy = 10000 * random.nextDouble();
429             double vz = 10000 * random.nextDouble();
430             Vector3D cNaive = new Vector3D(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
431             Vector3D cAccurate = new Vector3D(ux, uy, uz).crossProduct(new Vector3D(vx, vy, vz));
432             assertEquals(0.0, cAccurate.distance(cNaive), 6.0e-15 * cAccurate.getNorm());
433         }
434     }
435 
436     @Test
437     void testArithmeticBlending() {
438 
439         // Given
440         final Vector3D v1 = new Vector3D(1,2,3);
441         final Vector3D v2 = new Vector3D(4,5,6);
442 
443         final double blendingValue = 0.7;
444 
445         // When
446         final Vector3D blendedVector = v1.blendArithmeticallyWith(v2, blendingValue);
447 
448         // Then
449         checkVector(blendedVector, 3.1, 4.1, 5.1);
450     }
451 
452     @Test
453     void testMoveTowards() {
454         checkVector(new Vector3D(5.0, -1.0, 2.0).moveTowards(new Vector3D(3.0, 4.0, 6.0), 0.0), 5.0, -1.0, 2.0);
455         checkVector(new Vector3D(5.0, -1.0, 2.0).moveTowards(new Vector3D(3.0, 4.0, 6.0), 0.5), 4.0,  1.5, 4.0);
456         checkVector(new Vector3D(5.0, -1.0, 2.0).moveTowards(new Vector3D(3.0, 4.0, 6.0), 1.0), 3.0,  4.0, 6.0);
457     }
458 
459     private void checkVector(Vector3D v, double x, double y, double z) {
460         assertEquals(x, v.getX(), 1.0e-12);
461         assertEquals(y, v.getY(), 1.0e-12);
462         assertEquals(z, v.getZ(), 1.0e-12);
463     }
464 }