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.io.euclidean;
    18
    19  import java.io.FileNotFoundException;
    20  import java.io.IOException;
    21  import java.net.URL;
    22  import java.util.ArrayList;
    23  import java.util.List;
    24
    25  import org.apache.commons.geometry.core.RegionLocation;
    26  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    27  import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
    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.shape.Parallelepiped;
    31  import org.apache.commons.geometry.euclidean.threed.shape.Sphere;
    32  import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition;
    33  import org.apache.commons.geometry.io.euclidean.threed.FacetDefinitionReader;
    34  import org.apache.commons.numbers.core.Precision;
    35  import org.junit.jupiter.api.Assertions;
    36
    37  /** Class containing utility methods for IO tests.
    38   */
    39  public final class EuclideanIOTestUtils {
    40
    41      /** Utility class; no instantiation. */
    42      private EuclideanIOTestUtils() {}
    43
    44      /** Return a test cube.
    45       * @param precision precision context used for floating point comparisons
    46       * @return a test cube
    47       */
    48      public static Parallelepiped cube(final Precision.DoubleEquivalence precision) {
    49          return Parallelepiped.unitCube(precision);
    50      }
    51
    52      /** Assert that the given boundary source defines a cube equivalent to that returned by
    53       * {@link #cube(Precision.DoubleEquivalence)}.
    54       * @param src boundary source to test
    55       * @param eps floating point comparison epsilon
    56       */
    57      public static void assertCube(final BoundarySource3D src, final double eps) {
    58          final RegionBSPTree3D tree = src.toTree();
    59
    60          Assertions.assertEquals(1, tree.getSize(), eps);
    61          Assertions.assertEquals(6, tree.getBoundarySize(), eps);
    62
    63          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, tree.getCentroid(), eps);
    64
    65          EuclideanTestUtils.assertRegionLocation(tree, RegionLocation.INSIDE,
    66                  Vector3D.ZERO,
    67                  Vector3D.of(0.25, 0, 0), Vector3D.of(-0.25, 0, 0),
    68                  Vector3D.of(0, 0.25, 0), Vector3D.of(0, -0.25, 0),
    69                  Vector3D.of(0, 0, 0.25), Vector3D.of(0, 0, -0.25));
    70
    71          EuclideanTestUtils.assertRegionLocation(tree, RegionLocation.BOUNDARY,
    72                  Vector3D.of(-0.5, -0.5, -0.5), Vector3D.of(-0.5, -0.5, +0.5),
    73                  Vector3D.of(-0.5, +0.5, -0.5), Vector3D.of(-0.5, +0.5, +0.5),
    74                  Vector3D.of(+0.5, -0.5, -0.5), Vector3D.of(+0.5, -0.5, +0.5),
    75                  Vector3D.of(+0.5, +0.5, -0.5), Vector3D.of(+0.5, +0.5, +0.5));
    76
    77          EuclideanTestUtils.assertRegionLocation(tree, RegionLocation.OUTSIDE,
    78                  Vector3D.of(0.5, 0.5, 1), Vector3D.of(0.5, 0.5, -1),
    79                  Vector3D.of(0.5, 1, 0.5), Vector3D.of(0.5, -1, 0.5),
    80                  Vector3D.of(1, 0.5, 0.5), Vector3D.of(-1, 0.5, 0.5));
    81      }
    82
    83      /** Return a test cube with a sphere removed from the center.
    84       * @param precision precision context used for floating point comparisons
    85       * @return a test cube with a sphere removed from the center
    86       */
    87      public static RegionBSPTree3D cubeMinusSphere(final Precision.DoubleEquivalence precision) {
    88          final RegionBSPTree3D tree = Parallelepiped.unitCube(precision).toTree();
    89          final Sphere sphere = Sphere.from(Vector3D.ZERO, 0.65, precision);
    90
    91          tree.difference(sphere.toTree(3));
    92
    93          return tree;
    94      }
    95
    96      /** Assert that the given boundary source defines a region equivalent to that returned by
    97       * {@link #cubeMinusSphere(Precision.DoubleEquivalence)}.
    98       * @param src boundary source to test
    99       * @param eps floating point comparison epsilon
    100      */
    101     public static void assertCubeMinusSphere(final BoundarySource3D src, final double eps) {
    102         final RegionBSPTree3D tree = src.toTree();
    103
    104         Assertions.assertEquals(0.11509505362599505, tree.getSize(), eps);
    105         Assertions.assertEquals(4.585561662505128, tree.getBoundarySize(), eps);
    106
    107         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, tree.getCentroid(), eps);
    108
    109         EuclideanTestUtils.assertRegionLocation(tree, RegionLocation.INSIDE,
    110                 Vector3D.of(0.45, 0.45, 0.45), Vector3D.of(0.45, 0.45, -0.45),
    111                 Vector3D.of(0.45, -0.45, 0.45), Vector3D.of(0.45, -0.45, -0.45),
    112                 Vector3D.of(-0.45, 0.45, 0.45), Vector3D.of(-0.45, 0.45, -0.45),
    113                 Vector3D.of(-0.45, -0.45, 0.45), Vector3D.of(-0.45, -0.45, -0.45));
    114
    115         EuclideanTestUtils.assertRegionLocation(tree, RegionLocation.BOUNDARY,
    116                 Vector3D.of(-0.5, -0.5, -0.5), Vector3D.of(-0.5, -0.5, +0.5),
    117                 Vector3D.of(-0.5, +0.5, -0.5), Vector3D.of(-0.5, +0.5, +0.5),
    118                 Vector3D.of(+0.5, -0.5, -0.5), Vector3D.of(+0.5, -0.5, +0.5),
    119                 Vector3D.of(+0.5, +0.5, -0.5), Vector3D.of(+0.5, +0.5, +0.5));
    120
    121         EuclideanTestUtils.assertRegionLocation(tree, RegionLocation.OUTSIDE,
    122                 Vector3D.ZERO,
    123                 Vector3D.of(0.5, 0.5, 1), Vector3D.of(0.5, 0.5, -1),
    124                 Vector3D.of(0.5, 1, 0.5), Vector3D.of(0.5, -1, 0.5),
    125                 Vector3D.of(1, 0.5, 0.5), Vector3D.of(-1, 0.5, 0.5));
    126     }
    127
    128     /** Read all facets available from the given facet reader.
    129      * @param reader instance to read facets from
    130      * @return list containing all facets available from the given facet reader
    131      * @throws IllegalStateException if a data format error occurs
    132      * @throws java.io.UncheckedIOException if an I/O error occurs
    133      */
    134     public static List<FacetDefinition> readAll(final FacetDefinitionReader reader) {
    135         final List<FacetDefinition> facets = new ArrayList<>();
    136
    137         FacetDefinition f;
    138         while ((f = reader.readFacet()) != null) {
    139             facets.add(f);
    140         }
    141
    142         return facets;
    143     }
    144
    145     /** Get the classpath resource at the given location, throwing an exception if it could not be found.
    146      * @param location classpath location
    147      * @return classpath resource at the given location
    148      * @throws IOException if the resource cannot be found
    149      */
    150     public static URL resource(final String location) throws IOException {
    151         final URL url = EuclideanIOTestUtils.class.getResource(location);
    152         if (url == null) {
    153             throw new FileNotFoundException("Unable to find classpath resource: " + location);
    154         }
    155
    156         return url;
    157     }
    158
    159     /** Assert that the facet definition contains the given vertices in order.
    160      * @param facet facet to test
    161      * @param expectedVertices expected vertices
    162      * @param eps floating point comparison epsilon
    163      */
    164     public static void assertFacetVertices(final FacetDefinition facet, final List<Vector3D> expectedVertices,
    165             final double eps) {
    166         List<Vector3D> vertices = facet.getVertices();
    167         Assertions.assertEquals(expectedVertices.size(), vertices.size());
    168
    169         for (int i = 0; i < expectedVertices.size(); ++i) {
    170             EuclideanTestUtils.assertCoordinatesEqual(expectedVertices.get(i), facet.getVertices().get(i), eps);
    171         }
    172     }
    173
    174     /** Assert that the facet definition contains the given vertices (in order) and normal.
    175      * @param facet facet to test
    176      * @param expectedVertices expected vertices
    177      * @param expectedNormal expected normal; may be null
    178      * @param eps floating point comparison epsilon
    179      */
    180     public static void assertFacetVerticesAndNormal(final FacetDefinition facet, final List<Vector3D> expectedVertices,
    181             final Vector3D expectedNormal, final double eps) {
    182         assertFacetVertices(facet, expectedVertices, eps);
    183
    184         if (expectedNormal == null) {
    185             Assertions.assertNull(facet.getNormal(), "Expected facet normal to be null");
    186         } else {
    187             EuclideanTestUtils.assertCoordinatesEqual(expectedNormal, facet.getNormal(), eps);
    188         }
    189     }
    190 }