001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.io.euclidean.threed.obj; 018 019import java.nio.charset.Charset; 020import java.util.Iterator; 021import java.util.function.DoubleFunction; 022import java.util.stream.Stream; 023 024import org.apache.commons.geometry.euclidean.threed.BoundarySource3D; 025import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset; 026import org.apache.commons.geometry.euclidean.threed.mesh.Mesh; 027import org.apache.commons.geometry.io.core.GeometryFormat; 028import org.apache.commons.geometry.io.core.internal.GeometryIOUtils; 029import org.apache.commons.geometry.io.core.output.GeometryOutput; 030import org.apache.commons.geometry.io.euclidean.threed.AbstractBoundaryWriteHandler3D; 031import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition; 032import org.apache.commons.geometry.io.euclidean.threed.GeometryFormat3D; 033 034/** {@link org.apache.commons.geometry.io.euclidean.threed.BoundaryWriteHandler3D BoundaryWriteHandler3D} 035 * implementation for writing OBJ content. Output is written using the UTF-8 charset by default. 036 */ 037public class ObjBoundaryWriteHandler3D extends AbstractBoundaryWriteHandler3D { 038 039 /** The default line separator value. */ 040 private static final String DEFAULT_LINE_SEPARATOR = "\n"; 041 042 /** Default mesh buffer batch size. */ 043 private static final int DEFAULT_MESH_BUFFER_BATCH_SIZE = -1; 044 045 /** Charset used for text output. */ 046 private Charset defaultCharset = ObjConstants.DEFAULT_CHARSET; 047 048 /** Line separator string. */ 049 private String lineSeparator = DEFAULT_LINE_SEPARATOR; 050 051 /** Double format function. */ 052 private DoubleFunction<String> doubleFormat = Double::toString; 053 054 /** Batch size used for mesh buffer creation. */ 055 private int meshBufferBatchSize = DEFAULT_MESH_BUFFER_BATCH_SIZE; 056 057 /** {@inheritDoc} */ 058 @Override 059 public GeometryFormat getFormat() { 060 return GeometryFormat3D.OBJ; 061 } 062 063 /** Get the text output default charset, used if the output does not 064 * specify a charset. 065 * @return text output default charset 066 */ 067 public Charset getDefaultCharset() { 068 return defaultCharset; 069 } 070 071 /** Set the text output default charset, used if the output does not 072 * specify a charset. 073 * @param charset text output default charset 074 */ 075 public void setDefaultCharset(final Charset charset) { 076 this.defaultCharset = charset; 077 } 078 079 /** Get the line separator. This value defaults to {@value #DEFAULT_LINE_SEPARATOR}. 080 * @return the current line separator 081 */ 082 public String getLineSeparator() { 083 return lineSeparator; 084 } 085 086 /** Set the line separator. 087 * @param lineSeparator the line separator to use 088 */ 089 public void setLineSeparator(final String lineSeparator) { 090 this.lineSeparator = lineSeparator; 091 } 092 093 /** Get the function used to convert double values to strings. 094 * @return double format function 095 */ 096 public DoubleFunction<String> getDoubleFormat() { 097 return doubleFormat; 098 } 099 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}