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 }