From f275461354b7260f6d0eefceb9714fe8ce7ab9aa Mon Sep 17 00:00:00 2001
From: Lukas Matena <lukasmatena@seznam.cz>
Date: Fri, 1 Nov 2019 12:23:49 +0100
Subject: [PATCH] WIP: FDM supports gizmo now works with multiple part objects
 into some extent

---
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 280 ++++++++++---------
 src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp |  16 +-
 src/slic3r/GUI/MeshUtils.hpp                 |   5 +-
 3 files changed, 154 insertions(+), 147 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 8c8f731ce..727c7dcea 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -17,9 +17,8 @@ namespace GUI {
 GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
     : GLGizmoBase(parent, icon_filename, sprite_id)
     , m_quadric(nullptr)
-    , m_its(nullptr)
 {
-    m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
+    m_clipping_plane.reset(new ClippingPlane());
     m_quadric = ::gluNewQuadric();
     if (m_quadric != nullptr)
         // using GLU_FILL does not work when the instance's transformation
@@ -99,7 +98,7 @@ void GLGizmoFdmSupports::on_render() const
         return;
     }
 
-    if (! m_its || ! m_mesh)
+    if (m_meshes.empty())
         const_cast<GLGizmoFdmSupports*>(this)->update_mesh();
 
     glsafe(::glEnable(GL_BLEND));
@@ -114,60 +113,67 @@ void GLGizmoFdmSupports::on_render() const
 
 void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
 {
-    if (! m_mesh)
-        return;
+//    if (m_meshes.empty())
+//        return;
 
-    // Get transformation of the instance
-    const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
-    Transform3d trafo = vol->get_instance_transformation().get_matrix();
 
-    ::glColor3f(0.0f, 0.37f, 1.0f);
+    for (size_t mesh_id=0; mesh_id<m_meshes.size(); ++mesh_id) {
 
-    for (size_t facet_idx=0; facet_idx<m_selected_facets.size(); ++facet_idx) {
-        if (! m_selected_facets[facet_idx])
-            continue;
-        stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(m_mesh->its, facet_idx);
-        ::glPushMatrix();
-        ::glTranslatef(normal(0), normal(1), normal(2));
-        ::glMultMatrixd(trafo.data());
+        const Transform3d trafo_matrix =
+            m_model_object->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
+            m_model_object->volumes[mesh_id]->get_matrix();
+        const TriangleMesh* mesh = m_meshes[mesh_id];
 
-        ::glBegin(GL_TRIANGLES);
-        ::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](2));
-        ::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](2));
-        ::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](2));
-        ::glEnd();
-        ::glPopMatrix();
+
+
+        ::glColor3f(0.0f, 0.37f, 1.0f);
+
+        for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
+            if (! m_selected_facets[mesh_id][facet_idx])
+                continue;
+            stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(mesh->its, facet_idx);
+            ::glPushMatrix();
+            ::glTranslatef(normal(0), normal(1), normal(2));
+            ::glMultMatrixd(trafo_matrix.data());
+
+            ::glBegin(GL_TRIANGLES);
+            ::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](0)](0), mesh->its.vertices[mesh->its.indices[facet_idx](0)](1), mesh->its.vertices[mesh->its.indices[facet_idx](0)](2));
+            ::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](1)](0), mesh->its.vertices[mesh->its.indices[facet_idx](1)](1), mesh->its.vertices[mesh->its.indices[facet_idx](1)](2));
+            ::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](2)](0), mesh->its.vertices[mesh->its.indices[facet_idx](2)](1), mesh->its.vertices[mesh->its.indices[facet_idx](2)](2));
+            ::glEnd();
+            ::glPopMatrix();
+        }
     }
 }
 
 void GLGizmoFdmSupports::render_clipping_plane(const Selection& selection) const
 {
-    if (m_clipping_plane_distance == 0.f)
-        return;
+//    if (m_clipping_plane_distance == 0.f)
+//        return;
 
-    // Get transformation of the instance
-    const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
-    Geometry::Transformation trafo = vol->get_instance_transformation();
+//    // Get transformation of the instance
+//    const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
+//    Geometry::Transformation trafo = vol->get_instance_transformation();
 
 
-    // Now initialize the TMS for the object, perform the cut and save the result.
-    if (! m_object_clipper) {
-        m_object_clipper.reset(new MeshClipper);
-        m_object_clipper->set_mesh(*m_mesh);
-    }
-    m_object_clipper->set_plane(*m_clipping_plane);
-    m_object_clipper->set_transformation(trafo);
+//    // Now initialize the TMS for the object, perform the cut and save the result.
+//    if (! m_object_clipper) {
+//        m_object_clipper.reset(new MeshClipper);
+//        m_object_clipper->set_mesh(*m_mesh);
+//    }
+//    m_object_clipper->set_plane(*m_clipping_plane);
+//    m_object_clipper->set_transformation(trafo);
 
-    // At this point we have the triangulated cuts for both the object and supports - let's render.
-    if (! m_object_clipper->get_triangles().empty()) {
-		::glPushMatrix();
-        ::glColor3f(1.0f, 0.37f, 0.0f);
-        ::glBegin(GL_TRIANGLES);
-        for (const Vec3f& point : m_object_clipper->get_triangles())
-            ::glVertex3f(point(0), point(1), point(2));
-        ::glEnd();
-		::glPopMatrix();
-	}
+//    // At this point we have the triangulated cuts for both the object and supports - let's render.
+//    if (! m_object_clipper->get_triangles().empty()) {
+//		::glPushMatrix();
+//        ::glColor3f(1.0f, 0.37f, 0.0f);
+//        ::glBegin(GL_TRIANGLES);
+//        for (const Vec3f& point : m_object_clipper->get_triangles())
+//            ::glVertex3f(point(0), point(1), point(2));
+//        ::glEnd();
+//		::glPopMatrix();
+//	}
 }
 
 void GLGizmoFdmSupports::render_cursor_circle() const
@@ -187,6 +193,8 @@ void GLGizmoFdmSupports::render_cursor_circle() const
 
     glsafe(::glLineWidth(1.5f));
     float color[3];
+    color[0] = 0.f;
+    color[1] = 1.f;
     color[2] = 0.3f;
     glsafe(::glColor3fv(color));
     glsafe(::glDisable(GL_DEPTH_TEST));
@@ -219,23 +227,14 @@ void GLGizmoFdmSupports::on_render_for_picking() const
 }
 
 
-
-
-bool GLGizmoFdmSupports::is_point_clipped(const Vec3d& point) const
-{
-    if (m_clipping_plane_distance == 0.f)
-        return false;
-
-    Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
-    return m_clipping_plane->distance(transformed_point) < 0.;
-}
-
-
-
 bool GLGizmoFdmSupports::is_mesh_update_necessary() const
 {
+    std::vector<ObjectID> volumes_ids;
+    for (const ModelVolume* vol : m_model_object->volumes)
+        volumes_ids.push_back(vol->id());
+
     return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
-        && ((m_model_object->id() != m_model_object_id) || m_its == nullptr);
+        && (m_model_object->id() != m_model_object_id || m_volumes_ids != volumes_ids);
 }
 
 
@@ -246,26 +245,33 @@ void GLGizmoFdmSupports::update_mesh()
         return;
 
     wxBusyCursor wait;
-    // this way we can use that mesh directly.
-    // This mesh does not account for the possible Z up SLA offset.
-    m_mesh = &m_model_object->volumes.front()->mesh();
-    m_its = &m_mesh->its;
 
-    m_selected_facets.assign(m_mesh->its.indices.size(), false);
+    size_t num_of_volumes = m_model_object->volumes.size();
+    m_meshes.clear();
+    m_selected_facets.resize(num_of_volumes);
+    m_neighbors.resize(num_of_volumes);
+    m_meshes_raycaster.clear();
 
-    // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets
-    m_neighbors.resize(3 * m_mesh->its.indices.size());
-    for (size_t i=0; i<m_mesh->its.indices.size(); ++i) {
-        const stl_triangle_vertex_indices& ind  = m_mesh->its.indices[i];
-        m_neighbors[3*i] = std::make_pair(ind(0), i);
-        m_neighbors[3*i+1] = std::make_pair(ind(1), i);
-        m_neighbors[3*i+2] = std::make_pair(ind(2), i);
+    for (size_t volume_id=0; volume_id<num_of_volumes; ++volume_id) {
+        // This mesh does not account for the possible Z up SLA offset.
+        const TriangleMesh* mesh = &m_model_object->volumes[volume_id]->mesh();
+        m_meshes.push_back(mesh);
+
+        m_selected_facets[volume_id].assign(mesh->its.indices.size(), false);
+        m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
+
+        // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets
+        for (size_t i=0; i<mesh->its.indices.size(); ++i) {
+            const stl_triangle_vertex_indices& ind  = mesh->its.indices[i];
+            m_neighbors[volume_id][3*i] = std::make_pair(ind(0), i);
+            m_neighbors[volume_id][3*i+1] = std::make_pair(ind(1), i);
+            m_neighbors[volume_id][3*i+2] = std::make_pair(ind(2), i);
+        }
+        std::sort(m_neighbors[volume_id].begin(), m_neighbors[volume_id].end());
+
+        // Recalculate raycaster.
+        m_meshes_raycaster.emplace_back(new MeshRaycaster(*mesh));
     }
-    std::sort(m_neighbors.begin(), m_neighbors.end());
-
-    // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it.
-    if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster)
-        m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh));
 
     m_model_object_id = m_model_object->id();
 }
@@ -275,22 +281,21 @@ void GLGizmoFdmSupports::update_mesh()
 // Unprojects the mouse position on the mesh and saves hit facet index into facet_idx
 // Position of the hit in mesh coords is copied into *position, if provided.
 // Returns false if no intersection was found, true otherwise.
-bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position)
+bool GLGizmoFdmSupports::unproject_on_mesh(size_t mesh_id, const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position)
 {
     // if the gizmo doesn't have the V, F structures for igl, calculate them first:
-    if (! m_mesh_raycaster)
-        update_mesh();
-
+    //if (! m_meshes_raycaster[mesh_id])
+    //    update_mesh();
     const Camera& camera = m_parent.get_camera();
     const Selection& selection = m_parent.get_selection();
-    const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
-    Geometry::Transformation trafo = volume->get_instance_transformation();
-    trafo.set_offset(trafo.get_offset());
+    const Transform3d trafo_matrix =
+            m_model_object->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
+            m_model_object->volumes[mesh_id]->get_matrix();
 
     // The raycaster query
     Vec3f hit;
     Vec3f normal;
-    if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get(), &facet_idx)) {
+    if (m_meshes_raycaster[mesh_id]->unproject_on_mesh(mouse_pos, trafo_matrix, camera, hit, normal, m_clipping_plane.get(), &facet_idx)) {
         if (position)
             *position = hit;
         return true;
@@ -330,66 +335,74 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
     if (action == SLAGizmoEventType::LeftDown || (action == SLAGizmoEventType::Dragging && m_wait_for_up_event)) {
         size_t facet = 0;
         Vec3f hit_pos;
-        if (unproject_on_mesh(mouse_position, facet, &hit_pos)) {
-            bool select = ! shift_down;
+        bool mesh_was_hit = false;
 
-            // Calculate direction from camera to the hit (in mesh coords):
-            const Selection& selection = m_parent.get_selection();
-            const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
-            Geometry::Transformation trafo = volume->get_instance_transformation();
-            trafo.set_offset(trafo.get_offset());
-            Vec3f dir = ((trafo.get_matrix().inverse() * m_parent.get_camera().get_position()).cast<float>() - hit_pos).normalized();
+        for (size_t mesh_id=0; mesh_id<m_model_object->volumes.size(); ++mesh_id) {
+            if (unproject_on_mesh(mesh_id, mouse_position, facet, &hit_pos)) {
+                mesh_was_hit = true;
+                const TriangleMesh* mesh = m_meshes[mesh_id];
+                std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
 
-            // Calculate how far can a point be from the line (in mesh coords).
-            // FIXME: This should account for (possibly non-uniform) scaling of the mesh.
-            float limit = pow(m_cursor_radius, 2.f);
+                bool select = ! shift_down;
 
+                // Calculate direction from camera to the hit (in mesh coords):
+                const Selection& selection = m_parent.get_selection();
+                const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
+                Geometry::Transformation trafo = volume->get_instance_transformation();
+                trafo.set_offset(trafo.get_offset());
+                Vec3f dir = ((trafo.get_matrix().inverse() * m_parent.get_camera().get_position()).cast<float>() - hit_pos).normalized();
 
-            // A lambda to calculate distance from the line:
-            auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float {
-                Vec3f diff = hit_pos - point;
-                return (diff - diff.dot(dir) * dir).squaredNorm();
-            };
+                // Calculate how far can a point be from the line (in mesh coords).
+                // FIXME: This should account for (possibly non-uniform) scaling of the mesh.
+                float limit = pow(m_cursor_radius, 2.f);
 
-            // A lambda to determine whether this facet is potentionally visible (still can be obscured)
-            auto faces_camera = [&dir, this](const size_t& facet) -> bool {
-                return (m_mesh->stl.facet_start[facet].normal.dot(dir) > 0.);
-            };
+                // A lambda to calculate distance from the line:
+                auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float {
+                    Vec3f diff = hit_pos - point;
+                    return (diff - diff.dot(dir) * dir).squaredNorm();
+                };
 
-            // Now start with the facet the pointer points to and check all adjacent facets. m_neighbors vector stores
-            // pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
-            // quickly found by finding a vertex in the list and read the respective facet ids.
-            std::vector<size_t> facets_to_select{facet};
-            NeighborData vertex = std::make_pair(0, 0);
-            std::vector<bool> visited(m_selected_facets.size(), false); // keep track of facets we already processed
-            size_t facet_idx = 0; // index into facets_to_select
-            auto it = m_neighbors.end();
-
-            while (facet_idx < facets_to_select.size()) {
-                size_t facet = facets_to_select[facet_idx];
-                if (! visited[facet]) {
-                    // check all three vertices and in case they're close enough, find the remaining facets
-                    // and add them to the list to be proccessed later
-                    for (size_t i=0; i<3; ++i) {
-                        vertex.first = m_mesh->its.indices[facet](i); // vertex index
-                        float dist = squared_distance_from_line(m_mesh->its.vertices[vertex.first]);
-                        if (dist < limit) {
-                            it = std::lower_bound(m_neighbors.begin(), m_neighbors.end(), vertex);
-                            while (it != m_neighbors.end() && it->first == vertex.first) {
-                                if (it->second != facet && faces_camera(it->second))
-                                    facets_to_select.push_back(it->second);
-                                ++it;
+                // A lambda to determine whether this facet is potentionally visible (still can be obscured)
+                auto faces_camera = [&dir, this](const size_t& mesh_id, const size_t& facet) -> bool {
+                    return (m_meshes[mesh_id]->stl.facet_start[facet].normal.dot(dir) > 0.);
+                };
+                // Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores
+                // pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
+                // quickly found by finding a vertex in the list and read the respective facet ids.
+                std::vector<size_t> facets_to_select{facet};
+                NeighborData vertex = std::make_pair(0, 0);
+                std::vector<bool> visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed
+                size_t facet_idx = 0; // index into facets_to_select
+                auto it = neighbors.end();
+                while (facet_idx < facets_to_select.size()) {
+                    size_t facet = facets_to_select[facet_idx];
+                    if (! visited[facet]) {
+                        // check all three vertices and in case they're close enough, find the remaining facets
+                        // and add them to the list to be proccessed later
+                        for (size_t i=0; i<3; ++i) {
+                            vertex.first = mesh->its.indices[facet](i); // vertex index
+                            float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]);
+                            if (dist < limit) {
+                                it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex);
+                                while (it != neighbors.end() && it->first == vertex.first) {
+                                    if (it->second != facet && faces_camera(mesh_id, it->second))
+                                        facets_to_select.push_back(it->second);
+                                    ++it;
+                                }
                             }
                         }
+                        visited[facet] = true;
                     }
-                    visited[facet] = true;
+                    ++facet_idx;
                 }
-                ++facet_idx;
+                // Now just select all facets that passed
+                for (size_t next_facet : facets_to_select)
+                    m_selected_facets[mesh_id][next_facet] = select;
             }
-            // Now just select all facets that passed
-            for (size_t next_facet : facets_to_select)
-                m_selected_facets[next_facet] = select;
+        }
 
+        if (mesh_was_hit)
+        {
             m_wait_for_up_event = true;
             m_parent.set_as_dirty();
             return true;
@@ -529,9 +542,8 @@ void GLGizmoFdmSupports::on_set_state()
         m_parent.toggle_model_objects_visibility(true);
         m_clipping_plane_distance = 0.f;
         // Release clippers and the AABB raycaster.
-        m_its = nullptr;
-        m_object_clipper.reset();
-        m_mesh_raycaster.reset();
+        m_meshes_clipper.clear();
+        m_meshes_raycaster.clear();
     }
     m_old_state = m_state;
 }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
index fadb993c8..104823e5c 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
@@ -19,20 +19,20 @@ class GLGizmoFdmSupports : public GLGizmoBase
 private:
     ModelObject* m_model_object = nullptr;
     ObjectID m_model_object_id = 0;
+    std::vector<ObjectID> m_volumes_ids;
     int m_active_instance = -1;
     float m_active_instance_bb_radius; // to cache the bb
-    bool unproject_on_mesh(const Vec2d& mouse_pos,  size_t& facet_idx, Vec3f* position = nullptr);
+    bool unproject_on_mesh(size_t mesh_id, const Vec2d& mouse_pos,  size_t& facet_idx, Vec3f* position = nullptr);
 
 
     GLUquadricObj* m_quadric;
 
-    std::unique_ptr<MeshRaycaster> m_mesh_raycaster;
-    const TriangleMesh* m_mesh;
-    const indexed_triangle_set* m_its;
+    std::vector<std::unique_ptr<MeshRaycaster>> m_meshes_raycaster;
+    std::vector<const TriangleMesh*> m_meshes;
     mutable std::vector<Vec2f> m_triangles;
     float m_cursor_radius = 2.f;
 
-    std::vector<bool> m_selected_facets;
+    std::vector<std::vector<bool>> m_selected_facets;
 
 public:
     GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@@ -64,12 +64,10 @@ private:
     bool m_wait_for_up_event = false;
     EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
 
-    mutable std::unique_ptr<MeshClipper> m_object_clipper;
+    mutable std::vector<std::unique_ptr<MeshClipper>> m_meshes_clipper;
 
-    std::vector<NeighborData> m_neighbors; // pairs of vertex_index - facet_index
+    std::vector<std::vector<NeighborData>> m_neighbors; // pairs of vertex_index - facet_index for each mesh
 
-
-    bool is_point_clipped(const Vec3d& point) const;
     void update_clipping_plane(bool keep_normal = false) const;
 
 protected:
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
index 9d37b40ae..92f444f55 100644
--- a/src/slic3r/GUI/MeshUtils.hpp
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -28,10 +28,7 @@ class ClippingPlane
 public:
     ClippingPlane()
     {
-        m_data[0] = 0.0;
-        m_data[1] = 0.0;
-        m_data[2] = 1.0;
-        m_data[3] = 0.0;
+        *this = ClipsNothing();
     }
 
     ClippingPlane(const Vec3d& direction, double offset)