diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index b18756a5e8..79273bbbe7 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1378,7 +1378,7 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn return connector_mesh; } -void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) +void ModelObject::apply_cut_connectors(const std::string& name) { // discard old connector markers for volumes for (ModelVolume* volume : volumes) @@ -1387,11 +1387,9 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr if (cut_connectors.empty()) return; - indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes); - size_t connector_id = cut_id.connectors_cnt(); for (const CutConnector& connector : cut_connectors) { - TriangleMesh mesh = TriangleMesh(connector_mesh); + 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); @@ -1403,7 +1401,7 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr Vec3d::Ones() )); - new_volume->cut_info = { true, connector.radius_tolerance, connector.height_tolerance }; + new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; new_volume->name = name + "-" + std::to_string(++connector_id); } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1541,13 +1539,13 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const ModelVolume* vol = lower->add_volume(*volume); vol->set_transformation(volume_matrix); - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) + if (volume->cut_info.connector_type == CutConnectorType::Dowel) apply_tolerance(vol); else // 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); } - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + if (volume->cut_info.connector_type == CutConnectorType::Dowel) { // add one more solid part same as connector if this connector is a dowel ModelVolume* vol = dowels->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3242dd2557..a82c081369 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -219,48 +219,6 @@ private: friend class ModelObject; }; -struct CutConnector -{ - Vec3d pos; - Vec3d rotation; - float radius; - float height; - float radius_tolerance;// [0.f : 1.f] - float height_tolerance;// [0.f : 1.f] - bool failed = false; - - CutConnector() - : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) - {} - - CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, bool fl = false) - : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), failed(fl) - {} - - CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.failed) {} - - bool operator==(const CutConnector& sp) const; - - bool operator!=(const CutConnector& sp) const { return !(sp == (*this)); } -/* - bool is_inside(const Vec3f& pt) const; - - bool get_intersections(const Vec3f& s, const Vec3f& dir, - std::array, 2>& out) const; - - indexed_triangle_set to_mesh() const; -*/ - template inline void serialize(Archive& ar) - { - ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, failed); - } - - static constexpr size_t steps = 32; -}; - -using CutConnectors = std::vector; - enum class CutConnectorType : int { Plug , Dowel @@ -283,8 +241,8 @@ enum class CutConnectorShape : int { struct CutConnectorAttributes { CutConnectorType type{ CutConnectorType::Plug }; - CutConnectorStyle style{ CutConnectorStyle::Prizm}; - CutConnectorShape shape{CutConnectorShape::Circle}; + CutConnectorStyle style{ CutConnectorStyle::Prizm }; + CutConnectorShape shape{ CutConnectorShape::Circle }; CutConnectorAttributes() {} @@ -294,8 +252,55 @@ struct CutConnectorAttributes CutConnectorAttributes(const CutConnectorAttributes& rhs) : CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} + + bool operator==(const CutConnectorAttributes& other) const; + + bool operator!=(const CutConnectorAttributes& other) const { return !(other == (*this)); } + + bool operator<(const CutConnectorAttributes& other) const { + return this->type < other.type || + (this->type == other.type && this->style < other.style) || + (this->type == other.type && this->style == other.style && this->shape < other.shape); + } + + template inline void serialize(Archive& ar) { + ar(type, style, shape); + } }; +struct CutConnector +{ + Vec3d pos; + Vec3d rotation; + float radius; + float height; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] + CutConnectorAttributes attribs; + + CutConnector() + : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) + {} + + CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) + : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + {} + + CutConnector(const CutConnector& rhs) : + CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + + bool operator==(const CutConnector& other) const; + + bool operator!=(const CutConnector& other) const { return !(other == (*this)); } + + template inline void serialize(Archive& ar) { + ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, attribs); + } +}; + +using CutConnectors = std::vector; + + // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -436,7 +441,7 @@ public: size_t parts_count() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); - void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); + void apply_cut_connectors(const std::string& name); // invalidate cut state for this and related objects from the whole model void invalidate_cut(); void synchronize_model_after_cut(); @@ -710,12 +715,14 @@ public: // It contains information about connetors struct CutInfo { - bool is_connector {false}; - float radius_tolerance;// [0.f : 1.f] - float height_tolerance;// [0.f : 1.f] + bool is_connector{ false }; + CutConnectorType connector_type{ CutConnectorType::Plug }; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] void discard() { is_connector = false; } - } cut_info; + }; + CutInfo cut_info; // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 417cf6037e..21917b1d66 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -226,12 +226,15 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.Moving() && !cut_line_processing()) return false; - if (use_grabbers(mouse_event)) - return true; - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); + if (use_grabbers(mouse_event)) { + if (m_hover_id >= m_connectors_group_id && mouse_event.LeftUp() && !mouse_event.ShiftDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + return true; + } + static bool pending_right_up = false; if (mouse_event.LeftDown()) { bool grabber_contains_mouse = (get_hover_id() != -1); @@ -407,6 +410,9 @@ bool GLGizmoCut3D::render_combo(const std::string& label, const std::vectorradio_button(m_connector_types[size_t(type)], m_connector_type == type)) { m_connector_type = type; update_connector_shape(); + return true; } + return false; } void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode) @@ -1268,31 +1276,63 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_connect_mode_radio_button(CutConnectorMode::Auto); render_connect_mode_radio_button(CutConnectorMode::Manual); */ + + if (m_selected_count == 1) + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + auto& connector = connectors[idx]; + m_connector_depth_ratio = connector.height; + m_connector_depth_ratio_tolerance = 100 * connector.height_tolerance; + m_connector_size = 2. * connector.radius; + m_connector_size_tolerance = 100 * connector.radius_tolerance; + m_connector_type = connector.attribs.type; + m_connector_style = size_t(connector.attribs.style); + m_connector_shape_id = size_t(connector.attribs.shape); + + break; + } + m_imgui->text(_L("Type")); - render_connect_type_radio_button(CutConnectorType::Plug); - render_connect_type_radio_button(CutConnectorType::Dowel); + bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); + type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); + if (type_changed) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.type = CutConnectorType(m_connector_type); if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) - update_connector_shape(); - if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) - update_connector_shape(); + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.style = CutConnectorStyle(m_connector_style) ; + + if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); + + if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) { + auto& connector = connectors[idx]; + connector.height = float(m_connector_depth_ratio); + connector.height_tolerance = 0.01f * m_connector_depth_ratio_tolerance; + } - if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) - for (auto& connector : connectors) { - connector.height = float(m_connector_depth_ratio); - connector.height_tolerance = 0.01f * m_connector_depth_ratio; - } if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) - for (auto& connector : connectors) { - connector.radius = float(m_connector_size * 0.5); - connector.radius_tolerance = 0.01f * m_connector_size_tolerance; - } + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + auto& connector = connectors[idx]; + connector.radius = float(m_connector_size * 0.5); + connector.radius_tolerance = 0.01f * m_connector_size_tolerance; + } m_imgui->disabled_end(); if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_connectors_editing = false; + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; } m_parent.request_extra_frame(); } @@ -1380,7 +1420,6 @@ void GLGizmoCut3D::render_connectors(bool picking) m_selected.resize(connectors.size(), false); } -#if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -1388,19 +1427,8 @@ void GLGizmoCut3D::render_connectors(bool picking) shader->start_using(); ScopeGuard guard([shader]() { shader->stop_using(); }); -#else - GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); - if (shader) - shader->start_using(); - ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GL_SHADERS_ATTRIBUTES const Camera& camera = wxGetApp().plater()->get_camera(); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES ColorRGBA render_color; @@ -1417,13 +1445,12 @@ void GLGizmoCut3D::render_connectors(bool picking) for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; -// const bool& point_selected = m_selected[i]; double height = connector.height; // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; - if (m_connector_type == CutConnectorType::Dowel && - m_connector_style == size_t(CutConnectorStyle::Prizm)) { + if (connector.attribs.type == CutConnectorType::Dowel && + connector.attribs.style == CutConnectorStyle::Prizm) { pos -= height * normal; height *= 2; } @@ -1435,7 +1462,13 @@ void GLGizmoCut3D::render_connectors(bool picking) else { if (size_t(m_hover_id- m_connectors_group_id) == i) render_color = ColorRGBA::CYAN(); - else { // neither hover nor picking + else if (m_selected[i]) + render_color = ColorRGBA::DARK_GRAY(); + else // neither hover nor picking + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + + // ! #ysFIXME rework get_volume_transformation + if (0) { // else { // neither hover nor picking int mesh_id = -1; for (const ModelVolume* mv : mo->volumes) { ++mesh_id; @@ -1454,42 +1487,18 @@ void GLGizmoCut3D::render_connectors(bool picking) } } -#if ENABLE_GL_SHADERS_ATTRIBUTES - m_connector_shape.set_color(render_color); + m_shapes[connector.attribs].set_color(render_color); const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( - Vec3d(pos.x(), pos.y(), pos.z()), + pos, Geometry::Transformation(m_rotation_m).get_rotation(), - Vec3d(connector.radius, connector.radius, height), - Vec3d::Ones() + Vec3d(connector.radius, connector.radius, height) ); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#else - const_cast(&m_connector_shape)->set_color(-1, render_color); - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(pos.x(), pos.y(), pos.z())); - - const Vec3d& angles = m_rotation_gizmo.get_rotation(); - glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - - glsafe(::glTranslated(0., 0., -0.5*connector.height)); - glsafe(::glScaled(connector.radius, connector.radius, height)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - m_connector_shape.render(); - -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif //!ENABLE_GL_SHADERS_ATTRIBUTES + m_shapes[connector.attribs].render(); } - -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif //!ENABLE_GL_SHADERS_ATTRIBUTES } bool GLGizmoCut3D::can_perform_cut() const @@ -1535,9 +1544,10 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) for (CutConnector& connector : mo->cut_connectors) { connector.rotation = rotation; - if (m_connector_type == CutConnectorType::Dowel) { - if (m_connector_style == size_t(CutConnectorStyle::Prizm)) + if (connector.attribs.type == CutConnectorType::Dowel) { + if (connector.attribs.style == CutConnectorStyle::Prizm) connector.height *= 2; + create_dowels_as_separate_object = true; } else { // culculate shift of the connector center regarding to the position on the cut plane @@ -1551,9 +1561,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) connector.pos += norm * (0.5 * connector.height); } } - mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); - if (m_connector_type == CutConnectorType::Dowel) - create_dowels_as_separate_object = true; + mo->apply_cut_connectors(_u8L("Connector")); } } @@ -1623,15 +1631,15 @@ void GLGizmoCut3D::reset_connectors() void GLGizmoCut3D::update_connector_shape() { - if (m_connector_shape.is_initialized()) - m_connector_shape.reset(); - - const indexed_triangle_set its = ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }); - m_connector_shape.init_from(its); + CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; + if (m_shapes.find(attribs) == m_shapes.end()) { + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + m_shapes[attribs].init_from(its); + } + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); m_connector_mesh.clear(); m_connector_mesh = TriangleMesh(its); - } void GLGizmoCut3D::update_model_object() const @@ -1709,14 +1717,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - // left down without selection rectangle - place connector on the cut plane: - if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down && m_connectors_editing) { - // If any point is in hover state, this should initiate its move - return control back to GLCanvas: - if (m_hover_id != -1) - return false; - - // If there is some selection, don't add new point and deselect everything instead. - if (m_selection_empty) { + if (action == SLAGizmoEventType::LeftDown && !shift_down && m_connectors_editing) { + // If there is no selection and no hovering, add new point + if (m_hover_id == -1 && !control_down && !alt_down) { std::pair pos_and_normal; if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { const Vec3d& hit = pos_and_normal.first; @@ -1726,12 +1729,15 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio), - float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance)); + float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); update_model_object(); - m_selected.push_back(false); + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected.push_back(true); assert(m_selected.size() == connectors.size()); m_parent.set_as_dirty(); - m_wait_for_up_event = true; return true; } @@ -1739,6 +1745,23 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi } return true; } + else if (action == SLAGizmoEventType::LeftUp && !shift_down && m_connectors_editing) { + if (m_hover_id >= m_connectors_group_id) { + if (alt_down) { + m_selected[m_hover_id - m_connectors_group_id] = false; + --m_selected_count; + } + else { + if (!control_down) { + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; + } + m_selected[m_hover_id - m_connectors_group_id] = true; + ++m_selected_count; + } + return true; + } + } else if (action == SLAGizmoEventType::RightDown && !shift_down && m_connectors_editing) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id < m_connectors_group_id) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 91f8749e76..fecb3e846c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -12,6 +12,7 @@ namespace Slic3r { enum class CutConnectorType : int; class ModelVolume; +struct CutConnectorAttributes; namespace GUI { class Selection; @@ -44,7 +45,7 @@ class GLGizmoCut3D : public GLGizmoBase Transform3d m_start_dragging_m{ Transform3d::Identity() }; double m_angle{ 0.0 }; - GLModel m_connector_shape; + std::map m_shapes; TriangleMesh m_connector_mesh; // workaround for using of the clipping plane normal Vec3d m_clp_normal{ Vec3d::Ones() }; @@ -90,8 +91,7 @@ class GLGizmoCut3D : public GLGizmoBase bool force_update_clipper_on_render{false}; mutable std::vector m_selected; // which pins are currently selected - bool m_selection_empty = true; - bool m_wait_for_up_event = false; + int m_selected_count{ 0 }; bool m_has_invalid_connector{ false }; @@ -181,7 +181,7 @@ private: void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); - void render_connect_type_radio_button(CutConnectorType type); + bool render_connect_type_radio_button(CutConnectorType type); Transform3d get_volume_transformation(const ModelVolume* volume) const; void render_connectors(bool picking);