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 }