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.Collection;
20 import java.util.stream.Stream;
21
22 import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
23 import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
24 import org.apache.commons.geometry.euclidean.threed.Triangle3D;
25 import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
26 import org.apache.commons.geometry.io.core.BoundaryIOManager;
27 import org.apache.commons.geometry.io.core.GeometryFormat;
28 import org.apache.commons.geometry.io.core.input.GeometryInput;
29 import org.apache.commons.geometry.io.core.output.GeometryOutput;
30 import org.apache.commons.geometry.io.euclidean.threed.obj.ObjBoundaryReadHandler3D;
31 import org.apache.commons.geometry.io.euclidean.threed.obj.ObjBoundaryWriteHandler3D;
32 import org.apache.commons.geometry.io.euclidean.threed.stl.StlBoundaryReadHandler3D;
33 import org.apache.commons.geometry.io.euclidean.threed.stl.StlBoundaryWriteHandler3D;
34 import org.apache.commons.geometry.io.euclidean.threed.txt.CsvBoundaryReadHandler3D;
35 import org.apache.commons.geometry.io.euclidean.threed.txt.CsvBoundaryWriteHandler3D;
36 import org.apache.commons.geometry.io.euclidean.threed.txt.TextBoundaryReadHandler3D;
37 import org.apache.commons.geometry.io.euclidean.threed.txt.TextBoundaryWriteHandler3D;
38 import org.apache.commons.numbers.core.Precision;
39
40 /** Class managing IO operations for geometric data formats containing 3D region boundaries.
41 * IO operation are performed by read and write handlers registered for specific data formats.
42 *
43 * <p><strong>Implementation note:</strong>Instances of this class are thread-safe as long as the
44 * registered handler instances are thread-safe.</p>
45 * @see BoundaryReadHandler3D
46 * @see BoundaryWriteHandler3D
47 * @see <a href="https://en.wikipedia.org/wiki/Boundary_representations">Boundary representations</a>
48 */
49 public class BoundaryIOManager3D extends BoundaryIOManager<
50 PlaneConvexSubset,
51 BoundarySource3D,
52 BoundaryReadHandler3D,
53 BoundaryWriteHandler3D> {
54
55 /** Get a {@link FacetDefinitionReader} for reading facet information from the given input.
56 * @param in input to read facets from
57 * @param fmt format of the input; if null, the format is determined implicitly from the
58 * file extension of the input {@link GeometryInput#getFileName() file name}
59 * @return facet definition reader
60 * @throws IllegalArgumentException if no read handler can be found for the input format
61 * @throws IllegalStateException if a data format error occurs
62 * @throws java.io.UncheckedIOException if an I/O error occurs
63 */
64 public FacetDefinitionReader facetDefinitionReader(final GeometryInput in, final GeometryFormat fmt) {
65 return requireReadHandler(in, fmt).facetDefinitionReader(in);
66 }
67
68 /** Return a {@link Stream} providing access to all facets from the given input. The underlying input
69 * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
70 * in a try-with-resources statement to ensure that all resources are properly released.
71 * <pre>
72 * try (Stream<FacetDefinition> stream = manager.facets(in, fmt)) {
73 * // access stream content
74 * }
75 * </pre>
76 * <p>The following exceptions may be thrown during stream iteration:</p>
77 * <ul>
78 * <li>{@link IllegalStateException} if a data format error occurs</li>
79 * <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
80 * </ul>
81 * @param in input to read from
82 * @param fmt format of the input; if null, the format is determined implicitly from the
83 * file extension of the input {@link GeometryInput#getFileName() file name}
84 * @return stream providing access to the facets in the input
85 * @throws IllegalArgumentException if no read handler can be found for the input format
86 * @throws IllegalStateException if a data format error occurs during stream creation
87 * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
88 */
89 public Stream<FacetDefinition> facets(final GeometryInput in, final GeometryFormat fmt) {
90 return requireReadHandler(in, fmt).facets(in);
91 }
92
93 /** Return a {@link Stream} providing access to all triangles from the given input. The underlying input
94 * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
95 * in a try-with-resources statement to ensure that all resources are properly released.
96 * <pre>
97 * try (Stream<Triangle3D> stream = manager.triangles(in, fmt, precision)) {
98 * // access stream content
99 * }
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 }