1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.threed;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.function.BiFunction;
23 import java.util.function.ToDoubleFunction;
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.geometry.euclidean.threed.shape.Parallelepiped;
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 Bounds3DTest {
34
35 private static final double TEST_EPS = 1e-10;
36
37 private static final Precision.DoubleEquivalence TEST_PRECISION =
38 Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
39
40 private static final String NO_POINTS_MESSAGE = "Cannot construct bounds: no points given";
41
42 private static final Pattern INVALID_BOUNDS_PATTERN =
43 Pattern.compile("^Invalid bounds: min= \\([^\\)]+\\), max= \\([^\\)]+\\)");
44
45 @Test
46 void testFrom_varargs_singlePoint() {
47
48 final Vector3D p1 = Vector3D.of(-1, 2, -3);
49
50
51 final Bounds3D b = Bounds3D.from(p1);
52
53
54 EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMin(), TEST_EPS);
55 EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMax(), TEST_EPS);
56 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, b.getDiagonal(), TEST_EPS);
57 EuclideanTestUtils.assertCoordinatesEqual(p1, b.getCentroid(), TEST_EPS);
58 }
59
60 @Test
61 void testFrom_varargs_multiplePoints() {
62
63 final Vector3D p1 = Vector3D.of(1, 6, 7);
64 final Vector3D p2 = Vector3D.of(0, 5, 11);
65 final Vector3D p3 = Vector3D.of(3, 6, 8);
66
67
68 final Bounds3D b = Bounds3D.from(p1, p2, p3);
69
70
71 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 5, 7), b.getMin(), TEST_EPS);
72 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(3, 6, 11), b.getMax(), TEST_EPS);
73 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(3, 1, 4), b.getDiagonal(), TEST_EPS);
74 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1.5, 5.5, 9), b.getCentroid(), TEST_EPS);
75 }
76
77 @Test
78 void testFrom_iterable_singlePoint() {
79
80 final Vector3D p1 = Vector3D.of(-1, 2, -3);
81
82
83 final Bounds3D b = Bounds3D.from(Collections.singletonList(p1));
84
85
86 EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMin(), TEST_EPS);
87 EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMax(), TEST_EPS);
88 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, b.getDiagonal(), TEST_EPS);
89 EuclideanTestUtils.assertCoordinatesEqual(p1, b.getCentroid(), TEST_EPS);
90 }
91
92 @Test
93 void testFrom_iterable_multiplePoints() {
94
95 final Vector3D p1 = Vector3D.of(1, 6, 7);
96 final Vector3D p2 = Vector3D.of(2, 5, 9);
97 final Vector3D p3 = Vector3D.of(3, 4, 8);
98
99
100 final Bounds3D b = Bounds3D.from(Arrays.asList(p1, p2, p3));
101
102
103 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 4, 7), b.getMin(), TEST_EPS);
104 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(3, 6, 9), b.getMax(), TEST_EPS);
105 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2, 2, 2), b.getDiagonal(), TEST_EPS);
106 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2, 5, 8), b.getCentroid(), TEST_EPS);
107 }
108
109 @Test
110 void testFrom_iterable_noPoints() {
111
112 GeometryTestUtils.assertThrowsWithMessage(() -> {
113 Bounds3D.from(new ArrayList<>());
114 }, IllegalStateException.class, NO_POINTS_MESSAGE);
115 }
116
117 @Test
118 void testFrom_invalidBounds() {
119
120 final Vector3D good = Vector3D.of(1, 1, 1);
121
122 final Vector3D nan = Vector3D.of(Double.NaN, 1, 1);
123 final Vector3D posInf = Vector3D.of(1, Double.POSITIVE_INFINITY, 1);
124 final Vector3D negInf = Vector3D.of(1, 1, Double.NEGATIVE_INFINITY);
125
126
127 GeometryTestUtils.assertThrowsWithMessage(() -> {
128 Bounds3D.from(Vector3D.NaN);
129 }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
130
131 GeometryTestUtils.assertThrowsWithMessage(() -> {
132 Bounds3D.from(Vector3D.POSITIVE_INFINITY);
133 }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
134
135 GeometryTestUtils.assertThrowsWithMessage(() -> {
136 Bounds3D.from(Vector3D.NEGATIVE_INFINITY);
137 }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
138
139 GeometryTestUtils.assertThrowsWithMessage(() -> {
140 Bounds3D.from(good, nan);
141 }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
142
143 GeometryTestUtils.assertThrowsWithMessage(() -> {
144 Bounds3D.from(posInf, good);
145 }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
146
147 GeometryTestUtils.assertThrowsWithMessage(() -> {
148 Bounds3D.from(good, negInf, good);
149 }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
150 }
151
152 @Test
153 void testHasSize() {
154
155 final Precision.DoubleEquivalence low = Precision.doubleEquivalenceOfEpsilon(1e-2);
156 final Precision.DoubleEquivalence high = Precision.doubleEquivalenceOfEpsilon(1e-10);
157
158 final Vector3D p1 = Vector3D.ZERO;
159
160 final Vector3D p2 = Vector3D.of(1e-5, 1, 1);
161 final Vector3D p3 = Vector3D.of(1, 1e-5, 1);
162 final Vector3D p4 = Vector3D.of(1, 1, 1e-5);
163
164 final Vector3D p5 = Vector3D.of(1, 1, 1);
165
166
167 Assertions.assertFalse(Bounds3D.from(p1).hasSize(high));
168 Assertions.assertFalse(Bounds3D.from(p1).hasSize(low));
169
170 Assertions.assertTrue(Bounds3D.from(p1, p2).hasSize(high));
171 Assertions.assertFalse(Bounds3D.from(p1, p2).hasSize(low));
172
173 Assertions.assertTrue(Bounds3D.from(p1, p3).hasSize(high));
174 Assertions.assertFalse(Bounds3D.from(p1, p3).hasSize(low));
175
176 Assertions.assertTrue(Bounds3D.from(p1, p4).hasSize(high));
177 Assertions.assertFalse(Bounds3D.from(p1, p4).hasSize(low));
178
179 Assertions.assertTrue(Bounds3D.from(p1, p5).hasSize(high));
180 Assertions.assertTrue(Bounds3D.from(p1, p5).hasSize(low));
181 }
182
183 @Test
184 void testContains_strict() {
185
186 final Bounds3D b = Bounds3D.from(
187 Vector3D.of(0, 4, 8),
188 Vector3D.of(2, 6, 10));
189
190
191 assertContainsStrict(b, true,
192 b.getCentroid(),
193 Vector3D.of(0, 4, 8), Vector3D.of(2, 6, 10),
194 Vector3D.of(1, 5, 9),
195 Vector3D.of(0, 5, 9), Vector3D.of(2, 5, 9),
196 Vector3D.of(1, 4, 9), Vector3D.of(1, 6, 9),
197 Vector3D.of(1, 5, 8), Vector3D.of(1, 5, 10));
198
199 assertContainsStrict(b, false,
200 Vector3D.ZERO,
201 Vector3D.of(-1, 5, 9), Vector3D.of(3, 5, 9),
202 Vector3D.of(1, 3, 9), Vector3D.of(1, 7, 9),
203 Vector3D.of(1, 5, 7), Vector3D.of(1, 5, 11),
204 Vector3D.of(-1e-15, 4, 8), Vector3D.of(2, 6 + 1e-15, 10), Vector3D.of(0, 4, 10 + 1e-15));
205 }
206
207 @Test
208 void testContains_precision() {
209
210 final Bounds3D b = Bounds3D.from(
211 Vector3D.of(0, 4, 8),
212 Vector3D.of(2, 6, 10));
213
214
215 assertContainsWithPrecision(b, true,
216 b.getCentroid(),
217 Vector3D.of(0, 4, 8), Vector3D.of(2, 6, 10),
218 Vector3D.of(1, 5, 9),
219 Vector3D.of(0, 5, 9), Vector3D.of(2, 5, 9),
220 Vector3D.of(1, 4, 9), Vector3D.of(1, 6, 9),
221 Vector3D.of(1, 5, 8), Vector3D.of(1, 5, 10),
222 Vector3D.of(-1e-15, 4, 8), Vector3D.of(2, 6 + 1e-15, 10), Vector3D.of(0, 4, 10 + 1e-15));
223
224 assertContainsWithPrecision(b, false,
225 Vector3D.ZERO,
226 Vector3D.of(-1, 5, 9), Vector3D.of(3, 5, 9),
227 Vector3D.of(1, 3, 9), Vector3D.of(1, 7, 9),
228 Vector3D.of(1, 5, 7), Vector3D.of(1, 5, 11));
229 }
230
231 @Test
232 void testIntersects() {
233
234 final Bounds3D b = Bounds3D.from(Vector3D.ZERO, Vector3D.of(1, 1, 1));
235
236
237 checkIntersects(b, Vector3D::getX, (v, x) -> Vector3D.of(x, v.getY(), v.getZ()));
238 checkIntersects(b, Vector3D::getY, (v, y) -> Vector3D.of(v.getX(), y, v.getZ()));
239 checkIntersects(b, Vector3D::getZ, (v, z) -> Vector3D.of(v.getX(), v.getY(), z));
240 }
241
242 private void checkIntersects(final Bounds3D b, final ToDoubleFunction<? super Vector3D> getter,
243 final BiFunction<? super Vector3D, Double, ? extends Vector3D> setter) {
244
245 final Vector3D min = b.getMin();
246 final Vector3D max = b.getMax();
247
248 final double minValue = getter.applyAsDouble(min);
249 final double maxValue = getter.applyAsDouble(max);
250 final double midValue = (0.5 * (maxValue - minValue)) + minValue;
251
252
253
254
255 Assertions.assertFalse(b.intersects(Bounds3D.from(
256 setter.apply(min, minValue - 2), setter.apply(max, minValue - 1))));
257
258 Assertions.assertTrue(b.intersects(Bounds3D.from(
259 setter.apply(min, minValue - 2), setter.apply(max, minValue))));
260 Assertions.assertTrue(b.intersects(Bounds3D.from(
261 setter.apply(min, minValue - 2), setter.apply(max, midValue))));
262 Assertions.assertTrue(b.intersects(Bounds3D.from(
263 setter.apply(min, minValue - 2), setter.apply(max, maxValue))));
264 Assertions.assertTrue(b.intersects(Bounds3D.from(
265 setter.apply(min, minValue - 2), setter.apply(max, maxValue + 1))));
266
267
268 Assertions.assertTrue(b.intersects(Bounds3D.from(
269 setter.apply(min, minValue), setter.apply(max, minValue))));
270 Assertions.assertTrue(b.intersects(Bounds3D.from(
271 setter.apply(min, minValue), setter.apply(max, midValue))));
272 Assertions.assertTrue(b.intersects(Bounds3D.from(
273 setter.apply(min, minValue), setter.apply(max, maxValue))));
274 Assertions.assertTrue(b.intersects(Bounds3D.from(
275 setter.apply(min, minValue), setter.apply(max, maxValue + 1))));
276
277
278 Assertions.assertTrue(b.intersects(Bounds3D.from(
279 setter.apply(min, midValue), setter.apply(max, midValue))));
280 Assertions.assertTrue(b.intersects(Bounds3D.from(
281 setter.apply(min, midValue), setter.apply(max, maxValue))));
282 Assertions.assertTrue(b.intersects(Bounds3D.from(
283 setter.apply(min, midValue), setter.apply(max, maxValue + 1))));
284
285
286 Assertions.assertTrue(b.intersects(Bounds3D.from(
287 setter.apply(min, maxValue), setter.apply(max, maxValue))));
288 Assertions.assertTrue(b.intersects(Bounds3D.from(
289 setter.apply(min, maxValue), setter.apply(max, maxValue + 1))));
290
291
292 Assertions.assertFalse(b.intersects(Bounds3D.from(
293 setter.apply(min, maxValue + 1), setter.apply(max, maxValue + 2))));
294 }
295
296 @Test
297 void testIntersection() {
298
299 final Bounds3D b = Bounds3D.from(Vector3D.ZERO, Vector3D.of(1, 1, 1));
300
301
302
303
304 Assertions.assertNull(b.intersection(Bounds3D.from(Vector3D.of(-2, 0, 0), Vector3D.of(-1, 1, 1))));
305 checkIntersection(b, Vector3D.of(-1, 0, 0), Vector3D.of(0, 1, 1),
306 Vector3D.of(0, 0, 0), Vector3D.of(0, 1, 1));
307 checkIntersection(b, Vector3D.of(-1, 0, 0), Vector3D.of(0.5, 1, 1),
308 Vector3D.of(0, 0, 0), Vector3D.of(0.5, 1, 1));
309 checkIntersection(b, Vector3D.of(-1, 0, 0), Vector3D.of(1, 1, 1),
310 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
311 checkIntersection(b, Vector3D.of(-1, 0, 0), Vector3D.of(2, 1, 1),
312 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
313 checkIntersection(b, Vector3D.of(0, 0, 0), Vector3D.of(2, 1, 1),
314 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
315 checkIntersection(b, Vector3D.of(0.5, 0, 0), Vector3D.of(2, 1, 1),
316 Vector3D.of(0.5, 0, 0), Vector3D.of(1, 1, 1));
317 checkIntersection(b, Vector3D.of(1, 0, 0), Vector3D.of(2, 1, 1),
318 Vector3D.of(1, 0, 0), Vector3D.of(1, 1, 1));
319 Assertions.assertNull(b.intersection(Bounds3D.from(Vector3D.of(2, 0, 0), Vector3D.of(3, 1, 1))));
320
321
322 Assertions.assertNull(b.intersection(Bounds3D.from(Vector3D.of(0, -2, 0), Vector3D.of(1, -1, 1))));
323 checkIntersection(b, Vector3D.of(0, -1, 0), Vector3D.of(1, 0, 1),
324 Vector3D.of(0, 0, 0), Vector3D.of(1, 0, 1));
325 checkIntersection(b, Vector3D.of(0, -1, 0), Vector3D.of(1, 0.5, 1),
326 Vector3D.of(0, 0, 0), Vector3D.of(1, 0.5, 1));
327 checkIntersection(b, Vector3D.of(0, -1, 0), Vector3D.of(1, 1, 1),
328 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
329 checkIntersection(b, Vector3D.of(0, -1, 0), Vector3D.of(1, 2, 1),
330 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
331 checkIntersection(b, Vector3D.of(0, 0, 0), Vector3D.of(1, 2, 1),
332 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
333 checkIntersection(b, Vector3D.of(0, 0.5, 0), Vector3D.of(1, 2, 1),
334 Vector3D.of(0, 0.5, 0), Vector3D.of(1, 1, 1));
335 checkIntersection(b, Vector3D.of(0, 1, 0), Vector3D.of(1, 2, 1),
336 Vector3D.of(0, 1, 0), Vector3D.of(1, 1, 1));
337 Assertions.assertNull(b.intersection(Bounds3D.from(Vector3D.of(0, 2, 0), Vector3D.of(1, 3, 1))));
338
339
340 Assertions.assertNull(b.intersection(Bounds3D.from(Vector3D.of(0, 0, -2), Vector3D.of(1, 1, -1))));
341 checkIntersection(b, Vector3D.of(0, 0, -1), Vector3D.of(1, 1, 0),
342 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 0));
343 checkIntersection(b, Vector3D.of(0, 0, -1), Vector3D.of(1, 1, 0.5),
344 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 0.5));
345 checkIntersection(b, Vector3D.of(0, 0, -1), Vector3D.of(1, 1, 1),
346 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
347 checkIntersection(b, Vector3D.of(0, 0, -1), Vector3D.of(1, 1, 2),
348 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
349 checkIntersection(b, Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 2),
350 Vector3D.of(0, 0, 0), Vector3D.of(1, 1, 1));
351 checkIntersection(b, Vector3D.of(0, 0, 0.5), Vector3D.of(1, 1, 2),
352 Vector3D.of(0, 0, 0.5), Vector3D.of(1, 1, 1));
353 checkIntersection(b, Vector3D.of(0, 0, 1), Vector3D.of(1, 1, 2),
354 Vector3D.of(0, 0, 1), Vector3D.of(1, 1, 1));
355 Assertions.assertNull(b.intersection(Bounds3D.from(Vector3D.of(0, 0, 2), Vector3D.of(1, 1, 3))));
356 }
357
358 private void checkIntersection(final Bounds3D b, final Vector3D a1, final Vector3D a2, final Vector3D r1, final Vector3D r2) {
359 final Bounds3D a = Bounds3D.from(a1, a2);
360 final Bounds3D result = b.intersection(a);
361
362 checkBounds(result, r1, r2);
363 }
364
365 @Test
366 void toRegion() {
367
368 final Bounds3D b = Bounds3D.from(
369 Vector3D.of(0, 4, 8),
370 Vector3D.of(2, 6, 10));
371
372
373 final Parallelepiped p = b.toRegion(TEST_PRECISION);
374
375
376 Assertions.assertEquals(8, p.getSize(), TEST_EPS);
377 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 5, 9), p.getCentroid(), TEST_EPS);
378 }
379
380 @Test
381 void toRegion_boundingBoxTooSmall() {
382
383 Assertions.assertThrows(IllegalArgumentException.class, () -> Bounds3D.from(Vector3D.ZERO, Vector3D.of(1e-12, 1e-12, 1e-12))
384 .toRegion(TEST_PRECISION));
385 }
386
387 @Test
388 void testEq() {
389
390 final Precision.DoubleEquivalence low = Precision.doubleEquivalenceOfEpsilon(1e-2);
391 final Precision.DoubleEquivalence high = Precision.doubleEquivalenceOfEpsilon(1e-10);
392
393 final Bounds3D b1 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
394
395 final Bounds3D b2 = Bounds3D.from(Vector3D.of(1.1, 1, 1), Vector3D.of(2, 2, 2));
396 final Bounds3D b3 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(1.9, 2, 2));
397
398 final Bounds3D b4 = Bounds3D.from(Vector3D.of(1.001, 1.001, 1.001), Vector3D.of(2.001, 2.001, 2.001));
399
400
401 Assertions.assertTrue(b1.eq(b1, low));
402
403 Assertions.assertFalse(b1.eq(b2, low));
404 Assertions.assertFalse(b1.eq(b3, low));
405
406 Assertions.assertTrue(b1.eq(b4, low));
407 Assertions.assertTrue(b4.eq(b1, low));
408
409 Assertions.assertFalse(b1.eq(b4, high));
410 Assertions.assertFalse(b4.eq(b1, high));
411 }
412
413 @Test
414 void testHashCode() {
415
416 final Bounds3D b1 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
417
418 final Bounds3D b2 = Bounds3D.from(Vector3D.of(-2, 1, 1), Vector3D.of(2, 2, 2));
419 final Bounds3D b3 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(3, 2, 2));
420 final Bounds3D b4 = Bounds3D.from(Vector3D.of(1 + 1e-15, 1, 1), Vector3D.of(2, 2, 2));
421 final Bounds3D b5 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2 + 1e-15, 2, 2));
422
423 final Bounds3D b6 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
424
425
426 final int hash = b1.hashCode();
427
428
429 Assertions.assertEquals(hash, b1.hashCode());
430
431 Assertions.assertNotEquals(hash, b2.hashCode());
432 Assertions.assertNotEquals(hash, b3.hashCode());
433 Assertions.assertNotEquals(hash, b4.hashCode());
434 Assertions.assertNotEquals(hash, b5.hashCode());
435
436 Assertions.assertEquals(hash, b6.hashCode());
437 }
438
439 @Test
440 void testEquals() {
441
442 final Bounds3D b1 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
443
444 final Bounds3D b2 = Bounds3D.from(Vector3D.of(-1, 1, 1), Vector3D.of(2, 2, 2));
445 final Bounds3D b3 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(3, 2, 2));
446 final Bounds3D b4 = Bounds3D.from(Vector3D.of(1 + 1e-15, 1, 1), Vector3D.of(2, 2, 2));
447 final Bounds3D b5 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2 + 1e-15, 2, 2));
448
449 final Bounds3D b6 = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
450
451
452 GeometryTestUtils.assertSimpleEqualsCases(b1);
453
454 Assertions.assertNotEquals(b1, b2);
455 Assertions.assertNotEquals(b1, b3);
456 Assertions.assertNotEquals(b1, b4);
457 Assertions.assertNotEquals(b1, b5);
458
459 Assertions.assertEquals(b1, b6);
460 }
461
462 @Test
463 void testToString() {
464
465 final Bounds3D b = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
466
467
468 final String str = b.toString();
469
470
471 GeometryTestUtils.assertContains("Bounds3D[min= (1", str);
472 GeometryTestUtils.assertContains(", max= (2", str);
473 }
474
475 @Test
476 void testBuilder_addMethods() {
477
478 final Vector3D p1 = Vector3D.of(1, 10, 11);
479 final Vector3D p2 = Vector3D.of(2, 9, 12);
480 final Vector3D p3 = Vector3D.of(3, 8, 13);
481 final Vector3D p4 = Vector3D.of(4, 7, 14);
482 final Vector3D p5 = Vector3D.of(5, 6, 15);
483
484
485 final Bounds3D b = Bounds3D.builder()
486 .add(p1)
487 .addAll(Arrays.asList(p2, p3))
488 .add(Bounds3D.from(p4, p5))
489 .build();
490
491
492 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 6, 11), b.getMin(), TEST_EPS);
493 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(5, 10, 15), b.getMax(), TEST_EPS);
494 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(3, 8, 13), b.getCentroid(), TEST_EPS);
495 }
496
497 @Test
498 void testBuilder_hasBounds() {
499
500 Assertions.assertFalse(Bounds3D.builder().hasBounds());
501
502 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(Double.NaN, 1, 1)).hasBounds());
503 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(1, Double.NaN, 1)).hasBounds());
504 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(1, 1, Double.NaN)).hasBounds());
505
506 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(Double.POSITIVE_INFINITY, 1, 1)).hasBounds());
507 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(1, Double.POSITIVE_INFINITY, 1)).hasBounds());
508 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(1, 1, Double.POSITIVE_INFINITY)).hasBounds());
509
510 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(Double.NEGATIVE_INFINITY, 1, 1)).hasBounds());
511 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(1, Double.NEGATIVE_INFINITY, 1)).hasBounds());
512 Assertions.assertFalse(Bounds3D.builder().add(Vector3D.of(1, 1, Double.NEGATIVE_INFINITY)).hasBounds());
513
514 Assertions.assertTrue(Bounds3D.builder().add(Vector3D.ZERO).hasBounds());
515 }
516
517 private static void checkBounds(final Bounds3D b, final Vector3D min, final Vector3D max) {
518 EuclideanTestUtils.assertCoordinatesEqual(min, b.getMin(), TEST_EPS);
519 EuclideanTestUtils.assertCoordinatesEqual(max, b.getMax(), TEST_EPS);
520 }
521
522 private static void assertContainsStrict(final Bounds3D bounds, final boolean contains, final Vector3D... pts) {
523 for (final Vector3D pt : pts) {
524 Assertions.assertEquals(contains, bounds.contains(pt), "Unexpected location for point " + pt);
525 }
526 }
527
528 private static void assertContainsWithPrecision(final Bounds3D bounds, final boolean contains, final Vector3D... pts) {
529 for (final Vector3D pt : pts) {
530 Assertions.assertEquals(contains, bounds.contains(pt, TEST_PRECISION), "Unexpected location for point " + pt);
531 }
532 }
533 }