From 7bbc93e2184cdf7fc4b18a9d7d1cf529057540d5 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 30 Aug 2023 16:55:21 +0200 Subject: [PATCH] Mesh: fixes issue #10075: 3MF files exported from FreeCAD don't work in PrusaSlicer Add an option to force to always write a mesh as model type even if it's not a solid --- src/Mod/Mesh/App/AppMeshPy.cpp | 2 + src/Mod/Mesh/App/Core/IO/Writer3MF.cpp | 51 ++++++++++++------- src/Mod/Mesh/App/Core/IO/Writer3MF.h | 14 +++-- src/Mod/Mesh/App/Exporter.cpp | 5 ++ src/Mod/Mesh/App/Exporter.h | 6 +++ src/Mod/Mesh/Gui/DlgSettingsImportExport.ui | 19 +++++++ .../Mesh/Gui/DlgSettingsImportExportImp.cpp | 2 + 7 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/Mod/Mesh/App/AppMeshPy.cpp b/src/Mod/Mesh/App/AppMeshPy.cpp index a157286215..2e01572cbc 100644 --- a/src/Mod/Mesh/App/AppMeshPy.cpp +++ b/src/Mod/Mesh/App/AppMeshPy.cpp @@ -211,6 +211,7 @@ private: auto fTolerance( hGrp->GetFloat("MaxDeviationExport", 0.1f) ); int exportAmfCompressed( hGrp->GetBool("ExportAmfCompressed", true) ); + bool export3mfModel( hGrp->GetBool("Export3mfModel", true) ); static const std::array kwList{"objectList", "filename", "tolerance", "exportAmfCompressed", nullptr}; @@ -261,6 +262,7 @@ private: else if (exportFormat == MeshIO::ThreeMF) { Extension3MFFactory::initialize(); exporter = std::make_unique(outputFileName, Extension3MFFactory::createExtensions()); + dynamic_cast(exporter.get())->setForceModel(export3mfModel); } else if (exportFormat != MeshIO::Undefined) { exporter = std::make_unique(outputFileName, exportFormat); diff --git a/src/Mod/Mesh/App/Core/IO/Writer3MF.cpp b/src/Mod/Mesh/App/Core/IO/Writer3MF.cpp index 4b799010ed..15c29286da 100644 --- a/src/Mod/Mesh/App/Core/IO/Writer3MF.cpp +++ b/src/Mod/Mesh/App/Core/IO/Writer3MF.cpp @@ -37,7 +37,6 @@ using namespace MeshCore; Writer3MF::Writer3MF(std::ostream &str) : zip(str) - , objectIndex(0) { zip.putNextEntry("3D/3dmodel.model"); Initialize(zip); @@ -45,12 +44,16 @@ Writer3MF::Writer3MF(std::ostream &str) Writer3MF::Writer3MF(const std::string &filename) : zip(filename) - , objectIndex(0) { zip.putNextEntry("3D/3dmodel.model"); Initialize(zip); } +void Writer3MF::SetForceModel(bool model) +{ + forceModel = model; +} + void Writer3MF::Initialize(std::ostream &str) { str << "\n" @@ -63,8 +66,9 @@ void Writer3MF::Finish(std::ostream &str) { str << Base::blanks(1) << "\n"; str << Base::blanks(1) << "\n"; - for (const auto& it : items) + for (const auto& it : items) { str << Base::blanks(2) << it; + } str << Base::blanks(1) << "\n"; str << "\n"; } @@ -87,13 +91,15 @@ bool Writer3MF::Save() zip.closeEntry(); zip.putNextEntry("_rels/.rels"); - if (!SaveRels(zip)) + if (!SaveRels(zip)) { return false; + } zip.closeEntry(); zip.putNextEntry("[Content_Types].xml"); - if (!SaveContent(zip)) + if (!SaveContent(zip)) { return false; + } zip.closeEntry(); for (const auto& it : resources) { zip.putNextEntry(it.fileNameInZip); @@ -106,11 +112,13 @@ bool Writer3MF::Save() bool Writer3MF::SaveObject(std::ostream &str, int id, const MeshKernel& mesh) const { + // NOLINTBEGIN(readability-magic-numbers, cppcoreguidelines-avoid-magic-numbers) const MeshPointArray& rPoints = mesh.GetPoints(); const MeshFacetArray& rFacets = mesh.GetFacets(); - if (!str || str.bad()) + if (!str || str.bad()) { return false; + } str << Base::blanks(2) << "\n"; str << Base::blanks(3) << "\n"; @@ -118,7 +126,7 @@ bool Writer3MF::SaveObject(std::ostream &str, int id, const MeshKernel& mesh) co // vertices str << Base::blanks(4) << "\n"; std::size_t index = 0; - for (MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it, ++index) { + for (auto it = rPoints.begin(); it != rPoints.end(); ++it, ++index) { str << Base::blanks(5) << "x << "\" y=\"" << it->y << "\" z=\"" << it->z @@ -138,16 +146,15 @@ bool Writer3MF::SaveObject(std::ostream &str, int id, const MeshKernel& mesh) co str << Base::blanks(3) << "\n"; str << Base::blanks(2) << "\n"; + // NOLINTEND(readability-magic-numbers, cppcoreguidelines-avoid-magic-numbers) return true; } std::string Writer3MF::GetType(const MeshKernel& mesh) const { - if (MeshEvalSolid(mesh).Evaluate()) - return "model"; - else - return "surface"; + bool isSolid = (forceModel || MeshEvalSolid(mesh).Evaluate()); + return isSolid ? "model" : "surface"; } void Writer3MF::SaveBuildItem(int id, const Base::Matrix4D& mat) @@ -157,8 +164,9 @@ void Writer3MF::SaveBuildItem(int id, const Base::Matrix4D& mat) items.push_back(str.str()); } -std::string Writer3MF::DumpMatrix(const Base::Matrix4D& mat) const +std::string Writer3MF::DumpMatrix(const Base::Matrix4D& mat) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) // The matrix representation in the specs is the transposed version of Matrix4D // This means that for the 3x3 sub-matrix the indices must be swapped // @@ -170,18 +178,25 @@ std::string Writer3MF::DumpMatrix(const Base::Matrix4D& mat) const << mat[0][2] << " " << mat[1][2] << " " << mat[2][2] << " " << mat[0][3] << " " << mat[1][3] << " " << mat[2][3]; return str.str(); + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) } bool Writer3MF::SaveRels(std::ostream &str) const { + // NOLINTBEGIN(modernize-raw-string-literal) int ids = 1; str << "\n" << "\n" - << " \n"; - for (const auto& it : resources) - str << " \n"; + << " \n"; + for (const auto& it : resources) { + str << " \n"; + } str << "\n"; return true; + // NOLINTEND(modernize-raw-string-literal) } bool Writer3MF::SaveContent(std::ostream &str) const @@ -190,8 +205,10 @@ bool Writer3MF::SaveContent(std::ostream &str) const << "\n" << " \n" << " \n"; - for (const auto& it : resources) - str << " \n"; + for (const auto& it : resources) { + str << " \n"; + } str << ""; return true; } diff --git a/src/Mod/Mesh/App/Core/IO/Writer3MF.h b/src/Mod/Mesh/App/Core/IO/Writer3MF.h index 03b9a04a11..cb5f5df834 100644 --- a/src/Mod/Mesh/App/Core/IO/Writer3MF.h +++ b/src/Mod/Mesh/App/Core/IO/Writer3MF.h @@ -62,7 +62,12 @@ public: * \param filename */ explicit Writer3MF(const std::string &filename); - + /*! + * \brief SetForceModel + * Forcces to write the mesh as model even if itsn't a solid. + * \param model + */ + void SetForceModel(bool model); /*! * \brief Add a mesh object resource to the 3MF file. * \param mesh The mesh object to be written @@ -83,20 +88,21 @@ public: bool Save(); private: - void Initialize(std::ostream &str); + static void Initialize(std::ostream &str); void Finish(std::ostream &str); std::string GetType(const MeshKernel& mesh) const; void SaveBuildItem(int id, const Base::Matrix4D& mat); - std::string DumpMatrix(const Base::Matrix4D& mat) const; + static std::string DumpMatrix(const Base::Matrix4D& mat); bool SaveObject(std::ostream &str, int id, const MeshKernel& mesh) const; bool SaveRels(std::ostream &str) const; bool SaveContent(std::ostream &str) const; private: zipios::ZipOutputStream zip; - int objectIndex; + int objectIndex = 0; std::vector items; std::vector resources; + bool forceModel = true; }; } // namespace MeshCore diff --git a/src/Mod/Mesh/App/Exporter.cpp b/src/Mod/Mesh/App/Exporter.cpp index 86c3da6628..8ac2eb0655 100644 --- a/src/Mod/Mesh/App/Exporter.cpp +++ b/src/Mod/Mesh/App/Exporter.cpp @@ -300,6 +300,11 @@ bool Exporter3MF::addMesh(const char *name, const MeshObject & mesh) return ok; } +void Exporter3MF::setForceModel(bool model) +{ + d->writer3mf.SetForceModel(model); +} + void Exporter3MF::write() { d->writer3mf.Save(); diff --git a/src/Mod/Mesh/App/Exporter.h b/src/Mod/Mesh/App/Exporter.h index 64d817c266..fd42f9042e 100644 --- a/src/Mod/Mesh/App/Exporter.h +++ b/src/Mod/Mesh/App/Exporter.h @@ -187,6 +187,12 @@ public: ~Exporter3MF() override; bool addMesh(const char *name, const MeshObject & mesh) override; + /*! + * \brief SetForceModel + * Forcces to write the mesh as model even if itsn't a solid. + * \param model + */ + void setForceModel(bool model); private: /// Write the meshes of the added objects to the output file diff --git a/src/Mod/Mesh/Gui/DlgSettingsImportExport.ui b/src/Mod/Mesh/Gui/DlgSettingsImportExport.ui index d5af012028..457e6a931f 100644 --- a/src/Mod/Mesh/Gui/DlgSettingsImportExport.ui +++ b/src/Mod/Mesh/Gui/DlgSettingsImportExport.ui @@ -76,6 +76,25 @@ + + + + Always export mesh as model type in 3MF format even if not a solid + + + Export 3MF files as model type + + + true + + + Export3mfModel + + + Mod/Mesh + + + diff --git a/src/Mod/Mesh/Gui/DlgSettingsImportExportImp.cpp b/src/Mod/Mesh/Gui/DlgSettingsImportExportImp.cpp index ed085a17bb..76e3825f60 100644 --- a/src/Mod/Mesh/Gui/DlgSettingsImportExportImp.cpp +++ b/src/Mod/Mesh/Gui/DlgSettingsImportExportImp.cpp @@ -53,6 +53,7 @@ void DlgSettingsImportExport::saveSettings() handle->SetFloat("MaxDeviationExport", value); ui->exportAmfCompressed->onSave(); + ui->export3mfModel->onSave(); ParameterGrp::handle asy = handle->GetGroup("Asymptote"); asy->SetASCII("Width", ui->asymptoteWidth->text().toLatin1()); @@ -71,6 +72,7 @@ void DlgSettingsImportExport::loadSettings() ui->maxDeviationExport->setValue(value); ui->exportAmfCompressed->onRestore(); + ui->export3mfModel->onRestore(); ParameterGrp::handle asy = handle->GetGroup("Asymptote"); ui->asymptoteWidth->setText(QString::fromStdString(asy->GetASCII("Width")));