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