Mesh: refactor mesh exporter for better support of Link

This commit is contained in:
Zheng, Lei
2020-12-29 15:39:02 +08:00
committed by wwmayer
parent 13927b2465
commit ea61253c67
3 changed files with 89 additions and 221 deletions

View File

@@ -382,29 +382,16 @@ private:
// collect all object types that can be exported as mesh
std::vector<App::DocumentObject*> objectList;
std::string label;
for (auto it : list) {
PyObject *item = it.ptr();
if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) {
auto obj( static_cast<App::DocumentObjectPy *>(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()) );

View File

@@ -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 <zipios++/zipoutputstream.h>
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<std::string>
expandSubObjects(const App::DocumentObject *obj,
std::map<const App::DocumentObject*, std::vector<std::string> > &cache,
int depth)
{
if (!App::GetApplication().checkLinkDepth(depth))
return {};
auto subs = obj->getSubObjects();
if (subs.empty()) {
subs.emplace_back("");
return subs;
}
std::vector<std::string> 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<App::GroupExtension>() );
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<Mesh::Feature*>(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<Base::Vector3d> aPoints;
std::vector<Data::ComplexGeoData::Facet> aTopo;
auto geoData = static_cast<Data::ComplexGeoDataPy*>(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<App::GroupExtension>() );
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<Mesh::Feature *>(obj)->Mesh.getValue() );
Base::Placement plm = static_cast<Mesh::Feature *>(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<unsigned long>(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<MeshObject> mesh(new MeshObject());
auto countFacets( mergingMesh.countFacets() );
auto geoData( static_cast<App::PropertyComplexGeoData*>(shape)->getComplexData() );
if (geoData) {
App::GeoFeature* gf = static_cast<App::GeoFeature*>(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<Base::Vector3d> aPoints;
std::vector<Data::ComplexGeoData::Facet> 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<unsigned long> indices;
indices.resize(mergingMesh.countFacets() - countFacets);
std::generate(indices.begin(), indices.end(), Base::iotaGen<unsigned long>(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<std::string, std::string> &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<MeshObject> mesh(new MeshObject());
auto geoData( static_cast<App::PropertyComplexGeoData*>(shape)->getComplexData() );
if (geoData) {
std::vector<Base::Vector3d> aPoints;
std::vector<Data::ComplexGeoData::Facet> 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<std::string, std::string> meta;
meta["name"] = xmlEscape(obj->Label.getStrValue());
return addMesh(kernel, meta);
}
return false;
}
bool AmfExporter::addMeshFeat(App::DocumentObject *obj)
{
const MeshObject &mesh( static_cast<Mesh::Feature *>(obj)->Mesh.getValue() );
MeshCore::MeshKernel kernel( mesh.getKernel() );
kernel.Transform(mesh.getTransform());
std::map<std::string, std::string> meta;
meta["name"] = xmlEscape(obj->Label.getStrValue());
return addMesh(kernel, meta);
}
bool AmfExporter::addMesh(const MeshCore::MeshKernel &kernel,
const std::map<std::string, std::string> &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<object id=\"" << nextObjectIndex << "\">\n";
for (auto const &metaEntry : meta) {
*outputStreamPtr << "\t\t<metadata type=\"" << metaEntry.first
<< "\">" << metaEntry.second << "</metadata>\n";
}
*outputStreamPtr << "\t\t<metadata type=\"name\">"
<< xmlEscape(name) << "</metadata>\n";
*outputStreamPtr << "\t\t<mesh>\n"
<< "\t\t\t<vertices>\n";

View File

@@ -24,6 +24,7 @@
#define MESH_EXPORTER_H
#include <map>
#include <vector>
#include <ostream>
#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<const App::DocumentObject *, std::vector<std::string> > cache;
std::map<const App::DocumentObject *, MeshObject> 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<std::string, std::string> &meta);
private:
std::ostream *outputStreamPtr;
int nextObjectIndex;