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.net.URL;
020import java.nio.file.Path;
021import java.util.Collection;
022import java.util.stream.Stream;
023
024import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
025import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
026import org.apache.commons.geometry.euclidean.threed.Triangle3D;
027import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
028import org.apache.commons.geometry.io.core.GeometryFormat;
029import org.apache.commons.geometry.io.core.input.FileGeometryInput;
030import org.apache.commons.geometry.io.core.input.GeometryInput;
031import org.apache.commons.geometry.io.core.input.UrlGeometryInput;
032import org.apache.commons.geometry.io.core.output.FileGeometryOutput;
033import org.apache.commons.geometry.io.core.output.GeometryOutput;
034import org.apache.commons.numbers.core.Precision;
035
036/** Utility class providing convenient access to 3D IO functionality. The static read and write methods here
037 * delegate to a default {@link #getDefaultManager() BoundaryIOManager3D} instance. The default
038 * configuration should be sufficient for most purposes. If customization is required, consider directly
039 * creating and configuring and a {@link BoundaryIOManager3D} instance.
040 *
041 * <p><strong>Examples</strong></p>
042 * <p>The example below reads an OBJ file as a stream of triangles, transforms each triangle, and writes the
043 * result as a CSV file. The data formats are inferred from the input and output file extensions.</p>
044 * <pre>
045 * GeometryInput input = new FileGeometryInput(Paths.get("orig.obj"));
046 * GeometryOutput scaledOutput = new FileGeometryOutput(Paths.get("scaled.csv"));
047 * AffineTransformMatrix3D transform = AffineTransformMatrix3D.createScale(2);
048 *
049 * // Use the input triangle stream in a try-with-resources statement to ensure
050 * // all resources are properly released.
051 * try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(input, null, precision)) {
052 *      IO3D.write(stream.map(t -&gt; t.transform(transform)), scaledOutput, null);
053 * }
054 * </pre>
055 * @see BoundaryIOManager3D
056 */
057public final class IO3D {
058
059    /** Utility class; no instantiation. */
060    private IO3D() {}
061
062    /** Get a {@link FacetDefinitionReader} for reading facet information from the given file path.
063     * The data format is determined by the file extension of the argument.
064     * @param path path to obtain a reader for
065     * @return facet definition reader
066     * @throws IllegalArgumentException if no handler has been registered with the
067     *      {@link #getDefaultManager() default manager} for the input format
068     * @throws IllegalStateException if a data format error occurs
069     * @throws java.io.UncheckedIOException if an I/O error occurs
070     * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
071     */
072    public static FacetDefinitionReader facetDefinitionReader(final Path path) {
073        return facetDefinitionReader(new FileGeometryInput(path), null);
074    }
075
076    /** Get a {@link FacetDefinitionReader} for reading facet information from the given URL.
077     * The data format is determined by the file extension of the argument.
078     * @param url URL to read from
079     * @return facet definition reader
080     * @throws IllegalArgumentException if no handler has been registered with the
081     *      {@link #getDefaultManager() default manager} for the input format
082     * @throws IllegalStateException if a data format error occurs
083     * @throws java.io.UncheckedIOException if an I/O error occurs
084     * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
085     */
086    public static FacetDefinitionReader facetDefinitionReader(final URL url) {
087        return facetDefinitionReader(new UrlGeometryInput(url), null);
088    }
089
090    /** Get a {@link FacetDefinitionReader} for reading facet information from the given input.
091     * @param in input to read from
092     * @param fmt format of the input; if null, the format is determined implicitly from the
093     *      file extension of the input {@link GeometryInput#getFileName() file name}
094     * @return facet definition reader
095     * @throws IllegalArgumentException if no handler has been registered with the
096     *      {@link #getDefaultManager() default manager} for the input format
097     * @throws IllegalStateException if a data format error occurs
098     * @throws java.io.UncheckedIOException if an I/O error occurs
099     * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
100     */
101    public static FacetDefinitionReader facetDefinitionReader(final GeometryInput in, final GeometryFormat fmt) {
102        return getDefaultManager().facetDefinitionReader(in, fmt);
103    }
104
105    /** Return a {@link Stream} providing access to all facets from the given file path. The data format
106     * is determined by the file extension of the argument.
107     *
108     * <p>The underlying input stream is closed when the returned stream is closed. Callers should
109     * therefore use the returned stream in a try-with-resources statement to ensure that all
110     * resources are properly released. Ex:
111     * </p>
112     * <pre>
113     *  try (Stream&lt;FacetDefinition&gt; stream = IO3D.facets(path)) {
114     *      // access stream content
115     *  }
116     * </pre>
117     * <p>The following exceptions may be thrown during stream iteration:
118     *  <ul>
119     *      <li>{@link IllegalStateException} if a data format error occurs</li>
120     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
121     *  </ul>
122     * @param path file path to read from
123     * @return stream providing access to the facets in the specified file
124     * @throws IllegalArgumentException if no handler has been registered with the
125     *      {@link #getDefaultManager() default manager} for the input format
126     * @throws IllegalStateException if a data format error occurs during stream creation
127     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
128     * @see BoundaryIOManager3D#facets(GeometryInput, GeometryFormat)
129     */
130    public static Stream<FacetDefinition> facets(final Path path) {
131        return facets(new FileGeometryInput(path), null);
132    }
133
134    /** Return a {@link Stream} providing access to all facets from the given URL. he data format
135     * is determined by the file extension of the argument.
136     *
137     * <p>The underlying input stream is closed when the returned stream is closed. Callers should
138     * therefore use the returned stream in a try-with-resources statement to ensure that all
139     * resources are properly released. Ex:
140     * </p>
141     * <pre>
142     *  try (Stream&lt;FacetDefinition&gt; stream = IO3D.facets(url)) {
143     *      // access stream content
144     *  }
145     * </pre>
146     * <p>The following exceptions may be thrown during stream iteration:
147     *  <ul>
148     *      <li>{@link IllegalStateException} if a data format error occurs</li>
149     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
150     *  </ul>
151     * @param url URL to read from
152     * @return stream providing access to the facets from the specified URL
153     * @throws IllegalArgumentException if no handler has been registered with the
154     *      {@link #getDefaultManager() default manager} for the input format
155     * @throws IllegalStateException if a data format error occurs during stream creation
156     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
157     * @see BoundaryIOManager3D#facets(GeometryInput, GeometryFormat)
158     */
159    public static Stream<FacetDefinition> facets(final URL url) {
160        return facets(new UrlGeometryInput(url), null);
161    }
162
163    /** Return a {@link Stream} providing access to all facets from the given input. The underlying input
164     * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
165     * in a try-with-resources statement to ensure that all resources are properly released.
166     * <pre>
167     *  try (Stream&lt;FacetDefinition&gt; stream = IO3D.facets(in, fmt)) {
168     *      // access stream content
169     *  }
170     * </pre>
171     * <p>The following exceptions may be thrown during stream iteration:
172     *  <ul>
173     *      <li>{@link IllegalStateException} if a data format error occurs</li>
174     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
175     *  </ul>
176     * @param in input to read from
177     * @param fmt format of the input; if null, the format is determined implicitly from the
178     *      file extension of the input {@link GeometryInput#getFileName() file name}
179     * @return stream providing access to the facets in the input
180     * @throws IllegalArgumentException if no read handler has been registered with the
181     *      {@link #getDefaultManager() default manager} for the input format
182     * @throws IllegalStateException if a data format error occurs during stream creation
183     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
184     * @see BoundaryIOManager3D#facets(GeometryInput, GeometryFormat)
185     */
186    public static Stream<FacetDefinition> facets(final GeometryInput in, final GeometryFormat fmt) {
187        return getDefaultManager().facets(in, fmt);
188    }
189
190    /** Return a {@link Stream} providing access to all boundaries from the given file path. The
191     * data format is determined by the file extension of the argument.
192     *
193     * <p>The underlying input stream is closed when the returned stream is closed. Callers should
194     * therefore use the returned stream in a try-with-resources statement to ensure that all
195     * resources are properly released. Ex:
196     * </p>
197     * <pre>
198     *  try (Stream&lt;PlaneConvexSubset&gt; stream = IO3D.boundaries(path, precision)) {
199     *      // access stream content
200     *  }
201     * </pre>
202     * <p>The following exceptions may be thrown during stream iteration:
203     *  <ul>
204     *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
205     *      <li>{@link IllegalStateException} if a data format error occurs</li>
206     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
207     *  </ul>
208     * @param path file path to read from
209     * @param precision precision context used for floating point comparisons
210     * @return stream providing access to the boundaries in the specified file
211     * @throws IllegalArgumentException if no read handler has been registered with the
212     *      {@link #getDefaultManager() default manager} for the input format
213     * @throws IllegalStateException if a data format error occurs during stream creation
214     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
215     * @see BoundaryIOManager3D#boundaries(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
216     */
217    public static Stream<PlaneConvexSubset> boundaries(final Path path, final Precision.DoubleEquivalence precision) {
218        return boundaries(new FileGeometryInput(path), null, precision);
219    }
220
221    /** Return a {@link Stream} providing access to all boundaries from the given URL. The data
222     * format is determined by the file extension of the argument.
223     *
224     * <p>The underlying input stream is closed when the returned stream is closed. Callers should
225     * therefore use the returned stream in a try-with-resources statement to ensure that all
226     * resources are properly released. Ex:
227     * </p>
228     * <pre>
229     *  try (Stream&lt;PlaneConvexSubset&gt; stream = IO3D.boundaries(url, precision)) {
230     *      // access stream content
231     *  }
232     * </pre>
233     * <p>The following exceptions may be thrown during stream iteration:
234     *  <ul>
235     *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
236     *      <li>{@link IllegalStateException} if a data format error occurs</li>
237     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
238     *  </ul>
239     * @param url URL to read from
240     * @param precision precision context used for floating point comparisons
241     * @return stream providing access to the boundaries in the specified URL
242     * @throws IllegalArgumentException if no read handler has been registered with the
243     *      {@link #getDefaultManager() default manager} for the input format
244     * @throws IllegalStateException if a data format error occurs during stream creation
245     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
246     * @see BoundaryIOManager3D#boundaries(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
247     */
248    public static Stream<PlaneConvexSubset> boundaries(final URL url, final Precision.DoubleEquivalence precision) {
249        return boundaries(new UrlGeometryInput(url), null, precision);
250    }
251
252    /** Return a {@link Stream} providing access to all boundaries from the given input. The underlying input
253     * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
254     * in a try-with-resources statement to ensure that all resources are properly released. Ex:
255     * <pre>
256     *  try (Stream&lt;H&gt; stream = IO3D.boundaries(in, fmt, precision)) {
257     *      // access stream content
258     *  }
259     *  </pre>
260     * <p>The following exceptions may be thrown during stream iteration:
261     *  <ul>
262     *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
263     *      <li>{@link IllegalStateException} if a data format error occurs</li>
264     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
265     *  </ul>
266     * @param in input to read boundaries from
267     * @param fmt format of the input; if null, the format is determined implicitly from the
268     *      file extension of the input {@link GeometryInput#getFileName() file name}
269     * @param precision precision context used for floating point comparisons
270     * @return stream providing access to the boundaries in the input
271     * @throws IllegalArgumentException if no read handler is registered with the
272     *      {@link #getDefaultManager() default manager} for the input format
273     * @throws IllegalStateException if a data format error occurs during stream creation
274     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
275     * @see BoundaryIOManager3D#boundaries(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
276     */
277    public static Stream<PlaneConvexSubset> boundaries(final GeometryInput in, final GeometryFormat fmt,
278            final Precision.DoubleEquivalence precision) {
279        return getDefaultManager().boundaries(in, fmt, precision);
280    }
281
282    /** Return a {@link Stream} providing access to all triangles from the given file path. The data
283     * format is determined by the file extension of the argument.
284     *
285     * <p>The underlying input stream is closed when the returned stream is closed. Callers should
286     * therefore use the returned stream in a try-with-resources statement to ensure that all
287     * resources are properly released. Ex:
288     * </p>
289     * <pre>
290     *  try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(path, precision)) {
291     *      // access stream content
292     *  }
293     * </pre>
294     * <p>The following exceptions may be thrown during stream iteration:
295     *  <ul>
296     *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
297     *      <li>{@link IllegalStateException} if a data format error occurs</li>
298     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
299     *  </ul>
300     * @param path file path to read from
301     * @param precision precision context used for floating point comparisons
302     * @return stream providing access to the triangles in the specified file
303     * @throws IllegalArgumentException if no read handler is registered with the
304     *      {@link #getDefaultManager() default manager} for the input format
305     * @throws IllegalStateException if a data format error occurs during stream creation
306     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
307     * @see BoundaryIOManager3D#triangles(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
308     */
309    public static Stream<Triangle3D> triangles(final Path path, final Precision.DoubleEquivalence precision) {
310        return triangles(new FileGeometryInput(path), null, precision);
311    }
312
313    /** Return a {@link Stream} providing access to all triangles from the given URL. The data format
314     * is determined by the file extension of the argument.
315     *
316     * <p>The underlying input stream is closed when the returned stream is closed. Callers should
317     * therefore use the returned stream in a try-with-resources statement to ensure that all
318     * resources are properly released. Ex:
319     * </p>
320     * <pre>
321     *  try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(url, precision)) {
322     *      // access stream content
323     *  }
324     * </pre>
325     * <p>The following exceptions may be thrown during stream iteration:
326     *  <ul>
327     *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
328     *      <li>{@link IllegalStateException} if a data format error occurs</li>
329     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
330     *  </ul>
331     * @param url URL to read from
332     * @param precision precision context used for floating point comparisons
333     * @return stream providing access to the triangles from the specified URL
334     * @throws IllegalArgumentException if no read handler is registered with the
335     *      {@link #getDefaultManager() default manager} for the input format
336     * @throws IllegalStateException if a data format error occurs during stream creation
337     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
338     * @see BoundaryIOManager3D#triangles(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
339     */
340    public static Stream<Triangle3D> triangles(final URL url, final Precision.DoubleEquivalence precision) {
341        return triangles(new UrlGeometryInput(url), null, precision);
342    }
343
344    /** Return a {@link Stream} providing access to all triangles from the given input. The underlying input
345     * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
346     * in a try-with-resources statement to ensure that all resources are properly released.
347     * <pre>
348     *  try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(in, fmt, precision)) {
349     *      // access stream content
350     *  }
351     * </pre>
352     * <p>The following exceptions may be thrown during stream iteration:
353     *  <ul>
354     *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
355     *      <li>{@link IllegalStateException} if a data format error occurs</li>
356     *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
357     *  </ul>
358     * @param in input to read from
359     * @param fmt format of the input; if null, the format is determined implicitly from the
360     *      file extension of the input {@link GeometryInput#getFileName() file name}
361     * @param precision precision context used for floating point comparisons
362     * @return stream providing access to the triangles in the input
363     * @throws IllegalArgumentException if no read handler is registered with the
364     *      {@link #getDefaultManager() default manager} for the input format
365     * @throws IllegalStateException if a data format error occurs during stream creation
366     * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
367     * @see BoundaryIOManager3D#triangles(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
368     */
369    public static Stream<Triangle3D> triangles(final GeometryInput in, final GeometryFormat fmt,
370            final Precision.DoubleEquivalence precision) {
371        return getDefaultManager().triangles(in, fmt, precision);
372    }
373
374    /** Return a {@link BoundarySource3D} containing all boundaries from the file at the
375     * given path. The data format is determined from the file extension. A runtime exception may be
376     * thrown if mathematically invalid boundaries are encountered.
377     * @param path file path to read from
378     * @param precision precision context used for floating point comparisons
379     * @return object containing all boundaries from the file at the given path
380     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
381     *      is registered with the {@link #getDefaultManager() default manager} for the input format
382     * @throws IllegalStateException if a data format error occurs
383     * @throws java.io.UncheckedIOException if an I/O error occurs
384     * @see BoundaryIOManager3D#read(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
385     */
386    public static BoundarySource3D read(final Path path, final Precision.DoubleEquivalence precision) {
387        return read(new FileGeometryInput(path), null, precision);
388    }
389
390    /** Return a {@link BoundarySource3D} containing all boundaries from the given URL. The data
391     * format is determined from the file extension of the URL path. A runtime exception may be
392     * thrown if mathematically invalid boundaries are encountered.
393     * @param url URL to read from
394     * @param precision precision context used for floating point comparisons
395     * @return object containing all boundaries from the given URL
396     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
397     *      is registered with the {@link #getDefaultManager() default manager} for the input format
398     * @throws IllegalStateException if a data format error occurs
399     * @throws java.io.UncheckedIOException if an I/O error occurs
400     * @see BoundaryIOManager3D#read(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
401     */
402    public static BoundarySource3D read(final URL url, final Precision.DoubleEquivalence precision) {
403        return read(new UrlGeometryInput(url), null, precision);
404    }
405
406    /** Return a {@link BoundarySource3D} containing all boundaries from the given input. A runtime
407     * exception may be thrown if mathematically invalid boundaries are encountered.
408     * @param in input to read boundaries from
409     * @param fmt format of the input; if null, the format is determined implicitly from the
410     *      file extension of the input {@link GeometryInput#getFileName() file name}
411     * @param precision precision context used for floating point comparisons
412     * @return object containing all boundaries from the input
413     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
414     *      is registered with the {@link #getDefaultManager() default manager} for the input format
415     * @throws IllegalStateException if a data format error occurs
416     * @throws java.io.UncheckedIOException if an I/O error occurs
417     * @see BoundaryIOManager3D#read(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
418     */
419    public static BoundarySource3D read(final GeometryInput in, final GeometryFormat fmt,
420            final Precision.DoubleEquivalence precision) {
421        return getDefaultManager().read(in, fmt, precision);
422    }
423
424    /** Return a {@link TriangleMesh} containing all triangles from the given file path. The data
425     * format is determined from the file extension of the path. A runtime exception may be
426     * thrown if mathematically invalid boundaries are encountered.
427     * @param path file path to read from
428     * @param precision precision context used for floating point comparisons
429     * @return mesh containing all triangles from the given file path
430     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
431     *      is registered with the {@link #getDefaultManager() default manager} for the input format
432     * @throws IllegalStateException if a data format error occurs
433     * @throws java.io.UncheckedIOException if an I/O error occurs
434     * @see BoundaryIOManager3D#readTriangleMesh(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
435     */
436    public static TriangleMesh readTriangleMesh(final Path path, final Precision.DoubleEquivalence precision) {
437        return readTriangleMesh(new FileGeometryInput(path), null, precision);
438    }
439
440    /** Return a {@link TriangleMesh} containing all triangles from the given URL. The data
441     * format is determined from the file extension of the URL path. A runtime exception may be
442     * thrown if mathematically invalid boundaries are encountered.
443     * @param url URL to read from
444     * @param precision precision context used for floating point comparisons
445     * @return mesh containing all triangles from the given URL
446     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
447     *      is registered with the {@link #getDefaultManager() default manager} for the input format
448     * @throws IllegalStateException if a data format error occurs
449     * @throws java.io.UncheckedIOException if an I/O error occurs
450     * @see BoundaryIOManager3D#readTriangleMesh(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
451     */
452    public static TriangleMesh readTriangleMesh(final URL url, final Precision.DoubleEquivalence precision) {
453        return readTriangleMesh(new UrlGeometryInput(url), null, precision);
454    }
455
456    /** Return a {@link TriangleMesh} containing all triangles from the given input. A runtime exception
457     * may be thrown if mathematically invalid boundaries are encountered.
458     * @param in input to read from
459     * @param fmt format of the input; if null, the format is determined implicitly from the
460     *      file extension of the input {@link GeometryInput#getFileName() file name}
461     * @param precision precision context used for floating point comparisons
462     * @return a mesh containing all triangles from the input
463     * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
464     *      is registered with the {@link #getDefaultManager() default manager} for the input format
465     * @throws IllegalStateException if a data format error occurs
466     * @throws java.io.UncheckedIOException if an I/O error occurs
467     * @see BoundaryIOManager3D#readTriangleMesh(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
468     */
469    public static TriangleMesh readTriangleMesh(final GeometryInput in, final GeometryFormat fmt,
470            final Precision.DoubleEquivalence precision) {
471        return getDefaultManager().readTriangleMesh(in, fmt, precision);
472    }
473
474    /** Write all boundaries in the stream to given file path. The data format is determined by
475     * the file extension of the target path. If the target path already exists, it is overwritten.
476     *
477     * <p>This method does not explicitly close the {@code boundaries} stream. Callers should use the stream
478     * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
479     * @param boundaries stream containing boundaries to write
480     * @param path file path to write to
481     * @throws IllegalArgumentException if no write handler is registered with the
482     *      {@link #getDefaultManager() default manager} for the output format
483     * @throws java.io.UncheckedIOException if an I/O error occurs
484     * @see BoundaryIOManager3D#write(Stream, GeometryOutput, GeometryFormat)
485     */
486    public static void write(final Stream<? extends PlaneConvexSubset> boundaries, final Path path) {
487        write(boundaries, new FileGeometryOutput(path), null);
488    }
489
490    /** Write all boundaries in the stream to the output.
491     *
492     * <p>This method does not explicitly close the {@code boundaries} stream. Callers should use the stream
493     * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
494     * @param boundaries stream containing boundaries to write
495     * @param out output to write to
496     * @param fmt format of the output; if null, the format is determined implicitly from the
497     *      file extension of the output {@link GeometryOutput#getFileName() file name}
498     * @throws IllegalArgumentException if no write handler is registered with the
499     *      {@link #getDefaultManager() default manager} for the output format
500     * @throws java.io.UncheckedIOException if an I/O error occurs
501     * @see BoundaryIOManager3D#write(Stream, GeometryOutput, GeometryFormat)
502     */
503    public static void write(final Stream<? extends PlaneConvexSubset> boundaries, final GeometryOutput out,
504            final GeometryFormat fmt) {
505        getDefaultManager().write(boundaries, out, fmt);
506    }
507
508    /** Write all boundaries from {@code src} to the given file path. The data format
509     * is determined by the file extension of the target path. If the target path already exists,
510     * it is overwritten.
511     * @param src boundary source containing the boundaries to write
512     * @param path file path to write to
513     * @throws IllegalArgumentException if no write handler is registered with the
514     *      {@link #getDefaultManager() default manager} for the output format
515     * @throws java.io.UncheckedIOException if an I/O error occurs
516     * @see org.apache.commons.geometry.io.core.BoundaryIOManager#write(
517     *      org.apache.commons.geometry.core.partitioning.BoundarySource, GeometryOutput, GeometryFormat)
518     */
519    public static void write(final BoundarySource3D src, final Path path) {
520        write(src, new FileGeometryOutput(path), null);
521    }
522
523    /** Write all boundaries from {@code src} to the given output.
524     * @param src boundary source containing the boundaries to write
525     * @param out output to write to
526     * @param fmt format of the output; if null, the format is determined implicitly from the
527     *      file extension of the output {@link GeometryOutput#getFileName() file name}
528     * @throws IllegalArgumentException if no write handler is registered with the
529     *      {@link #getDefaultManager() default manager} for the output format
530     * @throws java.io.UncheckedIOException if an I/O error occurs
531     * @see org.apache.commons.geometry.io.core.BoundaryIOManager#write(
532     *      org.apache.commons.geometry.core.partitioning.BoundarySource, GeometryOutput, GeometryFormat)
533     */
534    public static void write(final BoundarySource3D src, final GeometryOutput out, final GeometryFormat fmt) {
535        getDefaultManager().write(src, out, fmt);
536    }
537
538    /** Write the given facets to the file path. The data format is determined by the file extension of
539     * the target path. If the target path already exists, it is overwritten.
540     * @param facets facets to write
541     * @param path path to write to
542     * @throws IllegalArgumentException if no write handler is registered with the
543     *      {@link #getDefaultManager() default manager} for the output format
544     * @throws java.io.UncheckedIOException if an I/O error occurs
545     * @see BoundaryIOManager3D#writeFacets(Collection, GeometryOutput, GeometryFormat)
546     */
547    public static void writeFacets(final Collection<? extends FacetDefinition> facets, final Path path) {
548        writeFacets(facets, new FileGeometryOutput(path), null);
549    }
550
551    /** Write the given collection of facets to the output.
552     * @param facets facets to write
553     * @param out output to write to
554     * @param fmt format of the output; if null, the format is determined implicitly from the
555     *      file extension of the output {@link GeometryOutput#getFileName() file name}
556     * @throws IllegalArgumentException if no write handler is registered with the
557     *      {@link #getDefaultManager() default manager} for the output format
558     * @throws java.io.UncheckedIOException if an I/O error occurs
559     * @see BoundaryIOManager3D#writeFacets(Collection, GeometryOutput, GeometryFormat)
560     */
561    public static void writeFacets(final Collection<? extends FacetDefinition> facets, final GeometryOutput out,
562            final GeometryFormat fmt) {
563        getDefaultManager().writeFacets(facets, out, fmt);
564    }
565
566    /** Write all facets in the stream to the file path. The data format is determined by the file
567     * extension of the target path. If the target path already exists, it is overwritten.
568     *
569     * <p>This method does not explicitly close the {@code facets} stream. Callers should use the stream
570     * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
571     * @param facets stream containing facets to write
572     * @param path path to write to
573     * @throws IllegalArgumentException if no write handler is registered with the
574     *      {@link #getDefaultManager() default manager} for the output format
575     * @throws java.io.UncheckedIOException if an I/O error occurs
576     * @see BoundaryIOManager3D#writeFacets(Stream, GeometryOutput, GeometryFormat)
577     */
578    public static void writeFacets(final Stream<? extends FacetDefinition> facets, final Path path) {
579        writeFacets(facets, new FileGeometryOutput(path), null);
580    }
581
582    /** Write all facets in the stream to the output.
583     *
584     * <p>This method does not explicitly close the {@code facets} stream. Callers should use the stream
585     * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
586     * @param facets stream containing facets to write
587     * @param out output to write to
588     * @param fmt format of the output; if null, the format is determined implicitly from the
589     *      file extension of the output {@link GeometryOutput#getFileName() file name}
590     * @throws IllegalArgumentException if no write handler is registered with the
591     *      {@link #getDefaultManager() default manager} for the output format
592     * @throws java.io.UncheckedIOException if an I/O error occurs
593     * @see BoundaryIOManager3D#writeFacets(Stream, GeometryOutput, GeometryFormat)
594     */
595    public static void writeFacets(final Stream<? extends FacetDefinition> facets, final GeometryOutput out,
596            final GeometryFormat fmt) {
597        getDefaultManager().writeFacets(facets, out, fmt);
598    }
599
600    /** Get the default {@link BoundaryIOManager3D} instance.
601     * @return the default {@link BoundaryIOManager3D} instance
602     */
603    public static BoundaryIOManager3D getDefaultManager() {
604        return ManagerHolder.DEFAULT_MANAGER;
605    }
606
607    /** Class holding a reference to the default IO manager instance.
608     */
609    private static final class ManagerHolder {
610
611        /** Default IO manager instance. */
612        private static final BoundaryIOManager3D DEFAULT_MANAGER;
613
614        static {
615            DEFAULT_MANAGER = new BoundaryIOManager3D();
616            DEFAULT_MANAGER.registerDefaultHandlers();
617        }
618
619        /** Utility class; no instantiation. */
620        private ManagerHolder() {}
621    }
622}