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.obj;
18  
19  import java.io.StringReader;
20  import java.util.Arrays;
21  import java.util.regex.Pattern;
22  
23  import org.apache.commons.geometry.core.GeometryTestUtils;
24  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
25  import org.apache.commons.geometry.euclidean.threed.Vector3D;
26  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
27  import org.apache.commons.geometry.io.core.test.CloseCountReader;
28  import org.apache.commons.numbers.core.Precision;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.Test;
31  
32  class ObjTriangleMeshReaderTest {
33  
34      private static final double TEST_EPS = 1e-10;
35  
36      private static final Precision.DoubleEquivalence TEST_PRECISION =
37              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
38  
39      @Test
40      void testDefaults() {
41          // arrange
42          final ObjTriangleMeshReader reader = reader("");
43  
44          // act/assert
45          Assertions.assertFalse(reader.isFailOnNonPolygonKeywords());
46      }
47  
48      @Test
49      void testClose() {
50          // arrange
51          final CloseCountReader closeReader = new CloseCountReader(new StringReader(""));
52  
53          // act/assert
54          try (ObjTriangleMeshReader reader = new ObjTriangleMeshReader(closeReader, TEST_PRECISION)) {
55              Assertions.assertEquals(0, closeReader.getCloseCount());
56          }
57  
58          Assertions.assertEquals(1, closeReader.getCloseCount());
59      }
60  
61      @Test
62      void testReadTriangleMesh_withNormal() {
63          // arrange
64          final ObjTriangleMeshReader reader = reader(
65                  "o test\n\n" +
66                  "v 0 0 0\r\n" +
67                  "v 0.5 0 0\n" +
68                  "v 1 1 0\r" +
69                  "v 0 1 0\n" +
70                  "vn 0 0 -1\n" +
71                  "f 1//1 2//1 3//1 4//1\n" +
72                  "curv non-polygon data\n");
73  
74          // act
75          final TriangleMesh mesh = reader.readTriangleMesh();
76  
77          // assert
78          Assertions.assertEquals(4, mesh.getVertexCount());
79          Assertions.assertEquals(2, mesh.getFaceCount());
80  
81          final TriangleMesh.Face face1 = mesh.getFace(0);
82  
83          EuclideanTestUtils.assertVertexLoopSequence(Arrays.asList(
84                  Vector3D.of(0.5, 0, 0), Vector3D.of(0, 1, 0), Vector3D.of(1, 1, 0)
85              ), face1.getVertices(), TEST_PRECISION);
86  
87          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.MINUS_Z,
88                  face1.getPolygon().getPlane().getNormal(), TEST_EPS);
89  
90          final TriangleMesh.Face face2 = mesh.getFace(1);
91  
92          EuclideanTestUtils.assertVertexLoopSequence(Arrays.asList(
93                  Vector3D.ZERO, Vector3D.of(0, 1, 0), Vector3D.of(0.5, 0, 0)
94              ), face2.getVertices(), TEST_PRECISION);
95  
96          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.MINUS_Z,
97                  face2.getPolygon().getPlane().getNormal(), TEST_EPS);
98      }
99  
100     @Test
101     void testReadTriangleMesh_withoutNormal() {
102         // arrange
103         final ObjTriangleMeshReader reader = reader(
104                 "o test\n\n" +
105                 "v -1 0 0\n" +
106                 "v 0 0 0\r\n" +
107                 "v 1 0 0\n" +
108                 "v 1 1 0\r" +
109                 "v -2 0 0\n" +
110                 "f 2 3 4\n");
111 
112         // act
113         final TriangleMesh mesh = reader.readTriangleMesh();
114 
115         // assert
116         Assertions.assertEquals(5, mesh.getVertexCount());
117         Assertions.assertEquals(1, mesh.getFaceCount());
118 
119         final TriangleMesh.Face face = mesh.getFace(0);
120 
121         EuclideanTestUtils.assertVertexLoopSequence(Arrays.asList(
122                     Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(1, 1, 0)
123                 ), face.getVertices(), TEST_PRECISION);
124 
125         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 1),
126                 face.getPolygon().getPlane().getNormal(), TEST_EPS);
127     }
128 
129     @Test
130     void testReadTriangleMesh_failOnNonPolygon() {
131         // arrange
132         final ObjTriangleMeshReader reader = reader(
133                 "o test\n\n" +
134                 "v 0 0 0\r\n" +
135                 "v 1 0 0\n" +
136                 "v 1 1 0\r" +
137                 "v 0 1 0\n" +
138                 "vn 0 0 1\n" +
139                 "f 1//1 2//1 3//1\n" +
140                 "curv non-polygon data\n");
141 
142         reader.setFailOnNonPolygonKeywords(true);
143 
144         // act/assert
145         GeometryTestUtils.assertThrowsWithMessage(
146                 () -> reader.readTriangleMesh(),
147                 IllegalStateException.class, Pattern.compile("^Parsing failed.*"));
148     }
149 
150     private static ObjTriangleMeshReader reader(final String str) {
151         return new ObjTriangleMeshReader(new StringReader(str), TEST_PRECISION);
152     }
153 }