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.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          // arrange
    48          final Vector3D p1 = Vector3D.of(-1, 2, -3);
    49
    50          // act
    51          final Bounds3D b = Bounds3D.from(p1);
    52
    53          // assert
    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          // arrange
    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          // act
    68          final Bounds3D b = Bounds3D.from(p1, p2, p3);
    69
    70          // assert
    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          // arrange
    80          final Vector3D p1 = Vector3D.of(-1, 2, -3);
    81
    82          // act
    83          final Bounds3D b = Bounds3D.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(Vector3D.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 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          // act
    100         final Bounds3D b = Bounds3D.from(Arrays.asList(p1, p2, p3));
    101
    102         // assert
    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         // act/assert
    112         GeometryTestUtils.assertThrowsWithMessage(() -> {
    113             Bounds3D.from(new ArrayList<>());
    114         }, IllegalStateException.class, NO_POINTS_MESSAGE);
    115     }
    116
    117     @Test
    118     void testFrom_invalidBounds() {
    119         // arrange
    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         // act/assert
    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         // arrange
    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         // act/assert
    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         // arrange
    186         final Bounds3D b = Bounds3D.from(
    187                 Vector3D.of(0, 4, 8),
    188                 Vector3D.of(2, 6, 10));
    189
    190         // act/assert
    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         // arrange
    210         final Bounds3D b = Bounds3D.from(
    211                 Vector3D.of(0, 4, 8),
    212                 Vector3D.of(2, 6, 10));
    213
    214         // act/assert
    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         // arrange
    234         final Bounds3D b = Bounds3D.from(Vector3D.ZERO, Vector3D.of(1, 1, 1));
    235
    236         // act/assert
    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         // check all possible interval relationships
    253
    254         // start below minValue
    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         // start on minValue
    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         // start on midValue
    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         // start on maxValue
    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         // start above maxValue
    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         // -- arrange
    299         final Bounds3D b = Bounds3D.from(Vector3D.ZERO, Vector3D.of(1, 1, 1));
    300
    301         // -- act/assert
    302
    303         // move along x-axis
    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         // move along y-axis
    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         // move along z-axis
    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         // arrange
    368         final Bounds3D b = Bounds3D.from(
    369                 Vector3D.of(0, 4, 8),
    370                 Vector3D.of(2, 6, 10));
    371
    372         // act
    373         final Parallelepiped p = b.toRegion(TEST_PRECISION);
    374
    375         // assert
    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         // act/assert
    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         // arrange
    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         // act/assert
    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         // arrange
    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         // act
    426         final int hash = b1.hashCode();
    427
    428         // assert
    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         // arrange
    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         // act/assert
    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         // arrange
    465         final Bounds3D b = Bounds3D.from(Vector3D.of(1, 1, 1), Vector3D.of(2, 2, 2));
    466
    467         // act
    468         final String str = b.toString();
    469
    470         // assert
    471         GeometryTestUtils.assertContains("Bounds3D[min= (1", str);
    472         GeometryTestUtils.assertContains(", max= (2", str);
    473     }
    474
    475     @Test
    476     void testBuilder_addMethods() {
    477         // arrange
    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         // act
    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         // assert
    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         // act/assert
    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 }