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 7912613dc8)
This commit is contained in:
YuSanka 2022-08-18 13:57:25 +02:00
parent 70a575198b
commit cf144da4fe
5 changed files with 131 additions and 10 deletions

View file

@ -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<int, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, CutObjectBase> IdToCutObjectIdMap;
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
typedef std::map<int, std::vector<sla::DrainHole>> 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<int>("<xmlattr>.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<size_t>("<xmlattr>.id"));
CutObjectBase cut_id(ObjectID(cut_id_tree.get<size_t>("<xmlattr>.id")),
cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
cut_id_tree.get<size_t>("<xmlattr>.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("<xmlattr>.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("<xmlattr>.id", object->cut_id.id().id);
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
cut_id_tree.put("<xmlattr>.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, "><object", ">\n <object");
boost::replace_all(out, "><cut_id", ">\n <cut_id");
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
boost::replace_all(out, "></object>", ">\n </object>");
// 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());

View file

@ -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;

View file

@ -89,7 +89,9 @@ private:
friend class cereal::access;
friend class Slic3r::UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) { ar(m_id); }
protected: // #vbCHECKME && #ysFIXME
ObjectBase(const ObjectID id) : m_id(id) {}
private:
template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &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;

View file

@ -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<double>(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;

View file

@ -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(),