FIX: issue 551: support to import prusa 3mf file

Change-Id: I6d00fab62304893cc5f1aba0d0a74e4c6194bce9
This commit is contained in:
zhimin.zeng 2022-07-29 15:28:56 +08:00 committed by Lane.Wei
parent acc6d70db9
commit 09d5651c39
5 changed files with 209 additions and 14 deletions

View file

@ -271,6 +271,105 @@ namespace Slic3r {
//! return same string
#define L(s) (s)
#define _(s) Slic3r::I18N::translate(s)
void XMLCALL PrusaFileParser::start_element_handler(void *userData, const char *name, const char **attributes)
{
PrusaFileParser *prusa_parser = (PrusaFileParser *) userData;
if (prusa_parser != nullptr) { prusa_parser->_start_element_handler(name, attributes); }
}
void XMLCALL PrusaFileParser::characters_handler(void *userData, const XML_Char *s, int len)
{
PrusaFileParser *prusa_parser = (PrusaFileParser *) userData;
if (prusa_parser != nullptr) { prusa_parser->_characters_handler(s, len); }
}
bool PrusaFileParser::check_3mf_from_prusa(const std::string filename)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
if (!open_zip_reader(&archive, filename)) {
// throw Slic3r::RuntimeError("Loading 3mf file failed.");
return false;
}
const std::string sub_relationship_file = "3D/_rels/3dmodel.model.rels";
int sub_index = mz_zip_reader_locate_file(&archive, sub_relationship_file.c_str(), nullptr, 0);
if (sub_index == -1) {
const std::string model_file = "3D/3dmodel.model";
int model_file_index = mz_zip_reader_locate_file(&archive, model_file.c_str(), nullptr, 0);
if (model_file_index != -1) {
int depth = 0;
m_parser = XML_ParserCreate(nullptr);
XML_SetUserData(m_parser, (void *) this);
XML_SetElementHandler(m_parser, start_element_handler, nullptr);
XML_SetCharacterDataHandler(m_parser, characters_handler);
mz_zip_archive_file_stat stat;
if (!mz_zip_reader_file_stat(&archive, model_file_index, &stat)) goto EXIT;
void *parser_buffer = XML_GetBuffer(m_parser, (int) stat.m_uncomp_size);
if (parser_buffer == nullptr) goto EXIT;
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t) stat.m_uncomp_size, 0);
if (res == 0) goto EXIT;
XML_ParseBuffer(m_parser, (int) stat.m_uncomp_size, 1);
}
}
EXIT:
close_zip_reader(&archive);
return m_from_prusa;
}
void PrusaFileParser::_characters_handler(const XML_Char *s, int len)
{
if (m_is_application_key) {
std::string str(s, len);
if (!str.empty() && str.find("PrusaSlicer") != std::string::npos) m_from_prusa = true;
}
}
void PrusaFileParser::_start_element_handler(const char *name, const char **attributes)
{
if (::strcmp(name, "metadata") == 0) {
unsigned int num_attributes = (unsigned int) XML_GetSpecifiedAttributeCount(m_parser);
std::string str_name = get_attribute_value_string(attributes, num_attributes, "name");
if (!str_name.empty() && str_name.find("Application") != std::string::npos) { m_is_application_key = true; }
}
}
const char *PrusaFileParser::get_attribute_value_charptr(const char **attributes, unsigned int attributes_size, const char *attribute_key)
{
if ((attributes == nullptr) || (attributes_size == 0) || (attributes_size % 2 != 0) || (attribute_key == nullptr)) return nullptr;
for (unsigned int a = 0; a < attributes_size; a += 2) {
if (::strcmp(attributes[a], attribute_key) == 0) return attributes[a + 1];
}
return nullptr;
}
std::string PrusaFileParser::get_attribute_value_string(const char **attributes, unsigned int attributes_size, const char *attribute_key)
{
const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
return (text != nullptr) ? text : "";
}
ModelVolumeType type_from_string(const std::string &s)
{
// Legacy support
if (s == "1") return ModelVolumeType::PARAMETER_MODIFIER;
// New type (supporting the support enforcers & blockers)
if (s == "ModelPart") return ModelVolumeType::MODEL_PART;
if (s == "NegativeVolume") return ModelVolumeType::NEGATIVE_VOLUME;
if (s == "ParameterModifier") return ModelVolumeType::PARAMETER_MODIFIER;
if (s == "SupportEnforcer") return ModelVolumeType::SUPPORT_ENFORCER;
if (s == "SupportBlocker") return ModelVolumeType::SUPPORT_BLOCKER;
// Default value if invalud type string received.
return ModelVolumeType::MODEL_PART;
}
// Base class with error messages management
class _3MF_Base
@ -668,6 +767,7 @@ namespace Slic3r {
std::string name(stat.m_filename);
std::replace(name.begin(), name.end(), '\\', '/');
/*
if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE)) {
// extract slic3r layer heights profile file
_extract_layer_heights_profile_config_from_archive(archive, stat);
@ -692,7 +792,9 @@ namespace Slic3r {
// extract slic3r layer config ranges file
_extract_custom_gcode_per_print_z_from_archive(archive, stat);
}
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) {
*/
// only read the model config for Prusa 3mf
if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) {
// extract slic3r model config file
if (!_extract_model_config_from_archive(archive, stat, model)) {
close_zip_reader(&archive);
@ -1913,6 +2015,27 @@ namespace Slic3r {
std::string key = get_attribute_value_string(attributes, num_attributes, KEY_ATTR);
std::string value = get_attribute_value_string(attributes, num_attributes, VALUE_ATTR);
// filter the prusa model config keys
std::vector<std::string> valid_keys = {
"name",
"volume_type",
"matrix",
"source_file",
"source_object_id",
"source_volume_id",
"source_offset_x",
"source_offset_y",
"source_offset_z",
"extruder",
"modifier"
};
auto itor = std::find(valid_keys.begin(), valid_keys.end(), key);
if (itor == valid_keys.end()) {
// do nothing if not valid keys
return true;
}
if (type == OBJECT_TYPE)
object->second.metadata.emplace_back(key, value);
else if (type == VOLUME_TYPE) {
@ -2044,7 +2167,7 @@ namespace Slic3r {
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
else if (metadata.key == VOLUME_TYPE_KEY)
volume->set_type(ModelVolume::type_from_string(metadata.value));
volume->set_type(type_from_string(metadata.value));
else if (metadata.key == SOURCE_FILE_KEY)
volume->source.input_file = metadata.value;
else if (metadata.key == SOURCE_OBJECT_ID_KEY)

View file

@ -1,7 +1,30 @@
#ifndef slic3r_Format_3mf_hpp_
#define slic3r_Format_3mf_hpp_
#include "../expat.h"
namespace Slic3r {
// PrusaFileParser is used to check 3mf file is from Prusa
class PrusaFileParser
{
public:
PrusaFileParser() {}
~PrusaFileParser() {}
bool check_3mf_from_prusa(const std::string filename);
void _start_element_handler(const char *name, const char **attributes);
void _characters_handler(const XML_Char *s, int len);
private:
const char *get_attribute_value_charptr(const char **attributes, unsigned int attributes_size, const char *attribute_key);
std::string get_attribute_value_string(const char **attributes, unsigned int attributes_size, const char *attribute_key);
static void XMLCALL start_element_handler(void *userData, const char *name, const char **attributes);
static void XMLCALL characters_handler(void *userData, const XML_Char *s, int len);
private:
bool m_from_prusa = false;
bool m_is_application_key = false;
XML_Parser m_parser;
};
/* The format for saving the SLA points was changing in the past. This enum holds the latest version that is being currently used.
* Examples of the Slic3r_PE_sla_support_points.txt for historically used versions:

View file

@ -33,6 +33,7 @@
// BBS: for segment
#include "MeshBoolean.hpp"
#include "Format/3mf.hpp"
namespace Slic3r {
// BBS initialization of static variables
@ -202,7 +203,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
//BBS: add part plate related logic
// BBS: backup & restore
// Loading model from a file (3MF or AMF), not from a simple geometry file (STL or OBJ).
Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, bool *is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn, BBLProject *project)
Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, En3mfType& out_file_type, LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, Semver* file_version, Import3mfProgressFn proFn, BBLProject *project)
{
assert(config != nullptr);
assert(config_substitutions != nullptr);
@ -210,15 +211,28 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
Model model;
bool result = false;
if (boost::algorithm::iends_with(input_file, ".3mf"))
//BBS: add part plate related logic
// BBS: backup & restore
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_bbl_3mf, file_version, proFn, options, project);
bool is_bbl_3mf;
if (boost::algorithm::iends_with(input_file, ".3mf")) {
PrusaFileParser prusa_file_parser;
if (prusa_file_parser.check_3mf_from_prusa(input_file)) {
// for Prusa 3mf
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, true);
out_file_type = En3mfType::From_Prusa;
} else {
// BBS: add part plate related logic
// BBS: backup & restore
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, &is_bbl_3mf, file_version, proFn, options, project);
}
}
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
result = load_amf(input_file.c_str(), config, config_substitutions, &model, is_bbl_3mf);
result = load_amf(input_file.c_str(), config, config_substitutions, &model, &is_bbl_3mf);
else
throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension.");
if (out_file_type != En3mfType::From_Prusa) {
out_file_type = is_bbl_3mf ? En3mfType::From_BBS : En3mfType::From_Other;
}
if (!result)
throw Slic3r::RuntimeError("Loading of a model file failed.");

View file

@ -577,6 +577,12 @@ enum class ConversionType : int {
CONV_FROM_METER,
};
enum class En3mfType : int {
From_BBS,
From_Prusa,
From_Other
};
class FacetsAnnotation final : public ObjectWithTimestamp {
public:
// Assign the content if the timestamp differs, don't assign an ObjectID.
@ -1280,8 +1286,8 @@ public:
// BBS: backup
static Model read_from_archive(
const std::string& input_file,
DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions,
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr, std::vector<Preset*>* project_presets = nullptr, bool* is_bbl_3mf = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr, BBLProject* project = nullptr);
DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, En3mfType& out_file_type,
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr, std::vector<Preset*>* project_presets = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr, BBLProject* project = nullptr);
// Add a new ModelObject to this Model, generate a new ID for this ModelObject.
ModelObject* add_object();