Basic AMF writing support
TODO: * Compression * Understand translations, constellations * Curved surfaces * Material parameters * Metadata, etc
This commit is contained in:
parent
ff1ac5fdad
commit
a9949ab0a4
161
src/Mod/Mesh/App/AmfExport.cpp
Normal file
161
src/Mod/Mesh/App/AmfExport.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) Ian Rees <ian.rees@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#endif // #ifndef _PreComp_
|
||||
|
||||
#include "AmfExport.h"
|
||||
|
||||
#include "Core/Iterator.h"
|
||||
|
||||
#include "Base/Exception.h"
|
||||
#include "Base/FileInfo.h"
|
||||
#include "Base/Sequencer.h"
|
||||
#include "Base/Stream.h"
|
||||
|
||||
using namespace Mesh;
|
||||
|
||||
AmfExporter::AmfExporter(const char *fileName) :
|
||||
outputStreamPtr(nullptr), nextObjectIndex(0)
|
||||
{
|
||||
// ask for write permission
|
||||
Base::FileInfo fi(fileName);
|
||||
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);
|
||||
|
||||
outputStreamPtr = new Base::ofstream(fi, std::ios::out | std::ios::binary);
|
||||
if (outputStreamPtr) {
|
||||
*outputStreamPtr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
<< "<amf unit=\"millimeter\">\n";
|
||||
}
|
||||
}
|
||||
|
||||
AmfExporter::~AmfExporter()
|
||||
{
|
||||
if (outputStreamPtr) {
|
||||
*outputStreamPtr << "\t<constellation id=\"0\">\n";
|
||||
for (auto objId(0); objId < nextObjectIndex; ++objId) {
|
||||
*outputStreamPtr << "\t\t<instance objectid=\"" << objId << "\">\n"
|
||||
<< "\t\t\t<deltax>0</deltax>\n"
|
||||
<< "\t\t\t<deltay>0</deltay>\n"
|
||||
<< "\t\t\t<rz>0</rz>\n"
|
||||
<< "\t\t</instance>\n";
|
||||
}
|
||||
*outputStreamPtr << "\t</constellation>\n"
|
||||
<< "</amf>\n";
|
||||
delete outputStreamPtr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int AmfExporter::addObject(const MeshCore::MeshKernel &meshKernel)
|
||||
{
|
||||
if (!outputStreamPtr || outputStreamPtr->bad()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto numFacets( meshKernel.CountFacets() );
|
||||
|
||||
if (numFacets == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
MeshCore::MeshFacetIterator clIter(meshKernel), clEnd(meshKernel);
|
||||
|
||||
Base::SequencerLauncher seq("Saving...", 2 * numFacets + 1);
|
||||
|
||||
*outputStreamPtr << "\t<object id=\"" << nextObjectIndex << "\">\n"
|
||||
<< "\t\t<mesh>\n"
|
||||
<< "\t\t\t<vertices>\n";
|
||||
|
||||
const MeshCore::MeshGeomFacet *facet;
|
||||
|
||||
// Iterate through all facets of the mesh, and construct a:
|
||||
// * Cache (map) of used vertices, outputting each new unique vertex to
|
||||
// the output stream as we find it
|
||||
// * Vector of the vertices, referred to by the indices from 1
|
||||
std::map<Base::Vector3f, unsigned long, AmfExporter::VertLess> vertices;
|
||||
auto vertItr(vertices.begin());
|
||||
auto vertexCount(0UL);
|
||||
|
||||
// {facet1A, facet1B, facet1C, facet2A, ..., facetNC}
|
||||
std::vector<unsigned long> facets;
|
||||
|
||||
// For each facet in mesh
|
||||
for(clIter.Begin(), clEnd.End(); clIter < clEnd; ++clIter) {
|
||||
facet = &(*clIter);
|
||||
|
||||
// For each vertex in facet
|
||||
for (auto i(0); i < 3; ++i) {
|
||||
vertItr = vertices.find(facet->_aclPoints[i]);
|
||||
|
||||
if ( vertItr == vertices.end() ) {
|
||||
facets.push_back(vertexCount);
|
||||
|
||||
vertices[facet->_aclPoints[i]] = vertexCount++;
|
||||
|
||||
// Output facet
|
||||
*outputStreamPtr << "\t\t\t\t<vertex>\n"
|
||||
<< "\t\t\t\t\t<coordinates>\n";
|
||||
for ( auto j(0); j < 3; ++j) {
|
||||
char axis('x' + j);
|
||||
*outputStreamPtr << "\t\t\t\t\t\t<" << axis << '>'
|
||||
<< facet->_aclPoints[i][j]
|
||||
<< "</" << axis << ">\n";
|
||||
}
|
||||
*outputStreamPtr << "\t\t\t\t\t</coordinates>\n"
|
||||
<< "\t\t\t\t</vertex>\n";
|
||||
} else {
|
||||
facets.push_back(vertItr->second);
|
||||
}
|
||||
}
|
||||
|
||||
seq.next(true); // allow to cancel
|
||||
}
|
||||
|
||||
*outputStreamPtr << "\t\t\t</vertices>\n"
|
||||
<< "\t\t\t<volume>\n";
|
||||
|
||||
// Now that we've output all the vertices, we can
|
||||
// output the facets that refer to them!
|
||||
for (auto triItr(facets.begin()); triItr != facets.end(); ) {
|
||||
*outputStreamPtr << "\t\t\t\t<triangle>\n";
|
||||
for (auto i(1); i < 4; ++i) {
|
||||
*outputStreamPtr << "\t\t\t\t\t<v" << i << '>'
|
||||
<< *(triItr++) << "</v" << i << ">\n";
|
||||
}
|
||||
*outputStreamPtr << "\t\t\t\t</triangle>\n";
|
||||
seq.next(true); // allow to cancel
|
||||
}
|
||||
|
||||
*outputStreamPtr << "\t\t\t</volume>\n"
|
||||
<< "\t\t</mesh>\n"
|
||||
<< "\t</object>\n";
|
||||
|
||||
return nextObjectIndex++;
|
||||
}
|
||||
|
83
src/Mod/Mesh/App/AmfExport.h
Normal file
83
src/Mod/Mesh/App/AmfExport.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/***************************************************************************
|
||||
* Copyright (c) Ian Rees <ian.rees@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MESH_AMFEXPORTER_H
|
||||
#define MESH_AMFEXPORTER_H
|
||||
|
||||
#include "Core/MeshKernel.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <ostream>
|
||||
#endif // #ifndef _PreComp_
|
||||
|
||||
namespace Mesh
|
||||
{
|
||||
|
||||
/// Used for exporting Additive Manufacturing Format (AMF) files
|
||||
/*!
|
||||
* The constructor and destructor write the beginning and end of the AMF,
|
||||
* addObject() is used to add... objects!
|
||||
*/
|
||||
class AmfExporter
|
||||
{
|
||||
public:
|
||||
/// Writes AMF header
|
||||
AmfExporter(const char *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);
|
||||
|
||||
private:
|
||||
std::ostream *outputStreamPtr;
|
||||
int nextObjectIndex;
|
||||
|
||||
/// Helper for putting Base::Vector3f objects into a std::map in addObject()
|
||||
class VertLess
|
||||
{
|
||||
public:
|
||||
bool operator()(const Base::Vector3f &a, const Base::Vector3f &b) const
|
||||
{
|
||||
if (a.x == b.x) {
|
||||
if (a.y == b.y) {
|
||||
if (a.z == b.z) {
|
||||
return false;
|
||||
} else {
|
||||
return a.z < b.z;
|
||||
}
|
||||
} else {
|
||||
return a.y < b.y;
|
||||
}
|
||||
} else {
|
||||
return a.x < b.x;
|
||||
}
|
||||
}
|
||||
};
|
||||
}; // class AmfExporter
|
||||
|
||||
} // namespace Mesh
|
||||
#endif // MESH_AMFEXPORTER_H
|
|
@ -50,6 +50,7 @@
|
|||
#include "WildMagic4/Wm4ContBox3.h"
|
||||
|
||||
#include "Mesh.h"
|
||||
#include "AmfExport.h"
|
||||
#include "FeatureMeshImport.h"
|
||||
#include <Mod/Mesh/App/MeshPy.h>
|
||||
|
||||
|
@ -301,31 +302,35 @@ private:
|
|||
|
||||
auto exportFormat( MeshOutput::GetFormat(EncodedName.c_str()) );
|
||||
|
||||
// Currently, AMF is the only export format where we export separate meshes
|
||||
// into the same file.
|
||||
auto combineMeshes( exportFormat != MeshIO::AMF );
|
||||
if (!combineMeshes) {
|
||||
Base::Console().Message("AMF Export isn't quite supported yet.");
|
||||
return Py::None();
|
||||
// TODO: Make a similar exporter class to replace global_mesh with
|
||||
AmfExporter *exporter(nullptr);
|
||||
if (exportFormat == MeshIO::AMF) {
|
||||
exporter = new AmfExporter(EncodedName.c_str());
|
||||
}
|
||||
|
||||
Py::Sequence list(object);
|
||||
Base::Type meshId = Base::Type::fromName("Mesh::Feature");
|
||||
Base::Type partId = Base::Type::fromName("Part::Feature");
|
||||
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
|
||||
PyObject* item = (*it).ptr();
|
||||
|
||||
Py::Sequence list(object);
|
||||
for (auto it : list) {
|
||||
PyObject* item = it.ptr();
|
||||
if (PyObject_TypeCheck(item, &(App::DocumentObjectPy::Type))) {
|
||||
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(item)->getDocumentObjectPtr();
|
||||
auto countFacets( global_mesh.countFacets() );
|
||||
|
||||
if (obj->getTypeId().isDerivedFrom(meshId)) {
|
||||
const MeshObject& mesh = static_cast<Mesh::Feature*>(obj)->Mesh.getValue();
|
||||
MeshCore::MeshKernel kernel = mesh.getKernel();
|
||||
kernel.Transform(mesh.getTransform());
|
||||
|
||||
unsigned long countFacets = global_mesh.countFacets();
|
||||
if (countFacets == 0)
|
||||
global_mesh.setKernel(kernel);
|
||||
else
|
||||
global_mesh.addMesh(kernel);
|
||||
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();
|
||||
|
@ -346,7 +351,6 @@ private:
|
|||
global_mesh.addSegment(new_segm);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// now create a segment for the added mesh
|
||||
|
@ -357,11 +361,12 @@ private:
|
|||
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");
|
||||
Base::Reference<MeshObject> mesh(new MeshObject());
|
||||
|
||||
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();
|
||||
|
@ -369,11 +374,18 @@ private:
|
|||
data->getFaces(aPoints, aTopo, fTolerance);
|
||||
mesh->addFacets(aTopo, aPoints);
|
||||
|
||||
unsigned long countFacets = global_mesh.countFacets();
|
||||
if (countFacets == 0)
|
||||
global_mesh = *mesh;
|
||||
else
|
||||
global_mesh.addMesh(*mesh);
|
||||
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;
|
||||
|
@ -391,14 +403,18 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
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());
|
||||
}
|
||||
// export mesh compound
|
||||
global_mesh.save(EncodedName.c_str());
|
||||
|
||||
return Py::None();
|
||||
}
|
||||
|
|
|
@ -316,6 +316,8 @@ SET(Mesh_SRCS
|
|||
${Mesh_XML_SRCS}
|
||||
AppMesh.cpp
|
||||
AppMeshPy.cpp
|
||||
AmfExport.cpp
|
||||
AmfExport.h
|
||||
Facet.cpp
|
||||
Facet.h
|
||||
FacetPyImp.cpp
|
||||
|
|
Loading…
Reference in New Issue
Block a user