1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.io.euclidean.threed.stl;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.IOException;
21 import java.net.URL;
22 import java.nio.charset.StandardCharsets;
23 import java.util.List;
24 import java.util.stream.Collectors;
25 import java.util.stream.Stream;
26
27 import org.apache.commons.geometry.euclidean.threed.BoundaryList3D;
28 import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
29 import org.apache.commons.geometry.euclidean.threed.ConvexPolygon3D;
30 import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
31 import org.apache.commons.geometry.io.core.input.GeometryInput;
32 import org.apache.commons.geometry.io.core.input.StreamGeometryInput;
33 import org.apache.commons.geometry.io.core.input.UrlGeometryInput;
34 import org.apache.commons.geometry.io.core.test.CloseCountInputStream;
35 import org.apache.commons.geometry.io.euclidean.EuclideanIOTestUtils;
36 import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition;
37 import org.apache.commons.geometry.io.euclidean.threed.FacetDefinitionReader;
38 import org.apache.commons.geometry.io.euclidean.threed.FacetDefinitions;
39 import org.apache.commons.geometry.io.euclidean.threed.GeometryFormat3D;
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 StlBoundaryReadHandler3DTest {
45
46 private static final double TEST_EPS = 1e-10;
47
48 private static final Precision.DoubleEquivalence TEST_PRECISION =
49 Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
50
51 private final StlBoundaryReadHandler3D handler = new StlBoundaryReadHandler3D();
52
53 @Test
54 void testProperties() {
55
56 Assertions.assertEquals(GeometryFormat3D.STL, handler.getFormat());
57 Assertions.assertEquals(StandardCharsets.UTF_8, handler.getDefaultCharset());
58 }
59
60 @Test
61 void testReadMethods_cubeAscii() throws IOException {
62
63 final URL url = EuclideanIOTestUtils.resource("/models/cube-ascii.stl");
64 final GeometryInput input = new UrlGeometryInput(url);
65
66
67 EuclideanIOTestUtils.assertCube(readerToBoundarySource(handler.facetDefinitionReader(input)), TEST_EPS);
68 EuclideanIOTestUtils.assertCube(facetsToBoundarySource(handler.facets(input)), TEST_EPS);
69 EuclideanIOTestUtils.assertCube(handler.read(input, TEST_PRECISION), TEST_EPS);
70 EuclideanIOTestUtils.assertCube(handler.readTriangleMesh(input, TEST_PRECISION), TEST_EPS);
71 EuclideanIOTestUtils.assertCube(
72 boundariesToBoundarySource(handler.boundaries(input, TEST_PRECISION)), TEST_EPS);
73 }
74
75 @Test
76 void testReadMethods_cubeBinary() throws IOException {
77
78 final URL url = EuclideanIOTestUtils.resource("/models/cube-ascii.stl");
79 final GeometryInput input = new UrlGeometryInput(url);
80
81
82 EuclideanIOTestUtils.assertCube(readerToBoundarySource(handler.facetDefinitionReader(input)), TEST_EPS);
83 EuclideanIOTestUtils.assertCube(facetsToBoundarySource(handler.facets(input)), TEST_EPS);
84 EuclideanIOTestUtils.assertCube(handler.read(input, TEST_PRECISION), TEST_EPS);
85 EuclideanIOTestUtils.assertCube(handler.readTriangleMesh(input, TEST_PRECISION), TEST_EPS);
86 EuclideanIOTestUtils.assertCube(
87 boundariesToBoundarySource(handler.boundaries(input, TEST_PRECISION)), TEST_EPS);
88 }
89
90 @Test
91 void testRead_usesInputCharset() {
92
93 final String content = "solid test\n" +
94 "facet normal 1 2 3 " +
95 "outer loop " +
96 "vertex 4 5 6 " +
97 "vertex 7 8 9 " +
98 "vertex 10 11 12 " +
99 "endloop " +
100 "endfacet " +
101 "endsolid test";
102
103 final ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_16));
104 final GeometryInput input = new StreamGeometryInput(in, null, StandardCharsets.UTF_16);
105
106
107 try (FacetDefinitionReader reader = handler.facetDefinitionReader(input)) {
108 Assertions.assertNotNull(reader.readFacet());
109 Assertions.assertNull(reader.readFacet());
110 }
111 }
112
113 @Test
114 void testRead_setDefaultCharset() {
115
116 final String content = "solid test\n" +
117 "facet normal 1 2 3 " +
118 "outer loop " +
119 "vertex 4 5 6 " +
120 "vertex 7 8 9 " +
121 "vertex 10 11 12 " +
122 "endloop " +
123 "endfacet " +
124 "endsolid test";
125
126 final ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_16));
127 final GeometryInput input = new StreamGeometryInput(in);
128
129
130 handler.setDefaultCharset(StandardCharsets.UTF_16);
131
132
133 try (FacetDefinitionReader reader = handler.facetDefinitionReader(input)) {
134 Assertions.assertNotNull(reader.readFacet());
135 Assertions.assertNull(reader.readFacet());
136 }
137 }
138
139 @Test
140 void testRead_incorrectCharset() {
141
142 final String content = "solid test\n" +
143 "facet normal 1 2 3 " +
144 "outer loop " +
145 "vertex 4 5 6 " +
146 "vertex 7 8 9 " +
147 "vertex 10 11 12 " +
148 "endloop " +
149 "endfacet " +
150 "endsolid test";
151
152 final ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_16));
153 final GeometryInput input = new StreamGeometryInput(in);
154
155
156 try (FacetDefinitionReader reader = handler.facetDefinitionReader(input)) {
157 Assertions.assertNotNull(reader.readFacet());
158 Assertions.assertNotNull(reader.readFacet());
159
160 Assertions.assertThrows(IllegalStateException.class, () -> reader.readFacet());
161 }
162 }
163
164 @Test
165 void testRead_notEnoughBytes() {
166
167 final ByteArrayInputStream in = new ByteArrayInputStream(new byte[1]);
168 final GeometryInput input = new StreamGeometryInput(in);
169
170
171 Assertions.assertThrows(IllegalStateException.class, () -> handler.facetDefinitionReader(input));
172 }
173
174 @Test
175 void testRead_closesInputOnReaderCreationFailure() {
176
177 final CloseCountInputStream in = new CloseCountInputStream(new ByteArrayInputStream(new byte[1]));
178 final GeometryInput input = new StreamGeometryInput(in);
179
180
181 Assertions.assertThrows(IllegalStateException.class, () -> handler.facetDefinitionReader(input));
182
183 Assertions.assertEquals(1, in.getCloseCount());
184 }
185
186 private static BoundarySource3D boundariesToBoundarySource(final Stream<? extends PlaneConvexSubset> boundaries) {
187 try (Stream<? extends PlaneConvexSubset> toClose = boundaries) {
188 return new BoundaryList3D(boundaries.collect(Collectors.toList()));
189 }
190 }
191
192 private static BoundarySource3D facetsToBoundarySource(final Stream<? extends FacetDefinition> facets) {
193 try (Stream<? extends FacetDefinition> toClose = facets) {
194 final List<ConvexPolygon3D> polygons = facets
195 .map(f -> FacetDefinitions.toPolygon(f, TEST_PRECISION))
196 .collect(Collectors.toList());
197 return new BoundaryList3D(polygons);
198 }
199 }
200
201 private static BoundarySource3D readerToBoundarySource(final FacetDefinitionReader reader) {
202 return facetsToBoundarySource(EuclideanIOTestUtils.readAll(reader).stream());
203 }
204 }