PolygonObjParserTest.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.geometry.io.euclidean.threed.obj;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.IntFunction;
import org.apache.commons.geometry.core.GeometryTestUtils;
import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
import org.apache.commons.geometry.euclidean.threed.Vector3D;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class PolygonObjParserTest {
private static final double EPS = 1e-10;
@Test
void testInitialState() {
// act
final PolygonObjParser p = parser("");
// assert
Assertions.assertNull(p.getCurrentKeyword());
Assertions.assertEquals(0, p.getVertexCount());
Assertions.assertEquals(0, p.getVertexNormalCount());
Assertions.assertEquals(0, p.getTextureCoordinateCount());
Assertions.assertFalse(p.isFailOnNonPolygonKeywords());
}
@Test
void testNextKeyword() {
// arrange
final PolygonObjParser p = parser(lines(
"#comment",
"",
" \t",
"o test",
"v",
"v 1 0 0 1",
"v 0 1 0",
"# comment",
" ",
"g triangle-\\",
"group",
"f 1 2 3",
"",
"curv2",
"# end"
));
// act/assert
assertNextKeyword("o", p);
assertNextKeyword("v", p);
assertNextKeyword("v", p);
assertNextKeyword("v", p);
assertNextKeyword("g", p);
assertNextKeyword("f", p);
assertNextKeyword("curv2", p);
assertNextKeyword(null, p);
}
@Test
void testNextKeyword_polygonKeywordsOnly_valid() {
// arrange
final PolygonObjParser p = parser(lines(
"v",
"vn",
"vt",
"f",
"o",
"s",
"g",
"mtllib",
"usemtl"
));
p.setFailOnNonPolygonKeywords(true);
// act/assert
assertNextKeyword("v", p);
assertNextKeyword("vn", p);
assertNextKeyword("vt", p);
assertNextKeyword("f", p);
assertNextKeyword("o", p);
assertNextKeyword("s", p);
assertNextKeyword("g", p);
assertNextKeyword("mtllib", p);
assertNextKeyword("usemtl", p);
assertNextKeyword(null, p);
}
@Test
void testNextKeyword_polygonKeywordsOnly_invalid() {
// arrange
final PolygonObjParser p = parser(lines(
"",
"curv2 abc"
));
p.setFailOnNonPolygonKeywords(true);
// act/assert
GeometryTestUtils.assertThrowsWithMessage(p::nextKeyword,
IllegalStateException.class,
"Parsing failed at line 2, column 1: expected keyword to be one of " +
"[f, g, mtllib, o, s, usemtl, v, vn, vt] but was [curv2]");
}
@Test
void testNextKeyword_emptyContent() {
// arrange
final PolygonObjParser p = parser("");
// act/assert
assertNextKeyword(null, p);
}
@Test
void testNextKeyword_unexpectedContent() {
// arrange
final PolygonObjParser p = parser(lines(
" f",
"-- bad comment attempt"
));
// act/assert
GeometryTestUtils.assertThrowsWithMessage(p::nextKeyword,
IllegalStateException.class, "Parsing failed at line 1, column 2: " +
"non-blank lines must begin with an OBJ keyword or comment character");
GeometryTestUtils.assertThrowsWithMessage(p::nextKeyword,
IllegalStateException.class, "Parsing failed at line 2, column 1: " +
"expected OBJ keyword but found empty token followed by [-]");
}
@Test
void testReadDataLine() {
// arrange
final PolygonObjParser p = parser(lines(
" line\t",
"",
" \\",
"a \\",
"b\\",
"cd\\",
".\\"
));
// act/assert
Assertions.assertEquals(" line\t", p.readDataLine());
Assertions.assertEquals("", p.readDataLine());
Assertions.assertEquals(" a bcd.", p.readDataLine());
Assertions.assertNull(p.readDataLine());
}
@Test
void testDiscardDataLine() {
// arrange
final PolygonObjParser p = parser(lines(
" line\t",
"",
" \\",
"a \\",
"b\\",
"cd\\",
".\\"
));
// act/assert
p.discardDataLine();
Assertions.assertEquals(2, p.getTextParser().getLineNumber());
Assertions.assertEquals(1, p.getTextParser().getColumnNumber());
p.discardDataLine();
Assertions.assertEquals(3, p.getTextParser().getLineNumber());
Assertions.assertEquals(1, p.getTextParser().getColumnNumber());
p.discardDataLine();
Assertions.assertEquals(8, p.getTextParser().getLineNumber());
Assertions.assertEquals(1, p.getTextParser().getColumnNumber());
p.discardDataLine();
Assertions.assertEquals(8, p.getTextParser().getLineNumber());
Assertions.assertEquals(1, p.getTextParser().getColumnNumber());
}
@Test
void testReadVector() {
// arrange
final PolygonObjParser p = parser(lines(
"1.01 3e-02 123.999 extra"
));
// act/assert
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1.01, 0.03, 123.999), p.readVector(), EPS);
}
@Test
void testReadVector_parseFailures() {
// arrange
final PolygonObjParser p = parser(lines(
"0.1 0.2 a",
"1",
""
));
// act/assert
GeometryTestUtils.assertThrowsWithMessage(p::readVector,
IllegalStateException.class, "Parsing failed at line 1, column 9: expected double but found [a]");
p.readDataLine();
GeometryTestUtils.assertThrowsWithMessage(p::readVector,
IllegalStateException.class, "Parsing failed at line 2, column 2: expected double but found end of line");
}
@Test
void testReadDoubles() {
// arrange
final PolygonObjParser p = parser(lines(
"0.1 0.2 3e2 4e2 500.01",
" 12.001 ",
" ",
""
));
// act/assert
Assertions.assertArrayEquals(new double[] {
0.1, 0.2, 3e2, 4e2, 500.01
}, p.readDoubles(), EPS);
Assertions.assertArrayEquals(new double[0], p.readDoubles(), EPS);
p.readDataLine();
Assertions.assertArrayEquals(new double[] {12.001}, p.readDoubles(), EPS);
p.readDataLine();
Assertions.assertArrayEquals(new double[0], p.readDoubles(), EPS);
p.readDataLine();
Assertions.assertArrayEquals(new double[0], p.readDoubles(), EPS);
}
@Test
void testReadDoubles_parseFailures() {
// arrange
final PolygonObjParser p = parser(lines(
"0.1 0.2 a",
"b"
));
// act/assert
GeometryTestUtils.assertThrowsWithMessage(p::readDoubles,
IllegalStateException.class, "Parsing failed at line 1, column 9: expected double but found [a]");
p.readDataLine();
GeometryTestUtils.assertThrowsWithMessage(p::readDoubles,
IllegalStateException.class, "Parsing failed at line 2, column 1: expected double but found [b]");
}
@Test
void testReadFace() {
// arrange
final PolygonObjParser p = parser(lines(
"# test content",
"o test",
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"v 0 1 0",
"vt 1 2",
"vt 3 4",
"vt 5 6",
"vt 7 8",
"vt 9 10",
"vn 0 0 1",
"vn 0 0 -1",
"f 1 2 3 4",
"f -4// -3// -2// -1//",
"f 1//1 2//2 3//1 4//2",
"f -4//-2 -3//-1 -2//-2 -1//-1",
"f 1/4/1 2/3/2 3/2/1 4/1/2",
"f -4/-1/-2 -3/-2/-1 -2/-3/-2 -1/-4/-1",
"f 1/4 2/3 3/2 4/1",
"f -4/-1 -3/-2 -2/-3 -1/-4"
));
nextFace(p);
// act/assert
assertFace(new int[][] {
{0, -1, -1},
{1, -1, -1},
{2, -1, -1},
{3, -1, -1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, -1, -1},
{1, -1, -1},
{2, -1, -1},
{3, -1, -1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, -1, 0},
{1, -1, 1},
{2, -1, 0},
{3, -1, 1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, -1, 0},
{1, -1, 1},
{2, -1, 0},
{3, -1, 1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, 3, 0},
{1, 2, 1},
{2, 1, 0},
{3, 0, 1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, 4, 0},
{1, 3, 1},
{2, 2, 0},
{3, 1, 1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, 3, -1},
{1, 2, -1},
{2, 1, -1},
{3, 0, -1},
}, p.readFace());
nextFace(p);
assertFace(new int[][] {
{0, 4, -1},
{1, 3, -1},
{2, 2, -1},
{3, 1, -1},
}, p.readFace());
}
@Test
void testReadFace_notEnoughVertices() {
// arrange
final PolygonObjParser p = parser(lines(
"# test content",
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"f 1 2"
));
// act/assert
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 5, column 6: " +
"face must contain at least 3 vertices but found only 2");
}
@Test
void testReadFace_invalidVertexIndex() {
// arrange
final PolygonObjParser p = parser(lines(
"# test content",
"f 1 2 3",
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"f 1 2 -4",
"f 1 0 3",
"f 4 2 3"
));
// act/assert
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 2, column 3: " +
"vertex index cannot be used because no values of that type have been defined");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 6, column 7: " +
"vertex index must evaluate to be within the range [1, 3] but was -4");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 7, column 5: " +
"vertex index must evaluate to be within the range [1, 3] but was 0");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 8, column 3: " +
"vertex index must evaluate to be within the range [1, 3] but was 4");
}
@Test
void testReadFace_invalidTextureIndex() {
// arrange
final PolygonObjParser p = parser(lines(
"# test content",
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"f 1/1 2/2 3/3",
"vt 1 2",
"vt 3 4",
"vt 5 6",
"f 1/1 2/2 3/-4",
"f 1/1 1/0 3/3",
"f 1/4 2/2 3/3"
));
// act/assert
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 5, column 5: " +
"texture index cannot be used because no values of that type have been defined");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 9, column 13: " +
"texture index must evaluate to be within the range [1, 3] but was -4");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 10, column 9: " +
"texture index must evaluate to be within the range [1, 3] but was 0");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 11, column 5: " +
"texture index must evaluate to be within the range [1, 3] but was 4");
}
@Test
void testReadFace_invalidNormalIndex() {
// arrange
final PolygonObjParser p = parser(lines(
"# test content",
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"f 1//1 2//2 3//3",
"vn 1 0 0",
"vn 0 1 0",
"vn 0 0 1",
"f 1//1 2//2 3//-4",
"f 1//1 1//0 3//3",
"f 1//4 2//2 3//3"
));
// act/assert
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 5, column 6: " +
"normal index cannot be used because no values of that type have been defined");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 9, column 16: " +
"normal index must evaluate to be within the range [1, 3] but was -4");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 10, column 11: " +
"normal index must evaluate to be within the range [1, 3] but was 0");
nextFace(p);
GeometryTestUtils.assertThrowsWithMessage(p::readFace,
IllegalStateException.class, "Parsing failed at line 11, column 6: " +
"normal index must evaluate to be within the range [1, 3] but was 4");
}
@Test
void testParse() {
// arrange
final PolygonObjParser p = parser(lines(
"# test content",
"o test",
"g test",
"s test",
"mtllib mylib.mtl",
"usemtl mymaterial",
"",
"\\", // line continuation
" \\", // line continuation
"",
"v 0 0 0",
"v 1\\", ".0 0 0", // line continuation
"v 1 1 0",
"v 0 1 0",
"",
"vt 0 0",
"vt 1 0",
"vt 1 1",
"",
"vn 0 0 1",
"",
"f 1 2 4",
"f 1/1/1 2/2/1 3\\", "/3/1" // line continuation
));
// act/assert
assertNextKeyword("o", p);
Assertions.assertEquals("test", p.readDataLine());
assertNextKeyword("g", p);
Assertions.assertEquals("test", p.readDataLine());
assertNextKeyword("s", p);
Assertions.assertEquals("test", p.readDataLine());
assertNextKeyword("mtllib", p);
Assertions.assertEquals("mylib.mtl", p.readDataLine());
assertNextKeyword("usemtl", p);
Assertions.assertEquals("mymaterial", p.readDataLine());
assertNextKeyword("v", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.readVector(), EPS);
assertNextKeyword("v", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_X, p.readVector(), EPS);
assertNextKeyword("v", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 1, 0), p.readVector(), EPS);
assertNextKeyword("v", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Y, p.readVector(), EPS);
assertNextKeyword("vt", p);
Assertions.assertArrayEquals(new double[] {0, 0}, p.readDoubles(), EPS);
assertNextKeyword("vt", p);
Assertions.assertArrayEquals(new double[] {1, 0}, p.readDoubles(), EPS);
assertNextKeyword("vt", p);
Assertions.assertArrayEquals(new double[] {1, 1}, p.readDoubles(), EPS);
assertNextKeyword("vn", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, p.readVector(), EPS);
assertNextKeyword("f", p);
assertFace(new int[][] {
{0, -1, -1},
{1, -1, -1},
{3, -1, -1},
}, p.readFace());
assertNextKeyword("f", p);
assertFace(new int[][] {
{0, 0, 0},
{1, 1, 0},
{2, 2, 0},
}, p.readFace());
Assertions.assertEquals(4, p.getVertexCount());
Assertions.assertEquals(3, p.getTextureCoordinateCount());
Assertions.assertEquals(1, p.getVertexNormalCount());
}
@Test
void testFace_getDefinedCompositeNormal() {
// arrange
final PolygonObjParser p = parser(lines(
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"v 0 1 0",
"",
"vn 0 0 1",
"vn 0 0 -1",
"vn 2 2 2",
"vn -2 2 2",
"",
"f 1 2 3 4",
"f 1//1 2 3",
"f 1//1 2//1 3//1 4//1",
"f 1//1 2//2 3//1 4//2",
"f 1//-2 2//-1 3//3 4//4"
));
final List<Vector3D> normals = Arrays.asList(
Vector3D.Unit.PLUS_Z,
Vector3D.Unit.MINUS_Z,
Vector3D.of(1, 1, 1),
Vector3D.of(-1, 1, 1));
final IntFunction<Vector3D> normalFn = normals::get;
// act/assert
nextMatchingKeyword("f", p);
Assertions.assertNull(p.readFace().getDefinedCompositeNormal(normalFn));
nextMatchingKeyword("f", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z,
p.readFace().getDefinedCompositeNormal(normalFn), EPS);
nextMatchingKeyword("f", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z,
p.readFace().getDefinedCompositeNormal(normalFn), EPS);
nextMatchingKeyword("f", p);
Assertions.assertNull(p.readFace().getDefinedCompositeNormal(normalFn));
nextMatchingKeyword("f", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 1).normalize(),
p.readFace().getDefinedCompositeNormal(normalFn), EPS);
}
@Test
void testFace_computeNormalFromVertices() {
// arrange
final PolygonObjParser p = parser(lines(
"v 0 0 0",
"v 1 0 0",
"v 2 0 0",
"v 0 1 0",
"",
"vn 0 0 1",
"",
"f 1 2 4",
"f 1//1 2//1 3//1"
));
final List<Vector3D> vertices = Arrays.asList(
Vector3D.ZERO,
Vector3D.Unit.PLUS_X,
Vector3D.of(2, 0, 0),
Vector3D.of(0, 1, 0));
final IntFunction<Vector3D> vertexFn = vertices::get;
// act/assert
nextMatchingKeyword("f", p);
EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z,
p.readFace().computeNormalFromVertices(vertexFn), EPS);
nextMatchingKeyword("f", p);
Assertions.assertNull(p.readFace().computeNormalFromVertices(vertexFn));
}
@Test
void testFace_getVertexAttributesCounterClockwise() {
// arrange
final PolygonObjParser p = parser(lines(
"v 0 0 0",
"v 1 0 0",
"v 0 1 0",
"f 1 2 3"
));
final List<Vector3D> vertices = Arrays.asList(
Vector3D.ZERO,
Vector3D.Unit.PLUS_X,
Vector3D.Unit.PLUS_Y,
Vector3D.of(2, 0, 0));
final IntFunction<Vector3D> vertexFn = vertices::get;
nextMatchingKeyword("f", p);
final PolygonObjParser.Face f = p.readFace();
final List<PolygonObjParser.VertexAttributes> attrs = f.getVertexAttributes();
final List<PolygonObjParser.VertexAttributes> reverseAttrs = new ArrayList<>(attrs);
Collections.reverse(reverseAttrs);
// act/assert
Assertions.assertEquals(attrs, f.getVertexAttributesCounterClockwise(null, vertexFn));
Assertions.assertEquals(attrs, f.getVertexAttributesCounterClockwise(Vector3D.Unit.PLUS_Z, vertexFn));
Assertions.assertEquals(attrs, f.getVertexAttributesCounterClockwise(Vector3D.of(1, 0, 0.1), vertexFn));
Assertions.assertEquals(attrs, f.getVertexAttributesCounterClockwise(Vector3D.Unit.PLUS_X, vertexFn));
Assertions.assertEquals(reverseAttrs, f.getVertexAttributesCounterClockwise(Vector3D.Unit.MINUS_Z, vertexFn));
Assertions.assertEquals(reverseAttrs, f.getVertexAttributesCounterClockwise(Vector3D.of(1, 0, -0.1), vertexFn));
}
@Test
void testFace_getVertices() {
// arrange
final PolygonObjParser p = parser(lines(
"v 0 0 0",
"v 1 0 0",
"v 1 1 0",
"v 0 1 0",
"v 0 0 1",
"v 0 0 -1",
"",
"f 2 3 4"
));
final List<Vector3D> vertices = Arrays.asList(
Vector3D.ZERO,
Vector3D.Unit.PLUS_X,
Vector3D.of(1, 1, 0),
Vector3D.Unit.PLUS_Y,
Vector3D.of(0, 0, 1),
Vector3D.of(0, 0, -1));
final IntFunction<Vector3D> vertexFn = vertices::get;
// act/assert
nextMatchingKeyword("f", p);
Assertions.assertEquals(vertices.subList(1, 4), p.readFace().getVertices(vertexFn));
}
@Test
void testFace_getVerticesCounterClockwise() {
// arrange
final PolygonObjParser p = parser(lines(
"v 0 0 0",
"v 1 0 0",
"v 0 1 0",
"v 0 0 -1",
"f 1 2 3"
));
final List<Vector3D> vertices = Arrays.asList(
Vector3D.ZERO,
Vector3D.Unit.PLUS_X,
Vector3D.Unit.PLUS_Y,
Vector3D.of(0, 0, -1));
final IntFunction<Vector3D> vertexFn = vertices::get;
final List<Vector3D> faceVertices = vertices.subList(0, 3);
final List<Vector3D> reverseFaceVertices = new ArrayList<>(faceVertices);
Collections.reverse(reverseFaceVertices);
nextMatchingKeyword("f", p);
final PolygonObjParser.Face f = p.readFace();
// act/assert
Assertions.assertEquals(faceVertices, f.getVerticesCounterClockwise(null, vertexFn));
Assertions.assertEquals(faceVertices, f.getVerticesCounterClockwise(Vector3D.Unit.PLUS_Z, vertexFn));
Assertions.assertEquals(faceVertices, f.getVerticesCounterClockwise(Vector3D.of(1, 0, 0.1), vertexFn));
Assertions.assertEquals(faceVertices, f.getVerticesCounterClockwise(Vector3D.Unit.PLUS_X, vertexFn));
Assertions.assertEquals(reverseFaceVertices, f.getVerticesCounterClockwise(Vector3D.Unit.MINUS_Z, vertexFn));
Assertions.assertEquals(reverseFaceVertices, f.getVerticesCounterClockwise(Vector3D.of(1, 0, -0.1), vertexFn));
}
private static PolygonObjParser parser(final String content) {
return new PolygonObjParser(new StringReader(content));
}
private static String lines(final String... lines) {
final String[] newlineOptions = {"\n", "\r", "\r\n"};
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < lines.length; ++i) {
sb.append(lines[i])
.append(newlineOptions[i % newlineOptions.length]);
}
return sb.toString();
}
private static void nextFace(final PolygonObjParser parser) {
nextMatchingKeyword(ObjConstants.FACE_KEYWORD, parser);
}
private static void nextMatchingKeyword(final String keyword, final PolygonObjParser parser) {
while (parser.nextKeyword()) {
if (keyword.equals(parser.getCurrentKeyword())) {
return;
}
}
}
private static void assertNextKeyword(final String expected, final PolygonObjParser parser) {
Assertions.assertEquals(expected != null, parser.nextKeyword());
Assertions.assertEquals(expected, parser.getCurrentKeyword());
}
private static void assertFace(final int[][] vertexAttributes, final PolygonObjParser.Face face) {
Assertions.assertEquals(vertexAttributes.length, face.getVertexAttributes().size());
final int[] expectedVertexIndices = new int[vertexAttributes.length];
final int[] expectedTextureIndices = new int[vertexAttributes.length];
final int[] expectedNormalIndices = new int[vertexAttributes.length];
// check the indices directly on the vertex attributes
PolygonObjParser.VertexAttributes attrs;
String msg;
for (int i = 0; i < vertexAttributes.length; ++i) {
attrs = face.getVertexAttributes().get(i);
msg = "Unexpected face vertex attributes at index " + i;
Assertions.assertArrayEquals(vertexAttributes[i], new int[] {
attrs.getVertexIndex(),
attrs.getTextureIndex(),
attrs.getNormalIndex()
}, msg);
expectedVertexIndices[i] = attrs.getVertexIndex();
expectedTextureIndices[i] = attrs.getTextureIndex();
expectedNormalIndices[i] = attrs.getNormalIndex();
}
// check the individual index arrays from the face
Assertions.assertArrayEquals(expectedVertexIndices, face.getVertexIndices());
Assertions.assertArrayEquals(expectedTextureIndices, face.getTextureIndices());
Assertions.assertArrayEquals(expectedNormalIndices, face.getNormalIndices());
}
}