Skip to content

Commit

Permalink
Mesh: add basic support of 3MF file format
Browse files Browse the repository at this point in the history
  • Loading branch information
wwmayer committed Oct 22, 2021
1 parent c2502b0 commit e3ebe4b
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
105 changes: 105 additions & 0 deletions src/Mod/Mesh/App/Core/MeshIO.cpp
Expand Up @@ -41,6 +41,7 @@
#include <Base/Placement.h>
#include <Base/Tools.h>
#include <zipios++/gzipoutputstream.h>
#include <zipios++/zipoutputstream.h>

#include <cmath>
#include <sstream>
Expand Down Expand Up @@ -1947,6 +1948,7 @@ std::vector<std::string> MeshOutput::supportedMeshFormats()
fmt.emplace_back("wrz");
fmt.emplace_back("amf");
fmt.emplace_back("asy");
fmt.emplace_back("3mf");
return fmt;
}

Expand Down Expand Up @@ -2004,6 +2006,9 @@ MeshIO::Format MeshOutput::GetFormat(const char* FileName)
else if (file.hasExtension("amf")) {
return MeshIO::AMF;
}
else if (file.hasExtension("3mf")) {
return MeshIO::ThreeMF;
}
else if (file.hasExtension("smf")) {
return MeshIO::SMF;
}
Expand Down Expand Up @@ -2113,6 +2118,11 @@ bool MeshOutput::SaveAny(const char* FileName, MeshIO::Format format) const
if (!SaveX3DOM(str))
throw Base::FileException("Export of X3DOM failed",FileName);
}
else if (fileformat == MeshIO::ThreeMF) {
// write file
if (!Save3MF(str))
throw Base::FileException("Export of 3MF failed",FileName);
}
else if (fileformat == MeshIO::PY) {
// write file
if (!SavePython(str))
Expand Down Expand Up @@ -2183,6 +2193,8 @@ bool MeshOutput::SaveFormat(std::ostream &str, MeshIO::Format fmt) const
case MeshIO::WRZ:
// it's up to the client to create the needed stream
return SaveVRML(str);
case MeshIO::ThreeMF:
return Save3MF(str);
case MeshIO::NAS:
return SaveNastran(str);
case MeshIO::PLY:
Expand Down Expand Up @@ -2974,6 +2986,99 @@ void MeshOutput::SaveXML (Base::Writer &writer) const
writer.decInd();
}

/** Saves the mesh object into a 3MF file. */
bool MeshOutput::Save3MF(std::ostream &str) const
{
zipios::ZipOutputStream zip(str);
zip.putNextEntry("/3D/3dmodel.model");
if (!Save3MFModel(zip))
return false;
zip.closeEntry();

zip.putNextEntry("_rels/.rels");
if (!Save3MFRels(zip))
return false;
zip.closeEntry();

zip.putNextEntry("[Content_Types].xml");
if (!Save3MFContent(zip))
return false;
zip.closeEntry();
return true;
}

bool MeshOutput::Save3MFRels(std::ostream &str) const
{
str << "<?xml version='1.0' encoding='UTF-8'?>\n"
<< "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">"
"<Relationship Id=\"rel0\" Target=\"/3D/3dmodel.model\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\" />"
"</Relationships>";
return true;
}

bool MeshOutput::Save3MFContent(std::ostream &str) const
{
str << "<?xml version='1.0' encoding='UTF-8'?>\n"
<< "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"
"<Default ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" Extension=\"rels\" />"
"<Default ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" Extension=\"model\" />"
"</Types>";
return true;
}

bool MeshOutput::Save3MFModel (std::ostream &str) const
{
const MeshPointArray& rPoints = _rclMesh.GetPoints();
const MeshFacetArray& rFacets = _rclMesh.GetFacets();

if (!str || str.bad() == true)
return false;

str << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
<< "<model unit=\"millimeter\"\n"
<< " xml:lang=\"en-US\"\n"
<< " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">\n"
<< "<metadata name=\"Application\">FreeCAD</metadata>\n";
str << Base::blanks(2) << "<resources>\n";
str << Base::blanks(4) << "<object id=\"1\" type=\"model\">\n";
str << Base::blanks(6) << "<mesh>\n";

// vertices
str << Base::blanks(8) << "<vertices>\n";
Base::Vector3f pt;
std::size_t index = 0;
for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) {
pt.Set(it->x, it->y, it->z);
if (this->apply_transform) {
this->_transform.multVec(pt, pt);
}
str << Base::blanks(10) << "<vertex x=\"" << pt.x
<< "\" y=\"" << pt.y
<< "\" z=\"" << pt.z
<< "\" />\n";
}
str << Base::blanks(8) << "</vertices>\n";

// facet indices
str << Base::blanks(8) << "<triangles>\n";
for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) {
str << Base::blanks(10) << "<triangle v1=\"" << it->_aulPoints[0]
<< "\" v2=\"" << it->_aulPoints[1]
<< "\" v3=\"" << it->_aulPoints[2]
<< "\" />\n";
}
str << Base::blanks(8) << "</triangles>\n";

str << Base::blanks(6) << "</mesh>\n";
str << Base::blanks(4) << "</object>\n";
str << Base::blanks(2) << "</resources>\n";
str << Base::blanks(2) << "<build>\n";
str << Base::blanks(4) << "<item objectid=\"1\" />\n";
str << Base::blanks(2) << "</build>\n";
str << "</model>\n";
return true;
}

/** Writes an IDTF file. */
bool MeshOutput::SaveIDTF (std::ostream &str) const
{
Expand Down
8 changes: 7 additions & 1 deletion src/Mod/Mesh/App/Core/MeshIO.h
Expand Up @@ -60,7 +60,8 @@ namespace MeshIO {
PY,
AMF,
SMF,
ASY
ASY,
ThreeMF
};
enum Binding {
OVERALL,
Expand Down Expand Up @@ -197,6 +198,8 @@ class MeshExport MeshOutput
bool SaveAsymptote (std::ostream &rstrOut) const;
/** Saves the mesh object into an XML file. */
void SaveXML (Base::Writer &writer) const;
/** Saves the mesh object into a 3MF file. */
bool Save3MF (std::ostream &str) const;
/** Saves a node to an OpenInventor file. */
bool SaveMeshNode (std::ostream &rstrIn);
/** Writes an IDTF file. */
Expand All @@ -223,6 +226,9 @@ class MeshExport MeshOutput
protected:
/** Writes an X3D file. */
bool SaveX3DContent (std::ostream &rstrOut, bool exportViewpoints) const;
bool Save3MFModel(std::ostream &str) const;
bool Save3MFRels(std::ostream &str) const;
bool Save3MFContent(std::ostream &str) const;

protected:
const MeshKernel &_rclMesh; /**< reference to mesh data structure */
Expand Down
1 change: 1 addition & 0 deletions src/Mod/Mesh/App/MeshPyImp.cpp
Expand Up @@ -222,6 +222,7 @@ PyObject* MeshPy::write(PyObject *args, PyObject *kwds)
ext["APLY" ] = MeshCore::MeshIO::APLY;
ext["PY" ] = MeshCore::MeshIO::PY;
ext["ASY" ] = MeshCore::MeshIO::ASY;
ext["3MF" ] = MeshCore::MeshIO::ThreeMF;

static char* keywords_path[] = {"Filename","Format","Name","Material",NULL};
if (PyArg_ParseTupleAndKeywords(args, kwds, "et|ssO", keywords_path, "utf-8",
Expand Down
1 change: 1 addition & 0 deletions src/Mod/Mesh/Gui/Command.cpp
Expand Up @@ -524,6 +524,7 @@ void CmdMeshExport::activated(int)
ext << qMakePair<QString, QByteArray>(QString::fromLatin1("%1 (*.nas *.bdf)").arg(QObject::tr("Nastran")), "NAS");
ext << qMakePair<QString, QByteArray>(QString::fromLatin1("%1 (*.py)").arg(QObject::tr("Python module def")), "PY");
ext << qMakePair<QString, QByteArray>(QString::fromLatin1("%1 (*.asy)").arg(QObject::tr("Asymptote Format")), "ASY");
ext << qMakePair<QString, QByteArray>(QString::fromLatin1("%1 (*.3mf)").arg(QObject::tr("3D Manufacturing Format")), "3MF");
ext << qMakePair<QString, QByteArray>(QString::fromLatin1("%1 (*.*)").arg(QObject::tr("All Files")), ""); // Undefined
QStringList filter;
for (QList<QPair<QString, QByteArray> >::iterator it = ext.begin(); it != ext.end(); ++it)
Expand Down

0 comments on commit e3ebe4b

Please sign in to comment.