OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::TreeSupport Class Reference

Generates a tree structure to support your models. More...

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Support/TreeSupport.hpp>

Collaboration diagram for Slic3r::TreeSupport:

Public Types

enum  OverhangType { Detected = 0 , Enforced , SharpTail }

Public Member Functions

 TreeSupport (PrintObject &object, const SlicingParameters &slicing_params)
 Creates an instance of the tree support generator.
void move_bounds_to_contact_nodes (std::vector< TreeSupport3D::SupportElements > &move_bounds, PrintObject &print_object, const TreeSupport3D::TreeSupportSettings &config)
void generate ()
 Create the areas that need support.
void detect_overhangs (bool check_support_necessity=false)
SupportNode * create_node (const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode *parent, coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0, coordf_t radius_=0)

Public Attributes

int avg_node_per_layer = 0
float nodes_angle = 0
bool has_sharp_tails = false
bool has_cantilever = false
double max_cantilever_dist = 0
SupportType support_type
std::unique_ptr< FillLightning::Generator > generator
std::unordered_map< double, size_t > printZ_to_lightninglayer
std::function< void()> throw_on_cancel
const PrintConfig * m_print_config
ExPolygon m_machine_border
 Polygons representing the limits of the printable area of the machine.
std::map< const ExPolygon *, OverhangType > overhang_types
std::vector< std::pair< Vec3f, Vec3f > > m_vertical_enforcer_points

Private Member Functions

double belt_floor_print_z (const Point &pos_slicing) const
bool belt_node_landed (const Point &pos_slicing, double radius, double print_z) const
bool belt_polygon_landed (const ExPolygon &poly, double print_z) const
void draw_circles ()
 Draws circles around each node of the tree into the final support.
void drop_nodes ()
 Drops down the nodes of the tree support towards the build plate.
void smooth_nodes ()
std::vector< LayerHeightData > plan_layer_heights ()
 Optimize the generation of tree support by pre-planning the layer_heights.
void generate_contact_points ()
 Creates points where support contacts the model.
void insert_dropped_node (std::vector< SupportNode * > &nodes_layer, SupportNode *node)
 Add a node to the next layer.
void create_tree_support_layers ()
void generate_toolpaths ()
coordf_t calc_branch_radius (coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor)
coordf_t calc_branch_radius (coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true)
coordf_t calc_radius (coordf_t mm_to_top)
coordf_t get_radius (const SupportNode *node)
const ExPolygons & get_avoidance (coordf_t radius, size_t obj_layer_nr)
const ExPolygons & get_collision (coordf_t radius, size_t layer_nr)
Polygons get_collision_polys (coordf_t radius, size_t layer_nr)
Polygons get_trim_support_regions (const PrintObject &object, SupportLayer *support_layer_ptr, const coordf_t gap_extra_above, const coordf_t gap_extra_below, const coordf_t gap_xy)

Private Attributes

std::vector< std::vector< SupportNode * > > contact_nodes
 Generator for model collision, avoidance and internal guide volumes.
std::shared_ptr< TreeSupportData > m_ts_data
std::unique_ptr< TreeSupport3D::TreeModelVolumes > m_model_volumes
PrintObject * m_object
const PrintObjectConfig * m_object_config
SlicingParameters m_slicing_params
SupportParameters m_support_params
size_t m_raft_layers = 0
size_t m_highest_overhang_layer = 0
std::vector< std::vector< MinimumSpanningTree > > m_spanning_trees
std::vector< std::unordered_map< Line, bool, LineHash > > m_mst_line_x_layer_contour_caches
coordf_t base_radius = 0.0
const coordf_t MAX_BRANCH_RADIUS = 10.0
const coordf_t MIN_BRANCH_RADIUS = 0.4
const coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0
const coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0
double diameter_angle_scale_factor = tan(5.0*M_PI/180.0)
const double minimum_roof_area {SQ(scaled<double>(1.))}
float top_z_distance = 0.0
bool is_strong = false
bool is_slim = false
bool with_infill = false

Detailed Description

Generates a tree structure to support your models.

Member Enumeration Documentation

◆ OverhangType

Enumerator
Detected 
Enforced 
SharpTail 
425{ Detected = 0, Enforced, SharpTail };
@ Enforced
Definition TreeSupport.hpp:425
@ SharpTail
Definition TreeSupport.hpp:425
@ Detected
Definition TreeSupport.hpp:425

Constructor & Destructor Documentation

◆ TreeSupport()

Slic3r::TreeSupport::TreeSupport ( PrintObject & object,
const SlicingParameters & slicing_params )

Creates an instance of the tree support generator.

Parameters
storageThe data storage to get global settings from.
680 : m_object(&object), m_slicing_params(slicing_params), m_support_params(object), m_object_config(&object.config())
681{
682 m_print_config = &m_object->print()->config();
683 m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers;
684 support_type = m_object_config->support_type;
685
686 SupportMaterialPattern support_pattern = m_object_config->support_base_pattern;
687 if (m_support_params.support_style == smsTreeHybrid && support_pattern == smpDefault)
688 support_pattern = smpRectilinear;
689
690 if(support_pattern == smpLightning)
691 m_support_params.base_fill_pattern = ipLightning;
692 diameter_angle_scale_factor = std::clamp<double>(m_object_config->tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
695 base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2);
696 // by default tree support needs no infill, unless it's tree hybrid which contains normal nodes.
697 with_infill = support_pattern != smpNone && support_pattern != smpDefault;
699 Vec3d plate_offset = m_object->print()->get_plate_origin();
700 // align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset)
701 m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift);
702 top_z_distance = m_object_config->support_top_z_distance.value;
703 if (top_z_distance > EPSILON) top_z_distance = std::max(top_z_distance, float(m_slicing_params.min_layer_height));
704#ifdef SUPPORT_TREE_DEBUG_TO_SVG
705 SVG svg(debug_out_path("machine_boarder.svg"), m_object->bounding_box());
706 if (svg.is_opened()) svg.draw(m_machine_border, "yellow");
707#endif
708}
#define EPSILON
Definition ColorUtils.cpp:64
#define M_PI
Definition ExtrusionSimulator.cpp:31
SlicingParameters m_slicing_params
Definition TreeSupport.hpp:441
bool is_strong
Definition TreeSupport.hpp:458
SupportType support_type
Definition TreeSupport.hpp:412
PrintObject * m_object
Definition TreeSupport.hpp:439
ExPolygon m_machine_border
Polygons representing the limits of the printable area of the machine.
Definition TreeSupport.hpp:423
float top_z_distance
Definition TreeSupport.hpp:456
const PrintObjectConfig * m_object_config
Definition TreeSupport.hpp:440
SupportParameters m_support_params
Definition TreeSupport.hpp:443
const coordf_t MIN_BRANCH_RADIUS
Definition TreeSupport.hpp:450
bool with_infill
Definition TreeSupport.hpp:460
bool is_slim
Definition TreeSupport.hpp:459
double diameter_angle_scale_factor
Definition TreeSupport.hpp:453
size_t m_raft_layers
Definition TreeSupport.hpp:444
const PrintConfig * m_print_config
Definition TreeSupport.hpp:418
coordf_t base_radius
Definition TreeSupport.hpp:448
#define scale_(val)
Definition libslic3r.h:106
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
@ ipLightning
Definition PrintConfig.hpp:131
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Polygon get_bed_shape_with_excluded_area(const PrintConfig &cfg, bool use_share)
Definition PrintConfig.cpp:14394
bool is_tree(SupportType stype)
Definition PrintConfig.hpp:381
SupportMaterialPattern
Definition PrintConfig.hpp:355
@ smpNone
Definition PrintConfig.hpp:359
@ smpDefault
Definition PrintConfig.hpp:356
@ smpRectilinear
Definition PrintConfig.hpp:357
@ smpLightning
Definition PrintConfig.hpp:358
@ smsTreeHybrid
Definition PrintConfig.hpp:363
@ smsTreeStrong
Definition PrintConfig.hpp:363
bool is_tree_slim(SupportType type, SupportMaterialStyle style)
Definition PrintConfig.hpp:385

References base_radius, Slic3r::SlicingParameters::base_raft_layers, Slic3r::debug_out_path(), diameter_angle_scale_factor, Slic3r::SVG::draw(), EPSILON, Slic3r::get_bed_shape_with_excluded_area(), Slic3r::SlicingParameters::interface_raft_layers, Slic3r::ipLightning, is_slim, is_strong, Slic3r::is_tree(), Slic3r::is_tree_slim(), m_machine_border, m_object, m_object_config, M_PI, m_print_config, m_raft_layers, m_slicing_params, m_support_params, MIN_BRANCH_RADIUS, scale_, Slic3r::smpDefault, Slic3r::smpLightning, Slic3r::smpNone, Slic3r::smpRectilinear, Slic3r::smsTreeHybrid, Slic3r::smsTreeStrong, support_type, top_z_distance, and with_infill.

Here is the call graph for this function:

Member Function Documentation

◆ belt_floor_print_z()

double Slic3r::TreeSupport::belt_floor_print_z ( const Point & pos_slicing) const
private
712{
713 BeltFloorContext ctx;
714 if (!ctx.init(m_slicing_params, *m_print_config))
715 return -std::numeric_limits<double>::infinity();
716 return ctx.floor_print_z(pos_slicing);
717}

References Slic3r::BeltFloorContext::floor_print_z(), Slic3r::BeltFloorContext::init(), m_print_config, and m_slicing_params.

Here is the call graph for this function:

◆ belt_node_landed()

bool Slic3r::TreeSupport::belt_node_landed ( const Point & pos_slicing,
double radius,
double print_z ) const
private
720{
721 BeltFloorContext ctx;
722 if (!ctx.init(m_slicing_params, *m_print_config))
723 return false;
724 return print_z <= ctx.floor_print_z(pos_slicing) - std::abs(ctx.shear_factor()) * std::max(0., radius);
725}

References Slic3r::BeltFloorContext::floor_print_z(), Slic3r::BeltFloorContext::init(), m_print_config, m_slicing_params, and Slic3r::BeltFloorContext::shear_factor().

Here is the call graph for this function:

◆ belt_polygon_landed()

bool Slic3r::TreeSupport::belt_polygon_landed ( const ExPolygon & poly,
double print_z ) const
private
728{
729 BeltFloorContext ctx;
730 if (!ctx.init(m_slicing_params, *m_print_config))
731 return false;
732 double min_floor = std::numeric_limits<double>::max();
733 for (const Point &pt : poly.contour.points)
734 min_floor = std::min(min_floor, ctx.floor_print_z(pt));
735 return print_z <= min_floor;
736}
void poly(std::vector< std::pair< Vec2d, Vec2d > > &out, std::initializer_list< Vec2d > p)
Definition DesignSketchTool.cpp:7751

References Slic3r::BeltFloorContext::floor_print_z(), Slic3r::BeltFloorContext::init(), m_print_config, and m_slicing_params.

Here is the call graph for this function:

◆ calc_branch_radius() [1/2]

coordf_t Slic3r::TreeSupport::calc_branch_radius ( coordf_t base_radius,
coordf_t mm_to_top,
double diameter_angle_scale_factor,
bool use_min_distance = true )
private
1979{
1980 double radius;
1981 coordf_t tip_height = base_radius;// this is a 45 degree tip
1982 if (mm_to_top > tip_height)
1983 {
1984 radius = base_radius + (mm_to_top-tip_height) * diameter_angle_scale_factor;
1985 }
1986 else
1987 {
1988 radius = mm_to_top;// this is a 45 degree tip
1989 }
1990 radius = std::clamp(radius, MIN_BRANCH_RADIUS, MAX_BRANCH_RADIUS);
1991 // if have interface layers, radius should be larger
1992 if (m_object_config->support_interface_top_layers.value > 0)
1993 radius = std::max(radius, base_radius);
1994 return radius;
1995}
const coordf_t MAX_BRANCH_RADIUS
Definition TreeSupport.hpp:449
double coordf_t
Definition GUI_ObjectList.hpp:46

References base_radius, calc_branch_radius(), diameter_angle_scale_factor, m_object_config, MAX_BRANCH_RADIUS, and MIN_BRANCH_RADIUS.

Here is the call graph for this function:

◆ calc_branch_radius() [2/2]

coordf_t Slic3r::TreeSupport::calc_branch_radius ( coordf_t base_radius,
size_t layers_to_top,
size_t tip_layers,
double diameter_angle_scale_factor )
private
1959{
1960 double radius;
1961 if (!is_slim) {
1962 if ((layers_to_top + 1) > tip_layers) {
1963 radius = base_radius + base_radius * (layers_to_top + 1) * diameter_angle_scale_factor;
1964 } else {
1965 radius = base_radius * (layers_to_top + 1) / tip_layers;
1966 }
1967 } else {
1968 if ((layers_to_top + 1) > tip_layers * 2) {
1969 radius = base_radius + base_radius * (layers_to_top + 1) * diameter_angle_scale_factor;
1970 } else {
1971 radius = base_radius * (layers_to_top + 1) / (tip_layers * 2);
1972 }
1973 }
1974 radius = std::clamp(radius, MIN_BRANCH_RADIUS, MAX_BRANCH_RADIUS);
1975 return radius;
1976}

References base_radius, calc_branch_radius(), diameter_angle_scale_factor, is_slim, MAX_BRANCH_RADIUS, and MIN_BRANCH_RADIUS.

Referenced by calc_branch_radius(), calc_branch_radius(), and calc_radius().

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

◆ calc_radius()

coordf_t Slic3r::TreeSupport::calc_radius ( coordf_t mm_to_top)
private
1998{
2000}
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor)
Definition TreeSupport.cpp:1958

References base_radius, calc_branch_radius(), calc_radius(), and diameter_angle_scale_factor.

Referenced by calc_radius(), drop_nodes(), and get_radius().

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

◆ create_node()

SupportNode * Slic3r::TreeSupport::create_node ( const Point position,
const int distance_to_top,
const int obj_layer_nr,
const int support_roof_layers_below,
const bool to_buildplate,
SupportNode * parent,
coordf_t print_z_,
coordf_t height_,
coordf_t dist_mm_to_top_ = 0,
coordf_t radius_ = 0 )
inline
403 {
404 return m_ts_data->create_node(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_);
405 }
std::shared_ptr< TreeSupportData > m_ts_data
Definition TreeSupport.hpp:437

References m_ts_data.

Referenced by move_bounds_to_contact_nodes().

Here is the caller graph for this function:

◆ create_tree_support_layers()

void Slic3r::TreeSupport::create_tree_support_layers ( )
private
1335{
1336 int layer_id = 0;
1337 if (m_raft_layers > 0) { //create raft layers
1338 coordf_t raft_print_z = 0.f;
1339 coordf_t raft_slice_z = 0.f;
1340 {
1341 // Do not add the raft contact layer, 1st layer should use first_print_layer_height
1342 coordf_t height = m_slicing_params.first_print_layer_height;
1343 raft_print_z += height;
1344 raft_slice_z = raft_print_z - height / 2;
1345 m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z);
1346 }
1347 // Insert the base layers.
1348 for (size_t i = 1; i < m_slicing_params.base_raft_layers; i++) {
1349 coordf_t height = m_slicing_params.base_raft_layer_height;
1350 raft_print_z += height;
1351 raft_slice_z = raft_print_z - height / 2;
1352 m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z);
1353 }
1354 // Insert the interface layers.
1355 for (size_t i = 0; i < m_slicing_params.interface_raft_layers; i++) {
1356 coordf_t height = m_slicing_params.interface_raft_layer_height;
1357 raft_print_z += height;
1358 raft_slice_z = raft_print_z - height / 2;
1359 m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z);
1360 }
1361
1362 // Layers between the raft contacts and bottom of the object.
1363 double dist_to_go = m_slicing_params.object_print_z_min - raft_print_z;
1364 // ORCA: Guard tiny residual raft-to-object gaps so the EPSILON-biased ceil()
1365 // below cannot turn them into zero steps after floating-point accumulation.
1366 if (dist_to_go > EPSILON) {
1367 // ORCA: Bias by EPSILON so near-equal gaps do not get an extra split from FP noise.
1368 auto nsteps = int(ceil((dist_to_go - EPSILON) / m_slicing_params.max_suport_layer_height));
1369 double height = dist_to_go / nsteps;
1370 for (int i = 0; i < nsteps; ++i) {
1371 raft_print_z += height;
1372 raft_slice_z = raft_print_z - height / 2;
1373 m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z);
1374 }
1375 }
1376 m_raft_layers = layer_id;
1377 }
1378}
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:1185

References create_tree_support_layers(), EPSILON, m_object, m_raft_layers, and m_slicing_params.

Referenced by create_tree_support_layers(), and generate().

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

◆ detect_overhangs()

void Slic3r::TreeSupport::detect_overhangs ( bool check_support_necessity = false)
740{
741 bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value);
742 if (!tree_support_enable && !check_support_necessity) {
743 BOOST_LOG_TRIVIAL(info) << "Tree support is disabled.";
744 return;
745 }
746
747 // Clear and create Tree Support Layers
748 m_object->clear_support_layers();
749 m_object->clear_tree_support_preview_cache();
750
751 const PrintObjectConfig& config = m_object->config();
753 const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution;
754 const double nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(0);
755 const coordf_t extrusion_width = config.get_abs_value("line_width", nozzle_diameter);
756 const coordf_t extrusion_width_scaled = scale_(extrusion_width);
757 const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
758 const bool bridge_no_support = max_bridge_length > 0;
759 const bool support_critical_regions_only = config.support_critical_regions_only.value;
760 bool config_remove_small_overhangs = config.support_remove_small_overhang.value;
761 bool config_detect_sharp_tails = g_config_support_sharp_tails;
762 const int enforce_support_layers = config.enforce_support_layers.value;
763 const double area_thresh_well_supported = SQ(scale_(6));
764 const double length_thresh_well_supported = scale_(6);
765 static const double sharp_tail_max_support_height = 16.f;
766 // a region is considered well supported if the number of layers below it exceeds this threshold
767 const int thresh_layers_below = 10 / config.layer_height;
768 // +1 makes the threshold inclusive
769 double thresh_angle = config.support_threshold_angle.value > EPSILON ? config.support_threshold_angle.value + 1 : 30;
770 thresh_angle = std::min(thresh_angle, 89.); // should be smaller than 90
771 const double threshold_rad = Geometry::deg2rad(thresh_angle);
772 // Build plate tilt: compute per-layer XY shift for tilted gravity direction
773 const PrintConfig& print_cfg = m_object->print()->config();
774 const Vec2d tilt_slope = build_plate_tilt_slope(print_cfg);
775 const bool has_tilt = tilt_slope.cwiseAbs().maxCoeff() > EPSILON;
776
777 // Belt printers: the object is pre-rotated by the belt angle before slicing, so a wall
778 // that is vertical in the world advances by one layer height per layer in the sliced
779 // frame. The build-plate tilt shift above compensates for that, but its direction has to
780 // follow the belt shear -- the sign and axis are already known exactly from the slicing
781 // parameters, so take them from there rather than from tan(build_plate_tilt), which
782 // carries a magnitude but no direction. Non-belt tilted beds keep the existing behaviour.
783 BeltFloorContext ovh_belt_ctx;
784 const bool belt_ovh_active = ovh_belt_ctx.init(m_slicing_params, print_cfg);
785 const double belt_shear = ovh_belt_ctx.shear_factor();
786 const int belt_axis = ovh_belt_ctx.from_axis();
787 // FIXME this is a fudge constant!
788 double support_tree_tip_diameter = 0.8;
789 auto enforcer_overhang_offset = scaled<double>(support_tree_tip_diameter);
790
791 // for small overhang removal
792 struct OverhangCluster {
793 std::map<int, const ExPolygon*> layer_overhangs;
794 ExPolygons merged_poly;
795 BoundingBox merged_bbox;
796 int min_layer = 1e7;
797 int max_layer = 0;
798 coordf_t offset = 0;
799 bool is_cantilever = false;
800 bool is_sharp_tail = false;
801 bool is_small_overhang = false;
802 OverhangCluster(const ExPolygon* expoly, int layer_nr) {
803 push_back(expoly, layer_nr);
804 }
805 void push_back(const ExPolygon* expoly, int layer_nr) {
806 layer_overhangs.emplace(layer_nr, expoly);
807 auto dilate1 = offset_ex(*expoly, offset);
808 if (!dilate1.empty())
809 merged_poly = union_ex(merged_poly, dilate1);
810 min_layer = std::min(min_layer, layer_nr);
811 max_layer = std::max(max_layer, layer_nr);
812 merged_bbox.merge(get_extents(dilate1));
813 }
814 int height() {
815 return max_layer - min_layer + 1;
816 }
817 bool intersects(const ExPolygon& region, int layer_nr, coordf_t offset) {
818 if (layer_nr < 1) return false;
819 auto it = layer_overhangs.find(layer_nr - 1);
820 if (it == layer_overhangs.end()) return false;
821 const ExPolygon* overhang = it->second;
822
823 this->offset = offset;
824 auto dilate1 = offset_ex(region, offset);
825 BoundingBox bbox = get_extents(dilate1);
826 if (!merged_bbox.overlap(bbox))
827 return false;
828 return overlaps({ *overhang }, dilate1);
829 }
830 // it's basically the combination of push_back and intersects, but saves an offset_ex
831 bool push_back_if_intersects(const ExPolygon& region, int layer_nr, coordf_t offset) {
832 bool is_intersect = false;
833 ExPolygons dilate1;
834 BoundingBox bbox;
835 do {
836 if (layer_nr < 1) break;
837 auto it = layer_overhangs.find(layer_nr - 1);
838 if (it == layer_overhangs.end()) break;
839 const ExPolygon* overhang = it->second;
840
841 this->offset = offset;
842 dilate1 = offset_ex(region, offset);
843 if (dilate1.empty()) break;
844 bbox = get_extents(dilate1);
845 if (!merged_bbox.overlap(bbox))
846 break;
847 is_intersect = overlaps({ *overhang }, dilate1);
848 } while (0);
849 if (is_intersect) {
850 layer_overhangs.emplace(layer_nr, &region);
851 merged_poly = union_ex(merged_poly, dilate1);
852 min_layer = std::min(min_layer, layer_nr);
853 max_layer = std::max(max_layer, layer_nr);
854 merged_bbox.merge(bbox);
855 }
856 return is_intersect;
857 }
858 };
859 std::vector<OverhangCluster> overhangClusters;
860
861 auto find_and_insert_cluster = [](auto &regionClusters, const ExPolygon &region, int layer_nr, coordf_t offset) {
862 OverhangCluster *cluster = nullptr;
863 for (int i = 0; i < regionClusters.size(); i++) {
864 auto cluster_i = &regionClusters[i];
865 if (cluster_i->push_back_if_intersects(region, layer_nr, offset)) {
866 cluster = cluster_i;
867 break;
868 }
869 }
870 if (!cluster) {
871 cluster = &regionClusters.emplace_back(&region, layer_nr);
872 }
873 return cluster;
874 };
875
876 if (!is_tree(stype)) return;
877
880
881 // calc the extrudable expolygons of each layer
882 tbb::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
883 [&](const tbb::blocked_range<size_t>& range) {
884 for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
885 if (m_object->print()->canceled())
886 break;
887 Layer* layer = m_object->get_layer(layer_nr);
888 // Filter out areas whose diameter that is smaller than extrusion_width, but we don't want to lose any details.
889 layer->lslices_extrudable = intersection_ex(layer->lslices, offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled));
890 layer->loverhangs.clear();
891 }
892 });
893
894 typedef std::chrono::high_resolution_clock clock_;
895 typedef std::chrono::duration<double, std::ratio<1> > second_;
896 std::chrono::time_point<clock_> t0{ clock_::now() };
897 // main part of overhang detection can be parallel
898 tbb::concurrent_vector<ExPolygons> overhangs_all_layers(m_object->layer_count());
899 tbb::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
900 [&](const tbb::blocked_range<size_t>& range) {
901 for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
902 if (m_object->print()->canceled())
903 break;
904
905 if (!is_auto(stype) && layer_nr > enforce_support_layers)
906 continue;
907
908 Layer* layer = m_object->get_layer(layer_nr);
909
910 if (layer->lower_layer == nullptr) {
911 for (auto& slice : layer->lslices_extrudable) {
912 auto bbox_size = get_extents(slice).size();
913 if (!((bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported))
914 && g_config_support_sharp_tails) {
915 layer->sharp_tails.push_back(slice);
916 layer->sharp_tails_height.push_back(layer->height);
917 }
918 }
919 continue;
920 }
921
922 Layer* lower_layer = layer->lower_layer;
923 coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad);
924 //lower_layer_offset = std::min(lower_layer_offset, extrusion_width);
925 coordf_t support_offset_scaled = scale_(lower_layer_offset);
926 ExPolygons& curr_polys = layer->lslices_extrudable;
927 ExPolygons& lower_polys = lower_layer->lslices_extrudable;
928
929 // Apply build plate tilt: shift lower layer polygons to simulate tilted gravity
930 //
931 // On a belt the object's very first slice can come out empty (the bottom
932 // vertex is a sub-extrudable sliver), leaving the layer above it with an
933 // empty predecessor even though it rests on the belt. That case needs no
934 // special handling here: the belt surface is unioned into effective_lower
935 // below and sampled at the bottom of the layer, so a contacting island is
936 // covered and a genuinely floating one still reports its overhang. Doing it
937 // that way keeps the decision per-island -- an earlier whole-layer skip,
938 // conditioned on the nearest point of the *union* of the cross-section,
939 // let one contacting island silence a separate floating one.
940 ExPolygons shifted_lower;
941 if (belt_ovh_active || has_tilt) {
942 shifted_lower = lower_polys; // copy
943 const double lh = lower_layer->height;
944 Point tilt_shift(0, 0);
945 if (belt_ovh_active) {
946 // Advance the lower layer along the belt by exactly the amount a
947 // world-vertical wall moves per layer, so such a wall stops reading
948 // as an overhang. Sign comes from the shear, not from a tilt angle.
949 const coord_t d = coord_t(-scale_(lh * belt_shear));
950 if (belt_axis == 0) tilt_shift.x() = d; else tilt_shift.y() = d;
951 } else {
952 tilt_shift = Point::new_scale(tilt_slope * lh);
953 }
954 translate(shifted_lower, tilt_shift);
955 }
956 ExPolygons effective_lower = (belt_ovh_active || has_tilt) ? shifted_lower : lower_polys;
957
958 // Belt printers: material resting on the belt is held up by the belt, not by
959 // the layer below it, so the belt surface counts as support from underneath.
960 // Without this the object's belt-contact face reads as a fresh overhang on
961 // every layer -- the leading strip that produced the spurious support nub.
962 if (belt_ovh_active) {
963 // surface_polygon() is a +/-1000mm half-plane. Unioning that raw with
964 // 20mm-scale geometry and then offsetting it puts a huge dynamic range
965 // through Clipper, which left intermittent artefacts every few layers.
966 // Clip it to the layer's own bounding box first.
967 // Evaluate the belt surface at the BOTTOM of the layer, not its top:
968 // a layer meets the belt across its whole thickness, and print_z is the
969 // top. On the object's first layer -- which is thicker, and whose lower
970 // layer is empty -- using print_z left the leading 0.37mm uncovered and
971 // produced the one remaining spurious overhang.
972 Polygons belt_surface = ovh_belt_ctx.surface_polygon(layer->print_z - layer->height);
973 if (! belt_surface.empty()) {
974 BoundingBox clip_bb = get_extents(curr_polys);
975 clip_bb.merge(get_extents(lower_polys));
976 clip_bb.offset(scale_(10.));
977 belt_surface = intersection(belt_surface, Polygons{ clip_bb.polygon() });
978 if (! belt_surface.empty()) {
979 append(effective_lower, union_ex(belt_surface));
980 effective_lower = union_ex(effective_lower);
981 }
982 }
983 }
984
985 // normal overhang
986 ExPolygons lower_layer_offseted = offset_ex(effective_lower, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
987 overhangs_all_layers[layer_nr] = diff_ex(curr_polys, lower_layer_offseted);
988
989 double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
990 if (duration > 30 || overhangs_all_layers[layer_nr].size() > 100) {
991 BOOST_LOG_TRIVIAL(info) << "detect_overhangs takes more than 30 secs, skip cantilever and sharp tails detection: layer_nr=" << layer_nr << " duration=" << duration;
992 config_detect_sharp_tails = false;
993 config_remove_small_overhangs = false;
994 continue;
995 }
996 if (is_auto(stype) && config_detect_sharp_tails)
997 {
998 // BBS detect sharp tail
999 // On a belt, "below" has to include the belt itself and the
1000 // shear-advanced lower layer, or every belt-contact island reads as
1001 // a sharp tail -- which is what the empty-predecessor skip above was
1002 // really masking. effective_lower is exactly that notion of below.
1003 const ExPolygons &tail_lower = belt_ovh_active ? effective_lower : lower_polys;
1004 // Each island is tested only against the lower islands whose box meets its own; overlaps() tries
1005 // every pair, which is quadratic in the island counts of the two layers.
1006 std::vector<BoundingBox> lower_bboxes;
1007 lower_bboxes.reserve(tail_lower.size());
1008 for (const ExPolygon &lower : tail_lower)
1009 lower_bboxes.emplace_back(get_extents(lower));
1010 for (const ExPolygon& expoly : curr_polys) {
1011 bool is_sharp_tail = false;
1012 // 1. nothing below
1013 // this is a sharp tail region if it's floating and non-ignorable
1014 const ExPolygons expanded = offset_ex(expoly, 0.1 * extrusion_width_scaled);
1015 const BoundingBox bbox = get_extents(expanded);
1016 ExPolygons lower_nearby;
1017 for (size_t i = 0; i < tail_lower.size(); ++i)
1018 if (lower_bboxes[i].overlap(bbox))
1019 lower_nearby.emplace_back(tail_lower[i]);
1020 // As overlaps(expanded, lower_nearby), with each lower island cut to the island's box first:
1021 // below a fine relief the lower layer is a few islands with thousands of holes, and the whole
1022 // of that boundary would otherwise be intersected once per island above.
1023 const auto overlaps_nearby = [&]() {
1024 for (const ExPolygon &a : expanded) {
1025 if (a.empty())
1026 continue;
1027 const BoundingBox a_bbox = get_extents(a);
1028 for (const ExPolygon &b : lower_nearby) {
1029 if (b.empty() || !get_extents(b).overlap(a_bbox))
1030 continue;
1031 const Polygons b_near = ClipperUtils::clip_clipper_polygons_with_subject_bbox(b, a_bbox.inflated(SCALED_EPSILON));
1032 if (!intersection_pl(to_polylines(b_near), a).empty() || b.contains(a.contour.points.front()))
1033 return true;
1034 }
1035 }
1036 return false;
1037 };
1038 if (!overlaps_nearby()) {
1039 is_sharp_tail = !offset_ex(expoly, -0.1 * extrusion_width_scaled).empty();
1040 }
1041
1042 if (is_sharp_tail) {
1043 layer->sharp_tails.push_back(expoly);
1044 layer->sharp_tails_height.push_back(0);
1045
1046 has_sharp_tails = true;
1047#ifdef SUPPORT_TREE_DEBUG_TO_SVG
1048 SVG::export_expolygons(debug_out_path("sharp_tail_orig_%.02f.svg", layer->print_z), { expoly });
1049#endif
1050 }
1051 }
1052 }
1053
1054
1055 // check cantilever
1056 // lower_layer_offset may be very small, so we need to do max and then add 0.1
1057 lower_layer_offseted = offset_ex(lower_layer_offseted, scale_(std::max(extrusion_width - lower_layer_offset, 0.) + 0.1));
1058 for (ExPolygon& poly : overhangs_all_layers[layer_nr]) {
1059 auto cluster_boundary_ex = intersection_ex(poly, lower_layer_offseted);
1060 Polygons cluster_boundary = to_polygons(cluster_boundary_ex);
1061 if (cluster_boundary.empty()) continue;
1062 double dist_max = 0;
1063 for (auto& pt : poly.contour.points) {
1064 double dist_pt = std::numeric_limits<double>::max();
1065 for (auto& ply : cluster_boundary) {
1066 double d = ply.distance_to(pt);
1067 dist_pt = std::min(dist_pt, d);
1068 }
1069 dist_max = std::max(dist_max, dist_pt);
1070 }
1071 if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base
1072 max_cantilever_dist = std::max(max_cantilever_dist, dist_max);
1073 layer->cantilevers.emplace_back(poly);
1074 BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max;
1075 has_cantilever = true;
1076 }
1077 }
1078 }
1079 }
1080 ); // end tbb::parallel_for
1081
1082 BOOST_LOG_TRIVIAL(info) << "max_cantilever_dist=" << max_cantilever_dist;
1083 if (check_support_necessity)
1084 return;
1085
1086 // check if the sharp tails should be extended higher
1087 if (is_auto(stype) && config_detect_sharp_tails) {
1088 for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
1089 if (m_object->print()->canceled())
1090 break;
1091
1092 Layer* layer = m_object->get_layer(layer_nr);
1093 Layer* lower_layer = layer->lower_layer;
1094 if (!lower_layer)
1095 continue;
1096
1097 // BBS detect sharp tail
1098 const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails;
1099 const auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height;
1100 for (ExPolygon& expoly : layer->lslices_extrudable) {
1101 bool is_sharp_tail = false;
1102 float accum_height = layer->height;
1103 do {
1104 // 2. something below
1105 // check whether this is above a sharp tail region.
1106
1107 // 2.1 If no sharp tail below, this is considered as common region.
1108 ExPolygons supported_by_lower = intersection_ex({ expoly }, lower_layer_sharptails);
1109 if (supported_by_lower.empty()) {
1110 is_sharp_tail = false;
1111 break;
1112 }
1113
1114 // 2.2 If sharp tail below, check whether it support this region enough.
1115#if 0
1116 // judge by area isn't reliable, failure cases include 45 degree rotated cube
1117 float supported_area = area(supported_by_lower);
1118 if (supported_area > area_thresh_well_supported) {
1119 is_sharp_tail = false;
1120 break;
1121 }
1122#endif
1123 BoundingBox bbox = get_extents(supported_by_lower);
1124 if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) {
1125 is_sharp_tail = false;
1126 break;
1127 }
1128
1129 // 2.3 check whether sharp tail exceed the max height
1130 for(size_t i=0;i<lower_layer_sharptails.size();i++) {
1131 if (lower_layer_sharptails[i].overlaps(expoly)) {
1132 accum_height += lower_layer_sharptails_height[i];
1133 break;
1134 }
1135 }
1136 if (accum_height > sharp_tail_max_support_height) {
1137 is_sharp_tail = false;
1138 break;
1139 }
1140
1141 // 2.4 if the area grows fast than threshold, it get connected to other part or
1142 // it has a sharp slop and will be auto supported.
1143 ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails);
1144 if ((get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5), scale_(5)), ">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) {
1145 is_sharp_tail = false;
1146 break;
1147 }
1148
1149 // 2.5 mark the expoly as sharptail
1150 is_sharp_tail = true;
1151 } while (0);
1152
1153 if (is_sharp_tail) {
1154 layer->sharp_tails.push_back(expoly);
1155 layer->sharp_tails_height.push_back( accum_height);
1156 }
1157
1158 }
1159 }
1160 }
1161
1162 // group overhang clusters
1163 for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
1164 if (m_object->print()->canceled())
1165 break;
1166 Layer* layer = m_object->get_layer(layer_nr);
1167 for (auto& overhang : overhangs_all_layers[layer_nr]) {
1168 OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled);
1169 if (overlaps({ overhang }, layer->cantilevers))
1170 cluster->is_cantilever = true;
1171 }
1172 }
1173
1174 auto enforcers = m_object->slice_support_enforcers();
1175 auto blockers = m_object->slice_support_blockers();
1177 m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers, &m_vertical_enforcer_points);
1178 m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers);
1179
1180 if (is_auto(stype) && config_remove_small_overhangs) {
1181 // remove small overhangs
1182 for (auto& cluster : overhangClusters) {
1183 // 3. check whether the small overhang is sharp tail
1184 cluster.is_sharp_tail = false;
1185 for (size_t layer_id = cluster.min_layer; layer_id <= cluster.max_layer; layer_id++) {
1186 Layer* layer = m_object->get_layer(layer_id);
1187 if (overlaps(layer->sharp_tails, cluster.merged_poly)) {
1188 cluster.is_sharp_tail = true;
1189 break;
1190 }
1191 }
1192
1193 if (!cluster.is_sharp_tail && !cluster.is_cantilever) {
1194 // 2. check overhang cluster size is smaller than 3.0 * fw_scaled
1195 auto erode1 = offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled);
1196 Point bbox_sz = get_extents(erode1).size();
1197 if (bbox_sz.x() < 2 * extrusion_width_scaled || bbox_sz.y() < 2 * extrusion_width_scaled) {
1198 cluster.is_small_overhang = true;
1199 }
1200 }
1201
1202#ifdef SUPPORT_TREE_DEBUG_TO_SVG
1203 const Layer* layer1 = m_object->get_layer(cluster.min_layer);
1204 std::string fname = debug_out_path("overhangCluster_%d-%d_%.2f-%.2f_tail=%d_cantilever=%d_small=%d.svg",
1205 cluster.min_layer, cluster.max_layer, layer1->print_z, m_object->get_layer(cluster.max_layer)->print_z,
1206 cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang);
1207 SVG::export_expolygons(fname, {
1208 { layer1->lslices, {"min_layer_lslices","red",0.5} },
1209 { m_object->get_layer(cluster.max_layer)->lslices, {"max_layer_lslices","yellow",0.5} },
1210 { cluster.merged_poly,{"overhang", "blue", 0.5} },
1211 { cluster.is_cantilever? layer1->cantilevers: offset_ex(cluster.merged_poly, -1 * extrusion_width_scaled), {cluster.is_cantilever ? "cantilever":"erode1","green",0.5}} });
1212#endif
1213 }
1214 }
1215
1216 for (auto& cluster : overhangClusters) {
1217 if (cluster.is_small_overhang) continue;
1218 // collect overhangs that's not small overhangs
1219 for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
1220 int layer_nr = it->first;
1221 auto p_overhang = it->second;
1222 m_object->get_layer(layer_nr)->loverhangs.emplace_back(*p_overhang);
1223 }
1224 }
1225
1226 int layers_with_overhangs = 0;
1227 int layers_with_enforcers = 0;
1228 for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
1229 if (m_object->print()->canceled())
1230 break;
1231
1232 auto layer = m_object->get_layer(layer_nr);
1233 auto lower_layer = layer->lower_layer;
1234
1235 // add support for every 1mm height for sharp tails
1236 ExPolygons sharp_tail_overhangs;
1237 if (lower_layer == nullptr)
1238 sharp_tail_overhangs = layer->sharp_tails;
1239 else {
1240 ExPolygons lower_layer_expanded = offset_ex(lower_layer->lslices_extrudable, SCALED_RESOLUTION);
1241 for (size_t i = 0; i < layer->sharp_tails_height.size();i++) {
1242 ExPolygons areas = diff_clipped({ layer->sharp_tails[i]}, lower_layer_expanded);
1243 float accum_height = layer->sharp_tails_height[i];
1244 if (!areas.empty() && int(accum_height * 10) % 5 == 0) {
1245 append(sharp_tail_overhangs, areas);
1246 has_sharp_tails = true;
1247#ifdef SUPPORT_TREE_DEBUG_TO_SVG
1248 SVG::export_expolygons(debug_out_path("sharp_tail_%.02f.svg", layer->print_z), areas);
1249#endif
1250 }
1251 }
1252 }
1253
1254 if (layer_nr < blockers.size()) {
1255 // Arthur: union_ is a must because after mirroring, the blocker polygons are in left-hand coordinates, ie clockwise,
1256 // which are not valid polygons, and will be removed by offset_ex. union_ can make these polygons right.
1257 ExPolygons blocker = offset_ex(union_(blockers[layer_nr]), scale_(radius_sample_resolution));
1258 layer->loverhangs = diff_ex(layer->loverhangs, blocker);
1259 layer->cantilevers = diff_ex(layer->cantilevers, blocker);
1260 sharp_tail_overhangs = diff_ex(sharp_tail_overhangs, blocker);
1261 }
1262
1263 if (support_critical_regions_only && is_auto(stype)) {
1264 layer->loverhangs.clear(); // remove oridinary overhangs, only keep cantilevers and sharp tails (added later)
1265 append(layer->loverhangs, layer->cantilevers);
1266 }
1267
1268 if (max_bridge_length > 0 && layer->loverhangs.size() > 0 && lower_layer) {
1269 // do not break bridge as the interface will be poor, see #4318
1270 bool break_bridge = false;
1271 m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &layer->loverhangs, max_bridge_length, break_bridge);
1272 }
1273
1274 int nDetected = layer->loverhangs.size();
1275 // enforcers now follow same logic as normal support. See STUDIO-3692
1276 if (layer_nr < enforcers.size() && lower_layer) {
1277 ExPolygons enforced_overhangs = intersection_ex(diff_ex(layer->lslices_extrudable, lower_layer->lslices_extrudable), enforcers[layer_nr]);
1278 if (!enforced_overhangs.empty()) {
1279 // FIXME this is a hack to make enforcers work on steep overhangs. See STUDIO-7538.
1280 enforced_overhangs = diff_ex(offset_ex(enforced_overhangs, enforcer_overhang_offset), lower_layer->lslices_extrudable);
1281 append(layer->loverhangs, enforced_overhangs);
1282 }
1283 }
1284 int nEnforced = layer->loverhangs.size();
1285
1286 // add sharp tail overhangs
1287 append(layer->loverhangs, sharp_tail_overhangs);
1288
1289 // fill overhang_types
1290 for (size_t i = 0; i < layer->loverhangs.size(); i++)
1291 overhang_types.emplace(&layer->loverhangs[i], i < nDetected ? OverhangType::Detected :
1293
1294 if (!layer->loverhangs.empty()) {
1295 layers_with_overhangs++;
1296 m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr));
1297 }
1298 if (nEnforced > 0) layers_with_enforcers++;
1299 if (!layer->cantilevers.empty()) has_cantilever = true;
1300 }
1301
1302 BOOST_LOG_TRIVIAL(info) << "Tree support overhang detection done. " << layers_with_overhangs << " layers with overhangs. nEnforced=" << layers_with_enforcers;
1303
1304#ifdef SUPPORT_TREE_DEBUG_TO_SVG
1305 for (const Layer* layer : m_object->layers()) {
1306 if (layer->loverhangs.empty() && (blockers.size()<=layer->id() || blockers[layer->id()].empty()))
1307 continue;
1308
1309 SVG::export_expolygons(debug_out_path("overhang_areas_%d_%.2f.svg",layer->id(), layer->print_z), {
1310 { m_object->get_layer(layer->id())->lslices_extrudable, {"lslices_extrudable","yellow",0.5} },
1311 { layer->loverhangs, {"overhang","red",0.5} }
1312 });
1313
1314 if (enforcers.size() > layer->id()) {
1315 SVG svg(format("SVG/enforcer_%s.svg", layer->print_z), m_object->bounding_box());
1316 if (svg.is_opened()) {
1317 svg.draw_outline(m_object->get_layer(layer->id())->lslices_extrudable, "yellow");
1318 svg.draw(enforcers[layer->id()], "red");
1319 }
1320 }
1321 if (blockers.size() > layer->id()) {
1322 SVG svg(format("SVG/blocker_%s.svg", layer->print_z), m_object->bounding_box());
1323 if (svg.is_opened()) {
1324 svg.draw_outline(m_object->get_layer(layer->id())->lslices_extrudable, "yellow");
1325 svg.draw(blockers[layer->id()], "red");
1326 }
1327 }
1328 }
1329#endif
1330}
#define SQ(x)
Definition Orient.cpp:42
PointType size() const
Definition BoundingBox.cpp:178
static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer="black", std::string stroke_holes="blue", coordf_t stroke_width=0)
Definition SVG.cpp:381
size_t m_highest_overhang_layer
Definition TreeSupport.hpp:445
std::vector< std::pair< Vec3f, Vec3f > > m_vertical_enforcer_points
Definition TreeSupport.hpp:427
bool has_cantilever
Definition TreeSupport.hpp:410
double max_cantilever_dist
Definition TreeSupport.hpp:411
std::map< const ExPolygon *, OverhangType > overhang_types
Definition TreeSupport.hpp:426
bool has_sharp_tails
Definition TreeSupport.hpp:409
static constexpr float g_config_tree_support_collision_resolution
Definition libslic3r.h:118
static constexpr bool g_config_support_sharp_tails
Definition libslic3r.h:116
double coordf_t
Definition libslic3r.h:51
#define SCALED_RESOLUTION
Definition libslic3r.h:87
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:312
ClusteredPoints cluster(Index3D &sindex, unsigned max_points, std::function< std::vector< PointIndexEl >(const Index3D &, const PointIndexEl &)> qfn)
Definition Clustering.cpp:27
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:801
zaa_minimize_perimeter_height nozzle_diameter((ConfigOptionBool, reduce_infill_retraction))((ConfigOptionBool
static const double area_thresh_well_supported
Definition SupportMaterial.cpp:1441
Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:735
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:831
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:20
static const double length_thresh_well_supported
Definition SupportMaterial.cpp:1440
auto range(Cont &&cont)
Definition libslic3r.h:405
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
@ ENFORCER
Definition TriangleSelector.hpp:27
@ BLOCKER
Definition TriangleSelector.hpp:28
bool is_auto(SupportType stype)
Definition PrintConfig.hpp:389
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
zaa_minimize_perimeter_height PrintObjectConfig
Definition PrintConfig.hpp:2183
static const double sharp_tail_max_support_height
Definition SupportMaterial.cpp:1444
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:605
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:878
SupportType
Definition PrintConfig.hpp:378
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type)
Definition ClipperUtils.cpp:1011
bool overlaps(const ExPolygons &expolys1, const ExPolygons &expolys2)
Definition ExPolygon.cpp:192
Vec2d build_plate_tilt_slope(const PrintConfig &print_config)
Definition SupportCommon.cpp:2195
void append(std::vector< T, Alloc > &dest, const std::vector< T, Alloc2 > &src)
Definition libslic3r.h:157
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

References Slic3r::area_thresh_well_supported, Slic3r::build_plate_tilt_slope(), Slic3r::Geometry::deg2rad(), EPSILON, Slic3r::BeltFloorContext::from_axis(), g_config_support_sharp_tails, g_config_tree_support_collision_resolution, Slic3r::get_extents(), Slic3r::BeltFloorContext::init(), Slic3r::is_tree(), Slic3r::length_thresh_well_supported, m_highest_overhang_layer, m_object, m_object_config, m_slicing_params, max_cantilever_dist, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::nozzle_diameter(), Slic3r::offset(), Slic3r::offset_ex(), Slic3r::BoundingBoxBase< PointType, APointsType >::overlap(), Slic3r::overlaps(), Slic3r::PrintObjectConfig, Slic3r::range(), scale_, Slic3r::scaled(), Slic3r::sharp_tail_max_support_height, Slic3r::BeltFloorContext::shear_factor(), SQ, support_type, and Slic3r::union_ex().

Referenced by generate(), Slic3r::TreeSupport3D::generate_support_areas(), and Slic3r::PrintObject::is_support_necessary().

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

◆ draw_circles()

void Slic3r::TreeSupport::draw_circles ( )
private

Draws circles around each node of the tree into the final support.

This also handles the areas that have to become support roof, support bottom, the Z distances, etc.

Parameters
storage[in,out]The settings storage to get settings from and to save the resulting support polygons to.
contact_nodesThe nodes to draw as support.
2134{
2135 const PrintObjectConfig &config = m_object->config();
2136 const Print* print = m_object->print();
2137 bool has_brim = print->has_brim();
2138 const coordf_t bottom_gap_height = m_slicing_params.gap_object_support;
2139 const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
2140 const coordf_t branch_radius_scaled = scale_(branch_radius);
2141 bool on_buildplate_only = m_object_config->support_on_build_plate_only.value;
2142 Polygon branch_circle; //Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
2143
2144 // Use square support if there are too many nodes per layer because circle support needs much longer time to compute
2145 // Hower circle support can be printed faster, so we prefer circle for fewer nodes case.
2146 const bool SQUARE_SUPPORT = avg_node_per_layer > 200;
2147 const int CIRCLE_RESOLUTION = SQUARE_SUPPORT ? 4 : 100; // The number of vertices in each circle.
2148
2149
2150 for (int i = 0; i < CIRCLE_RESOLUTION; i++)
2151 {
2152 double angle;
2153 if (SQUARE_SUPPORT)
2154 angle = (double) i / CIRCLE_RESOLUTION * TAU + M_PI_4 + nodes_angle;
2155 else
2156 angle = (double) i / CIRCLE_RESOLUTION * TAU;
2157 branch_circle.append(Point(cos(angle) * branch_radius_scaled, sin(angle) * branch_radius_scaled));
2158 }
2159
2160 // Performance optimization. Only generate lslices for brim and skirt.
2161 size_t brim_skirt_layers = has_brim ? 1 : 0;
2162 const PrintConfig& print_config = print->config();
2163 for (const PrintObject* object : print->objects())
2164 {
2165 size_t skirt_layers = print->has_infinite_skirt() ? object->layer_count() : std::min(size_t(print_config.skirt_height.value), object->layer_count());
2166 brim_skirt_layers = std::max(brim_skirt_layers, skirt_layers);
2167 }
2168
2169 // generate areas
2170 const coordf_t layer_height = config.layer_height.value;
2171 const size_t top_interface_layers = m_support_params.num_top_interface_layers;
2172 const int top_base_interface_layers = std::min<int>(
2173 int(m_support_params.num_top_base_interface_layers),
2174 top_interface_layers > 0 ? int(top_interface_layers) - 1 : 0);
2175 const size_t bottom_interface_layers = number_of_support_interface_bottom_layers(config);
2176 const double nozzle_diameter = m_object->print()->config().nozzle_diameter.get_at(0);
2177 const coordf_t line_width = config.get_abs_value("support_line_width", nozzle_diameter);
2178 const coordf_t line_width_scaled = scale_(line_width);
2179 const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning;
2180 coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
2181 const float tree_brim_width = config.tree_support_brim_width.value;
2182 // Reinforce branches moving more than half a support-line width between layers.
2183 // Square the threshold for comparison with the squared XY movement below.
2184 const double max_move_sq = sqr(double(scale_(m_object_config->support_line_width / 2)));
2185 // Belt floor: the first object layer is not on a flat bed — it rests on the
2186 // tilted, moving belt. So the first-object-layer adhesion features (the tree
2187 // support brim, the hybrid first-layer base expansion) must be suppressed:
2188 // their expanded contact rings project to a stray brim/skirt loop sitting in
2189 // the Z=0 belt plane around the support footprint. false on non-belt printers,
2190 // so behavior there is unchanged.
2191 BeltFloorContext belt_ctx;
2192 const bool belt_floor_active = belt_ctx.init(m_slicing_params, *m_print_config);
2193
2194 if (m_object->support_layer_count() <= m_raft_layers)
2195 return;
2196 BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name;
2197
2198 tbb::parallel_for(tbb::blocked_range<size_t>(0, m_ts_data->layer_heights.size()),
2199 [&](const tbb::blocked_range<size_t>& range)
2200 {
2201 for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++)
2202 {
2203 if (print->canceled())
2204 break;
2205
2206 const std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
2207 SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
2208 assert(ts_layer != nullptr);
2209
2210 // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
2211 if (curr_layer_nodes.empty()) {
2212 ts_layer->print_z = 0.0;
2213 ts_layer->height = 0.0;
2214 continue;
2215 }
2216
2217 ts_layer->print_z = m_ts_data->layer_heights[layer_nr].print_z;
2218 ts_layer->height = m_ts_data->layer_heights[layer_nr].height;
2219 size_t obj_layer_nr = m_ts_data->layer_heights[layer_nr].obj_layer_nr;
2220 if (ts_layer->height < EPSILON) {
2221 continue;
2222 }
2223
2224 ExPolygons& base_areas = ts_layer->base_areas;
2225 ExPolygons& roof_areas = ts_layer->roof_areas;
2226 ExPolygons roof_base_areas;
2227 ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer;
2228 ExPolygons& floor_areas = ts_layer->floor_areas;
2229 ExPolygons& roof_gap_areas = ts_layer->roof_gap_areas;
2230 coordf_t max_layers_above_base = 0;
2231 coordf_t max_layers_above_roof = 0;
2232 coordf_t max_layers_above_roof1 = 0;
2233 size_t first_base_roof_area = 0;
2234 bool floor_interface_as_base = false;
2235 bool has_circle_node = false;
2236 ExPolygons extra_wall_areas;
2237 ExPolygons collision_sharp_tails;
2238 ExPolygons collision_base;
2239 auto get_collision = [&](bool sharp_tail) -> ExPolygons &{
2240 ExPolygons &collision = sharp_tail ? collision_sharp_tails : collision_base;
2241 if (collision.empty()) {
2242 collision = offset_ex(m_ts_data->m_layer_outlines[obj_layer_nr],
2243 sharp_tail ? scale_(top_z_distance) :
2244 scale_((obj_layer_nr == 0) ? config.support_object_first_layer_gap : m_ts_data->m_xy_distance));
2245 // the top layers may be too close to interface with adaptive layer heights and very small overhang angle
2246 if (top_z_distance > EPSILON) {
2247 float accum_height = 0;
2248 for (size_t layer_id = obj_layer_nr + 1;
2249 layer_id < m_ts_data->m_layer_outlines.size() && (accum_height += m_object->get_layer(layer_id)->height) && accum_height <= top_z_distance;
2250 layer_id++) {
2251 collision = union_ex(collision, offset_ex(m_ts_data->m_layer_outlines[layer_id], scale_(top_z_distance)));
2252 }
2253 }
2254 }
2255 return collision;
2256 };
2257
2258 BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << curr_layer_nodes.size();
2259 //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas.
2260 ts_layer->lslices.reserve(curr_layer_nodes.size());
2261 ExPolygons area_poly; // the polygon node area which will be printed as normal support
2262 for (const SupportNode* p_node : curr_layer_nodes)
2263 {
2264 if (print->canceled())
2265 break;
2266
2267 const SupportNode& node = *p_node;
2268 ExPolygons area;
2269 // Generate directly from overhang polygon if one of the following is true:
2270 // 1) node is a normal part of hybrid support
2271 // 2) node is virtual
2272 if (node.type == ePolygon || (node.distance_to_top<0 && !node.is_sharp_tail)) {
2273 if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1)
2274 area.emplace_back(node.overhang);
2275 else {
2276 area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance));
2277 }
2278 area = diff_clipped(area, get_collision(node.is_sharp_tail && node.distance_to_top <= 0));
2279 if (node.type == ePolygon) append(area_poly, area);
2280 }
2281 else {
2282 Polygon circle(branch_circle);
2283 double scale = node.radius / branch_radius;
2284 double moveX = node.movement.x() / (scale * branch_radius_scaled);
2285 double moveY = node.movement.y() / (scale * branch_radius_scaled);
2286 //BOOST_LOG_TRIVIAL(debug) << format("scale,moveX,moveY: %.3f,%.3f,%.3f", scale, moveX, moveY);
2287
2288 if (!SQUARE_SUPPORT && std::abs(moveX)>0.001 && std::abs(moveY)>0.001) { // draw ellipse along movement direction
2289 const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY));
2290 double matrix[2*2] = {
2291 scale * (1 + moveX * moveX * vsize_inv),scale * (0 + moveX * moveY * vsize_inv),
2292 scale * (0 + moveX * moveY * vsize_inv),scale * (1 + moveY * moveY * vsize_inv),
2293 };
2294 int i = 0;
2295 for (auto vertex: branch_circle.points) {
2296 vertex = Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y());
2297 circle.points[i++] = node.position + vertex;
2298 }
2299 } else {
2300 for (int i = 0;i< circle.points.size(); i++) {
2301 circle.points[i] = circle.points[i] * scale + node.position;
2302 }
2303 }
2304 if (obj_layer_nr == 0 && m_raft_layers == 0 && !belt_floor_active) {
2305 double brim_width = !config.tree_support_auto_brim ? tree_brim_width : std::max(MIN_BRANCH_RADIUS_FIRST_LAYER, std::min(node.radius + node.dist_mm_to_top / (scale * branch_radius) * 0.5, MAX_BRANCH_RADIUS_FIRST_LAYER) - node.radius);
2306 auto tmp=offset(circle, scale_(brim_width));
2307 if(!tmp.empty())
2308 circle = tmp[0];
2309 }
2310 area = avoid_object_remove_extra_small_parts(ExPolygon(circle), get_collision(node.is_sharp_tail && node.distance_to_top <= 0));
2311 // area = diff_clipped({ ExPolygon(circle) }, get_collision(node.is_sharp_tail && node.distance_to_top <= 0));
2312
2313 if (!area.empty()) has_circle_node = true;
2314
2315 // merge overhang to get a smoother interface surface
2316 // Do not merge when buildplate_only is on, because some underneath nodes may have been deleted.
2317 if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) {
2318 ExPolygons overhang_expanded;
2319 if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1)
2320 overhang_expanded.emplace_back(node.overhang);
2321 else {
2322 overhang_expanded = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance));
2323 }
2324 append(area, overhang_expanded);
2325 }
2326 }
2327
2328 // Use the Pythagorean theorem to compare movement magnitude (dx² + dy²) without a square root.
2329 if (node.is_processed && node.movement.cast<double>().squaredNorm() > max_move_sq)
2330 append(extra_wall_areas, area);
2331
2332 if (obj_layer_nr>0 && node.distance_to_top < 0)
2333 append(roof_gap_areas, area);
2334 else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 &&
2335 node.is_sharp_tail == false)
2336 {
2337 append(roof_1st_layer, area);
2338 max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top);
2339 }
2340 else if (obj_layer_nr > 0 && node.support_roof_layers_below > 1 &&
2341 node.is_sharp_tail == false)
2342 {
2343 append(node.support_roof_layers_below <= top_base_interface_layers ? roof_base_areas : roof_areas, area);
2344 max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top);
2345 }
2346 else
2347 {
2348 append(base_areas, area);
2349 max_layers_above_base = std::max(max_layers_above_base, node.dist_mm_to_top);
2350 }
2351
2352 }
2353
2354 //m_object->print()->set_status(65, (boost::format( _u8L("Support: generate polygons at layer %d")) % layer_nr).str());
2355
2356 // join roof segments
2357 roof_areas = diff_clipped(closing_ex(roof_areas, line_width_scaled), get_collision(false));
2358 roof_areas = intersection_ex(roof_areas, m_machine_border);
2359 roof_base_areas = diff_clipped(closing_ex(roof_base_areas, line_width_scaled), get_collision(false));
2360 roof_base_areas = intersection_ex(roof_base_areas, m_machine_border);
2361 if (!roof_base_areas.empty() && !roof_areas.empty())
2362 roof_base_areas = diff_ex(roof_base_areas,
2363 ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas, get_extents(roof_base_areas)));
2364
2365 first_base_roof_area = roof_areas.size();
2366 append(roof_areas, std::move(roof_base_areas));
2367 roof_1st_layer = diff_clipped(closing_ex(roof_1st_layer, line_width_scaled), get_collision(false));
2368
2369 // roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
2370 roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas,get_extents(roof_1st_layer)));
2371 roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border);
2372
2373 ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas);
2374 base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas)));
2375 base_areas = intersection_ex(base_areas, m_machine_border);
2376
2377 // Belt floor: clip tree support polygons by the belt surface plane.
2378 // Non-organic tree support layers inherit their print_z from the
2379 // (already globally-offset) object layers — see plan_layer_heights()
2380 // and add_tree_support_layer(); only ORGANIC layers get the global
2381 // Z offset applied later in _generate_support_material(). So here
2382 // ts_layer->print_z is in the GLOBAL frame and we must use init()
2383 // (global), not init_local(): mixing a local-frame clip plane with
2384 // a global print_z displaces the cutoff line by belt_global_z_offset
2385 // along the shear axis, leaving an un-clipped wedge of support below
2386 // the belt floor. In per-object (non-global) mode belt_global_z_offset
2387 // is 0 so init() and init_local() coincide — this is a no-op there.
2388 {
2389 BeltFloorContext ctx;
2390 if (ctx.init(m_slicing_params, *m_print_config)) {
2391 Polygons belt_surface = ctx.surface_polygon(ts_layer->print_z);
2392 base_areas = diff_ex(base_areas, belt_surface);
2393 roof_areas = diff_ex(roof_areas, belt_surface);
2394 roof_1st_layer = diff_ex(roof_1st_layer, belt_surface);
2395 floor_areas = diff_ex(floor_areas, belt_surface);
2396 roof_gap_areas = diff_ex(roof_gap_areas, belt_surface);
2397 }
2398 }
2399
2400 if (SQUARE_SUPPORT) {
2401 // simplify support contours
2402 ExPolygons base_areas_simplified;
2403 for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified); }
2404 base_areas = std::move(base_areas_simplified);
2405 }
2406 // ORCA:
2407 // Bottom interface / bottom gap must be anchored to the *true* support-to-model contact surface.
2408 // Do NOT window the contact search by gap or interface height.
2409 // First find the real contact below, then enforce:
2410 // - an empty gap below (contact_z + gap)
2411 // - exactly N interface layers above that
2412 if (!base_areas.empty() && !m_object_config->support_on_build_plate_only.value &&
2413 (bottom_gap_height > EPSILON || bottom_interface_layers > 0))
2414 {
2415 const coordf_t interface_height =
2416 bottom_interface_layers > 0 ? coordf_t(bottom_interface_layers) * m_slicing_params.layer_height : 0.0;
2417
2418 const coordf_t layer_top_z = ts_layer->print_z;
2419 const coordf_t layer_bottom_z = ts_layer->bottom_z();
2420 ExPolygons new_base_areas;
2421 ExPolygons new_floor_areas;
2422 struct ContactBand {
2423 coordf_t z = 0.0;
2424 Polygons surfaces;
2425 };
2426 for (const ExPolygon& comp : base_areas) {
2427 ExPolygons comp_poly { comp };
2428 bool found_contact = false;
2429 std::vector<ContactBand> bands;
2430
2431 // Search downward for object layers whose TOP/BOTTOM surfaces intersect this component.
2432 for (size_t idx = obj_layer_nr + 1; idx-- > 0;) {
2433 const Layer* below_layer = m_object->get_layer(idx);
2434 Polygons top_surfaces = collect_region_slices_by_type(*below_layer, stTop);
2435 Polygons bottom_surfaces = collect_region_slices_by_type(*below_layer, stBottom);
2436 Polygons surf_union = top_surfaces;
2437 polygons_append(surf_union, bottom_surfaces);
2438 if (surf_union.empty())
2439 continue;
2440
2441 ExPolygons inter = intersection_ex(comp_poly, surf_union);
2442 if (!inter.empty()) {
2443 bands.push_back(ContactBand{ below_layer->print_z, std::move(surf_union) });
2444 found_contact = true;
2445 }
2446 }
2447
2448 if (found_contact) {
2449 std::sort(bands.begin(), bands.end(), [](const ContactBand &a, const ContactBand &b) {
2450 return a.z < b.z;
2451 });
2452 }
2453
2454 if (!found_contact) {
2455 append(new_base_areas, comp_poly);
2456 continue;
2457 }
2458
2459 bool interface_id_set = false;
2460 bool any_gap_cleared = false;
2461
2462 for (const ContactBand &band : bands) {
2463 const coordf_t band_gap_top = band.z + bottom_gap_height;
2464 const coordf_t band_iface_start = band_gap_top;
2465 const bool band_applies = layer_top_z >= band.z - EPSILON;
2466 if (!band_applies)
2467 continue;
2468
2469
2470 // Inside the gap: remove only the part overlapping the contact surface, keep the rest.
2471 if (bottom_gap_height > EPSILON && layer_bottom_z < band_gap_top - EPSILON) {
2472 any_gap_cleared = true;
2473 comp_poly = diff_ex(comp_poly, band.surfaces);
2474 }
2475
2476 // Overlaps interface band
2477 if (bottom_interface_layers > 0 &&
2478 layer_bottom_z >= band_iface_start - EPSILON &&
2479 layer_bottom_z < band_iface_start + interface_height - EPSILON) {
2480 if (!interface_id_set) {
2481 size_t first_interface_layer = layer_nr;
2482 while (first_interface_layer > 0) {
2483 if (m_ts_data->layer_heights[first_interface_layer - 1].print_z <= band_iface_start + EPSILON)
2484 break;
2485 --first_interface_layer;
2486 }
2487 // ORCA: Use support-layer index for base-interface selection (robust with independent heights).
2488 if (m_support_params.num_bottom_base_interface_layers > 0) {
2489 const int bottom_interface_idx =
2490 std::max(0, int(layer_nr) - int(first_interface_layer));
2491 const int bottom_base_start_idx =
2492 std::max(0, int(bottom_interface_layers) - int(m_support_params.num_bottom_base_interface_layers));
2493 floor_interface_as_base = bottom_interface_idx >= bottom_base_start_idx;
2494 }
2495 interface_id_set = true;
2496 }
2497
2498 ExPolygons band_ex = union_ex(band.surfaces);
2499 if (!band_ex.empty()) {
2500 const coordf_t margin = scale_(m_support_params.support_extrusion_width);
2501 ExPolygons comp_margin = offset_ex(comp_poly, margin);
2502 ExPolygons band_clipped = intersection_ex(band_ex, comp_margin);
2503 band_ex = std::move(band_clipped);
2504 }
2505 ExPolygons comp_interface = band_ex.empty() ? ExPolygons {} : intersection_ex(comp_poly, band_ex);
2506 if (!comp_interface.empty()) {
2507 append(new_floor_areas, comp_interface);
2508 comp_poly = diff_ex(comp_poly, offset_ex(comp_interface, 10));
2509 }
2510 }
2511
2512 }
2513
2514 if (any_gap_cleared && comp_poly.empty()) {
2515 continue;
2516 }
2517
2518 if (!comp_poly.empty())
2519 append(new_base_areas, comp_poly);
2520
2521 }
2522
2523
2524 base_areas = std::move(new_base_areas);
2525 floor_areas = std::move(new_floor_areas);
2526 }
2527
2528 // Orca: Hybrid tree first-layer expansion belongs only to the normal-support
2529 // part. area_poly is collected from ePolygon nodes above, which are the normal
2530 // support nodes in Hybrid mode. Apply the expansion before area_groups and
2531 // lslices are built so toolpaths and brim avoidance use the same footprint.
2532 if (layer_nr == 0 && m_raft_layers == 0 && !belt_floor_active && m_support_params.support_style == smsTreeHybrid &&
2533 m_object_config->raft_first_layer_expansion.value > 0.f) {
2534 ExPolygons expanded_base_areas;
2535 const float inflate_factor_1st_layer = float(scale_(m_object_config->raft_first_layer_expansion.value));
2536 Polygons trimming = offset(m_object->layers().front()->lslices, float(scale_(m_support_params.gap_xy_first_layer)),
2537 SUPPORT_SURFACES_OFFSET_PARAMETERS);
2538 // Orca: Match normal support expansion: grow in steps and re-trim against the object each time.
2539 const int nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_support_params.first_layer_flow.scaled_width())));
2540 const float step = inflate_factor_1st_layer / nsteps;
2541 for (const ExPolygon &expoly : ts_layer->base_areas) {
2542 if (overlaps({ expoly }, area_poly)) { // normal support in Hybrid mode
2543 Polygons expanded = to_polygons(expoly);
2544 for (int i = 0; i < nsteps; ++i)
2545 expanded = diff(expand(expanded, step), trimming);
2546 append(expanded_base_areas, union_ex(expanded));
2547 } else
2548 expanded_base_areas.emplace_back(expoly);
2549 }
2550 ts_layer->base_areas = std::move(expanded_base_areas);
2551 }
2552
2553 // Orca: Final tree base polygons may be too close above model surfaces.
2554 // Enforce bottom Z clearance for non-contact support layers as well.
2555 if (!ts_layer->base_areas.empty()) {
2556 const Polygons trimming = get_trim_support_regions(
2557 *m_object, ts_layer, 0., m_slicing_params.gap_object_support, 0);
2558 if (!trimming.empty())
2559 ts_layer->base_areas = diff_ex(ts_layer->base_areas, trimming);
2560 }
2561
2562 auto &area_groups = ts_layer->area_groups;
2563
2564 for (auto& expoly : ts_layer->base_areas) {
2565 //if (area(expoly) < SQ(scale_(1))) continue;
2566 area_groups.emplace_back(&expoly, SupportLayer::BaseType, max_layers_above_base);
2567 area_groups.back().need_infill = overlaps({ expoly }, area_poly);
2568 area_groups.back().need_extra_wall = !area_groups.back().need_infill &&
2569 !extra_wall_areas.empty() && overlaps({expoly}, extra_wall_areas);
2570 }
2571 for (size_t roof_idx = 0; roof_idx < ts_layer->roof_areas.size(); ++roof_idx) {
2572 auto &expoly = ts_layer->roof_areas[roof_idx];
2573 //if (area(expoly) < SQ(scale_(1))) continue;
2574 area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof);
2575 area_groups.back().interface_as_base = roof_idx >= first_base_roof_area;
2576 }
2577 for (auto &expoly : ts_layer->floor_areas) {
2578 //if (area(expoly) < SQ(scale_(1))) continue;
2579 area_groups.emplace_back(&expoly, SupportLayer::FloorType, 10000);
2580 area_groups.back().interface_as_base = floor_interface_as_base;
2581 }
2582 for (auto &expoly : ts_layer->roof_1st_layer) {
2583 //if (area(expoly) < SQ(scale_(1))) continue;
2584 area_groups.emplace_back(&expoly, SupportLayer::Roof1stLayer, max_layers_above_roof1);
2585 area_groups.back().interface_as_base = top_base_interface_layers > 0;
2586 }
2587
2588 for (auto &area_group : area_groups) {
2589 auto& expoly = area_group.area;
2590 expoly->holes.erase(std::remove_if(expoly->holes.begin(), expoly->holes.end(),
2591 [](auto &hole) {
2592 auto bbox_size = get_extents(hole).size();
2593 return bbox_size[0] < scale_(2) && bbox_size[1] < scale_(2);
2594 }),
2595 expoly->holes.end());
2596
2597 if (layer_nr < brim_skirt_layers)
2598 ts_layer->lslices.emplace_back(*expoly);
2599 }
2600
2601 ts_layer->lslices = union_ex(ts_layer->lslices);
2602 //Must update bounding box which is used in avoid crossing perimeter
2603 ts_layer->lslices_bboxes.clear();
2604 ts_layer->lslices_bboxes.reserve(ts_layer->lslices.size());
2605
2606 for (const ExPolygon& expoly : ts_layer->lslices)
2607 ts_layer->lslices_bboxes.emplace_back(get_extents(expoly));
2608
2609 ts_layer->backup_untyped_slices();
2610
2611 }
2612 });
2613 // ORCA: normalize interface_id sequencing to follow printed interface layers only.
2614 const bool interlaced = m_object_config->support_interface_pattern == smipRectilinearInterlaced;
2615 int roof_interface_id = 0;
2616 int floor_interface_id = 0;
2617 bool has_roof_interface;
2618 bool has_floor_interface;
2619
2620 for (size_t layer_nr = 0; layer_nr < m_ts_data->layer_heights.size(); ++layer_nr) {
2621 SupportLayer *ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
2622 if (ts_layer == nullptr)
2623 continue;
2624
2625 has_roof_interface = false;
2626 has_floor_interface = false;
2627
2628 for (auto &area_group : ts_layer->area_groups) {
2629 if (area_group.type == SupportLayer::RoofType || area_group.type == SupportLayer::Roof1stLayer) {
2630 if (interlaced)
2631 area_group.interface_id = roof_interface_id;
2632 has_roof_interface = true;
2633 } else if (area_group.type == SupportLayer::FloorType) {
2634 if (interlaced)
2635 area_group.interface_id = floor_interface_id;
2636 has_floor_interface = true;
2637 }
2638 }
2639
2640 if (has_roof_interface)
2641 ++roof_interface_id;
2642
2643 if (has_floor_interface)
2644 ++floor_interface_id;
2645 }
2646
2647 if (with_lightning_infill)
2648 {
2649 const bool global_lightning_infill = true;
2650
2651 std::vector<Polygons> contours;
2652 std::vector<Polygons> overhangs;
2653 for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) {
2654 if (print->canceled()) break;
2655 const std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
2656 SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
2657 assert(ts_layer != nullptr);
2658
2659 // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
2660 if (curr_layer_nodes.empty()) continue;
2661 if (ts_layer->height < EPSILON) continue;
2662 if (ts_layer->area_groups.empty()) continue;
2663
2664 ExPolygons& base_areas = ts_layer->base_areas;
2665
2666 int layer_nr_lower = layer_nr - 1;
2667 for (; layer_nr_lower >= 0; layer_nr_lower--) {
2668 if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
2669 }
2670 if (layer_nr_lower <= 0) continue;
2671
2672 SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers);
2673 ExPolygons& base_areas_lower = lower_layer->base_areas;
2674
2675 ExPolygons overhang;
2676 if (global_lightning_infill)
2677 {
2678 //search overhangs globally
2679 overhang = diff_ex(offset_ex(base_areas_lower, -2.0 * scale_(support_extrusion_width)), base_areas);
2680 }
2681 else
2682 {
2683 //search overhangs only on floating islands
2684 for (auto& base_area : base_areas)
2685 for (auto& hole : base_area.holes)
2686 {
2687 Polygon rev_hole = hole;
2688 rev_hole.make_counter_clockwise();
2689 ExPolygons ex_hole;
2690 ex_hole.emplace_back(ExPolygon(rev_hole));
2691 for (auto& other_area : base_areas)
2692 //if (&other_area != &base_area)
2693 ex_hole = diff_ex(ex_hole, other_area);
2694 overhang = union_ex(overhang, ex_hole);
2695 }
2696 overhang = intersection_ex(overhang, offset_ex(base_areas_lower, -0.5 * scale_(support_extrusion_width)));
2697 }
2698
2699 overhangs.emplace_back(to_polygons(overhang));
2700 contours.emplace_back(to_polygons(base_areas_lower));
2701 printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1;
2702 }
2703
2704
2705 auto m_support_material_flow = support_material_flow(m_object, m_slicing_params.layer_height);
2706 coordf_t support_spacing = m_object_config->support_base_pattern_spacing.value + m_support_material_flow.spacing();
2707 coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing * 2); // for lightning infill the density is defined differently, so need to double it
2708 generator = std::make_unique<FillLightning::Generator>(m_object, contours, overhangs, []() {}, support_density);
2709 }
2710
2711 else if (!with_infill) {
2712 // move the holes to contour so they can be well supported
2713
2714 // check if poly's contour intersects with expoly's contour
2715 auto intersects_contour = [](Polygon poly, ExPolygon expoly, Point& pt_on_poly, Point& pt_on_expoly, Point& pt_far_on_poly, float dist_thresh = 0.01) {
2716 Polylines pl_out = intersection_pl(to_polylines(expoly), ExPolygon(poly));
2717 if (pl_out.empty()) return false;
2718 float min_dist = std::numeric_limits<float>::max();
2719 float max_dist = 0;
2720 for (auto from : poly.points) {
2721 for (int i = 0; i < expoly.num_contours(); i++) {
2722 const Point* candidate = expoly.contour_or_hole(i).closest_point(from);
2723 double dist2 = vsize2_with_unscale(*candidate - from);
2724 if (dist2 < min_dist) {
2725 min_dist = dist2;
2726 pt_on_poly = from;
2727 pt_on_expoly = *candidate;
2728 }
2729 if (dist2 > max_dist) {
2730 max_dist = dist2;
2731 pt_far_on_poly = from;
2732 }
2733 if (dist2 < dist_thresh) { return true; }
2734 }
2735 }
2736 return false;
2737 };
2738
2739 // polygon pointer: depth, direction, farPoint
2740 std::map<const Polygon*, std::tuple<int, Point, Point>> holePropagationInfos;
2741 for (int layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) {
2742 if (print->canceled()) break;
2743 //m_object->print()->set_status(66, (boost::format(_u8L("Support: fix holes at layer %d")) % layer_nr).str());
2744
2745 const std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
2746 SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
2747 assert(ts_layer != nullptr);
2748
2749 // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
2750 if (curr_layer_nodes.empty()) continue;
2751 if (ts_layer->height < EPSILON) continue;
2752 if (ts_layer->area_groups.empty()) continue;
2753
2754 int layer_nr_lower = layer_nr - 1;
2755 for (; layer_nr_lower >= 0; layer_nr_lower--) {
2756 if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
2757 }
2758 if (layer_nr_lower < 0) continue;
2759 auto& area_groups_lower = m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups;
2760
2761 for (const auto& area_group : ts_layer->area_groups) {
2762 if (area_group.type != SupportLayer::BaseType) continue;
2763 const auto& area = area_group.area;
2764 for (const auto& hole : area->holes) {
2765 // auto hole_bbox = get_extents(hole).polygon();
2766 for (auto& area_group_lower : area_groups_lower) {
2767 if (area_group.type != SupportLayer::BaseType) continue;
2768 auto& base_area_lower = *area_group_lower.area;
2769 Point pt_on_poly, pt_on_expoly, pt_far_on_poly;
2770 // if a hole doesn't intersect with lower layer's contours, add a hole to lower layer and move it slightly to the contour
2771 if (base_area_lower.contour.contains(hole.points.front()) && !intersects_contour(hole, base_area_lower, pt_on_poly, pt_on_expoly, pt_far_on_poly)) {
2772 Polygon hole_lower = hole;
2773 Point direction = normal(pt_on_expoly - pt_on_poly, line_width_scaled / 2);
2774 hole_lower.translate(direction);
2775 // note to expand a hole, we need to do negative offset
2776 auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, jtSquare);
2777 if (!hole_expanded.empty()) {
2778 base_area_lower.holes.push_back(std::move(hole_expanded[0]));
2779 holePropagationInfos.insert({ &base_area_lower.holes.back(), {25, direction, pt_far_on_poly} });
2780 }
2781 break;
2782 }
2783 else if (holePropagationInfos.find(&hole) != holePropagationInfos.end() && std::get<0>(holePropagationInfos[&hole]) > 0 &&
2784 base_area_lower.contour.contains(std::get<2>(holePropagationInfos[&hole]))) {
2785 Polygon hole_lower = hole;
2786 auto&& direction = std::get<1>(holePropagationInfos[&hole]);
2787 hole_lower.translate(direction);
2788 // note to shrink a hole, we need to do positive offset
2789 auto hole_expanded = offset(hole_lower, line_width_scaled / 2, jtSquare);
2790 Point farPoint = std::get<2>(holePropagationInfos[&hole]) + direction * 2;
2791 if (!hole_expanded.empty()) {
2792 base_area_lower.holes.push_back(std::move(hole_expanded[0]));
2793 holePropagationInfos.insert({ &base_area_lower.holes.back(), {std::get<0>(holePropagationInfos[&hole]) - 1, direction, farPoint} });
2794 }
2795 break;
2796 }
2797 }
2798 {
2799 // if roof1 interface is inside a hole, need to expand the interface
2800 for (auto& roof1 : ts_layer->roof_1st_layer) {
2801 //if (hole.contains(roof1.contour.points.front()) && hole.contains(roof1.contour.bounding_box().center()))
2802 bool is_inside_hole = std::all_of(roof1.contour.points.begin(), roof1.contour.points.end(), [&hole](Point& pt) { return hole.contains(pt); });
2803 if (is_inside_hole) {
2804 Polygon hole_reoriented = hole;
2805 if (roof1.contour.is_counter_clockwise())
2806 hole_reoriented.make_counter_clockwise();
2807 else if (roof1.contour.is_clockwise())
2808 hole_reoriented.make_clockwise();
2809 auto tmp = union_({ roof1.contour }, { hole_reoriented });
2810 if (!tmp.empty()) roof1.contour = tmp[0];
2811
2812 // make sure 1) roof1 and object 2) roof1 and roof, won't intersect
2813 // Note: We can't replace roof1 directly, as we have recorded its address.
2814 // So instead we need to replace its members one by one.
2815 auto tmp1 = diff_ex(roof1, get_collision(0, m_ts_data->layer_heights[layer_nr].obj_layer_nr));
2816 tmp1 = diff_ex(tmp1, ts_layer->roof_areas);
2817 if (!tmp1.empty()) {
2818 roof1.contour = std::move(tmp1[0].contour);
2819 roof1.holes = std::move(tmp1[0].holes);
2820 }
2821 break;
2822 }
2823 }
2824 }
2825 }
2826 }
2827 }
2828 }
2829
2830
2831#ifdef SUPPORT_TREE_DEBUG_TO_SVG
2832 for (int layer_nr = m_object->support_layer_count() - 1; layer_nr >= 0; layer_nr--) {
2833 SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
2834 ExPolygons& base_areas = ts_layer->base_areas;
2835 ExPolygons& roof_areas = ts_layer->roof_areas;
2836 ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer;
2837 ExPolygons roofs = roof_areas; append(roofs, roof_1st_layer);
2838 if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue;
2839 draw_contours_and_nodes_to_svg(debug_out_path("circles_%d_%.2f.svg", layer_nr, ts_layer->print_z), base_areas, roofs, ts_layer->lslices_extrudable, {}, {}, {"base", "roof", "lslices"}, {"blue","red","black"});
2840 }
2841#endif // SUPPORT_TREE_DEBUG_TO_SVG
2842
2843 SupportLayerPtrs& ts_layers = m_object->support_layers();
2844 // Orca: the vector owns its layers, so the dropped ones are deleted before they are erased.
2845 // std::stable_partition (unlike std::remove_if) leaves exactly the dropped layers in the tail.
2846 // Only heights below EPSILON are dropped; written as !(height < EPSILON), a NaN height is kept.
2847 auto iter = std::stable_partition(ts_layers.begin(), ts_layers.end(), [](SupportLayer* ts_layer) { return !(ts_layer->height < EPSILON); });
2848 for (auto it = iter; it != ts_layers.end(); ++it)
2849 delete *it;
2850 ts_layers.erase(iter, ts_layers.end());
2851 for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) {
2852 ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr;
2853 ts_layers[layer_nr]->lower_layer = layer_nr > 0 ? ts_layers[layer_nr - 1] : nullptr;
2854 }
2855}
#define TAU
Definition TreeSupport.cpp:80
bool make_counter_clockwise()
Definition Polygon.cpp:94
@ RoofType
Definition Layer.hpp:341
@ FloorType
Definition Layer.hpp:341
@ BaseType
Definition Layer.hpp:341
@ Roof1stLayer
Definition Layer.hpp:341
float nodes_angle
Definition TreeSupport.hpp:408
const ExPolygons & get_collision(coordf_t radius, size_t layer_nr)
Definition TreeSupport.cpp:2015
int avg_node_per_layer
Definition TreeSupport.hpp:407
std::unordered_map< double, size_t > printZ_to_lightninglayer
Definition TreeSupport.hpp:415
std::unique_ptr< FillLightning::Generator > generator
Definition TreeSupport.hpp:414
std::vector< std::vector< SupportNode * > > contact_nodes
Generator for model collision, avoidance and internal guide volumes.
Definition TreeSupport.hpp:436
@ jtSquare
Definition ClipperUtils.hpp:21
@ smipRectilinearInterlaced
Definition PrintConfig.hpp:374
Polylines to_polylines(const ExPolygon &src)
Definition ExPolygon.hpp:235
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat
std::vector< Polyline > Polylines
Definition Polyline.hpp:22
double vsize2_with_unscale(const Point pt)
Definition MinimumSpanningTree.cpp:22
int number_of_support_interface_bottom_layers(const PrintObjectConfig &object_config)
Definition SupportParameters.hpp:21
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
Flow support_material_flow(const PrintObject *object, float layer_height)
Definition Flow.cpp:239
std::vector< SupportLayer * > SupportLayerPtrs
Definition Print.hpp:228
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:307
constexpr T sqr(T x)
Definition libslic3r.h:301
Point normal(Point pt, double scale)
Definition TreeSupport.cpp:112
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
Definition ClipperUtils.cpp:1111
const Polygon & contour(const ExPolygon &p)
Definition AGGRaster.hpp:30
const Polygons & holes(const ExPolygon &p)
Definition AGGRaster.hpp:31
Slic3r::Polygon Polygon
Definition Emboss.cpp:101

References Slic3r::angle(), Slic3r::MultiPoint::append(), avg_node_per_layer, Slic3r::Print::config(), draw_circles(), Slic3r::Print::has_brim(), Slic3r::Print::has_infinite_skirt(), Slic3r::BeltFloorContext::init(), Slic3r::ipLightning, Slic3r::layer_height(), m_object, m_object_config, m_print_config, m_raft_layers, m_slicing_params, m_support_params, m_ts_data, nodes_angle, Slic3r::nozzle_diameter(), Slic3r::number_of_support_interface_bottom_layers(), Slic3r::Print::objects(), Slic3r::PrintObjectConfig, Slic3r::range(), scale_, Slic3r::sqr(), and TAU.

Referenced by draw_circles(), and generate().

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

◆ drop_nodes()

void Slic3r::TreeSupport::drop_nodes ( )
private

Drops down the nodes of the tree support towards the build plate.

This is where the cleverness of tree support comes in: The nodes stay on their 2D layers but on the next layer they are slightly shifted. This causes them to move towards each other as they are copied to lower layers which ultimately results in a 3D tree.

Parameters
contact_nodes[in,out]The nodes in the space that need to be dropped down. The nodes are dropped to lower layers inside the same vector of layers.
2860{
2861 const PrintObjectConfig &config = m_object->config();
2862 // Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down.
2863 const coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
2864 const coordf_t layer_height = config.layer_height.value;
2865 const double angle = config.tree_support_branch_angle.value * M_PI / 180.;
2866 const int wall_count = std::max(1, config.tree_support_wall_count.value);
2867 double tan_angle = tan(angle); // when nodes are thick, they can move further. this is the max angle
2868 const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits<coordf_t>::max();
2869 const double max_move_distance2 = max_move_distance * max_move_distance;
2870 const size_t tip_layers = base_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle.
2871 const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution;
2872 const bool support_on_buildplate_only = config.support_on_build_plate_only.value;
2873 const bool has_belt_floor = std::abs(m_slicing_params.belt_floor_shear_factor) > EPSILON;
2874 const size_t bottom_interface_layers = number_of_support_interface_bottom_layers(config);
2876 float DO_NOT_MOVER_UNDER_MM = is_slim ? 0 : 5; // do not move contact points under 5mm
2877
2878 auto get_max_move_dist = [this, tan_angle, support_extrusion_width](const SupportNode *node, int power = 1) {
2879 if (node->max_move_dist == 0) {
2880 node->radius = get_radius(node);
2881 node->max_move_dist = std::min(tan_angle * node->height, support_extrusion_width);
2882 }
2883 double move_dist = node->max_move_dist;
2884 if (power == 2) move_dist = SQ(move_dist);
2885 return move_dist;
2886 };
2887
2888 std::vector<LayerHeightData> &layer_heights = m_ts_data->layer_heights;
2889 if (layer_heights.empty()) return;
2890
2891 // precalculate avoidance of all possible radii.
2892 // This will cause computing more (radius, layer_nr) pairs, but it's worth to do so since we are doning this in parallel.
2893 if (1) {
2894 typedef std::chrono::high_resolution_clock clock_;
2895 typedef std::chrono::duration<double, std::ratio<1> > second_;
2896 std::chrono::time_point<clock_> t0{ clock_::now() };
2897
2898 // get all the possible radiis
2899 std::vector<std::set<coordf_t> > all_layer_radius(contact_nodes.size());
2900 std::vector<std::set<coordf_t>> all_layer_node_dist(contact_nodes.size());
2901 for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) {
2902 auto& layer_radius = all_layer_radius[layer_nr];
2903 auto& layer_node_dist = all_layer_node_dist[layer_nr];
2904 for (auto* p_node : contact_nodes[layer_nr]) {
2905 layer_node_dist.emplace(p_node->dist_mm_to_top);
2906 }
2907 size_t layer_nr_next = layer_nr - 1;
2908 if (layer_nr_next <= contact_nodes.size() - 1 && layer_nr_next > 0) {
2909 for (auto node_dist : layer_node_dist)
2910 all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height);
2911 }
2912 for (auto node_dist : layer_node_dist) {
2913 layer_radius.emplace(calc_radius(node_dist));
2914 }
2915 }
2916
2917 // parallel pre-compute avoidance
2918 tbb::parallel_for(tbb::blocked_range<size_t>(0, contact_nodes.size() - 1), [&](const tbb::blocked_range<size_t> &range) {
2919 for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
2920 for (auto node_radius : all_layer_radius[layer_nr]) {
2921 size_t obj_layer_nr= layer_heights[layer_nr].obj_layer_nr;
2922 m_ts_data->get_avoidance(node_radius, obj_layer_nr);
2923 get_collision(0, obj_layer_nr);
2924 get_collision(node_radius, obj_layer_nr);
2925 }
2926 }
2927 });
2928
2929 double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
2930 BOOST_LOG_TRIVIAL(debug) << "finish pre calculate_avoidance. before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size()
2931 << ", takes " << duration << " secs.";
2932 }
2933
2934 m_spanning_trees.resize(contact_nodes.size());
2935 //m_mst_line_x_layer_contour_caches.resize(contact_nodes.size());
2936
2937 for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) // Skip layer 0, since we can't drop down the vertices there.
2938 {
2939 if (m_object->print()->canceled())
2940 break;
2941
2942 auto& layer_contact_nodes = contact_nodes[layer_nr];
2943 if (layer_contact_nodes.empty())
2944 continue;
2945
2946 int layer_nr_next = layer_nr - 1;
2947 coordf_t print_z = layer_heights[layer_nr].print_z;
2948 coordf_t print_z_next = layer_heights[layer_nr_next].print_z;
2949 coordf_t height_next = layer_heights[layer_nr_next].height;
2950 size_t obj_layer_nr = layer_heights[layer_nr].obj_layer_nr;
2951 size_t obj_layer_nr_next = layer_heights[layer_nr_next].obj_layer_nr;
2952
2953 std::deque<std::pair<size_t, SupportNode*>> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches.
2954
2955 m_object->print()->set_status(60 + int(10 * (1 - float(layer_nr) / contact_nodes.size())), _u8L("Generating support"));// (boost::format(_u8L("Support: propagate branches at layer %d")) % layer_nr).str());
2956
2957 Polygons layer_contours = m_ts_data->get_contours_with_holes(obj_layer_nr);
2958 //std::unordered_map<Line, bool, LineHash>& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr];
2959 tbb::concurrent_unordered_map<Line, bool, LineHash> mst_line_x_layer_contour_cache;
2960 auto is_line_cut_by_contour = [&mst_line_x_layer_contour_cache,&layer_contours](Point a, Point b)
2961 {
2962 auto iter = mst_line_x_layer_contour_cache.find({ a, b });
2963 if (iter != mst_line_x_layer_contour_cache.end()) {
2964 if (iter->second)
2965 return true;
2966 }
2967 else {
2968 profiler.tic();
2969 Line ln(b, a);
2970 Lines pls_intersect = intersection_ln(ln, layer_contours);
2971 mst_line_x_layer_contour_cache.insert({ {a, b}, !pls_intersect.empty() });
2972 mst_line_x_layer_contour_cache.insert({ ln, !pls_intersect.empty() });
2974 if (!pls_intersect.empty())
2975 return true;
2976 }
2977 return false;
2978 };
2979
2980 //Group together all nodes for each part.
2981 const ExPolygons& parts = m_ts_data->m_layer_outlines_below[obj_layer_nr];
2982 std::vector<std::unordered_map<Point, SupportNode*, PointHash>> nodes_per_part(1 + parts.size()); //All nodes that aren't inside a part get grouped together in the 0th part.
2983 for (SupportNode* p_node : layer_contact_nodes)
2984 {
2985 const SupportNode& node = *p_node;
2986
2987 if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported.
2988 {
2989 unsupported_branch_leaves.push_front({ layer_nr, p_node });
2990 continue;
2991 }
2992 if (node.to_buildplate || parts.empty()) //It's outside, so make it go towards the build plate.
2993 {
2994 nodes_per_part[0][node.position] = p_node;
2995 continue;
2996 }
2997
2998 /* Find which part this node is located in and group the nodes in
2999 * the same part together. Since nodes have a radius and the
3000 * avoidance areas are offset by that radius, the set of parts may
3001 * be different per node. Here we consider a node to be inside the
3002 * part that is closest. The node may be inside a bigger part that
3003 * is actually two parts merged together due to an offset. In that
3004 * case we may incorrectly keep two nodes separate, but at least
3005 * every node falls into some group.
3006 */
3007 coordf_t closest_part_distance2 = std::numeric_limits<coordf_t>::max();
3008 size_t closest_part = -1;
3009 for (size_t part_index = 0; part_index < parts.size(); part_index++)
3010 {
3011 //constexpr bool border_result = true;
3012 if (is_inside_ex(parts[part_index], node.position)) //If it's inside, the distance is 0 and this part is considered the best.
3013 {
3014 closest_part = part_index;
3015 closest_part_distance2 = 0;
3016 break;
3017 }
3018
3019 Point closest_point = *parts[part_index].contour.closest_point(node.position);
3020 const coordf_t distance2 = vsize2_with_unscale(node.position - closest_point);
3021 if (distance2 < closest_part_distance2)
3022 {
3023 closest_part_distance2 = distance2;
3024 closest_part = part_index;
3025 }
3026 }
3027 //Put it in the best one.
3028 nodes_per_part[closest_part + 1][node.position] = p_node; //Index + 1 because the 0th index is the outside part.
3029 }
3030
3031 //Create a MST for every part.
3032 profiler.tic();
3033 //std::vector<MinimumSpanningTree>& spanning_trees = m_spanning_trees[layer_nr];
3034 std::vector<MinimumSpanningTree> spanning_trees;
3035 for (const std::unordered_map<Point, SupportNode*, PointHash>& group : nodes_per_part)
3036 {
3037 std::vector<Point> points_to_buildplate;
3038 for (const std::pair<const Point, SupportNode*>& entry : group)
3039 {
3040 points_to_buildplate.emplace_back(entry.first); //Just the position of the node.
3041 }
3042 spanning_trees.emplace_back(points_to_buildplate);
3043 }
3045
3046#ifdef SUPPORT_TREE_DEBUG_TO_SVG
3047 coordf_t branch_radius_temp = 0;
3048 coordf_t max_y = std::numeric_limits<coordf_t>::min();
3049 draw_layer_mst(debug_out_path("mtree_%.2f.svg", print_z), spanning_trees, m_object->get_layer(obj_layer_nr)->lslices_extrudable);
3050#endif
3051 for (size_t group_index = 0; group_index < nodes_per_part.size(); group_index++)
3052 {
3053 auto& nodes_this_part = nodes_per_part[group_index];
3054 const MinimumSpanningTree& mst = spanning_trees[group_index];
3055 //In the first pass, merge all nodes that are close together.
3056 std::vector<std::pair<const Point, SupportNode*>> nodes_vec(nodes_this_part.begin(), nodes_this_part.end());
3057 // Sequential: nodes merge into and invalidate each other in place, so parallel execution
3058 // makes the merge order (and thus the result) depend on thread scheduling.
3059 std::for_each(nodes_vec.begin(), nodes_vec.end(), [&](const std::pair<const Point, SupportNode*>& entry) {
3060 SupportNode* p_node = entry.second;
3061 SupportNode& node = *p_node;
3062 if (!p_node->valid)
3063 {
3064 return; //Delete this node (don't create a new node for it on the next layer).
3065 }
3066 const std::vector<Point>& neighbours = mst.adjacent_nodes(node.position);
3067 if (node.type == ePolygon) {
3068 // Remove all circle neighbours that are completely inside the polygon and merge them into this node.
3069 for (const Point &neighbour : neighbours) {
3070 SupportNode * neighbour_node = nodes_this_part[neighbour];
3071 if (neighbour_node->valid == false) continue;
3072 if (neighbour_node->type == ePolygon) continue;
3073 coord_t neighbour_radius = scale_(neighbour_node->radius);
3074 Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius),
3075 pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0);
3076 if (is_inside_ex(node.overhang, neighbour) && is_inside_ex(node.overhang, pt_north) && is_inside_ex(node.overhang, pt_south)
3077 && is_inside_ex(node.overhang, pt_west) && is_inside_ex(node.overhang, pt_east)){
3078 node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top);
3079 node.support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour_node->support_roof_layers_below);
3080 node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top);
3081 node.merged_neighbours.push_front(neighbour_node);
3082 node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
3083 neighbour_node->valid = false;
3084 }
3085 }
3086 } else if (neighbours.size() == 1 && vsize2_with_unscale(neighbours[0] - node.position) < get_max_move_dist(p_node, 2) &&
3087 mst.adjacent_nodes(neighbours[0]).size() == 1 &&
3088 nodes_this_part[neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon
3089 {
3090 //Insert a completely new node and let both original nodes fade.
3091 Point next_position = (node.position + neighbours[0]) / 2; //Average position of the two nodes.
3092 coordf_t next_radius = calc_radius(node.dist_mm_to_top+height_next);
3093 const ExPolygons& avoid_layer = get_avoidance(next_radius, obj_layer_nr_next);
3094 if (group_index == 0)
3095 {
3096 //Avoid collisions.
3097 const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; //100 micron extra for rounding errors.
3098 move_out_expolys(avoid_layer, next_position, radius_sample_resolution + EPSILON, max_move_between_samples);
3099 }
3100
3101 SupportNode* neighbour = nodes_this_part[neighbours[0]];
3102 SupportNode* node_parent;
3103 if (p_node->parent && neighbour->parent)
3104 node_parent = (node.dist_mm_to_top >= neighbour->dist_mm_to_top) ? p_node : neighbour;
3105 else
3106 node_parent = p_node->parent ? p_node : neighbour;
3107 // Make sure the next pass doesn't drop down either of these (since that already happened).
3108 node_parent->merged_neighbours.push_front(node_parent == p_node ? neighbour : p_node);
3109 // Belt floor: a merged node ends once its whole circle is in the belt
3110 // (its slices are clipped to the belt plane in draw_circles(), so it
3111 // tapers to a tip on the belt). Treat as object-surface termination
3112 // (not buildplate) so the node gets floor/interface areas instead of
3113 // base pads.
3114 if (has_belt_floor && belt_node_landed(next_position, std::max(node.radius, neighbour->radius), print_z_next)) {
3115 std::scoped_lock lock(m_ts_data->m_mutex);
3116 node_parent->to_buildplate = false;
3117 neighbour->valid = false;
3118 p_node->valid = false;
3119 } else {
3120 const bool to_buildplate = !is_inside_ex(get_collision(0, obj_layer_nr_next), next_position);
3121 SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, obj_layer_nr_next,
3122 node_parent->support_roof_layers_below - (node_parent->distance_to_top >= 0 ? 1 : 0),
3123 to_buildplate, node_parent, print_z_next, height_next);
3124 get_max_move_dist(next_node);
3125 std::scoped_lock lock(m_ts_data->m_mutex);
3126 contact_nodes[layer_nr_next].push_back(next_node);
3127 neighbour->valid = false;
3128 p_node->valid = false;
3129 }
3130 }
3131 else if (neighbours.size() > 1) //Don't merge leaf nodes because we would then incur movement greater than the maximum move distance.
3132 {
3133 //Remove all neighbours that are too close and merge them into this node.
3134 for (const Point& neighbour : neighbours)
3135 {
3136 if (vsize2_with_unscale(neighbour - node.position) < get_max_move_dist(&node,2))
3137 {
3138 SupportNode* neighbour_node = nodes_this_part[neighbour];
3139 if (neighbour_node->type == ePolygon) continue;
3140 // only allow bigger node to merge smaller nodes. See STUDIO-6326
3141 if(node.dist_mm_to_top < neighbour_node->dist_mm_to_top) continue;
3142
3143 m_ts_data->m_mutex.lock();
3144 if (p_node->valid)
3145 { // since we are processing all nodes in parallel, p_node may have been deleted by another thread. In this case, we should not delete neighbour_node.
3146 node.merged_neighbours.push_front(neighbour_node);
3147 node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
3148 neighbour_node->valid = false;
3149 }
3150 m_ts_data->m_mutex.unlock();
3151 }
3152 }
3153 }
3154 }
3155 );
3156
3157 //In the second pass, move all middle nodes.
3158 // Still parallel: this pass only reads other nodes. Side effects (invalidation, new
3159 // nodes, contact_nodes/unsupported_branch_leaves updates) are recorded per node and
3160 // applied afterwards in node order. Node creation must be deferred too, since
3161 // SupportNode's constructor writes `parent->child = this` on other nodes.
3162 struct PendingNode {
3163 Point position;
3164 int distance_to_top = 0;
3165 int support_roof_layers_below = 0;
3166 bool to_buildplate = false;
3167 SupportNode *parent = nullptr;
3168 bool zero_max_move = false;
3169 bool has_overhang = false;
3170 ExPolygon overhang;
3171 bool clamp_radius = false;
3172 coordf_t parent_radius = 0;
3173 double dist_to_outer = 0;
3174 };
3175 struct PassTwoResult {
3176 bool invalidate = false;
3177 bool unsupported_leaf = false;
3178 std::vector<PendingNode> pending;
3179 };
3180 std::vector<PassTwoResult> pass2_results(nodes_vec.size());
3181 auto pass2_body = [&](size_t node_idx) {
3182 const std::pair<const Point, SupportNode*>& entry = nodes_vec[node_idx];
3183 PassTwoResult& pass2_out = pass2_results[node_idx];
3184
3185 SupportNode* p_node = entry.second;
3186 const SupportNode& node = *p_node;
3187 if (!p_node->valid)
3188 {
3189 return;
3190 }
3191 if (node.type == ePolygon) {
3192 // polygon node do not merge or move
3193 const bool to_buildplate = true;
3194 // keep only the part that won't be removed by the next layer
3195 ExPolygons overhangs_next = diff_clipped({ node.overhang }, get_collision(0, obj_layer_nr_next));
3196 for(auto& overhang:overhangs_next) {
3197 Point next_pt = overhang.contour.centroid();
3198 // Belt floor: a polygon node ends once all of it is in the belt.
3199 // Treat as object-surface termination (not buildplate).
3200 if (has_belt_floor && belt_polygon_landed(overhang, print_z_next)) {
3201 p_node->to_buildplate = false;
3202 continue;
3203 }
3204 PendingNode pending;
3205 pending.position = next_pt;
3206 pending.distance_to_top = p_node->distance_to_top + 1;
3207 pending.support_roof_layers_below = p_node->support_roof_layers_below - (p_node->distance_to_top >= 0 ? 1 : 0);
3208 pending.to_buildplate = to_buildplate;
3209 pending.parent = p_node;
3210 pending.zero_max_move = true;
3211 pending.has_overhang = true;
3212 pending.overhang = std::move(overhang);
3213 pass2_out.pending.emplace_back(std::move(pending));
3214
3215 }
3216 return;
3217 }
3218
3219 //If the branch falls completely inside a collision area (the entire branch would be removed by the X/Y offset), delete it.
3220 if (group_index > 0 && is_inside_ex(get_collision(0, obj_layer_nr), node.position))
3221 {
3222 std::scoped_lock lock(m_ts_data->m_mutex);
3223 const coordf_t branch_radius_node = get_radius(p_node);
3224 Point to_outside = projection_onto(get_collision(0, obj_layer_nr), node.position);
3225 double dist2_to_outside = vsize2_with_unscale(node.position - to_outside);
3226 if (dist2_to_outside >= branch_radius_node * branch_radius_node) //Too far inside.
3227 {
3228 if (support_on_buildplate_only)
3229 {
3230 pass2_out.unsupported_leaf = true;
3231 }
3232 else {
3233 pass2_out.invalidate = true;
3234 }
3235 return;
3236 }
3237 // if the link between parent and current is cut by contours, mark current as bottom contact node
3238 if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false)
3239 {
3240 pass2_out.invalidate = true;
3241 return;
3242 }
3243 }
3244 Point next_layer_vertex = node.position;
3245 Point move_to_neighbor_center;
3246 std::vector<Point> moves;
3247 std::vector<float> weights;
3248 const std::vector<Point>& neighbours = mst.adjacent_nodes(node.position);
3249 // 1. do not merge neighbors under 5mm
3250 // 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height]
3251 float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position);
3252 if (node.print_z > DO_NOT_MOVER_UNDER_MM &&
3253 (neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= get_max_move_dist(p_node, 2)))) // Only nodes that aren't about to collapse.
3254 {
3255 // Move towards the average position of all neighbours.
3256 Point sum_direction(0, 0);
3257 for (const Point &neighbour : neighbours) {
3258 // do not move to the neighbor to be deleted
3259 SupportNode *neighbour_node = nodes_this_part[neighbour];
3260 if (!neighbour_node->valid) continue;
3261
3262 Point direction = neighbour - node.position;
3263 // do not move to neighbor that's too far away (即使以最大速度移动,在接触热床之前都无法汇聚)
3264 float dist2_to_neighbor = vsize2_with_unscale(direction);
3265
3266 coordf_t branch_bottom_radius = calc_radius(node.dist_mm_to_top + node.print_z);
3267 coordf_t neighbour_bottom_radius = calc_radius(neighbour_node->dist_mm_to_top + neighbour_node->print_z);
3268 double max_converge_distance = tan_angle * (p_node->print_z - DO_NOT_MOVER_UNDER_MM) + std::max(branch_bottom_radius, neighbour_bottom_radius);
3269 if (dist2_to_neighbor > max_converge_distance * max_converge_distance) continue;
3270
3271 if (is_line_cut_by_contour(node.position, neighbour)) continue;
3272
3273 if (!is_strong)
3274 sum_direction += direction * (1 / dist2_to_neighbor);
3275 else
3276 sum_direction += direction;
3277 }
3278
3279 if (!is_strong)
3280 move_to_neighbor_center = sum_direction;
3281 else {
3282 if (vsize2_with_unscale(sum_direction) <= get_max_move_dist(p_node, 2)) {
3283 move_to_neighbor_center = sum_direction;
3284 } else {
3285 move_to_neighbor_center = normal(sum_direction, scale_(get_max_move_dist(p_node)));
3286 }
3287 }
3288 }
3289
3290#ifdef SUPPORT_TREE_DEBUG_TO_SVG
3291 if (node.position(1) > max_y) {
3292 max_y = node.position(1);
3293 branch_radius_temp = get_radius(p_node);
3294 }
3295#endif
3296 coordf_t next_radius = calc_radius(node.dist_mm_to_top + height_next);
3297 const ExPolygons& avoidance_next = get_avoidance(next_radius, obj_layer_nr_next);
3298
3299 Point to_outside = projection_onto(avoidance_next, node.position);
3300 Point direction_to_outer = to_outside - node.position;
3301 double dist2_to_outer = vsize2_with_unscale(direction_to_outer);
3302 // don't move if
3303 // 1) line of node and to_outside is cut by contour (means supports may intersect with object)
3304 // 2) it's impossible to move to build plate
3305 if (is_line_cut_by_contour(node.position, to_outside) || dist2_to_outer > max_move_distance2 * SQ(obj_layer_nr) ||
3306 !is_inside_ex(avoidance_next, node.position)) {
3307 // try move to outside of lower layer instead
3308 Point candidate_vertex = node.position;
3309 const coordf_t max_move_between_samples = max_move_distance + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors.
3310 // use get_collision instead of get_avoidance here (See STUDIO-4252)
3311 bool is_outside = move_out_expolys(get_collision(next_radius,obj_layer_nr_next), candidate_vertex, max_move_between_samples, max_move_between_samples);
3312 if (is_outside) {
3313 direction_to_outer = candidate_vertex - node.position;
3314 dist2_to_outer = vsize2_with_unscale(direction_to_outer);
3315 } else {
3316 direction_to_outer = Point(0, 0);
3317 dist2_to_outer = 0;
3318 }
3319 }
3320 // move to the averaged direction of neighbor center and contour edge if they are roughly same direction
3321 Point movement;
3322 if (!is_strong)
3323 movement = move_to_neighbor_center*2 + (dist2_to_outer > EPSILON ? direction_to_outer * (1 / dist2_to_outer) : Point(0, 0));
3324 else {
3325 if (movement.dot(move_to_neighbor_center) >= 0.2 || move_to_neighbor_center == Point(0, 0))
3326 movement = direction_to_outer + move_to_neighbor_center;
3327 else
3328 movement = move_to_neighbor_center; // otherwise move to neighbor center first
3329 }
3330
3331 if (node.is_sharp_tail && node.dist_mm_to_top < 3) {
3332 movement = normal(node.skin_direction, scale_(get_max_move_dist(&node)));
3333 }
3334 else if (dist2_to_outer > 0)
3335 movement = normal(direction_to_outer, scale_(get_max_move_dist(&node)));
3336 else
3337 movement = normal(move_to_neighbor_center, scale_(get_max_move_dist(&node)));
3338
3339 next_layer_vertex += movement;
3340
3341 if (group_index == 0 && 0) {
3342 // Avoid collisions.
3343 const coordf_t max_move_between_samples = get_max_move_dist(&node, 1) + radius_sample_resolution + EPSILON; // 100 micron extra for rounding errors.
3344 bool is_outside = move_out_expolys(avoidance_next, next_layer_vertex, radius_sample_resolution + EPSILON, max_move_between_samples);
3345 if (!is_outside) {
3346 Point candidate_vertex = node.position;
3347 is_outside = move_out_expolys(avoidance_next, candidate_vertex, radius_sample_resolution + EPSILON, max_move_between_samples);
3348 if (is_outside) { next_layer_vertex = candidate_vertex; }
3349 }
3350 }
3351 // Belt floor: a node ends once its whole circle is in the belt; until
3352 // then it keeps dropping and draw_circles() clips each layer's circle
3353 // to the belt plane, so the branch tapers to a tip on the belt.
3354 // Treat as object-surface termination (not buildplate).
3355 if (has_belt_floor && belt_node_landed(next_layer_vertex, node.radius, print_z_next)) {
3356 p_node->to_buildplate = false;
3357 return; // from parallel_for_each lambda
3358 }
3359 const ExPolygons& next_collision = get_collision(0, obj_layer_nr_next);
3360 const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[obj_layer_nr_next], next_layer_vertex);
3361 // don't increase radius if next node will collide partially with the object (STUDIO-7883)
3362 to_outside = projection_onto(next_collision, next_layer_vertex);
3363 direction_to_outer = to_outside - node.position;
3364 double dist_to_outer = unscale_(direction_to_outer.cast<double>().norm());
3365 PendingNode pending;
3366 pending.position = next_layer_vertex;
3367 pending.distance_to_top = node.distance_to_top + 1;
3368 pending.support_roof_layers_below = node.support_roof_layers_below - (node.distance_to_top >= 0 ? 1 : 0);
3369 pending.to_buildplate = to_buildplate;
3370 pending.parent = p_node;
3371 pending.clamp_radius = true;
3372 pending.parent_radius = node.radius;
3373 pending.dist_to_outer = dist_to_outer;
3374 pass2_out.pending.emplace_back(std::move(pending));
3375 };
3376 tbb::parallel_for(tbb::blocked_range<size_t>(0, nodes_vec.size()),
3377 [&pass2_body](const tbb::blocked_range<size_t>& node_range) {
3378 for (size_t node_idx = node_range.begin(); node_idx < node_range.end(); ++ node_idx)
3379 pass2_body(node_idx);
3380 });
3381 // Apply the recorded side effects in node order.
3382 for (size_t node_idx = 0; node_idx < nodes_vec.size(); ++ node_idx) {
3383 PassTwoResult& pass2_out = pass2_results[node_idx];
3384 for (PendingNode& pending : pass2_out.pending) {
3385 SupportNode* next_node = m_ts_data->create_node(pending.position, pending.distance_to_top, obj_layer_nr_next,
3386 pending.support_roof_layers_below, pending.to_buildplate, pending.parent, print_z_next, height_next);
3387 if (pending.zero_max_move)
3388 next_node->max_move_dist = 0;
3389 if (pending.has_overhang)
3390 next_node->overhang = std::move(pending.overhang);
3391 if (pending.clamp_radius) {
3392 next_node->radius = std::max(pending.parent_radius, std::min(next_node->radius, pending.dist_to_outer));
3393 get_max_move_dist(next_node);
3394 }
3395 contact_nodes[layer_nr_next].push_back(next_node);
3396 }
3397 if (pass2_out.unsupported_leaf)
3398 unsupported_branch_leaves.push_front({ layer_nr, nodes_vec[node_idx].second });
3399 if (pass2_out.invalidate)
3400 nodes_vec[node_idx].second->valid = false;
3401 }
3402 }
3403
3404#ifdef SUPPORT_TREE_DEBUG_TO_SVG
3405 if (contact_nodes[layer_nr].empty() == false) {
3406 draw_contours_and_nodes_to_svg(debug_out_path("contact_points_%.2f.svg", contact_nodes[layer_nr][0]->print_z), get_collision(0,obj_layer_nr_next),
3407 get_avoidance(branch_radius_temp, obj_layer_nr),
3408 m_ts_data->m_layer_outlines[obj_layer_nr],
3409 contact_nodes[layer_nr], contact_nodes[layer_nr_next], { "overhang","avoid","outline" }, { "blue","red","yellow" });
3410
3411 BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer->next " << layer_nr << "->" << layer_nr_next << ", print_z=" << print_z
3412 << ", num points: " << contact_nodes[layer_nr].size() << "->" << contact_nodes[layer_nr_next].size();
3413 for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) {
3414 auto &node = contact_nodes[layer_nr][i];
3415 BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement;
3416 }
3417 }
3418#endif
3419
3420 // Prune all branches that couldn't find support on either the model or the buildplate (resulting in 'mid-air' branches).
3421 for (;! unsupported_branch_leaves.empty(); unsupported_branch_leaves.pop_back())
3422 {
3423 const auto& entry = unsupported_branch_leaves.back();
3424 SupportNode* i_node = entry.second;
3425 for (; i_node != nullptr; i_node = i_node->parent)
3426 {
3427 size_t i_layer = i_node->obj_layer_nr;
3428 {
3429 // update the parent-child chain
3430 if (i_node->parent) {
3431 i_node->parent->child = i_node->child;
3432 for (SupportNode* parent : i_node->parents) {
3433 if (parent->child==i_node)
3434 parent->child = i_node->child;
3435 }
3436 }
3437 if (i_node->child) {
3438 i_node->child->parent = i_node->parent;
3439 i_node->child->parents.erase(std::find(i_node->child->parents.begin(), i_node->child->parents.end(), i_node));
3440 append(i_node->child->parents, i_node->parents);
3441 }
3442 i_node->is_processed = true; // mark to be deleted later
3443
3444 for (SupportNode* neighbour : i_node->merged_neighbours)
3445 {
3446 if (neighbour && !neighbour->is_processed)
3447 unsupported_branch_leaves.push_front({ i_layer, neighbour });
3448 }
3449 }
3450 }
3451 }
3452 for (auto &layer_contact_nodes : contact_nodes) {
3453 if (!layer_contact_nodes.empty())
3454 layer_contact_nodes.erase(std::remove_if(layer_contact_nodes.begin(), layer_contact_nodes.end(), [](SupportNode *node) { return node->is_processed; }),
3455 layer_contact_nodes.end());
3456 }
3457 }
3458
3459 BOOST_LOG_TRIVIAL(debug) << "after m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size();
3460}
void stage_add(TreeSupportStage stage)
Definition TreeSupport.cpp:200
void tic()
Definition TreeSupport.cpp:195
coordf_t calc_radius(coordf_t mm_to_top)
Definition TreeSupport.cpp:1997
bool belt_polygon_landed(const ExPolygon &poly, double print_z) const
Definition TreeSupport.cpp:727
bool belt_node_landed(const Point &pos_slicing, double radius, double print_z) const
Definition TreeSupport.cpp:719
std::vector< std::vector< MinimumSpanningTree > > m_spanning_trees
Definition TreeSupport.hpp:446
const ExPolygons & get_avoidance(coordf_t radius, size_t obj_layer_nr)
Definition TreeSupport.cpp:2011
coordf_t get_radius(const SupportNode *node)
Definition TreeSupport.cpp:2002
#define unscale_(val)
Definition libslic3r.h:107
std::vector< Line > Lines
Definition Line.hpp:20
Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
Definition ClipperUtils.hpp:571
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
static bool is_inside_ex(const ExPolygon &polygon, const Point &pt)
Definition TreeSupport.cpp:625
Point projection_onto(const ExPolygons &polygons, const Point &from)
Definition ExPolygon.cpp:212
TreeSupportProfiler profiler
Definition TreeSupport.cpp:227
@ ePolygon
Definition TreeSupport.hpp:56
static bool move_out_expolys(const ExPolygons &polygons, Point &from, double distance, double max_move_distance)
Definition TreeSupport.cpp:644
@ STAGE_intersection_ln
Definition TreeSupport.cpp:158
@ STAGE_MinimumSpanningTree
Definition TreeSupport.cpp:154
@ lock
Definition GLGizmoEmboss.cpp:209
#define _u8L(s)
Definition I18N.hpp:11
static double diameter_angle_scale_factor
Definition TreeSupport.hpp:126

References Slic3r::angle(), base_radius, calc_radius(), contact_nodes, Slic3r::SupportNode::diameter_angle_scale_factor, diameter_angle_scale_factor, drop_nodes(), EPSILON, get_radius(), is_slim, Slic3r::layer_height(), m_object, M_PI, m_slicing_params, m_support_params, m_ts_data, Slic3r::number_of_support_interface_bottom_layers(), Slic3r::PrintObjectConfig, Slic3r::range(), and SQ.

Referenced by drop_nodes(), and generate().

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

◆ generate()

void Slic3r::TreeSupport::generate ( )

Create the areas that need support.

These areas are stored inside the given SliceDataStorage object.

Parameters
storageThe data storage where the mesh data is gotten from and where the resulting support areas are stored.
1902{
1903 if (!is_tree(m_object_config->support_type.value)) return;
1904
1905 if (m_support_params.support_style == smsTreeOrganic) {
1907 return;
1908 }
1909
1911
1912 // Generate overhang areas
1914 m_object->print()->set_status(55, _u8L("Generating support"));
1917
1919 m_ts_data = m_object->alloc_tree_support_preview_cache();
1920 m_ts_data->is_slim = is_slim;
1921 // // get the ring of outside plate
1922 // auto tmp= diff_ex(offset_ex(m_machine_border, scale_(100)), m_machine_border);
1923 // if (!tmp.empty()) m_ts_data->m_machine_border = tmp[0];
1924
1925 std::vector<TreeSupport3D::SupportElements> move_bounds(m_highest_overhang_layer + 1);
1927 m_object->print()->set_status(56, _u8L("Support: generate contact points"));
1930
1931 m_ts_data->layer_heights = plan_layer_heights();
1932
1933 //Drop nodes to lower layers.
1935 m_object->print()->set_status(60, _u8L("Generating support"));
1936 drop_nodes();
1938
1939 smooth_nodes();// , tree_support_3d_config);
1940
1941 //Generate support areas.
1943 m_object->print()->set_status(65, _u8L("Generating support"));
1944 draw_circles();
1946
1947
1948
1950 m_object->print()->set_status(70, _u8L("Generating support"));
1953
1955 BOOST_LOG_TRIVIAL(info) << "tree support time " << profiler.report();
1956}
std::string report()
Definition TreeSupport.cpp:207
void stage_finish(TreeSupportStage stage)
Definition TreeSupport.cpp:186
void stage_start(TreeSupportStage stage)
Definition TreeSupport.cpp:178
void draw_circles()
Draws circles around each node of the tree into the final support.
Definition TreeSupport.cpp:2133
std::function< void()> throw_on_cancel
Definition TreeSupport.hpp:417
void detect_overhangs(bool check_support_necessity=false)
Definition TreeSupport.cpp:739
std::vector< LayerHeightData > plan_layer_heights()
Optimize the generation of tree support by pre-planning the layer_heights.
Definition TreeSupport.cpp:3523
void generate_toolpaths()
Definition TreeSupport.cpp:1532
void smooth_nodes()
Definition TreeSupport.cpp:3462
void create_tree_support_layers()
Definition TreeSupport.cpp:1334
void drop_nodes()
Drops down the nodes of the tree support towards the build plate.
Definition TreeSupport.cpp:2859
void generate_contact_points()
Creates points where support contacts the model.
Definition TreeSupport.cpp:3659
@ STAGE_DETECT_OVERHANGS
Definition TreeSupport.cpp:149
@ STAGE_DROP_DOWN_NODES
Definition TreeSupport.cpp:151
@ STAGE_GENERATE_TOOLPATHS
Definition TreeSupport.cpp:153
@ STAGE_DRAW_CIRCLES
Definition TreeSupport.cpp:152
@ STAGE_total
Definition TreeSupport.cpp:159
@ STAGE_GENERATE_CONTACT_NODES
Definition TreeSupport.cpp:150
@ smsTreeOrganic
Definition PrintConfig.hpp:363
void generate_tree_support_3D(PrintObject &print_object, TreeSupport *tree_support, std::function< void()> throw_on_cancel)
Definition TreeSupport3D.cpp:4315

References _u8L, create_tree_support_layers(), detect_overhangs(), draw_circles(), drop_nodes(), generate(), generate_contact_points(), generate_toolpaths(), Slic3r::generate_tree_support_3D(), is_slim, Slic3r::is_tree(), m_highest_overhang_layer, m_object, m_object_config, m_support_params, m_ts_data, plan_layer_heights(), Slic3r::profiler, smooth_nodes(), Slic3r::smsTreeOrganic, Slic3r::STAGE_DETECT_OVERHANGS, Slic3r::STAGE_DRAW_CIRCLES, Slic3r::STAGE_DROP_DOWN_NODES, Slic3r::STAGE_GENERATE_CONTACT_NODES, Slic3r::STAGE_GENERATE_TOOLPATHS, Slic3r::STAGE_total, and throw_on_cancel.

Referenced by Slic3r::PrintObject::_generate_support_material(), and generate().

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

◆ generate_contact_points()

void Slic3r::TreeSupport::generate_contact_points ( )
private

Creates points where support contacts the model.

A set of points is created for each layer.

Parameters
meshThe mesh to get the overhang areas to support of.
contact_nodes[out]A vector of mappings from contact points to their tree nodes.
collision_areasFor every layer, the areas where a generated contact point would immediately collide with the model due to the X/Y distance.
Returns
For each layer, a list of points where the tree should connect with the model.
3660{
3661 const PrintObjectConfig &config = m_object->config();
3662 const coordf_t point_spread = scale_(config.tree_support_branch_distance.value);
3663 const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
3664 coord_t radius_scaled = scale_(base_radius);
3665 bool on_buildplate_only = m_object_config->support_on_build_plate_only.value;
3666 const bool has_belt_floor = std::abs(m_slicing_params.belt_floor_shear_factor) > EPSILON;
3667
3668 //First generate grid points to cover the entire area of the print.
3669 BoundingBox bounding_box = m_object->bounding_box();
3670 const Point bounding_box_size = bounding_box.max - bounding_box.min;
3671 constexpr double rotate_angle = 22.0 / 180.0 * M_PI;
3672
3674 const auto sin_angle = std::sin(rotate_angle);
3675 const auto cos_angle = std::cos(rotate_angle);
3676 const Point rotated_dims = Point(
3677 bounding_box_size(0) * cos_angle + bounding_box_size(1) * sin_angle,
3678 bounding_box_size(0) * sin_angle + bounding_box_size(1) * cos_angle) / 2;
3679
3680 std::vector<Point> grid_points;
3681 coordf_t sample_step = std::max(point_spread, max_bridge_length / 2);
3682 for (auto x = -rotated_dims(0); x < rotated_dims(0); x += sample_step) {
3683 for (auto y = -rotated_dims(1); y < rotated_dims(1); y += sample_step) {
3684 Point pt(x, y);
3685 pt.rotate(cos_angle, sin_angle);
3686 pt += center;
3687 if (bounding_box.contains(pt)) {
3688 grid_points.push_back(pt);
3689 }
3690 }
3691 }
3692
3693 const coordf_t layer_height = config.layer_height.value;
3694 coordf_t z_distance_top = this->top_z_distance;
3695 // if (!m_support_params.independent_layer_height) {
3696 // z_distance_top = round(z_distance_top / layer_height) * layer_height;
3697 // // BBS: add extra distance if thick bridge is enabled
3698 // // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height
3699 // if (!m_slicing_params.zero_gap_interface_top && m_object_config->thick_bridges) {
3700 // z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height;
3701 //}
3702 // }
3703 const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang.
3704 int gap_layers = z_distance_top == 0 ? 0 : 1;
3705
3706 size_t support_roof_layers = config.support_interface_top_layers.value;
3707 coordf_t thresh_angle = std::min(89.f, config.support_threshold_angle.value < EPSILON ? 30.f : config.support_threshold_angle.value);
3708 coordf_t half_overhang_distance = scale_(tan(thresh_angle * M_PI / 180.0) * layer_height / 2);
3709
3710 // fix bug of generating support for very thin objects
3711 if (m_object->layers().size() <= z_distance_top_layers + 1)
3712 return;
3713
3714 contact_nodes.clear();
3715 contact_nodes.resize(m_object->layers().size());
3716
3717 tbb::spin_mutex mtx;
3718
3719 // add vertical enforcer points
3720 std::vector<float> zs = zs_from_layers(m_object->layers());
3721 std::vector<std::vector<std::pair<Point,Point>>> vertical_enforcer_points_by_layers(m_object->layer_count());
3722 for (auto& pt_and_normal : m_vertical_enforcer_points) {
3723 auto pt = pt_and_normal.first;
3724 auto normal = pt_and_normal.second; // normal seems useless
3725 auto iter = std::lower_bound(zs.begin(), zs.end(), pt.z());
3726 if (iter != zs.end()) {
3727 size_t layer_nr = iter - zs.begin();
3728 if (layer_nr > 0 && layer_nr < contact_nodes.size()) {
3729 vertical_enforcer_points_by_layers[layer_nr].push_back({ to_2d(pt).cast<coord_t>(),scaled(to_2d(normal)) });
3730 }
3731 }
3732 }
3733
3734 int nonempty_layers = 0;
3735 tbb::concurrent_vector<Slic3r::Vec3f> all_nodes;
3736 tbb::parallel_for(tbb::blocked_range<size_t>(1, m_object->layers().size()), [&](const tbb::blocked_range<size_t>& range) {
3737 for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
3738 if (m_object->print()->canceled())
3739 break;
3740 Layer* layer = m_object->get_layer(layer_nr);
3741 auto& curr_nodes = contact_nodes[layer_nr-1];
3742
3743 std::unordered_set<Point, PointHash> already_inserted;
3744 auto bottom_z = m_object->get_layer(layer_nr)->bottom_z();
3745 bool added = false; // Did we add a point this way?
3746 bool is_sharp_tail = false;
3747
3748 // take the least restrictive avoidance possible
3749 ExPolygons relevant_forbidden = offset_ex(m_ts_data->m_layer_outlines[layer_nr - 1], scale_(MIN_BRANCH_RADIUS));
3750 // prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise.
3751 relevant_forbidden = offset_ex(union_ex(relevant_forbidden), scaled<float>(0.005), jtMiter, 1.2);
3752
3753
3754 auto insert_point = [&](Point pt, const ExPolygon& overhang, double radius, bool force_add = false, bool add_interface=true) {
3755 // Belt floor: skip contact points whose bottom_z is at or below
3756 // the belt floor at this XY position (overhang rests on the belt).
3757 if (has_belt_floor && bottom_z <= belt_floor_print_z(pt))
3758 return (SupportNode*) nullptr;
3759 Point hash_pos = pt / ((radius_scaled + 1) / 1);
3760 SupportNode* contact_node = nullptr;
3761 if (force_add || !already_inserted.count(hash_pos)) {
3762 already_inserted.emplace(hash_pos);
3763 bool to_buildplate = true;
3764 size_t roof_layers = add_interface ? support_roof_layers : 0;
3765 // add a new node as a virtual node which acts as the invisible gap between support and object
3766 // distance_to_top=-1: it's virtual
3767 // print_z=object_layer->bottom_z: it directly contacts the bottom
3768 // height=z_distance_top: it's height is exactly the gap distance
3769 // dist_mm_to_top=0: it directly contacts the bottom
3770 contact_node = m_ts_data->create_node(pt, -gap_layers, layer_nr-1, roof_layers, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0,
3771 radius);
3772 contact_node->overhang = overhang;
3773 contact_node->is_sharp_tail = is_sharp_tail;
3774 curr_nodes.emplace_back(contact_node);
3775 added = true;
3776 };
3777 return contact_node;
3778 };
3779
3780 for (const auto& overhang_part : layer->loverhangs) {
3781 const auto& overhang_type = this->overhang_types[&overhang_part];
3782 is_sharp_tail = overhang_type == OverhangType::SharpTail;
3783 ExPolygons overhangs_regular;
3784 if (m_support_params.support_style == smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang && !is_sharp_tail) {
3785 overhangs_regular = offset_ex(intersection_ex({overhang_part}, m_ts_data->m_layer_outlines_below[layer_nr - 1]), radius_scaled);
3786 ExPolygons overhangs_normal = diff_ex({overhang_part}, overhangs_regular);
3787 if (area(overhangs_normal) > m_support_params.thresh_big_overhang) {
3788 // if the outside area is still big, we can need normal nodes
3789 for (auto &overhang : overhangs_normal) {
3790 BoundingBox overhang_bounds = get_extents(overhang);
3791 double radius = unscale_(overhang_bounds.radius());
3792 Point candidate = overhang_bounds.center();
3793 SupportNode *contact_node = insert_point(candidate, overhang, radius, true, true);
3794 if (contact_node) {
3795 contact_node->type = ePolygon;
3796 curr_nodes.emplace_back(contact_node);
3797 }
3798 }
3799 }else{
3800 // otherwise, all nodes should be circle nodes
3801 overhangs_regular = ExPolygons{overhang_part};
3802 }
3803 } else {
3804 overhangs_regular = ExPolygons{overhang_part};
3805 }
3806
3807 for (auto &overhang : overhangs_regular) {
3808 bool add_interface = area(overhang) > minimum_roof_area && !is_sharp_tail;
3809 BoundingBox overhang_bounds = get_extents(overhang);
3810 double radius = std::clamp(unscale_(overhang_bounds.radius()), MIN_BRANCH_RADIUS, base_radius);
3811 // add supports at corners for both auto and manual overhangs, github #2008
3812 auto &points = overhang.contour.points;
3813 int nSize = points.size();
3814 for (int i = 0; i < nSize; i++) {
3815 auto pt = points[i];
3816 Vec2d v1 = (pt - points[(i - 1 + nSize) % nSize]).cast<double>().normalized();
3817 Vec2d v2 = (pt - points[(i + 1) % nSize]).cast<double>().normalized();
3818 if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees
3819 SupportNode *contact_node = insert_point(pt, overhang, radius, false, add_interface);
3820 if (contact_node) {
3821 contact_node->is_corner = true;
3822 }
3823 }
3824 }
3825
3826 // add supports along contours
3827 ExPolygon closed_overhang = overhang; // make a copy to add closing point for edge cache
3828 if (closed_overhang.contour.points.size() > 1)
3829 closed_overhang.contour.points.emplace_back(closed_overhang.contour.points.front());
3830 for (Polygon &hole : closed_overhang.holes)
3831 if (hole.points.size() > 1)
3832 hole.points.emplace_back(hole.points.front());
3833
3834 libnest2d::placers::EdgeCache<ExPolygon> edge_cache(closed_overhang);
3835 for (size_t i = 0; i < edge_cache.holeCount() + 1; i++) {
3836 double step = point_spread / (i == 0 ? edge_cache.circumference() : edge_cache.circumference(i - 1));
3837 double distance = 0;
3838 while (distance < 1) {
3839 auto pt = i == 0 ? edge_cache.coords(distance) : edge_cache.coords(i - 1, distance);
3840 SupportNode *contact_node = insert_point(pt, overhang,radius, false, add_interface);
3841 distance += step;
3842 }
3843 }
3844
3845 // don't add inner supports for sharp tails
3846 if (is_sharp_tail) continue;
3847
3848 // add inner supports
3849 overhang_bounds.inflated(-radius_scaled);
3850 ExPolygons overhang_inner = offset_ex(overhang, -radius_scaled);
3851 for (Point candidate : grid_points) {
3852 if (overhang_bounds.contains(candidate)) {
3853 // BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported!
3854 bool is_inside = is_inside_ex(overhang_inner, candidate);
3855 if (is_inside) { SupportNode *contact_node = insert_point(candidate, overhang,radius, false, add_interface); }
3856 }
3857 }
3858 }
3859 }
3860 for (auto& pt_and_normal : vertical_enforcer_points_by_layers[layer_nr]) {
3861 is_sharp_tail = true;// fake it as sharp tail point so the contact distance will be 0
3862 auto vertical_enforcer_point= pt_and_normal.first;
3863 auto node=insert_point(vertical_enforcer_point, ExPolygon(), false);
3864 if (node)
3865 node->skin_direction = pt_and_normal.second;
3866 }
3867 if (!curr_nodes.empty()) nonempty_layers++;
3868 for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); }
3869#ifdef SUPPORT_TREE_DEBUG_TO_SVG
3870 if (!curr_nodes.empty())
3871 draw_contours_and_nodes_to_svg(debug_out_path("init_contact_points_%.2f.svg", bottom_z), layer->loverhangs,layer->lslices_extrudable, m_ts_data->m_layer_outlines_below[layer_nr],
3872 contact_nodes[layer_nr], contact_nodes[layer_nr - 1], { "overhang","lslices","outlines_below"});
3873#endif
3874 }}
3875 ); // end tbb::parallel_for
3876
3877
3878
3879 int nNodes = all_nodes.size();
3881 if (nNodes > 0) {
3882 avg_node_per_layer = nNodes / nonempty_layers;
3883 // get orientation of nodes by line fitting
3884 // line: y=kx+b, where
3885 // k=tan(nodes_angle)=(n\sum{xy}-\sum{x}\sum{y})/(n\sum{x^2}-\sum{x}^2)
3886 float mx = 0, my = 0, mxy = 0, mx2 = 0;
3887 for (auto &pt : all_nodes) {
3888 float x = unscale_(pt(0));
3889 float y = unscale_(pt(1));
3890 mx += x;
3891 my += y;
3892 mxy += x * y;
3893 mx2 += x * x;
3894 }
3895 nodes_angle = atan2(nNodes * mxy - mx * my, nNodes * mx2 - SQ(mx));
3896
3897 BOOST_LOG_TRIVIAL(info) << "avg_node_per_layer=" << avg_node_per_layer << ", nodes_angle=" << nodes_angle;
3898 }
3899}
bool contains(const PointType &point) const
Definition BoundingBox.hpp:161
int64_t coord_t
Definition libslic3r.h:48
static Point bounding_box_middle(const BoundingBox &bbox)
Definition TreeSupport.cpp:674
std::vector< float > zs_from_layers(const LayerContainer &layers)
Definition Layer.hpp:357
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382
BoundingBoxf3 bounding_box(const TriangleMesh &m)
Definition TriangleMesh.hpp:391
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > to_2d(const Eigen::MatrixBase< Derived > &ptN)
Definition Point.hpp:147
INDEX_TYPE round_up_divide(const INDEX_TYPE dividend, const INDEX_TYPE divisor)
Definition Utils.hpp:463

References base_radius, Slic3r::bounding_box(), Slic3r::bounding_box_middle(), Slic3r::center(), contact_nodes, EPSILON, generate_contact_points(), Slic3r::layer_height(), m_object, m_object_config, M_PI, m_slicing_params, m_vertical_enforcer_points, Slic3r::normal(), Slic3r::PrintObjectConfig, Slic3r::range(), Slic3r::Point::rotate(), Slic3r::round_up_divide(), scale_, Slic3r::scaled(), Slic3r::to_2d(), top_z_distance, and Slic3r::zs_from_layers().

Referenced by generate(), and generate_contact_points().

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

◆ generate_toolpaths()

void Slic3r::TreeSupport::generate_toolpaths ( )
private
1533{
1534 const PrintObjectConfig &object_config = m_object->config();
1535 coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
1536 coordf_t nozzle_diameter = nozzle_diameter_for_filament(*m_print_config, object_config.support_filament, m_object->print()->is_BBL_printer());
1537 coordf_t layer_height = object_config.layer_height.value;
1538 const size_t wall_count = object_config.tree_support_wall_count.value;
1539
1540 // Check if set to zero, use default if so.
1541 if (support_extrusion_width <= 0.0)
1543
1544 // coconut: use same intensity settings as SupportMaterial.cpp
1545 auto m_support_material_interface_flow = support_material_interface_flow(m_object, float(m_slicing_params.layer_height));
1546 coordf_t interface_spacing = object_config.support_interface_spacing.value + m_support_material_interface_flow.spacing();
1547 coordf_t bottom_interface_spacing = object_config.support_bottom_interface_spacing.value + m_support_material_interface_flow.spacing();
1548 coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing);
1549 coordf_t bottom_interface_density = std::min(1., m_support_material_interface_flow.spacing() / bottom_interface_spacing);
1550
1551 const coordf_t branch_radius = object_config.tree_support_branch_diameter.value / 2;
1552 const coordf_t branch_radius_scaled = scale_(branch_radius);
1553
1554 if (m_object->support_layers().empty())
1555 return;
1556
1557 // calculate fill areas for raft layers
1558 ExPolygons raft_areas;
1559 if (m_object->layer_count() > 0) {
1560 const Layer *layer = m_object->layers().front();
1561 for (const ExPolygon &expoly : layer->lslices) {
1562 raft_areas.push_back(expoly);
1563 }
1564 }
1565
1566 if (m_object->support_layer_count() > m_raft_layers) {
1567 const SupportLayer *ts_layer = m_object->get_support_layer(m_raft_layers);
1568 for (const ExPolygon& expoly : ts_layer->floor_areas)
1569 raft_areas.push_back(expoly);
1570 for (const ExPolygon& expoly : ts_layer->roof_areas)
1571 raft_areas.push_back(expoly);
1572 for (const ExPolygon& expoly : ts_layer->base_areas)
1573 raft_areas.push_back(expoly);
1574 }
1575
1576 raft_areas = offset_ex(raft_areas, scale_(object_config.raft_first_layer_expansion));
1577
1578 size_t layer_nr = 0;
1579 for (; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) {
1580 SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
1581 coordf_t expand_offset = (layer_nr == 0 ? m_object_config->raft_first_layer_expansion.value : 0.);
1582 auto raft_areas1 = offset_ex(raft_areas, scale_(expand_offset));
1583
1584 Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
1585 Fill* filler_raft = Fill::new_from_type(ipRectilinear);
1586 filler_raft->angle = layer_nr == 0 ? PI/2 : 0;
1587 filler_raft->spacing = support_flow.spacing();
1588
1589 FillParams fill_params;
1590 coordf_t raft_density = std::min(1., support_flow.spacing() / (object_config.support_base_pattern_spacing.value + support_flow.spacing()));
1591 fill_params.density = layer_nr == 0 ? object_config.raft_first_layer_density * 0.01 : raft_density;
1592 fill_params.dont_adjust = true;
1593
1594 // wall of first layer raft
1595 if (layer_nr == 0) {
1596 Flow flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
1597 extrusion_entities_append_loops(ts_layer->support_fills.entities, to_polygons(raft_areas1), erSupportMaterial,
1598 float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
1599 raft_areas1 = offset_ex(raft_areas1, -flow.scaled_spacing() / 2.);
1600 }
1601 fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_areas1,
1602 filler_raft, fill_params, erSupportMaterial, support_flow);
1603 }
1604
1605 // subtract the non-raft support bases, otherwise we'll get support base on top of raft interfaces which is not stable
1606 ExPolygons first_non_raft_base;
1607 SupportLayer* first_non_raft_layer = m_object->get_support_layer(m_raft_layers);
1608 if (first_non_raft_layer) {
1609 for (auto& area_group : first_non_raft_layer->area_groups) {
1610 if (area_group.type == SupportLayer::BaseType)
1611 first_non_raft_base.emplace_back(*area_group.area);
1612 }
1613 }
1614 first_non_raft_base = offset_ex(first_non_raft_base, support_extrusion_width);
1615 ExPolygons raft_base_areas = intersection_ex(raft_areas, first_non_raft_base);
1616 ExPolygons raft_interface_areas = diff_ex(raft_areas, raft_base_areas);
1617
1618
1619 // raft interfaces
1620 for (layer_nr = m_slicing_params.base_raft_layers;
1621 layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers;
1622 layer_nr++)
1623 {
1624 SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
1625
1626 Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
1627 Fill* filler_interface = Fill::new_from_type(ipRectilinear);
1628 filler_interface->angle = M_PI_2; // interface should be perpendicular to base
1629 filler_interface->spacing = support_flow.spacing();
1630
1631 FillParams fill_params;
1632 fill_params.density = interface_density;
1633 fill_params.dont_adjust = true;
1634
1635 fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_interface_areas,
1636 filler_interface, fill_params, erSupportMaterialInterface, support_flow);
1637
1638 fill_params.density = object_config.raft_first_layer_density * 0.01;
1639 fill_expolygons_generate_paths(ts_layer->support_fills.entities, raft_base_areas,
1640 filler_interface, fill_params, erSupportMaterial, support_flow);
1641 }
1642
1643 // layers between raft and object
1644 for (; layer_nr < m_raft_layers; layer_nr++) {
1645 SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
1646 Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
1647 Fill* filler_raft = Fill::new_from_type(ipRectilinear);
1648 filler_raft->angle = M_PI_2;
1649 filler_raft->spacing = support_flow.spacing();
1650 for (auto& poly : first_non_raft_base)
1651 make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::min(size_t(1), wall_count), support_flow, erSupportMaterial, filler_raft, interface_density, false);
1652 }
1653
1654 if (m_object->support_layer_count() <= m_raft_layers)
1655 return;
1656
1657 BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.)));
1658 // ORCA: base angle used for explicit interlaced interface orientation.
1659 const float base_support_angle = Geometry::deg2rad(object_config.support_angle.value);
1660
1661 // Belt floor: the lowest support layer rests on the moving, tilted belt, not
1662 // on a flat bed — so it must NOT get the bed first-layer treatment (a brim on
1663 // interface areas, a first-layer-flow sheath at raft_first_layer_density on
1664 // base areas). That treatment draws a loop along the Z=0 belt-floor line that
1665 // reads as a stray brim/skirt. Gate those layer_id==0 special cases off when
1666 // the belt floor is active; false on non-belt printers so behavior is unchanged.
1667 BeltFloorContext belt_ctx;
1668 const bool belt_floor_active = belt_ctx.init(m_slicing_params, *m_print_config);
1669
1670 // generate tree support tool paths
1671 tbb::parallel_for(
1672 tbb::blocked_range<size_t>(m_raft_layers, m_object->support_layer_count()),
1673 [&](const tbb::blocked_range<size_t>& range)
1674 {
1675 for (size_t layer_id = range.begin(); layer_id < range.end(); layer_id++) {
1676 if (m_object->print()->canceled())
1677 break;
1678
1679 //m_object->print()->set_status(70, (boost::format(_u8L("Support: generate toolpath at layer %d")) % layer_id).str());
1680
1681 SupportLayer* ts_layer = m_object->get_support_layer(layer_id);
1682 Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
1683 Flow interface_flow = support_material_interface_flow(m_object, ts_layer->height); // update flow using real support layer height
1684 coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing();
1685 coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing);
1686 ts_layer->support_fills.no_sort = false;
1687 // ORCA: per-layer Fill instances to avoid shared-state races during interlaced interfaces.
1688 std::shared_ptr<Fill> filler_interface = std::shared_ptr<Fill>(Fill::new_from_type(m_support_params.contact_fill_pattern));
1689 std::shared_ptr<Fill> filler_Roof1stLayer = std::shared_ptr<Fill>(Fill::new_from_type(ipRectilinear));
1690 filler_interface->set_bounding_box(bbox_object);
1691 filler_Roof1stLayer->set_bounding_box(bbox_object);
1692
1693 for (auto& area_group : ts_layer->area_groups) {
1694 ExPolygon& poly = *area_group.area;
1695 ExPolygons polys;
1696 FillParams fill_params;
1697 // ORCA: reset interface Fill state per area group to keep angles deterministic.
1698 filler_interface->fixed_angle = false;
1699 filler_interface->layer_id = size_t(-1);
1700 filler_Roof1stLayer->fixed_angle = false;
1701 filler_Roof1stLayer->layer_id = size_t(-1);
1702 filler_interface->angle = m_support_params.support_interface_angle(area_group.interface_id);
1703 if (area_group.type != SupportLayer::BaseType) {
1704 // interface
1705 if (layer_id == 0 && !belt_floor_active) {
1706 Flow flow = m_raft_layers == 0 ? m_object->print()->brim_flow() : support_flow;
1707 ExtrusionRole brim_role = (area_group.type == SupportLayer::RoofType && !area_group.interface_as_base) ?
1708 erSupportMaterialInterface : erSupportMaterial;
1709 make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, wall_count, flow,
1710 brim_role);
1711 polys = offset_ex(poly, -flow.scaled_spacing());
1712 } else if (area_group.type == SupportLayer::Roof1stLayer) {
1713 polys = offset_ex(poly, 0.5*support_flow.scaled_width());
1714 }
1715 else {
1716 polys.push_back(poly);
1717 }
1718 fill_params.density = interface_density;
1719 fill_params.dont_adjust = true;
1720 }
1721 if (area_group.type == SupportLayer::Roof1stLayer) {
1722 // roof_1st_layer
1723 // ORCA: Roof1stLayer may be printed with base material when it acts as a contact layer.
1724 bool interface_as_base = area_group.interface_as_base;
1725 fill_params.density = interface_density;
1726 // Note: spacing means the separation between two lines as if they are tightly extruded
1727 filler_Roof1stLayer->spacing = interface_flow.spacing();
1728 filler_Roof1stLayer->angle = m_support_params.support_interface_angle(area_group.interface_id);
1729 fill_params.dont_sort = true;
1730 filler_Roof1stLayer->fixed_angle = (m_object_config->support_interface_pattern == smipRectilinearInterlaced ||
1731 m_object_config->support_interface_pattern == smipRectilinear);
1732 Flow interface_base_flow = interface_as_base ? support_flow : interface_flow;
1733 ExtrusionRole interface_role = interface_as_base ? erSupportMaterial : erSupportMaterialInterface;
1734 // generate a perimeter first to support interface better
1735 ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection();
1736 make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_base_flow, interface_role,
1737 filler_Roof1stLayer.get(), interface_density, false);
1738 temp_support_fills->no_sort = true; // make sure loops are first
1739 if (!temp_support_fills->entities.empty())
1740 ts_layer->support_fills.entities.push_back(temp_support_fills);
1741 else
1742 delete temp_support_fills;
1743 } else if (area_group.type == SupportLayer::FloorType) {
1744 // floor_areas
1745 bool interface_as_base = area_group.interface_as_base;
1746 fill_params.density = bottom_interface_density;
1747 filler_interface->spacing = interface_flow.spacing();
1748
1749 fill_params.dont_sort = (m_object_config->support_interface_pattern == smipGrid ||
1750 m_object_config->support_interface_pattern == smipRectilinearInterlaced);
1751
1752 filler_interface->fixed_angle = (m_object_config->support_interface_pattern == smipRectilinearInterlaced ||
1753 m_object_config->support_interface_pattern == smipRectilinear);
1754
1755 Flow interface_base_flow = interface_as_base ? support_flow : interface_flow;
1756 ExtrusionRole interface_role = interface_as_base ? erSupportMaterial : erSupportMaterialInterface;
1757 fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys,
1758 filler_interface.get(), fill_params, interface_role, interface_base_flow);
1759 } else if (area_group.type == SupportLayer::RoofType) {
1760 // roof_areas
1761 bool interface_as_base = area_group.interface_as_base;
1762 fill_params.density = interface_density;
1763 filler_interface->spacing = interface_flow.spacing();
1764
1765 fill_params.dont_sort = (m_object_config->support_interface_pattern == smipGrid ||
1766 m_object_config->support_interface_pattern == smipRectilinearInterlaced);
1767
1768 filler_interface->fixed_angle = (m_object_config->support_interface_pattern == smipRectilinearInterlaced ||
1769 m_object_config->support_interface_pattern == smipRectilinear);
1770
1771 Flow interface_base_flow = interface_as_base ? support_flow : interface_flow;
1772 ExtrusionRole interface_role = interface_as_base ? erSupportMaterial : erSupportMaterialInterface;
1773 fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, interface_role,
1774 interface_base_flow);
1775 }
1776 else {
1777 // base_areas
1778 bool support_base_on_bed = (layer_id == 0 && m_raft_layers == 0 && !belt_floor_active);
1779 Flow flow = support_base_on_bed ? m_support_params.first_layer_flow : support_flow;
1780 bool need_infill = with_infill;
1781 if(m_object_config->support_base_pattern==smpDefault)
1782 need_infill &= area_group.need_infill;
1783 // Orca: Use rectilinear for support base on the bed
1784 const InfillPattern base_fill_pattern = support_base_on_bed ? ipRectilinear : m_support_params.base_fill_pattern;
1785 std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(base_fill_pattern));
1786 filler_support->set_bounding_box(bbox_object);
1787
1788 filler_support->spacing =
1789 support_base_on_bed ?
1790 flow.spacing() : // Orca: On the bed-contacting support base layer, use first-layer flow spacing directly.
1791 support_spacing * support_density; // constant spacing to align support infill lines
1792 filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);
1793
1794 Polygons loops = to_polygons(poly);
1795 //ORCA: Group base per area as no_sort to keep outline->fill together.
1796 std::unique_ptr<ExtrusionEntityCollection> base_eec = std::make_unique<ExtrusionEntityCollection>();
1797 base_eec->no_sort = true;
1798 ExtrusionEntitiesPtr &base_dst = base_eec->entities;
1799 if (layer_id == 0 && !belt_floor_active) {
1800 float density = float(m_object_config->raft_first_layer_density.value * 0.01);
1801 fill_expolygons_with_sheath_generate_paths(base_dst, loops, filler_support.get(), density, erSupportMaterial, flow,
1802 m_support_params, true, false);
1803 }
1804 else {
1805 //ORCA: Force base walls before infill to keep outline->fill order.
1806 if (need_infill && m_support_params.base_fill_pattern != ipLightning) {
1807 // allow infill-only mode if support is thick enough (so min_wall_count is 0);
1808 // otherwise must draw 1 wall
1809 // Don't need extra walls if we have infill. Extra walls may overlap with the infills.
1810 size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0;
1811 make_perimeter_and_infill(base_dst, poly, std::max(min_wall_count, wall_count), flow,
1812 erSupportMaterial, filler_support.get(), support_density, false);
1813 }
1814 else {
1815 SupportParameters support_params = m_support_params;
1816 if (area_group.need_extra_wall && object_config.tree_support_wall_count.value == 0)
1817 support_params.tree_branch_diameter_double_wall_area_scaled = 0.1;
1818 tree_supports_generate_paths(base_dst, loops, flow, support_params);
1819 }
1820 }
1821
1822 //ORCA: Emit lightning infill per base area to avoid interleaving across islands.
1823 if (m_support_params.base_fill_pattern == ipLightning) {
1824 double print_z = ts_layer->print_z;
1825 auto lightning_layer_mapping = printZ_to_lightninglayer.find(print_z);
1826 if (lightning_layer_mapping != printZ_to_lightninglayer.end()) {
1827 auto &lightning_layer = generator->getTreesForLayer(lightning_layer_mapping->second);
1828 ExPolygons areas;
1829 areas.emplace_back(poly);
1830 areas = offset_ex(areas, -flow.scaled_spacing());
1831 for (auto &area : areas) {
1832 Polylines polylines = lightning_layer.convertToLines(to_polygons(area), 0);
1833 for (auto itr = polylines.begin(); itr != polylines.end();) {
1834 if (itr->length() < scale_(1.0))
1835 itr = polylines.erase(itr);
1836 else
1837 itr++;
1838 }
1839 Polylines opt_polylines;
1840 append(opt_polylines, chain_polylines(std::move(polylines)));
1841 extrusion_entities_append_paths(base_dst, opt_polylines, erSupportMaterial,
1842 float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
1843#ifdef SUPPORT_TREE_DEBUG_TO_SVG
1844 std::string name = debug_out_path("trees_polyline_%.2f.svg", ts_layer->print_z);
1845 BoundingBox bbox = get_extents(ts_layer->base_areas);
1846 SVG svg(name, bbox);
1847 if (svg.is_opened()) {
1848 svg.draw(ts_layer->base_areas, "blue");
1849 svg.draw(generator->Overhangs()[lightning_layer_mapping->second], "red");
1850 for (auto &line : opt_polylines) svg.draw(line, "yellow");
1851 }
1852#endif
1853 }
1854 }
1855 }
1856
1857 //ORCA: Keep per-area base paths grouped for outline->fill preservation.
1858 if (!base_eec->empty())
1859 ts_layer->support_fills.entities.push_back(base_eec.release());
1860 }
1861 }
1862
1863 // sort extrusions to reduce travel, also make sure walls go before infills
1864 if (ts_layer->support_fills.no_sort == false) {
1865 chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities);
1866 }
1867 }
1868 }
1869 );
1870}
static Fill * new_from_type(const InfillPattern type)
Definition FillBase.cpp:65
static float auto_extrusion_width(FlowRole role, float nozzle_diameter)
Definition Flow.cpp:27
static constexpr double PI
Definition libslic3r.h:81
@ ipRectilinear
Definition PrintConfig.hpp:128
@ frSupportMaterial
Definition Flow.hpp:24
void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height)
Definition ExtrusionEntity.hpp:665
double nozzle_diameter_for_filament(const PrintConfig &config, int filament_id, bool is_bbl_printer)
Definition PrintConfig.cpp:148
@ erSupportMaterialInterface
Definition ExtrusionEntity.hpp:42
@ erSupportMaterial
Definition ExtrusionEntity.hpp:41
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
Definition Flow.cpp:269
static void fill_expolygons_generate_paths(ExtrusionEntitiesPtr &dst, ExPolygons &&expolygons, Fill *filler, const FillParams &fill_params, float density, ExtrusionRole role, const Flow &flow)
Definition SupportCommon.cpp:597
static void make_perimeter_and_infill(ExtrusionEntitiesPtr &dst, const ExPolygon &support_area, size_t wall_count, const Flow &flow, ExtrusionRole role, Fill *filler_support, double support_density, bool infill_first=true)
Definition TreeSupport.cpp:1465

References Slic3r::Fill::angle, Slic3r::SupportLayer::area_groups, Slic3r::Flow::auto_extrusion_width(), Slic3r::SupportLayer::base_areas, Slic3r::SupportLayer::BaseType, Slic3r::Geometry::deg2rad(), Slic3r::FillParams::density, Slic3r::diff_ex(), Slic3r::FillParams::dont_adjust, Slic3r::ExtrusionEntityCollection::entities, Slic3r::erSupportMaterial, Slic3r::erSupportMaterialInterface, Slic3r::extrusion_entities_append_loops(), Slic3r::fill_expolygons_generate_paths(), Slic3r::SupportLayer::floor_areas, Slic3r::frSupportMaterial, generate_toolpaths(), Slic3r::Flow::height(), Slic3r::Layer::height, Slic3r::BeltFloorContext::init(), Slic3r::intersection_ex(), Slic3r::ipRectilinear, Slic3r::layer_height(), Slic3r::Layer::lslices, m_object, m_object_config, m_print_config, m_raft_layers, m_slicing_params, m_support_params, Slic3r::make_perimeter_and_infill(), Slic3r::Flow::mm3_per_mm(), Slic3r::Fill::new_from_type(), Slic3r::nozzle_diameter(), Slic3r::nozzle_diameter_for_filament(), Slic3r::offset_ex(), PI, Slic3r::PrintObjectConfig, Slic3r::range(), Slic3r::SupportLayer::roof_areas, scale_, Slic3r::Flow::scaled_spacing(), Slic3r::Fill::spacing, Slic3r::Flow::spacing(), Slic3r::SupportLayer::support_fills, Slic3r::support_material_interface_flow(), Slic3r::to_polygons(), and Slic3r::Flow::width().

Referenced by generate(), and generate_toolpaths().

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

◆ get_avoidance()

const ExPolygons & Slic3r::TreeSupport::get_avoidance ( coordf_t radius,
size_t obj_layer_nr )
private
2012{
2013 return m_ts_data->get_avoidance(radius, obj_layer_nr);
2014}

References get_avoidance(), and m_ts_data.

Referenced by get_avoidance().

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

◆ get_collision()

const ExPolygons & Slic3r::TreeSupport::get_collision ( coordf_t radius,
size_t layer_nr )
private
2016{
2017 return m_ts_data->get_collision(radius, layer_nr);
2018}

References get_collision(), and m_ts_data.

Referenced by get_collision().

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

◆ get_collision_polys()

Polygons Slic3r::TreeSupport::get_collision_polys ( coordf_t radius,
size_t layer_nr )
private
2020{
2021#if USE_SUPPORT_3D
2022 if (m_model_volumes) {
2023 bool on_build_plate = m_object_config->support_on_build_plate_only.value;
2024 const Polygons& collision_polys = m_model_volumes->getCollision(radius, layer_nr, true);
2025 return collision_polys;
2026 }
2027#else
2028 ExPolygons expolys = m_ts_data->get_collision(radius, layer_nr);
2029 Polygons polys;
2030 for (auto& expoly : expolys)
2031 for (int i = 0; i < expoly.num_contours(); i++)
2032 polys.emplace_back(std::move(expoly.contour_or_hole(i)));
2033 return polys;
2034#endif
2035 return Polygons();
2036}
std::unique_ptr< TreeSupport3D::TreeModelVolumes > m_model_volumes
Definition TreeSupport.hpp:438

References get_collision_polys(), m_model_volumes, m_object_config, and m_ts_data.

Referenced by get_collision_polys().

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

◆ get_radius()

coordf_t Slic3r::TreeSupport::get_radius ( const SupportNode * node)
private
2003{
2004 if (node->radius == 0)
2005 node->radius = calc_radius(node->dist_mm_to_top);
2006 return node->radius;
2007}

References calc_radius(), Slic3r::SupportNode::dist_mm_to_top, get_radius(), and Slic3r::SupportNode::radius.

Referenced by drop_nodes(), and get_radius().

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

◆ get_trim_support_regions()

Polygons Slic3r::TreeSupport::get_trim_support_regions ( const PrintObject & object,
SupportLayer * support_layer_ptr,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy )
private
2063{
2064 static const double sharp_tail_xy_gap = 0.2f;
2065 static const double no_overlap_xy_gap = 0.2f;
2066 double gap_xy_scaled = scale_(gap_xy);
2067 SupportLayer& support_layer = *support_layer_ptr;
2068 const PrintConfig& print_config = object.print()->config();
2069
2070 size_t idx_object_layer_overlapping = size_t(-1);
2071
2072 auto is_layers_overlap = [](const SupportLayer& support_layer, const Layer& object_layer, coordf_t bridging_height = 0.f) -> bool {
2073 if (std::abs(support_layer.print_z - object_layer.print_z) < EPSILON)
2074 return true;
2075
2076 coordf_t object_lh = bridging_height > EPSILON ? bridging_height : object_layer.height;
2077 if (support_layer.print_z < object_layer.print_z && support_layer.print_z > object_layer.print_z - object_lh)
2078 return true;
2079
2080 if (support_layer.print_z > object_layer.print_z && support_layer.bottom_z() < object_layer.print_z - EPSILON)
2081 return true;
2082
2083 return false;
2084 };
2085
2086 // Find the overlapping object layers including the extra above / below gap.
2087 coordf_t z_threshold = support_layer.bottom_z() - gap_extra_below + EPSILON;
2088 idx_object_layer_overlapping = Layer::idx_higher_or_equal(
2089 object.layers().begin(), object.layers().end(), idx_object_layer_overlapping,
2090 [z_threshold](const Layer* layer) { return layer->print_z >= z_threshold; });
2091 // Collect all the object layers intersecting with this layer.
2092 Polygons polygons_trimming;
2093 size_t i = idx_object_layer_overlapping;
2094 for (; i < object.layers().size(); ++i) {
2095 const Layer& object_layer = *object.layers()[i];
2096 if (object_layer.bottom_z() > support_layer.print_z + gap_extra_above - EPSILON)
2097 break;
2098
2099 bool is_overlap = is_layers_overlap(support_layer, object_layer);
2100 for (const ExPolygon& expoly : object_layer.lslices) {
2101 // BBS
2102 bool is_sharptail = !intersection_ex({ expoly }, object_layer.sharp_tails).empty();
2103 coordf_t trimming_offset = is_sharptail ? scale_(sharp_tail_xy_gap) :
2104 is_overlap ? gap_xy_scaled :
2106 polygons_append(polygons_trimming, offset({ expoly }, trimming_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
2107 }
2108 }
2109 if (!m_slicing_params.zero_gap_interface_top && m_object_config->thick_bridges) {
2110 // Collect all bottom surfaces, which will be extruded with a bridging flow.
2111 for (; i < object.layers().size(); ++i) {
2112 const Layer& object_layer = *object.layers()[i];
2113 bool some_region_overlaps = false;
2114 for (LayerRegion* region : object_layer.regions()) {
2115 coordf_t bridging_height = region->region().bridging_height_avg(print_config);
2116 if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
2117 break;
2118 some_region_overlaps = true;
2119
2120 bool is_overlap = is_layers_overlap(support_layer, object_layer, bridging_height);
2121 coordf_t trimming_offset = is_overlap ? gap_xy_scaled : scale_(no_overlap_xy_gap);
2122 polygons_append(polygons_trimming,
2123 offset(region->fill_surfaces.filter_by_type(stBottomBridge), trimming_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
2124 }
2125 if (!some_region_overlaps)
2126 break;
2127 }
2128 }
2129
2130 return polygons_trimming;
2131}
#define SUPPORT_SURFACES_OFFSET_PARAMETERS
Definition PrintObject.cpp:5148
static IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
Definition Layer.hpp:243
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
static const double no_overlap_xy_gap
Definition SupportMaterial.cpp:1443
static const double sharp_tail_xy_gap
Definition SupportMaterial.cpp:1442
void polygons_append(Polygons &dst, const ExPolygon &src)
Definition ExPolygon.hpp:411
@ stBottomBridge
Definition Surface.hpp:22

References Slic3r::Layer::bottom_z(), Slic3r::empty(), EPSILON, get_trim_support_regions(), Slic3r::Layer::idx_higher_or_equal(), Slic3r::intersection_ex(), Slic3r::Layer::lslices, m_object_config, m_slicing_params, Slic3r::no_overlap_xy_gap, Slic3r::offset(), Slic3r::polygons_append(), Slic3r::Layer::print_z, Slic3r::Layer::regions(), scale_, Slic3r::sharp_tail_xy_gap, Slic3r::Layer::sharp_tails, Slic3r::stBottomBridge, and SUPPORT_SURFACES_OFFSET_PARAMETERS.

Referenced by get_trim_support_regions().

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

◆ insert_dropped_node()

void Slic3r::TreeSupport::insert_dropped_node ( std::vector< SupportNode * > & nodes_layer,
SupportNode * node )
private

Add a node to the next layer.

If a node is already at that position in the layer, the nodes are merged.

3902{
3903 std::vector<SupportNode*>::iterator conflicting_node_it = std::find(nodes_layer.begin(), nodes_layer.end(), p_node);
3904 if (conflicting_node_it == nodes_layer.end()) //No conflict.
3905 {
3906 nodes_layer.emplace_back(p_node);
3907 return;
3908 }
3909
3910 SupportNode* conflicting_node = *conflicting_node_it;
3911 conflicting_node->distance_to_top = std::max(conflicting_node->distance_to_top, p_node->distance_to_top);
3912 conflicting_node->support_roof_layers_below = std::max(conflicting_node->support_roof_layers_below, p_node->support_roof_layers_below);
3913}

References Slic3r::SupportNode::distance_to_top, insert_dropped_node(), and Slic3r::SupportNode::support_roof_layers_below.

Referenced by insert_dropped_node().

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

◆ move_bounds_to_contact_nodes()

void Slic3r::TreeSupport::move_bounds_to_contact_nodes ( std::vector< TreeSupport3D::SupportElements > & move_bounds,
PrintObject & print_object,
const TreeSupport3D::TreeSupportSettings & config )
1875{
1876 m_ts_data = print_object.alloc_tree_support_preview_cache();
1877 // convert move_bounds back to Support Nodes for tree skeleton preview
1878 this->contact_nodes.resize(move_bounds.size());
1879 for (int layer_nr = move_bounds.size() - 1; layer_nr >= 0; layer_nr--) {
1880 TreeSupport3D::SupportElements &elements = move_bounds[layer_nr];
1881 auto &contact_nodes_layer = this->contact_nodes[layer_nr];
1882 for (size_t i = 0; i < elements.size(); i++) {
1883 auto &elem = elements[i];
1884 auto &state = elem.state;
1885 state.print_z = layer_z(print_object.slicing_parameters(), config, layer_nr);
1886 auto node = this->create_node(state.result_on_layer, state.distance_to_top, layer_nr, 0, state.to_buildplate, nullptr, state.print_z, config.layer_height);
1887 contact_nodes_layer.push_back(node);
1888 if (layer_nr < move_bounds.size() - 1 && !elem.parents.empty()) {
1889 node->parent = contact_nodes[layer_nr + 1][elem.parents.front()];
1890 for (int j : elem.parents) {
1891 contact_nodes[layer_nr + 1][j]->child = node;
1892 node->parents.push_back(contact_nodes[layer_nr + 1][j]);
1893 }
1894 }
1895
1896 if (i == 0) { m_ts_data->layer_heights.emplace_back(state.print_z, config.layer_height, std::max(0, layer_nr - 1)); }
1897 }
1898 }
1899}
SupportNode * create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode *parent, coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0, coordf_t radius_=0)
Definition TreeSupport.hpp:393
std::deque< SupportElement > SupportElements
Definition TreeSupport3D.hpp:308
double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
Definition TreeSupportCommon.hpp:626

References Slic3r::PrintObject::alloc_tree_support_preview_cache(), contact_nodes, create_node(), Slic3r::TreeSupport3D::TreeSupportSettings::layer_height, m_ts_data, move_bounds_to_contact_nodes(), and Slic3r::PrintObject::slicing_parameters().

Referenced by move_bounds_to_contact_nodes().

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

◆ plan_layer_heights()

std::vector< LayerHeightData > Slic3r::TreeSupport::plan_layer_heights ( )
private

Optimize the generation of tree support by pre-planning the layer_heights.

BBS: MusangKing: maximum layer height

3524{
3525 std::vector<LayerHeightData> layer_heights;
3526 std::map<coordf_t, coordf_t> z_heights; // print_z:height
3527 if (!m_support_params.independent_layer_height) {
3528 layer_heights.resize(m_object->layer_count());
3529 for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
3530 z_heights[m_object->get_layer(layer_nr)->print_z] = m_object->get_layer(layer_nr)->height;
3531 layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, size_t(layer_nr)};
3532 }
3533 } else {
3534 const coordf_t max_layer_height = m_slicing_params.max_suport_layer_height;
3535 const coordf_t min_layer_height = m_slicing_params.min_layer_height;
3536 std::map<coordf_t, coordf_t> bounds; // print_z: height
3537 // Keep first layer still
3538 bounds[m_object->get_layer(0)->print_z] = {m_object->get_layer(0)->height};
3539 std::vector<float> obj_layer_zs;
3540 obj_layer_zs.reserve(m_object->layer_count());
3541 for (const Layer *l : m_object->layers()) obj_layer_zs.emplace_back((float) l->print_z);
3542 z_heights[m_object->get_layer(0)->print_z] = m_object->get_layer(0)->height;
3543 const coordf_t min_print_z = m_object->get_layer(0)->print_z;
3544 // Collect top contact layers
3545 for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) {
3546 if (!contact_nodes[layer_nr].empty()) {
3547 coordf_t print_z = contact_nodes[layer_nr].front()->print_z;
3548 coordf_t height = contact_nodes[layer_nr].front()->height;
3549 // insertion will fail if there is already a key of print_z, so no need to check
3550 bounds.insert({print_z, height});
3551 bounds.insert({std::max(min_print_z, print_z - height), 0}); // the bottom_z of the layer
3552 }
3553 }
3554
3555 auto it1 = bounds.begin();
3556 auto it2 = std::next(it1);
3557 for (; it2 != bounds.end(); it2++) {
3558 coordf_t z2 = it2->first;
3559 coordf_t h2 = it2->second;
3560 coordf_t z1 = it1->first;
3561 coordf_t h1 = it1->second;
3562 coordf_t dist = z2 - z1;
3563 if (dist < min_layer_height - EPSILON) continue;
3564
3565 BOOST_LOG_TRIVIAL(trace) << format("plan_layer_heights0 (%.2f,%.2f)->(%.2f,%.2f): ", z1, h1, z2, h2);
3566
3567 // Insert intermediate layers.
3568 size_t n_layers_extra = size_t(ceil(dist / max_layer_height));
3569 coordf_t step = dist / coordf_t(n_layers_extra);
3570 coordf_t print_z = z1 + step;
3571 for (int i = 0; i < n_layers_extra; i++, print_z += step) {
3572 z_heights[print_z] = step;
3573 BOOST_LOG_TRIVIAL(debug) << "plan_layer_heights add entry print_z, height: " << print_z << " " << step;
3574 }
3575 it1=it2;
3576 }
3577
3578 // map z_heights to layer_heights
3579 int i = 0;
3580 size_t obj_layer_nr = 0;
3581 layer_heights.resize(z_heights.size());
3582 for (auto it = z_heights.begin(); it != z_heights.end(); it++, i++) {
3583 coordf_t print_z = it->first;
3584 coordf_t height = it->second;
3585 while (obj_layer_nr < obj_layer_zs.size() && obj_layer_zs[obj_layer_nr] < print_z - height / 2) obj_layer_nr++;
3586 layer_heights[i] = {print_z, height, obj_layer_nr};
3587
3588 }
3589 }
3590
3591 // add support layers according to layer_heights
3592 int support_layer_nr = m_raft_layers;
3593 for (size_t i = 0; i < layer_heights.size(); i++, support_layer_nr++) {
3594 // SupportLayer *ts_layer = m_object->add_tree_support_layer(support_layer_nr, layer_heights[i].print_z, layer_heights[i].height, layer_heights[i].print_z);
3595
3596 // ORCA: add_tree_support_layer() argument order is (id, height, print_z, slice_z).
3597 // Passing print_z as height breaks support layer geometry.
3598 SupportLayer *ts_layer = m_object->add_tree_support_layer(support_layer_nr, layer_heights[i].height, layer_heights[i].print_z, layer_heights[i].print_z);
3599
3600 if (ts_layer->id() > m_raft_layers) {
3601 SupportLayer *lower_layer = m_object->get_support_layer(ts_layer->id() - 1);
3602 if (lower_layer) {
3603 lower_layer->upper_layer = ts_layer;
3604 ts_layer->lower_layer = lower_layer;
3605 }
3606 }
3607 }
3608
3609
3610 // re-distribute contact_nodes to support layers
3611 decltype(contact_nodes) contact_nodes2(support_layer_nr);
3612 for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
3613 if (contact_nodes[layer_nr].empty()) continue;
3614 SupportNode *node1 = contact_nodes[layer_nr].front();
3615 auto it = std::min_element(layer_heights.begin(), layer_heights.end(), [node1](const LayerHeightData &l1, const LayerHeightData &l2) {
3616 return std::abs(l1.print_z - node1->print_z) < std::abs(l2.print_z - node1->print_z);
3617 });
3618 if (it == layer_heights.end()) it = std::prev(it);
3619 int layer_nr2 = std::distance(layer_heights.begin(), it);
3620 contact_nodes2[layer_nr2].insert(contact_nodes2[layer_nr2].end(), contact_nodes[layer_nr].begin(), contact_nodes[layer_nr].end());
3621 }
3622 contact_nodes = contact_nodes2;
3623
3624 // adjust contact nodes' distance_to_top and support_roof_layers_below according to layer_heights
3625 // In case of very large top z distance, one gap layer is not enough, we need to split it into multiple layers
3626 for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
3627 if (contact_nodes[layer_nr].empty()) continue;
3628 SupportNode *node1 = contact_nodes[layer_nr].front();
3629 BOOST_LOG_TRIVIAL(debug) << format("plan_layer_heights node1->layer_nr,printz,height,distance_to_top: %d, %.2f,%.2f, %d", layer_nr, node1->print_z, node1->height, node1->distance_to_top)
3630 << ", object_layer_zs[" << layer_heights[layer_nr].obj_layer_nr << "]=" << m_object->get_layer(layer_heights[layer_nr].obj_layer_nr)->print_z;
3631 coordf_t new_height = layer_heights[layer_nr].height;
3632 if (std::abs(node1->height - new_height) < EPSILON) continue;
3633 if (top_z_distance < EPSILON && node1->height < EPSILON) continue; // top_z_distance==0, this is soluable interface
3634 coordf_t accum_height = 0;
3635 int num_layers = 0;
3636 for (int i=layer_nr;i>=0;i--){
3637 if (layer_heights[i].height > EPSILON) {
3638 accum_height += layer_heights[i].height;
3639 num_layers++;
3640 if (accum_height > node1->height - EPSILON) break;
3641 }
3642 }
3643 BOOST_LOG_TRIVIAL(debug) << format("plan_layer_heights adjust node's height print_z[%d]=%.2f: (%.3f,%d)->(%.3f,%.3f,%d)", layer_nr, node1->print_z,
3644 node1->height, node1->distance_to_top, new_height, accum_height, -num_layers);
3645 for (SupportNode *node : contact_nodes[layer_nr]) {
3646 node->height = new_height;
3647 node->distance_to_top = -num_layers;
3648 }
3649 }
3650
3651 // log layer_heights
3652 for (size_t i = 0; i < layer_heights.size(); i++) {
3653 //if (layer_heights[i].height > EPSILON)
3654 BOOST_LOG_TRIVIAL(trace) << format("plan_layer_heights[%d] print_z, height: %.2f, %.2f",i, layer_heights[i].print_z, layer_heights[i].height);
3655 }
3656 return layer_heights;
3657}
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
void trace(unsigned int level, const char *message)
Definition utils.cpp:247
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:177
void bounds(const NSVGimage &image, Vec2f &min, Vec2f &max)
Definition NSVGUtils.cpp:91
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References Slic3r::bounds(), contact_nodes, Slic3r::Geometry::dist(), Slic3r::SupportNode::distance_to_top, Slic3r::empty(), EPSILON, Slic3r::format(), Slic3r::SupportNode::height, Slic3r::Layer::id(), Slic3r::l2(), Slic3r::Layer::lower_layer, m_object, m_raft_layers, m_slicing_params, m_support_params, plan_layer_heights(), Slic3r::SupportNode::print_z, Slic3r::trace(), and Slic3r::Layer::upper_layer.

Referenced by generate(), and plan_layer_heights().

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

◆ smooth_nodes()

void Slic3r::TreeSupport::smooth_nodes ( )
private
3463{
3464 for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
3465 std::vector<SupportNode *> &curr_layer_nodes = contact_nodes[layer_nr];
3466 if (curr_layer_nodes.empty()) continue;
3467 for (SupportNode *node : curr_layer_nodes) {
3468 node->is_processed = false;
3469 }
3470 }
3471
3472 for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) {
3473 std::vector<SupportNode *> &curr_layer_nodes = contact_nodes[layer_nr];
3474 if (curr_layer_nodes.empty()) continue;
3475 for (SupportNode *node : curr_layer_nodes) {
3476 if (!node->is_processed) {
3477 std::vector<Point> pts;
3478 std::vector<double> radii;
3479 std::vector<SupportNode *> branch;
3480 SupportNode * p_node = node;
3481 // add a fixed head if it's not a polygon node, see STUDIO-4403
3482 // Polygon node can't be added because the move distance might be huge, making the nodes in between jump and dangling
3483 if (node->child && node->child->type!=ePolygon) {
3484 pts.push_back(p_node->child->position);
3485 radii.push_back(p_node->child->radius);
3486 branch.push_back(p_node->child);
3487 }
3488 do {
3489 pts.push_back(p_node->position);
3490 radii.push_back(p_node->radius);
3491 branch.push_back(p_node);
3492 p_node = p_node->parent;
3493 } while (p_node && !p_node->is_processed);
3494 if (pts.size() < 3) continue;
3495
3496 std::vector<Point> pts1 = pts;
3497 std::vector<double> radii1 = radii;
3498 // TODO here we assume layer height gap is constant. If not true, need to consider height jump
3499 const int iterations = 100;
3500 for (size_t k = 0; k < iterations; k++) {
3501 for (size_t i = 1; i < pts.size() - 1; i++) {
3502 Point pt = ( pts[i - 1] + pts[i] + pts[i + 1] ) / 3;
3503 pts1[i] = pt;
3504 radii1[i] = (radii[i - 1] + radii[i] + radii[i + 1] ) / 3;
3505 if (k == iterations - 1) {
3506 branch[i]->position = pt;
3507 branch[i]->radius = radii1[i];
3508 branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2;
3509 branch[i]->is_processed = true;
3510 BOOST_LOG_TRIVIAL(info) << "smooth_nodes: layer_nr=" << layer_nr << ", i=" << i << ", pt=" << pt << ", movement=" << branch[i]->movement << ", radius=" << branch[i]->radius;
3511 }
3512 }
3513 if (k < iterations - 1) {
3514 std::swap(pts, pts1);
3515 std::swap(radii, radii1);
3516 }
3517 }
3518 }
3519 }
3520 }
3521}

References Slic3r::SupportNode::child, contact_nodes, Slic3r::ePolygon, Slic3r::SupportNode::is_processed, Slic3r::SupportNode::parent, Slic3r::SupportNode::position, Slic3r::SupportNode::radius, and smooth_nodes().

Referenced by generate(), and smooth_nodes().

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

Member Data Documentation

◆ avg_node_per_layer

int Slic3r::TreeSupport::avg_node_per_layer = 0

Referenced by draw_circles().

◆ base_radius

coordf_t Slic3r::TreeSupport::base_radius = 0.0
private

◆ contact_nodes

std::vector<std::vector<SupportNode*> > Slic3r::TreeSupport::contact_nodes
private

Generator for model collision, avoidance and internal guide volumes.

Lazily computes volumes as needed.

Warning
This class is NOT currently thread-safe and should not be accessed in OpenMP blocks

Referenced by drop_nodes(), generate_contact_points(), move_bounds_to_contact_nodes(), plan_layer_heights(), and smooth_nodes().

◆ diameter_angle_scale_factor

double Slic3r::TreeSupport::diameter_angle_scale_factor = tan(5.0*M_PI/180.0)
private

◆ generator

std::unique_ptr<FillLightning::Generator> Slic3r::TreeSupport::generator

◆ has_cantilever

bool Slic3r::TreeSupport::has_cantilever = false

◆ has_sharp_tails

bool Slic3r::TreeSupport::has_sharp_tails = false

◆ is_slim

bool Slic3r::TreeSupport::is_slim = false
private

◆ is_strong

bool Slic3r::TreeSupport::is_strong = false
private

Referenced by TreeSupport().

◆ m_highest_overhang_layer

size_t Slic3r::TreeSupport::m_highest_overhang_layer = 0
private

Referenced by detect_overhangs(), and generate().

◆ m_machine_border

ExPolygon Slic3r::TreeSupport::m_machine_border

Polygons representing the limits of the printable area of the machine.

Referenced by TreeSupport(), and Slic3r::generate_tree_support_3D().

◆ m_model_volumes

std::unique_ptr<TreeSupport3D::TreeModelVolumes> Slic3r::TreeSupport::m_model_volumes
private

Referenced by get_collision_polys().

◆ m_mst_line_x_layer_contour_caches

std::vector< std::unordered_map<Line, bool, LineHash> > Slic3r::TreeSupport::m_mst_line_x_layer_contour_caches
private

◆ m_object

◆ m_object_config

◆ m_print_config

◆ m_raft_layers

size_t Slic3r::TreeSupport::m_raft_layers = 0
private

◆ m_slicing_params

◆ m_spanning_trees

std::vector<std::vector<MinimumSpanningTree> > Slic3r::TreeSupport::m_spanning_trees
private

◆ m_support_params

SupportParameters Slic3r::TreeSupport::m_support_params
private

◆ m_ts_data

◆ m_vertical_enforcer_points

std::vector<std::pair<Vec3f, Vec3f> > Slic3r::TreeSupport::m_vertical_enforcer_points

◆ MAX_BRANCH_RADIUS

const coordf_t Slic3r::TreeSupport::MAX_BRANCH_RADIUS = 10.0
private

◆ MAX_BRANCH_RADIUS_FIRST_LAYER

const coordf_t Slic3r::TreeSupport::MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0
private

◆ max_cantilever_dist

double Slic3r::TreeSupport::max_cantilever_dist = 0

◆ MIN_BRANCH_RADIUS

const coordf_t Slic3r::TreeSupport::MIN_BRANCH_RADIUS = 0.4
private

◆ MIN_BRANCH_RADIUS_FIRST_LAYER

const coordf_t Slic3r::TreeSupport::MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0
private

◆ minimum_roof_area

const double Slic3r::TreeSupport::minimum_roof_area {SQ(scaled<double>(1.))}
private
455{SQ(scaled<double>(1.))};

◆ nodes_angle

float Slic3r::TreeSupport::nodes_angle = 0

Referenced by draw_circles().

◆ overhang_types

std::map<const ExPolygon*, OverhangType> Slic3r::TreeSupport::overhang_types

◆ printZ_to_lightninglayer

std::unordered_map<double, size_t> Slic3r::TreeSupport::printZ_to_lightninglayer

◆ support_type

SupportType Slic3r::TreeSupport::support_type

◆ throw_on_cancel

std::function<void()> Slic3r::TreeSupport::throw_on_cancel

◆ top_z_distance

float Slic3r::TreeSupport::top_z_distance = 0.0
private

◆ with_infill

bool Slic3r::TreeSupport::with_infill = false
private

Referenced by TreeSupport().


The documentation for this class was generated from the following files:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Support/TreeSupport.hpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Support/TreeSupport.cpp