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