Skip to content

Commit

Permalink
Support 3MF Production Extension
Browse files Browse the repository at this point in the history
  • Loading branch information
cmguo committed Feb 2, 2024
1 parent 97c3679 commit baf82c8
Showing 1 changed file with 169 additions and 23 deletions.
192 changes: 169 additions & 23 deletions src/libslic3r/Format/3mf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
const std::string CUT_INFORMATION_FILE = "Metadata/Prusa_Slicer_cut_information.xml";

static constexpr const char *RELATIONSHIP_TAG = "Relationship";

static constexpr const char* TARGET_ATTR = "Target";
static constexpr const char* RELS_TYPE_ATTR = "Type";

static constexpr const char* MODEL_TAG = "model";
static constexpr const char* RESOURCES_TAG = "resources";
static constexpr const char* OBJECT_TAG = "object";
Expand Down Expand Up @@ -118,6 +123,7 @@ static constexpr const char* Z_ATTR = "z";
static constexpr const char* V1_ATTR = "v1";
static constexpr const char* V2_ATTR = "v2";
static constexpr const char* V3_ATTR = "v3";
static constexpr const char* PPATH_ATTR = "p:path";
static constexpr const char* OBJECTID_ATTR = "objectid";
static constexpr const char* TRANSFORM_ATTR = "transform";
static constexpr const char* PRINTABLE_ATTR = "printable";
Expand Down Expand Up @@ -336,18 +342,21 @@ namespace Slic3r {

class _3MF_Importer : public _3MF_Base
{
typedef std::pair<std::string, int> PathId;

struct Component
{
int object_id;
PathId object_id;
std::string path;
Transform3d transform;

explicit Component(int object_id)
explicit Component(PathId object_id)
: object_id(object_id)
, transform(Transform3d::Identity())
{
}

Component(int object_id, const Transform3d& transform)
Component(PathId object_id, const Transform3d &transform)
: object_id(object_id)
, transform(transform)
{
Expand Down Expand Up @@ -465,11 +474,11 @@ namespace Slic3r {
};

// Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
typedef std::map<int, int> IdToModelObjectMap;
typedef std::map<int, ComponentsList> IdToAliasesMap;
typedef std::map<PathId, int> IdToModelObjectMap;
typedef std::map<PathId, ComponentsList> IdToAliasesMap;
typedef std::vector<Instance> InstancesList;
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, Geometry> IdToGeometryMap;
typedef std::map<PathId, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
Expand Down Expand Up @@ -509,6 +518,8 @@ namespace Slic3r {
std::string m_curr_metadata_name;
std::string m_curr_characters;
std::string m_name;
std::string m_start_part_path;
std::string m_model_path;

public:
_3MF_Importer();
Expand All @@ -533,6 +544,7 @@ namespace Slic3r {

bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
bool _is_svg_shape_file(const std::string &filename) const;
bool _extract_relationships_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
Expand All @@ -546,6 +558,11 @@ namespace Slic3r {
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
void _extract_embossed_svg_shape_file(const std::string &filename, mz_zip_archive &archive, const mz_zip_archive_file_stat &stat);

// handlers to parse the .rels file
void _handle_start_relationships_element(const char* name, const char** attributes);
void _handle_end_relationships_element(const char* name);
bool _handle_start_relationship(const char **attributes, unsigned int num_attributes);

// handlers to parse the .model file
void _handle_start_model_xml_element(const char* name, const char** attributes);
void _handle_end_model_xml_element(const char* name);
Expand Down Expand Up @@ -597,7 +614,7 @@ namespace Slic3r {
bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes);
bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes);

bool _create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
bool _create_object_instance(PathId object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);

void _apply_transform(ModelInstance& instance, const Transform3d& transform);

Expand All @@ -617,6 +634,10 @@ namespace Slic3r {

bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);

// callbacks to parse the .rels file
static void XMLCALL _handle_start_relationships_element(void *userData, const char *name, const char **attributes);
static void XMLCALL _handle_end_relationships_element(void* userData, const char* name);

// callbacks to parse the .model file
static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name);
Expand Down Expand Up @@ -705,7 +726,14 @@ namespace Slic3r {

m_name = boost::filesystem::path(filename).stem().string();

int index = mz_zip_reader_locate_file(&archive, RELATIONSHIPS_FILE.c_str(), nullptr, 0);
if (index < 0 || !mz_zip_reader_file_stat(&archive, index, &stat))
return false;
if (!_extract_relationships_from_archive(archive, stat))
return false;

// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
mz_zip_archive_file_stat start_part_stat { -1 };
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) {
std::string name(stat.m_filename);
Expand All @@ -715,6 +743,11 @@ namespace Slic3r {
try
{
// valid model name -> extract model
m_model_path = "/" + name;
if (m_model_path == m_start_part_path) {
start_part_stat = stat;
continue;
}
if (!_extract_model_from_archive(archive, stat)) {
close_zip_reader(&archive);
add_error("Archive does not contain a valid model");
Expand All @@ -731,6 +764,21 @@ namespace Slic3r {
}
}

if (start_part_stat.m_file_index >= 0) {
try {
m_model_path.clear();
if (!_extract_model_from_archive(archive, start_part_stat)) {
close_zip_reader(&archive);
add_error("Archive does not contain a valid model");
return false;
}
} catch (const std::exception &e) {
// ensure the zip archive is closed and rethrow the exception
close_zip_reader(&archive);
throw Slic3r::FileIOError(e.what());
}
}

// we then loop again the entries to read other files stored in the archive
for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) {
Expand Down Expand Up @@ -863,7 +911,7 @@ namespace Slic3r {
ObjectMetadata::VolumeMetadataList volumes;
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;

IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
if (obj_metadata != m_objects_metadata.end()) {
// config data has been found, this model was saved using slic3r pe

Expand Down Expand Up @@ -954,6 +1002,47 @@ namespace Slic3r {
return boost::starts_with(name, MODEL_FOLDER) && boost::ends_with(name, ".svg");
}

bool _3MF_Importer::_extract_relationships_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
{
if (stat.m_uncomp_size == 0) {
add_error("Found invalid size");
return false;
}

_destroy_xml_parser();

m_xml_parser = XML_ParserCreate(nullptr);
if (m_xml_parser == nullptr) {
add_error("Unable to create parser");
return false;
}

XML_SetUserData(m_xml_parser, (void *) this);
XML_SetElementHandler(m_xml_parser, _handle_start_relationships_element, _handle_end_relationships_element);

void *parser_buffer = XML_GetBuffer(m_xml_parser, (int) stat.m_uncomp_size);
if (parser_buffer == nullptr) {
add_error("Unable to create buffer");
return false;
}

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) {
add_error("Error while reading config data to buffer");
return false;
}

if (!XML_ParseBuffer(m_xml_parser, (int) stat.m_uncomp_size, 1)) {
char error_buf[1024];
::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)),
(int) XML_GetCurrentLineNumber(m_xml_parser));
add_error(error_buf);
return false;
}

return true;
}

bool _3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
{
if (stat.m_uncomp_size == 0) {
Expand Down Expand Up @@ -1511,7 +1600,58 @@ namespace Slic3r {
}
}

void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes)
void XMLCALL _3MF_Importer::_handle_start_relationships_element(void *userData, const char *name, const char **attributes)
{
_3MF_Importer *importer = (_3MF_Importer *) userData;
if (importer != nullptr)
importer->_handle_start_relationships_element(name, attributes);
}

void XMLCALL _3MF_Importer::_handle_end_relationships_element(void *userData, const char *name)
{
_3MF_Importer *importer = (_3MF_Importer *) userData;
if (importer != nullptr)
importer->_handle_end_relationships_element(name);
}

void _3MF_Importer::_handle_start_relationships_element(const char *name, const char **attributes)
{
if (m_xml_parser == nullptr)
return;

bool res = true;
unsigned int num_attributes = (unsigned int) XML_GetSpecifiedAttributeCount(m_xml_parser);

if (::strcmp(RELATIONSHIP_TAG, name) == 0)
res = _handle_start_relationship(attributes, num_attributes);

m_curr_characters.clear();
if (!res)
_stop_xml_parser();
}

void _3MF_Importer::_handle_end_relationships_element(const char *name)
{
if (m_xml_parser == nullptr)
return;

bool res = true;

if (!res)
_stop_xml_parser();
}

bool _3MF_Importer::_handle_start_relationship(const char **attributes, unsigned int num_attributes)
{
std::string path = get_attribute_value_string(attributes, num_attributes, TARGET_ATTR);
std::string type = get_attribute_value_string(attributes, num_attributes, RELS_TYPE_ATTR);
if (boost::starts_with(type, "http://schemas.microsoft.com/3dmanufacturing/") && boost::ends_with(type, "3dmodel")) {
m_start_part_path = path;
}
return true;
}

void _3MF_Importer::_handle_start_model_xml_element(const char *name, const char **attributes)
{
if (m_xml_parser == nullptr)
return;
Expand Down Expand Up @@ -1650,6 +1790,9 @@ namespace Slic3r {

bool _3MF_Importer::_handle_end_model()
{
if (!m_model_path.empty())
return true;

// deletes all non-built or non-instanced objects
for (const IdToModelObjectMap::value_type& object : m_objects) {
if (object.second >= int(m_model->objects.size())) {
Expand Down Expand Up @@ -1718,33 +1861,34 @@ namespace Slic3r {
bool _3MF_Importer::_handle_end_object()
{
if (m_curr_object.object != nullptr) {
PathId object_id{m_model_path, m_curr_object.id};
if (m_curr_object.geometry.empty()) {
// no geometry defined
// remove the object from the model
m_model->delete_object(m_curr_object.object);

if (m_curr_object.components.empty()) {
// no components defined -> invalid object, delete it
IdToModelObjectMap::iterator object_item = m_objects.find(m_curr_object.id);
IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
if (object_item != m_objects.end())
m_objects.erase(object_item);

IdToAliasesMap::iterator alias_item = m_objects_aliases.find(m_curr_object.id);
IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id);
if (alias_item != m_objects_aliases.end())
m_objects_aliases.erase(alias_item);
}
else
// adds components to aliases
m_objects_aliases.insert({ m_curr_object.id, m_curr_object.components });
m_objects_aliases.insert({ object_id, m_curr_object.components });
}
else {
// geometry defined, store it for later use
m_geometries.insert({ m_curr_object.id, std::move(m_curr_object.geometry) });
m_geometries.insert({ object_id, std::move(m_curr_object.geometry) });

// stores the object for later use
if (m_objects.find(m_curr_object.id) == m_objects.end()) {
m_objects.insert({ m_curr_object.id, m_curr_object.model_object_idx });
m_objects_aliases.insert({ m_curr_object.id, { 1, Component(m_curr_object.id) } }); // aliases itself
if (m_objects.find(object_id) == m_objects.end()) {
m_objects.insert({ object_id, m_curr_object.model_object_idx });
m_objects_aliases.insert({object_id, {1, Component(object_id)}}); // aliases itself
}
else {
add_error("Found object with duplicate id");
Expand Down Expand Up @@ -1855,19 +1999,21 @@ namespace Slic3r {

bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes)
{
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
std::string path = get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));

IdToModelObjectMap::iterator object_item = m_objects.find(object_id);

PathId path_id { path, object_id };
IdToModelObjectMap::iterator object_item = m_objects.find(path_id);
if (object_item == m_objects.end()) {
IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id);
IdToAliasesMap::iterator alias_item = m_objects_aliases.find(path_id);
if (alias_item == m_objects_aliases.end()) {
add_error("Found component with invalid object id");
return false;
}
}

m_curr_object.components.emplace_back(object_id, transform);
m_curr_object.components.emplace_back(path_id, transform);

return true;
}
Expand Down Expand Up @@ -1903,7 +2049,7 @@ namespace Slic3r {
Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);

return _create_object_instance(object_id, transform, printable, 1);
return _create_object_instance({m_model_path, object_id}, transform, printable, 1);
}

bool _3MF_Importer::_handle_end_item()
Expand Down Expand Up @@ -2059,7 +2205,7 @@ namespace Slic3r {
return true;
}

bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
bool _3MF_Importer::_create_object_instance(PathId object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
{
static const unsigned int MAX_RECURSIONS = 10;

Expand Down

0 comments on commit baf82c8

Please sign in to comment.