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
This commit is contained in:
wmayer
2023-08-30 16:55:21 +02:00
committed by wwmayer
parent 9583c1c0cb
commit 2a65715248
7 changed files with 78 additions and 21 deletions

View File

@@ -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<const char *, 5> kwList{"objectList", "filename", "tolerance",
"exportAmfCompressed", nullptr};
@@ -261,6 +262,7 @@ private:
else if (exportFormat == MeshIO::ThreeMF) {
Extension3MFFactory::initialize();
exporter = std::make_unique<Exporter3MF>(outputFileName, Extension3MFFactory::createExtensions());
dynamic_cast<Exporter3MF*>(exporter.get())->setForceModel(export3mfModel);
}
else if (exportFormat != MeshIO::Undefined) {
exporter = std::make_unique<MergeExporter>(outputFileName, exportFormat);

View File

@@ -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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@@ -63,8 +66,9 @@ void Writer3MF::Finish(std::ostream &str)
{
str << Base::blanks(1) << "</resources>\n";
str << Base::blanks(1) << "<build>\n";
for (const auto& it : items)
for (const auto& it : items) {
str << Base::blanks(2) << it;
}
str << Base::blanks(1) << "</build>\n";
str << "</model>\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) << "<object id=\"" << id << "\" type=\"" << GetType(mesh) << "\">\n";
str << Base::blanks(3) << "<mesh>\n";
@@ -118,7 +126,7 @@ bool Writer3MF::SaveObject(std::ostream &str, int id, const MeshKernel& mesh) co
// vertices
str << Base::blanks(4) << "<vertices>\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) << "<vertex x=\"" << it->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) << "</mesh>\n";
str << Base::blanks(2) << "</object>\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 << "<?xml version='1.0' encoding='UTF-8'?>\n"
<< "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"
<< " <Relationship Target=\"/3D/3dmodel.model\" Id=\"rel0\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\" />\n";
for (const auto& it : resources)
str << " <Relationship Target=\"" << it.relationshipTarget << "\" Id=\"rel" << ++ids << "\" Type=\"" << it.relationshipType << "\" />\n";
<< " <Relationship Target=\"/3D/3dmodel.model\" Id=\"rel0\""
<< " Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\" />\n";
for (const auto& it : resources) {
str << " <Relationship Target=\""
<< it.relationshipTarget << "\" Id=\"rel" << ++ids
<< "\" Type=\"" << it.relationshipType << "\" />\n";
}
str << "</Relationships>\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
<< "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n"
<< " <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n"
<< " <Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\"/>\n";
for (const auto& it : resources)
str << " <Default Extension=\"" << it.extension << "\" ContentType=\"" << it.contentType << "\"/>\n";
for (const auto& it : resources) {
str << " <Default Extension=\"" << it.extension
<< "\" ContentType=\"" << it.contentType << "\"/>\n";
}
str << "</Types>";
return true;
}

View File

@@ -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<std::string> items;
std::vector<Resource3MF> resources;
bool forceModel = true;
};
} // namespace MeshCore

View File

@@ -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();

View File

@@ -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

View File

@@ -76,6 +76,25 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="Gui::PrefCheckBox" name="export3mfModel">
<property name="toolTip">
<string>Always export mesh as model type in 3MF format even if not a solid</string>
</property>
<property name="text">
<string>Export 3MF files as model type</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>Export3mfModel</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Mesh</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@@ -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")));