IO3D.java

  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. import java.net.URL;
  19. import java.nio.file.Path;
  20. import java.util.Collection;
  21. import java.util.stream.Stream;

  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.GeometryFormat;
  27. import org.apache.commons.geometry.io.core.input.FileGeometryInput;
  28. import org.apache.commons.geometry.io.core.input.GeometryInput;
  29. import org.apache.commons.geometry.io.core.input.UrlGeometryInput;
  30. import org.apache.commons.geometry.io.core.output.FileGeometryOutput;
  31. import org.apache.commons.geometry.io.core.output.GeometryOutput;
  32. import org.apache.commons.numbers.core.Precision;

  33. /** Utility class providing convenient access to 3D IO functionality. The static read and write methods here
  34.  * delegate to a default {@link #getDefaultManager() BoundaryIOManager3D} instance. The default
  35.  * configuration should be sufficient for most purposes. If customization is required, consider directly
  36.  * creating and configuring and a {@link BoundaryIOManager3D} instance.
  37.  *
  38.  * <p><strong>Examples</strong></p>
  39.  * <p>The example below reads an OBJ file as a stream of triangles, transforms each triangle, and writes the
  40.  * result as a CSV file. The data formats are inferred from the input and output file extensions.</p>
  41.  * <pre>
  42.  * GeometryInput input = new FileGeometryInput(Paths.get("orig.obj"));
  43.  * GeometryOutput scaledOutput = new FileGeometryOutput(Paths.get("scaled.csv"));
  44.  * AffineTransformMatrix3D transform = AffineTransformMatrix3D.createScale(2);
  45.  *
  46.  * // Use the input triangle stream in a try-with-resources statement to ensure
  47.  * // all resources are properly released.
  48.  * try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(input, null, precision)) {
  49.  *      IO3D.write(stream.map(t -&gt; t.transform(transform)), scaledOutput, null);
  50.  * }
  51.  * </pre>
  52.  * @see BoundaryIOManager3D
  53.  */
  54. public final class IO3D {

  55.     /** Utility class; no instantiation. */
  56.     private IO3D() {}

  57.     /** Get a {@link FacetDefinitionReader} for reading facet information from the given file path.
  58.      * The data format is determined by the file extension of the argument.
  59.      * @param path path to obtain a reader for
  60.      * @return facet definition reader
  61.      * @throws IllegalArgumentException if no handler has been registered with the
  62.      *      {@link #getDefaultManager() default manager} for the input format
  63.      * @throws IllegalStateException if a data format error occurs
  64.      * @throws java.io.UncheckedIOException if an I/O error occurs
  65.      * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
  66.      */
  67.     public static FacetDefinitionReader facetDefinitionReader(final Path path) {
  68.         return facetDefinitionReader(new FileGeometryInput(path), null);
  69.     }

  70.     /** Get a {@link FacetDefinitionReader} for reading facet information from the given URL.
  71.      * The data format is determined by the file extension of the argument.
  72.      * @param url URL to read from
  73.      * @return facet definition reader
  74.      * @throws IllegalArgumentException if no handler has been registered with the
  75.      *      {@link #getDefaultManager() default manager} for the input format
  76.      * @throws IllegalStateException if a data format error occurs
  77.      * @throws java.io.UncheckedIOException if an I/O error occurs
  78.      * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
  79.      */
  80.     public static FacetDefinitionReader facetDefinitionReader(final URL url) {
  81.         return facetDefinitionReader(new UrlGeometryInput(url), null);
  82.     }

  83.     /** Get a {@link FacetDefinitionReader} for reading facet information from the given input.
  84.      * @param in input to read from
  85.      * @param fmt format of the input; if null, the format is determined implicitly from the
  86.      *      file extension of the input {@link GeometryInput#getFileName() file name}
  87.      * @return facet definition reader
  88.      * @throws IllegalArgumentException if no handler has been registered with the
  89.      *      {@link #getDefaultManager() default manager} for the input format
  90.      * @throws IllegalStateException if a data format error occurs
  91.      * @throws java.io.UncheckedIOException if an I/O error occurs
  92.      * @see BoundaryIOManager3D#facetDefinitionReader(GeometryInput, GeometryFormat)
  93.      */
  94.     public static FacetDefinitionReader facetDefinitionReader(final GeometryInput in, final GeometryFormat fmt) {
  95.         return getDefaultManager().facetDefinitionReader(in, fmt);
  96.     }

  97.     /** Return a {@link Stream} providing access to all facets from the given file path. The data format
  98.      * is determined by the file extension of the argument.
  99.      *
  100.      * <p>The underlying input stream is closed when the returned stream is closed. Callers should
  101.      * therefore use the returned stream in a try-with-resources statement to ensure that all
  102.      * resources are properly released. Ex:
  103.      * </p>
  104.      * <pre>
  105.      *  try (Stream&lt;FacetDefinition&gt; stream = IO3D.facets(path)) {
  106.      *      // access stream content
  107.      *  }
  108.      * </pre>
  109.      * <p>The following exceptions may be thrown during stream iteration:
  110.      *  <ul>
  111.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  112.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  113.      *  </ul>
  114.      * @param path file path to read from
  115.      * @return stream providing access to the facets in the specified file
  116.      * @throws IllegalArgumentException if no handler has been registered with the
  117.      *      {@link #getDefaultManager() default manager} for the input format
  118.      * @throws IllegalStateException if a data format error occurs during stream creation
  119.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  120.      * @see BoundaryIOManager3D#facets(GeometryInput, GeometryFormat)
  121.      */
  122.     public static Stream<FacetDefinition> facets(final Path path) {
  123.         return facets(new FileGeometryInput(path), null);
  124.     }

  125.     /** Return a {@link Stream} providing access to all facets from the given URL. he data format
  126.      * is determined by the file extension of the argument.
  127.      *
  128.      * <p>The underlying input stream is closed when the returned stream is closed. Callers should
  129.      * therefore use the returned stream in a try-with-resources statement to ensure that all
  130.      * resources are properly released. Ex:
  131.      * </p>
  132.      * <pre>
  133.      *  try (Stream&lt;FacetDefinition&gt; stream = IO3D.facets(url)) {
  134.      *      // access stream content
  135.      *  }
  136.      * </pre>
  137.      * <p>The following exceptions may be thrown during stream iteration:
  138.      *  <ul>
  139.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  140.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  141.      *  </ul>
  142.      * @param url URL to read from
  143.      * @return stream providing access to the facets from the specified URL
  144.      * @throws IllegalArgumentException if no handler has been registered with the
  145.      *      {@link #getDefaultManager() default manager} for the input format
  146.      * @throws IllegalStateException if a data format error occurs during stream creation
  147.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  148.      * @see BoundaryIOManager3D#facets(GeometryInput, GeometryFormat)
  149.      */
  150.     public static Stream<FacetDefinition> facets(final URL url) {
  151.         return facets(new UrlGeometryInput(url), null);
  152.     }

  153.     /** Return a {@link Stream} providing access to all facets from the given input. The underlying input
  154.      * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
  155.      * in a try-with-resources statement to ensure that all resources are properly released.
  156.      * <pre>
  157.      *  try (Stream&lt;FacetDefinition&gt; stream = IO3D.facets(in, fmt)) {
  158.      *      // access stream content
  159.      *  }
  160.      * </pre>
  161.      * <p>The following exceptions may be thrown during stream iteration:
  162.      *  <ul>
  163.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  164.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  165.      *  </ul>
  166.      * @param in input to read from
  167.      * @param fmt format of the input; if null, the format is determined implicitly from the
  168.      *      file extension of the input {@link GeometryInput#getFileName() file name}
  169.      * @return stream providing access to the facets in the input
  170.      * @throws IllegalArgumentException if no read handler has been registered with the
  171.      *      {@link #getDefaultManager() default manager} for the input format
  172.      * @throws IllegalStateException if a data format error occurs during stream creation
  173.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  174.      * @see BoundaryIOManager3D#facets(GeometryInput, GeometryFormat)
  175.      */
  176.     public static Stream<FacetDefinition> facets(final GeometryInput in, final GeometryFormat fmt) {
  177.         return getDefaultManager().facets(in, fmt);
  178.     }

  179.     /** Return a {@link Stream} providing access to all boundaries from the given file path. The
  180.      * data format is determined by the file extension of the argument.
  181.      *
  182.      * <p>The underlying input stream is closed when the returned stream is closed. Callers should
  183.      * therefore use the returned stream in a try-with-resources statement to ensure that all
  184.      * resources are properly released. Ex:
  185.      * </p>
  186.      * <pre>
  187.      *  try (Stream&lt;PlaneConvexSubset&gt; stream = IO3D.boundaries(path, precision)) {
  188.      *      // access stream content
  189.      *  }
  190.      * </pre>
  191.      * <p>The following exceptions may be thrown during stream iteration:
  192.      *  <ul>
  193.      *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
  194.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  195.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  196.      *  </ul>
  197.      * @param path file path to read from
  198.      * @param precision precision context used for floating point comparisons
  199.      * @return stream providing access to the boundaries in the specified file
  200.      * @throws IllegalArgumentException if no read handler has been registered with the
  201.      *      {@link #getDefaultManager() default manager} for the input format
  202.      * @throws IllegalStateException if a data format error occurs during stream creation
  203.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  204.      * @see BoundaryIOManager3D#boundaries(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  205.      */
  206.     public static Stream<PlaneConvexSubset> boundaries(final Path path, final Precision.DoubleEquivalence precision) {
  207.         return boundaries(new FileGeometryInput(path), null, precision);
  208.     }

  209.     /** Return a {@link Stream} providing access to all boundaries from the given URL. The data
  210.      * format is determined by the file extension of the argument.
  211.      *
  212.      * <p>The underlying input stream is closed when the returned stream is closed. Callers should
  213.      * therefore use the returned stream in a try-with-resources statement to ensure that all
  214.      * resources are properly released. Ex:
  215.      * </p>
  216.      * <pre>
  217.      *  try (Stream&lt;PlaneConvexSubset&gt; stream = IO3D.boundaries(url, precision)) {
  218.      *      // access stream content
  219.      *  }
  220.      * </pre>
  221.      * <p>The following exceptions may be thrown during stream iteration:
  222.      *  <ul>
  223.      *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
  224.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  225.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  226.      *  </ul>
  227.      * @param url URL to read from
  228.      * @param precision precision context used for floating point comparisons
  229.      * @return stream providing access to the boundaries in the specified URL
  230.      * @throws IllegalArgumentException if no read handler has been registered with the
  231.      *      {@link #getDefaultManager() default manager} for the input format
  232.      * @throws IllegalStateException if a data format error occurs during stream creation
  233.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  234.      * @see BoundaryIOManager3D#boundaries(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  235.      */
  236.     public static Stream<PlaneConvexSubset> boundaries(final URL url, final Precision.DoubleEquivalence precision) {
  237.         return boundaries(new UrlGeometryInput(url), null, precision);
  238.     }

  239.     /** Return a {@link Stream} providing access to all boundaries from the given input. The underlying input
  240.      * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
  241.      * in a try-with-resources statement to ensure that all resources are properly released. Ex:
  242.      * <pre>
  243.      *  try (Stream&lt;H&gt; stream = IO3D.boundaries(in, fmt, precision)) {
  244.      *      // access stream content
  245.      *  }
  246.      *  </pre>
  247.      * <p>The following exceptions may be thrown during stream iteration:
  248.      *  <ul>
  249.      *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
  250.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  251.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  252.      *  </ul>
  253.      * @param in input to read boundaries from
  254.      * @param fmt format of the input; if null, the format is determined implicitly from the
  255.      *      file extension of the input {@link GeometryInput#getFileName() file name}
  256.      * @param precision precision context used for floating point comparisons
  257.      * @return stream providing access to the boundaries in the input
  258.      * @throws IllegalArgumentException if no read handler is registered with the
  259.      *      {@link #getDefaultManager() default manager} for the input format
  260.      * @throws IllegalStateException if a data format error occurs during stream creation
  261.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  262.      * @see BoundaryIOManager3D#boundaries(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  263.      */
  264.     public static Stream<PlaneConvexSubset> boundaries(final GeometryInput in, final GeometryFormat fmt,
  265.             final Precision.DoubleEquivalence precision) {
  266.         return getDefaultManager().boundaries(in, fmt, precision);
  267.     }

  268.     /** Return a {@link Stream} providing access to all triangles from the given file path. The data
  269.      * format is determined by the file extension of the argument.
  270.      *
  271.      * <p>The underlying input stream is closed when the returned stream is closed. Callers should
  272.      * therefore use the returned stream in a try-with-resources statement to ensure that all
  273.      * resources are properly released. Ex:
  274.      * </p>
  275.      * <pre>
  276.      *  try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(path, precision)) {
  277.      *      // access stream content
  278.      *  }
  279.      * </pre>
  280.      * <p>The following exceptions may be thrown during stream iteration:
  281.      *  <ul>
  282.      *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
  283.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  284.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  285.      *  </ul>
  286.      * @param path file path to read from
  287.      * @param precision precision context used for floating point comparisons
  288.      * @return stream providing access to the triangles in the specified file
  289.      * @throws IllegalArgumentException if no read handler is registered with the
  290.      *      {@link #getDefaultManager() default manager} for the input format
  291.      * @throws IllegalStateException if a data format error occurs during stream creation
  292.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  293.      * @see BoundaryIOManager3D#triangles(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  294.      */
  295.     public static Stream<Triangle3D> triangles(final Path path, final Precision.DoubleEquivalence precision) {
  296.         return triangles(new FileGeometryInput(path), null, precision);
  297.     }

  298.     /** Return a {@link Stream} providing access to all triangles from the given URL. The data format
  299.      * is determined by the file extension of the argument.
  300.      *
  301.      * <p>The underlying input stream is closed when the returned stream is closed. Callers should
  302.      * therefore use the returned stream in a try-with-resources statement to ensure that all
  303.      * resources are properly released. Ex:
  304.      * </p>
  305.      * <pre>
  306.      *  try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(url, precision)) {
  307.      *      // access stream content
  308.      *  }
  309.      * </pre>
  310.      * <p>The following exceptions may be thrown during stream iteration:
  311.      *  <ul>
  312.      *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
  313.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  314.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  315.      *  </ul>
  316.      * @param url URL to read from
  317.      * @param precision precision context used for floating point comparisons
  318.      * @return stream providing access to the triangles from the specified URL
  319.      * @throws IllegalArgumentException if no read handler is registered with the
  320.      *      {@link #getDefaultManager() default manager} for the input format
  321.      * @throws IllegalStateException if a data format error occurs during stream creation
  322.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  323.      * @see BoundaryIOManager3D#triangles(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  324.      */
  325.     public static Stream<Triangle3D> triangles(final URL url, final Precision.DoubleEquivalence precision) {
  326.         return triangles(new UrlGeometryInput(url), null, precision);
  327.     }

  328.     /** Return a {@link Stream} providing access to all triangles from the given input. The underlying input
  329.      * stream is closed when the returned stream is closed. Callers should therefore use the returned stream
  330.      * in a try-with-resources statement to ensure that all resources are properly released.
  331.      * <pre>
  332.      *  try (Stream&lt;Triangle3D&gt; stream = IO3D.triangles(in, fmt, precision)) {
  333.      *      // access stream content
  334.      *  }
  335.      * </pre>
  336.      * <p>The following exceptions may be thrown during stream iteration:
  337.      *  <ul>
  338.      *      <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
  339.      *      <li>{@link IllegalStateException} if a data format error occurs</li>
  340.      *      <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
  341.      *  </ul>
  342.      * @param in input to read from
  343.      * @param fmt format of the input; if null, the format is determined implicitly from the
  344.      *      file extension of the input {@link GeometryInput#getFileName() file name}
  345.      * @param precision precision context used for floating point comparisons
  346.      * @return stream providing access to the triangles in the input
  347.      * @throws IllegalArgumentException if no read handler is registered with the
  348.      *      {@link #getDefaultManager() default manager} for the input format
  349.      * @throws IllegalStateException if a data format error occurs during stream creation
  350.      * @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
  351.      * @see BoundaryIOManager3D#triangles(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  352.      */
  353.     public static Stream<Triangle3D> triangles(final GeometryInput in, final GeometryFormat fmt,
  354.             final Precision.DoubleEquivalence precision) {
  355.         return getDefaultManager().triangles(in, fmt, precision);
  356.     }

  357.     /** Return a {@link BoundarySource3D} containing all boundaries from the file at the
  358.      * given path. The data format is determined from the file extension. A runtime exception may be
  359.      * thrown if mathematically invalid boundaries are encountered.
  360.      * @param path file path to read from
  361.      * @param precision precision context used for floating point comparisons
  362.      * @return object containing all boundaries from the file at the given path
  363.      * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
  364.      *      is registered with the {@link #getDefaultManager() default manager} for the input format
  365.      * @throws IllegalStateException if a data format error occurs
  366.      * @throws java.io.UncheckedIOException if an I/O error occurs
  367.      * @see BoundaryIOManager3D#read(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  368.      */
  369.     public static BoundarySource3D read(final Path path, final Precision.DoubleEquivalence precision) {
  370.         return read(new FileGeometryInput(path), null, precision);
  371.     }

  372.     /** Return a {@link BoundarySource3D} containing all boundaries from the given URL. The data
  373.      * format is determined from the file extension of the URL path. A runtime exception may be
  374.      * thrown if mathematically invalid boundaries are encountered.
  375.      * @param url URL to read from
  376.      * @param precision precision context used for floating point comparisons
  377.      * @return object containing all boundaries from the given URL
  378.      * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
  379.      *      is registered with the {@link #getDefaultManager() default manager} for the input format
  380.      * @throws IllegalStateException if a data format error occurs
  381.      * @throws java.io.UncheckedIOException if an I/O error occurs
  382.      * @see BoundaryIOManager3D#read(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  383.      */
  384.     public static BoundarySource3D read(final URL url, final Precision.DoubleEquivalence precision) {
  385.         return read(new UrlGeometryInput(url), null, precision);
  386.     }

  387.     /** Return a {@link BoundarySource3D} containing all boundaries from the given input. A runtime
  388.      * exception may be thrown if mathematically invalid boundaries are encountered.
  389.      * @param in input to read boundaries from
  390.      * @param fmt format of the input; if null, the format is determined implicitly from the
  391.      *      file extension of the input {@link GeometryInput#getFileName() file name}
  392.      * @param precision precision context used for floating point comparisons
  393.      * @return object containing all boundaries from the input
  394.      * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
  395.      *      is registered with the {@link #getDefaultManager() default manager} for the input format
  396.      * @throws IllegalStateException if a data format error occurs
  397.      * @throws java.io.UncheckedIOException if an I/O error occurs
  398.      * @see BoundaryIOManager3D#read(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  399.      */
  400.     public static BoundarySource3D read(final GeometryInput in, final GeometryFormat fmt,
  401.             final Precision.DoubleEquivalence precision) {
  402.         return getDefaultManager().read(in, fmt, precision);
  403.     }

  404.     /** Return a {@link TriangleMesh} containing all triangles from the given file path. The data
  405.      * format is determined from the file extension of the path. A runtime exception may be
  406.      * thrown if mathematically invalid boundaries are encountered.
  407.      * @param path file path to read from
  408.      * @param precision precision context used for floating point comparisons
  409.      * @return mesh containing all triangles from the given file path
  410.      * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
  411.      *      is registered with the {@link #getDefaultManager() default manager} for the input format
  412.      * @throws IllegalStateException if a data format error occurs
  413.      * @throws java.io.UncheckedIOException if an I/O error occurs
  414.      * @see BoundaryIOManager3D#readTriangleMesh(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  415.      */
  416.     public static TriangleMesh readTriangleMesh(final Path path, final Precision.DoubleEquivalence precision) {
  417.         return readTriangleMesh(new FileGeometryInput(path), null, precision);
  418.     }

  419.     /** Return a {@link TriangleMesh} containing all triangles from the given URL. The data
  420.      * format is determined from the file extension of the URL path. A runtime exception may be
  421.      * thrown if mathematically invalid boundaries are encountered.
  422.      * @param url URL to read from
  423.      * @param precision precision context used for floating point comparisons
  424.      * @return mesh containing all triangles from the given URL
  425.      * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
  426.      *      is registered with the {@link #getDefaultManager() default manager} for the input format
  427.      * @throws IllegalStateException if a data format error occurs
  428.      * @throws java.io.UncheckedIOException if an I/O error occurs
  429.      * @see BoundaryIOManager3D#readTriangleMesh(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  430.      */
  431.     public static TriangleMesh readTriangleMesh(final URL url, final Precision.DoubleEquivalence precision) {
  432.         return readTriangleMesh(new UrlGeometryInput(url), null, precision);
  433.     }

  434.     /** Return a {@link TriangleMesh} containing all triangles from the given input. A runtime exception
  435.      * may be thrown if mathematically invalid boundaries are encountered.
  436.      * @param in input to read from
  437.      * @param fmt format of the input; if null, the format is determined implicitly from the
  438.      *      file extension of the input {@link GeometryInput#getFileName() file name}
  439.      * @param precision precision context used for floating point comparisons
  440.      * @return a mesh containing all triangles from the input
  441.      * @throws IllegalArgumentException if mathematically invalid data is encountered or no read handler
  442.      *      is registered with the {@link #getDefaultManager() default manager} for the input format
  443.      * @throws IllegalStateException if a data format error occurs
  444.      * @throws java.io.UncheckedIOException if an I/O error occurs
  445.      * @see BoundaryIOManager3D#readTriangleMesh(GeometryInput, GeometryFormat, Precision.DoubleEquivalence)
  446.      */
  447.     public static TriangleMesh readTriangleMesh(final GeometryInput in, final GeometryFormat fmt,
  448.             final Precision.DoubleEquivalence precision) {
  449.         return getDefaultManager().readTriangleMesh(in, fmt, precision);
  450.     }

  451.     /** Write all boundaries in the stream to given file path. The data format is determined by
  452.      * the file extension of the target path. If the target path already exists, it is overwritten.
  453.      *
  454.      * <p>This method does not explicitly close the {@code boundaries} stream. Callers should use the stream
  455.      * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
  456.      * @param boundaries stream containing boundaries to write
  457.      * @param path file path to write to
  458.      * @throws IllegalArgumentException if no write handler is registered with the
  459.      *      {@link #getDefaultManager() default manager} for the output format
  460.      * @throws java.io.UncheckedIOException if an I/O error occurs
  461.      * @see BoundaryIOManager3D#write(Stream, GeometryOutput, GeometryFormat)
  462.      */
  463.     public static void write(final Stream<? extends PlaneConvexSubset> boundaries, final Path path) {
  464.         write(boundaries, new FileGeometryOutput(path), null);
  465.     }

  466.     /** Write all boundaries in the stream to the output.
  467.      *
  468.      * <p>This method does not explicitly close the {@code boundaries} stream. Callers should use the stream
  469.      * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
  470.      * @param boundaries stream containing boundaries to write
  471.      * @param out output to write to
  472.      * @param fmt format of the output; if null, the format is determined implicitly from the
  473.      *      file extension of the output {@link GeometryOutput#getFileName() file name}
  474.      * @throws IllegalArgumentException if no write handler is registered with the
  475.      *      {@link #getDefaultManager() default manager} for the output format
  476.      * @throws java.io.UncheckedIOException if an I/O error occurs
  477.      * @see BoundaryIOManager3D#write(Stream, GeometryOutput, GeometryFormat)
  478.      */
  479.     public static void write(final Stream<? extends PlaneConvexSubset> boundaries, final GeometryOutput out,
  480.             final GeometryFormat fmt) {
  481.         getDefaultManager().write(boundaries, out, fmt);
  482.     }

  483.     /** Write all boundaries from {@code src} to the given file path. The data format
  484.      * is determined by the file extension of the target path. If the target path already exists,
  485.      * it is overwritten.
  486.      * @param src boundary source containing the boundaries to write
  487.      * @param path file path to write to
  488.      * @throws IllegalArgumentException if no write handler is registered with the
  489.      *      {@link #getDefaultManager() default manager} for the output format
  490.      * @throws java.io.UncheckedIOException if an I/O error occurs
  491.      * @see org.apache.commons.geometry.io.core.BoundaryIOManager#write(
  492.      *      org.apache.commons.geometry.core.partitioning.BoundarySource, GeometryOutput, GeometryFormat)
  493.      */
  494.     public static void write(final BoundarySource3D src, final Path path) {
  495.         write(src, new FileGeometryOutput(path), null);
  496.     }

  497.     /** Write all boundaries from {@code src} to the given output.
  498.      * @param src boundary source containing the boundaries to write
  499.      * @param out output to write to
  500.      * @param fmt format of the output; if null, the format is determined implicitly from the
  501.      *      file extension of the output {@link GeometryOutput#getFileName() file name}
  502.      * @throws IllegalArgumentException if no write handler is registered with the
  503.      *      {@link #getDefaultManager() default manager} for the output format
  504.      * @throws java.io.UncheckedIOException if an I/O error occurs
  505.      * @see org.apache.commons.geometry.io.core.BoundaryIOManager#write(
  506.      *      org.apache.commons.geometry.core.partitioning.BoundarySource, GeometryOutput, GeometryFormat)
  507.      */
  508.     public static void write(final BoundarySource3D src, final GeometryOutput out, final GeometryFormat fmt) {
  509.         getDefaultManager().write(src, out, fmt);
  510.     }

  511.     /** Write the given facets to the file path. The data format is determined by the file extension of
  512.      * the target path. If the target path already exists, it is overwritten.
  513.      * @param facets facets to write
  514.      * @param path path to write to
  515.      * @throws IllegalArgumentException if no write handler is registered with the
  516.      *      {@link #getDefaultManager() default manager} for the output format
  517.      * @throws java.io.UncheckedIOException if an I/O error occurs
  518.      * @see BoundaryIOManager3D#writeFacets(Collection, GeometryOutput, GeometryFormat)
  519.      */
  520.     public static void writeFacets(final Collection<? extends FacetDefinition> facets, final Path path) {
  521.         writeFacets(facets, new FileGeometryOutput(path), null);
  522.     }

  523.     /** Write the given collection of facets to the output.
  524.      * @param facets facets 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 BoundaryIOManager3D#writeFacets(Collection, GeometryOutput, GeometryFormat)
  532.      */
  533.     public static void writeFacets(final Collection<? extends FacetDefinition> facets, final GeometryOutput out,
  534.             final GeometryFormat fmt) {
  535.         getDefaultManager().writeFacets(facets, out, fmt);
  536.     }

  537.     /** Write all facets in the stream to the file path. The data format is determined by the file
  538.      * extension of the target path. If the target path already exists, it is overwritten.
  539.      *
  540.      * <p>This method does not explicitly close the {@code facets} stream. Callers should use the stream
  541.      * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
  542.      * @param facets stream containing facets to write
  543.      * @param path path to write to
  544.      * @throws IllegalArgumentException if no write handler is registered with the
  545.      *      {@link #getDefaultManager() default manager} for the output format
  546.      * @throws java.io.UncheckedIOException if an I/O error occurs
  547.      * @see BoundaryIOManager3D#writeFacets(Stream, GeometryOutput, GeometryFormat)
  548.      */
  549.     public static void writeFacets(final Stream<? extends FacetDefinition> facets, final Path path) {
  550.         writeFacets(facets, new FileGeometryOutput(path), null);
  551.     }

  552.     /** Write all facets in the stream to the output.
  553.      *
  554.      * <p>This method does not explicitly close the {@code facets} stream. Callers should use the stream
  555.      * in a try-with-resources statement outside of this method if the stream is required to be closed.</p>
  556.      * @param facets stream containing facets to write
  557.      * @param out output to write to
  558.      * @param fmt format of the output; if null, the format is determined implicitly from the
  559.      *      file extension of the output {@link GeometryOutput#getFileName() file name}
  560.      * @throws IllegalArgumentException if no write handler is registered with the
  561.      *      {@link #getDefaultManager() default manager} for the output format
  562.      * @throws java.io.UncheckedIOException if an I/O error occurs
  563.      * @see BoundaryIOManager3D#writeFacets(Stream, GeometryOutput, GeometryFormat)
  564.      */
  565.     public static void writeFacets(final Stream<? extends FacetDefinition> facets, final GeometryOutput out,
  566.             final GeometryFormat fmt) {
  567.         getDefaultManager().writeFacets(facets, out, fmt);
  568.     }

  569.     /** Get the default {@link BoundaryIOManager3D} instance.
  570.      * @return the default {@link BoundaryIOManager3D} instance
  571.      */
  572.     public static BoundaryIOManager3D getDefaultManager() {
  573.         return ManagerHolder.DEFAULT_MANAGER;
  574.     }

  575.     /** Class holding a reference to the default IO manager instance.
  576.      */
  577.     private static final class ManagerHolder {

  578.         /** Default IO manager instance. */
  579.         private static final BoundaryIOManager3D DEFAULT_MANAGER;

  580.         static {
  581.             DEFAULT_MANAGER = new BoundaryIOManager3D();
  582.             DEFAULT_MANAGER.registerDefaultHandlers();
  583.         }

  584.         /** Utility class; no instantiation. */
  585.         private ManagerHolder() {}
  586.     }
  587. }