diff options
Diffstat (limited to 'src/libslic3r/Format/3mf.cpp')
-rw-r--r-- | src/libslic3r/Format/3mf.cpp | 324 |
1 files changed, 192 insertions, 132 deletions
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index efeb23700..d021f0f6d 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1,11 +1,11 @@ #include "../libslic3r.h" +#include "../Exception.hpp" #include "../Model.hpp" #include "../Utils.hpp" #include "../GCode.hpp" #include "../Geometry.hpp" -#if ENABLE_THUMBNAIL_GENERATOR #include "../GCode/ThumbnailData.hpp" -#endif // ENABLE_THUMBNAIL_GENERATOR +#include "../Time.hpp" #include "../I18N.hpp" @@ -47,9 +47,7 @@ const std::string MODEL_EXTENSION = ".model"; const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA const std::string CONTENT_TYPES_FILE = "[Content_Types].xml"; const std::string RELATIONSHIPS_FILE = "_rels/.rels"; -#if ENABLE_THUMBNAIL_GENERATOR const std::string THUMBNAIL_FILE = "Metadata/thumbnail.png"; -#endif // ENABLE_THUMBNAIL_GENERATOR const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config"; const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config"; const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt"; @@ -58,56 +56,59 @@ const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_poin 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 char* MODEL_TAG = "model"; -const char* RESOURCES_TAG = "resources"; -const char* OBJECT_TAG = "object"; -const char* MESH_TAG = "mesh"; -const char* VERTICES_TAG = "vertices"; -const char* VERTEX_TAG = "vertex"; -const char* TRIANGLES_TAG = "triangles"; -const char* TRIANGLE_TAG = "triangle"; -const char* COMPONENTS_TAG = "components"; -const char* COMPONENT_TAG = "component"; -const char* BUILD_TAG = "build"; -const char* ITEM_TAG = "item"; -const char* METADATA_TAG = "metadata"; - -const char* CONFIG_TAG = "config"; -const char* VOLUME_TAG = "volume"; - -const char* UNIT_ATTR = "unit"; -const char* NAME_ATTR = "name"; -const char* TYPE_ATTR = "type"; -const char* ID_ATTR = "id"; -const char* X_ATTR = "x"; -const char* Y_ATTR = "y"; -const char* Z_ATTR = "z"; -const char* V1_ATTR = "v1"; -const char* V2_ATTR = "v2"; -const char* V3_ATTR = "v3"; -const char* OBJECTID_ATTR = "objectid"; -const char* TRANSFORM_ATTR = "transform"; -const char* PRINTABLE_ATTR = "printable"; -const char* INSTANCESCOUNT_ATTR = "instances_count"; - -const char* KEY_ATTR = "key"; -const char* VALUE_ATTR = "value"; -const char* FIRST_TRIANGLE_ID_ATTR = "firstid"; -const char* LAST_TRIANGLE_ID_ATTR = "lastid"; - -const char* OBJECT_TYPE = "object"; -const char* VOLUME_TYPE = "volume"; - -const char* NAME_KEY = "name"; -const char* MODIFIER_KEY = "modifier"; -const char* VOLUME_TYPE_KEY = "volume_type"; -const char* MATRIX_KEY = "matrix"; -const char* SOURCE_FILE_KEY = "source_file"; -const char* SOURCE_OBJECT_ID_KEY = "source_object_id"; -const char* SOURCE_VOLUME_ID_KEY = "source_volume_id"; -const char* SOURCE_OFFSET_X_KEY = "source_offset_x"; -const char* SOURCE_OFFSET_Y_KEY = "source_offset_y"; -const char* SOURCE_OFFSET_Z_KEY = "source_offset_z"; +static constexpr const char* MODEL_TAG = "model"; +static constexpr const char* RESOURCES_TAG = "resources"; +static constexpr const char* OBJECT_TAG = "object"; +static constexpr const char* MESH_TAG = "mesh"; +static constexpr const char* VERTICES_TAG = "vertices"; +static constexpr const char* VERTEX_TAG = "vertex"; +static constexpr const char* TRIANGLES_TAG = "triangles"; +static constexpr const char* TRIANGLE_TAG = "triangle"; +static constexpr const char* COMPONENTS_TAG = "components"; +static constexpr const char* COMPONENT_TAG = "component"; +static constexpr const char* BUILD_TAG = "build"; +static constexpr const char* ITEM_TAG = "item"; +static constexpr const char* METADATA_TAG = "metadata"; + +static constexpr const char* CONFIG_TAG = "config"; +static constexpr const char* VOLUME_TAG = "volume"; + +static constexpr const char* UNIT_ATTR = "unit"; +static constexpr const char* NAME_ATTR = "name"; +static constexpr const char* TYPE_ATTR = "type"; +static constexpr const char* ID_ATTR = "id"; +static constexpr const char* X_ATTR = "x"; +static constexpr const char* Y_ATTR = "y"; +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* OBJECTID_ATTR = "objectid"; +static constexpr const char* TRANSFORM_ATTR = "transform"; +static constexpr const char* PRINTABLE_ATTR = "printable"; +static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count"; +static constexpr const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports"; +static constexpr const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam"; + +static constexpr const char* KEY_ATTR = "key"; +static constexpr const char* VALUE_ATTR = "value"; +static constexpr const char* FIRST_TRIANGLE_ID_ATTR = "firstid"; +static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid"; + +static constexpr const char* OBJECT_TYPE = "object"; +static constexpr const char* VOLUME_TYPE = "volume"; + +static constexpr const char* NAME_KEY = "name"; +static constexpr const char* MODIFIER_KEY = "modifier"; +static constexpr const char* VOLUME_TYPE_KEY = "volume_type"; +static constexpr const char* MATRIX_KEY = "matrix"; +static constexpr const char* SOURCE_FILE_KEY = "source_file"; +static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id"; +static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id"; +static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x"; +static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y"; +static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z"; +static constexpr const char* SOURCE_IN_INCHES = "source_in_inches"; const unsigned int VALID_OBJECT_TYPES_COUNT = 1; const char* VALID_OBJECT_TYPES[] = @@ -124,11 +125,11 @@ const char* INVALID_OBJECT_TYPES[] = "other" }; -class version_error : public std::runtime_error +class version_error : public Slic3r::FileIOError { public: - version_error(const std::string& what_arg) : std::runtime_error(what_arg) {} - version_error(const char* what_arg) : std::runtime_error(what_arg) {} + version_error(const std::string& what_arg) : Slic3r::FileIOError(what_arg) {} + version_error(const char* what_arg) : Slic3r::FileIOError(what_arg) {} }; const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key) @@ -286,6 +287,8 @@ namespace Slic3r { { std::vector<float> vertices; std::vector<unsigned int> triangles; + std::vector<std::string> custom_supports; + std::vector<std::string> custom_seam; bool empty() { @@ -296,6 +299,8 @@ namespace Slic3r { { vertices.clear(); triangles.clear(); + custom_supports.clear(); + custom_seam.clear(); } }; @@ -604,7 +609,7 @@ namespace Slic3r { { // ensure the zip archive is closed and rethrow the exception close_zip_reader(&archive); - throw std::runtime_error(e.what()); + throw Slic3r::FileIOError(e.what()); } } } @@ -679,23 +684,23 @@ namespace Slic3r { // m_layer_heights_profiles are indexed by a 1 based model object index. IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1); if (obj_layer_heights_profile != m_layer_heights_profiles.end()) - model_object->layer_height_profile = obj_layer_heights_profile->second; + model_object->layer_height_profile.set(std::move(obj_layer_heights_profile->second)); // m_layer_config_ranges are indexed by a 1 based model object index. IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1); if (obj_layer_config_ranges != m_layer_config_ranges.end()) - model_object->layer_config_ranges = obj_layer_config_ranges->second; + model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second); // m_sla_support_points are indexed by a 1 based model object index. IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1); if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) { - model_object->sla_support_points = obj_sla_support_points->second; + model_object->sla_support_points = std::move(obj_sla_support_points->second); model_object->sla_points_status = sla::PointsStatus::UserModified; } IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1); if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) { - model_object->sla_drain_holes = obj_drain_holes->second; + model_object->sla_drain_holes = std::move(obj_drain_holes->second); } IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); @@ -777,7 +782,7 @@ namespace Slic3r { { char error_buf[1024]; ::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", XML_ErrorString(XML_GetErrorCode(data->parser)), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser)); - throw std::runtime_error(error_buf); + throw Slic3r::FileIOError(error_buf); } return n; @@ -786,7 +791,7 @@ namespace Slic3r { catch (const version_error& e) { // rethrow the exception - throw std::runtime_error(e.what()); + throw Slic3r::FileIOError(e.what()); } catch (std::exception& e) { @@ -930,7 +935,7 @@ namespace Slic3r { double max_z = range_tree.get<double>("<xmlattr>.max_z"); // get Z range information - DynamicPrintConfig& config = config_ranges[{ min_z, max_z }]; + DynamicPrintConfig config; for (const auto& option : range_tree) { @@ -941,10 +946,12 @@ namespace Slic3r { config.set_deserialize(opt_key, value); } + + config_ranges[{ min_z, max_z }].assign_config(std::move(config)); } if (!config_ranges.empty()) - m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, config_ranges)); + m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, std::move(config_ranges))); } } } @@ -1114,6 +1121,15 @@ namespace Slic3r { float(std::atof(object_data_points[i+6].c_str())), float(std::atof(object_data_points[i+7].c_str()))); } + + // The holes are saved elevated above the mesh and deeper (bad idea indeed). + // This is retained for compatibility. + // Place the hole to the mesh and make it shallower to compensate. + // The offset is 1 mm above the mesh. + for (sla::DrainHole& hole : sla_drain_holes) { + hole.pos += hole.normal.normalized(); + hole.height -= 1.f; + } if (!sla_drain_holes.empty()) m_sla_drain_holes.insert(IdToSlaDrainHolesMap::value_type(object_id, sla_drain_holes)); @@ -1201,13 +1217,32 @@ namespace Slic3r { } if (code.first != "code") continue; + pt::ptree tree = code.second; - double print_z = tree.get<double> ("<xmlattr>.print_z" ); - std::string gcode = tree.get<std::string> ("<xmlattr>.gcode" ); - int extruder = tree.get<int> ("<xmlattr>.extruder" ); - std::string color = tree.get<std::string> ("<xmlattr>.color" ); + double print_z = tree.get<double> ("<xmlattr>.print_z" ); + int extruder = tree.get<int> ("<xmlattr>.extruder"); + std::string color = tree.get<std::string> ("<xmlattr>.color" ); - m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}) ; + CustomGCode::Type type; + std::string extra; + if (tree.find("type") == tree.not_found()) + { + // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer + // read old data ... + std::string gcode = tree.get<std::string> ("<xmlattr>.gcode"); + // ... and interpret them to the new data + type = gcode == "M600" ? CustomGCode::ColorChange : + gcode == "M601" ? CustomGCode::PausePrint : + gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom; + extra = type == CustomGCode::PausePrint ? color : + type == CustomGCode::Custom ? gcode : ""; + } + else + { + type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type")); + extra = tree.get<std::string>("<xmlattr>.extra"); + } + m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ; } } } @@ -1523,6 +1558,9 @@ namespace Slic3r { m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR)); m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR)); m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR)); + + m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR)); + m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR)); return true; } @@ -1722,7 +1760,7 @@ namespace Slic3r { } // Added because of github #3435, currently not used by PrusaSlicer - int instances_count_id = get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR); + // int instances_count_id = get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR); m_objects_metadata.insert(IdToMetadataMap::value_type(object_id, ObjectMetadata())); m_curr_config.object_id = object_id; @@ -1856,6 +1894,18 @@ namespace Slic3r { volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object); volume->calculate_convex_hull(); + // recreate custom supports and seam from previously loaded attribute + for (unsigned i=0; i<triangles_count; ++i) { + size_t index = src_start_id/3 + i; + assert(index < geometry.custom_supports.size()); + assert(index < geometry.custom_seam.size()); + if (! geometry.custom_supports[index].empty()) + volume->supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]); + if (! geometry.custom_seam[index].empty()) + volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]); + } + + // apply the remaining volume's metadata for (const Metadata& metadata : volume_data.metadata) { @@ -1877,6 +1927,8 @@ namespace Slic3r { volume->source.mesh_offset(1) = ::atof(metadata.value.c_str()); else if (metadata.key == SOURCE_OFFSET_Z_KEY) volume->source.mesh_offset(2) = ::atof(metadata.value.c_str()); + else if (metadata.key == SOURCE_IN_INCHES) + volume->source.is_converted_from_inches = metadata.value == "1"; else volume->config.set_deserialize(metadata.key, metadata.value); } @@ -1969,24 +2021,14 @@ namespace Slic3r { bool m_fullpath_sources{ true }; public: -#if ENABLE_THUMBNAIL_GENERATOR bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); -#else - bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources); -#endif // ENABLE_THUMBNAIL_GENERATOR private: -#if ENABLE_THUMBNAIL_GENERATOR bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data); -#else - bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); -#endif // ENABLE_THUMBNAIL_GENERATOR bool _add_content_types_file_to_archive(mz_zip_archive& archive); -#if ENABLE_THUMBNAIL_GENERATOR bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data); -#endif // ENABLE_THUMBNAIL_GENERATOR bool _add_relationships_file_to_archive(mz_zip_archive& archive); - bool _add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data); + bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data); bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets); bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets); bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); @@ -1996,29 +2038,17 @@ namespace Slic3r { bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data); - bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model); + bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); }; -#if ENABLE_THUMBNAIL_GENERATOR bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) { clear_errors(); m_fullpath_sources = fullpath_sources; return _save_model_to_file(filename, model, config, thumbnail_data); } -#else - bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources) - { - clear_errors(); - return _save_model_to_file(filename, model, config); - } -#endif // ENABLE_THUMBNAIL_GENERATOR -#if ENABLE_THUMBNAIL_GENERATOR bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) -#else - bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config) -#endif // ENABLE_THUMBNAIL_GENERATOR { mz_zip_archive archive; mz_zip_zero_struct(&archive); @@ -2037,7 +2067,6 @@ namespace Slic3r { return false; } -#if ENABLE_THUMBNAIL_GENERATOR if ((thumbnail_data != nullptr) && thumbnail_data->is_valid()) { // Adds the file Metadata/thumbnail.png. @@ -2048,7 +2077,6 @@ namespace Slic3r { return false; } } -#endif // ENABLE_THUMBNAIL_GENERATOR // Adds relationships file ("_rels/.rels"). // The content of this file is the same for each PrusaSlicer 3mf. @@ -2063,7 +2091,7 @@ namespace Slic3r { // Adds model file ("3D/3dmodel.model"). // This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes. IdToObjectDataMap objects_data; - if (!_add_model_file_to_archive(archive, model, objects_data)) + if (!_add_model_file_to_archive(filename, archive, model, objects_data)) { close_zip_writer(&archive); boost::filesystem::remove(filename); @@ -2110,7 +2138,7 @@ namespace Slic3r { // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"). // All custom gcode per height of whole Model are stored here - if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model)) + if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config)) { close_zip_writer(&archive); boost::filesystem::remove(filename); @@ -2160,9 +2188,7 @@ namespace Slic3r { stream << "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n"; stream << " <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" />\n"; stream << " <Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" />\n"; -#if ENABLE_THUMBNAIL_GENERATOR stream << " <Default Extension=\"png\" ContentType=\"image/png\" />\n"; -#endif // ENABLE_THUMBNAIL_GENERATOR stream << "</Types>"; std::string out = stream.str(); @@ -2176,7 +2202,6 @@ namespace Slic3r { return true; } -#if ENABLE_THUMBNAIL_GENERATOR bool _3MF_Exporter::_add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data) { bool res = false; @@ -2194,7 +2219,6 @@ namespace Slic3r { return res; } -#endif // ENABLE_THUMBNAIL_GENERATOR bool _3MF_Exporter::_add_relationships_file_to_archive(mz_zip_archive& archive) { @@ -2202,9 +2226,7 @@ namespace Slic3r { stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; stream << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n"; stream << " <Relationship Target=\"/" << MODEL_FILE << "\" Id=\"rel-1\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\" />\n"; -#if ENABLE_THUMBNAIL_GENERATOR stream << " <Relationship Target=\"/" << THUMBNAIL_FILE << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\" />\n"; -#endif // ENABLE_THUMBNAIL_GENERATOR stream << "</Relationships>"; std::string out = stream.str(); @@ -2218,7 +2240,7 @@ namespace Slic3r { return true; } - bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data) + bool _3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data) { std::stringstream stream; // https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10 @@ -2229,6 +2251,19 @@ namespace Slic3r { stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n"; stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n"; + std::string name = xml_escape(boost::filesystem::path(filename).stem().string()); + stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"Rating\">" << "</" << METADATA_TAG << ">\n"; + std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc()); + // keep only the date part of the string + date = date.substr(0, 10); + stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n"; + stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n"; stream << " <" << RESOURCES_TAG << ">\n"; // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects). @@ -2331,9 +2366,9 @@ namespace Slic3r { continue; if (!volume->mesh().repaired) - throw std::runtime_error("store_3mf() requires repair()"); + throw Slic3r::FileIOError("store_3mf() requires repair()"); if (!volume->mesh().has_shared_vertices()) - throw std::runtime_error("store_3mf() requires shared vertices"); + throw Slic3r::FileIOError("store_3mf() requires shared vertices"); volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; @@ -2377,13 +2412,22 @@ namespace Slic3r { triangles_count += (int)its.indices.size(); volume_it->second.last_triangle_id = triangles_count - 1; - for (size_t i = 0; i < its.indices.size(); ++ i) + for (int i = 0; i < int(its.indices.size()); ++ i) { stream << " <" << TRIANGLE_TAG << " "; for (int j = 0; j < 3; ++j) { stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" "; } + + std::string custom_supports_data_string = volume->supported_facets.get_triangle_as_string(i); + if (! custom_supports_data_string.empty()) + stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" "; + + std::string custom_seam_data_string = volume->seam_facets.get_triangle_as_string(i); + if (! custom_seam_data_string.empty()) + stream << CUSTOM_SEAM_ATTR << "=\"" << custom_seam_data_string << "\" "; + stream << "/>\n"; } } @@ -2433,7 +2477,7 @@ namespace Slic3r { for (const ModelObject* object : model.objects) { ++count; - const std::vector<double> &layer_height_profile = object->layer_height_profile; + const std::vector<double>& layer_height_profile = object->layer_height_profile.get(); if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0)) { sprintf(buffer, "object_id=%d|", count); @@ -2488,7 +2532,7 @@ namespace Slic3r { range_tree.put("<xmlattr>.max_z", range.first.second); // store range configuration - const DynamicPrintConfig& config = range.second; + const ModelConfig& config = range.second; for (const std::string& opt_key : config.keys()) { pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key)); @@ -2574,7 +2618,18 @@ namespace Slic3r { for (const ModelObject* object : model.objects) { ++count; - auto& drain_holes = object->sla_drain_holes; + sla::DrainHoles drain_holes = object->sla_drain_holes; + + // The holes were placed 1mm above the mesh in the first implementation. + // This was a bad idea and the reference point was changed in 2.3 so + // to be on the mesh exactly. The elevated position is still saved + // in 3MFs for compatibility reasons. + for (sla::DrainHole& hole : drain_holes) { + hole.pos -= hole.normal.normalized(); + hole.height += 1.f; + } + + if (!drain_holes.empty()) { out += string_printf(fmt, count); @@ -2697,15 +2752,20 @@ namespace Slic3r { stream << "\"/>\n"; // stores volume's source data - if (!volume->source.input_file.empty()) { std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string()); - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n"; - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n"; - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n"; - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n"; - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n"; - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n"; + std::string prefix = std::string(" <") + METADATA_TAG + " " + TYPE_ATTR + "=\"" + VOLUME_TYPE + "\" " + KEY_ATTR + "=\""; + if (! volume->source.input_file.empty()) { + stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n"; + stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n"; + stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n"; + stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n"; + stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n"; + stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n"; + stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n"; + } + if (volume->source.is_converted_from_inches) + stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n"; } // stores volume's config data @@ -2736,7 +2796,7 @@ namespace Slic3r { return true; } -bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model) +bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config) { std::string out = ""; @@ -2748,11 +2808,20 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) { pt::ptree& code_tree = main_tree.add("code", ""); - // store minX and maxZ + + // store data of custom_gcode_per_print_z code_tree.put("<xmlattr>.print_z" , code.print_z ); - code_tree.put("<xmlattr>.gcode" , code.gcode ); + code_tree.put("<xmlattr>.type" , static_cast<int>(code.type)); code_tree.put("<xmlattr>.extruder" , code.extruder ); code_tree.put("<xmlattr>.color" , code.color ); + code_tree.put("<xmlattr>.extra" , code.extra ); + + // add gcode field data for the old version of the PrusaSlicer + std::string gcode = code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") : + code.type == CustomGCode::PausePrint ? config->opt_string("pause_print_gcode") : + code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : + code.type == CustomGCode::ToolChange ? "tool_change" : code.extra; + code_tree.put("<xmlattr>.gcode" , gcode ); } pt::ptree& mode_tree = main_tree.add("mode", ""); @@ -2795,22 +2864,13 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c return res; } -#if ENABLE_THUMBNAIL_GENERATOR bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) -#else -bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources) -#endif // ENABLE_THUMBNAIL_GENERATOR { if ((path == nullptr) || (model == nullptr)) return false; _3MF_Exporter exporter; -#if ENABLE_THUMBNAIL_GENERATOR bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data); -#else - bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources); -#endif // ENABLE_THUMBNAIL_GENERATOR - if (!res) exporter.log_errors(); |