1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.geometry.euclidean.threed;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.regex.Pattern;
25
26 import org.apache.commons.geometry.core.GeometryTestUtils;
27 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
28 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
29 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
30 import org.apache.commons.numbers.angle.PlaneAngleRadians;
31 import org.apache.commons.numbers.core.Precision;
32 import org.apache.commons.rng.UniformRandomProvider;
33 import org.apache.commons.rng.simple.RandomSource;
34 import org.junit.Assert;
35 import org.junit.Test;
36
37 public class Vector3DTest {
38
39 private static final double EPS = 1e-15;
40
41 @Test
42 public void testConstants() {
43
44 checkVector(Vector3D.ZERO, 0, 0, 0);
45
46 checkVector(Vector3D.Unit.PLUS_X, 1, 0, 0);
47 checkVector(Vector3D.Unit.MINUS_X, -1, 0, 0);
48
49 checkVector(Vector3D.Unit.PLUS_Y, 0, 1, 0);
50 checkVector(Vector3D.Unit.MINUS_Y, 0, -1, 0);
51
52 checkVector(Vector3D.Unit.PLUS_Z, 0, 0, 1);
53 checkVector(Vector3D.Unit.MINUS_Z, 0, 0, -1);
54
55 checkVector(Vector3D.NaN, Double.NaN, Double.NaN, Double.NaN);
56 checkVector(Vector3D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
57 checkVector(Vector3D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
58 }
59
60 @Test
61 public void testConstants_normalize() {
62
63 GeometryTestUtils.assertThrows(Vector3D.ZERO::normalize, IllegalArgumentException.class);
64 GeometryTestUtils.assertThrows(Vector3D.NaN::normalize, IllegalArgumentException.class);
65 GeometryTestUtils.assertThrows(Vector3D.POSITIVE_INFINITY::normalize, IllegalArgumentException.class);
66 GeometryTestUtils.assertThrows(Vector3D.NEGATIVE_INFINITY::normalize, IllegalArgumentException.class);
67
68 Assert.assertSame(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_X.normalize());
69 Assert.assertSame(Vector3D.Unit.MINUS_X, Vector3D.Unit.MINUS_X.normalize());
70
71 Assert.assertSame(Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_Y.normalize());
72 Assert.assertSame(Vector3D.Unit.MINUS_Y, Vector3D.Unit.MINUS_Y.normalize());
73
74 Assert.assertSame(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_Z.normalize());
75 Assert.assertSame(Vector3D.Unit.MINUS_Z, Vector3D.Unit.MINUS_Z.normalize());
76 }
77
78 @Test
79 public void testCoordinateAscendingOrder() {
80
81 final Comparator<Vector3D> cmp = Vector3D.COORDINATE_ASCENDING_ORDER;
82
83
84 Assert.assertEquals(0, cmp.compare(Vector3D.of(1, 2, 3), Vector3D.of(1, 2, 3)));
85
86 Assert.assertEquals(-1, cmp.compare(Vector3D.of(0, 2, 3), Vector3D.of(1, 2, 3)));
87 Assert.assertEquals(-1, cmp.compare(Vector3D.of(1, 1, 3), Vector3D.of(1, 2, 3)));
88 Assert.assertEquals(-1, cmp.compare(Vector3D.of(1, 2, 2), Vector3D.of(1, 2, 3)));
89
90 Assert.assertEquals(1, cmp.compare(Vector3D.of(2, 2, 3), Vector3D.of(1, 2, 3)));
91 Assert.assertEquals(1, cmp.compare(Vector3D.of(1, 3, 3), Vector3D.of(1, 2, 3)));
92 Assert.assertEquals(1, cmp.compare(Vector3D.of(1, 2, 4), Vector3D.of(1, 2, 3)));
93
94 Assert.assertEquals(-1, cmp.compare(Vector3D.of(1, 2, 3), null));
95 Assert.assertEquals(1, cmp.compare(null, Vector3D.of(1, 2, 3)));
96 Assert.assertEquals(0, cmp.compare(null, null));
97 }
98
99 @Test
100 public void testCoordinates() {
101
102 final Vector3D c = Vector3D.of(1, 2, 3);
103
104
105 Assert.assertEquals(1.0, c.getX(), EPS);
106 Assert.assertEquals(2.0, c.getY(), EPS);
107 Assert.assertEquals(3.0, c.getZ(), EPS);
108 }
109
110 @Test
111 public void testToArray() {
112
113 final Vector3D c = Vector3D.of(1, 2, 3);
114
115
116 final double[] arr = c.toArray();
117
118
119 Assert.assertEquals(3, arr.length);
120 Assert.assertEquals(1.0, arr[0], EPS);
121 Assert.assertEquals(2.0, arr[1], EPS);
122 Assert.assertEquals(3.0, arr[2], EPS);
123 }
124
125 @Test
126 public void testDimension() {
127
128 final Vector3D c = Vector3D.of(1, 2, 3);
129
130
131 Assert.assertEquals(3, c.getDimension());
132 }
133
134 @Test
135 public void testNaN() {
136
137 Assert.assertTrue(Vector3D.of(0, 0, Double.NaN).isNaN());
138 Assert.assertTrue(Vector3D.of(0, Double.NaN, 0).isNaN());
139 Assert.assertTrue(Vector3D.of(Double.NaN, 0, 0).isNaN());
140
141 Assert.assertFalse(Vector3D.of(1, 1, 1).isNaN());
142 Assert.assertFalse(Vector3D.of(1, 1, Double.NEGATIVE_INFINITY).isNaN());
143 Assert.assertFalse(Vector3D.of(1, Double.POSITIVE_INFINITY, 1).isNaN());
144 Assert.assertFalse(Vector3D.of(Double.NEGATIVE_INFINITY, 1, 1).isNaN());
145 }
146
147 @Test
148 public void testInfinite() {
149
150 Assert.assertTrue(Vector3D.of(0, 0, Double.NEGATIVE_INFINITY).isInfinite());
151 Assert.assertTrue(Vector3D.of(0, Double.NEGATIVE_INFINITY, 0).isInfinite());
152 Assert.assertTrue(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0).isInfinite());
153 Assert.assertTrue(Vector3D.of(0, 0, Double.POSITIVE_INFINITY).isInfinite());
154 Assert.assertTrue(Vector3D.of(0, Double.POSITIVE_INFINITY, 0).isInfinite());
155 Assert.assertTrue(Vector3D.of(Double.POSITIVE_INFINITY, 0, 0).isInfinite());
156
157 Assert.assertFalse(Vector3D.of(1, 1, 1).isInfinite());
158 Assert.assertFalse(Vector3D.of(0, 0, Double.NaN).isInfinite());
159 Assert.assertFalse(Vector3D.of(0, Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
160 Assert.assertFalse(Vector3D.of(Double.NaN, 0, Double.NEGATIVE_INFINITY).isInfinite());
161 Assert.assertFalse(Vector3D.of(Double.POSITIVE_INFINITY, Double.NaN, 0).isInfinite());
162 Assert.assertFalse(Vector3D.of(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
163 }
164
165 @Test
166 public void testFinite() {
167
168 Assert.assertTrue(Vector3D.ZERO.isFinite());
169 Assert.assertTrue(Vector3D.of(1, 1, 1).isFinite());
170
171 Assert.assertFalse(Vector3D.of(0, 0, Double.NEGATIVE_INFINITY).isFinite());
172 Assert.assertFalse(Vector3D.of(0, Double.NEGATIVE_INFINITY, 0).isFinite());
173 Assert.assertFalse(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0).isFinite());
174 Assert.assertFalse(Vector3D.of(0, 0, Double.POSITIVE_INFINITY).isFinite());
175 Assert.assertFalse(Vector3D.of(0, Double.POSITIVE_INFINITY, 0).isFinite());
176 Assert.assertFalse(Vector3D.of(Double.POSITIVE_INFINITY, 0, 0).isFinite());
177
178 Assert.assertFalse(Vector3D.of(0, 0, Double.NaN).isFinite());
179 Assert.assertFalse(Vector3D.of(0, Double.NEGATIVE_INFINITY, Double.NaN).isFinite());
180 Assert.assertFalse(Vector3D.of(Double.NaN, 0, Double.NEGATIVE_INFINITY).isFinite());
181 Assert.assertFalse(Vector3D.of(Double.POSITIVE_INFINITY, Double.NaN, 0).isFinite());
182 Assert.assertFalse(Vector3D.of(0, Double.NaN, Double.POSITIVE_INFINITY).isFinite());
183 }
184
185 @Test
186 public void testZero() {
187
188 final Vector3D zero = Vector3D.of(1, 2, 3).getZero();
189
190
191 checkVector(zero, 0, 0, 0);
192 Assert.assertEquals(0, zero.norm(), EPS);
193 }
194
195 @Test
196 public void testNorm() {
197
198 Assert.assertEquals(0.0, Vector3D.ZERO.norm(), 0);
199 Assert.assertEquals(Math.sqrt(29), Vector3D.of(2, 3, 4).norm(), EPS);
200 Assert.assertEquals(Math.sqrt(29), Vector3D.of(-2, -3, -4).norm(), EPS);
201 }
202
203 @Test
204 public void testNorm_unitVectors() {
205
206 final Vector3D v = Vector3D.of(1.0, 2.0, 3.0).normalize();
207
208
209 Assert.assertEquals(1.0, v.norm(), 0.0);
210 }
211
212 @Test
213 public void testNormSq() {
214
215 Assert.assertEquals(0.0, Vector3D.ZERO.normSq(), 0);
216 Assert.assertEquals(29, Vector3D.of(2, 3, 4).normSq(), EPS);
217 Assert.assertEquals(29, Vector3D.of(-2, -3, -4).normSq(), EPS);
218 }
219
220 @Test
221 public void testNormSq_unitVectors() {
222
223 final Vector3D v = Vector3D.of(1.0, 2.0, 3.0).normalize();
224
225
226 Assert.assertEquals(1.0, v.normSq(), 0.0);
227 }
228
229 @Test
230 public void testWithNorm() {
231
232 final double x = 2;
233 final double y = 3;
234 final double z = 4;
235
236 final double len = Math.sqrt((x * x) + (y * y) + (z * z));
237
238 final double normX = x / len;
239 final double normY = y / len;
240 final double normZ = z / len;
241
242
243 checkVector(Vector3D.of(x, y, z).withNorm(0.0), 0.0, 0.0, 0.0);
244
245 checkVector(Vector3D.of(x, y, z).withNorm(1.0), normX, normY, normZ);
246 checkVector(Vector3D.of(x, y, -z).withNorm(1.0), normX, normY, -normZ);
247 checkVector(Vector3D.of(x, -y, z).withNorm(1.0), normX, -normY, normZ);
248 checkVector(Vector3D.of(x, -y, -z).withNorm(1.0), normX, -normY, -normZ);
249 checkVector(Vector3D.of(-x, y, z).withNorm(1.0), -normX, normY, normZ);
250 checkVector(Vector3D.of(-x, y, -z).withNorm(1.0), -normX, normY, -normZ);
251 checkVector(Vector3D.of(-x, -y, z).withNorm(1.0), -normX, -normY, normZ);
252 checkVector(Vector3D.of(-x, -y, -z).withNorm(1.0), -normX, -normY, -normZ);
253
254 checkVector(Vector3D.of(x, y, z).withNorm(0.5), 0.5 * normX, 0.5 * normY, 0.5 * normZ);
255 checkVector(Vector3D.of(x, y, z).withNorm(3), 3 * normX, 3 * normY, 3 * normZ);
256
257 checkVector(Vector3D.of(x, y, z).withNorm(-0.5), -0.5 * normX, -0.5 * normY, -0.5 * normZ);
258 checkVector(Vector3D.of(x, y, z).withNorm(-3), -3 * normX, -3 * normY, -3 * normZ);
259
260 for (int i = 0; i <= 10; i++) {
261 final double mag = i * 0.12345 - 5;
262 Assert.assertEquals(Math.abs(mag), Vector3D.of(x, y, z).withNorm(mag).norm(), EPS);
263 }
264 }
265
266 @Test
267 public void testWithNorm_illegalNorm() {
268
269 GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.withNorm(2.0),
270 IllegalArgumentException.class);
271 GeometryTestUtils.assertThrows(() -> Vector3D.NaN.withNorm(2.0),
272 IllegalArgumentException.class);
273 GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.withNorm(2.0),
274 IllegalArgumentException.class);
275 GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.withNorm(2.0),
276 IllegalArgumentException.class);
277 }
278
279 @Test
280 public void testWithNorm_unitVectors() {
281
282 final Vector3D v = Vector3D.of(2.0, -3.0, 4.0).normalize();
283
284
285 checkVector(Vector3D.Unit.PLUS_X.withNorm(2.5), 2.5, 0.0, 0.0);
286 checkVector(Vector3D.Unit.MINUS_Y.withNorm(3.14), 0.0, -3.14, 0.0);
287 checkVector(Vector3D.Unit.PLUS_Z.withNorm(-1.1), 0.0, 0.0, -1.1);
288
289 for (double mag = -10.0; mag <= 10.0; ++mag) {
290 Assert.assertEquals(Math.abs(mag), v.withNorm(mag).norm(), EPS);
291 }
292 }
293
294 @Test
295 public void testAdd() {
296
297 final Vector3D v1 = Vector3D.of(1, 2, 3);
298 final Vector3D v2 = Vector3D.of(-4, -5, -6);
299 final Vector3D v3 = Vector3D.of(7, 8, 9);
300
301
302 checkVector(v1.add(v1), 2, 4, 6);
303
304 checkVector(v1.add(v2), -3, -3, -3);
305 checkVector(v2.add(v1), -3, -3, -3);
306
307 checkVector(v1.add(v3), 8, 10, 12);
308 checkVector(v3.add(v1), 8, 10, 12);
309 }
310
311 @Test
312 public void testAdd_scaled() {
313
314 final Vector3D v1 = Vector3D.of(1, 2, 3);
315 final Vector3D v2 = Vector3D.of(-4, -5, -6);
316 final Vector3D v3 = Vector3D.of(7, 8, 9);
317
318
319 checkVector(v1.add(0, v1), 1, 2, 3);
320 checkVector(v1.add(0.5, v1), 1.5, 3, 4.5);
321 checkVector(v1.add(1, v1), 2, 4, 6);
322
323 checkVector(v1.add(2, v2), -7, -8, -9);
324 checkVector(v2.add(2, v1), -2, -1, -0);
325
326 checkVector(v1.add(-2, v3), -13, -14, -15);
327 checkVector(v3.add(-2, v1), 5, 4, 3);
328 }
329
330 @Test
331 public void testSubtract() {
332
333 final Vector3D v1 = Vector3D.of(1, 2, 3);
334 final Vector3D v2 = Vector3D.of(-4, -5, -6);
335 final Vector3D v3 = Vector3D.of(7, 8, 9);
336
337
338 checkVector(v1.subtract(v1), 0, 0, 0);
339
340 checkVector(v1.subtract(v2), 5, 7, 9);
341 checkVector(v2.subtract(v1), -5, -7, -9);
342
343 checkVector(v1.subtract(v3), -6, -6, -6);
344 checkVector(v3.subtract(v1), 6, 6, 6);
345 }
346
347 @Test
348 public void testSubtract_scaled() {
349
350 final Vector3D v1 = Vector3D.of(1, 2, 3);
351 final Vector3D v2 = Vector3D.of(-4, -5, -6);
352 final Vector3D v3 = Vector3D.of(7, 8, 9);
353
354
355 checkVector(v1.subtract(0, v1), 1, 2, 3);
356 checkVector(v1.subtract(0.5, v1), 0.5, 1, 1.5);
357 checkVector(v1.subtract(1, v1), 0, 0, 0);
358
359 checkVector(v1.subtract(2, v2), 9, 12, 15);
360 checkVector(v2.subtract(2, v1), -6, -9, -12);
361
362 checkVector(v1.subtract(-2, v3), 15, 18, 21);
363 checkVector(v3.subtract(-2, v1), 9, 12, 15);
364 }
365
366 @Test
367 public void testNegate() {
368
369 checkVector(Vector3D.of(0.1, 2.5, 1.3).negate(), -0.1, -2.5, -1.3);
370 checkVector(Vector3D.of(-0.1, -2.5, -1.3).negate(), 0.1, 2.5, 1.3);
371 }
372
373 @Test
374 public void testNegate_unitVectors() {
375
376 final Vector3D v1 = Vector3D.of(1.0, 2.0, 3.0).normalize();
377 final Vector3D v2 = Vector3D.of(-2.0, -4.0, -3.0).normalize();
378
379
380 checkVector(v1.negate(), -1.0 / Math.sqrt(14.0), -Math.sqrt(2.0 / 7.0), -3.0 / Math.sqrt(14.0));
381 checkVector(v2.negate(), 2.0 / Math.sqrt(29.0), 4.0 / Math.sqrt(29.0), 3.0 / Math.sqrt(29.0));
382 }
383
384 @Test
385 public void testNormalize() {
386
387 final double invSqrt3 = 1 / Math.sqrt(3);
388
389
390 checkVector(Vector3D.of(100, 0, 0).normalize(), 1, 0, 0);
391 checkVector(Vector3D.of(-100, 0, 0).normalize(), -1, 0, 0);
392
393 checkVector(Vector3D.of(0, 100, 0).normalize(), 0, 1, 0);
394 checkVector(Vector3D.of(0, -100, 0).normalize(), 0, -1, 0);
395
396 checkVector(Vector3D.of(0, 0, 100).normalize(), 0, 0, 1);
397 checkVector(Vector3D.of(0, 0, -100).normalize(), 0, 0, -1);
398
399 checkVector(Vector3D.of(2, 2, 2).normalize(), invSqrt3, invSqrt3, invSqrt3);
400 checkVector(Vector3D.of(-2, -2, -2).normalize(), -invSqrt3, -invSqrt3, -invSqrt3);
401
402 Assert.assertEquals(1.0, Vector3D.of(5, -4, 2).normalize().norm(), EPS);
403 }
404
405 @Test
406 public void testNormalize_illegalNorm() {
407
408 GeometryTestUtils.assertThrows(Vector3D.ZERO::normalize, IllegalArgumentException.class);
409 GeometryTestUtils.assertThrows(Vector3D.NaN::normalize, IllegalArgumentException.class);
410 GeometryTestUtils.assertThrows(Vector3D.POSITIVE_INFINITY::normalize, IllegalArgumentException.class);
411 GeometryTestUtils.assertThrows(Vector3D.NEGATIVE_INFINITY::normalize, IllegalArgumentException.class);
412 }
413
414 @Test
415 public void testNormalize_isIdempotent() {
416
417 final double invSqrt3 = 1 / Math.sqrt(3);
418 final Vector3D v = Vector3D.of(2, 2, 2).normalize();
419
420
421 Assert.assertSame(v, v.normalize());
422 checkVector(v.normalize(), invSqrt3, invSqrt3, invSqrt3);
423 }
424
425 @Test
426 public void testOrthogonal() {
427
428 final Vector3D v1 = Vector3D.of(0.1, 2.5, 1.3);
429 final Vector3D v2 = Vector3D.of(2.3, -0.003, 7.6);
430 final Vector3D v3 = Vector3D.of(-1.7, 1.4, 0.2);
431 final Vector3D v4 = Vector3D.of(4.2, 0.1, -1.8);
432
433
434 Assert.assertEquals(0.0, v1.dot(v1.orthogonal()), EPS);
435 Assert.assertEquals(0.0, v2.dot(v2.orthogonal()), EPS);
436 Assert.assertEquals(0.0, v3.dot(v3.orthogonal()), EPS);
437 Assert.assertEquals(0.0, v4.dot(v4.orthogonal()), EPS);
438 }
439
440 @Test
441 public void testOrthogonal_illegalNorm() {
442
443 GeometryTestUtils.assertThrows(Vector3D.ZERO::orthogonal, IllegalArgumentException.class);
444 GeometryTestUtils.assertThrows(Vector3D.NaN::orthogonal, IllegalArgumentException.class);
445 GeometryTestUtils.assertThrows(Vector3D.POSITIVE_INFINITY::orthogonal, IllegalArgumentException.class);
446 GeometryTestUtils.assertThrows(Vector3D.NEGATIVE_INFINITY::orthogonal, IllegalArgumentException.class);
447 }
448
449 @Test
450 public void testOrthogonal_givenDirection() {
451
452 final double invSqrt2 = 1.0 / Math.sqrt(2.0);
453
454
455 checkVector(Vector3D.Unit.PLUS_X.orthogonal(Vector3D.of(-1.0, 0.1, 0.0)), 0.0, 1.0, 0.0);
456 checkVector(Vector3D.Unit.PLUS_Y.orthogonal(Vector3D.of(2.0, 2.0, 2.0)), invSqrt2, 0.0, invSqrt2);
457 checkVector(Vector3D.Unit.PLUS_Z.orthogonal(Vector3D.of(3.0, 3.0, -3.0)), invSqrt2, invSqrt2, 0.0);
458
459 checkVector(Vector3D.of(invSqrt2, invSqrt2, 0.0).orthogonal(Vector3D.of(1.0, 1.0, 0.2)), 0.0, 0.0, 1.0);
460 }
461
462 @Test
463 public void testOrthogonal_givenDirection_illegalNorm() {
464
465 GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.orthogonal(Vector3D.Unit.PLUS_X),
466 IllegalArgumentException.class);
467 GeometryTestUtils.assertThrows(() -> Vector3D.NaN.orthogonal(Vector3D.Unit.PLUS_X),
468 IllegalArgumentException.class);
469 GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.orthogonal(Vector3D.Unit.PLUS_X),
470 IllegalArgumentException.class);
471 GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.orthogonal(Vector3D.Unit.PLUS_X),
472 IllegalArgumentException.class);
473
474 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.PLUS_X.orthogonal(Vector3D.ZERO),
475 IllegalArgumentException.class);
476 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.PLUS_X.orthogonal(Vector3D.NaN),
477 IllegalArgumentException.class);
478 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.PLUS_X.orthogonal(Vector3D.POSITIVE_INFINITY),
479 IllegalArgumentException.class);
480 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.PLUS_X.orthogonal(Vector3D.NEGATIVE_INFINITY),
481 IllegalArgumentException.class);
482 }
483
484 @Test
485 public void testOrthogonal_givenDirection_directionIsCollinear() {
486
487 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.PLUS_X.orthogonal(Vector3D.Unit.PLUS_X),
488 IllegalArgumentException.class);
489 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.PLUS_X.orthogonal(Vector3D.Unit.MINUS_X),
490 IllegalArgumentException.class);
491 GeometryTestUtils.assertThrows(() -> Vector3D.of(1.0, 1.0, 1.0).orthogonal(Vector3D.of(2.0, 2.0, 2.0)),
492 IllegalArgumentException.class);
493 GeometryTestUtils.assertThrows(() -> Vector3D.of(-1.01, -1.01, -1.01).orthogonal(Vector3D.of(20.1, 20.1, 20.1)),
494 IllegalArgumentException.class);
495 }
496
497 @Test
498 public void testAngle() {
499
500 final double tolerance = 1e-10;
501
502 final Vector3D v1 = Vector3D.of(1, 2, 3);
503 final Vector3D v2 = Vector3D.of(4, 5, 6);
504
505
506 Assert.assertEquals(0.22572612855273393616, v1.angle(v2), tolerance);
507 Assert.assertEquals(7.98595620686106654517199e-8, v1.angle(Vector3D.of(2, 4, 6.000001)), tolerance);
508 Assert.assertEquals(3.14159257373023116985197793156, v1.angle(Vector3D.of(-2, -4, -6.000001)), tolerance);
509
510 Assert.assertEquals(0.0, Vector3D.Unit.PLUS_X.angle(Vector3D.Unit.PLUS_X), tolerance);
511 Assert.assertEquals(PlaneAngleRadians.PI, Vector3D.Unit.PLUS_X.angle(Vector3D.Unit.MINUS_X), tolerance);
512
513 Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, Vector3D.Unit.PLUS_X.angle(Vector3D.Unit.PLUS_Y), tolerance);
514 Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, Vector3D.Unit.PLUS_X.angle(Vector3D.Unit.MINUS_Y), tolerance);
515 Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, Vector3D.Unit.PLUS_X.angle(Vector3D.Unit.PLUS_Z), tolerance);
516 Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, Vector3D.Unit.PLUS_X.angle(Vector3D.Unit.MINUS_Z), tolerance);
517 }
518
519 @Test
520 public void testAngle_illegalNorm() {
521
522 final Vector3D v = Vector3D.of(1.0, 1.0, 1.0);
523
524
525 GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.angle(v),
526 IllegalArgumentException.class);
527 GeometryTestUtils.assertThrows(() -> Vector3D.NaN.angle(v),
528 IllegalArgumentException.class);
529 GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.angle(v),
530 IllegalArgumentException.class);
531 GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.angle(v),
532 IllegalArgumentException.class);
533
534 GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.ZERO),
535 IllegalArgumentException.class);
536 GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.NaN),
537 IllegalArgumentException.class);
538 GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.POSITIVE_INFINITY),
539 IllegalArgumentException.class);
540 GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.NEGATIVE_INFINITY),
541 IllegalArgumentException.class);
542 }
543
544 @Test
545 public void testAngle_angularSeparation() {
546
547 final Vector3D v1 = Vector3D.of(2, -1, 4);
548
549 final Vector3D k = v1.normalize();
550 final Vector3D i = k.orthogonal();
551 final Vector3D v2 = k.multiply(Math.cos(1.2)).add(i.multiply(Math.sin(1.2)));
552
553
554 Assert.assertTrue(Math.abs(v1.angle(v2) - 1.2) < 1.0e-12);
555 }
556
557 @Test
558 public void testCrossProduct() {
559
560 checkVector(Vector3D.Unit.PLUS_X.cross(Vector3D.Unit.PLUS_Y), 0, 0, 1);
561 checkVector(Vector3D.Unit.PLUS_X.cross(Vector3D.Unit.MINUS_Y), 0, 0, -1);
562
563 checkVector(Vector3D.Unit.MINUS_X.cross(Vector3D.Unit.MINUS_Y), 0, 0, 1);
564 checkVector(Vector3D.Unit.MINUS_X.cross(Vector3D.Unit.PLUS_Y), 0, 0, -1);
565
566 checkVector(Vector3D.of(2, 1, -4).cross(Vector3D.of(3, 1, -1)), 3, -10, -1);
567
568 final double invSqrt6 = 1 / Math.sqrt(6);
569 checkVector(Vector3D.of(1, 1, 1).cross(Vector3D.of(-1, 0, 1)).normalize(), invSqrt6, -2 * invSqrt6, invSqrt6);
570 }
571
572 @Test
573 public void testCrossProduct_nearlyAntiParallel() {
574
575
576
577
578
579
580
581 final Vector3D u1 = Vector3D.of(-1321008684645961.0 / 268435456.0,
582 -5774608829631843.0 / 268435456.0,
583 -7645843051051357.0 / 8589934592.0);
584 final Vector3D u2 = Vector3D.of(1796571811118507.0 / 2147483648.0,
585 7853468008299307.0 / 2147483648.0,
586 2599586637357461.0 / 17179869184.0);
587 final Vector3D u3 = Vector3D.of(12753243807587107.0 / 18446744073709551616.0,
588 -2313766922703915.0 / 18446744073709551616.0,
589 -227970081415313.0 / 288230376151711744.0);
590
591
592 final Vector3D cNaive = Vector3D.of(u1.getY() * u2.getZ() - u1.getZ() * u2.getY(),
593 u1.getZ() * u2.getX() - u1.getX() * u2.getZ(),
594 u1.getX() * u2.getY() - u1.getY() * u2.getX());
595 final Vector3D cAccurate = u1.cross(u2);
596
597
598 Assert.assertTrue(u3.distance(cNaive) > 2.9 * u3.norm());
599 Assert.assertEquals(0.0, u3.distance(cAccurate), 1.0e-30 * cAccurate.norm());
600 }
601
602 @Test
603 public void testCrossProduct_accuracy() {
604
605
606 final UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 885362227452043215L);
607 for (int i = 0; i < 10000; ++i) {
608
609 final double ux = 10000 * random.nextDouble();
610 final double uy = 10000 * random.nextDouble();
611 final double uz = 10000 * random.nextDouble();
612 final double vx = 10000 * random.nextDouble();
613 final double vy = 10000 * random.nextDouble();
614 final double vz = 10000 * random.nextDouble();
615
616
617 final Vector3D cNaive = Vector3D.of(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
618 final Vector3D cAccurate = Vector3D.of(ux, uy, uz).cross(Vector3D.of(vx, vy, vz));
619
620
621 Assert.assertEquals(0.0, cAccurate.distance(cNaive), 6.0e-15 * cAccurate.norm());
622 }
623 }
624
625 @Test
626 public void testCrossProduct_cancellation() {
627
628 final Vector3D v1 = Vector3D.of(9070467121.0, 4535233560.0, 1);
629 final Vector3D v2 = Vector3D.of(9070467123.0, 4535233561.0, 1);
630 checkVector(v1.cross(v2), -1, 2, 1);
631
632 final double scale = Math.scalb(1.0, 100);
633 final Vector3D big1 = Vector3D.linearCombination(scale, v1);
634 final Vector3D small2 = Vector3D.linearCombination(1 / scale, v2);
635 checkVector(big1.cross(small2), -1, 2, 1);
636 }
637
638 @Test
639 public void testScalarMultiply() {
640
641 final Vector3D v1 = Vector3D.of(2, 3, 4);
642 final Vector3D v2 = Vector3D.of(-2, -3, -4);
643
644
645 checkVector(v1.multiply(0), 0, 0, 0);
646 checkVector(v1.multiply(0.5), 1, 1.5, 2);
647 checkVector(v1.multiply(1), 2, 3, 4);
648 checkVector(v1.multiply(2), 4, 6, 8);
649 checkVector(v1.multiply(-2), -4, -6, -8);
650
651 checkVector(v2.multiply(0), 0, 0, 0);
652 checkVector(v2.multiply(0.5), -1, -1.5, -2);
653 checkVector(v2.multiply(1), -2, -3, -4);
654 checkVector(v2.multiply(2), -4, -6, -8);
655 checkVector(v2.multiply(-2), 4, 6, 8);
656 }
657
658 @Test
659 public void testDistance() {
660
661 final Vector3D v1 = Vector3D.of(1, -2, 3);
662 final Vector3D v2 = Vector3D.of(-4, 2, 0);
663 final Vector3D v3 = Vector3D.of(5, -6, -7);
664
665
666 Assert.assertEquals(0.0, v1.distance(v1), EPS);
667 Assert.assertEquals(0.0, v2.distance(v2), EPS);
668
669 Assert.assertEquals(Math.sqrt(50), v1.distance(v2), EPS);
670 Assert.assertEquals(Math.sqrt(50), v2.distance(v1), EPS);
671
672 Assert.assertEquals(v1.subtract(v2).norm(), v1.distance(v2), EPS);
673
674 Assert.assertEquals(Math.sqrt(132), v1.distance(v3), EPS);
675 Assert.assertEquals(Math.sqrt(132), v3.distance(v1), EPS);
676 }
677
678 @Test
679 public void testDistanceSq() {
680
681 final Vector3D v1 = Vector3D.of(1, -2, 3);
682 final Vector3D v2 = Vector3D.of(-4, 2, 0);
683 final Vector3D v3 = Vector3D.of(5, -6, -7);
684
685
686 Assert.assertEquals(0.0, v1.distanceSq(v1), EPS);
687 Assert.assertEquals(0.0, v2.distanceSq(v2), EPS);
688
689 Assert.assertEquals(50, v1.distanceSq(v2), EPS);
690 Assert.assertEquals(50, v2.distanceSq(v1), EPS);
691
692 Assert.assertEquals(v1.subtract(v2).normSq(), v1.distanceSq(v2), EPS);
693
694 Assert.assertEquals(132, v1.distanceSq(v3), EPS);
695 Assert.assertEquals(132, v3.distanceSq(v1), EPS);
696 }
697
698 @Test
699 public void testDotProduct() {
700
701 final Vector3D v1 = Vector3D.of(1, -2, 3);
702 final Vector3D v2 = Vector3D.of(-4, 5, -6);
703 final Vector3D v3 = Vector3D.of(7, 8, 9);
704
705
706 Assert.assertEquals(14, v1.dot(v1), EPS);
707
708 Assert.assertEquals(-32, v1.dot(v2), EPS);
709 Assert.assertEquals(-32, v2.dot(v1), EPS);
710
711 Assert.assertEquals(18, v1.dot(v3), EPS);
712 Assert.assertEquals(18, v3.dot(v1), EPS);
713 }
714
715 @Test
716 public void testDotProduct_nearlyOrthogonal() {
717
718
719
720
721
722 final Vector3D u1 = Vector3D.of(-1321008684645961.0 / 268435456.0,
723 -5774608829631843.0 / 268435456.0,
724 -7645843051051357.0 / 8589934592.0);
725 final Vector3D u2 = Vector3D.of(-5712344449280879.0 / 2097152.0,
726 -4550117129121957.0 / 2097152.0,
727 8846951984510141.0 / 131072.0);
728
729
730 final double sNaive = u1.getX() * u2.getX() + u1.getY() * u2.getY() + u1.getZ() * u2.getZ();
731 final double sAccurate = u1.dot(u2);
732
733
734 Assert.assertEquals(0.0, sNaive, 1.0e-30);
735 Assert.assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate, 1.0e-15);
736 }
737
738 @Test
739 public void testDotProduct_accuracy() {
740
741
742 final UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 553267312521321237L);
743 for (int i = 0; i < 10000; ++i) {
744
745 final double ux = 10000 * random.nextDouble();
746 final double uy = 10000 * random.nextDouble();
747 final double uz = 10000 * random.nextDouble();
748 final double vx = 10000 * random.nextDouble();
749 final double vy = 10000 * random.nextDouble();
750 final double vz = 10000 * random.nextDouble();
751
752
753 final double sNaive = ux * vx + uy * vy + uz * vz;
754 final double sAccurate = Vector3D.of(ux, uy, uz).dot(Vector3D.of(vx, vy, vz));
755
756
757 Assert.assertEquals(sNaive, sAccurate, 2.5e-16 * sAccurate);
758 }
759 }
760
761 @Test
762 public void testProject() {
763
764 final Vector3D v1 = Vector3D.of(2.0, 3.0, 4.0);
765 final Vector3D v2 = Vector3D.of(-5.0, -6.0, -7.0);
766
767
768 checkVector(Vector3D.ZERO.project(Vector3D.Unit.PLUS_X), 0.0, 0.0, 0.0);
769
770 checkVector(v1.project(Vector3D.Unit.PLUS_X), 2.0, 0.0, 0.0);
771 checkVector(v1.project(Vector3D.Unit.MINUS_X), 2.0, 0.0, 0.0);
772 checkVector(v1.project(Vector3D.Unit.PLUS_Y), 0.0, 3.0, 0.0);
773 checkVector(v1.project(Vector3D.Unit.MINUS_Y), 0.0, 3.0, 0.0);
774 checkVector(v1.project(Vector3D.Unit.PLUS_Z), 0.0, 0.0, 4.0);
775 checkVector(v1.project(Vector3D.Unit.MINUS_Z), 0.0, 0.0, 4.0);
776
777 checkVector(v2.project(Vector3D.Unit.PLUS_X), -5.0, 0.0, 0.0);
778 checkVector(v2.project(Vector3D.Unit.MINUS_X), -5.0, 0.0, 0.0);
779 checkVector(v2.project(Vector3D.Unit.PLUS_Y), 0.0, -6.0, 0.0);
780 checkVector(v2.project(Vector3D.Unit.MINUS_Y), 0.0, -6.0, 0.0);
781 checkVector(v2.project(Vector3D.Unit.PLUS_Z), 0.0, 0.0, -7.0);
782 checkVector(v2.project(Vector3D.Unit.MINUS_Z), 0.0, 0.0, -7.0);
783
784 checkVector(v1.project(Vector3D.of(1.0, 1.0, 1.0)), 3.0, 3.0, 3.0);
785 checkVector(v1.project(Vector3D.of(-1.0, -1.0, -1.0)), 3.0, 3.0, 3.0);
786
787 checkVector(v2.project(Vector3D.of(1.0, 1.0, 1.0)), -6.0, -6.0, -6.0);
788 checkVector(v2.project(Vector3D.of(-1.0, -1.0, -1.0)), -6.0, -6.0, -6.0);
789 }
790
791 @Test
792 public void testProject_baseHasIllegalNorm() {
793
794 final Vector3D v = Vector3D.of(1.0, 1.0, 1.0);
795
796
797 GeometryTestUtils.assertThrows(() -> v.project(Vector3D.ZERO),
798 IllegalArgumentException.class);
799 GeometryTestUtils.assertThrows(() -> v.project(Vector3D.NaN),
800 IllegalArgumentException.class);
801 GeometryTestUtils.assertThrows(() -> v.project(Vector3D.POSITIVE_INFINITY),
802 IllegalArgumentException.class);
803 GeometryTestUtils.assertThrows(() -> v.project(Vector3D.NEGATIVE_INFINITY),
804 IllegalArgumentException.class);
805 }
806
807 @Test
808 public void testReject() {
809
810 final Vector3D v1 = Vector3D.of(2.0, 3.0, 4.0);
811 final Vector3D v2 = Vector3D.of(-5.0, -6.0, -7.0);
812
813
814 checkVector(Vector3D.ZERO.reject(Vector3D.Unit.PLUS_X), 0.0, 0.0, 0.0);
815
816 checkVector(v1.reject(Vector3D.Unit.PLUS_X), 0.0, 3.0, 4.0);
817 checkVector(v1.reject(Vector3D.Unit.MINUS_X), 0.0, 3.0, 4.0);
818 checkVector(v1.reject(Vector3D.Unit.PLUS_Y), 2.0, 0.0, 4.0);
819 checkVector(v1.reject(Vector3D.Unit.MINUS_Y), 2.0, 0.0, 4.0);
820 checkVector(v1.reject(Vector3D.Unit.PLUS_Z), 2.0, 3.0, 0.0);
821 checkVector(v1.reject(Vector3D.Unit.MINUS_Z), 2.0, 3.0, 0.0);
822
823 checkVector(v2.reject(Vector3D.Unit.PLUS_X), 0.0, -6.0, -7.0);
824 checkVector(v2.reject(Vector3D.Unit.MINUS_X), 0.0, -6.0, -7.0);
825 checkVector(v2.reject(Vector3D.Unit.PLUS_Y), -5.0, 0.0, -7.0);
826 checkVector(v2.reject(Vector3D.Unit.MINUS_Y), -5.0, 0.0, -7.0);
827 checkVector(v2.reject(Vector3D.Unit.PLUS_Z), -5.0, -6.0, 0.0);
828 checkVector(v2.reject(Vector3D.Unit.MINUS_Z), -5.0, -6.0, 0.0);
829
830 checkVector(v1.reject(Vector3D.of(1.0, 1.0, 1.0)), -1.0, 0.0, 1.0);
831 checkVector(v1.reject(Vector3D.of(-1.0, -1.0, -1.0)), -1.0, 0.0, 1.0);
832
833 checkVector(v2.reject(Vector3D.of(1.0, 1.0, 1.0)), 1.0, 0.0, -1.0);
834 checkVector(v2.reject(Vector3D.of(-1.0, -1.0, -1.0)), 1.0, 0.0, -1.0);
835 }
836
837 @Test
838 public void testReject_baseHasIllegalNorm() {
839
840 final Vector3D v = Vector3D.of(1.0, 1.0, 1.0);
841
842
843 GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.ZERO),
844 IllegalArgumentException.class);
845 GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.NaN),
846 IllegalArgumentException.class);
847 GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.POSITIVE_INFINITY),
848 IllegalArgumentException.class);
849 GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.NEGATIVE_INFINITY),
850 IllegalArgumentException.class);
851 }
852
853 @Test
854 public void testProjectAndReject_areComplementary() {
855
856 final double eps = 1e-12;
857
858
859 checkProjectAndRejectFullSphere(Vector3D.of(1.0, 0.0, 0.0), 1.0, eps);
860 checkProjectAndRejectFullSphere(Vector3D.of(0.0, 1.0, 0.0), 2.0, eps);
861 checkProjectAndRejectFullSphere(Vector3D.of(0.0, 0.0, 1.0), 2.0, eps);
862 checkProjectAndRejectFullSphere(Vector3D.of(1.0, 1.0, 1.0), 3.0, eps);
863
864 checkProjectAndRejectFullSphere(Vector3D.of(-2.0, 0.0, 0.0), 1.0, eps);
865 checkProjectAndRejectFullSphere(Vector3D.of(0.0, -2.0, 0.0), 2.0, eps);
866 checkProjectAndRejectFullSphere(Vector3D.of(0.0, 0.0, -2.0), 2.0, eps);
867 checkProjectAndRejectFullSphere(Vector3D.of(-2.0, -2.0, -2.0), 3.0, eps);
868 }
869
870 private void checkProjectAndRejectFullSphere(final Vector3D vec, final double baseMag, final double eps) {
871 for (double polar = 0.0; polar <= PlaneAngleRadians.PI; polar += 0.5) {
872 for (double azimuth = 0.0; azimuth <= PlaneAngleRadians.TWO_PI; azimuth += 0.5) {
873 final Vector3D base = SphericalCoordinates.toCartesian(baseMag, azimuth, polar);
874
875 final Vector3D proj = vec.project(base);
876 final Vector3D rej = vec.reject(base);
877
878
879 EuclideanTestUtils.assertCoordinatesEqual(vec, proj.add(rej), eps);
880
881 final double angle = base.angle(vec);
882
883
884
885
886 if (angle < PlaneAngleRadians.PI_OVER_TWO) {
887 Assert.assertEquals(0.0, proj.angle(base), eps);
888 } else if (angle > PlaneAngleRadians.PI_OVER_TWO) {
889 Assert.assertEquals(PlaneAngleRadians.PI, proj.angle(base), eps);
890 }
891
892
893
894
895 if (angle > 0.0 && angle < PlaneAngleRadians.PI) {
896 Assert.assertEquals(PlaneAngleRadians.PI_OVER_TWO, rej.angle(base), eps);
897 }
898 }
899 }
900 }
901
902 @Test
903 public void testVectorTo() {
904
905 final Vector3D p1 = Vector3D.of(1, 2, 3);
906 final Vector3D p2 = Vector3D.of(4, 5, 6);
907 final Vector3D p3 = Vector3D.of(-7, -8, -9);
908
909
910 checkVector(p1.vectorTo(p1), 0, 0, 0);
911 checkVector(p2.vectorTo(p2), 0, 0, 0);
912 checkVector(p3.vectorTo(p3), 0, 0, 0);
913
914 checkVector(p1.vectorTo(p2), 3, 3, 3);
915 checkVector(p2.vectorTo(p1), -3, -3, -3);
916
917 checkVector(p1.vectorTo(p3), -8, -10, -12);
918 checkVector(p3.vectorTo(p1), 8, 10, 12);
919 }
920
921 @Test
922 public void testDirectionTo() {
923
924 final double invSqrt3 = 1.0 / Math.sqrt(3);
925
926 final Vector3D p1 = Vector3D.of(1, 1, 1);
927 final Vector3D p2 = Vector3D.of(1, 5, 1);
928 final Vector3D p3 = Vector3D.of(-2, -2, -2);
929
930
931 checkVector(p1.directionTo(p2), 0, 1, 0);
932 checkVector(p2.directionTo(p1), 0, -1, 0);
933
934 checkVector(p1.directionTo(p3), -invSqrt3, -invSqrt3, -invSqrt3);
935 checkVector(p3.directionTo(p1), invSqrt3, invSqrt3, invSqrt3);
936 }
937
938 @Test
939 public void testDirectionTo_illegalNorm() {
940
941 final Vector3D p = Vector3D.of(1, 2, 3);
942
943
944 GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.directionTo(Vector3D.ZERO),
945 IllegalArgumentException.class);
946 GeometryTestUtils.assertThrows(() -> p.directionTo(p),
947 IllegalArgumentException.class);
948 GeometryTestUtils.assertThrows(() -> p.directionTo(Vector3D.NaN),
949 IllegalArgumentException.class);
950 GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.directionTo(p),
951 IllegalArgumentException.class);
952 GeometryTestUtils.assertThrows(() -> p.directionTo(Vector3D.POSITIVE_INFINITY),
953 IllegalArgumentException.class);
954 }
955
956 @Test
957 public void testLerp() {
958
959 final Vector3D v1 = Vector3D.of(1, -5, 2);
960 final Vector3D v2 = Vector3D.of(-4, 0, 2);
961 final Vector3D v3 = Vector3D.of(10, -4, 0);
962
963
964 checkVector(v1.lerp(v1, 0), 1, -5, 2);
965 checkVector(v1.lerp(v1, 1), 1, -5, 2);
966
967 checkVector(v1.lerp(v2, -0.25), 2.25, -6.25, 2);
968 checkVector(v1.lerp(v2, 0), 1, -5, 2);
969 checkVector(v1.lerp(v2, 0.25), -0.25, -3.75, 2);
970 checkVector(v1.lerp(v2, 0.5), -1.5, -2.5, 2);
971 checkVector(v1.lerp(v2, 0.75), -2.75, -1.25, 2);
972 checkVector(v1.lerp(v2, 1), -4, 0, 2);
973 checkVector(v1.lerp(v2, 1.25), -5.25, 1.25, 2);
974
975 checkVector(v1.lerp(v3, 0), 1, -5, 2);
976 checkVector(v1.lerp(v3, 0.25), 3.25, -4.75, 1.5);
977 checkVector(v1.lerp(v3, 0.5), 5.5, -4.5, 1);
978 checkVector(v1.lerp(v3, 0.75), 7.75, -4.25, 0.5);
979 checkVector(v1.lerp(v3, 1), 10, -4, 0);
980 }
981
982 @Test
983 public void testTransform() {
984
985 final AffineTransformMatrix3D transform = AffineTransformMatrix3D.identity()
986 .scale(2)
987 .translate(1, 2, 3);
988
989 final Vector3D v1 = Vector3D.of(1, 2, 3);
990 final Vector3D v2 = Vector3D.of(-4, -5, -6);
991
992
993 checkVector(v1.transform(transform), 3, 6, 9);
994 checkVector(v2.transform(transform), -7, -8, -9);
995 }
996
997 @Test
998 public void testPrecisionEquals() {
999
1000 final DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6);
1001 final DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1);
1002
1003 final Vector3D vec = Vector3D.of(1, -2, 3);
1004
1005
1006 Assert.assertTrue(vec.eq(vec, smallEps));
1007 Assert.assertTrue(vec.eq(vec, largeEps));
1008
1009 Assert.assertTrue(vec.eq(Vector3D.of(1.0000007, -2.0000009, 3.0000009), smallEps));
1010 Assert.assertTrue(vec.eq(Vector3D.of(1.0000007, -2.0000009, 3.0000009), largeEps));
1011
1012 Assert.assertFalse(vec.eq(Vector3D.of(1.004, -2, 3), smallEps));
1013 Assert.assertFalse(vec.eq(Vector3D.of(1, -2.004, 3), smallEps));
1014 Assert.assertFalse(vec.eq(Vector3D.of(1, -2, 2.999), smallEps));
1015 Assert.assertTrue(vec.eq(Vector3D.of(1.004, -2.004, 2.999), largeEps));
1016
1017 Assert.assertFalse(vec.eq(Vector3D.of(2, -2, 3), smallEps));
1018 Assert.assertFalse(vec.eq(Vector3D.of(1, -3, 3), smallEps));
1019 Assert.assertFalse(vec.eq(Vector3D.of(1, -2, 4), smallEps));
1020 Assert.assertFalse(vec.eq(Vector3D.of(2, -3, 4), smallEps));
1021
1022 Assert.assertFalse(vec.eq(Vector3D.of(2, -2, 3), largeEps));
1023 Assert.assertFalse(vec.eq(Vector3D.of(1, -3, 3), largeEps));
1024 Assert.assertFalse(vec.eq(Vector3D.of(1, -2, 4), largeEps));
1025 Assert.assertFalse(vec.eq(Vector3D.of(2, -3, 4), largeEps));
1026 }
1027
1028 @Test
1029 public void testIsZero() {
1030
1031 final DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6);
1032 final DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1);
1033
1034
1035 Assert.assertTrue(Vector3D.of(0.0, -0.0, 0.0).isZero(smallEps));
1036 Assert.assertTrue(Vector3D.of(-0.0, 0.0, -0.0).isZero(largeEps));
1037
1038 Assert.assertTrue(Vector3D.of(-1e-7, 1e-7, -1e-8).isZero(smallEps));
1039 Assert.assertTrue(Vector3D.of(1e-7, -1e-7, 1e-8).isZero(largeEps));
1040
1041 Assert.assertFalse(Vector3D.of(1e-2, 0.0, 0.0).isZero(smallEps));
1042 Assert.assertFalse(Vector3D.of(0.0, 1e-2, 0.0).isZero(smallEps));
1043 Assert.assertFalse(Vector3D.of(0.0, 0.0, 1e-2).isZero(smallEps));
1044 Assert.assertTrue(Vector3D.of(1e-2, -1e-2, 1e-2).isZero(largeEps));
1045
1046 Assert.assertFalse(Vector3D.of(0.2, 0.0, 0.0).isZero(smallEps));
1047 Assert.assertFalse(Vector3D.of(0.0, 0.2, 0.0).isZero(smallEps));
1048 Assert.assertFalse(Vector3D.of(0.0, 0.0, 0.2).isZero(smallEps));
1049 Assert.assertFalse(Vector3D.of(0.2, 0.2, 0.2).isZero(smallEps));
1050
1051 Assert.assertFalse(Vector3D.of(0.2, 0.0, 0.0).isZero(largeEps));
1052 Assert.assertFalse(Vector3D.of(0.0, 0.2, 0.0).isZero(largeEps));
1053 Assert.assertFalse(Vector3D.of(0.0, 0.0, 0.2).isZero(largeEps));
1054 Assert.assertFalse(Vector3D.of(0.2, 0.2, 0.2).isZero(largeEps));
1055 }
1056
1057 @Test
1058 public void testHashCode() {
1059
1060 final double delta = 10 * Precision.EPSILON;
1061 final Vector3D u = Vector3D.of(1, 1, 1);
1062 final Vector3D v = Vector3D.of(1 + delta, 1 + delta, 1 + delta);
1063 final Vector3D w = Vector3D.of(1, 1, 1);
1064
1065
1066 Assert.assertTrue(u.hashCode() != v.hashCode());
1067 Assert.assertEquals(u.hashCode(), w.hashCode());
1068
1069 Assert.assertEquals(Vector3D.of(0, 0, Double.NaN).hashCode(), Vector3D.NaN.hashCode());
1070 Assert.assertEquals(Vector3D.of(0, Double.NaN, 0).hashCode(), Vector3D.NaN.hashCode());
1071 Assert.assertEquals(Vector3D.of(Double.NaN, 0, 0).hashCode(), Vector3D.NaN.hashCode());
1072 Assert.assertEquals(Vector3D.of(0, 0, Double.NaN).hashCode(), Vector3D.of(Double.NaN, 0, 0).hashCode());
1073 }
1074
1075 @Test
1076 public void testEquals() {
1077
1078 final double delta = 10 * Precision.EPSILON;
1079 final Vector3D u1 = Vector3D.of(1, 2, 3);
1080 final Vector3D u2 = Vector3D.of(1, 2, 3);
1081
1082
1083 Assert.assertFalse(u1.equals(null));
1084 Assert.assertFalse(u1.equals(new Object()));
1085
1086 Assert.assertEquals(u1, u1);
1087 Assert.assertEquals(u1, u2);
1088
1089 Assert.assertNotEquals(u1, Vector3D.of(-1, -2, -3));
1090 Assert.assertNotEquals(u1, Vector3D.of(1 + delta, 2, 3));
1091 Assert.assertNotEquals(u1, Vector3D.of(1, 2 + delta, 3));
1092 Assert.assertNotEquals(u1, Vector3D.of(1, 2, 3 + delta));
1093
1094 Assert.assertEquals(Vector3D.of(0, Double.NaN, 0), Vector3D.of(Double.NaN, 0, 0));
1095
1096 Assert.assertEquals(Vector3D.of(0, 0, Double.POSITIVE_INFINITY), Vector3D.of(0, 0, Double.POSITIVE_INFINITY));
1097 Assert.assertNotEquals(Vector3D.of(0, Double.POSITIVE_INFINITY, 0), Vector3D.of(0, 0, Double.POSITIVE_INFINITY));
1098 Assert.assertNotEquals(Vector3D.of(Double.POSITIVE_INFINITY, 0, 0), Vector3D.of(0, 0, Double.POSITIVE_INFINITY));
1099
1100 Assert.assertEquals(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0), Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0));
1101 Assert.assertNotEquals(Vector3D.of(0, Double.NEGATIVE_INFINITY, 0), Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0));
1102 Assert.assertNotEquals(Vector3D.of(0, 0, Double.NEGATIVE_INFINITY), Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0));
1103 }
1104
1105 @Test
1106 public void testEqualsAndHashCode_signedZeroConsistency() {
1107
1108 final Vector3D a = Vector3D.of(0.0, -0.0, 0.0);
1109 final Vector3D b = Vector3D.of(-0.0, 0.0, -0.0);
1110 final Vector3D c = Vector3D.of(0.0, -0.0, 0.0);
1111 final Vector3D d = Vector3D.of(-0.0, 0.0, -0.0);
1112
1113
1114 Assert.assertFalse(a.equals(b));
1115
1116 Assert.assertTrue(a.equals(c));
1117 Assert.assertEquals(a.hashCode(), c.hashCode());
1118
1119 Assert.assertTrue(b.equals(d));
1120 Assert.assertEquals(b.hashCode(), d.hashCode());
1121 }
1122
1123 @Test
1124 public void testToString() {
1125
1126 final Vector3D v = Vector3D.of(1, 2, 3);
1127 final Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
1128
1129
1130 final String str = v.toString();
1131
1132
1133 Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
1134 pattern.matcher(str).matches());
1135 }
1136
1137 @Test
1138 public void testParse() {
1139
1140 checkVector(Vector3D.parse("(1, 2, 3)"), 1, 2, 3);
1141 checkVector(Vector3D.parse("(-1, -2, -3)"), -1, -2, -3);
1142
1143 checkVector(Vector3D.parse("(0.01, -1e-3, 0)"), 1e-2, -1e-3, 0);
1144
1145 checkVector(Vector3D.parse("(NaN, -Infinity, Infinity)"), Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
1146
1147 checkVector(Vector3D.parse(Vector3D.ZERO.toString()), 0, 0, 0);
1148 checkVector(Vector3D.parse(Vector3D.Unit.MINUS_X.toString()), -1, 0, 0);
1149 }
1150
1151 @Test(expected = IllegalArgumentException.class)
1152 public void testParse_failure() {
1153
1154 Vector3D.parse("abc");
1155 }
1156
1157 @Test
1158 public void testOf() {
1159
1160 checkVector(Vector3D.of(1, 2, 3), 1, 2, 3);
1161 checkVector(Vector3D.of(-1, -2, -3), -1, -2, -3);
1162 checkVector(Vector3D.of(Math.PI, Double.NaN, Double.POSITIVE_INFINITY),
1163 Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
1164 checkVector(Vector3D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E),
1165 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
1166 }
1167
1168 @Test
1169 public void testOf_arrayArg() {
1170
1171 checkVector(Vector3D.of(new double[] {1, 2, 3}), 1, 2, 3);
1172 checkVector(Vector3D.of(new double[] {-1, -2, -3}), -1, -2, -3);
1173 checkVector(Vector3D.of(new double[] {Math.PI, Double.NaN, Double.POSITIVE_INFINITY}),
1174 Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
1175 checkVector(Vector3D.of(new double[] {Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E}),
1176 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
1177 }
1178
1179 @Test(expected = IllegalArgumentException.class)
1180 public void testOf_arrayArg_invalidDimensions() {
1181
1182 Vector3D.of(new double[] {0.0, 0.0});
1183 }
1184
1185 @Test
1186 public void testUnitFrom_coordinates() {
1187
1188 final double invSqrt3 = 1.0 / Math.sqrt(3.0);
1189
1190
1191 checkVector(Vector3D.Unit.from(2.0, -2.0, 2.0), invSqrt3, -invSqrt3, invSqrt3);
1192 checkVector(Vector3D.Unit.from(-4.0, 4.0, -4.0), -invSqrt3, invSqrt3, -invSqrt3);
1193 }
1194
1195 @Test
1196 public void testUnitFrom_vector() {
1197
1198 final double invSqrt3 = 1.0 / Math.sqrt(3.0);
1199 final Vector3D vec = Vector3D.of(2.0, -2.0, 2.0);
1200 final Vector3D.Unit unitVec = Vector3D.Unit.from(2.0, -2.0, 2.0);
1201
1202
1203 checkVector(Vector3D.Unit.from(vec), invSqrt3, -invSqrt3, invSqrt3);
1204 Assert.assertSame(unitVec, Vector3D.Unit.from(unitVec));
1205 }
1206
1207 @Test
1208 public void testUnitFrom_static_illegalNorm() {
1209 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.from(0.0, 0.0, 0.0),
1210 IllegalArgumentException.class);
1211 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.from(Double.NaN, 1.0, 1.0),
1212 IllegalArgumentException.class);
1213 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.from(1.0, Double.NEGATIVE_INFINITY, 1.0),
1214 IllegalArgumentException.class);
1215 GeometryTestUtils.assertThrows(() -> Vector3D.Unit.from(1.0, 1.0, Double.POSITIVE_INFINITY),
1216 IllegalArgumentException.class);
1217 }
1218
1219 @Test
1220 public void testMax() {
1221
1222 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-100, 1, 100),
1223 Vector3D.max(Collections.singletonList(Vector3D.of(-100, 1, 100))), EPS);
1224
1225 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 100),
1226 Vector3D.max(Arrays.asList(Vector3D.of(-100, 1, 100), Vector3D.of(0, 1, 0))), EPS);
1227
1228 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 2),
1229 Vector3D.max(Vector3D.of(-2, 0, 0), Vector3D.of(-1, -5, 1), Vector3D.of(-10, -10, 2)), EPS);
1230 }
1231
1232 @Test
1233 public void testMax_noPointsGiven() {
1234
1235 final String msg = "Cannot compute vector max: no vectors given";
1236
1237
1238 GeometryTestUtils.assertThrows(() -> {
1239 Vector3D.max(new ArrayList<>());
1240 }, IllegalArgumentException.class, msg);
1241 }
1242
1243 @Test
1244 public void testMin() {
1245
1246 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-100, 1, 100),
1247 Vector3D.min(Collections.singletonList(Vector3D.of(-100, 1, 100))), EPS);
1248
1249 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-100, 1, 0),
1250 Vector3D.min(Arrays.asList(Vector3D.of(-100, 1, 100), Vector3D.of(0, 1, 0))), EPS);
1251
1252 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-10, -10, 0),
1253 Vector3D.min(Vector3D.of(-2, 0, 0), Vector3D.of(-1, -5, 1), Vector3D.of(-10, -10, 2)), EPS);
1254 }
1255
1256 @Test
1257 public void testMin_noPointsGiven() {
1258
1259 final String msg = "Cannot compute vector min: no vectors given";
1260
1261
1262 GeometryTestUtils.assertThrows(() -> {
1263 Vector3D.min(new ArrayList<>());
1264 }, IllegalArgumentException.class, msg);
1265 }
1266
1267 @Test
1268 public void testCentroid() {
1269
1270 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 3),
1271 Vector3D.centroid(Vector3D.of(1, 2, 3)), EPS);
1272
1273 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2.5, 3.5, 4.5),
1274 Vector3D.centroid(Vector3D.of(1, 2, 3), Vector3D.of(2, 3, 4),
1275 Vector3D.of(3, 4, 5), Vector3D.of(4, 5, 6)), EPS);
1276
1277 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 3),
1278 Vector3D.centroid(Collections.singletonList(Vector3D.of(1, 2, 3))), EPS);
1279
1280 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0.5, 1, 1.5),
1281 Vector3D.centroid(Arrays.asList(Vector3D.of(1, 2, 3), Vector3D.of(1, 2, 3),
1282 Vector3D.ZERO, Vector3D.ZERO)), EPS);
1283 }
1284
1285 @Test
1286 public void testCentroid_noPointsGiven() {
1287
1288 final String msg = "Cannot compute centroid: no points given";
1289
1290
1291 GeometryTestUtils.assertThrows(() -> {
1292 Vector3D.centroid(new ArrayList<>());
1293 }, IllegalArgumentException.class, msg);
1294 }
1295
1296 @Test
1297 public void testLinearCombination1() {
1298
1299 final Vector3D p1 = Vector3D.of(1, 2, 3);
1300
1301
1302 checkVector(Vector3D.linearCombination(0, p1), 0, 0, 0);
1303
1304 checkVector(Vector3D.linearCombination(1, p1), 1, 2, 3);
1305 checkVector(Vector3D.linearCombination(-1, p1), -1, -2, -3);
1306
1307 checkVector(Vector3D.linearCombination(0.5, p1), 0.5, 1, 1.5);
1308 checkVector(Vector3D.linearCombination(-0.5, p1), -0.5, -1, -1.5);
1309 }
1310
1311 @Test
1312 public void testLinearCombination2() {
1313
1314 final Vector3D p1 = Vector3D.of(1, 2, 3);
1315 final Vector3D p2 = Vector3D.of(-3, -4, -5);
1316
1317
1318 checkVector(Vector3D.linearCombination(2, p1, -3, p2), 11, 16, 21);
1319 checkVector(Vector3D.linearCombination(-3, p1, 2, p2), -9, -14, -19);
1320 }
1321
1322 @Test
1323 public void testLinearCombination3() {
1324
1325 final Vector3D p1 = Vector3D.of(1, 2, 3);
1326 final Vector3D p2 = Vector3D.of(-3, -4, -5);
1327 final Vector3D p3 = Vector3D.of(5, 6, 7);
1328
1329
1330 checkVector(Vector3D.linearCombination(2, p1, -3, p2, 4, p3), 31, 40, 49);
1331 checkVector(Vector3D.linearCombination(-3, p1, 2, p2, -4, p3), -29, -38, -47);
1332 }
1333
1334 @Test
1335 public void testLinearCombination4() {
1336
1337 final Vector3D p1 = Vector3D.of(1, 2, 3);
1338 final Vector3D p2 = Vector3D.of(-3, -4, -5);
1339 final Vector3D p3 = Vector3D.of(5, 6, 7);
1340 final Vector3D p4 = Vector3D.of(-7, -8, 9);
1341
1342
1343 checkVector(Vector3D.linearCombination(2, p1, -3, p2, 4, p3, -5, p4), 66, 80, 4);
1344 checkVector(Vector3D.linearCombination(-3, p1, 2, p2, -4, p3, 5, p4), -64, -78, -2);
1345 }
1346
1347 @Test
1348 public void testUnitFactoryOptimization() {
1349
1350 final Vector3D v = Vector3D.of(3, 4, 5).normalize();
1351 Assert.assertSame(v, v.normalize());
1352 }
1353
1354 private void checkVector(final Vector3D v, final double x, final double y, final double z) {
1355 Assert.assertEquals(x, v.getX(), EPS);
1356 Assert.assertEquals(y, v.getY(), EPS);
1357 Assert.assertEquals(z, v.getZ(), EPS);
1358 }
1359 }