From cf144da4fe8dc0797096b4c352db2bde7cc0c96f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 18 Aug 2022 13:57:25 +0200 Subject: [PATCH] Cut WIP: Import/Export cut information to/from .3mf file + Fixed a crash during change object selection, when CutGizmo is On + Fixed Undo/Redo (was accidentally broken with 7912613dc8e11db2ddea8018ce16b26a01756a3b) --- src/libslic3r/Format/3mf.cpp | 110 +++++++++++++++++++++++++++ src/libslic3r/Model.hpp | 2 +- src/libslic3r/ObjectID.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 23 +++--- src/slic3r/GUI/Plater.cpp | 2 + 5 files changed, 131 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 5caff6d3a7..4251110c41 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -77,6 +77,7 @@ const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; 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* MODEL_TAG = "model"; static constexpr const char* RESOURCES_TAG = "resources"; @@ -416,6 +417,7 @@ namespace Slic3r { typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; + typedef std::map IdToCutObjectIdMap; typedef std::map> IdToSlaSupportPointsMap; typedef std::map> IdToSlaDrainHolesMap; @@ -443,6 +445,7 @@ namespace Slic3r { IdToGeometryMap m_geometries; CurrentConfig m_curr_config; IdToMetadataMap m_objects_metadata; + IdToCutObjectIdMap m_cut_object_ids; IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerConfigRangesMap m_layer_config_ranges; IdToSlaSupportPointsMap m_sla_support_points; @@ -474,6 +477,7 @@ namespace Slic3r { bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions); 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); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); @@ -676,6 +680,10 @@ namespace Slic3r { // extract slic3r layer heights profile file _extract_layer_heights_profile_config_from_archive(archive, stat); } + else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) { + // extract slic3r layer config ranges file + _extract_cut_information_from_archive(archive, stat, config_substitutions); + } else if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE)) { // extract slic3r layer config ranges file _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions); @@ -766,6 +774,11 @@ namespace Slic3r { return false; } + // m_cut_object_ids are indexed by a 1 based model object index. + IdToCutObjectIdMap::iterator cut_object_id = m_cut_object_ids.find(object.second + 1); + if (cut_object_id != m_cut_object_ids.end()) + model_object->cut_id = std::move(cut_object_id->second); + // 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()) @@ -944,6 +957,48 @@ namespace Slic3r { return true; } + void _3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions) + { + if (stat.m_uncomp_size > 0) { + std::string buffer((size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) { + add_error("Error while reading cut information data to buffer"); + return; + } + + std::istringstream iss(buffer); // wrap returned xml to istringstream + pt::ptree objects_tree; + pt::read_xml(iss, objects_tree); + + for (const auto& object : objects_tree.get_child("objects")) { + pt::ptree object_tree = object.second; + int obj_idx = object_tree.get(".id", -1); + if (obj_idx <= 0) { + add_error("Found invalid object id"); + continue; + } + + IdToCutObjectIdMap::iterator object_item = m_cut_object_ids.find(obj_idx); + if (object_item != m_cut_object_ids.end()) { + add_error("Found duplicated cut_object_id"); + continue; + } + + for (const auto& obj_cut_id : object_tree) { + if (obj_cut_id.first != "cut_id") + continue; + pt::ptree cut_id_tree = obj_cut_id.second; + ObjectID obj_id(cut_id_tree.get(".id")); + CutObjectBase cut_id(ObjectID(cut_id_tree.get(".id")), + cut_id_tree.get(".check_sum"), + cut_id_tree.get(".connectors_cnt")); + m_cut_object_ids.insert({ obj_idx, std::move(cut_id) }); + } + } + } + } + void _3MF_Importer::_extract_print_config_from_archive( mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, @@ -2219,6 +2274,7 @@ namespace Slic3r { bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets); bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets); bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); + bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); @@ -2281,6 +2337,15 @@ namespace Slic3r { return false; } + // Adds file with information for object cut ("Metadata/Slic3r_PE_cut_information.txt"). + // All information for object cut of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model. + // The index differes from the index of an object ID of an object instance of a 3MF file! + if (!_add_cut_information_file_to_archive(archive, model)) { + close_zip_writer(&archive); + boost::filesystem::remove(filename); + return false; + } + // Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt"). // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model. // The index differes from the index of an object ID of an object instance of a 3MF file! @@ -2781,6 +2846,51 @@ namespace Slic3r { return true; } + bool _3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model) + { + std::string out = ""; + pt::ptree tree; + + unsigned int object_cnt = 0; + for (const ModelObject* object : model.objects) { + object_cnt++; + pt::ptree& obj_tree = tree.add("objects.object", ""); + + obj_tree.put(".id", object_cnt); + + // Store info for cut_id + pt::ptree& cut_id_tree = obj_tree.add("cut_id", ""); + + // store cut_id atributes + cut_id_tree.put(".id", object->cut_id.id().id); + cut_id_tree.put(".check_sum", object->cut_id.check_sum()); + cut_id_tree.put(".connectors_cnt", object->cut_id.connectors_cnt()); + } + + if (!tree.empty()) { + std::ostringstream oss; + pt::write_xml(oss, tree); + out = oss.str(); + + // Post processing("beautification") of the output string for a better preview + boost::replace_all(out, ">\n \n ", ">\n "); + boost::replace_all(out, ">", ">\n "); + // OR just + boost::replace_all(out, "><", ">\n<"); + } + + if (!out.empty()) { + if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { + add_error("Unable to add cut information file to archive"); + return false; + } + } + + return true; + } + bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model) { assert(is_decimal_separator_point()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 9fa38ecd7d..21b0523370 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -351,7 +351,7 @@ public: // Holes to be drilled into the object so resin can flow out sla::DrainHoles sla_drain_holes; - // Connectors to be added into the object after cut + // Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform CutConnectors cut_connectors; CutObjectBase cut_id; diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 42cd216991..452f620c47 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -89,7 +89,9 @@ private: friend class cereal::access; friend class Slic3r::UndoRedo::StackImpl; template void serialize(Archive &ar) { ar(m_id); } +protected: // #vbCHECKME && #ysFIXME ObjectBase(const ObjectID id) : m_id(id) {} +private: template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } }; @@ -141,6 +143,8 @@ public: // Constructor with ignored int parameter to assign an invalid ID, to be replaced // by an existing ID copied from elsewhere. CutObjectBase(int) : ObjectBase(-1) {} + // Constructor to initialize full information from 3mf + CutObjectBase(ObjectID id, size_t check_sum, size_t connectors_cnt) : ObjectBase(id), m_check_sum(check_sum), m_connectors_cnt(connectors_cnt) {} // The class tree will have virtual tables and type information. virtual ~CutObjectBase() = default; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index c260959a13..7b6c729f8d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -627,7 +627,7 @@ void GLGizmoCut3D::render_cut_center_graber() const BoundingBoxf3 box = bounding_box(); - const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 6.0); + const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); double size = m_dragging && m_hover_id == Z ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); @@ -821,8 +821,8 @@ bool GLGizmoCut3D::on_init() void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) { - ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, //m_selected, - m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, + // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, m_ar_plane_center, m_ar_rotations); set_center_pos(m_ar_plane_center, true); @@ -835,8 +835,8 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const { - ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, //m_selected, - m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, + // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, m_ar_plane_center, m_ar_rotations); } @@ -1224,7 +1224,10 @@ bool GLGizmoCut3D::update_bb() m_max_pos = box.max; m_min_pos = box.min; m_bb_center = box.center(); - set_center_pos(m_bb_center + m_center_offset, true); + if (box.contains(m_center_offset)) + set_center_pos(m_bb_center + m_center_offset, true); + else + set_center_pos(m_bb_center, true); m_radius = box.radius(); m_grabber_connection_len = 0.75 * m_radius;// std::min(0.75 * m_radius, 35.0); @@ -1242,6 +1245,9 @@ bool GLGizmoCut3D::update_bb() m_circle.reset(); m_scale.reset(); m_snap_radii.reset(); + m_reference_radius.reset(); + + on_unregister_raycasters_for_picking(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { m_selected.clear(); @@ -1271,8 +1277,7 @@ void GLGizmoCut3D::on_render() { if (update_bb() || force_update_clipper_on_render) { update_clipper_on_render(); - if (force_update_clipper_on_render) - m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); } init_picking_models(); @@ -1725,11 +1730,11 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) const bool has_connectors = !mo->cut_connectors.empty(); { + Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); // update connectors pos as offset of its center before cut performing if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { m_selected.clear(); - Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); for (CutConnector& connector : mo->cut_connectors) { connector.rotation = rotation; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 27764a6989..96df8892c9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5962,6 +5962,8 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& // suppress to call selection update for Object List to avoid call of early Gizmos on/off update p->load_model_objects(new_objects, false, false); + this->allow_snapshots(); + // now process all updates of the 3d scene update(); // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(),