From 1c4d43b3a498530908eaf4877289548a13984511 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Feb 2023 12:34:08 +0100 Subject: [PATCH] Cut WIP: Use contours to perform a cut. --- src/libslic3r/Model.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 159 ++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 12 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 3 +- src/slic3r/GUI/MeshUtils.cpp | 8 +- src/slic3r/GUI/MeshUtils.hpp | 3 +- 7 files changed, 171 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f2a1db3c9..74dabb8e1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1565,7 +1565,6 @@ void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& ins if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower_mesh = TriangleMesh(lower_its); } - void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index f5ee14812..9d9344617 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -289,11 +289,28 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); } else if (m_hover_id == CutPlane) { - if (mouse_event.LeftDown()) - m_was_cut_plane_dragged = false; - else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged) + if (mouse_event.LeftDown()) { + m_was_cut_plane_dragged = m_was_contour_selected = false; + + // disable / enable current contour + Vec3d pos; + Vec3d pos_world; + m_was_contour_selected = unproject_on_cut_plane(mouse_pos.cast(), pos, pos_world, false); + if (m_was_contour_selected) { + // Following would inform the clipper about the mouse click, so it can + // toggle the respective contour as disabled. + m_c->object_clipper()->pass_mouse_click(pos_world); + process_contours(); + return true; + } + + } + else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged && !m_was_contour_selected) flip_cut_plane(); } + + if (!m_cut_by_contour_glmodels.empty()) + m_parent.toggle_model_objects_visibility(false); return true; } @@ -429,6 +446,9 @@ void GLGizmoCut3D::update_clipper() on_register_raycasters_for_picking(); else update_raycasters_for_picking_transform(); + + if (!m_c->object_clipper()->has_disable_contour()) + reset_cut_by_contours(); } void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) @@ -559,6 +579,8 @@ void GLGizmoCut3D::render_move_center_input(int axis) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); set_center(move, true); m_ar_plane_center = m_plane_center; + + reset_cut_by_contours(); } } @@ -1160,10 +1182,14 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - if (m_hover_id == Z || m_hover_id == CutPlane) + if (m_hover_id == Z || m_hover_id == CutPlane) { dragging_grabber_z(data); - else if (m_hover_id == X || m_hover_id == Y) + reset_cut_by_contours(); + } + else if (m_hover_id == X || m_hover_id == Y) { dragging_grabber_xy(data); + reset_cut_by_contours(); + } else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) dragging_connector(data); } @@ -1358,6 +1384,31 @@ void GLGizmoCut3D::render_clipper_cut() ::glEnable(GL_DEPTH_TEST); } +void GLGizmoCut3D::render_colored_parts() +{ + if (m_cut_by_contour_glmodels.empty()) + return; + assert(m_cut_by_contour_objects[0]->volumes.size() == m_cut_by_contour_glmodels.size()); + + if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { + const Camera& camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const Vec3d inst_offset = m_cut_by_contour_objects[0]->instances[0]->get_offset(); + const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); + + for (size_t id = 0; id < m_cut_by_contour_glmodels.size(); id++) { + const Vec3d volume_offset = m_cut_by_contour_objects[0]->volumes[id]->get_offset(); + shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); + m_cut_by_contour_glmodels[id].render(); + } + + shader->stop_using(); + } +} + void GLGizmoCut3D::on_render() { if (m_state == On) { @@ -1374,6 +1425,9 @@ void GLGizmoCut3D::on_render() render_connectors(); + if (!m_connectors_editing) + render_colored_parts(); + render_clipper_cut(); if (!m_hide_cut_plane && !m_connectors_editing) { @@ -1587,6 +1641,8 @@ void GLGizmoCut3D::reset_cut_plane() set_center(m_bb_center); m_start_dragging_m = m_rotation_m = Transform3d::Identity(); m_ar_plane_center = m_plane_center; + + reset_cut_by_contours(); } void GLGizmoCut3D::invalidate_cut_plane() @@ -1623,6 +1679,62 @@ void GLGizmoCut3D::flip_cut_plane() update_clipper(); } +void GLGizmoCut3D::reset_cut_by_contours() +{ + if (!m_cut_by_contour_objects.empty()) { + m_cut_by_contour_objects.clear(); + m_cut_by_contour_glmodels.clear(); + m_disabled_contours.clear(); + + m_parent.toggle_model_objects_visibility(true); + } +} + +void GLGizmoCut3D::process_contours() +{ + reset_cut_by_contours(); + + if (!m_c->object_clipper()->has_disable_contour()) + return; + + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + + wxBusyCursor wait; + + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + + m_cut_by_contour_objects = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), + ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts | + ModelObjectCutAttribute::InvalidateCutInfo); + + assert(m_cut_by_contour_objects.size() == 1); + + const ModelVolumePtrs& volumes = m_cut_by_contour_objects[0]->volumes; + + // split to parts + for (int id = int(volumes.size())-1; id >= 0; id--) + if (volumes[id]->is_splittable()) + volumes[id]->split(1); + + m_cut_by_contour_glmodels.reserve(volumes.size()); + for (const ModelVolume* volume : volumes) { + assert(volume != nullptr); + + GLModel glmodel; + // any condition to test + if (volume->name.find("_A") != std::string::npos) + glmodel.set_color({ 0.5f, 0.9f, 0.9f, 0.5f }); // glmodel.set_color(UPPER_PART_COLOR); + else + glmodel.set_color({ 0.9f, 0.5f, 0.9f, 0.5f }); // glmodel.set_color(LOWER_PART_COLOR); + + m_cut_by_contour_glmodels.push_back(glmodel); + } +} + void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/) { ImGui::SameLine(); @@ -1949,7 +2061,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) { // check if connector pos is out of clipping plane - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos)) { + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos, true)) { m_info_stats.outside_cut_contour++; return true; } @@ -1975,7 +2087,7 @@ bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& co its_transform(mesh, translation_transform(cur_pos) * m_rotation_m); for (auto vertex : vertices) { - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast())) { + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast(), true)) { m_info_stats.outside_cut_contour++; return true; } @@ -2132,6 +2244,24 @@ void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowel } } +Transform3d GLGizmoCut3D::get_cut_matrix(const Selection& selection) +{ + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + ModelObject* mo = selection.get_model()->objects[object_idx]; + if (!mo) + return Transform3d::Identity(); + + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. + const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); + + const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); + Vec3d cut_center_offset = m_plane_center - instance_offset; + cut_center_offset[Z] -= sla_shift_z; + + return translation_transform(cut_center_offset) * m_rotation_m; +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { if (!can_perform_cut()) @@ -2149,13 +2279,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // deactivate CutGizmo and than perform a cut m_parent.reset_all_gizmos(); - // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); - - const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); - Vec3d cut_center_offset = m_plane_center - instance_offset; - cut_center_offset[Z] -= sla_shift_z; - // perform cut { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); @@ -2165,7 +2288,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // update connectors pos as offset of its center before cut performing apply_connectors_in_model(mo, create_dowels_as_separate_object); - plater->cut(object_idx, instance_idx, translation_transform(cut_center_offset) * m_rotation_m, + plater->cut(object_idx, instance_idx, get_cut_matrix(selection), only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | @@ -2182,7 +2305,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. -bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world, bool respect_disabled_contour/* = true*/) { const float sla_shift = m_c->selection_info()->get_sla_shift(); @@ -2204,7 +2327,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po } else return false; - if (! m_c->object_clipper()->is_projection_inside_cut(hit)) + if (! m_c->object_clipper()->is_projection_inside_cut(hit, respect_disabled_contour)) return false; // recalculate hit to object's local position @@ -2284,6 +2407,8 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse } if (cut_line_processing()) { + reset_cut_by_contours(); + m_line_end = pt; if (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp) { Vec3d line_dir = m_line_end - m_line_beg; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 49d1848e4..ca2084aa1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -135,6 +135,12 @@ class GLGizmoCut3D : public GLGizmoBase bool m_has_invalid_connector{ false }; bool m_was_cut_plane_dragged { false }; + bool m_was_contour_selected { false }; + + std::vector m_cut_by_contour_glmodels; + ModelObjectPtrs m_cut_by_contour_objects; + // attributes for disabled contours + std::vector> m_disabled_contours; bool m_show_shortcuts{ false }; std::vector> m_shortcuts; @@ -176,7 +182,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; - bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_disabled_contour = true); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool is_in_editing_mode() const override { return m_connectors_editing; } @@ -229,6 +235,8 @@ protected: void reset_cut_plane(); void set_connectors_editing(bool connectors_editing); void flip_cut_plane(); + void process_contours(); + void reset_cut_by_contours(); void render_flip_plane_button(bool disable_pred = false); void add_vertical_scaled_interval(float interval); void add_horizontal_scaled_interval(float interval); @@ -257,6 +265,7 @@ protected: std::string get_action_snapshot_name() const override { return _u8L("Cut gizmo editing"); } void data_changed(bool is_serializing) override; + Transform3d get_cut_matrix(const Selection& selection); private: void set_center(const Vec3d& center, bool update_tbb = false); @@ -290,6 +299,7 @@ private: void init_picking_models(); void init_rendering_items(); void render_clipper_cut(); + void render_colored_parts(); void clear_selection(); void reset_connectors(); void init_connector_shapes(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 4edb01c2b..b642da5ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -388,9 +388,10 @@ void ObjectClipper::render_cut() const } } -bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const +bool ObjectClipper::is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto& cl) { return cl.first->is_projection_inside_cut(point); }); + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), + [point, respect_disabled_contour](const auto& cl) { return cl.first->is_projection_inside_cut(point, respect_disabled_contour); }); } bool ObjectClipper::has_valid_contour() const @@ -398,6 +399,11 @@ bool ObjectClipper::has_valid_contour() const return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); } +bool ObjectClipper::has_disable_contour() const +{ + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_disable_contour(); }); +} + void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { const ModelObject* mo = get_pool()->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index e0d2cdb68..15bf65814 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -250,8 +250,9 @@ public: void pass_mouse_click(const Vec3d& pt); std::vector get_disabled_contours() const; - bool is_projection_inside_cut(const Vec3d& point_in) const; + bool is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const; bool has_valid_contour() const; + bool has_disable_contour() const; protected: diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 59be6cabb..9f6cd1960 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -146,7 +146,7 @@ void MeshClipper::render_contour(const ColorRGBA& color) curr_shader->start_using(); } -bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const +bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const { if (!m_result || m_result->cut_islands.empty()) return false; @@ -155,7 +155,7 @@ bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const for (const CutIsland& isl : m_result->cut_islands) { if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) - return !isl.disabled; + return respect_disabled_contour ? !isl.disabled : true; } return false; } @@ -165,6 +165,10 @@ bool MeshClipper::has_valid_contour() const return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); }); } +bool MeshClipper::has_disable_contour() const +{ + return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return isl.disabled; }); +} void MeshClipper::pass_mouse_click(const Vec3d& point_in) { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index b10fa5ff8..0a854e5f3 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -120,8 +120,9 @@ public: void pass_mouse_click(const Vec3d& pt); - bool is_projection_inside_cut(const Vec3d& point) const; + bool is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const; bool has_valid_contour() const; + bool has_disable_contour() const; private: void recalculate_triangles();