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.examples.io.threed.obj;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.io.UncheckedIOException;
26  import java.net.URL;
27  import java.nio.file.Files;
28  
29  import org.apache.commons.geometry.core.GeometryTestUtils;
30  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
31  import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
32  import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
33  import org.apache.commons.geometry.euclidean.threed.Planes;
34  import org.apache.commons.geometry.euclidean.threed.Vector3D;
35  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
36  import org.junit.Assert;
37  import org.junit.Rule;
38  import org.junit.Test;
39  import org.junit.rules.TemporaryFolder;
40  
41  public class OBJModelIOHandlerTest {
42  
43      private static final double TEST_EPS = 1e-10;
44  
45      private static final DoublePrecisionContext TEST_PRECISION =
46              new EpsilonDoublePrecisionContext(TEST_EPS);
47  
48      private static final String CUBE_MINUS_SPHERE_MODEL = "/models/cube-minus-sphere.obj";
49  
50      private static final int CUBE_MINUS_SPHERE_VERTICES = 1688;
51  
52      private static final int CUBE_MINUS_SPHERE_FACES = 728;
53  
54      @Rule
55      public TemporaryFolder tempFolder = new TemporaryFolder();
56  
57      private OBJModelIOHandler handler = new OBJModelIOHandler();
58  
59      @Test
60      public void testHandlesType() {
61          // act/assert
62          Assert.assertFalse(handler.handlesType(null));
63          Assert.assertFalse(handler.handlesType(""));
64          Assert.assertFalse(handler.handlesType(" "));
65          Assert.assertFalse(handler.handlesType("abc"));
66          Assert.assertFalse(handler.handlesType("stl"));
67  
68          Assert.assertTrue(handler.handlesType("obj"));
69          Assert.assertTrue(handler.handlesType("OBJ"));
70          Assert.assertTrue(handler.handlesType("oBj"));
71      }
72  
73      @Test
74      public void testRead_fromFile() throws Exception {
75          // act
76          BoundarySource3D src = handler.read("obj", cubeMinusSphereFile(), TEST_PRECISION);
77  
78          // assert
79          TriangleMesh mesh = (TriangleMesh) src;
80          Assert.assertEquals(CUBE_MINUS_SPHERE_VERTICES, mesh.getVertexCount());
81          Assert.assertEquals(CUBE_MINUS_SPHERE_FACES, mesh.getFaceCount());
82      }
83  
84      @Test
85      public void testRead_fromFile_unsupportedType() throws Exception {
86          // arrange
87          File file = cubeMinusSphereFile();
88  
89          // act/assert
90          GeometryTestUtils.assertThrows(() -> {
91              handler.read("stl", file, TEST_PRECISION);
92          }, IllegalArgumentException.class, "File type is not supported by this handler: stl");
93      }
94  
95      @Test
96      public void testRead_fromFile_ioException() throws Exception {
97          // arrange
98          File file = new File("doesnotexist.obj");
99  
100         // act/assert
101         GeometryTestUtils.assertThrows(() -> {
102             handler.read("obj", file, TEST_PRECISION);
103         }, UncheckedIOException.class);
104     }
105 
106     @Test
107     public void testRead_fromStream() throws Exception {
108         // act
109         BoundarySource3D src;
110         try (InputStream in = Files.newInputStream(cubeMinusSphereFile().toPath())) {
111             src = handler.read("obj", cubeMinusSphereFile(), TEST_PRECISION);
112         }
113 
114         // assert
115         TriangleMesh mesh = (TriangleMesh) src;
116         Assert.assertEquals(CUBE_MINUS_SPHERE_VERTICES, mesh.getVertexCount());
117         Assert.assertEquals(CUBE_MINUS_SPHERE_FACES, mesh.getFaceCount());
118     }
119 
120     @Test
121     public void testRead_fromStream_unsupportedType() throws Exception {
122         // arrange
123         File file = cubeMinusSphereFile();
124 
125         // act/assert
126         try (InputStream in = Files.newInputStream(file.toPath())) {
127             GeometryTestUtils.assertThrows(() -> {
128                 handler.read("stl", in, TEST_PRECISION);
129             }, IllegalArgumentException.class, "File type is not supported by this handler: stl");
130         }
131     }
132 
133     @Test
134     public void testRead_fromStream_ioException() throws Exception {
135         // act/assert
136         GeometryTestUtils.assertThrows(() -> {
137             handler.read("obj", new FailingInputStream(), TEST_PRECISION);
138         }, UncheckedIOException.class, "IOException: test");
139     }
140 
141     @Test
142     public void testWrite_toFile() throws Exception {
143         // arrange
144         File out = tempFolder.newFile("out.obj");
145 
146         BoundarySource3D src = BoundarySource3D.from(
147                 Planes.triangleFromVertices(Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), TEST_PRECISION)
148             );
149 
150         // act
151         handler.write(src, "OBJ", out);
152 
153         // assert
154         TriangleMesh mesh = (TriangleMesh) handler.read("obj", out, TEST_PRECISION);
155         Assert.assertEquals(3, mesh.getVertexCount());
156         Assert.assertEquals(1, mesh.getFaceCount());
157     }
158 
159     @Test
160     public void testWrite_toFile_unsupportedFormat() throws Exception {
161         // arrange
162         File out = tempFolder.newFile("out.obj");
163 
164         BoundarySource3D src = BoundarySource3D.from(
165                 Planes.triangleFromVertices(Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), TEST_PRECISION)
166             );
167 
168         // act/assert
169         GeometryTestUtils.assertThrows(() -> {
170             handler.write(src, "stl", out);
171         }, IllegalArgumentException.class, "File type is not supported by this handler: stl");
172     }
173 
174     @Test
175     public void testWrite_toFile_ioException() throws Exception {
176         // arrange
177         File out = tempFolder.newFolder("notafile");
178 
179         BoundarySource3D src = BoundarySource3D.from(
180                 Planes.triangleFromVertices(Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), TEST_PRECISION)
181             );
182 
183         // act/assert
184         GeometryTestUtils.assertThrows(() -> {
185             handler.write(src, "OBJ", out);
186         }, UncheckedIOException.class);
187     }
188 
189     @Test
190     public void testWrite_toStream() throws Exception {
191         // arrange
192         ByteArrayOutputStream out = new ByteArrayOutputStream();
193 
194         BoundarySource3D src = BoundarySource3D.from(
195                 Planes.triangleFromVertices(Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), TEST_PRECISION)
196             );
197 
198         // act
199         handler.write(src, "OBJ", out);
200 
201         // assert
202         TriangleMesh mesh = (TriangleMesh) handler.read("obj", new ByteArrayInputStream(out.toByteArray()),
203                 TEST_PRECISION);
204         Assert.assertEquals(3, mesh.getVertexCount());
205         Assert.assertEquals(1, mesh.getFaceCount());
206     }
207 
208     @Test
209     public void testWrite_toStream_unsupportedFormat() throws Exception {
210         // arrange
211         File file = tempFolder.newFile("out.obj");
212 
213         BoundarySource3D src = BoundarySource3D.from(
214                 Planes.triangleFromVertices(Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), TEST_PRECISION)
215             );
216 
217         // act/assert
218         try (OutputStream out = Files.newOutputStream(file.toPath())) {
219             GeometryTestUtils.assertThrows(() -> {
220                 handler.write(src, "stl", out);
221             }, IllegalArgumentException.class, "File type is not supported by this handler: stl");
222         }
223     }
224 
225     @Test
226     public void testWrite_toStream_ioException() throws Exception {
227         // arrange
228         BoundarySource3D src = BoundarySource3D.from(
229                 Planes.triangleFromVertices(Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), TEST_PRECISION)
230             );
231 
232         // act/assert
233         GeometryTestUtils.assertThrows(() -> {
234             handler.write(src, "OBJ", new FailingOutputStream());
235         }, UncheckedIOException.class, "IOException: test");
236     }
237 
238     private static File cubeMinusSphereFile() throws Exception {
239         URL url = OBJModelIOHandlerTest.class.getResource(CUBE_MINUS_SPHERE_MODEL);
240         return new File(url.toURI());
241     }
242 
243     private static final class FailingInputStream extends InputStream {
244 
245         @Override
246         public int read() throws IOException {
247             throw new IOException("test");
248         }
249     }
250 
251     private static final class FailingOutputStream extends OutputStream {
252 
253         @Override
254         public void write(int b) throws IOException {
255             throw new IOException("test");
256         }
257     }
258 }