diff --git a/src/Mod/Mesh/App/AppMeshPy.cpp b/src/Mod/Mesh/App/AppMeshPy.cpp index 6b1e69da83..305fbbcdd9 100644 --- a/src/Mod/Mesh/App/AppMeshPy.cpp +++ b/src/Mod/Mesh/App/AppMeshPy.cpp @@ -382,29 +382,16 @@ private: // collect all object types that can be exported as mesh std::vector objectList; - std::string label; for (auto it : list) { PyObject *item = it.ptr(); if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) { auto obj( static_cast(item)->getDocumentObjectPtr() ); - label = obj->Label.getValue(); - if (Exporter::isSupported(obj)) - objectList.push_back(obj); + objectList.push_back(obj); } } if (objectList.empty()) { - std::string errorMessage; - if (list.length() == 1) { - std::stringstream str; - str << label << " cannot be exported to a mesh file"; - errorMessage = str.str(); - } - else { - errorMessage = "None of the objects can be exported to a mesh file"; - } - - throw Py::TypeError(errorMessage); + throw Py::TypeError("None of the objects can be exported to a mesh file"); } auto exportFormat( MeshOutput::GetFormat(outputFileName.c_str()) ); diff --git a/src/Mod/Mesh/App/Exporter.cpp b/src/Mod/Mesh/App/Exporter.cpp index 9de3f6ac72..2ec054d986 100644 --- a/src/Mod/Mesh/App/Exporter.cpp +++ b/src/Mod/Mesh/App/Exporter.cpp @@ -28,6 +28,7 @@ #endif // #ifndef _PreComp_ #include "Exporter.h" +#include "MeshFeature.h" #include "Core/Iterator.h" @@ -38,17 +39,49 @@ #include "Base/Stream.h" #include "Base/Tools.h" -#include "App/Part.h" +#include "App/Application.h" +#include "App/ComplexGeoData.h" +#include "App/ComplexGeoDataPy.h" +#include "App/DocumentObject.h" #include using namespace Mesh; using namespace MeshCore; -Exporter::Exporter() : - meshFeatId( Base::Type::fromName("Mesh::Feature") ), - appPartId( Base::Type::fromName("Part::Feature") ), - groupExtensionId( App::GroupExtension::getExtensionClassTypeId() ) +static std::vector +expandSubObjects(const App::DocumentObject *obj, + std::map > &cache, + int depth) +{ + if (!App::GetApplication().checkLinkDepth(depth)) + return {}; + + auto subs = obj->getSubObjects(); + if (subs.empty()) { + subs.emplace_back(""); + return subs; + } + + std::vector res; + for (auto & sub : subs) { + int vis = sub.empty() ? 1 : obj->isElementVisible(sub.c_str()); + if (vis == 0) + continue; + auto sobj = obj->getSubObject(sub.c_str()); + if (!sobj || (vis < 0 && !sobj->Visibility.getValue())) + continue; + auto linked = sobj->getLinkedObject(true); + auto it = cache.find(linked); + if (it == cache.end()) + it = cache.emplace(linked, expandSubObjects(linked, cache, depth+1)).first; + for (auto & ssub : it->second) + res.push_back(sub + ssub); + } + return res; +} + +Exporter::Exporter() { } //static @@ -63,63 +96,42 @@ std::string Exporter::xmlEscape(const std::string &input) return out; } -bool Exporter::isSupported(App::DocumentObject *obj) +int Exporter::addObject(App::DocumentObject *obj, float tol) { - Base::Type meshFeatId(Base::Type::fromName("Mesh::Feature")); - Base::Type appPartId(Base::Type::fromName("Part::Feature")); - Base::Type groupExtensionId(App::GroupExtension::getExtensionClassTypeId()); - - if (obj->getTypeId().isDerivedFrom(meshFeatId)) { - return true; - } - else if (obj->getTypeId().isDerivedFrom(appPartId)) { - return true; - } - else if (obj->hasExtension(groupExtensionId)) { - auto groupEx( obj->getExtensionByType() ); - for (auto it : groupEx->Group.getValues()) { - bool ok = isSupported(it); - if (ok) - return true; + int count = 0; + for (std::string & sub : expandSubObjects(obj, cache, 0)) { + Base::Matrix4D matrix; + auto sobj = obj->getSubObject(sub.c_str(), nullptr, &matrix); + auto linked = sobj->getLinkedObject(true, &matrix, false); + auto it = meshCache.find(linked); + if (it == meshCache.end()) { + if (linked->isDerivedFrom(Mesh::Feature::getClassTypeId())) { + it = meshCache.emplace(linked, + static_cast(linked)->Mesh.getValue()).first; + it->second.setTransform(Base::Matrix4D()); + } else { + Base::PyGILStateLocker lock; + PyObject *pyobj = nullptr; + linked->getSubObject("", &pyobj, nullptr, false); + if (!pyobj) + continue; + if (PyObject_TypeCheck(pyobj, &Data::ComplexGeoDataPy::Type)) { + std::vector aPoints; + std::vector aTopo; + auto geoData = static_cast(pyobj)->getComplexGeoDataPtr(); + geoData->getFaces(aPoints, aTopo, tol); + it = meshCache.emplace(linked, MeshObject()).first; + it->second.setFacets(aTopo, aPoints); + } + Py_DECREF(pyobj); + } } + MeshObject mesh(it->second); + mesh.transformGeometry(matrix); + if (addMesh(sobj->Label.getValue(), mesh)) + ++count; } - - return false; -} - -bool Exporter::addAppGroup(App::DocumentObject *obj, float tol) -{ - auto ret(true); - - auto groupEx( obj->getExtensionByType() ); - for (auto it : groupEx->Group.getValues()) { - if (it->getTypeId().isDerivedFrom(meshFeatId)) { - ret &= addMeshFeat(it); - } else if (it->getTypeId().isDerivedFrom(appPartId)) { - ret &= addPartFeat(it, tol); - } else if (it->hasExtension(groupExtensionId)) { - // Recurse - ret &= addAppGroup(it, tol); - } - } - - return ret; -} - -bool Exporter::addObject(App::DocumentObject *obj, float tol) -{ - if (obj->getTypeId().isDerivedFrom(meshFeatId)) { - return addMeshFeat( obj ); - } else if (obj->getTypeId().isDerivedFrom(appPartId)) { - return addPartFeat( obj, tol ); - } else if (obj->hasExtension(groupExtensionId)) { - return addAppGroup( obj, tol ); - } else { - Base::Console().Message( - "'%s' is of type %s, and can not be exported as a mesh.\n", - obj->Label.getValue(), obj->getTypeId().getName() ); - return false; - } + return count; } MergeExporter::MergeExporter(std::string fileName, MeshIO::Format) @@ -145,14 +157,9 @@ MergeExporter::~MergeExporter() } -bool MergeExporter::addMeshFeat(App::DocumentObject *obj) +bool MergeExporter::addMesh(const char *name, const MeshObject & mesh) { - const MeshObject &mesh( static_cast(obj)->Mesh.getValue() ); - Base::Placement plm = static_cast(obj)->globalPlacement(); - - MeshCore::MeshKernel kernel(mesh.getKernel()); - kernel.Transform(plm.toMatrix()); - + const auto & kernel = mesh.getKernel(); auto countFacets( mergingMesh.countFacets() ); if (countFacets == 0) { mergingMesh.setKernel(kernel); @@ -188,68 +195,13 @@ bool MergeExporter::addMeshFeat(App::DocumentObject *obj) indices.resize(mergingMesh.countFacets() - countFacets); std::generate(indices.begin(), indices.end(), Base::iotaGen(countFacets)); Segment segm(&mergingMesh, indices, true); - segm.setName(obj->Label.getValue()); + segm.setName(name); mergingMesh.addSegment(segm); } return true; } -bool MergeExporter::addPartFeat(App::DocumentObject *obj, float tol) -{ - auto *shape(obj->getPropertyByName("Shape")); - 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) { - App::GeoFeature* gf = static_cast(obj); - Base::Placement plm = gf->globalPlacement(); - Base::Placement pl = gf->Placement.getValue(); - bool applyGlobal = false; - if (pl == plm) { - //no extension placement - applyGlobal = false; - } else { - //there is a placement from extension - applyGlobal = true; - } - - std::vector aPoints; - std::vector aTopo; - geoData->getFaces(aPoints, aTopo, tol); - - if (applyGlobal) { - Base::Placement diff_plm = plm * pl.inverse(); - for (auto& it : aPoints) { - diff_plm.multVec(it,it); - } - } - - mesh->addFacets(aTopo, aPoints, false); - 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); - segm.setName(obj->Label.getValue()); - mergingMesh.addSegment(segm); - - return true; - } - return false; -} - AmfExporter::AmfExporter( std::string fileName, const std::map &meta, bool compress ) : @@ -304,50 +256,10 @@ AmfExporter::~AmfExporter() } } -bool AmfExporter::addPartFeat(App::DocumentObject *obj, float tol) +bool AmfExporter::addMesh(const char *name, const MeshObject & mesh) { - auto *shape(obj->getPropertyByName("Shape")); + const auto & kernel = mesh.getKernel(); - if (shape && shape->getTypeId().isDerivedFrom(App::PropertyComplexGeoData::getClassTypeId())) { - Base::Reference mesh(new MeshObject()); - - auto geoData( static_cast(shape)->getComplexData() ); - if (geoData) { - std::vector aPoints; - std::vector aTopo; - geoData->getFaces(aPoints, aTopo, tol); - - mesh->addFacets(aTopo, aPoints, false); - } else { - return false; - } - - MeshCore::MeshKernel kernel = mesh->getKernel(); - kernel.Transform(mesh->getTransform()); - - std::map meta; - meta["name"] = xmlEscape(obj->Label.getStrValue()); - - return addMesh(kernel, meta); - } - return false; -} - -bool AmfExporter::addMeshFeat(App::DocumentObject *obj) -{ - const MeshObject &mesh( static_cast(obj)->Mesh.getValue() ); - MeshCore::MeshKernel kernel( mesh.getKernel() ); - kernel.Transform(mesh.getTransform()); - - std::map meta; - meta["name"] = xmlEscape(obj->Label.getStrValue()); - - return addMesh(kernel, meta); -} - -bool AmfExporter::addMesh(const MeshCore::MeshKernel &kernel, - const std::map &meta) -{ if (!outputStreamPtr || outputStreamPtr->bad()) { return false; } @@ -363,11 +275,8 @@ bool AmfExporter::addMesh(const MeshCore::MeshKernel &kernel, Base::SequencerLauncher seq("Saving...", 2 * numFacets + 1); *outputStreamPtr << "\t\n"; - - for (auto const &metaEntry : meta) { - *outputStreamPtr << "\t\t" << metaEntry.second << "\n"; - } + *outputStreamPtr << "\t\t" + << xmlEscape(name) << "\n"; *outputStreamPtr << "\t\t\n" << "\t\t\t\n"; diff --git a/src/Mod/Mesh/App/Exporter.h b/src/Mod/Mesh/App/Exporter.h index af6bcbdede..3b0b564b57 100644 --- a/src/Mod/Mesh/App/Exporter.h +++ b/src/Mod/Mesh/App/Exporter.h @@ -24,6 +24,7 @@ #define MESH_EXPORTER_H #include +#include #include #include "Base/Type.h" @@ -42,8 +43,6 @@ namespace Mesh * Constructors of derived classes are expected to be required, for passing * in the name of output file. * - * Objects are added using the addMeshFeat(), addPartFeat(), etc. - * * If objects are meant to be combined into a single file, then the file should * be saved from the derived class' destructor. */ @@ -51,33 +50,18 @@ class Exporter { public: Exporter(); - - /*! - * \return true if \a is an object that can be exported as mesh. - */ - static bool isSupported(App::DocumentObject *obj); - - virtual bool addMeshFeat(App::DocumentObject *obj) = 0; - virtual bool addPartFeat(App::DocumentObject *obj, float tol) = 0; - - /// Recursively adds objects from App::Part & App::DocumentObjectGroup - /*! - * \return true if all applicable objects within the group were - * added successfully. - */ - bool addAppGroup(App::DocumentObject *obj, float tol); - - bool addObject(App::DocumentObject *obj, float tol); - virtual ~Exporter() = default; + int addObject(App::DocumentObject *obj, float tol); + + virtual bool addMesh(const char *name, const MeshObject & mesh) = 0; + protected: /// Does some simple escaping of characters for XML-type exports static std::string xmlEscape(const std::string &input); - const Base::Type meshFeatId; - const Base::Type appPartId; - const Base::Type groupExtensionId; + std::map > cache; + std::map meshCache; }; /// Creates a single mesh, in a file, from one or more objects @@ -87,11 +71,7 @@ class MergeExporter : public Exporter MergeExporter(std::string fileName, MeshCore::MeshIO::Format fmt); ~MergeExporter(); - /// Directly adds a mesh feature - bool addMeshFeat(App::DocumentObject *obj) override; - - /// Converts the a Part::Feature to a mesh, adds that mesh - bool addPartFeat(App::DocumentObject *obj, float tol) override; + bool addMesh(const char *name, const MeshObject & mesh) override; protected: MeshObject mergingMesh; @@ -117,16 +97,8 @@ class AmfExporter : public Exporter /// Writes AMF footer ~AmfExporter(); - bool addMeshFeat(App::DocumentObject *obj) override; + bool addMesh(const char *name, const MeshObject & mesh) override; - bool addPartFeat(App::DocumentObject *obj, float tol) override; - - /*! - * meta is included for the AMF object created - */ - bool addMesh(const MeshCore::MeshKernel &kernel, - const std::map &meta); - private: std::ostream *outputStreamPtr; int nextObjectIndex;