mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Working hole drilling one by one without linear slowdown.
This commit is contained in:
		
							parent
							
								
									6059d89bc8
								
							
						
					
					
						commit
						3d0d96d8f9
					
				
					 4 changed files with 199 additions and 32 deletions
				
			
		|  | @ -11,6 +11,8 @@ | |||
| #include <type_traits> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <Eigen/Geometry> | ||||
| 
 | ||||
| #include "Utils.hpp" // for next_highest_power_of_2()
 | ||||
| 
 | ||||
| extern "C" | ||||
|  | @ -752,6 +754,83 @@ void get_candidate_idxs(const TreeType& tree, const VectorType& v, std::vector<s | |||
|     return; | ||||
| } | ||||
| 
 | ||||
| // Predicate: need to be specialized for intersections of different geomteries
 | ||||
| template<class G> struct Intersecting {}; | ||||
| 
 | ||||
| // Intersection predicate specialization for box-box intersections
 | ||||
| template<class CoordType, int NumD> | ||||
| struct Intersecting<Eigen::AlignedBox<CoordType, NumD>> { | ||||
|     Eigen::AlignedBox<CoordType, NumD> box; | ||||
| 
 | ||||
|     Intersecting(const Eigen::AlignedBox<CoordType, NumD> &bb): box{bb} {} | ||||
| 
 | ||||
|     bool operator() (const typename Tree<NumD, CoordType>::Node &node) const | ||||
|     { | ||||
|         return box.intersects(node.bbox); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<class G> auto intersecting(const G &g) { return Intersecting<G>{g}; } | ||||
| 
 | ||||
| template<class G> struct Containing {}; | ||||
| 
 | ||||
| // Intersection predicate specialization for box-box intersections
 | ||||
| template<class CoordType, int NumD> | ||||
| struct Containing<Eigen::AlignedBox<CoordType, NumD>> { | ||||
|     Eigen::AlignedBox<CoordType, NumD> box; | ||||
| 
 | ||||
|     Containing(const Eigen::AlignedBox<CoordType, NumD> &bb): box{bb} {} | ||||
| 
 | ||||
|     bool operator() (const typename Tree<NumD, CoordType>::Node &node) const | ||||
|     { | ||||
|         return box.contains(node.bbox); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<class G> auto containing(const G &g) { return Containing<G>{g}; } | ||||
| 
 | ||||
| namespace detail { | ||||
| 
 | ||||
| template<int Dims, typename T, typename Pred, typename Fn> | ||||
| void traverse_recurse(const Tree<Dims, T> &tree, | ||||
|                       size_t               idx, | ||||
|                       Pred &&              pred, | ||||
|                       Fn &&                callback) | ||||
| { | ||||
|     assert(tree.node(idx).is_valid()); | ||||
| 
 | ||||
|     if (!pred(tree.node(idx))) return; | ||||
| 
 | ||||
|     if (tree.node(idx).is_leaf()) { | ||||
|         callback(tree.node(idx).idx); | ||||
|     } else { | ||||
| 
 | ||||
|         // call this with left and right node idx:
 | ||||
|         auto trv = [&](size_t idx) { | ||||
|             traverse_recurse(tree, idx, std::forward<Pred>(pred), | ||||
|                              std::forward<Fn>(callback)); | ||||
|         }; | ||||
| 
 | ||||
|         // Left / right child node index.
 | ||||
|         trv(Tree<Dims, T>::left_child_idx(idx)); | ||||
|         trv(Tree<Dims, T>::right_child_idx(idx)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace detail
 | ||||
| 
 | ||||
| // Tree traversal with a predicate. Example usage:
 | ||||
| // traverse(tree, intersecting(QueryBox), [](size_t face_idx) {
 | ||||
| //      /* ... */
 | ||||
| // });
 | ||||
| template<int Dims, typename T, typename Predicate, typename Fn> | ||||
| void traverse(const Tree<Dims, T> &tree, Predicate &&pred, Fn &&callback) | ||||
| { | ||||
|     if (tree.empty()) return; | ||||
| 
 | ||||
|     detail::traverse_recurse(tree, size_t(0), std::forward<Predicate>(pred), | ||||
|                              std::forward<Fn>(callback)); | ||||
| } | ||||
| 
 | ||||
| } // namespace AABBTreeIndirect
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -110,15 +110,18 @@ struct CGALMesh { _EpicMesh m; }; | |||
| // Converions from and to CGAL mesh
 | ||||
| // /////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out) | ||||
| {  | ||||
|     if (M.empty()) return; | ||||
| template<class _Mesh> | ||||
| void triangle_mesh_to_cgal(const std::vector<stl_vertex> &                 V, | ||||
|                            const std::vector<stl_triangle_vertex_indices> &F, | ||||
|                            _Mesh &out) | ||||
| { | ||||
|     if (F.empty()) return; | ||||
| 
 | ||||
|     for (auto &v : M.its.vertices) | ||||
|     for (auto &v : V) | ||||
|         out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()}); | ||||
| 
 | ||||
|     using VI = typename _Mesh::Vertex_index; | ||||
|     for (auto &f : M.its.indices) | ||||
|     for (auto &f : F) | ||||
|         out.add_face(VI(f(0)), VI(f(1)), VI(f(2))); | ||||
| } | ||||
| 
 | ||||
|  | @ -155,14 +158,16 @@ template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh) | |||
|     } | ||||
|      | ||||
|     TriangleMesh out{points, facets}; | ||||
|     out.require_shared_vertices(); | ||||
|     out.repair(); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M) | ||||
| std::unique_ptr<CGALMesh, CGALMeshDeleter> | ||||
| triangle_mesh_to_cgal(const std::vector<stl_vertex> &V, | ||||
|                       const std::vector<stl_triangle_vertex_indices> &F) | ||||
| { | ||||
|     std::unique_ptr<CGALMesh, CGALMeshDeleter> out(new CGALMesh{}); | ||||
|     triangle_mesh_to_cgal(M, out->m); | ||||
|     triangle_mesh_to_cgal(V, F, out->m); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
|  | @ -221,8 +226,8 @@ template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl | |||
| { | ||||
|     CGALMesh meshA; | ||||
|     CGALMesh meshB; | ||||
|     triangle_mesh_to_cgal(A, meshA.m); | ||||
|     triangle_mesh_to_cgal(B, meshB.m); | ||||
|     triangle_mesh_to_cgal(A.its.vertices, A.its.indices, meshA.m); | ||||
|     triangle_mesh_to_cgal(B.its.vertices, B.its.indices, meshB.m); | ||||
|      | ||||
|     _cgal_do(op, meshA, meshB); | ||||
|      | ||||
|  | @ -247,7 +252,7 @@ void intersect(TriangleMesh &A, const TriangleMesh &B) | |||
| bool does_self_intersect(const TriangleMesh &mesh) | ||||
| { | ||||
|     CGALMesh cgalm; | ||||
|     triangle_mesh_to_cgal(mesh, cgalm.m); | ||||
|     triangle_mesh_to_cgal(mesh.its.vertices, mesh.its.indices, cgalm.m); | ||||
|     return CGALProc::does_self_intersect(cgalm.m); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,7 +27,19 @@ namespace cgal { | |||
| struct CGALMesh; | ||||
| struct CGALMeshDeleter { void operator()(CGALMesh *ptr); }; | ||||
| 
 | ||||
| std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M); | ||||
| std::unique_ptr<CGALMesh, CGALMeshDeleter> | ||||
| triangle_mesh_to_cgal(const std::vector<stl_vertex> &V, | ||||
|                       const std::vector<stl_triangle_vertex_indices> &F); | ||||
| 
 | ||||
| inline std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const indexed_triangle_set &M) | ||||
| { | ||||
|     return triangle_mesh_to_cgal(M.vertices, M.indices); | ||||
| } | ||||
| inline std::unique_ptr<CGALMesh, CGALMeshDeleter> triangle_mesh_to_cgal(const TriangleMesh &M) | ||||
| { | ||||
|     return triangle_mesh_to_cgal(M.its); | ||||
| } | ||||
| 
 | ||||
| TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh); | ||||
|      | ||||
| // Do boolean mesh difference with CGAL bypassing igl.
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| #include <libslic3r/SLA/SupportPointGenerator.hpp> | ||||
| 
 | ||||
| #include <libslic3r/ElephantFootCompensation.hpp> | ||||
| #include <libslic3r/AABBTreeIndirect.hpp> | ||||
| 
 | ||||
| #include <libslic3r/ClipperUtils.hpp> | ||||
| 
 | ||||
|  | @ -272,6 +273,36 @@ static std::vector<bool> create_exclude_mask( | |||
|     return exclude_mask; | ||||
| } | ||||
| 
 | ||||
| static indexed_triangle_set | ||||
| remove_unconnected_vertices(const indexed_triangle_set &its) | ||||
| { | ||||
|     if (its.indices.empty()) {}; | ||||
| 
 | ||||
|     indexed_triangle_set M; | ||||
| 
 | ||||
|     std::vector<int> vtransl(its.vertices.size(), -1); | ||||
|     int vcnt = 0; | ||||
|     for (auto &f : its.indices) { | ||||
| 
 | ||||
|         for (int i = 0; i < 3; ++i) | ||||
|             if (vtransl[size_t(f(i))] < 0) { | ||||
| 
 | ||||
|                 M.vertices.emplace_back(its.vertices[size_t(f(i))]); | ||||
|                 vtransl[size_t(f(i))] = vcnt++; | ||||
|             } | ||||
| 
 | ||||
|         std::array<int, 3> new_f = { | ||||
|             vtransl[size_t(f(0))], | ||||
|             vtransl[size_t(f(1))], | ||||
|             vtransl[size_t(f(2))] | ||||
|         }; | ||||
| 
 | ||||
|         M.indices.emplace_back(new_f[0], new_f[1], new_f[2]); | ||||
|     } | ||||
| 
 | ||||
|     return M; | ||||
| } | ||||
| 
 | ||||
| // Drill holes into the hollowed/original mesh.
 | ||||
| void SLAPrint::Steps::drill_holes(SLAPrintObject &po) | ||||
| { | ||||
|  | @ -314,43 +345,83 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) | |||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; | ||||
|     sla::DrainHoles drainholes = po.transformed_drainhole_points(); | ||||
|      | ||||
|     auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); | ||||
| 
 | ||||
|     auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( | ||||
|         hollowed_mesh.its.vertices, | ||||
|         hollowed_mesh.its.indices | ||||
|     ); | ||||
| 
 | ||||
|     std::uniform_real_distribution<float> dist(0., float(EPSILON)); | ||||
|     auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}, {}); | ||||
|     indexed_triangle_set part_to_drill = hollowed_mesh.its; | ||||
| 
 | ||||
|     bool hole_fail = false; | ||||
|     for (size_t i = 0; i < drainholes.size(); ++i) { | ||||
|         const sla::DrainHole &holept = drainholes[i]; | ||||
|         po.model_object()->sla_drain_holes[i].failed = false; | ||||
|         sla::DrainHole holept = drainholes[i]; | ||||
| 
 | ||||
|         holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; | ||||
|         holept.normal.normalize(); | ||||
|         holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; | ||||
|         TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh()); | ||||
|         m.require_shared_vertices(); | ||||
|         auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m); | ||||
| 
 | ||||
|         try { | ||||
|             MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *cgal_m); | ||||
|         } catch (const std::runtime_error &) { | ||||
|         part_to_drill.indices.clear(); | ||||
|         auto bb = m.bounding_box(); | ||||
|         Eigen::AlignedBox<float, 3> ebb{bb.min.cast<float>(), | ||||
|                                         bb.max.cast<float>()}; | ||||
| 
 | ||||
|         AABBTreeIndirect::traverse( | ||||
|                     tree, | ||||
|                     AABBTreeIndirect::intersecting(ebb), | ||||
|                     [&part_to_drill, &hollowed_mesh](size_t faceid) | ||||
|         { | ||||
|             part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]); | ||||
|         }); | ||||
| 
 | ||||
|         auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal( | ||||
|             remove_unconnected_vertices(part_to_drill)); | ||||
| 
 | ||||
|         if (MeshBoolean::cgal::does_self_intersect(*cgal_meshpart)) { | ||||
|             BOOST_LOG_TRIVIAL(error) << "Failed to drill hole"; | ||||
| 
 | ||||
|             hole_fail = drainholes[i].failed = | ||||
|                     po.model_object()->sla_drain_holes[i].failed = true; | ||||
| 
 | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         auto cgal_hole = MeshBoolean::cgal::triangle_mesh_to_cgal(m); | ||||
|         MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_hole); | ||||
|     } | ||||
| 
 | ||||
|     if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) | ||||
|         throw Slic3r::SlicingError(L("Too many overlapping holes.")); | ||||
| 
 | ||||
|     auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); | ||||
| 
 | ||||
|     try { | ||||
|         MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); | ||||
| 
 | ||||
|         hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); | ||||
|         mesh_view = hollowed_mesh; | ||||
| 
 | ||||
|         if (is_hollowed) { | ||||
|             auto &interior = *po.m_hollowing_data->interior; | ||||
|             std::vector<bool> exclude_mask = | ||||
|                     create_exclude_mask(mesh_view.its, interior, drainholes); | ||||
| 
 | ||||
|             sla::remove_inside_triangles(mesh_view, interior, exclude_mask); | ||||
|         } | ||||
| 
 | ||||
|     } catch (const std::runtime_error &) { | ||||
|         throw Slic3r::SlicingError(L( | ||||
|             "Drilling holes into the mesh failed. " | ||||
|             "This is usually caused by broken model. Try to fix it first.")); | ||||
|     } | ||||
| 
 | ||||
|     if (hole_fail) | ||||
|         po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, | ||||
|                                    L("Failed to drill some holes into the model")); | ||||
| 
 | ||||
| 
 | ||||
|     hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); | ||||
|     mesh_view     = hollowed_mesh; | ||||
| 
 | ||||
|     if (is_hollowed) { | ||||
|         auto &interior = *po.m_hollowing_data->interior; | ||||
|         std::vector<bool> exclude_mask = | ||||
|                 create_exclude_mask(mesh_view.its, interior, drainholes); | ||||
| 
 | ||||
|         sla::remove_inside_triangles(mesh_view, interior, exclude_mask); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // The slicing will be performed on an imaginary 1D grid which starts from
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros