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.obj;
18  
19  import java.nio.charset.Charset;
20  import java.util.Iterator;
21  import java.util.function.DoubleFunction;
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.mesh.Mesh;
27  import org.apache.commons.geometry.io.core.GeometryFormat;
28  import org.apache.commons.geometry.io.core.internal.GeometryIOUtils;
29  import org.apache.commons.geometry.io.core.output.GeometryOutput;
30  import org.apache.commons.geometry.io.euclidean.threed.AbstractBoundaryWriteHandler3D;
31  import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition;
32  import org.apache.commons.geometry.io.euclidean.threed.GeometryFormat3D;
33  
34  /** {@link org.apache.commons.geometry.io.euclidean.threed.BoundaryWriteHandler3D BoundaryWriteHandler3D}
35   * implementation for writing OBJ content. Output is written using the UTF-8 charset by default.
36   */
37  public class ObjBoundaryWriteHandler3D extends AbstractBoundaryWriteHandler3D {
38  
39      /** The default line separator value. */
40      private static final String DEFAULT_LINE_SEPARATOR = "\n";
41  
42      /** Default mesh buffer batch size. */
43      private static final int DEFAULT_MESH_BUFFER_BATCH_SIZE = -1;
44  
45      /** Charset used for text output. */
46      private Charset defaultCharset = ObjConstants.DEFAULT_CHARSET;
47  
48      /** Line separator string. */
49      private String lineSeparator = DEFAULT_LINE_SEPARATOR;
50  
51      /** Double format function. */
52      private DoubleFunction<String> doubleFormat = Double::toString;
53  
54      /** Batch size used for mesh buffer creation. */
55      private int meshBufferBatchSize = DEFAULT_MESH_BUFFER_BATCH_SIZE;
56  
57      /** {@inheritDoc} */
58      @Override
59      public GeometryFormat getFormat() {
60          return GeometryFormat3D.OBJ;
61      }
62  
63      /** Get the text output default charset, used if the output does not
64       * specify a charset.
65       * @return text output default charset
66       */
67      public Charset getDefaultCharset() {
68          return defaultCharset;
69      }
70  
71      /** Set the text output default charset, used if the output does not
72       * specify a charset.
73       * @param charset text output default charset
74       */
75      public void setDefaultCharset(final Charset charset) {
76          this.defaultCharset = charset;
77      }
78  
79      /** Get the line separator. This value defaults to {@value #DEFAULT_LINE_SEPARATOR}.
80       * @return the current line separator
81       */
82      public String getLineSeparator() {
83          return lineSeparator;
84      }
85  
86      /** Set the line separator.
87       * @param lineSeparator the line separator to use
88       */
89      public void setLineSeparator(final String lineSeparator) {
90          this.lineSeparator = lineSeparator;
91      }
92  
93      /** Get the function used to convert double values to strings.
94       * @return double format function
95       */
96      public DoubleFunction<String> getDoubleFormat() {
97          return doubleFormat;
98      }
99  
100     /** Set the function used to convert double values to strings. The given function
101      * must be thread-safe if this handler is to be used in a multi-threaded context.
102      * @param doubleFormat double format function
103      */
104     public void setDoubleFormat(final DoubleFunction<String> doubleFormat) {
105         this.doubleFormat = doubleFormat;
106     }
107 
108     /** Get the batch size when generating OBJ mesh content from facet sequences. Larger batch sizes
109      * allow for reuse of vertex definitions but at the cost of more memory usage. The buffer size is
110      * unlimited if set to {@code -1}. Default value is {@value #DEFAULT_MESH_BUFFER_BATCH_SIZE}.
111      * @return mesh buffer batch size
112      * @see ObjWriter#meshBuffer(int)
113      */
114     public int getMeshBufferBatchSize() {
115         return meshBufferBatchSize;
116     }
117 
118     /** Set the batch size when generating OBJ mesh content from facet sequences. Larger batch sizes
119      * allow for reuse of vertex definitions but at the cost of more memory usage. Set to {@code -1}
120      * to allow unlimited buffer size. Default value is {@value #DEFAULT_MESH_BUFFER_BATCH_SIZE}.
121      * @param batchSize mesh buffer batch size; set to {@code -1} to allow unlimited buffer sizes
122      * @see ObjWriter#meshBuffer(int)
123      */
124     public void setMeshBufferBatchSize(final int batchSize) {
125         this.meshBufferBatchSize = batchSize;
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     public void write(final BoundarySource3D src, final GeometryOutput out) {
131         // write meshes directly instead of iterating through boundaries
132         if (src instanceof Mesh) {
133             try (ObjWriter writer = createWriter(out)) {
134                 writer.writeMesh((Mesh<?>) src);
135             }
136         } else {
137             super.write(src, out);
138         }
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public void write(final Stream<? extends PlaneConvexSubset> boundaries, final GeometryOutput out) {
144         try (ObjWriter writer = createWriter(out)) {
145             final ObjWriter.MeshBuffer meshBuffer = writer.meshBuffer(meshBufferBatchSize);
146 
147             final Iterator<? extends PlaneConvexSubset> it = boundaries.iterator();
148             while (it.hasNext()) {
149                 meshBuffer.add(it.next());
150             }
151 
152             meshBuffer.flush();
153         }
154     }
155 
156     /** {@inheritDoc} */
157     @Override
158     public void writeFacets(final Stream<? extends FacetDefinition> facets, final GeometryOutput out) {
159         try (ObjWriter writer = createWriter(out)) {
160             final ObjWriter.MeshBuffer meshBuffer = writer.meshBuffer(meshBufferBatchSize);
161 
162             final Iterator<? extends FacetDefinition> it = facets.iterator();
163             while (it.hasNext()) {
164                 meshBuffer.add(it.next());
165             }
166 
167             meshBuffer.flush();
168         }
169     }
170 
171     /** Construct a new, configured {@link ObjWriter} instance for writing content to the given
172      * output stream.
173      * @param out output stream to write to
174      * @return new {@code OBJWriter} for writing content to the given output stream
175      * @throws java.io.UncheckedIOException if an I/O error occurs
176      */
177     private ObjWriter createWriter(final GeometryOutput out) {
178         final ObjWriter writer = new ObjWriter(GeometryIOUtils.createBufferedWriter(out, defaultCharset));
179         writer.setLineSeparator(lineSeparator);
180         writer.setDoubleFormat(doubleFormat);
181 
182         return writer;
183     }
184 }