diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index b11c570f6..76aa36194 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -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 diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 4b18d6c1c..5a518d580 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -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); } diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index ce17a1328..8e88e2536 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -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. diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4d8abb798..fc6686201 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -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