aimsdata  5.0.5
Neuroimaging data handling
wavefrontmeshW.h
Go to the documentation of this file.
1 /* This software and supporting documentation are distributed by
2  * Institut Federatif de Recherche 49
3  * CEA/NeuroSpin, Batiment 145,
4  * 91191 Gif-sur-Yvette cedex
5  * France
6  *
7  * This software is governed by the CeCILL-B license under
8  * French law and abiding by the rules of distribution of free software.
9  * You can use, modify and/or redistribute the software under the
10  * terms of the CeCILL-B license as circulated by CEA, CNRS
11  * and INRIA at the following URL "http://www.cecill.info".
12  *
13  * As a counterpart to the access to the source code and rights to copy,
14  * modify and redistribute granted by the license, users are provided only
15  * with a limited warranty and the software's author, the holder of the
16  * economic rights, and the successive licensors have only limited
17  * liability.
18  *
19  * In this respect, the user's attention is drawn to the risks associated
20  * with loading, using, modifying and/or developing or reproducing the
21  * software by the user in light of its specific status of free software,
22  * that may mean that it is complicated to manipulate, and that also
23  * therefore means that it is reserved for developers and experienced
24  * professionals having in-depth computer knowledge. Users are therefore
25  * encouraged to load and test the software's suitability as regards their
26  * requirements in conditions enabling the security of their systems and/or
27  * data to be ensured and, more generally, to use and operate it in the
28  * same conditions as regards security.
29  *
30  * The fact that you are presently reading this means that you have had
31  * knowledge of the CeCILL-B license and that you accept its terms.
32  */
33 
34 #ifndef AIMS_IO_WAVEFRONTMESHW_H
35 #define AIMS_IO_WAVEFRONTMESHW_H
36 
38 #include <aims/io/defaultItemW.h>
39 #include <aims/data/pheader.h>
40 #include <aims/mesh/surface.h>
41 #include <aims/io/writer.h>
42 #include <aims/io/datatypecode.h>
45 
46 
47 namespace
48 {
49 
50  template <typename T>
51  inline
52  T _clamp( const T& value, float m )
53  {
54  T new_value;
55 
56  if( (float) value < m ) // clamp
57  new_value = (T) m;
58  else if( (float) value > 1. - m )
59  new_value = (T) ( 1. - m );
60  else
61  return value;
62 
63  return new_value;
64  }
65 
66 
67  template <>
68  inline
69  Void _clamp( const Void &, float )
70  {
71  return Void();
72  }
73 
74 
75  template <typename T>
76  class _printTextureCoord
77  {
78  public:
79  inline static
80  std::string p( const T & tex, float m = 0.001 )
81  {
82  std::stringstream s;
83  s << _clamp( tex, m ) << " 0"; // mut extend to 2D
84  return s.str();
85  }
86  };
87 
88 
89  template <typename T, int D>
90  class _printTextureCoord< AimsVector<T, D> >
91  {
92  public:
93  inline static
94  std::string p( const AimsVector<T, D> & tex, float m )
95  {
96  std::stringstream s;
97  for( int i=0; i<3 && i<D; ++i )
98  {
99  if( i != 0 )
100  s << " ";
101  s << _clamp( tex[i], m );
102  }
103  if( D == 1 )
104  s << " 0";
105  return s.str();
106  }
107  };
108 
109 }
110 
111 
112 namespace aims
113 {
114 
115  template<int D, class T>
117  {
118  public:
119  WavefrontMeshWriter( const std::string & name )
120  : _name( name ) { }
122 
123  inline void write( const AimsTimeSurface<D,T> & thing,
124  carto::Object options = carto::none() );
125 
127  inline static std::string removeExtension( const std::string& name );
128 
129  private:
130  inline static PythonHeader* writeHeader(
131  const Header & header, std::ostream & os, const std::string & filename,
132  const std::string & dtype, carto::Object options );
133  inline static void writeObjectHeader( std::ostream & os, int timestep,
134  PythonHeader *hdr,
135  const std::string & obj_filename,
136  carto::Object options );
137  inline static void writeMtl( PythonHeader *hdr, carto::Object options );
138  std::string _name;
139  };
140 
141 
142  template<int D>
144  {
145  public:
146  WavefrontMeshWriter( const std::string & name )
147  : _name( name ) { }
149 
150  inline void write( const AimsTimeSurface<D, Void> & thing,
151  carto::Object options = carto::none() );
152 
153  private:
154  template <int U, typename T>
156 
158  inline static std::string removeExtension(const std::string& name);
159 
160  inline static PythonHeader* writeHeader(
161  const Header & header, std::ostream & os, const std::string & filename,
162  const std::string & dtype, carto::Object options );
163  inline static void writeObjectHeader(
164  std::ostream & os, int timestep, PythonHeader *hdr,
165  const std::string & obj_filename, carto::Object options );
166  inline static void writeMtl( PythonHeader *hdr, carto::Object options );
167 
168  private:
169  std::string _name;
170  };
171 
172 
173  template <int D, class T>
174  inline
176  operator << ( WavefrontMeshWriter<D,T> & writer,
177  const AimsTimeSurface<D,T> & thing )
178  {
179  writer.write( thing );
180  return( writer );
181  }
182 
183 
184  template <int D, class T> inline
186  const std::string& name )
187  {
189  }
190 
191 
192  template <int D, class T> inline
194  const Header & header, std::ostream & os, const std::string & filename,
195  const std::string & dtype, carto::Object options )
196  {
197  return WavefrontMeshWriter<D, Void>::writeHeader( header, os, filename,
198  dtype, options );
199  }
200 
201 
202  template <int D, class T> inline
204  std::ostream & os, int timestep,
205  PythonHeader *hdr, const std::string & obj_filename,
206  carto::Object options )
207  {
209  obj_filename, options );
210  }
211 
212 
213  template <int D, class T> inline
215  carto::Object options )
216  {
218  }
219 
220  // ---
221 
222  template <int D> inline
224  const std::string& name )
225  {
226  std::string res = name;
227  std::string ext="";
228  if( res.length() > 4 )
229  ext = res.substr( int(res.length() - 4), 4 );
230  if( ext == ".obj" )
231  res = res.substr( 0, res.length() - 4 );
232  return res;
233  }
234 
235 
236  template <int D>
238  const Header & header, std::ostream & os, const std::string & filename,
239  const std::string & dtype, carto::Object /* options */ )
240  {
241  PythonHeader *hdr = new PythonHeader;
242  const PythonHeader
243  *ph = dynamic_cast<const PythonHeader *>( &header );
244  if( ph )
245  hdr->copy( *ph );
246 
247  if( hdr->hasProperty( "nb_t_pos" ) )
248  hdr->removeProperty( "nb_t_pos" );
249  hdr->setProperty( "file_type", std::string( "WAVEFRONT" ) );
250  hdr->setProperty( "object_type",
252  AimsTimeSurface<D, Void> >::objectType() );
253  if( !dtype.empty() )
254  hdr->setProperty( "data_type", dtype );
255 
256  os << "# Wavefront OBJ\n" << std::endl;
257 
258  carto::Object material;
259  try
260  {
261  material = hdr->getProperty( "material" );
262  if( material.get() )
263  {
264  std::string mtlfilename = removeExtension( filename ) + ".mtl";
265  os << "mtllib " << carto::FileUtil::basename( mtlfilename )
266  << std::endl << std::endl;
267 
268  hdr->setProperty( "mtl", carto::Dictionary() );
269  carto::Object mtl = hdr->getProperty( "mtl" );
270  mtl->setProperty( "filename", mtlfilename );
271  }
272  }
273  catch( ... )
274  {
275  }
276 
277  return hdr;
278  }
279 
280 
281  template <int D>
283  std::ostream & os, int timestep, PythonHeader *hdr,
284  const std::string & obj_filename, carto::Object /* options */ )
285  {
286  std::stringstream namess;
288  obj_filename ) ) << "_" << timestep;
289  std::string name = namess.str();
290  os << "o " << name << std::endl << std::endl;
291  os << "s 1\n" << std::endl;
292 
293  if( hdr->hasProperty( "material" ) )
294  {
295  carto::Object material, mtl;
296  material = hdr->getProperty( "material" );
297  mtl = hdr->getProperty( "mtl" );
298 
299  os << "usemtl " << name << std::endl << std::endl;
300 
302 
303  try
304  {
305  mtldict = mtl->getProperty( name );
306  }
307  catch( ... )
308  {
309  mtl->setProperty( name, mtldict );
310  }
311 
312  try
313  {
314  carto::Object ambient = material->getProperty( "ambient" );
315  carto::Object it = ambient->objectIterator();
316  std::stringstream s;
317  for( int i=0; it->isValid() && i < 3; it->next(), ++i )
318  {
319  if( i != 0 )
320  s << " ";
321  s << it->currentValue()->getScalar();
322  }
323  mtldict->setProperty( "Ka", s.str() );
324  }
325  catch( ... )
326  {
327  }
328 
329  try
330  {
331  carto::Object diffuse = material->getProperty( "diffuse" );
332  carto::Object it = diffuse->objectIterator();
333  std::stringstream s;
334  int i;
335  for( i=0; it->isValid() && i < 3; it->next(), ++i )
336  {
337  if( i != 0 )
338  s << " ";
339  s << it->currentValue()->getScalar();
340  }
341  mtldict->setProperty( "Kd", s.str() );
342  if( i == 3 && it->isValid() )
343  {
344  std::stringstream sd;
345  sd << it->currentValue()->getScalar();
346  mtldict->setProperty( "d", sd.str() );
347  }
348  }
349  catch( ... )
350  {
351  }
352 
353  try
354  {
355  carto::Object specular = material->getProperty( "specular" );
356  carto::Object it = specular->objectIterator();
357  std::stringstream s;
358  for( int i=0; it->isValid() && i < 3; it->next(), ++i )
359  {
360  if( i != 0 )
361  s << " ";
362  s << it->currentValue()->getScalar();
363  }
364  mtldict->setProperty( "Ks", s.str() );
365  }
366  catch( ... )
367  {
368  }
369 
370  try
371  {
372  carto::Object shininess = material->getProperty( "shininess" );
373  std::stringstream s;
374  s << shininess->getScalar();
375  mtldict->setProperty( "Ns", s.str() );
376  }
377  catch( ... )
378  {
379  }
380 
381  }
382 
383  if( hdr->hasProperty( "_texture_palettes" ) )
384  {
385  std::map<int, carto::Object> palettes;
386  hdr->getProperty( "_texture_palettes", palettes );
387  std::map<int, carto::Object>::iterator ip = palettes.find( timestep );
388  if( ip != palettes.end() )
389  {
390  std::string pal_fname = name + ".png";
391 
392  carto::Object mtl = hdr->getProperty( "mtl" );
394 
395  try
396  {
397  mtldict = mtl->getProperty( name );
398  }
399  catch( ... )
400  {
401  mtl->setProperty( name, mtldict );
402  }
403 
404  // texture space offsets / scaling.
405  // WARNING: blender doesn't support texture map options.
406  std::stringstream mapkd_val_s;
407  std::vector<float> texoffset;
408  if( hdr->getProperty( "texture_offset", texoffset )
409  && texoffset.size() == 3 )
410  {
411  mapkd_val_s << "-o " << texoffset[0] << " " << texoffset[1] << " "
412  << texoffset[2] << " ";
413  }
414  std::vector<float> texscale;
415  if( hdr->getProperty( "texture_scale", texscale )
416  && texscale.size() == 3 )
417  {
418  mapkd_val_s << "-s " << texscale[0] << " " << texscale[1] << " "
419  << texscale[2] << " ";
420  }
421  mtldict->setProperty( "map_Kd", mapkd_val_s.str() + pal_fname );
422  pal_fname = carto::FileUtil::dirname( obj_filename ) +
423  carto::FileUtil::separator() + pal_fname;
424  Writer<carto::Volume<AimsRGBA> > w( pal_fname );
425  w.write(
426  ip->second->value<carto::rc_ptr<carto::Volume<AimsRGBA> > >() );
427  }
428  }
429  }
430 
431 
432  template <int D>
434  carto::Object /* options */ )
435  {
436  carto::Object mtl;
437  try
438  {
439  mtl = hdr->getProperty( "mtl" );
440  }
441  catch( ... )
442  {
443  return;
444  }
445 
446  std::string mtlfilename = mtl->getProperty( "filename" )->getString();
447  std::ofstream ms( mtlfilename.c_str() );
448  carto::Object oit = mtl->objectIterator();
449  for( ; oit->isValid(); oit->next() )
450  {
451  if( oit->key() == "filename" )
452  continue;
453  std::string oname = oit->key();
454  ms << "newmtl " << oname << std::endl << std::endl;
455  carto::Object it = oit->currentValue()->objectIterator();
456  for( ; it->isValid(); it->next() )
457  {
458  ms << it->key() << " " << it->currentValue()->getString() << std::endl;
459  }
460  }
461  hdr->removeProperty( "mtl" );
462  }
463 
464  // ---
465 
466  template <int D, class T>
468  carto::Object options )
469  {
470  std::string fname = _name;
471  std::ofstream os( fname.c_str() );
472  if( !os )
473  throw carto::file_error( fname );
474 
475  PythonHeader *hdr = writeHeader(
476  thing.header(), os, _name,
477  carto::DataTypeCode< AimsTimeSurface<D, T> >::dataType(), options );
478 
479  typename AimsTimeSurface<D,T>::const_iterator is, es = thing.end();
480  int timestep = 0;
481 
482  for( is=thing.begin(); is!=es; ++is, ++timestep )
483  {
484  writeObjectHeader( os, timestep, hdr, fname, options );
485 
486  const std::vector<Point3df> &vert = (*is).second.vertex();
487  const std::vector<Point3df> &norm = (*is).second.normal();
488  const std::vector<AimsVector<uint,D> > &poly= is->second.polygon();
489 
490  // vertices
491  for( std::vector<Point3df>::const_iterator iv = vert.begin();
492  iv != vert.end(); ++iv )
493  os << "v " << (*iv)[0] << " " << (*iv)[1] << " " << (*iv)[2]
494  << std::endl;
495  os << std::endl;
496 
497  // normals
498  for( std::vector<Point3df>::const_iterator in = norm.begin();
499  in != norm.end(); ++in )
500  os << "vn " << (*in)[0] << " " << (*in)[1] << " " << (*in)[2]
501  << std::endl;
502  os << std::endl;
503 
504  // texture
505  typename std::vector<T>::const_iterator it,
506  et=is->second.texture().end();
507 
508  float m = 1. / 512; // minimum value to clamp, for a 256 size texture
509  /* need to clamp is maybe just due to blender ?
510  Apparently texture coord values of 0 and 1 are "outside" the texture
511  (rendered as a strange pink color (?))
512  */
513 
514  for( it=is->second.texture().begin(); it!=et; ++it )
515  {
516  os << "vt " << _printTextureCoord<T>::p( *it, m ) << std::endl;
517  }
518  os << std::endl;
519 
520  // polygons
521  for( typename std::vector<AimsVector<uint,D> >::const_iterator ip =
522  poly.begin(); ip != poly.end(); ++ip )
523  {
524  if( D == 2 )
525  os << "l";
526  else
527  os << "f";
528  for( int p=0; p<D; ++p )
529  // WARNING obj indices start at 1 (like matlab...)
530  os << " " << (*ip)[p] + 1 << "/" << (*ip)[p] + 1 << "/"
531  << (*ip)[p] + 1;
532  os << std::endl;
533  }
534  os << std::endl;
535 
536  }
537 
538  writeMtl( hdr, options );
539  hdr->writeMinf( fname + ".minf" );
540 
541  }
542 
543 
544 
545  template <int D>
547  const AimsTimeSurface<D, Void> & thing, carto::Object options )
548  {
549  std::string fname = _name;
550  std::ofstream os( fname.c_str() );
551  if( !os )
552  throw carto::file_error( fname );
553 
554  PythonHeader *hdr = writeHeader( thing.header(), os, _name, "VOID",
555  options );
556 
557  typename AimsTimeSurface<D, Void>::const_iterator is, es = thing.end();
558  int timestep = 0;
559 
560  for( is=thing.begin(); is!=es; ++is, ++timestep )
561  {
562  writeObjectHeader( os, timestep, hdr, fname, options );
563 
564  const std::vector<Point3df> &vert = (*is).second.vertex();
565  const std::vector<Point3df> &norm = (*is).second.normal();
566  const std::vector<AimsVector<uint,D> > &poly= is->second.polygon();
567 
568  // vertices
569  for( std::vector<Point3df>::const_iterator iv = vert.begin();
570  iv != vert.end(); ++iv )
571  os << "v " << (*iv)[0] << " " << (*iv)[1] << " " << (*iv)[2]
572  << std::endl;
573  os << std::endl;
574 
575  // normals
576  for( std::vector<Point3df>::const_iterator in = norm.begin();
577  in != norm.end(); ++in )
578  os << "vn " << (*in)[0] << " " << (*in)[1] << " " << (*in)[2]
579  << std::endl;
580  os << std::endl;
581 
582  // polygons
583  for( typename std::vector<AimsVector<uint,D> >::const_iterator ip =
584  poly.begin(); ip != poly.end(); ++ip )
585  {
586  if( D == 2 )
587  os << "l";
588  else
589  os << "f";
590  for( int p=0; p<D; ++p )
591  // WARNING obj indices start at 1 (like matlab...)
592  os << " " << (*ip)[p] + 1 << "//" << (*ip)[p] + 1;
593  os << std::endl;
594  }
595  os << std::endl;
596 
597  }
598 
599  writeMtl( hdr, options );
600  hdr->writeMinf( fname + ".minf" );
601  }
602 }
603 
604 
605 #endif
virtual bool getProperty(const std::string &, Object &) const
Attributed python-like header, stores all needed information about an object, currently used for volu...
Definition: pheader.h:51
static std::string basename(const std::string &)
virtual void copy(const PythonHeader &, bool keepUuid=false)
The class for EcatSino data write operation.
Definition: border.h:44
virtual bool writeMinf(const std::string &filename)
write meta-info header, non-const version (may change some attributes)
static std::string removeExtension(const std::string &)
virtual bool removeProperty(const std::string &)
static char separator()
The template class to manage a mesh with time if needed.
Definition: surface.h:290
virtual bool write(const T &obj, bool ascii=false, const std::string *format=0)
Finds the correct format and writes the object.
Definition: writer_d.h:108
void write(const AimsTimeSurface< D, T > &thing, carto::Object options=carto::none())
static std::string dirname(const std::string &)
WavefrontMeshWriter(const std::string &name)
WavefrontMeshWriter(const std::string &name)
static std::string removeExtension(const std::string &name)
Return a name without .obj extension.
static Object value()
GenericObject * get() const
Generic writer for every format of Aims object.
Definition: writer.h:92
std::map< std::string, Object > Dictionary
virtual void setProperty(const std::string &, Object)
AIMSDATA_API float norm(const Tensor &thing)
Definition: tensor.h:141
Object none()
Pointer to an empty aims::StructuringElement.
std::map< int, AimsSurface< D, T > >::const_iterator const_iterator
Definition: surface.h:310
virtual bool hasProperty(const std::string &) const
const aims::PythonHeader & header() const
Get the header.
Definition: surface.h:317