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