From a9a0414cc310ecd0974a08d55b323e854579913e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 16 Jun 2023 18:06:08 +0200 Subject: [PATCH 01/51] WIP: Cut with Tongue and Groove - Implemented rendering for the cut plane --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 107 +++++++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 12 ++- 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 885f9d97a2..655acd5573 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -183,9 +183,10 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_style (int(CutConnectorStyle::Prism)) , m_connector_shape_id (int(CutConnectorShape::Circle)) { -// m_modes = { _u8L("Planar"), _u8L("Grid") + m_mode = size_t(CutMode::cutTongueAndGroove); + m_modes = { _u8L("Planar"), _u8L("Tongue and Groove")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") -// }; + }; m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; @@ -466,6 +467,18 @@ void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) update_clipper(); } +bool GLGizmoCut3D::render_cut_mode_combo() +{ + ImGui::AlignTextToFramePadding(); + int selection_idx = int(m_mode); + const bool is_changed = m_imgui->combo(_u8L("Mode"), m_modes, selection_idx, 0, m_label_width, m_control_width); + + if (is_changed) + m_mode = size_t(selection_idx); + + return is_changed; +} + bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, int& selection_idx) { ImGui::AlignTextToFramePadding(); @@ -606,6 +619,71 @@ bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::s return revert; } +static double get_grabber_mean_size(const BoundingBoxf3& bb) +{ + return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.; +} + +void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader) +{ + const Camera & camera = wxGetApp().plater()->get_camera(); + const Transform3d cp_matrix = translation_transform(m_plane_center) * m_rotation_m; + ColorRGBA cp_clr = m_plane.model.get_color(); + // input values + const double groove_depth = 2; + const double groove_width = 6; + + // values for calculaton + const double groove_half_depth = 0.5 * groove_depth; + const double groove_half_width = 0.5 * groove_width; + + const float cp_radius = m_cut_plane_radius_koef * (float)m_radius; + const float cp_length = 1.5f * cp_radius; + const float cp_width = 0.02f * (float)get_grabber_mean_size(m_bounding_box); + const double h_shift = 0.25 * (double)cp_radius + groove_half_width; + + GLModel model; + model.init_from(its_make_prism(0.5f * cp_radius, cp_length, cp_width)); + model.set_color(cp_clr); + + // upper halfs of cut_plane + + Transform3d view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * Vec3d(-h_shift, 0, groove_half_depth)) * cp_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix_); + model.render(); + + view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * Vec3d(h_shift, 0, groove_half_depth)) * cp_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix_); + model.render(); + + // lower part of cut_plane + + model.reset(); + model.init_from(its_make_prism(groove_width + 2*groove_depth, cp_length, cp_width)); + cp_clr.a(cp_clr.a() + 0.1f); + model.set_color(cp_clr); + + view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cp_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix_); + model.render(); + + // side parts of cut_plane + + model.reset(); + model.init_from(its_make_prism(sqrt(2* groove_depth* groove_depth), cp_length, cp_width)); + + const double h_side_shift = groove_half_width + groove_half_depth; + const double angle = 0.25 * PI; + + view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(-angle * Vec3d::UnitY()); + shader->set_uniform("view_model_matrix", view_model_matrix_); + model.render(); + + view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(angle * Vec3d::UnitY()); + shader->set_uniform("view_model_matrix", view_model_matrix_); + model.render(); +} + void GLGizmoCut3D::render_cut_plane() { if (cut_line_processing()) @@ -623,9 +701,7 @@ void GLGizmoCut3D::render_cut_plane() shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (can_perform_cut() && has_valid_contour()) { @@ -636,7 +712,14 @@ void GLGizmoCut3D::render_cut_plane() } else m_plane.model.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); - m_plane.model.render(); + + if (m_mode == size_t(CutMode::cutPlanar)) { + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + shader->set_uniform("view_model_matrix", view_model_matrix); + m_plane.model.render(); + } + else if (m_mode == size_t(CutMode::cutTongueAndGroove)) + render_cut_plate_for_tongue_and_groove(shader); glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -644,11 +727,6 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } -static double get_grabber_mean_size(const BoundingBoxf3& bb) -{ - return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.; -} - static double get_half_size(double size) { return std::max(size * 0.35, 0.05); @@ -1951,9 +2029,11 @@ void GLGizmoCut3D::render_color_marker(float size, const ImU32& color) void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { // WIP : cut plane mode - // render_combo(_u8L("Mode"), m_modes, m_mode); + render_cut_mode_combo(); - if (m_mode == size_t(CutMode::cutPlanar)) { +// if (m_mode == size_t(CutMode::cutPlanar)) { + CutMode mode = CutMode(m_mode); + if (mode == CutMode::cutPlanar || mode == CutMode::cutTongueAndGroove) { ImGui::AlignTextToFramePadding(); ImGuiWrapper::text(wxString(ImGui::InfoMarkerSmall)); ImGui::SameLine(); @@ -1968,7 +2048,6 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::SameLine(); render_move_center_input(Z); ImGui::SameLine(); - const bool has_connectors = !connectors.empty(); const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center); @@ -1982,6 +2061,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) // render_flip_plane_button(); + if (mode == CutMode::cutPlanar) { add_vertical_scaled_interval(0.75f); m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object())); @@ -1999,6 +2079,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) reset_connectors(); } m_imgui->disabled_end(); + } ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 51174a51af..73bd42fcb0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -13,6 +13,7 @@ namespace Slic3r { enum class CutConnectorType : int; class ModelVolume; +class GLShaderProgram; struct CutConnectorAttributes; namespace GUI { @@ -180,7 +181,8 @@ class GLGizmoCut3D : public GLGizmoBase enum class CutMode { cutPlanar - , cutGrig + , cutTongueAndGroove + //, cutGrig //,cutRadial //,cutModular }; @@ -190,7 +192,7 @@ class GLGizmoCut3D : public GLGizmoBase , Manual }; -// std::vector m_modes; + std::vector m_modes; size_t m_mode{ size_t(CutMode::cutPlanar) }; std::vector m_connector_modes; @@ -301,8 +303,9 @@ protected: Transform3d get_cut_matrix(const Selection& selection); private: - void set_center(const Vec3d& center, bool update_tbb = false); - bool render_combo(const std::string& label, const std::vector& lines, int& selection_idx); + void set_center(const Vec3d¢er, bool update_tbb = false); + bool render_cut_mode_combo(); + bool render_combo(const std::string&label, const std::vector&lines, int&selection_idx); bool render_double_input(const std::string& label, double& value_in); bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in); void render_move_center_input(int axis); @@ -319,6 +322,7 @@ private: bool cut_line_processing() const; void discard_cut_line_processing(); + void render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader); void render_cut_plane(); static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); From 519ee6af92a4822631db79042f9ee9eed912ea99 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 21 Jun 2023 15:39:07 +0200 Subject: [PATCH 02/51] WIP: Cut with Tongue and Groove - Implemented parameters input for the cut plane --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 128 ++++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 11 +++ 2 files changed, 105 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 655acd5573..8c1ae9baeb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -629,21 +629,24 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade const Camera & camera = wxGetApp().plater()->get_camera(); const Transform3d cp_matrix = translation_transform(m_plane_center) * m_rotation_m; ColorRGBA cp_clr = m_plane.model.get_color(); - // input values - const double groove_depth = 2; - const double groove_width = 6; // values for calculaton - const double groove_half_depth = 0.5 * groove_depth; - const double groove_half_width = 0.5 * groove_width; + const double groove_half_depth = 0.5 * double(m_groove_depth); + const double groove_half_width = 0.5 * double(m_groove_width); - const float cp_radius = m_cut_plane_radius_koef * (float)m_radius; - const float cp_length = 1.5f * cp_radius; - const float cp_width = 0.02f * (float)get_grabber_mean_size(m_bounding_box); - const double h_shift = 0.25 * (double)cp_radius + groove_half_width; + const double cp_radius = 1.5 * m_radius; + + const double cp_half_width = 0.5 * cp_radius; + const float cp_length = 1.5f * float(cp_radius); + const float cp_height = 0.02f * (float)get_grabber_mean_size(m_bounding_box); + + const double h_shift = 0.5 * cp_half_width + groove_half_width; + + const float side_width = is_approx(m_groove_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove_angle); GLModel model; - model.init_from(its_make_prism(0.5f * cp_radius, cp_length, cp_width)); + model.init_from(its_make_prism(float(cp_half_width), cp_length, cp_height)); model.set_color(cp_clr); // upper halfs of cut_plane @@ -659,7 +662,7 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade // lower part of cut_plane model.reset(); - model.init_from(its_make_prism(groove_width + 2*groove_depth, cp_length, cp_width)); + model.init_from(its_make_prism(m_groove_width + flaps_width, cp_length, cp_height)); cp_clr.a(cp_clr.a() + 0.1f); model.set_color(cp_clr); @@ -670,16 +673,16 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade // side parts of cut_plane model.reset(); - model.init_from(its_make_prism(sqrt(2* groove_depth* groove_depth), cp_length, cp_width)); - const double h_side_shift = groove_half_width + groove_half_depth; - const double angle = 0.25 * PI; + model.init_from(its_make_prism(float(side_width), cp_length, cp_height)); - view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(-angle * Vec3d::UnitY()); + const double h_side_shift = groove_half_width + 0.25 * double(flaps_width); + + view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); shader->set_uniform("view_model_matrix", view_model_matrix_); model.render(); - view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(angle * Vec3d::UnitY()); + view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); shader->set_uniform("view_model_matrix", view_model_matrix_); model.render(); } @@ -1383,6 +1386,11 @@ void GLGizmoCut3D::update_bb() m_snap_fine_in_radius = m_grabber_connection_len * 0.85; m_snap_fine_out_radius = m_grabber_connection_len * 1.15; + // input params for cut with tongue and groove + m_groove_depth = m_groove_depth_init = 0.5f * float(get_grabber_mean_size(m_bounding_box)); + m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; + m_groove_angle = m_groove_angle_init = 0.25f * float(PI); + m_plane.reset(); m_cone.reset(); m_sphere.reset(); @@ -2026,11 +2034,47 @@ void GLGizmoCut3D::render_color_marker(float size, const ImU32& color) ImGuiWrapper::text(" "); } +void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, const float& init_val, float& in_tolerance, bool is_angle /*=false*/) +{ + bool is_changed{false}; + + if (is_angle) { + ImGuiWrapper::text(label); + + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width * 0.7f); + + float val = rad2deg(in_val); + const float old_val = val; + + const std::string format = "%.0f " + _u8L("°"); + m_imgui->slider_float(("##groove_" + label).c_str(), &val, 30.f, 120.f, format.c_str(), 1.f, true, from_u8(label)); + + if (!is_approx(old_val, val)) { + in_val = deg2rad(val); + is_changed = true; + } + } + else if (render_slider_double_input(label, in_val, in_tolerance)) + is_changed = true; + + ImGui::SameLine(); + + m_imgui->disabled_begin(is_approx(in_val, init_val)); + const std::string act_name = _u8L("Reset"); + if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + in_val = init_val; + in_tolerance = 0.1f; + is_changed = true; + } + m_imgui->disabled_end(); + + //if (is_changed) + // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); +}; + void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { - // WIP : cut plane mode - render_cut_mode_combo(); - // if (m_mode == size_t(CutMode::cutPlanar)) { CutMode mode = CutMode(m_mode); if (mode == CutMode::cutPlanar || mode == CutMode::cutTongueAndGroove) { @@ -2041,6 +2085,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) get_wraped_wxString(_L("Hold SHIFT key to draw a cut line"), 40)); ImGui::Separator(); + // WIP : cut plane mode + render_cut_mode_combo(); + render_build_size(); ImGui::AlignTextToFramePadding(); @@ -2062,23 +2109,31 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) // render_flip_plane_button(); if (mode == CutMode::cutPlanar) { - add_vertical_scaled_interval(0.75f); + add_vertical_scaled_interval(0.75f); - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object())); - if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) - set_connectors_editing(true); - m_imgui->disabled_end(); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object())); + if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) + set_connectors_editing(true); + m_imgui->disabled_end(); - ImGui::SameLine(1.5f * m_control_width); + ImGui::SameLine(1.5f * m_control_width); - m_imgui->disabled_begin(is_cut_plane_init && !has_connectors); - act_name = _L("Reset cut"); - if (m_imgui->button(act_name, _L("Reset cutting plane and remove connectors"))) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), act_name, UndoRedo::SnapshotType::GizmoAction); - reset_cut_plane(); - reset_connectors(); - } - m_imgui->disabled_end(); + m_imgui->disabled_begin(is_cut_plane_init && !has_connectors); + act_name = _L("Reset cut"); + if (m_imgui->button(act_name, _L("Reset cutting plane and remove connectors"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), act_name, UndoRedo::SnapshotType::GizmoAction); + reset_cut_plane(); + reset_connectors(); + } + m_imgui->disabled_end(); + } + else if (mode == CutMode::cutTongueAndGroove) { + ImGui::Separator(); + ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Groove") + ": "); + + render_groove_input(_u8L("Depth"), m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); + render_groove_input(_u8L("Width"), m_groove_width, m_groove_width_init, m_groove_width_tolerance); + render_groove_input(_u8L("Angle"), m_groove_angle, m_groove_angle_init, m_groove_width_tolerance, true); } ImGui::Separator(); @@ -2496,6 +2551,11 @@ bool GLGizmoCut3D::can_perform_cut() const if (m_part_selection.valid()) return ! m_part_selection.is_one_object(); + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + const float flaps_width = -2.f * m_groove_depth / tan(m_groove_angle); + return flaps_width < m_groove_width; + } + return true;// has_valid_contour(); } @@ -2835,7 +2895,7 @@ void GLGizmoCut3D::init_connector_shapes() continue; for (const CutConnectorShape& shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) { const CutConnectorAttributes attribs = { type, style, shape }; - const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); m_shapes[attribs].model.init_from(its); m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 73bd42fcb0..8e758ec773 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -112,6 +112,16 @@ class GLGizmoCut3D : public GLGizmoBase bool m_rotate_upper{ false }; bool m_rotate_lower{ false }; + // Input params for cut with tongue and groove + float m_groove_depth; + float m_groove_width; + float m_groove_angle; + float m_groove_depth_init; + float m_groove_width_init; + float m_groove_angle_init; + float m_groove_depth_tolerance{ 0.1f }; + float m_groove_width_tolerance{ 0.1f }; + bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; bool m_cut_plane_as_circle{ false }; @@ -277,6 +287,7 @@ protected: void add_horizontal_scaled_interval(float interval); void add_horizontal_shift(float shift); void render_color_marker(float size, const ImU32& color); + void render_groove_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance, bool is_angle = false); void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); void render_input_window_warning() const; From ceb9582cb1fe337696ccdbfa406f83fb0cee16e2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 22 Jun 2023 15:34:28 +0200 Subject: [PATCH 03/51] WIP: Cut with Tongue and Groove * Implemented cut with groove + Cut by contour extracted to separate function perform_cut_by_contour --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 371 +++++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 + 2 files changed, 261 insertions(+), 112 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 8c1ae9baeb..969e6f57d6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -2633,6 +2633,261 @@ void synchronize_model_after_cut(Model& model, const CutObjectBase& cut_id) obj->cut_id.copy(cut_id); } +ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count) +{ + const Selection& selection = m_parent.get_selection(); + const int instance_idx = selection.get_instance_idx(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_keep_upper) cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + if (m_keep_lower) cut_mo->clone_for_cut(&lower); + + const Transform3d cut_matrix = get_cut_matrix(selection); + + auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { + if (upper && !upper->volumes.empty()) { + ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); + cut_objects.push_back(upper); + } + if (lower && !lower->volumes.empty()) { + ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower); + cut_objects.push_back(lower); + } + }; + + const size_t cut_parts_cnt = m_part_selection.parts().size(); + bool has_modifiers = false; + + // Distribute SolidParts to the Upper/Lower object + for (size_t id = 0; id < cut_parts_cnt; ++id) { + if (m_part_selection.parts()[id].is_modifier) + has_modifiers = true; // modifiers will be added later to the related parts + else if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower)) + obj->add_volume(*(cut_mo->volumes[id])); + } + + if (has_modifiers) { + // Distribute Modifiers to the Upper/Lower object + auto upper_bb = upper ? upper->instance_bounding_box(instance_idx) : BoundingBoxf3(); + auto lower_bb = lower ? lower->instance_bounding_box(instance_idx) : BoundingBoxf3(); + const Transform3d inst_matrix = cut_mo->instances[instance_idx]->get_transformation().get_matrix(); + + for (size_t id = 0; id < cut_parts_cnt; ++id) + if (m_part_selection.parts()[id].is_modifier) { + ModelVolume* vol = cut_mo->volumes[id]; + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + // Don't add modifiers which are not intersecting with solid parts + if (upper_bb.intersects(bb)) + upper->add_volume(*vol); + if (lower_bb.intersects(bb)) + lower->add_volume(*vol); + } + } + + ModelObjectPtrs cut_object_ptrs; + + ModelVolumePtrs& volumes = cut_mo->volumes; + if (volumes.size() == cut_parts_cnt) { + // Means that object is cut without connectors + + // Just add Upper and Lower objects to cut_object_ptrs + add_cut_objects(cut_object_ptrs, upper, lower); + } + else if (volumes.size() > cut_parts_cnt) { + // Means that object is cut with connectors + + // All volumes are distributed to Upper / Lower object, + // So we don’t need them anymore + for (size_t id = 0; id < cut_parts_cnt; id++) + delete *(volumes.begin() + id); + volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); + + // Perform cut just to get connectors + const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); + assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); + + // Connectors from upper object + for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) + upper->add_volume(*volume, volume->type()); + + // Connectors from lower object + for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) + lower->add_volume(*volume, volume->type()); + + // Add Upper and Lower objects to cut_object_ptrs + add_cut_objects(cut_object_ptrs, upper, lower); + + // Add Dowel-connectors as separate objects to cut_object_ptrs + if (cut_connectors_obj.size() >= 3) + for (size_t id = 2; id < cut_connectors_obj.size(); id++) + cut_object_ptrs.push_back(cut_connectors_obj[id]); + } + + // Now merge all model parts together: + { + for (ModelObject* mo : cut_object_ptrs) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } + } + } + } + + return cut_object_ptrs; +} + +ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes) +{ + const Selection& selection = m_parent.get_selection(); + const int instance_idx = selection.get_instance_idx(); + + const ModelObjectCutAttributes groove_cut_attributes = ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts ; + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + cut_mo->clone_for_cut(&lower); + + const double groove_half_depth = 0.5 * double(m_groove_depth); + + const Transform3d cut_matrix = get_cut_matrix(selection); + + Model tmp_model; + ModelObjectPtrs cut_part_ptrs; + + ModelObject* tmp_object{ nullptr }; + cut_mo->clone_for_cut(&tmp_object); + + auto cut = [&tmp_model, instance_idx](ModelObject* object, const Transform3d& cut_matrix, bool leave_upper, ModelObjectPtrs& cut_part_ptrs) { + tmp_model = Model(); + tmp_model.add_object(*object); + cut_part_ptrs = tmp_model.objects.back()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts); + auto& volumes = cut_part_ptrs.front()->volumes; + + assert(cut_part_ptrs.size() == 1); + assert(volumes.size() == 2); + + object->clear_volumes(); + object->add_volume(*(leave_upper ? volumes.front() : volumes.back())); + ModelObject::reset_instance_transformation(object, instance_idx); + }; + + // cut by upper plane + + const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; + { + cut(cut_mo, cut_matrix_upper, false, cut_part_ptrs); + upper->add_volume(*cut_part_ptrs.front()->volumes.front()); + } + + // cut by lower plane + + const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; + { + cut(cut_mo, cut_matrix_lower, true, cut_part_ptrs); + lower->add_volume(*cut_part_ptrs.front()->volumes.back()); + } + + // cut middle part with 2 angles and add parts to related upper/lower objects + + const double h_side_shift = 0.5 * double(m_groove_width + m_groove_depth / tan(m_groove_angle)); + + // cut by angle1 plane + { + const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); + + cut(cut_mo, cut_matrix_angle1, false, cut_part_ptrs); + lower->add_volume(*cut_part_ptrs.front()->volumes.front()); + } + + // cut by angle2 plane + { + const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); + + cut(cut_mo, cut_matrix_angle2, false, cut_part_ptrs); + lower->add_volume(*cut_part_ptrs.front()->volumes.front()); + } + + // apply tolerance to the middle part + { + const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; + + const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; + cut(cut_mo, cut_matrix_lower_tolerance, true, cut_part_ptrs); + + const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); + + const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); + cut(cut_mo, cut_matrix_angle1_tolerance, false, cut_part_ptrs); + + const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); + cut(cut_mo, cut_matrix_angle2_tolerance, true, cut_part_ptrs); + + // this part can be added to the upper object now + upper->add_volume(*cut_part_ptrs.front()->volumes.back()); + } + + auto add_cut_objects = [this, &instance_idx](ModelObjectPtrs& cut_objects, ModelObject* object, const Transform3d& cut_matrix) { + if (object && !object->volumes.empty()) { + ModelObject::reset_instance_transformation(object, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); + cut_objects.push_back(object); + } + }; + + ModelObjectPtrs cut_object_ptrs; + + add_cut_objects(cut_object_ptrs, upper, cut_matrix_upper); + add_cut_objects(cut_object_ptrs, lower, cut_matrix_lower); + + // Now merge all model parts together: + { + for (ModelObject* mo : cut_object_ptrs) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } + } + } + } + return cut_object_ptrs; +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { if (!can_perform_cut()) @@ -2658,6 +2913,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) ScopeGuard part_selection_killer([this]() { m_part_selection = PartSelection(); }); const bool cut_by_contour = m_part_selection.valid(); + const bool cut_with_groove = CutMode(m_mode) == CutMode::cutTongueAndGroove; ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object() : nullptr; if (cut_mo) cut_mo->cut_connectors = mo->cut_connectors; @@ -2686,118 +2942,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // update cut_id for the cut object in respect to the attributes update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); - ModelObjectPtrs cut_object_ptrs; - if (cut_by_contour) { - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - if (m_keep_upper) cut_mo->clone_for_cut(&upper); - ModelObject* lower{ nullptr }; - if (m_keep_lower) cut_mo->clone_for_cut(&lower); - - auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { - if (upper && !upper->volumes.empty()) { - ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); - cut_objects.push_back(upper); - } - if (lower && !lower->volumes.empty()) { - ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower); - cut_objects.push_back(lower); - } - }; - - const size_t cut_parts_cnt = m_part_selection.parts().size(); - bool has_modifiers = false; - - // Distribute SolidParts to the Upper/Lower object - for (size_t id = 0; id < cut_parts_cnt; ++id) { - if (m_part_selection.parts()[id].is_modifier) - has_modifiers = true; // modifiers will be added later to the related parts - else if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower)) - obj->add_volume(*(cut_mo->volumes[id])); - } - - if (has_modifiers) { - // Distribute Modifiers to the Upper/Lower object - auto upper_bb = upper ? upper->instance_bounding_box(instance_idx) : BoundingBoxf3(); - auto lower_bb = lower ? lower->instance_bounding_box(instance_idx) : BoundingBoxf3(); - const Transform3d inst_matrix = cut_mo->instances[instance_idx]->get_transformation().get_matrix(); - - for (size_t id = 0; id < cut_parts_cnt; ++id) - if (m_part_selection.parts()[id].is_modifier) { - ModelVolume* vol = cut_mo->volumes[id]; - auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); - // Don't add modifiers which are not intersecting with solid parts - if (upper_bb.intersects(bb)) - upper->add_volume(*vol); - if (lower_bb.intersects(bb)) - lower->add_volume(*vol); - } - } - - ModelVolumePtrs& volumes = cut_mo->volumes; - if (volumes.size() == cut_parts_cnt) { - // Means that object is cut without connectors - - // Just add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); - } - else if (volumes.size() > cut_parts_cnt) { - // Means that object is cut with connectors - - // All volumes are distributed to Upper / Lower object, - // So we don’t need them anymore - for (size_t id = 0; id < cut_parts_cnt; id++) - delete *(volumes.begin() + id); - volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); - - // Perform cut just to get connectors - const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); - assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); - - // Connectors from upper object - for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) - upper->add_volume(*volume, volume->type()); - - // Connectors from lower object - for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) - lower->add_volume(*volume, volume->type()); - - // Add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); - - // Add Dowel-connectors as separate objects to cut_object_ptrs - if (cut_connectors_obj.size() >= 3) - for (size_t id = 2; id < cut_connectors_obj.size(); id++) - cut_object_ptrs.push_back(cut_connectors_obj[id]); - } - - // Now merge all model parts together: - { - for (ModelObject* mo : cut_object_ptrs) { - TriangleMesh mesh; - // Merge all SolidPart but not Connectors - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector()) { - TriangleMesh m = mv->mesh(); - m.transform(mv->get_matrix()); - mesh.merge(m); - } - } - if (! mesh.empty()) { - ModelVolume* new_volume = mo->add_volume(mesh); - new_volume->name = mo->name; - // Delete all merged SolidPart but not Connectors - for (int i=int(mo->volumes.size())-2; i>=0; --i) { - const ModelVolume* mv = mo->volumes[i]; - if (mv->is_model_part() && !mv->is_cut_connector()) - mo->delete_volume(i); - } - } - } - } - } - else - cut_object_ptrs = cut_mo->cut(instance_idx, cut_matrix, attributes); + ModelObjectPtrs cut_object_ptrs = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) : + cut_with_groove ? perform_cut_with_groove(cut_mo, attributes) : + cut_mo->cut(instance_idx, cut_matrix, attributes); // save cut_id to post update synchronization const CutObjectBase cut_id = cut_mo->cut_id; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 8e758ec773..352584231b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -341,6 +341,8 @@ private: void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); void render_cut_plane_grabbers(); void render_cut_line(); + ModelObjectPtrs perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count); + ModelObjectPtrs perform_cut_with_groove(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false); void update_bb(); From 1aa8d8ea99a56a622d234f71be8d312e1ebe4735 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 23 Jun 2023 16:53:29 +0200 Subject: [PATCH 04/51] WIP: Cut with Tongue and Groove * Implemented preview rendering of groove --- src/libslic3r/Model.cpp | 1 + src/libslic3r/Model.hpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 195 +++++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 4 +- 4 files changed, 135 insertions(+), 73 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 16cc75225a..e8ae239c3a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1552,6 +1552,7 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + upper->volumes.back()->cut_info.is_from_upper = false; return; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index bedd26436f..2ee48f9917 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -239,6 +239,7 @@ enum class CutConnectorShape : int { , Square , Hexagon , Circle + , Rivet , Undef //,D-shape }; @@ -480,7 +481,7 @@ private: void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); public: - static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, + static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix = Transform3d::Identity(), bool place_on_cut = false, bool flip = false); ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes); @@ -777,6 +778,7 @@ public: // It contains information about connetors struct CutInfo { + bool is_from_upper{ true }; bool is_connector{ false }; bool is_processed{ true }; CutConnectorType connector_type{ CutConnectorType::Plug }; @@ -794,6 +796,7 @@ public: void set_processed() { is_processed = true; } void invalidate() { is_connector = false; } + void reset_from_upper() { is_from_upper = true; } template inline void serialize(Archive& ar) { ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); @@ -801,6 +804,9 @@ public: }; CutInfo cut_info; + bool is_from_upper() const { return cut_info.is_from_upper; } + void reset_from_upper() { cut_info.reset_from_upper(); } + bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } void invalidate_cut_info() { cut_info.invalidate(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 969e6f57d6..828dc95155 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -184,6 +184,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_shape_id (int(CutConnectorShape::Circle)) { m_mode = size_t(CutMode::cutTongueAndGroove); + m_contour_width = .0f; + m_modes = { _u8L("Planar"), _u8L("Tongue and Groove")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") }; @@ -1299,7 +1301,8 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=fa bool can_set_center_pos = false; { - if (tbb.max.z() > -.5 && tbb.min.z() < .5) + double limit_val = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove_depth) : 0.5; + if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val) can_set_center_pos = true; else { const double old_dist = (m_bb_center - m_plane_center).norm(); @@ -1551,6 +1554,28 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor m_valid = true; } +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in) +{ + m_instance_idx = instance_idx_in; + + m_model = Model(); + // add upper object + m_model.add_object(*object); + + m_parts.clear(); + + for (const ModelVolume* volume : object->volumes) { + assert(volume != nullptr); + m_parts.emplace_back(Part{ GLModel(), MeshRaycaster(volume->mesh()), true, !volume->is_model_part() }); + m_parts.back().glmodel.init_from(volume->mesh()); + + // Now check whether this part is below or above the plane. + m_parts.back().selected = volume->is_from_upper(); + } + + m_valid = true; +} + void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_model) { if (! valid()) @@ -1701,6 +1726,17 @@ void GLGizmoCut3D::on_render() m_c->selection_info()->set_use_shift(true); } + // init part_selection for groove mode + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + { + if (!m_part_selection.valid()) + process_contours(); + + m_contour_width = 0.f; + //check_and_update_connectors_state(); + } + else + m_contour_width = .4f; update_clipper(); @@ -1988,7 +2024,10 @@ void GLGizmoCut3D::process_contours() const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); - m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal, *m_c->object_clipper()); + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + m_part_selection = PartSelection(perform_cut_with_groove(model_objects[object_idx], true).front(), instance_idx); + else + m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal, *m_c->object_clipper()); m_parent.toggle_model_objects_visibility(false); } @@ -2069,8 +2108,10 @@ void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, } m_imgui->disabled_end(); - //if (is_changed) - // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + if (is_changed) { + reset_cut_by_contours(); + // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + } }; void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) @@ -2228,7 +2269,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::Separator(); - m_imgui->disabled_begin(!m_is_contour_changed && !can_perform_cut()); + m_imgui->disabled_begin(!can_perform_cut()); if(m_imgui->button(_L("Perform cut"))) perform_cut(m_parent.get_selection()); m_imgui->disabled_end(); @@ -2324,8 +2365,6 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) void GLGizmoCut3D::render_input_window_warning() const { - if (m_is_contour_changed) - return; if (! m_invalid_connectors_idxs.empty()) { wxString out = wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected") + ":"; if (m_info_stats.outside_cut_contour > size_t(0)) @@ -2472,7 +2511,7 @@ void GLGizmoCut3D::render_connectors() { ::glEnable(GL_DEPTH_TEST); - if (m_is_contour_changed || cut_line_processing() || + if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) return; @@ -2548,15 +2587,16 @@ bool GLGizmoCut3D::can_perform_cut() const { if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; - if (m_part_selection.valid()) - return ! m_part_selection.is_one_object(); if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { const float flaps_width = -2.f * m_groove_depth / tan(m_groove_angle); return flaps_width < m_groove_width; } - return true;// has_valid_contour(); + if (m_part_selection.valid()) + return ! m_part_selection.is_one_object(); + + return true; } bool GLGizmoCut3D::has_valid_contour() const @@ -2753,15 +2793,11 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const return cut_object_ptrs; } -ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes) +ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts/* = false*/) { const Selection& selection = m_parent.get_selection(); const int instance_idx = selection.get_instance_idx(); - const ModelObjectCutAttributes groove_cut_attributes = ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts ; - // Clone the object to duplicate instances, materials etc. ModelObject* upper{ nullptr }; cut_mo->clone_for_cut(&upper); @@ -2772,25 +2808,31 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, const const Transform3d cut_matrix = get_cut_matrix(selection); - Model tmp_model; ModelObjectPtrs cut_part_ptrs; + Model tmp_model = Model(); + tmp_model.add_object(*cut_mo); + ModelObject* tmp_object = tmp_model.objects.front(); - ModelObject* tmp_object{ nullptr }; - cut_mo->clone_for_cut(&tmp_object); + auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const ModelObjectPtrs& cut_part_ptrs) { + const auto& volumes = cut_part_ptrs.front()->volumes; + for (const ModelVolume* volume : volumes) + if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || + (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper()) ) { + ModelVolume* new_vol = object->add_volume(*volume); + new_vol->reset_from_upper(); + } + }; - auto cut = [&tmp_model, instance_idx](ModelObject* object, const Transform3d& cut_matrix, bool leave_upper, ModelObjectPtrs& cut_part_ptrs) { - tmp_model = Model(); - tmp_model.add_object(*object); - cut_part_ptrs = tmp_model.objects.back()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts); - auto& volumes = cut_part_ptrs.front()->volumes; + auto cut = [instance_idx, add_volumes_from_cut] + (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute attribute, ModelObjectPtrs& cut_part_ptrs) { + Model model = Model(); + model.add_object(*object); + cut_part_ptrs = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); assert(cut_part_ptrs.size() == 1); - assert(volumes.size() == 2); object->clear_volumes(); - object->add_volume(*(leave_upper ? volumes.front() : volumes.back())); + add_volumes_from_cut(object, attribute, cut_part_ptrs); ModelObject::reset_instance_transformation(object, instance_idx); }; @@ -2798,16 +2840,16 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, const const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - cut(cut_mo, cut_matrix_upper, false, cut_part_ptrs); - upper->add_volume(*cut_part_ptrs.front()->volumes.front()); + cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } // cut by lower plane const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - cut(cut_mo, cut_matrix_lower, true, cut_part_ptrs); - lower->add_volume(*cut_part_ptrs.front()->volumes.back()); + cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); } // cut middle part with 2 angles and add parts to related upper/lower objects @@ -2818,16 +2860,16 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, const { const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); - cut(cut_mo, cut_matrix_angle1, false, cut_part_ptrs); - lower->add_volume(*cut_part_ptrs.front()->volumes.front()); + cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } // cut by angle2 plane { const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); - cut(cut_mo, cut_matrix_angle2, false, cut_part_ptrs); - lower->add_volume(*cut_part_ptrs.front()->volumes.front()); + cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } // apply tolerance to the middle part @@ -2835,56 +2877,69 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, const const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; - cut(cut_mo, cut_matrix_lower_tolerance, true, cut_part_ptrs); + cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); - cut(cut_mo, cut_matrix_angle1_tolerance, false, cut_part_ptrs); + cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); - cut(cut_mo, cut_matrix_angle2_tolerance, true, cut_part_ptrs); - - // this part can be added to the upper object now - upper->add_volume(*cut_part_ptrs.front()->volumes.back()); + cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } - auto add_cut_objects = [this, &instance_idx](ModelObjectPtrs& cut_objects, ModelObject* object, const Transform3d& cut_matrix) { - if (object && !object->volumes.empty()) { - ModelObject::reset_instance_transformation(object, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); - cut_objects.push_back(object); - } - }; + // this part can be added to the upper object now + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); ModelObjectPtrs cut_object_ptrs; - add_cut_objects(cut_object_ptrs, upper, cut_matrix_upper); - add_cut_objects(cut_object_ptrs, lower, cut_matrix_lower); + if (keep_as_parts) { + // add volumes from lower object to the upper, but mark them as a lower + const auto& volumes = lower->volumes; + for (const ModelVolume* volume : volumes) { + ModelVolume* new_vol = upper->add_volume(*volume); + new_vol->cut_info.is_from_upper = false; + } + cut_object_ptrs.push_back(upper); + } + else { - // Now merge all model parts together: - { - for (ModelObject* mo : cut_object_ptrs) { - TriangleMesh mesh; - // Merge all SolidPart but not Connectors - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector()) { - TriangleMesh m = mv->mesh(); - m.transform(mv->get_matrix()); - mesh.merge(m); - } + auto add_cut_objects = [this, &instance_idx](ModelObjectPtrs& cut_objects, ModelObject* object, const Transform3d& cut_matrix) { + if (object && !object->volumes.empty()) { + ModelObject::reset_instance_transformation(object, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); + cut_objects.push_back(object); } - if (!mesh.empty()) { - ModelVolume* new_volume = mo->add_volume(mesh); - new_volume->name = mo->name; - // Delete all merged SolidPart but not Connectors - for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { - const ModelVolume* mv = mo->volumes[i]; - if (mv->is_model_part() && !mv->is_cut_connector()) - mo->delete_volume(i); + }; + + add_cut_objects(cut_object_ptrs, upper, cut_matrix_upper); + add_cut_objects(cut_object_ptrs, lower, cut_matrix_lower); + + // Now merge all model parts together: + { + for (ModelObject* mo : cut_object_ptrs) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } } } } } + return cut_object_ptrs; } @@ -2943,7 +2998,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); ModelObjectPtrs cut_object_ptrs = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) : - cut_with_groove ? perform_cut_with_groove(cut_mo, attributes) : + cut_with_groove ? perform_cut_with_groove(cut_mo) : cut_mo->cut(instance_idx, cut_matrix, attributes); // save cut_id to post update synchronization diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 352584231b..e418ce3121 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -138,7 +138,6 @@ class GLGizmoCut3D : public GLGizmoBase float m_contour_width{ 0.4f }; float m_cut_plane_radius_koef{ 1.5f }; - bool m_is_contour_changed{ false }; float m_shortcut_label_width{ -1.f }; mutable std::vector m_selected; // which pins are currently selected @@ -154,6 +153,7 @@ class GLGizmoCut3D : public GLGizmoBase public: PartSelection() = default; PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc); + PartSelection(const ModelObject* mo, int instance_idx_in); ~PartSelection() { m_model.clear_objects(); } struct Part { @@ -342,7 +342,7 @@ private: void render_cut_plane_grabbers(); void render_cut_line(); ModelObjectPtrs perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count); - ModelObjectPtrs perform_cut_with_groove(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes); + ModelObjectPtrs perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts = false); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false); void update_bb(); From 159d92cf6a9e418cf1d7a2a963e5c1c0f0b0c753 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 26 Jun 2023 13:49:48 +0200 Subject: [PATCH 05/51] WIP: Cut with Tongue and Groove * Implemented a check for invalid groove detection --- src/libslic3r/Model.cpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 81 +++++++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 + 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e8ae239c3a..0d4a2877c1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1645,6 +1645,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, // Post-process cut parts ModelObjectPtrs res; + if (upper->volumes.empty()) + return res; if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { reset_instance_transformation(upper, instance, cut_matrix); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 828dc95155..e14184fd65 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -356,7 +356,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return true; } else if (mouse_event.RightDown()) { - if (! m_connectors_editing && mouse_event.GetModifiers() == wxMOD_NONE) { + if (! m_connectors_editing && mouse_event.GetModifiers() == wxMOD_NONE && + CutMode(m_mode) == CutMode::cutPlanar) { // Check the internal part raycasters. if (! m_part_selection.valid()) process_contours(); @@ -2015,17 +2016,29 @@ void GLGizmoCut3D::reset_cut_by_contours() void GLGizmoCut3D::process_contours() { - reset_cut_by_contours(); - 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(); + ModelObjectPtrs cut_objects; + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + cut_objects = perform_cut_with_groove(model_objects[object_idx], true); + if (cut_objects.empty() || m_invalid_groove) { + m_parent.set_color_clip_plane_colors({ CONNECTOR_DEF_COLOR , CONNECTOR_DEF_COLOR }); + return; + } + else + m_parent.set_color_clip_plane_colors({ UPPER_PART_COLOR , LOWER_PART_COLOR }); + } + + reset_cut_by_contours(); + + wxBusyCursor wait; + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) - m_part_selection = PartSelection(perform_cut_with_groove(model_objects[object_idx], true).front(), instance_idx); + m_part_selection = PartSelection(cut_objects.front(), instance_idx); else m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal, *m_c->object_clipper()); m_parent.toggle_model_objects_visibility(false); @@ -2381,6 +2394,8 @@ void GLGizmoCut3D::render_input_window_warning() const m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Select at least one object to keep after cutting.")); if (!has_valid_contour()) m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Cut plane is placed out of object")); + if (m_invalid_groove) + m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Cut plane with groove is invalid")); } void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) @@ -2590,7 +2605,7 @@ bool GLGizmoCut3D::can_perform_cut() const if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { const float flaps_width = -2.f * m_groove_depth / tan(m_groove_angle); - return flaps_width < m_groove_width; + return flaps_width < m_groove_width && !m_invalid_groove; } if (m_part_selection.valid()) @@ -2795,6 +2810,8 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts/* = false*/) { + m_invalid_groove = false; + const Selection& selection = m_parent.get_selection(); const int instance_idx = selection.get_instance_idx(); @@ -2813,43 +2830,54 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool tmp_model.add_object(*cut_mo); ModelObject* tmp_object = tmp_model.objects.front(); + bool invalid_added_volumes = false; + auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const ModelObjectPtrs& cut_part_ptrs) { const auto& volumes = cut_part_ptrs.front()->volumes; + bool has_volume = false; for (const ModelVolume* volume : volumes) if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper()) ) { ModelVolume* new_vol = object->add_volume(*volume); new_vol->reset_from_upper(); + has_volume = true; } + return has_volume; }; - auto cut = [instance_idx, add_volumes_from_cut] + auto cut = [instance_idx, add_volumes_from_cut, &invalid_added_volumes] (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute attribute, ModelObjectPtrs& cut_part_ptrs) { Model model = Model(); model.add_object(*object); cut_part_ptrs = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - assert(cut_part_ptrs.size() == 1); + if (cut_part_ptrs.empty()) + return false; object->clear_volumes(); - add_volumes_from_cut(object, attribute, cut_part_ptrs); + invalid_added_volumes |= !add_volumes_from_cut(object, attribute, cut_part_ptrs); ModelObject::reset_instance_transformation(object, instance_idx); + return true; }; + ModelObjectPtrs cut_object_ptrs; + // cut by upper plane const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); - add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + return cut_object_ptrs; + invalid_added_volumes |= !add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } // cut by lower plane const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) + return cut_object_ptrs; + invalid_added_volumes |= !add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); } // cut middle part with 2 angles and add parts to related upper/lower objects @@ -2860,16 +2888,18 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool { const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); - cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + return cut_object_ptrs; + invalid_added_volumes |= !add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } // cut by angle2 plane { const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); - cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + return cut_object_ptrs; + invalid_added_volumes |= !add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); } // apply tolerance to the middle part @@ -2877,23 +2907,27 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; - cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) + return cut_object_ptrs; const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); - cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + return cut_object_ptrs; const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); - cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + if (!cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) + return cut_object_ptrs; } // this part can be added to the upper object now - add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); - - ModelObjectPtrs cut_object_ptrs; + invalid_added_volumes |= !add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); if (keep_as_parts) { + if (!m_invalid_groove) + m_invalid_groove = upper->volumes.empty() || lower->volumes.empty() || invalid_added_volumes; + // add volumes from lower object to the upper, but mark them as a lower const auto& volumes = lower->volumes; for (const ModelVolume* volume : volumes) { @@ -2903,7 +2937,6 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool cut_object_ptrs.push_back(upper); } else { - auto add_cut_objects = [this, &instance_idx](ModelObjectPtrs& cut_objects, ModelObject* object, const Transform3d& cut_matrix) { if (object && !object->volumes.empty()) { ModelObject::reset_instance_transformation(object, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index e418ce3121..dc0700f555 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -104,6 +104,8 @@ class GLGizmoCut3D : public GLGizmoBase } } m_info_stats; + bool m_invalid_groove{ false }; + bool m_keep_upper{ true }; bool m_keep_lower{ true }; bool m_keep_as_parts{ false }; From 0777139d1904ae206d1a51683f25115817483956 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 27 Jun 2023 17:06:38 +0200 Subject: [PATCH 06/51] WIP: Cut with Tongue and Groove * Implemented Z rotation for TAG plane --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 81 +++++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 +- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e14184fd65..7d2d896f1e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -257,8 +257,8 @@ std::string GLGizmoCut3D::get_tooltip() const "Drag to move the cut plane\n" "Right-click a part to assign it to the other side"); - if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { - std::string axis = m_hover_id == X ? "X" : "Y"; + if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation)) { + std::string axis = m_hover_id == X ? "X" : m_hover_id == Y ? "Y" : "Z"; return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°"); } @@ -787,8 +787,10 @@ void GLGizmoCut3D::render_rotation_snapping(GrabberID axis, const ColorRGBA& col if (axis == X) view_model_matrix = view_model_matrix * rotation_transform(0.5 * PI * Vec3d::UnitY()) * rotation_transform(-PI * Vec3d::UnitZ()); - else + else if (axis == Y) view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * rotation_transform(-0.5 * PI * Vec3d::UnitY()); + else + view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()); line_shader->start_using(); line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); @@ -808,9 +810,9 @@ void GLGizmoCut3D::render_rotation_snapping(GrabberID axis, const ColorRGBA& col line_shader->stop_using(); } -void GLGizmoCut3D::render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix) +void GLGizmoCut3D::render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef/* = 1.0*/) { - const Transform3d line_view_matrix = view_matrix * scale_transform(Vec3d(1.0, 1.0, m_grabber_connection_len)); + const Transform3d line_view_matrix = view_matrix * scale_transform(Vec3d(1.0, 1.0, line_len_koef * m_grabber_connection_len)); render_line(m_grabber_connection, color, line_view_matrix, 0.2f); }; @@ -826,9 +828,9 @@ void GLGizmoCut3D::render_cut_plane_grabbers() const double mean_size = get_grabber_mean_size(m_bounding_box); double size; - const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane; + const bool no_xy_dragging = m_dragging && m_hover_id == CutPlane; - if (!dragging_by_cut_plane) { + if (!no_xy_dragging && m_hover_id != CutPlaneZRotation) { render_grabber_connection(GRABBER_COLOR, view_matrix); // render sphere grabber @@ -839,11 +841,11 @@ void GLGizmoCut3D::render_cut_plane_grabbers() render_model(m_sphere.model, color, view_matrix * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); } - const bool no_one_grabber_hovered = !m_dragging && (m_hover_id < 0 || m_hover_id == CutPlane); + const bool no_xy_grabber_hovered = !m_dragging && (m_hover_id < 0 || m_hover_id == CutPlane); // render X grabber - if (no_one_grabber_hovered || m_hover_id == X) + if (no_xy_grabber_hovered || m_hover_id == X) { size = m_dragging && m_hover_id == X ? get_dragging_half_size(mean_size) : get_half_size(mean_size); const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); @@ -862,7 +864,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() // render Y grabber - if (no_one_grabber_hovered || m_hover_id == Y) + if (no_xy_grabber_hovered || m_hover_id == Y) { size = m_dragging && m_hover_id == Y ? get_dragging_half_size(mean_size) : get_half_size(mean_size); const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); @@ -876,7 +878,33 @@ void GLGizmoCut3D::render_cut_plane_grabbers() Vec3d offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); - render_model(m_cone.model, color, view_matrix * translation_transform(offset)* rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + } + + // render CutPlaneZRotation grabber + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove && (no_xy_grabber_hovered || m_hover_id == CutPlaneZRotation)) + { + size = 0.75 * (m_dragging && m_hover_id == CutPlaneZRotation ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + + color = ColorRGBA::BLUE(); + ColorRGBA cp_color = m_hover_id == CutPlaneZRotation ? color : m_plane.model.get_color();; + + const double grabber_shift = -1.75 * m_grabber_connection_len; + + render_model(m_sphere.model, cp_color, view_matrix * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + + if (m_hover_id == CutPlaneZRotation) { + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + + render_rotation_snapping(CutPlaneZRotation, color); + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(0.5 * PI * Vec3d::UnitX()), 1.75); + + Vec3d offset = Vec3d(1.25 * size, grabber_shift, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + offset = Vec3d(-1.25 * size, grabber_shift, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + } } } @@ -996,6 +1024,12 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity())); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_sphere.mesh_raycaster, Transform3d::Identity())); + } } update_raycasters_for_picking_transform(); @@ -1087,10 +1121,21 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - offset = 1.25 * size * Vec3d::UnitZ(); m_raycasters[id++]->set_transform(trafo * translation_transform(m_grabber_connection_len * Vec3d::UnitZ()) * scale_transform(size)); m_raycasters[id++]->set_transform(trafo); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + + const double grabber_shift = -1.75 * m_grabber_connection_len; + + offset = Vec3d(1.25 * size, grabber_shift, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + offset = Vec3d(-1.25 * size, grabber_shift, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + + m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + } } } @@ -1142,8 +1187,8 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); break; } - default: case Z: + default: { // no rotation applied break; @@ -1217,14 +1262,14 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) if (is_approx(theta, two_pi)) theta = 0.0; - if (m_hover_id == X) + if (m_hover_id != Y) theta += 0.5 * PI; if (!is_approx(theta, 0.0)) reset_cut_by_contours(); Vec3d rotation = Vec3d::Zero(); - rotation[m_hover_id] = theta; + rotation[m_hover_id == CutPlaneZRotation ? Z : m_hover_id] = theta; const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation); const bool update_tbb = !m_rotation_m.rotation().isApprox(rotation_tmp.rotation()); @@ -1259,7 +1304,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) return; if (m_hover_id == Z || m_hover_id == CutPlane) dragging_grabber_z(data); - else if (m_hover_id == X || m_hover_id == Y) + else if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) dragging_grabber_xy(data); else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) dragging_connector(data); @@ -1272,13 +1317,13 @@ void GLGizmoCut3D::on_start_dragging() if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move connector"), UndoRedo::SnapshotType::GizmoAction); - if (m_hover_id == X || m_hover_id == Y) + if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) m_start_dragging_m = m_rotation_m; } void GLGizmoCut3D::on_stop_dragging() { - if (m_hover_id == X || m_hover_id == Y) { + if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) { m_angle_arc.reset(); m_angle = 0.0; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index dc0700f555..ff5f45fb8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -30,6 +30,7 @@ class GLGizmoCut3D : public GLGizmoBase Y, Z, CutPlane, + CutPlaneZRotation, Count, }; @@ -340,7 +341,7 @@ private: static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); void render_rotation_snapping(GrabberID axis, const ColorRGBA& color); - void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); + void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0); void render_cut_plane_grabbers(); void render_cut_line(); ModelObjectPtrs perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count); From 034bb31341006885c5d8556b94386ef79c4dc3c2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 28 Jun 2023 11:12:47 +0200 Subject: [PATCH 07/51] WIP: Cut with Tongue and Groove * Improved cut plane rendering, when moving the plane * Added update of the scene after switching between cut modes --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 46 +++++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7d2d896f1e..d9201a25ec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -35,6 +35,9 @@ static const ColorRGBA CONNECTOR_DEF_COLOR = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); static const ColorRGBA CONNECTOR_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); static const ColorRGBA HOVERED_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 1.0f); +static const ColorRGBA CUT_PLANE_DEF_COLOR = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); +static const ColorRGBA CUT_PLANE_ERR_COLOR = ColorRGBA(1.0f, 0.8f, 0.8f, 0.5f); + const unsigned int AngleResolution = 64; const unsigned int ScaleStepsCount = 72; const float ScaleStepRad = 2.0f * float(PI) / ScaleStepsCount; @@ -476,9 +479,18 @@ bool GLGizmoCut3D::render_cut_mode_combo() int selection_idx = int(m_mode); const bool is_changed = m_imgui->combo(_u8L("Mode"), m_modes, selection_idx, 0, m_label_width, m_control_width); - if (is_changed) + if (is_changed) { m_mode = size_t(selection_idx); + apply_color_clip_plane_colors(); + if (auto oc = m_c->object_clipper()) { + m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; + oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + } + if (m_part_selection.valid()) + reset_cut_by_contours(); + } + return is_changed; } @@ -710,14 +722,7 @@ void GLGizmoCut3D::render_cut_plane() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (can_perform_cut() && has_valid_contour()) { - if (m_hover_id == CutPlane) - m_plane.model.set_color({ 0.9f, 0.9f, 0.9f, 0.5f }); - else - m_plane.model.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); - } - else - m_plane.model.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); + m_plane.model.set_color(can_perform_cut() && has_valid_contour() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR); if (m_mode == size_t(CutMode::cutPlanar)) { const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; @@ -964,11 +969,19 @@ std::string GLGizmoCut3D::on_get_name() const return _u8L("Cut"); } +void GLGizmoCut3D::apply_color_clip_plane_colors() +{ + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + m_parent.set_color_clip_plane_colors({ CUT_PLANE_DEF_COLOR , CUT_PLANE_DEF_COLOR }); + else + m_parent.set_color_clip_plane_colors({ UPPER_PART_COLOR , LOWER_PART_COLOR }); +} + void GLGizmoCut3D::on_set_state() { if (m_state == On) { m_parent.set_use_color_clip_plane(true); - m_parent.set_color_clip_plane_colors({ UPPER_PART_COLOR , LOWER_PART_COLOR }); + apply_color_clip_plane_colors(); update_bb(); m_connectors_editing = !m_selected.empty(); @@ -1777,12 +1790,8 @@ void GLGizmoCut3D::on_render() { if (!m_part_selection.valid()) process_contours(); - - m_contour_width = 0.f; //check_and_update_connectors_state(); } - else - m_contour_width = .4f; update_clipper(); @@ -2053,6 +2062,8 @@ void GLGizmoCut3D::flip_cut_plane() void GLGizmoCut3D::reset_cut_by_contours() { m_part_selection = PartSelection(); + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return; const Selection& selection = m_parent.get_selection(); const ModelObjectPtrs& model_objects = selection.get_model()->objects; @@ -2071,11 +2082,9 @@ void GLGizmoCut3D::process_contours() if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { cut_objects = perform_cut_with_groove(model_objects[object_idx], true); if (cut_objects.empty() || m_invalid_groove) { - m_parent.set_color_clip_plane_colors({ CONNECTOR_DEF_COLOR , CONNECTOR_DEF_COLOR }); + m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx()); return; } - else - m_parent.set_color_clip_plane_colors({ UPPER_PART_COLOR , LOWER_PART_COLOR }); } reset_cut_by_contours(); @@ -2185,7 +2194,8 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::Separator(); // WIP : cut plane mode - render_cut_mode_combo(); + if (render_cut_mode_combo()) + mode = CutMode(m_mode); render_build_size(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ff5f45fb8e..292294d880 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -336,6 +336,7 @@ private: bool cut_line_processing() const; void discard_cut_line_processing(); + void apply_color_clip_plane_colors(); void render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader); void render_cut_plane(); static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); From 92c7a31f42e4b67ee68887cb877ac08b2962d46c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 29 Jun 2023 13:03:14 +0200 Subject: [PATCH 08/51] WIP: Cut with Tongue and Groove * Added move possibility during X axis for cut plane * Added possibility to optimize rendering (recalculate new cut only on stop dragging) * Next fix for flickering (code refactoring for toggle_model_objects_visibility()) --- src/libslic3r/Model.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 169 ++++++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 8 +- 3 files changed, 118 insertions(+), 61 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0d4a2877c1..5c001e3538 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1645,7 +1645,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, // Post-process cut parts ModelObjectPtrs res; - if (upper->volumes.empty()) + if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) return res; if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d9201a25ec..27585aefd4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -317,8 +317,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) flip_cut_plane(); } - if (m_part_selection.valid()) - m_parent.toggle_model_objects_visibility(false); + toggle_model_objects_visibility(); return true; } @@ -487,8 +486,7 @@ bool GLGizmoCut3D::render_cut_mode_combo() m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); } - if (m_part_selection.valid()) - reset_cut_by_contours(); + reset_cut_by_contours(); } return is_changed; @@ -886,28 +884,49 @@ void GLGizmoCut3D::render_cut_plane_grabbers() render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); } - // render CutPlaneZRotation grabber + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - if (CutMode(m_mode) == CutMode::cutTongueAndGroove && (no_xy_grabber_hovered || m_hover_id == CutPlaneZRotation)) - { - size = 0.75 * (m_dragging && m_hover_id == CutPlaneZRotation ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + // render CutPlaneZRotation grabber - color = ColorRGBA::BLUE(); - ColorRGBA cp_color = m_hover_id == CutPlaneZRotation ? color : m_plane.model.get_color();; + if (no_xy_grabber_hovered || m_hover_id == CutPlaneZRotation) + { + size = 0.75 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + color = ColorRGBA::BLUE(); + const ColorRGBA cp_color = m_hover_id == CutPlaneZRotation ? color : m_plane.model.get_color(); - const double grabber_shift = -1.75 * m_grabber_connection_len; + const double grabber_shift = -1.75 * m_grabber_connection_len; - render_model(m_sphere.model, cp_color, view_matrix * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + render_model(m_sphere.model, cp_color, view_matrix * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + + if (m_hover_id == CutPlaneZRotation) { + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + + render_rotation_snapping(CutPlaneZRotation, color); + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(0.5 * PI * Vec3d::UnitX()), 1.75); + + Vec3d offset = Vec3d(1.25 * size, grabber_shift, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + offset = Vec3d(-1.25 * size, grabber_shift, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); + } + } + + // render CutPlaneXMove grabber + + if (no_xy_grabber_hovered || m_hover_id == CutPlaneXMove) + { + size = 0.5 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + color = m_hover_id == CutPlaneXMove ? ColorRGBA::RED() : m_plane.model.get_color(); + + const double grabber_shift = 0.2 * m_grabber_connection_len; + + render_model(m_sphere.model, color, view_matrix * translation_transform(grabber_shift * Vec3d::UnitX()) * scale_transform(size)); - if (m_hover_id == CutPlaneZRotation) { const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - render_rotation_snapping(CutPlaneZRotation, color); - render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(0.5 * PI * Vec3d::UnitX()), 1.75); - - Vec3d offset = Vec3d(1.25 * size, grabber_shift, 0.0); + Vec3d offset = Vec3d(1.25 * size + grabber_shift, 0.0, 0.0); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); - offset = Vec3d(-1.25 * size, grabber_shift, 0.0); + offset = Vec3d(-1.25 * size + grabber_shift, 0.0, 0.0); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); } } @@ -1039,9 +1058,13 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity())); if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_sphere.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_sphere.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); } } @@ -1140,14 +1163,23 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - const double grabber_shift = -1.75 * m_grabber_connection_len; + double grabber_shift = -1.75 * m_grabber_connection_len; + + m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); offset = Vec3d(1.25 * size, grabber_shift, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); offset = Vec3d(-1.25 * size, grabber_shift, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + grabber_shift = 0.2 * m_grabber_connection_len; + + m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_shift * Vec3d::UnitX()) * scale_transform(size)); + + offset = Vec3d(1.25 * size + grabber_shift, 0.0, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + offset = Vec3d(-1.25 * size + grabber_shift, 0.0, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); } } } @@ -1214,13 +1246,14 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& return transform(mouse_ray, m).intersect_plane(0.0); } -void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) +void GLGizmoCut3D::dragging_grabber_move(const GLGizmoBase::UpdateData &data) { - const Vec3d grabber_init_pos = (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ(); + const Vec3d grabber_init_pos = m_hover_id == CutPlaneXMove ? 0.2 * m_grabber_connection_len * Vec3d::UnitX(): + (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ(); const Vec3d starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * grabber_init_pos; double projection = 0.0; - Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ(); + Vec3d starting_vec = m_rotation_m * (m_hover_id == CutPlaneXMove ? Vec3d::UnitX() : Vec3d::UnitZ()); if (starting_vec.norm() != 0.0) { const Vec3d mouse_dir = data.mouse_ray.unit_vector(); // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position @@ -1248,7 +1281,7 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) m_was_cut_plane_dragged = true; } -void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) +void GLGizmoCut3D::dragging_grabber_rotation(const GLGizmoBase::UpdateData &data) { const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((GrabberID)m_hover_id, data.mouse_ray)); @@ -1315,13 +1348,16 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - if (m_hover_id == Z || m_hover_id == CutPlane) - dragging_grabber_z(data); + if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove) + dragging_grabber_move(data); else if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) - dragging_grabber_xy(data); + dragging_grabber_rotation(data); else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) dragging_connector(data); check_and_update_connectors_state(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + reset_cut_by_contours(); } void GLGizmoCut3D::on_start_dragging() @@ -1342,11 +1378,14 @@ void GLGizmoCut3D::on_stop_dragging() Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); m_start_dragging_m = m_rotation_m; } - else if (m_hover_id == Z || m_hover_id == CutPlane) { + else if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove) { if (m_was_cut_plane_dragged) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); m_ar_plane_center = m_plane_center; } + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove && m_optimaze_groove_rendering) + reset_cut_by_contours(); //check_and_update_connectors_state(); } @@ -1468,6 +1507,8 @@ void GLGizmoCut3D::update_bb() if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info(); selection && selection->model_object()) m_selected.resize(selection->model_object()->cut_connectors.size(), false); + +// reset_cut_by_contours(); } } @@ -1785,13 +1826,8 @@ void GLGizmoCut3D::on_render() m_c->selection_info()->set_use_shift(true); } - // init part_selection for groove mode - if (CutMode(m_mode) == CutMode::cutTongueAndGroove) - { - if (!m_part_selection.valid()) - process_contours(); - //check_and_update_connectors_state(); - } + // check objects visibility + toggle_model_objects_visibility(); update_clipper(); @@ -2062,12 +2098,14 @@ void GLGizmoCut3D::flip_cut_plane() void GLGizmoCut3D::reset_cut_by_contours() { m_part_selection = PartSelection(); - if (CutMode(m_mode) == CutMode::cutTongueAndGroove) - return; - const Selection& selection = m_parent.get_selection(); - const ModelObjectPtrs& model_objects = selection.get_model()->objects; - m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx()); + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + if (m_optimaze_groove_rendering && m_dragging) + return; + process_contours(); + } + else + toggle_model_objects_visibility(); } void GLGizmoCut3D::process_contours() @@ -2078,24 +2116,19 @@ void GLGizmoCut3D::process_contours() const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); - ModelObjectPtrs cut_objects; - if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - cut_objects = perform_cut_with_groove(model_objects[object_idx], true); - if (cut_objects.empty() || m_invalid_groove) { - m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx()); - return; - } - } - - reset_cut_by_contours(); - wxBusyCursor wait; - if (CutMode(m_mode) == CutMode::cutTongueAndGroove) - m_part_selection = PartSelection(cut_objects.front(), instance_idx); - else + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + ModelObjectPtrs cut_objects = perform_cut_with_groove(model_objects[object_idx], true); + if (!cut_objects.empty() && !m_invalid_groove) + m_part_selection = PartSelection(cut_objects.front(), instance_idx); + } + else { + reset_cut_by_contours(); m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal, *m_c->object_clipper()); - m_parent.toggle_model_objects_visibility(false); + } + + toggle_model_objects_visibility(); } void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/) @@ -2239,10 +2272,11 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) else if (mode == CutMode::cutTongueAndGroove) { ImGui::Separator(); ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Groove") + ": "); - render_groove_input(_u8L("Depth"), m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); render_groove_input(_u8L("Width"), m_groove_width, m_groove_width_init, m_groove_width_tolerance); render_groove_input(_u8L("Angle"), m_groove_angle, m_groove_angle_init, m_groove_width_tolerance, true); + + m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); } ImGui::Separator(); @@ -2577,6 +2611,25 @@ void GLGizmoCut3D::check_and_update_connectors_state() } } +void GLGizmoCut3D::toggle_model_objects_visibility() +{ + bool has_active_volume = false; + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + for (const auto raycaster : *raycasters) + if (raycaster->is_active()) { + has_active_volume = true; + break; + } + + if (m_part_selection.valid() && has_active_volume) + m_parent.toggle_model_objects_visibility(false); + else if (!m_part_selection.valid() && !has_active_volume) { + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx()); + } +} + void GLGizmoCut3D::render_connectors() { ::glEnable(GL_DEPTH_TEST); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 292294d880..433356cad7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -31,6 +31,7 @@ class GLGizmoCut3D : public GLGizmoBase Z, CutPlane, CutPlaneZRotation, + CutPlaneXMove, Count, }; @@ -124,6 +125,7 @@ class GLGizmoCut3D : public GLGizmoBase float m_groove_angle_init; float m_groove_depth_tolerance{ 0.1f }; float m_groove_width_tolerance{ 0.1f }; + bool m_optimaze_groove_rendering{ false }; bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; @@ -264,8 +266,8 @@ protected: bool on_is_activable() const override; bool on_is_selectable() const override; Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const; - void dragging_grabber_z(const GLGizmoBase::UpdateData &data); - void dragging_grabber_xy(const GLGizmoBase::UpdateData &data); + void dragging_grabber_move(const GLGizmoBase::UpdateData &data); + void dragging_grabber_rotation(const GLGizmoBase::UpdateData &data); void dragging_connector(const GLGizmoBase::UpdateData &data); void on_dragging(const UpdateData&data) override; void on_start_dragging() override; @@ -360,6 +362,8 @@ private: void validate_connector_settings(); bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); void check_and_update_connectors_state(); + + void toggle_model_objects_visibility(); }; } // namespace GUI From 6a2afc915392b1681ff6391d19a057d328ef3b55 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 29 Jun 2023 10:02:18 +0200 Subject: [PATCH 09/51] WIP: Add new connector - "Rivet" * Improves for UI * Implemented Rivet mesh --- resources/icons/rivet.svg | 13 ++ src/imgui/imconfig.h | 1 + src/libslic3r/Model.cpp | 4 +- src/libslic3r/Model.hpp | 1 + src/libslic3r/TriangleMesh.cpp | 175 +++++++++++++++++++++++++++ src/libslic3r/TriangleMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 35 ++++-- src/slic3r/GUI/ImGuiWrapper.cpp | 1 + 8 files changed, 217 insertions(+), 14 deletions(-) create mode 100644 resources/icons/rivet.svg diff --git a/resources/icons/rivet.svg b/resources/icons/rivet.svg new file mode 100644 index 0000000000..db4760cb65 --- /dev/null +++ b/resources/icons/rivet.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 5aed978426..a152252bac 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -152,6 +152,7 @@ namespace ImGui // const wchar_t MmuSegmentationMarker = 0x1F; const wchar_t PlugMarker = 0x1C; const wchar_t DowelMarker = 0x1D; + const wchar_t RivetMarker = 0x1E; // Do not forget use following letters only in wstring const wchar_t DocumentationButton = 0x2600; const wchar_t DocumentationHoverButton = 0x2601; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5c001e3538..7bf8608dde 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1302,7 +1302,9 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn break; } - if (connector_attributes.style == CutConnectorStyle::Prism) + if (connector_attributes.type == CutConnectorType::Rivet) + connector_mesh = its_make_rivet(1.0, 1.0, (2 * PI / /*sectorCount*/10)); + else if (connector_attributes.style == CutConnectorStyle::Prism) connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); else if (connector_attributes.type == CutConnectorType::Plug) connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount)); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2ee48f9917..d0409588ff 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -224,6 +224,7 @@ private: enum class CutConnectorType : int { Plug , Dowel + , Rivet , Undef }; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 4bf9fd486a..374387273f 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1262,6 +1262,181 @@ indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorC return mesh; } +indexed_triangle_set its_make_rivet(double r, double h, double fa) +{ + const float radius = (float)r; + const float height = (float)h; + const size_t sectors_cnt = 10;//(float)fa; + const float halfPI = 0.5f * (float)PI; + + const float space_len = 0.2f * radius; + + const float b_len = 0.75f * radius; + const float m_len = radius; + const float t_len = 0.5f * radius; + + const float b_height = 0.f; + const float m_height = 0.5f * height; + const float t_height = height; + + const float b_angle = acos(space_len/b_len); + const float m_angle = acos(space_len/m_len); + const float t_angle = acos(space_len/t_len); + + const float b_angle_step = b_angle / (float)sectors_cnt; + const float m_angle_step = m_angle / (float)sectors_cnt; + const float t_angle_step = t_angle / (float)sectors_cnt; + + const Vec2f b_vec = Eigen::Vector2f(0, b_len); + const Vec2f m_vec = Eigen::Vector2f(0, m_len); + const Vec2f t_vec = Eigen::Vector2f(0, t_len); + + const Vec2f space_vec = Eigen::Vector2f(0, space_len); + Vec2f b_pt = Eigen::Rotation2Df(halfPI) * space_vec; + + + indexed_triangle_set mesh; + + auto& vertices = mesh.vertices; + auto& facets = mesh.indices; + + vertices.reserve(2 * 3 * sectors_cnt + 2); + facets.reserve(2 * 6 * sectors_cnt); + + + float b_angle_start = halfPI - b_angle; + float m_angle_start = halfPI - m_angle; + float t_angle_start = halfPI - t_angle; + + float b_angle_stop = halfPI + b_angle; + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.emplace_back(Vec3f(-space_len, 0.f, b_height)); + vertices.emplace_back(Vec3f(-space_len, 0.f, t_height)); + + int frst_id = 0; + int scnd_id = 1; + + auto add_side_vertices = [b_vec, m_vec, t_vec, b_height, m_height, t_height](std::vector& vertices, float b_angle, float m_angle, float t_angle) { + Vec2f b_pt = Eigen::Rotation2Df(b_angle) * b_vec; + Vec2f m_pt = Eigen::Rotation2Df(m_angle) * m_vec; + Vec2f t_pt = Eigen::Rotation2Df(t_angle) * t_vec; + + vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); + vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height)); + vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); + }; + + // add first vertices and facets + { + add_side_vertices(vertices, b_angle_start, m_angle_start, t_angle_start); + + int id = (int)vertices.size() - 1; + + facets.emplace_back(id - 4, id - 2, id - 1); + facets.emplace_back(id - 4, id - 1, id); + facets.emplace_back(id - 4, id, id - 3); + } + + while (b_angle_start < b_angle_stop) { + b_angle_start += b_angle_step; + m_angle_start += m_angle_step; + t_angle_start += t_angle_step; + + add_side_vertices(vertices, b_angle_start, m_angle_start, t_angle_start); + + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, id - 2, id - 5); + + facets.emplace_back(id - 2, id - 1, id - 5); + facets.emplace_back(id - 1, id - 4, id - 5); + facets.emplace_back(id - 4, id - 1, id); + facets.emplace_back(id, id - 3, id - 4); + + facets.emplace_back(id, scnd_id, id - 3); + } + + // add facets to close the mesh + { + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, scnd_id, id); + facets.emplace_back(frst_id, id, id - 1); + facets.emplace_back(frst_id, id - 1, id - 2); + } + + + + + b_angle_start = 3 * halfPI - b_angle; + m_angle_start = 3 * halfPI - m_angle; + t_angle_start = 3 * halfPI - t_angle; + + b_angle_stop = 3 * halfPI + b_angle; + + frst_id = (int)vertices.size(); + scnd_id = frst_id+1; + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.emplace_back(Vec3f(space_len, 0.f, b_height)); + vertices.emplace_back(Vec3f(space_len, 0.f, t_height)); + + // add first vertices and facets + { + Vec2f b_pt = Eigen::Rotation2Df(b_angle_start) * b_vec; + Vec2f m_pt = Eigen::Rotation2Df(m_angle_start) * m_vec; + Vec2f t_pt = Eigen::Rotation2Df(t_angle_start) * t_vec; + + vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); + vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height)); + vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); + + int id = (int)vertices.size() - 1; + + facets.emplace_back(id - 4, id - 2, id - 1); + facets.emplace_back(id - 4, id - 1, id); + facets.emplace_back(id - 4, id , id - 3); + } + + while (b_angle_start < b_angle_stop) { + b_angle_start += b_angle_step; + m_angle_start += m_angle_step; + t_angle_start += t_angle_step; + + Vec2f b_pt = Eigen::Rotation2Df(b_angle_start) * b_vec; + Vec2f m_pt = Eigen::Rotation2Df(m_angle_start) * m_vec; + Vec2f t_pt = Eigen::Rotation2Df(t_angle_start) * t_vec; + + vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); + vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height)); + vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); + + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, id - 2, id - 5); + + facets.emplace_back(id - 2, id - 1, id - 5); + facets.emplace_back(id - 1, id - 4, id - 5); + facets.emplace_back(id - 4, id - 1, id); + facets.emplace_back(id, id - 3, id - 4); + + facets.emplace_back(id, scnd_id, id - 3); + } + + // add facets to close the mesh + { + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, scnd_id, id); + facets.emplace_back(frst_id, id, id - 1); + facets.emplace_back(frst_id, id - 1, id - 2); + } + + + return mesh; +} + indexed_triangle_set its_convex_hull(const std::vector &pts) { std::vector dst_vertices; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 0f43f9d58d..f6a18f59ce 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -321,6 +321,7 @@ indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360 indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); +indexed_triangle_set its_make_rivet(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_convex_hull(const std::vector &pts); inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 27585aefd4..9a217a461d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -186,8 +186,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_style (int(CutConnectorStyle::Prism)) , m_connector_shape_id (int(CutConnectorShape::Circle)) { - m_mode = size_t(CutMode::cutTongueAndGroove); - m_contour_width = .0f; + m_connector_type = CutConnectorType::Rivet; m_modes = { _u8L("Planar"), _u8L("Tongue and Groove")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -197,7 +196,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, std::map connetor_types = { {ImGui::PlugMarker , _u8L("Plug") }, - {ImGui::DowelMarker, _u8L("Dowel") }, + {ImGui::DowelMarker, _u8L("Dowel") }, + {ImGui::RivetMarker, _u8L("Rivet") }, }; for (auto connector : connetor_types) { std::string type_label = " " + connector.second + " "; @@ -1986,20 +1986,27 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) m_imgui->text(m_labels_map["Type"]); bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); + type_changed |= render_connect_type_radio_button(CutConnectorType::Rivet); if (type_changed) apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); - m_imgui->disabled_begin(m_connector_type == CutConnectorType::Dowel); - if (type_changed && m_connector_type == CutConnectorType::Dowel) { - m_connector_style = int(CutConnectorStyle::Prism); - apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); - } - if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style)) - apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + m_imgui->disabled_begin(m_connector_type != CutConnectorType::Plug); + if (type_changed && m_connector_type == CutConnectorType::Dowel) { + m_connector_style = int(CutConnectorStyle::Prism); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + } + if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style)) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); m_imgui->disabled_end(); - if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id)) - apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); + m_imgui->disabled_begin(m_connector_type == CutConnectorType::Rivet); + if (type_changed && m_connector_type == CutConnectorType::Rivet) { + m_connector_shape_id = int(CutConnectorShape::Circle); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); + } + if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id)) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); + m_imgui->disabled_end(); if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) apply_selected_connectors([this, &connectors](size_t idx) { @@ -3232,11 +3239,13 @@ void GLGizmoCut3D::reset_connectors() void GLGizmoCut3D::init_connector_shapes() { - for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug}) + for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug, CutConnectorType::Rivet}) for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prism}) { if (type == CutConnectorType::Dowel && style == CutConnectorStyle::Frustum) continue; for (const CutConnectorShape& shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) { + if (type == CutConnectorType::Rivet && shape != CutConnectorShape::Circle) + continue; const CutConnectorAttributes attribs = { type, style, shape }; indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); m_shapes[attribs].model.init_from(its); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 1a800eee42..ec1d039888 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -67,6 +67,7 @@ static const std::map font_icons = { {ImGui::InfoMarkerSmall , "notification_info" }, {ImGui::PlugMarker , "plug" }, {ImGui::DowelMarker , "dowel" }, + {ImGui::RivetMarker , "rivet" }, }; static const std::map font_icons_large = { From 9fd3de5e46ba93282fcab80bc8db7369ebc77109 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Jul 2023 15:04:29 +0200 Subject: [PATCH 10/51] WIP: Add new connector - "Rivet" * Code refactoring for its_make_rivet() to change a shape of rivet + Implemented perform the cut with rivets --- src/libslic3r/Model.cpp | 20 +++- src/libslic3r/TriangleMesh.cpp | 198 ++++++++++++--------------------- src/libslic3r/TriangleMesh.hpp | 2 +- 3 files changed, 90 insertions(+), 130 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 7bf8608dde..e57d576ff2 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1303,7 +1303,7 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn } if (connector_attributes.type == CutConnectorType::Rivet) - connector_mesh = its_make_rivet(1.0, 1.0, (2 * PI / /*sectorCount*/10)); + connector_mesh = its_make_rivet(1.0, 1.0); else if (connector_attributes.style == CutConnectorStyle::Prism) connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); else if (connector_attributes.type == CutConnectorType::Plug) @@ -1415,7 +1415,8 @@ void ModelVolume::apply_tolerance() rot_norm.normalize(); double z_offset = 0.5 * static_cast(cut_info.height_tolerance); - if (cut_info.connector_type == CutConnectorType::Plug) + if (cut_info.connector_type == CutConnectorType::Plug || + cut_info.connector_type == CutConnectorType::Rivet) z_offset -= 0.05; // add small Z offset to better preview set_offset(get_offset() + rot_norm * z_offset); @@ -1452,7 +1453,20 @@ void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& // This transformation is already there if (volume->cut_info.connector_type != CutConnectorType::Dowel) { if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume* vol = upper->add_volume(*volume); + ModelVolume* vol = nullptr; + if (volume->cut_info.connector_type == CutConnectorType::Rivet) { + TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); + + vol = upper->add_volume(std::move(mesh)); + vol->set_transformation(volume->get_transformation()); + vol->set_type(ModelVolumeType::NEGATIVE_VOLUME); + + vol->cut_info = volume->cut_info; + vol->name = volume->name; + } + else + vol = upper->add_volume(*volume); + vol->set_transformation(volume_matrix); vol->apply_tolerance(); } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 374387273f..4d1d163851 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1262,17 +1262,17 @@ indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorC return mesh; } -indexed_triangle_set its_make_rivet(double r, double h, double fa) +indexed_triangle_set its_make_rivet(double r, double h, float space_proportion) { const float radius = (float)r; const float height = (float)h; - const size_t sectors_cnt = 10;//(float)fa; + const size_t sectors_cnt = 10; //(float)fa; const float halfPI = 0.5f * (float)PI; - const float space_len = 0.2f * radius; + const float space_len = space_proportion * radius; - const float b_len = 0.75f * radius; - const float m_len = radius; + const float b_len = radius; + const float m_len = (1 + 0.5f*space_proportion) * radius; const float t_len = 0.5f * radius; const float b_height = 0.f; @@ -1280,46 +1280,18 @@ indexed_triangle_set its_make_rivet(double r, double h, double fa) const float t_height = height; const float b_angle = acos(space_len/b_len); - const float m_angle = acos(space_len/m_len); const float t_angle = acos(space_len/t_len); const float b_angle_step = b_angle / (float)sectors_cnt; - const float m_angle_step = m_angle / (float)sectors_cnt; const float t_angle_step = t_angle / (float)sectors_cnt; const Vec2f b_vec = Eigen::Vector2f(0, b_len); - const Vec2f m_vec = Eigen::Vector2f(0, m_len); const Vec2f t_vec = Eigen::Vector2f(0, t_len); - const Vec2f space_vec = Eigen::Vector2f(0, space_len); - Vec2f b_pt = Eigen::Rotation2Df(halfPI) * space_vec; - - indexed_triangle_set mesh; - - auto& vertices = mesh.vertices; - auto& facets = mesh.indices; - - vertices.reserve(2 * 3 * sectors_cnt + 2); - facets.reserve(2 * 6 * sectors_cnt); - - - float b_angle_start = halfPI - b_angle; - float m_angle_start = halfPI - m_angle; - float t_angle_start = halfPI - t_angle; - - float b_angle_stop = halfPI + b_angle; - - // 2 special vertices, top and bottom center, rest are relative to this - vertices.emplace_back(Vec3f(-space_len, 0.f, b_height)); - vertices.emplace_back(Vec3f(-space_len, 0.f, t_height)); - - int frst_id = 0; - int scnd_id = 1; - - auto add_side_vertices = [b_vec, m_vec, t_vec, b_height, m_height, t_height](std::vector& vertices, float b_angle, float m_angle, float t_angle) { + auto add_side_vertices = [b_vec, t_vec, b_height, m_height, t_height](std::vector& vertices, float b_angle, float t_angle, const Vec2f& m_vec) { Vec2f b_pt = Eigen::Rotation2Df(b_angle) * b_vec; - Vec2f m_pt = Eigen::Rotation2Df(m_angle) * m_vec; + Vec2f m_pt = Eigen::Rotation2Df(b_angle) * m_vec; Vec2f t_pt = Eigen::Rotation2Df(t_angle) * t_vec; vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); @@ -1327,25 +1299,8 @@ indexed_triangle_set its_make_rivet(double r, double h, double fa) vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); }; - // add first vertices and facets - { - add_side_vertices(vertices, b_angle_start, m_angle_start, t_angle_start); - - int id = (int)vertices.size() - 1; - - facets.emplace_back(id - 4, id - 2, id - 1); - facets.emplace_back(id - 4, id - 1, id); - facets.emplace_back(id - 4, id, id - 3); - } - - while (b_angle_start < b_angle_stop) { - b_angle_start += b_angle_step; - m_angle_start += m_angle_step; - t_angle_start += t_angle_step; - - add_side_vertices(vertices, b_angle_start, m_angle_start, t_angle_start); - - int id = (int)vertices.size() - 1; + auto add_side_facets = [](std::vector& facets, int vertices_cnt, int frst_id, int scnd_id) { + int id = vertices_cnt - 1; facets.emplace_back(frst_id, id - 2, id - 5); @@ -1355,84 +1310,75 @@ indexed_triangle_set its_make_rivet(double r, double h, double fa) facets.emplace_back(id, id - 3, id - 4); facets.emplace_back(id, scnd_id, id - 3); - } + }; - // add facets to close the mesh - { - int id = (int)vertices.size() - 1; + const float f = (b_len - m_len) / m_len; // Flattening - facets.emplace_back(frst_id, scnd_id, id); - facets.emplace_back(frst_id, id, id - 1); - facets.emplace_back(frst_id, id - 1, id - 2); - } + auto get_m_len = [b_len, f](float angle) { + const float rad_sqr = b_len * b_len; + const float sin_sqr = sin(angle) * sin(angle); + const float f_sqr = (1-f)*(1-f); + return sqrtf(rad_sqr / (1 + (1 / f_sqr - 1) * sin_sqr)); + }; + + auto add_sub_mesh = [add_side_vertices, add_side_facets, get_m_len, + b_height, t_height, b_angle, t_angle, b_angle_step, t_angle_step] + (indexed_triangle_set& mesh, float center_x, float angle_rotation, int frst_vertex_id) { + auto& vertices = mesh.vertices; + auto& facets = mesh.indices; + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.emplace_back(Vec3f(center_x, 0.f, b_height)); + vertices.emplace_back(Vec3f(center_x, 0.f, t_height)); + + float b_angle_start = angle_rotation - b_angle; + float t_angle_start = angle_rotation - t_angle; + const float b_angle_stop = angle_rotation + b_angle; + + const int frst_id = frst_vertex_id; + const int scnd_id = frst_id + 1; + + // add first side vertices and internal facets + { + const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start)); + add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec); + + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, id - 2, id - 1); + facets.emplace_back(frst_id, id - 1, id); + facets.emplace_back(frst_id, id, scnd_id); + } + + // add d side vertices and facets + while (b_angle_start < b_angle_stop) { + b_angle_start += b_angle_step; + t_angle_start += t_angle_step; + + const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start)); + add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec); + + add_side_facets(facets, (int)vertices.size(), frst_id, scnd_id); + } + + // add last internal facets to close the mesh + { + int id = (int)vertices.size() - 1; + + facets.emplace_back(frst_id, scnd_id, id); + facets.emplace_back(frst_id, id, id - 1); + facets.emplace_back(frst_id, id - 1, id - 2); + } + }; + indexed_triangle_set mesh; + mesh.vertices.reserve(2 * (3 * (2 * sectors_cnt + 1) + 2)); + mesh.indices.reserve(2 * (6 * 2 * sectors_cnt + 6)); - b_angle_start = 3 * halfPI - b_angle; - m_angle_start = 3 * halfPI - m_angle; - t_angle_start = 3 * halfPI - t_angle; - - b_angle_stop = 3 * halfPI + b_angle; - - frst_id = (int)vertices.size(); - scnd_id = frst_id+1; - - // 2 special vertices, top and bottom center, rest are relative to this - vertices.emplace_back(Vec3f(space_len, 0.f, b_height)); - vertices.emplace_back(Vec3f(space_len, 0.f, t_height)); - - // add first vertices and facets - { - Vec2f b_pt = Eigen::Rotation2Df(b_angle_start) * b_vec; - Vec2f m_pt = Eigen::Rotation2Df(m_angle_start) * m_vec; - Vec2f t_pt = Eigen::Rotation2Df(t_angle_start) * t_vec; - - vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); - vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height)); - vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); - - int id = (int)vertices.size() - 1; - - facets.emplace_back(id - 4, id - 2, id - 1); - facets.emplace_back(id - 4, id - 1, id); - facets.emplace_back(id - 4, id , id - 3); - } - - while (b_angle_start < b_angle_stop) { - b_angle_start += b_angle_step; - m_angle_start += m_angle_step; - t_angle_start += t_angle_step; - - Vec2f b_pt = Eigen::Rotation2Df(b_angle_start) * b_vec; - Vec2f m_pt = Eigen::Rotation2Df(m_angle_start) * m_vec; - Vec2f t_pt = Eigen::Rotation2Df(t_angle_start) * t_vec; - - vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height)); - vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height)); - vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height)); - - int id = (int)vertices.size() - 1; - - facets.emplace_back(frst_id, id - 2, id - 5); - - facets.emplace_back(id - 2, id - 1, id - 5); - facets.emplace_back(id - 1, id - 4, id - 5); - facets.emplace_back(id - 4, id - 1, id); - facets.emplace_back(id, id - 3, id - 4); - - facets.emplace_back(id, scnd_id, id - 3); - } - - // add facets to close the mesh - { - int id = (int)vertices.size() - 1; - - facets.emplace_back(frst_id, scnd_id, id); - facets.emplace_back(frst_id, id, id - 1); - facets.emplace_back(frst_id, id - 1, id - 2); - } - + add_sub_mesh(mesh, -space_len, halfPI , 0); + add_sub_mesh(mesh, space_len, 3 * halfPI, (int)mesh.vertices.size()); return mesh; } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index f6a18f59ce..d56f868637 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -321,7 +321,7 @@ indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360 indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); -indexed_triangle_set its_make_rivet(double r, double h, double fa=(2*PI/360)); +indexed_triangle_set its_make_rivet(double r, double h, float space_proportion = 0.25f); indexed_triangle_set its_convex_hull(const std::vector &pts); inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); } From 83fa61ee41677fe8b43d19321949bbf9e6e8df07 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Jul 2023 12:49:37 +0200 Subject: [PATCH 11/51] Cut: Extend WIP: Add new connector - "Rivet" to set max value and max tolerance --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 14 +++++++++----- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9a217a461d..22370f0a84 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -523,7 +523,7 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i return !is_approx(old_val, value); } -bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in) +bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float max_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/) { constexpr float UndefMinVal = -0.1f; const float f_mm_to_in = static_cast(ObjectManipulation::mm_to_in); @@ -547,18 +547,22 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v const BoundingBoxf3 bbox = m_bounding_box; const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f); + const float max_v = max_val > 0.f ? /*std::min(max_val, mean_size)*/max_val : mean_size; ImGuiWrapper::text(label); ImGui::SameLine(m_label_width); ImGui::PushItemWidth(m_control_width * 0.7f); - const bool is_value_changed = render_slider("##" + label, value_in, 1.f, mean_size, _L("Value")); +// const bool is_value_changed = render_slider("##" + label, value_in, 1.f, mean_size, _L("Value")); + const bool is_value_changed = render_slider("##" + label, value_in, 1.f, max_v, _L("Value")); ImGui::SameLine(); ImGui::PushItemWidth(m_control_width * 0.45f); - const bool is_tolerance_changed = render_slider("##tolerance_" + label, tolerance_in, 0.f, 0.5f * mean_size, _L("Tolerance")); +// const bool is_tolerance_changed = render_slider("##tolerance_" + label, tolerance_in, 0.f, 0.5f * mean_size, _L("Tolerance")); + const float max_tolerance_v = max_tolerance > 0.f ? std::min(max_tolerance, 0.5f * mean_size) : 0.5f * mean_size; + const bool is_tolerance_changed = render_slider("##tolerance_" + label, tolerance_in, 0.f, max_tolerance_v, _L("Tolerance")); return is_value_changed || is_tolerance_changed; } @@ -1490,7 +1494,7 @@ void GLGizmoCut3D::update_bb() // input params for cut with tongue and groove m_groove_depth = m_groove_depth_init = 0.5f * float(get_grabber_mean_size(m_bounding_box)); m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; - m_groove_angle = m_groove_angle_init = 0.25f * float(PI); + m_groove_angle = m_groove_angle_init = float(PI) / 3.f;// 0.25f * float(PI); m_plane.reset(); m_cone.reset(); @@ -2201,7 +2205,7 @@ void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, is_changed = true; } } - else if (render_slider_double_input(label, in_val, in_tolerance)) + else if (render_slider_double_input(label, in_val, in_tolerance, -0.1f, 2.0)) is_changed = true; ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 433356cad7..f2842ffbca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -323,7 +323,7 @@ private: bool render_cut_mode_combo(); bool render_combo(const std::string&label, const std::vector&lines, int&selection_idx); bool render_double_input(const std::string& label, double& value_in); - bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in); + bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float max_val = -0.1f, float max_tolerance = -0.1f); void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; From 2e6d1ff08fc8f2dfb7c9108c48e7441d12f21271 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Jul 2023 13:44:17 +0200 Subject: [PATCH 12/51] WIP: Cut with Tongue and Groove * Changed limit value for tolerances * Set optimize rendering value to true by default --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 22370f0a84..be7ad12a2c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -480,6 +480,7 @@ bool GLGizmoCut3D::render_cut_mode_combo() if (is_changed) { m_mode = size_t(selection_idx); + update_raycasters_for_picking(); apply_color_clip_plane_colors(); if (auto oc = m_c->object_clipper()) { @@ -2205,7 +2206,7 @@ void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, is_changed = true; } } - else if (render_slider_double_input(label, in_val, in_tolerance, -0.1f, 2.0)) + else if (render_slider_double_input(label, in_val, in_tolerance, -0.1f, std::min(0.3f*in_val, 1.5f))) is_changed = true; ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f2842ffbca..9d9c8bcf89 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -125,7 +125,7 @@ class GLGizmoCut3D : public GLGizmoBase float m_groove_angle_init; float m_groove_depth_tolerance{ 0.1f }; float m_groove_width_tolerance{ 0.1f }; - bool m_optimaze_groove_rendering{ false }; + bool m_optimaze_groove_rendering{ true }; bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; From 7cd99d98f537198b78158feced2aa8977bfcbe08 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Jul 2023 18:06:51 +0200 Subject: [PATCH 13/51] WIP: Cut with Rivets * Code refactoring: get_connector_mesh() and apply_cut_connectors() moved from ModelObject to CutGizmo. * Allow to change values of space and bulges for snaps --- src/libslic3r/Model.cpp | 60 ------------ src/libslic3r/Model.hpp | 2 - src/libslic3r/TriangleMesh.cpp | 6 +- src/libslic3r/TriangleMesh.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 141 ++++++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 9 +- 6 files changed, 139 insertions(+), 81 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e57d576ff2..dd0553100e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1280,66 +1280,6 @@ bool ModelObject::has_connectors() const return false; } -indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes) -{ - indexed_triangle_set connector_mesh; - - int sectorCount {1}; - switch (CutConnectorShape(connector_attributes.shape)) { - case CutConnectorShape::Triangle: - sectorCount = 3; - break; - case CutConnectorShape::Square: - sectorCount = 4; - break; - case CutConnectorShape::Circle: - sectorCount = 360; - break; - case CutConnectorShape::Hexagon: - sectorCount = 6; - break; - default: - break; - } - - if (connector_attributes.type == CutConnectorType::Rivet) - connector_mesh = its_make_rivet(1.0, 1.0); - else if (connector_attributes.style == CutConnectorStyle::Prism) - connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); - else if (connector_attributes.type == CutConnectorType::Plug) - connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount)); - else - connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount); - - return connector_mesh; -} - -void ModelObject::apply_cut_connectors(const std::string& new_name) -{ - if (cut_connectors.empty()) - return; - - using namespace Geometry; - - size_t connector_id = cut_id.connectors_cnt(); - for (const CutConnector& connector : cut_connectors) { - TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); - // Mesh will be centered when loading. - ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); - - // Transform the new modifier to be aligned inside the instance - new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m * - scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); - - new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; - new_volume->name = new_name + "-" + std::to_string(++connector_id); - } - cut_id.increase_connectors_cnt(cut_connectors.size()); - - // delete all connectors - cut_connectors.clear(); -} - void ModelObject::invalidate_cut() { this->cut_id.invalidate(); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index d0409588ff..7f9b354408 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -463,8 +463,6 @@ public: size_t materials_count() const; size_t facets_count() const; size_t parts_count() const; - static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); - void apply_cut_connectors(const std::string& name); // invalidate cut state for this object and its connectors/volumes void invalidate_cut(); // delete volumes which are marked as connector for this object diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 4d1d163851..236fa22c51 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1262,7 +1262,7 @@ indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorC return mesh; } -indexed_triangle_set its_make_rivet(double r, double h, float space_proportion) +indexed_triangle_set its_make_snap(double r, double h, float space_proportion, float bulge_proportion) { const float radius = (float)r; const float height = (float)h; @@ -1272,7 +1272,7 @@ indexed_triangle_set its_make_rivet(double r, double h, float space_proportion) const float space_len = space_proportion * radius; const float b_len = radius; - const float m_len = (1 + 0.5f*space_proportion) * radius; + const float m_len = (1 + bulge_proportion) * radius; const float t_len = 0.5f * radius; const float b_height = 0.f; @@ -1351,7 +1351,7 @@ indexed_triangle_set its_make_rivet(double r, double h, float space_proportion) } // add d side vertices and facets - while (b_angle_start < b_angle_stop) { + while (!is_approx(b_angle_start, b_angle_stop)) { b_angle_start += b_angle_step; t_angle_start += t_angle_step; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index d56f868637..4b524402c0 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -321,7 +321,7 @@ indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360 indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); -indexed_triangle_set its_make_rivet(double r, double h, float space_proportion = 0.25f); +indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f); indexed_triangle_set its_convex_hull(const std::vector &pts); inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index be7ad12a2c..8b00172ab9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -230,7 +230,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, {"Size" , _u8L("Size")}, }; - update_connector_shape(); +// update_connector_shape(); } std::string GLGizmoCut3D::get_tooltip() const @@ -498,8 +498,8 @@ bool GLGizmoCut3D::render_combo(const std::string& label, const std::vectorcombo(label, lines, selection_idx, 0, m_label_width, m_control_width); - if (is_changed) - update_connector_shape(); + //if (is_changed) + // update_connector_shape(); return is_changed; } @@ -524,7 +524,7 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i return !is_approx(old_val, value); } -bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float max_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/) +bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/) { constexpr float UndefMinVal = -0.1f; const float f_mm_to_in = static_cast(ObjectManipulation::mm_to_in); @@ -548,7 +548,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v const BoundingBoxf3 bbox = m_bounding_box; const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f); - const float max_v = max_val > 0.f ? /*std::min(max_val, mean_size)*/max_val : mean_size; + const float min_v = min_val > 0.f ? /*std::min(max_val, mean_size)*/min_val : 1.f; ImGuiWrapper::text(label); @@ -556,7 +556,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v ImGui::PushItemWidth(m_control_width * 0.7f); // const bool is_value_changed = render_slider("##" + label, value_in, 1.f, mean_size, _L("Value")); - const bool is_value_changed = render_slider("##" + label, value_in, 1.f, max_v, _L("Value")); + const bool is_value_changed = render_slider("##" + label, value_in, min_v, mean_size, _L("Value")); ImGui::SameLine(); ImGui::PushItemWidth(m_control_width * 0.45f); @@ -599,7 +599,7 @@ bool GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type) ImGui::PushItemWidth(m_control_width); if (ImGui::RadioButton(m_connector_types[size_t(type)].c_str(), m_connector_type == type)) { m_connector_type = type; - update_connector_shape(); +// update_connector_shape(); return true; } return false; @@ -2013,7 +2013,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); m_imgui->disabled_end(); - if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + const float depth_min_value = m_connector_type == CutConnectorType::Rivet ? m_connector_size : -0.1f; + if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance, depth_min_value)) apply_selected_connectors([this, &connectors](size_t idx) { if (m_connector_depth_ratio > 0) connectors[idx].height = m_connector_depth_ratio; @@ -2029,6 +2030,45 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) connectors[idx].radius_tolerance = 0.5f * m_connector_size_tolerance; }); + if (m_connector_type == CutConnectorType::Rivet) { + + const std::string format = "%.0f %%"; + + bool is_changed = false; + { + const std::string label = _u8L("Bulge"); + ImGuiWrapper::text(label); + + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width * 0.7f); + + float val = m_snap_bulge_proportion *100.f; + if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, 5.f, 100.f * m_snap_space_proportion, format.c_str(), 1.f, true, _u8L("Bulge proportion related to radius"))) { + m_snap_bulge_proportion = val * 0.01f; + is_changed = true; + } + } + + { + const std::string label = _u8L("Space"); + ImGuiWrapper::text(label); + + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width * 0.7f); + + float val = m_snap_space_proportion *100.f; + if (m_imgui->slider_float(("##snap_" + label).c_str(), &val, 10.f, 50.f, format.c_str(), 1.f, true, _u8L("Space proportion related to radius"))) { + m_snap_space_proportion = val * 0.01f; + is_changed = true; + } + } + + if (is_changed) { + update_connector_shape(); + update_raycasters_for_picking(); + } + } + ImGui::Separator(); if (m_imgui->button(_L("Confirm connectors"))) { @@ -2224,7 +2264,7 @@ void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, reset_cut_by_contours(); // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } -}; +} void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { @@ -2758,7 +2798,7 @@ void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, int &dowels_count) connector.pos += m_cut_normal * 0.5 * double(connector.height); } } - mo->apply_cut_connectors(_u8L("Connector")); + apply_cut_connectors(mo, _u8L("Connector")); } } @@ -3252,7 +3292,7 @@ void GLGizmoCut3D::init_connector_shapes() if (type == CutConnectorType::Rivet && shape != CutConnectorShape::Circle) continue; const CutConnectorAttributes attribs = { type, style, shape }; - indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + indexed_triangle_set its = get_connector_mesh(attribs); m_shapes[attribs].model.init_from(its); m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } @@ -3263,9 +3303,18 @@ void GLGizmoCut3D::update_connector_shape() { CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; - const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); - m_connector_mesh.clear(); - m_connector_mesh = TriangleMesh(its); + if (m_connector_type == CutConnectorType::Rivet) { + indexed_triangle_set its = get_connector_mesh(attribs); + m_shapes[attribs].reset(); + m_shapes[attribs].model.init_from(its); + m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + + //const indexed_triangle_set its = get_connector_mesh(attribs); + //m_connector_mesh.clear(); + //m_connector_mesh = TriangleMesh(its); + } + + } bool GLGizmoCut3D::cut_line_processing() const @@ -3522,5 +3571,69 @@ void GLGizmoCut3D::data_changed(bool is_serializing) } + + +indexed_triangle_set GLGizmoCut3D::get_connector_mesh(CutConnectorAttributes connector_attributes) +{ + indexed_triangle_set connector_mesh; + + int sectorCount{ 1 }; + switch (CutConnectorShape(connector_attributes.shape)) { + case CutConnectorShape::Triangle: + sectorCount = 3; + break; + case CutConnectorShape::Square: + sectorCount = 4; + break; + case CutConnectorShape::Circle: + sectorCount = 360; + break; + case CutConnectorShape::Hexagon: + sectorCount = 6; + break; + default: + break; + } + + if (connector_attributes.type == CutConnectorType::Rivet) + connector_mesh = its_make_snap(1.0, 1.0, m_snap_space_proportion, m_snap_bulge_proportion); + else if (connector_attributes.style == CutConnectorStyle::Prism) + connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); + else if (connector_attributes.type == CutConnectorType::Plug) + connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount)); + else + connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount); + + return connector_mesh; +} + +void GLGizmoCut3D::apply_cut_connectors(ModelObject* mo, const std::string& connector_name) +{ + if (mo->cut_connectors.empty()) + return; + + using namespace Geometry; + + size_t connector_id = mo->cut_id.connectors_cnt(); + for (const CutConnector& connector : mo->cut_connectors) { + TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); + // Mesh will be centered when loading. + ModelVolume* new_volume = mo->add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); + + // Transform the new modifier to be aligned inside the instance + new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m * + scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); + + new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; + new_volume->name = connector_name + "-" + std::to_string(++connector_id); + } + mo->cut_id.increase_connectors_cnt(mo->cut_connectors.size()); + + // delete all connectors + mo->cut_connectors.clear(); +} + + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 9d9c8bcf89..f4140c9c26 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -127,6 +127,10 @@ class GLGizmoCut3D : public GLGizmoBase float m_groove_width_tolerance{ 0.1f }; bool m_optimaze_groove_rendering{ true }; + // Input params for cut with snaps + float m_snap_bulge_proportion{ 0.15f }; + float m_snap_space_proportion{ 0.3f }; + bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; bool m_cut_plane_as_circle{ false }; @@ -323,7 +327,7 @@ private: bool render_cut_mode_combo(); bool render_combo(const std::string&label, const std::vector&lines, int&selection_idx); bool render_double_input(const std::string& label, double& value_in); - bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float max_val = -0.1f, float max_tolerance = -0.1f); + bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f); void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; @@ -364,6 +368,9 @@ private: void check_and_update_connectors_state(); void toggle_model_objects_visibility(); + + indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); + void apply_cut_connectors(ModelObject* mo, const std::string& connector_name); }; } // namespace GUI From e4fde63c47800e744a8a68ab5ac2c6cef2ce9e69 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 Jul 2023 12:35:43 +0200 Subject: [PATCH 14/51] WIP: Cut with tongue and groove * Allow to change angle of the groove * Rewrite rendering for the cut plane + Added reset_cut_by_contours() on flip of cutPlane and switch of cutPlane mode --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 345 ++++++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 9 +- 2 files changed, 294 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 8b00172ab9..76964983a6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -642,6 +642,225 @@ static double get_grabber_mean_size(const BoundingBoxf3& bb) return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.; } +indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() +{ + const float ghw = 0.5f * m_groove_width; // groove half width + const float cpr = 1.5f * float(m_radius); // cut plane radius + + const float cpl = 1.5f * cpr; // cut plane length + const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height + + // We need two upward facing triangles + float x = 0.5f * cpr, y = 0.5f * cpl; + + const float proj = y * tan(m_groove_angle); + const float extension_x = ghw + proj; + + // upper cut plane is simple + + if (is_approx(ghw, proj)) { + + return { + { + // roof + {0,1,6}, {6,1,2}, {2,3,5}, {5,3,4}, + // sides + {0,7,8}, {0,8,1}, {1,8,9}, {1,9,2}, {2,9,10}, {2,10,3}, {3,10,11}, {3,11,4}, + {4,11,12}, {4,12,5}, {5, 12,13}, {5,13,6}, {6,13,7}, {6, 7,0}, + // bottom + {7,13,8}, {8,13,9}, {10,9,12}, {10,12,4} + }, + { + // roof vertices + {-x, -y, cph}, {-extension_x, -y, cph}, {0.f, y, cph}, {extension_x, -y, cph}, {x, -y, cph}, {x, y, cph}, {-x, y, cph}, + // bottom vertices + {-x, -y, 0.f}, {-extension_x, -y, 0.f}, {0.f, y, 0.f}, {extension_x, -y, 0.f}, {x, -y, 0.f}, {x, y, 0.f}, {-x, y, 0.f}, + } + }; + } + + if (ghw < proj) { + + const float cross_pt_y = ghw / tan(m_groove_angle); + return { + { + // roof + {0,1,6}, {1,2,6}, {2,5,6}, {2,3,5}, {3,4,5}, + // sides + {0,7,8}, {0,8,1}, {1,8,9}, {1,9,2}, {2,9,10}, {2,10,3}, {3,10,11}, {3,11,4}, + {4,11,12}, {4,12,5}, {5,12,13}, {5,13,6}, {6,13,7}, {6,7,0}, + // bottom + {7,13,8}, {8,13,9}, {9,13,12}, {9,12,10}, {10,12, 11} + }, + { + // roof vertices + {-x, -y, cph}, {-extension_x, -y, cph}, {0.f, cross_pt_y, cph}, {extension_x, -y, cph}, {x, -y, cph}, {x, y, cph}, {-x, y, cph}, + // bottom vertices + {-x, -y, 0.f}, {-extension_x, -y, 0.f}, {0.f, cross_pt_y, 0.f}, {extension_x, -y, 0.f}, {x, -y, 0.f}, {x, y, 0.f}, {-x, y, 0.f} + } + }; + } + + // upper cut plane contains 2 sub planes + + const float narrowing_x = ghw - proj; + return { + { + // roof + {0,1,3}, {3,1,2}, {7,4,6}, {6,4,5}, + // sides + {0,8,9}, {0,9,1}, {1,9,10}, {1,10,2}, {2,10,11}, {2,11,3}, {3,11,8}, {3,8,0}, + {4,12,13}, {4,13,5}, {5,13,14}, {5,14,6}, {6,14,15}, {6,15,7}, {7,15,12}, {7,12,4}, + // bottom + {9,8,11}, {9,11,10}, {12,15,14}, {12,14,13} + }, + { + // roof vertices + {-x, -y, cph}, {-extension_x, -y, cph}, {-narrowing_x, y, cph}, {-x, y, cph}, {extension_x, -y, cph}, {x, -y, cph}, {x, y, cph}, {narrowing_x, y, cph}, + // bottom vertices + {-x, -y, 0.f}, {-extension_x, -y, 0.f}, {-narrowing_x, y, 0.f}, {-x, y, 0.f}, {extension_x, -y, 0.f}, {x, -y, 0.f}, {x, y, 0.f}, {narrowing_x, y, 0.f}, + } + }; +} + +indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width) +{ + const float ghw = 0.5f * (m_groove_width + flaps_width); // groove half width + const float cpr = 1.5f * float(m_radius); // cut plane radius + + const float cpl = 1.5f * cpr; // cut plane length + const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height + + // We need two upward facing triangles + float x = 0.5f * cpr, y = 0.5f * cpl; + + const float proj = y * tan(m_groove_angle); + const float extension_x = ghw + proj; + + // upper cut plane is trapezium + + if (ghw > proj) { + + const float narrowing_x = ghw - proj; + return { + { + // roof + {0,3,1}, {1,3,2}, + // sides + {0,4,7}, {0,7,3}, {3,7,6}, {3,6,2}, {2,6,5}, {2,5,1}, {1,5,4}, {1,4,0}, + // bottom + {4,5,7}, {7,5,6} + }, + { + // roof vertices + {-extension_x, -y, cph}, {-narrowing_x, y, cph}, {narrowing_x, y, cph}, {extension_x, -y, cph}, + // bottom vertices + {-extension_x, -y, 0.f}, {-narrowing_x, y, 0.f}, {narrowing_x, y, 0.f}, {extension_x, -y, 0.f} + } + }; + } + + // upper cut plane is triangle + + const float cross_pt_y = ghw / tan(m_groove_angle); + return { + { + // roof + {0,2,1}, + // sides + {0,3,5}, {0,5,2}, {2,5,4}, {2,4,1}, {1,4,3}, {0,1,3}, + // bottom + {3,4,5} + }, + { + // roof vertices + {-extension_x, -y, cph}, {0.f, cross_pt_y, cph}, {extension_x, -y, cph}, + // bottom vertices + {-extension_x, -y, 0.f}, {0.f, cross_pt_y, 0.f}, {extension_x, -y, 0.f} + } + }; +} + +indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width) +{ + const float ghw_upper = 0.5f * (m_groove_width); // groove half width + const float ghw_lower = 0.5f * (m_groove_width + flaps_width); // groove half width + const float cpr = 1.5f * float(m_radius); // cut plane radius + + const float cpl = 1.5f * cpr; // cut plane length + const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height + + const float ghd = 0.5f * m_groove_depth; // groove half depth + + + // We need two upward facing triangles + float x = 0.5f * cpr, y = 0.5f * cpl; + float z_upper = ghd; + float z_lower = -ghd; + + const float proj = y * tan(m_groove_angle); + + const float extension_upper_x = ghw_upper + proj; + const float extension_lower_x = ghw_lower + proj; + + const float narrowing_upper_x = ghw_upper - proj; + const float narrowing_lower_x = ghw_lower - proj; + + // groove is open + + if (ghw_upper > proj && ghw_lower > proj) { + return { + { + {1,0,2}, {2,0,3}, {5,4,7}, {5,7,6}, + {0,1,2}, {3,0,2}, {4,5,7}, {7,5,6} + }, + { + // left vertices + {-extension_lower_x, -y, z_lower}, {-extension_upper_x, -y, z_upper}, {-narrowing_upper_x, y, z_upper}, {-narrowing_lower_x, y, z_lower}, + // right vertices + {narrowing_lower_x, y, z_lower}, {narrowing_upper_x, y, z_upper}, {extension_upper_x, -y, z_upper}, {extension_lower_x, -y, z_lower} + } + }; + } + + const float cross_pt_upper_y = ghw_upper / tan(m_groove_angle); + + // groove is closed + + if (ghw_upper < proj && ghw_lower < proj) { + const float cross_pt_lower_y = ghw_lower / tan(m_groove_angle); + + return { + { + {1,0,3}, {1,3,4}, {1,4,5}, {1,5,2}, + {0,1,3}, {3,1,4}, {4,1,5}, {5,1,2} + }, + { + // roof vertices + {-extension_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {extension_upper_x, -y, z_upper}, + // bottom vertices + {-extension_lower_x, -y, z_lower}, {0.f, cross_pt_lower_y, z_lower}, {extension_lower_x, -y, z_lower} + } + }; + + } + + // groove is closed from the roof + + return { + { + {1,0,3}, {1,3,4}, {1,5,6}, {1,6,2}, + {0,1,3}, {3,1,4}, {5,1,6}, {6,1,2} + }, + { + // roof vertices + {-extension_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {extension_upper_x, -y, z_upper}, + // bottom vertices + {-extension_lower_x, -y, z_lower}, {-narrowing_lower_x, y, z_lower}, {narrowing_lower_x, y, z_lower}, {extension_lower_x, -y, z_lower} + } + }; +} + void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader) { const Camera & camera = wxGetApp().plater()->get_camera(); @@ -650,37 +869,25 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade // values for calculaton const double groove_half_depth = 0.5 * double(m_groove_depth); - const double groove_half_width = 0.5 * double(m_groove_width); - const double cp_radius = 1.5 * m_radius; - - const double cp_half_width = 0.5 * cp_radius; - const float cp_length = 1.5f * float(cp_radius); - const float cp_height = 0.02f * (float)get_grabber_mean_size(m_bounding_box); - - const double h_shift = 0.5 * cp_half_width + groove_half_width; - - const float side_width = is_approx(m_groove_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_angle)); - const float flaps_width = 2.f * side_width * cos(m_groove_angle); + const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle); GLModel model; - model.init_from(its_make_prism(float(cp_half_width), cp_length, cp_height)); + + // upper cut_plane + + model.init_from(its_make_upper_groove_plane()); model.set_color(cp_clr); - // upper halfs of cut_plane - - Transform3d view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * Vec3d(-h_shift, 0, groove_half_depth)) * cp_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix_); - model.render(); - - view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * Vec3d(h_shift, 0, groove_half_depth)) * cp_matrix; + Transform3d view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cp_matrix; shader->set_uniform("view_model_matrix", view_model_matrix_); model.render(); // lower part of cut_plane model.reset(); - model.init_from(its_make_prism(m_groove_width + flaps_width, cp_length, cp_height)); + model.init_from(its_make_lower_groove_plane(flaps_width)); cp_clr.a(cp_clr.a() + 0.1f); model.set_color(cp_clr); @@ -691,16 +898,9 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade // side parts of cut_plane model.reset(); + model.init_from(its_make_sides_groove_plane(flaps_width)); - model.init_from(its_make_prism(float(side_width), cp_length, cp_height)); - - const double h_side_shift = groove_half_width + 0.25 * double(flaps_width); - - view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); - shader->set_uniform("view_model_matrix", view_model_matrix_); - model.render(); - - view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cp_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); + view_model_matrix_ = camera.get_view_matrix() * cp_matrix; shader->set_uniform("view_model_matrix", view_model_matrix_); model.render(); } @@ -1495,8 +1695,8 @@ void GLGizmoCut3D::update_bb() // input params for cut with tongue and groove m_groove_depth = m_groove_depth_init = 0.5f * float(get_grabber_mean_size(m_bounding_box)); m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; - m_groove_angle = m_groove_angle_init = float(PI) / 3.f;// 0.25f * float(PI); - + m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f;// 0.25f * float(PI); + m_groove_angle = m_groove_angle_init = 0.f; m_plane.reset(); m_cone.reset(); m_sphere.reset(); @@ -2145,6 +2345,9 @@ void GLGizmoCut3D::flip_cut_plane() update_clipper(); m_part_selection.turn_over_selection(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + reset_cut_by_contours(); } void GLGizmoCut3D::reset_cut_by_contours() @@ -2225,28 +2428,11 @@ void GLGizmoCut3D::render_color_marker(float size, const ImU32& color) ImGuiWrapper::text(" "); } -void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, const float& init_val, float& in_tolerance, bool is_angle /*=false*/) +void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in_val, const float& init_val, float& in_tolerance) { bool is_changed{false}; - if (is_angle) { - ImGuiWrapper::text(label); - - ImGui::SameLine(m_label_width); - ImGui::PushItemWidth(m_control_width * 0.7f); - - float val = rad2deg(in_val); - const float old_val = val; - - const std::string format = "%.0f " + _u8L("°"); - m_imgui->slider_float(("##groove_" + label).c_str(), &val, 30.f, 120.f, format.c_str(), 1.f, true, from_u8(label)); - - if (!is_approx(old_val, val)) { - in_val = deg2rad(val); - is_changed = true; - } - } - else if (render_slider_double_input(label, in_val, in_tolerance, -0.1f, std::min(0.3f*in_val, 1.5f))) + if (render_slider_double_input(label, in_val, in_tolerance, -0.1f, std::min(0.3f*in_val, 1.5f))) is_changed = true; ImGui::SameLine(); @@ -2266,6 +2452,43 @@ void GLGizmoCut3D::render_groove_input(const std::string& label, float& in_val, } } +void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +{ + bool is_changed{ false }; + + ImGuiWrapper::text(label); + + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width * 0.7f); + + float val = rad2deg(in_val); + const float old_val = val; + + const std::string format = "%.0f " + _u8L("°"); + m_imgui->slider_float(("##groove_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); + + if (!is_approx(old_val, val)) { + in_val = deg2rad(val); + is_changed = true; + } + + ImGui::SameLine(); + + m_imgui->disabled_begin(is_approx(in_val, init_val)); + const std::string act_name = _u8L("Reset"); + if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + in_val = init_val; + is_changed = true; + } + m_imgui->disabled_end(); + + if (is_changed) { + reset_cut_by_contours(); + // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + } +} + + void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { // if (m_mode == size_t(CutMode::cutPlanar)) { @@ -2324,9 +2547,10 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) else if (mode == CutMode::cutTongueAndGroove) { ImGui::Separator(); ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Groove") + ": "); - render_groove_input(_u8L("Depth"), m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); - render_groove_input(_u8L("Width"), m_groove_width, m_groove_width_init, m_groove_width_tolerance); - render_groove_input(_u8L("Angle"), m_groove_angle, m_groove_angle_init, m_groove_width_tolerance, true); + render_groove_float_input(_u8L("Depth"), m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); + render_groove_float_input(_u8L("Width"), m_groove_width, m_groove_width_init, m_groove_width_tolerance); + render_groove_angle_input(_u8L("Flaps Angle"), m_groove_flaps_angle, m_groove_flaps_angle_init, 30.f, 120.f); + render_groove_angle_input(_u8L("Groove Angle"), m_groove_angle, m_groove_angle_init, 0.f, 15.f); m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); } @@ -2764,7 +2988,7 @@ bool GLGizmoCut3D::can_perform_cut() const return false; if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - const float flaps_width = -2.f * m_groove_depth / tan(m_groove_angle); + const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); return flaps_width < m_groove_width && !m_invalid_groove; } @@ -3042,11 +3266,11 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool // cut middle part with 2 angles and add parts to related upper/lower objects - const double h_side_shift = 0.5 * double(m_groove_width + m_groove_depth / tan(m_groove_angle)); + const double h_side_shift = 0.5 * double(m_groove_width + m_groove_depth / tan(m_groove_flaps_angle)); // cut by angle1 plane { - const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); + const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); if (!cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) return cut_object_ptrs; @@ -3055,7 +3279,7 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool // cut by angle2 plane { - const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); + const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); if (!cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) return cut_object_ptrs; @@ -3072,11 +3296,11 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); - const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(-m_groove_angle * Vec3d::UnitY()); + const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); if (!cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) return cut_object_ptrs; - const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(m_groove_angle * Vec3d::UnitY()); + const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); if (!cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) return cut_object_ptrs; } @@ -3376,6 +3600,9 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse m_angle_arc.reset(); discard_cut_line_processing(); + + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + reset_cut_by_contours(); } else if (action == SLAGizmoEventType::Moving) this->set_dirty(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f4140c9c26..ff52ddc1ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -119,9 +119,11 @@ class GLGizmoCut3D : public GLGizmoBase // Input params for cut with tongue and groove float m_groove_depth; float m_groove_width; + float m_groove_flaps_angle; float m_groove_angle; float m_groove_depth_init; float m_groove_width_init; + float m_groove_flaps_angle_init; float m_groove_angle_init; float m_groove_depth_tolerance{ 0.1f }; float m_groove_width_tolerance{ 0.1f }; @@ -296,7 +298,8 @@ protected: void add_horizontal_scaled_interval(float interval); void add_horizontal_shift(float shift); void render_color_marker(float size, const ImU32& color); - void render_groove_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance, bool is_angle = false); + void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance); + void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val); void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); void render_input_window_warning() const; @@ -369,6 +372,10 @@ private: void toggle_model_objects_visibility(); + indexed_triangle_set its_make_upper_groove_plane(); + indexed_triangle_set its_make_lower_groove_plane(float flaps_width); + indexed_triangle_set its_make_sides_groove_plane(float flaps_width); + indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(ModelObject* mo, const std::string& connector_name); }; From e8bdd9e78d0879dfb1b0ef328bb78b136652fd00 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Jul 2023 12:51:39 +0200 Subject: [PATCH 15/51] Code refactoring: Rivet is changed to Snap --- resources/icons/{rivet.svg => snap.svg} | 6 +++--- src/imgui/imconfig.h | 2 +- src/libslic3r/Model.cpp | 4 ++-- src/libslic3r/Model.hpp | 3 +-- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 22 +++++++++++----------- src/slic3r/GUI/ImGuiWrapper.cpp | 2 +- 6 files changed, 19 insertions(+), 20 deletions(-) rename resources/icons/{rivet.svg => snap.svg} (77%) diff --git a/resources/icons/rivet.svg b/resources/icons/snap.svg similarity index 77% rename from resources/icons/rivet.svg rename to resources/icons/snap.svg index db4760cb65..0242c8e3bc 100644 --- a/resources/icons/rivet.svg +++ b/resources/icons/snap.svg @@ -3,11 +3,11 @@ - - + - + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index a152252bac..99d933ea87 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -152,7 +152,7 @@ namespace ImGui // const wchar_t MmuSegmentationMarker = 0x1F; const wchar_t PlugMarker = 0x1C; const wchar_t DowelMarker = 0x1D; - const wchar_t RivetMarker = 0x1E; + const wchar_t SnapMarker = 0x1E; // Do not forget use following letters only in wstring const wchar_t DocumentationButton = 0x2600; const wchar_t DocumentationHoverButton = 0x2601; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index dd0553100e..92bb15df85 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1356,7 +1356,7 @@ void ModelVolume::apply_tolerance() double z_offset = 0.5 * static_cast(cut_info.height_tolerance); if (cut_info.connector_type == CutConnectorType::Plug || - cut_info.connector_type == CutConnectorType::Rivet) + cut_info.connector_type == CutConnectorType::Snap) z_offset -= 0.05; // add small Z offset to better preview set_offset(get_offset() + rot_norm * z_offset); @@ -1394,7 +1394,7 @@ void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& if (volume->cut_info.connector_type != CutConnectorType::Dowel) { if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { ModelVolume* vol = nullptr; - if (volume->cut_info.connector_type == CutConnectorType::Rivet) { + if (volume->cut_info.connector_type == CutConnectorType::Snap) { TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); vol = upper->add_volume(std::move(mesh)); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 7f9b354408..a14bf37705 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -224,7 +224,7 @@ private: enum class CutConnectorType : int { Plug , Dowel - , Rivet + , Snap , Undef }; @@ -240,7 +240,6 @@ enum class CutConnectorShape : int { , Square , Hexagon , Circle - , Rivet , Undef //,D-shape }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 76964983a6..2f05270b1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -186,7 +186,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_style (int(CutConnectorStyle::Prism)) , m_connector_shape_id (int(CutConnectorShape::Circle)) { - m_connector_type = CutConnectorType::Rivet; + m_connector_type = CutConnectorType::Snap; m_modes = { _u8L("Planar"), _u8L("Tongue and Groove")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -197,7 +197,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, std::map connetor_types = { {ImGui::PlugMarker , _u8L("Plug") }, {ImGui::DowelMarker, _u8L("Dowel") }, - {ImGui::RivetMarker, _u8L("Rivet") }, + {ImGui::SnapMarker, _u8L("Snap") }, }; for (auto connector : connetor_types) { std::string type_label = " " + connector.second + " "; @@ -2191,7 +2191,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) m_imgui->text(m_labels_map["Type"]); bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); - type_changed |= render_connect_type_radio_button(CutConnectorType::Rivet); + type_changed |= render_connect_type_radio_button(CutConnectorType::Snap); if (type_changed) apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); @@ -2204,8 +2204,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); m_imgui->disabled_end(); - m_imgui->disabled_begin(m_connector_type == CutConnectorType::Rivet); - if (type_changed && m_connector_type == CutConnectorType::Rivet) { + m_imgui->disabled_begin(m_connector_type == CutConnectorType::Snap); + if (type_changed && m_connector_type == CutConnectorType::Snap) { m_connector_shape_id = int(CutConnectorShape::Circle); apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); } @@ -2213,7 +2213,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); m_imgui->disabled_end(); - const float depth_min_value = m_connector_type == CutConnectorType::Rivet ? m_connector_size : -0.1f; + const float depth_min_value = m_connector_type == CutConnectorType::Snap ? m_connector_size : -0.1f; if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance, depth_min_value)) apply_selected_connectors([this, &connectors](size_t idx) { if (m_connector_depth_ratio > 0) @@ -2230,7 +2230,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) connectors[idx].radius_tolerance = 0.5f * m_connector_size_tolerance; }); - if (m_connector_type == CutConnectorType::Rivet) { + if (m_connector_type == CutConnectorType::Snap) { const std::string format = "%.0f %%"; @@ -3508,12 +3508,12 @@ void GLGizmoCut3D::reset_connectors() void GLGizmoCut3D::init_connector_shapes() { - for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug, CutConnectorType::Rivet}) + for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug, CutConnectorType::Snap}) for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prism}) { if (type == CutConnectorType::Dowel && style == CutConnectorStyle::Frustum) continue; for (const CutConnectorShape& shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) { - if (type == CutConnectorType::Rivet && shape != CutConnectorShape::Circle) + if (type == CutConnectorType::Snap && shape != CutConnectorShape::Circle) continue; const CutConnectorAttributes attribs = { type, style, shape }; indexed_triangle_set its = get_connector_mesh(attribs); @@ -3527,7 +3527,7 @@ void GLGizmoCut3D::update_connector_shape() { CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; - if (m_connector_type == CutConnectorType::Rivet) { + if (m_connector_type == CutConnectorType::Snap) { indexed_triangle_set its = get_connector_mesh(attribs); m_shapes[attribs].reset(); m_shapes[attribs].model.init_from(its); @@ -3822,7 +3822,7 @@ indexed_triangle_set GLGizmoCut3D::get_connector_mesh(CutConnectorAttributes con break; } - if (connector_attributes.type == CutConnectorType::Rivet) + if (connector_attributes.type == CutConnectorType::Snap) connector_mesh = its_make_snap(1.0, 1.0, m_snap_space_proportion, m_snap_bulge_proportion); else if (connector_attributes.style == CutConnectorStyle::Prism) connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index ec1d039888..4673b338f2 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -67,7 +67,7 @@ static const std::map font_icons = { {ImGui::InfoMarkerSmall , "notification_info" }, {ImGui::PlugMarker , "plug" }, {ImGui::DowelMarker , "dowel" }, - {ImGui::RivetMarker , "rivet" }, + {ImGui::SnapMarker , "snap" }, }; static const std::map font_icons_large = { From 9067f601d84f1e23544c4031facdf731ef8e4991 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Jul 2023 16:45:48 +0200 Subject: [PATCH 16/51] WIP: Cut with tongue and groove * Suppress to apply connectors to the model, when cut with TAG * Add grabbers to move cutPlane during Y axes, when groove angle is non-zero --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 72 +++++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 2f05270b1f..91fdcf3b4b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1116,6 +1116,8 @@ void GLGizmoCut3D::render_cut_plane_grabbers() } } + const double grabber_x_shift = 0.2 * m_grabber_connection_len; + // render CutPlaneXMove grabber if (no_xy_grabber_hovered || m_hover_id == CutPlaneXMove) @@ -1123,17 +1125,32 @@ void GLGizmoCut3D::render_cut_plane_grabbers() size = 0.5 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); color = m_hover_id == CutPlaneXMove ? ColorRGBA::RED() : m_plane.model.get_color(); - const double grabber_shift = 0.2 * m_grabber_connection_len; - - render_model(m_sphere.model, color, view_matrix * translation_transform(grabber_shift * Vec3d::UnitX()) * scale_transform(size)); + render_model(m_sphere.model, color, view_matrix * translation_transform(grabber_x_shift * Vec3d::UnitX()) * scale_transform(size)); const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - Vec3d offset = Vec3d(1.25 * size + grabber_shift, 0.0, 0.0); + Vec3d offset = Vec3d(1.25 * size + grabber_x_shift, 0.0, 0.0); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); - offset = Vec3d(-1.25 * size + grabber_shift, 0.0, 0.0); + offset = Vec3d(-1.25 * size + grabber_x_shift, 0.0, 0.0); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); } + + // render CutPlaneYMove grabber + + if (m_groove_angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) + { + size = 0.5 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color(); + + render_model(m_sphere.model, color, view_matrix * translation_transform(grabber_x_shift * Vec3d::UnitX()) * scale_transform(size)); + + const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + + Vec3d offset = Vec3d( grabber_x_shift, 1.25 * size, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); + offset = Vec3d(grabber_x_shift, -1.25 * size, 0.0); + render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); + } } } @@ -1267,9 +1284,11 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_sphere.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cone.mesh_raycaster, Transform3d::Identity())); } } @@ -1368,23 +1387,39 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - double grabber_shift = -1.75 * m_grabber_connection_len; + double grabber_x_shift = -1.75 * m_grabber_connection_len; - m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_shift * Vec3d::UnitY()) * scale_transform(size)); + m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_x_shift * Vec3d::UnitY()) * scale_transform(size)); - offset = Vec3d(1.25 * size, grabber_shift, 0.0); + offset = Vec3d(1.25 * size, grabber_x_shift, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - offset = Vec3d(-1.25 * size, grabber_shift, 0.0); + offset = Vec3d(-1.25 * size, grabber_x_shift, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - grabber_shift = 0.2 * m_grabber_connection_len; + // set grabbers to un-hovered size - m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_shift * Vec3d::UnitX()) * scale_transform(size)); + const double def_size = size * 0.5; + scale = Vec3d(0.75 * def_size, 0.75 * def_size, 1.8 * def_size); - offset = Vec3d(1.25 * size + grabber_shift, 0.0, 0.0); + const double offset_shift = def_size * 0.25; + grabber_x_shift = 0.2 * m_grabber_connection_len; + + offset = Vec3d(offset_shift + grabber_x_shift, 0.0, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - offset = Vec3d(-1.25 * size + grabber_shift, 0.0, 0.0); + offset = Vec3d(-offset_shift + grabber_x_shift, 0.0, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + + if (m_groove_angle > 0.0f) { + offset = Vec3d(grabber_x_shift, offset_shift, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + offset = Vec3d(grabber_x_shift, -offset_shift, 0.0); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + } + else { + // discard transformation for CutPlaneYMove grabbers + m_raycasters[id++]->set_transform(Transform3d::Identity()); + m_raycasters[id++]->set_transform(Transform3d::Identity()); + } } } } @@ -1454,11 +1489,12 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& void GLGizmoCut3D::dragging_grabber_move(const GLGizmoBase::UpdateData &data) { const Vec3d grabber_init_pos = m_hover_id == CutPlaneXMove ? 0.2 * m_grabber_connection_len * Vec3d::UnitX(): + m_hover_id == CutPlaneYMove ? 0.0 * Vec3d::UnitY() : (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ(); const Vec3d starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * grabber_init_pos; double projection = 0.0; - Vec3d starting_vec = m_rotation_m * (m_hover_id == CutPlaneXMove ? Vec3d::UnitX() : Vec3d::UnitZ()); + Vec3d starting_vec = m_rotation_m * (m_hover_id == CutPlaneXMove ? Vec3d::UnitX() : m_hover_id == CutPlaneYMove ? Vec3d::UnitY() : Vec3d::UnitZ()); if (starting_vec.norm() != 0.0) { const Vec3d mouse_dir = data.mouse_ray.unit_vector(); // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position @@ -1553,7 +1589,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove) + if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove || m_hover_id == CutPlaneYMove) dragging_grabber_move(data); else if (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation) dragging_grabber_rotation(data); @@ -1583,7 +1619,7 @@ void GLGizmoCut3D::on_stop_dragging() Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); m_start_dragging_m = m_rotation_m; } - else if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove) { + else if (m_hover_id == Z || m_hover_id == CutPlane || m_hover_id == CutPlaneXMove|| m_hover_id == CutPlaneYMove) { if (m_was_cut_plane_dragged) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); m_ar_plane_center = m_plane_center; @@ -3006,6 +3042,8 @@ bool GLGizmoCut3D::has_valid_contour() const void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, int &dowels_count) { + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return; if (m_connector_mode == CutConnectorMode::Manual) { clear_selection(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ff52ddc1ba..30663a99c7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -32,6 +32,7 @@ class GLGizmoCut3D : public GLGizmoBase CutPlane, CutPlaneZRotation, CutPlaneXMove, + CutPlaneYMove, Count, }; From 34af44b4bf649b1ddc17c82a0082221fae704561 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Jul 2023 17:32:02 +0200 Subject: [PATCH 17/51] WIP: Cut with TAG Added experimental rendering for the cutPlane mesh --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 306 ++++++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 + 2 files changed, 308 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 91fdcf3b4b..6b2968bb0e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -187,6 +187,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_shape_id (int(CutConnectorShape::Circle)) { m_connector_type = CutConnectorType::Snap; + m_mode = size_t(CutMode::cutTongueAndGroove); m_modes = { _u8L("Planar"), _u8L("Tongue and Groove")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -487,6 +488,10 @@ bool GLGizmoCut3D::render_cut_mode_combo() m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); } + if (m_use_TAG_mesh) { + m_plane.reset(); + on_unregister_raycasters_for_picking(); + } reset_cut_by_contours(); } @@ -861,6 +866,263 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width }; } + + +indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() +{ + // values for calculation + + const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle); + + const float groove_half_width_upper = 0.5f * (m_groove_width); + const float groove_half_width_lower = 0.5f * (m_groove_width + flaps_width); + + const float cut_plane_radius = 1.5f * float(m_radius); + const float cut_plane_length = 1.5f * cut_plane_radius; + + const float groove_half_depth = 0.5f * m_groove_depth; + + const float x = 0.5f * cut_plane_radius; + const float y = 0.5f * cut_plane_length; + float z_upper = groove_half_depth; + float z_lower = -groove_half_depth; + + const float proj = y * tan(m_groove_angle); + + float ext_upper_x = groove_half_width_upper + proj; // upper_x extension + float ext_lower_x = groove_half_width_lower + proj; // lower_x extension + + float nar_upper_x = groove_half_width_upper - proj; // upper_x narrowing + float nar_lower_x = groove_half_width_lower - proj; // lower_x narrowing + + const float cut_plane_thiknes = 0.02f;// 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut_plane_thiknes + + + // Different cases of groove plane: + + // groove is open + + if (groove_half_width_upper > proj && groove_half_width_lower > proj) { + indexed_triangle_set mesh; + + auto get_vertices = [x, y](float z_upper, float z_lower, float nar_upper_x, float nar_lower_x, float ext_upper_x, float ext_lower_x) { + return std::vector({ + // upper left part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {-nar_upper_x, y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {-nar_lower_x, y, z_lower}, {nar_lower_x, y, z_lower}, {ext_lower_x, -y, z_lower}, + // upper right part vertices + {ext_upper_x, -y, z_upper}, {nar_upper_x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper} + }); + }; + + mesh.vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); + mesh.vertices.reserve(2 * mesh.vertices.size()); + + z_upper -= cut_plane_thiknes; + z_lower -= cut_plane_thiknes; + if (m_use_TAG_mesh_full) { + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + + nar_upper_x += under_x_shift; + nar_lower_x += under_x_shift; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; + + std::vector vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {5,4,7}, {5,7,6}, // lower part + {3,4,5}, {3,5,2}, // left side + {9,6,8}, {8,6,7}, // right side + {1,0,2}, {2,0,3}, // upper left part + {9,8,10}, {10,8,11}, // upper right part + // under view + {20,21,22}, {20,22,23}, // upper right part + {12,13,14}, {12,14,15}, // upper left part + {18,21,20}, {18,20,19}, // right side + {16,15,14}, {16,14,17}, // left side + {16,17,18}, {16,18,19}, // lower part + // left edge + {1,13,0}, {0,13,12}, + // front edge + {0,12,3}, {3,12,15}, {3,15,4}, {4,15,16}, {4,16,7}, {7,16,19}, {7,19,20}, {7,20,8}, {8,20,11}, {11,20,23}, + // right edge + {11,23,10}, {10,23,22}, + // back edge + {1,13,2}, {2,13,14}, {2,14,17}, {2,17,5}, {5,17,6}, {6,17,18}, {6,18,9}, {9,18,21}, {9,21,10}, {10,21,22} + }; + } + else { + std::vector vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {5,4,7}, {5,7,6}, // lower part + {3,4,2}, {2,4,5}, // left side + {9,6,8}, {8,6,7}, // right side + {1,0,2}, {2,0,3}, // upper left part + {9,8,10}, {10,8,11}, // upper right part + // under view + {16,15,14}, {16,14,17}, // left side + {18,21,20}, {18,20,19}, // right side + {16,17,18}, {16,18,19} // lower part + }; + } + return mesh; + } + + float cross_pt_upper_y = groove_half_width_upper / tan(m_groove_angle); + + // groove is closed + + if (groove_half_width_upper < proj && groove_half_width_lower < proj) { + float cross_pt_lower_y = groove_half_width_lower / tan(m_groove_angle); + + indexed_triangle_set mesh; + + auto get_vertices = [x, y](float z_upper, float z_lower, float cross_pt_upper_y, float cross_pt_lower_y, float ext_upper_x, float ext_lower_x) { + return std::vector({ + // upper part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper}, + {ext_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {0.f, cross_pt_lower_y, z_lower}, {ext_lower_x, -y, z_lower} + }); + }; + + mesh.vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); + mesh.vertices.reserve(2 * mesh.vertices.size()); + + z_upper -= cut_plane_thiknes; + z_lower -= cut_plane_thiknes; + + if (m_use_TAG_mesh_full) { + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + + cross_pt_upper_y += cut_plane_thiknes; + cross_pt_lower_y += cut_plane_thiknes; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; + + std::vector vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {8,7,9}, // lower part + {5,8,6}, {6,8,7}, // left side + {4,9,8}, {4,8,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {10,11,16}, {16,11,15}, {15,11,12}, {15,12,14}, {14,12,13}, // upper part + {18,15,14}, {14,18,19}, // right side + {17,16,15}, {17,15,18}, // left side + {17,18,19}, // lower part + // left edge + {1,11,0}, {0,11,10}, + // front edge + {0,10,6}, {6,10,16}, {6,17,16}, {6,7,17}, {7,17,19}, {7,19,9}, {4,14,19}, {4,19,9}, {4,14,13}, {4,13,3}, + // right edge + {3,13,12}, {3,12,2}, + // back edge + {2,12,11}, {2,11,1} + }; + } + else { + std::vector vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + mesh.indices = { + // above view + {8,7,9}, // lower part + {5,8,6}, {6,8,7}, // left side + {4,9,8}, {4,8,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {18,15,14}, {14,18,19}, // right side + {17,16,15}, {17,15,18}, // left side + {17,18,19}, // lower part + }; + } + return mesh; + + } + + // groove is closed from the roof + + indexed_triangle_set mesh; + mesh.vertices = { + // upper part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper}, + {ext_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {-nar_lower_x, y, z_lower}, {nar_lower_x, y, z_lower}, {ext_lower_x, -y, z_lower} + }; + + mesh.vertices.reserve(2 * mesh.vertices.size() + 1); + + z_upper -= cut_plane_thiknes; + z_lower -= cut_plane_thiknes; + + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + + nar_lower_x += under_x_shift; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; + + std::vector vertices = { + // upper part vertices + {-x, -y, z_upper}, {-x, y, z_upper}, {x, y, z_upper}, {x, -y, z_upper}, + {ext_upper_x, -y, z_upper}, {under_x_shift, cross_pt_upper_y, z_upper}, {-under_x_shift, cross_pt_upper_y, z_upper}, {-ext_upper_x, -y, z_upper}, + // lower part vertices + {-ext_lower_x, -y, z_lower}, {-nar_lower_x, y, z_lower}, {nar_lower_x, y, z_lower}, {ext_lower_x, -y, z_lower} + }; + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + + if (m_use_TAG_mesh_full) { + mesh.indices = { + // above view + {8,7,10}, {8,10,9}, // lower part + {5,8,7}, {5,7,6}, // left side + {4,10,9}, {4,9,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {11,12,18}, {18,12,17}, {17,12,16}, {16,12,13}, {16,13,15}, {15,13,14}, // upper part + {21,16,15}, {21,15,22}, // right side + {19,18,17}, {19,17,20}, // left side + {19,20,21}, {19,21,22}, // lower part + // left edge + {1,12,11}, {1,11,0}, + // front edge + {0,11,18}, {0,18,6}, {7,19,18}, {7,18,6}, {7,19,22}, {7,22,10}, {10,22,15}, {10,15,4}, {4,15,14}, {4,14,3}, + // right edge + {3,14,13}, {3,14,2}, + // back edge + {2,13,12}, {2,12,1}, {5,16,21}, {5,21,9}, {9,21,20}, {9,20,8}, {5,17,20}, {5,20,8} + }; + } + else { + mesh.indices = { + // above view + {8,7,10}, {8,10,9}, // lower part + {5,8,7}, {5,7,6}, // left side + {4,10,9}, {4,9,5}, // right side + {1,0,6}, {1,6,5}, {1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {21,16,15}, {21,15,22}, // right side + {19,18,17}, {19,17,20}, // left side + {19,20,21}, {19,21,22}, // lower part + }; + } + + return mesh; +} + void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader) { const Camera & camera = wxGetApp().plater()->get_camera(); @@ -925,6 +1187,17 @@ void GLGizmoCut3D::render_cut_plane() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (m_use_TAG_mesh) { + ColorRGBA cp_clr = can_perform_cut() && has_valid_contour() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR; + if (m_mode == size_t(CutMode::cutTongueAndGroove)) + cp_clr.a(cp_clr.a() - 0.1f); + m_plane.model.set_color(cp_clr); + + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + shader->set_uniform("view_model_matrix", view_model_matrix); + m_plane.model.render(); + } + else { m_plane.model.set_color(can_perform_cut() && has_valid_contour() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR); if (m_mode == size_t(CutMode::cutPlanar)) { @@ -934,7 +1207,7 @@ void GLGizmoCut3D::render_cut_plane() } else if (m_mode == size_t(CutMode::cutTongueAndGroove)) render_cut_plate_for_tongue_and_groove(shader); - + } glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -1731,7 +2004,7 @@ void GLGizmoCut3D::update_bb() // input params for cut with tongue and groove m_groove_depth = m_groove_depth_init = 0.5f * float(get_grabber_mean_size(m_bounding_box)); m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; - m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f;// 0.25f * float(PI); + m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f; m_groove_angle = m_groove_angle_init = 0.f; m_plane.reset(); m_cone.reset(); @@ -1768,7 +2041,13 @@ void GLGizmoCut3D::init_picking_models() if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box); - indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); + indexed_triangle_set its; + if (m_use_TAG_mesh) + its = m_mode == size_t(CutMode::cutTongueAndGroove) ? its_make_groove_plane() : + its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); + else + its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); + m_plane.model.init_from(its); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } @@ -2097,12 +2376,23 @@ void GLGizmoCut3D::on_render() void GLGizmoCut3D::render_debug_input_window(float x) { - return; m_imgui->begin(wxString("DEBUG")); ImVec2 pos = ImGui::GetWindowPos(); pos.x = x; ImGui::SetWindowPos(pos, ImGuiCond_Always); + + bool is_changed = m_imgui->checkbox(("Render Cut plane as a one mesh"), m_use_TAG_mesh); + is_changed |= m_imgui->checkbox(("Render Cut plane as a full mesh"), m_use_TAG_mesh_full); + + if (is_changed) + { + m_plane.reset(); + on_unregister_raycasters_for_picking(); + } + + m_imgui->end(); + return; /* static bool hide_clipped = false; static bool fill_cut = false; @@ -2483,6 +2773,10 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in m_imgui->disabled_end(); if (is_changed) { + if (m_use_TAG_mesh) { + m_plane.reset(); + on_unregister_raycasters_for_picking(); + } reset_cut_by_contours(); // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } @@ -2519,6 +2813,10 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->disabled_end(); if (is_changed) { + if (m_use_TAG_mesh) { + m_plane.reset(); + on_unregister_raycasters_for_picking(); + } reset_cut_by_contours(); // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 30663a99c7..d611dca63c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -235,6 +235,11 @@ class GLGizmoCut3D : public GLGizmoBase std::map m_labels_map; + // Debug values + bool m_use_TAG_mesh {true}; + bool m_use_TAG_mesh_full {true}; + // + public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -376,6 +381,7 @@ private: indexed_triangle_set its_make_upper_groove_plane(); indexed_triangle_set its_make_lower_groove_plane(float flaps_width); indexed_triangle_set its_make_sides_groove_plane(float flaps_width); + indexed_triangle_set its_make_groove_plane(); indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(ModelObject* mo, const std::string& connector_name); From 9c254b6782f9207a78913693f467b7d45d674bc6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Jul 2023 17:23:01 +0200 Subject: [PATCH 18/51] CutGizmo: Fixed a moving of the cut plane on dragging. There no unexpected jumping any more --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 37 +++++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 ++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 6b2968bb0e..0264531d5a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -290,6 +290,14 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.Moving()) return false; + if (m_hover_id >= CutPlane && mouse_event.LeftDown() && !m_connectors_editing) { + // before processing of a use_grabbers(), detect start move position as a projection of mouse position to the cut plane + Vec3d pos; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_pos, pos, pos_world, false)) + m_cut_plane_start_move_pos = pos_world; + } + if (use_grabbers(mouse_event)) { if (m_hover_id >= m_connectors_group_id) { if (mouse_event.LeftDown() && !mouse_event.CmdDown() && !mouse_event.AltDown()) @@ -304,7 +312,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) // disable / enable current contour Vec3d pos; Vec3d pos_world; - m_was_contour_selected = unproject_on_cut_plane(mouse_pos.cast(), pos, pos_world, false); + m_was_contour_selected = unproject_on_cut_plane(mouse_pos.cast(), pos, pos_world); if (m_was_contour_selected) { // Following would inform the clipper about the mouse click, so it can // toggle the respective contour as disabled. @@ -318,6 +326,14 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) flip_cut_plane(); } + if (m_hover_id >= CutPlane && mouse_event.Dragging() && !m_connectors_editing) { + // if we continue to dragging a cut plane, than update a start move position as a projection of mouse position to the cut plane after processing of a use_grabbers() + Vec3d pos; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_pos, pos, pos_world, false)) + m_cut_plane_start_move_pos = pos_world; + } + toggle_model_objects_visibility(); return true; } @@ -1761,17 +1777,19 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(GrabberID axis, const Linef3& void GLGizmoCut3D::dragging_grabber_move(const GLGizmoBase::UpdateData &data) { - const Vec3d grabber_init_pos = m_hover_id == CutPlaneXMove ? 0.2 * m_grabber_connection_len * Vec3d::UnitX(): - m_hover_id == CutPlaneYMove ? 0.0 * Vec3d::UnitY() : - (m_hover_id == CutPlane ? 0. : m_grabber_connection_len) * Vec3d::UnitZ(); - const Vec3d starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * grabber_init_pos; - double projection = 0.0; + Vec3d starting_drag_position; + if (m_hover_id == Z) + starting_drag_position = translation_transform(m_plane_center) * m_rotation_m * (m_grabber_connection_len * Vec3d::UnitZ()); + else + starting_drag_position = m_cut_plane_start_move_pos; + + double projection = 0.0; Vec3d starting_vec = m_rotation_m * (m_hover_id == CutPlaneXMove ? Vec3d::UnitX() : m_hover_id == CutPlaneYMove ? Vec3d::UnitY() : Vec3d::UnitZ()); if (starting_vec.norm() != 0.0) { const Vec3d mouse_dir = data.mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing through the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebraic form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal const Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; @@ -3766,7 +3784,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 respect_disabled_contour/* = true*/) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world, bool respect_contours/* = true*/) { const float sla_shift = m_c->selection_info()->get_sla_shift(); @@ -3803,6 +3821,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po } }*/ + if (respect_contours) { // Do not react to clicks outside a contour (or inside a contour that is ignored) int cont_id = m_c->object_clipper()->is_projection_inside_cut(hit); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index d611dca63c..d461948735 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -58,6 +58,7 @@ class GLGizmoCut3D : public GLGizmoBase double m_radius{ 0.0 }; double m_grabber_radius{ 0.0 }; double m_grabber_connection_len{ 0.0 }; + Vec3d m_cut_plane_start_move_pos {Vec3d::Zero()}; double m_snap_coarse_in_radius{ 0.0 }; double m_snap_coarse_out_radius{ 0.0 }; @@ -244,7 +245,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 respect_disabled_contour = true); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_contours = 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; } From 724d2aec20027173ca2f793d5d0fd2bcc28f353c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 19 Jun 2023 10:09:08 +0200 Subject: [PATCH 19/51] Fix for SPE-1788 - Redundant sidebar layout on object move --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 28 ++++++++++++++++------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 1 + 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index ae40e0f766..9ea6198f7a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -503,6 +503,11 @@ void ObjectManipulation::Show(const bool show) } m_word_local_combo->Show(show_world_local_combo); m_empty_str->Show(!show_world_local_combo); + + m_skew_label->Show(m_show_skew); + m_reset_skew_button->Show(m_show_skew); + + m_parent->Layout(); } } @@ -795,15 +800,22 @@ void ObjectManipulation::update_reset_buttons_visibility() m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap); m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0)); m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : ""); - m_reset_skew_button->Show(show_skew); - m_skew_label->Show(show_skew); - // Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time - Sidebar& panel = wxGetApp().sidebar(); - if (!panel.IsFrozen()) { - panel.Freeze(); - panel.Layout(); - panel.Thaw(); + if (m_show_skew == show_skew) + get_sizer()->Layout(); + else { + // Call sidebar layout only if it's really needed, + // it means, when we show/hide additional line for skew information + m_show_skew = show_skew; + m_reset_skew_button->Show(m_show_skew); + m_skew_label->Show(m_show_skew); + // Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time + Sidebar& panel = wxGetApp().sidebar(); + if (!panel.IsFrozen()) { + panel.Freeze(); + panel.Layout(); + panel.Thaw(); + } } }); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 139171d999..6b5dfe5447 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -169,6 +169,7 @@ private: bool m_is_enabled { true }; bool m_is_enabled_size_and_scale { true }; + bool m_show_skew { false }; public: ObjectManipulation(wxWindow* parent); From bd43118148b9f1f06f7b3aaec9fe8090abb9bcb0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 15 Jun 2023 14:00:43 +0200 Subject: [PATCH 20/51] Fix of Asserts and incompatible filament selection, when hide/show template filaments (SPE-1786) --- src/libslic3r/PresetBundle.cpp | 4 +++- src/slic3r/GUI/Plater.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 730574af13..af57c9314d 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -675,7 +675,9 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p void PresetBundle::export_selections(AppConfig &config) { assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() >= 1); - assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() > 1 || filaments.get_selected_preset().alias == extruders_filaments.front().get_selected_preset()->alias); + // #ysFIXME_delete_after_test !All filament selections are always saved in extruder_filaments (for MM and SM printers), + // so there is no need to control a correspondence between filaments and extruders_filaments + //assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() > 1 || filaments.get_selected_preset().alias == extruders_filaments.front().get_selected_preset()->alias); config.clear_section("presets"); config.set("presets", "print", prints.get_selected_preset_name()); config.set("presets", "filament", extruders_filaments.front().get_selected_preset_name()); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 902eeb6ded..af6fdc3bf4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -7133,7 +7133,9 @@ void Plater::force_filament_cb_update() // Update preset comboboxes on sidebar and filaments tab p->sidebar->update_presets(Preset::TYPE_FILAMENT); - wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filaments.get_selected_preset_name()); + + TabFilament* tab = dynamic_cast(wxGetApp().get_tab(Preset::TYPE_FILAMENT)); + tab->select_preset(wxGetApp().preset_bundle->extruders_filaments[tab->get_active_extruder()].get_selected_preset_name()); } void Plater::force_print_bed_update() From 5de69b962ecadf1002b5bfde5e028a32258ee27c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 31 Jul 2023 16:04:41 +0200 Subject: [PATCH 21/51] MeshRaycaster: added a function to check for mesh-line intersections --- src/slic3r/GUI/MeshUtils.cpp | 9 ++++++--- src/slic3r/GUI/MeshUtils.hpp | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 60ea6c856d..88a4f29728 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -465,14 +465,17 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& -bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const +bool MeshRaycaster::intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const { - point = trafo.inverse() * point; + Transform3d trafo_inv = trafo.inverse(); + Vec3d to = trafo_inv * (point + direction); + point = trafo_inv * point; + direction = (to-point).normalized(); std::vector hits = m_emesh.query_ray_hits(point, direction); std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); - return !hits.empty() && !neg_hits.empty(); + return !hits.empty() || !neg_hits.empty(); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 3645ecc026..a7dd3d7f75 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -187,7 +187,9 @@ public: const AABBMesh &get_aabb_mesh() const { return m_emesh; } - bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; + // Given a point and direction in world coords, returns whether the respective line + // intersects the mesh if it is transformed into world by trafo. + bool intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const; // Given a vector of points in woorld coordinates, this returns vector // of indices of points that are visible (i.e. not cut by clipping plane From c239c54dc691cbf482050d74c63996908a5f8adb Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 1 Aug 2023 10:58:31 +0200 Subject: [PATCH 22/51] CutGizmo: Bug fixing and improvements: * Get correct labels width form all label * Set Plane mode as default for cut * Fixed tooltip for CutPlane in TaG mode * Fixed Crash when "Instances" item is selected with open CutGizmo * Fixed move limits for CutPlane * TaG mode: Suppressed perform a cut when CutPlane is out of object * Update warning info lines when switch between modes * Don't allow to switch to TaG mode, when cut has connectors --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 55 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 0264531d5a..9f060c778c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -186,9 +186,6 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_style (int(CutConnectorStyle::Prism)) , m_connector_shape_id (int(CutConnectorShape::Circle)) { - m_connector_type = CutConnectorType::Snap; - m_mode = size_t(CutMode::cutTongueAndGroove); - m_modes = { _u8L("Planar"), _u8L("Tongue and Groove")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") }; @@ -229,6 +226,10 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, {"Shape" , _u8L("Shape")}, {"Depth" , _u8L("Depth")}, {"Size" , _u8L("Size")}, + {"Groove" , _u8L("Groove")}, + {"Width" , _u8L("Width")}, + {"Flaps Angle" , _u8L("Flaps Angle")}, + {"Groove Angle" , _u8L("Groove Angle")}, }; // update_connector_shape(); @@ -256,10 +257,14 @@ std::string GLGizmoCut3D::get_tooltip() const return tooltip; } - if (!m_dragging && m_hover_id == CutPlane) + if (!m_dragging && m_hover_id == CutPlane) { + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return _u8L("Click to flip the cut plane\n" + "Drag to move the cut plane"); return _u8L("Click to flip the cut plane\n" "Drag to move the cut plane\n" "Right-click a part to assign it to the other side"); + } if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y || m_hover_id == CutPlaneZRotation)) { std::string axis = m_hover_id == X ? "X" : m_hover_id == Y ? "Y" : "Z"; @@ -509,6 +514,8 @@ bool GLGizmoCut3D::render_cut_mode_combo() on_unregister_raycasters_for_picking(); } reset_cut_by_contours(); + update_clipper(); + check_and_update_connectors_state(); } return is_changed; @@ -1931,7 +1938,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=fa bool can_set_center_pos = false; { - double limit_val = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove_depth) : 0.5; + double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove_depth) : */0.5; if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val) can_set_center_pos = true; else { @@ -2020,7 +2027,7 @@ void GLGizmoCut3D::update_bb() m_snap_fine_out_radius = m_grabber_connection_len * 1.15; // input params for cut with tongue and groove - m_groove_depth = m_groove_depth_init = 0.5f * float(get_grabber_mean_size(m_bounding_box)); + m_groove_depth = m_groove_depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box))); m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f; m_groove_angle = m_groove_angle_init = 0.f; @@ -2703,8 +2710,10 @@ void GLGizmoCut3D::reset_cut_by_contours() return; process_contours(); } - else + else { + m_invalid_groove = false; toggle_model_objects_visibility(); + } } void GLGizmoCut3D::process_contours() @@ -2713,6 +2722,8 @@ void GLGizmoCut3D::process_contours() const ModelObjectPtrs& model_objects = selection.get_model()->objects; const int instance_idx = selection.get_instance_idx(); + if (instance_idx < 0) + return; const int object_idx = selection.get_object_idx(); wxBusyCursor wait; @@ -2721,6 +2732,8 @@ void GLGizmoCut3D::process_contours() ModelObjectPtrs cut_objects = perform_cut_with_groove(model_objects[object_idx], true); if (!cut_objects.empty() && !m_invalid_groove) m_part_selection = PartSelection(cut_objects.front(), instance_idx); + else + m_invalid_groove = true; } else { reset_cut_by_contours(); @@ -2853,9 +2866,12 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) get_wraped_wxString(_L("Hold SHIFT key to draw a cut line"), 40)); ImGui::Separator(); - // WIP : cut plane mode + const bool has_connectors = !connectors.empty(); + + m_imgui->disabled_begin(has_connectors); if (render_cut_mode_combo()) mode = CutMode(m_mode); + m_imgui->disabled_end(); render_build_size(); @@ -2864,7 +2880,6 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::SameLine(); render_move_center_input(Z); ImGui::SameLine(); - const bool has_connectors = !connectors.empty(); const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center); m_imgui->disabled_begin(is_cut_plane_init); @@ -2898,13 +2913,12 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) } else if (mode == CutMode::cutTongueAndGroove) { ImGui::Separator(); - ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Groove") + ": "); - render_groove_float_input(_u8L("Depth"), m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); - render_groove_float_input(_u8L("Width"), m_groove_width, m_groove_width_init, m_groove_width_tolerance); - render_groove_angle_input(_u8L("Flaps Angle"), m_groove_flaps_angle, m_groove_flaps_angle_init, 30.f, 120.f); - render_groove_angle_input(_u8L("Groove Angle"), m_groove_angle, m_groove_angle_init, 0.f, 15.f); - - m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); + ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": "); + render_groove_float_input(m_labels_map["Depth"], m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); + render_groove_float_input(m_labels_map["Width"], m_groove_width, m_groove_width_init, m_groove_width_tolerance); + render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove_flaps_angle, m_groove_flaps_angle_init, 30.f, 120.f); + render_groove_angle_input(m_labels_map["Groove Angle"], m_groove_angle, m_groove_angle_init, 0.f, 15.f); +// m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); } ImGui::Separator(); @@ -2974,7 +2988,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) add_vertical_scaled_interval(0.75f); - m_imgui->disabled_begin(has_connectors || m_part_selection.valid()); + m_imgui->disabled_begin(has_connectors || m_part_selection.valid() || mode == CutMode::cutTongueAndGroove); ImGuiWrapper::text(_L("Cut into") + ":"); if (m_part_selection.valid()) @@ -3222,6 +3236,8 @@ void GLGizmoCut3D::check_and_update_connectors_state() { m_info_stats.invalidate(); m_invalid_connectors_idxs.clear(); + if (CutMode(m_mode) != CutMode::cutPlanar) + return; const ModelObject* mo = m_c->selection_info()->model_object(); auto inst_id = m_c->selection_info()->get_active_instance(); if (inst_id < 0) @@ -3262,7 +3278,8 @@ void GLGizmoCut3D::render_connectors() { ::glEnable(GL_DEPTH_TEST); - if (cut_line_processing() || + if (cut_line_processing() || + CutMode(m_mode) != CutMode::cutPlanar || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) return; @@ -3341,7 +3358,7 @@ bool GLGizmoCut3D::can_perform_cut() const if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); - return flaps_width < m_groove_width && !m_invalid_groove; + return flaps_width < m_groove_width && !m_invalid_groove && has_valid_contour(); } if (m_part_selection.valid()) From 0c686f1ff96c7157f39f891411432673277128da Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 1 Aug 2023 16:15:51 +0200 Subject: [PATCH 23/51] CutGizmo improvements: Reworked CutPlane grabber for TaG mode --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 73 +++++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9f060c778c..717f42d6e7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1334,7 +1334,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() const bool no_xy_dragging = m_dragging && m_hover_id == CutPlane; - if (!no_xy_dragging && m_hover_id != CutPlaneZRotation) { + if (!no_xy_dragging && m_hover_id != CutPlaneZRotation && m_hover_id != CutPlaneXMove && m_hover_id != CutPlaneYMove) { render_grabber_connection(GRABBER_COLOR, view_matrix); // render sphere grabber @@ -1412,40 +1412,42 @@ void GLGizmoCut3D::render_cut_plane_grabbers() } } - const double grabber_x_shift = 0.2 * m_grabber_connection_len; + const double xy_connection_len = 0.75 * m_grabber_connection_len; // render CutPlaneXMove grabber if (no_xy_grabber_hovered || m_hover_id == CutPlaneXMove) { - size = 0.5 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); color = m_hover_id == CutPlaneXMove ? ColorRGBA::RED() : m_plane.model.get_color(); - render_model(m_sphere.model, color, view_matrix * translation_transform(grabber_x_shift * Vec3d::UnitX()) * scale_transform(size)); + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(0.5 * PI * Vec3d::UnitY()), 0.75); - const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + Vec3d offset = xy_connection_len * Vec3d::UnitX() - 0.5 * size * Vec3d::Ones(); + render_model(m_cube.model, color, view_matrix * translation_transform(offset) * scale_transform(size)); - Vec3d offset = Vec3d(1.25 * size + grabber_x_shift, 0.0, 0.0); + const Vec3d cone_scale = Vec3d(0.5 * size, 0.5 * size, 1.8 * size); + + offset = (size + xy_connection_len) * Vec3d::UnitX(); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); - offset = Vec3d(-1.25 * size + grabber_x_shift, 0.0, 0.0); - render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); } // render CutPlaneYMove grabber if (m_groove_angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) { - size = 0.5 * (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); + size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color(); - render_model(m_sphere.model, color, view_matrix * translation_transform(grabber_x_shift * Vec3d::UnitX()) * scale_transform(size)); + render_grabber_connection(GRABBER_COLOR, view_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitX()), 0.75); - const Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + Vec3d offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones(); + render_model(m_cube.model, color, view_matrix * translation_transform(offset) * scale_transform(size)); - Vec3d offset = Vec3d( grabber_x_shift, 1.25 * size, 0.0); + const Vec3d cone_scale = Vec3d(0.5 * size, 0.5 * size, 1.8 * size); + + offset = (size + xy_connection_len) * Vec3d::UnitY(); render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); - offset = Vec3d(grabber_x_shift, -1.25 * size, 0.0); - render_model(m_cone.model, color, view_matrix * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); } } } @@ -1580,10 +1582,10 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_cone.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cube.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneXMove, *m_cone.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cube.mesh_raycaster, Transform3d::Identity())); m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneYMove, *m_cone.mesh_raycaster, Transform3d::Identity())); } } @@ -1683,33 +1685,28 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - double grabber_x_shift = -1.75 * m_grabber_connection_len; + double grabber_y_shift = -1.75 * m_grabber_connection_len; - m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_x_shift * Vec3d::UnitY()) * scale_transform(size)); + m_raycasters[id++]->set_transform(trafo * translation_transform(grabber_y_shift * Vec3d::UnitY()) * scale_transform(size)); - offset = Vec3d(1.25 * size, grabber_x_shift, 0.0); + offset = Vec3d(1.25 * size, grabber_y_shift, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - offset = Vec3d(-1.25 * size, grabber_x_shift, 0.0); + offset = Vec3d(-1.25 * size, grabber_y_shift, 0.0); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - // set grabbers to un-hovered size + const double xy_connection_len = 0.75 * m_grabber_connection_len; + const Vec3d cone_scale = Vec3d(0.5 * size, 0.5 * size, 1.8 * size); - const double def_size = size * 0.5; - scale = Vec3d(0.75 * def_size, 0.75 * def_size, 1.8 * def_size); - - const double offset_shift = def_size * 0.25; - grabber_x_shift = 0.2 * m_grabber_connection_len; - - offset = Vec3d(offset_shift + grabber_x_shift, 0.0, 0.0); - m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); - offset = Vec3d(-offset_shift + grabber_x_shift, 0.0, 0.0); - m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitY()) * scale_transform(scale)); + offset = xy_connection_len * Vec3d::UnitX() - 0.5 * size * Vec3d::Ones(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size)); + offset = (size + xy_connection_len) * Vec3d::UnitX(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); if (m_groove_angle > 0.0f) { - offset = Vec3d(grabber_x_shift, offset_shift, 0.0); - m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); - offset = Vec3d(grabber_x_shift, -offset_shift, 0.0); - m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitX()) * scale_transform(scale)); + offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size)); + offset = (size + xy_connection_len) * Vec3d::UnitY(); + m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(-0.5 * PI * Vec3d::UnitX()) * scale_transform(cone_scale)); } else { // discard transformation for CutPlaneYMove grabbers @@ -2034,6 +2031,7 @@ void GLGizmoCut3D::update_bb() m_plane.reset(); m_cone.reset(); m_sphere.reset(); + m_cube.reset(); m_grabber_connection.reset(); m_circle.reset(); m_scale.reset(); @@ -2063,6 +2061,11 @@ void GLGizmoCut3D::init_picking_models() m_sphere.model.init_from(its); m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } + if (!m_cube.model.is_initialized()) { + indexed_triangle_set its = its_make_cube(1., 1., 1.); + m_cube.model.init_from(its); + m_cube.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index d461948735..db62cacdd1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -83,6 +83,7 @@ class GLGizmoCut3D : public GLGizmoBase PickingModel m_plane; PickingModel m_sphere; PickingModel m_cone; + PickingModel m_cube; std::map m_shapes; std::vector> m_raycasters; From 75eccfd6507e4cb446569e2d4a53bb64158c5521 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 1 Aug 2023 16:23:18 +0200 Subject: [PATCH 24/51] Added new type of SceneRaycaster (FallbackGizmo) to add possibility to split the Gizmo grabbers to two priority of "raycastering". meshes with Gizmo type has highest priority then FallbackGizmo. + CutGizmo: Use SceneRaycaster type for CutPlane grabber --- src/slic3r/GUI/GLCanvas3D.cpp | 21 +++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 3 ++- src/slic3r/GUI/SceneRaycaster.cpp | 22 ++++++++++++++++++++++ src/slic3r/GUI/SceneRaycaster.hpp | 9 +++++++-- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index debdc2054d..687317c390 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2684,6 +2684,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (curr_gizmo != nullptr) curr_gizmo->unregister_raycasters_for_picking(); m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo); + if (curr_gizmo != nullptr && !m_selection.is_empty()) + curr_gizmo->register_raycasters_for_picking(); + m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::FallbackGizmo); if (curr_gizmo != nullptr && !m_selection.is_empty()) curr_gizmo->register_raycasters_for_picking(); @@ -5744,6 +5747,7 @@ void GLCanvas3D::_picking_pass() break; } case SceneRaycaster::EType::Gizmo: + case SceneRaycaster::EType::FallbackGizmo: { const Size& cnv_size = get_canvas_size(); const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && @@ -5776,6 +5780,7 @@ void GLCanvas3D::_picking_pass() { case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; } case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; } + case SceneRaycaster::EType::FallbackGizmo: { object_type = "Gizmo2 element"; break; } case SceneRaycaster::EType::Volume: { if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower) @@ -5830,6 +5835,8 @@ void GLCanvas3D::_picking_pass() add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.fallback_gizmos_count(), (int)m_scene_raycaster.active_fallback_gizmos_count()); + add_strings_row_to_table("Gizmo2 elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -5847,6 +5854,20 @@ void GLCanvas3D::_picking_pass() } } + std::vector>* gizmo2_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::FallbackGizmo); + if (gizmo2_raycasters != nullptr && !gizmo2_raycasters->empty()) { + ImGui::Separator(); + imgui.text("Gizmo2 raycasters IDs:"); + if (ImGui::BeginTable("Gizmo2Raycasters", 3)) { + for (size_t i = 0; i < gizmo2_raycasters->size(); ++i) { + add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT, + std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::FallbackGizmo, (*gizmo2_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text), + to_string(Geometry::Transformation((*gizmo2_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + } + imgui.end(); #endif // ENABLE_RAYCAST_PICKING_DEBUG } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 717f42d6e7..8c27a96f0c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1575,7 +1575,7 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity())); - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::FallbackGizmo, CutPlane, *m_plane.mesh_raycaster, Transform3d::Identity())); if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CutPlaneZRotation, *m_sphere.mesh_raycaster, Transform3d::Identity())); @@ -1596,6 +1596,7 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() void GLGizmoCut3D::on_unregister_raycasters_for_picking() { m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::FallbackGizmo); m_raycasters.clear(); // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account m_parent.set_raycaster_gizmos_on_top(false); diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 64493d86b4..08c3407632 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -40,6 +40,7 @@ std::shared_ptr SceneRaycaster::add_raycaster(EType type, in case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Volume: { return m_volumes.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::FallbackGizmo: { return m_fallback_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } default: { assert(false); return nullptr; } }; } @@ -62,6 +63,7 @@ void SceneRaycaster::remove_raycasters(EType type) case EType::Bed: { m_bed.clear(); break; } case EType::Volume: { m_volumes.clear(); break; } case EType::Gizmo: { m_gizmos.clear(); break; } + case EType::FallbackGizmo: { m_fallback_gizmos.clear(); break; } default: { break; } }; } @@ -86,6 +88,12 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr item) return; } } + for (auto it = m_fallback_gizmos.begin(); it != m_fallback_gizmos.end(); ++it) { + if (*it == item) { + m_fallback_gizmos.erase(it); + return; + } + } } SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const @@ -174,6 +182,9 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came if (!m_gizmos.empty()) test_raycasters(EType::Gizmo, mouse_pos, camera, ret); + if (!m_fallback_gizmos.empty() && !ret.is_valid()) + test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret); + if (!m_gizmos_on_top || !ret.is_valid()) { if (camera.is_looking_downward() && !m_bed.empty()) test_raycasters(EType::Bed, mouse_pos, camera, ret); @@ -241,6 +252,14 @@ size_t SceneRaycaster::active_gizmos_count() const { } return count; } +size_t SceneRaycaster::active_fallback_gizmos_count() const { + size_t count = 0; + for (const auto& g : m_fallback_gizmos) { + if (g->is_active()) + ++count; + } + return count; +} #endif // ENABLE_RAYCAST_PICKING_DEBUG std::vector>* SceneRaycaster::get_raycasters(EType type) @@ -251,6 +270,7 @@ std::vector>* SceneRaycaster::get_raycasters case EType::Bed: { ret = &m_bed; break; } case EType::Volume: { ret = &m_volumes; break; } case EType::Gizmo: { ret = &m_gizmos; break; } + case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; } default: { break; } } assert(ret != nullptr); @@ -265,6 +285,7 @@ const std::vector>* SceneRaycaster::get_rayc case EType::Bed: { ret = &m_bed; break; } case EType::Volume: { ret = &m_volumes; break; } case EType::Gizmo: { ret = &m_gizmos; break; } + case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; } default: { break; } } assert(ret != nullptr); @@ -278,6 +299,7 @@ int SceneRaycaster::base_id(EType type) case EType::Bed: { return int(EIdBase::Bed); } case EType::Volume: { return int(EIdBase::Volume); } case EType::Gizmo: { return int(EIdBase::Gizmo); } + case EType::FallbackGizmo: { return int(EIdBase::FallbackGizmo); } default: { break; } }; diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index df44b1701c..8102e20de4 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -42,14 +42,16 @@ public: None, Bed, Volume, - Gizmo + Gizmo, + FallbackGizmo // Is used for gizmo grabbers which will be hit after all grabbers of Gizmo type }; enum class EIdBase { Bed = 0, Volume = 1000, - Gizmo = 1000000 + Gizmo = 1000000, + FallbackGizmo = 2000000 }; struct HitResult @@ -66,6 +68,7 @@ private: std::vector> m_bed; std::vector> m_volumes; std::vector> m_gizmos; + std::vector> m_fallback_gizmos; // When set to true, if checking gizmos returns a valid hit, // the search is not performed on other types @@ -99,9 +102,11 @@ public: size_t beds_count() const { return m_bed.size(); } size_t volumes_count() const { return m_volumes.size(); } size_t gizmos_count() const { return m_gizmos.size(); } + size_t fallback_gizmos_count() const { return m_fallback_gizmos.size(); } size_t active_beds_count() const; size_t active_volumes_count() const; size_t active_gizmos_count() const; + size_t active_fallback_gizmos_count() const; #endif // ENABLE_RAYCAST_PICKING_DEBUG static int decode_id(EType type, int id); From eacffe0a578187e5db87a6e0703d7a4571010dc8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 9 Jun 2023 13:37:18 +0200 Subject: [PATCH 25/51] SPE-1765: Fixed redundant emit of EVT_GLCANVAS_WIPETOWER_MOVED event --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index debdc2054d..ad42dd2738 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4066,7 +4066,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) model_object->invalidate_bounding_box(); } } - else if (v->is_wipe_tower) + else if (m_selection.is_wipe_tower() && v->is_wipe_tower) // Move a wipe tower proxy. wipe_tower_origin = v->get_volume_offset(); } From b6ed64428a62345f178e919726ea3d74d0ba384d Mon Sep 17 00:00:00 2001 From: Pavel Date: Mon, 31 Jul 2023 10:00:47 +0200 Subject: [PATCH 26/51] SPE-1820 Fix missing solid infill in thin parts by improving the ensuring filtering --- src/libslic3r/PrintObject.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0b135f0b94..49573a041a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1539,21 +1539,33 @@ void PrintObject::discover_vertical_shells() // Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions. narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare); + Polygons object_volume; Polygons internal_volume; { Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{}; Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ? to_polygons(m_layers[idx_layer + 1]->lslices) : Polygons{}; - internal_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); + object_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); + internal_volume = closing(polygonsInternal, SCALED_EPSILON); } - // The opening operation may cause scattered tiny drops on the smooth parts of the model, filter them out + // The regularization operation may cause scattered tiny drops on the smooth parts of the model, filter them out + // If the region checks both following conditions, it is removed: + // 1. the area is very small, + // OR the area is quite small and it is fully wrapped in model (not visible) + // the in-model condition is there due to small sloping surfaces, e.g. top of the hull of the benchy + // 2. the area does not fully cover an internal polygon + // This is there mainly for a very thin parts, where the solid layers would be missing if the part area is quite small regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(), - [&min_perimeter_infill_spacing, &internal_volume](const ExPolygon &p) { - return p.area() < min_perimeter_infill_spacing * scaled(1.5) || - (p.area() < min_perimeter_infill_spacing * scaled(8.0) && - diff(to_polygons(p), internal_volume).empty()); + [&internal_volume, &min_perimeter_infill_spacing, + &object_volume](const ExPolygon &p) { + return (p.area() < min_perimeter_infill_spacing * scaled(1.5) || + (p.area() < min_perimeter_infill_spacing * scaled(8.0) && + diff(to_polygons(p), object_volume).empty())) && + diff(internal_volume, + expand(to_polygons(p), min_perimeter_infill_spacing)) + .size() >= internal_volume.size(); }), regularized_shell.end()); } From ba25da571a87c855061ed2a3085887fd1f5f22d5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 31 Jul 2023 16:04:41 +0200 Subject: [PATCH 27/51] MeshRaycaster: added a function to check for mesh-line intersections --- src/slic3r/GUI/MeshUtils.cpp | 9 ++++++--- src/slic3r/GUI/MeshUtils.hpp | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 60ea6c856d..88a4f29728 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -465,14 +465,17 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& -bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const +bool MeshRaycaster::intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const { - point = trafo.inverse() * point; + Transform3d trafo_inv = trafo.inverse(); + Vec3d to = trafo_inv * (point + direction); + point = trafo_inv * point; + direction = (to-point).normalized(); std::vector hits = m_emesh.query_ray_hits(point, direction); std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); - return !hits.empty() && !neg_hits.empty(); + return !hits.empty() || !neg_hits.empty(); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 3645ecc026..a7dd3d7f75 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -187,7 +187,9 @@ public: const AABBMesh &get_aabb_mesh() const { return m_emesh; } - bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; + // Given a point and direction in world coords, returns whether the respective line + // intersects the mesh if it is transformed into world by trafo. + bool intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const; // Given a vector of points in woorld coordinates, this returns vector // of indices of points that are visible (i.e. not cut by clipping plane From 4e74e4ecc06daa71869ddd1c48914ffb29e87761 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 3 Aug 2023 10:01:23 +0200 Subject: [PATCH 28/51] CutGizmo: Implemented validation of groove size/position --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 103 +++++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 8 ++- 2 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 8c27a96f0c..7effe33107 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -509,10 +509,8 @@ bool GLGizmoCut3D::render_cut_mode_combo() m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); } - if (m_use_TAG_mesh) { - m_plane.reset(); - on_unregister_raycasters_for_picking(); - } + if (m_use_TAG_mesh) + update_plane_model(); reset_cut_by_contours(); update_clipper(); check_and_update_connectors_state(); @@ -921,6 +919,23 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() const float cut_plane_thiknes = 0.02f;// 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut_plane_thiknes + // Vertices of the groove used to detection if groove is valid + // They are written as: + // {left_ext_lower, left_nar_lower, left_ext_upper, left_nar_upper, + // right_ext_lower, right_nar_lower, right_ext_upper, right_nar_upper } + { + m_groove_vertices.clear(); + m_groove_vertices.reserve(8); + + m_groove_vertices.emplace_back(Vec3f(-ext_lower_x, -y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f(-nar_lower_x, y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f(-ext_upper_x, -y, z_upper).cast()); + m_groove_vertices.emplace_back(Vec3f(-nar_upper_x, y, z_upper).cast()); + m_groove_vertices.emplace_back(Vec3f( ext_lower_x, -y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f( nar_lower_x, y, z_lower).cast()); + m_groove_vertices.emplace_back(Vec3f( ext_upper_x, -y, z_upper).cast()); + m_groove_vertices.emplace_back(Vec3f( nar_upper_x, y, z_upper).cast()); + } // Different cases of groove plane: @@ -1211,7 +1226,7 @@ void GLGizmoCut3D::render_cut_plane() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (m_use_TAG_mesh) { - ColorRGBA cp_clr = can_perform_cut() && has_valid_contour() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR; + ColorRGBA cp_clr = can_perform_cut() && has_valid_groove() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR; if (m_mode == size_t(CutMode::cutTongueAndGroove)) cp_clr.a(cp_clr.a() - 0.1f); m_plane.model.set_color(cp_clr); @@ -1718,6 +1733,14 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() } } +void GLGizmoCut3D::update_plane_model() +{ + m_plane.reset(); + on_unregister_raycasters_for_picking(); + + init_picking_models(); +} + void GLGizmoCut3D::on_set_hover_id() { } @@ -2415,10 +2438,7 @@ void GLGizmoCut3D::render_debug_input_window(float x) is_changed |= m_imgui->checkbox(("Render Cut plane as a full mesh"), m_use_TAG_mesh_full); if (is_changed) - { - m_plane.reset(); - on_unregister_raycasters_for_picking(); - } + update_plane_model(); m_imgui->end(); return; @@ -2710,14 +2730,12 @@ void GLGizmoCut3D::reset_cut_by_contours() m_part_selection = PartSelection(); if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - if (m_optimaze_groove_rendering && m_dragging) + if (m_optimaze_groove_rendering && m_dragging || !has_valid_groove()) return; process_contours(); } - else { - m_invalid_groove = false; + else toggle_model_objects_visibility(); - } } void GLGizmoCut3D::process_contours() @@ -2734,10 +2752,8 @@ void GLGizmoCut3D::process_contours() if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { ModelObjectPtrs cut_objects = perform_cut_with_groove(model_objects[object_idx], true); - if (!cut_objects.empty() && !m_invalid_groove) + if (!cut_objects.empty()) m_part_selection = PartSelection(cut_objects.front(), instance_idx); - else - m_invalid_groove = true; } else { reset_cut_by_contours(); @@ -2808,10 +2824,8 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in m_imgui->disabled_end(); if (is_changed) { - if (m_use_TAG_mesh) { - m_plane.reset(); - on_unregister_raycasters_for_picking(); - } + if (m_use_TAG_mesh) + update_plane_model(); reset_cut_by_contours(); // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } @@ -2848,10 +2862,8 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->disabled_end(); if (is_changed) { - if (m_use_TAG_mesh) { - m_plane.reset(); - on_unregister_raycasters_for_picking(); - } + if (m_use_TAG_mesh) + update_plane_model(); reset_cut_by_contours(); // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } @@ -3129,7 +3141,7 @@ void GLGizmoCut3D::render_input_window_warning() const m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Select at least one object to keep after cutting.")); if (!has_valid_contour()) m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Cut plane is placed out of object")); - if (m_invalid_groove) + else if (!has_valid_groove()) m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Cut plane with groove is invalid")); } @@ -3362,7 +3374,7 @@ bool GLGizmoCut3D::can_perform_cut() const if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); - return flaps_width < m_groove_width && !m_invalid_groove && has_valid_contour(); + return flaps_width < m_groove_width && has_valid_groove(); } if (m_part_selection.valid()) @@ -3371,6 +3383,39 @@ bool GLGizmoCut3D::can_perform_cut() const return true; } +bool GLGizmoCut3D::has_valid_groove() const +{ + if (CutMode(m_mode) != CutMode::cutTongueAndGroove) + return true; + + const Selection& selection = m_parent.get_selection(); + const auto&list = selection.get_volume_idxs(); + // is more volumes selected? + if (list.empty()) + return false; + + const Transform3d cp_matrix = translation_transform(m_plane_center) * m_rotation_m; + + for (size_t id = 0; id < m_groove_vertices.size(); id += 2) { + const Vec3d beg = cp_matrix * m_groove_vertices[id]; + const Vec3d end = cp_matrix * m_groove_vertices[id + 1]; + + bool intersection = false; + for (const unsigned int volume_idx : list) { + const GLVolume* glvol = selection.get_volume(volume_idx); + if (!glvol->is_modifier && + glvol->mesh_raycaster->intersects_line(beg, end - beg, glvol->world_matrix())) { + intersection = true; + break; + } + } + if (!intersection) + return false; + } + + return true; +} + bool GLGizmoCut3D::has_valid_contour() const { const auto clipper = m_c->object_clipper(); @@ -3569,7 +3614,8 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts/* = false*/) { - m_invalid_groove = false; + if (!has_valid_groove()) + return {}; const Selection& selection = m_parent.get_selection(); const int instance_idx = selection.get_instance_idx(); @@ -3684,9 +3730,6 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool invalid_added_volumes |= !add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); if (keep_as_parts) { - if (!m_invalid_groove) - m_invalid_groove = upper->volumes.empty() || lower->volumes.empty() || invalid_added_volumes; - // add volumes from lower object to the upper, but mark them as a lower const auto& volumes = lower->volumes; for (const ModelVolume* volume : volumes) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index db62cacdd1..c8bf50c29c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -109,8 +109,6 @@ class GLGizmoCut3D : public GLGizmoBase } } m_info_stats; - bool m_invalid_groove{ false }; - bool m_keep_upper{ true }; bool m_keep_lower{ true }; bool m_keep_as_parts{ false }; @@ -163,6 +161,9 @@ class GLGizmoCut3D : public GLGizmoBase bool m_was_cut_plane_dragged { false }; bool m_was_contour_selected { false }; + // Vertices of the groove used to detection if groove is valid + std::vector m_groove_vertices; + class PartSelection { public: PartSelection() = default; @@ -323,6 +324,8 @@ protected: void set_volumes_picking_state(bool state); void update_raycasters_for_picking_transform(); + void update_plane_model(); + void on_render_input_window(float x, float y, float bottom_limit) override; bool wants_enter_leave_snapshots() const override { return true; } @@ -348,6 +351,7 @@ private: void render_connectors(); bool can_perform_cut() const; + bool has_valid_groove() const; bool has_valid_contour() const; void apply_connectors_in_model(ModelObject* mo, int &dowels_count); bool cut_line_processing() const; From 492e356a21734b3503caae115fbb280da5fbaa22 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 3 Aug 2023 16:09:28 +0200 Subject: [PATCH 29/51] CutGizmo: Fixed and improved Undo/Redo. --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 90 ++++++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 2 + src/slic3r/GUI/ImGuiWrapper.hpp | 6 ++ 4 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7effe33107..072c000320 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -494,6 +494,22 @@ void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) update_clipper(); } +void GLGizmoCut3D::switch_to_mode(size_t new_mode) +{ + m_mode = new_mode; + update_raycasters_for_picking(); + + apply_color_clip_plane_colors(); + if (auto oc = m_c->object_clipper()) { + m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; + oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + } + if (m_use_TAG_mesh) + update_plane_model(); + reset_cut_by_contours(); + update_clipper(); +} + bool GLGizmoCut3D::render_cut_mode_combo() { ImGui::AlignTextToFramePadding(); @@ -501,18 +517,8 @@ bool GLGizmoCut3D::render_cut_mode_combo() const bool is_changed = m_imgui->combo(_u8L("Mode"), m_modes, selection_idx, 0, m_label_width, m_control_width); if (is_changed) { - m_mode = size_t(selection_idx); - update_raycasters_for_picking(); - - apply_color_clip_plane_colors(); - if (auto oc = m_c->object_clipper()) { - m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; - oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); - } - if (m_use_TAG_mesh) - update_plane_model(); - reset_cut_by_contours(); - update_clipper(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Change cut mode"), UndoRedo::SnapshotType::GizmoAction); + switch_to_mode(size_t(selection_idx)); check_and_update_connectors_state(); } @@ -1503,19 +1509,50 @@ 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_connectors_editing, - m_ar_plane_center, m_rotation_m); + size_t mode; + float groove_depth; + float groove_width; + float groove_flaps_angle; + float groove_angle; + float groove_depth_tolerance; + float groove_width_tolerance; + + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, mode, m_connectors_editing, + m_ar_plane_center, m_rotation_m, + groove_depth, groove_width, groove_flaps_angle, groove_angle, groove_depth_tolerance, groove_width_tolerance); m_transformed_bounding_box = transformed_bounding_box(m_ar_plane_center, m_rotation_m); set_center_pos(m_ar_plane_center); + if (m_mode != mode) + switch_to_mode(mode); + else if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + if (!is_approx(m_groove_depth , groove_depth) || + !is_approx(m_groove_width , groove_width) || + !is_approx(m_groove_flaps_angle , groove_flaps_angle) || + !is_approx(m_groove_angle , groove_angle) || + !is_approx(m_groove_depth_tolerance, groove_depth_tolerance) || + !is_approx(m_groove_width_tolerance, groove_width_tolerance) ) + { + m_groove_depth = groove_depth; + m_groove_width = groove_width; + m_groove_flaps_angle = groove_flaps_angle; + m_groove_angle = groove_angle; + m_groove_depth_tolerance= groove_depth_tolerance; + m_groove_width_tolerance= groove_width_tolerance; + update_plane_model(); + } + reset_cut_by_contours(); + } + m_parent.request_extra_frame(); } 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_connectors_editing, - m_ar_plane_center, m_start_dragging_m); + m_ar_plane_center, m_start_dragging_m, + m_groove_depth, m_groove_width, m_groove_flaps_angle, m_groove_angle, m_groove_depth_tolerance, m_groove_width_tolerance); } std::string GLGizmoCut3D::on_get_name() const @@ -1568,7 +1605,9 @@ void GLGizmoCut3D::on_set_state() void GLGizmoCut3D::on_register_raycasters_for_picking() { - assert(m_raycasters.empty()); + // assert(m_raycasters.empty()); + if (!m_raycasters.empty()) + on_unregister_raycasters_for_picking(); // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account m_parent.set_raycaster_gizmos_on_top(true); @@ -2809,14 +2848,24 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in { bool is_changed{false}; - if (render_slider_double_input(label, in_val, in_tolerance, -0.1f, std::min(0.3f*in_val, 1.5f))) + float val = in_val; + float tolerance = in_tolerance; + if (render_slider_double_input(label, val, tolerance, -0.1f, std::min(0.3f*in_val, 1.5f))) { + if (m_imgui->get_last_slider_status().can_take_snapshot) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + m_imgui->get_last_slider_status().invalidate_snapshot(); + } + in_val = val; + in_tolerance = tolerance; is_changed = true; + } ImGui::SameLine(); m_imgui->disabled_begin(is_approx(in_val, init_val)); const std::string act_name = _u8L("Reset"); if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); in_val = init_val; in_tolerance = 0.1f; is_changed = true; @@ -2827,7 +2876,6 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in if (m_use_TAG_mesh) update_plane_model(); reset_cut_by_contours(); - // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } } @@ -2847,6 +2895,10 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->slider_float(("##groove_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); if (!is_approx(old_val, val)) { + if (m_imgui->get_last_slider_status().can_take_snapshot) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + m_imgui->get_last_slider_status().invalidate_snapshot(); + } in_val = deg2rad(val); is_changed = true; } @@ -2856,6 +2908,7 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->disabled_begin(is_approx(in_val, init_val)); const std::string act_name = _u8L("Reset"); if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); in_val = init_val; is_changed = true; } @@ -2865,7 +2918,6 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in if (m_use_TAG_mesh) update_plane_model(); reset_cut_by_contours(); - // Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index c8bf50c29c..11ac1ee7a8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -338,6 +338,7 @@ protected: private: void set_center(const Vec3d¢er, bool update_tbb = false); + void switch_to_mode(size_t new_mode); bool render_cut_mode_combo(); bool render_combo(const std::string&label, const std::vector&lines, int&selection_idx); bool render_double_input(const std::string& label, double& value_in); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 4673b338f2..0023a4f63b 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -640,6 +640,8 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float m_last_slider_status.edited = ImGui::IsItemEdited(); m_last_slider_status.clicked = ImGui::IsItemClicked(); m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit(); + if (!m_last_slider_status.can_take_snapshot) + m_last_slider_status.can_take_snapshot = ImGui::IsItemClicked(); if (!tooltip.empty() && ImGui::IsItemHovered()) this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 26e58d4ad1..92e761a434 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -49,6 +49,11 @@ public: bool edited { false }; bool clicked { false }; bool deactivated_after_edit { false }; + // flag to indicate possibility to take snapshot from the slider value + // It's used from Gizmos to take snapshots just from the very beginning of the editing + bool can_take_snapshot { false }; + // When Undo/Redo snapshot is taken, then call this function + void invalidate_snapshot() { can_take_snapshot = false; } }; ImGuiWrapper(); @@ -80,6 +85,7 @@ public: ImVec2 get_item_spacing() const; float get_slider_float_height() const; const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; } + LastSliderStatus& get_last_slider_status() { return m_last_slider_status; } void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha); From 6659d108d5905b0f1fa05ccea6699fbe5e4ca1cf Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Aug 2023 10:10:23 +0200 Subject: [PATCH 30/51] CutGizmo: Fixed memory leaks. All temporary ModelObjectPtrs have to belong to some Model to correct memory de-allocation --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 69 +++++++++++++++------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 072c000320..91bf6d6c86 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -2186,13 +2186,14 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor { m_model = Model(); m_model.add_object(*mo); - ModelObjectPtrs cut_part_ptrs = m_model.objects.front()->cut(instance_idx_in, cut_matrix, + + Model tmp_model = Model(); + tmp_model.objects = m_model.objects.front()->cut(instance_idx_in, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - assert(cut_part_ptrs.size() == 1); - m_model = Model(); - m_model.add_object(*cut_part_ptrs.front()); + assert(tmp_model.objects.size() == 1); + m_model = tmp_model; m_instance_idx = instance_idx_in; @@ -2790,9 +2791,11 @@ void GLGizmoCut3D::process_contours() wxBusyCursor wait; if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - ModelObjectPtrs cut_objects = perform_cut_with_groove(model_objects[object_idx], true); - if (!cut_objects.empty()) - m_part_selection = PartSelection(cut_objects.front(), instance_idx); + Model tmp_model = Model(); + tmp_model.objects = perform_cut_with_groove(model_objects[object_idx], true); + if (!tmp_model.objects.empty()) + m_part_selection = PartSelection(tmp_model.objects.front(), instance_idx); + tmp_model = Model(); } else { reset_cut_by_contours(); @@ -3682,15 +3685,14 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const Transform3d cut_matrix = get_cut_matrix(selection); - ModelObjectPtrs cut_part_ptrs; + Model tmp_model_for_cut = Model(); + Model tmp_model = Model(); tmp_model.add_object(*cut_mo); ModelObject* tmp_object = tmp_model.objects.front(); - bool invalid_added_volumes = false; - - auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const ModelObjectPtrs& cut_part_ptrs) { - const auto& volumes = cut_part_ptrs.front()->volumes; + auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { + const auto& volumes = tmp_model_for_cut.objects.front()->volumes; bool has_volume = false; for (const ModelVolume* volume : volumes) if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || @@ -3702,17 +3704,18 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool return has_volume; }; - auto cut = [instance_idx, add_volumes_from_cut, &invalid_added_volumes] - (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute attribute, ModelObjectPtrs& cut_part_ptrs) { + auto cut = [instance_idx, add_volumes_from_cut] + (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute attribute, Model& tmp_model_for_cut) { Model model = Model(); model.add_object(*object); - cut_part_ptrs = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - if (cut_part_ptrs.empty()) + tmp_model_for_cut = Model(); + tmp_model_for_cut.objects = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); + if (tmp_model_for_cut.objects.empty()) return false; object->clear_volumes(); - invalid_added_volumes |= !add_volumes_from_cut(object, attribute, cut_part_ptrs); + add_volumes_from_cut(object, attribute, tmp_model_for_cut); ModelObject::reset_instance_transformation(object, instance_idx); return true; }; @@ -3723,18 +3726,18 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - if (!cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) return cut_object_ptrs; - invalid_added_volumes |= !add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } // cut by lower plane const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - if (!cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut)) return cut_object_ptrs; - invalid_added_volumes |= !add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); } // cut middle part with 2 angles and add parts to related upper/lower objects @@ -3745,18 +3748,18 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool { const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) return cut_object_ptrs; - invalid_added_volumes |= !add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } // cut by angle2 plane { const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) return cut_object_ptrs; - invalid_added_volumes |= !add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } // apply tolerance to the middle part @@ -3764,22 +3767,22 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; - if (!cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut)) return cut_object_ptrs; const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) return cut_object_ptrs; const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, cut_part_ptrs)) + if (!cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut)) return cut_object_ptrs; } // this part can be added to the upper object now - invalid_added_volumes |= !add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, cut_part_ptrs); + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); if (keep_as_parts) { // add volumes from lower object to the upper, but mark them as a lower @@ -3789,6 +3792,9 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool new_vol->cut_info.is_from_upper = false; } cut_object_ptrs.push_back(upper); + + // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks + cut_object_ptrs.push_back(lower); } else { auto add_cut_objects = [this, &instance_idx](ModelObjectPtrs& cut_objects, ModelObject* object, const Transform3d& cut_matrix) { @@ -3884,7 +3890,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // update cut_id for the cut object in respect to the attributes update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); - ModelObjectPtrs cut_object_ptrs = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) : + Model tmp_model = Model(); + tmp_model.objects = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) : cut_with_groove ? perform_cut_with_groove(cut_mo) : cut_mo->cut(instance_idx, cut_matrix, attributes); @@ -3892,7 +3899,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) const CutObjectBase cut_id = cut_mo->cut_id; // update cut results on plater and in the model - plater->cut(object_idx, cut_object_ptrs); + plater->cut(object_idx, tmp_model.objects); synchronize_model_after_cut(plater->model(), cut_id); } From 831ea574835497a422d554d05fec6501bb3a086a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Aug 2023 14:10:50 +0200 Subject: [PATCH 31/51] Simplify and fix wxWidgets find procedure with up to date find module --- cmake/modules/FindwxWidgets.cmake | 1219 +++++++++++++++++++++++++++++ src/CMakeLists.txt | 24 +- 2 files changed, 1224 insertions(+), 19 deletions(-) create mode 100644 cmake/modules/FindwxWidgets.cmake diff --git a/cmake/modules/FindwxWidgets.cmake b/cmake/modules/FindwxWidgets.cmake new file mode 100644 index 0000000000..e1f93a6a26 --- /dev/null +++ b/cmake/modules/FindwxWidgets.cmake @@ -0,0 +1,1219 @@ +# PrusaSlicer: this is a direct copy of the FindwxWidgets.cmake module +# within the original CMake 3.27 distribution + +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindwxWidgets +------------- + +Find a wxWidgets (a.k.a., wxWindows) installation. + +This module finds if wxWidgets is installed and selects a default +configuration to use. wxWidgets is a modular library. To specify the +modules that you will use, you need to name them as components to the +package: + +find_package(wxWidgets COMPONENTS core base ... OPTIONAL_COMPONENTS net ...) + +.. versionadded:: 3.4 + Support for :command:`find_package` version argument; ``webview`` component. + +.. versionadded:: 3.14 + ``OPTIONAL_COMPONENTS`` support. + +There are two search branches: a windows style and a unix style. For +windows, the following variables are searched for and set to defaults +in case of multiple choices. Change them if the defaults are not +desired (i.e., these are the only variables you should change to +select a configuration): + +:: + + wxWidgets_ROOT_DIR - Base wxWidgets directory + (e.g., C:/wxWidgets-3.2.0). + wxWidgets_LIB_DIR - Path to wxWidgets libraries + (e.g., C:/wxWidgets-3.2.0/lib/vc_x64_lib). + wxWidgets_CONFIGURATION - Configuration to use + (e.g., msw, mswd, mswu, mswunivud, etc.) + wxWidgets_EXCLUDE_COMMON_LIBRARIES + - Set to TRUE to exclude linking of + commonly required libs (e.g., png tiff + jpeg zlib regex expat). + + + +For unix style it uses the wx-config utility. You can select between +debug/release, unicode/ansi, universal/non-universal, and +static/shared in the QtDialog or ccmake interfaces by turning ON/OFF +the following variables: + +:: + + wxWidgets_USE_DEBUG + wxWidgets_USE_UNICODE + wxWidgets_USE_UNIVERSAL + wxWidgets_USE_STATIC + +There is also a wxWidgets_CONFIG_OPTIONS variable for all other +options that need to be passed to the wx-config utility. For example, +to use the base toolkit found in the /usr/local path, set the variable +(before calling the FIND_PACKAGE command) as such: + +:: + + set(wxWidgets_CONFIG_OPTIONS --toolkit=base --prefix=/usr) + + + +The following are set after the configuration is done for both windows +and unix style: + +:: + + wxWidgets_FOUND - Set to TRUE if wxWidgets was found. + wxWidgets_INCLUDE_DIRS - Include directories for WIN32 + i.e., where to find "wx/wx.h" and + "wx/setup.h"; possibly empty for unices. + wxWidgets_LIBRARIES - Path to the wxWidgets libraries. + wxWidgets_LIBRARY_DIRS - compile time link dirs, useful for + rpath on UNIX. Typically an empty string + in WIN32 environment. + wxWidgets_DEFINITIONS - Contains defines required to compile/link + against WX, e.g. WXUSINGDLL + wxWidgets_DEFINITIONS_DEBUG- Contains defines required to compile/link + against WX debug builds, e.g. __WXDEBUG__ + wxWidgets_CXX_FLAGS - Include dirs and compiler flags for + unices, empty on WIN32. Essentially + "`wx-config --cxxflags`". + wxWidgets_USE_FILE - Convenience include file. + +.. versionadded:: 3.11 + The following environment variables can be used as hints: ``WX_CONFIG``, + ``WXRC_CMD``. + + +Sample usage: + +:: + + # Note that for MinGW users the order of libs is important! + find_package(wxWidgets COMPONENTS gl core base OPTIONAL_COMPONENTS net) + if(wxWidgets_FOUND) + include(${wxWidgets_USE_FILE}) + # and for each of your dependent executable/library targets: + target_link_libraries( ${wxWidgets_LIBRARIES}) + endif() + + + +If wxWidgets is required (i.e., not an optional part): + +:: + + find_package(wxWidgets REQUIRED gl core base OPTIONAL_COMPONENTS net) + include(${wxWidgets_USE_FILE}) + # and for each of your dependent executable/library targets: + target_link_libraries( ${wxWidgets_LIBRARIES}) + +Imported targets +^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.27 + +This module defines the following :prop_tgt:`IMPORTED` targets: + +``wxWidgets::wxWidgets`` + An interface library providing usage requirements for the found components. +#]=======================================================================] + +# +# FIXME: check this and provide a correct sample usage... +# Remember to connect back to the upper text. +# Sample usage with monolithic wx build: +# +# find_package(wxWidgets COMPONENTS mono) +# ... + +# NOTES +# +# This module has been tested on the WIN32 platform with wxWidgets +# 2.6.2, 2.6.3, and 2.5.3. However, it has been designed to +# easily extend support to all possible builds, e.g., static/shared, +# debug/release, unicode, universal, multilib/monolithic, etc.. +# +# If you want to use the module and your build type is not supported +# out-of-the-box, please contact me to exchange information on how +# your system is setup and I'll try to add support for it. +# +# AUTHOR +# +# Miguel A. Figueroa-Villanueva (miguelf at ieee dot org). +# Jan Woetzel (jw at mip.informatik.uni-kiel.de). +# +# Based on previous works of: +# Jan Woetzel (FindwxWindows.cmake), +# Jorgen Bodde and Jerry Fath (FindwxWin.cmake). + +# TODO/ideas +# +# (1) Option/Setting to use all available wx libs +# In contrast to expert developer who lists the +# minimal set of required libs in wxWidgets_USE_LIBS +# there is the newbie user: +# - who just wants to link against WX with more 'magic' +# - doesn't know the internal structure of WX or how it was built, +# in particular if it is monolithic or not +# - want to link against all available WX libs +# Basically, the intent here is to mimic what wx-config would do by +# default (i.e., `wx-config --libs`). +# +# Possible solution: +# Add a reserved keyword "std" that initializes to what wx-config +# would default to. If the user has not set the wxWidgets_USE_LIBS, +# default to "std" instead of "base core" as it is now. To implement +# "std" will basically boil down to a FOR_EACH lib-FOUND, but maybe +# checking whether a minimal set was found. + + +# FIXME: This and all the DBG_MSG calls should be removed after the +# module stabilizes. +# +# Helper macro to control the debugging output globally. There are +# two versions for controlling how verbose your output should be. +macro(DBG_MSG _MSG) +# message(STATUS +# "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): ${_MSG}") +endmacro() +macro(DBG_MSG_V _MSG) +# message(STATUS +# "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): ${_MSG}") +endmacro() + +# Clear return values in case the module is loaded more than once. +set(wxWidgets_FOUND FALSE) +set(wxWidgets_INCLUDE_DIRS "") +set(wxWidgets_LIBRARIES "") +set(wxWidgets_LIBRARY_DIRS "") +set(wxWidgets_CXX_FLAGS "") + +# DEPRECATED: This is a patch to support the DEPRECATED use of +# wxWidgets_USE_LIBS. +# +# If wxWidgets_USE_LIBS is set: +# - if using , then override wxWidgets_USE_LIBS +# - else set wxWidgets_FIND_COMPONENTS to wxWidgets_USE_LIBS +if(wxWidgets_USE_LIBS AND NOT wxWidgets_FIND_COMPONENTS) + set(wxWidgets_FIND_COMPONENTS ${wxWidgets_USE_LIBS}) +endif() +DBG_MSG("wxWidgets_FIND_COMPONENTS : ${wxWidgets_FIND_COMPONENTS}") + +# Add the convenience use file if available. +# +# Get dir of this file which may reside in: +# - CMAKE_MAKE_ROOT/Modules on CMake installation +# - CMAKE_MODULE_PATH if user prefers his own specialized version +set(wxWidgets_USE_FILE "") +get_filename_component( + wxWidgets_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +# Prefer an existing customized version, but the user might override +# the FindwxWidgets module and not the UsewxWidgets one. +if(EXISTS "${wxWidgets_CURRENT_LIST_DIR}/UsewxWidgets.cmake") + set(wxWidgets_USE_FILE + "${wxWidgets_CURRENT_LIST_DIR}/UsewxWidgets.cmake") +else() + set(wxWidgets_USE_FILE UsewxWidgets) +endif() + +# Known wxWidgets versions. +set(wx_versions 3.3 3.2 3.1 3.0 2.9 2.8 2.7 2.6 2.5) + +macro(wx_extract_version) + unset(_wx_filename) + find_file(_wx_filename wx/version.h PATHS ${wxWidgets_INCLUDE_DIRS} NO_DEFAULT_PATH) + dbg_msg("_wx_filename: ${_wx_filename}") + + if(NOT _wx_filename) + message(FATAL_ERROR "wxWidgets wx/version.h file not found in ${wxWidgets_INCLUDE_DIRS}.") + endif() + + file(READ "${_wx_filename}" _wx_version_h) + unset(_wx_filename CACHE) + + string(REGEX REPLACE "^(.*\n)?#define +wxMAJOR_VERSION +([0-9]+).*" + "\\2" wxWidgets_VERSION_MAJOR "${_wx_version_h}" ) + string(REGEX REPLACE "^(.*\n)?#define +wxMINOR_VERSION +([0-9]+).*" + "\\2" wxWidgets_VERSION_MINOR "${_wx_version_h}" ) + string(REGEX REPLACE "^(.*\n)?#define +wxRELEASE_NUMBER +([0-9]+).*" + "\\2" wxWidgets_VERSION_PATCH "${_wx_version_h}" ) + set(wxWidgets_VERSION_STRING + "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}.${wxWidgets_VERSION_PATCH}" ) + dbg_msg("wxWidgets_VERSION_STRING: ${wxWidgets_VERSION_STRING}") +endmacro() + +#===================================================================== +# Determine whether unix or win32 paths should be used +#===================================================================== +if(WIN32 AND NOT CYGWIN AND NOT MSYS AND NOT CMAKE_CROSSCOMPILING) + set(wxWidgets_FIND_STYLE "win32") +else() + set(wxWidgets_FIND_STYLE "unix") +endif() + +#===================================================================== +# WIN32_FIND_STYLE +#===================================================================== +if(wxWidgets_FIND_STYLE STREQUAL "win32") + # Useful common wx libs needed by almost all components. + set(wxWidgets_COMMON_LIBRARIES png tiff jpeg zlib regex expat) + + # DEPRECATED: Use find_package(wxWidgets COMPONENTS mono) instead. + if(NOT wxWidgets_FIND_COMPONENTS) + if(wxWidgets_USE_MONOLITHIC) + set(wxWidgets_FIND_COMPONENTS mono) + else() + set(wxWidgets_FIND_COMPONENTS core base) # this is default + endif() + endif() + + # Add the common (usually required libs) unless + # wxWidgets_EXCLUDE_COMMON_LIBRARIES has been set. + if(NOT wxWidgets_EXCLUDE_COMMON_LIBRARIES) + list(APPEND wxWidgets_FIND_COMPONENTS + ${wxWidgets_COMMON_LIBRARIES}) + endif() + + #------------------------------------------------------------------- + # WIN32: Helper MACROS + #------------------------------------------------------------------- + # + # Get filename components for a configuration. For example, + # if _CONFIGURATION = mswunivud, then _PF="msw", _UNV=univ, _UCD=u _DBG=d + # if _CONFIGURATION = mswu, then _PF="msw", _UNV="", _UCD=u _DBG="" + # + macro(WX_GET_NAME_COMPONENTS _CONFIGURATION _PF _UNV _UCD _DBG) + DBG_MSG_V(${_CONFIGURATION}) + string(REGEX MATCH "univ" ${_UNV} "${_CONFIGURATION}") + string(REGEX REPLACE "[msw|qt].*(u)[d]*$" "u" ${_UCD} "${_CONFIGURATION}") + if(${_UCD} STREQUAL ${_CONFIGURATION}) + set(${_UCD} "") + endif() + string(REGEX MATCH "d$" ${_DBG} "${_CONFIGURATION}") + string(REGEX MATCH "^[msw|qt]*" ${_PF} "${_CONFIGURATION}") + endmacro() + + # + # Find libraries associated to a configuration. + # + macro(WX_FIND_LIBS _PF _UNV _UCD _DBG _VER) + DBG_MSG_V("m_unv = ${_UNV}") + DBG_MSG_V("m_ucd = ${_UCD}") + DBG_MSG_V("m_dbg = ${_DBG}") + DBG_MSG_V("m_ver = ${_VER}") + + # FIXME: What if both regex libs are available. regex should be + # found outside the loop and only wx${LIB}${_UCD}${_DBG}. + # Find wxWidgets common libraries. + foreach(LIB ${wxWidgets_COMMON_LIBRARIES} scintilla) + find_library(WX_${LIB}${_DBG} + NAMES + wx${LIB}${_UCD}${_DBG} # for regex + wx${LIB}${_DBG} + PATHS ${WX_LIB_DIR} + NO_DEFAULT_PATH + ) + mark_as_advanced(WX_${LIB}${_DBG}) + endforeach() + + # Find wxWidgets multilib base libraries. + find_library(WX_base${_DBG} + NAMES wxbase${_VER}${_UCD}${_DBG} + PATHS ${WX_LIB_DIR} + NO_DEFAULT_PATH + ) + mark_as_advanced(WX_base${_DBG}) + foreach(LIB net odbc xml) + find_library(WX_${LIB}${_DBG} + NAMES wxbase${_VER}${_UCD}${_DBG}_${LIB} + PATHS ${WX_LIB_DIR} + NO_DEFAULT_PATH + ) + mark_as_advanced(WX_${LIB}${_DBG}) + endforeach() + + # Find wxWidgets monolithic library. + find_library(WX_mono${_DBG} + NAMES wx${_PF}${_UNV}${_VER}${_UCD}${_DBG} + PATHS ${WX_LIB_DIR} + NO_DEFAULT_PATH + ) + mark_as_advanced(WX_mono${_DBG}) + + # Find wxWidgets multilib libraries. + foreach(LIB core adv aui html media xrc dbgrid gl qa richtext + stc ribbon propgrid webview) + find_library(WX_${LIB}${_DBG} + NAMES wx${_PF}${_UNV}${_VER}${_UCD}${_DBG}_${LIB} + PATHS ${WX_LIB_DIR} + NO_DEFAULT_PATH + ) + mark_as_advanced(WX_${LIB}${_DBG}) + endforeach() + endmacro() + + # + # Clear all library paths, so that FIND_LIBRARY refinds them. + # + # Clear a lib, reset its found flag, and mark as advanced. + macro(WX_CLEAR_LIB _LIB) + set(${_LIB} "${_LIB}-NOTFOUND" CACHE FILEPATH "Cleared." FORCE) + set(${_LIB}_FOUND FALSE) + mark_as_advanced(${_LIB}) + endmacro() + # Clear all debug or release library paths (arguments are "d" or ""). + macro(WX_CLEAR_ALL_LIBS _DBG) + # Clear wxWidgets common libraries. + foreach(LIB ${wxWidgets_COMMON_LIBRARIES} scintilla) + WX_CLEAR_LIB(WX_${LIB}${_DBG}) + endforeach() + + # Clear wxWidgets multilib base libraries. + WX_CLEAR_LIB(WX_base${_DBG}) + foreach(LIB net odbc xml) + WX_CLEAR_LIB(WX_${LIB}${_DBG}) + endforeach() + + # Clear wxWidgets monolithic library. + WX_CLEAR_LIB(WX_mono${_DBG}) + + # Clear wxWidgets multilib libraries. + foreach(LIB core adv aui html media xrc dbgrid gl qa richtext + webview stc ribbon propgrid) + WX_CLEAR_LIB(WX_${LIB}${_DBG}) + endforeach() + endmacro() + # Clear all wxWidgets debug libraries. + macro(WX_CLEAR_ALL_DBG_LIBS) + WX_CLEAR_ALL_LIBS("d") + endmacro() + # Clear all wxWidgets release libraries. + macro(WX_CLEAR_ALL_REL_LIBS) + WX_CLEAR_ALL_LIBS("") + endmacro() + + # + # Set the wxWidgets_LIBRARIES variable. + # Also, Sets output variable wxWidgets_FOUND to FALSE if it fails. + # + macro(WX_SET_LIBRARIES _LIBS _DBG) + DBG_MSG_V("Looking for ${${_LIBS}}") + if(WX_USE_REL_AND_DBG) + foreach(LIB ${${_LIBS}}) + DBG_MSG_V("Searching for ${LIB} and ${LIB}d") + DBG_MSG_V("WX_${LIB} : ${WX_${LIB}}") + DBG_MSG_V("WX_${LIB}d : ${WX_${LIB}d}") + if(WX_${LIB} AND WX_${LIB}d) + DBG_MSG_V("Found ${LIB} and ${LIB}d") + list(APPEND wxWidgets_LIBRARIES + debug ${WX_${LIB}d} optimized ${WX_${LIB}} + ) + set(wxWidgets_${LIB}_FOUND TRUE) + elseif(NOT wxWidgets_FIND_REQUIRED_${LIB}) + DBG_MSG_V("- ignored optional missing WX_${LIB}=${WX_${LIB}} or WX_${LIB}d=${WX_${LIB}d}") + else() + DBG_MSG_V("- not found due to missing WX_${LIB}=${WX_${LIB}} or WX_${LIB}d=${WX_${LIB}d}") + set(wxWidgets_FOUND FALSE) + endif() + endforeach() + else() + foreach(LIB ${${_LIBS}}) + DBG_MSG_V("Searching for ${LIB}${_DBG}") + DBG_MSG_V("WX_${LIB}${_DBG} : ${WX_${LIB}${_DBG}}") + if(WX_${LIB}${_DBG}) + DBG_MSG_V("Found ${LIB}${_DBG}") + list(APPEND wxWidgets_LIBRARIES ${WX_${LIB}${_DBG}}) + set(wxWidgets_${LIB}_FOUND TRUE) + elseif(NOT wxWidgets_FIND_REQUIRED_${LIB}) + DBG_MSG_V("- ignored optional missing WX_${LIB}${_DBG}=${WX_${LIB}${_DBG}}") + else() + DBG_MSG_V("- not found due to missing WX_${LIB}${_DBG}=${WX_${LIB}${_DBG}}") + set(wxWidgets_FOUND FALSE) + endif() + endforeach() + endif() + + DBG_MSG_V("OpenGL") + list(FIND ${_LIBS} gl WX_USE_GL) + if(NOT WX_USE_GL EQUAL -1) + DBG_MSG_V("- is required.") + list(APPEND wxWidgets_LIBRARIES opengl32 glu32) + endif() + + list(APPEND wxWidgets_LIBRARIES winmm comctl32 uuid oleacc uxtheme rpcrt4 shlwapi version wsock32) + endmacro() + + #------------------------------------------------------------------- + # WIN32: Start actual work. + #------------------------------------------------------------------- + + set(wx_paths "wxWidgets") + foreach(version ${wx_versions}) + foreach(patch RANGE 15 0 -1) + list(APPEND wx_paths "wxWidgets-${version}.${patch}") + endforeach() + endforeach() + + # Look for an installation tree. + find_path(wxWidgets_ROOT_DIR + NAMES include/wx/wx.h + PATHS + ENV wxWidgets_ROOT_DIR + ENV WXWIN + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\wxWidgets_is1;Inno Setup: App Path]" # WX 2.6.x + C:/ + D:/ + ENV ProgramFiles + PATH_SUFFIXES + ${wx_paths} + DOC "wxWidgets base/installation directory" + ) + + # If wxWidgets_ROOT_DIR changed, clear lib dir. + if(NOT WX_ROOT_DIR STREQUAL wxWidgets_ROOT_DIR) + if(NOT wxWidgets_LIB_DIR OR WX_ROOT_DIR) + set(wxWidgets_LIB_DIR "wxWidgets_LIB_DIR-NOTFOUND" + CACHE PATH "Cleared." FORCE) + endif() + set(WX_ROOT_DIR ${wxWidgets_ROOT_DIR} + CACHE INTERNAL "wxWidgets_ROOT_DIR") + endif() + + if(WX_ROOT_DIR) + # Select one default tree inside the already determined wx tree. + # Prefer static/shared order usually consistent with build + # settings. + set(_WX_TOOL "") + set(_WX_TOOLVER "") + set(_WX_ARCH "") + if(MINGW) + set(_WX_TOOL gcc) + elseif(MSVC) + set(_WX_TOOL vc) + set(_WX_TOOLVER ${MSVC_TOOLSET_VERSION}) + # support for a lib/vc14x_x64_dll/ path from wxW 3.1.3 distribution + string(REGEX REPLACE ".$" "x" _WX_TOOLVERx ${_WX_TOOLVER}) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_WX_ARCH _x64) + endif() + endif() + if(BUILD_SHARED_LIBS) + find_path(wxWidgets_LIB_DIR + NAMES + qtu/wx/setup.h + qtud/wx/setup.h + msw/wx/setup.h + mswd/wx/setup.h + mswu/wx/setup.h + mswud/wx/setup.h + mswuniv/wx/setup.h + mswunivd/wx/setup.h + mswunivu/wx/setup.h + mswunivud/wx/setup.h + PATHS + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_dll # prefer shared + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_dll # prefer shared + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_dll # prefer shared + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_dll # prefer shared + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_dll # prefer shared + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_lib + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_lib + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_lib + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_lib + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_lib + DOC "Path to wxWidgets libraries" + NO_DEFAULT_PATH + ) + else() + find_path(wxWidgets_LIB_DIR + NAMES + qtu/wx/setup.h + qtud/wx/setup.h + msw/wx/setup.h + mswd/wx/setup.h + mswu/wx/setup.h + mswud/wx/setup.h + mswuniv/wx/setup.h + mswunivd/wx/setup.h + mswunivu/wx/setup.h + mswunivud/wx/setup.h + PATHS + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_lib # prefer static + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_lib # prefer static + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_lib # prefer static + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_lib # prefer static + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_lib # prefer static + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_dll + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_dll + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_dll + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_dll + ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_dll + DOC "Path to wxWidgets libraries" + NO_DEFAULT_PATH + ) + endif() + unset(_WX_TOOL) + unset(_WX_TOOLVER) + unset(_WX_ARCH) + + # If wxWidgets_LIB_DIR changed, clear all libraries. + if(NOT WX_LIB_DIR STREQUAL wxWidgets_LIB_DIR) + set(WX_LIB_DIR ${wxWidgets_LIB_DIR} CACHE INTERNAL "wxWidgets_LIB_DIR") + WX_CLEAR_ALL_DBG_LIBS() + WX_CLEAR_ALL_REL_LIBS() + endif() + + if(WX_LIB_DIR) + # If building shared libs, define WXUSINGDLL to use dllimport. + if(WX_LIB_DIR MATCHES "[dD][lL][lL]") + set(wxWidgets_DEFINITIONS WXUSINGDLL) + DBG_MSG_V("detected SHARED/DLL tree WX_LIB_DIR=${WX_LIB_DIR}") + endif() + + # Search for available configuration types. + foreach(CFG mswunivud mswunivd mswud mswd mswunivu mswuniv mswu msw qt qtd qtu qtud) + set(WX_${CFG}_FOUND FALSE) + if(EXISTS ${WX_LIB_DIR}/${CFG}) + list(APPEND WX_CONFIGURATION_LIST ${CFG}) + set(WX_${CFG}_FOUND TRUE) + set(WX_CONFIGURATION ${CFG}) + endif() + endforeach() + DBG_MSG_V("WX_CONFIGURATION_LIST=${WX_CONFIGURATION_LIST}") + + if(WX_CONFIGURATION) + set(wxWidgets_FOUND TRUE) + + # If the selected configuration wasn't found force the default + # one. Otherwise, use it but still force a refresh for + # updating the doc string with the current list of available + # configurations. + if(NOT WX_${wxWidgets_CONFIGURATION}_FOUND) + set(wxWidgets_CONFIGURATION ${WX_CONFIGURATION} CACHE STRING + "Set wxWidgets configuration (${WX_CONFIGURATION_LIST})" FORCE) + else() + set(wxWidgets_CONFIGURATION ${wxWidgets_CONFIGURATION} CACHE STRING + "Set wxWidgets configuration (${WX_CONFIGURATION_LIST})" FORCE) + endif() + + # If release config selected, and both release/debug exist. + if(WX_${wxWidgets_CONFIGURATION}d_FOUND) + option(wxWidgets_USE_REL_AND_DBG + "Use release and debug configurations?" TRUE) + set(WX_USE_REL_AND_DBG ${wxWidgets_USE_REL_AND_DBG}) + else() + # If the option exists (already in cache), force it false. + if(wxWidgets_USE_REL_AND_DBG) + set(wxWidgets_USE_REL_AND_DBG FALSE CACHE BOOL + "No ${wxWidgets_CONFIGURATION}d found." FORCE) + endif() + set(WX_USE_REL_AND_DBG FALSE) + endif() + + # Get configuration parameters from the name. + WX_GET_NAME_COMPONENTS(${wxWidgets_CONFIGURATION} PF UNV UCD DBG) + + # Set wxWidgets lib setup include directory. + if(EXISTS ${WX_LIB_DIR}/${wxWidgets_CONFIGURATION}/wx/setup.h) + set(wxWidgets_INCLUDE_DIRS + ${WX_LIB_DIR}/${wxWidgets_CONFIGURATION}) + else() + DBG_MSG("wxWidgets_FOUND FALSE because ${WX_LIB_DIR}/${wxWidgets_CONFIGURATION}/wx/setup.h does not exist.") + set(wxWidgets_FOUND FALSE) + endif() + + # Set wxWidgets main include directory. + if(EXISTS ${WX_ROOT_DIR}/include/wx/wx.h) + list(APPEND wxWidgets_INCLUDE_DIRS ${WX_ROOT_DIR}/include) + else() + DBG_MSG("wxWidgets_FOUND FALSE because WX_ROOT_DIR=${WX_ROOT_DIR} has no ${WX_ROOT_DIR}/include/wx/wx.h") + set(wxWidgets_FOUND FALSE) + endif() + + # Get version number. + wx_extract_version() + set(VER "${wxWidgets_VERSION_MAJOR}${wxWidgets_VERSION_MINOR}") + + # Find wxWidgets libraries. + WX_FIND_LIBS("${PF}" "${UNV}" "${UCD}" "${DBG}" "${VER}") + if(WX_USE_REL_AND_DBG) + WX_FIND_LIBS("${PF}" "${UNV}" "${UCD}" "d" "${VER}") + endif() + + # Settings for requested libs (i.e., include dir, libraries, etc.). + WX_SET_LIBRARIES(wxWidgets_FIND_COMPONENTS "${DBG}") + + # Add necessary definitions for unicode builds + if("${UCD}" STREQUAL "u") + list(APPEND wxWidgets_DEFINITIONS UNICODE _UNICODE) + endif() + + # Add necessary definitions for debug builds + set(wxWidgets_DEFINITIONS_DEBUG _DEBUG __WXDEBUG__) + + endif() + endif() + endif() + + if(MINGW AND NOT wxWidgets_FOUND) + # Try unix search mode as well. + set(wxWidgets_FIND_STYLE "unix") + dbg_msg_v("wxWidgets_FIND_STYLE changed to unix") + endif() +endif() + +#===================================================================== +# UNIX_FIND_STYLE +#===================================================================== +if(wxWidgets_FIND_STYLE STREQUAL "unix") + #----------------------------------------------------------------- + # UNIX: Helper MACROS + #----------------------------------------------------------------- + # + # Set the default values based on "wx-config --selected-config". + # + macro(WX_CONFIG_SELECT_GET_DEFAULT) + execute_process( + COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" + ${wxWidgets_CONFIG_OPTIONS} --selected-config + OUTPUT_VARIABLE _wx_selected_config + RESULT_VARIABLE _wx_result + ERROR_QUIET + ) + if(_wx_result EQUAL 0) + foreach(_opt_name debug static unicode universal) + string(TOUPPER ${_opt_name} _upper_opt_name) + if(_wx_selected_config MATCHES "${_opt_name}") + set(wxWidgets_DEFAULT_${_upper_opt_name} ON) + else() + set(wxWidgets_DEFAULT_${_upper_opt_name} OFF) + endif() + endforeach() + else() + foreach(_upper_opt_name DEBUG STATIC UNICODE UNIVERSAL) + set(wxWidgets_DEFAULT_${_upper_opt_name} OFF) + endforeach() + endif() + endmacro() + + # + # Query a boolean configuration option to determine if the system + # has both builds available. If so, provide the selection option + # to the user. + # + macro(WX_CONFIG_SELECT_QUERY_BOOL _OPT_NAME _OPT_HELP) + execute_process( + COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" + ${wxWidgets_CONFIG_OPTIONS} --${_OPT_NAME}=yes + RESULT_VARIABLE _wx_result_yes + OUTPUT_QUIET + ERROR_QUIET + ) + execute_process( + COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" + ${wxWidgets_CONFIG_OPTIONS} --${_OPT_NAME}=no + RESULT_VARIABLE _wx_result_no + OUTPUT_QUIET + ERROR_QUIET + ) + string(TOUPPER ${_OPT_NAME} _UPPER_OPT_NAME) + if(_wx_result_yes EQUAL 0 AND _wx_result_no EQUAL 0) + option(wxWidgets_USE_${_UPPER_OPT_NAME} + ${_OPT_HELP} ${wxWidgets_DEFAULT_${_UPPER_OPT_NAME}}) + else() + # If option exists (already in cache), force to available one. + if(DEFINED wxWidgets_USE_${_UPPER_OPT_NAME}) + if(_wx_result_yes EQUAL 0) + set(wxWidgets_USE_${_UPPER_OPT_NAME} ON CACHE BOOL ${_OPT_HELP} FORCE) + else() + set(wxWidgets_USE_${_UPPER_OPT_NAME} OFF CACHE BOOL ${_OPT_HELP} FORCE) + endif() + endif() + endif() + endmacro() + + # + # Set wxWidgets_SELECT_OPTIONS to wx-config options for selecting + # among multiple builds. + # + macro(WX_CONFIG_SELECT_SET_OPTIONS) + set(wxWidgets_SELECT_OPTIONS ${wxWidgets_CONFIG_OPTIONS}) + foreach(_opt_name debug static unicode universal) + string(TOUPPER ${_opt_name} _upper_opt_name) + if(DEFINED wxWidgets_USE_${_upper_opt_name}) + if(wxWidgets_USE_${_upper_opt_name}) + list(APPEND wxWidgets_SELECT_OPTIONS --${_opt_name}=yes) + else() + list(APPEND wxWidgets_SELECT_OPTIONS --${_opt_name}=no) + endif() + endif() + endforeach() + endmacro() + + #----------------------------------------------------------------- + # UNIX: Start actual work. + #----------------------------------------------------------------- + # Support cross-compiling, only search in the target platform. + # + # Look for wx-config -- this can be set in the environment, + # or try versioned and toolchain-versioned variants of the -config + # executable as well. + set(wx_config_names "wx-config") + foreach(version ${wx_versions}) + list(APPEND wx_config_names "wx-config-${version}" "wxgtk3u-${version}-config" "wxgtk2u-${version}-config") + endforeach() + find_program(wxWidgets_CONFIG_EXECUTABLE + NAMES + $ENV{WX_CONFIG} + ${wx_config_names} + DOC "Location of wxWidgets library configuration provider binary (wx-config)." + ONLY_CMAKE_FIND_ROOT_PATH + ) + + if(wxWidgets_CONFIG_EXECUTABLE) + set(wxWidgets_FOUND TRUE) + + # get defaults based on "wx-config --selected-config" + WX_CONFIG_SELECT_GET_DEFAULT() + + # for each option: if both builds are available, provide option + WX_CONFIG_SELECT_QUERY_BOOL(debug "Use debug build?") + WX_CONFIG_SELECT_QUERY_BOOL(unicode "Use unicode build?") + WX_CONFIG_SELECT_QUERY_BOOL(universal "Use universal build?") + WX_CONFIG_SELECT_QUERY_BOOL(static "Link libraries statically?") + + # process selection to set wxWidgets_SELECT_OPTIONS + WX_CONFIG_SELECT_SET_OPTIONS() + DBG_MSG("wxWidgets_SELECT_OPTIONS=${wxWidgets_SELECT_OPTIONS}") + + # run the wx-config program to get cxxflags + execute_process( + COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" + ${wxWidgets_SELECT_OPTIONS} --cxxflags + OUTPUT_VARIABLE wxWidgets_CXX_FLAGS + RESULT_VARIABLE RET + ERROR_QUIET + ) + if(RET EQUAL 0) + string(STRIP "${wxWidgets_CXX_FLAGS}" wxWidgets_CXX_FLAGS) + separate_arguments(wxWidgets_CXX_FLAGS_LIST NATIVE_COMMAND "${wxWidgets_CXX_FLAGS}") + + DBG_MSG_V("wxWidgets_CXX_FLAGS=${wxWidgets_CXX_FLAGS}") + + # parse definitions and include dirs from cxxflags + # drop the -D and -I prefixes + set(wxWidgets_CXX_FLAGS) + foreach(arg IN LISTS wxWidgets_CXX_FLAGS_LIST) + if("${arg}" MATCHES "^-I(.*)$") + # include directory + list(APPEND wxWidgets_INCLUDE_DIRS "${CMAKE_MATCH_1}") + elseif("${arg}" MATCHES "^-D(.*)$") + # compile definition + list(APPEND wxWidgets_DEFINITIONS "${CMAKE_MATCH_1}") + else() + list(APPEND wxWidgets_CXX_FLAGS "${arg}") + endif() + endforeach() + + DBG_MSG_V("wxWidgets_DEFINITIONS=${wxWidgets_DEFINITIONS}") + DBG_MSG_V("wxWidgets_INCLUDE_DIRS=${wxWidgets_INCLUDE_DIRS}") + DBG_MSG_V("wxWidgets_CXX_FLAGS=${wxWidgets_CXX_FLAGS}") + + else() + set(wxWidgets_FOUND FALSE) + DBG_MSG_V( + "${wxWidgets_CONFIG_EXECUTABLE} --cxxflags FAILED with RET=${RET}") + endif() + + # run the wx-config program to get the libs + # - NOTE: wx-config doesn't verify that the libs requested exist + # it just produces the names. Maybe a TRY_COMPILE would + # be useful here... + unset(_cmp_req) + unset(_cmp_opt) + foreach(_cmp IN LISTS wxWidgets_FIND_COMPONENTS) + if(wxWidgets_FIND_REQUIRED_${_cmp}) + list(APPEND _cmp_req "${_cmp}") + else() + list(APPEND _cmp_opt "${_cmp}") + endif() + endforeach() + DBG_MSG_V("wxWidgets required components : ${_cmp_req}") + DBG_MSG_V("wxWidgets optional components : ${_cmp_opt}") + if(DEFINED _cmp_opt) + string(REPLACE ";" "," _cmp_opt "--optional-libs ${_cmp_opt}") + endif() + string(REPLACE ";" "," _cmp_req "${_cmp_req}") + execute_process( + COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" + ${wxWidgets_SELECT_OPTIONS} --libs ${_cmp_req} ${_cmp_opt} + OUTPUT_VARIABLE wxWidgets_LIBRARIES + RESULT_VARIABLE RET + ERROR_QUIET + ) + if(RET EQUAL 0) + string(STRIP "${wxWidgets_LIBRARIES}" wxWidgets_LIBRARIES) + separate_arguments(wxWidgets_LIBRARIES) + string(REPLACE "-framework;" "-framework " + wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") + string(REPLACE "-weak_framework;" "-weak_framework " + wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") + string(REPLACE "-arch;" "-arch " + wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") + string(REPLACE "-isysroot;" "-isysroot " + wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") + + # extract linkdirs (-L) for rpath (i.e., LINK_DIRECTORIES) + string(REGEX MATCHALL "-L[^;]+" + wxWidgets_LIBRARY_DIRS "${wxWidgets_LIBRARIES}") + string(REGEX REPLACE "-L([^;]+)" "\\1" + wxWidgets_LIBRARY_DIRS "${wxWidgets_LIBRARY_DIRS}") + + DBG_MSG_V("wxWidgets_LIBRARIES=${wxWidgets_LIBRARIES}") + DBG_MSG_V("wxWidgets_LIBRARY_DIRS=${wxWidgets_LIBRARY_DIRS}") + + else() + set(wxWidgets_FOUND FALSE) + DBG_MSG("${wxWidgets_CONFIG_EXECUTABLE} --libs ${_cmp_req} ${_cmp_opt} FAILED with RET=${RET}") + endif() + unset(_cmp_req) + unset(_cmp_opt) + endif() + + # When using wx-config in MSYS, the include paths are UNIX style paths which may or may + # not work correctly depending on you MSYS/MinGW configuration. CMake expects native + # paths internally. + if(wxWidgets_FOUND AND MSYS) + find_program(_cygpath_exe cygpath ONLY_CMAKE_FIND_ROOT_PATH) + DBG_MSG_V("_cygpath_exe: ${_cygpath_exe}") + if(_cygpath_exe) + set(_tmp_path "") + foreach(_path ${wxWidgets_INCLUDE_DIRS}) + execute_process( + COMMAND cygpath -w ${_path} + OUTPUT_VARIABLE _native_path + RESULT_VARIABLE _retv + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if(_retv EQUAL 0) + file(TO_CMAKE_PATH ${_native_path} _native_path) + DBG_MSG_V("Path ${_path} converted to ${_native_path}") + string(APPEND _tmp_path " ${_native_path}") + endif() + endforeach() + DBG_MSG("Setting wxWidgets_INCLUDE_DIRS = ${_tmp_path}") + set(wxWidgets_INCLUDE_DIRS ${_tmp_path}) + separate_arguments(wxWidgets_INCLUDE_DIRS) + list(REMOVE_ITEM wxWidgets_INCLUDE_DIRS "") + + set(_tmp_path "") + foreach(_path ${wxWidgets_LIBRARY_DIRS}) + execute_process( + COMMAND cygpath -w ${_path} + OUTPUT_VARIABLE _native_path + RESULT_VARIABLE _retv + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if(_retv EQUAL 0) + file(TO_CMAKE_PATH ${_native_path} _native_path) + DBG_MSG_V("Path ${_path} converted to ${_native_path}") + string(APPEND _tmp_path " ${_native_path}") + endif() + endforeach() + DBG_MSG("Setting wxWidgets_LIBRARY_DIRS = ${_tmp_path}") + set(wxWidgets_LIBRARY_DIRS ${_tmp_path}) + separate_arguments(wxWidgets_LIBRARY_DIRS) + list(REMOVE_ITEM wxWidgets_LIBRARY_DIRS "") + endif() + unset(_cygpath_exe CACHE) + endif() +endif() + +# Check that all libraries are present, as wx-config does not check it +set(_wx_lib_missing "") +foreach(_wx_lib_ ${wxWidgets_LIBRARIES}) + if("${_wx_lib_}" MATCHES "^-l(.*)") + set(_wx_lib_name "${CMAKE_MATCH_1}") + unset(_wx_lib_found CACHE) + find_library(_wx_lib_found NAMES ${_wx_lib_name} HINTS ${wxWidgets_LIBRARY_DIRS}) + if(_wx_lib_found STREQUAL _wx_lib_found-NOTFOUND) + list(APPEND _wx_lib_missing ${_wx_lib_name}) + endif() + unset(_wx_lib_found CACHE) + endif() +endforeach() + +if (_wx_lib_missing) + string(REPLACE ";" " " _wx_lib_missing "${_wx_lib_missing}") + DBG_MSG_V("wxWidgets not found due to following missing libraries: ${_wx_lib_missing}") + set(wxWidgets_FOUND FALSE) + unset(wxWidgets_LIBRARIES) +endif() +unset(_wx_lib_missing) + +# Check if a specific version was requested by find_package(). +if(wxWidgets_FOUND) + wx_extract_version() +endif() + +# Debug output: +DBG_MSG("wxWidgets_FOUND : ${wxWidgets_FOUND}") +DBG_MSG("wxWidgets_INCLUDE_DIRS : ${wxWidgets_INCLUDE_DIRS}") +DBG_MSG("wxWidgets_LIBRARY_DIRS : ${wxWidgets_LIBRARY_DIRS}") +DBG_MSG("wxWidgets_LIBRARIES : ${wxWidgets_LIBRARIES}") +DBG_MSG("wxWidgets_CXX_FLAGS : ${wxWidgets_CXX_FLAGS}") +DBG_MSG("wxWidgets_USE_FILE : ${wxWidgets_USE_FILE}") + +#===================================================================== +#===================================================================== + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs_SLIC3R.cmake) + +# FIXME: set wxWidgets__FOUND for wx-config branch +# and use HANDLE_COMPONENTS on Unix too +if(wxWidgets_FIND_STYLE STREQUAL "win32") + set(wxWidgets_HANDLE_COMPONENTS "HANDLE_COMPONENTS") +endif() + +find_package_handle_standard_args(wxWidgets + REQUIRED_VARS wxWidgets_LIBRARIES wxWidgets_INCLUDE_DIRS + VERSION_VAR wxWidgets_VERSION_STRING + ${wxWidgets_HANDLE_COMPONENTS} + ) +unset(wxWidgets_HANDLE_COMPONENTS) + +if(wxWidgets_FOUND AND NOT TARGET wxWidgets::wxWidgets) + add_library(wxWidgets::wxWidgets INTERFACE IMPORTED) + target_link_libraries(wxWidgets::wxWidgets INTERFACE ${wxWidgets_LIBRARIES}) + target_link_directories(wxWidgets::wxWidgets INTERFACE ${wxWidgets_LIBRARY_DIRS}) + target_include_directories(wxWidgets::wxWidgets INTERFACE ${wxWidgets_INCLUDE_DIRS}) + target_compile_options(wxWidgets::wxWidgets INTERFACE ${wxWidgets_CXX_FLAGS}) + target_compile_definitions(wxWidgets::wxWidgets INTERFACE ${wxWidgets_DEFINITIONS}) + # FIXME: Add "$<$:${wxWidgets_DEFINITIONS_DEBUG}>" + # if the debug library variant is available. +endif() + +#===================================================================== +# Macros for use in wxWidgets apps. +# - This module will not fail to find wxWidgets based on the code +# below. Hence, it's required to check for validity of: +# +# wxWidgets_wxrc_EXECUTABLE +#===================================================================== + +# Resource file compiler. +find_program(wxWidgets_wxrc_EXECUTABLE + NAMES $ENV{WXRC_CMD} wxrc + PATHS ${wxWidgets_ROOT_DIR}/utils/wxrc/vc_msw + DOC "Location of wxWidgets resource file compiler binary (wxrc)" + ) + +# +# WX_SPLIT_ARGUMENTS_ON( ...) +# +# Sets and to contain arguments to the left and right, +# respectively, of . +# +# Example usage: +# function(WXWIDGETS_ADD_RESOURCES outfiles) +# WX_SPLIT_ARGUMENTS_ON(OPTIONS wxrc_files wxrc_options ${ARGN}) +# ... +# endfunction() +# +# WXWIDGETS_ADD_RESOURCES(sources ${xrc_files} OPTIONS -e -o file.C) +# +# NOTE: This is a generic piece of code that should be renamed to +# SPLIT_ARGUMENTS_ON and put in a file serving the same purpose as +# FindPackageStandardArgs.cmake. At the time of this writing +# FindQt4.cmake has a QT4_EXTRACT_OPTIONS, which I basically copied +# here a bit more generalized. So, there are already two find modules +# using this approach. +# +function(WX_SPLIT_ARGUMENTS_ON _keyword _leftvar _rightvar) + # FIXME: Document that the input variables will be cleared. + #list(APPEND ${_leftvar} "") + #list(APPEND ${_rightvar} "") + set(${_leftvar} "") + set(${_rightvar} "") + + set(_doing_right FALSE) + foreach(element ${ARGN}) + if("${element}" STREQUAL "${_keyword}") + set(_doing_right TRUE) + else() + if(_doing_right) + list(APPEND ${_rightvar} "${element}") + else() + list(APPEND ${_leftvar} "${element}") + endif() + endif() + endforeach() + + set(${_leftvar} ${${_leftvar}} PARENT_SCOPE) + set(${_rightvar} ${${_rightvar}} PARENT_SCOPE) +endfunction() + +# +# WX_GET_DEPENDENCIES_FROM_XML( +# +# +# +# +# +# ) +# +# FIXME: Add documentation here... +# +function(WX_GET_DEPENDENCIES_FROM_XML + _depends + _match_patt + _clean_patt + _xml_contents + _depends_path + ) + + string(REGEX MATCHALL + ${_match_patt} + dep_file_list + "${${_xml_contents}}" + ) + foreach(dep_file ${dep_file_list}) + string(REGEX REPLACE ${_clean_patt} "" dep_file "${dep_file}") + + # make the file have an absolute path + if(NOT IS_ABSOLUTE "${dep_file}") + set(dep_file "${${_depends_path}}/${dep_file}") + endif() + + # append file to dependency list + list(APPEND ${_depends} "${dep_file}") + endforeach() + + set(${_depends} ${${_depends}} PARENT_SCOPE) +endfunction() + +# +# WXWIDGETS_ADD_RESOURCES( +# OPTIONS [NO_CPP_CODE]) +# +# Adds a custom command for resource file compilation of the +# and appends the output files to . +# +# Example usages: +# WXWIDGETS_ADD_RESOURCES(sources xrc/main_frame.xrc) +# WXWIDGETS_ADD_RESOURCES(sources ${xrc_files} OPTIONS -e -o altname.cxx) +# +function(WXWIDGETS_ADD_RESOURCES _outfiles) + WX_SPLIT_ARGUMENTS_ON(OPTIONS rc_file_list rc_options ${ARGN}) + + # Parse files for dependencies. + set(rc_file_list_abs "") + set(rc_depends "") + foreach(rc_file ${rc_file_list}) + get_filename_component(depends_path ${rc_file} PATH) + + get_filename_component(rc_file_abs ${rc_file} ABSOLUTE) + list(APPEND rc_file_list_abs "${rc_file_abs}") + + # All files have absolute paths or paths relative to the location + # of the rc file. + file(READ "${rc_file_abs}" rc_file_contents) + + # get bitmap/bitmap2 files + WX_GET_DEPENDENCIES_FROM_XML( + rc_depends + "]*>" + rc_file_contents + depends_path + ) + + # get url files + WX_GET_DEPENDENCIES_FROM_XML( + rc_depends + "]*>" + rc_file_contents + depends_path + ) + + # get wxIcon files + WX_GET_DEPENDENCIES_FROM_XML( + rc_depends + "]*class=\"wxIcon\"[^<]+" + "^]*>" + rc_file_contents + depends_path + ) + endforeach() + + # + # Parse options. + # + # If NO_CPP_CODE option specified, then produce .xrs file rather + # than a .cpp file (i.e., don't add the default --cpp-code option). + list(FIND rc_options NO_CPP_CODE index) + if(index EQUAL -1) + list(APPEND rc_options --cpp-code) + # wxrc's default output filename for cpp code. + set(outfile resource.cpp) + else() + list(REMOVE_AT rc_options ${index}) + # wxrc's default output filename for xrs file. + set(outfile resource.xrs) + endif() + + # Get output name for use in ADD_CUSTOM_COMMAND. + # - short option scanning + list(FIND rc_options -o index) + if(NOT index EQUAL -1) + math(EXPR filename_index "${index} + 1") + list(GET rc_options ${filename_index} outfile) + #list(REMOVE_AT rc_options ${index} ${filename_index}) + endif() + # - long option scanning + string(REGEX MATCH "--output=[^;]*" outfile_opt "${rc_options}") + if(outfile_opt) + string(REPLACE "--output=" "" outfile "${outfile_opt}") + endif() + #string(REGEX REPLACE "--output=[^;]*;?" "" rc_options "${rc_options}") + #string(REGEX REPLACE ";$" "" rc_options "${rc_options}") + + if(NOT IS_ABSOLUTE "${outfile}") + set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}") + endif() + add_custom_command( + OUTPUT "${outfile}" + COMMAND ${wxWidgets_wxrc_EXECUTABLE} ${rc_options} ${rc_file_list_abs} + DEPENDS ${rc_file_list_abs} ${rc_depends} + ) + + # Add generated header to output file list. + list(FIND rc_options -e short_index) + list(FIND rc_options --extra-cpp-code long_index) + if(NOT short_index EQUAL -1 OR NOT long_index EQUAL -1) + get_filename_component(outfile_ext ${outfile} EXT) + string(REPLACE "${outfile_ext}" ".h" outfile_header "${outfile}") + list(APPEND ${_outfiles} "${outfile_header}") + set_source_files_properties( + "${outfile_header}" PROPERTIES GENERATED TRUE + ) + endif() + + # Add generated file to output file list. + list(APPEND ${_outfiles} "${outfile}") + + set(${_outfiles} ${${_outfiles}} PARENT_SCOPE) +endfunction() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fec0d4cf70..dc23da212a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,26 +49,12 @@ if (SLIC3R_GUI) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}") - find_package(wxWidgets 3.2 QUIET COMPONENTS base core adv html gl) - if (NOT wxWidgets_FOUND) - message(FATAL_ERROR "Could not find wxWidgets >= 3.2") - endif () - - if (NOT "${wxWidgets_USE_FILE}" STREQUAL "") - include(${wxWidgets_USE_FILE}) - endif () - else () - find_package(wxWidgets 3.2 COMPONENTS html adv gl core base) - if (NOT wxWidgets_FOUND) - message(STATUS "Trying to find wxWidgets in CONFIG mode...") - find_package(wxWidgets 3.2 CONFIG REQUIRED COMPONENTS html adv gl core base) - slic3r_remap_configs(wx::wxhtml wx::wxadv wx::wxgl wx::wxcore wx::wxbase RelWithDebInfo Release) - else () - if (NOT "${wxWidgets_USE_FILE}" STREQUAL "") - include(${wxWidgets_USE_FILE}) - endif () - endif () endif () + find_package(wxWidgets 3.2 MODULE REQUIRED COMPONENTS base core adv html gl) + + include(${wxWidgets_USE_FILE}) + + slic3r_remap_configs(wx::wxhtml wx::wxadv wx::wxgl wx::wxcore wx::wxbase RelWithDebInfo Release) if(UNIX) message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}") From 0c2913f20c1e8eb73be7e9c7f9266f726b6ba384 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 4 Aug 2023 15:36:30 +0200 Subject: [PATCH 32/51] CutGizmo: Code refactoring to correct a modifiers processing during a cut. Now it's work in same way for all types of cut. After cut we leave just only modifiers which are intersecting with solid parts bounding box. + Clean code in perform_cut_with_groove. Deleted non-used anymore code. + Extract same code (for perform_cut_by_contour and perform_cut_with_groove) to separate functions + Save CutInfo for objects which was cut with Tongue and Groove mode --- src/libslic3r/Model.cpp | 17 +++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 180 ++++++++++++--------------- 2 files changed, 100 insertions(+), 97 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 92bb15df85..cfe76f8622 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1609,7 +1609,23 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, res.push_back(upper); } else { + // Delete all modifiers which are not intersecting with solid parts bounding box + auto delete_extra_modifiers = [instance](ModelObject* mo) { + if (!mo) return; + const BoundingBoxf3 obj_bb = mo->instance_bounding_box(instance); + const Transform3d inst_matrix = mo->instances[instance]->get_transformation().get_matrix(); + + for (int i = int(mo->volumes.size()) - 1; i >= 0; --i) + if (const ModelVolume* vol = mo->volumes[i]; + !vol->is_model_part() && !vol->is_cut_connector()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + if (!obj_bb.intersects(bb)) + mo->delete_volume(i); + } + }; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { + delete_extra_modifiers(upper); reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), attributes.has(ModelObjectCutAttribute::FlipUpper)); @@ -1617,6 +1633,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, } if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { + delete_extra_modifiers(lower); reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 91bf6d6c86..9d4aeb24ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -3547,6 +3547,48 @@ void synchronize_model_after_cut(Model& model, const CutObjectBase& cut_id) obj->cut_id.copy(cut_id); } +void distribute_modifiers_from_object(ModelObject *from_obj, const int instance_idx, ModelObject *to_obj1, ModelObject *to_obj2) +{ + auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3(); + auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3(); + const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix(); + + for (ModelVolume*vol : from_obj->volumes) + if (!vol->is_model_part()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + // Don't add modifiers which are not intersecting with solid parts + if (obj1_bb.intersects(bb)) + to_obj1->add_volume(*vol); + if (obj2_bb.intersects(bb)) + to_obj2->add_volume(*vol); + } +} + +void merge_solid_parts_inside_object(ModelObjectPtrs& objects) +{ + for (ModelObject* mo : objects) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } + } + } +} + ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count) { const Selection& selection = m_parent.get_selection(); @@ -3584,20 +3626,7 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const if (has_modifiers) { // Distribute Modifiers to the Upper/Lower object - auto upper_bb = upper ? upper->instance_bounding_box(instance_idx) : BoundingBoxf3(); - auto lower_bb = lower ? lower->instance_bounding_box(instance_idx) : BoundingBoxf3(); - const Transform3d inst_matrix = cut_mo->instances[instance_idx]->get_transformation().get_matrix(); - - for (size_t id = 0; id < cut_parts_cnt; ++id) - if (m_part_selection.parts()[id].is_modifier) { - ModelVolume* vol = cut_mo->volumes[id]; - auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); - // Don't add modifiers which are not intersecting with solid parts - if (upper_bb.intersects(bb)) - upper->add_volume(*vol); - if (lower_bb.intersects(bb)) - lower->add_volume(*vol); - } + distribute_modifiers_from_object(cut_mo, instance_idx, upper, lower); } ModelObjectPtrs cut_object_ptrs; @@ -3640,29 +3669,7 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const } // Now merge all model parts together: - { - for (ModelObject* mo : cut_object_ptrs) { - TriangleMesh mesh; - // Merge all SolidPart but not Connectors - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector()) { - TriangleMesh m = mv->mesh(); - m.transform(mv->get_matrix()); - mesh.merge(m); - } - } - if (!mesh.empty()) { - ModelVolume* new_volume = mo->add_volume(mesh); - new_volume->name = mo->name; - // Delete all merged SolidPart but not Connectors - for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { - const ModelVolume* mv = mo->volumes[i]; - if (mv->is_model_part() && !mv->is_cut_connector()) - mo->delete_volume(i); - } - } - } - } + merge_solid_parts_inside_object(cut_object_ptrs); return cut_object_ptrs; } @@ -3693,41 +3700,35 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { const auto& volumes = tmp_model_for_cut.objects.front()->volumes; - bool has_volume = false; for (const ModelVolume* volume : volumes) - if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || - (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper()) ) { - ModelVolume* new_vol = object->add_volume(*volume); - new_vol->reset_from_upper(); - has_volume = true; + if (volume->is_model_part()) { + if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || + (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper()) ) { + ModelVolume* new_vol = object->add_volume(*volume); + new_vol->reset_from_upper(); + } } - return has_volume; }; auto cut = [instance_idx, add_volumes_from_cut] - (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute attribute, Model& tmp_model_for_cut) { + (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) { Model model = Model(); model.add_object(*object); tmp_model_for_cut = Model(); tmp_model_for_cut.objects = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - if (tmp_model_for_cut.objects.empty()) - return false; + assert(!tmp_model_for_cut.objects.empty()); object->clear_volumes(); - add_volumes_from_cut(object, attribute, tmp_model_for_cut); + add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut); ModelObject::reset_instance_transformation(object, instance_idx); - return true; }; - ModelObjectPtrs cut_object_ptrs; - // cut by upper plane const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - if (!cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } @@ -3735,8 +3736,7 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; { - if (!cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); } @@ -3748,8 +3748,7 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool { const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } @@ -3757,8 +3756,7 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool { const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } @@ -3767,23 +3765,22 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; - if (!cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - if (!cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut)) - return cut_object_ptrs; + cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); } // this part can be added to the upper object now add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + ModelObjectPtrs cut_object_ptrs; + if (keep_as_parts) { // add volumes from lower object to the upper, but mark them as a lower const auto& volumes = lower->volumes; @@ -3791,46 +3788,34 @@ ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool ModelVolume* new_vol = upper->add_volume(*volume); new_vol->cut_info.is_from_upper = false; } + + // add modifiers + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) + upper->add_volume(*volume); + cut_object_ptrs.push_back(upper); // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks cut_object_ptrs.push_back(lower); } else { - auto add_cut_objects = [this, &instance_idx](ModelObjectPtrs& cut_objects, ModelObject* object, const Transform3d& cut_matrix) { - if (object && !object->volumes.empty()) { - ModelObject::reset_instance_transformation(object, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); - cut_objects.push_back(object); + // add modifiers if object has any + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) { + distribute_modifiers_from_object(cut_mo, instance_idx, upper, lower); + break; } - }; - add_cut_objects(cut_object_ptrs, upper, cut_matrix_upper); - add_cut_objects(cut_object_ptrs, lower, cut_matrix_lower); + assert(!upper->volumes.empty() && !lower->volumes.empty()); + + ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix_upper, m_place_on_cut_upper, m_rotate_upper); + cut_object_ptrs.push_back(upper); + ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix_lower, m_place_on_cut_upper, m_rotate_upper); + cut_object_ptrs.push_back(lower); // Now merge all model parts together: - { - for (ModelObject* mo : cut_object_ptrs) { - TriangleMesh mesh; - // Merge all SolidPart but not Connectors - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector()) { - TriangleMesh m = mv->mesh(); - m.transform(mv->get_matrix()); - mesh.merge(m); - } - } - if (!mesh.empty()) { - ModelVolume* new_volume = mo->add_volume(mesh); - new_volume->name = mo->name; - // Delete all merged SolidPart but not Connectors - for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { - const ModelVolume* mv = mo->volumes[i]; - if (mv->is_model_part() && !mv->is_cut_connector()) - mo->delete_volume(i); - } - } - } - } + merge_solid_parts_inside_object(cut_object_ptrs); } return cut_object_ptrs; @@ -3860,8 +3845,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // This shall delete the part selection class and deallocate the memory. ScopeGuard part_selection_killer([this]() { m_part_selection = PartSelection(); }); - const bool cut_by_contour = m_part_selection.valid(); const bool cut_with_groove = CutMode(m_mode) == CutMode::cutTongueAndGroove; + const bool cut_by_contour = !cut_with_groove && m_part_selection.valid(); + ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object() : nullptr; if (cut_mo) cut_mo->cut_connectors = mo->cut_connectors; @@ -3885,7 +3871,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(dowels_count > 0, ModelObjectCutAttribute::CreateDowels) | - only_if(!has_connectors && cut_mo->cut_id.id().invalid(), ModelObjectCutAttribute::InvalidateCutInfo); + only_if(!has_connectors && !cut_with_groove && cut_mo->cut_id.id().invalid(), ModelObjectCutAttribute::InvalidateCutInfo); // update cut_id for the cut object in respect to the attributes update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); From 89d66737b5274f7a638ce032ac50d1dc779f56c0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 7 Aug 2023 11:03:56 +0200 Subject: [PATCH 33/51] CutGizmo: Put the check of groove flaps validity to the has_valid_groove() function to avoid perform a cut with non valid groove --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9d4aeb24ea..5a4637b126 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -3427,10 +3427,8 @@ bool GLGizmoCut3D::can_perform_cut() const if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; - if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); - return flaps_width < m_groove_width && has_valid_groove(); - } + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + return has_valid_groove(); if (m_part_selection.valid()) return ! m_part_selection.is_one_object(); @@ -3443,6 +3441,10 @@ bool GLGizmoCut3D::has_valid_groove() const if (CutMode(m_mode) != CutMode::cutTongueAndGroove) return true; + const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); + if (flaps_width > m_groove_width) + return false; + const Selection& selection = m_parent.get_selection(); const auto&list = selection.get_volume_idxs(); // is more volumes selected? From e9d967f4b273eb7e37b22ecde41ff882ab9010a9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 7 Aug 2023 12:58:17 +0200 Subject: [PATCH 34/51] Fix for #11070 - Crash on cut --- src/slic3r/GUI/Plater.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index af6fdc3bf4..9c8297b29c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1276,9 +1276,11 @@ void Sidebar::show_info_sizer() { Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); ModelObjectPtrs objects = p->plater->model().objects; - int obj_idx = selection.get_object_idx(); + const int obj_idx = selection.get_object_idx(); + const int inst_idx = selection.get_instance_idx(); if (m_mode < comExpert || objects.empty() || obj_idx < 0 || int(objects.size()) <= obj_idx || + inst_idx < 0 || int(objects[obj_idx]->instances.size()) <= inst_idx || objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed (selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) || !(selection.is_single_full_instance() || selection.is_single_volume())) { @@ -1288,9 +1290,6 @@ void Sidebar::show_info_sizer() const ModelObject* model_object = objects[obj_idx]; - int inst_idx = selection.get_instance_idx(); - assert(inst_idx >= 0); - bool imperial_units = wxGetApp().app_config->get_bool("use_inches"); double koef = imperial_units ? ObjectManipulation::mm_to_in : 1.0f; From 096d10ef62b5cd47695d2f2004274a4a5a448ae3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 7 Aug 2023 15:30:44 +0200 Subject: [PATCH 35/51] Fix for #10941 - Prusa 2.6.0 freezes when trying to change color --- src/slic3r/GUI/DoubleSlider.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 380e2f5b7e..d96e794621 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -2757,6 +2757,11 @@ bool TickCodeInfo::add_tick(const int tick, Type type, const int extruder, doubl bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) { + // Save previously value of the tick before the call a Dialog from get_... functions, + // otherwise a background process can change ticks values and current iterator wouldn't be valid for the moment of a Dialog close + // and PS will crash (see https://github.com/prusa3d/PrusaSlicer/issues/10941) + TickCode changed_tick = *it; + std::string edited_value; if (it->type == ColorChange) edited_value = get_new_color(it->color); @@ -2768,7 +2773,10 @@ bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) if (edited_value.empty()) return false; - TickCode changed_tick = *it; + // Update iterator. For this moment its value can be invalid + if (it = ticks.find(changed_tick); it == ticks.end()) + return false; + if (it->type == ColorChange) { if (it->color == edited_value) return false; From bb9a04fc4892efb5742044e0c1e778dc9e765e2f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Aug 2023 12:00:11 +0200 Subject: [PATCH 36/51] Fixed TextCtrl::value_was_changed() function to respect changes, when type of Field is coPoints. --- src/slic3r/GUI/Field.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 74ad028d36..121493b68b 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -588,6 +588,8 @@ bool TextCtrl::value_was_changed() case coFloatOrPercent: case coFloatsOrPercents: return boost::any_cast(m_value) != boost::any_cast(val); + case coPoints: + return boost::any_cast>(m_value) != boost::any_cast>(val); default: return true; } From 190a4cde48df9f167468f37d1a95f36bb7f24fa8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Aug 2023 10:25:26 +0200 Subject: [PATCH 37/51] CutGizmo: Next bug fixing * Fixed a crash on UndoRedo action when switching between cut modes * Fixed update of the CutGizmo parameters when selected object is changed --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5a4637b126..55d57f7178 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -507,7 +507,6 @@ void GLGizmoCut3D::switch_to_mode(size_t new_mode) if (m_use_TAG_mesh) update_plane_model(); reset_cut_by_contours(); - update_clipper(); } bool GLGizmoCut3D::render_cut_mode_combo() @@ -1572,7 +1571,6 @@ void GLGizmoCut3D::on_set_state() { if (m_state == On) { m_parent.set_use_color_clip_plane(true); - apply_color_clip_plane_colors(); update_bb(); m_connectors_editing = !m_selected.empty(); @@ -2065,8 +2063,14 @@ void GLGizmoCut3D::update_bb() m_bounding_box = box; + // check, if mode is set to Planar, when object has a connectors + if (const int object_idx = m_parent.get_selection().get_object_idx(); + object_idx >= 0 && !wxGetApp().plater()->model().objects[object_idx]->cut_connectors.empty()) + m_mode = size_t(CutMode::cutPlanar); + invalidate_cut_plane(); reset_cut_by_contours(); + apply_color_clip_plane_colors(); m_max_pos = box.max; m_min_pos = box.min; @@ -2077,6 +2081,8 @@ void GLGizmoCut3D::update_bb() else set_center_pos(m_bb_center); + m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; + m_radius = box.radius(); m_grabber_connection_len = 0.5 * m_radius;// std::min(0.75 * m_radius, 35.0); m_grabber_radius = m_grabber_connection_len * 0.85; @@ -2107,8 +2113,6 @@ void GLGizmoCut3D::update_bb() if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info(); selection && selection->model_object()) m_selected.resize(selection->model_object()->cut_connectors.size(), false); - -// reset_cut_by_contours(); } } From 57b2f364e0d839c64d865c0e49f6c73361261393 Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 8 Aug 2023 13:12:26 +0200 Subject: [PATCH 38/51] Improve and fix curvature estimation algorithm --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 95 +++++++++++----------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 968ba4024f..5314e9afe4 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -146,67 +146,68 @@ std::vector estimate_points_properties(const POINTS std::vector angles_for_curvature(points.size()); std::vector distances_for_curvature(points.size()); - for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { + for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) { ExtendedPoint &a = points[point_idx]; - ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx]; + size_t prev = prev_idx_modulo(point_idx, points.size()); + size_t next = next_idx_modulo(point_idx, points.size()); - int prev_point_idx = point_idx; - while (prev_point_idx > 0) { - prev_point_idx--; - if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { - break; - } + int iter_limit = points.size(); + while ((a.position - points[prev].position).squaredNorm() < 1 && iter_limit > 0) { + prev = prev_idx_modulo(prev, points.size()); + iter_limit--; } - int next_point_index = point_idx; - while (next_point_index < int(points.size()) - 1) { - next_point_index++; - if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { - break; - } + while ((a.position - points[next].position).squaredNorm() < 1 && iter_limit > 0) { + next = next_idx_modulo(next, points.size()); + iter_limit--; } - distances_for_curvature[point_idx] = (prev.position - a.position).norm(); - if (prev_point_idx != point_idx && next_point_index != point_idx) { - float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position); - angles_for_curvature[point_idx] = alfa; - } // else keep zero + distances_for_curvature[point_idx] = (points[prev].position - a.position).norm(); + float alfa = angle(a.position - points[prev].position, points[next].position - a.position); + angles_for_curvature[point_idx] = alfa; } - for (float window_size : {3.0f, 9.0f, 16.0f}) { - size_t tail_point = 0; - float tail_window_acc = 0; - float tail_angle_acc = 0; + if (std::accumulate(distances_for_curvature.begin(), distances_for_curvature.end(), 0) > EPSILON) + for (float window_size : {3.0f, 9.0f, 16.0f}) { + size_t tail_point = 0; + float tail_window_acc = 0; + float tail_angle_acc = 0; - size_t head_point = 0; - float head_window_acc = 0; - float head_angle_acc = 0; + size_t head_point = 0; + float head_window_acc = 0; + float head_angle_acc = 0; - for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { - if (point_idx > 0) { - tail_window_acc += distances_for_curvature[point_idx - 1]; - tail_angle_acc += angles_for_curvature[point_idx - 1]; - head_window_acc -= distances_for_curvature[point_idx - 1]; - head_angle_acc -= angles_for_curvature[point_idx - 1]; - } - while (tail_window_acc > window_size * 0.5 && int(tail_point) < point_idx) { - tail_window_acc -= distances_for_curvature[tail_point]; - tail_angle_acc -= angles_for_curvature[tail_point]; - tail_point++; - } + for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) { + if (point_idx == 0) { + while (tail_window_acc < window_size * 0.5) { + tail_window_acc += distances_for_curvature[tail_point]; + tail_angle_acc += angles_for_curvature[tail_point]; + tail_point = prev_idx_modulo(tail_point, points.size()); + } + } + while (tail_window_acc - distances_for_curvature[next_idx_modulo(tail_point, points.size())] > window_size * 0.5) { + tail_point = next_idx_modulo(tail_point, points.size()); + tail_window_acc -= distances_for_curvature[tail_point]; + tail_angle_acc -= angles_for_curvature[tail_point]; + } - while (head_window_acc < window_size * 0.5 && int(head_point) < int(points.size()) - 1) { - head_window_acc += distances_for_curvature[head_point]; - head_angle_acc += angles_for_curvature[head_point]; - head_point++; - } + while (head_window_acc < window_size * 0.5) { + head_point = next_idx_modulo(head_point, points.size()); + head_window_acc += distances_for_curvature[head_point]; + head_angle_acc += angles_for_curvature[head_point]; + } - float curvature = (tail_angle_acc + head_angle_acc) / (tail_window_acc + head_window_acc); - if (std::abs(curvature) > std::abs(points[point_idx].curvature)) { - points[point_idx].curvature = curvature; + float curvature = (tail_angle_acc + head_angle_acc) / window_size; + if (std::abs(curvature) > std::abs(points[point_idx].curvature)) { + points[point_idx].curvature = curvature; + } + + tail_window_acc += distances_for_curvature[point_idx]; + tail_angle_acc += angles_for_curvature[point_idx]; + head_window_acc -= distances_for_curvature[point_idx]; + head_angle_acc -= angles_for_curvature[point_idx]; } } - } return points; } From 1b451cdf9f8859aff58df54fa89d689aa47518d7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 9 Aug 2023 13:22:11 +0200 Subject: [PATCH 39/51] CutGizmo: Big code refactoring. All manipulations related to cut are extracted to CutUtils now --- src/PrusaSlicer.cpp | 6 +- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/CutUtils.cpp | 627 +++++++++++++++++++++++++++ src/libslic3r/CutUtils.hpp | 65 +++ src/libslic3r/Model.cpp | 321 -------------- src/libslic3r/Model.hpp | 20 - src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 424 ++++-------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 16 +- src/slic3r/GUI/Plater.cpp | 15 +- src/slic3r/GUI/Plater.hpp | 3 +- 10 files changed, 781 insertions(+), 718 deletions(-) create mode 100644 src/libslic3r/CutUtils.cpp create mode 100644 src/libslic3r/CutUtils.hpp diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index e600f343ca..480f42811e 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -40,6 +40,7 @@ #include "libslic3r/Geometry.hpp" #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/CutUtils.hpp" #include "libslic3r/ModelArrange.hpp" #include "libslic3r/Platform.hpp" #include "libslic3r/Print.hpp" @@ -437,8 +438,11 @@ int CLI::run(int argc, char **argv) } #else // model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower); - model.objects.front()->cut(0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()), + Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper); + auto cut_objects = cut.perform_with_plane(); + for (ModelObject* obj : cut_objects) + model.add_object(*obj); #endif model.delete_object(size_t(0)); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..a31e4cc7c8 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -201,6 +201,8 @@ set(SLIC3R_SOURCES BlacklistedLibraryCheck.hpp LocalesUtils.cpp LocalesUtils.hpp + CutUtils.cpp + CutUtils.hpp Model.cpp Model.hpp ModelArrange.hpp diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp new file mode 100644 index 0000000000..7d107b1d05 --- /dev/null +++ b/src/libslic3r/CutUtils.cpp @@ -0,0 +1,627 @@ + +#include "CutUtils.hpp" +#include "libslic3r.h" +#include "Model.hpp" +#include "TriangleMeshSlicer.hpp" +#include "TriangleSelector.hpp" + + +namespace Slic3r { + +using namespace Geometry; + +static void apply_tolerance(ModelVolume* vol) +{ + ModelVolume::CutInfo& cut_info = vol->cut_info; + + assert(cut_info.is_connector); + if (!cut_info.is_processed) + return; + + Vec3d sf = vol->get_scaling_factor(); + + // make a "hole" wider + sf[X] += double(cut_info.radius_tolerance); + sf[Y] += double(cut_info.radius_tolerance); + + // make a "hole" dipper + sf[Z] += double(cut_info.height_tolerance); + + vol->set_scaling_factor(sf); + + // correct offset in respect to the new depth + Vec3d rot_norm = rotation_transform(vol->get_rotation()) * Vec3d::UnitZ(); + if (rot_norm.norm() != 0.0) + rot_norm.normalize(); + + double z_offset = 0.5 * static_cast(cut_info.height_tolerance); + if (cut_info.connector_type == CutConnectorType::Plug || + cut_info.connector_type == CutConnectorType::Snap) + z_offset -= 0.05; // add small Z offset to better preview + + vol->set_offset(vol->get_offset() + rot_norm * z_offset); +} + +static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART) +{ + if (mesh.empty()) + return; + + mesh.transform(cut_matrix); + ModelVolume* vol = object->add_volume(mesh); + vol->set_type(type); + + vol->name = src_volume->name + suffix; + // Don't copy the config's ID. + vol->config.assign_config(src_volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != src_volume->config.id()); + vol->set_material(src_volume->material_id(), *src_volume->material()); + vol->cut_info = src_volume->cut_info; +} + +static void process_volume_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh) +{ + const auto volume_matrix = volume->get_matrix(); + + const Transformation cut_transformation = Transformation(cut_matrix); + const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1 * cut_transformation.get_offset()); + + // Transform the mesh by the combined transformation matrix. + // Flip the triangles in case the composite transformation is left handed. + TriangleMesh mesh(volume->mesh()); + mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true); + + indexed_triangle_set upper_its, lower_its; + cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper_mesh = TriangleMesh(upper_its); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower_mesh = TriangleMesh(lower_its); +} + +static void process_connector_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels) +{ + assert(volume->cut_info.is_connector); + volume->cut_info.set_processed(); + + const auto volume_matrix = volume->get_matrix(); + + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there + if (volume->cut_info.connector_type != CutConnectorType::Dowel) { + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume* vol = nullptr; + if (volume->cut_info.connector_type == CutConnectorType::Snap) { + TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); + + vol = upper->add_volume(std::move(mesh)); + vol->set_transformation(volume->get_transformation()); + vol->set_type(ModelVolumeType::NEGATIVE_VOLUME); + + vol->cut_info = volume->cut_info; + vol->name = volume->name; + } + else + vol = upper->add_volume(*volume); + + vol->set_transformation(volume_matrix); + apply_tolerance(vol); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume* vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); + // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug + vol->set_type(ModelVolumeType::MODEL_PART); + } + } + else { + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject* dowel{ nullptr }; + // Clone the object to duplicate instances, materials etc. + volume->get_object()->clone_for_cut(&dowel); + + // add one more solid part same as connector if this connector is a dowel + ModelVolume* vol = dowel->add_volume(*volume); + vol->set_type(ModelVolumeType::MODEL_PART); + + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + + dowels.push_back(dowel); + } + + // Cut the dowel + apply_tolerance(volume); + + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh); + + // add small Z offset to better preview + upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast()); + lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast()); + + // Add cut parts to the related objects + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); + add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); + } +} + +static void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + const auto volume_matrix = instance_matrix * volume->get_matrix(); + + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + volume->set_transformation(Transformation(volume_matrix)); + + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + upper->add_volume(*volume); + return; + } + + // Some logic for the negative volumes/connectors. Add only needed modifiers + auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); + bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) + lower->add_volume(*volume); +} + +static void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // Add required cut parts to the objects + + if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); + add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + upper->volumes.back()->cut_info.is_from_upper = false; + return; + } + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + add_cut_volume(upper_mesh, upper, volume, cut_matrix); + + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) + add_cut_volume(lower_mesh, lower, volume, cut_matrix); +} + +static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, + const Transform3d& cut_matrix = Transform3d::Identity(), + bool place_on_cut = false, bool flip = false) +{ + // Reset instance transformation except offset and Z-rotation + + for (size_t i = 0; i < object->instances.size(); ++i) { + auto& obj_instance = object->instances[i]; + const double rot_z = obj_instance->get_rotation().z(); + + obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor())); + + Vec3d rotation = Vec3d::Zero(); + if (!flip && !place_on_cut) { + if ( i != src_instance_idx) + rotation[Z] = rot_z; + } + else { + Transform3d rotation_matrix = Transform3d::Identity(); + if (flip) + rotation_matrix = rotation_transform(PI * Vec3d::UnitX()); + + if (place_on_cut) + rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse(); + + if (i != src_instance_idx) + rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix; + + rotation = Transformation(rotation_matrix).get_rotation(); + } + + obj_instance->set_rotation(rotation); + } +} + + +Cut::Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes/*= ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts*/) + : m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes) +{ + m_model = Model(); + if (object) + m_model.add_object(*object); +} + +const ModelObjectPtrs& Cut::perform_with_plane() +{ + if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) { + m_model.clear_objects(); + return m_model.objects; + } + + ModelObject* mo = m_model.objects.front(); + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) + mo->clone_for_cut(&upper); + + ModelObject* lower{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !m_attributes.has(ModelObjectCutAttribute::KeepAsParts)) + mo->clone_for_cut(&lower); + + std::vector dowels; + + // Because transformations are going to be applied to meshes directly, + // we reset transformation of all instances and volumes, + // except for translation and Z-rotation on instances, which are preserved + // in the transformation matrix and not applied to the mesh transform. + + const auto instance_matrix = mo->instances[m_instance]->get_transformation().get_matrix_no_offset(); + const Transformation cut_transformation = Transformation(m_cut_matrix); + const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset()); + + for (ModelVolume* volume : mo->volumes) { + volume->reset_extra_facets(); + + if (!volume->is_model_part()) { + if (volume->cut_info.is_processed) + process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, m_attributes, upper, lower); + else + process_connector_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower, dowels); + } + else if (!volume->mesh().empty()) + process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower); + } + + // Post-process cut parts + + if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) { + m_model = Model(); + m_model.objects.push_back(upper); + return m_model.objects; + } + + ModelObjectPtrs cut_object_ptrs; + + if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { + reset_instance_transformation(upper, m_instance, m_cut_matrix); + cut_object_ptrs.push_back(upper); + } + else { + // Delete all modifiers which are not intersecting with solid parts bounding box + auto delete_extra_modifiers = [this](ModelObject* mo) { + if (!mo) return; + const BoundingBoxf3 obj_bb = mo->instance_bounding_box(m_instance); + const Transform3d inst_matrix = mo->instances[m_instance]->get_transformation().get_matrix(); + + for (int i = int(mo->volumes.size()) - 1; i >= 0; --i) + if (const ModelVolume* vol = mo->volumes[i]; + !vol->is_model_part() && !vol->is_cut_connector()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + if (!obj_bb.intersects(bb)) + mo->delete_volume(i); + } + }; + + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { + delete_extra_modifiers(upper); + reset_instance_transformation(upper, m_instance, m_cut_matrix, + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(upper); + } + + if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { + delete_extra_modifiers(lower); + reset_instance_transformation(lower, m_instance, m_cut_matrix, + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); + cut_object_ptrs.push_back(lower); + } + + if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { + for (auto dowel : dowels) { + reset_instance_transformation(dowel, m_instance, Transform3d::Identity()); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + cut_object_ptrs.push_back(dowel); + } + } + } + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + + m_model.clear_objects(); + m_model.objects = cut_object_ptrs; + + return m_model.objects; +} + +static void distribute_modifiers_from_object(ModelObject* from_obj, const int instance_idx, ModelObject* to_obj1, ModelObject* to_obj2) +{ + auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3(); + auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3(); + const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix(); + + for (ModelVolume* vol : from_obj->volumes) + if (!vol->is_model_part()) { + auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); + // Don't add modifiers which are not intersecting with solid parts + if (obj1_bb.intersects(bb)) + to_obj1->add_volume(*vol); + if (obj2_bb.intersects(bb)) + to_obj2->add_volume(*vol); + } +} + +static void merge_solid_parts_inside_object(ModelObjectPtrs& objects) +{ + for (ModelObject* mo : objects) { + TriangleMesh mesh; + // Merge all SolidPart but not Connectors + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part() && !mv->is_cut_connector()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (!mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + new_volume->name = mo->name; + // Delete all merged SolidPart but not Connectors + for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { + const ModelVolume* mv = mo->volumes[i]; + if (mv->is_model_part() && !mv->is_cut_connector()) + mo->delete_volume(i); + } + } + } +} + + +const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowels_count) +{ + ModelObject* cut_mo = m_model.objects.front(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower); + + auto add_cut_objects = [this](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { + if (upper && !upper->volumes.empty()) { + reset_instance_transformation(upper, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_objects.push_back(upper); + } + if (lower && !lower->volumes.empty()) { + reset_instance_transformation(lower, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); + cut_objects.push_back(lower); + } + }; + + const size_t cut_parts_cnt = parts.size(); + bool has_modifiers = false; + + // Distribute SolidParts to the Upper/Lower object + for (size_t id = 0; id < cut_parts_cnt; ++id) { + if (parts[id].is_modifier) + has_modifiers = true; // modifiers will be added later to the related parts + else if (ModelObject* obj = (parts[id].selected ? upper : lower)) + obj->add_volume(*(cut_mo->volumes[id])); + } + + if (has_modifiers) { + // Distribute Modifiers to the Upper/Lower object + distribute_modifiers_from_object(cut_mo, m_instance, upper, lower); + } + + ModelObjectPtrs cut_object_ptrs; + + ModelVolumePtrs& volumes = cut_mo->volumes; + if (volumes.size() == cut_parts_cnt) { + // Means that object is cut without connectors + + // Just add Upper and Lower objects to cut_object_ptrs + add_cut_objects(cut_object_ptrs, upper, lower); + } + else if (volumes.size() > cut_parts_cnt) { + // Means that object is cut with connectors + + // All volumes are distributed to Upper / Lower object, + // So we don’t need them anymore + for (size_t id = 0; id < cut_parts_cnt; id++) + delete* (volumes.begin() + id); + volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); + + // Perform cut just to get connectors + Cut cut(cut_mo, m_instance, m_cut_matrix, m_attributes); + const ModelObjectPtrs& cut_connectors_obj = cut.perform_with_plane(); + assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); + + // Connectors from upper object + for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) + upper->add_volume(*volume, volume->type()); + + // Connectors from lower object + for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) + lower->add_volume(*volume, volume->type()); + + // Add Upper and Lower objects to cut_object_ptrs + add_cut_objects(cut_object_ptrs, upper, lower); + + // Add Dowel-connectors as separate objects to cut_object_ptrs + if (cut_connectors_obj.size() >= 3) + for (size_t id = 2; id < cut_connectors_obj.size(); id++) + cut_object_ptrs.push_back(cut_connectors_obj[id]); + } + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + + m_model.clear_objects(); + m_model.objects = cut_object_ptrs; + + return m_model.objects; +} + + +const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts/* = false*/) +{ + ModelObject* cut_mo = m_model.objects.front(); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + cut_mo->clone_for_cut(&lower); + + const double groove_half_depth = 0.5 * double(groove.depth); + + Model tmp_model_for_cut = Model(); + + Model tmp_model = Model(); + tmp_model.add_object(*cut_mo); + ModelObject* tmp_object = tmp_model.objects.front(); + + auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { + const auto& volumes = tmp_model_for_cut.objects.front()->volumes; + for (const ModelVolume* volume : volumes) + if (volume->is_model_part()) { + if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || + (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper())) { + ModelVolume* new_vol = object->add_volume(*volume); + new_vol->reset_from_upper(); + } + } + }; + + auto cut = [this, add_volumes_from_cut] + (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) { + Cut cut(object, m_instance, cut_matrix); + + tmp_model_for_cut = Model(); + tmp_model_for_cut.add_object(*cut.perform_with_plane().front()); + assert(!tmp_model_for_cut.objects.empty()); + + object->clear_volumes(); + add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut); + reset_instance_transformation(object, m_instance); + }; + + // cut by upper plane + + const Transform3d cut_matrix_upper = translation_transform(rotation_m * (groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix; + { + cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // cut by lower plane + + const Transform3d cut_matrix_lower = translation_transform(rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix; + { + cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + } + + // cut middle part with 2 angles and add parts to related upper/lower objects + + const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle)); + + // cut by angle1 plane + { + const Transform3d cut_matrix_angle1 = translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + + cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // cut by angle2 plane + { + const Transform3d cut_matrix_angle2 = translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + + cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // apply tolerance to the middle part + { + const double h_groove_shift_tolerance = groove_half_depth - (double)groove.depth_tolerance; + + const Transform3d cut_matrix_lower_tolerance = translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * m_cut_matrix; + cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + + const double h_side_shift_tolerance = h_side_shift - 0.5 * double(groove.width_tolerance); + + const Transform3d cut_matrix_angle1_tolerance = translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle)); + cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + const Transform3d cut_matrix_angle2_tolerance = translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle)); + cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); + } + + // this part can be added to the upper object now + add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); + + ModelObjectPtrs cut_object_ptrs; + + if (keep_as_parts) { + // add volumes from lower object to the upper, but mark them as a lower + const auto& volumes = lower->volumes; + for (const ModelVolume* volume : volumes) { + ModelVolume* new_vol = upper->add_volume(*volume); + new_vol->cut_info.is_from_upper = false; + } + + // add modifiers + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) + upper->add_volume(*volume); + + cut_object_ptrs.push_back(upper); + + // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks + cut_object_ptrs.push_back(lower); + } + else { + // add modifiers if object has any + for (const ModelVolume* volume : cut_mo->volumes) + if (!volume->is_model_part()) { + distribute_modifiers_from_object(cut_mo, m_instance, upper, lower); + break; + } + + assert(!upper->volumes.empty() && !lower->volumes.empty()); + + reset_instance_transformation(upper, m_instance, cut_matrix_upper, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(upper); + reset_instance_transformation(lower, m_instance, cut_matrix_lower, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(lower); + + // Now merge all model parts together: + merge_solid_parts_inside_object(cut_object_ptrs); + } + + m_model.clear_objects(); + m_model.objects = cut_object_ptrs; + + return m_model.objects; +} + +} // namespace Slic3r + diff --git a/src/libslic3r/CutUtils.hpp b/src/libslic3r/CutUtils.hpp new file mode 100644 index 0000000000..a31404e898 --- /dev/null +++ b/src/libslic3r/CutUtils.hpp @@ -0,0 +1,65 @@ +#ifndef slic3r_CutUtils_hpp_ +#define slic3r_CutUtils_hpp_ + +#include "libslic3r.h" +#include "enum_bitmask.hpp" +#include "Geometry.hpp" +#include "ObjectID.hpp" +#include "Point.hpp" +#include "Model.hpp" + +#include + +namespace Slic3r { + +using ModelObjectPtrs = std::vector; + +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo }; +using ModelObjectCutAttributes = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); + + +class Cut { + + Model m_model; + int m_instance; + const Transform3d& m_cut_matrix; + ModelObjectCutAttributes m_attributes; + +public: + + Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts ); + ~Cut() { m_model.clear_objects(); } + + struct Groove + { + float depth{ 0.f }; + float width{ 0.f }; + float flaps_angle{ 0.f }; + float angle{ 0.f }; + float depth_init{ 0.f }; + float width_init{ 0.f }; + float flaps_angle_init{ 0.f }; + float angle_init{ 0.f }; + float depth_tolerance{ 0.1f }; + float width_tolerance{ 0.1f }; + }; + + struct Part + { + bool selected; + bool is_modifier; + }; + + const ModelObjectPtrs& perform_with_plane(); + const ModelObjectPtrs& perform_by_contour(std::vector parts, int dowels_count); + const ModelObjectPtrs& perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts = false); + +}; // namespace Cut + +} // namespace Slic3r + +#endif /* slic3r_CutUtils_hpp_ */ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index cfe76f8622..2a8c78729b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1332,327 +1332,6 @@ void ModelVolume::reset_extra_facets() this->mmu_segmentation_facets.reset(); } -void ModelVolume::apply_tolerance() -{ - assert(cut_info.is_connector); - if (!cut_info.is_processed) - return; - - Vec3d sf = get_scaling_factor(); - - // make a "hole" wider - sf[X] += double(cut_info.radius_tolerance); - sf[Y] += double(cut_info.radius_tolerance); - - // make a "hole" dipper - sf[Z] += double(cut_info.height_tolerance); - - set_scaling_factor(sf); - - // correct offset in respect to the new depth - Vec3d rot_norm = Geometry::rotation_transform(get_rotation()) * Vec3d::UnitZ(); - if (rot_norm.norm() != 0.0) - rot_norm.normalize(); - - double z_offset = 0.5 * static_cast(cut_info.height_tolerance); - if (cut_info.connector_type == CutConnectorType::Plug || - cut_info.connector_type == CutConnectorType::Snap) - z_offset -= 0.05; // add small Z offset to better preview - - set_offset(get_offset() + rot_norm * z_offset); -} - -static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART) -{ - if (mesh.empty()) - return; - - mesh.transform(cut_matrix); - ModelVolume* vol = object->add_volume(mesh); - vol->set_type(type); - - vol->name = src_volume->name + suffix; - // Don't copy the config's ID. - vol->config.assign_config(src_volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != src_volume->config.id()); - vol->set_material(src_volume->material_id(), *src_volume->material()); - vol->cut_info = src_volume->cut_info; -} - -void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels) -{ - assert(volume->cut_info.is_connector); - volume->cut_info.set_processed(); - - const auto volume_matrix = volume->get_matrix(); - - // ! Don't apply instance transformation for the conntectors. - // This transformation is already there - if (volume->cut_info.connector_type != CutConnectorType::Dowel) { - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume* vol = nullptr; - if (volume->cut_info.connector_type == CutConnectorType::Snap) { - TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.)); - - vol = upper->add_volume(std::move(mesh)); - vol->set_transformation(volume->get_transformation()); - vol->set_type(ModelVolumeType::NEGATIVE_VOLUME); - - vol->cut_info = volume->cut_info; - vol->name = volume->name; - } - else - vol = upper->add_volume(*volume); - - vol->set_transformation(volume_matrix); - vol->apply_tolerance(); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - ModelVolume* vol = lower->add_volume(*volume); - vol->set_transformation(volume_matrix); - // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug - vol->set_type(ModelVolumeType::MODEL_PART); - } - } - else { - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { - ModelObject* dowel{ nullptr }; - // Clone the object to duplicate instances, materials etc. - clone_for_cut(&dowel); - - // add one more solid part same as connector if this connector is a dowel - ModelVolume* vol = dowel->add_volume(*volume); - vol->set_type(ModelVolumeType::MODEL_PART); - - // But discard rotation and Z-offset for this volume - vol->set_rotation(Vec3d::Zero()); - vol->set_offset(Z, 0.0); - - dowels.push_back(dowel); - } - - // Cut the dowel - volume->apply_tolerance(); - - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh); - - // add small Z offset to better preview - upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast()); - lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast()); - - // Add cut parts to the related objects - add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); - add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); - } -} - -void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) -{ - const auto volume_matrix = instance_matrix * volume->get_matrix(); - - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - volume->set_transformation(Geometry::Transformation(volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { - upper->add_volume(*volume); - return; - } - - // Some logic for the negative volumes/connectors. Add only needed modifiers - auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); - bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) - lower->add_volume(*volume); -} - -void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh) -{ - const auto volume_matrix = volume->get_matrix(); - - using namespace Geometry; - - const Transformation cut_transformation = Transformation(cut_matrix); - const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1 * cut_transformation.get_offset()); - - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true); - - indexed_triangle_set upper_its, lower_its; - cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper_mesh = TriangleMesh(upper_its); - 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) -{ - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); - - // Add required cut parts to the objects - - if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { - add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); - add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); - upper->volumes.back()->cut_info.is_from_upper = false; - return; - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - add_cut_volume(upper_mesh, upper, volume, cut_matrix); - - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) - add_cut_volume(lower_mesh, lower, volume, cut_matrix); -} - -void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, - bool place_on_cut/* = false*/, bool flip/* = false*/) -{ - using namespace Geometry; - - // Reset instance transformation except offset and Z-rotation - - for (size_t i = 0; i < object->instances.size(); ++i) { - auto& obj_instance = object->instances[i]; - const double rot_z = obj_instance->get_rotation().z(); - - obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor())); - - Vec3d rotation = Vec3d::Zero(); - if (!flip && !place_on_cut) { - if ( i != src_instance_idx) - rotation[Z] = rot_z; - } - else { - Transform3d rotation_matrix = Transform3d::Identity(); - if (flip) - rotation_matrix = rotation_transform(PI * Vec3d::UnitX()); - - if (place_on_cut) - rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse(); - - if (i != src_instance_idx) - rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix; - - rotation = Transformation(rotation_matrix).get_rotation(); - } - - obj_instance->set_rotation(rotation); - } -} - -ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) -{ - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) - return {}; - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - clone_for_cut(&upper); - - ModelObject* lower{ nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::KeepAsParts)) - clone_for_cut(&lower); - - std::vector dowels; - - using namespace Geometry; - - // Because transformations are going to be applied to meshes directly, - // we reset transformation of all instances and volumes, - // except for translation and Z-rotation on instances, which are preserved - // in the transformation matrix and not applied to the mesh transform. - - // const auto instance_matrix = instances[instance]->get_matrix(true); - const auto instance_matrix = instances[instance]->get_transformation().get_matrix_no_offset(); - const Transformation cut_transformation = Transformation(cut_matrix); - const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset()); - - for (ModelVolume* volume : volumes) { - volume->reset_extra_facets(); - - if (!volume->is_model_part()) { - if (volume->cut_info.is_processed) - process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); - else - process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels); - } - else if (!volume->mesh().empty()) - process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower); - } - - // Post-process cut parts - - ModelObjectPtrs res; - if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) - return res; - - if (attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) { - reset_instance_transformation(upper, instance, cut_matrix); - res.push_back(upper); - } - else { - // Delete all modifiers which are not intersecting with solid parts bounding box - auto delete_extra_modifiers = [instance](ModelObject* mo) { - if (!mo) return; - const BoundingBoxf3 obj_bb = mo->instance_bounding_box(instance); - const Transform3d inst_matrix = mo->instances[instance]->get_transformation().get_matrix(); - - for (int i = int(mo->volumes.size()) - 1; i >= 0; --i) - if (const ModelVolume* vol = mo->volumes[i]; - !vol->is_model_part() && !vol->is_cut_connector()) { - auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); - if (!obj_bb.intersects(bb)) - mo->delete_volume(i); - } - }; - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { - delete_extra_modifiers(upper); - reset_instance_transformation(upper, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - attributes.has(ModelObjectCutAttribute::FlipUpper)); - res.push_back(upper); - } - - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { - delete_extra_modifiers(lower); - reset_instance_transformation(lower, instance, cut_matrix, - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower)); - res.push_back(lower); - } - - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { - for (auto dowel : dowels) { - reset_instance_transformation(dowel, instance, Transform3d::Identity()); - dowel->name += "-Dowel-" + dowel->volumes[0]->name; - res.push_back(dowel); - } - } - } - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; - - return res; -} /// /// Compare TriangleMeshes by Bounding boxes (mainly for sort) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a14bf37705..221033c521 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -317,10 +317,6 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo }; -using ModelObjectCutAttributes = enum_bitmask; -ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); - // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), // and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. // Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, @@ -468,21 +464,6 @@ public: void delete_connectors(); void clone_for_cut(ModelObject **obj); -private: - void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels); - void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); - void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh); - void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); -public: - static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix = Transform3d::Identity(), - bool place_on_cut = false, bool flip = false); - - ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs*new_objects); void merge(); // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, @@ -850,7 +831,6 @@ public: bool is_the_only_one_part() const; // behave like an object t_model_material_id material_id() const { return m_material_id; } void reset_extra_facets(); - void apply_tolerance(); void set_material_id(t_model_material_id material_id); ModelMaterial* material() const; void set_material(t_model_material_id material_id, const ModelMaterial &material); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 55d57f7178..3dd4fe6edd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -675,7 +675,7 @@ static double get_grabber_mean_size(const BoundingBoxf3& bb) indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() { - const float ghw = 0.5f * m_groove_width; // groove half width + const float ghw = 0.5f * m_groove.width; // groove half width const float cpr = 1.5f * float(m_radius); // cut plane radius const float cpl = 1.5f * cpr; // cut plane length @@ -684,7 +684,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() // We need two upward facing triangles float x = 0.5f * cpr, y = 0.5f * cpl; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); const float extension_x = ghw + proj; // upper cut plane is simple @@ -712,7 +712,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() if (ghw < proj) { - const float cross_pt_y = ghw / tan(m_groove_angle); + const float cross_pt_y = ghw / tan(m_groove.angle); return { { // roof @@ -756,7 +756,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width) { - const float ghw = 0.5f * (m_groove_width + flaps_width); // groove half width + const float ghw = 0.5f * (m_groove.width + flaps_width); // groove half width const float cpr = 1.5f * float(m_radius); // cut plane radius const float cpl = 1.5f * cpr; // cut plane length @@ -765,7 +765,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width // We need two upward facing triangles float x = 0.5f * cpr, y = 0.5f * cpl; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); const float extension_x = ghw + proj; // upper cut plane is trapezium @@ -793,7 +793,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width // upper cut plane is triangle - const float cross_pt_y = ghw / tan(m_groove_angle); + const float cross_pt_y = ghw / tan(m_groove.angle); return { { // roof @@ -814,14 +814,14 @@ indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width) { - const float ghw_upper = 0.5f * (m_groove_width); // groove half width - const float ghw_lower = 0.5f * (m_groove_width + flaps_width); // groove half width + const float ghw_upper = 0.5f * (m_groove.width); // groove half width + const float ghw_lower = 0.5f * (m_groove.width + flaps_width); // groove half width const float cpr = 1.5f * float(m_radius); // cut plane radius const float cpl = 1.5f * cpr; // cut plane length const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height - const float ghd = 0.5f * m_groove_depth; // groove half depth + const float ghd = 0.5f * m_groove.depth; // groove half depth // We need two upward facing triangles @@ -829,7 +829,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width float z_upper = ghd; float z_lower = -ghd; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); const float extension_upper_x = ghw_upper + proj; const float extension_lower_x = ghw_lower + proj; @@ -854,12 +854,12 @@ indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width }; } - const float cross_pt_upper_y = ghw_upper / tan(m_groove_angle); + const float cross_pt_upper_y = ghw_upper / tan(m_groove.angle); // groove is closed if (ghw_upper < proj && ghw_lower < proj) { - const float cross_pt_lower_y = ghw_lower / tan(m_groove_angle); + const float cross_pt_lower_y = ghw_lower / tan(m_groove.angle); return { { @@ -898,23 +898,23 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() { // values for calculation - const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle)); - const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle); + const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle); - const float groove_half_width_upper = 0.5f * (m_groove_width); - const float groove_half_width_lower = 0.5f * (m_groove_width + flaps_width); + const float groove_half_width_upper = 0.5f * (m_groove.width); + const float groove_half_width_lower = 0.5f * (m_groove.width + flaps_width); const float cut_plane_radius = 1.5f * float(m_radius); const float cut_plane_length = 1.5f * cut_plane_radius; - const float groove_half_depth = 0.5f * m_groove_depth; + const float groove_half_depth = 0.5f * m_groove.depth; const float x = 0.5f * cut_plane_radius; const float y = 0.5f * cut_plane_length; float z_upper = groove_half_depth; float z_lower = -groove_half_depth; - const float proj = y * tan(m_groove_angle); + const float proj = y * tan(m_groove.angle); float ext_upper_x = groove_half_width_upper + proj; // upper_x extension float ext_lower_x = groove_half_width_lower + proj; // lower_x extension @@ -966,7 +966,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_upper -= cut_plane_thiknes; z_lower -= cut_plane_thiknes; if (m_use_TAG_mesh_full) { - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); nar_upper_x += under_x_shift; nar_lower_x += under_x_shift; @@ -1019,12 +1019,12 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() return mesh; } - float cross_pt_upper_y = groove_half_width_upper / tan(m_groove_angle); + float cross_pt_upper_y = groove_half_width_upper / tan(m_groove.angle); // groove is closed if (groove_half_width_upper < proj && groove_half_width_lower < proj) { - float cross_pt_lower_y = groove_half_width_lower / tan(m_groove_angle); + float cross_pt_lower_y = groove_half_width_lower / tan(m_groove.angle); indexed_triangle_set mesh; @@ -1045,7 +1045,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_lower -= cut_plane_thiknes; if (m_use_TAG_mesh_full) { - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); cross_pt_upper_y += cut_plane_thiknes; cross_pt_lower_y += cut_plane_thiknes; @@ -1112,7 +1112,7 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_upper -= cut_plane_thiknes; z_lower -= cut_plane_thiknes; - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove_flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); nar_lower_x += under_x_shift; ext_upper_x += under_x_shift; @@ -1173,10 +1173,10 @@ void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shade ColorRGBA cp_clr = m_plane.model.get_color(); // values for calculaton - const double groove_half_depth = 0.5 * double(m_groove_depth); + const double groove_half_depth = 0.5 * double(m_groove.depth); - const float side_width = is_approx(m_groove_flaps_angle, 0.f) ? m_groove_depth : (m_groove_depth / sin(m_groove_flaps_angle)); - const float flaps_width = 2.f * side_width * cos(m_groove_flaps_angle); + const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle)); + const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle); GLModel model; @@ -1454,7 +1454,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() // render CutPlaneYMove grabber - if (m_groove_angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) + if (m_groove.angle > 0.0f && (no_xy_grabber_hovered || m_hover_id == CutPlaneYMove)) { size = (m_dragging ? get_dragging_half_size(mean_size) : get_half_size(mean_size)); color = m_hover_id == CutPlaneYMove ? ColorRGBA::GREEN() : m_plane.model.get_color(); @@ -1526,19 +1526,19 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) if (m_mode != mode) switch_to_mode(mode); else if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - if (!is_approx(m_groove_depth , groove_depth) || - !is_approx(m_groove_width , groove_width) || - !is_approx(m_groove_flaps_angle , groove_flaps_angle) || - !is_approx(m_groove_angle , groove_angle) || - !is_approx(m_groove_depth_tolerance, groove_depth_tolerance) || - !is_approx(m_groove_width_tolerance, groove_width_tolerance) ) + if (!is_approx(m_groove.depth , groove_depth) || + !is_approx(m_groove.width , groove_width) || + !is_approx(m_groove.flaps_angle , groove_flaps_angle) || + !is_approx(m_groove.angle , groove_angle) || + !is_approx(m_groove.depth_tolerance, groove_depth_tolerance) || + !is_approx(m_groove.width_tolerance, groove_width_tolerance) ) { - m_groove_depth = groove_depth; - m_groove_width = groove_width; - m_groove_flaps_angle = groove_flaps_angle; - m_groove_angle = groove_angle; - m_groove_depth_tolerance= groove_depth_tolerance; - m_groove_width_tolerance= groove_width_tolerance; + m_groove.depth = groove_depth; + m_groove.width = groove_width; + m_groove.flaps_angle = groove_flaps_angle; + m_groove.angle = groove_angle; + m_groove.depth_tolerance= groove_depth_tolerance; + m_groove.width_tolerance= groove_width_tolerance; update_plane_model(); } reset_cut_by_contours(); @@ -1551,7 +1551,7 @@ 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_connectors_editing, m_ar_plane_center, m_start_dragging_m, - m_groove_depth, m_groove_width, m_groove_flaps_angle, m_groove_angle, m_groove_depth_tolerance, m_groove_width_tolerance); + m_groove.depth, m_groove.width, m_groove.flaps_angle, m_groove.angle, m_groove.depth_tolerance, m_groove.width_tolerance); } std::string GLGizmoCut3D::on_get_name() const @@ -1755,7 +1755,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() offset = (size + xy_connection_len) * Vec3d::UnitX(); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * rotation_transform(0.5 * PI * Vec3d::UnitY()) * scale_transform(cone_scale)); - if (m_groove_angle > 0.0f) { + if (m_groove.angle > 0.0f) { offset = xy_connection_len * Vec3d::UnitY() - 0.5 * size * Vec3d::Ones(); m_raycasters[id++]->set_transform(trafo * translation_transform(offset) * scale_transform(size)); offset = (size + xy_connection_len) * Vec3d::UnitY(); @@ -1996,7 +1996,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=fa bool can_set_center_pos = false; { - double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove_depth) : */0.5; + double limit_val = /*CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.5 * double(m_groove.depth) : */0.5; if (tbb.max.z() > -limit_val && tbb.min.z() < limit_val) can_set_center_pos = true; else { @@ -2093,10 +2093,10 @@ void GLGizmoCut3D::update_bb() m_snap_fine_out_radius = m_grabber_connection_len * 1.15; // input params for cut with tongue and groove - m_groove_depth = m_groove_depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box))); - m_groove_width = m_groove_width_init = 4.0f * m_groove_depth; - m_groove_flaps_angle = m_groove_flaps_angle_init = float(PI) / 3.f; - m_groove_angle = m_groove_angle_init = 0.f; + m_groove.depth = m_groove.depth_init = std::max(1.f , 0.5f * float(get_grabber_mean_size(m_bounding_box))); + m_groove.width = m_groove.width_init = 4.0f * m_groove.depth; + m_groove.flaps_angle = m_groove.flaps_angle_init = float(PI) / 3.f; + m_groove.angle = m_groove.angle_init = 0.f; m_plane.reset(); m_cone.reset(); m_sphere.reset(); @@ -2188,16 +2188,9 @@ void GLGizmoCut3D::render_clipper_cut() GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) { + Cut cut(mo, instance_idx_in, cut_matrix); m_model = Model(); - m_model.add_object(*mo); - - Model tmp_model = Model(); - tmp_model.objects = m_model.objects.front()->cut(instance_idx_in, cut_matrix, - ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts); - assert(tmp_model.objects.size() == 1); - m_model = tmp_model; + m_model.add_object(*cut.perform_with_plane().front()); m_instance_idx = instance_idx_in; @@ -2270,6 +2263,7 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor m_valid = true; } +// In CutMode::cutTongueAndGroove we use PartSelection just for rendering GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in) { m_instance_idx = instance_idx_in; @@ -2388,6 +2382,16 @@ bool GLGizmoCut3D::PartSelection::is_one_object() const }); } +std::vector GLGizmoCut3D::PartSelection::get_cut_parts() +{ + std::vector parts; + + for (const auto& part : m_parts) + parts.push_back({part.selected, part.is_modifier}); + + return parts; +} + void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) { @@ -2795,11 +2799,12 @@ void GLGizmoCut3D::process_contours() wxBusyCursor wait; if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - Model tmp_model = Model(); - tmp_model.objects = perform_cut_with_groove(model_objects[object_idx], true); - if (!tmp_model.objects.empty()) - m_part_selection = PartSelection(tmp_model.objects.front(), instance_idx); - tmp_model = Model(); + if (has_valid_groove()) { + Cut cut(model_objects[object_idx], instance_idx, get_cut_matrix(selection)); + const ModelObjectPtrs& new_objects = cut.perform_with_groove(m_groove, m_rotation_m, true); + if (!new_objects.empty()) + m_part_selection = PartSelection(new_objects.front(), instance_idx); + } } else { reset_cut_by_contours(); @@ -2989,10 +2994,10 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) else if (mode == CutMode::cutTongueAndGroove) { ImGui::Separator(); ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": "); - render_groove_float_input(m_labels_map["Depth"], m_groove_depth, m_groove_depth_init, m_groove_depth_tolerance); - render_groove_float_input(m_labels_map["Width"], m_groove_width, m_groove_width_init, m_groove_width_tolerance); - render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove_flaps_angle, m_groove_flaps_angle_init, 30.f, 120.f); - render_groove_angle_input(m_labels_map["Groove Angle"], m_groove_angle, m_groove_angle_init, 0.f, 15.f); + render_groove_float_input(m_labels_map["Depth"], m_groove.depth, m_groove.depth_init, m_groove.depth_tolerance); + render_groove_float_input(m_labels_map["Width"], m_groove.width, m_groove.width_init, m_groove.width_tolerance); + render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove.flaps_angle, m_groove.flaps_angle_init, 30.f, 120.f); + render_groove_angle_input(m_labels_map["Groove Angle"], m_groove.angle, m_groove.angle_init, 0.f, 15.f); // m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); } @@ -3445,8 +3450,8 @@ bool GLGizmoCut3D::has_valid_groove() const if (CutMode(m_mode) != CutMode::cutTongueAndGroove) return true; - const float flaps_width = -2.f * m_groove_depth / tan(m_groove_flaps_angle); - if (flaps_width > m_groove_width) + const float flaps_width = -2.f * m_groove.depth / tan(m_groove.flaps_angle); + if (flaps_width > m_groove.width) return false; const Selection& selection = m_parent.get_selection(); @@ -3553,280 +3558,6 @@ void synchronize_model_after_cut(Model& model, const CutObjectBase& cut_id) obj->cut_id.copy(cut_id); } -void distribute_modifiers_from_object(ModelObject *from_obj, const int instance_idx, ModelObject *to_obj1, ModelObject *to_obj2) -{ - auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3(); - auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3(); - const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix(); - - for (ModelVolume*vol : from_obj->volumes) - if (!vol->is_model_part()) { - auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix()); - // Don't add modifiers which are not intersecting with solid parts - if (obj1_bb.intersects(bb)) - to_obj1->add_volume(*vol); - if (obj2_bb.intersects(bb)) - to_obj2->add_volume(*vol); - } -} - -void merge_solid_parts_inside_object(ModelObjectPtrs& objects) -{ - for (ModelObject* mo : objects) { - TriangleMesh mesh; - // Merge all SolidPart but not Connectors - for (const ModelVolume* mv : mo->volumes) { - if (mv->is_model_part() && !mv->is_cut_connector()) { - TriangleMesh m = mv->mesh(); - m.transform(mv->get_matrix()); - mesh.merge(m); - } - } - if (!mesh.empty()) { - ModelVolume* new_volume = mo->add_volume(mesh); - new_volume->name = mo->name; - // Delete all merged SolidPart but not Connectors - for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) { - const ModelVolume* mv = mo->volumes[i]; - if (mv->is_model_part() && !mv->is_cut_connector()) - mo->delete_volume(i); - } - } - } -} - -ModelObjectPtrs GLGizmoCut3D::perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count) -{ - const Selection& selection = m_parent.get_selection(); - const int instance_idx = selection.get_instance_idx(); - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - if (m_keep_upper) cut_mo->clone_for_cut(&upper); - ModelObject* lower{ nullptr }; - if (m_keep_lower) cut_mo->clone_for_cut(&lower); - - const Transform3d cut_matrix = get_cut_matrix(selection); - - auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { - if (upper && !upper->volumes.empty()) { - ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); - cut_objects.push_back(upper); - } - if (lower && !lower->volumes.empty()) { - ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower); - cut_objects.push_back(lower); - } - }; - - const size_t cut_parts_cnt = m_part_selection.parts().size(); - bool has_modifiers = false; - - // Distribute SolidParts to the Upper/Lower object - for (size_t id = 0; id < cut_parts_cnt; ++id) { - if (m_part_selection.parts()[id].is_modifier) - has_modifiers = true; // modifiers will be added later to the related parts - else if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower)) - obj->add_volume(*(cut_mo->volumes[id])); - } - - if (has_modifiers) { - // Distribute Modifiers to the Upper/Lower object - distribute_modifiers_from_object(cut_mo, instance_idx, upper, lower); - } - - ModelObjectPtrs cut_object_ptrs; - - ModelVolumePtrs& volumes = cut_mo->volumes; - if (volumes.size() == cut_parts_cnt) { - // Means that object is cut without connectors - - // Just add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); - } - else if (volumes.size() > cut_parts_cnt) { - // Means that object is cut with connectors - - // All volumes are distributed to Upper / Lower object, - // So we don’t need them anymore - for (size_t id = 0; id < cut_parts_cnt; id++) - delete *(volumes.begin() + id); - volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); - - // Perform cut just to get connectors - const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); - assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); - - // Connectors from upper object - for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) - upper->add_volume(*volume, volume->type()); - - // Connectors from lower object - for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) - lower->add_volume(*volume, volume->type()); - - // Add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); - - // Add Dowel-connectors as separate objects to cut_object_ptrs - if (cut_connectors_obj.size() >= 3) - for (size_t id = 2; id < cut_connectors_obj.size(); id++) - cut_object_ptrs.push_back(cut_connectors_obj[id]); - } - - // Now merge all model parts together: - merge_solid_parts_inside_object(cut_object_ptrs); - - return cut_object_ptrs; -} - -ModelObjectPtrs GLGizmoCut3D::perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts/* = false*/) -{ - if (!has_valid_groove()) - return {}; - - const Selection& selection = m_parent.get_selection(); - const int instance_idx = selection.get_instance_idx(); - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper{ nullptr }; - cut_mo->clone_for_cut(&upper); - ModelObject* lower{ nullptr }; - cut_mo->clone_for_cut(&lower); - - const double groove_half_depth = 0.5 * double(m_groove_depth); - - const Transform3d cut_matrix = get_cut_matrix(selection); - - Model tmp_model_for_cut = Model(); - - Model tmp_model = Model(); - tmp_model.add_object(*cut_mo); - ModelObject* tmp_object = tmp_model.objects.front(); - - auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) { - const auto& volumes = tmp_model_for_cut.objects.front()->volumes; - for (const ModelVolume* volume : volumes) - if (volume->is_model_part()) { - if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) || - (attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper()) ) { - ModelVolume* new_vol = object->add_volume(*volume); - new_vol->reset_from_upper(); - } - } - }; - - auto cut = [instance_idx, add_volumes_from_cut] - (ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) { - Model model = Model(); - model.add_object(*object); - - tmp_model_for_cut = Model(); - tmp_model_for_cut.objects = model.objects.front()->cut(instance_idx, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - assert(!tmp_model_for_cut.objects.empty()); - - object->clear_volumes(); - add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut); - ModelObject::reset_instance_transformation(object, instance_idx); - }; - - // cut by upper plane - - const Transform3d cut_matrix_upper = translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cut_matrix; - { - cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // cut by lower plane - - const Transform3d cut_matrix_lower = translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cut_matrix; - { - cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - } - - // cut middle part with 2 angles and add parts to related upper/lower objects - - const double h_side_shift = 0.5 * double(m_groove_width + m_groove_depth / tan(m_groove_flaps_angle)); - - // cut by angle1 plane - { - const Transform3d cut_matrix_angle1 = translation_transform(m_rotation_m * (-h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - - cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // cut by angle2 plane - { - const Transform3d cut_matrix_angle2 = translation_transform(m_rotation_m * (h_side_shift * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - - cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // apply tolerance to the middle part - { - const double h_groove_shift_tolerance = groove_half_depth - (double)m_groove_depth_tolerance; - - const Transform3d cut_matrix_lower_tolerance = translation_transform(m_rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * cut_matrix; - cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - - const double h_side_shift_tolerance = h_side_shift - 0.5 * double(m_groove_width_tolerance); - - const Transform3d cut_matrix_angle1_tolerance = translation_transform(m_rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, -m_groove_flaps_angle, -m_groove_angle)); - cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - - const Transform3d cut_matrix_angle2_tolerance = translation_transform(m_rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * cut_matrix * rotation_transform(Vec3d(0, m_groove_flaps_angle, m_groove_angle)); - cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut); - } - - // this part can be added to the upper object now - add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut); - - ModelObjectPtrs cut_object_ptrs; - - if (keep_as_parts) { - // add volumes from lower object to the upper, but mark them as a lower - const auto& volumes = lower->volumes; - for (const ModelVolume* volume : volumes) { - ModelVolume* new_vol = upper->add_volume(*volume); - new_vol->cut_info.is_from_upper = false; - } - - // add modifiers - for (const ModelVolume* volume : cut_mo->volumes) - if (!volume->is_model_part()) - upper->add_volume(*volume); - - cut_object_ptrs.push_back(upper); - - // add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks - cut_object_ptrs.push_back(lower); - } - else { - // add modifiers if object has any - for (const ModelVolume* volume : cut_mo->volumes) - if (!volume->is_model_part()) { - distribute_modifiers_from_object(cut_mo, instance_idx, upper, lower); - break; - } - - assert(!upper->volumes.empty() && !lower->volumes.empty()); - - ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix_upper, m_place_on_cut_upper, m_rotate_upper); - cut_object_ptrs.push_back(upper); - ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix_lower, m_place_on_cut_upper, m_rotate_upper); - cut_object_ptrs.push_back(lower); - - // Now merge all model parts together: - merge_solid_parts_inside_object(cut_object_ptrs); - } - - return cut_object_ptrs; -} - void GLGizmoCut3D::perform_cut(const Selection& selection) { if (!can_perform_cut()) @@ -3867,8 +3598,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) wxBusyCursor wait; - const Transform3d cut_matrix = get_cut_matrix(selection); - ModelObjectCutAttributes attributes = 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) | @@ -3882,16 +3611,15 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // update cut_id for the cut object in respect to the attributes update_object_cut_id(cut_mo->cut_id, attributes, dowels_count); - Model tmp_model = Model(); - tmp_model.objects = cut_by_contour ? perform_cut_by_contour(cut_mo, attributes, dowels_count) : - cut_with_groove ? perform_cut_with_groove(cut_mo) : - cut_mo->cut(instance_idx, cut_matrix, attributes); - + Cut cut(cut_mo, instance_idx, get_cut_matrix(selection), attributes); + const ModelObjectPtrs& new_objects = cut_by_contour ? cut.perform_by_contour(m_part_selection.get_cut_parts(), dowels_count): + cut_with_groove ? cut.perform_with_groove(m_groove, m_rotation_m) : + cut.perform_with_plane(); // save cut_id to post update synchronization const CutObjectBase cut_id = cut_mo->cut_id; // update cut results on plater and in the model - plater->cut(object_idx, tmp_model.objects); + plater->apply_cut_object_to_model(object_idx, new_objects); synchronize_model_after_cut(plater->model(), cut_id); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 11ac1ee7a8..34ba6e9ab1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -7,6 +7,7 @@ #include "slic3r/GUI/I18N.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/CutUtils.hpp" #include "imgui/imgui.h" namespace Slic3r { @@ -118,16 +119,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_rotate_lower{ false }; // Input params for cut with tongue and groove - float m_groove_depth; - float m_groove_width; - float m_groove_flaps_angle; - float m_groove_angle; - float m_groove_depth_init; - float m_groove_width_init; - float m_groove_flaps_angle_init; - float m_groove_angle_init; - float m_groove_depth_tolerance{ 0.1f }; - float m_groove_width_tolerance{ 0.1f }; + Cut::Groove m_groove; bool m_optimaze_groove_rendering{ true }; // Input params for cut with snaps @@ -187,6 +179,8 @@ class GLGizmoCut3D : public GLGizmoBase const std::vector& parts() const { return m_parts; } const std::vector* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); } + std::vector get_cut_parts(); + private: Model m_model; int m_instance_idx; @@ -367,8 +361,6 @@ private: void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0); void render_cut_plane_grabbers(); void render_cut_line(); - ModelObjectPtrs perform_cut_by_contour(ModelObject* cut_mo, const ModelObjectCutAttributes& attributes, int dowels_count); - ModelObjectPtrs perform_cut_with_groove(ModelObject* cut_mo, bool keep_as_parts = false); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool update_tbb = false); void update_bb(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fcd97cdc15..fb8cfb99e7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6318,20 +6318,7 @@ void Plater::toggle_layers_editing(bool enable) canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting")); } -void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) -{ - wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); - auto* object = p->model.objects[obj_idx]; - - wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - - wxBusyCursor wait; - - const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); - cut(obj_idx, new_objects); -} - -void Plater::cut(size_t obj_idx, const ModelObjectPtrs& new_objects) +void Plater::apply_cut_object_to_model(size_t obj_idx, const ModelObjectPtrs& new_objects) { model().delete_object(obj_idx); sidebar().obj_list()->delete_object_from_list(obj_idx); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 8eeabae428..bed65d7d13 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -264,8 +264,7 @@ public: void convert_unit(ConversionType conv_type); void toggle_layers_editing(bool enable); - void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes); - void cut(size_t init_obj_idx, const ModelObjectPtrs& cut_objects); + void apply_cut_object_to_model(size_t init_obj_idx, const ModelObjectPtrs& cut_objects); void export_gcode(bool prefer_removable); void export_stl_obj(bool extended = false, bool selection_only = false); From 91fae9a154a9bf37cde44f95dff0bc818a19f061 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 9 Aug 2023 13:48:38 +0200 Subject: [PATCH 40/51] CutGizmo: Code cleaning --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 529 +++++---------------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 10 - 2 files changed, 92 insertions(+), 447 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3dd4fe6edd..885b8582ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -504,8 +504,8 @@ void GLGizmoCut3D::switch_to_mode(size_t new_mode) m_contour_width = CutMode(m_mode) == CutMode::cutTongueAndGroove ? 0.f : 0.4f; oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); } - if (m_use_TAG_mesh) - update_plane_model(); + + update_plane_model(); reset_cut_by_contours(); } @@ -673,227 +673,6 @@ static double get_grabber_mean_size(const BoundingBoxf3& bb) return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.; } -indexed_triangle_set GLGizmoCut3D::its_make_upper_groove_plane() -{ - const float ghw = 0.5f * m_groove.width; // groove half width - const float cpr = 1.5f * float(m_radius); // cut plane radius - - const float cpl = 1.5f * cpr; // cut plane length - const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height - - // We need two upward facing triangles - float x = 0.5f * cpr, y = 0.5f * cpl; - - const float proj = y * tan(m_groove.angle); - const float extension_x = ghw + proj; - - // upper cut plane is simple - - if (is_approx(ghw, proj)) { - - return { - { - // roof - {0,1,6}, {6,1,2}, {2,3,5}, {5,3,4}, - // sides - {0,7,8}, {0,8,1}, {1,8,9}, {1,9,2}, {2,9,10}, {2,10,3}, {3,10,11}, {3,11,4}, - {4,11,12}, {4,12,5}, {5, 12,13}, {5,13,6}, {6,13,7}, {6, 7,0}, - // bottom - {7,13,8}, {8,13,9}, {10,9,12}, {10,12,4} - }, - { - // roof vertices - {-x, -y, cph}, {-extension_x, -y, cph}, {0.f, y, cph}, {extension_x, -y, cph}, {x, -y, cph}, {x, y, cph}, {-x, y, cph}, - // bottom vertices - {-x, -y, 0.f}, {-extension_x, -y, 0.f}, {0.f, y, 0.f}, {extension_x, -y, 0.f}, {x, -y, 0.f}, {x, y, 0.f}, {-x, y, 0.f}, - } - }; - } - - if (ghw < proj) { - - const float cross_pt_y = ghw / tan(m_groove.angle); - return { - { - // roof - {0,1,6}, {1,2,6}, {2,5,6}, {2,3,5}, {3,4,5}, - // sides - {0,7,8}, {0,8,1}, {1,8,9}, {1,9,2}, {2,9,10}, {2,10,3}, {3,10,11}, {3,11,4}, - {4,11,12}, {4,12,5}, {5,12,13}, {5,13,6}, {6,13,7}, {6,7,0}, - // bottom - {7,13,8}, {8,13,9}, {9,13,12}, {9,12,10}, {10,12, 11} - }, - { - // roof vertices - {-x, -y, cph}, {-extension_x, -y, cph}, {0.f, cross_pt_y, cph}, {extension_x, -y, cph}, {x, -y, cph}, {x, y, cph}, {-x, y, cph}, - // bottom vertices - {-x, -y, 0.f}, {-extension_x, -y, 0.f}, {0.f, cross_pt_y, 0.f}, {extension_x, -y, 0.f}, {x, -y, 0.f}, {x, y, 0.f}, {-x, y, 0.f} - } - }; - } - - // upper cut plane contains 2 sub planes - - const float narrowing_x = ghw - proj; - return { - { - // roof - {0,1,3}, {3,1,2}, {7,4,6}, {6,4,5}, - // sides - {0,8,9}, {0,9,1}, {1,9,10}, {1,10,2}, {2,10,11}, {2,11,3}, {3,11,8}, {3,8,0}, - {4,12,13}, {4,13,5}, {5,13,14}, {5,14,6}, {6,14,15}, {6,15,7}, {7,15,12}, {7,12,4}, - // bottom - {9,8,11}, {9,11,10}, {12,15,14}, {12,14,13} - }, - { - // roof vertices - {-x, -y, cph}, {-extension_x, -y, cph}, {-narrowing_x, y, cph}, {-x, y, cph}, {extension_x, -y, cph}, {x, -y, cph}, {x, y, cph}, {narrowing_x, y, cph}, - // bottom vertices - {-x, -y, 0.f}, {-extension_x, -y, 0.f}, {-narrowing_x, y, 0.f}, {-x, y, 0.f}, {extension_x, -y, 0.f}, {x, -y, 0.f}, {x, y, 0.f}, {narrowing_x, y, 0.f}, - } - }; -} - -indexed_triangle_set GLGizmoCut3D::its_make_lower_groove_plane(float flaps_width) -{ - const float ghw = 0.5f * (m_groove.width + flaps_width); // groove half width - const float cpr = 1.5f * float(m_radius); // cut plane radius - - const float cpl = 1.5f * cpr; // cut plane length - const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height - - // We need two upward facing triangles - float x = 0.5f * cpr, y = 0.5f * cpl; - - const float proj = y * tan(m_groove.angle); - const float extension_x = ghw + proj; - - // upper cut plane is trapezium - - if (ghw > proj) { - - const float narrowing_x = ghw - proj; - return { - { - // roof - {0,3,1}, {1,3,2}, - // sides - {0,4,7}, {0,7,3}, {3,7,6}, {3,6,2}, {2,6,5}, {2,5,1}, {1,5,4}, {1,4,0}, - // bottom - {4,5,7}, {7,5,6} - }, - { - // roof vertices - {-extension_x, -y, cph}, {-narrowing_x, y, cph}, {narrowing_x, y, cph}, {extension_x, -y, cph}, - // bottom vertices - {-extension_x, -y, 0.f}, {-narrowing_x, y, 0.f}, {narrowing_x, y, 0.f}, {extension_x, -y, 0.f} - } - }; - } - - // upper cut plane is triangle - - const float cross_pt_y = ghw / tan(m_groove.angle); - return { - { - // roof - {0,2,1}, - // sides - {0,3,5}, {0,5,2}, {2,5,4}, {2,4,1}, {1,4,3}, {0,1,3}, - // bottom - {3,4,5} - }, - { - // roof vertices - {-extension_x, -y, cph}, {0.f, cross_pt_y, cph}, {extension_x, -y, cph}, - // bottom vertices - {-extension_x, -y, 0.f}, {0.f, cross_pt_y, 0.f}, {extension_x, -y, 0.f} - } - }; -} - -indexed_triangle_set GLGizmoCut3D::its_make_sides_groove_plane(float flaps_width) -{ - const float ghw_upper = 0.5f * (m_groove.width); // groove half width - const float ghw_lower = 0.5f * (m_groove.width + flaps_width); // groove half width - const float cpr = 1.5f * float(m_radius); // cut plane radius - - const float cpl = 1.5f * cpr; // cut plane length - const float cph = 0.02f * (float)get_grabber_mean_size(m_bounding_box); // cut plane height - - const float ghd = 0.5f * m_groove.depth; // groove half depth - - - // We need two upward facing triangles - float x = 0.5f * cpr, y = 0.5f * cpl; - float z_upper = ghd; - float z_lower = -ghd; - - const float proj = y * tan(m_groove.angle); - - const float extension_upper_x = ghw_upper + proj; - const float extension_lower_x = ghw_lower + proj; - - const float narrowing_upper_x = ghw_upper - proj; - const float narrowing_lower_x = ghw_lower - proj; - - // groove is open - - if (ghw_upper > proj && ghw_lower > proj) { - return { - { - {1,0,2}, {2,0,3}, {5,4,7}, {5,7,6}, - {0,1,2}, {3,0,2}, {4,5,7}, {7,5,6} - }, - { - // left vertices - {-extension_lower_x, -y, z_lower}, {-extension_upper_x, -y, z_upper}, {-narrowing_upper_x, y, z_upper}, {-narrowing_lower_x, y, z_lower}, - // right vertices - {narrowing_lower_x, y, z_lower}, {narrowing_upper_x, y, z_upper}, {extension_upper_x, -y, z_upper}, {extension_lower_x, -y, z_lower} - } - }; - } - - const float cross_pt_upper_y = ghw_upper / tan(m_groove.angle); - - // groove is closed - - if (ghw_upper < proj && ghw_lower < proj) { - const float cross_pt_lower_y = ghw_lower / tan(m_groove.angle); - - return { - { - {1,0,3}, {1,3,4}, {1,4,5}, {1,5,2}, - {0,1,3}, {3,1,4}, {4,1,5}, {5,1,2} - }, - { - // roof vertices - {-extension_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {extension_upper_x, -y, z_upper}, - // bottom vertices - {-extension_lower_x, -y, z_lower}, {0.f, cross_pt_lower_y, z_lower}, {extension_lower_x, -y, z_lower} - } - }; - - } - - // groove is closed from the roof - - return { - { - {1,0,3}, {1,3,4}, {1,5,6}, {1,6,2}, - {0,1,3}, {3,1,4}, {5,1,6}, {6,1,2} - }, - { - // roof vertices - {-extension_upper_x, -y, z_upper}, {0.f, cross_pt_upper_y, z_upper}, {extension_upper_x, -y, z_upper}, - // bottom vertices - {-extension_lower_x, -y, z_lower}, {-narrowing_lower_x, y, z_lower}, {narrowing_lower_x, y, z_lower}, {extension_lower_x, -y, z_lower} - } - }; -} - - - indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() { // values for calculation @@ -965,57 +744,39 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_upper -= cut_plane_thiknes; z_lower -= cut_plane_thiknes; - if (m_use_TAG_mesh_full) { - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); - nar_upper_x += under_x_shift; - nar_lower_x += under_x_shift; - ext_upper_x += under_x_shift; - ext_lower_x += under_x_shift; + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); - std::vector vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); - mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + nar_upper_x += under_x_shift; + nar_lower_x += under_x_shift; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; - mesh.indices = { - // above view - {5,4,7}, {5,7,6}, // lower part - {3,4,5}, {3,5,2}, // left side - {9,6,8}, {8,6,7}, // right side - {1,0,2}, {2,0,3}, // upper left part - {9,8,10}, {10,8,11}, // upper right part - // under view - {20,21,22}, {20,22,23}, // upper right part - {12,13,14}, {12,14,15}, // upper left part - {18,21,20}, {18,20,19}, // right side - {16,15,14}, {16,14,17}, // left side - {16,17,18}, {16,18,19}, // lower part - // left edge - {1,13,0}, {0,13,12}, - // front edge - {0,12,3}, {3,12,15}, {3,15,4}, {4,15,16}, {4,16,7}, {7,16,19}, {7,19,20}, {7,20,8}, {8,20,11}, {11,20,23}, - // right edge - {11,23,10}, {10,23,22}, - // back edge - {1,13,2}, {2,13,14}, {2,14,17}, {2,17,5}, {5,17,6}, {6,17,18}, {6,18,9}, {9,18,21}, {9,21,10}, {10,21,22} - }; - } - else { - std::vector vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); - mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + std::vector vertices = get_vertices(z_upper, z_lower, nar_upper_x, nar_lower_x, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); - mesh.indices = { - // above view - {5,4,7}, {5,7,6}, // lower part - {3,4,2}, {2,4,5}, // left side - {9,6,8}, {8,6,7}, // right side - {1,0,2}, {2,0,3}, // upper left part - {9,8,10}, {10,8,11}, // upper right part - // under view - {16,15,14}, {16,14,17}, // left side - {18,21,20}, {18,20,19}, // right side - {16,17,18}, {16,18,19} // lower part - }; - } + mesh.indices = { + // above view + {5,4,7}, {5,7,6}, // lower part + {3,4,5}, {3,5,2}, // left side + {9,6,8}, {8,6,7}, // right side + {1,0,2}, {2,0,3}, // upper left part + {9,8,10}, {10,8,11}, // upper right part + // under view + {20,21,22}, {20,22,23}, // upper right part + {12,13,14}, {12,14,15}, // upper left part + {18,21,20}, {18,20,19}, // right side + {16,15,14}, {16,14,17}, // left side + {16,17,18}, {16,18,19}, // lower part + // left edge + {1,13,0}, {0,13,12}, + // front edge + {0,12,3}, {3,12,15}, {3,15,4}, {4,15,16}, {4,16,7}, {7,16,19}, {7,19,20}, {7,20,8}, {8,20,11}, {11,20,23}, + // right edge + {11,23,10}, {10,23,22}, + // back edge + {1,13,2}, {2,13,14}, {2,14,17}, {2,17,5}, {5,17,6}, {6,17,18}, {6,18,9}, {9,18,21}, {9,21,10}, {10,21,22} + }; return mesh; } @@ -1044,56 +805,38 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() z_upper -= cut_plane_thiknes; z_lower -= cut_plane_thiknes; - if (m_use_TAG_mesh_full) { - const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); + const float under_x_shift = cut_plane_thiknes / tan(0.5f * m_groove.flaps_angle); - cross_pt_upper_y += cut_plane_thiknes; - cross_pt_lower_y += cut_plane_thiknes; - ext_upper_x += under_x_shift; - ext_lower_x += under_x_shift; + cross_pt_upper_y += cut_plane_thiknes; + cross_pt_lower_y += cut_plane_thiknes; + ext_upper_x += under_x_shift; + ext_lower_x += under_x_shift; - std::vector vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); - mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + std::vector vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); + mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); - mesh.indices = { - // above view - {8,7,9}, // lower part - {5,8,6}, {6,8,7}, // left side - {4,9,8}, {4,8,5}, // right side - {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part - // under view - {10,11,16}, {16,11,15}, {15,11,12}, {15,12,14}, {14,12,13}, // upper part - {18,15,14}, {14,18,19}, // right side - {17,16,15}, {17,15,18}, // left side - {17,18,19}, // lower part - // left edge - {1,11,0}, {0,11,10}, - // front edge - {0,10,6}, {6,10,16}, {6,17,16}, {6,7,17}, {7,17,19}, {7,19,9}, {4,14,19}, {4,19,9}, {4,14,13}, {4,13,3}, - // right edge - {3,13,12}, {3,12,2}, - // back edge - {2,12,11}, {2,11,1} - }; - } - else { - std::vector vertices = get_vertices(z_upper, z_lower, cross_pt_upper_y, cross_pt_lower_y, ext_upper_x, ext_lower_x); - mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); + mesh.indices = { + // above view + {8,7,9}, // lower part + {5,8,6}, {6,8,7}, // left side + {4,9,8}, {4,8,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {10,11,16}, {16,11,15}, {15,11,12}, {15,12,14}, {14,12,13}, // upper part + {18,15,14}, {14,18,19}, // right side + {17,16,15}, {17,15,18}, // left side + {17,18,19}, // lower part + // left edge + {1,11,0}, {0,11,10}, + // front edge + {0,10,6}, {6,10,16}, {6,17,16}, {6,7,17}, {7,17,19}, {7,19,9}, {4,14,19}, {4,19,9}, {4,14,13}, {4,13,3}, + // right edge + {3,13,12}, {3,12,2}, + // back edge + {2,12,11}, {2,11,1} + }; - mesh.indices = { - // above view - {8,7,9}, // lower part - {5,8,6}, {6,8,7}, // left side - {4,9,8}, {4,8,5}, // right side - {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part - // under view - {18,15,14}, {14,18,19}, // right side - {17,16,15}, {17,15,18}, // left side - {17,18,19}, // lower part - }; - } return mesh; - } // groove is closed from the roof @@ -1127,89 +870,30 @@ indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() }; mesh.vertices.insert(mesh.vertices.end(), vertices.begin(), vertices.end()); - if (m_use_TAG_mesh_full) { - mesh.indices = { - // above view - {8,7,10}, {8,10,9}, // lower part - {5,8,7}, {5,7,6}, // left side - {4,10,9}, {4,9,5}, // right side - {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part - // under view - {11,12,18}, {18,12,17}, {17,12,16}, {16,12,13}, {16,13,15}, {15,13,14}, // upper part - {21,16,15}, {21,15,22}, // right side - {19,18,17}, {19,17,20}, // left side - {19,20,21}, {19,21,22}, // lower part - // left edge - {1,12,11}, {1,11,0}, - // front edge - {0,11,18}, {0,18,6}, {7,19,18}, {7,18,6}, {7,19,22}, {7,22,10}, {10,22,15}, {10,15,4}, {4,15,14}, {4,14,3}, - // right edge - {3,14,13}, {3,14,2}, - // back edge - {2,13,12}, {2,12,1}, {5,16,21}, {5,21,9}, {9,21,20}, {9,20,8}, {5,17,20}, {5,20,8} - }; - } - else { - mesh.indices = { - // above view - {8,7,10}, {8,10,9}, // lower part - {5,8,7}, {5,7,6}, // left side - {4,10,9}, {4,9,5}, // right side - {1,0,6}, {1,6,5}, {1,5,2}, {2,5,4}, {2,4,3}, // upper part - // under view - {21,16,15}, {21,15,22}, // right side - {19,18,17}, {19,17,20}, // left side - {19,20,21}, {19,21,22}, // lower part - }; - } + mesh.indices = { + // above view + {8,7,10}, {8,10,9}, // lower part + {5,8,7}, {5,7,6}, // left side + {4,10,9}, {4,9,5}, // right side + {1,0,6}, {1,6,5},{1,5,2}, {2,5,4}, {2,4,3}, // upper part + // under view + {11,12,18}, {18,12,17}, {17,12,16}, {16,12,13}, {16,13,15}, {15,13,14}, // upper part + {21,16,15}, {21,15,22}, // right side + {19,18,17}, {19,17,20}, // left side + {19,20,21}, {19,21,22}, // lower part + // left edge + {1,12,11}, {1,11,0}, + // front edge + {0,11,18}, {0,18,6}, {7,19,18}, {7,18,6}, {7,19,22}, {7,22,10}, {10,22,15}, {10,15,4}, {4,15,14}, {4,14,3}, + // right edge + {3,14,13}, {3,14,2}, + // back edge + {2,13,12}, {2,12,1}, {5,16,21}, {5,21,9}, {9,21,20}, {9,20,8}, {5,17,20}, {5,20,8} + }; return mesh; } -void GLGizmoCut3D::render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader) -{ - const Camera & camera = wxGetApp().plater()->get_camera(); - const Transform3d cp_matrix = translation_transform(m_plane_center) * m_rotation_m; - ColorRGBA cp_clr = m_plane.model.get_color(); - - // values for calculaton - const double groove_half_depth = 0.5 * double(m_groove.depth); - - const float side_width = is_approx(m_groove.flaps_angle, 0.f) ? m_groove.depth : (m_groove.depth / sin(m_groove.flaps_angle)); - const float flaps_width = 2.f * side_width * cos(m_groove.flaps_angle); - - GLModel model; - - // upper cut_plane - - model.init_from(its_make_upper_groove_plane()); - model.set_color(cp_clr); - - Transform3d view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (groove_half_depth * Vec3d::UnitZ())) * cp_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix_); - model.render(); - - // lower part of cut_plane - - model.reset(); - model.init_from(its_make_lower_groove_plane(flaps_width)); - cp_clr.a(cp_clr.a() + 0.1f); - model.set_color(cp_clr); - - view_model_matrix_ = camera.get_view_matrix() * translation_transform(m_rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * cp_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix_); - model.render(); - - // side parts of cut_plane - - model.reset(); - model.init_from(its_make_sides_groove_plane(flaps_width)); - - view_model_matrix_ = camera.get_view_matrix() * cp_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix_); - model.render(); -} - void GLGizmoCut3D::render_cut_plane() { if (cut_line_processing()) @@ -1230,27 +914,15 @@ void GLGizmoCut3D::render_cut_plane() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (m_use_TAG_mesh) { - ColorRGBA cp_clr = can_perform_cut() && has_valid_groove() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR; - if (m_mode == size_t(CutMode::cutTongueAndGroove)) - cp_clr.a(cp_clr.a() - 0.1f); - m_plane.model.set_color(cp_clr); + ColorRGBA cp_clr = can_perform_cut() && has_valid_groove() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR; + if (m_mode == size_t(CutMode::cutTongueAndGroove)) + cp_clr.a(cp_clr.a() - 0.1f); + m_plane.model.set_color(cp_clr); - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - shader->set_uniform("view_model_matrix", view_model_matrix); - m_plane.model.render(); - } - else { - m_plane.model.set_color(can_perform_cut() && has_valid_contour() ? CUT_PLANE_DEF_COLOR : CUT_PLANE_ERR_COLOR); + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + shader->set_uniform("view_model_matrix", view_model_matrix); + m_plane.model.render(); - if (m_mode == size_t(CutMode::cutPlanar)) { - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - shader->set_uniform("view_model_matrix", view_model_matrix); - m_plane.model.render(); - } - else if (m_mode == size_t(CutMode::cutTongueAndGroove)) - render_cut_plate_for_tongue_and_groove(shader); - } glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -1981,7 +1653,7 @@ void GLGizmoCut3D::on_stop_dragging() m_ar_plane_center = m_plane_center; } - if (CutMode(m_mode) == CutMode::cutTongueAndGroove && m_optimaze_groove_rendering) + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) reset_cut_by_contours(); //check_and_update_connectors_state(); } @@ -2136,12 +1808,8 @@ void GLGizmoCut3D::init_picking_models() if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box); - indexed_triangle_set its; - if (m_use_TAG_mesh) - its = m_mode == size_t(CutMode::cutTongueAndGroove) ? its_make_groove_plane() : - its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); - else - its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); + indexed_triangle_set its = m_mode == size_t(CutMode::cutTongueAndGroove) ? its_make_groove_plane() : + its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); m_plane.model.init_from(its); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); @@ -2476,20 +2144,10 @@ void GLGizmoCut3D::on_render() void GLGizmoCut3D::render_debug_input_window(float x) { + return; m_imgui->begin(wxString("DEBUG")); - ImVec2 pos = ImGui::GetWindowPos(); - pos.x = x; - ImGui::SetWindowPos(pos, ImGuiCond_Always); - - bool is_changed = m_imgui->checkbox(("Render Cut plane as a one mesh"), m_use_TAG_mesh); - is_changed |= m_imgui->checkbox(("Render Cut plane as a full mesh"), m_use_TAG_mesh_full); - - if (is_changed) - update_plane_model(); - m_imgui->end(); - return; /* static bool hide_clipped = false; static bool fill_cut = false; @@ -2778,7 +2436,7 @@ void GLGizmoCut3D::reset_cut_by_contours() m_part_selection = PartSelection(); if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - if (m_optimaze_groove_rendering && m_dragging || !has_valid_groove()) + if (m_dragging || !has_valid_groove()) return; process_contours(); } @@ -2885,8 +2543,7 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in m_imgui->disabled_end(); if (is_changed) { - if (m_use_TAG_mesh) - update_plane_model(); + update_plane_model(); reset_cut_by_contours(); } } @@ -2927,8 +2584,7 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->disabled_end(); if (is_changed) { - if (m_use_TAG_mesh) - update_plane_model(); + update_plane_model(); reset_cut_by_contours(); } } @@ -2998,7 +2654,6 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) render_groove_float_input(m_labels_map["Width"], m_groove.width, m_groove.width_init, m_groove.width_tolerance); render_groove_angle_input(m_labels_map["Flaps Angle"], m_groove.flaps_angle, m_groove.flaps_angle_init, 30.f, 120.f); render_groove_angle_input(m_labels_map["Groove Angle"], m_groove.angle, m_groove.angle_init, 0.f, 15.f); -// m_imgui->checkbox(_L("Optimize rendering"), m_optimaze_groove_rendering); } ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 34ba6e9ab1..060c9f1d0b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -120,7 +120,6 @@ class GLGizmoCut3D : public GLGizmoBase // Input params for cut with tongue and groove Cut::Groove m_groove; - bool m_optimaze_groove_rendering{ true }; // Input params for cut with snaps float m_snap_bulge_proportion{ 0.15f }; @@ -232,11 +231,6 @@ class GLGizmoCut3D : public GLGizmoBase std::map m_labels_map; - // Debug values - bool m_use_TAG_mesh {true}; - bool m_use_TAG_mesh_full {true}; - // - public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -353,7 +347,6 @@ private: void discard_cut_line_processing(); void apply_color_clip_plane_colors(); - void render_cut_plate_for_tongue_and_groove(GLShaderProgram* shader); void render_cut_plane(); static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); @@ -377,9 +370,6 @@ private: void toggle_model_objects_visibility(); - indexed_triangle_set its_make_upper_groove_plane(); - indexed_triangle_set its_make_lower_groove_plane(float flaps_width); - indexed_triangle_set its_make_sides_groove_plane(float flaps_width); indexed_triangle_set its_make_groove_plane(); indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); From 6b6f7bc309c80a4c30da6ec1c4d29c5c43919f49 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 9 Aug 2023 15:16:35 +0200 Subject: [PATCH 41/51] CutGizmo: Improvements for TaG mode. Don't recalculate cut during editing a groove parameters. --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 18 +++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 885b8582ad..a0864094f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -574,6 +574,8 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v m_imgui->slider_float(label.c_str(), &value, min_val, max_val, format.c_str(), 1.f, true, tooltip); val = value * (m_imperial_units ? static_cast(ObjectManipulation::in_to_mm) : 1.f); + m_is_slider_editing_done |= m_imgui->get_last_slider_status().deactivated_after_edit; + return !is_approx(old_val, value); }; @@ -2436,7 +2438,7 @@ void GLGizmoCut3D::reset_cut_by_contours() m_part_selection = PartSelection(); if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { - if (m_dragging || !has_valid_groove()) + if (m_dragging || m_groove_editing || !has_valid_groove()) return; process_contours(); } @@ -2524,6 +2526,7 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in if (m_imgui->get_last_slider_status().can_take_snapshot) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); m_imgui->get_last_slider_status().invalidate_snapshot(); + m_groove_editing = true; } in_val = val; in_tolerance = tolerance; @@ -2546,6 +2549,11 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in update_plane_model(); reset_cut_by_contours(); } + + if (m_is_slider_editing_done) { + m_groove_editing = false; + reset_cut_by_contours(); + } } void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) @@ -2563,10 +2571,12 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in const std::string format = "%.0f " + _u8L("°"); m_imgui->slider_float(("##groove_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); + m_is_slider_editing_done |= m_imgui->get_last_slider_status().deactivated_after_edit; if (!is_approx(old_val, val)) { if (m_imgui->get_last_slider_status().can_take_snapshot) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); m_imgui->get_last_slider_status().invalidate_snapshot(); + m_groove_editing = true; } in_val = deg2rad(val); is_changed = true; @@ -2587,6 +2597,11 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in update_plane_model(); reset_cut_by_contours(); } + + if (m_is_slider_editing_done) { + m_groove_editing = false; + reset_cut_by_contours(); + } } @@ -2648,6 +2663,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) m_imgui->disabled_end(); } else if (mode == CutMode::cutTongueAndGroove) { + m_is_slider_editing_done = false; ImGui::Separator(); ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Groove"] + ": "); render_groove_float_input(m_labels_map["Depth"], m_groove.depth, m_groove.depth_init, m_groove.depth_tolerance); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 060c9f1d0b..4409c1dbbd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -120,6 +120,9 @@ class GLGizmoCut3D : public GLGizmoBase // Input params for cut with tongue and groove Cut::Groove m_groove; + bool m_groove_editing { false }; + + bool m_is_slider_editing_done { false }; // Input params for cut with snaps float m_snap_bulge_proportion{ 0.15f }; From 5852c60b80a657820a3d9418a383050f760336e0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 9 Aug 2023 17:26:27 +0200 Subject: [PATCH 42/51] CutGizmo: Fixed a rendering of the sliced object in SLA mode --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 26 +++++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 ++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index a0864094f4..49917d09f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1855,14 +1855,25 @@ void GLGizmoCut3D::render_clipper_cut() ::glEnable(GL_DEPTH_TEST); } +void GLGizmoCut3D::PartSelection::add_object(const ModelObject* object) +{ + m_model = Model(); + m_model.add_object(*object); + + const double sla_shift_z = wxGetApp().plater()->canvas3D()->get_selection().get_first_volume()->get_sla_shift_z(); + if (!is_approx(sla_shift_z, 0.)) { + Vec3d inst_offset = model_object()->instances[m_instance_idx]->get_offset(); + inst_offset[Z] += sla_shift_z; + model_object()->instances[m_instance_idx]->set_offset(inst_offset); + } +} + GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) + : m_instance_idx(instance_idx_in) { Cut cut(mo, instance_idx_in, cut_matrix); - m_model = Model(); - m_model.add_object(*cut.perform_with_plane().front()); - - m_instance_idx = instance_idx_in; + add_object(cut.perform_with_plane().front()); const ModelVolumePtrs& volumes = model_object()->volumes; @@ -1935,12 +1946,9 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor // In CutMode::cutTongueAndGroove we use PartSelection just for rendering GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* object, int instance_idx_in) + : m_instance_idx (instance_idx_in) { - m_instance_idx = instance_idx_in; - - m_model = Model(); - // add upper object - m_model.add_object(*object); + add_object(object); m_parts.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 4409c1dbbd..aa8ed04dd9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -193,6 +193,8 @@ class GLGizmoCut3D : public GLGizmoBase std::vector m_contour_points; // Debugging std::vector> m_debug_pts; // Debugging + + void add_object(const ModelObject* object); }; PartSelection m_part_selection; From 88dbb2db6c6a48a449bb13b5425431f28eee8e0c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 10:43:45 +0200 Subject: [PATCH 43/51] Fix for SPE-1835 : Scaling one part of an object after cutting with connectors, when open Scale gizmo with "S"-shortcut. --- src/slic3r/GUI/GLCanvas3D.cpp | 5 ----- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 6 ++++++ src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 2 ++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ad42dd2738..4ae60fb1c0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3592,11 +3592,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) update_sequential_clearance(true); } } - else if (evt.LeftUp() && - m_gizmos.get_current_type() == GLGizmosManager::EType::Scale && - m_gizmos.get_current()->get_state() == GLGizmoBase::EState::On) { - wxGetApp().obj_list()->selection_changed(); - } return; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 1a7251ddf7..6cc852c6a6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -99,6 +99,12 @@ void GLGizmoScale3D::enable_ununiversal_scale(bool enable) m_grabbers[i].enabled = enable; } +void GLGizmoScale3D::on_set_state() +{ + if (m_state == On) + wxGetApp().obj_list()->selection_changed(); +} + void GLGizmoScale3D::data_changed(bool is_serializing) { set_scale(Vec3d::Ones()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 6a7b4a331e..6e304061ab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -74,6 +74,8 @@ protected: virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; + void on_set_state() override; + private: void render_grabbers_connection(unsigned int id_1, unsigned int id_2, const ColorRGBA& color); From 6215393ae5094df1ce46b2ada5119d12bfbe7c12 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 11:02:26 +0200 Subject: [PATCH 44/51] Fix for SPE-1836 : CUT_rotate circle isn't updated after undo --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 885f9d97a2..36576e9494 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -835,6 +835,8 @@ 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_connectors_editing, m_ar_plane_center, m_rotation_m); + m_start_dragging_m = m_rotation_m; + m_transformed_bounding_box = transformed_bounding_box(m_ar_plane_center, m_rotation_m); set_center_pos(m_ar_plane_center); From 6cd6ee2f189ff36e0e2a524e369688fbe75e41c6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 12:06:42 +0200 Subject: [PATCH 45/51] Cut: TaG mode: Fixed post-processing for the cut objects --- src/libslic3r/CutUtils.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index 7d107b1d05..c5e186823f 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -608,10 +608,21 @@ const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Tran assert(!upper->volumes.empty() && !lower->volumes.empty()); - reset_instance_transformation(upper, m_instance, cut_matrix_upper, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); - cut_object_ptrs.push_back(upper); - reset_instance_transformation(lower, m_instance, cut_matrix_lower, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); - cut_object_ptrs.push_back(lower); + // check which parts have to stay/be deleted + + if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) { + reset_instance_transformation(upper, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + cut_object_ptrs.push_back(upper); + } + else + m_model.objects.push_back(upper); // will be deleted in m_model.clear_objects(); + + if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) { + reset_instance_transformation(lower, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); + cut_object_ptrs.push_back(lower); + } + else + m_model.objects.push_back(lower); // will be deleted in m_model.clear_objects(); // Now merge all model parts together: merge_solid_parts_inside_object(cut_object_ptrs); From 1d69dd82f8c590fa307dcd3f8b77e0d3f8611815 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 13:02:19 +0200 Subject: [PATCH 46/51] CutGizmo: Code refactoring to extract post process procedures over the Upper/Lower objects into separate function --- src/libslic3r/CutUtils.cpp | 96 +++++++++++++++++++------------------- src/libslic3r/CutUtils.hpp | 4 ++ 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index c5e186823f..ba2c3452af 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -243,6 +243,42 @@ Cut::Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, m_model.add_object(*object); } +void Cut::post_process(ModelObject* object, ModelObjectPtrs& cut_object_ptrs, bool keep, bool place_on_cut, bool flip) +{ + if (!object) return; + + if (keep && !object->volumes.empty()) { + reset_instance_transformation(object, m_instance, m_cut_matrix, place_on_cut, flip); + cut_object_ptrs.push_back(object); + } + else + m_model.objects.push_back(object); // will be deleted in m_model.clear_objects(); +} + +void Cut::post_process(ModelObject* upper, ModelObject* lower, ModelObjectPtrs& cut_object_ptrs) +{ + post_process(upper, cut_object_ptrs, + m_attributes.has(ModelObjectCutAttribute::KeepUpper), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + m_attributes.has(ModelObjectCutAttribute::FlipUpper)); + + post_process(lower, cut_object_ptrs, + m_attributes.has(ModelObjectCutAttribute::KeepLower), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); +} + + +void Cut::finalize(const ModelObjectPtrs& objects) +{ + //clear model from temporarry objects + m_model.clear_objects(); + + // add to model result objects + m_model.objects = objects; +} + + const ModelObjectPtrs& Cut::perform_with_plane() { if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) { @@ -317,25 +353,13 @@ const ModelObjectPtrs& Cut::perform_with_plane() } }; - if (m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { - delete_extra_modifiers(upper); - reset_instance_transformation(upper, m_instance, m_cut_matrix, - m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - m_attributes.has(ModelObjectCutAttribute::FlipUpper)); - cut_object_ptrs.push_back(upper); - } - - if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { - delete_extra_modifiers(lower); - reset_instance_transformation(lower, m_instance, m_cut_matrix, - m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); - cut_object_ptrs.push_back(lower); - } + post_process(upper, lower, cut_object_ptrs); + delete_extra_modifiers(upper); + delete_extra_modifiers(lower); if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { for (auto dowel : dowels) { - reset_instance_transformation(dowel, m_instance, Transform3d::Identity()); + reset_instance_transformation(dowel, m_instance); dowel->name += "-Dowel-" + dowel->volumes[0]->name; cut_object_ptrs.push_back(dowel); } @@ -344,8 +368,7 @@ const ModelObjectPtrs& Cut::perform_with_plane() BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; - m_model.clear_objects(); - m_model.objects = cut_object_ptrs; + finalize(cut_object_ptrs); return m_model.objects; } @@ -403,17 +426,6 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe ModelObject* lower{ nullptr }; if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower); - auto add_cut_objects = [this](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower) { - if (upper && !upper->volumes.empty()) { - reset_instance_transformation(upper, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); - cut_objects.push_back(upper); - } - if (lower && !lower->volumes.empty()) { - reset_instance_transformation(lower, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); - cut_objects.push_back(lower); - } - }; - const size_t cut_parts_cnt = parts.size(); bool has_modifiers = false; @@ -437,7 +449,7 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe // Means that object is cut without connectors // Just add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); + post_process(upper, lower, cut_object_ptrs); } else if (volumes.size() > cut_parts_cnt) { // Means that object is cut with connectors @@ -462,7 +474,7 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe lower->add_volume(*volume, volume->type()); // Add Upper and Lower objects to cut_object_ptrs - add_cut_objects(cut_object_ptrs, upper, lower); + post_process(upper, lower, cut_object_ptrs); // Add Dowel-connectors as separate objects to cut_object_ptrs if (cut_connectors_obj.size() >= 3) @@ -473,8 +485,7 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector parts, int dowe // Now merge all model parts together: merge_solid_parts_inside_object(cut_object_ptrs); - m_model.clear_objects(); - m_model.objects = cut_object_ptrs; + finalize(cut_object_ptrs); return m_model.objects; } @@ -608,28 +619,15 @@ const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Tran assert(!upper->volumes.empty() && !lower->volumes.empty()); - // check which parts have to stay/be deleted + // Add Upper and Lower parts to cut_object_ptrs - if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) { - reset_instance_transformation(upper, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), m_attributes.has(ModelObjectCutAttribute::FlipUpper)); - cut_object_ptrs.push_back(upper); - } - else - m_model.objects.push_back(upper); // will be deleted in m_model.clear_objects(); - - if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) { - reset_instance_transformation(lower, m_instance, m_cut_matrix, m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower)); - cut_object_ptrs.push_back(lower); - } - else - m_model.objects.push_back(lower); // will be deleted in m_model.clear_objects(); + post_process(upper, lower, cut_object_ptrs); // Now merge all model parts together: merge_solid_parts_inside_object(cut_object_ptrs); } - m_model.clear_objects(); - m_model.objects = cut_object_ptrs; + finalize(cut_object_ptrs); return m_model.objects; } diff --git a/src/libslic3r/CutUtils.hpp b/src/libslic3r/CutUtils.hpp index a31404e898..7aa445160b 100644 --- a/src/libslic3r/CutUtils.hpp +++ b/src/libslic3r/CutUtils.hpp @@ -26,6 +26,10 @@ class Cut { const Transform3d& m_cut_matrix; ModelObjectCutAttributes m_attributes; + void post_process(ModelObject* object, ModelObjectPtrs& objects, bool keep, bool place_on_cut, bool flip); + void post_process(ModelObject* upper_object, ModelObject* lower_object, ModelObjectPtrs& objects); + void finalize(const ModelObjectPtrs& objects); + public: Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix, From e241e905ec6582cf45e3543173a007f7734b1cb1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 14:52:20 +0200 Subject: [PATCH 47/51] CutGizmo: Manually cherry-picked https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/6dbbbfea7aa55c088c28fd55c58c02ab533bde48. This changed fixed cut for mirrored object --- src/libslic3r/CutUtils.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index ba2c3452af..09f4142dac 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -208,7 +208,12 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan auto& obj_instance = object->instances[i]; const double rot_z = obj_instance->get_rotation().z(); - obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor())); + Transformation inst_trafo = Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor()); + // add respect to mirroring + if (obj_instance->is_left_handed()) + inst_trafo = inst_trafo * Transformation(scale_transform(Vec3d(-1, 1, 1))); + + obj_instance->set_transformation(inst_trafo); Vec3d rotation = Vec3d::Zero(); if (!flip && !place_on_cut) { From 8a1a5d22596deb2fe1af06ed525ac93f28ed8fae Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 15:06:36 +0200 Subject: [PATCH 48/51] GalleryDialog: Fixed a memory leak --- src/slic3r/GUI/GalleryDialog.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index a08673e235..5ac51d54f7 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -134,7 +134,12 @@ GalleryDialog::GalleryDialog(wxWindow* parent) : } GalleryDialog::~GalleryDialog() -{ +{ + // From wxWidgets docs: + // The method void wxListCtrl::SetImageList(wxImageList* imageList, int which) + // does not take ownership of the image list, you have to delete it yourself. + if (m_image_list) + delete m_image_list; } int GalleryDialog::show(bool show_from_menu) From b213ff01d4f3dde7f9379ed2a2a6818665b25498 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 10 Aug 2023 16:40:20 +0200 Subject: [PATCH 49/51] CutGizmo: Next bug-fixing: * Performance during drawing of the cut line. Recalculate cut only when Mouse button is up. * Reset button for Depth / Width modification + tolerance --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 49917d09f8..1749c9d2dc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -2543,7 +2543,7 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in ImGui::SameLine(); - m_imgui->disabled_begin(is_approx(in_val, init_val)); + m_imgui->disabled_begin(is_approx(in_val, init_val) && is_approx(in_tolerance, 0.1f)); const std::string act_name = _u8L("Reset"); if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); @@ -3446,6 +3446,8 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse } if (cut_line_processing()) { + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + m_groove_editing = true; reset_cut_by_contours(); m_line_end = pt; @@ -3478,8 +3480,10 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse m_angle_arc.reset(); discard_cut_line_processing(); - if (CutMode(m_mode) == CutMode::cutTongueAndGroove) + if (CutMode(m_mode) == CutMode::cutTongueAndGroove) { + m_groove_editing = false; reset_cut_by_contours(); + } } else if (action == SLAGizmoEventType::Moving) this->set_dirty(); From 49ed94ff7799eb6722d8045b4858e015637e6b23 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 11 Aug 2023 10:09:31 +0200 Subject: [PATCH 50/51] CutGizmo: TaG mode: Fix of volume relation to upper/lower object when whole volume is over a cut plane --- src/libslic3r/CutUtils.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index 09f4142dac..11c2d62d8c 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -186,8 +186,10 @@ static void process_solid_part_cut(ModelVolume* volume, const Transform3d& insta if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) { add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); - add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); - upper->volumes.back()->cut_info.is_from_upper = false; + if (!lower_mesh.empty()) { + add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + upper->volumes.back()->cut_info.is_from_upper = false; + } return; } From 840f1b0ea930dd0690039f334cd48dcc9d448a87 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 11 Aug 2023 13:05:50 +0200 Subject: [PATCH 51/51] Cut: Experiment: Fixed a variable declaration of m_cut_matrix, which caused an ASAN-crash --- src/libslic3r/CutUtils.cpp | 2 ++ src/libslic3r/CutUtils.hpp | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/CutUtils.cpp b/src/libslic3r/CutUtils.cpp index 11c2d62d8c..1622af87e1 100644 --- a/src/libslic3r/CutUtils.cpp +++ b/src/libslic3r/CutUtils.cpp @@ -1,9 +1,11 @@ #include "CutUtils.hpp" +#include "Geometry.hpp" #include "libslic3r.h" #include "Model.hpp" #include "TriangleMeshSlicer.hpp" #include "TriangleSelector.hpp" +#include "ObjectID.hpp" namespace Slic3r { diff --git a/src/libslic3r/CutUtils.hpp b/src/libslic3r/CutUtils.hpp index 7aa445160b..2c477a3e2b 100644 --- a/src/libslic3r/CutUtils.hpp +++ b/src/libslic3r/CutUtils.hpp @@ -1,10 +1,7 @@ #ifndef slic3r_CutUtils_hpp_ #define slic3r_CutUtils_hpp_ -#include "libslic3r.h" #include "enum_bitmask.hpp" -#include "Geometry.hpp" -#include "ObjectID.hpp" #include "Point.hpp" #include "Model.hpp" @@ -23,7 +20,7 @@ class Cut { Model m_model; int m_instance; - const Transform3d& m_cut_matrix; + const Transform3d m_cut_matrix; ModelObjectCutAttributes m_attributes; void post_process(ModelObject* object, ModelObjectPtrs& objects, bool keep, bool place_on_cut, bool flip);