diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 847a15a9da..58484c8a53 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -248,6 +248,8 @@ void GLGizmoCut3D::render_cut_plane() { const BoundingBoxf3 box = m_move_gizmo.bounding_box(); Vec3d plane_center = m_move_gizmo.get_center();// == Vec3d::Zero() ? box.center() : m_move_gizmo.get_center(); + // update_contours(); + m_c->object_clipper()->render_cut(); const float min_x = box.min.x() - GLGizmoCenterMove::Margin - plane_center.x(); const float max_x = box.max.x() + GLGizmoCenterMove::Margin - plane_center.x(); @@ -433,7 +435,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(m_label_width); for (Axis axis : {X, Y, Z}) render_rotation_input(axis); - m_imgui->text(_L("°")); + m_imgui->text(_L("°")); } else { ImGui::AlignTextToFramePadding(); @@ -489,6 +491,16 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) const bool cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); + //////// + static bool hide_clipped = true; + static bool fill_cut = true; + static float contour_width = 0.; + m_imgui->checkbox("hide_clipped", hide_clipped); + m_imgui->checkbox("fill_cut", fill_cut); + m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); + m_c->object_clipper()->set_behavior(hide_clipped, fill_cut, contour_width); + //////// + m_imgui->end(); if (cut_clicked && (m_keep_upper || m_keep_lower)) @@ -498,6 +510,12 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool GLGizmoCut3D::can_perform_cut() const { return true; + + const BoundingBoxf3 box = bounding_box(); + Vec3d plane_center = box.center(); + plane_center.z() = 0; + m_c->object_clipper()->set_range_and_pos(plane_center, + plane_center + m_max_z * Vec3d::UnitZ(), m_cut_z); } void GLGizmoCut3D::perform_cut(const Selection& selection) @@ -521,5 +539,45 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } } + + +bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (is_dragging()) + return false; + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Transform3d instance_trafo = mi->get_transformation().get_matrix(); + const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); + const Camera& camera = wxGetApp().plater()->get_camera(); + + int mesh_id = -1; + for (const ModelVolume *mv : mo->volumes) { + ++mesh_id; + if (! mv->is_model_part()) + continue; + Vec3f hit; + Vec3f normal; + bool clipping_plane_was_hit = false; + m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), + camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + nullptr, &clipping_plane_was_hit); + if (clipping_plane_was_hit) { + // 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; + return true; + } + } + return false; +} + +CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const { + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::ObjectClipper) + | int(CommonGizmosDataID::Raycaster)); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index d1461404b3..b450b6ec7b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -11,6 +11,8 @@ namespace Slic3r { namespace GUI { +enum class SLAGizmoEventType : unsigned char; + class GLGizmoCenterMove : public GLGizmoMove3D { public: @@ -108,13 +110,20 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void shift_cut_z(double delta); protected: bool on_init() override; + void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } std::string on_get_name() const override; void on_set_state() override; + bool on_is_activable() const override; + void on_start_dragging() override; + void on_update(const UpdateData& data) override; + CommonGizmosDataID on_get_requirements() const override; void on_set_hover_id() override; void on_enable_grabber(unsigned int id) override; void on_disable_grabber(unsigned int id) override; @@ -129,6 +138,7 @@ protected: } void on_render_input_window(float x, float y, float bottom_limit) override; + private: void set_center(const Vec3d& center); void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 66b6dcf609..b6da2e63d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -295,7 +295,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -304,7 +304,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::SameLine(sliders_left_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel"))) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index be52ebcb9a..e8a6f6e2c8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -383,19 +383,19 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (action == SLAGizmoEventType::MouseWheelUp && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::max(0., pos - 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } @@ -693,7 +693,7 @@ RENDER_AGAIN: else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -702,7 +702,7 @@ RENDER_AGAIN: ImGui::PushItemWidth(window_width - settings_sliders_left); float clp_dist = m_c->object_clipper()->get_position(); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); // make sure supports are shown/hidden as appropriate bool show_sups = m_c->instances_hider()->are_supports_shown(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index dd9cf0de2e..77dc3a9663 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -463,7 +463,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_imgui->text(m_desc.at("clipping_of_view")); } else { if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position(-1., false); }); + wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -471,7 +471,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::SameLine(sliders_left_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel"))) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 82a816d1b9..30cf2b31c5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -429,7 +429,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous pos = action == SLAGizmoEventType::MouseWheelDown ? std::max(0., pos - 0.01) : std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } else if (alt_down) { @@ -461,7 +461,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 5f6cd7a95f..6d49b276cd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -157,7 +157,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -166,7 +166,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(sliders_left_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel"))) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fa0b27269a..e1bc32bbe3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -516,19 +516,19 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (action == SLAGizmoEventType::MouseWheelUp && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::max(0., pos - 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } @@ -826,7 +826,7 @@ RENDER_AGAIN: else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -835,7 +835,7 @@ RENDER_AGAIN: ImGui::PushItemWidth(window_width - clipping_slider_left); float clp_dist = m_c->object_clipper()->get_position(); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); if (m_imgui->button("?")) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 1e49ebc8c3..48a54fbaee 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -425,18 +425,20 @@ void ObjectClipper::render_cut() const glsafe(::glPushMatrix()); #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); + clipper->render_contour({ 1.f, 1.f, 1.f, 1.f}); #else glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); clipper->render_cut(); -#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + glsafe(::glColor3f(1.f, 1.f, 1.f)); + clipper->render_contour(); +#endif glsafe(::glPopMatrix()); ++clipper_id; } } - -void ObjectClipper::set_position(double pos, bool keep_normal) +void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { const ModelObject* mo = get_pool()->selection_info()->model_object(); int active_inst = get_pool()->selection_info()->get_active_instance(); @@ -454,6 +456,28 @@ void ObjectClipper::set_position(double pos, bool keep_normal) get_pool()->get_canvas()->set_as_dirty(); } +void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos) +{ + Vec3d normal = end-origin; + double norm = normal.norm(); + pos = std::clamp(pos, 0.0001, norm); + m_clp.reset(new ClippingPlane(normal, pos)); + m_clp_ratio = pos/norm; + get_pool()->get_canvas()->set_as_dirty(); +} + +const ClippingPlane* ObjectClipper::get_clipping_plane() const +{ + static const ClippingPlane no_clip = ClippingPlane::ClipsNothing(); + return m_hide_clipped ? m_clp.get() : &no_clip; +} + +void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contour_width) +{ + m_hide_clipped = hide_clipped; + for (auto& clipper : m_clippers) + clipper->set_behaviour(fill_cut, contour_width); +} void SupportsClipper::on_update() @@ -542,9 +566,12 @@ void SupportsClipper::render_cut() const glsafe(::glPushMatrix()); #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f }); + m_clipper->render_contour({ 1.f, 1.f, 1.f, 1.f }); #else glsafe(::glColor3f(1.0f, 0.f, 0.37f)); m_clipper->render_cut(); + glsafe(::glColor3f(1.0f, 1.f, 1.f)); + m_clipper->render_contour(); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopMatrix()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 228f5b58c3..c5c2384080 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -255,10 +255,13 @@ public: CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG - void set_position(double pos, bool keep_normal); + void set_normal(const Vec3d& dir); double get_position() const { return m_clp_ratio; } - ClippingPlane* get_clipping_plane() const { return m_clp.get(); } + const ClippingPlane* get_clipping_plane() const; void render_cut() const; + void set_position_by_ratio(double pos, bool keep_normal); + void set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos); + void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); protected: @@ -271,6 +274,7 @@ private: std::unique_ptr m_clp; double m_clp_ratio = 0.; double m_active_inst_bb_radius = 0.; + bool m_hide_clipped = true; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ba1419fea4..b2d543c5d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -445,6 +445,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MmuSegmentation) return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Cut) + return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -656,7 +658,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) m_tooltip.clear(); if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 3787abb2f7..40b1dc3bcc 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -19,6 +19,16 @@ namespace Slic3r { namespace GUI { +void MeshClipper::set_behaviour(bool fill_cut, double contour_width) +{ + if (fill_cut != m_fill_cut || contour_width != m_contour_width) + m_triangles_valid = false; + m_fill_cut = fill_cut; + m_contour_width = contour_width; +} + + + void MeshClipper::set_plane(const ClippingPlane& plane) { if (m_plane != plane) { @@ -43,7 +53,6 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh) if (m_mesh != &mesh) { m_mesh = &mesh; m_triangles_valid = false; - m_triangles2d.resize(0); } } @@ -52,7 +61,6 @@ void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) if (m_negative_mesh != &mesh) { m_negative_mesh = &mesh; m_triangles_valid = false; - m_triangles2d.resize(0); } } @@ -63,12 +71,10 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo) if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) { m_trafo = trafo; m_triangles_valid = false; - m_triangles2d.resize(0); } } - #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void MeshClipper::render_cut(const ColorRGBA& color) #else @@ -77,7 +83,6 @@ void MeshClipper::render_cut() { if (! m_triangles_valid) recalculate_triangles(); - #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); if (curr_shader != nullptr) @@ -100,6 +105,36 @@ void MeshClipper::render_cut() } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void MeshClipper::render_contour(const ColorRGBA& color) +#else +void MeshClipper::render_contour() +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +{ + if (! m_triangles_valid) + recalculate_triangles(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + m_model_expanded.set_color(color); + m_model_expanded.render(); + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +#else + if (m_vertex_array_expanded.has_VBOs()) + m_vertex_array_expanded.render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +} + + void MeshClipper::recalculate_triangles() { @@ -181,24 +216,25 @@ void MeshClipper::recalculate_triangles() } } - m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.); - tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting + std::vector triangles2d = m_fill_cut + ? triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.) + : std::vector(); #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_model.reset(); GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(m_triangles2d.size()) }; - init_data.reserve_vertices(m_triangles2d.size()); - init_data.reserve_indices(m_triangles2d.size()); + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(triangles2d.size()) }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); // vertices + indices - for (auto it = m_triangles2d.cbegin(); it != m_triangles2d.cend(); it = it + 3) { + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - const size_t idx = it - m_triangles2d.cbegin(); + const size_t idx = it - triangles2d.cbegin(); if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); else @@ -209,16 +245,61 @@ void MeshClipper::recalculate_triangles() m_model.init_from(std::move(init_data)); #else m_vertex_array.release_geometry(); - for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { + for (auto it=triangles2d.cbegin(); it != triangles2d.cend(); it=it+3) { m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); m_vertex_array.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); m_vertex_array.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); - const size_t idx = it - m_triangles2d.cbegin(); + const size_t idx = it - triangles2d.cbegin(); m_vertex_array.push_triangle(idx, idx+1, idx+2); } m_vertex_array.finalize_geometry(true); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + + triangles2d = {}; + if (m_contour_width != 0.) { + ExPolygons expolys_exp = offset_ex(expolys, scale_(m_contour_width)); + expolys_exp = diff_ex(expolys_exp, expolys); + triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.); + } + + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_model_expanded.reset(); + + init_data = GLModel::Geometry(); + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(triangles2d.size()) }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); + + // vertices + indices + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - triangles2d.cbegin(); + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + else + init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + } + + if (!init_data.is_empty()) + m_model_expanded.init_from(std::move(init_data)); +#else + m_vertex_array_expanded.release_geometry(); + for (auto it=triangles2d.cbegin(); it != triangles2d.cend(); it=it+3) { + m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); + m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); + m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); + const size_t idx = it - triangles2d.cbegin(); + m_vertex_array_expanded.push_triangle(idx, idx+1, idx+2); + } + m_vertex_array_expanded.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + + + m_triangles_valid = true; } @@ -253,8 +334,11 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3 bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, - size_t* facet_idx) const + size_t* facet_idx, bool* was_clipping_plane_hit) const { + if (was_clipping_plane_hit) + *was_clipping_plane_hit = false; + Vec3d point; Vec3d direction; line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); @@ -275,9 +359,26 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& break; } - if (i==hits.size() || (hits.size()-i) % 2 != 0) { - // All hits are either clipped, or there is an odd number of unclipped - // hits - meaning the nearest must be from inside the mesh. + if (i==hits.size()) { + // All hits are clipped. + return false; + } + if ((hits.size()-i) % 2 != 0) { + // There is an odd number of unclipped hits - meaning the nearest must be from inside the mesh. + // In that case, calculate intersection with the clipping place. + if (clipping_plane && was_clipping_plane_hit) { + direction = direction + point; + point = trafo * point; // transform to world coords + direction = trafo * direction - point; + + Vec3d normal = -clipping_plane->get_normal().cast(); + double den = normal.dot(direction); + if (den != 0.) { + double t = (-clipping_plane->get_offset() - normal.dot(point))/den; + position = (point + t * direction).cast(); + *was_clipping_plane_hit = true; + } + } return false; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index cc961ee8f1..f5d031d920 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -77,6 +77,10 @@ public: class MeshClipper { public: + // Set whether the cut should be triangulated and whether a cut + // contour should be calculated and shown. + void set_behaviour(bool fill_cut, double contour_width); + // Inform MeshClipper about which plane we want to use to cut the mesh // This is supposed to be in world coordinates. void set_plane(const ClippingPlane& plane); @@ -100,8 +104,12 @@ public: // be set in world coords. #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void render_cut(const ColorRGBA& color); + void render_contour(const ColorRGBA& color); #else void render_cut(); + // Render the triangulated contour. Transformation matrices should + // be set in world coords. + void render_contour(); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL private: @@ -112,13 +120,16 @@ private: const TriangleMesh* m_negative_mesh = nullptr; ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); - std::vector m_triangles2d; #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLModel m_model; + GLModel m_model_expanded; #else GLIndexedVertexArray m_vertex_array; + GLIndexedVertexArray m_vertex_array_expanded; #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL bool m_triangles_valid = false; + bool m_fill_cut = true; + double m_contour_width = 0.; }; @@ -143,10 +154,11 @@ public: const Vec2d& mouse_pos, const Transform3d& trafo, // how to get the mesh into world coords const Camera& camera, // current camera position - Vec3f& position, // where to save the positibon of the hit (mesh coords) + Vec3f& position, // where to save the positibon of the hit (mesh coords if mesh, world coords if clipping plane) Vec3f& normal, // normal of the triangle that was hit const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) - size_t* facet_idx = nullptr // index of the facet hit + size_t* facet_idx = nullptr, // index of the facet hit + bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section? ) const; // Given a vector of points in woorld coordinates, this returns vector