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:
parent
70a575198b
commit
cf144da4fe
5 changed files with 131 additions and 10 deletions
src
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Add table
Reference in a new issue