001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.io.euclidean.threed.obj;
018
019import java.io.Reader;
020import java.util.ArrayList;
021import java.util.Iterator;
022import java.util.List;
023
024import org.apache.commons.geometry.euclidean.threed.Vector3D;
025import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh;
026import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
027import org.apache.commons.numbers.core.Precision;
028
029/** Class for reading OBJ content as a {@link TriangleMesh triangle mesh}.
030 */
031public class ObjTriangleMeshReader extends AbstractObjPolygonReader {
032
033    /** Object used to construct the mesh. */
034    private final SimpleTriangleMesh.Builder meshBuilder;
035
036    /** List of normals discovered in the input. */
037    private final List<Vector3D> normals = new ArrayList<>();
038
039    /** Construct a new instance that reads OBJ content from the given reader.
040     * @param reader reader to read from
041     * @param precision precision context used to compare floating point numbers
042     */
043    public ObjTriangleMeshReader(final Reader reader, final Precision.DoubleEquivalence precision) {
044        super(reader);
045
046        this.meshBuilder = SimpleTriangleMesh.builder(precision);
047    }
048
049    /** Return a {@link TriangleMesh triangle mesh} constructed from all of the OBJ content
050     * from the underlying reader. Non-triangle faces are converted to triangles using a simple
051     * triangle fan. All vertices present in the OBJ content are also present in the returned mesh,
052     * regardless of whether or not they are used in a face.
053     * @return triangle mesh containing all data from the OBJ content
054     * @throws IllegalStateException if data format error occurs
055     * @throws java.io.UncheckedIOException if an I/O error occurs
056     */
057    public TriangleMesh readTriangleMesh() {
058        PolygonObjParser.Face face;
059        Vector3D definedNormal;
060        Iterator<PolygonObjParser.VertexAttributes> attrs;
061        while ((face = readFace()) != null) {
062            // get the face attributes in the proper counter-clockwise orientation
063            definedNormal = face.getDefinedCompositeNormal(normals::get);
064            attrs = face.getVertexAttributesCounterClockwise(definedNormal, meshBuilder::getVertex).iterator();
065
066            // add the face vertices using a triangle fan
067            final int p0 = attrs.next().getVertexIndex();
068            int p1 = attrs.next().getVertexIndex();
069            int p2;
070
071            while (attrs.hasNext()) {
072                p2 = attrs.next().getVertexIndex();
073
074                meshBuilder.addFace(p0, p1, p2);
075
076                p1 = p2;
077            }
078        }
079
080        return meshBuilder.build();
081    }
082
083    /** {@inheritDoc} */
084    @Override
085    protected void handleVertex(final Vector3D vertex) {
086        meshBuilder.addVertex(vertex);
087    }
088
089    /** {@inheritDoc} */
090    @Override
091    protected void handleNormal(final Vector3D normal) {
092        normals.add(normal);
093    }
094}