From e3ebe4bc9f37736a06e03805192d5b26f29692e0 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 22 Oct 2021 17:11:16 +0200 Subject: [PATCH] Mesh: add basic support of 3MF file format --- src/Mod/Mesh/App/Core/MeshIO.cpp | 105 +++++++++++++++++++++++++++++++ src/Mod/Mesh/App/Core/MeshIO.h | 8 ++- src/Mod/Mesh/App/MeshPyImp.cpp | 1 + src/Mod/Mesh/Gui/Command.cpp | 1 + 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index 09d1ad91dcff..9fde9d851559 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1947,6 +1948,7 @@ std::vector MeshOutput::supportedMeshFormats() fmt.emplace_back("wrz"); fmt.emplace_back("amf"); fmt.emplace_back("asy"); + fmt.emplace_back("3mf"); return fmt; } @@ -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; } @@ -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)) @@ -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: @@ -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 << "\n" + << "" + "" + ""; + return true; +} + +bool MeshOutput::Save3MFContent(std::ostream &str) const +{ + str << "\n" + << "" + "" + "" + ""; + 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 << "\n" + << "\n" + << "FreeCAD\n"; + str << Base::blanks(2) << "\n"; + str << Base::blanks(4) << "\n"; + str << Base::blanks(6) << "\n"; + + // vertices + str << Base::blanks(8) << "\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) << "\n"; + } + str << Base::blanks(8) << "\n"; + + // facet indices + str << Base::blanks(8) << "\n"; + for (MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) { + str << Base::blanks(10) << "_aulPoints[0] + << "\" v2=\"" << it->_aulPoints[1] + << "\" v3=\"" << it->_aulPoints[2] + << "\" />\n"; + } + str << Base::blanks(8) << "\n"; + + str << Base::blanks(6) << "\n"; + str << Base::blanks(4) << "\n"; + str << Base::blanks(2) << "\n"; + str << Base::blanks(2) << "\n"; + str << Base::blanks(4) << "\n"; + str << Base::blanks(2) << "\n"; + str << "\n"; + return true; +} + /** Writes an IDTF file. */ bool MeshOutput::SaveIDTF (std::ostream &str) const { diff --git a/src/Mod/Mesh/App/Core/MeshIO.h b/src/Mod/Mesh/App/Core/MeshIO.h index fdb369c348ec..4e2fc61f0ca9 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.h +++ b/src/Mod/Mesh/App/Core/MeshIO.h @@ -60,7 +60,8 @@ namespace MeshIO { PY, AMF, SMF, - ASY + ASY, + ThreeMF }; enum Binding { OVERALL, @@ -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. */ @@ -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 */ diff --git a/src/Mod/Mesh/App/MeshPyImp.cpp b/src/Mod/Mesh/App/MeshPyImp.cpp index 4f47e92368c7..3876f0ff91f4 100644 --- a/src/Mod/Mesh/App/MeshPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPyImp.cpp @@ -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", diff --git a/src/Mod/Mesh/Gui/Command.cpp b/src/Mod/Mesh/Gui/Command.cpp index f92ca4673510..52f078b09949 100644 --- a/src/Mod/Mesh/Gui/Command.cpp +++ b/src/Mod/Mesh/Gui/Command.cpp @@ -524,6 +524,7 @@ void CmdMeshExport::activated(int) ext << qMakePair(QString::fromLatin1("%1 (*.nas *.bdf)").arg(QObject::tr("Nastran")), "NAS"); ext << qMakePair(QString::fromLatin1("%1 (*.py)").arg(QObject::tr("Python module def")), "PY"); ext << qMakePair(QString::fromLatin1("%1 (*.asy)").arg(QObject::tr("Asymptote Format")), "ASY"); + ext << qMakePair(QString::fromLatin1("%1 (*.3mf)").arg(QObject::tr("3D Manufacturing Format")), "3MF"); ext << qMakePair(QString::fromLatin1("%1 (*.*)").arg(QObject::tr("All Files")), ""); // Undefined QStringList filter; for (QList >::iterator it = ext.begin(); it != ext.end(); ++it)