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