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