2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * @file SurfaceMeshWriter.ih
19 * @author Jacques-Olivier Lachaud (\c jacques-olivier.lachaud@univ-savoie.fr )
20 * Laboratory of Mathematics (CNRS, UMR 5127), University of Savoie, France
24 * Implementation of inline methods defined in SurfaceMeshWriter.h
26 * This file is part of the DGtal library.
30 //////////////////////////////////////////////////////////////////////////////
33 #include "DGtal/shapes/MeshHelpers.h"
34 #include "DGtal/helpers/Shortcuts.h"
35 //////////////////////////////////////////////////////////////////////////////
38 ///////////////////////////////////////////////////////////////////////////////
39 // IMPLEMENTATION of inline methods.
40 ///////////////////////////////////////////////////////////////////////////////
42 //-----------------------------------------------------------------------------
43 template <typename TRealPoint, typename TRealVector>
45 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
46 writeOBJ( std::ostream & output, const SurfaceMesh & smesh )
48 output << "# OBJ format" << std::endl;
49 output << "# DGtal::SurfaceMeshWriter::writeOBJ" << std::endl;
50 output << "o anObject" << std::endl;
51 for ( auto v : smesh.positions() )
52 output << "v " << v[ 0 ] << " " << v[ 1 ] << " " << v[ 2 ] << std::endl;
53 output << "# " << smesh.positions().size() << " vertices" << std::endl;
54 if ( ! smesh.vertexNormals().empty() )
56 for ( auto vn : smesh.vertexNormals() )
57 output << "vn " << vn[ 0 ] << " " << vn[ 1 ] << " " << vn[ 2 ] << std::endl;
58 output << "# " << smesh.vertexNormals().size() << " normal vectors" << std::endl;
60 for ( auto f : smesh.allIncidentVertices() )
63 for ( auto v : f ) output << " " << (v+1);
66 output << "# " << smesh.allIncidentVertices().size() << " faces" << std::endl;
70 //-----------------------------------------------------------------------------
71 template <typename TRealPoint, typename TRealVector>
73 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
74 writeOBJ( std::string objfile,
75 const SurfaceMesh & smesh,
76 const Colors& diffuse_colors,
77 const Color& ambient_color,
78 const Color& diffuse_color,
79 const Color& specular_color )
82 auto lastindex = objfile.find_last_of(".");
83 if ( lastindex == std::string::npos )
85 mtlfile = objfile + ".mtl";
86 objfile = objfile + ".obj";
90 mtlfile = objfile.substr(0, lastindex) + ".mtl";
92 std::ofstream output_obj( objfile.c_str() );
93 output_obj << "# OBJ format" << std::endl;
94 output_obj << "# DGtal::SurfaceMeshWriter::writeOBJ" << std::endl;
95 output_obj << "o anObject" << std::endl;
96 // remove directory to write material
97 auto indexpath = objfile.find_last_of("/");
98 output_obj << "mtllib " << mtlfile.substr(indexpath+1) << std::endl;
99 std::ofstream output_mtl( mtlfile.c_str() );
100 output_mtl << "# MTL format"<< std::endl;
101 output_mtl << "# generated from SurfaceMeshWriter from the DGTal library"<< std::endl;
103 for ( auto v : smesh.positions() )
104 output_obj << "v " << v[ 0 ] << " " << v[ 1 ] << " " << v[ 2 ] << std::endl;
105 output_obj << "# " << smesh.positions().size() << " vertices" << std::endl;
106 // Write vertex normals
107 if ( ! smesh.vertexNormals().empty() )
109 for ( auto vn : smesh.vertexNormals() )
110 output_obj << "vn " << vn[ 0 ] << " " << vn[ 1 ] << " " << vn[ 2 ] << std::endl;
111 output_obj << "# " << smesh.vertexNormals().size() << " normal vectors" << std::endl;
113 // Taking care of materials
114 bool has_material = ( smesh.nbFaces() == diffuse_colors.size() );
115 Index idxMaterial = 0;
116 std::map<Color, Index > mapMaterial;
119 for ( Index f = 0; f < diffuse_colors.size(); ++f )
121 Color c = diffuse_colors[ f ];
122 if ( mapMaterial.count( c ) == 0 )
124 MeshHelpers::exportMTLNewMaterial
125 ( output_mtl, idxMaterial, ambient_color, c, specular_color );
126 mapMaterial[ c ] = idxMaterial++;
132 MeshHelpers::exportMTLNewMaterial
133 ( output_mtl, idxMaterial, ambient_color, diffuse_color, specular_color );
135 // Write faces with material(s)
137 for ( auto f : smesh.allIncidentVertices() )
139 output_obj << "usemtl material_"
140 << ( has_material ? mapMaterial[ diffuse_colors[ idx_f ] ] : idxMaterial )
144 if ( smesh.vertexNormals().empty() )
145 output_obj << " " << (v+1);
147 output_obj << " " << (v+1) << "//" << (v+1);
148 output_obj << std::endl;
151 output_obj << "# " << smesh.allIncidentVertices().size() << " faces" << std::endl;
153 return output_obj.good();
156 //-----------------------------------------------------------------------------
157 template <typename TRealPoint, typename TRealVector>
158 template <typename EdgePredicate>
160 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
161 writeEdgeLinesOBJ( std::string objfile,
162 const SurfaceMesh & smesh,
163 const EdgePredicate & edge_predicate,
164 const double relative_thickness,
165 const Color& ambient_color,
166 const Color& diffuse_color,
167 const Color& specular_color )
169 typedef KhalimskySpaceND< 3, int > KSpace;
170 typedef Shortcuts< KSpace > SH;
171 typedef typename SH::RealPoints RealPoints;
172 typedef typename SH::RealVectors RealVectors;
173 typedef typename SurfaceMesh::Edge Edge;
175 RealPoints positions;
176 RealVectors edge_vectors;
177 double lengths = 0.0;
178 for ( Edge e = 0; e < smesh.nbEdges(); ++e )
180 auto vtcs = smesh.edgeVertices()[ e ];
181 const RealPoint p = smesh.positions()[ vtcs.first ];
182 const RealVector pq = smesh.positions()[ vtcs.second ] - p;
183 lengths += pq.norm();
184 if ( ! edge_predicate( e ) ) continue;
185 positions.push_back( p );
186 edge_vectors.push_back( pq );
188 lengths /= smesh.nbEdges();
189 return SH::saveVectorFieldOBJ
190 ( positions, edge_vectors, lengths*relative_thickness,
191 Colors(), objfile, ambient_color, diffuse_color, specular_color );
193 //-----------------------------------------------------------------------------
194 template <typename TRealPoint, typename TRealVector>
196 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
197 writeIsoLinesOBJ( std::string objfile,
198 const SurfaceMesh & smesh,
199 const Scalars& face_values,
200 const Scalars& vertex_values,
201 const Scalar iso_value,
202 const double relative_thickness,
203 const Color& ambient_color,
204 const Color& diffuse_color,
205 const Color& specular_color )
207 typedef KhalimskySpaceND< 3, int > KSpace;
208 typedef Shortcuts< KSpace > SH;
209 typedef typename SH::RealPoints RealPoints;
210 typedef typename SH::RealVectors RealVectors;
212 RealPoints positions;
213 RealVectors edge_vectors;
214 Scalar lengths = 0.0;
215 // We have: (1-t)*v0 + t*v1 = v
216 const auto t = [] ( Scalar v0, Scalar v1, Scalar v )
217 { return ( v - v0 ) / ( v1 - v0 ); };
218 for ( Face f = 0; f < smesh.nbFaces(); ++f )
219 { // form triangles between barycenter and consecutives vertices
220 const auto vb = face_values[ f ];
221 const auto xb = smesh.faceCentroid( f );
222 const auto& iv = smesh.incidentVertices( f );
223 for ( Size i = 0; i < iv.size(); ++i )
225 const auto vv0 = vertex_values[ iv[ i ] ];
226 const auto vv1 = vertex_values[ iv[ (i+1) % iv.size() ] ];
227 const auto xv0 = smesh.positions()[ iv[ i ] ];
228 const auto xv1 = smesh.positions()[ iv[ (i+1) % iv.size() ] ];
229 const bool ebv0 = ( vb - iso_value ) * ( vv0 - iso_value ) <= 0.0;
230 const bool ebv1 = ( vb - iso_value ) * ( vv1 - iso_value ) <= 0.0;
231 const bool ev0v1 = ( vv0 - iso_value ) * ( vv1 - iso_value ) <= 0.0;
232 if ( ! ebv0 && ! ebv1 ) continue;
233 std::vector<RealPoint> crossings;
235 const Scalar tbv0 = t( vb, vv0, iso_value );
236 crossings.push_back( (1.0 - tbv0)*xb + tbv0*xv0 );
239 const Scalar tbv1 = t( vb, vv1, iso_value );
240 crossings.push_back( (1.0 - tbv1)*xb + tbv1*xv1 );
243 const Scalar tv0v1 = t( vv0, vv1, iso_value );
244 crossings.push_back( (1.0 - tv0v1)*xv0 + tv0v1*xv1 );
246 if ( crossings.size() < 2 )
247 trace.warning() << "[SurfaceMeshWriter::writeIsoLinesOBJ]"
248 << " Weird iso-line on face " << f << std::endl;
250 for ( Size ii = 0; ii < crossings.size(); ++ii )
251 for ( Size j = ii+1; j < crossings.size(); ++j )
253 RealVector pq = crossings[ j ] - crossings[ii ];
254 if ( pq.squaredNorm() < 1e-8 ) continue;
255 positions.push_back( crossings[ ii ] );
256 edge_vectors.push_back( pq );
261 const Scalar avg_el = smesh.averageEdgeLength();
262 return SH::saveVectorFieldOBJ
263 ( positions, edge_vectors, avg_el*relative_thickness,
264 Colors(), objfile, ambient_color, diffuse_color, specular_color );
267 //-----------------------------------------------------------------------------
268 template <typename TRealPoint, typename TRealVector>
270 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
271 writeIsoLinesOBJ( std::string objfile,
272 const SurfaceMesh & smesh,
273 const Scalars& face_values,
274 const Scalars& vertex_values,
275 const Scalars& iso_values,
276 const double relative_thickness,
277 const Colors& diffuse_colors,
278 const Color& ambient_color,
279 const Color& diffuse_color,
280 const Color& specular_color )
282 typedef KhalimskySpaceND< 3, int > KSpace;
283 typedef Shortcuts< KSpace > SH;
284 typedef typename SH::RealPoints RealPoints;
285 typedef typename SH::RealVectors RealVectors;
287 RealPoints positions;
288 RealVectors edge_vectors;
289 Colors output_colors;
290 // We have: (1-t)*v0 + t*v1 = v
291 const auto t = [] ( Scalar v0, Scalar v1, Scalar v )
292 { return ( v - v0 ) / ( v1 - v0 ); };
293 for ( Face f = 0; f < smesh.nbFaces(); ++f )
294 { // form triangles between barycenter and consecutives vertices
295 const auto vb = face_values[ f ];
296 const auto xb = smesh.faceCentroid( f );
297 const auto& iv = smesh.incidentVertices( f );
298 for ( Size i = 0; i < iv.size(); ++i )
300 const auto vv0 = vertex_values[ iv[ i ] ];
301 const auto vv1 = vertex_values[ iv[ (i+1) % iv.size() ] ];
302 const auto xv0 = smesh.positions()[ iv[ i ] ];
303 const auto xv1 = smesh.positions()[ iv[ (i+1) % iv.size() ] ];
304 for ( Size iso_i = 0; iso_i < iso_values.size(); ++iso_i )
306 const auto iso_value = iso_values[ iso_i ];
307 const bool ebv0 = ( vb - iso_value ) * ( vv0 - iso_value ) <= 0.0;
308 const bool ebv1 = ( vb - iso_value ) * ( vv1 - iso_value ) <= 0.0;
309 const bool ev0v1 = ( vv0 - iso_value ) * ( vv1 - iso_value ) <= 0.0;
310 if ( ! ebv0 && ! ebv1 ) continue;
311 std::vector<RealPoint> crossings;
313 const Scalar tbv0 = t( vb, vv0, iso_value );
314 crossings.push_back( (1.0 - tbv0)*xb + tbv0*xv0 );
317 const Scalar tbv1 = t( vb, vv1, iso_value );
318 crossings.push_back( (1.0 - tbv1)*xb + tbv1*xv1 );
321 const Scalar tv0v1 = t( vv0, vv1, iso_value );
322 crossings.push_back( (1.0 - tv0v1)*xv0 + tv0v1*xv1 );
324 if ( crossings.size() < 2 )
325 trace.warning() << "[SurfaceMeshWriter::writeIsoLinesOBJ]"
326 << " Weird iso-line on face " << f << std::endl;
328 for ( Size ii = 0; ii < crossings.size(); ++ii )
329 for ( Size j = ii+1; j < crossings.size(); ++j )
331 RealVector pq = crossings[ j ] - crossings[ ii ];
332 if ( pq.squaredNorm() < 1e-8 ) continue;
333 positions.push_back( crossings[ ii ] );
334 edge_vectors.push_back( pq );
335 if ( ! diffuse_colors.empty() )
336 output_colors.push_back( diffuse_colors[ iso_i ] );
339 } // for ( Size iso_i = 0; iso_i < iso_values.size(); ++iso_i )
340 } // for ( Size i = 0; i < iv.size(); ++i )
341 } // for ( Face f = 0; f < smesh.nbFaces(); ++f )
342 const Scalar avg_el = smesh.averageEdgeLength();
343 return SH::saveVectorFieldOBJ
344 ( positions, edge_vectors, avg_el*relative_thickness,
345 output_colors, objfile, ambient_color, diffuse_color, specular_color );
348 ///////////////////////////////////////////////////////////////////////////////
349 ///////////////////////////////////////////////////////////////////////////////