From 665bc9e960a1375ab9bc5f8265bf987e26f7b615 Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Thu, 6 Feb 2020 17:53:03 +0100
Subject: [PATCH] Hollowing: allow to place drain holes on the inner surface of
 a hollowed cavity

Drain holes are not allowed to be placed on its own inner surface. This was recently (0e3ebb3)
done by forcing invalidation of the slaposDrillHoles, which also invalidates and therefore
hides the drilled mesh. However, that also hides the hollowed mesh and it is not possible
to place holes inside the cavity.

This change does not dump the drilled mesh, but checks that no raycast hit ends up in a hole.
---
 src/libslic3r/SLA/Hollowing.cpp              |  2 +-
 src/libslic3r/SLA/Hollowing.hpp              |  5 +++
 src/slic3r/GUI/Gizmos/GLGizmoBase.cpp        |  2 +-
 src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp      | 37 ++++++++++++--------
 src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp      |  1 +
 src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp |  9 ++++-
 6 files changed, 38 insertions(+), 18 deletions(-)

diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp
index ecc42b889..2a9662e85 100644
--- a/src/libslic3r/SLA/Hollowing.cpp
+++ b/src/libslic3r/SLA/Hollowing.cpp
@@ -119,7 +119,7 @@ Contour3D DrainHole::to_mesh() const
 {
     auto r = double(radius);
     auto h = double(height);
-    sla::Contour3D hole = sla::cylinder(r, h);
+    sla::Contour3D hole = sla::cylinder(r, h, steps);
     Eigen::Quaterniond q;
     q.setFromTwoVectors(Vec3d{0., 0., 1.}, normal.cast<double>());
     for(auto& p : hole.points) p = q * p + pos.cast<double>();
diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp
index b3375ed1a..cc7d310ea 100644
--- a/src/libslic3r/SLA/Hollowing.hpp
+++ b/src/libslic3r/SLA/Hollowing.hpp
@@ -34,6 +34,9 @@ struct DrainHole
     DrainHole(Vec3f p, Vec3f n, float r, float h)
         : pos(p), normal(n), radius(r), height(h)
     {}
+
+    DrainHole(const DrainHole& rhs) :
+        DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height) {}
     
     bool operator==(const DrainHole &sp) const;
     
@@ -50,6 +53,8 @@ struct DrainHole
     {
         ar(pos, normal, radius, height);
     }
+
+    static constexpr size_t steps = 32;
 };
 
 using DrainHoles = std::vector<DrainHole>;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index c237198a9..daf7e1fd1 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -384,7 +384,7 @@ bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* mode
     if (! recent_update)
         recent_update = m_print_object_idx < 0 && old_po_idx >= 0;
 
-    return m_print_object_idx < 0 ? old_po_idx >=0 : false;
+    return recent_update;
 }
 
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
index 11a78fd60..87a4960ee 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
@@ -12,7 +12,6 @@
 #include "slic3r/GUI/PresetBundle.hpp"
 #include "libslic3r/SLAPrint.hpp"
 #include "libslic3r/TriangleMesh.hpp"
-#include "libslic3r/MeshBoolean.hpp"
 
 
 namespace Slic3r {
@@ -59,8 +58,11 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
 {
     if (m_c->recent_update) {
 
-        if (m_c->m_model_object)
+        if (m_c->m_model_object) {
             reload_cache();
+            if (m_c->has_drilled_mesh())
+                m_holes_in_drilled_mesh = m_c->m_model_object->sla_drain_holes;
+        }
 
         if (m_state == On) {
             m_parent.toggle_model_objects_visibility(false);
@@ -344,18 +346,25 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
     // The raycaster query
     Vec3f hit;
     Vec3f normal;
-    if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) {
-
-        // User is about to manipulate a hole. If the gizmo currently shows drilled mesh,
-        // invalidate slaposDrillHoles so it returns to normal. To do this, hackishly
-        // add a hole, force SLAPrint::apply call that will invalidate the step because
-        // of it and then remove the hole again.
+    if (m_c->m_mesh_raycaster->unproject_on_mesh(
+            mouse_pos,
+            trafo.get_matrix(),
+            camera,
+            hit,
+            normal,
+            m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr))
+    {
         if (m_c->has_drilled_mesh()) {
-            m_c->m_model_object->sla_drain_holes.push_back(sla::DrainHole());
-            m_selected.push_back(false);
-            m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
-            wxGetApp().CallAfter([this] { m_c->m_model_object->sla_drain_holes.pop_back(); m_selected.pop_back(); });
-            return false;
+            // in this case the raycaster sees the hollowed and drilled mesh.
+            // if the point lies on the surface created by the hole, we want
+            // to ignore it.
+            for (const sla::DrainHole& hole : m_holes_in_drilled_mesh) {
+                sla::DrainHole outer(hole);
+                outer.radius *= 1.001f;
+                outer.height *= 1.001f;
+                if (outer.is_inside(hit))
+                    return false;
+            }
         }
 
         // Return both the point and the facet normal.
@@ -527,8 +536,6 @@ void GLGizmoHollow::delete_selected_points()
         }
     }
 
-    m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
-
     select_point(NoPoints);
 }
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
index c0e518e4d..2daf28b2a 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
@@ -70,6 +70,7 @@ private:
     float m_quality_stash = 0.5f;
     float m_closing_d_stash = 2.f;
     Vec3f m_hole_before_drag = Vec3f::Zero();
+    sla::DrainHoles m_holes_in_drilled_mesh;
 
     sla::DrainHoles m_holes_stash;
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 5130b7a9a..05f33ae52 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -408,7 +408,14 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
     // The raycaster query
     Vec3f hit;
     Vec3f normal;
-    if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) {
+    if (m_c->m_mesh_raycaster->unproject_on_mesh(
+            mouse_pos,
+            trafo.get_matrix(),
+            camera,
+            hit,
+            normal,
+            m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr))
+    {
         // Check whether the hit is in a hole
         bool in_hole = false;
         // In case the hollowed and drilled mesh is available, we can allow