OpenWalnut  1.5.0dev
WMWriteMesh.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <fstream>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 #include "WMWriteMesh.h"
31 #include "WMWriteMesh.xpm"
32 #include "core/common/WLimits.h"
33 #include "core/common/WPathHelper.h"
34 #include "core/common/WStringUtils.h"
35 #include "core/common/math/WMath.h"
36 #include "core/kernel/WKernel.h"
37 
38 // This line is needed by the module loader to actually find your module. Do not remove. Do NOT add a ";" here.
39 W_LOADABLE_MODULE( WMWriteMesh )
40 
41 WMWriteMesh::WMWriteMesh():
42  WModule()
43 {
44 }
45 
46 WMWriteMesh::~WMWriteMesh()
47 {
48  // Cleanup!
49 }
50 
51 std::shared_ptr< WModule > WMWriteMesh::factory() const
52 {
53  // See "src/modules/template/" for an extensively documented example.
54  return std::shared_ptr< WModule >( new WMWriteMesh() );
55 }
56 
57 const char** WMWriteMesh::getXPMIcon() const
58 {
59  return WMWriteMesh_xpm;
60 }
61 const std::string WMWriteMesh::getName() const
62 {
63  return "Write Mesh";
64 }
65 
66 const std::string WMWriteMesh::getDescription() const
67 {
68  // Specify your module description here. Be detailed. This text is read by the user.
69  // See "src/modules/template/" for an extensively documented example.
70  return "This module writes a triangle mesh to a file. Different file formats are supported.";
71 }
72 
74 {
75  m_meshInput = std::shared_ptr< WModuleInputData < WTriangleMesh > >(
76  new WModuleInputData< WTriangleMesh >( shared_from_this(), "mesh", "The mesh to save" )
77  );
78 
80 
82 }
83 
85 {
86  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
87  m_savePropGroup = m_properties->addPropertyGroup( "Save Surface", "" );
89  m_meshFile = m_savePropGroup->addProperty( "Mesh file", "", WPathHelper::getAppPath() );
90 
91  m_fileTypeSelectionsList = std::shared_ptr< WItemSelection >( new WItemSelection() );
92  m_fileTypeSelectionsList->addItem( "VTK ASCII", "" );
93  m_fileTypeSelectionsList->addItem( "json", "" );
94  m_fileTypeSelectionsList->addItem( "STL", "Ascii Stereo Lithography File." );
95 
96  m_fileTypeSelection = m_properties->addProperty( "File type", "file type.", m_fileTypeSelectionsList->getSelectorFirst() );
98 
99  m_writeColors = m_properties->addProperty( "Write colors", "", true );
100 
102 }
103 
105 {
106  // Put the code for your module's main functionality here.
107  // See "src/modules/template/" for an extensively documented example.
108  m_moduleState.add( m_meshInput->getDataChangedCondition() );
110 
111  // signal ready state
112  ready();
113 
114  // loop until the module container requests the module to quit
115  while( !m_shutdownFlag() )
116  {
117  // this waits for m_moduleState to fire. By default, this is only the m_shutdownFlag condition.
118  // NOTE: you can add your own conditions to m_moduleState using m_moduleState.add( ... )
120 
121  if( m_triMesh != m_meshInput->getData() )
122  {
123  m_triMesh = m_meshInput->getData();
124  }
125 
127 
128  switch( m_fileTypeSelection->get( true ).getItemIndexOfSelected( 0 ) )
129  {
130  case 0:
131  debugLog() << "type VTK ASCII file selected";
132  saveVTKASCII();
133  break;
134  case 1:
135  debugLog() << "type json file selected";
136  saveJson();
137  break;
138  case 2:
139  debugLog() << "type STL file selected";
140  saveSTL();
141  break;
142  default:
143  debugLog() << "this shouldn't be reached";
144  break;
145  }
147  }
148 }
149 
151 {
152  if( !m_triMesh )
153  {
154  return false;
155  }
156 
157  if( m_triMesh->vertSize() == 0 )
158  {
159  WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Write Mesh", LL_ERROR );
160  return false;
161  }
162 
163  if( m_triMesh->triangleSize() == 0 )
164  {
165  WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 triangles.", "Write Mesh", LL_ERROR );
166  return false;
167  }
168 
169  const char* c_file = m_meshFile->get().string().c_str();
170  std::ofstream dataFile( c_file, std::ios_base::binary );
171 
172  if( dataFile )
173  {
174  WLogger::getLogger()->addLogMessage( "opening file", "Write Mesh", LL_DEBUG );
175  }
176  else
177  {
178  WLogger::getLogger()->addLogMessage( "open file failed" + m_meshFile->get().string() , "Write Mesh", LL_ERROR );
179  return false;
180  }
181 
182  dataFile.precision( 16 );
183 
184  WLogger::getLogger()->addLogMessage( "start writing file", "Write Mesh", LL_DEBUG );
185  dataFile << ( "# vtk DataFile Version 2.0\n" );
186  dataFile << ( "generated using OpenWalnut\n" );
187  dataFile << ( "ASCII\n" );
188  dataFile << ( "DATASET UNSTRUCTURED_GRID\n" );
189 
190  WPosition point;
191  dataFile << "POINTS " << m_triMesh->vertSize() << " float\n";
192  for( size_t i = 0; i < m_triMesh->vertSize(); ++i )
193  {
194  point = m_triMesh->getVertex( i );
195  if( !( !wlimits::isInf( point[0] ) && !wlimits::isInf( point[1] ) && !wlimits::isInf( point[2] ) ) )
196  {
197  WLogger::getLogger()->addLogMessage( "Will not write file from data that contains NAN or INF.", "Write Mesh", LL_ERROR );
198  return false;
199  }
200  dataFile << point[0] << " " << point[1] << " " << point[2] << "\n";
201  }
202 
203  dataFile << "CELLS " << m_triMesh->triangleSize() << " " << m_triMesh->triangleSize() * 4 << "\n";
204  for( size_t i = 0; i < m_triMesh->triangleSize(); ++i )
205  {
206  dataFile << "3 " << m_triMesh->getTriVertId0( i ) << " "
207  << m_triMesh->getTriVertId1( i ) << " "
208  << m_triMesh->getTriVertId2( i ) << "\n";
209  }
210  dataFile << "CELL_TYPES "<< m_triMesh->triangleSize() <<"\n";
211  for( size_t i = 0; i < m_triMesh->triangleSize(); ++i )
212  {
213  dataFile << "5\n";
214  }
215  dataFile << "POINT_DATA " << m_triMesh->vertSize() << "\n";
216  dataFile << "SCALARS scalars float\n";
217  dataFile << "LOOKUP_TABLE default\n";
218  for( size_t i = 0; i < m_triMesh->vertSize(); ++i )
219  {
220  dataFile << "0\n";
221  }
222  dataFile.close();
223  WLogger::getLogger()->addLogMessage( "saving done", "Write Mesh", LL_DEBUG );
224  return true;
225 }
226 
228 {
229  if( !m_triMesh )
230  {
231  return false;
232  }
233 
234  if( m_triMesh->vertSize() == 0 )
235  {
236  WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Write Mesh", LL_ERROR );
237  return false;
238  }
239 
240  if( m_triMesh->triangleSize() == 0 )
241  {
242  WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 triangles.", "Write Mesh", LL_ERROR );
243  return false;
244  }
245 
246  const char* c_file = m_meshFile->get().string().c_str();
247  std::ofstream dataFile( c_file, std::ios_base::binary );
248 
249  if( dataFile )
250  {
251  WLogger::getLogger()->addLogMessage( "Opening file", "Write Mesh", LL_DEBUG );
252  }
253  else
254  {
255  WLogger::getLogger()->addLogMessage( "Open file failed" + m_meshFile->get().string() , "Write Mesh", LL_ERROR );
256  return false;
257  }
258 
259  WLogger::getLogger()->addLogMessage( "Start writing file", "Write Mesh", LL_DEBUG );
260  dataFile << ( "solid OpenWalnut_TriangleMesh\n" );
261 
262  // STL requires special number format. Look at Wikipedia: "in sign-mantissa 'e'-sign-exponent format, e.g., "-2.648000e-002"
263  dataFile.precision( 16 );
264  dataFile << std::scientific;
265 
266  // write each triangle. Remember: only one normal per facet. If 0,0,0, viewer program calculates them.
267  for( size_t i = 0; i < m_triMesh->triangleSize(); ++i )
268  {
269  osg::Vec3f v1 = m_triMesh->getTriVert( i, 0 );
270  osg::Vec3f v2 = m_triMesh->getTriVert( i, 1 );
271  osg::Vec3f v3 = m_triMesh->getTriVert( i, 2 );
272 
273  // write face, use right hand rule
274  dataFile << "facet normal 0 0 0 " << std::endl;
275  dataFile << " outer loop" << std::endl;
276  dataFile << " vertex " << v1.x() << " " << v1.y() << " " << v1.z() << std::endl;
277  dataFile << " vertex " << v3.x() << " " << v3.y() << " " << v3.z() << std::endl;
278  dataFile << " vertex " << v2.x() << " " << v2.y() << " " << v2.z() << std::endl;
279  dataFile << " endloop" << std::endl;
280  dataFile << "endfacet" << std::endl;
281  }
282 
283  dataFile << ( "endsolid OpenWalnut_TriangleMesh\n" );
284  dataFile.close();
285  WLogger::getLogger()->addLogMessage( "Saving done", "Write Mesh", LL_DEBUG );
286  return true;
287 }
288 
290 {
291  if( !m_triMesh )
292  {
293  return false;
294  }
295 
296  if( m_triMesh->vertSize() == 0 )
297  {
298  WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Write Mesh", LL_ERROR );
299  return false;
300  }
301 
302  if( m_triMesh->triangleSize() == 0 )
303  {
304  WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 triangles.", "Write Mesh", LL_ERROR );
305  return false;
306  }
307 
308  std::vector< std::shared_ptr< WTriangleMesh > >meshes;
309  meshes = splitMesh( m_triMesh, 65000 );
310 
311  for( size_t k = 0; k < meshes.size(); ++k )
312  {
313  std::string fn = m_meshFile->get().string() + "_" + string_utils::toString( k );
314  const char* c_file = fn.c_str();
315  std::ofstream dataFile( c_file, std::ios_base::binary );
316 
317  if( dataFile )
318  {
319  WLogger::getLogger()->addLogMessage( "opening file", "Write Mesh", LL_DEBUG );
320  }
321  else
322  {
323  WLogger::getLogger()->addLogMessage( "open file failed" + m_meshFile->get().string() , "Write Mesh", LL_ERROR );
324  return false;
325  }
326 
327  dataFile.precision( 5 );
328 
329  WLogger::getLogger()->addLogMessage( "start writing file", "Write Mesh", LL_DEBUG );
330 
331  std::cout << meshes[k]->vertSize() << " vertices and " << meshes[k]->triangleSize() << " triangles" << std::endl;
332  dataFile << ( "{\n" );
333  dataFile << ( " \"vertices\" : [" );
334  WPosition point;
335  for( size_t i = 0; i < meshes[k]->vertSize() - 1; ++i )
336  {
337  point = meshes[k]->getVertex( i );
338  if( !( !wlimits::isInf( point[0] ) && !wlimits::isInf( point[1] ) && !wlimits::isInf( point[2] ) ) )
339  {
340  WLogger::getLogger()->addLogMessage( "Will not write file from data that contains NAN or INF.", "Write Mesh", LL_ERROR );
341  return false;
342  }
343  dataFile << point[0] << "," << point[1] << "," << point[2] << ",";
344  }
345  point = meshes[k]->getVertex( meshes[k]->vertSize() - 1 );
346  dataFile << point[0] << "," << point[1] << "," << point[2] << "],\n";
347 
348  dataFile << ( " \"normals\" : [" );
349  WPosition normal;
350  for( size_t i = 0; i < meshes[k]->vertSize() - 1; ++i )
351  {
352  normal = meshes[k]->getNormal( i );
353  if( !( !wlimits::isInf( normal[0] ) && !wlimits::isInf( normal[1] ) && !wlimits::isInf( normal[2] ) ) )
354  {
355  WLogger::getLogger()->addLogMessage( "Will not write file from data that contains NAN or INF.", "Write Mesh", LL_ERROR );
356  return false;
357  }
358  dataFile << normal[0] << "," << normal[1] << "," << normal[2] << ",";
359  }
360  normal = meshes[k]->getNormal( meshes[k]->vertSize() - 1 );
361  dataFile << normal[0] << "," << normal[1] << "," << normal[2] << "],\n";
362 
363  dataFile << ( " \"indices\" : [" );
364  for( size_t i = 0; i < meshes[k]->triangleSize() - 1; ++i )
365  {
366  dataFile << meshes[k]->getTriVertId0( i ) << "," << meshes[k]->getTriVertId1( i ) << "," << meshes[k]->getTriVertId2( i ) << ",";
367  }
368  size_t i = meshes[k]->triangleSize() - 1;
369  dataFile << meshes[k]->getTriVertId0( i ) << "," << meshes[k]->getTriVertId1( i ) << "," << meshes[k]->getTriVertId2( i ) << "]";
370 
371 
372  if( m_writeColors->get() )
373  {
374  dataFile << ",\n";
375  dataFile << ( " \"colors\" : [" );
376  for( size_t j = 0; j < meshes[k]->vertSize() - 1; ++j )
377  {
378  osg::Vec4 color = meshes[k]->getVertColor( j );
379 
380  dataFile << color[0] << "," << color[1] << "," << color[2] << "," << color[3] << ",";
381  }
382  osg::Vec4 color = meshes[k]->getVertColor( meshes[k]->vertSize() - 1 );
383  dataFile << color[0] << "," << color[1] << "," << color[2] << "," << color[3] << "]";
384  }
385  dataFile << "\n}";
386 
387  dataFile.close();
388  }
389  WLogger::getLogger()->addLogMessage( "saving done", "Write Mesh", LL_DEBUG );
390  return true;
391 }
392 
393 std::vector< std::shared_ptr< WTriangleMesh > >WMWriteMesh::splitMesh( std::shared_ptr< WTriangleMesh > triMesh, size_t targetSize )
394 {
395  std::vector< std::shared_ptr< WTriangleMesh > >meshes;
396  if( triMesh->vertSize() <= targetSize )
397  {
398  meshes.push_back( triMesh );
399  return meshes;
400  }
401  size_t currentTri( 0 );
402  size_t id0, id1, id2;
403 
404  while( currentTri < triMesh->triangleSize() )
405  {
406  std::shared_ptr< WTriangleMesh > newMesh( new WTriangleMesh( 0, 0 ) );
407  std::vector<int>newIds( triMesh->vertSize(), -1 );
408  while( newMesh->vertSize() < targetSize )
409  {
410  id0 = triMesh->getTriVertId0( currentTri );
411  id1 = triMesh->getTriVertId1( currentTri );
412  id2 = triMesh->getTriVertId2( currentTri );
413  if( newIds[id0] == -1 )
414  {
415  newIds[id0] = newMesh->vertSize();
416  osg::Vec3 vert = triMesh->getTriVert( currentTri, 0 );
417  newMesh->addVertex( vert.x(), vert.y(), vert.z() );
418  newMesh->setVertexColor( newMesh->vertSize() - 1, triMesh->getVertColor( id0 ) );
419  }
420  if( newIds[id1] == -1 )
421  {
422  newIds[id1] = newMesh->vertSize();
423  osg::Vec3 vert = triMesh->getTriVert( currentTri, 1 );
424  newMesh->addVertex( vert.x(), vert.y(), vert.z() );
425  newMesh->setVertexColor( newMesh->vertSize() - 1, triMesh->getVertColor( id1 ) );
426  }
427  if( newIds[id2] == -1 )
428  {
429  newIds[id2] = newMesh->vertSize();
430  osg::Vec3 vert = triMesh->getTriVert( currentTri, 2 );
431  newMesh->addVertex( vert.x(), vert.y(), vert.z() );
432  newMesh->setVertexColor( newMesh->vertSize() - 1, triMesh->getVertColor( id2 ) );
433  }
434  newMesh->addTriangle( newIds[id0], newIds[id1], newIds[id2] );
435  ++currentTri;
436  if( currentTri == triMesh->triangleSize() )
437  {
438  break;
439  }
440  }
441 
442  meshes.push_back( newMesh );
443  }
444  return meshes;
445 }
virtual void wait() const
Wait for the condition.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
A class containing a list of named items.
void addLogMessage(std::string message, std::string source="", LogLevel level=LL_DEBUG)
Appends a log message to the logging queue.
Definition: WLogger.cpp:84
static WLogger * getLogger()
Returns pointer to the currently running logger instance.
Definition: WLogger.cpp:64
This module writes the triangle mesh given at its input connector as a VTK ASCII file or ....
Definition: WMWriteMesh.h:46
virtual void moduleMain()
Entry point after loading the module.
bool saveSTL()
Write STL format.
WPropSelection m_fileTypeSelection
Selection property for file types.
Definition: WMWriteMesh.h:152
bool saveVTKASCII() const
Store the mesh in legacy vtk file format.
virtual const std::string getName() const
Gives back the name of this module.
Definition: WMWriteMesh.cpp:61
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
Definition: WMWriteMesh.h:139
bool saveJson()
Store the mesh in a json (javascript object notation) file.
virtual const std::string getDescription() const
Gives back a description of this module.
Definition: WMWriteMesh.cpp:66
std::shared_ptr< WItemSelection > m_fileTypeSelectionsList
A list of file type selection types.
Definition: WMWriteMesh.h:147
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
Definition: WMWriteMesh.cpp:57
WPropBool m_writeColors
If true, colors get exported too.
Definition: WMWriteMesh.h:157
WPropFilename m_meshFile
The mesh will be written to this file.
Definition: WMWriteMesh.h:142
std::shared_ptr< WModuleInputData< WTriangleMesh > > m_meshInput
Input connector for a mesh.
Definition: WMWriteMesh.h:137
virtual void properties()
Initialize the properties for this module.
Definition: WMWriteMesh.cpp:84
std::shared_ptr< WTriangleMesh > m_triMesh
A pointer to the currently processed tri mesh.
Definition: WMWriteMesh.h:138
WPropTrigger m_saveTriggerProp
This property triggers the actual writing,.
Definition: WMWriteMesh.h:141
virtual void connectors()
Initialize the connectors this module is using.
Definition: WMWriteMesh.cpp:73
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
Definition: WMWriteMesh.cpp:51
std::vector< std::shared_ptr< WTriangleMesh > > splitMesh(std::shared_ptr< WTriangleMesh > triMesh, size_t targetSize)
Splits the mesh in several mesh files (to solve maximum mesh limits in BrainGlL).
WPropGroup m_savePropGroup
Property group containing properties needed for saving the mesh.
Definition: WMWriteMesh.h:140
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
static boost::filesystem::path getAppPath()
The path where the binary file resides in.
Definition: WPathHelper.cpp:93
This only is a 3d double vector.
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
Triangle mesh data structure allowing for convenient access of the elements.
Definition: WTriangleMesh.h:46
@ PV_TRIGGER_READY
Trigger property: is ready to be triggered (again)
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.
std::string toString(const T &value)
Convert a given value to a string.
Definition: WStringUtils.h:120
bool isInf(T value)
Determines if a number is considered as infinity or not.
Definition: WLimits.h:101