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.shape;
    18
    19  import java.util.Arrays;
    20  import java.util.Comparator;
    21  import java.util.List;
    22  import java.util.Set;
    23  import java.util.TreeSet;
    24
    25  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    26  import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
    27  import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
    28  import org.apache.commons.geometry.euclidean.threed.RegionBSPTree3D;
    29  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    30  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
    31  import org.apache.commons.numbers.angle.Angle;
    32  import org.apache.commons.numbers.core.Precision;
    33  import org.junit.jupiter.api.Assertions;
    34  import org.junit.jupiter.api.Test;
    35
    36  class ParallelepipedTest {
    37
    38      private static final double TEST_EPS = 1e-10;
    39
    40      private static final Precision.DoubleEquivalence TEST_PRECISION =
    41              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    42
    43      private static final Comparator<Vector3D> VERTEX_COMPARATOR = (a, b) -> {
    44          int cmp = TEST_PRECISION.compare(a.getX(), b.getX());
    45          if (cmp == 0) {
    46              cmp = TEST_PRECISION.compare(a.getY(), b.getY());
    47              if (cmp == 0) {
    48                  cmp = TEST_PRECISION.compare(a.getZ(), b.getZ());
    49              }
    50          }
    51          return cmp;
    52      };
    53
    54      @Test
    55      void testUnitCube() {
    56          // act
    57          final Parallelepiped p = Parallelepiped.unitCube(TEST_PRECISION);
    58
    59          // assert
    60          Assertions.assertEquals(1, p.getSize(), TEST_EPS);
    61          Assertions.assertEquals(6, p.getBoundarySize(), TEST_EPS);
    62          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.getCentroid(), TEST_EPS);
    63
    64          final List<PlaneConvexSubset> boundaries = p.getBoundaries();
    65          Assertions.assertEquals(6, boundaries.size());
    66
    67          assertVertices(p,
    68              Vector3D.of(-0.5, -0.5, -0.5),
    69              Vector3D.of(0.5, -0.5, -0.5),
    70              Vector3D.of(0.5, 0.5, -0.5),
    71              Vector3D.of(-0.5, 0.5, -0.5),
    72
    73              Vector3D.of(-0.5, -0.5, 0.5),
    74              Vector3D.of(0.5, -0.5, 0.5),
    75              Vector3D.of(0.5, 0.5, 0.5),
    76              Vector3D.of(-0.5, 0.5, 0.5)
    77          );
    78      }
    79
    80      @Test
    81      void testFromTransformedUnitCube() {
    82          // arrange
    83          final AffineTransformMatrix3D t = AffineTransformMatrix3D.createTranslation(Vector3D.of(1, 0, 2))
    84                  .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Z, Math.PI * 0.25))
    85                  .scale(Vector3D.of(2, 1, 1));
    86
    87          // act
    88          final Parallelepiped p = Parallelepiped.fromTransformedUnitCube(t, TEST_PRECISION);
    89
    90          // assert
    91          final double sqrt2 = Math.sqrt(2);
    92          final double invSqrt2 = 1 / sqrt2;
    93
    94          Assertions.assertEquals(2, p.getSize(), TEST_EPS);
    95          Assertions.assertEquals(4 + (4 * Math.sqrt(2.5)), p.getBoundarySize(), TEST_EPS);
    96          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2 * invSqrt2, invSqrt2, 2),
    97                  p.getCentroid(), TEST_EPS);
    98
    99          assertVertices(p,
    100             Vector3D.of(0, invSqrt2, 1.5),
    101             Vector3D.of(2 * invSqrt2, 0, 1.5),
    102             Vector3D.of(2 * sqrt2, invSqrt2, 1.5),
    103             Vector3D.of(2 * invSqrt2, sqrt2, 1.5),
    104
    105             Vector3D.of(0, invSqrt2, 2.5),
    106             Vector3D.of(2 * invSqrt2, 0, 2.5),
    107             Vector3D.of(2 * sqrt2, invSqrt2, 2.5),
    108             Vector3D.of(2 * invSqrt2, sqrt2, 2.5)
    109         );
    110     }
    111
    112     @Test
    113     void testFromTransformedUnitCube_transformDoesNotPreserveOrientation() {
    114         // arrange
    115         final AffineTransformMatrix3D t = AffineTransformMatrix3D.createTranslation(Vector3D.of(1, 0, 2))
    116                 .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Z, Math.PI * 0.25))
    117                 .scale(Vector3D.of(2, 1, -1));
    118
    119         // act
    120         final Parallelepiped p = Parallelepiped.fromTransformedUnitCube(t, TEST_PRECISION);
    121
    122         // assert
    123         final double sqrt2 = Math.sqrt(2);
    124         final double invSqrt2 = 1 / sqrt2;
    125
    126         Assertions.assertEquals(2, p.getSize(), TEST_EPS);
    127         Assertions.assertEquals(4 + (4 * Math.sqrt(2.5)), p.getBoundarySize(), TEST_EPS);
    128         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2 * invSqrt2, invSqrt2, -2),
    129                 p.getCentroid(), TEST_EPS);
    130
    131         assertVertices(p,
    132             Vector3D.of(0, invSqrt2, -1.5),
    133             Vector3D.of(2 * invSqrt2, 0, -1.5),
    134             Vector3D.of(2 * sqrt2, invSqrt2, -1.5),
    135             Vector3D.of(2 * invSqrt2, sqrt2, -1.5),
    136
    137             Vector3D.of(0, invSqrt2, -2.5),
    138             Vector3D.of(2 * invSqrt2, 0, -2.5),
    139             Vector3D.of(2 * sqrt2, invSqrt2, -2.5),
    140             Vector3D.of(2 * invSqrt2, sqrt2, -2.5)
    141         );
    142     }
    143
    144     @Test
    145     void testFromTransformedUnitCube_zeroSizeRegion() {
    146         // act/assert
    147         Assertions.assertThrows(IllegalArgumentException.class, () -> Parallelepiped.fromTransformedUnitCube(AffineTransformMatrix3D.createScale(Vector3D.of(1e-16, 1, 1)),
    148                 TEST_PRECISION));
    149         Assertions.assertThrows(IllegalArgumentException.class, () -> Parallelepiped.fromTransformedUnitCube(AffineTransformMatrix3D.createScale(Vector3D.of(1, 1e-16, 1)),
    150                 TEST_PRECISION));
    151         Assertions.assertThrows(IllegalArgumentException.class, () -> Parallelepiped.fromTransformedUnitCube(AffineTransformMatrix3D.createScale(Vector3D.of(1, 1, 1e-16)),
    152                 TEST_PRECISION));
    153     }
    154
    155     @Test
    156     void testAxisAligned_minFirst() {
    157         // act
    158         final Parallelepiped p = Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(4, 5, 6), TEST_PRECISION);
    159
    160         // assert
    161         final List<PlaneConvexSubset> boundaries = p.getBoundaries();
    162         Assertions.assertEquals(6, boundaries.size());
    163
    164         assertVertices(p,
    165             Vector3D.of(1, 2, 3),
    166             Vector3D.of(4, 2, 3),
    167             Vector3D.of(4, 5, 3),
    168             Vector3D.of(1, 5, 3),
    169
    170             Vector3D.of(1, 2, 6),
    171             Vector3D.of(4, 2, 6),
    172             Vector3D.of(4, 5, 6),
    173             Vector3D.of(1, 5, 6)
    174         );
    175     }
    176
    177     @Test
    178     void testAxisAligned_maxFirst() {
    179         // act
    180         final Parallelepiped p = Parallelepiped.axisAligned(Vector3D.of(4, 5, 6), Vector3D.of(1, 2, 3), TEST_PRECISION);
    181
    182         // assert
    183         final List<PlaneConvexSubset> boundaries = p.getBoundaries();
    184         Assertions.assertEquals(6, boundaries.size());
    185
    186         assertVertices(p,
    187             Vector3D.of(1, 2, 3),
    188             Vector3D.of(4, 2, 3),
    189             Vector3D.of(4, 5, 3),
    190             Vector3D.of(1, 5, 3),
    191
    192             Vector3D.of(1, 2, 6),
    193             Vector3D.of(4, 2, 6),
    194             Vector3D.of(4, 5, 6),
    195             Vector3D.of(1, 5, 6)
    196         );
    197     }
    198
    199     @Test
    200     void testAxisAligned_illegalArgs() {
    201         // act/assert
    202         Assertions.assertThrows(IllegalArgumentException.class, () -> Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(1, 5, 6), TEST_PRECISION));
    203         Assertions.assertThrows(IllegalArgumentException.class, () -> Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(4, 2, 6), TEST_PRECISION));
    204         Assertions.assertThrows(IllegalArgumentException.class, () -> Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(1, 5, 3), TEST_PRECISION));
    205     }
    206
    207     @Test
    208     void testBuilder_defaultValues() {
    209         // arrange
    210         final Parallelepiped.Builder builder = Parallelepiped.builder(TEST_PRECISION);
    211
    212         // act
    213         final Parallelepiped p = builder.build();
    214
    215         // assert
    216         Assertions.assertEquals(1, p.getSize(), TEST_EPS);
    217         Assertions.assertEquals(6, p.getBoundarySize(), TEST_EPS);
    218         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.getCentroid(), TEST_EPS);
    219
    220         final List<PlaneConvexSubset> boundaries = p.getBoundaries();
    221         Assertions.assertEquals(6, boundaries.size());
    222
    223         assertVertices(p,
    224             Vector3D.of(-0.5, -0.5, -0.5),
    225             Vector3D.of(0.5, -0.5, -0.5),
    226             Vector3D.of(0.5, 0.5, -0.5),
    227             Vector3D.of(-0.5, 0.5, -0.5),
    228
    229             Vector3D.of(-0.5, -0.5, 0.5),
    230             Vector3D.of(0.5, -0.5, 0.5),
    231             Vector3D.of(0.5, 0.5, 0.5),
    232             Vector3D.of(-0.5, 0.5, 0.5)
    233         );
    234     }
    235
    236     @Test
    237     void testBuilder_withRotation() {
    238         // arrange
    239         final Parallelepiped.Builder builder = Parallelepiped.builder(TEST_PRECISION);
    240
    241         // act
    242         final Parallelepiped p = builder
    243                 .setScale(1, 2, 3)
    244                 .setRotation(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Z, Angle.PI_OVER_TWO))
    245                 .setPosition(Vector3D.of(1, 2, -1))
    246                 .build();
    247
    248         // assert
    249         Assertions.assertEquals(6, p.getSize(), TEST_EPS);
    250         Assertions.assertEquals(22, p.getBoundarySize(), TEST_EPS);
    251         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, -1), p.getCentroid(), TEST_EPS);
    252
    253         assertVertices(p,
    254             Vector3D.of(0, 1.5, 0.5),
    255             Vector3D.of(2, 1.5, 0.5),
    256             Vector3D.of(2, 2.5, 0.5),
    257             Vector3D.of(0, 2.5, 0.5),
    258
    259             Vector3D.of(0, 1.5, -2.5),
    260             Vector3D.of(2, 1.5, -2.5),
    261             Vector3D.of(2, 2.5, -2.5),
    262             Vector3D.of(0, 2.5, -2.5)
    263         );
    264     }
    265
    266     @Test
    267     void testBuilder_withUniformScale() {
    268         // arrange
    269         final Parallelepiped.Builder builder = Parallelepiped.builder(TEST_PRECISION);
    270
    271         // act
    272         final Parallelepiped p = builder
    273                 .setScale(0.5)
    274                 .build();
    275
    276         // assert
    277         Assertions.assertEquals(0.125, p.getSize(), TEST_EPS);
    278         Assertions.assertEquals(1.5, p.getBoundarySize(), TEST_EPS);
    279         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.getCentroid(), TEST_EPS);
    280
    281         assertVertices(p,
    282             Vector3D.of(-0.25, -0.25, -0.25),
    283             Vector3D.of(0.25, -0.25, -0.25),
    284             Vector3D.of(0.25, 0.25, -0.25),
    285             Vector3D.of(-0.25, 0.25, -0.25),
    286
    287             Vector3D.of(-0.25, -0.25, 0.25),
    288             Vector3D.of(0.25, -0.25, 0.25),
    289             Vector3D.of(0.25, 0.25, 0.25),
    290             Vector3D.of(-0.25, 0.25, 0.25)
    291         );
    292     }
    293
    294     @Test
    295     void testToTree() {
    296         // arrange
    297         final Parallelepiped p = Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(4, 5, 6), TEST_PRECISION);
    298
    299         // act
    300         final RegionBSPTree3D tree = p.toTree();
    301
    302         // assert
    303         Assertions.assertEquals(27, tree.getSize(), TEST_EPS);
    304         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2.5, 3.5, 4.5), tree.getCentroid(), TEST_EPS);
    305     }
    306
    307     private static void assertVertices(final Parallelepiped p, final Vector3D... vertices) {
    308         final Set<Vector3D> expectedVertices = new TreeSet<>(VERTEX_COMPARATOR);
    309         expectedVertices.addAll(Arrays.asList(vertices));
    310
    311         final Set<Vector3D> actualVertices = new TreeSet<>(VERTEX_COMPARATOR);
    312         for (final PlaneConvexSubset boundary : p.getBoundaries()) {
    313             actualVertices.addAll(boundary.getVertices());
    314         }
    315
    316         Assertions.assertEquals(expectedVertices.size(), actualVertices.size());
    317         for (final Vector3D expected : expectedVertices) {
    318             Assertions.assertTrue(actualVertices.contains(expected), "Expected vertices to contain " + expected);
    319         }
    320     }
    321 }