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 <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <Eigen/Geometry>
|
||||||
|
|
||||||
#include "Utils.hpp" // for next_highest_power_of_2()
|
#include "Utils.hpp" // for next_highest_power_of_2()
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
@ -752,6 +754,83 @@ void get_candidate_idxs(const TreeType& tree, const VectorType& v, std::vector<s
|
||||||
return;
|
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 AABBTreeIndirect
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -110,15 +110,18 @@ struct CGALMesh { _EpicMesh m; };
|
||||||
// Converions from and to CGAL mesh
|
// Converions from and to CGAL mesh
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template<class _Mesh> void triangle_mesh_to_cgal(const TriangleMesh &M, _Mesh &out)
|
template<class _Mesh>
|
||||||
{
|
void triangle_mesh_to_cgal(const std::vector<stl_vertex> & V,
|
||||||
if (M.empty()) return;
|
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()});
|
out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()});
|
||||||
|
|
||||||
using VI = typename _Mesh::Vertex_index;
|
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)));
|
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};
|
TriangleMesh out{points, facets};
|
||||||
out.require_shared_vertices();
|
out.repair();
|
||||||
return out;
|
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{});
|
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;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,8 +226,8 @@ template<class Op> void _mesh_boolean_do(Op &&op, TriangleMesh &A, const Triangl
|
||||||
{
|
{
|
||||||
CGALMesh meshA;
|
CGALMesh meshA;
|
||||||
CGALMesh meshB;
|
CGALMesh meshB;
|
||||||
triangle_mesh_to_cgal(A, meshA.m);
|
triangle_mesh_to_cgal(A.its.vertices, A.its.indices, meshA.m);
|
||||||
triangle_mesh_to_cgal(B, meshB.m);
|
triangle_mesh_to_cgal(B.its.vertices, B.its.indices, meshB.m);
|
||||||
|
|
||||||
_cgal_do(op, meshA, meshB);
|
_cgal_do(op, meshA, meshB);
|
||||||
|
|
||||||
|
@ -247,7 +252,7 @@ void intersect(TriangleMesh &A, const TriangleMesh &B)
|
||||||
bool does_self_intersect(const TriangleMesh &mesh)
|
bool does_self_intersect(const TriangleMesh &mesh)
|
||||||
{
|
{
|
||||||
CGALMesh cgalm;
|
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);
|
return CGALProc::does_self_intersect(cgalm.m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,19 @@ namespace cgal {
|
||||||
struct CGALMesh;
|
struct CGALMesh;
|
||||||
struct CGALMeshDeleter { void operator()(CGALMesh *ptr); };
|
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);
|
TriangleMesh cgal_to_triangle_mesh(const CGALMesh &cgalmesh);
|
||||||
|
|
||||||
// Do boolean mesh difference with CGAL bypassing igl.
|
// Do boolean mesh difference with CGAL bypassing igl.
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <libslic3r/SLA/SupportPointGenerator.hpp>
|
#include <libslic3r/SLA/SupportPointGenerator.hpp>
|
||||||
|
|
||||||
#include <libslic3r/ElephantFootCompensation.hpp>
|
#include <libslic3r/ElephantFootCompensation.hpp>
|
||||||
|
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||||
|
|
||||||
#include <libslic3r/ClipperUtils.hpp>
|
#include <libslic3r/ClipperUtils.hpp>
|
||||||
|
|
||||||
|
@ -272,6 +273,36 @@ static std::vector<bool> create_exclude_mask(
|
||||||
return 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.
|
// Drill holes into the hollowed/original mesh.
|
||||||
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
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.";
|
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
|
||||||
sla::DrainHoles drainholes = po.transformed_drainhole_points();
|
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;
|
bool hole_fail = false;
|
||||||
for (size_t i = 0; i < drainholes.size(); ++i) {
|
for (size_t i = 0; i < drainholes.size(); ++i) {
|
||||||
const sla::DrainHole &holept = drainholes[i];
|
sla::DrainHole holept = drainholes[i];
|
||||||
po.model_object()->sla_drain_holes[i].failed = false;
|
|
||||||
|
|
||||||
|
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());
|
TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh());
|
||||||
m.require_shared_vertices();
|
m.require_shared_vertices();
|
||||||
auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
|
|
||||||
|
|
||||||
try {
|
part_to_drill.indices.clear();
|
||||||
MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *cgal_m);
|
auto bb = m.bounding_box();
|
||||||
} catch (const std::runtime_error &) {
|
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";
|
BOOST_LOG_TRIVIAL(error) << "Failed to drill hole";
|
||||||
|
|
||||||
hole_fail = drainholes[i].failed =
|
hole_fail = drainholes[i].failed =
|
||||||
po.model_object()->sla_drain_holes[i].failed = true;
|
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)
|
if (hole_fail)
|
||||||
po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
|
po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
|
||||||
L("Failed to drill some holes into the model"));
|
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
|
// The slicing will be performed on an imaginary 1D grid which starts from
|
||||||
|
|
Loading…
Reference in a new issue