DGtal  1.1.0
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 //-----------------------------------------------------------------------------
43 template <typename TRealPoint, typename TRealVector>
44 bool
45 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
46 writeOBJ( 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 //-----------------------------------------------------------------------------
71 template <typename TRealPoint, typename TRealVector>
72 bool
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 )
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 //-----------------------------------------------------------------------------
157 template <typename TRealPoint, typename TRealVector>
158 template <typename EdgePredicate>
159 bool
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 )
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 //-----------------------------------------------------------------------------
197 template <typename TRealPoint, typename TRealVector>
198 bool
199 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
200 writeIsoLinesOBJ( 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 //-----------------------------------------------------------------------------
274 template <typename TRealPoint, typename TRealVector>
275 bool
276 DGtal::SurfaceMeshWriter<TRealPoint, TRealVector>::
277 writeIsoLinesOBJ( 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 ///////////////////////////////////////////////////////////////////////////////