DGtal 1.3.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::RealPoint RealPoint;
172 typedef typename SH::RealPoints RealPoints;
173 typedef typename SH::RealVector RealVector;
174 typedef typename SH::RealVectors RealVectors;
175 typedef typename SH::Colors Colors;
176 typedef typename SurfaceMesh::Edge Edge;
177
178 RealPoints positions;
179 RealVectors edge_vectors;
180 double lengths = 0.0;
181 for ( Edge e = 0; e < smesh.nbEdges(); ++e )
182 {
183 auto vtcs = smesh.edgeVertices()[ e ];
184 const RealPoint p = smesh.positions()[ vtcs.first ];
185 const RealVector pq = smesh.positions()[ vtcs.second ] - p;
186 lengths += pq.norm();
187 if ( ! edge_predicate( e ) ) continue;
188 positions.push_back( p );
189 edge_vectors.push_back( pq );
190 }
191 lengths /= smesh.nbEdges();
192 return SH::saveVectorFieldOBJ
193 ( positions, edge_vectors, lengths*relative_thickness,
194 Colors(), objfile, ambient_color, diffuse_color, specular_color );
195}
196//-----------------------------------------------------------------------------
197template <typename TRealPoint, typename TRealVector>
198bool
199DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
200writeIsoLinesOBJ( std::string objfile,
201 const SurfaceMesh & smesh,
202 const Scalars& face_values,
203 const Scalars& vertex_values,
204 const Scalar iso_value,
205 const double relative_thickness,
206 const Color& ambient_color,
207 const Color& diffuse_color,
208 const Color& specular_color )
209{
210 typedef KhalimskySpaceND< 3, int > KSpace;
211 typedef Shortcuts< KSpace > SH;
212 typedef typename SH::RealPoint RealPoint;
213 typedef typename SH::RealPoints RealPoints;
214 typedef typename SH::RealVector RealVector;
215 typedef typename SH::RealVectors RealVectors;
216 typedef typename SH::Colors Colors;
217
218 RealPoints positions;
219 RealVectors edge_vectors;
220 Scalar lengths = 0.0;
221 // We have: (1-t)*v0 + t*v1 = v
222 const auto t = [] ( Scalar v0, Scalar v1, Scalar v )
223 { return ( v - v0 ) / ( v1 - v0 ); };
224 for ( Face f = 0; f < smesh.nbFaces(); ++f )
225 { // form triangles between barycenter and consecutives vertices
226 const auto vb = face_values[ f ];
227 const auto xb = smesh.faceCentroid( f );
228 const auto& iv = smesh.incidentVertices( f );
229 for ( Size i = 0; i < iv.size(); ++i )
230 {
231 const auto vv0 = vertex_values[ iv[ i ] ];
232 const auto vv1 = vertex_values[ iv[ (i+1) % iv.size() ] ];
233 const auto xv0 = smesh.positions()[ iv[ i ] ];
234 const auto xv1 = smesh.positions()[ iv[ (i+1) % iv.size() ] ];
235 const bool ebv0 = ( vb - iso_value ) * ( vv0 - iso_value ) <= 0.0;
236 const bool ebv1 = ( vb - iso_value ) * ( vv1 - iso_value ) <= 0.0;
237 const bool ev0v1 = ( vv0 - iso_value ) * ( vv1 - iso_value ) <= 0.0;
238 if ( ! ebv0 && ! ebv1 ) continue;
239 std::vector<RealPoint> crossings;
240 if ( ebv0 ) {
241 const Scalar tbv0 = t( vb, vv0, iso_value );
242 crossings.push_back( (1.0 - tbv0)*xb + tbv0*xv0 );
243 }
244 if ( ebv1 ) {
245 const Scalar tbv1 = t( vb, vv1, iso_value );
246 crossings.push_back( (1.0 - tbv1)*xb + tbv1*xv1 );
247 }
248 if ( ev0v1 ) {
249 const Scalar tv0v1 = t( vv0, vv1, iso_value );
250 crossings.push_back( (1.0 - tv0v1)*xv0 + tv0v1*xv1 );
251 }
252 if ( crossings.size() < 2 )
253 trace.warning() << "[SurfaceMeshWriter::writeIsoLinesOBJ]"
254 << " Weird iso-line on face " << f << std::endl;
255 else {
256 for ( Size i = 0; i < crossings.size(); ++i )
257 for ( Size j = i+1; j < crossings.size(); ++j )
258 {
259 RealVector pq = crossings[ j ] - crossings[ i ];
260 if ( pq.squaredNorm() < 1e-8 ) continue;
261 positions.push_back( crossings[ i ] );
262 edge_vectors.push_back( pq );
263 }
264 }
265 }
266 }
267 const Scalar avg_el = smesh.averageEdgeLength();
268 return SH::saveVectorFieldOBJ
269 ( positions, edge_vectors, avg_el*relative_thickness,
270 Colors(), objfile, ambient_color, diffuse_color, specular_color );
271}
272
273//-----------------------------------------------------------------------------
274template <typename TRealPoint, typename TRealVector>
275bool
276DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
277writeIsoLinesOBJ( std::string objfile,
278 const SurfaceMesh & smesh,
279 const Scalars& face_values,
280 const Scalars& vertex_values,
281 const Scalars& iso_values,
282 const double relative_thickness,
283 const Colors& diffuse_colors,
284 const Color& ambient_color,
285 const Color& diffuse_color,
286 const Color& specular_color )
287{
288 typedef KhalimskySpaceND< 3, int > KSpace;
289 typedef Shortcuts< KSpace > SH;
290 typedef typename SH::RealPoint RealPoint;
291 typedef typename SH::RealPoints RealPoints;
292 typedef typename SH::RealVector RealVector;
293 typedef typename SH::RealVectors RealVectors;
294 typedef typename SH::Colors Colors;
295
296 RealPoints positions;
297 RealVectors edge_vectors;
298 Scalar lengths = 0.0;
299 Colors output_colors;
300 // We have: (1-t)*v0 + t*v1 = v
301 const auto t = [] ( Scalar v0, Scalar v1, Scalar v )
302 { return ( v - v0 ) / ( v1 - v0 ); };
303 for ( Face f = 0; f < smesh.nbFaces(); ++f )
304 { // form triangles between barycenter and consecutives vertices
305 const auto vb = face_values[ f ];
306 const auto xb = smesh.faceCentroid( f );
307 const auto& iv = smesh.incidentVertices( f );
308 for ( Size i = 0; i < iv.size(); ++i )
309 {
310 const auto vv0 = vertex_values[ iv[ i ] ];
311 const auto vv1 = vertex_values[ iv[ (i+1) % iv.size() ] ];
312 const auto xv0 = smesh.positions()[ iv[ i ] ];
313 const auto xv1 = smesh.positions()[ iv[ (i+1) % iv.size() ] ];
314 for ( Size iso_i = 0; iso_i < iso_values.size(); ++iso_i )
315 {
316 const auto iso_value = iso_values[ iso_i ];
317 const bool ebv0 = ( vb - iso_value ) * ( vv0 - iso_value ) <= 0.0;
318 const bool ebv1 = ( vb - iso_value ) * ( vv1 - iso_value ) <= 0.0;
319 const bool ev0v1 = ( vv0 - iso_value ) * ( vv1 - iso_value ) <= 0.0;
320 if ( ! ebv0 && ! ebv1 ) continue;
321 std::vector<RealPoint> crossings;
322 if ( ebv0 ) {
323 const Scalar tbv0 = t( vb, vv0, iso_value );
324 crossings.push_back( (1.0 - tbv0)*xb + tbv0*xv0 );
325 }
326 if ( ebv1 ) {
327 const Scalar tbv1 = t( vb, vv1, iso_value );
328 crossings.push_back( (1.0 - tbv1)*xb + tbv1*xv1 );
329 }
330 if ( ev0v1 ) {
331 const Scalar tv0v1 = t( vv0, vv1, iso_value );
332 crossings.push_back( (1.0 - tv0v1)*xv0 + tv0v1*xv1 );
333 }
334 if ( crossings.size() < 2 )
335 trace.warning() << "[SurfaceMeshWriter::writeIsoLinesOBJ]"
336 << " Weird iso-line on face " << f << std::endl;
337 else {
338 for ( Size i = 0; i < crossings.size(); ++i )
339 for ( Size j = i+1; j < crossings.size(); ++j )
340 {
341 RealVector pq = crossings[ j ] - crossings[ i ];
342 if ( pq.squaredNorm() < 1e-8 ) continue;
343 positions.push_back( crossings[ i ] );
344 edge_vectors.push_back( pq );
345 if ( ! diffuse_colors.empty() )
346 output_colors.push_back( diffuse_colors[ iso_i ] );
347 }
348 }
349 } // for ( Size iso_i = 0; iso_i < iso_values.size(); ++iso_i )
350 } // for ( Size i = 0; i < iv.size(); ++i )
351 } // for ( Face f = 0; f < smesh.nbFaces(); ++f )
352 const Scalar avg_el = smesh.averageEdgeLength();
353 return SH::saveVectorFieldOBJ
354 ( positions, edge_vectors, avg_el*relative_thickness,
355 output_colors, objfile, ambient_color, diffuse_color, specular_color );
356}
357
358///////////////////////////////////////////////////////////////////////////////
359///////////////////////////////////////////////////////////////////////////////