1 /*
2  MDAL - Mesh Data Abstraction Library (MIT License)
3  Copyright (C) 2020 Runette Software Ltd.
4 */
5 
6 #include <stddef.h>
7 #include <iosfwd>
8 #include <iostream>
9 #include <fstream>
10 #include <sstream>
11 #include <string>
12 #include <memory>
13 #include <vector>
14 #include <map>
15 #include <cassert>
16 #include <limits>
17 #include <algorithm>
18 #include <string.h>
19 
20 #include "mdal_ply.hpp"
21 #include "mdal.h"
22 #include "mdal_utils.hpp"
23 #include "mdal_logger.hpp"
24 #include "mdal_data_model.hpp"
25 #include "mdal_memory_data_model.hpp"
26 #include "libplyxx.h"
27 #include "mdal_driver_manager.hpp"
28 
29 #define DRIVER_NAME "PLY"
30 
DriverPly()31 MDAL::DriverPly::DriverPly() :
32   Driver( DRIVER_NAME,
33           "Stanford PLY Ascii Mesh File",
34           "*.ply",
35           Capability::ReadMesh |
36           Capability::SaveMesh |
37           Capability::WriteDatasetsOnVertices |
38           Capability::WriteDatasetsOnFaces |
39           Capability::WriteDatasetsOnEdges |
40           Capability::WriteDatasetsOnVolumes
41         )
42 {
43 }
44 
create()45 MDAL::DriverPly *MDAL::DriverPly::create()
46 {
47   return new DriverPly();
48 }
49 
50 MDAL::DriverPly::~DriverPly() = default;
51 
getIndex(std::vector<std::pair<std::string,bool>> v,std::string in)52 size_t getIndex( std::vector<std::pair<std::string, bool>> v, std::string in )
53 {
54   auto is_equal = [ in ]( std::pair<std::string, bool> s ) { return  s.first == in; };
55 
56   auto it = std::find_if( v.begin(), v.end(), is_equal );
57   return ( size_t )std::distance( v.begin(), it );
58 }
59 
60 // check for the magic number which in  a PLY file is "ply"
canReadMesh(const std::string & uri)61 bool MDAL::DriverPly::canReadMesh( const std::string &uri )
62 {
63   std::ifstream in( uri, std::ifstream::in );
64   std::string line;
65   if ( !MDAL::getHeaderLine( in, line ) || !startsWith( line, "ply" ) )
66   {
67     return false;
68   }
69   return true;
70 }
71 
saveMeshOnFileSuffix() const72 std::string MDAL::DriverPly::saveMeshOnFileSuffix() const
73 {
74   return "ply";
75 }
76 
load(const std::string & meshFile,const std::string &)77 std::unique_ptr<MDAL::Mesh> MDAL::DriverPly::load( const std::string &meshFile, const std::string & )
78 {
79   MDAL::Log::resetLastStatus();
80   Vertices vertices( 0 );
81   Faces faces( 0 );
82   Edges edges( 0 );
83   size_t maxSizeFace = 0;
84 
85   size_t vertexCount = 0;
86   size_t faceCount = 0;
87   size_t edgeCount = 0;
88 
89   //data structures that will contain all of the datasets, categorised by vertex, face and edge datasets
90   std::vector<std::vector<double>> vertexDatasets; // contains the data
91   std::vector<std::pair<std::string, bool>> vProp2Ds; // contains the dataset name and a flag for scalar / vector
92   std::vector<std::vector<double>> faceDatasets;
93   std::vector<std::pair<std::string, bool>> fProp2Ds;
94   std::vector<std::vector<double>> edgeDatasets;
95   std::vector<std::pair<std::string, bool>> eProp2Ds;
96   std::unordered_map<std::string, std::pair<std::vector<double>, std::vector<int>>> listProps; // contains the list datasets as vector of values and vector indices for each face into the vector of values
97 
98   libply::File file( meshFile );
99   if ( MDAL::Log::getLastStatus() != MDAL_Status::None ) { return nullptr; }
100   const libply::ElementsDefinition &definitions = file.definitions();
101   const libply::Metadata &metadata = file.metadata();
102   for ( const libply::Element &element : definitions )
103   {
104     if ( element.name == "vertex" )
105     {
106       vertexCount = element.size;
107     }
108     else if ( element.name == "face" )
109     {
110       faceCount = element.size;
111     }
112     else if ( element.name == "edge" )
113     {
114       edgeCount = element.size;
115     }
116     for ( const libply::Property &property : element.properties )
117     {
118       if ( element.name == "vertex" &&
119            property.name != "X" &&
120            property.name != "x" &&
121            property.name != "Y" &&
122            property.name != "y" &&
123            property.name != "Z" &&
124            property.name != "z"
125          )
126       {
127         vProp2Ds.emplace_back( property.name, property.isList );
128         vertexDatasets.push_back( std::vector<double>() );
129         if ( property.isList ) listProps.emplace( property.name, std::make_pair( std::vector<double>(), std::vector<int>() ) );
130       }
131       else if ( element.name == "face" &&
132                 property.name != "vertex_indices"
133               )
134       {
135         fProp2Ds.emplace_back( property.name, property.isList );
136         faceDatasets.push_back( std::vector<double>() );
137         if ( property.isList ) listProps.emplace( property.name, std::make_pair( std::vector<double>(), std::vector<int>() ) );
138       }
139       else if ( element.name == "edge" &&
140                 property.name != "vertex1" &&
141                 property.name != "vertex2"
142               )
143       {
144         eProp2Ds.emplace_back( property.name, property.isList );
145         edgeDatasets.push_back( std::vector<double>() );
146         if ( property.isList ) listProps.emplace( property.name, std::make_pair( std::vector<double>(), std::vector<int>() ) );
147       }
148     }
149   }
150 
151 
152 
153   for ( const libply::Element &el : definitions )
154   {
155     if ( el.name == "vertex" )
156     {
157       libply::ElementReadCallback vertexCallback = [&vertices, &el, &vProp2Ds, &vertexDatasets, &listProps]( libply::ElementBuffer & e )
158       {
159         Vertex vertex;
160         for ( size_t i = 0; i < el.properties.size(); i++ )
161         {
162           libply::Property p = el.properties[i];
163           if ( p.name == "X" || p.name == "x" )
164           {
165             vertex.x = e[i];
166           }
167           else if ( p.name == "Y" || p.name == "y" )
168           {
169             vertex.y = e[i];
170           }
171           else if ( p.name == "Z" || p.name == "z" )
172           {
173             vertex.z = e[i];
174           }
175           else
176           {
177             int dsIdx = getIndex( vProp2Ds, p.name );
178             if ( vProp2Ds[ dsIdx ].second )
179             {
180               const std::string name = vProp2Ds[ dsIdx ].first;
181               auto &vals = listProps.at( name );
182               libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[i] );
183               vals.second.push_back( lp->size() );
184               for ( size_t j = 0; j < lp->size(); j++ )
185               {
186                 vals.first.push_back( lp->value( j ) );
187               }
188             }
189             else
190             {
191               std::vector<double> &ds = vertexDatasets[dsIdx];
192               ds.push_back( e[i] );
193             }
194           }
195         }
196         vertices.push_back( vertex );
197       };
198       file.setElementReadCallback( "vertex", vertexCallback );
199     }
200     else if ( el.name == "face" )
201     {
202       libply::ElementReadCallback faceCallback = [&faces, &el, &maxSizeFace, &fProp2Ds, &faceDatasets, &listProps]( libply::ElementBuffer & e )
203       {
204         Face face;
205         for ( size_t i = 0; i < el.properties.size(); i++ )
206         {
207           libply::Property p = el.properties[i];
208           if ( p.name == "vertex_indices" )
209           {
210             if ( !p.isList )
211             {
212               MDAL::Log::error( MDAL_Status::Err_InvalidData, "PLY: the triangles are not a list" );
213             }
214             else
215             {
216               libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[i] );
217               if ( maxSizeFace < lp->size() ) maxSizeFace = lp->size();
218               face.resize( lp->size() );
219               for ( size_t j = 0; j < lp->size(); j++ )
220               {
221                 face[j] = int( lp->value( j ) );
222               }
223             }
224           }
225           else
226           {
227             int dsIdx = getIndex( fProp2Ds, p.name );
228             if ( fProp2Ds[ dsIdx ].second )
229             {
230               const std::string name = fProp2Ds[ dsIdx ].first;
231               auto &vals = listProps.at( name );
232               libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[i] );
233               vals.second.push_back( lp->size() );
234               for ( size_t j = 0; j < lp->size(); j++ )
235               {
236                 vals.first.push_back( lp->value( j ) );
237               }
238             }
239             else
240             {
241               std::vector<double> &ds = faceDatasets[dsIdx];
242               ds.push_back( e[i] );
243             }
244           }
245         }
246         faces.push_back( face );
247       };
248       file.setElementReadCallback( "face", faceCallback );
249     }
250     else if ( el.name == "edge" )
251     {
252       libply::ElementReadCallback edgeCallback = [&edges, &el, &eProp2Ds, &edgeDatasets, &listProps]( libply::ElementBuffer & e )
253       {
254         Edge edge;
255         for ( size_t i = 0; i < el.properties.size(); i++ )
256         {
257           libply::Property p = el.properties[i];
258           if ( p.name == "vertex1" )
259           {
260             edge.startVertex = int( e[i] );
261           }
262           else if ( p.name == "vertex2" )
263           {
264             edge.endVertex = int( e[i] );
265           }
266           else
267           {
268             int dsIdx = getIndex( eProp2Ds, p.name );
269             if ( eProp2Ds[ dsIdx ].second )
270             {
271               const std::string name = eProp2Ds[ dsIdx ].first;
272               auto &vals = listProps.at( name );
273               libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[i] );
274               vals.second.push_back( lp->size() );
275               for ( size_t j = 0; j < lp->size(); j++ )
276               {
277                 vals.first.push_back( lp->value( j ) );
278               }
279             }
280             else
281             {
282               std::vector<double> &ds = edgeDatasets[dsIdx];
283               ds.push_back( e[i] );
284             }
285           }
286         }
287         edges.push_back( edge );
288       };
289       file.setElementReadCallback( "edge", edgeCallback );
290     }
291   }
292 
293   file.read();
294   if ( MDAL::Log::getLastStatus() != MDAL_Status::None ) { return nullptr; }
295   if ( vertices.size() != vertexCount ||
296        faces.size() != faceCount ||
297        edges.size() != edgeCount
298      )
299   {
300     MDAL_SetStatus( MDAL_LogLevel::Error, MDAL_Status::Err_InvalidData, "Incomplete Mesh" );
301     return nullptr;
302   }
303 
304   std::unique_ptr< MemoryMesh > mesh(
305     new MemoryMesh(
306       DRIVER_NAME,
307       maxSizeFace,
308       meshFile
309     )
310   );
311   mesh->setFaces( std::move( faces ) );
312   mesh->setVertices( std::move( vertices ) );
313   mesh->setEdges( std::move( edges ) );
314   if ( metadata.find( "crs" ) != metadata.end() )
315   {
316     mesh->setSourceCrs( metadata.at( "crs" ) );
317   }
318 
319 
320   // Add Bed Elevation
321   MDAL::addBedElevationDatasetGroup( mesh.get(), mesh->vertices() );
322 
323   // Add Vertex Datasets
324   for ( size_t i = 0; i < vertexDatasets.size(); ++i )
325   {
326     if ( vProp2Ds[i].second )
327     {
328       auto &vals = listProps.at( vProp2Ds[i].first );
329       vertexDatasets[i].clear();
330       auto it = vals.second.begin();
331       size_t idx = 0;
332       while ( idx < vals.first.size() )
333       {
334         vertexDatasets[i].push_back( vals.first[idx] );
335         vertexDatasets[i].push_back( vals.first[idx + 1] );
336         idx += *it;
337         it = std::next( it, 1 );
338       }
339       listProps.erase( vProp2Ds[i].first );
340     }
341     std::shared_ptr<DatasetGroup> group = addDatasetGroup( mesh.get(), vProp2Ds[i].first, DataOnVertices, !vProp2Ds[i].second );
342     addDataset2D( group.get(), vertexDatasets[i] );
343   }
344 
345   //Add face datasets and volume datasets
346   for ( size_t i = 0; i < faceDatasets.size(); ++i )
347   {
348     if ( fProp2Ds[i].second )
349     {
350       std::string name = fProp2Ds[i].first;
351       if ( name.find( "__vols" ) != std::string::npos ) break;
352       auto &vals = listProps.at( name );
353       faceDatasets[i].clear();
354       auto it = vals.second.begin();
355       size_t idx = 0;
356       if ( listProps.find( name + "__vols" ) == listProps.end() )
357       {
358         while ( idx < vals.first.size() )
359         {
360           faceDatasets[i].push_back( vals.first[idx] );
361           faceDatasets[i].push_back( vals.first[idx + 1] );
362           idx += *it;
363           it = std::next( it, 1 );
364         }
365         std::shared_ptr<DatasetGroup> group = addDatasetGroup( mesh.get(), name, DataOnFaces, false );
366         addDataset2D( group.get(), faceDatasets[i] );
367       }
368       else
369       {
370         auto levels = listProps.at( name + "__vols" );
371         std::shared_ptr<DatasetGroup> group = addDatasetGroup( mesh.get(), name, DataOnVolumes, true );
372         addDataset3D( group.get(), vals.first, vals.second, levels.first, levels.second );
373         listProps.erase( name + "__vols" );
374       }
375       listProps.erase( name );
376     }
377     else
378     {
379       std::shared_ptr<DatasetGroup> group = addDatasetGroup( mesh.get(), fProp2Ds[i].first, DataOnFaces, true );
380       addDataset2D( group.get(), faceDatasets[i] );
381     }
382   }
383 
384   // Add Edge Datasets
385   for ( size_t i = 0; i < edgeDatasets.size(); ++i )
386   {
387     if ( eProp2Ds[i].second )
388     {
389       auto vals = listProps.at( eProp2Ds[i].first );
390       edgeDatasets[i].clear();
391       auto it = vals.second.begin();
392       size_t idx = 0;
393       while ( idx < vals.first.size() )
394       {
395         edgeDatasets[i].push_back( vals.first[idx] );
396         edgeDatasets[i].push_back( vals.first[idx + 1] );
397         idx += *it;
398         it = std::next( it, 1 );
399       }
400       listProps.erase( eProp2Ds[i].first );
401     }
402     std::shared_ptr<DatasetGroup> group = addDatasetGroup( mesh.get(), eProp2Ds[i].first, DataOnEdges, !eProp2Ds[i].second );
403     addDataset2D( group.get(), edgeDatasets[i] );
404   }
405 
406   /*
407   * Clean up
408   */
409   for ( size_t i = 0; i < vertexDatasets.size(); ++i )
410   {
411     vertexDatasets.pop_back();
412   };
413 
414   for ( size_t i = 0; i < faceDatasets.size(); ++i )
415   {
416     faceDatasets.pop_back();
417   };
418 
419   for ( size_t i = 0; i < edgeDatasets.size(); ++i )
420   {
421     edgeDatasets.pop_back();
422   };
423 
424   return std::unique_ptr<Mesh>( mesh.release() );
425 
426 }
427 
addDatasetGroup(MDAL::Mesh * mesh,const std::string & name,const MDAL_DataLocation location,bool isScalar)428 std::shared_ptr< MDAL::DatasetGroup> MDAL::DriverPly::addDatasetGroup( MDAL::Mesh *mesh, const std::string &name, const MDAL_DataLocation location, bool isScalar )
429 {
430   if ( !mesh )
431     return NULL;
432 
433   if ( location == DataOnFaces && mesh->facesCount() == 0 )
434     return NULL;
435 
436   if ( location == DataOnEdges && mesh->edgesCount() == 0 )
437     return NULL;
438 
439   std::shared_ptr< DatasetGroup > group = std::make_shared< DatasetGroup >( mesh->driverName(), mesh, name, name );
440   group->setDataLocation( location );
441   group->setIsScalar( isScalar );
442   group->setStatistics( MDAL::calculateStatistics( group ) );
443   mesh->datasetGroups.push_back( group );
444   return group;
445 }
446 
addDataset2D(MDAL::DatasetGroup * group,const std::vector<double> & values)447 void MDAL::DriverPly::addDataset2D( MDAL::DatasetGroup *group, const std::vector<double> &values )
448 {
449   if ( !group )
450     return;
451 
452   size_t mult = 1;
453   if ( !group->isScalar() ) mult = 2;
454 
455   MDAL::Mesh *mesh = group->mesh();
456 
457   if ( values.empty() )
458     return;
459 
460   if ( 0 == mesh->verticesCount() )
461     return;
462 
463   if ( group->dataLocation() == DataOnVertices )
464   {
465     if ( values.size() != mesh->verticesCount()*mult )
466     {
467       MDAL_SetStatus( MDAL_LogLevel::Error, MDAL_Status::Err_InvalidData, "PLY: Invalid Number of Data Values" );
468       return;
469     }
470   }
471 
472   if ( group->dataLocation() == DataOnFaces )
473   {
474     if ( values.size() != mesh->facesCount()*mult )
475     {
476       MDAL_SetStatus( MDAL_LogLevel::Error, MDAL_Status::Err_InvalidData, "PLY: Invalid Number of Data Values" );
477       return;
478     }
479     if ( mesh->facesCount() == 0 )
480       return;
481   }
482 
483   if ( group->dataLocation() == DataOnEdges )
484   {
485     if ( values.size() != mesh->edgesCount()*mult )
486     {
487       MDAL_SetStatus( MDAL_LogLevel::Error, MDAL_Status::Err_InvalidData, "PLY: Invalid Number of Data Values" );
488       return;
489     }
490     if ( mesh->edgesCount() == 0 )
491       return;
492   }
493 
494   std::shared_ptr< MDAL::MemoryDataset2D > dataset = std::make_shared< MemoryDataset2D >( group );
495   dataset->setTime( 0.0 );
496   memcpy( dataset->values(), values.data(), sizeof( double ) * values.size() );
497   dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
498   group->datasets.push_back( dataset );
499   group->setStatistics( MDAL::calculateStatistics( group ) );
500 }
501 
addDataset3D(MDAL::DatasetGroup * group,const std::vector<double> & values,const std::vector<int> & valueIndexes,const std::vector<double> & levels,const std::vector<int> & levelIndexes)502 void MDAL::DriverPly::addDataset3D( MDAL::DatasetGroup *group,
503                                     const std::vector<double> &values,
504                                     const std::vector<int> &valueIndexes,
505                                     const std::vector<double> &levels,
506                                     const std::vector<int> &levelIndexes )
507 {
508   if ( !group )
509     return;
510 
511 
512   MDAL::Mesh *mesh = group->mesh();
513 
514   if ( values.empty() )
515     return;
516 
517   if ( 0 == mesh->facesCount() )
518     return;
519 
520   if ( valueIndexes.size() != mesh->facesCount() ||
521        levelIndexes.size() != mesh->facesCount() ||
522        levels.size() != values.size() + mesh->facesCount()
523      )
524   {
525     MDAL_SetStatus( MDAL_LogLevel::Error, MDAL_Status::Err_InvalidData, "Incomplete Volume Dataset" );
526     return;
527   }
528 
529   int maxVerticalLevelCount = * std::max_element( valueIndexes.begin(), valueIndexes.end() );
530 
531   std::shared_ptr< MDAL::MemoryDataset3D > dataset = std::make_shared< MemoryDataset3D >( group, values.size(), maxVerticalLevelCount, valueIndexes.data(), levels.data() );
532   dataset->setTime( 0.0 );
533   memcpy( dataset->values(), values.data(), sizeof( double ) * values.size() );
534   dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
535   group->datasets.push_back( dataset );
536   group->setStatistics( MDAL::calculateStatistics( group ) );
537 }
538 
save(const std::string & fileName,const std::string & meshName,Mesh * mesh)539 void MDAL::DriverPly::save( const std::string &fileName, const std::string &meshName, Mesh *mesh )
540 {
541   MDAL_UNUSED( meshName );
542 
543   MDAL::Log::resetLastStatus();
544 
545   DatasetGroups groups = mesh->datasetGroups;
546 
547   // vectors to hold the different types of group
548   DatasetGroups vgroups;
549   DatasetGroups fgroups;
550   DatasetGroups egroups;
551   DatasetGroups volGroups;
552 
553   for ( std::shared_ptr<DatasetGroup> group : groups )
554   {
555     if ( group->dataLocation() == MDAL_DataLocation::DataOnVertices )
556     {
557       if ( group->name() != "Bed Elevation" ) vgroups.push_back( group );
558     }
559     else if ( group->dataLocation() == MDAL_DataLocation::DataOnFaces )
560     {
561       fgroups.push_back( group );
562     }
563     else if ( group->dataLocation() == MDAL_DataLocation::DataOnEdges )
564     {
565       egroups.push_back( group );
566     }
567     else if ( group->dataLocation() == MDAL_DataLocation::DataOnVolumes )
568     {
569       if ( group->isScalar() )
570       {
571         volGroups.push_back( group );
572       }
573       else
574       {
575         MDAL_SetStatus( MDAL_LogLevel::Warn, MDAL_Status::Err_IncompatibleDatasetGroup, "PLY: Vector Datasets on Volumes are not supported" );
576       }
577     }
578   }
579 
580 
581   libply::FileOut file( fileName, libply::File::Format::ASCII );
582   if ( MDAL::Log::getLastStatus() != MDAL_Status::None ) return;
583 
584   libply::ElementsDefinition definitions;
585   std::vector<libply::Property> vproperties;
586   vproperties.emplace_back( "X", libply::Type::COORDINATE, false );
587   vproperties.emplace_back( "Y", libply::Type::COORDINATE, false );
588   vproperties.emplace_back( "Z", libply::Type::COORDINATE, false );
589   for ( std::shared_ptr<DatasetGroup> group : vgroups )
590   {
591     vproperties.emplace_back( group->name(), libply::Type::FLOAT64, ! group->isScalar() );
592   }
593 
594   definitions.emplace_back( "vertex", mesh->verticesCount(), vproperties );
595   if ( mesh->facesCount() > 0 )
596   {
597     std::vector<libply::Property> fproperties;
598     fproperties.emplace_back( "vertex_indices", libply::Type::UINT32, true );
599     for ( std::shared_ptr<DatasetGroup> group : fgroups )
600     {
601       fproperties.emplace_back( group->name(), libply::Type::FLOAT64, ! group->isScalar() );
602     }
603     for ( std::shared_ptr<DatasetGroup> group : volGroups )
604     {
605       fproperties.emplace_back( group->name(), libply::Type::FLOAT64, true );
606       fproperties.emplace_back( group->name() + "__vols", libply::Type::FLOAT64, true );
607     }
608     definitions.emplace_back( "face", mesh->facesCount(), fproperties );
609   }
610 
611   if ( mesh->edgesCount() > 0 )
612   {
613     std::vector<libply::Property> eproperties;
614     eproperties.emplace_back( "vertex1", libply::Type::UINT32, false );
615     eproperties.emplace_back( "vertex2", libply::Type::UINT32, false );
616     for ( std::shared_ptr<DatasetGroup> group : egroups )
617     {
618       eproperties.emplace_back( group->name(), libply::Type::FLOAT64, ! group->isScalar() );
619     }
620     definitions.emplace_back( "edge", mesh->edgesCount(), eproperties );
621   }
622   file.setElementsDefinition( definitions );
623 
624   // write vertices
625   std::unique_ptr<MDAL::MeshVertexIterator> vertices = mesh->readVertices();
626 
627 
628   libply::ElementWriteCallback vertexCallback = [&vertices, &vgroups]( libply::ElementBuffer & e, size_t index )
629   {
630     double vertex[3];
631     vertices->next( 1, vertex );
632     e[0] = vertex[0];
633     e[1] = vertex[1];
634     e[2] = vertex[2];
635     for ( size_t i = 0; i < vgroups.size(); i++ )
636     {
637       if ( vgroups[i]->isScalar() )
638       {
639         double val[1];
640         vgroups[i]->datasets[0]->scalarData( index, 1, &val[0] );
641         e[i + 3] = val[0];
642       }
643       else
644       {
645         double val[2];
646         vgroups[i]->datasets[0]->vectorData( index, 1, &val[0] );
647         libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[i + 3] );
648         lp->define( libply::Type::FLOAT64, 2 );
649         lp->value( 0 ) = val[0];
650         lp->value( 1 ) = val[1];
651       };
652     }
653   };
654 
655 
656   // write faces
657   std::vector<int> vertexIndices( mesh->faceVerticesMaximumCount() );
658   std::unique_ptr<MDAL::MeshFaceIterator> faces = mesh->readFaces();
659 
660   libply::ElementWriteCallback faceCallback = [&faces, &fgroups, &vertexIndices, &volGroups]( libply::ElementBuffer & e, size_t index )
661   {
662     int idx = 0;
663     int faceOffsets[1];
664     faces->next( 1, faceOffsets, vertexIndices.size(), vertexIndices.data() );
665     libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[idx] );
666     lp->define( libply::Type::UINT32, faceOffsets[0] );
667     for ( int j = 0; j < faceOffsets[0]; ++j )
668     {
669       lp->value( j ) = vertexIndices[j];
670     };
671     idx++;
672     for ( size_t i = 0; i < fgroups.size(); i++ )
673     {
674       if ( fgroups[i]->isScalar() )
675       {
676         double val[1];
677         fgroups[i]->datasets[0]->scalarData( index, 1, &val[0] );
678         e[idx] = val[0];
679       }
680       else
681       {
682         double val[2];
683         fgroups[i]->datasets[0]->vectorData( index, 1, &val[0] );
684         libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[idx] );
685         lp->define( libply::Type::FLOAT64, 2 );
686         lp->value( 0 ) = val[0];
687         lp->value( 1 ) = val[1];
688       };
689       idx++;
690     }
691     int vCount[1];
692     int f2v[1];
693     for ( size_t i = 0; i < volGroups.size(); i++ )
694     {
695       std::shared_ptr<MDAL::MemoryDataset3D> ds = std::dynamic_pointer_cast<MDAL::MemoryDataset3D>( volGroups[i]->datasets[0] );
696       ds->verticalLevelCountData( index, 1, &vCount[0] );
697       const int count = vCount[0];
698       ds->faceToVolumeData( index, 1, &f2v[0] );
699       const int vindex = f2v[0];
700       std::vector<double> val( count, 0 );
701       ds->scalarVolumesData( vindex, count, val.data() );
702       libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[idx] );
703       lp->define( libply::Type::FLOAT64, count );
704 
705       for ( int j = 0; j < count; ++j )
706       {
707         lp->value( j ) = val[j];
708       };
709       idx++;
710       std::vector<double> ex( count + 1, 0 );
711       ds->verticalLevelData( vindex + index, count + 1, ex.data() );
712       libply::ListProperty *lp1 = dynamic_cast<libply::ListProperty *>( &e[idx] );
713       lp1->define( libply::Type::FLOAT64, count + 1 );
714       for ( int j = 0; j < count + 1; ++j )
715       {
716         lp1->value( j ) = ex[j];
717       };
718       idx++;
719     }
720   };
721 
722   // write edges
723 
724   std::unique_ptr<MDAL::MeshEdgeIterator> edges = mesh->readEdges();
725 
726   libply::ElementWriteCallback edgeCallback = [&edges, &egroups]( libply::ElementBuffer & e, size_t index )
727   {
728     int startIndex;
729     int endIndex;
730     edges->next( 1, &startIndex, &endIndex );
731     e[0] = startIndex;
732     e[1] = endIndex;
733     for ( size_t i = 0; i < egroups.size(); i++ )
734     {
735       if ( egroups[i]->isScalar() )
736       {
737         double val[1];
738         egroups[i]->datasets[0]->scalarData( index, 1, &val[0] );
739         e[i + 2] = val[0];
740       }
741       else
742       {
743         double val[2];
744         egroups[i]->datasets[0]->vectorData( index, 1, &val[0] );
745         libply::ListProperty *lp = dynamic_cast<libply::ListProperty *>( &e[i + 2] );
746         lp->define( libply::Type::FLOAT64, 2 );
747         lp->value( 0 ) = val[0];
748         lp->value( 1 ) = val[1];
749       };
750     }
751   };
752 
753   file.setElementWriteCallback( "vertex", vertexCallback );
754   if ( mesh->facesCount() > 0 ) file.setElementWriteCallback( "face", faceCallback );
755   if ( mesh->edgesCount() > 0 ) file.setElementWriteCallback( "edge", edgeCallback );
756 
757   file.write();
758 
759 
760   /*
761   * Clean up
762   */
763   for ( size_t i = 0; i < vgroups.size(); ++i )
764   {
765     vgroups.pop_back();
766   };
767 
768   for ( size_t i = 0; i < fgroups.size(); ++i )
769   {
770     fgroups.pop_back();
771   };
772 
773   for ( size_t i = 0; i < egroups.size(); ++i )
774   {
775     egroups.pop_back();
776   };
777 }
778 
persist(DatasetGroup * group)779 bool MDAL::DriverPly::persist( DatasetGroup *group )
780 {
781   save( group->uri(), "", group->mesh() );
782   return false;
783 }
784