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;
18  
19  import java.util.ArrayList;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.NoSuchElementException;
23  import java.util.Spliterator;
24  import java.util.Spliterators;
25  import java.util.stream.Stream;
26  import java.util.stream.StreamSupport;
27  
28  import org.apache.commons.geometry.euclidean.threed.BoundaryList3D;
29  import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
30  import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
31  import org.apache.commons.geometry.euclidean.threed.Triangle3D;
32  import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh;
33  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
34  import org.apache.commons.geometry.io.core.input.GeometryInput;
35  import org.apache.commons.geometry.io.core.internal.GeometryIOUtils;
36  import org.apache.commons.numbers.core.Precision;
37  
38  /** Abstract base class for {@link BoundaryReadHandler3D} implementations.
39   */
40  public abstract class AbstractBoundaryReadHandler3D implements BoundaryReadHandler3D {
41  
42      /** {@inheritDoc} */
43      @Override
44      public BoundarySource3D read(final GeometryInput in, final Precision.DoubleEquivalence precision) {
45          // read the input as a simple list of boundaries
46          final List<PlaneConvexSubset> list = new ArrayList<>();
47  
48          try (FacetDefinitionReader reader = facetDefinitionReader(in)) {
49              FacetDefinition facet;
50              while ((facet = reader.readFacet()) != null) {
51                  list.add(FacetDefinitions.toPolygon(facet, precision));
52              }
53          }
54  
55          return new BoundaryList3D(list);
56      }
57  
58      /** {@inheritDoc} */
59      @Override
60      public TriangleMesh readTriangleMesh(final GeometryInput in, final Precision.DoubleEquivalence precision) {
61          final SimpleTriangleMesh.Builder meshBuilder = SimpleTriangleMesh.builder(precision);
62  
63          try (FacetDefinitionReader reader = facetDefinitionReader(in)) {
64              FacetDefinition facet;
65              while ((facet = reader.readFacet()) != null) {
66                  for (final Triangle3D tri : FacetDefinitions.toPolygon(facet, precision).toTriangles()) {
67                      meshBuilder.addFaceUsingVertices(
68                          tri.getPoint1(),
69                          tri.getPoint2(),
70                          tri.getPoint3()
71                      );
72                  }
73              }
74          }
75  
76          return meshBuilder.build();
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public Stream<PlaneConvexSubset> boundaries(final GeometryInput in, final Precision.DoubleEquivalence precision) {
82          return facets(in)
83                  .map(f -> FacetDefinitions.toPolygon(f, precision));
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      public Stream<FacetDefinition> facets(final GeometryInput in) {
89          return GeometryIOUtils.createCloseableStream(inputStream -> {
90              final FacetDefinitionReader fdReader = facetDefinitionReader(in);
91              final FacetDefinitionReaderIterator it = new FacetDefinitionReaderIterator(fdReader);
92  
93              return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED), false);
94          }, in::getInputStream);
95      }
96  
97      /** Class exposing a {@link FacetDefinitionReader} as an iterator.
98       */
99      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 }