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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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")));
|
||||
|
||||
Reference in New Issue
Block a user