diff --git a/src/Mod/Mesh/App/AmfExport.cpp b/src/Mod/Mesh/App/AmfExport.cpp index 32401aca9..3ce019226 100644 --- a/src/Mod/Mesh/App/AmfExport.cpp +++ b/src/Mod/Mesh/App/AmfExport.cpp @@ -21,8 +21,8 @@ ***************************************************************************/ #include "PreCompiled.h" - #ifndef _PreComp_ + #include #include #include #endif // #ifndef _PreComp_ @@ -35,14 +35,119 @@ #include "Base/FileInfo.h" #include "Base/Sequencer.h" #include "Base/Stream.h" +#include "Base/Tools.h" using namespace Mesh; +using namespace MeshCore; -AmfExporter::AmfExporter(const char *fileName) : + +MergeExporter::MergeExporter(std::string fileName, MeshIO::Format fmt) + :fName(fileName) +{ +} + +MergeExporter::~MergeExporter() +{ + // if we have more than one segment set the 'save' flag + if (mergingMesh.countSegments() > 1) { + for (unsigned long i = 0; i < mergingMesh.countSegments(); ++i) { + mergingMesh.getSegment(i).save(true); + } + } + + mergingMesh.save(fName.c_str()); +} + + +bool MergeExporter::addMesh(Mesh::Feature *meshFeat) +{ + const MeshObject &mesh( meshFeat->Mesh.getValue() ); + + MeshCore::MeshKernel kernel( mesh.getKernel() ); + kernel.Transform(mesh.getTransform()); + + auto countFacets( mergingMesh.countFacets() ); + if (countFacets == 0) { + mergingMesh.setKernel(kernel); + } else { + mergingMesh.addMesh(kernel); + } + + // if the mesh already has persistent segments then use them instead + unsigned long numSegm = mesh.countSegments(); + unsigned long canSave = 0; + for (unsigned long i=0; i 0) { + for (unsigned long i=0; i indices = segm.getIndices(); + std::for_each( indices.begin(), indices.end(), + [countFacets] (unsigned long &v) { + v += countFacets; + } ); + Segment new_segm(&mergingMesh, indices, true); + new_segm.setName(segm.getName()); + mergingMesh.addSegment(new_segm); + } + } + } else { + // now create a segment for the added mesh + std::vector indices; + indices.resize(mergingMesh.countFacets() - countFacets); + std::generate(indices.begin(), indices.end(), Base::iotaGen(countFacets)); + Segment segm(&mergingMesh, indices, true); + // TODO: pass in the object? segm.setName(obj->Label.getValue()); + mergingMesh.addSegment(segm); + } + + return true; +} + +bool MergeExporter::addShape(App::Property *shape, float tol) +{ + if (shape && shape->getTypeId().isDerivedFrom(App::PropertyComplexGeoData::getClassTypeId())) { + Base::Reference mesh(new MeshObject()); + + auto countFacets( mergingMesh.countFacets() ); + + auto geoData( static_cast(shape)->getComplexData() ); + if (geoData) { + std::vector aPoints; + std::vector aTopo; + geoData->getFaces(aPoints, aTopo, tol); + + mesh->addFacets(aTopo, aPoints); + if (countFacets == 0) + mergingMesh = *mesh; + else + mergingMesh.addMesh(*mesh); + } else { + return false; + } + + // now create a segment for the added mesh + std::vector indices; + indices.resize(mergingMesh.countFacets() - countFacets); + std::generate(indices.begin(), indices.end(), Base::iotaGen(countFacets)); + Segment segm(&mergingMesh, indices, true); + // TODO: pass in the object? segm.setName(obj->Label.getValue()); + mergingMesh.addSegment(segm); + + return true; + } + return false; +} + +AmfExporter::AmfExporter(std::string fileName) : outputStreamPtr(nullptr), nextObjectIndex(0) { // ask for write permission - Base::FileInfo fi(fileName); + Base::FileInfo fi(fileName.c_str()); Base::FileInfo di(fi.dirPath().c_str()); if ((fi.exists() && !fi.isWritable()) || !di.exists() || !di.isWritable()) throw Base::FileException("No write permission for file", fileName); @@ -71,20 +176,54 @@ AmfExporter::~AmfExporter() } } +bool AmfExporter::addShape(App::Property *shape, float tol) +{ + // TODO: Add meta info, look into a different way to extract mesh with vertex normals + if (shape && shape->getTypeId().isDerivedFrom(App::PropertyComplexGeoData::getClassTypeId())) { + Base::Reference mesh(new MeshObject()); -int AmfExporter::addObject(const MeshCore::MeshKernel &meshKernel) + auto geoData( static_cast(shape)->getComplexData() ); + if (geoData) { + std::vector aPoints; + std::vector aTopo; + geoData->getFaces(aPoints, aTopo, tol); + + mesh->addFacets(aTopo, aPoints); + } else { + return false; + } + + MeshCore::MeshKernel kernel = mesh->getKernel(); + kernel.Transform(mesh->getTransform()); + + return addMesh(kernel); + } + return false; +} + +bool AmfExporter::addMesh(Mesh::Feature *meshFeat) +{ + // TODO: Add name, colour, etc. from the mesh feature + const MeshObject &mesh( meshFeat->Mesh.getValue() ); + MeshCore::MeshKernel kernel( mesh.getKernel() ); + kernel.Transform(mesh.getTransform()); + + return addMesh(kernel); +} + +bool AmfExporter::addMesh(const MeshCore::MeshKernel &kernel) { if (!outputStreamPtr || outputStreamPtr->bad()) { - return -1; + return false; } - auto numFacets( meshKernel.CountFacets() ); + auto numFacets( kernel.CountFacets() ); if (numFacets == 0) { - return -1; + return false; } - MeshCore::MeshFacetIterator clIter(meshKernel), clEnd(meshKernel); + MeshCore::MeshFacetIterator clIter(kernel), clEnd(kernel); Base::SequencerLauncher seq("Saving...", 2 * numFacets + 1); @@ -156,6 +295,7 @@ int AmfExporter::addObject(const MeshCore::MeshKernel &meshKernel) << "\t\t\n" << "\t\n"; - return nextObjectIndex++; + ++nextObjectIndex; + return true; } diff --git a/src/Mod/Mesh/App/AmfExport.h b/src/Mod/Mesh/App/AmfExport.h index edcf1f620..debbc61f7 100644 --- a/src/Mod/Mesh/App/AmfExport.h +++ b/src/Mod/Mesh/App/AmfExport.h @@ -23,40 +23,77 @@ #ifndef MESH_AMFEXPORTER_H #define MESH_AMFEXPORTER_H -#include "Core/MeshKernel.h" - +#include "PreCompiled.h" #ifndef _PreComp_ #include #endif // #ifndef _PreComp_ +#include + +#include "MeshFeature.h" +#include "Core/MeshIO.h" +#include "Core/MeshKernel.h" + namespace Mesh { -/// Used for exporting Additive Manufacturing Format (AMF) files +/// Virtual base class for exporting meshes +/*! + * Constructors of derived classes are expected to be required, for passing + * in the name of output file. + * + * Objects are added using the addMesh(), addShape(), etc. + * + * If objects are meant to be combined into a single file, then the file should + * be saved from the derived class' destructor. + */ +class Exporter +{ + public: + virtual bool addMesh(Mesh::Feature *meshFeat) = 0; + virtual bool addShape(App::Property *shape, float tol) = 0; + virtual ~Exporter() {}; +}; + +/// Creates a single mesh, in a file, from one or more objects +class MergeExporter : public Exporter +{ + public: + MergeExporter(std::string fileName, MeshCore::MeshIO::Format fmt); + ~MergeExporter(); + + /// Directly adds a mesh + bool addMesh(Mesh::Feature *meshFeat); + /// Converts the a Part::Feature to a mesh, adds that mesh + bool addShape(App::Property *shape, float tol); + protected: + MeshObject mergingMesh; + std::string fName; +}; + +/// Used for exporting to Additive Manufacturing File (AMF) format /*! * The constructor and destructor write the beginning and end of the AMF, - * addObject() is used to add... objects! + * add____() is used to add geometry */ -class AmfExporter +class AmfExporter : public Exporter { public: /// Writes AMF header - AmfExporter(const char *fileName); + AmfExporter(std::string fileName); /// Writes AMF footer ~AmfExporter(); - /// Writes an object tag with data from passed-in mesh - /*! - * \return -1 on error, or the index of the object in AMF file - */ - int addObject(const MeshCore::MeshKernel &meshKernel); + bool addMesh(Mesh::Feature *meshFeat); + bool addMesh(const MeshCore::MeshKernel &kernel); + bool addShape(App::Property *shape, float tol); private: std::ostream *outputStreamPtr; int nextObjectIndex; - /// Helper for putting Base::Vector3f objects into a std::map in addObject() + /// Helper for putting Base::Vector3f objects into a std::map in addMesh() class VertLess { public: diff --git a/src/Mod/Mesh/App/AppMeshPy.cpp b/src/Mod/Mesh/App/AppMeshPy.cpp index 3b0ebbd8a..7841473d0 100644 --- a/src/Mod/Mesh/App/AppMeshPy.cpp +++ b/src/Mod/Mesh/App/AppMeshPy.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include #endif #include @@ -302,10 +303,12 @@ private: auto exportFormat( MeshOutput::GetFormat(EncodedName.c_str()) ); - // TODO: Make a similar exporter class to replace global_mesh with - AmfExporter *exporter(nullptr); + std::unique_ptr exporter; if (exportFormat == MeshIO::AMF) { - exporter = new AmfExporter(EncodedName.c_str()); + exporter.reset( new AmfExporter(EncodedName) ); + } else { + // TODO: How do we handle unknown exportFormats? + exporter.reset( new MergeExporter(EncodedName, exportFormat) ); } Base::Type meshId = Base::Type::fromName("Mesh::Feature"); @@ -315,106 +318,18 @@ private: for (auto it : list) { PyObject* item = it.ptr(); if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) { - App::DocumentObject* obj = static_cast(item)->getDocumentObjectPtr(); - auto countFacets( global_mesh.countFacets() ); + App::DocumentObject* obj( static_cast(item)->getDocumentObjectPtr() ); if (obj->getTypeId().isDerivedFrom(meshId)) { - const MeshObject& mesh = static_cast(obj)->Mesh.getValue(); - MeshCore::MeshKernel kernel = mesh.getKernel(); - kernel.Transform(mesh.getTransform()); - - if (exporter) { - exporter->addObject(kernel); - } else { - if (countFacets == 0) - global_mesh.setKernel(kernel); - else - global_mesh.addMesh(kernel); - } - - // if the mesh already has persistent segments then use them instead - unsigned long numSegm = mesh.countSegments(); - unsigned long canSave = 0; - for (unsigned long i=0; i 0) { - for (unsigned long i=0; i indices = segm.getIndices(); - std::for_each(indices.begin(), indices.end(), add_offset(countFacets)); - Segment new_segm(&global_mesh, indices, true); - new_segm.setName(segm.getName()); - global_mesh.addSegment(new_segm); - } - } - } - else { - // now create a segment for the added mesh - std::vector indices; - indices.resize(global_mesh.countFacets() - countFacets); - std::generate(indices.begin(), indices.end(), Base::iotaGen(countFacets)); - Segment segm(&global_mesh, indices, true); - segm.setName(obj->Label.getValue()); - global_mesh.addSegment(segm); - } - } // if (obj->getTypeId().isDerivedFrom(meshId)) - else if (obj->getTypeId().isDerivedFrom(partId)) { - App::Property* shape = obj->getPropertyByName("Shape"); - - if (shape && shape->getTypeId().isDerivedFrom(App::PropertyComplexGeoData::getClassTypeId())) { - Base::Reference mesh(new MeshObject()); - std::vector aPoints; - std::vector aTopo; - const Data::ComplexGeoData* data = static_cast(shape)->getComplexData(); - if (data) { - data->getFaces(aPoints, aTopo, fTolerance); - mesh->addFacets(aTopo, aPoints); - - if (exporter) { - // TODO: Figure out Tranform-constellation-iterator interaction - MeshCore::MeshKernel kernel = mesh->getKernel(); - kernel.Transform(mesh->getTransform()); - exporter->addObject(kernel); - // delete mesh; - } else { - if (countFacets == 0) - global_mesh = *mesh; - else - global_mesh.addMesh(*mesh); - } - - // now create a segment for the added mesh - std::vector indices; - indices.resize(global_mesh.countFacets() - countFacets); - std::generate(indices.begin(), indices.end(), Base::iotaGen(countFacets)); - Segment segm(&global_mesh, indices, true); - segm.setName(obj->Label.getValue()); - global_mesh.addSegment(segm); - } - } - } - else { + exporter->addMesh( static_cast(obj) ); + } else if (obj->getTypeId().isDerivedFrom(partId)) { + exporter->addShape( obj->getPropertyByName("Shape"), fTolerance ); + } else { Base::Console().Message("'%s' is not a mesh or shape, export will be ignored.\n", obj->Label.getValue()); } } } - - if (exporter) { - delete exporter; - } else { - // if we have more than one segment set the 'save' flag - if (global_mesh.countSegments() > 1) { - for (unsigned long i = 0; i < global_mesh.countSegments(); ++i) { - global_mesh.getSegment(i).save(true); - } - } - // export mesh compound - global_mesh.save(EncodedName.c_str()); - } + exporter.reset(); // deletes Exporter, mesh file is written by destructor return Py::None(); }