DGtal  1.5.beta
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::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 //-----------------------------------------------------------------------------
194 template <typename TRealPoint, typename TRealVector>
195 bool
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 )
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 //-----------------------------------------------------------------------------
268 template <typename TRealPoint, typename TRealVector>
269 bool
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 )
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 ///////////////////////////////////////////////////////////////////////////////