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.threed;
18  
19  import java.io.ByteArrayInputStream;
20  import java.util.Arrays;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  import java.util.stream.Collectors;
26  import java.util.stream.Stream;
27  
28  import org.apache.commons.geometry.core.GeometryTestUtils;
29  import org.apache.commons.geometry.euclidean.threed.BoundaryList3D;
30  import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
31  import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
32  import org.apache.commons.geometry.euclidean.threed.Vector3D;
33  import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh;
34  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
35  import org.apache.commons.geometry.io.core.GeometryFormat;
36  import org.apache.commons.geometry.io.core.input.GeometryInput;
37  import org.apache.commons.geometry.io.core.input.StreamGeometryInput;
38  import org.apache.commons.geometry.io.core.test.CloseCountInputStream;
39  import org.apache.commons.geometry.io.euclidean.threed.AbstractBoundaryReadHandler3D.FacetDefinitionReaderIterator;
40  import org.apache.commons.numbers.core.Precision;
41  import org.junit.jupiter.api.Assertions;
42  import org.junit.jupiter.api.Test;
43  
44  class AbstractBoundaryReadHandler3DTest {
45  
46      private static final double TEST_EPS = 1e-10;
47  
48      private static final Precision.DoubleEquivalence TEST_PRECISION = Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
49  
50      private static final FacetDefinition FACET_1 = new SimpleFacetDefinition(Arrays.asList(
51              Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(1, 1, 0), Vector3D.of(0, 1, 0)));
52  
53      private static final FacetDefinition FACET_2 = new SimpleFacetDefinition(Arrays.asList(
54              Vector3D.ZERO, Vector3D.of(0, 1, 0), Vector3D.of(-1, 1, 0), Vector3D.of(-1, 0, 0)));
55  
56      @Test
57      void testRead() {
58          // arrange
59          final List<FacetDefinition> facets = Arrays.asList(FACET_1, FACET_2);
60          final TestReadHandler3D handler = new TestReadHandler3D(facets);
61  
62          final GeometryInput in = new StreamGeometryInput(new ByteArrayInputStream(new byte[0]));
63  
64          // act
65          final BoundarySource3D result = handler.read(in, TEST_PRECISION);
66  
67          // assert
68          Assertions.assertSame(in, handler.inArg);
69  
70          Assertions.assertEquals(BoundaryList3D.class, result.getClass());
71          Assertions.assertEquals(2, result.toList().getBoundaries().size());
72      }
73  
74      @Test
75      void testReadTriangleMesh() {
76          // arrange
77          final List<FacetDefinition> facets = Arrays.asList(FACET_1, FACET_2);
78          final TestReadHandler3D handler = new TestReadHandler3D(facets);
79  
80          final GeometryInput in = new StreamGeometryInput(new ByteArrayInputStream(new byte[0]));
81  
82          // act
83          final TriangleMesh result = handler.readTriangleMesh(in, TEST_PRECISION);
84  
85          // assert
86          Assertions.assertSame(in, handler.inArg);
87  
88          Assertions.assertEquals(SimpleTriangleMesh.class, result.getClass());
89          Assertions.assertEquals(6, result.getVertexCount());
90          Assertions.assertEquals(4, result.getFaceCount());
91      }
92  
93      @Test
94      void testBoundaries() {
95          // arrange
96          final List<FacetDefinition> facets = Arrays.asList(FACET_1, FACET_2);
97          final TestReadHandler3D handler = new TestReadHandler3D(facets);
98  
99          final CloseCountInputStream inputStream = new CloseCountInputStream(new ByteArrayInputStream(new byte[0]));
100         final GeometryInput in = new StreamGeometryInput(inputStream);
101 
102         // act
103         final List<PlaneConvexSubset> list;
104         try (Stream<PlaneConvexSubset> stream = handler.boundaries(in, TEST_PRECISION)) {
105             list = stream.collect(Collectors.toList());
106 
107             Assertions.assertEquals(0, inputStream.getCloseCount());
108         }
109 
110         // assert
111         Assertions.assertSame(in, handler.inArg);
112 
113         Assertions.assertEquals(2, list.size());
114         Assertions.assertEquals(1, inputStream.getCloseCount());
115     }
116 
117     @Test
118     void testFacets() {
119         // arrange
120         final List<FacetDefinition> facets = Arrays.asList(FACET_1, FACET_2);
121         final TestReadHandler3D handler = new TestReadHandler3D(facets);
122 
123         final CloseCountInputStream inputStream = new CloseCountInputStream(new ByteArrayInputStream(new byte[0]));
124         final GeometryInput in = new StreamGeometryInput(inputStream);
125 
126         // act
127         final List<FacetDefinition> list;
128         try (Stream<FacetDefinition> stream = handler.facets(in)) {
129             list = stream.collect(Collectors.toList());
130 
131             Assertions.assertEquals(0, inputStream.getCloseCount());
132         }
133 
134         // assert
135         Assertions.assertSame(in, handler.inArg);
136 
137         Assertions.assertEquals(2, list.size());
138         Assertions.assertEquals(1, inputStream.getCloseCount());
139     }
140 
141     @Test
142     void testFacetIterator() {
143         // arrange
144         final StubFacetDefinitionReader reader = new StubFacetDefinitionReader(Arrays.asList(FACET_1, FACET_2));
145         final FacetDefinitionReaderIterator it = new FacetDefinitionReaderIterator(reader);
146 
147         // act/assert
148         Assertions.assertTrue(it.hasNext());
149         Assertions.assertSame(FACET_1, it.next());
150 
151         Assertions.assertTrue(it.hasNext());
152         Assertions.assertSame(FACET_2, it.next());
153 
154         Assertions.assertFalse(it.hasNext());
155         Assertions.assertThrows(NoSuchElementException.class, it::next);
156     }
157 
158     @Test
159     void testFacetIterator_readFails() {
160         // arrange
161         final StubFacetDefinitionReader reader = new StubFacetDefinitionReader(Arrays.asList(FACET_1, FACET_2));
162         reader.fail = true;
163 
164         final FacetDefinitionReaderIterator it = new FacetDefinitionReaderIterator(reader);
165 
166         // act/assert
167         GeometryTestUtils.assertThrowsWithMessage(it::next, IllegalStateException.class, "Read failure");
168     }
169 
170     private static final class TestReadHandler3D extends AbstractBoundaryReadHandler3D {
171 
172         private final Collection<FacetDefinition> facets;
173 
174         private GeometryInput inArg;
175 
176         TestReadHandler3D(final Collection<FacetDefinition> facets) {
177             this.facets = facets;
178         }
179 
180         /** {@inheritDoc} */
181         @Override
182         public GeometryFormat getFormat() {
183             throw new UnsupportedOperationException();
184         }
185 
186         /** {@inheritDoc} */
187         @Override
188         public FacetDefinitionReader facetDefinitionReader(final GeometryInput in) {
189             this.inArg = in;
190 
191             return new StubFacetDefinitionReader(facets);
192         }
193     }
194 
195     private static final class StubFacetDefinitionReader implements FacetDefinitionReader {
196 
197         private final Iterator<FacetDefinition> iterator;
198 
199         private boolean fail = false;
200 
201         StubFacetDefinitionReader(final Collection<FacetDefinition> facets) {
202             this.iterator = facets.iterator();
203         }
204 
205         /** {@inheritDoc} */
206         @Override
207         public FacetDefinition readFacet() {
208             if (fail) {
209                 throw new IllegalStateException("Read failure");
210             }
211 
212             return iterator.hasNext() ?
213                 iterator.next() :
214                 null;
215         }
216 
217         /** {@inheritDoc} */
218         @Override
219         public void close() {
220             // do nothing
221         }
222     }
223 }