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.net.URL;
20  import java.nio.file.Path;
21  import java.util.Collection;
22  import java.util.stream.Stream;
23  
24  import org.apache.commons.geometry.euclidean.threed.BoundarySource3D;
25  import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
26  import org.apache.commons.geometry.euclidean.threed.Triangle3D;
27  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
28  import org.apache.commons.geometry.io.core.GeometryFormat;
29  import org.apache.commons.geometry.io.core.input.FileGeometryInput;
30  import org.apache.commons.geometry.io.core.input.GeometryInput;
31  import org.apache.commons.geometry.io.core.input.UrlGeometryInput;
32  import org.apache.commons.geometry.io.core.output.FileGeometryOutput;
33  import org.apache.commons.geometry.io.core.output.GeometryOutput;
34  import org.apache.commons.numbers.core.Precision;
35  
36  /** Utility class providing convenient access to 3D IO functionality. The static read and write methods here
37   * delegate to a default {@link #getDefaultManager() BoundaryIOManager3D} instance. The default
38   * configuration should be sufficient for most purposes. If customization is required, consider directly
39   * creating and configuring and a {@link BoundaryIOManager3D} instance.
40   *
41   * <p><strong>Examples</strong></p>
42   * <p>The example below reads an OBJ file as a stream of triangles, transforms each triangle, and writes the
43   * result as a CSV file. The data formats are inferred from the input and output file extensions.</p>
44   * <pre>
45   * GeometryInput input = new FileGeometryInput(Paths.get("orig.obj"));
46   * GeometryOutput scaledOutput = new FileGeometryOutput(Paths.get("scaled.csv"));
47   * AffineTransformMatrix3D transform = AffineTransformMatrix3D.createScale(2);
48   *
49   * // Use the input triangle stream in a try-with-resources statement to ensure
50   * // all resources are properly released.
51   * try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(input, null, precision)) {
52   *      IO3D.write(stream.map(t -&gt; t.transform(transform)), scaledOutput, null);
53   * }
54   * </pre>
55   * @see BoundaryIOManager3D
56   */
57  public final class IO3D {
58  
59      /** Utility class; no instantiation. */
60      private IO3D() {}
61  
62      /** Get a {@link FacetDefinitionReader} for reading facet information from the given file path.
63       * The data format is determined by the file extension of the argument.
64       * @param path path to obtain a reader for
65       * @return facet definition reader
66       * @throws IllegalArgumentException if no handler has been registered with the
67       *      {@link #getDefaultManager() default manager} for the input format
68       * @throws IllegalStateException if a data format error occurs
69       * @throws java.io.UncheckedIOException if an I/O error occurs
70       * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
71       */
72      public static FacetDefinitionReader facetDefinitionReader(final Path path) {
73          return facetDefinitionReader(new FileGeometryInput(path), null);
74      }
75  
76      /** Get a {@link FacetDefinitionReader} for reading facet information from the given URL.
77       * The data format is determined by the file extension of the argument.
78       * @param url URL to read from
79       * @return facet definition reader
80       * @throws IllegalArgumentException if no handler has been registered with the
81       *      {@link #getDefaultManager() default manager} for the input format
82       * @throws IllegalStateException if a data format error occurs
83       * @throws java.io.UncheckedIOException if an I/O error occurs
84       * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
85       */
86      public static FacetDefinitionReader facetDefinitionReader(final URL url) {
87          return facetDefinitionReader(new UrlGeometryInput(url), null);
88      }
89  
90      /** Get a {@link FacetDefinitionReader} for reading facet information from the given input.
91       * @param in input to read from
92       * @param fmt format of the input; if null, the format is determined implicitly from the
93       *      file extension of the input {@link GeometryInput#getFileName() file name}
94       * @return facet definition reader
95       * @throws IllegalArgumentException if no handler has been registered with the
96       *      {@link #getDefaultManager() default manager} for the input format
97       * @throws IllegalStateException if a data format error occurs
98       * @throws java.io.UncheckedIOException if an I/O error occurs
99       * @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 }