FIX: issue 551: support to import prusa 3mf file
Change-Id: I6d00fab62304893cc5f1aba0d0a74e4c6194bce9
This commit is contained in:
parent
acc6d70db9
commit
09d5651c39
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.");
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -2642,11 +2642,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
|
||||
// BBS: add part plate related logic
|
||||
PlateDataPtrs plate_data;
|
||||
bool is_bbs_3mf;
|
||||
En3mfType en_3mf_file_type = En3mfType::From_BBS;
|
||||
ConfigSubstitutionContext config_substitutions{ForwardCompatibilitySubstitutionRule::Enable};
|
||||
std::vector<Preset *> project_presets;
|
||||
// BBS: backup & restore
|
||||
model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, &config_substitutions, strategy, &plate_data, &project_presets, &is_bbs_3mf,
|
||||
model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, &config_substitutions, en_3mf_file_type, strategy, &plate_data, &project_presets,
|
||||
&file_version,
|
||||
[this, &dlg, real_filename, progress_percent](int import_stage, int current, int total, bool &cancel) {
|
||||
bool cont = true;
|
||||
|
@ -2657,11 +2657,40 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
|
||||
<< boost::format(", plate_data.size %1%, project_preset.size %2%, is_bbs_3mf %3%, file_version %4% \n") % plate_data.size() %
|
||||
project_presets.size() % is_bbs_3mf % file_version.to_string();
|
||||
project_presets.size() % (en_3mf_file_type == En3mfType::From_BBS) % file_version.to_string();
|
||||
|
||||
// add extruder for prusa model if the number of existing extruders is not enough
|
||||
if (en_3mf_file_type == En3mfType::From_Prusa) {
|
||||
std::set<int> extruderIds;
|
||||
for (ModelObject *o : model.objects) {
|
||||
if (o->config.option("extruder")) extruderIds.insert(o->config.extruder());
|
||||
for (auto volume : o->volumes) {
|
||||
if (volume->config.option("extruder")) extruderIds.insert(volume->config.extruder());
|
||||
for (int extruder : volume->get_extruders()) { extruderIds.insert(extruder); }
|
||||
}
|
||||
}
|
||||
int size = extruderIds.size() == 0 ? 0 : *(extruderIds.rbegin());
|
||||
|
||||
int filament_size = sidebar->combos_filament().size();
|
||||
while (filament_size < 16 && filament_size < size) {
|
||||
int filament_count = filament_size + 1;
|
||||
wxColour new_col = Plater::get_next_color_for_filament();
|
||||
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
|
||||
wxGetApp().plater()->on_filaments_change(filament_count);
|
||||
++filament_size;
|
||||
}
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
|
||||
}
|
||||
|
||||
// BBS: version check
|
||||
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
|
||||
if (load_config && (file_version.maj() != app_version.maj())) {
|
||||
if (en_3mf_file_type == En3mfType::From_Prusa) {
|
||||
// do not reset the model config
|
||||
load_config = false;
|
||||
show_info(q, _L("the 3mf is not compatible, load geometry data only!"), _L("Incompatible 3mf"));
|
||||
}
|
||||
else if (load_config && (file_version.maj() != app_version.maj())) {
|
||||
// version mismatch, only load geometries
|
||||
load_config = false;
|
||||
if (!load_model) {
|
||||
|
|
Loading…
Reference in New Issue