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.Collection;
020import java.util.stream.Stream;
021
022import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
023import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
024import org.apache.commons.geometry.euclidean.threed.Triangle3D;
025import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
026import org.apache.commons.geometry.io.core.BoundaryIOManager;
027import org.apache.commons.geometry.io.core.GeometryFormat;
028import org.apache.commons.geometry.io.core.input.GeometryInput;
029import org.apache.commons.geometry.io.core.output.GeometryOutput;
030import org.apache.commons.geometry.io.euclidean.threed.obj.ObjBoundaryReadHandler3D;
031import org.apache.commons.geometry.io.euclidean.threed.obj.ObjBoundaryWriteHandler3D;
032import org.apache.commons.geometry.io.euclidean.threed.stl.StlBoundaryReadHandler3D;
033import org.apache.commons.geometry.io.euclidean.threed.stl.StlBoundaryWriteHandler3D;
034import org.apache.commons.geometry.io.euclidean.threed.txt.CsvBoundaryReadHandler3D;
035import org.apache.commons.geometry.io.euclidean.threed.txt.CsvBoundaryWriteHandler3D;
036import org.apache.commons.geometry.io.euclidean.threed.txt.TextBoundaryReadHandler3D;
037import org.apache.commons.geometry.io.euclidean.threed.txt.TextBoundaryWriteHandler3D;
038import org.apache.commons.numbers.core.Precision;
039
040/** Class managing IO operations for geometric data formats containing 3D region boundaries.
041 * IO operation are performed by read and write handlers registered for specific data formats.
042 *
043 * <p><strong>Implementation note:</strong>Instances of this class are thread-safe as long as the
044 * registered handler instances are thread-safe.</p>
045 * @see BoundaryReadHandler3D
046 * @see BoundaryWriteHandler3D
047 * @see <a href="https://en.wikipedia.org/wiki/Boundary_representations">Boundary representations</a>
048 */
049public class BoundaryIOManager3D extends BoundaryIOManager<
050        PlaneConvexSubset,
051        BoundarySource3D,
052        BoundaryReadHandler3D,
053        BoundaryWriteHandler3D> {
054
055    /** Get a {@link FacetDefinitionReader} for reading facet information from the given input.
056     * @param in input to read facets from
057     * @param fmt format of the input; if null, the format is determined implicitly from the
058     *      file extension of the input {@link GeometryInput#getFileName() file name}
059     * @return facet definition reader
060     * @throws IllegalArgumentException if no read handler can be found for the input format
061     * @throws IllegalStateException if a data format error occurs
062     * @throws java.io.UncheckedIOException if an I/O error occurs
063     */
064    public FacetDefinitionReader facetDefinitionReader(final GeometryInput in, final GeometryFormat fmt) {
065        return requireReadHandler(in, fmt).facetDefinitionReader(in);
066    }
067
068    /** Return a {@link Stream} providing access to all facets from the given input. The underlying input
069     * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
070     * in a try-with-resources statement to ensure that all resources are properly released.
071     * <pre>
072     *  try (Stream&lt;FacetDefinition&gt; stream = manager.facets(in, fmt)) {
073     *      // access stream content
074     *  }
075     * </pre>
076     * <p>The following exceptions may be thrown during stream iteration:</p>
077     * <ul>
078     *  <li>{@link IllegalStateException} if a data format error occurs</li>
079     *  <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
080     * </ul>
081     * @param in input to read from
082     * @param fmt format of the input; if null, the format is determined implicitly from the
083     *      file extension of the input {@link GeometryInput#getFileName() file name}
084     * @return stream providing access to the facets in the input
085     * @throws IllegalArgumentException if no read handler can be found for the input format
086     * @throws IllegalStateException if a data format error occurs during stream creation
087     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
088     */
089    public Stream<FacetDefinition> facets(final GeometryInput in, final GeometryFormat fmt) {
090        return requireReadHandler(in, fmt).facets(in);
091    }
092
093    /** Return a {@link Stream} providing access to all triangles from the given input. The underlying input
094     * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
095     * in a try-with-resources statement to ensure that all resources are properly released.
096     * <pre>
097     *  try (Stream&lt;Triangle3D&gt; stream = manager.triangles(in, fmt, precision)) {
098     *      // access stream content
099     *  }
100     * </pre>
101     * <p>The following exceptions may be thrown during stream iteration:</p>
102     * <ul>
103     *  <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
104     *  <li>{@link IllegalStateException} if a data format error occurs</li>
105     *  <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
106     * </ul>
107     * @param in input to read from
108     * @param fmt format of the input; if null, the format is determined implicitly from the
109     *      file extension of the input {@link GeometryInput#getFileName() file name}
110     * @param precision precision context used for floating point comparisons
111     * @return stream providing access to the triangles in the input
112     * @throws IllegalArgumentException if no read handler can be found for the input format
113     * @throws IllegalStateException if a data format error occurs during stream creation
114     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
115     */
116    public Stream<Triangle3D> triangles(final GeometryInput in, final GeometryFormat fmt,
117            final Precision.DoubleEquivalence precision) {
118        return boundaries(in, fmt, precision)
119                .flatMap(p -> p.toTriangles().stream());
120    }
121
122    /** Return a {@link TriangleMesh} containing all triangles from the given input.
123     * @param in input to read from
124     * @param fmt format of the input; if null, the format is determined implicitly from the
125     *      file extension of the input {@link GeometryInput#getFileName() file name}
126     * @param precision precision context used for floating point comparisons
127     * @return mesh containing all triangles from the input
128     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read
129     *      handler can be found for the input format
130     * @throws IllegalStateException if a data format error occurs
131     * @throws java.io.UncheckedIOException if an I/O error occurs
132     */
133    public TriangleMesh readTriangleMesh(final GeometryInput in, final GeometryFormat fmt,
134            final Precision.DoubleEquivalence precision) {
135        return requireReadHandler(in, fmt).readTriangleMesh(in, precision);
136    }
137
138    /** Write all boundaries in the stream to the output.
139     *
140     * <p>This method does not explicitly close the {@code boundaries} stream. If callers need to ensure that
141     * the stream is closed (for example, if the stream is reading from a file), they should use it in a
142     * try-with-resources statement outside of this method.</p>
143     * @param boundaries stream containing boundaries to write
144     * @param out output to write to
145     * @param fmt format of the output; if null, the format is determined implicitly from the
146     *      file extension of the output {@link GeometryOutput#getFileName() file name}
147     * @throws IllegalArgumentException if no write handler can be found for the output format
148     * @throws java.io.UncheckedIOException if an I/O error occurs
149     */
150    public void write(final Stream<? extends PlaneConvexSubset> boundaries, final GeometryOutput out,
151            final GeometryFormat fmt) {
152        requireWriteHandler(out, fmt).write(boundaries, out);
153    }
154
155    /** Write all facet in the stream to the output.
156     *
157     * <p>This method does not explicitly close the {@code boundaries} stream. If callers need to ensure that
158     * the stream is closed (for example, if the stream is reading from a file), they should use it in a
159     * try-with-resources statement outside of this method.</p>
160     * @param facets stream containing facets to write
161     * @param out output to write to
162     * @param fmt format of the output; if null, the format is determined implicitly from the
163     *      file extension of the output {@link GeometryOutput#getFileName() file name}
164     * @throws IllegalArgumentException if no write handler can be found for the output format
165     * @throws java.io.UncheckedIOException if an I/O error occurs
166     */
167    public void writeFacets(final Stream<? extends FacetDefinition> facets, final GeometryOutput out,
168            final GeometryFormat fmt) {
169        requireWriteHandler(out, fmt).writeFacets(facets, out);
170    }
171
172    /** Write the given facets to the output.
173     * @param facets facets to write
174     * @param out output to write to
175     * @param fmt format of the output; if null, the format is determined implicitly from the
176     *      file extension of the output {@link GeometryOutput#getFileName() file name}
177     * @throws IllegalArgumentException if no write handler can be found for the output format
178     * @throws java.io.UncheckedIOException if an I/O error occurs
179     */
180    public void writeFacets(final Collection<? extends FacetDefinition> facets, final GeometryOutput out,
181            final GeometryFormat fmt) {
182        requireWriteHandler(out, fmt).writeFacets(facets, out);
183    }
184
185    /** Register default read/write handlers. This method registers a read and write handler
186     * for each value in {@link GeometryFormat3D}.
187     */
188    public void registerDefaultHandlers() {
189        // obj
190        registerReadHandler(new ObjBoundaryReadHandler3D());
191        registerWriteHandler(new ObjBoundaryWriteHandler3D());
192
193        // stl
194        registerReadHandler(new StlBoundaryReadHandler3D());
195        registerWriteHandler(new StlBoundaryWriteHandler3D());
196
197        // txt
198        registerReadHandler(new TextBoundaryReadHandler3D());
199        registerWriteHandler(new TextBoundaryWriteHandler3D());
200
201        // csv
202        registerReadHandler(new CsvBoundaryReadHandler3D());
203        registerWriteHandler(new CsvBoundaryWriteHandler3D());
204    }
205}