@@ -29,6 +29,7 @@
# endif
# define TAU (2.0 * M_PI)
# define NO_INDEX (std::numeric_limits<unsigned int>::max())
# define USE_SUPPORT_3D 0
// #define SUPPORT_TREE_DEBUG_TO_SVG
@@ -331,7 +332,8 @@ static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines)
// Move point from inside polygon if distance>0, outside if distance<0.
// Special case: distance=0 means find the nearest point of from on the polygon contour.
// The max move distance should not excceed max_move_distance.
static unsigned int move_inside_expoly ( const ExPolygon & polygon , Point & from , double distance = 0 , double max_move_distance = std : : numeric_limits < double > : : max ( ) )
// @return success(true) or not(false)
static bool move_inside_expoly ( const ExPolygon & polygon , Point & from , double distance = 0 , double max_move_distance = std : : numeric_limits < double > : : max ( ) )
{
//TODO: This is copied from the moveInside of Polygons.
/*
@@ -347,7 +349,7 @@ static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, do
if ( contour . points . size ( ) < 2 )
{
return 0 ;
return false ;
}
Point p0 = contour . points [ polygon . contour . size ( ) - 2 ] ;
Point p1 = contour . points . back ( ) ;
@@ -444,12 +446,14 @@ static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, do
{
from = ret ;
}
return true ;
}
else if ( bestDist2 < max_move_distance * max_move_distance )
{
from = ret ;
return true ;
}
return 0 ;
return false ;
}
/*
@@ -622,6 +626,7 @@ static bool is_inside_ex(const ExPolygons &polygons, const Point &pt)
return false ;
}
// use project_onto which is more accurate but more expensive
static bool move_out_expolys ( const ExPolygons & polygons , Point & from , double distance , double max_move_distance )
{
Point from0 = from ;
@@ -810,6 +815,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
if ( ! is_tree ( stype ) ) return ;
max_cantilever_dist = 0 ;
m_highest_overhang_layer = 0 ;
// main part of overhang detection can be parallel
tbb : : parallel_for ( tbb : : blocked_range < size_t > ( 0 , m_object - > layer_count ( ) ) ,
@@ -1125,7 +1131,10 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
}
}
if ( ! ts_layer - > overhang_areas . empty ( ) ) has_overhangs = true ;
if ( ! ts_layer - > overhang_areas . empty ( ) ) {
has_overhangs = true ;
m_highest_overhang_layer = std : : max ( m_highest_overhang_layer , size_t ( layer_nr ) ) ;
}
if ( ! layer - > cantilevers . empty ( ) ) has_cantilever = true ;
}
@@ -1383,7 +1392,7 @@ void TreeSupport::generate_toolpaths()
{
ExtrusionRole raft_contour_er = m_slicing_params . base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface ;
SupportLayer * ts_layer = m_object - > support_layers ( ) . front ( ) ;
Flow flow = m_object - > print ( ) - > brim_flow ( ) ;
Flow flow = Flow ( support_extrusion_width , ts_layer - > height , nozzle_diameter ) ;
Polygons loops ;
for ( const ExPolygon & expoly : raft_areas ) {
@@ -1399,7 +1408,7 @@ void TreeSupport::generate_toolpaths()
SupportLayer * ts_layer = m_object - > get_support_layer ( layer_nr ) ;
coordf_t expand_offset = ( layer_nr = = 0 ? 0. : - 1. ) ;
Flow support_flow = layer_nr = = 0 ? m_object - > print ( ) - > brim_flow ( ) : Flow( support_extrusion_width , ts_layer - > height , nozzle_diameter ) ;
Flow support_flow = Flow ( support_extrusion_width , ts_layer - > height , nozzle_diameter ) ;
Fill * filler_interface = Fill : : new_from_type ( ipRectilinear ) ;
filler_interface - > angle = layer_nr = = 0 ? 90 : 0 ;
filler_interface - > spacing = support_extrusion_width ;
@@ -1517,27 +1526,34 @@ void TreeSupport::generate_toolpaths()
bool need_infill = with_infill ;
if ( m_object_config - > support_base_pattern = = smpDefault )
need_infill & = area_group . need_infill ;
if ( layer_id > 0 & & area_group . dist_to_top < 10 & & ! need_infill & & m_support_params . support_style ! = smsTreeHybrid ) {
if ( area_group . dist_to_top < 5 ) // 1 wall at the top <5mm
make_perimeter_and_inner_brim ( ts_layer - > support_fills . entities , poly , 1 , flow , erSupportMaterial ) ;
else // at least 2 walls for range [5,10)
make_perimeter_and_inner_brim ( ts_layer - > support_fills . entities , poly , std : : max ( wall_count , size_t ( 2 ) ) , flow , erSupportMaterial ) ;
}
else if ( layer_id > 0 & & need_infill & & m_support_params . base_fill_pattern ! = ipLightning ) {
std : : shared_ptr < Fill > filler_support = std : : shared_ptr < Fill > ( Fill : : new_from_type ( m_support_params . base_fill_pattern ) ) ;
filler_support - > set_bounding_box ( bbox_object ) ;
filler_support - > spacing = object_config . support_base_pattern_spacing . value * support_density ; // constant spacing to align support infill lines
filler_support - > angle = Geometry : : deg2rad ( object_config . support_angle . value ) ;
size_t walls = wall_count ;
if ( layer_id = = 0 ) walls = std : : numeric_limits < size_t > : : max ( ) ;
else if ( area_group . need_extra_wall & & walls < 2 ) walls + = 1 ;
std : : shared_ptr < Fill > filler_support = std : : shared_ptr < Fill > ( Fill : : new_from_type ( layer_id = = 0 ? ipConcentric : m_support_params . base_fill_pattern ) ) ;
filler_support - > set_bounding_box ( bbox_object ) ;
filler_support - > spacing = object_config . support_base_pattern_spacing . value * support_density ; // constant spacing to align support infill lines
filler_support - > angle = Geometry : : deg2rad ( object_config . support_angle . value ) ;
if ( need_infill & & m_support_params . base_fill_pattern ! = ipLightning ) {
// allow infill-only mode if support is thick enough (so min_wall_count is 0);
// otherwise must draw 1 wall
size_t min_wall_count = offset ( poly , - scale_ ( support_spacing * 1.5 ) ) . empty ( ) ? 1 : 0 ;
make_perimeter_and_infill ( ts_layer - > support_fills . entities , * m_object - > print ( ) , poly , std : : max ( min_wall_count , wall_count ) , flow ,
make_perimeter_and_infill ( ts_layer - > support_fills . entities , * m_object - > print ( ) , poly , std : : max ( min_wall_count , walls ) , flow ,
erSupportMaterial , filler_support . get ( ) , support_density ) ;
}
else {
make_perimeter_and_inner_brim ( ts_layer - > support_fills . entities , poly ,
layer_id > 0 ? wall_count : std : : numeric_limits < size_t > : : max ( ) , flow , erSupportMaterial ) ;
Polygons loops ;
loops . emplace_back ( poly . contour ) ;
if ( layer_id = = 0 ) {
float density = float ( m_object_config - > raft_first_layer_density . value * 0.01 ) ;
fill_expolygons_with_sheath_generate_paths ( ts_layer - > support_fills . entities , loops , filler_support . get ( ) , density , erSupportMaterial , flow , m_support_params , true , false ) ;
}
else {
for ( size_t i = 1 ; i < walls ; i + + ) {
Polygons contour_new = offset ( poly . contour , - ( i - 0.5f ) * flow . scaled_spacing ( ) , jtSquare ) ;
loops . insert ( loops . end ( ) , contour_new . begin ( ) , contour_new . end ( ) ) ;
}
fill_expolygons_with_sheath_generate_paths ( ts_layer - > support_fills . entities , loops , nullptr , 0 , erSupportMaterial , flow , m_support_params , true , false ) ;
}
}
}
@@ -1611,8 +1627,6 @@ void TreeSupport::generate()
return ;
}
std : : vector < std : : vector < SupportNode * > > contact_nodes ( m_object - > layers ( ) . size ( ) ) ;
profiler . stage_start ( STAGE_total ) ;
// Generate overhang areas
@@ -1627,23 +1641,22 @@ void TreeSupport::generate()
m_ts_data - > is_slim = is_slim ;
// Generate contact points of tree support
std : : vector < std : : vector < SupportNode * > > contact_nodes ( m_object - > layers ( ) . size ( ) ) ;
std : : vector < TreeSupport3D : : SupportElements > move_bounds ( m_highest_overhang_layer + 1 ) ;
profiler . stage_start ( STAGE_GENERATE_CONTACT_NODES ) ;
m_object - > print ( ) - > set_status ( 56 , _L ( " Support: generate contact points " ) ) ;
generate_contact_points ( contact_nodes ) ;
generate_contact_points ( contact_nodes , move_bounds );
profiler . stage_finish ( STAGE_GENERATE_CONTACT_NODES ) ;
//Drop nodes to lower layers.
profiler . stage_start ( STAGE_DROP_DOWN_NODES ) ;
m_object - > print ( ) - > set_status ( 60 , _L ( " Support: propagate branches " ) ) ;
drop_nodes ( contact_nodes ) ;
profiler . stage_finish ( STAGE_DROP_DOWN_NODES ) ;
smooth_nodes ( contact_nodes ) ;
if ( ! m_object - > config ( ) . tree_support_adaptive_layer_height )
// Adjust support layer heights
adjust_layer_heights ( contact_nodes ) ;
smooth_nodes ( contact_nodes ) ; // , tree_support_3d_config);
//Generate support areas.
profiler . stage_start ( STAGE_DRAW_CIRCLES ) ;
@@ -1691,7 +1704,7 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, size_t layers_to_
return radius ;
}
coordf_t TreeSupport : : calc_branch_radius ( coordf_t base_radius , coordf_t mm_to_top , double diameter_angle_scale_factor )
coordf_t TreeSupport : : calc_branch_radius ( coordf_t base_radius , coordf_t mm_to_top , double diameter_angle_scale_factor , bool use_min_distance )
{
double radius ;
coordf_t tip_height = base_radius ; // this is a 45 degree tip
@@ -1712,11 +1725,63 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to
return radius ;
}
ExPolygons TreeSupport : : get_avoidance ( coordf_t radius , size_t obj_layer_nr )
{
# if USE_SUPPORT_3D
if ( m_model_volumes ) {
bool on_build_plate = m_object_config - > support_on_build_plate_only . value ;
const Polygons & avoid_polys = m_model_volumes - > getAvoidance ( radius , obj_layer_nr , TreeSupport3D : : TreeModelVolumes : : AvoidanceType : : Fast , on_build_plate , true ) ;
ExPolygons expolys ;
for ( auto & poly : avoid_polys )
expolys . emplace_back ( std : : move ( poly ) ) ;
return expolys ;
}
return ExPolygons ( ) ;
# else
return m_ts_data - > get_avoidance ( radius , obj_layer_nr ) ;
# endif
}
ExPolygons TreeSupport : : get_collision ( coordf_t radius , size_t layer_nr )
{
# if USE_SUPPORT_3D
if ( m_model_volumes ) {
bool on_build_plate = m_object_config - > support_on_build_plate_only . value ;
const Polygons & collision_polys = m_model_volumes - > getCollision ( radius , layer_nr , true ) ;
ExPolygons expolys ;
for ( auto & poly : collision_polys )
expolys . emplace_back ( std : : move ( poly ) ) ;
return expolys ;
}
# else
return m_ts_data - > get_collision ( radius , layer_nr ) ;
# endif
return ExPolygons ( ) ;
}
Polygons TreeSupport : : get_collision_polys ( coordf_t radius , size_t layer_nr )
{
# if USE_SUPPORT_3D
if ( m_model_volumes ) {
bool on_build_plate = m_object_config - > support_on_build_plate_only . value ;
const Polygons & collision_polys = m_model_volumes - > getCollision ( radius , layer_nr , true ) ;
return collision_polys ;
}
# else
ExPolygons expolys = m_ts_data - > get_collision ( radius , layer_nr ) ;
Polygons polys ;
for ( auto & expoly : expolys )
polys . emplace_back ( std : : move ( expoly . contour ) ) ;
return polys ;
# endif
return Polygons ( ) ;
}
template < typename RegionType > // RegionType could be ExPolygons or Polygons
ExPolygons avoid_object_remove_extra_small_parts ( ExPolygons & expolys , const RegionType & avoid_region ) {
ExPolygons expolys_out ;
if ( expolys . empty ( ) ) return expolys_out ;
auto clipped_avoid_region = ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( avoid_region , get_extents ( expolys ) ) ;
for ( auto expoly : expolys ) {
auto expolys_avoid = diff_ex ( expoly , avoid_region ) ;
auto expolys_avoid = diff_ex ( expoly , clipped_ avoid_region) ;
int idx_max_area = - 1 ;
float max_area = 0 ;
for ( int i = 0 ; i < expolys_avoid . size ( ) ; + + i ) {
@@ -1815,7 +1880,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
int bottom_gap_layers = round ( m_slicing_params . gap_object_support / m_slicing_params . layer_height ) ;
const coordf_t branch_radius = config . tree_support_branch_diameter . value / 2 ;
const coordf_t branch_radius_scaled = scale_ ( branch_radius ) ;
bool on_buildplate_only = config . support_on_build_plate_only . value ;
bool on_buildplate_only = m_object_ config- > support_on_build_plate_only . value ;
Polygon branch_circle ; //Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
// Use square support if there are too many nodes per layer because circle support needs much longer time to compute
@@ -1861,7 +1926,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
// coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , m_object - > layer_count ( ) ) ,
tbb : : blocked_range < size_t > ( 0 , m_object - > layer_count ( ) , m_object - > layer_count ( ) ),
[ & ] ( const tbb : : blocked_range < size_t > & range )
{
for ( size_t layer_nr = range . begin ( ) ; layer_nr < range . end ( ) ; layer_nr + + )
@@ -1912,10 +1977,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
// 1) node is a normal part of hybrid support
// 2) node is virtual
if ( node . type = = ePolygon | | node . distance_to_top < 0 ) {
if ( node . overhang - > contour . size ( ) > 100 | | node . overhang - > holes . size ( ) > 1 )
area . emplace_back ( * node . overhang ) ;
if ( node . overhang . contour . size ( ) > 100 | | node . overhang . holes . size ( ) > 1 )
area . emplace_back ( node . overhang ) ;
else {
area = offset_ex ( { * node . overhang } , scale_ ( m_ts_data - > m_xy_distance ) ) ;
area = offset_ex ( { node . overhang } , scale_ ( m_ts_data - > m_xy_distance ) ) ;
}
if ( node . type = = ePolygon )
has_polygon_node = true ;
@@ -1923,7 +1988,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
else {
Polygon circle ;
size_t layers_to_top = node . distance_to_top ;
double scale = calc_branch_radius ( branch_radius , node . dist_mm_to_top , diameter_angle_scale_factor ) / branch_radius ;
double scale = calc_branch_radius ( branch_radius , node . dist_mm_to_top , diameter_angle_scale_factor , false ) / branch_radius ;
if ( /*is_slim*/ 1 ) { // draw ellipse along movement direction
double moveX = node . movement . x ( ) / ( scale * branch_radius_scaled ) ;
@@ -1949,18 +2014,20 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
? layers_to_top * layer_height /
( scale * branch_radius ) * 0.5
: config . tree_support_brim_width ;
circle = offset ( circle , scale_ ( brim_width ) ) [ 0 ] ;
auto tmp = offset ( circle , scale_ ( brim_width ) ) ;
if ( ! tmp . empty ( ) )
circle = tmp [ 0 ] ;
}
area . emplace_back ( ExPolygon ( circle ) ) ;
// merge overhang to get a smoother interface surface
// Do not merge when buildplate_only is on, because some underneath nodes may have been deleted.
if ( top_interface_layers > 0 & & node . support_roof_layers_below > 0 & & ! on_buildplate_only ) {
ExPolygons overhang_expanded ;
if ( node . overhang - > contour . size ( ) > 100 | | node . overhang - > holes . size ( ) > 1 )
overhang_expanded . emplace_back ( * node . overhang ) ;
if ( node . overhang . contour . size ( ) > 100 | | node . overhang . holes . size ( ) > 1 )
overhang_expanded . emplace_back ( node . overhang ) ;
else {
// 对于有缺陷的模型, overhang膨胀以后可能是空的!
overhang_expanded = offset_ex ( { * node . overhang } , scale_ ( m_ts_data - > m_xy_distance ) ) ;
overhang_expanded = offset_ex ( { node . overhang } , scale_ ( m_ts_data - > m_xy_distance ) ) ;
}
append ( area , overhang_expanded ) ;
}
@@ -2013,21 +2080,21 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
roof_1st_layer = avoid_object_remove_extra_small_parts ( roof_1st_layer , avoid_region_interface ) ;
}
else {
roof_areas = std : : move ( diff_ex( roof_areas , avoid_region_interface ) ) ;
roof_1st_layer = std : : move ( diff_ex( roof_1st_layer , avoid_region_interface ) ) ;
roof_areas = diff_ex ( roof_areas , ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( avoid_region_interface , get_extents ( roof_areas ) ) ) ;
roof_1st_layer = diff_ex ( roof_1st_layer , ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( avoid_region_interface , get_extents ( roof_1st_layer ) ) ) ;
}
roof_areas = intersection_ex ( roof_areas , m_machine_border ) ;
// roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
roof_1st_layer = std : : move ( diff_ex( roof_1st_layer , roof_areas ) ) ;
roof_1st_layer = diff_ex ( roof_1st_layer , ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( roof_areas , get_extents ( roof_1st_layer ) ) ) ;
roof_1st_layer = intersection_ex ( roof_1st_layer , m_machine_border ) ;
// let supports touch objects when brim is on
auto avoid_region = m_ts_data - > get_collision( ( layer_nr = = 0 & & has_brim ) ? config . brim_object_gap : m_ts_data - > m_xy_distance , layer_nr ) ;
base_areas = avoid_object_remove_extra_small_parts( base_areas, avoid_region) ;
base_areas = std : : move ( diff_ex ( base_areas , roof_areas ) ) ;
base_areas = std : : move ( diff_ex ( base_areas , roof_1st_layer ) ) ;
base_areas = std : : move ( diff_ex( base_areas , roof_gap _areas) ) ;
auto avoid_region = get_collision_polys ( ( layer_nr = = 0 & & has_brim ) ? config . brim_object_gap : m_ts_data - > m_xy_distance , layer_nr ) ;
// base_areas = avoid_object_remove_extra_small_parts( base_areas, avoid_region);
base_areas = diff_clipped ( base_areas , avoid_region ) ;
ExPolygons roofs ; append ( roofs , roof_1st_layer ) ; append ( roofs , roof_areas ) ; append ( roofs , roof_gap_areas ) ;
base_areas = diff_ex ( base_areas , ClipperUtils : : clip_clipper_polygons_with_subject_bbox ( roofs , get_extents ( base _areas) ) ) ;
base_areas = intersection_ex ( base_areas , m_machine_border ) ;
if ( SQUARE_SUPPORT ) {
@@ -2255,7 +2322,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
// make sure 1) roof1 and object 2) roof1 and roof, won't intersect
// Note: We can't replace roof1 directly, as we have recorded its address.
// So instead we need to replace its members one by one.
auto tmp1 = diff_ex ( roof1 , m_ts_data - > get_collision( ( layer_nr = = 0 & & has_brim ) ? config . brim_object_gap : m_ts_data - > m_xy_distance , layer_nr ) ) ;
auto tmp1 = diff_ex ( roof1 , get_collision ( ( layer_nr = = 0 & & has_brim ) ? config . brim_object_gap : m_ts_data - > m_xy_distance , layer_nr ) ) ;
tmp1 = diff_ex ( tmp1 , ts_layer - > roof_areas ) ;
if ( ! tmp1 . empty ( ) ) {
roof1 . contour = std : : move ( tmp1 [ 0 ] . contour ) ;
@@ -2327,8 +2394,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
if ( angle > 30.0 & & node - > radius > MIN_BRANCH_RADIUS )
angle = ( node - > radius - MIN_BRANCH_RADIUS ) / ( MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS ) * ( config . tree_support_branch_angle . value - 30.0 ) + 30.0 ;
double tan_angle = tan ( angle * M_PI / 180 ) ;
int wall_count_ = node - > radius > 2 * support_line_width ? wall_count : 1 ;
node - > max_move_dist = ( angle < 90 ) ? ( coordf_t ) ( tan_angle * node - > height ) * wall_count_ : std : : numeric_limits < coordf_t > : : max ( ) ;
node - > max_move_dist = ( angle < 90 ) ? ( coordf_t ) ( tan_angle * node - > height ) : std : : numeric_limits < coordf_t > : : max ( ) ;
node - > max_move_dist = std : : min ( node - > max_move_dist , support_extrusion_width ) ;
move_dist = node - > max_move_dist ;
}
@@ -2344,50 +2410,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
m_spanning_trees . resize ( contact_nodes . size ( ) ) ;
//m_mst_line_x_layer_contour_caches.resize(contact_nodes.size());
if ( 0 )
{ // get outlines below and avoidance area using tbb
// This part only takes very little time, so we disable it.
typedef std : : chrono : : high_resolution_clock clock_ ;
typedef std : : chrono : : duration < double , std : : ratio < 1 > > second_ ;
std : : chrono : : time_point < clock_ > t0 { clock_ : : now ( ) } ;
// get all the possible radiis
std : : vector < std : : set < coordf_t > > all_layer_radius ( m_highest_overhang_layer + 1 ) ;
std : : vector < std : : set < coordf_t > > all_layer_node_dist ( m_highest_overhang_layer + 1 ) ;
for ( size_t layer_nr = m_highest_overhang_layer ; layer_nr > 0 ; layer_nr - - )
{
if ( layer_heights [ layer_nr ] . height < EPSILON ) continue ;
auto & layer_radius = all_layer_radius [ layer_nr ] ;
auto & layer_node_dist = all_layer_node_dist [ layer_nr ] ;
for ( SupportNode * p_node : contact_nodes [ layer_nr ] ) {
layer_node_dist . emplace ( p_node - > dist_mm_to_top ) ;
}
size_t layer_nr_next = layer_heights [ layer_nr ] . next_layer_nr ;
if ( layer_nr_next < = m_highest_overhang_layer & & layer_nr_next > 0 ) {
for ( auto node_dist : layer_node_dist )
all_layer_node_dist [ layer_nr_next ] . emplace ( node_dist + layer_heights [ layer_nr ] . height ) ;
}
for ( auto node_dist : layer_node_dist ) {
layer_radius . emplace ( calc_branch_radius ( branch_radius , node_dist , diameter_angle_scale_factor ) ) ;
}
}
// parallel pre-compute avoidance
//tbb::parallel_for(tbb::blocked_range<size_t>(1, m_highest_overhang_layer), [&](const tbb::blocked_range<size_t> &range) {
//for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
for ( size_t layer_nr = 0 ; layer_nr < all_layer_radius . size ( ) ; layer_nr + + ) {
BOOST_LOG_TRIVIAL ( debug ) < < " pre calculate_avoidance layer= " < < layer_nr ;
for ( auto node_radius : all_layer_radius [ layer_nr ] ) {
m_ts_data - > get_avoidance ( 0 , layer_nr ) ;
m_ts_data - > get_avoidance ( node_radius , layer_nr ) ;
}
}
//});
double duration { std : : chrono : : duration_cast < second_ > ( clock_ : : now ( ) - t0 ) . count ( ) } ;
BOOST_LOG_TRIVIAL ( debug ) < < " before m_avoidance_cache.size()= " < < m_ts_data - > m_avoidance_cache . size ( )
< < " , takes " < < duration < < " secs. " ;
}
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.
{
if ( m_object - > print ( ) - > canceled ( ) )
@@ -2436,15 +2458,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
{
const SupportNode & node = * p_node ;
if ( node . distance_to_top < 0 ) {
// gap nodes do not merge or move
SupportNode * next_node = new SupportNode ( p_node - > position , p_node - > distance_to_top + 1 , layer_nr_next , p_node - > support_roof_layers_below - 1 , p_node - > to_buildplate , p_node ,
print_z_next , height_next ) ;
get_max_move_dist ( next_node ) ;
next_node - > is_merged = false ;
contact_nodes [ layer_nr_next ] . emplace_back ( next_node ) ;
continue ;
}
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.
{
unsupported_branch_leaves . push_front ( { layer_nr , p_node } ) ;
@@ -2530,14 +2543,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
coord_t neighbour_radius = scale_ ( calc_branch_radius ( branch_radius , neighbour_node - > dist_mm_to_top , diameter_angle_scale_factor ) ) ;
Point pt_north = neighbour + Point ( 0 , neighbour_radius ) , pt_south = neighbour - Point ( 0 , neighbour_radius ) ,
pt_west = neighbour - Point ( neighbour_radius , 0 ) , pt_east = neighbour + Point ( neighbour_radius , 0 ) ;
if ( is_inside_ex ( * node . overhang , neighbour ) & & is_inside_ex ( * node . overhang , pt_north ) & & is_inside_ex ( * node . overhang , pt_south )
& & is_inside_ex ( * node . overhang , pt_west ) & & is_inside_ex ( * node . overhang , pt_east ) ) {
if ( is_inside_ex ( node . overhang , neighbour ) & & is_inside_ex ( node . overhang , pt_north ) & & is_inside_ex ( node . overhang , pt_south )
& & is_inside_ex ( node . overhang , pt_west ) & & is_inside_ex ( node . overhang , pt_east ) ) {
node . distance_to_top = std : : max ( node . distance_to_top , neighbour_node - > distance_to_top ) ;
node . support_roof_layers_below = std : : max ( node . support_roof_layers_below , neighbour_node - > support_roof_layers_below ) ;
node . dist_mm_to_top = std : : max ( node . dist_mm_to_top , neighbour_node - > dist_mm_to_top ) ;
node . merged_neighbours . push_front ( neighbour_node ) ;
node . merged_neighbours . insert ( node . merged_neighbours . end ( ) , neighbour_node - > merged_neighbours . begin ( ) , neighbour_node - > merged_neighbours . end ( ) ) ;
node . is_merged = true ;
to_delete . insert ( neighbour_node ) ;
}
}
@@ -2550,7 +2562,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
const coordf_t branch_radius_node = calc_branch_radius ( branch_radius , node . dist_mm_to_top , diameter_angle_scale_factor ) ;
auto avoid_layer = m_ts_data - > get_avoidance( branch_radius_node , layer_nr_next ) ;
auto avoid_layer = get_avoidance ( branch_radius_node , layer_nr_next ) ;
if ( group_index = = 0 )
{
//Avoid collisions.
@@ -2566,12 +2578,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
node_ = p_node - > parent ? p_node : neighbour ;
// Make sure the next pass doesn't drop down either of these (since that already happened).
node_ - > merged_neighbours . push_front ( node_ = = p_node ? neighbour : p_node ) ;
const bool to_buildplate = ! is_inside_ex( m_ts_data - > get_avoidance ( 0 , layer_nr_next) , next_position) ;
// const bool to_buildplate = ! is_inside_ex(get_collision(0, layer_nr_next), next_position);
const bool to_buildplate = ! is_inside_ex ( m_ts_data - > m_layer_outlines [ layer_nr_next ] , next_position ) ;
SupportNode * next_node = new SupportNode ( next_position , node_ - > distance_to_top + 1 , layer_nr_next , node_ - > support_roof_layers_below - 1 , to_buildplate , node_ ,
print_z_next , height_next ) ;
next_node - > movement = next_position - node . position ;
get_max_move_dist ( next_node ) ;
next_node - > is_merged = true ;
contact_nodes [ layer_nr_next ] . push_back ( next_node ) ;
@@ -2593,7 +2605,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
node . dist_mm_to_top = std : : max ( node . dist_mm_to_top , neighbour_node - > dist_mm_to_top ) ;
node . merged_neighbours . push_front ( neighbour_node ) ;
node . merged_neighbours . insert ( node . merged_neighbours . end ( ) , neighbour_node - > merged_neighbours . begin ( ) , neighbour_node - > merged_neighbours . end ( ) ) ;
node . is_merged = true ;
to_delete . insert ( neighbour_node ) ;
}
}
@@ -2615,7 +2626,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
SupportNode * next_node = new SupportNode ( p_node - > position , p_node - > distance_to_top + 1 , layer_nr_next , p_node - > support_roof_layers_below - 1 , to_buildplate ,
p_node , print_z_next , height_next ) ;
next_node - > max_move_dist = 0 ;
next_node - > is_merged = false ;
contact_nodes [ layer_nr_next ] . emplace_back ( next_node ) ;
continue ;
}
@@ -2633,9 +2643,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
unsupported_branch_leaves . push_front ( { layer_nr , p_node } ) ;
}
else {
SupportNode * pn = p_node ;
for ( int i = 0 ; i < = bottom_interface_layers & & pn ; i + + , pn = pn - > parent )
pn - > support_floor_layers_above = bottom_interface_layers - i + 1 ; // +1 so the parent node has support_floor_layers_above=2
to_delete . insert ( p_node ) ;
}
continue ;
@@ -2643,9 +2650,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
// if the link between parent and current is cut by contours, mark current as bottom contact node
if ( p_node - > parent & & intersection_ln ( { p_node - > position , p_node - > parent - > position } , layer_contours ) . empty ( ) = = false )
{
SupportNode * pn = p_node - > parent ;
for ( int i = 0 ; i < = bottom_interface_layers & & pn ; i + + , pn = pn - > parent )
pn - > support_floor_layers_above = bottom_interface_layers - i + 1 ;
to_delete . insert ( p_node ) ;
continue ;
}
@@ -2703,7 +2707,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
branch_radius_temp = branch_radius_node ;
}
# endif
auto avoid_layer = m_ts_data - > get_avoidance( branch_radius_node , layer_nr_next ) ;
auto avoid_layer = get_avoidance ( branch_radius_node , layer_nr_next ) ;
Point to_outside = projection_onto ( avoid_layer , node . position ) ;
Point direction_to_outer = to_outside - node . position ;
@@ -2755,27 +2759,26 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
}
}
const bool to_buildplate = ! is_inside_ex ( m_ts_data - > m_layer_outlines [ layer_nr ] , next_layer_vertex ) ; // !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex);
const bool to_buildplate = ! is_inside_ex ( m_ts_data - > m_layer_outlines [ layer_nr ] , next_layer_vertex ) ;
SupportNode * next_node = new SupportNode ( next_layer_vertex , node . distance_to_top + 1 , layer_nr_next , node . support_roof_layers_below - 1 , to_buildplate , p_node ,
print_z_next , height_next ) ;
next_node - > movement = movement ;
get_max_move_dist ( next_node ) ;
next_node - > is_merged = false ;
contact_nodes [ layer_nr_next ] . push_back ( next_node ) ;
}
}
# ifdef SUPPORT_TREE_DEBUG_TO_SVG
if ( contact_nodes [ layer_nr ] . empty ( ) = = false ) {
draw_contours_and_nodes_to_svg ( ( boost : : format ( " %.2f " ) % contact_nodes [ layer_nr ] [ 0 ] - > print_z ) . str ( ) , m_ts_data - > get_avoidance( 0 , layer_nr ) ,
m_ts_data - > get_avoidance( branch_radius_temp , layer_nr ) ,
draw_contours_and_nodes_to_svg ( ( boost : : format ( " %.2f " ) % contact_nodes [ layer_nr ] [ 0 ] - > print_z ) . str ( ) , get_avoidance ( 0 , layer_nr ) ,
get_avoidance ( branch_radius_temp , layer_nr ) ,
m_ts_data - > m_layer_outlines_below [ layer_nr ] ,
contact_nodes [ layer_nr ] , contact_nodes [ layer_nr_next ] , " contact_points " , { " overhang " , " avoid " , " outline " } , { " blue " , " red " , " yellow " } ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " drop_nodes layer " < < layer_nr < < " , print_z= " < < ts_layer - > print_z ;
for ( size_t i = 0 ; i < std : : min ( size_t ( 5 ) , contact_nodes [ layer_nr ] . size ( ) ) ; i + + ) {
auto & node = contact_nodes [ layer_nr ] [ i ] ;
BOOST_LOG_TRIVIAL ( debug ) < < " \t node " < < i < < " , pos= " < < node - > position < < " , move = " < < node - > movement < < " , is_merged= " < < node - > is_merged ;
BOOST_LOG_TRIVIAL ( debug ) < < " \t node " < < i < < " , pos= " < < node - > position < < " , move = " < < node - > movement ;
}
}
# endif
@@ -2792,10 +2795,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
if ( to_erase ! = contact_nodes [ i_layer ] . end ( ) )
{
// update the parent-child chain
if ( i_node - > parent )
i_node - > parent - > child = i_node - > child ;
if ( i_node - > child )
i_node - > child - > parent = i_node - > parent ;
for ( SupportNode * parent : i_node - > parents ) {
if ( parent - > child = = i_node )
parent - > child = i_node - > child ;
}
if ( i_node - > child ) {
i_node - > child - > parents = i_node - > parents ;
}
contact_nodes [ i_layer ] . erase ( to_erase ) ;
to_free_node_set . insert ( i_node ) ;
@@ -2824,47 +2830,66 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_
if ( curr_layer_nodes . empty ( ) ) continue ;
for ( SupportNode * node : curr_layer_nodes ) {
node - > is_processed = false ;
if ( layer_nr = = 0 ) node - > is_merged = true ; // nodes on plate are also merged nodes
}
}
float max_move = scale_ ( m_object_config - > support_line_width / 2 ) ;
for ( int layer_nr = 0 ; layer_nr < contact_nodes . size ( ) ; layer_nr + + ) {
std : : vector < SupportNode * > & curr_layer_nodes = contact_nodes [ layer_nr ] ;
if ( curr_layer_nodes . empty ( ) ) continue ;
for ( SupportNode * node : curr_layer_nodes ) {
if ( ! node - > is_processed ) {
std : : vector < Point > pts ;
std : : vector < double > radii ;
std : : vector < SupportNode * > branch ;
SupportNode * p_node = node ;
// add a fixed head
if ( node - > child ) {
// add a fixed head if it's not a polygon node, see STUDIO-4403
// Polygon node can't be added because the move distance might be huge, making the nodes in between jump and dangling
if ( node - > child & & node - > child - > type ! = ePolygon ) {
pts . push_back ( p_node - > child - > position ) ;
radii . push_back ( p_node - > child - > radius ) ;
branch . push_back ( p_node - > child ) ;
}
do {
pts . push_back ( p_node - > position ) ;
radii . push_back ( p_node - > radius ) ;
branch . push_back ( p_node ) ;
p_node = p_node - > parent ;
} while ( p_node & & ! p_node - > is_processed ) ;
if ( pts . size ( ) < 3 ) continue ;
std : : vector < Point > pts1 = pts ;
std : : vector < double > radii1 = radii ;
// TODO here we assume layer height gap is constant. If not true, need to consider height jump
const int iterations = 100 ;
for ( size_t k = 0 ; k < iterations ; k + + ) {
for ( size_t i = 1 ; i < pts . size ( ) - 1 ; i + + ) {
size_t i2 = i > = 2 ? i - 2 : 0 ;
size_t i3 = i < pts . size ( ) - 2 ? i + 2 : pts . size ( ) - 1 ;
Point pt = ( pts [ i2 ] + pts [ i - 1 ] + pts [ i ] + pts [ i + 1 ] + pts [ i3 ] ) / 5 ;
Point pt = ( pts [ i - 1 ] + pts [ i ] + pts [ i + 1 ] ) / 3 ;
pts1 [ i ] = pt ;
radii1 [ i ] = ( radii [ i - 1 ] + radii [ i ] + radii [ i + 1 ] ) / 3 ;
if ( k = = iterations - 1 ) {
branch [ i ] - > position = pt ;
branch [ i ] - > radius = radii1 [ i ] ;
branch [ i ] - > movement = ( pts [ i + 1 ] - pts [ i - 1 ] ) / 2 ;
branch [ i ] - > is_processed = true ;
if ( branch [ i ] - > parents . size ( ) > 1 | | ( branch [ i ] - > movement . x ( ) > max_move | | branch [ i ] - > movement . y ( ) > max_move ) )
branch [ i ] - > need_extra_wall = true ;
BOOST_LOG_TRIVIAL ( info ) < < " smooth_nodes: layer_nr= " < < layer_nr < < " , i= " < < i < < " , pt= " < < pt < < " , movement= " < < branch [ i ] - > movement < < " , radius= " < < branch [ i ] - > radius ;
}
}
if ( k < iterations - 1 )
if ( k < iterations - 1 ) {
std : : swap ( pts , pts1 ) ;
std : : swap ( radii , radii1 ) ;
}
else {
// interpolate need_extra_wall in the end
for ( size_t i = 1 ; i < branch . size ( ) - 1 ; i + + ) {
if ( branch [ i - 1 ] - > need_extra_wall & & branch [ i + 1 ] - > need_extra_wall )
branch [ i ] - > need_extra_wall = true ;
}
}
}
}
}
@@ -2905,116 +2930,22 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_
# endif
}
void TreeSupport : : adjust_layer_heights ( std : : vector < std : : vector < SupportNode * > > & contact_nodes )
{
if ( contact_nodes . empty ( ) )
return ;
const PrintConfig & print_config = m_object - > print ( ) - > config ( ) ;
const PrintObjectConfig & config = m_object - > config ( ) ;
// don't merge layers for Vine support, or the branches will be unsmooth
// TODO can we merge layers in a way that guaranttees smoothness?
if ( ! print_config . independent_support_layer_height | | is_slim ) {
for ( int layer_nr = 0 ; layer_nr < contact_nodes . size ( ) ; layer_nr + + ) {
std : : vector < SupportNode * > & curr_layer_nodes = contact_nodes [ layer_nr ] ;
for ( SupportNode * node : curr_layer_nodes ) {
node - > print_z = m_object - > get_layer ( layer_nr ) - > print_z ;
node - > height = m_object - > get_layer ( layer_nr ) - > height ;
}
}
return ;
}
// extreme layer_id
std : : vector < int > extremes ;
const coordf_t layer_height = config . layer_height . value ;
const coordf_t max_layer_height = m_slicing_params . max_layer_height ;
const size_t bot_intf_layers = config . support_interface_bottom_layers . value ;
const size_t top_intf_layers = config . support_interface_top_layers . value ;
// if already using max layer height, no need to adjust
if ( layer_height = = max_layer_height ) return ;
extremes . push_back ( 0 ) ;
for ( SupportNode * node : contact_nodes [ 0 ] ) {
node - > print_z = m_object - > get_layer ( 0 ) - > print_z ;
node - > height = m_object - > get_layer ( 0 ) - > height ;
}
for ( int layer_nr = 1 ; layer_nr < contact_nodes . size ( ) ; layer_nr + + ) {
std : : vector < SupportNode * > & curr_layer_nodes = contact_nodes [ layer_nr ] ;
for ( SupportNode * node : curr_layer_nodes ) {
if ( node - > support_roof_layers_below > 0 | | node - > support_floor_layers_above = = bot_intf_layers ) {
extremes . push_back ( layer_nr ) ;
break ;
}
}
if ( extremes . back ( ) = = layer_nr ) {
// contact layer use the same print_z and layer height with object layer
for ( SupportNode * node : curr_layer_nodes ) {
node - > print_z = m_object - > get_layer ( layer_nr ) - > print_z ;
node - > height = m_object - > get_layer ( layer_nr ) - > height ;
}
}
}
// schedule new layer heights and print_z
for ( size_t idx_extreme = 0 ; idx_extreme < extremes . size ( ) ; idx_extreme + + ) {
int extr2_layer_nr = extremes [ idx_extreme ] ;
coordf_t extr2z = m_object - > get_layer ( extr2_layer_nr ) - > bottom_z ( ) ;
int extr1_layer_nr = idx_extreme = = 0 ? - 1 : extremes [ idx_extreme - 1 ] ;
coordf_t extr1z = idx_extreme = = 0 ? 0.f : m_object - > get_layer ( extr1_layer_nr ) - > print_z ;
coordf_t dist = extr2z - extr1z ;
// Insert intermediate layers.
size_t n_layers_extra = size_t ( ceil ( dist / m_slicing_params . max_suport_layer_height ) ) ;
if ( n_layers_extra < = 1 )
continue ;
coordf_t step = dist / coordf_t ( n_layers_extra ) ;
coordf_t print_z = extr1z + step ;
assert ( step > = layer_height - EPSILON ) ;
for ( int layer_nr = extr1_layer_nr + 1 ; layer_nr < extr2_layer_nr ; layer_nr + + ) {
std : : vector < SupportNode * > & curr_layer_nodes = contact_nodes [ layer_nr ] ;
if ( curr_layer_nodes . empty ( ) ) continue ;
if ( std : : abs ( print_z - curr_layer_nodes [ 0 ] - > print_z ) < step / 2 + EPSILON ) {
for ( SupportNode * node : curr_layer_nodes ) {
node - > print_z = print_z ;
node - > height = step ;
}
print_z + = step ;
}
else {
// can't clear curr_layer_nodes, or the model will have empty layers
for ( SupportNode * node : curr_layer_nodes ) {
node - > print_z = 0.0 ;
node - > height = 0.0 ;
}
}
}
}
}
std : : vector < LayerHeightData > TreeSupport : : plan_layer_heights ( std : : vector < std : : vector < SupportNode * > > & contact_nodes )
{
const PrintObjectConfig & config = m_object - > config ( ) ;
const PrintConfig & print_config = m_object - > print ( ) - > config ( ) ;
const coordf_t max_layer_height = m_slicing_params . max_layer_height ;
const coordf_t layer_height = config . layer_height . value ;
const coordf_t layer_height = m_object_ config- > layer_height . value ;
coordf_t z_distance_top = m_slicing_params . gap_support_object ;
// BBS: add extra distance if thick bridge is enabled
// Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height
if ( ! m_slicing_params . soluble_interface & & m_object_config - > thick_bridges ) {
z_distance_top + = m_object - > layers ( ) [ 0 ] - > regions ( ) [ 0 ] - > region ( ) . bridging_height_avg ( m_object - > print ( ) - > config ( ) ) - layer_height ;
z_distance_top + = m_object - > layers ( ) [ 0 ] - > regions ( ) [ 0 ] - > region ( ) . bridging_height_avg ( * m_print_config ) - layer_height ;
}
const size_t support_roof_layers = config . support_interface_top_layers . value ;
const size_t support_roof_layers = m_object_ config- > support_interface_top_layers . value ;
const int z_distance_top_layers = round_up_divide ( scale_ ( z_distance_top ) , scale_ ( layer_height ) ) + 1 ;
std : : vector < LayerHeightData > layer_heights ( contact_nodes . size ( ) ) ;
std : : vector < int > bounds ;
if ( ! config . tree_support_adaptive_layer_height | | layer_height = = max_layer_height | | ! print_config . independent_support_ layer_height ) {
if ( ! m_object_ config- > tree_support_adaptive_layer_height | | layer_height = = max_layer_height | | ! m_support_params . independent_layer_height ) {
for ( int layer_nr = 0 ; layer_nr < contact_nodes . size ( ) ; layer_nr + + ) {
layer_heights [ layer_nr ] = { m_object - > get_layer ( layer_nr ) - > print_z , m_object - > get_layer ( layer_nr ) - > height , layer_nr > 0 ? size_t ( layer_nr - 1 ) : 0 } ;
}
@@ -3096,7 +3027,7 @@ std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::ve
return layer_heights ;
}
void TreeSupport : : generate_contact_points ( std : : vector < std : : vector < SupportNode * > > & contact_nodes )
void TreeSupport : : generate_contact_points ( std : : vector < std : : vector < SupportNode * > > & contact_nodes , const std : : vector < TreeSupport3D : : SupportElements > & move_bounds )
{
const PrintObjectConfig & config = m_object - > config ( ) ;
const coordf_t point_spread = scale_ ( config . tree_support_branch_distance . value ) ;
@@ -3144,7 +3075,6 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
if ( m_object - > layers ( ) . size ( ) < = z_distance_top_layers + 1 )
return ;
m_highest_overhang_layer = 0 ;
int nonempty_layers = 0 ;
std : : vector < Slic3r : : Vec3f > all_nodes ;
for ( size_t layer_nr = 1 ; layer_nr < m_object - > layers ( ) . size ( ) ; layer_nr + + )
@@ -3157,7 +3087,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
if ( overhang . empty ( ) )
continue ;
m_highest_overhang_layer = std : : max ( m_highest_overhang_layer , layer_nr ) ;
std : : unordered_set < Point , PointHash > already_inserted ;
auto print_z = m_object - > get_layer ( layer_nr ) - > print_z ;
auto height = m_object - > get_layer ( layer_nr ) - > height ;
@@ -3172,92 +3102,90 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
SupportNode * contact_node = new SupportNode ( candidate , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , true , SupportNode : : NO_PARENT , print_z ,
height , z_distance_top ) ;
contact_node - > type = ePolygon ;
contact_node - > overhang = & overhang_part ;
contact_node - > overhang = overhang_part ;
curr_nodes . emplace_back ( contact_node ) ;
continue ;
}
}
overhang_bounds . inflated ( half_overhang_distance ) ;
bool added = false ; //Did we add a point this way?
for ( Point candidate : grid_points )
{
if ( overhang_bounds . contains ( candidate ) )
{
// 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!
bool is_inside = is_inside_ex ( overhang_part , candidate ) ;
if ( ! is_inside ) {
constexpr coordf_t distance_inside = 0 ; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that
// fall between overhang areas on constant surfaces.
move_inside_expoly ( overhang_part , candidate , distance_inside , half_overhang_distance ) ;
is_inside = is_inside_ex ( overhang_part , candidate ) ;
}
if ( is_inside )
{
// collision radius has to be 0 or the supports are too few at curved slopes
//if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate))
{
constexpr bool to_buildplate = true ;
SupportNode * contact_node = new SupportNode ( candidate , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , to_buildplate ,
SupportNode : : NO_PARENT , print_z , height , z_distance_top ) ;
contact_node - > overhang = & overhang_part ;
curr_nodes . emplace_back ( contact_node ) ;
added = true ;
}
}
}
}
if ( ! added ) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported.
{
auto bbox = overhang_part . contour . bounding_box ( ) ;
Points candidates ;
if ( ts_layer - > overhang_types [ & overhang_part ] = = SupportLayer : : Detected )
candidates = { bbox . min , bounding_box_middle ( bbox ) , bbox . max } ;
else
candidates = { bounding_box_middle ( bbox ) } ;
for ( Point candidate : candidates ) {
if ( ! overhang_part . contains ( candidate ) )
move_inside_expoly ( overhang_part , candidate ) ;
constexpr bool to_buildplate = true ;
SupportNode * contact_node = new SupportNode ( candidate , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , to_buildplate , SupportNode : : NO_PARENT ,
print_z , height , z_distance_top ) ;
contact_node - > overhang = & overhang_part ;
curr_nodes . emplace_back ( contact_node ) ;
}
}
// add supports at corners for both auto and manual overhangs, github #2008
if ( /*ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected*/ 1 ) {
// add points at corners
auto & points = overhang_part . contour . points ;
auto & points = overhang_part . contour . points ;
int nSize = points . size ( ) ;
for ( int i = 0 ; i < nSize ; i + + ) {
auto pt = points [ i ] ;
auto v1 = ( pt - points [ ( i - 1 + nSize ) % nSize ] ) . cast < double > ( ) . normalized ( ) ;
auto v2 = ( pt - points [ ( i + 1 ) % nSize ] ) . cast < double > ( ) . normalized ( ) ;
if ( v1 . dot ( v2 ) > - 0.7 ) { // angle smaller than 135 degrees
SupportNode * contact_node = new SupportNode ( pt , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , true , SupportNode : : NO_PARENT , print_z ,
height , z_distance_top ) ;
contact_node - > overhang = & overhang_part ;
SupportNode * contact_node = new SupportNode ( pt , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , true , SupportNode : : NO_PARENT , print_z ,
height , z_distance_top ) ;
contact_node - > overhang = overhang_part ;
contact_node - > is_corner = true ;
curr_nodes . emplace_back ( contact_node ) ;
Point hash_pos = pt / ( ( m_min_radius + 1 ) / 1 ) ;
already_inserted . emplace ( hash_pos ) ;
}
}
}
auto & move_bounds_layer = move_bounds [ layer_nr ] ;
if ( move_bounds_layer . empty ( ) ) {
overhang_bounds . inflated ( half_overhang_distance ) ;
bool added = false ; //Did we add a point this way?
for ( Point candidate : grid_points ) {
if ( overhang_bounds . contains ( candidate ) ) {
// 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!
bool is_inside = is_inside_ex ( overhang_part , candidate ) ;
if ( ! is_inside ) {
constexpr coordf_t distance_inside = 0 ; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that
// fall between overhang areas on constant surfaces.
is_inside = move_inside_expoly ( overhang_part , candidate , distance_inside , half_overhang_distance ) ;
}
if ( is_inside ) {
// normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
Point hash_pos = candidate / ( ( m_min_radius + 1 ) / 1 ) ;
if ( ! already_inserted . count ( hash_pos ) ) {
already_inserted . emplace ( hash_pos ) ;
constexpr bool to_buildplate = true ;
SupportNode * contact_node = new SupportNode ( candidate , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , to_buildplate ,
SupportNode : : NO_PARENT , print_z , height , z_distance_top ) ;
contact_node - > overhang = overhang_part ;
curr_nodes . emplace_back ( contact_node ) ;
added = true ;
}
}
}
}
if ( ! added ) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported.
{
auto bbox = overhang_part . contour . bounding_box ( ) ;
Points candidates ;
if ( ts_layer - > overhang_types [ & overhang_part ] = = SupportLayer : : Detected )
candidates = { bbox . min , bounding_box_middle ( bbox ) , bbox . max } ;
else
candidates = { bounding_box_middle ( bbox ) } ;
for ( Point candidate : candidates ) {
if ( ! overhang_part . contains ( candidate ) )
move_inside_expoly ( overhang_part , candidate ) ;
constexpr bool to_buildplate = true ;
SupportNode * contact_node = new SupportNode ( candidate , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , to_buildplate , SupportNode : : NO_PARENT ,
print_z , height , z_distance_top ) ;
contact_node - > overhang = overhang_part ;
curr_nodes . emplace_back ( contact_node ) ;
}
}
}
if ( ts_layer - > overhang_types [ & overhang_part ] = = SupportLayer : : Enforced | | is_slim ) {
// remove close points in Enforcers
// auto above_nodes = contact_nodes[layer_nr - 1];
if ( ! curr_nodes . empty ( ) /*&& !above_nodes.empty()*/ ) {
if ( ts_layer - > overhang_types [ & overhang_part ] = = SupportLayer : : Enforced | | is_slim ) {
// remove close points
for ( auto it = curr_nodes . begin ( ) ; it ! = curr_nodes . end ( ) ; ) {
bool is_duplicate = false ;
if ( ! ( * it ) - > is_corner ) {
Slic3r : : Vec3f curr_pt ( ( * it ) - > position ( 0 ) , ( * it ) - > position ( 1 ) , scale_ ( ( * it ) - > print_z ) ) ;
for ( auto & pt : all_nodes ) {
for ( auto & pt : all_nodes ) {
auto dif = curr_pt - pt ;
if ( dif . norm ( ) < point_spread / 2 ) {
delete ( * it ) ;
it = curr_nodes . erase ( it ) ;
it = curr_nodes . erase ( it ) ;
is_duplicate = true ;
break ;
}
@@ -3267,6 +3195,14 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
}
}
}
else {
for ( auto & elem : move_bounds_layer ) {
SupportNode * contact_node = new SupportNode ( elem . state . result_on_layer , - z_distance_top_layers , layer_nr , support_roof_layers + z_distance_top_layers , elem . state . to_buildplate ,
SupportNode : : NO_PARENT , print_z , height , z_distance_top ) ;
contact_node - > overhang = ExPolygon ( elem . influence_area . front ( ) ) ;
curr_nodes . emplace_back ( contact_node ) ;
}
}
}
if ( ! curr_nodes . empty ( ) ) nonempty_layers + + ;
for ( auto node : curr_nodes ) { all_nodes . emplace_back ( node - > position ( 0 ) , node - > position ( 1 ) , scale_ ( node - > print_z ) ) ; }
@@ -3395,6 +3331,7 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke
assert ( key . layer_nr < m_layer_outlines . size ( ) ) ;
ExPolygons collision_areas = offset_ex ( m_layer_outlines [ key . layer_nr ] , scale_ ( key . radius ) ) ;
collision_areas = expolygons_simplify ( collision_areas , scale_ ( m_radius_sample_resolution ) ) ;
const auto ret = m_collision_cache . insert ( { key , std : : move ( collision_areas ) } ) ;
return ret . first - > second ;
}
@@ -3434,7 +3371,7 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke
//assert(ret.second);
return ret . first - > second ;
} else {
ExPolygons avoidance_areas = offset_ex( m_layer_outlines_below[ layer_nr] , scale_( m_xy_distance + radius ) ) ;
ExPolygons avoidance_areas = get_collision ( radius , layer_nr ) ; // std::move( offset_ex( m_layer_outlines_below[ layer_nr], scale_( m_xy_distance + radius)));
auto ret = m_avoidance_cache . insert ( { key , std : : move ( avoidance_areas ) } ) ;
assert ( ret . second ) ;
return ret . first - > second ;