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;
018
019import java.util.ArrayList;
020import java.util.Iterator;
021import java.util.List;
022import java.util.NoSuchElementException;
023import java.util.Spliterator;
024import java.util.Spliterators;
025import java.util.stream.Stream;
026import java.util.stream.StreamSupport;
027
028import org.apache.commons.geometry.euclidean.threed.BoundaryList3D;
029import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
030import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
031import org.apache.commons.geometry.euclidean.threed.Triangle3D;
032import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh;
033import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
034import org.apache.commons.geometry.io.core.input.GeometryInput;
035import org.apache.commons.geometry.io.core.internal.GeometryIOUtils;
036import org.apache.commons.numbers.core.Precision;
037
038/** Abstract base class for {@link BoundaryReadHandler3D} implementations.
039 */
040public abstract class AbstractBoundaryReadHandler3D implements BoundaryReadHandler3D {
041
042    /** {@inheritDoc} */
043    @Override
044    public BoundarySource3D read(final GeometryInput in, final Precision.DoubleEquivalence precision) {
045        // read the input as a simple list of boundaries
046        final List<PlaneConvexSubset> list = new ArrayList<>();
047
048        try (FacetDefinitionReader reader = facetDefinitionReader(in)) {
049            FacetDefinition facet;
050            while ((facet = reader.readFacet()) != null) {
051                list.add(FacetDefinitions.toPolygon(facet, precision));
052            }
053        }
054
055        return new BoundaryList3D(list);
056    }
057
058    /** {@inheritDoc} */
059    @Override
060    public TriangleMesh readTriangleMesh(final GeometryInput in, final Precision.DoubleEquivalence precision) {
061        final SimpleTriangleMesh.Builder meshBuilder = SimpleTriangleMesh.builder(precision);
062
063        try (FacetDefinitionReader reader = facetDefinitionReader(in)) {
064            FacetDefinition facet;
065            while ((facet = reader.readFacet()) != null) {
066                for (final Triangle3D tri : FacetDefinitions.toPolygon(facet, precision).toTriangles()) {
067                    meshBuilder.addFaceUsingVertices(
068                        tri.getPoint1(),
069                        tri.getPoint2(),
070                        tri.getPoint3()
071                    );
072                }
073            }
074        }
075
076        return meshBuilder.build();
077    }
078
079    /** {@inheritDoc} */
080    @Override
081    public Stream<PlaneConvexSubset> boundaries(final GeometryInput in, final Precision.DoubleEquivalence precision) {
082        return facets(in)
083                .map(f -> FacetDefinitions.toPolygon(f, precision));
084    }
085
086    /** {@inheritDoc} */
087    @Override
088    public Stream<FacetDefinition> facets(final GeometryInput in) {
089        return GeometryIOUtils.createCloseableStream(inputStream -> {
090            final FacetDefinitionReader fdReader = facetDefinitionReader(in);
091            final FacetDefinitionReaderIterator it = new FacetDefinitionReaderIterator(fdReader);
092
093            return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED), false);
094        }, in::getInputStream);
095    }
096
097    /** Class exposing a {@link FacetDefinitionReader} as an iterator.
098     */
099    static final class FacetDefinitionReaderIterator implements Iterator<FacetDefinition> {
100
101        /** Reader supplying the facets for iteration. */
102        private final FacetDefinitionReader reader;
103
104        /** Number of facets read from the reader. */
105        private int loadCount = 0;
106
107        /** Next facet to return from the instance; may be null. */
108        private FacetDefinition next;
109
110        /** Construct a new iterator instance that iterates through the facets available from the
111         * argument.
112         * @param reader read supplying facets for iteration
113         */
114        FacetDefinitionReaderIterator(final FacetDefinitionReader reader) {
115            this.reader = reader;
116        }
117
118        /** {@inheritDoc} */
119        @Override
120        public boolean hasNext() {
121            ensureLoaded();
122            return next != null;
123        }
124
125        /** {@inheritDoc} */
126        @Override
127        public FacetDefinition next() {
128            if (!hasNext()) {
129                throw new NoSuchElementException();
130            }
131
132            final FacetDefinition result = next;
133            loadNext();
134
135            return result;
136        }
137
138        /** Ensure that the instance has attempted to load at least one facet from
139         * the underlying reader.
140         */
141        private void ensureLoaded() {
142            if (loadCount < 1) {
143                loadNext();
144            }
145        }
146
147        /** Load the next facet from the underlying reader.
148         */
149        private void loadNext() {
150            ++loadCount;
151            next = reader.readFacet();
152        }
153    }
154}