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 }