Mesh: refactor mesh exporter for better support of Link
This commit is contained in:
@@ -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()) );
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user