View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  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.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.twod.shape.Parallelogram;
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 Bounds2DTest {
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          // arrange
48          final Vector2D p1 = Vector2D.of(-1, 2);
49  
50          // act
51          final Bounds2D b = Bounds2D.from(p1);
52  
53          // assert
54          EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMin(), TEST_EPS);
55          EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMax(), TEST_EPS);
56          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, b.getDiagonal(), TEST_EPS);
57          EuclideanTestUtils.assertCoordinatesEqual(p1, b.getCentroid(), TEST_EPS);
58      }
59  
60      @Test
61      void testFrom_varargs_multiplePoints() {
62          // arrange
63          final Vector2D p1 = Vector2D.of(1, 6);
64          final Vector2D p2 = Vector2D.of(0, 5);
65          final Vector2D p3 = Vector2D.of(3, 6);
66  
67          // act
68          final Bounds2D b = Bounds2D.from(p1, p2, p3);
69  
70          // assert
71          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 5), b.getMin(), TEST_EPS);
72          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 6), b.getMax(), TEST_EPS);
73          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 1), b.getDiagonal(), TEST_EPS);
74          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1.5, 5.5), b.getCentroid(), TEST_EPS);
75      }
76  
77      @Test
78      void testFrom_iterable_singlePoint() {
79          // arrange
80          final Vector2D p1 = Vector2D.of(-1, 2);
81  
82          // act
83          final Bounds2D b = Bounds2D.from(Collections.singletonList(p1));
84  
85          // assert
86          EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMin(), TEST_EPS);
87          EuclideanTestUtils.assertCoordinatesEqual(p1, b.getMax(), TEST_EPS);
88          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, b.getDiagonal(), TEST_EPS);
89          EuclideanTestUtils.assertCoordinatesEqual(p1, b.getCentroid(), TEST_EPS);
90      }
91  
92      @Test
93      void testFrom_iterable_multiplePoints() {
94          // arrange
95          final Vector2D p1 = Vector2D.of(1, 6);
96          final Vector2D p2 = Vector2D.of(2, 5);
97          final Vector2D p3 = Vector2D.of(3, 4);
98  
99          // act
100         final Bounds2D b = Bounds2D.from(Arrays.asList(p1, p2, p3));
101 
102         // assert
103         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 4), b.getMin(), TEST_EPS);
104         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 6), b.getMax(), TEST_EPS);
105         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 2), b.getDiagonal(), TEST_EPS);
106         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 5), b.getCentroid(), TEST_EPS);
107     }
108 
109     @Test
110     void testFrom_iterable_noPoints() {
111         // act/assert
112         GeometryTestUtils.assertThrowsWithMessage(() -> {
113             Bounds2D.from(new ArrayList<>());
114         }, IllegalStateException.class, NO_POINTS_MESSAGE);
115     }
116 
117     @Test
118     void testFrom_invalidBounds() {
119         // arrange
120         final Vector2D good = Vector2D.of(1, 1);
121 
122         final Vector2D nan = Vector2D.of(Double.NaN, 1);
123         final Vector2D posInf = Vector2D.of(1, Double.POSITIVE_INFINITY);
124         final Vector2D negInf = Vector2D.of(1, Double.NEGATIVE_INFINITY);
125 
126         // act/assert
127         GeometryTestUtils.assertThrowsWithMessage(() -> {
128             Bounds2D.from(Vector2D.NaN);
129         }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
130 
131         GeometryTestUtils.assertThrowsWithMessage(() -> {
132             Bounds2D.from(Vector2D.POSITIVE_INFINITY);
133         }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
134 
135         GeometryTestUtils.assertThrowsWithMessage(() -> {
136             Bounds2D.from(Vector2D.NEGATIVE_INFINITY);
137         }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
138 
139         GeometryTestUtils.assertThrowsWithMessage(() -> {
140             Bounds2D.from(good, nan);
141         }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
142 
143         GeometryTestUtils.assertThrowsWithMessage(() -> {
144             Bounds2D.from(posInf, good);
145         }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
146 
147         GeometryTestUtils.assertThrowsWithMessage(() -> {
148             Bounds2D.from(good, negInf, good);
149         }, IllegalStateException.class, INVALID_BOUNDS_PATTERN);
150     }
151 
152     @Test
153     void testHasSize() {
154         // arrange
155         final Precision.DoubleEquivalence low = Precision.doubleEquivalenceOfEpsilon(1e-2);
156         final Precision.DoubleEquivalence high = Precision.doubleEquivalenceOfEpsilon(1e-10);
157 
158         final Vector2D p1 = Vector2D.ZERO;
159 
160         final Vector2D p2 = Vector2D.of(1e-5, 1);
161         final Vector2D p3 = Vector2D.of(1, 1e-5);
162 
163         final Vector2D p4 = Vector2D.of(1, 1);
164 
165         // act/assert
166         Assertions.assertFalse(Bounds2D.from(p1).hasSize(high));
167         Assertions.assertFalse(Bounds2D.from(p1).hasSize(low));
168 
169         Assertions.assertTrue(Bounds2D.from(p1, p2).hasSize(high));
170         Assertions.assertFalse(Bounds2D.from(p1, p2).hasSize(low));
171 
172         Assertions.assertTrue(Bounds2D.from(p1, p3).hasSize(high));
173         Assertions.assertFalse(Bounds2D.from(p1, p3).hasSize(low));
174 
175         Assertions.assertTrue(Bounds2D.from(p1, p4).hasSize(high));
176         Assertions.assertTrue(Bounds2D.from(p1, p4).hasSize(low));
177     }
178 
179     @Test
180     void testContains_strict() {
181         // arrange
182         final Bounds2D b = Bounds2D.from(
183                 Vector2D.of(0, 4),
184                 Vector2D.of(2, 6));
185 
186         // act/assert
187         assertContainsStrict(b, true,
188                 b.getCentroid(),
189                 Vector2D.of(0, 4), Vector2D.of(2, 6),
190                 Vector2D.of(1, 5),
191                 Vector2D.of(0, 5), Vector2D.of(2, 5),
192                 Vector2D.of(1, 4), Vector2D.of(1, 6));
193 
194         assertContainsStrict(b, false,
195                 Vector2D.ZERO,
196                 Vector2D.of(-1, 5), Vector2D.of(3, 5),
197                 Vector2D.of(1, 3), Vector2D.of(1, 7),
198                 Vector2D.of(-1e-15, 4), Vector2D.of(2, 6 + 1e-15));
199     }
200 
201     @Test
202     void testContains_precision() {
203         // arrange
204         final Bounds2D b = Bounds2D.from(
205                 Vector2D.of(0, 4),
206                 Vector2D.of(2, 6));
207 
208         // act/assert
209         assertContainsWithPrecision(b, true,
210                 b.getCentroid(),
211                 Vector2D.of(1, 5), Vector2D.of(0, 4), Vector2D.of(2, 6),
212                 Vector2D.of(0, 5), Vector2D.of(2, 5),
213                 Vector2D.of(1, 4), Vector2D.of(1, 6),
214                 Vector2D.of(-1e-15, 4), Vector2D.of(2, 6 + 1e-15));
215 
216         assertContainsWithPrecision(b, false,
217                 Vector2D.ZERO,
218                 Vector2D.of(-1, 5), Vector2D.of(3, 5),
219                 Vector2D.of(1, 3), Vector2D.of(1, 7));
220     }
221 
222     @Test
223     void testIntersects() {
224         // arrange
225         final Bounds2D b = Bounds2D.from(Vector2D.ZERO, Vector2D.of(1, 1));
226 
227         // act/assert
228         checkIntersects(b, Vector2D::getX, (v, x) -> Vector2D.of(x, v.getY()));
229         checkIntersects(b, Vector2D::getY, (v, y) -> Vector2D.of(v.getX(), y));
230     }
231 
232     private void checkIntersects(final Bounds2D b, final ToDoubleFunction<? super Vector2D> getter,
233                                  final BiFunction<? super Vector2D, Double, ? extends Vector2D> setter) {
234 
235         final Vector2D min = b.getMin();
236         final Vector2D max = b.getMax();
237 
238         final double minValue = getter.applyAsDouble(min);
239         final double maxValue = getter.applyAsDouble(max);
240         final double midValue = (0.5 * (maxValue - minValue)) + minValue;
241 
242         // check all possible interval relationships
243 
244         // start below minValue
245         Assertions.assertFalse(b.intersects(Bounds2D.from(
246                 setter.apply(min, minValue - 2), setter.apply(max, minValue - 1))));
247 
248         Assertions.assertTrue(b.intersects(Bounds2D.from(
249                 setter.apply(min, minValue - 2), setter.apply(max, minValue))));
250         Assertions.assertTrue(b.intersects(Bounds2D.from(
251                 setter.apply(min, minValue - 2), setter.apply(max, midValue))));
252         Assertions.assertTrue(b.intersects(Bounds2D.from(
253                 setter.apply(min, minValue - 2), setter.apply(max, maxValue))));
254         Assertions.assertTrue(b.intersects(Bounds2D.from(
255                 setter.apply(min, minValue - 2), setter.apply(max, maxValue + 1))));
256 
257         // start on minValue
258         Assertions.assertTrue(b.intersects(Bounds2D.from(
259                 setter.apply(min, minValue), setter.apply(max, minValue))));
260         Assertions.assertTrue(b.intersects(Bounds2D.from(
261                 setter.apply(min, minValue), setter.apply(max, midValue))));
262         Assertions.assertTrue(b.intersects(Bounds2D.from(
263                 setter.apply(min, minValue), setter.apply(max, maxValue))));
264         Assertions.assertTrue(b.intersects(Bounds2D.from(
265                 setter.apply(min, minValue), setter.apply(max, maxValue + 1))));
266 
267         // start on midValue
268         Assertions.assertTrue(b.intersects(Bounds2D.from(
269                 setter.apply(min, midValue), setter.apply(max, midValue))));
270         Assertions.assertTrue(b.intersects(Bounds2D.from(
271                 setter.apply(min, midValue), setter.apply(max, maxValue))));
272         Assertions.assertTrue(b.intersects(Bounds2D.from(
273                 setter.apply(min, midValue), setter.apply(max, maxValue + 1))));
274 
275         // start on maxValue
276         Assertions.assertTrue(b.intersects(Bounds2D.from(
277                 setter.apply(min, maxValue), setter.apply(max, maxValue))));
278         Assertions.assertTrue(b.intersects(Bounds2D.from(
279                 setter.apply(min, maxValue), setter.apply(max, maxValue + 1))));
280 
281         // start above maxValue
282         Assertions.assertFalse(b.intersects(Bounds2D.from(
283                 setter.apply(min, maxValue + 1), setter.apply(max, maxValue + 2))));
284     }
285 
286     @Test
287     void testIntersection() {
288         // -- arrange
289         final Bounds2D b = Bounds2D.from(Vector2D.ZERO, Vector2D.of(1, 1));
290 
291         // -- act/assert
292 
293         // move along x-axis
294         Assertions.assertNull(b.intersection(Bounds2D.from(Vector2D.of(-2, 0), Vector2D.of(-1, 1))));
295         checkIntersection(b, Vector2D.of(-1, 0), Vector2D.of(0, 1),
296                 Vector2D.of(0, 0), Vector2D.of(0, 1));
297         checkIntersection(b, Vector2D.of(-1, 0), Vector2D.of(0.5, 1),
298                 Vector2D.of(0, 0), Vector2D.of(0.5, 1));
299         checkIntersection(b, Vector2D.of(-1, 0), Vector2D.of(1, 1),
300                 Vector2D.of(0, 0), Vector2D.of(1, 1));
301         checkIntersection(b, Vector2D.of(-1, 0), Vector2D.of(2, 1),
302                 Vector2D.of(0, 0), Vector2D.of(1, 1));
303         checkIntersection(b, Vector2D.of(0, 0), Vector2D.of(2, 1),
304                 Vector2D.of(0, 0), Vector2D.of(1, 1));
305         checkIntersection(b, Vector2D.of(0.5, 0), Vector2D.of(2, 1),
306                 Vector2D.of(0.5, 0), Vector2D.of(1, 1));
307         checkIntersection(b, Vector2D.of(1, 0), Vector2D.of(2, 1),
308                 Vector2D.of(1, 0), Vector2D.of(1, 1));
309         Assertions.assertNull(b.intersection(Bounds2D.from(Vector2D.of(2, 0), Vector2D.of(3, 1))));
310 
311         // move along y-axis
312         Assertions.assertNull(b.intersection(Bounds2D.from(Vector2D.of(0, -2), Vector2D.of(1, -1))));
313         checkIntersection(b, Vector2D.of(0, -1), Vector2D.of(1, 0),
314                 Vector2D.of(0, 0), Vector2D.of(1, 0));
315         checkIntersection(b, Vector2D.of(0, -1), Vector2D.of(1, 0.5),
316                 Vector2D.of(0, 0), Vector2D.of(1, 0.5));
317         checkIntersection(b, Vector2D.of(0, -1), Vector2D.of(1, 1),
318                 Vector2D.of(0, 0), Vector2D.of(1, 1));
319         checkIntersection(b, Vector2D.of(0, -1), Vector2D.of(1, 2),
320                 Vector2D.of(0, 0), Vector2D.of(1, 1));
321         checkIntersection(b, Vector2D.of(0, 0), Vector2D.of(1, 2),
322                 Vector2D.of(0, 0), Vector2D.of(1, 1));
323         checkIntersection(b, Vector2D.of(0, 0.5), Vector2D.of(1, 2),
324                 Vector2D.of(0, 0.5), Vector2D.of(1, 1));
325         checkIntersection(b, Vector2D.of(0, 1), Vector2D.of(1, 2),
326                 Vector2D.of(0, 1), Vector2D.of(1, 1));
327         Assertions.assertNull(b.intersection(Bounds2D.from(Vector2D.of(0, 2), Vector2D.of(1, 3))));
328     }
329 
330     private void checkIntersection(final Bounds2D b, final Vector2D a1, final Vector2D a2, final Vector2D r1, final Vector2D r2) {
331         final Bounds2D a = Bounds2D.from(a1, a2);
332         final Bounds2D result = b.intersection(a);
333 
334         checkBounds(result, r1, r2);
335     }
336 
337     @Test
338     void toRegion() {
339         // arrange
340         final Bounds2D b = Bounds2D.from(
341                 Vector2D.of(0, 4),
342                 Vector2D.of(2, 6));
343 
344         // act
345         final Parallelogram p = b.toRegion(TEST_PRECISION);
346 
347         // assert
348         Assertions.assertEquals(4, p.getSize(), TEST_EPS);
349         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 5), p.getCentroid(), TEST_EPS);
350     }
351 
352     @Test
353     void toRegion_boundingBoxTooSmall() {
354         // act/assert
355         Assertions.assertThrows(IllegalArgumentException.class, () -> Bounds2D.from(Vector2D.ZERO, Vector2D.of(1e-12, 1e-12)).toRegion(TEST_PRECISION));
356     }
357 
358     @Test
359     void testEq() {
360         // arrange
361         final Precision.DoubleEquivalence low = Precision.doubleEquivalenceOfEpsilon(1e-2);
362         final Precision.DoubleEquivalence high = Precision.doubleEquivalenceOfEpsilon(1e-10);
363 
364         final Bounds2D b1 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2, 2));
365 
366         final Bounds2D b2 = Bounds2D.from(Vector2D.of(1.1, 1), Vector2D.of(2, 2));
367         final Bounds2D b3 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(1.9, 2));
368 
369         final Bounds2D b4 = Bounds2D.from(Vector2D.of(1.001, 1.001), Vector2D.of(2.001, 2.001));
370 
371         // act/assert
372         Assertions.assertTrue(b1.eq(b1, low));
373 
374         Assertions.assertFalse(b1.eq(b2, low));
375         Assertions.assertFalse(b1.eq(b3, low));
376 
377         Assertions.assertTrue(b1.eq(b4, low));
378         Assertions.assertTrue(b4.eq(b1, low));
379 
380         Assertions.assertFalse(b1.eq(b4, high));
381         Assertions.assertFalse(b4.eq(b1, high));
382     }
383 
384     @Test
385     void testHashCode() {
386         // arrange
387         final Bounds2D b1 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2, 2));
388 
389         final Bounds2D b2 = Bounds2D.from(Vector2D.of(-2, 1), Vector2D.of(2, 2));
390         final Bounds2D b3 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(3, 2));
391         final Bounds2D b4 = Bounds2D.from(Vector2D.of(1 + 1e-15, 1), Vector2D.of(2, 2));
392         final Bounds2D b5 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2 + 1e-15, 2));
393 
394         final Bounds2D b6 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2, 2));
395 
396         // act
397         final int hash = b1.hashCode();
398 
399         // assert
400         Assertions.assertEquals(hash, b1.hashCode());
401 
402         Assertions.assertNotEquals(hash, b2.hashCode());
403         Assertions.assertNotEquals(hash, b3.hashCode());
404         Assertions.assertNotEquals(hash, b4.hashCode());
405         Assertions.assertNotEquals(hash, b5.hashCode());
406 
407         Assertions.assertEquals(hash, b6.hashCode());
408     }
409 
410     @Test
411     void testEquals() {
412         // arrange
413         final Bounds2D b1 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2, 2));
414 
415         final Bounds2D b2 = Bounds2D.from(Vector2D.of(-1, 1), Vector2D.of(2, 2));
416         final Bounds2D b3 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(3, 2));
417         final Bounds2D b4 = Bounds2D.from(Vector2D.of(1 + 1e-15, 1), Vector2D.of(2, 2));
418         final Bounds2D b5 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2 + 1e-15, 2));
419 
420         final Bounds2D b6 = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2, 2));
421 
422         // act/assert
423         GeometryTestUtils.assertSimpleEqualsCases(b1);
424 
425         Assertions.assertNotEquals(b1, b2);
426         Assertions.assertNotEquals(b1, b3);
427         Assertions.assertNotEquals(b1, b4);
428         Assertions.assertNotEquals(b1, b5);
429 
430         Assertions.assertEquals(b1, b6);
431     }
432 
433     @Test
434     void testToString() {
435         // arrange
436         final Bounds2D b = Bounds2D.from(Vector2D.of(1, 1), Vector2D.of(2, 2));
437 
438         // act
439         final String str = b.toString();
440 
441         // assert
442         GeometryTestUtils.assertContains("Bounds2D[min= (1", str);
443         GeometryTestUtils.assertContains(", max= (2", str);
444     }
445 
446     @Test
447     void testBuilder_addMethods() {
448         // arrange
449         final Vector2D p1 = Vector2D.of(1, 10);
450         final Vector2D p2 = Vector2D.of(2, 9);
451         final Vector2D p3 = Vector2D.of(3, 8);
452         final Vector2D p4 = Vector2D.of(4, 7);
453         final Vector2D p5 = Vector2D.of(5, 6);
454 
455         // act
456         final Bounds2D b = Bounds2D.builder()
457                 .add(p1)
458                 .addAll(Arrays.asList(p2, p3))
459                 .add(Bounds2D.from(p4, p5))
460                 .build();
461 
462         // assert
463         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 6), b.getMin(), TEST_EPS);
464         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(5, 10), b.getMax(), TEST_EPS);
465         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 8), b.getCentroid(), TEST_EPS);
466     }
467 
468     @Test
469     void testBuilder_hasBounds() {
470         // act/assert
471         Assertions.assertFalse(Bounds2D.builder().hasBounds());
472 
473         Assertions.assertFalse(Bounds2D.builder().add(Vector2D.of(Double.NaN, 1)).hasBounds());
474         Assertions.assertFalse(Bounds2D.builder().add(Vector2D.of(1, Double.NaN)).hasBounds());
475 
476         Assertions.assertFalse(Bounds2D.builder().add(Vector2D.of(Double.POSITIVE_INFINITY, 1)).hasBounds());
477         Assertions.assertFalse(Bounds2D.builder().add(Vector2D.of(1, Double.POSITIVE_INFINITY)).hasBounds());
478 
479         Assertions.assertFalse(Bounds2D.builder().add(Vector2D.of(Double.NEGATIVE_INFINITY, 1)).hasBounds());
480         Assertions.assertFalse(Bounds2D.builder().add(Vector2D.of(1, Double.NEGATIVE_INFINITY)).hasBounds());
481 
482         Assertions.assertTrue(Bounds2D.builder().add(Vector2D.ZERO).hasBounds());
483     }
484 
485     private static void checkBounds(final Bounds2D b, final Vector2D min, final Vector2D max) {
486         EuclideanTestUtils.assertCoordinatesEqual(min, b.getMin(), TEST_EPS);
487         EuclideanTestUtils.assertCoordinatesEqual(max, b.getMax(), TEST_EPS);
488     }
489 
490     private static void assertContainsStrict(final Bounds2D bounds, final boolean contains, final Vector2D... pts) {
491         for (final Vector2D pt : pts) {
492             Assertions.assertEquals(contains, bounds.contains(pt), "Unexpected location for point " + pt);
493         }
494     }
495 
496     private static void assertContainsWithPrecision(final Bounds2D bounds, final boolean contains, final Vector2D... pts) {
497         for (final Vector2D pt : pts) {
498             Assertions.assertEquals(contains, bounds.contains(pt, TEST_PRECISION), "Unexpected location for point " + pt);
499         }
500     }
501 }