DGtal 1.4.0
Loading...
Searching...
No Matches
SurfaceMeshWriter.ih
1/**
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.
6 *
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.
11 *
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/>.
14 *
15 **/
16
17/**
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
21 *
22 * @date 2020/02/18
23 *
24 * Implementation of inline methods defined in SurfaceMeshWriter.h
25 *
26 * This file is part of the DGtal library.
27 */
28
29
30//////////////////////////////////////////////////////////////////////////////
31#include <cstdlib>
32#include <limits>
33#include "DGtal/shapes/MeshHelpers.h"
34#include "DGtal/helpers/Shortcuts.h"
35//////////////////////////////////////////////////////////////////////////////
36
37
38///////////////////////////////////////////////////////////////////////////////
39// IMPLEMENTATION of inline methods.
40///////////////////////////////////////////////////////////////////////////////
41
42//-----------------------------------------------------------------------------
43template <typename TRealPoint, typename TRealVector>
44bool
45DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
46writeOBJ( std::ostream & output, const SurfaceMesh & smesh )
47{
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() )
55 {
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;
59 }
60 for ( auto f : smesh.allIncidentVertices() )
61 {
62 output << "f";
63 for ( auto v : f ) output << " " << (v+1);
64 output << std::endl;
65 }
66 output << "# " << smesh.allIncidentVertices().size() << " faces" << std::endl;
67 return output.good();
68}
69
70//-----------------------------------------------------------------------------
71template <typename TRealPoint, typename TRealVector>
72bool
73DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
74writeOBJ( 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 )
80{
81 std::string mtlfile;
82 auto lastindex = objfile.find_last_of(".");
83 if ( lastindex == std::string::npos )
84 {
85 mtlfile = objfile + ".mtl";
86 objfile = objfile + ".obj";
87 }
88 else
89 {
90 mtlfile = objfile.substr(0, lastindex) + ".mtl";
91 }
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;
102 // Write positions
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() )
108 {
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;
112 }
113 // Taking care of materials
114 bool has_material = ( smesh.nbFaces() == diffuse_colors.size() );
115 Index idxMaterial = 0;
116 std::map<Color, Index > mapMaterial;
117 if ( has_material )
118 {
119 for ( Index f = 0; f < diffuse_colors.size(); ++f )
120 {
121 Color c = diffuse_colors[ f ];
122 if ( mapMaterial.count( c ) == 0 )
123 {
124 MeshHelpers::exportMTLNewMaterial
125 ( output_mtl, idxMaterial, ambient_color, c, specular_color );
126 mapMaterial[ c ] = idxMaterial++;
127 }
128 }
129 }
130 else
131 {
132 MeshHelpers::exportMTLNewMaterial
133 ( output_mtl, idxMaterial, ambient_color, diffuse_color, specular_color );
134 }
135 // Write faces with material(s)
136 Index idx_f = 0;
137 for ( auto f : smesh.allIncidentVertices() )
138 {
139 output_obj << "usemtl material_"
140 << ( has_material ? mapMaterial[ diffuse_colors[ idx_f ] ] : idxMaterial )
141 << std::endl;
142 output_obj << "f";
143 for ( auto v : f )
144 if ( smesh.vertexNormals().empty() )
145 output_obj << " " << (v+1);
146 else
147 output_obj << " " << (v+1) << "//" << (v+1);
148 output_obj << std::endl;
149 idx_f++;
150 }
151 output_obj << "# " << smesh.allIncidentVertices().size() << " faces" << std::endl;
152 output_mtl.close();
153 return output_obj.good();
154}
155
156//-----------------------------------------------------------------------------
157template <typename TRealPoint, typename TRealVector>
158template <typename EdgePredicate>
159bool
160DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
161writeEdgeLinesOBJ( 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 )
168{
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;
174
175 RealPoints positions;
176 RealVectors edge_vectors;
177 double lengths = 0.0;
178 for ( Edge e = 0; e < smesh.nbEdges(); ++e )
179 {
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 );
187 }
188 lengths /= smesh.nbEdges();
189 return SH::saveVectorFieldOBJ
190 ( positions, edge_vectors, lengths*relative_thickness,
191 Colors(), objfile, ambient_color, diffuse_color, specular_color );
192}
193//-----------------------------------------------------------------------------
194template <typename TRealPoint, typename TRealVector>
195bool
196DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
197writeIsoLinesOBJ( 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 )
206{
207 typedef KhalimskySpaceND< 3, int > KSpace;
208 typedef Shortcuts< KSpace > SH;
209 typedef typename SH::RealPoints RealPoints;
210 typedef typename SH::RealVectors RealVectors;
211
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 )
224 {
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;
234 if ( ebv0 ) {
235 const Scalar tbv0 = t( vb, vv0, iso_value );
236 crossings.push_back( (1.0 - tbv0)*xb + tbv0*xv0 );
237 }
238 if ( ebv1 ) {
239 const Scalar tbv1 = t( vb, vv1, iso_value );
240 crossings.push_back( (1.0 - tbv1)*xb + tbv1*xv1 );
241 }
242 if ( ev0v1 ) {
243 const Scalar tv0v1 = t( vv0, vv1, iso_value );
244 crossings.push_back( (1.0 - tv0v1)*xv0 + tv0v1*xv1 );
245 }
246 if ( crossings.size() < 2 )
247 trace.warning() << "[SurfaceMeshWriter::writeIsoLinesOBJ]"
248 << " Weird iso-line on face " << f << std::endl;
249 else {
250 for ( Size ii = 0; ii < crossings.size(); ++ii )
251 for ( Size j = ii+1; j < crossings.size(); ++j )
252 {
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 );
257 }
258 }
259 }
260 }
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 );
265}
266
267//-----------------------------------------------------------------------------
268template <typename TRealPoint, typename TRealVector>
269bool
270DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
271writeIsoLinesOBJ( 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 )
281{
282 typedef KhalimskySpaceND< 3, int > KSpace;
283 typedef Shortcuts< KSpace > SH;
284 typedef typename SH::RealPoints RealPoints;
285 typedef typename SH::RealVectors RealVectors;
286
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 )
299 {
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 )
305 {
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;
312 if ( ebv0 ) {
313 const Scalar tbv0 = t( vb, vv0, iso_value );
314 crossings.push_back( (1.0 - tbv0)*xb + tbv0*xv0 );
315 }
316 if ( ebv1 ) {
317 const Scalar tbv1 = t( vb, vv1, iso_value );
318 crossings.push_back( (1.0 - tbv1)*xb + tbv1*xv1 );
319 }
320 if ( ev0v1 ) {
321 const Scalar tv0v1 = t( vv0, vv1, iso_value );
322 crossings.push_back( (1.0 - tv0v1)*xv0 + tv0v1*xv1 );
323 }
324 if ( crossings.size() < 2 )
325 trace.warning() << "[SurfaceMeshWriter::writeIsoLinesOBJ]"
326 << " Weird iso-line on face " << f << std::endl;
327 else {
328 for ( Size ii = 0; ii < crossings.size(); ++ii )
329 for ( Size j = ii+1; j < crossings.size(); ++j )
330 {
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 ] );
337 }
338 }
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 );
346}
347
348///////////////////////////////////////////////////////////////////////////////
349///////////////////////////////////////////////////////////////////////////////