Refactoring mesh export code
This commit is contained in:
parent
a9949ab0a4
commit
3eefe3e8ba
|
@ -21,8 +21,8 @@
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include "PreCompiled.h"
|
#include "PreCompiled.h"
|
||||||
|
|
||||||
#ifndef _PreComp_
|
#ifndef _PreComp_
|
||||||
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#endif // #ifndef _PreComp_
|
#endif // #ifndef _PreComp_
|
||||||
|
@ -35,14 +35,119 @@
|
||||||
#include "Base/FileInfo.h"
|
#include "Base/FileInfo.h"
|
||||||
#include "Base/Sequencer.h"
|
#include "Base/Sequencer.h"
|
||||||
#include "Base/Stream.h"
|
#include "Base/Stream.h"
|
||||||
|
#include "Base/Tools.h"
|
||||||
|
|
||||||
using namespace Mesh;
|
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<numSegm; i++) {
|
||||||
|
if (mesh.getSegment(i).isSaved())
|
||||||
|
canSave++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canSave > 0) {
|
||||||
|
for (unsigned long i=0; i<numSegm; i++) {
|
||||||
|
const Segment& segm = mesh.getSegment(i);
|
||||||
|
if (segm.isSaved()) {
|
||||||
|
std::vector<unsigned long> 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<unsigned long> indices;
|
||||||
|
indices.resize(mergingMesh.countFacets() - countFacets);
|
||||||
|
std::generate(indices.begin(), indices.end(), Base::iotaGen<unsigned long>(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<MeshObject> mesh(new MeshObject());
|
||||||
|
|
||||||
|
auto countFacets( mergingMesh.countFacets() );
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
// 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)
|
outputStreamPtr(nullptr), nextObjectIndex(0)
|
||||||
{
|
{
|
||||||
// ask for write permission
|
// ask for write permission
|
||||||
Base::FileInfo fi(fileName);
|
Base::FileInfo fi(fileName.c_str());
|
||||||
Base::FileInfo di(fi.dirPath().c_str());
|
Base::FileInfo di(fi.dirPath().c_str());
|
||||||
if ((fi.exists() && !fi.isWritable()) || !di.exists() || !di.isWritable())
|
if ((fi.exists() && !fi.isWritable()) || !di.exists() || !di.isWritable())
|
||||||
throw Base::FileException("No write permission for file", fileName);
|
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<MeshObject> mesh(new MeshObject());
|
||||||
|
|
||||||
int AmfExporter::addObject(const MeshCore::MeshKernel &meshKernel)
|
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);
|
||||||
|
} 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()) {
|
if (!outputStreamPtr || outputStreamPtr->bad()) {
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto numFacets( meshKernel.CountFacets() );
|
auto numFacets( kernel.CountFacets() );
|
||||||
|
|
||||||
if (numFacets == 0) {
|
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);
|
Base::SequencerLauncher seq("Saving...", 2 * numFacets + 1);
|
||||||
|
|
||||||
|
@ -156,6 +295,7 @@ int AmfExporter::addObject(const MeshCore::MeshKernel &meshKernel)
|
||||||
<< "\t\t</mesh>\n"
|
<< "\t\t</mesh>\n"
|
||||||
<< "\t</object>\n";
|
<< "\t</object>\n";
|
||||||
|
|
||||||
return nextObjectIndex++;
|
++nextObjectIndex;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,40 +23,77 @@
|
||||||
#ifndef MESH_AMFEXPORTER_H
|
#ifndef MESH_AMFEXPORTER_H
|
||||||
#define MESH_AMFEXPORTER_H
|
#define MESH_AMFEXPORTER_H
|
||||||
|
|
||||||
#include "Core/MeshKernel.h"
|
#include "PreCompiled.h"
|
||||||
|
|
||||||
#ifndef _PreComp_
|
#ifndef _PreComp_
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#endif // #ifndef _PreComp_
|
#endif // #ifndef _PreComp_
|
||||||
|
|
||||||
|
#include <App/Property.h>
|
||||||
|
|
||||||
|
#include "MeshFeature.h"
|
||||||
|
#include "Core/MeshIO.h"
|
||||||
|
#include "Core/MeshKernel.h"
|
||||||
|
|
||||||
namespace Mesh
|
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,
|
* 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:
|
public:
|
||||||
/// Writes AMF header
|
/// Writes AMF header
|
||||||
AmfExporter(const char *fileName);
|
AmfExporter(std::string fileName);
|
||||||
|
|
||||||
/// Writes AMF footer
|
/// Writes AMF footer
|
||||||
~AmfExporter();
|
~AmfExporter();
|
||||||
|
|
||||||
/// Writes an object tag with data from passed-in mesh
|
bool addMesh(Mesh::Feature *meshFeat);
|
||||||
/*!
|
bool addMesh(const MeshCore::MeshKernel &kernel);
|
||||||
* \return -1 on error, or the index of the object in AMF file
|
bool addShape(App::Property *shape, float tol);
|
||||||
*/
|
|
||||||
int addObject(const MeshCore::MeshKernel &meshKernel);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ostream *outputStreamPtr;
|
std::ostream *outputStreamPtr;
|
||||||
int nextObjectIndex;
|
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
|
class VertLess
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "PreCompiled.h"
|
#include "PreCompiled.h"
|
||||||
#ifndef _PreComp_
|
#ifndef _PreComp_
|
||||||
# include <algorithm>
|
# include <algorithm>
|
||||||
|
# include <memory>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <CXX/Extensions.hxx>
|
#include <CXX/Extensions.hxx>
|
||||||
|
@ -302,10 +303,12 @@ private:
|
||||||
|
|
||||||
auto exportFormat( MeshOutput::GetFormat(EncodedName.c_str()) );
|
auto exportFormat( MeshOutput::GetFormat(EncodedName.c_str()) );
|
||||||
|
|
||||||
// TODO: Make a similar exporter class to replace global_mesh with
|
std::unique_ptr<Exporter> exporter;
|
||||||
AmfExporter *exporter(nullptr);
|
|
||||||
if (exportFormat == MeshIO::AMF) {
|
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");
|
Base::Type meshId = Base::Type::fromName("Mesh::Feature");
|
||||||
|
@ -315,106 +318,18 @@ private:
|
||||||
for (auto it : list) {
|
for (auto it : list) {
|
||||||
PyObject* item = it.ptr();
|
PyObject* item = it.ptr();
|
||||||
if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) {
|
if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) {
|
||||||
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(item)->getDocumentObjectPtr();
|
App::DocumentObject* obj( static_cast<App::DocumentObjectPy*>(item)->getDocumentObjectPtr() );
|
||||||
auto countFacets( global_mesh.countFacets() );
|
|
||||||
|
|
||||||
if (obj->getTypeId().isDerivedFrom(meshId)) {
|
if (obj->getTypeId().isDerivedFrom(meshId)) {
|
||||||
const MeshObject& mesh = static_cast<Mesh::Feature*>(obj)->Mesh.getValue();
|
exporter->addMesh( static_cast<Mesh::Feature*>(obj) );
|
||||||
MeshCore::MeshKernel kernel = mesh.getKernel();
|
} else if (obj->getTypeId().isDerivedFrom(partId)) {
|
||||||
kernel.Transform(mesh.getTransform());
|
exporter->addShape( obj->getPropertyByName("Shape"), fTolerance );
|
||||||
|
|
||||||
if (exporter) {
|
|
||||||
exporter->addObject(kernel);
|
|
||||||
} else {
|
} 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<numSegm; i++) {
|
|
||||||
if (mesh.getSegment(i).isSaved())
|
|
||||||
canSave++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canSave > 0) {
|
|
||||||
for (unsigned long i=0; i<numSegm; i++) {
|
|
||||||
const Segment& segm = mesh.getSegment(i);
|
|
||||||
if (segm.isSaved()) {
|
|
||||||
std::vector<unsigned long> 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<unsigned long> indices;
|
|
||||||
indices.resize(global_mesh.countFacets() - countFacets);
|
|
||||||
std::generate(indices.begin(), indices.end(), Base::iotaGen<unsigned long>(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<MeshObject> mesh(new MeshObject());
|
|
||||||
std::vector<Base::Vector3d> aPoints;
|
|
||||||
std::vector<Data::ComplexGeoData::Facet> aTopo;
|
|
||||||
const Data::ComplexGeoData* data = static_cast<App::PropertyComplexGeoData*>(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<unsigned long> indices;
|
|
||||||
indices.resize(global_mesh.countFacets() - countFacets);
|
|
||||||
std::generate(indices.begin(), indices.end(), Base::iotaGen<unsigned long>(countFacets));
|
|
||||||
Segment segm(&global_mesh, indices, true);
|
|
||||||
segm.setName(obj->Label.getValue());
|
|
||||||
global_mesh.addSegment(segm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Base::Console().Message("'%s' is not a mesh or shape, export will be ignored.\n", obj->Label.getValue());
|
Base::Console().Message("'%s' is not a mesh or shape, export will be ignored.\n", obj->Label.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exporter.reset(); // deletes Exporter, mesh file is written by destructor
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Py::None();
|
return Py::None();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user