Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::sla::DefaultSupportTree Class Reference

#include <src/libslic3r/SLA/DefaultSupportTree.hpp>

+ Collaboration diagram for Slic3r::sla::DefaultSupportTree:

Public Member Functions

 DefaultSupportTree (SupportTreeBuilder &builder, const SupportableMesh &sm)
 
void add_pinheads ()
 
void classify ()
 
void routing_to_ground ()
 
void routing_to_model ()
 
void interconnect_pillars ()
 
void merge_result ()
 

Static Public Member Functions

static bool execute (SupportTreeBuilder &builder, const SupportableMesh &sm)
 

Private Types

using PtIndices = std::vector< unsigned >
 

Private Member Functions

AABBMesh::hit_result ray_mesh_intersect (const Vec3d &s, const Vec3d &dir)
 
AABBMesh::hit_result pinhead_mesh_intersect (const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width, double safety_d)
 
AABBMesh::hit_result pinhead_mesh_intersect (const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width)
 
AABBMesh::hit_result bridge_mesh_intersect (const Vec3d &s, const Vec3d &dir, double r, double safety_d)
 
AABBMesh::hit_result bridge_mesh_intersect (const Vec3d &s, const Vec3d &dir, double r)
 
template<class... Args>
double bridge_mesh_distance (Args &&...args)
 
bool interconnect (const Pillar &pillar, const Pillar &nextpillar)
 
bool connect_to_nearpillar (const Head &head, long nearpillar_id)
 
bool connect_to_ground (Head &head)
 
bool connect_to_model_body (Head &head)
 
bool search_pillar_and_connect (const Head &source)
 
bool create_ground_pillar (const Junction &jp, const Vec3d &sourcedir, long head_id=SupportTreeNode::ID_UNSET)
 
void add_pillar_base (long pid)
 
std::optional< DiffBridgesearch_widening_path (const Vec3d &jp, const Vec3d &dir, double radius, double new_radius)
 

Private Attributes

const SupportableMeshm_sm
 
PtIndices m_iheads
 
PtIndices m_iheads_onmodel
 
std::map< unsigned, AABBMesh::hit_resultm_head_to_ground_scans
 
Eigen::MatrixXd m_support_nmls
 
std::vector< PtIndicesm_pillar_clusters
 
SupportTreeBuilderm_builder
 
Eigen::MatrixXd m_points
 
ThrowOnCancel m_thr
 
PillarIndex m_pillar_index
 
execution::BlockingMutex< ExecutionTBBm_bridge_mutex
 

Detailed Description

Member Typedef Documentation

◆ PtIndices

using Slic3r::sla::DefaultSupportTree::PtIndices = std::vector<unsigned>
private

Constructor & Destructor Documentation

◆ DefaultSupportTree()

Slic3r::sla::DefaultSupportTree::DefaultSupportTree ( SupportTreeBuilder builder,
const SupportableMesh sm 
)
19 : m_sm(sm)
20 , m_support_nmls(sm.pts.size(), 3)
21 , m_builder(builder)
22 , m_points(sm.pts.size(), 3)
23 , m_thr(builder.ctl().cancelfn)
24{
25 // Prepare the support points in Eigen/IGL format as well, we will use
26 // it mostly in this form.
27
28 long i = 0;
29 for (const SupportPoint &sp : m_sm.pts) {
30 m_points.row(i).x() = double(sp.pos.x());
31 m_points.row(i).y() = double(sp.pos.y());
32 m_points.row(i).z() = double(sp.pos.z());
33 ++i;
34 }
35}
const SupportableMesh & m_sm
Definition DefaultSupportTree.hpp:59
SupportTreeBuilder & m_builder
Definition DefaultSupportTree.hpp:77
ThrowOnCancel m_thr
Definition DefaultSupportTree.hpp:84
Eigen::MatrixXd m_support_nmls
Definition DefaultSupportTree.hpp:69
Eigen::MatrixXd m_points
Definition DefaultSupportTree.hpp:80

References m_points, m_sm, and Slic3r::sla::SupportableMesh::pts.

Member Function Documentation

◆ add_pillar_base()

void Slic3r::sla::DefaultSupportTree::add_pillar_base ( long  pid)
inlineprivate
183 {
185 }
void add_pillar_base(long pid, double baseheight=3, double radius=2)
Definition SupportTreeBuilder.cpp:74
double base_height_mm
Definition SupportTree.hpp:57
double base_radius_mm
Definition SupportTree.hpp:54
SupportTreeConfig cfg
Definition SupportTree.hpp:117

References Slic3r::sla::SupportTreeBuilder::add_pillar_base(), Slic3r::sla::SupportTreeConfig::base_height_mm, Slic3r::sla::SupportTreeConfig::base_radius_mm, Slic3r::sla::SupportableMesh::cfg, m_builder, and m_sm.

+ Here is the call graph for this function:

◆ add_pinheads()

void Slic3r::sla::DefaultSupportTree::add_pinheads ( )
364{
365 // The minimum distance for two support points to remain valid.
366 const double /*constexpr*/ D_SP = 0.1;
367
368 // Get the points that are too close to each other and keep only the
369 // first one
370 auto aliases = cluster(m_points, D_SP, 2);
371
372 PtIndices filtered_indices;
373 filtered_indices.reserve(aliases.size());
374 m_iheads.reserve(aliases.size());
375 for(auto& a : aliases) {
376 // Here we keep only the front point of the cluster.
377 filtered_indices.emplace_back(a.front());
378 }
379
380 // calculate the normals to the triangles for filtered points
383 filtered_indices);
384
385 // Not all of the support points have to be a valid position for
386 // support creation. The angle may be inappropriate or there may
387 // not be enough space for the pinhead. Filtering is applied for
388 // these reasons.
389
390 auto heads = reserve_vector<Head>(m_sm.pts.size());
391 for (const SupportPoint &sp : m_sm.pts) {
392 m_thr();
393 heads.emplace_back(
394 NaNd,
395 sp.head_front_radius,
396 0.,
398 Vec3d::Zero(), // dir
399 sp.pos.cast<double>() // displacement
400 );
401 }
402
403 std::function<void(unsigned, size_t, double)> filterfn;
404 filterfn = [this, &nmls, &heads, &filterfn](unsigned fidx, size_t i, double back_r) {
405 m_thr();
406
407 Vec3d n = nmls.row(Eigen::Index(i));
408
409 // for all normals we generate the spherical coordinates and
410 // saturate the polar angle to 45 degrees from the bottom then
411 // convert back to standard coordinates to get the new normal.
412 // Then we just create a quaternion from the two normals
413 // (Quaternion::FromTwoVectors) and apply the rotation to the
414 // arrow head.
415
416 auto [polar, azimuth] = dir_to_spheric(n);
417
418 // skip if the tilt is not sane
419 if (polar < PI - m_sm.cfg.normal_cutoff_angle) return;
420
421 // We saturate the polar angle to 3pi/4
422 polar = std::max(polar, PI - m_sm.cfg.bridge_slope);
423
424 // save the head (pinpoint) position
425 Vec3d hp = m_points.row(fidx);
426
427 double lmin = m_sm.cfg.head_width_mm, lmax = lmin;
428
429 if (back_r < m_sm.cfg.head_back_radius_mm) {
430 lmin = 0., lmax = m_sm.cfg.head_penetration_mm;
431 }
432
433 // The distance needed for a pinhead to not collide with model.
434 double w = lmin + 2 * back_r + 2 * m_sm.cfg.head_front_radius_mm -
436
437 double pin_r = double(m_sm.pts[fidx].head_front_radius);
438
439 // Reassemble the now corrected normal
440 auto nn = spheric_to_dir(polar, azimuth).normalized();
441
442 // check available distance
443 AABBMesh::hit_result t = pinhead_mesh_intersect(hp, nn, pin_r, back_r, w);
444
445 if (t.distance() < w) {
446 // Let's try to optimize this angle, there might be a
447 // viable normal that doesn't collide with the model
448 // geometry and its very close to the default.
449
450 Optimizer<AlgNLoptGenetic> solver(get_criteria(m_sm.cfg));
451 solver.seed(0); // we want deterministic behavior
452
453 auto oresult = solver.to_max().optimize(
454 [this, pin_r, back_r, hp](const opt::Input<3> &input)
455 {
456 auto &[plr, azm, l] = input;
457
458 auto dir = spheric_to_dir(plr, azm).normalized();
459
461 hp, dir, pin_r, back_r, l).distance();
462 },
463 initvals({polar, azimuth, (lmin + lmax) / 2.}), // start with what we have
464 bounds({
465 {PI - m_sm.cfg.bridge_slope, PI}, // Must not exceed the slope limit
466 {-PI, PI}, // azimuth can be a full search
467 {lmin, lmax}
468 }));
469
470 if(oresult.score > w) {
471 polar = std::get<0>(oresult.optimum);
472 azimuth = std::get<1>(oresult.optimum);
473 nn = spheric_to_dir(polar, azimuth).normalized();
474 lmin = std::get<2>(oresult.optimum);
475 t = AABBMesh::hit_result(oresult.score);
476 }
477 }
478
479 if (t.distance() > w && hp.z() + w * nn.z() >= ground_level(m_sm)) {
480 Head &h = heads[fidx];
481 h.id = fidx;
482 h.dir = nn;
483 h.width_mm = lmin;
484 h.r_back_mm = back_r;
485 } else if (back_r > m_sm.cfg.head_fallback_radius_mm) {
486 filterfn(fidx, i, m_sm.cfg.head_fallback_radius_mm);
487 }
488 };
489
491 suptree_ex_policy, size_t(0), filtered_indices.size(),
492 [this, &filterfn, &filtered_indices](size_t i) {
493 filterfn(filtered_indices[i], i, m_sm.cfg.head_back_radius_mm);
494 },
496
497 for (size_t i = 0; i < heads.size(); ++i)
498 if (heads[i].is_valid()) {
499 m_builder.add_head(i, heads[i]);
500 m_iheads.emplace_back(i);
501 }
502
503 m_thr();
504}
double distance() const
Definition AABBMesh.hpp:84
std::vector< unsigned > PtIndices
Definition DefaultSupportTree.hpp:61
AABBMesh::hit_result pinhead_mesh_intersect(const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width, double safety_d)
Definition DefaultSupportTree.cpp:150
PtIndices m_iheads
Definition DefaultSupportTree.hpp:63
Head & add_head(unsigned id, Args &&... args)
Definition SupportTreeBuilder.hpp:258
typedef void(GLAPIENTRYP _GLUfuncptr)(void)
static int input(void)
static constexpr double PI
Definition libslic3r.h:58
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition Meta.h:33
Vec< 3, T > spheric_to_dir(double polar, double azimuth)
Definition Geometry.hpp:519
std::pair< Tout, Tout > dir_to_spheric(const Vec< 3, Tin > &n, Tout norm=1.)
Definition Geometry.hpp:509
void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity=1)
Definition Execution.hpp:46
size_t max_concurrency(const EP &ep)
Definition Execution.hpp:38
Bounds< N > bounds(const Bound(&b)[N])
Definition Optimizer.hpp:192
ClusteredPoints cluster(const std::vector< unsigned > &indices, std::function< Vec3d(unsigned)> pointfn, double dist, unsigned max_points)
Definition Clustering.cpp:93
constexpr const auto & suptree_ex_policy
Definition DefaultSupportTree.hpp:11
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition SpatIndex.hpp:15
double ground_level(const SupportableMesh &sm)
Definition SupportTree.hpp:134
StopCriteria get_criteria(const SupportTreeConfig &cfg)
Definition SupportTreeUtils.hpp:106
Eigen::MatrixXd normals(Ex ex_policy, const PointSet &points, const AABBMesh &mesh, double eps, std::function< void()> thr, const std::vector< unsigned > &pt_indices)
Definition MeshNormals.cpp:116
constexpr double NaNd
Definition libslic3r.h:365
tuple< Args... > initvals(Args...args)
Definition optimizer.hpp:54
bool is_valid(const FontFile &font, unsigned int index)
Definition Emboss.cpp:233
static const double constexpr normal_cutoff_angle
Definition SupportTree.hpp:96
double bridge_slope
Definition SupportTree.hpp:60
double head_back_radius_mm
Definition SupportTree.hpp:34
double head_front_radius_mm
Definition SupportTree.hpp:28
double head_penetration_mm
Definition SupportTree.hpp:31
double head_width_mm
Definition SupportTree.hpp:39
double head_fallback_radius_mm
Definition SupportTree.hpp:36
AABBMesh emesh
Definition SupportTree.hpp:115
SupportPoints pts
Definition SupportTree.hpp:116

References Slic3r::sla::SupportTreeBuilder::add_head(), Slic3r::sla::SupportTreeConfig::bridge_slope, Slic3r::sla::SupportableMesh::cfg, Slic3r::sla::cluster(), Slic3r::sla::Head::dir, Slic3r::AABBMesh::hit_result::distance(), Slic3r::sla::SupportableMesh::emesh, Slic3r::execution::for_each(), Slic3r::sla::get_criteria(), Slic3r::sla::ground_level(), Slic3r::sla::SupportTreeConfig::head_back_radius_mm, Slic3r::sla::SupportTreeConfig::head_fallback_radius_mm, Slic3r::sla::SupportTreeConfig::head_front_radius_mm, Slic3r::sla::SupportTreeConfig::head_penetration_mm, Slic3r::sla::SupportTreeConfig::head_width_mm, Slic3r::sla::SupportTreeNode::id, input(), m_builder, m_iheads, m_points, m_sm, m_thr, Slic3r::execution::max_concurrency(), Slic3r::NaNd, Slic3r::sla::SupportTreeConfig::normal_cutoff_angle, Slic3r::normals(), Slic3r::opt::Optimizer< Method, Enable >::optimize(), PI, pinhead_mesh_intersect(), Slic3r::sla::SupportableMesh::pts, Slic3r::sla::Head::r_back_mm, Slic3r::opt::Optimizer< Method, Enable >::seed(), Slic3r::sla::suptree_ex_policy, Slic3r::opt::Optimizer< Method, Enable >::to_max(), void(), and Slic3r::sla::Head::width_mm.

Referenced by execute().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bridge_mesh_distance()

template<class... Args>
double Slic3r::sla::DefaultSupportTree::bridge_mesh_distance ( Args &&...  args)
inlineprivate
153 {
154 return bridge_mesh_intersect(std::forward<Args>(args)...).distance();
155 }
AABBMesh::hit_result bridge_mesh_intersect(const Vec3d &s, const Vec3d &dir, double r, double safety_d)
Definition DefaultSupportTree.cpp:161

References bridge_mesh_intersect(), and Slic3r::AABBMesh::hit_result::distance().

Referenced by connect_to_nearpillar(), and interconnect().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ bridge_mesh_intersect() [1/2]

AABBMesh::hit_result Slic3r::sla::DefaultSupportTree::bridge_mesh_intersect ( const Vec3d s,
const Vec3d dir,
double  r 
)
inlineprivate
146 {
147 return bridge_mesh_intersect(s, dir, r,
150 }
static const double constexpr safety_distance_mm
Definition SupportTree.hpp:101

References bridge_mesh_intersect(), Slic3r::sla::SupportableMesh::cfg, Slic3r::sla::SupportTreeConfig::head_back_radius_mm, m_sm, and Slic3r::sla::SupportTreeConfig::safety_distance_mm.

+ Here is the call graph for this function:

◆ bridge_mesh_intersect() [2/2]

AABBMesh::hit_result Slic3r::sla::DefaultSupportTree::bridge_mesh_intersect ( const Vec3d s,
const Vec3d dir,
double  r,
double  safety_d 
)
private
163{
164 return sla::beam_mesh_hit(suptree_ex_policy, m_sm.emesh, {src, dir, r}, sd);
165}
Hit beam_mesh_hit(Ex policy, const AABBMesh &mesh, const Beam_< RayCount > &beam, double sd)
Definition SupportTreeUtils.hpp:146

References Slic3r::sla::beam_mesh_hit(), Slic3r::sla::SupportableMesh::emesh, m_sm, and Slic3r::sla::suptree_ex_policy.

Referenced by bridge_mesh_distance(), bridge_mesh_intersect(), and classify().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ classify()

void Slic3r::sla::DefaultSupportTree::classify ( )
507{
508 // We should first get the heads that reach the ground directly
509 PtIndices ground_head_indices;
510 ground_head_indices.reserve(m_iheads.size());
511 m_iheads_onmodel.reserve(m_iheads.size());
512
513 // First we decide which heads reach the ground and can be full
514 // pillars and which shall be connected to the model surface (or
515 // search a suitable path around the surface that leads to the
516 // ground -- TODO)
517 for(unsigned i : m_iheads) {
518 m_thr();
519
520 Head &head = m_builder.head(i);
521 double r = head.r_back_mm;
522 Vec3d headjp = head.junction_point();
523
524 // collision check
525 auto hit = bridge_mesh_intersect(headjp, DOWN, r);
526
527 if(std::isinf(hit.distance())) ground_head_indices.emplace_back(i);
528 else if(m_sm.cfg.ground_facing_only) head.invalidate();
529 else m_iheads_onmodel.emplace_back(i);
530
531 m_head_to_ground_scans[i] = hit;
532 }
533
534 // We want to search for clusters of points that are far enough
535 // from each other in the XY plane to not cross their pillar bases
536 // These clusters of support points will join in one pillar,
537 // possibly in their centroid support point.
538
539 auto pointfn = [this](unsigned i) {
540 return m_builder.head(i).junction_point();
541 };
542
543 auto predicate = [this](const PointIndexEl &e1,
544 const PointIndexEl &e2) {
545 double d2d = distance(to_2d(e1.first), to_2d(e2.first));
546 double d3d = distance(e1.first, e2.first);
547 return d2d < 2 * m_sm.cfg.base_radius_mm
549 };
550
551 m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate,
553}
EIGEN_DEVICE_FUNC SegmentReturnType head(Index n)
This is the const version of head(Index).
Definition BlockMethods.h:919
std::map< unsigned, AABBMesh::hit_result > m_head_to_ground_scans
Definition DefaultSupportTree.hpp:66
std::vector< PtIndices > m_pillar_clusters
Definition DefaultSupportTree.hpp:73
PtIndices m_iheads_onmodel
Definition DefaultSupportTree.hpp:64
Head & head(unsigned id)
Definition SupportTreeBuilder.hpp:380
std::pair< Vec3d, unsigned > PointIndexEl
Definition SpatIndex.hpp:16
T distance(const Vec< I, T > &p)
Definition SupportTreeBuilder.hpp:52
const Vec3d DOWN
Definition SupportTreeBuilder.hpp:61
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > to_2d(const Eigen::MatrixBase< Derived > &ptN)
Definition Point.hpp:121
Vec3d junction_point() const
Definition SupportTreeBuilder.hpp:122
unsigned max_bridges_on_pillar
Definition SupportTree.hpp:76
double max_bridge_length_mm
Definition SupportTree.hpp:63
bool ground_facing_only
Definition SupportTree.hpp:45

References Slic3r::sla::SupportTreeConfig::base_radius_mm, bridge_mesh_intersect(), Slic3r::sla::SupportableMesh::cfg, Slic3r::sla::cluster(), Slic3r::sla::distance(), Slic3r::sla::DOWN, Slic3r::sla::SupportTreeConfig::ground_facing_only, head(), Slic3r::sla::SupportTreeBuilder::head(), Slic3r::sla::Head::junction_point(), m_builder, m_head_to_ground_scans, m_iheads, m_iheads_onmodel, m_pillar_clusters, m_sm, m_thr, Slic3r::sla::SupportTreeConfig::max_bridge_length_mm, Slic3r::sla::SupportTreeConfig::max_bridges_on_pillar, and Slic3r::to_2d().

Referenced by execute().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ connect_to_ground()

bool Slic3r::sla::DefaultSupportTree::connect_to_ground ( Head head)
inlineprivate
629{
630 auto [ret, pillar_id] = sla::search_ground_route(suptree_ex_policy,
632 {head.junction_point(),
633 head.r_back_mm},
634 head.r_back_mm,
635 head.dir);
636
637 if (pillar_id >= 0) {
638 // Save the pillar endpoint in the spatial index
640 unsigned(pillar_id));
641
642 head.pillar_id = pillar_id;
643 }
644
645 return ret;
646}
PillarIndex m_pillar_index
Definition DefaultSupportTree.hpp:87
void guarded_insert(Args &&...args)
Definition DefaultSupportTree.hpp:20
IntegerOnly< T, const Pillar & > pillar(T id) const
Definition SupportTreeBuilder.hpp:399
std::pair< bool, long > search_ground_route(Ex policy, SupportTreeBuilder &builder, const SupportableMesh &sm, const Junction &j, double end_radius, const Vec3d &init_dir=DOWN)
Definition SupportTreeUtilsLegacy.hpp:261

References Slic3r::sla::PillarIndex::guarded_insert(), head(), m_builder, m_pillar_index, m_sm, Slic3r::sla::SupportTreeBuilder::pillar(), Slic3r::sla::search_ground_route(), and Slic3r::sla::suptree_ex_policy.

Referenced by routing_to_model().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ connect_to_model_body()

bool Slic3r::sla::DefaultSupportTree::connect_to_model_body ( Head head)
private
649{
650 if (head.id <= SupportTreeNode::ID_UNSET) return false;
651
652 auto it = m_head_to_ground_scans.find(unsigned(head.id));
653 if (it == m_head_to_ground_scans.end()) return false;
654
655 auto &hit = it->second;
656
657 if (!hit.is_hit()) {
658 // TODO scan for potential anchor points on model surface
659 return false;
660 }
661
662 Vec3d hjp = head.junction_point();
663 double zangle = std::asin(hit.direction().z());
664 zangle = std::max(zangle, PI/4);
665 double h = std::sin(zangle) * head.fullwidth();
666
667 // The width of the tail head that we would like to have...
668 h = std::min(hit.distance() - head.r_back_mm, h);
669
670 // If this is a mini pillar dont bother with the tail width, can be 0.
671 if (head.r_back_mm < m_sm.cfg.head_back_radius_mm) h = std::max(h, 0.);
672 else if (h <= 0.) return false;
673
674 Vec3d endp{hjp.x(), hjp.y(), hjp.z() - hit.distance() + h};
675 auto center_hit = m_sm.emesh.query_ray_hit(hjp, DOWN);
676
677 double hitdiff = center_hit.distance() - hit.distance();
678 Vec3d hitp = std::abs(hitdiff) < 2*head.r_back_mm?
679 center_hit.position() : hit.position();
680
681 long pillar_id = m_builder.add_pillar(head.id, hjp.z() - endp.z());
682 Pillar &pill = m_builder.pillar(pillar_id);
683
684 Vec3d taildir = endp - hitp;
685 double dist = (hitp - endp).norm() + m_sm.cfg.head_penetration_mm;
686 double w = dist - 2 * head.r_pin_mm - head.r_back_mm;
687
688 if (w < 0.) {
689 BOOST_LOG_TRIVIAL(warning) << "Pinhead width is negative!";
690 w = 0.;
691 }
692
693 m_builder.add_anchor(head.r_back_mm, head.r_pin_mm, w,
694 m_sm.cfg.head_penetration_mm, taildir, hitp);
695
696 m_pillar_index.guarded_insert(pill.endpoint(), pill.id);
697
698 return true;
699}
hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const
Definition AABBMesh.cpp:152
long add_pillar(long headid, double length)
Definition SupportTreeBuilder.hpp:271
const Anchor & add_anchor(Args &&...args)
Definition SupportTreeBuilder.hpp:295
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:280
static const constexpr long ID_UNSET
Definition SupportTreeBuilder.hpp:65

References Slic3r::sla::SupportTreeBuilder::add_anchor(), Slic3r::sla::SupportTreeBuilder::add_pillar(), Slic3r::sla::SupportableMesh::cfg, Slic3r::AABBMesh::hit_result::distance(), Slic3r::sla::DOWN, Slic3r::sla::SupportableMesh::emesh, Slic3r::sla::Pillar::endpoint(), Slic3r::sla::PillarIndex::guarded_insert(), head(), Slic3r::sla::SupportTreeConfig::head_back_radius_mm, Slic3r::sla::SupportTreeConfig::head_penetration_mm, Slic3r::sla::SupportTreeNode::id, Slic3r::sla::SupportTreeNode::ID_UNSET, m_builder, m_head_to_ground_scans, m_pillar_index, m_sm, PI, Slic3r::sla::SupportTreeBuilder::pillar(), and Slic3r::AABBMesh::query_ray_hit().

Referenced by routing_to_model().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ connect_to_nearpillar()

bool Slic3r::sla::DefaultSupportTree::connect_to_nearpillar ( const Head head,
long  nearpillar_id 
)
private
262{
263 auto nearpillar = [this, nearpillar_id]() -> const Pillar& {
264 return m_builder.pillar(nearpillar_id);
265 };
266
268 return false;
269
270 Vec3d headjp = head.junction_point();
271 Vec3d nearjp_u = nearpillar().startpoint();
272 Vec3d nearjp_l = nearpillar().endpoint();
273
274 double r = head.r_back_mm;
275 double d2d = distance(to_2d(headjp), to_2d(nearjp_u));
276 double d3d = distance(headjp, nearjp_u);
277
278 double hdiff = nearjp_u.z() - headjp.z();
279 double slope = std::atan2(hdiff, d2d);
280
281 Vec3d bridgestart = headjp;
282 Vec3d bridgeend = nearjp_u;
284 double max_slope = m_sm.cfg.bridge_slope;
285 double zdiff = 0.0;
286
287 // check the default situation if feasible for a bridge
288 if(d3d > max_len || slope > -max_slope) {
289 // not feasible to connect the two head junctions. We have to search
290 // for a suitable touch point.
291
292 double Zdown = headjp.z() + d2d * std::tan(-max_slope);
293 Vec3d touchjp = bridgeend; touchjp.z() = Zdown;
294 double D = distance(headjp, touchjp);
295 zdiff = Zdown - nearjp_u.z();
296
297 if(zdiff > 0) {
298 Zdown -= zdiff;
299 bridgestart.z() -= zdiff;
300 touchjp.z() = Zdown;
301
302 double t = bridge_mesh_distance(headjp, DOWN, r);
303
304 // We can't insert a pillar under the source head to connect
305 // with the nearby pillar's starting junction
306 if(t < zdiff) return false;
307 }
308
309 if(Zdown <= nearjp_u.z() && Zdown >= nearjp_l.z() && D < max_len)
310 bridgeend.z() = Zdown;
311 else
312 return false;
313 }
314
315 // There will be a minimum distance from the ground where the
316 // bridge is allowed to connect. This is an empiric value.
317 double minz = ground_level(m_sm) + 4 * head.r_back_mm;
318 if(bridgeend.z() < minz) return false;
319
320 double t = bridge_mesh_distance(bridgestart, dirv(bridgestart, bridgeend), r);
321
322 // Cannot insert the bridge. (further search might not worth the hassle)
323 if(t < distance(bridgestart, bridgeend)) return false;
324
325 std::lock_guard lk(m_bridge_mutex);
326
327 if (m_builder.bridgecount(nearpillar()) < m_sm.cfg.max_bridges_on_pillar) {
328 // A partial pillar is needed under the starting head.
329 if(zdiff > 0) {
330 m_builder.add_pillar(head.id, headjp.z() - bridgestart.z());
331 m_builder.add_junction(bridgestart, r);
332 m_builder.add_bridge(bridgestart, bridgeend, r);
333 } else {
334 m_builder.add_bridge(head.id, bridgeend);
335 }
336
337 m_builder.increment_bridges(nearpillar());
338 } else return false;
339
340 return true;
341}
execution::BlockingMutex< ExecutionTBB > m_bridge_mutex
Definition DefaultSupportTree.hpp:90
double bridge_mesh_distance(Args &&...args)
Definition DefaultSupportTree.hpp:153
const Bridge & add_bridge(const Vec3d &s, const Vec3d &e, double r)
Definition SupportTreeBuilder.hpp:351
const Junction & add_junction(Args &&... args)
Definition SupportTreeBuilder.hpp:342
void increment_bridges(const Pillar &pillar)
Definition SupportTreeBuilder.hpp:304
unsigned bridgecount(const Pillar &pillar) const
Definition SupportTreeBuilder.hpp:322
Vec< N, T > dirv(const Vec< N, T > &startp, const Vec< N, T > &endp)
Definition SupportTreeUtils.hpp:91

References Slic3r::sla::SupportTreeBuilder::add_bridge(), Slic3r::sla::SupportTreeBuilder::add_junction(), Slic3r::sla::SupportTreeBuilder::add_pillar(), bridge_mesh_distance(), Slic3r::sla::SupportTreeConfig::bridge_slope, Slic3r::sla::SupportTreeBuilder::bridgecount(), Slic3r::sla::SupportableMesh::cfg, Slic3r::sla::dirv(), Slic3r::sla::distance(), Slic3r::sla::DOWN, Slic3r::sla::ground_level(), head(), Slic3r::sla::SupportTreeConfig::head_back_radius_mm, Slic3r::sla::SupportTreeBuilder::increment_bridges(), m_bridge_mutex, m_builder, m_sm, Slic3r::sla::SupportTreeConfig::max_bridge_length_mm, Slic3r::sla::SupportTreeConfig::max_bridges_on_pillar, Slic3r::sla::SupportTreeBuilder::pillar(), and Slic3r::to_2d().

Referenced by routing_to_ground(), and search_pillar_and_connect().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ create_ground_pillar()

bool Slic3r::sla::DefaultSupportTree::create_ground_pillar ( const Junction jp,
const Vec3d sourcedir,
long  head_id = SupportTreeNode::ID_UNSET 
)
private
346{
347 auto [ret, pillar_id] = sla::create_ground_pillar(suptree_ex_policy,
348 m_builder,
349 m_sm,
350 hjp.pos,
351 sourcedir,
352 hjp.r,
353 hjp.r,
354 head_id);
355
356 if (pillar_id >= 0) // Save the pillar endpoint in the spatial index
358 unsigned(pillar_id));
359
360 return ret;
361}
std::pair< bool, long > create_ground_pillar(Ex policy, SupportTreeBuilder &builder, const SupportableMesh &sm, const Vec3d &pinhead_junctionpt, const Vec3d &sourcedir, double radius, double end_radius, long head_id=SupportTreeNode::ID_UNSET)
Definition SupportTreeUtilsLegacy.hpp:97

References Slic3r::sla::create_ground_pillar(), Slic3r::sla::PillarIndex::guarded_insert(), m_builder, m_pillar_index, m_sm, Slic3r::sla::SupportTreeBuilder::pillar(), Slic3r::sla::Junction::pos, Slic3r::sla::Junction::r, and Slic3r::sla::suptree_ex_policy.

Referenced by routing_to_ground().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ execute()

bool Slic3r::sla::DefaultSupportTree::execute ( SupportTreeBuilder builder,
const SupportableMesh sm 
)
static
39{
40 if(sm.pts.empty()) return false;
41
42 DefaultSupportTree alg(builder, sm);
43
44 // Let's define the individual steps of the processing. We can experiment
45 // later with the ordering and the dependencies between them.
46 enum Steps {
47 BEGIN,
48 PINHEADS,
49 CLASSIFY,
50 ROUTING_GROUND,
51 ROUTING_NONGROUND,
52 CASCADE_PILLARS,
53 MERGE_RESULT,
54 DONE,
55 ABORT,
56 NUM_STEPS
57 //...
58 };
59
60 // Collect the algorithm steps into a nice sequence
61 std::array<std::function<void()>, NUM_STEPS> program = {
62 [] () {
63 // Begin...
64 // Potentially clear up the shared data (not needed for now)
65 },
66
67 std::bind(&DefaultSupportTree::add_pinheads, &alg),
68
69 std::bind(&DefaultSupportTree::classify, &alg),
70
72
74
76
77 std::bind(&DefaultSupportTree::merge_result, &alg),
78
79 [] () {
80 // Done
81 },
82
83 [] () {
84 // Abort
85 }
86 };
87
88 Steps pc = BEGIN;
89
90 if(sm.cfg.ground_facing_only) {
91 program[ROUTING_NONGROUND] = []() {
92 BOOST_LOG_TRIVIAL(info)
93 << "Skipping model-facing supports as requested.";
94 };
95 }
96
97 // Let's define a simple automaton that will run our program.
98 auto progress = [&builder, &pc] () {
99 static const std::array<std::string, NUM_STEPS> stepstr {
100 "Starting",
101 "Generate pinheads",
102 "Classification",
103 "Routing to ground",
104 "Routing supports to model surface",
105 "Interconnecting pillars",
106 "Merging support mesh",
107 "Done",
108 "Abort"
109 };
110
111 static const std::array<unsigned, NUM_STEPS> stepstate {
112 0,
113 30,
114 50,
115 60,
116 70,
117 80,
118 99,
119 100,
120 0
121 };
122
123 if(builder.ctl().stopcondition()) pc = ABORT;
124
125 switch(pc) {
126 case BEGIN: pc = PINHEADS; break;
127 case PINHEADS: pc = CLASSIFY; break;
128 case CLASSIFY: pc = ROUTING_GROUND; break;
129 case ROUTING_GROUND: pc = ROUTING_NONGROUND; break;
130 case ROUTING_NONGROUND: pc = CASCADE_PILLARS; break;
131 case CASCADE_PILLARS: pc = MERGE_RESULT; break;
132 case MERGE_RESULT: pc = DONE; break;
133 case DONE:
134 case ABORT: break;
135 default: ;
136 }
137
138 builder.ctl().statuscb(stepstate[pc], stepstr[pc]);
139 };
140
141 // Just here we run the computation...
142 while(pc < DONE) {
143 progress();
144 program[pc]();
145 }
146
147 return pc == ABORT;
148}
void merge_result()
Definition DefaultSupportTree.hpp:233
void add_pinheads()
Definition DefaultSupportTree.cpp:363
void routing_to_ground()
Definition DefaultSupportTree.cpp:555
DefaultSupportTree(SupportTreeBuilder &builder, const SupportableMesh &sm)
Definition DefaultSupportTree.cpp:17
void classify()
Definition DefaultSupportTree.cpp:506
void routing_to_model()
Definition DefaultSupportTree.cpp:738
void interconnect_pillars()
Definition DefaultSupportTree.cpp:770
#define BEGIN
Definition lexer.c:117

References add_pinheads(), BEGIN, Slic3r::sla::SupportableMesh::cfg, classify(), Slic3r::sla::SupportTreeBuilder::ctl(), Slic3r::sla::SupportTreeConfig::ground_facing_only, interconnect_pillars(), merge_result(), Slic3r::sla::SupportableMesh::pts, routing_to_ground(), routing_to_model(), Slic3r::sla::JobController::statuscb, Slic3r::sla::JobController::stopcondition, and void().

Referenced by Slic3r::sla::create_default_tree().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ interconnect()

bool Slic3r::sla::DefaultSupportTree::interconnect ( const Pillar pillar,
const Pillar nextpillar 
)
private
169{
170 // We need to get the starting point of the zig-zag pattern. We have to
171 // be aware that the two head junctions are at different heights. We
172 // may start from the lowest junction and call it a day but this
173 // strategy would leave unconnected a lot of pillar duos where the
174 // shorter pillar is too short to start a new bridge but the taller
175 // pillar could still be bridged with the shorter one.
176 bool was_connected = false;
177
178 Vec3d supper = pillar.startpoint();
179 Vec3d slower = nextpillar.startpoint();
180 Vec3d eupper = pillar.endpoint();
181 Vec3d elower = nextpillar.endpoint();
182
183 double zmin = ground_level(m_sm) + m_sm.cfg.base_height_mm;
184 eupper.z() = std::max(eupper.z(), zmin);
185 elower.z() = std::max(elower.z(), zmin);
186
187 // The usable length of both pillars should be positive
188 if(slower.z() - elower.z() < 0) return false;
189 if(supper.z() - eupper.z() < 0) return false;
190
191 double pillar_dist = distance(Vec2d{slower.x(), slower.y()},
192 Vec2d{supper.x(), supper.y()});
193 double bridge_distance = pillar_dist / std::cos(-m_sm.cfg.bridge_slope);
194 double zstep = pillar_dist * std::tan(-m_sm.cfg.bridge_slope);
195
196 if(pillar_dist < 2 * m_sm.cfg.head_back_radius_mm ||
197 pillar_dist > m_sm.cfg.max_pillar_link_distance_mm) return false;
198
199 if(supper.z() < slower.z()) supper.swap(slower);
200 if(eupper.z() < elower.z()) eupper.swap(elower);
201
202 double startz = 0, endz = 0;
203
204 startz = slower.z() - zstep < supper.z() ? slower.z() - zstep : slower.z();
205 endz = eupper.z() + zstep > elower.z() ? eupper.z() + zstep : eupper.z();
206
207 if(slower.z() - eupper.z() < std::abs(zstep)) {
208 // no space for even one cross
209
210 // Get max available space
211 startz = std::min(supper.z(), slower.z() - zstep);
212 endz = std::max(eupper.z() + zstep, elower.z());
213
214 // Align to center
215 double available_dist = (startz - endz);
216 double rounds = std::floor(available_dist / std::abs(zstep));
217 startz -= 0.5 * (available_dist - rounds * std::abs(zstep));
218 }
219
221 bool docrosses =
224 pillar_dist > 2*m_sm.cfg.base_radius_mm);
225
226 // 'sj' means starting junction, 'ej' is the end junction of a bridge.
227 // They will be swapped in every iteration thus the zig-zag pattern.
228 // According to a config parameter, a second bridge may be added which
229 // results in a cross connection between the pillars.
230 Vec3d sj = supper, ej = slower; sj.z() = startz; ej.z() = sj.z() + zstep;
231
232 // TODO: This is a workaround to not have a faulty last bridge
233 while(ej.z() >= eupper.z() /*endz*/) {
234 if(bridge_mesh_distance(sj, dirv(sj, ej), pillar.r_start) >= bridge_distance)
235 {
236 m_builder.add_crossbridge(sj, ej, pillar.r_start);
237 was_connected = true;
238 }
239
240 // double bridging: (crosses)
241 if(docrosses) {
242 Vec3d sjback(ej.x(), ej.y(), sj.z());
243 Vec3d ejback(sj.x(), sj.y(), ej.z());
244 if (sjback.z() <= slower.z() && ejback.z() >= eupper.z() &&
245 bridge_mesh_distance(sjback, dirv(sjback, ejback),
246 pillar.r_start) >= bridge_distance) {
247 // need to check collision for the cross stick
248 m_builder.add_crossbridge(sjback, ejback, pillar.r_start);
249 was_connected = true;
250 }
251 }
252
253 sj.swap(ej);
254 ej.z() = sj.z() + zstep;
255 }
256
257 return was_connected;
258}
const Bridge & add_crossbridge(Args &&... args)
Definition SupportTreeBuilder.hpp:370
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:51
PillarConnectionMode pillar_connection_mode
Definition SupportTree.hpp:42
double max_pillar_link_distance_mm
Definition SupportTree.hpp:66

References Slic3r::sla::SupportTreeBuilder::add_crossbridge(), Slic3r::sla::SupportTreeConfig::base_height_mm, Slic3r::sla::SupportTreeConfig::base_radius_mm, bridge_mesh_distance(), Slic3r::sla::SupportTreeConfig::bridge_slope, Slic3r::sla::SupportableMesh::cfg, Slic3r::sla::cross, Slic3r::sla::dirv(), Slic3r::sla::distance(), Slic3r::sla::dynamic, Slic3r::sla::Pillar::endpoint(), Slic3r::sla::ground_level(), Slic3r::sla::SupportTreeConfig::head_back_radius_mm, m_builder, m_sm, Slic3r::sla::SupportTreeConfig::max_pillar_link_distance_mm, Slic3r::sla::SupportTreeConfig::pillar_connection_mode, Slic3r::sla::Pillar::r_start, Slic3r::sla::Pillar::startpoint(), and Eigen::PlainObjectBase< Derived >::swap().

+ Here is the call graph for this function:

◆ interconnect_pillars()

void Slic3r::sla::DefaultSupportTree::interconnect_pillars ( )
771{
772 // Now comes the algorithm that connects pillars with each other.
773 // Ideally every pillar should be connected with at least one of its
774 // neighbors if that neighbor is within max_pillar_link_distance
775
776 // Pillars with height exceeding H1 will require at least one neighbor
777 // to connect with. Height exceeding H2 require two neighbors.
781
782 // A connection between two pillars only counts if the height ratio is
783 // bigger than 50%
784 double min_height_ratio = 0.5;
785
786 std::set<unsigned long> pairs;
787
788 // A function to connect one pillar with its neighbors. THe number of
789 // neighbors is given in the configuration. This function if called
790 // for every pillar in the pillar index. A pair of pillar will not
791 // be connected multiple times this is ensured by the 'pairs' set which
792 // remembers the processed pillar pairs
793 auto cascadefn =
794 [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el)
795 {
796 Vec3d qp = el.first; // endpoint of the pillar
797
798 const Pillar& pillar = m_builder.pillar(el.second); // actual pillar
799
800 // Get the max number of neighbors a pillar should connect to
801 unsigned neighbors = m_sm.cfg.pillar_cascade_neighbors;
802
803 // connections are already enough for the pillar
804 if(pillar.links >= neighbors) return;
805
806 double max_d = d * pillar.r_start / m_sm.cfg.head_back_radius_mm;
807 // Query all remaining points within reach
808 auto qres = m_pillar_index.query([qp, max_d](const PointIndexEl& e){
809 return distance(e.first, qp) < max_d;
810 });
811
812 // sort the result by distance (have to check if this is needed)
813 std::sort(qres.begin(), qres.end(),
814 [qp](const PointIndexEl& e1, const PointIndexEl& e2){
815 return distance(e1.first, qp) < distance(e2.first, qp);
816 });
817
818 for(auto& re : qres) { // process the queried neighbors
819
820 if(re.second == el.second) continue; // Skip self
821
822 auto a = el.second, b = re.second;
823
824 // Get unique hash for the given pair (order doesn't matter)
825 auto hashval = pairhash(a, b);
826
827 // Search for the pair amongst the remembered pairs
828 if(pairs.find(hashval) != pairs.end()) continue;
829
830 const Pillar& neighborpillar = m_builder.pillar(re.second);
831
832 // this neighbor is occupied, skip
833 if (neighborpillar.links >= neighbors) continue;
834 if (neighborpillar.r_start < pillar.r_start) continue;
835
836 if(interconnect(pillar, neighborpillar)) {
837 pairs.insert(hashval);
838
839 // If the interconnection length between the two pillars is
840 // less than 50% of the longer pillar's height, don't count
841 if(pillar.height < H1 ||
842 neighborpillar.height / pillar.height > min_height_ratio)
844
845 if(neighborpillar.height < H1 ||
846 pillar.height / neighborpillar.height > min_height_ratio)
847 m_builder.increment_links(neighborpillar);
848
849 }
850
851 // connections are enough for one pillar
852 if(pillar.links >= neighbors) break;
853 }
854 };
855
856 // Run the cascade for the pillars in the index
857 m_pillar_index.foreach(cascadefn);
858
859 // We would be done here if we could allow some pillars to not be
860 // connected with any neighbors. But this might leave the support tree
861 // unprintable.
862 //
863 // The current solution is to insert additional pillars next to these
864 // lonely pillars. One or even two additional pillar might get inserted
865 // depending on the length of the lonely pillar.
866
867 size_t pillarcount = m_builder.pillarcount();
868
869 // Again, go through all pillars, this time in the whole support tree
870 // not just the index.
871 for(size_t pid = 0; pid < pillarcount; pid++) {
872 auto pillar = [this, pid]() { return m_builder.pillar(pid); };
873
874 // Decide how many additional pillars will be needed:
875
876 unsigned needpillars = 0;
877 if (pillar().bridges > m_sm.cfg.max_bridges_on_pillar)
878 needpillars = 3;
879 else if (pillar().links < 2 && pillar().height > H2) {
880 // Not enough neighbors to support this pillar
881 needpillars = 2;
882 } else if (pillar().links < 1 && pillar().height > H1) {
883 // No neighbors could be found and the pillar is too long.
884 needpillars = 1;
885 }
886
887 needpillars = std::max(pillar().links, needpillars) - pillar().links;
888 if (needpillars == 0) continue;
889
890 // Search for new pillar locations:
891
892 bool found = false;
893 double alpha = 0; // goes to 2Pi
894 double r = 2 * m_sm.cfg.base_radius_mm;
895 Vec3d pillarsp = pillar().startpoint();
896
897 // temp value for starting point detection
898 Vec3d sp(pillarsp.x(), pillarsp.y(), pillarsp.z() - r);
899
900 // A vector of bool for placement feasbility
901 std::vector<bool> canplace(needpillars, false);
902 std::vector<Vec3d> spts(needpillars); // vector of starting points
903
904 double gnd = ground_level(m_sm);
905 double min_dist = m_sm.cfg.pillar_base_safety_distance_mm +
907
908 while(!found && alpha < 2*PI) {
909 for (unsigned n = 0;
910 n < needpillars && (!n || canplace[n - 1]);
911 n++)
912 {
913 double a = alpha + n * PI / 3;
914 Vec3d s = sp;
915 s.x() += std::cos(a) * r;
916 s.y() += std::sin(a) * r;
917 spts[n] = s;
918
919 // Check the path vertically down
920 Vec3d check_from = s + Vec3d{0., 0., pillar().r_start};
921 auto hr = bridge_mesh_intersect(check_from, DOWN, pillar().r_start);
922 Vec3d gndsp{s.x(), s.y(), gnd};
923
924 // If the path is clear, check for pillar base collisions
925 canplace[n] = std::isinf(hr.distance()) &&
926 std::sqrt(m_sm.emesh.squared_distance(gndsp)) >
927 min_dist;
928 }
929
930 found = std::all_of(canplace.begin(), canplace.end(),
931 [](bool v) { return v; });
932
933 // 20 angles will be tried...
934 alpha += 0.1 * PI;
935 }
936
937 std::vector<long> newpills;
938 newpills.reserve(needpillars);
939
940 if (found)
941 for (unsigned n = 0; n < needpillars; n++) {
942 Vec3d s = spts[n];
943 Pillar p(Vec3d{s.x(), s.y(), gnd}, s.z() - gnd, pillar().r_start);
944
945 if (interconnect(pillar(), p)) {
946 Pillar &pp = m_builder.pillar(m_builder.add_pillar(p));
947
948 add_pillar_base(pp.id);
949
950 m_pillar_index.insert(pp.endpoint(), unsigned(pp.id));
951
952 m_builder.add_junction(s, pillar().r_start);
953 double t = bridge_mesh_distance(pillarsp, dirv(pillarsp, s),
954 pillar().r_start);
955 if (distance(pillarsp, s) < t)
956 m_builder.add_bridge(pillarsp, s, pillar().r_start);
957
958 if (pillar().endpoint().z() > ground_level(m_sm) + pillar().r_start)
959 m_builder.add_junction(pillar().endpoint(), pillar().r_start);
960
961 newpills.emplace_back(pp.id);
962 m_builder.increment_links(pillar());
964 }
965 }
966
967 if(!newpills.empty()) {
968 for(auto it = newpills.begin(), nx = std::next(it);
969 nx != newpills.end(); ++it, ++nx) {
970 const Pillar& itpll = m_builder.pillar(*it);
971 const Pillar& nxpll = m_builder.pillar(*nx);
972 if(interconnect(itpll, nxpll)) {
975 }
976 }
977
978 m_pillar_index.foreach(cascadefn);
979 }
980 }
981}
double squared_distance(const Vec3d &p, int &i, Vec3d &c) const
Definition AABBMesh.cpp:313
void add_pillar_base(long pid)
Definition DefaultSupportTree.hpp:182
bool interconnect(const Pillar &pillar, const Pillar &nextpillar)
Definition DefaultSupportTree.cpp:167
void foreach(Fn fn)
Definition DefaultSupportTree.hpp:44
void insert(Args &&...args)
Definition DefaultSupportTree.hpp:33
std::vector< PointIndexEl > query(Args &&...args) const
Definition DefaultSupportTree.hpp:39
void increment_links(const Pillar &pillar)
Definition SupportTreeBuilder.hpp:313
size_t pillarcount() const
Definition SupportTreeBuilder.hpp:389
static constexpr double EPSILON
Definition libslic3r.h:51
IntegerOnly< DoubleI > pairhash(I a, I b)
Definition SupportTreeUtilsLegacy.hpp:19
static const double constexpr max_dual_pillar_height_mm
Definition SupportTree.hpp:104
static const unsigned constexpr pillar_cascade_neighbors
Definition SupportTree.hpp:107
double pillar_base_safety_distance_mm
Definition SupportTree.hpp:74
static const double constexpr max_solo_pillar_height_mm
Definition SupportTree.hpp:103

References Slic3r::sla::dirv(), Slic3r::sla::Pillar::endpoint(), EPSILON, Slic3r::sla::ground_level(), Slic3r::sla::Pillar::height, Slic3r::sla::SupportTreeNode::id, Slic3r::sla::Pillar::links, Slic3r::sla::pairhash(), PI, and Slic3r::sla::Pillar::r_start.

Referenced by execute().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ merge_result()

void Slic3r::sla::DefaultSupportTree::merge_result ( )
inline
const indexed_triangle_set & merged_mesh(size_t steps=45) const
Definition SupportTreeBuilder.cpp:86

References m_builder, and Slic3r::sla::SupportTreeBuilder::merged_mesh().

Referenced by execute().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pinhead_mesh_intersect() [1/2]

AABBMesh::hit_result Slic3r::sla::DefaultSupportTree::pinhead_mesh_intersect ( const Vec3d s,
const Vec3d dir,
double  r_pin,
double  r_back,
double  width 
)
inlineprivate
122 {
123 return pinhead_mesh_intersect(s, dir, r_pin, r_back, width,
124 r_back * m_sm.cfg.safety_distance_mm /
126 }

References Slic3r::sla::SupportableMesh::cfg, Slic3r::sla::SupportTreeConfig::head_back_radius_mm, m_sm, pinhead_mesh_intersect(), and Slic3r::sla::SupportTreeConfig::safety_distance_mm.

+ Here is the call graph for this function:

◆ pinhead_mesh_intersect() [2/2]

AABBMesh::hit_result Slic3r::sla::DefaultSupportTree::pinhead_mesh_intersect ( const Vec3d s,
const Vec3d dir,
double  r_pin,
double  r_back,
double  width,
double  safety_d 
)
private
157{
158 return sla::pinhead_mesh_hit(suptree_ex_policy, m_sm.emesh, s, dir, r_pin, r_back, width, sd);
159}
Hit pinhead_mesh_hit(Ex ex, const AABBMesh &mesh, const Vec3d &s, const Vec3d &dir, double r_pin, double r_back, double width, double sd)
Definition SupportTreeUtils.hpp:193

References Slic3r::sla::SupportableMesh::emesh, m_sm, Slic3r::sla::pinhead_mesh_hit(), and Slic3r::sla::suptree_ex_policy.

Referenced by add_pinheads(), and pinhead_mesh_intersect().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ray_mesh_intersect()

AABBMesh::hit_result Slic3r::sla::DefaultSupportTree::ray_mesh_intersect ( const Vec3d s,
const Vec3d dir 
)
inlineprivate
94 {
95 return m_sm.emesh.query_ray_hit(s, dir);
96 }

References Slic3r::sla::SupportableMesh::emesh, m_sm, and Slic3r::AABBMesh::query_ray_hit().

+ Here is the call graph for this function:

◆ routing_to_ground()

void Slic3r::sla::DefaultSupportTree::routing_to_ground ( )
556{
557 ClusterEl cl_centroids;
558 cl_centroids.reserve(m_pillar_clusters.size());
559
560 for (auto &cl : m_pillar_clusters) {
561 m_thr();
562
563 // place all the centroid head positions into the index. We
564 // will query for alternative pillar positions. If a sidehead
565 // cannot connect to the cluster centroid, we have to search
566 // for another head with a full pillar. Also when there are two
567 // elements in the cluster, the centroid is arbitrary and the
568 // sidehead is allowed to connect to a nearby pillar to
569 // increase structural stability.
570
571 if (cl.empty()) continue;
572
573 // get the current cluster centroid
574 auto & thr = m_thr;
575 const auto &points = m_points;
576
577 long lcid = cluster_centroid(
578 cl, [&points](size_t idx) { return points.row(long(idx)); },
579 [thr](const Vec3d &p1, const Vec3d &p2) {
580 thr();
581 return distance(Vec2d(p1.x(), p1.y()), Vec2d(p2.x(), p2.y()));
582 });
583
584 assert(lcid >= 0);
585 unsigned hid = cl[size_t(lcid)]; // Head ID
586
587 cl_centroids.emplace_back(hid);
588
589 Head &h = m_builder.head(hid);
590
591 if (!create_ground_pillar(h.junction(), h.dir, h.id)) {
592 BOOST_LOG_TRIVIAL(warning)
593 << "Pillar cannot be created for support point id: " << hid;
594 m_iheads_onmodel.emplace_back(h.id);
595 continue;
596 }
597 }
598
599 // now we will go through the clusters ones again and connect the
600 // sidepoints with the cluster centroid (which is a ground pillar)
601 // or a nearby pillar if the centroid is unreachable.
602 size_t ci = 0;
603 for (const std::vector<unsigned> &cl : m_pillar_clusters) {
604 m_thr();
605
606 auto cidx = cl_centroids[ci++];
607
608 auto q = m_pillar_index.query(m_builder.head(cidx).junction_point(), 1);
609 if (!q.empty()) {
610 long centerpillarID = q.front().second;
611 for (auto c : cl) {
612 m_thr();
613 if (c == cidx) continue;
614
615 auto &sidehead = m_builder.head(c);
616
617 if (!connect_to_nearpillar(sidehead, centerpillarID) &&
618 !search_pillar_and_connect(sidehead)) {
619 // Vec3d pend = Vec3d{pstart.x(), pstart.y(), gndlvl};
620 // Could not find a pillar, create one
621 create_ground_pillar(sidehead.junction(), sidehead.dir, sidehead.id);
622 }
623 }
624 }
625 }
626}
bool connect_to_nearpillar(const Head &head, long nearpillar_id)
Definition DefaultSupportTree.cpp:260
bool create_ground_pillar(const Junction &jp, const Vec3d &sourcedir, long head_id=SupportTreeNode::ID_UNSET)
Definition DefaultSupportTree.cpp:343
bool search_pillar_and_connect(const Head &source)
Definition DefaultSupportTree.cpp:701
long cluster_centroid(const ClusterEl &clust, PointFn pointfn, DistFn df)
Definition Clustering.hpp:33
std::vector< unsigned > ClusterEl
Definition Clustering.hpp:11

References Slic3r::sla::cluster_centroid(), connect_to_nearpillar(), create_ground_pillar(), Slic3r::sla::Head::dir, Slic3r::sla::distance(), Slic3r::sla::SupportTreeBuilder::head(), Slic3r::sla::SupportTreeNode::id, Slic3r::sla::Head::junction(), Slic3r::sla::Head::junction_point(), m_builder, m_iheads_onmodel, m_pillar_clusters, m_pillar_index, m_points, m_thr, Slic3r::sla::PillarIndex::query(), and search_pillar_and_connect().

Referenced by execute().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ routing_to_model()

void Slic3r::sla::DefaultSupportTree::routing_to_model ( )
739{
740 // We need to check if there is an easy way out to the bed surface.
741 // If it can be routed there with a bridge shorter than
742 // min_bridge_distance.
743
746 [this](const unsigned idx) {
747 m_thr();
748
749 auto &head = m_builder.head(idx);
750
751 // Search nearby pillar
752 if (search_pillar_and_connect(head)) { return; }
753
754 // Cannot connect to nearby pillar. We will try to search for
755 // a route to the ground.
756 if (connect_to_ground(head)) { return; }
757
758 // No route to the ground, so connect to the model body as a last resort
759 if (connect_to_model_body(head)) { return; }
760
761 // We have failed to route this head.
762 BOOST_LOG_TRIVIAL(warning)
763 << "Failed to route model facing support point. ID: " << idx;
764
765 head.invalidate();
766 },
768}
bool connect_to_ground(Head &head)
Definition DefaultSupportTree.cpp:628
bool connect_to_model_body(Head &head)
Definition DefaultSupportTree.cpp:648
if(!(yy_init))
Definition lexer.c:1190

References connect_to_ground(), connect_to_model_body(), Slic3r::execution::for_each(), head(), if(), m_iheads_onmodel, and Slic3r::sla::suptree_ex_policy.

Referenced by execute().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ search_pillar_and_connect()

bool Slic3r::sla::DefaultSupportTree::search_pillar_and_connect ( const Head source)
private
702{
703 // Hope that a local copy takes less time than the whole search loop.
704 // We also need to remove elements progressively from the copied index.
705 PointIndex spindex = m_pillar_index.guarded_clone();
706
707 long nearest_id = SupportTreeNode::ID_UNSET;
708
709 Vec3d querypt = source.junction_point();
710
711 while(nearest_id < 0 && !spindex.empty()) { m_thr();
712 // loop until a suitable head is not found
713 // if there is a pillar closer than the cluster center
714 // (this may happen as the clustering is not perfect)
715 // than we will bridge to this closer pillar
716
717 Vec3d qp(querypt.x(), querypt.y(), ground_level(m_sm));
718 auto qres = spindex.nearest(qp, 1);
719 if(qres.empty()) break;
720
721 auto ne = qres.front();
722 nearest_id = ne.second;
723
724 if(nearest_id >= 0) {
725 if (size_t(nearest_id) < m_builder.pillarcount()) {
726 if(!connect_to_nearpillar(source, nearest_id) ||
727 m_builder.pillar(nearest_id).r_start < source.r_back_mm) {
728 nearest_id = SupportTreeNode::ID_UNSET; // continue searching
729 spindex.remove(ne); // without the current pillar
730 }
731 }
732 }
733 }
734
735 return nearest_id >= 0;
736}
PointIndex guarded_clone()
Definition DefaultSupportTree.hpp:51

References connect_to_nearpillar(), Slic3r::sla::PointIndex::empty(), Slic3r::sla::ground_level(), Slic3r::sla::PillarIndex::guarded_clone(), Slic3r::sla::SupportTreeNode::ID_UNSET, Slic3r::sla::Head::junction_point(), m_builder, m_pillar_index, m_sm, m_thr, Slic3r::sla::PointIndex::nearest(), Slic3r::sla::SupportTreeBuilder::pillar(), Slic3r::sla::SupportTreeBuilder::pillarcount(), Slic3r::sla::Head::r_back_mm, and Slic3r::sla::PointIndex::remove().

Referenced by routing_to_ground().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ search_widening_path()

std::optional< DiffBridge > Slic3r::sla::DefaultSupportTree::search_widening_path ( const Vec3d jp,
const Vec3d dir,
double  radius,
double  new_radius 
)
inlineprivate
191 {
192 return sla::search_widening_path(suptree_ex_policy, m_sm, jp, dir, radius, new_radius);
193 }
std::optional< DiffBridge > search_widening_path(Ex policy, const SupportableMesh &sm, const Vec3d &jp, const Vec3d &dir, double radius, double new_radius)
Definition SupportTreeUtilsLegacy.hpp:36

References m_sm, Slic3r::sla::search_widening_path(), and Slic3r::sla::suptree_ex_policy.

+ Here is the call graph for this function:

Member Data Documentation

◆ m_bridge_mutex

execution::BlockingMutex<ExecutionTBB> Slic3r::sla::DefaultSupportTree::m_bridge_mutex
private

Referenced by connect_to_nearpillar().

◆ m_builder

◆ m_head_to_ground_scans

std::map<unsigned, AABBMesh::hit_result> Slic3r::sla::DefaultSupportTree::m_head_to_ground_scans
private

Referenced by classify(), and connect_to_model_body().

◆ m_iheads

PtIndices Slic3r::sla::DefaultSupportTree::m_iheads
private

Referenced by add_pinheads(), and classify().

◆ m_iheads_onmodel

PtIndices Slic3r::sla::DefaultSupportTree::m_iheads_onmodel
private

◆ m_pillar_clusters

std::vector<PtIndices> Slic3r::sla::DefaultSupportTree::m_pillar_clusters
private

Referenced by classify(), and routing_to_ground().

◆ m_pillar_index

PillarIndex Slic3r::sla::DefaultSupportTree::m_pillar_index
private

◆ m_points

Eigen::MatrixXd Slic3r::sla::DefaultSupportTree::m_points
private

◆ m_sm

◆ m_support_nmls

Eigen::MatrixXd Slic3r::sla::DefaultSupportTree::m_support_nmls
private

◆ m_thr

ThrowOnCancel Slic3r::sla::DefaultSupportTree::m_thr
private

The documentation for this class was generated from the following files: