From 09249e3b8dfc2ecb7172903bac49c6ad1731a1bf Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 15 Mar 2022 17:08:15 +0100 Subject: [PATCH] Cut: Perform cut with connectors --- src/libslic3r/Model.cpp | 77 +++++++++++++++++++++++++--- src/libslic3r/Model.hpp | 55 +++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 40 +++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 4 files changed, 139 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 8d44f3e7cc..5e018a2129 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1336,6 +1336,53 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr return res; } +void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) +{ + if (cut_connectors.empty()) + return; + + bool is_prizm = connector_attributes.style == CutConnectorStyle::Prizm; + const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; + + indexed_triangle_set connector_mesh; + switch (CutConnectorShape(connector_attributes.shape)) { + case CutConnectorShape::Triangle: + connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 3)); + break; + case CutConnectorShape::Square: + connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 4)); + break; + case CutConnectorShape::Circle: + connector_mesh = its_make_shape(1.0, 1.0, 2 * PI / 360); + break; + case CutConnectorShape::Hexagon: + connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 6)); + break; + } + + size_t connector_id = 0; + + for (const CutConnector& connector : cut_connectors) { + TriangleMesh mesh = TriangleMesh(connector_mesh); + // 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(Geometry::assemble_transform( + connector.pos, + connector.rotation, + Vec3d(connector.radius, connector.radius, connector.height), + Vec3d::Ones() + )); + + new_volume->name = name + "-" + std::to_string(++connector_id); + new_volume->source.is_connector = true; + } + + // delete all connectors + cut_connectors.clear(); +} + ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) { if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) @@ -1405,15 +1452,31 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (!volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. + // But if this modifier is a connector, then just set volume transformation + if (volume->source.is_connector) + volume->set_transformation(Geometry::Transformation(volume_matrix)); + else + volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower->add_volume(*volume); + ModelVolume* vol = { nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume* vol = upper->add_volume(*volume); + if (volume->source.is_connector) + vol->source.is_connector = true; + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume* vol = lower->add_volume(*volume); + if (volume->source.is_connector) { + vol->source.is_connector = true; + // for lower part change type of conector from NEGATIVE_VOLUME to MODEL_PART + if (vol->type() == ModelVolumeType::NEGATIVE_VOLUME) + vol->set_type(ModelVolumeType::MODEL_PART); + } + } } - else if (!volume->mesh().empty()) { + else if (!volume->mesh().empty() && + !volume->source.is_connector // we don't allow to cut a connectors + ) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c0e7e907de..dbaded841c 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -221,22 +221,22 @@ private: struct CutConnector { - Vec3f pos; - Vec3f normal; + Vec3d pos; + Vec3d rotation; float radius; float height; bool failed = false; CutConnector() - : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) + : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f) {} - CutConnector(Vec3f p, Vec3f n, float r, float h, bool fl = false) - : pos(p), normal(n), radius(r), height(h), failed(fl) + CutConnector(Vec3d p, Vec3d n, float r, float h, bool fl = false) + : pos(p), rotation(n), radius(r), height(h), failed(fl) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {} + CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.failed) {} bool operator==(const CutConnector& sp) const; @@ -251,22 +251,12 @@ struct CutConnector */ template inline void serialize(Archive& ar) { - ar(pos, normal, radius, height, failed); + ar(pos, rotation, radius, height, failed); } static constexpr size_t steps = 32; }; -// Declared outside of ModelVolume, so it could be forward declared. -enum class ModelVolumeType : int { - INVALID = -1, - MODEL_PART = 0, - NEGATIVE_VOLUME, - PARAMETER_MODIFIER, - SUPPORT_BLOCKER, - SUPPORT_ENFORCER, -}; - using CutConnectors = std::vector; enum class CutConnectorType : int { @@ -288,6 +278,32 @@ enum class CutConnectorShape : int { //,D-shape }; +struct CutConnectorAttributes +{ + CutConnectorType type{ CutConnectorType::Plug }; + CutConnectorStyle style{ CutConnectorStyle::Prizm}; + CutConnectorShape shape{CutConnectorShape::Circle}; + + CutConnectorAttributes() {} + + CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) + : type(t), style(st), shape(sh) + {} + + CutConnectorAttributes(const CutConnectorAttributes& rhs) : + CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} +}; + +// Declared outside of ModelVolume, so it could be forward declared. +enum class ModelVolumeType : int { + INVALID = -1, + MODEL_PART = 0, + NEGATIVE_VOLUME, + PARAMETER_MODIFIER, + SUPPORT_BLOCKER, + SUPPORT_ENFORCER, +}; + enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); @@ -416,6 +432,7 @@ public: size_t facets_count() const; size_t parts_count() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); + void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); void merge(); @@ -671,10 +688,12 @@ public: bool is_converted_from_meters{ false }; bool is_from_builtin_objects{ false }; + bool is_connector{ false }; + template void serialize(Archive& ar) { //FIXME Vojtech: Serialize / deserialize only if the Source is set. // likely testing input_file or object_idx would be sufficient. - ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects); + ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects, is_connector); } }; Source source; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9c5074a45c..5448dd1a2d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -578,22 +578,22 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) if (wxGetKeyState(WXK_SHIFT)) projection = m_snap_step * (double)std::round(projection / m_snap_step); + Vec3d shift = starting_vec * projection; + // move cut plane center - set_center(starting_box_center + starting_vec * projection); + set_center(starting_box_center + shift); // move connectors - Vec3f shift = Vec3f(starting_vec.cast() * projection); for (auto& connector : connectors) connector.pos += shift; } else if (m_hover_id > m_group_id) { - std::pair pos_and_normal; + std::pair pos_and_normal; if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) return; connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; - connectors[m_hover_id - m_connectors_group_id].normal = -pos_and_normal.second; } } @@ -924,6 +924,28 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); if (0.0 < object_cut_z && can_perform_cut()) { + ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; + // update connectors pos as offset of its center before cut performing + if (!mo->cut_connectors.empty()) { + const std::string name = _u8L("Connector"); + for (CutConnector& connector : mo->cut_connectors) { + connector.rotation = m_rotation_gizmo.get_rotation(); + + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d norm = m_grabbers[0].center - m_plane_center; + norm.normalize(); + Vec3d shift = norm * (0.5 * connector.height); + + // culculate offset of the connector pos regarding to the instance offset and possible SLA elevation + Vec3d connector_offset = connector.pos - instance_offset; + connector_offset[Z] -= first_glvolume->get_sla_shift_z(); + + // Update connector pos. It will be used as a center of created modifiers + connector.pos = connector_offset + shift; + } + mo->apply_cut_connectors(name, CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); + } + wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | @@ -939,7 +961,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, std::pair& pos_and_normal) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) { if (!m_c->raycaster()->raycaster()) return false; @@ -965,7 +987,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); + pos_and_normal = std::make_pair(hit.cast(), normal.cast()); return true; } } @@ -1031,10 +1053,10 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi // If there is some selection, don't add new point and deselect everything instead. if (m_selection_empty) { - std::pair pos_and_normal; + std::pair pos_and_normal; if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { - const Vec3f& hit = pos_and_normal.first; - const Vec3f& normal = pos_and_normal.second; + const Vec3d& hit = pos_and_normal.first; + const Vec3d& normal = pos_and_normal.second; // The clipping plane was clicked, hit containts coordinates of the hit in world coords. std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 1fedcd22e9..33d254c0d9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -93,7 +93,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, std::pair& pos_and_normal); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); ///