From 1eec6c473c660196dbe7ca421d0abefbf4ea8739 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 25 Jun 2020 13:58:51 +0200 Subject: Rename EigenMesh3D to IndexedMesh and SupportConfig to SupportTreeConfig --- src/libslic3r/CMakeLists.txt | 4 +- src/libslic3r/SLA/Contour3D.cpp | 4 +- src/libslic3r/SLA/Contour3D.hpp | 4 +- src/libslic3r/SLA/EigenMesh3D.cpp | 433 ---------------------------- src/libslic3r/SLA/EigenMesh3D.hpp | 146 ---------- src/libslic3r/SLA/Hollowing.cpp | 4 +- src/libslic3r/SLA/IndexedMesh.cpp | 433 ++++++++++++++++++++++++++++ src/libslic3r/SLA/IndexedMesh.hpp | 146 ++++++++++ src/libslic3r/SLA/ReprojectPointsOnMesh.hpp | 6 +- src/libslic3r/SLA/SupportPointGenerator.cpp | 10 +- src/libslic3r/SLA/SupportPointGenerator.hpp | 8 +- src/libslic3r/SLA/SupportTree.hpp | 21 +- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 198 ++++++------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 19 +- src/libslic3r/SLAPrint.cpp | 6 +- src/libslic3r/SLAPrint.hpp | 2 +- src/slic3r/GUI/MeshUtils.cpp | 4 +- src/slic3r/GUI/MeshUtils.hpp | 4 +- 18 files changed, 729 insertions(+), 723 deletions(-) delete mode 100644 src/libslic3r/SLA/EigenMesh3D.cpp delete mode 100644 src/libslic3r/SLA/EigenMesh3D.hpp create mode 100644 src/libslic3r/SLA/IndexedMesh.cpp create mode 100644 src/libslic3r/SLA/IndexedMesh.hpp (limited to 'src') diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 20f3c6b4b..91da5df5d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -236,8 +236,8 @@ add_library(libslic3r STATIC SLA/SupportPointGenerator.cpp SLA/Contour3D.hpp SLA/Contour3D.cpp - SLA/EigenMesh3D.hpp - SLA/EigenMesh3D.cpp + SLA/IndexedMesh.hpp + SLA/IndexedMesh.cpp SLA/Clustering.hpp SLA/Clustering.cpp SLA/ReprojectPointsOnMesh.hpp diff --git a/src/libslic3r/SLA/Contour3D.cpp b/src/libslic3r/SLA/Contour3D.cpp index 408465d43..96d10af20 100644 --- a/src/libslic3r/SLA/Contour3D.cpp +++ b/src/libslic3r/SLA/Contour3D.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -27,7 +27,7 @@ Contour3D::Contour3D(TriangleMesh &&trmesh) faces3.swap(trmesh.its.indices); } -Contour3D::Contour3D(const EigenMesh3D &emesh) { +Contour3D::Contour3D(const IndexedMesh &emesh) { points.reserve(emesh.vertices().size()); faces3.reserve(emesh.indices().size()); diff --git a/src/libslic3r/SLA/Contour3D.hpp b/src/libslic3r/SLA/Contour3D.hpp index 1a4fa9a29..3380cd6ab 100644 --- a/src/libslic3r/SLA/Contour3D.hpp +++ b/src/libslic3r/SLA/Contour3D.hpp @@ -10,7 +10,7 @@ using Vec4i = Eigen::Matrix; namespace sla { -class EigenMesh3D; +class IndexedMesh; /// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with /// other meshes of this type and converting to and from other mesh formats. @@ -22,7 +22,7 @@ struct Contour3D { Contour3D() = default; Contour3D(const TriangleMesh &trmesh); Contour3D(TriangleMesh &&trmesh); - Contour3D(const EigenMesh3D &emesh); + Contour3D(const IndexedMesh &emesh); Contour3D& merge(const Contour3D& ctr); Contour3D& merge(const Pointf3s& triangles); diff --git a/src/libslic3r/SLA/EigenMesh3D.cpp b/src/libslic3r/SLA/EigenMesh3D.cpp deleted file mode 100644 index be44e324c..000000000 --- a/src/libslic3r/SLA/EigenMesh3D.cpp +++ /dev/null @@ -1,433 +0,0 @@ -#include "EigenMesh3D.hpp" -#include "Concurrency.hpp" - -#include -#include - -#include - -#ifdef SLIC3R_HOLE_RAYCASTER -#include -#endif - -namespace Slic3r { namespace sla { - -class EigenMesh3D::AABBImpl { -private: - AABBTreeIndirect::Tree3f m_tree; - -public: - void init(const TriangleMesh& tm) - { - m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( - tm.its.vertices, tm.its.indices); - } - - void intersect_ray(const TriangleMesh& tm, - const Vec3d& s, const Vec3d& dir, igl::Hit& hit) - { - AABBTreeIndirect::intersect_ray_first_hit(tm.its.vertices, - tm.its.indices, - m_tree, - s, dir, hit); - } - - void intersect_ray(const TriangleMesh& tm, - const Vec3d& s, const Vec3d& dir, std::vector& hits) - { - AABBTreeIndirect::intersect_ray_all_hits(tm.its.vertices, - tm.its.indices, - m_tree, - s, dir, hits); - } - - double squared_distance(const TriangleMesh& tm, - const Vec3d& point, int& i, Eigen::Matrix& closest) { - size_t idx_unsigned = 0; - Vec3d closest_vec3d(closest); - double dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( - tm.its.vertices, - tm.its.indices, - m_tree, point, idx_unsigned, closest_vec3d); - i = int(idx_unsigned); - closest = closest_vec3d; - return dist; - } -}; - -static const constexpr double MESH_EPS = 1e-6; - -EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh) - : m_aabb(new AABBImpl()), m_tm(&tmesh) -{ - auto&& bb = tmesh.bounding_box(); - m_ground_level += bb.min(Z); - - // Build the AABB accelaration tree - m_aabb->init(tmesh); -} - -EigenMesh3D::~EigenMesh3D() {} - -EigenMesh3D::EigenMesh3D(const EigenMesh3D &other): - m_tm(other.m_tm), m_ground_level(other.m_ground_level), - m_aabb( new AABBImpl(*other.m_aabb) ) {} - - -EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) -{ - m_tm = other.m_tm; - m_ground_level = other.m_ground_level; - m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; -} - -EigenMesh3D &EigenMesh3D::operator=(EigenMesh3D &&other) = default; - -EigenMesh3D::EigenMesh3D(EigenMesh3D &&other) = default; - - - -const std::vector& EigenMesh3D::vertices() const -{ - return m_tm->its.vertices; -} - - - -const std::vector& EigenMesh3D::indices() const -{ - return m_tm->its.indices; -} - - - -const Vec3f& EigenMesh3D::vertices(size_t idx) const -{ - return m_tm->its.vertices[idx]; -} - - - -const Vec3i& EigenMesh3D::indices(size_t idx) const -{ - return m_tm->its.indices[idx]; -} - - - -Vec3d EigenMesh3D::normal_by_face_id(int face_id) const { - return m_tm->stl.facet_start[face_id].normal.cast(); -} - - -EigenMesh3D::hit_result -EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const -{ - assert(is_approx(dir.norm(), 1.)); - igl::Hit hit; - hit.t = std::numeric_limits::infinity(); - -#ifdef SLIC3R_HOLE_RAYCASTER - if (! m_holes.empty()) { - - // If there are holes, the hit_results will be made by - // query_ray_hits (object) and filter_hits (holes): - return filter_hits(query_ray_hits(s, dir)); - } -#endif - - m_aabb->intersect_ray(*m_tm, s, dir, hit); - hit_result ret(*this); - ret.m_t = double(hit.t); - ret.m_dir = dir; - ret.m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) { - ret.m_normal = this->normal_by_face_id(hit.id); - ret.m_face_id = hit.id; - } - - return ret; -} - -std::vector -EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const -{ - std::vector outs; - std::vector hits; - m_aabb->intersect_ray(*m_tm, s, dir, hits); - - // The sort is necessary, the hits are not always sorted. - std::sort(hits.begin(), hits.end(), - [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); - - // Remove duplicates. They sometimes appear, for example when the ray is cast - // along an axis of a cube due to floating-point approximations in igl (?) - hits.erase(std::unique(hits.begin(), hits.end(), - [](const igl::Hit& a, const igl::Hit& b) - { return a.t == b.t; }), - hits.end()); - - // Convert the igl::Hit into hit_result - outs.reserve(hits.size()); - for (const igl::Hit& hit : hits) { - outs.emplace_back(EigenMesh3D::hit_result(*this)); - outs.back().m_t = double(hit.t); - outs.back().m_dir = dir; - outs.back().m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) { - outs.back().m_normal = this->normal_by_face_id(hit.id); - outs.back().m_face_id = hit.id; - } - } - - return outs; -} - - -#ifdef SLIC3R_HOLE_RAYCASTER -EigenMesh3D::hit_result EigenMesh3D::filter_hits( - const std::vector& object_hits) const -{ - assert(! m_holes.empty()); - hit_result out(*this); - - if (object_hits.empty()) - return out; - - const Vec3d& s = object_hits.front().source(); - const Vec3d& dir = object_hits.front().direction(); - - // A helper struct to save an intersetion with a hole - struct HoleHit { - HoleHit(float t_p, const Vec3d& normal_p, bool entry_p) : - t(t_p), normal(normal_p), entry(entry_p) {} - float t; - Vec3d normal; - bool entry; - }; - std::vector hole_isects; - hole_isects.reserve(m_holes.size()); - - auto sf = s.cast(); - auto dirf = dir.cast(); - - // Collect hits on all holes, preserve information about entry/exit - for (const sla::DrainHole& hole : m_holes) { - std::array, 2> isects; - if (hole.get_intersections(sf, dirf, isects)) { - // Ignore hole hits behind the source - if (isects[0].first > 0.f) hole_isects.emplace_back(isects[0].first, isects[0].second, true); - if (isects[1].first > 0.f) hole_isects.emplace_back(isects[1].first, isects[1].second, false); - } - } - - // Holes can intersect each other, sort the hits by t - std::sort(hole_isects.begin(), hole_isects.end(), - [](const HoleHit& a, const HoleHit& b) { return a.t < b.t; }); - - // Now inspect the intersections with object and holes, in the order of - // increasing distance. Keep track how deep are we nested in mesh/holes and - // pick the correct intersection. - // This needs to be done twice - first to find out how deep in the structure - // the source is, then to pick the correct intersection. - int hole_nested = 0; - int object_nested = 0; - for (int dry_run=1; dry_run>=0; --dry_run) { - hole_nested = -hole_nested; - object_nested = -object_nested; - - bool is_hole = false; - bool is_entry = false; - const HoleHit* next_hole_hit = hole_isects.empty() ? nullptr : &hole_isects.front(); - const hit_result* next_mesh_hit = &object_hits.front(); - - while (next_hole_hit || next_mesh_hit) { - if (next_hole_hit && next_mesh_hit) // still have hole and obj hits - is_hole = (next_hole_hit->t < next_mesh_hit->m_t); - else - is_hole = next_hole_hit; // one or the other ran out - - // Is this entry or exit hit? - is_entry = is_hole ? next_hole_hit->entry : ! next_mesh_hit->is_inside(); - - if (! dry_run) { - if (! is_hole && hole_nested == 0) { - // This is a valid object hit - return *next_mesh_hit; - } - if (is_hole && ! is_entry && object_nested != 0) { - // This holehit is the one we seek - out.m_t = next_hole_hit->t; - out.m_normal = next_hole_hit->normal; - out.m_source = s; - out.m_dir = dir; - return out; - } - } - - // Increase/decrease the counter - (is_hole ? hole_nested : object_nested) += (is_entry ? 1 : -1); - - // Advance the respective pointer - if (is_hole && next_hole_hit++ == &hole_isects.back()) - next_hole_hit = nullptr; - if (! is_hole && next_mesh_hit++ == &object_hits.back()) - next_mesh_hit = nullptr; - } - } - - // if we got here, the ray ended up in infinity - return out; -} -#endif - - -double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const { - double sqdst = 0; - Eigen::Matrix pp = p; - Eigen::Matrix cc; - sqdst = m_aabb->squared_distance(*m_tm, pp, i, cc); - c = cc; - return sqdst; -} - - -static bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, - double eps = 0.05) -{ - using Line3D = Eigen::ParametrizedLine; - - auto line = Line3D::Through(e1, e2); - double d = line.distance(p); - return std::abs(d) < eps; -} - -PointSet normals(const PointSet& points, - const EigenMesh3D& mesh, - double eps, - std::function thr, // throw on cancel - const std::vector& pt_indices) -{ - if (points.rows() == 0 || mesh.vertices().empty() || mesh.indices().empty()) - return {}; - - std::vector range = pt_indices; - if (range.empty()) { - range.resize(size_t(points.rows()), 0); - std::iota(range.begin(), range.end(), 0); - } - - PointSet ret(range.size(), 3); - - // for (size_t ridx = 0; ridx < range.size(); ++ridx) - ccr::enumerate( - range.begin(), range.end(), - [&ret, &mesh, &points, thr, eps](unsigned el, size_t ridx) { - thr(); - auto eidx = Eigen::Index(el); - int faceid = 0; - Vec3d p; - - mesh.squared_distance(points.row(eidx), faceid, p); - - auto trindex = mesh.indices(faceid); - - const Vec3d &p1 = mesh.vertices(trindex(0)).cast(); - const Vec3d &p2 = mesh.vertices(trindex(1)).cast(); - const Vec3d &p3 = mesh.vertices(trindex(2)).cast(); - - // We should check if the point lies on an edge of the hosting - // triangle. If it does then all the other triangles using the - // same two points have to be searched and the final normal should - // be some kind of aggregation of the participating triangle - // normals. We should also consider the cases where the support - // point lies right on a vertex of its triangle. The procedure is - // the same, get the neighbor triangles and calculate an average - // normal. - - // mark the vertex indices of the edge. ia and ib marks and edge - // ic will mark a single vertex. - int ia = -1, ib = -1, ic = -1; - - if (std::abs((p - p1).norm()) < eps) { - ic = trindex(0); - } else if (std::abs((p - p2).norm()) < eps) { - ic = trindex(1); - } else if (std::abs((p - p3).norm()) < eps) { - ic = trindex(2); - } else if (point_on_edge(p, p1, p2, eps)) { - ia = trindex(0); - ib = trindex(1); - } else if (point_on_edge(p, p2, p3, eps)) { - ia = trindex(1); - ib = trindex(2); - } else if (point_on_edge(p, p1, p3, eps)) { - ia = trindex(0); - ib = trindex(2); - } - - // 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 (size_t n = 0; n < mesh.indices().size(); ++n) { - thr(); - Vec3i ni = mesh.indices(n); - if ((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) - neigh.emplace_back(n); - } - } else if (ia >= 0 && ib >= 0) { // the point is on and edge - // now get all the neigboring triangles - for (size_t n = 0; n < mesh.indices().size(); ++n) { - thr(); - Vec3i ni = mesh.indices(n); - if ((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && - (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) - neigh.emplace_back(n); - } - } - - // Calculate the normals for the neighboring triangles - std::vector neighnorms; - neighnorms.reserve(neigh.size()); - for (size_t &tri_id : neigh) - neighnorms.emplace_back(mesh.normal_by_face_id(tri_id)); - - // Throw out duplicates. They would cause trouble with summing. We - // will use std::unique which works on sorted ranges. We will sort - // by the coefficient-wise sum of the normals. It should force the - // same elements to be consecutive. - std::sort(neighnorms.begin(), neighnorms.end(), - [](const Vec3d &v1, const Vec3d &v2) { - return v1.sum() < v2.sum(); - }); - - auto lend = std::unique(neighnorms.begin(), neighnorms.end(), - [](const Vec3d &n1, const Vec3d &n2) { - // Compare normals for equivalence. - // This is controvers stuff. - auto deq = [](double a, double b) { - return std::abs(a - b) < 1e-3; - }; - return deq(n1(X), n2(X)) && - deq(n1(Y), n2(Y)) && - deq(n1(Z), n2(Z)); - }); - - if (!neighnorms.empty()) { // there were neighbors to count with - // sum up the normals and then normalize the result again. - // This unification seems to be enough. - Vec3d sumnorm(0, 0, 0); - sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); - sumnorm.normalize(); - ret.row(long(ridx)) = sumnorm; - } else { // point lies safely within its triangle - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - ret.row(long(ridx)) = U.cross(V).normalized(); - } - }); - - return ret; -} - -}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp deleted file mode 100644 index c9196bb43..000000000 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef SLA_EIGENMESH3D_H -#define SLA_EIGENMESH3D_H - -#include -#include - -#include - -// There is an implementation of a hole-aware raycaster that was eventually -// not used in production version. It is now hidden under following define -// for possible future use. -// #define SLIC3R_HOLE_RAYCASTER - -#ifdef SLIC3R_HOLE_RAYCASTER - #include "libslic3r/SLA/Hollowing.hpp" -#endif - -namespace Slic3r { - -class TriangleMesh; - -namespace sla { - -using PointSet = Eigen::MatrixXd; - -/// An index-triangle structure for libIGL functions. Also serves as an -/// alternative (raw) input format for the SLASupportTree. -// Implemented in libslic3r/SLA/Common.cpp -class EigenMesh3D { - class AABBImpl; - - const TriangleMesh* m_tm; - double m_ground_level = 0, m_gnd_offset = 0; - - std::unique_ptr m_aabb; - -#ifdef SLIC3R_HOLE_RAYCASTER - // This holds a copy of holes in the mesh. Initialized externally - // by load_mesh setter. - std::vector m_holes; -#endif - -public: - - explicit EigenMesh3D(const TriangleMesh&); - - EigenMesh3D(const EigenMesh3D& other); - EigenMesh3D& operator=(const EigenMesh3D&); - - EigenMesh3D(EigenMesh3D &&other); - EigenMesh3D& operator=(EigenMesh3D &&other); - - ~EigenMesh3D(); - - inline double ground_level() const { return m_ground_level + m_gnd_offset; } - inline void ground_level_offset(double o) { m_gnd_offset = o; } - inline double ground_level_offset() const { return m_gnd_offset; } - - const std::vector& vertices() const; - const std::vector& indices() const; - const Vec3f& vertices(size_t idx) const; - const Vec3i& indices(size_t idx) const; - - // Result of a raycast - class hit_result { - // m_t holds a distance from m_source to the intersection. - double m_t = infty(); - int m_face_id = -1; - const EigenMesh3D *m_mesh = nullptr; - Vec3d m_dir; - Vec3d m_source; - Vec3d m_normal; - friend class EigenMesh3D; - - // A valid object of this class can only be obtained from - // EigenMesh3D::query_ray_hit method. - explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {} - public: - // This denotes no hit on the mesh. - static inline constexpr double infty() { return std::numeric_limits::infinity(); } - - explicit inline hit_result(double val = infty()) : m_t(val) {} - - inline double distance() const { return m_t; } - inline const Vec3d& direction() const { return m_dir; } - inline const Vec3d& source() const { return m_source; } - inline Vec3d position() const { return m_source + m_dir * m_t; } - inline int face() const { return m_face_id; } - inline bool is_valid() const { return m_mesh != nullptr; } - inline bool is_hit() const { return !std::isinf(m_t); } - - inline const Vec3d& normal() const { - assert(is_valid()); - return m_normal; - } - - inline bool is_inside() const { - return is_hit() && normal().dot(m_dir) > 0; - } - }; - -#ifdef SLIC3R_HOLE_RAYCASTER - // Inform the object about location of holes - // creates internal copy of the vector - void load_holes(const std::vector& holes) { - m_holes = holes; - } - - // Iterates over hits and holes and returns the true hit, possibly - // on the inside of a hole. - // This function is currently not used anywhere, it was written when the - // holes were subtracted on slices, that is, before we started using CGAL - // to actually cut the holes into the mesh. - hit_result filter_hits(const std::vector& obj_hits) const; -#endif - - // Casting a ray on the mesh, returns the distance where the hit occures. - hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; - - // Casts a ray on the mesh and returns all hits - std::vector query_ray_hits(const Vec3d &s, const Vec3d &dir) const; - - double squared_distance(const Vec3d& p, int& i, Vec3d& c) const; - inline double squared_distance(const Vec3d &p) const - { - int i; - Vec3d c; - return squared_distance(p, i, c); - } - - Vec3d normal_by_face_id(int face_id) const; - - const TriangleMesh * get_triangle_mesh() const { return m_tm; } -}; - -// Calculate the normals for the selected points (from 'points' set) on the -// mesh. This will call squared distance for each point. -PointSet normals(const PointSet& points, - const EigenMesh3D& convert_mesh, - double eps = 0.05, // min distance from edges - std::function throw_on_cancel = [](){}, - const std::vector& selected_points = {}); - -}} // namespace Slic3r::sla - -#endif // EIGENMESH3D_H diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 44e4dd839..5334054a0 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -159,7 +159,7 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, const Eigen::ParametrizedLine ray(s, dir.normalized()); for (size_t i=0; i<2; ++i) - out[i] = std::make_pair(sla::EigenMesh3D::hit_result::infty(), Vec3d::Zero()); + out[i] = std::make_pair(sla::IndexedMesh::hit_result::infty(), Vec3d::Zero()); const float sqr_radius = pow(radius, 2.f); diff --git a/src/libslic3r/SLA/IndexedMesh.cpp b/src/libslic3r/SLA/IndexedMesh.cpp new file mode 100644 index 000000000..573b62b6d --- /dev/null +++ b/src/libslic3r/SLA/IndexedMesh.cpp @@ -0,0 +1,433 @@ +#include "IndexedMesh.hpp" +#include "Concurrency.hpp" + +#include +#include + +#include + +#ifdef SLIC3R_HOLE_RAYCASTER +#include +#endif + +namespace Slic3r { namespace sla { + +class IndexedMesh::AABBImpl { +private: + AABBTreeIndirect::Tree3f m_tree; + +public: + void init(const TriangleMesh& tm) + { + m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( + tm.its.vertices, tm.its.indices); + } + + void intersect_ray(const TriangleMesh& tm, + const Vec3d& s, const Vec3d& dir, igl::Hit& hit) + { + AABBTreeIndirect::intersect_ray_first_hit(tm.its.vertices, + tm.its.indices, + m_tree, + s, dir, hit); + } + + void intersect_ray(const TriangleMesh& tm, + const Vec3d& s, const Vec3d& dir, std::vector& hits) + { + AABBTreeIndirect::intersect_ray_all_hits(tm.its.vertices, + tm.its.indices, + m_tree, + s, dir, hits); + } + + double squared_distance(const TriangleMesh& tm, + const Vec3d& point, int& i, Eigen::Matrix& closest) { + size_t idx_unsigned = 0; + Vec3d closest_vec3d(closest); + double dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + tm.its.vertices, + tm.its.indices, + m_tree, point, idx_unsigned, closest_vec3d); + i = int(idx_unsigned); + closest = closest_vec3d; + return dist; + } +}; + +static const constexpr double MESH_EPS = 1e-6; + +IndexedMesh::IndexedMesh(const TriangleMesh& tmesh) + : m_aabb(new AABBImpl()), m_tm(&tmesh) +{ + auto&& bb = tmesh.bounding_box(); + m_ground_level += bb.min(Z); + + // Build the AABB accelaration tree + m_aabb->init(tmesh); +} + +IndexedMesh::~IndexedMesh() {} + +IndexedMesh::IndexedMesh(const IndexedMesh &other): + m_tm(other.m_tm), m_ground_level(other.m_ground_level), + m_aabb( new AABBImpl(*other.m_aabb) ) {} + + +IndexedMesh &IndexedMesh::operator=(const IndexedMesh &other) +{ + m_tm = other.m_tm; + m_ground_level = other.m_ground_level; + m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; +} + +IndexedMesh &IndexedMesh::operator=(IndexedMesh &&other) = default; + +IndexedMesh::IndexedMesh(IndexedMesh &&other) = default; + + + +const std::vector& IndexedMesh::vertices() const +{ + return m_tm->its.vertices; +} + + + +const std::vector& IndexedMesh::indices() const +{ + return m_tm->its.indices; +} + + + +const Vec3f& IndexedMesh::vertices(size_t idx) const +{ + return m_tm->its.vertices[idx]; +} + + + +const Vec3i& IndexedMesh::indices(size_t idx) const +{ + return m_tm->its.indices[idx]; +} + + + +Vec3d IndexedMesh::normal_by_face_id(int face_id) const { + return m_tm->stl.facet_start[face_id].normal.cast(); +} + + +IndexedMesh::hit_result +IndexedMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +{ + assert(is_approx(dir.norm(), 1.)); + igl::Hit hit; + hit.t = std::numeric_limits::infinity(); + +#ifdef SLIC3R_HOLE_RAYCASTER + if (! m_holes.empty()) { + + // If there are holes, the hit_results will be made by + // query_ray_hits (object) and filter_hits (holes): + return filter_hits(query_ray_hits(s, dir)); + } +#endif + + m_aabb->intersect_ray(*m_tm, s, dir, hit); + hit_result ret(*this); + ret.m_t = double(hit.t); + ret.m_dir = dir; + ret.m_source = s; + if(!std::isinf(hit.t) && !std::isnan(hit.t)) { + ret.m_normal = this->normal_by_face_id(hit.id); + ret.m_face_id = hit.id; + } + + return ret; +} + +std::vector +IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const +{ + std::vector outs; + std::vector hits; + m_aabb->intersect_ray(*m_tm, s, dir, hits); + + // The sort is necessary, the hits are not always sorted. + std::sort(hits.begin(), hits.end(), + [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); + + // Remove duplicates. They sometimes appear, for example when the ray is cast + // along an axis of a cube due to floating-point approximations in igl (?) + hits.erase(std::unique(hits.begin(), hits.end(), + [](const igl::Hit& a, const igl::Hit& b) + { return a.t == b.t; }), + hits.end()); + + // Convert the igl::Hit into hit_result + outs.reserve(hits.size()); + for (const igl::Hit& hit : hits) { + outs.emplace_back(IndexedMesh::hit_result(*this)); + outs.back().m_t = double(hit.t); + outs.back().m_dir = dir; + outs.back().m_source = s; + if(!std::isinf(hit.t) && !std::isnan(hit.t)) { + outs.back().m_normal = this->normal_by_face_id(hit.id); + outs.back().m_face_id = hit.id; + } + } + + return outs; +} + + +#ifdef SLIC3R_HOLE_RAYCASTER +IndexedMesh::hit_result IndexedMesh::filter_hits( + const std::vector& object_hits) const +{ + assert(! m_holes.empty()); + hit_result out(*this); + + if (object_hits.empty()) + return out; + + const Vec3d& s = object_hits.front().source(); + const Vec3d& dir = object_hits.front().direction(); + + // A helper struct to save an intersetion with a hole + struct HoleHit { + HoleHit(float t_p, const Vec3d& normal_p, bool entry_p) : + t(t_p), normal(normal_p), entry(entry_p) {} + float t; + Vec3d normal; + bool entry; + }; + std::vector hole_isects; + hole_isects.reserve(m_holes.size()); + + auto sf = s.cast(); + auto dirf = dir.cast(); + + // Collect hits on all holes, preserve information about entry/exit + for (const sla::DrainHole& hole : m_holes) { + std::array, 2> isects; + if (hole.get_intersections(sf, dirf, isects)) { + // Ignore hole hits behind the source + if (isects[0].first > 0.f) hole_isects.emplace_back(isects[0].first, isects[0].second, true); + if (isects[1].first > 0.f) hole_isects.emplace_back(isects[1].first, isects[1].second, false); + } + } + + // Holes can intersect each other, sort the hits by t + std::sort(hole_isects.begin(), hole_isects.end(), + [](const HoleHit& a, const HoleHit& b) { return a.t < b.t; }); + + // Now inspect the intersections with object and holes, in the order of + // increasing distance. Keep track how deep are we nested in mesh/holes and + // pick the correct intersection. + // This needs to be done twice - first to find out how deep in the structure + // the source is, then to pick the correct intersection. + int hole_nested = 0; + int object_nested = 0; + for (int dry_run=1; dry_run>=0; --dry_run) { + hole_nested = -hole_nested; + object_nested = -object_nested; + + bool is_hole = false; + bool is_entry = false; + const HoleHit* next_hole_hit = hole_isects.empty() ? nullptr : &hole_isects.front(); + const hit_result* next_mesh_hit = &object_hits.front(); + + while (next_hole_hit || next_mesh_hit) { + if (next_hole_hit && next_mesh_hit) // still have hole and obj hits + is_hole = (next_hole_hit->t < next_mesh_hit->m_t); + else + is_hole = next_hole_hit; // one or the other ran out + + // Is this entry or exit hit? + is_entry = is_hole ? next_hole_hit->entry : ! next_mesh_hit->is_inside(); + + if (! dry_run) { + if (! is_hole && hole_nested == 0) { + // This is a valid object hit + return *next_mesh_hit; + } + if (is_hole && ! is_entry && object_nested != 0) { + // This holehit is the one we seek + out.m_t = next_hole_hit->t; + out.m_normal = next_hole_hit->normal; + out.m_source = s; + out.m_dir = dir; + return out; + } + } + + // Increase/decrease the counter + (is_hole ? hole_nested : object_nested) += (is_entry ? 1 : -1); + + // Advance the respective pointer + if (is_hole && next_hole_hit++ == &hole_isects.back()) + next_hole_hit = nullptr; + if (! is_hole && next_mesh_hit++ == &object_hits.back()) + next_mesh_hit = nullptr; + } + } + + // if we got here, the ray ended up in infinity + return out; +} +#endif + + +double IndexedMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const { + double sqdst = 0; + Eigen::Matrix pp = p; + Eigen::Matrix cc; + sqdst = m_aabb->squared_distance(*m_tm, pp, i, cc); + c = cc; + return sqdst; +} + + +static bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, + double eps = 0.05) +{ + using Line3D = Eigen::ParametrizedLine; + + auto line = Line3D::Through(e1, e2); + double d = line.distance(p); + return std::abs(d) < eps; +} + +PointSet normals(const PointSet& points, + const IndexedMesh& mesh, + double eps, + std::function thr, // throw on cancel + const std::vector& pt_indices) +{ + if (points.rows() == 0 || mesh.vertices().empty() || mesh.indices().empty()) + return {}; + + std::vector range = pt_indices; + if (range.empty()) { + range.resize(size_t(points.rows()), 0); + std::iota(range.begin(), range.end(), 0); + } + + PointSet ret(range.size(), 3); + + // for (size_t ridx = 0; ridx < range.size(); ++ridx) + ccr::enumerate( + range.begin(), range.end(), + [&ret, &mesh, &points, thr, eps](unsigned el, size_t ridx) { + thr(); + auto eidx = Eigen::Index(el); + int faceid = 0; + Vec3d p; + + mesh.squared_distance(points.row(eidx), faceid, p); + + auto trindex = mesh.indices(faceid); + + const Vec3d &p1 = mesh.vertices(trindex(0)).cast(); + const Vec3d &p2 = mesh.vertices(trindex(1)).cast(); + const Vec3d &p3 = mesh.vertices(trindex(2)).cast(); + + // We should check if the point lies on an edge of the hosting + // triangle. If it does then all the other triangles using the + // same two points have to be searched and the final normal should + // be some kind of aggregation of the participating triangle + // normals. We should also consider the cases where the support + // point lies right on a vertex of its triangle. The procedure is + // the same, get the neighbor triangles and calculate an average + // normal. + + // mark the vertex indices of the edge. ia and ib marks and edge + // ic will mark a single vertex. + int ia = -1, ib = -1, ic = -1; + + if (std::abs((p - p1).norm()) < eps) { + ic = trindex(0); + } else if (std::abs((p - p2).norm()) < eps) { + ic = trindex(1); + } else if (std::abs((p - p3).norm()) < eps) { + ic = trindex(2); + } else if (point_on_edge(p, p1, p2, eps)) { + ia = trindex(0); + ib = trindex(1); + } else if (point_on_edge(p, p2, p3, eps)) { + ia = trindex(1); + ib = trindex(2); + } else if (point_on_edge(p, p1, p3, eps)) { + ia = trindex(0); + ib = trindex(2); + } + + // 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 (size_t n = 0; n < mesh.indices().size(); ++n) { + thr(); + Vec3i ni = mesh.indices(n); + if ((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) + neigh.emplace_back(n); + } + } else if (ia >= 0 && ib >= 0) { // the point is on and edge + // now get all the neigboring triangles + for (size_t n = 0; n < mesh.indices().size(); ++n) { + thr(); + Vec3i ni = mesh.indices(n); + if ((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && + (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) + neigh.emplace_back(n); + } + } + + // Calculate the normals for the neighboring triangles + std::vector neighnorms; + neighnorms.reserve(neigh.size()); + for (size_t &tri_id : neigh) + neighnorms.emplace_back(mesh.normal_by_face_id(tri_id)); + + // Throw out duplicates. They would cause trouble with summing. We + // will use std::unique which works on sorted ranges. We will sort + // by the coefficient-wise sum of the normals. It should force the + // same elements to be consecutive. + std::sort(neighnorms.begin(), neighnorms.end(), + [](const Vec3d &v1, const Vec3d &v2) { + return v1.sum() < v2.sum(); + }); + + auto lend = std::unique(neighnorms.begin(), neighnorms.end(), + [](const Vec3d &n1, const Vec3d &n2) { + // Compare normals for equivalence. + // This is controvers stuff. + auto deq = [](double a, double b) { + return std::abs(a - b) < 1e-3; + }; + return deq(n1(X), n2(X)) && + deq(n1(Y), n2(Y)) && + deq(n1(Z), n2(Z)); + }); + + if (!neighnorms.empty()) { // there were neighbors to count with + // sum up the normals and then normalize the result again. + // This unification seems to be enough. + Vec3d sumnorm(0, 0, 0); + sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); + sumnorm.normalize(); + ret.row(long(ridx)) = sumnorm; + } else { // point lies safely within its triangle + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + ret.row(long(ridx)) = U.cross(V).normalized(); + } + }); + + return ret; +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/IndexedMesh.hpp b/src/libslic3r/SLA/IndexedMesh.hpp new file mode 100644 index 000000000..b0970608e --- /dev/null +++ b/src/libslic3r/SLA/IndexedMesh.hpp @@ -0,0 +1,146 @@ +#ifndef SLA_INDEXEDMESH_H +#define SLA_INDEXEDMESH_H + +#include +#include + +#include + +// There is an implementation of a hole-aware raycaster that was eventually +// not used in production version. It is now hidden under following define +// for possible future use. +// #define SLIC3R_HOLE_RAYCASTER + +#ifdef SLIC3R_HOLE_RAYCASTER + #include "libslic3r/SLA/Hollowing.hpp" +#endif + +namespace Slic3r { + +class TriangleMesh; + +namespace sla { + +using PointSet = Eigen::MatrixXd; + +/// An index-triangle structure for libIGL functions. Also serves as an +/// alternative (raw) input format for the SLASupportTree. +// Implemented in libslic3r/SLA/Common.cpp +class IndexedMesh { + class AABBImpl; + + const TriangleMesh* m_tm; + double m_ground_level = 0, m_gnd_offset = 0; + + std::unique_ptr m_aabb; + +#ifdef SLIC3R_HOLE_RAYCASTER + // This holds a copy of holes in the mesh. Initialized externally + // by load_mesh setter. + std::vector m_holes; +#endif + +public: + + explicit IndexedMesh(const TriangleMesh&); + + IndexedMesh(const IndexedMesh& other); + IndexedMesh& operator=(const IndexedMesh&); + + IndexedMesh(IndexedMesh &&other); + IndexedMesh& operator=(IndexedMesh &&other); + + ~IndexedMesh(); + + inline double ground_level() const { return m_ground_level + m_gnd_offset; } + inline void ground_level_offset(double o) { m_gnd_offset = o; } + inline double ground_level_offset() const { return m_gnd_offset; } + + const std::vector& vertices() const; + const std::vector& indices() const; + const Vec3f& vertices(size_t idx) const; + const Vec3i& indices(size_t idx) const; + + // Result of a raycast + class hit_result { + // m_t holds a distance from m_source to the intersection. + double m_t = infty(); + int m_face_id = -1; + const IndexedMesh *m_mesh = nullptr; + Vec3d m_dir; + Vec3d m_source; + Vec3d m_normal; + friend class IndexedMesh; + + // A valid object of this class can only be obtained from + // IndexedMesh::query_ray_hit method. + explicit inline hit_result(const IndexedMesh& em): m_mesh(&em) {} + public: + // This denotes no hit on the mesh. + static inline constexpr double infty() { return std::numeric_limits::infinity(); } + + explicit inline hit_result(double val = infty()) : m_t(val) {} + + inline double distance() const { return m_t; } + inline const Vec3d& direction() const { return m_dir; } + inline const Vec3d& source() const { return m_source; } + inline Vec3d position() const { return m_source + m_dir * m_t; } + inline int face() const { return m_face_id; } + inline bool is_valid() const { return m_mesh != nullptr; } + inline bool is_hit() const { return !std::isinf(m_t); } + + inline const Vec3d& normal() const { + assert(is_valid()); + return m_normal; + } + + inline bool is_inside() const { + return is_hit() && normal().dot(m_dir) > 0; + } + }; + +#ifdef SLIC3R_HOLE_RAYCASTER + // Inform the object about location of holes + // creates internal copy of the vector + void load_holes(const std::vector& holes) { + m_holes = holes; + } + + // Iterates over hits and holes and returns the true hit, possibly + // on the inside of a hole. + // This function is currently not used anywhere, it was written when the + // holes were subtracted on slices, that is, before we started using CGAL + // to actually cut the holes into the mesh. + hit_result filter_hits(const std::vector& obj_hits) const; +#endif + + // Casting a ray on the mesh, returns the distance where the hit occures. + hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + + // Casts a ray on the mesh and returns all hits + std::vector query_ray_hits(const Vec3d &s, const Vec3d &dir) const; + + double squared_distance(const Vec3d& p, int& i, Vec3d& c) const; + inline double squared_distance(const Vec3d &p) const + { + int i; + Vec3d c; + return squared_distance(p, i, c); + } + + Vec3d normal_by_face_id(int face_id) const; + + const TriangleMesh * get_triangle_mesh() const { return m_tm; } +}; + +// Calculate the normals for the selected points (from 'points' set) on the +// mesh. This will call squared distance for each point. +PointSet normals(const PointSet& points, + const IndexedMesh& convert_mesh, + double eps = 0.05, // min distance from edges + std::function throw_on_cancel = [](){}, + const std::vector& selected_points = {}); + +}} // namespace Slic3r::sla + +#endif // INDEXEDMESH_H diff --git a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp index 702d1bce1..4737a6c21 100644 --- a/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp +++ b/src/libslic3r/SLA/ReprojectPointsOnMesh.hpp @@ -4,7 +4,7 @@ #include "libslic3r/Point.hpp" #include "SupportPoint.hpp" #include "Hollowing.hpp" -#include "EigenMesh3D.hpp" +#include "IndexedMesh.hpp" #include "libslic3r/Model.hpp" #include @@ -15,7 +15,7 @@ template Vec3d pos(const Pt &p) { return p.pos.template cast() template void pos(Pt &p, const Vec3d &pp) { p.pos = pp.cast(); } template -void reproject_support_points(const EigenMesh3D &mesh, std::vector &pts) +void reproject_support_points(const IndexedMesh &mesh, std::vector &pts) { tbb::parallel_for(size_t(0), pts.size(), [&mesh, &pts](size_t idx) { int junk; @@ -40,7 +40,7 @@ inline void reproject_points_and_holes(ModelObject *object) TriangleMesh rmsh = object->raw_mesh(); rmsh.require_shared_vertices(); - EigenMesh3D emesh{rmsh}; + IndexedMesh emesh{rmsh}; if (has_sppoints) reproject_support_points(emesh, object->sla_support_points); diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index b598439ca..3cd075ae6 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -50,7 +50,7 @@ float SupportPointGenerator::distance_limit(float angle) const }*/ SupportPointGenerator::SupportPointGenerator( - const sla::EigenMesh3D &emesh, + const sla::IndexedMesh &emesh, const std::vector &slices, const std::vector & heights, const Config & config, @@ -64,7 +64,7 @@ SupportPointGenerator::SupportPointGenerator( } SupportPointGenerator::SupportPointGenerator( - const EigenMesh3D &emesh, + const IndexedMesh &emesh, const SupportPointGenerator::Config &config, std::function throw_on_cancel, std::function statusfn) @@ -95,8 +95,8 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po m_throw_on_cancel(); Vec3f& p = points[point_id].pos; // Project the point upward and downward and choose the closer intersection with the mesh. - sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); - sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); + sla::IndexedMesh::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); + sla::IndexedMesh::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); bool up = hit_up.is_hit(); bool down = hit_down.is_hit(); @@ -104,7 +104,7 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po if (!up && !down) continue; - sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; + sla::IndexedMesh::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; p = p + (hit.distance() * hit.direction()).cast(); } }); diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 3f07e9674..f1b377025 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -27,10 +27,10 @@ public: inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2) }; - SupportPointGenerator(const EigenMesh3D& emesh, const std::vector& slices, + SupportPointGenerator(const IndexedMesh& emesh, const std::vector& slices, const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn); - SupportPointGenerator(const EigenMesh3D& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); + SupportPointGenerator(const IndexedMesh& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); const std::vector& output() const { return m_output; } std::vector& output() { return m_output; } @@ -206,7 +206,7 @@ private: static void output_structures(const std::vector &structures); #endif // SLA_SUPPORTPOINTGEN_DEBUG - const EigenMesh3D& m_emesh; + const IndexedMesh& m_emesh; std::function m_throw_on_cancel; std::function m_statusfn; diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 1415ab8fe..7d54b76a4 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -31,7 +31,7 @@ enum class PillarConnectionMode dynamic }; -struct SupportConfig +struct SupportTreeConfig { bool enabled = true; @@ -107,23 +107,30 @@ struct SupportConfig }; +// TODO: Part of future refactor +//class SupportConfig { +// std::optional tree_cfg {std::in_place_t{}}; // fill up +// std::optional pad_cfg; +//}; + enum class MeshType { Support, Pad }; struct SupportableMesh { - EigenMesh3D emesh; + IndexedMesh emesh; SupportPoints pts; - SupportConfig cfg; + SupportTreeConfig cfg; + PadConfig pad_cfg; explicit SupportableMesh(const TriangleMesh & trmsh, const SupportPoints &sp, - const SupportConfig &c) + const SupportTreeConfig &c) : emesh{trmsh}, pts{sp}, cfg{c} {} - explicit SupportableMesh(const EigenMesh3D &em, + explicit SupportableMesh(const IndexedMesh &em, const SupportPoints &sp, - const SupportConfig &c) + const SupportTreeConfig &c) : emesh{em}, pts{sp}, cfg{c} {} }; diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 00f09b812..b29ad0b9c 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -14,7 +14,7 @@ using libnest2d::opt::StopCriteria; using libnest2d::opt::GeneticOptimizer; using libnest2d::opt::SubplexOptimizer; -template +template static Hit min_hit(const C &hits) { auto mit = std::min_element(hits.begin(), hits.end(), @@ -25,118 +25,118 @@ static Hit min_hit(const C &hits) return *mit; } -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &h) -{ - static const size_t SAMPLES = 8; +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Head &h) +//{ +// static const size_t SAMPLES = 8; + +// // Move away slightly from the touching point to avoid raycasting on the +// // inner surface of the mesh. + +// const double& sd = msh.cfg.safety_distance_mm; + +// auto& m = msh.emesh; +// using HitResult = IndexedMesh::hit_result; + +// // Hit results +// std::array hits; - // Move away slightly from the touching point to avoid raycasting on the - // inner surface of the mesh. +// Vec3d s1 = h.pos, s2 = h.junction_point(); - const double& sd = msh.cfg.safety_distance_mm; +// struct Rings { +// double rpin; +// double rback; +// Vec3d spin; +// Vec3d sback; +// PointRing ring; - auto& m = msh.emesh; - using HitResult = EigenMesh3D::hit_result; +// Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } +// Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } +// } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; - // Hit results - std::array hits; +// // 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. - Vec3d s1 = h.pos, s2 = h.junction_point(); +// auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { +// // Point on the circle on the pin sphere +// Vec3d ps = rings.pinring(i); +// // This is the point on the circle on the back sphere +// Vec3d p = rings.backring(i); - struct Rings { - double rpin; - double rback; - Vec3d spin; - Vec3d sback; - PointRing ring; +// // Point ps is not on mesh but can be inside or +// // outside as well. This would cause many problems +// // with ray-casting. To detect the position we will +// // use the ray-casting result (which has an is_inside +// // predicate). - Vec3d backring(size_t idx) { return ring.get(idx, sback, rback); } - Vec3d pinring(size_t idx) { return ring.get(idx, spin, rpin); } - } rings {h.r_pin_mm + sd, h.r_back_mm + sd, s1, s2, h.dir}; +// Vec3d n = (p - ps).normalized(); +// auto q = m.query_ray_hit(ps + sd * n, n); - // 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. +// if (q.is_inside()) { // the hit is inside the model +// if (q.distance() > rings.rpin) { +// // If we are inside the model and the hit +// // distance is bigger than our pin circle +// // diameter, it probably indicates that the +// // support point was already inside the +// // model, or there is really no space +// // around the point. We will assign a zero +// // hit distance to these cases which will +// // enforce the function return value to be +// // an invalid ray with zero hit distance. +// // (see min_element at the end) +// hit = HitResult(0.0); +// } else { +// // re-cast the ray from the outside of the +// // object. The starting point has an offset +// // of 2*safety_distance because the +// // original ray has also had an offset +// auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); +// hit = q2; +// } +// } else +// hit = q; +// }; - auto hitfn = [&m, &rings, sd](HitResult &hit, size_t i) { - // Point on the circle on the pin sphere - Vec3d ps = rings.pinring(i); - // This is the point on the circle on the back sphere - Vec3d p = rings.backring(i); - - // Point ps is not on mesh but can be inside or - // outside as well. This would cause many problems - // with ray-casting. To detect the position we will - // use the ray-casting result (which has an is_inside - // predicate). - - Vec3d n = (p - ps).normalized(); - auto q = m.query_ray_hit(ps + sd * n, n); - - if (q.is_inside()) { // the hit is inside the model - if (q.distance() > rings.rpin) { - // If we are inside the model and the hit - // distance is bigger than our pin circle - // diameter, it probably indicates that the - // support point was already inside the - // model, or there is really no space - // around the point. We will assign a zero - // hit distance to these cases which will - // enforce the function return value to be - // an invalid ray with zero hit distance. - // (see min_element at the end) - hit = HitResult(0.0); - } else { - // re-cast the ray from the outside of the - // object. The starting point has an offset - // of 2*safety_distance because the - // original ray has also had an offset - auto q2 = m.query_ray_hit(ps + (q.distance() + 2 * sd) * n, n); - hit = q2; - } - } else - hit = q; - }; +// ccr::enumerate(hits.begin(), hits.end(), hitfn); - ccr::enumerate(hits.begin(), hits.end(), hitfn); +// return min_hit(hits); +//} - return min_hit(hits); -} - -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) -{ +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d) +//{ - static const size_t SAMPLES = 8; +// static const size_t SAMPLES = 8; - Vec3d dir = (br.endp - br.startp).normalized(); - PointRing ring{dir}; +// Vec3d dir = (br.endp - br.startp).normalized(); +// PointRing ring{dir}; - using Hit = EigenMesh3D::hit_result; +// using Hit = IndexedMesh::hit_result; - // Hit results - std::array hits; +// // Hit results +// std::array hits; - double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; +// double sd = std::isnan(safety_d) ? msh.cfg.safety_distance_mm : safety_d; - auto hitfn = [&msh, &br, &ring, dir, sd] (Hit &hit, size_t i) { +// auto hitfn = [&msh, &br, &ring, dir, sd] (Hit &hit, size_t i) { - // Point on the circle on the pin sphere - Vec3d p = ring.get(i, br.startp, br.r + sd); +// // Point on the circle on the pin sphere +// Vec3d p = ring.get(i, br.startp, br.r + sd); - auto hr = msh.emesh.query_ray_hit(p + br.r * dir, dir); +// auto hr = msh.emesh.query_ray_hit(p + br.r * dir, dir); - if(hr.is_inside()) { - if(hr.distance() > 2 * br.r + sd) hit = Hit(0.0); - else { - // re-cast the ray from the outside of the object - hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); - } - } else hit = hr; - }; - - ccr::enumerate(hits.begin(), hits.end(), hitfn); - - return min_hit(hits); -} +// if(hr.is_inside()) { +// if(hr.distance() > 2 * br.r + sd) hit = Hit(0.0); +// else { +// // re-cast the ray from the outside of the object +// hit = msh.emesh.query_ray_hit(p + (hr.distance() + 2 * sd) * dir, dir); +// } +// } else hit = hr; +// }; + +// ccr::enumerate(hits.begin(), hits.end(), hitfn); + +// return min_hit(hits); +//} SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm) @@ -281,7 +281,7 @@ bool SupportTreeBuildsteps::execute(SupportTreeBuilder & builder, return pc == ABORT; } -EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( +IndexedMesh::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width) { static const size_t SAMPLES = 8; @@ -292,7 +292,7 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( const double& sd = m_cfg.safety_distance_mm; auto& m = m_mesh; - using HitResult = EigenMesh3D::hit_result; + using HitResult = IndexedMesh::hit_result; // Hit results std::array hits; @@ -357,13 +357,13 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::pinhead_mesh_intersect( return min_hit(hits); } -EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( +IndexedMesh::hit_result SupportTreeBuildsteps::bridge_mesh_intersect( const Vec3d &src, const Vec3d &dir, double r, double sd) { static const size_t SAMPLES = 8; PointRing ring{dir}; - using Hit = EigenMesh3D::hit_result; + using Hit = IndexedMesh::hit_result; // Hit results std::array hits; @@ -742,7 +742,7 @@ void SupportTreeBuildsteps::filter() auto nn = spheric_to_dir(polar, azimuth).normalized(); // check available distance - EigenMesh3D::hit_result t + IndexedMesh::hit_result t = pinhead_mesh_intersect(hp, // touching point nn, // normal pin_r, @@ -781,7 +781,7 @@ void SupportTreeBuildsteps::filter() polar = std::get<0>(oresult.optimum); azimuth = std::get<1>(oresult.optimum); nn = spheric_to_dir(polar, azimuth).normalized(); - t = EigenMesh3D::hit_result(oresult.score); + t = IndexedMesh::hit_result(oresult.score); } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index e8f73149e..a98586789 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -103,9 +103,8 @@ public: } }; -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); -EigenMesh3D::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); - +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Bridge &br, double safety_d = std::nan("")); +//IndexedMesh::hit_result query_hit(const SupportableMesh &msh, const Head &br, double safety_d = std::nan("")); inline Vec3d dirv(const Vec3d& startp, const Vec3d& endp) { return (endp - startp).normalized(); @@ -181,8 +180,8 @@ IntegerOnly pairhash(I a, I b) } class SupportTreeBuildsteps { - const SupportConfig& m_cfg; - const EigenMesh3D& m_mesh; + const SupportTreeConfig& m_cfg; + const IndexedMesh& m_mesh; const std::vector& m_support_pts; using PtIndices = std::vector; @@ -191,7 +190,7 @@ class SupportTreeBuildsteps { PtIndices m_iheads_onmodel; PtIndices m_iheadless; // headless support points - std::map m_head_to_ground_scans; + std::map m_head_to_ground_scans; // normals for support points from model faces. PointSet m_support_nmls; @@ -217,7 +216,7 @@ class SupportTreeBuildsteps { // When bridging heads to pillars... TODO: find a cleaner solution ccr::BlockingMutex m_bridge_mutex; - inline EigenMesh3D::hit_result ray_mesh_intersect(const Vec3d& s, + inline IndexedMesh::hit_result ray_mesh_intersect(const Vec3d& s, const Vec3d& dir) { return m_mesh.query_ray_hit(s, dir); @@ -234,7 +233,7 @@ class SupportTreeBuildsteps { // point was inside the model, an "invalid" hit_result will be returned // with a zero distance value instead of a NAN. This way the result can // be used safely for comparison with other distances. - EigenMesh3D::hit_result pinhead_mesh_intersect( + IndexedMesh::hit_result pinhead_mesh_intersect( const Vec3d& s, const Vec3d& dir, double r_pin, @@ -249,13 +248,13 @@ class SupportTreeBuildsteps { // point was inside the model, an "invalid" hit_result will be returned // with a zero distance value instead of a NAN. This way the result can // be used safely for comparison with other distances. - EigenMesh3D::hit_result bridge_mesh_intersect( + IndexedMesh::hit_result bridge_mesh_intersect( const Vec3d& s, const Vec3d& dir, double r, double safety_d); - EigenMesh3D::hit_result bridge_mesh_intersect( + IndexedMesh::hit_result bridge_mesh_intersect( const Vec3d& s, const Vec3d& dir, double r) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 2402207a8..eee3bbc9f 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -35,9 +35,9 @@ bool is_zero_elevation(const SLAPrintObjectConfig &c) } // Compile the argument for support creation from the static print config. -sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) +sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) { - sla::SupportConfig scfg; + sla::SupportTreeConfig scfg; scfg.enabled = c.supports_enable.getBool(); scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); @@ -616,7 +616,7 @@ std::string SLAPrint::validate() const return L("Cannot proceed without support points! " "Add support points or disable support generation."); - sla::SupportConfig cfg = make_support_cfg(po->config()); + sla::SupportTreeConfig cfg = make_support_cfg(po->config()); double elv = cfg.object_elevation_mm; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 9d41586ee..f4b220c58 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -544,7 +544,7 @@ private: bool is_zero_elevation(const SLAPrintObjectConfig &c); -sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c); +sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c); sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 581f50a88..ee0abe76f 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -134,7 +134,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& Vec3d direction; line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); - std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector hits = m_emesh.query_ray_hits(point, direction); if (hits.empty()) return false; // no intersection found @@ -184,7 +184,7 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector hits; + std::vector hits; // Offset the start of the ray by EPSILON to account for numerical inaccuracies. hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast(), direction_to_camera.cast()); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 2758577a2..60dcb30c8 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -3,7 +3,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" -#include "libslic3r/SLA/EigenMesh3D.hpp" +#include "libslic3r/SLA/IndexedMesh.hpp" #include "admesh/stl.h" #include "slic3r/GUI/3DScene.hpp" @@ -147,7 +147,7 @@ public: Vec3f get_triangle_normal(size_t facet_idx) const; private: - sla::EigenMesh3D m_emesh; + sla::IndexedMesh m_emesh; std::vector m_normals; }; -- cgit v1.2.3