From 7fa430c56defa04a24b75eb7fab72b9c9c10b828 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 14 Jan 2019 17:28:02 +0100 Subject: [PATCH 01/12] Adding an AABB tree to EigenMesh3D. Yet to be used. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 4 +- src/libslic3r/SLA/SLARotfinder.cpp | 12 +- src/libslic3r/SLA/SLASupportTree.cpp | 74 +++-------- src/libslic3r/SLA/SLASupportTree.hpp | 34 ++++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 166 +++++++++++++++--------- src/libslic3r/SLAPrint.cpp | 2 +- 6 files changed, 155 insertions(+), 137 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index ee87c6b66..98313be3f 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -15,7 +15,7 @@ namespace Slic3r { SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, const Config& config, std::function throw_on_cancel) -: m_config(config), m_V(emesh.V), m_F(emesh.F), m_throw_on_cancel(throw_on_cancel) +: m_config(config), m_V(emesh.V()), m_F(emesh.F()), m_throw_on_cancel(throw_on_cancel) { // FIXME: It might be safer to get rid of the rand() calls altogether, because it is probably // not always thread-safe and can be slow if it is. @@ -332,4 +332,4 @@ void SLAAutoSupports::project_upward_onto_mesh(std::vector& points) const } -} // namespace Slic3r \ No newline at end of file +} // namespace Slic3r diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp index e66e26706..1a91041b7 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -28,7 +28,7 @@ std::array find_best_rotation(const ModelObject& modelobj, // We will use only one instance of this converted mesh to examine different // rotations - EigenMesh3D emesh = to_eigenmesh(modelobj); + EigenMesh3D emesh(modelobj.raw_mesh()); // For current iteration number unsigned status = 0; @@ -68,12 +68,12 @@ std::array find_best_rotation(const ModelObject& modelobj, // area. The current function is only an example of how to optimize. // Later we can add more criteria like the number of overhangs, etc... - for(int i = 0; i < m.F.rows(); i++) { - auto idx = m.F.row(i); + for(int i = 0; i < m.F().rows(); i++) { + auto idx = m.F().row(i); - Vec3d p1 = m.V.row(idx(0)); - Vec3d p2 = m.V.row(idx(1)); - Vec3d p3 = m.V.row(idx(2)); + Vec3d p1 = m.V().row(idx(0)); + Vec3d p2 = m.V().row(idx(1)); + Vec3d p3 = m.V().row(idx(2)); Eigen::Vector3d U = p2 - p1; Eigen::Vector3d V = p3 - p1; diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index c2fcb3c3a..a105f3e4b 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -539,23 +539,6 @@ struct Pad { bool empty() const { return tmesh.facets_count() == 0; } }; -EigenMesh3D to_eigenmesh(const Contour3D& cntr) { - EigenMesh3D emesh; - - auto& V = emesh.V; - auto& F = emesh.F; - - V.resize(Eigen::Index(cntr.points.size()), 3); - F.resize(Eigen::Index(cntr.indices.size()), 3); - - for (int i = 0; i < V.rows(); ++i) { - V.row(i) = cntr.points[size_t(i)]; - F.row(i) = cntr.indices[size_t(i)]; - } - - return emesh; -} - // The minimum distance for two support points to remain valid. static const double /*constexpr*/ D_SP = 0.1; @@ -563,46 +546,6 @@ enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers X, Y, Z }; -EigenMesh3D to_eigenmesh(const TriangleMesh& tmesh) { - - const stl_file& stl = tmesh.stl; - - EigenMesh3D outmesh; - - auto&& bb = tmesh.bounding_box(); - outmesh.ground_level += bb.min(Z); - - auto& V = outmesh.V; - auto& F = outmesh.F; - - V.resize(3*stl.stats.number_of_facets, 3); - F.resize(stl.stats.number_of_facets, 3); - for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { - const stl_facet* facet = stl.facet_start+i; - V(3*i+0, 0) = double(facet->vertex[0](0)); - V(3*i+0, 1) = double(facet->vertex[0](1)); - V(3*i+0, 2) = double(facet->vertex[0](2)); - - V(3*i+1, 0) = double(facet->vertex[1](0)); - V(3*i+1, 1) = double(facet->vertex[1](1)); - V(3*i+1, 2) = double(facet->vertex[1](2)); - - V(3*i+2, 0) = double(facet->vertex[2](0)); - V(3*i+2, 1) = double(facet->vertex[2](1)); - V(3*i+2, 2) = double(facet->vertex[2](2)); - - F(i, 0) = int(3*i+0); - F(i, 1) = int(3*i+1); - F(i, 2) = int(3*i+2); - } - - return outmesh; -} - -EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { - return to_eigenmesh(modelobj.raw_mesh()); -} - PointSet to_point_set(const std::vector &v) { PointSet ret(v.size(), 3); @@ -618,6 +561,21 @@ double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); +double pinhead_mesh_intersect(const Vec3d& jp, + const Vec3d& dir, + double r1, + double r2, + const EigenMesh3D& m); + +// Wrapper only +inline double pinhead_mesh_intersect(const Head& head, const EigenMesh3D& m) { + return pinhead_mesh_intersect(head.junction_point(), + head.dir, + head.r_pin_mm, + head.r_back_mm, + m); +} + PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}); @@ -1789,7 +1747,7 @@ SLASupportTree::SLASupportTree(const PointSet &points, const Controller &ctl): m_impl(new Impl(ctl)) { - m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm; + m_impl->ground_level = emesh.ground_level() - cfg.object_elevation_mm; generate(points, emesh, cfg, ctl); } diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 5a86d4623..80f0bd68b 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -104,18 +104,40 @@ struct Controller { /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree -struct EigenMesh3D { - Eigen::MatrixXd V; - Eigen::MatrixXi F; - double ground_level = 0; +class EigenMesh3D { + class AABBImpl; + + Eigen::MatrixXd m_V; + Eigen::MatrixXi m_F; + double m_ground_level = 0; + + std::unique_ptr m_aabb; +public: + + EigenMesh3D(); + EigenMesh3D(const TriangleMesh&); + + ~EigenMesh3D(); + + EigenMesh3D(const EigenMesh3D& other); +// EigenMesh3D(EigenMesh3D&&) = default; + EigenMesh3D& operator=(const EigenMesh3D&); +// EigenMesh3D& operator=(EigenMesh3D&&) = default; + + inline double ground_level() const { return m_ground_level; } + + inline const Eigen::MatrixXd& V() const { return m_V; } + inline const Eigen::MatrixXi& F() const { return m_F; } + + double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; }; using PointSet = Eigen::MatrixXd; -EigenMesh3D to_eigenmesh(const TriangleMesh& m); +//EigenMesh3D to_eigenmesh(const TriangleMesh& m); // needed for find best rotation -EigenMesh3D to_eigenmesh(const ModelObject& model); +//EigenMesh3D to_eigenmesh(const ModelObject& model); // Simple conversion of 'vector of points' to an Eigen matrix PointSet to_point_set(const std::vector&); diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 0cc9f14e0..cacd1ceaa 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -19,6 +19,10 @@ namespace Slic3r { namespace sla { +/* ************************************************************************** + * SpatIndex implementation + * ************************************************************************** */ + class SpatIndex::Impl { public: using BoostIndex = boost::geometry::index::rtree< SpatElement, @@ -78,6 +82,73 @@ size_t SpatIndex::size() const return m_impl->m_store.size(); } +/* **************************************************************************** + * EigenMesh3D implementation + * ****************************************************************************/ + +class EigenMesh3D::AABBImpl: public igl::AABB {}; + +EigenMesh3D::EigenMesh3D(): m_aabb(new AABBImpl()) {} + +EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { + static const double dEPS = 1e-6; + + const stl_file& stl = tmesh.stl; + + auto&& bb = tmesh.bounding_box(); + m_ground_level += bb.min(Z); + + Eigen::MatrixXd V; + Eigen::MatrixXi F; + + V.resize(3*stl.stats.number_of_facets, 3); + F.resize(stl.stats.number_of_facets, 3); + for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { + const stl_facet* facet = stl.facet_start+i; + V(3*i+0, 0) = double(facet->vertex[0](0)); + V(3*i+0, 1) = double(facet->vertex[0](1)); + V(3*i+0, 2) = double(facet->vertex[0](2)); + + V(3*i+1, 0) = double(facet->vertex[1](0)); + V(3*i+1, 1) = double(facet->vertex[1](1)); + V(3*i+1, 2) = double(facet->vertex[1](2)); + + V(3*i+2, 0) = double(facet->vertex[2](0)); + V(3*i+2, 1) = double(facet->vertex[2](1)); + V(3*i+2, 2) = double(facet->vertex[2](2)); + + F(i, 0) = int(3*i+0); + F(i, 1) = int(3*i+1); + F(i, 2) = int(3*i+2); + } + + // We will convert this to a proper 3d mesh with no duplicate points. + Eigen::VectorXi SVI, SVJ; + igl::remove_duplicate_vertices(V, F, dEPS, m_V, SVI, SVJ, m_F); + + // Build the AABB accelaration tree + m_aabb->init(m_V, m_F); +} + +EigenMesh3D::~EigenMesh3D() {} + +EigenMesh3D::EigenMesh3D(const EigenMesh3D &other): + m_V(other.m_V), m_F(other.m_F), m_ground_level(other.m_ground_level), + m_aabb( new AABBImpl(*other.m_aabb) ) {} + +EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) +{ + m_V = other.m_V; + m_F = other.m_F; + m_ground_level = other.m_ground_level; + m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; +} + +double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +{ + return 0; +} + bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, double eps = 0.05) { @@ -93,10 +164,10 @@ template double distance(const Vec& pp1, const Vec& pp2) { return std::sqrt(p.transpose() * p); } -PointSet normals(const PointSet& points, const EigenMesh3D& emesh, +PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps, std::function throw_on_cancel) { - if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0) + if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) return {}; Eigen::VectorXd dists; @@ -105,23 +176,24 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // We need to remove duplicate vertices and have a true index triangle // structure + /* EigenMesh3D mesh; Eigen::VectorXi SVI, SVJ; static const double dEPS = 1e-6; igl::remove_duplicate_vertices(emesh.V, emesh.F, dEPS, - mesh.V, SVI, SVJ, mesh.F); + mesh.V, SVI, SVJ, mesh.F);*/ - igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); + igl::point_mesh_squared_distance( points, mesh.V(), mesh.F(), dists, I, C); PointSet ret(I.rows(), 3); for(int i = 0; i < I.rows(); i++) { throw_on_cancel(); auto idx = I(i); - auto trindex = mesh.F.row(idx); + auto trindex = mesh.F().row(idx); - const Vec3d& p1 = mesh.V.row(trindex(0)); - const Vec3d& p2 = mesh.V.row(trindex(1)); - const Vec3d& p3 = mesh.V.row(trindex(2)); + const Vec3d& p1 = mesh.V().row(trindex(0)); + const Vec3d& p2 = mesh.V().row(trindex(1)); + const Vec3d& p3 = mesh.V().row(trindex(2)); // We should check if the point lies on an edge of the hosting triangle. // If it does than all the other triangles using the same two points @@ -159,18 +231,18 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // vector for the neigboring triangles including the detected one. std::vector neigh; if(ic >= 0) { // The point is right on a vertex of the triangle - for(int n = 0; n < mesh.F.rows(); ++n) { + for(int n = 0; n < mesh.F().rows(); ++n) { throw_on_cancel(); - Vec3i ni = mesh.F.row(n); + Vec3i ni = mesh.F().row(n); if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) neigh.emplace_back(ni); } } else if(ia >= 0 && ib >= 0) { // the point is on and edge // now get all the neigboring triangles - for(int n = 0; n < mesh.F.rows(); ++n) { + for(int n = 0; n < mesh.F().rows(); ++n) { throw_on_cancel(); - Vec3i ni = mesh.F.row(n); + Vec3i ni = mesh.F().row(n); if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) neigh.emplace_back(ni); @@ -180,9 +252,9 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // Calculate the normals for the neighboring triangles std::vector neighnorms; neighnorms.reserve(neigh.size()); for(const Vec3i& tri : neigh) { - const Vec3d& pt1 = mesh.V.row(tri(0)); - const Vec3d& pt2 = mesh.V.row(tri(1)); - const Vec3d& pt3 = mesh.V.row(tri(2)); + const Vec3d& pt1 = mesh.V().row(tri(0)); + const Vec3d& pt2 = mesh.V().row(tri(1)); + const Vec3d& pt3 = mesh.V().row(tri(2)); Eigen::Vector3d U = pt2 - pt1; Eigen::Vector3d V = pt3 - pt1; neighnorms.emplace_back(U.cross(V).normalized()); @@ -228,10 +300,24 @@ double ray_mesh_intersect(const Vec3d& s, { igl::Hit hit; hit.t = std::numeric_limits::infinity(); - igl::ray_mesh_intersect(s, dir, m.V, m.F, hit); + + // Fck: this does not use any kind of spatial index acceleration... + igl::ray_mesh_intersect(s, dir, m.V(), m.F(), hit); return double(hit.t); } +// An enhanced version of ray_mesh_intersect for the pinheads. This will shoot +// multiple rays to detect collisions more accurately. +double pinhead_mesh_intersect(const Vec3d& jp, + const Vec3d& dir, + double r1, + double r2, + const EigenMesh3D& m) +{ + + return 0; +} + // Clustering a set of points by the given criteria ClusteredPoints cluster( const sla::PointSet& points, @@ -309,53 +395,5 @@ ClusteredPoints cluster( return result; } -using Segments = std::vector>; - -Segments model_boundary(const EigenMesh3D& emesh, double offs) -{ - Segments ret; - Polygons pp; - pp.reserve(size_t(emesh.F.rows())); - - for (int i = 0; i < emesh.F.rows(); i++) { - auto trindex = emesh.F.row(i); - auto& p1 = emesh.V.row(trindex(0)); - auto& p2 = emesh.V.row(trindex(1)); - auto& p3 = emesh.V.row(trindex(2)); - - Polygon p; - p.points.resize(3); - p.points[0] = Point::new_scale(p1(X), p1(Y)); - p.points[1] = Point::new_scale(p2(X), p2(Y)); - p.points[2] = Point::new_scale(p3(X), p3(Y)); - p.make_counter_clockwise(); - pp.emplace_back(p); - } - - ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true); - - for(auto& expoly : merged) { - auto lines = expoly.lines(); - for(Line& l : lines) { - Vec2d a(l.a(X) * SCALING_FACTOR, l.a(Y) * SCALING_FACTOR); - Vec2d b(l.b(X) * SCALING_FACTOR, l.b(Y) * SCALING_FACTOR); - ret.emplace_back(std::make_pair(a, b)); - } - } - - return ret; -} - -//struct SegmentIndex { - -//}; - -//using SegmentIndexEl = std::pair; - -//SegmentIndexEl - - - - } } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 5cddadb5b..445c78576 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -508,7 +508,7 @@ void SLAPrint::process() auto support_points = [this, ilh](SLAPrintObject& po) { const ModelObject& mo = *po.m_model_object; po.m_supportdata.reset(new SLAPrintObject::SupportData()); - po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); + po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh()); BOOST_LOG_TRIVIAL(debug) << "Support point count " << mo.sla_support_points.size(); From 8391e734164c3a256576049369a8ba2506c4038c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 15 Jan 2019 11:09:00 +0100 Subject: [PATCH 02/12] WIP --- src/libslic3r/SLA/SLASupportTree.cpp | 26 +++++++++---------- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 34 +++++++------------------ 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index a105f3e4b..317816084 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -557,23 +557,21 @@ Vec3d model_coord(const ModelInstance& object, const Vec3f& mesh_coord) { return object.transform_vector(mesh_coord.cast()); } -double ray_mesh_intersect(const Vec3d& s, - const Vec3d& dir, - const EigenMesh3D& m); - -double pinhead_mesh_intersect(const Vec3d& jp, - const Vec3d& dir, - double r1, - double r2, - const EigenMesh3D& m); +inline double ray_mesh_intersect(const Vec3d& s, + const Vec3d& dir, + const EigenMesh3D& m) +{ + return m.query_ray_hit(s, dir); +} // Wrapper only inline double pinhead_mesh_intersect(const Head& head, const EigenMesh3D& m) { - return pinhead_mesh_intersect(head.junction_point(), - head.dir, - head.r_pin_mm, - head.r_back_mm, - m); +// return pinhead_mesh_intersect(head.junction_point(), +// head.dir, +// head.r_pin_mm, +// head.r_back_mm, +// m); + return 0; } PointSet normals(const PointSet& points, const EigenMesh3D& mesh, diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index cacd1ceaa..22dfeb393 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -146,9 +146,17 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { - return 0; + igl::Hit hit; + hit.t = std::numeric_limits::infinity(); + m_aabb->intersect_ray(m_V, m_F, s, dir, hit); + + return double(hit.t); } +/* **************************************************************************** + * Misc functions + * ****************************************************************************/ + bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, double eps = 0.05) { @@ -294,30 +302,6 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh, return ret; } -double ray_mesh_intersect(const Vec3d& s, - const Vec3d& dir, - const EigenMesh3D& m) -{ - igl::Hit hit; - hit.t = std::numeric_limits::infinity(); - - // Fck: this does not use any kind of spatial index acceleration... - igl::ray_mesh_intersect(s, dir, m.V(), m.F(), hit); - return double(hit.t); -} - -// An enhanced version of ray_mesh_intersect for the pinheads. This will shoot -// multiple rays to detect collisions more accurately. -double pinhead_mesh_intersect(const Vec3d& jp, - const Vec3d& dir, - double r1, - double r2, - const EigenMesh3D& m) -{ - - return 0; -} - // Clustering a set of points by the given criteria ClusteredPoints cluster( const sla::PointSet& points, From 1e1d405d70f888771203078cd970a9ed3089aaf5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Jan 2019 15:35:01 +0100 Subject: [PATCH 03/12] Multiple rays for the pinhead collision detection. Seems to help a lot. --- src/libslic3r/SLA/SLASupportTree.cpp | 96 +++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 7bba1c80e..af128b108 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -564,14 +564,86 @@ inline double ray_mesh_intersect(const Vec3d& s, return m.query_ray_hit(s, dir); } -// Wrapper only -inline double pinhead_mesh_intersect(const Head& head, const EigenMesh3D& m) { -// return pinhead_mesh_intersect(head.junction_point(), -// head.dir, -// head.r_pin_mm, -// head.r_back_mm, -// m); - return 0; +// This function will test if a future pinhead would not collide with the model +// geometry. It does not take a 'Head' object because those are created after +// this test. +// Parameters: +// s: The touching point on the model surface. +// dir: This is the direction of the head from the pin to the back +// r_pin, r_back: the radiuses of the pin and the back sphere +// width: This is the full width from the pin center to the back center +// m: The object mesh +// +// Optional: +// samples: how many rays will be shot +// safety distance: This will be added to the radiuses to have a safety distance +// from the mesh. +inline double pinhead_mesh_intersect(const Vec3d& s_original, + const Vec3d& dir, + double r_pin, + double r_back, + double width, + const EigenMesh3D& m, + unsigned samples = 8, + double safety_distance = 0.05) { + + // We will shoot multiple rays from the head pinpoint in the direction of + // the pinhead robe (side) surface. The result will be the smallest hit + // distance. + + // Move away slightly from the touching point to avoid raycasting on the + // inner surface of the mesh. + Vec3d s = s_original + 1.1 * r_pin * dir; + + Vec3d v = dir; // Our direction (axis) + Vec3d c = s + width * dir; + + // Two vectors that will be perpendicular to each other and to the axis. + // Values for a(X) and a(Y) are now arbitrary, a(Z) is just a placeholder. + Vec3d a(0, 1, 0), b; + + // The portions of the circle (the head-back circle) for which we will shoot + // rays. + std::vector phis(samples); + for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); + + a(Z) = -(v(X)*a(X) + v(Y)*a(Y)) / v(Z); + + b = a.cross(v); + + // Now a and b vectors are perpendicular to v and to each other. Together + // they define the plane where we have to iterate with the given angles + // in the 'phis' vector + + for(double& phi : phis) { + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + // Let's have a safety coefficient for the radiuses. + double rpscos = (safety_distance + r_pin) * cosphi; + double rpssin = (safety_distance + r_pin) * sinphi; + double rpbcos = (safety_distance + r_back) * cosphi; + double rpbsin = (safety_distance + r_back) * sinphi; + + // Point on the circle on the pin sphere + Vec3d ps(s(X) + rpscos * a(X) + rpssin * b(X), + s(Y) + rpscos * a(Y) + rpssin * b(Y), + s(Z) + rpscos * a(Z) + rpssin * b(Z)); + + // This is the point on the circle on the back sphere + Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), + c(Y) + rpbcos * a(Y) + rpbsin * b(Y), + c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); + + Vec3d n = (p - ps).normalized(); + + phi = m.query_ray_hit(ps, n); + std::cout << "t = " << phi << std::endl; + } + + auto mit = std::min_element(phis.begin(), phis.end()); + + return *mit; } PointSet normals(const PointSet& points, const EigenMesh3D& mesh, @@ -1069,7 +1141,13 @@ bool SLASupportTree::generate(const PointSet &points, // We should shoot a ray in the direction of the pinhead and // see if there is enough space for it - double t = ray_mesh_intersect(hp + 0.1*nn, nn, mesh); + double t = pinhead_mesh_intersect( + hp, // touching point + nn, + cfg.head_front_radius_mm, // approx the radius + cfg.head_back_radius_mm, + w, + mesh); if(t > 2*w || std::isinf(t)) { // 2*w because of lower and upper pinhead From 7a677a673fdcb596b9f488f40f93e41a3d756dab Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Jan 2019 16:50:43 +0100 Subject: [PATCH 04/12] WIP --- src/libslic3r/SLA/SLASupportTree.cpp | 82 +++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index af128b108..8ff5645c9 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -578,14 +578,18 @@ inline double ray_mesh_intersect(const Vec3d& s, // samples: how many rays will be shot // safety distance: This will be added to the radiuses to have a safety distance // from the mesh. -inline double pinhead_mesh_intersect(const Vec3d& s_original, - const Vec3d& dir, - double r_pin, - double r_back, - double width, - const EigenMesh3D& m, - unsigned samples = 8, - double safety_distance = 0.05) { +double pinhead_mesh_intersect(const Vec3d& s_original, + const Vec3d& dir, + double r_pin, + double r_back, + double width, + const EigenMesh3D& m, + unsigned samples = 8, + double safety_distance = 0.05) +{ + // method based on: + // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space + // We will shoot multiple rays from the head pinpoint in the direction of // the pinhead robe (side) surface. The result will be the smallest hit @@ -593,7 +597,7 @@ inline double pinhead_mesh_intersect(const Vec3d& s_original, // Move away slightly from the touching point to avoid raycasting on the // inner surface of the mesh. - Vec3d s = s_original + 1.1 * r_pin * dir; + Vec3d s = s_original + r_pin * dir; Vec3d v = dir; // Our direction (axis) Vec3d c = s + width * dir; @@ -638,6 +642,10 @@ inline double pinhead_mesh_intersect(const Vec3d& s_original, Vec3d n = (p - ps).normalized(); phi = m.query_ray_hit(ps, n); + + // TODO: this should be an inside check + if(phi < r_pin) phi = std::numeric_limits::infinity(); + std::cout << "t = " << phi << std::endl; } @@ -646,6 +654,44 @@ inline double pinhead_mesh_intersect(const Vec3d& s_original, return *mit; } +double bridge_mesh_intersect(const Vec3d& s_original, + const Vec3d& dir, + double r, + const EigenMesh3D& m, + unsigned samples = 8, + double safety_distance = 0.05) +{ + // helper vector calculations + Vec3d s = s_original + r*dir; Vec3d a(0, 1, 0), b; + + a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z); + b = a.cross(dir); + + // circle portions + std::vector phis(samples); + for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); + + for(double& phi : phis) { + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + // Let's have a safety coefficient for the radiuses. + double rcos = (safety_distance + r) * cosphi; + double rsin = (safety_distance + r) * sinphi; + + // Point on the circle on the pin sphere + Vec3d p (s(X) + rcos * a(X) + rsin * b(X), + s(Y) + rcos * a(Y) + rsin * b(Y), + s(Z) + rcos * a(Z) + rsin * b(Z)); + + phi = m.query_ray_hit(p, dir); + } + + auto mit = std::min_element(phis.begin(), phis.end()); + + return *mit; +} + PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}); @@ -1275,7 +1321,8 @@ bool SLASupportTree::generate(const PointSet &points, double zstep = pillar_dist * std::tan(-cfg.tilt); ej(Z) = sj(Z) + zstep; - double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); +// double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); + double chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); double bridge_distance = pillar_dist / std::cos(-cfg.tilt); // If the pillars are so close that they touch each other, @@ -1301,9 +1348,15 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d bej(sj(X), sj(Y), ej(Z)); // need to check collision for the cross stick - double backchkd = ray_mesh_intersect(bsj, - dirv(bsj, bej), - emesh); +// double backchkd = ray_mesh_intersect(bsj, +// dirv(bsj, bej), +// emesh); + + double backchkd = bridge_mesh_intersect(bsj, + dirv(bsj, bej), + pillar.r, + emesh); + if(backchkd >= bridge_distance) { result.add_bridge(bsj, bej, pillar.r); @@ -1312,7 +1365,8 @@ bool SLASupportTree::generate(const PointSet &points, } sj.swap(ej); ej(Z) = sj(Z) + zstep; - chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); +// chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); + chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); } }; From e160cf3ffb7aae31e531a67031ce437f89fdde34 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 17 Jan 2019 16:44:26 +0100 Subject: [PATCH 05/12] EigenMesh upgraded with inside check capability. --- src/libslic3r/SLA/SLASupportTree.cpp | 88 ++++++++++++++++++------- src/libslic3r/SLA/SLASupportTree.hpp | 9 ++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 25 ++++++- 3 files changed, 95 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 8ff5645c9..6fa5ecd17 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -639,12 +639,10 @@ double pinhead_mesh_intersect(const Vec3d& s_original, c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - Vec3d n = (p - ps).normalized(); - - phi = m.query_ray_hit(ps, n); - - // TODO: this should be an inside check - if(phi < r_pin) phi = std::numeric_limits::infinity(); + if(m.inside(ps) && m.inside(p)) { + Vec3d n = (p - ps).normalized(); + phi = m.query_ray_hit(ps, n); + } else phi = std::numeric_limits::infinity(); std::cout << "t = " << phi << std::endl; } @@ -684,7 +682,8 @@ double bridge_mesh_intersect(const Vec3d& s_original, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - phi = m.query_ray_hit(p, dir); + if(m.inside(p)) phi = m.query_ray_hit(p, dir); + else phi = std::numeric_limits::infinity(); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1195,9 +1194,7 @@ bool SLASupportTree::generate(const PointSet &points, w, mesh); - if(t > 2*w || std::isinf(t)) { - // 2*w because of lower and upper pinhead - + if(t > w || std::isinf(t)) { head_pos.row(pcount) = hp; // save the verified and corrected normal @@ -1269,17 +1266,59 @@ bool SLASupportTree::generate(const PointSet &points, for(unsigned i = 0; i < head_pos.rows(); i++) { tifcl(); - auto& head = result.heads()[i]; + auto& head = result.head(i); Vec3d dir(0, 0, -1); - Vec3d startpoint = head.junction_point(); + bool accept = false; + int ri = 1; + double t = std::numeric_limits::infinity(); + double hw = head.width_mm; - double t = ray_mesh_intersect(startpoint, dir, mesh); + while(!accept && head.width_mm > 0) { + + Vec3d startpoint = head.junction_point(); + + // Collision detection + t = bridge_mesh_intersect(startpoint, dir, head.r_back_mm, mesh); + + // Precise distance measurement + double tprec = ray_mesh_intersect(startpoint, dir, mesh); + + if(std::isinf(tprec) && !std::isinf(t)) { + // This is a damned case where the pillar melds into the model + // but its center ray can reach the ground. We can not route + // this to the ground nor to the model surface. We have to + // modify the head or discard this support point. + head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm; + } else { + accept = true; t = tprec; + + auto id = head.id; + // We need to regenerate the head geometry + head = Head(head.r_back_mm, + head.r_pin_mm, + head.width_mm, + head.penetration_mm, + head.dir, + head.tr); + head.id = id; + } + + ri++; + } gndheight.emplace_back(t); - if(std::isinf(t)) gndidx.emplace_back(i); - else nogndidx.emplace_back(i); + if(accept) { + if(std::isinf(t)) gndidx.emplace_back(i); + else nogndidx.emplace_back(i); + } else { + BOOST_LOG_TRIVIAL(warning) << "A support point at " + << head.tr.transpose() + << " had to be discarded as there is" + << " nowhere to route it."; + head.invalidate(); + } } PointSet gnd(gndidx.size(), 3); @@ -1321,7 +1360,6 @@ bool SLASupportTree::generate(const PointSet &points, double zstep = pillar_dist * std::tan(-cfg.tilt); ej(Z) = sj(Z) + zstep; -// double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); double chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); double bridge_distance = pillar_dist / std::cos(-cfg.tilt); @@ -1348,10 +1386,6 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d bej(sj(X), sj(Y), ej(Z)); // need to check collision for the cross stick -// double backchkd = ray_mesh_intersect(bsj, -// dirv(bsj, bej), -// emesh); - double backchkd = bridge_mesh_intersect(bsj, dirv(bsj, bej), pillar.r, @@ -1365,7 +1399,6 @@ bool SLASupportTree::generate(const PointSet &points, } sj.swap(ej); ej(Z) = sj(Z) + zstep; -// chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); } }; @@ -1444,7 +1477,7 @@ bool SLASupportTree::generate(const PointSet &points, // is distributed more effectively on the pillar. auto search_nearest = - [&cfg, &result, &emesh, maxbridgelen, gndlvl] + [&cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] (SpatIndex& spindex, const Vec3d& jsh) { long nearest_id = -1; @@ -1476,7 +1509,9 @@ bool SLASupportTree::generate(const PointSet &points, double d = distance(jp, jn); if(jn(Z) <= gndlvl || d > max_len) break; - double chkd = ray_mesh_intersect(jp, dirv(jp, jn), emesh); + double chkd = bridge_mesh_intersect(jp, dirv(jp, jn), + pradius, + emesh); if(chkd >= d) nearest_id = ne.second; spindex.remove(ne); @@ -1695,10 +1730,15 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d dir = {0, 0, -1}; Vec3d sj = sp + R * n; - double dist = ray_mesh_intersect(sj, dir, emesh); + // This is only for checking + double dist = bridge_mesh_intersect(sj, dir, R, emesh); if(std::isinf(dist) || std::isnan(dist) || dist < 2*R) continue; + // This on the other hand will return the exact distance available + // measured through the center of the stick. + dist = ray_mesh_intersect(sj, dir, emesh); + Vec3d ej = sj + (dist + HWIDTH_MM)* dir; result.add_compact_bridge(sp, ej, n, R); } diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 80f0bd68b..ac7d7cab8 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -120,16 +120,21 @@ public: ~EigenMesh3D(); EigenMesh3D(const EigenMesh3D& other); -// EigenMesh3D(EigenMesh3D&&) = default; EigenMesh3D& operator=(const EigenMesh3D&); -// EigenMesh3D& operator=(EigenMesh3D&&) = default; inline double ground_level() const { return m_ground_level; } inline const Eigen::MatrixXd& V() const { return m_V; } inline const Eigen::MatrixXi& F() const { return m_F; } + // Casting a ray on the mesh, returns the distance where the hit occures. double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + + // The signed distance from a point to the mesh. Outputs the distance, + // the index of the triangle and the closest point in mesh coordinate space. + std::tuple signed_distance(const Vec3d& p) const; + + bool inside(const Vec3d& p) const; }; using PointSet = Eigen::MatrixXd; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 22dfeb393..358d9dc9c 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -3,6 +3,9 @@ #include "SLA/SLABoilerPlate.hpp" #include "SLA/SLASpatIndex.hpp" +// Workaround: IGL signed_distance.h will define PI in the igl namespace. +#undef PI + // HEAVY headers... takes eternity to compile // for concave hull merging decisions @@ -12,6 +15,7 @@ #include #include #include +#include #include "SLASpatIndex.hpp" #include "ClipperUtils.hpp" @@ -19,6 +23,9 @@ namespace Slic3r { namespace sla { +// Bring back PI from the igl namespace +using igl::PI; + /* ************************************************************************** * SpatIndex implementation * ************************************************************************** */ @@ -86,7 +93,10 @@ size_t SpatIndex::size() const * EigenMesh3D implementation * ****************************************************************************/ -class EigenMesh3D::AABBImpl: public igl::AABB {}; +class EigenMesh3D::AABBImpl: public igl::AABB { +public: + igl::WindingNumberAABB windtree; +}; EigenMesh3D::EigenMesh3D(): m_aabb(new AABBImpl()) {} @@ -128,6 +138,9 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { // Build the AABB accelaration tree m_aabb->init(m_V, m_F); + + m_aabb->windtree.set_mesh(m_V, m_F); + m_aabb->windtree.init(); } EigenMesh3D::~EigenMesh3D() {} @@ -153,6 +166,16 @@ double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return double(hit.t); } +std::tuple +EigenMesh3D::signed_distance(const Vec3d &/*p*/) const { + // TODO: implement + return std::make_tuple(0.0, 0, Vec3d()); +} + +bool EigenMesh3D::inside(const Vec3d &p) const { + return m_aabb->windtree.inside(p); +} + /* **************************************************************************** * Misc functions * ****************************************************************************/ From 83f75f25bd43987e6ecba1357e21f16857496080 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 17 Jan 2019 17:46:29 +0100 Subject: [PATCH 06/12] Further improvements for headless sticks. --- src/libslic3r/SLA/SLASupportTree.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 6fa5ecd17..29282b328 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -643,8 +643,6 @@ double pinhead_mesh_intersect(const Vec3d& s_original, Vec3d n = (p - ps).normalized(); phi = m.query_ray_hit(ps, n); } else phi = std::numeric_limits::infinity(); - - std::cout << "t = " << phi << std::endl; } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1368,7 +1366,7 @@ bool SLASupportTree::generate(const PointSet &points, if(pillar_dist > 2*cfg.head_back_radius_mm && bridge_distance < cfg.max_bridge_length_mm) while(sj(Z) > pillar.endpoint(Z) + cfg.base_radius_mm && - ej(Z) > nextpillar.endpoint(Z) + + cfg.base_radius_mm) + ej(Z) > nextpillar.endpoint(Z) + cfg.base_radius_mm) { if(chkd >= bridge_distance) { result.add_bridge(sj, ej, pillar.r); @@ -1732,12 +1730,16 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d sj = sp + R * n; // This is only for checking - double dist = bridge_mesh_intersect(sj, dir, R, emesh); - if(std::isinf(dist) || std::isnan(dist) || dist < 2*R) continue; + double idist = bridge_mesh_intersect(sj, dir, R, emesh); + double dist = ray_mesh_intersect(sj, dir, emesh); - // This on the other hand will return the exact distance available - // measured through the center of the stick. - dist = ray_mesh_intersect(sj, dir, emesh); + if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || + std::isinf(dist) || std::isnan(dist) || dist < 2*R) { + BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" + << " support stick at: " + << sj.transpose(); + continue; + } Vec3d ej = sj + (dist + HWIDTH_MM)* dir; result.add_compact_bridge(sp, ej, n, R); From 4f837032324aa013f5d7ca82302a5956ed46d556 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 18 Jan 2019 12:09:53 +0100 Subject: [PATCH 07/12] A lot of comments added. --- src/libslic3r/SLA/SLASupportTree.cpp | 111 +++++++++++++++++++++------ 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 29282b328..f756cfcf4 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -707,6 +707,19 @@ ClusteredPoints cluster( std::function pred, unsigned max_points = 0); +// This class will hold the support tree meshes with some additional bookkeeping +// as well. Various parts of the support geometry are stored separately and are +// merged when the caller queries the merged mesh. The merged result is cached +// for fast subsequent delivery of the merged mesh which can be quite complex. +// An object of this class will be used as the result type during the support +// generation algorithm. Parts will be added with the appropriate methods such +// as add_head or add_pillar which forwards the constructor arguments and fills +// the IDs of these substructures. The IDs are basically indices into the arrays +// of the appropriate type (heads, pillars, etc...). One can later query e.g. a +// pillar for a specific head... +// +// The support pad is considered an auxiliary geometry and is not part of the +// merged mesh. It can be retrieved using a dedicated method (pad()) class SLASupportTree::Impl { std::vector m_heads; std::vector m_pillars; @@ -1067,16 +1080,22 @@ bool SLASupportTree::generate(const PointSet &points, // Indices of those who don't touch the ground IndexSet noground_heads; + // Groups of the 'ground_head' indices that belong into one cluster. These + // are candidates to be connected to one pillar. ClusteredPoints ground_connectors; + // A help function to translate ground head index to the actual coordinates. auto gnd_head_pt = [&ground_heads, &head_positions] (size_t idx) { return Vec3d(head_positions.row(ground_heads[idx])); }; + // This algorithm uses the Impl class as its output stream. It will be + // filled gradually with support elements (heads, pillars, bridges, ...) using Result = SLASupportTree::Impl; - Result& result = *m_impl; + // Let's define the individual steps of the processing. We can experiment + // later with the ordering and the dependencies between them. enum Steps { BEGIN, FILTER, @@ -1092,14 +1111,15 @@ bool SLASupportTree::generate(const PointSet &points, //... }; - // Debug: - // for(int pn = 0; pn < points.rows(); ++pn) { - // std::cout << "p " << pn << " " << points.row(pn) << std::endl; - // } - - + // t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will + // come in handy. auto& tifcl = ctl.cancelfn; + // Filtering step: here we will discard inappropriate support points and + // decide the future of the appropriate ones. We will check if a pinhead + // is applicable and adjust its angle at each support point. + // We will also merge the support points that are just too close and can be + // considered as one. auto filterfn = [tifcl] ( const SupportConfig& cfg, const PointSet& points, @@ -1110,10 +1130,6 @@ bool SLASupportTree::generate(const PointSet &points, PointSet& headless_pos, PointSet& headless_norm) { - /* ******************************************************** */ - /* Filtering step */ - /* ******************************************************** */ - // Get the points that are too close to each other and keep only the // first one auto aliases = @@ -1214,7 +1230,8 @@ bool SLASupportTree::generate(const PointSet &points, headless_norm.conservativeResize(hlcount, Eigen::NoChange); }; - // Function to write the pinheads into the result + // Pinhead creation: based on the filtering results, the Head objects will + // be constructed (together with their triangle meshes). auto pinheadfn = [tifcl] ( const SupportConfig& cfg, PointSet& head_pos, @@ -1240,8 +1257,13 @@ bool SLASupportTree::generate(const PointSet &points, } }; - // &filtered_points, &head_positions, &result, &mesh, - // &gndidx, &gndheight, &nogndidx, cfg + // Further classification of the support points with pinheads. If the + // ground is directly reachable through a vertical line parallel to the Z + // axis we consider a support point as pillar candidate. If touches the + // model geometry, it will be marked as non-ground facing and further steps + // will process it. Also, the pillars will be grouped into clusters that can + // be interconnected with bridges. Elements of these groups may or may not + // be interconnected. Here we only run the clustering algorithm. auto classifyfn = [tifcl] ( const SupportConfig& cfg, const EigenMesh3D& mesh, @@ -1262,6 +1284,9 @@ bool SLASupportTree::generate(const PointSet &points, gndidx.reserve(size_t(head_pos.rows())); nogndidx.reserve(size_t(head_pos.rows())); + // First we search decide which heads reach the ground and can be full + // pillars and which shall be connected to the model surface (or search + // a suitable path around the surface that leads to the ground -- TODO) for(unsigned i = 0; i < head_pos.rows(); i++) { tifcl(); auto& head = result.head(i); @@ -1272,6 +1297,9 @@ bool SLASupportTree::generate(const PointSet &points, double t = std::numeric_limits::infinity(); double hw = head.width_mm; + // We will try to assign a pillar to all the pinheads. If a pillar + // would pierce the model surface, we will try to adjust slightly + // the head with so that the pillar can be deployed. while(!accept && head.width_mm > 0) { Vec3d startpoint = head.junction_point(); @@ -1283,10 +1311,9 @@ bool SLASupportTree::generate(const PointSet &points, double tprec = ray_mesh_intersect(startpoint, dir, mesh); if(std::isinf(tprec) && !std::isinf(t)) { - // This is a damned case where the pillar melds into the model - // but its center ray can reach the ground. We can not route - // this to the ground nor to the model surface. We have to - // modify the head or discard this support point. + // This is a damned case where the pillar melds into the + // model but its center ray can reach the ground. We can + // not route this to the ground nor to the model surface. head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm; } else { accept = true; t = tprec; @@ -1305,12 +1332,23 @@ bool SLASupportTree::generate(const PointSet &points, ri++; } + // Save the distance from a surface in the Z axis downwards. It may + // be infinity but that is telling us that it touches the ground. gndheight.emplace_back(t); if(accept) { if(std::isinf(t)) gndidx.emplace_back(i); else nogndidx.emplace_back(i); } else { + // This is a serious issue. There was no way to deploy a pillar + // for the given pinhead. The whole thing has to be discarded + // leaving the model potentially unprintable. + // + // TODO: In the future this has to be solved by searching for + // a path in 3D space from this support point to a suitable + // pillar position or an existing pillar. + // As a workaround we could mark this head as "sidehead only" + // let it go trough the nearby pillar search in the next step. BOOST_LOG_TRIVIAL(warning) << "A support point at " << head.tr.transpose() << " had to be discarded as there is" @@ -1319,8 +1357,8 @@ bool SLASupportTree::generate(const PointSet &points, } } + // Transform the ground facing point indices top actual coordinates. PointSet gnd(gndidx.size(), 3); - for(size_t i = 0; i < gndidx.size(); i++) gnd.row(long(i)) = head_pos.row(gndidx[i]); @@ -1340,7 +1378,8 @@ bool SLASupportTree::generate(const PointSet &points, }, 3); // max 3 heads to connect to one centroid }; - // Helper function for interconnecting two pillars with zig-zag bridges + // Helper function for interconnecting two pillars with zig-zag bridges. + // This is not an individual step. auto interconnect = [&cfg]( const Pillar& pillar, const Pillar& nextpillar, @@ -1401,6 +1440,11 @@ bool SLASupportTree::generate(const PointSet &points, } }; + // Step: Routing the ground connected pinheads, and interconnecting them + // with additional (angled) bridges. Not all of these pinheads will be + // a full pillar (ground connected). Some will connect to a nearby pillar + // using a bridge. The max number of such side-heads for a central pillar + // is limited to avoid bad weight distribution. auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl]( const SupportConfig& cfg, const ClusteredPoints& gnd_clusters, @@ -1505,7 +1549,7 @@ bool SLASupportTree::generate(const PointSet &points, } double d = distance(jp, jn); - if(jn(Z) <= gndlvl || d > max_len) break; + if(jn(Z) <= gndlvl + nearhead.r_back_mm || d > max_len) break; double chkd = bridge_mesh_intersect(jp, dirv(jp, jn), pradius, @@ -1644,6 +1688,11 @@ bool SLASupportTree::generate(const PointSet &points, } }; + // Step: routing the pinheads that are would connect to the model surface + // along the Z axis downwards. For now these will actually be connected with + // the model surface with a flipped pinhead. In the future here we could use + // some smart algorithms to search for a safe path to the ground or to a + // nearby pillar that can hold the supported weight. auto routing_nongnd_fn = [tifcl]( const SupportConfig& cfg, const std::vector& gndheight, @@ -1705,6 +1754,9 @@ bool SLASupportTree::generate(const PointSet &points, } }; + // Step: process the support points where there is not enough space for a + // full pinhead. In this case we will use a rounded sphere as a touching + // point and use a thinner bridge (let's call it a stick). auto process_headless = [tifcl]( const SupportConfig& cfg, const PointSet& headless_pts, @@ -1746,16 +1798,24 @@ bool SLASupportTree::generate(const PointSet &points, } }; - using std::ref; - using std::cref; + // Now that the individual blocks are defined, lets connect the wires. We + // will create an array of functions which represents a program. Place the + // step methods in the array and bind the right arguments to the methods + // This way the data dependencies will be easily traceable between + // individual steps. + // There will be empty steps as well like the begin step or the done or + // abort steps. These are slots for future initialization or cleanup. + + using std::cref; // Bind inputs with cref (read-only) + using std::ref; // Bind outputs with ref (writable) using std::bind; // Here we can easily track what goes in and what comes out of each step: // (see the cref-s as inputs and ref-s as outputs) std::array, NUM_STEPS> program = { [] () { - // Begin - // clear up the shared data + // Begin... + // Potentially clear up the shared data (not needed for now) }, // Filtering unnecessary support points @@ -1798,6 +1858,7 @@ bool SLASupportTree::generate(const PointSet &points, Steps pc = BEGIN, pc_prev = BEGIN; + // Let's define a simple automaton that will run our program. auto progress = [&ctl, &pc, &pc_prev] () { static const std::array stepstr { "Starting", From 6c0b65208fca79b2a3e158e8a1b30ef45ec5ba70 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 18 Jan 2019 16:21:44 +0100 Subject: [PATCH 08/12] Introducing signed_distance into the collision detection. Everything is broken O.o --- src/libslic3r/SLA/SLASupportTree.cpp | 28 +++++++++++++++---------- src/libslic3r/SLA/SLASupportTree.hpp | 19 ++++++++++++++++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 10 +++++---- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index f756cfcf4..1d9f981a6 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -578,7 +578,7 @@ inline double ray_mesh_intersect(const Vec3d& s, // samples: how many rays will be shot // safety distance: This will be added to the radiuses to have a safety distance // from the mesh. -double pinhead_mesh_intersect(const Vec3d& s_original, +double pinhead_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r_pin, double r_back, @@ -597,8 +597,6 @@ double pinhead_mesh_intersect(const Vec3d& s_original, // Move away slightly from the touching point to avoid raycasting on the // inner surface of the mesh. - Vec3d s = s_original + r_pin * dir; - Vec3d v = dir; // Our direction (axis) Vec3d c = s + width * dir; @@ -634,15 +632,20 @@ double pinhead_mesh_intersect(const Vec3d& s_original, s(Y) + rpscos * a(Y) + rpssin * b(Y), s(Z) + rpscos * a(Z) + rpssin * b(Z)); + // Point ps is not on mesh but can be inside or outside as well. This + // would cause many problems with ray-casting. So we query the closest + // point on the mesh to this. + auto result = m.signed_distance(ps); + // This is the point on the circle on the back sphere Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - if(m.inside(ps) && m.inside(p)) { - Vec3d n = (p - ps).normalized(); - phi = m.query_ray_hit(ps, n); - } else phi = std::numeric_limits::infinity(); + if(!m.inside(p)) { + Vec3d n = (p - result.point_on_mesh() + 0.01 * dir).normalized(); + phi = m.query_ray_hit(result.point_on_mesh(), n); + } else phi = 0; } auto mit = std::min_element(phis.begin(), phis.end()); @@ -650,7 +653,7 @@ double pinhead_mesh_intersect(const Vec3d& s_original, return *mit; } -double bridge_mesh_intersect(const Vec3d& s_original, +double bridge_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r, const EigenMesh3D& m, @@ -658,7 +661,7 @@ double bridge_mesh_intersect(const Vec3d& s_original, double safety_distance = 0.05) { // helper vector calculations - Vec3d s = s_original + r*dir; Vec3d a(0, 1, 0), b; + Vec3d a(0, 1, 0), b; a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z); b = a.cross(dir); @@ -680,8 +683,9 @@ double bridge_mesh_intersect(const Vec3d& s_original, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - if(m.inside(p)) phi = m.query_ray_hit(p, dir); - else phi = std::numeric_limits::infinity(); + auto result = m.signed_distance(p); + + phi = m.query_ray_hit(result.point_on_mesh() + 0.05*dir, dir); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1190,6 +1194,8 @@ bool SLASupportTree::generate(const PointSet &points, std::sin(azimuth) * std::sin(polar), std::cos(polar)); + nn.normalize(); + // save the head (pinpoint) position Vec3d hp = filt_pts.row(i); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index ac7d7cab8..e13dd8c76 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -130,9 +130,26 @@ public: // Casting a ray on the mesh, returns the distance where the hit occures. double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + class si_result { + double m_value; + int m_fidx; + Vec3d m_p; + si_result(double val, int i, const Vec3d& c): + m_value(val), m_fidx(i), m_p(c) {} + friend class EigenMesh3D; + public: + + si_result() = delete; + + double value() const { return m_value; } + operator double() const { return m_value; } + const Vec3d& point_on_mesh() const { return m_p; } + int F_idx() const { return m_fidx; } + }; + // The signed distance from a point to the mesh. Outputs the distance, // the index of the triangle and the closest point in mesh coordinate space. - std::tuple signed_distance(const Vec3d& p) const; + si_result signed_distance(const Vec3d& p) const; bool inside(const Vec3d& p) const; }; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 358d9dc9c..b84e66e41 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -166,10 +166,12 @@ double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return double(hit.t); } -std::tuple -EigenMesh3D::signed_distance(const Vec3d &/*p*/) const { - // TODO: implement - return std::make_tuple(0.0, 0, Vec3d()); +EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { + double sign = 0; double sqdst = 0; int i = 0; Vec3d c; + igl::signed_distance_winding_number(*m_aabb, m_V, m_F, m_aabb->windtree, + p, sign, sqdst, i, c); + + return si_result(sign * std::sqrt(sqdst), i, c); } bool EigenMesh3D::inside(const Vec3d &p) const { From 7c839b8469d99e54d6663210a63de76603bd7136 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Jan 2019 15:10:07 +0100 Subject: [PATCH 09/12] Many major issues solved. Performance may be degraded. --- src/libslic3r/SLA/SLASupportTree.cpp | 44 +++++++++++++------------ src/libslic3r/SLA/SLASupportTree.hpp | 6 ++-- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 13 -------- src/libslic3r/SLAPrint.cpp | 6 ++-- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index cd26c9622..74c93b824 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -510,7 +510,6 @@ struct CompactBridge { // A wrapper struct around the base pool (pad) struct Pad { -// Contour3D mesh; TriangleMesh tmesh; PoolConfig cfg; double zlevel = 0; @@ -588,7 +587,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double r_back, double width, const EigenMesh3D& m, - unsigned samples = 8, + unsigned samples = 4, double safety_distance = 0.05) { // method based on: @@ -603,6 +602,7 @@ double pinhead_mesh_intersect(const Vec3d& s, // inner surface of the mesh. Vec3d v = dir; // Our direction (axis) Vec3d c = s + width * dir; + const double& sd = safety_distance; // Two vectors that will be perpendicular to each other and to the axis. // Values for a(X) and a(Y) are now arbitrary, a(Z) is just a placeholder. @@ -626,10 +626,10 @@ double pinhead_mesh_intersect(const Vec3d& s, double cosphi = std::cos(phi); // Let's have a safety coefficient for the radiuses. - double rpscos = (safety_distance + r_pin) * cosphi; - double rpssin = (safety_distance + r_pin) * sinphi; - double rpbcos = (safety_distance + r_back) * cosphi; - double rpbsin = (safety_distance + r_back) * sinphi; + double rpscos = (sd + r_pin) * cosphi; + double rpssin = (sd + r_pin) * sinphi; + double rpbcos = (sd + r_back) * cosphi; + double rpbsin = (sd + r_back) * sinphi; // Point on the circle on the pin sphere Vec3d ps(s(X) + rpscos * a(X) + rpssin * b(X), @@ -639,17 +639,15 @@ double pinhead_mesh_intersect(const Vec3d& s, // Point ps is not on mesh but can be inside or outside as well. This // would cause many problems with ray-casting. So we query the closest // point on the mesh to this. - auto result = m.signed_distance(ps); + auto psq = m.signed_distance(ps); // This is the point on the circle on the back sphere Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - if(!m.inside(p)) { - Vec3d n = (p - result.point_on_mesh() + 0.01 * dir).normalized(); - phi = m.query_ray_hit(result.point_on_mesh(), n); - } else phi = 0; + Vec3d n = (p - psq.point_on_mesh()).normalized(); + phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -661,11 +659,12 @@ double bridge_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r, const EigenMesh3D& m, - unsigned samples = 8, + unsigned samples = 4, double safety_distance = 0.05) { // helper vector calculations Vec3d a(0, 1, 0), b; + const double& sd = safety_distance; a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z); b = a.cross(dir); @@ -679,8 +678,8 @@ double bridge_mesh_intersect(const Vec3d& s, double cosphi = std::cos(phi); // Let's have a safety coefficient for the radiuses. - double rcos = (safety_distance + r) * cosphi; - double rsin = (safety_distance + r) * sinphi; + double rcos = (sd + r) * cosphi; + double rsin = (sd + r) * sinphi; // Point on the circle on the pin sphere Vec3d p (s(X) + rcos * a(X) + rsin * b(X), @@ -689,7 +688,9 @@ double bridge_mesh_intersect(const Vec3d& s, auto result = m.signed_distance(p); - phi = m.query_ray_hit(result.point_on_mesh() + 0.05*dir, dir); + Vec3d sp = result.value() < 0 ? result.point_on_mesh() : p; + + phi = m.query_ray_hit(sp + sd*dir, dir); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1560,6 +1561,8 @@ bool SLASupportTree::generate(const PointSet &points, } double d = distance(jp, jn); + + if(jn(Z) <= gndlvl + 2*cfg.head_width_mm || d > max_len) break; double chkd = bridge_mesh_intersect(jp, dirv(jp, jn), @@ -1784,16 +1787,15 @@ bool SLASupportTree::generate(const PointSet &points, // We will sink the pins into the model surface for a distance of 1/3 of // the pin radius for(int i = 0; i < headless_pts.rows(); i++) { tifcl(); - Vec3d sp = headless_pts.row(i); - - Vec3d n = headless_norm.row(i); - sp = sp - n * HWIDTH_MM; + Vec3d sph = headless_pts.row(i); // Exact support position + Vec3d n = headless_norm.row(i); // mesh outward normal + Vec3d sp = sph - n * HWIDTH_MM; // stick head start point Vec3d dir = {0, 0, -1}; - Vec3d sj = sp + R * n; + Vec3d sj = sp + R * n; // stick start point // This is only for checking - double idist = bridge_mesh_intersect(sj, dir, R, emesh); + double idist = bridge_mesh_intersect(sph, dir, R, emesh); double dist = ray_mesh_intersect(sj, dir, emesh); if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index e13dd8c76..d6e128da0 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -114,14 +114,12 @@ class EigenMesh3D { std::unique_ptr m_aabb; public: - EigenMesh3D(); EigenMesh3D(const TriangleMesh&); - - ~EigenMesh3D(); - EigenMesh3D(const EigenMesh3D& other); EigenMesh3D& operator=(const EigenMesh3D&); + ~EigenMesh3D(); + inline double ground_level() const { return m_ground_level; } inline const Eigen::MatrixXd& V() const { return m_V; } diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index b84e66e41..b41c56acc 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -98,8 +98,6 @@ public: igl::WindingNumberAABB windtree; }; -EigenMesh3D::EigenMesh3D(): m_aabb(new AABBImpl()) {} - EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { static const double dEPS = 1e-6; @@ -138,9 +136,7 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { // Build the AABB accelaration tree m_aabb->init(m_V, m_F); - m_aabb->windtree.set_mesh(m_V, m_F); - m_aabb->windtree.init(); } EigenMesh3D::~EigenMesh3D() {} @@ -207,15 +203,6 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh, Eigen::VectorXi I; PointSet C; - // We need to remove duplicate vertices and have a true index triangle - // structure - /* - EigenMesh3D mesh; - Eigen::VectorXi SVI, SVJ; - static const double dEPS = 1e-6; - igl::remove_duplicate_vertices(emesh.V, emesh.F, dEPS, - mesh.V, SVI, SVJ, mesh.F);*/ - igl::point_mesh_squared_distance( points, mesh.V(), mesh.F(), dists, I, C); PointSet ret(I.rows(), 3); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 47914a895..dc55b196b 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -29,6 +29,8 @@ public: SupportTreePtr support_tree_ptr; // the supports SlicedSupports support_slices; // sliced supports std::vector level_ids; + + inline SupportData(const TriangleMesh& trmesh): emesh(trmesh) {} }; namespace { @@ -503,8 +505,8 @@ void SLAPrint::process() // support points. Then we sprinkle the rest of the mesh. auto support_points = [this, ilh](SLAPrintObject& po) { const ModelObject& mo = *po.m_model_object; - po.m_supportdata.reset(new SLAPrintObject::SupportData()); - po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh()); + po.m_supportdata.reset( + new SLAPrintObject::SupportData(po.transformed_mesh()) ); // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; From 4e82e32a2752532ad7bb64d5565ec7498e8317f7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Jan 2019 13:51:34 +0100 Subject: [PATCH 10/12] Trying to speed up collision detection with tbb --- src/libslic3r/SLA/SLASupportTree.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 74c93b824..e753a947a 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -13,6 +13,7 @@ #include #include +#include /** * Terminology: @@ -588,7 +589,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double width, const EigenMesh3D& m, unsigned samples = 4, - double safety_distance = 0.05) + double safety_distance = 0.001) { // method based on: // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space @@ -621,7 +622,10 @@ double pinhead_mesh_intersect(const Vec3d& s, // they define the plane where we have to iterate with the given angles // in the 'phis' vector - for(double& phi : phis) { + tbb::parallel_for(size_t(0), phis.size(), + [&phis, &m, sd, r_pin, r_back, s, a, b, c](size_t i) + { + double& phi = phis[i]; double sinphi = std::sin(phi); double cosphi = std::cos(phi); @@ -648,7 +652,7 @@ double pinhead_mesh_intersect(const Vec3d& s, Vec3d n = (p - psq.point_on_mesh()).normalized(); phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); - } + }); auto mit = std::min_element(phis.begin(), phis.end()); @@ -660,7 +664,7 @@ double bridge_mesh_intersect(const Vec3d& s, double r, const EigenMesh3D& m, unsigned samples = 4, - double safety_distance = 0.05) + double safety_distance = 0.001) { // helper vector calculations Vec3d a(0, 1, 0), b; @@ -673,7 +677,10 @@ double bridge_mesh_intersect(const Vec3d& s, std::vector phis(samples); for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); - for(double& phi : phis) { + tbb::parallel_for(size_t(0), phis.size(), + [&phis, &m, a, b, sd, dir, r, s](size_t i) + { + double& phi = phis[i]; double sinphi = std::sin(phi); double cosphi = std::cos(phi); @@ -691,7 +698,7 @@ double bridge_mesh_intersect(const Vec3d& s, Vec3d sp = result.value() < 0 ? result.point_on_mesh() : p; phi = m.query_ray_hit(sp + sd*dir, dir); - } + }); auto mit = std::min_element(phis.begin(), phis.end()); From 3f10b2f7f8e87d90198cd91543b54772ded0a73f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Jan 2019 17:35:39 +0100 Subject: [PATCH 11/12] Getting around signed_distance in pinhead_mesh_intersect --- src/libslic3r/SLA/SLASupportTree.cpp | 48 +++++++++++++++++-------- src/libslic3r/SLA/SLASupportTree.hpp | 32 ++++++++++++++++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 10 ++++-- 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index e753a947a..4640037bb 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -565,7 +565,7 @@ inline double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m) { - return m.query_ray_hit(s, dir); + return m.query_ray_hit(s, dir).distance(); } // This function will test if a future pinhead would not collide with the model @@ -588,7 +588,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double r_back, double width, const EigenMesh3D& m, - unsigned samples = 4, + unsigned samples = 8, double safety_distance = 0.001) { // method based on: @@ -643,15 +643,26 @@ double pinhead_mesh_intersect(const Vec3d& s, // Point ps is not on mesh but can be inside or outside as well. This // would cause many problems with ray-casting. So we query the closest // point on the mesh to this. - auto psq = m.signed_distance(ps); +// auto psq = m.signed_distance(ps); // This is the point on the circle on the back sphere Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - Vec3d n = (p - psq.point_on_mesh()).normalized(); - phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); +// Vec3d n = (p - psq.point_on_mesh()).normalized(); +// phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); + + Vec3d n = (p - ps).normalized(); + auto hr = m.query_ray_hit(ps + sd*n, n); + + if(hr.is_inside()) { // the hit is inside the model + if(hr.distance() > 2*r_pin) phi = 0; + else { + auto hr2 = m.query_ray_hit(ps + (hr.distance() + 2*sd)*n, n); + phi = hr2.distance(); + } + } else phi = hr.distance(); }); auto mit = std::min_element(phis.begin(), phis.end()); @@ -659,10 +670,14 @@ double pinhead_mesh_intersect(const Vec3d& s, return *mit; } +// Checking bridge (pillar and stick as well) intersection with the model. If +// the function is used for headless sticks, the ins_check parameter have to be +// true as the beginning of the stick might be inside the model geometry. double bridge_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r, const EigenMesh3D& m, + bool ins_check = false, unsigned samples = 4, double safety_distance = 0.001) { @@ -678,7 +693,7 @@ double bridge_mesh_intersect(const Vec3d& s, for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); tbb::parallel_for(size_t(0), phis.size(), - [&phis, &m, a, b, sd, dir, r, s](size_t i) + [&phis, &m, a, b, sd, dir, r, s, ins_check](size_t i) { double& phi = phis[i]; double sinphi = std::sin(phi); @@ -693,11 +708,13 @@ double bridge_mesh_intersect(const Vec3d& s, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - auto result = m.signed_distance(p); + Vec3d sp; + if(ins_check) { + auto result = m.signed_distance(p); + sp = result.value() < 0 ? result.point_on_mesh() : p; + } else sp = p; - Vec3d sp = result.value() < 0 ? result.point_on_mesh() : p; - - phi = m.query_ray_hit(sp + sd*dir, dir); + phi = m.query_ray_hit(sp + sd*dir, dir).distance(); }); auto mit = std::min_element(phis.begin(), phis.end()); @@ -1538,12 +1555,12 @@ bool SLASupportTree::generate(const PointSet &points, // is distributed more effectively on the pillar. auto search_nearest = - [&cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] + [&tifcl, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] (SpatIndex& spindex, const Vec3d& jsh) { long nearest_id = -1; const double max_len = maxbridgelen / 2; - while(nearest_id < 0 && !spindex.empty()) { + while(nearest_id < 0 && !spindex.empty()) { tifcl(); // loop until a suitable head is not found // if there is a pillar closer than the cluster center // (this may happen as the clustering is not perfect) @@ -1660,7 +1677,7 @@ bool SLASupportTree::generate(const PointSet &points, if(!ring.empty()) { // inner ring is now in 'newring' and outer ring is in 'ring' SpatIndex innerring; - for(unsigned i : newring) { + for(unsigned i : newring) { tifcl(); const Pillar& pill = result.head_pillar(gndidx[i]); assert(pill.id >= 0); innerring.insert(pill.endpoint, unsigned(pill.id)); @@ -1669,7 +1686,7 @@ bool SLASupportTree::generate(const PointSet &points, // For all pillars in the outer ring find the closest in the // inner ring and connect them. This will create the spider web // fashioned connections between pillars - for(unsigned i : ring) { + for(unsigned i : ring) { tifcl(); const Pillar& outerpill = result.head_pillar(gndidx[i]); auto res = innerring.nearest(outerpill.endpoint, 1); if(res.empty()) continue; @@ -1695,6 +1712,7 @@ bool SLASupportTree::generate(const PointSet &points, next != ring.end(); ++it, ++next) { + tifcl(); const Pillar& pillar = result.head_pillar(gndidx[*it]); const Pillar& nextpillar = result.head_pillar(gndidx[*next]); interconnect(pillar, nextpillar, emesh, result); @@ -1802,7 +1820,7 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d sj = sp + R * n; // stick start point // This is only for checking - double idist = bridge_mesh_intersect(sph, dir, R, emesh); + double idist = bridge_mesh_intersect(sph, dir, R, emesh, true); double dist = ray_mesh_intersect(sj, dir, emesh); if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index d6e128da0..b1e77b056 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -125,8 +125,38 @@ public: inline const Eigen::MatrixXd& V() const { return m_V; } inline const Eigen::MatrixXi& F() const { return m_F; } + // Result of a raycast + class hit_result { + double m_t = std::numeric_limits::infinity(); + int m_face_id = -1; + const EigenMesh3D& m_mesh; + Vec3d m_dir; + inline hit_result(const EigenMesh3D& em): m_mesh(em) {} + friend class EigenMesh3D; + public: + + inline double distance() const { return m_t; } + + inline int face() const { return m_face_id; } + + inline Vec3d normal() const { + if(m_face_id < 0) return {}; + auto trindex = m_mesh.m_F.row(m_face_id); + const Vec3d& p1 = m_mesh.V().row(trindex(0)); + const Vec3d& p2 = m_mesh.V().row(trindex(1)); + const Vec3d& p3 = m_mesh.V().row(trindex(2)); + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + return U.cross(V).normalized(); + } + + inline bool is_inside() { + return m_face_id >= 0 && normal().dot(m_dir) > 0; + } + }; + // Casting a ray on the mesh, returns the distance where the hit occures. - double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; class si_result { double m_value; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index b41c56acc..d3af1eac8 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -153,13 +153,19 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; } -double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +EigenMesh3D::hit_result +EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { igl::Hit hit; hit.t = std::numeric_limits::infinity(); m_aabb->intersect_ray(m_V, m_F, s, dir, hit); - return double(hit.t); + hit_result ret(*this); + ret.m_t = double(hit.t); + ret.m_dir = dir; + if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id; + + return ret; } EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { From 095dfcad9e23a88398c99cf49badb94d9d46066c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 31 Jan 2019 10:11:37 +0100 Subject: [PATCH 12/12] Getting rid of signed distance from collision detection. --- src/libslic3r/SLA/SLASupportTree.cpp | 16 ++++++++++------ src/libslic3r/SLA/SLASupportTree.hpp | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 4640037bb..54e3a0189 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -659,6 +659,7 @@ double pinhead_mesh_intersect(const Vec3d& s, if(hr.is_inside()) { // the hit is inside the model if(hr.distance() > 2*r_pin) phi = 0; else { + // re-cast the ray from the outside of the object auto hr2 = m.query_ray_hit(ps + (hr.distance() + 2*sd)*n, n); phi = hr2.distance(); } @@ -708,13 +709,16 @@ double bridge_mesh_intersect(const Vec3d& s, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - Vec3d sp; - if(ins_check) { - auto result = m.signed_distance(p); - sp = result.value() < 0 ? result.point_on_mesh() : p; - } else sp = p; + auto hr = m.query_ray_hit(p + sd*dir, dir); - phi = m.query_ray_hit(sp + sd*dir, dir).distance(); + if(ins_check && hr.is_inside()) { + if(hr.distance() > 2*r) phi = 0; + else { + // re-cast the ray from the outside of the object + auto hr2 = m.query_ray_hit(p + (hr.distance() + 2*sd)*dir, dir); + phi = hr2.distance(); + } + } else phi = hr.distance(); }); auto mit = std::min_element(phis.begin(), phis.end()); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index b1e77b056..8de8d2b33 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -78,7 +78,7 @@ struct SupportConfig { double object_elevation_mm = 10; // The max Z angle for a normal at which it will get completely ignored. - double normal_cutoff_angle = 110.0 * M_PI / 180.0; + double normal_cutoff_angle = 150.0 * M_PI / 180.0; };