2022-07-15 15:37:19 +00:00
# include "Layer.hpp"
# include "BridgeDetector.hpp"
# include "ClipperUtils.hpp"
# include "Geometry.hpp"
# include "PerimeterGenerator.hpp"
# include "Print.hpp"
# include "Surface.hpp"
# include "BoundingBox.hpp"
# include "SVG.hpp"
2024-03-29 07:37:11 +00:00
# include "RegionExpansion.hpp"
2022-07-15 15:37:19 +00:00
# include <string>
# include <map>
# include <boost/log/trivial.hpp>
2025-01-06 08:43:05 +00:00
static const double max_deviation = 0.5 * 1e6 ;
static const double max_variance = 500 * 1e6 ;
2022-07-15 15:37:19 +00:00
namespace Slic3r {
Flow LayerRegion : : flow ( FlowRole role ) const
{
return this - > flow ( role , m_layer - > height ) ;
}
Flow LayerRegion : : flow ( FlowRole role , double layer_height ) const
{
return m_region - > flow ( * m_layer - > object ( ) , role , layer_height , m_layer - > id ( ) = = 0 ) ;
}
Flow LayerRegion : : bridging_flow ( FlowRole role , bool thick_bridge ) const
{
const PrintRegion & region = this - > region ( ) ;
const PrintRegionConfig & region_config = region . config ( ) ;
const PrintObject & print_object = * this - > layer ( ) - > object ( ) ;
if ( thick_bridge ) {
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
auto nozzle_diameter = float ( print_object . print ( ) - > config ( ) . nozzle_diameter . get_at ( region . extruder ( role ) - 1 ) ) ;
// Applies default bridge spacing.
return Flow : : bridging_flow ( float ( sqrt ( region_config . bridge_flow ) ) * nozzle_diameter , nozzle_diameter ) ;
} else {
// The same way as other slicers: Use normal extrusions. Apply bridge_flow while maintaining the original spacing.
return this - > flow ( role ) . with_flow_ratio ( region_config . bridge_flow ) ;
}
}
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
void LayerRegion : : slices_to_fill_surfaces_clipped ( )
{
2024-11-22 10:32:47 +00:00
// Note: this method should be idempotent, but fill_surfaces gets modified
2022-07-15 15:37:19 +00:00
// in place. However we're now only using its boundaries (which are invariant)
// so we're safe. This guarantees idempotence of prepare_infill() also in case
// that combine_infill() turns some fill_surface into VOID surfaces.
// Collect polygons per surface type.
std : : array < SurfacesPtr , size_t ( stCount ) > by_surface ;
for ( Surface & surface : this - > slices . surfaces )
by_surface [ size_t ( surface . surface_type ) ] . emplace_back ( & surface ) ;
// Trim surfaces by the fill_boundaries.
this - > fill_surfaces . surfaces . clear ( ) ;
for ( size_t surface_type = 0 ; surface_type < size_t ( stCount ) ; + + surface_type ) {
const SurfacesPtr & this_surfaces = by_surface [ surface_type ] ;
if ( ! this_surfaces . empty ( ) )
this - > fill_surfaces . append ( intersection_ex ( this_surfaces , this - > fill_expolygons ) , SurfaceType ( surface_type ) ) ;
}
}
2025-01-17 03:03:18 +00:00
void LayerRegion : : auto_circle_compensation ( SurfaceCollection & slices , const AutoContourHolesCompensationParams & auto_contour_holes_compensation_params , float manual_offset )
2024-11-22 10:32:47 +00:00
{
2025-01-17 03:03:18 +00:00
// filament is 1 base
int filament_idx = this - > region ( ) . config ( ) . wall_filament - 1 ;
2024-11-22 10:32:47 +00:00
2025-01-06 08:43:05 +00:00
double limited_speed = auto_contour_holes_compensation_params . circle_compensation_speed [ filament_idx ] ;
double counter_speed_coef = auto_contour_holes_compensation_params . counter_speed_coef [ filament_idx ] / 1e6 ;
double counter_diameter_coef = auto_contour_holes_compensation_params . counter_diameter_coef [ filament_idx ] / 1e6 ;
double counter_compensate_coef = auto_contour_holes_compensation_params . counter_compensate_coef [ filament_idx ] ;
2024-11-22 10:32:47 +00:00
2025-01-06 08:43:05 +00:00
double hole_speed_coef = auto_contour_holes_compensation_params . hole_speed_coef [ filament_idx ] / 1e6 ;
double hole_diameter_coef = auto_contour_holes_compensation_params . hole_diameter_coef [ filament_idx ] / 1e6 ;
double hole_compensate_coef = auto_contour_holes_compensation_params . hole_compensate_coef [ filament_idx ] ;
2024-11-22 10:32:47 +00:00
2025-01-06 08:43:05 +00:00
double counter_limit_min_value = auto_contour_holes_compensation_params . counter_limit_min_value [ filament_idx ] ;
double counter_limit_max_value = auto_contour_holes_compensation_params . counter_limit_max_value [ filament_idx ] ;
double hole_limit_min_value = auto_contour_holes_compensation_params . hole_limit_min_value [ filament_idx ] ;
double hole_limit_max_value = auto_contour_holes_compensation_params . hole_limit_max_value [ filament_idx ] ;
2024-11-22 10:32:47 +00:00
2025-01-06 08:43:05 +00:00
double diameter_limit_value = auto_contour_holes_compensation_params . diameter_limit [ filament_idx ] ;
2024-11-22 10:32:47 +00:00
for ( Surface & surface : slices . surfaces ) {
Point center ;
double diameter = 0 ;
if ( surface . expolygon . contour . is_approx_circle ( max_deviation , max_variance , center , diameter ) ) {
2025-01-17 03:03:18 +00:00
double offset_value = counter_speed_coef * limited_speed + counter_diameter_coef * diameter + counter_compensate_coef + manual_offset ;
2024-11-22 10:32:47 +00:00
if ( offset_value < counter_limit_min_value ) {
offset_value = counter_limit_min_value ;
} else if ( offset_value > counter_limit_max_value ) {
offset_value = counter_limit_max_value ;
}
Polygons offseted_polys = offset ( surface . expolygon . contour , offset_value ) ;
if ( offseted_polys . size ( ) = = 1 ) {
surface . expolygon . contour = offseted_polys [ 0 ] ;
if ( diameter < diameter_limit_value * 1e6 )
surface . counter_circle_compensation = true ;
}
}
for ( size_t i = 0 ; i < surface . expolygon . holes . size ( ) ; + + i ) {
Polygon & hole = surface . expolygon . holes [ i ] ;
if ( hole . is_approx_circle ( max_deviation , max_variance , center , diameter ) ) {
2025-01-17 03:03:18 +00:00
double offset_value = hole_speed_coef * limited_speed + hole_diameter_coef * diameter + hole_compensate_coef + manual_offset ;
2024-11-22 10:32:47 +00:00
if ( offset_value < hole_limit_min_value ) {
offset_value = hole_limit_min_value ;
} else if ( offset_value > hole_limit_max_value ) {
offset_value = hole_limit_max_value ;
}
// positive value means shrinking hole, which oppsite to contour
offset_value = - offset_value ;
Polygons offseted_polys = offset ( hole , offset_value ) ;
if ( offseted_polys . size ( ) = = 1 ) {
hole = offseted_polys [ 0 ] ;
if ( diameter < diameter_limit_value * 1e6 )
surface . holes_circle_compensation . push_back ( i ) ;
}
}
}
}
}
2024-07-15 03:02:12 +00:00
void LayerRegion : : make_perimeters ( const SurfaceCollection & slices , SurfaceCollection * fill_surfaces , ExPolygons * fill_no_overlap , std : : vector < LoopNode > & loop_nodes )
2022-07-15 15:37:19 +00:00
{
this - > perimeters . clear ( ) ;
this - > thin_fills . clear ( ) ;
2024-11-22 10:32:47 +00:00
const PrintConfig & print_config = this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) ;
2022-07-15 15:37:19 +00:00
const PrintRegionConfig & region_config = this - > region ( ) . config ( ) ;
2022-07-22 09:46:10 +00:00
const PrintObjectConfig & object_config = this - > layer ( ) - > object ( ) - > config ( ) ;
2022-07-15 15:37:19 +00:00
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
bool spiral_mode = print_config . spiral_mode & &
//FIXME account for raft layers.
( this - > layer ( ) - > id ( ) > = size_t ( region_config . bottom_shell_layers . value ) & &
this - > layer ( ) - > print_z > = region_config . bottom_shell_thickness - EPSILON ) ;
PerimeterGenerator g (
// input:
& slices ,
this - > layer ( ) - > height ,
this - > flow ( frPerimeter ) ,
& region_config ,
& this - > layer ( ) - > object ( ) - > config ( ) ,
& print_config ,
spiral_mode ,
// output:
& this - > perimeters ,
& this - > thin_fills ,
2022-09-14 13:54:33 +00:00
fill_surfaces ,
//BBS
2024-07-15 03:02:12 +00:00
fill_no_overlap ,
& loop_nodes
2022-07-15 15:37:19 +00:00
) ;
2024-11-22 10:32:47 +00:00
2022-07-15 15:37:19 +00:00
if ( this - > layer ( ) - > lower_layer ! = nullptr )
// Cummulative sum of polygons over all the regions.
g . lower_slices = & this - > layer ( ) - > lower_layer - > lslices ;
if ( this - > layer ( ) - > upper_layer ! = NULL )
g . upper_slices = & this - > layer ( ) - > upper_layer - > lslices ;
2024-11-22 10:32:47 +00:00
2022-07-15 15:37:19 +00:00
g . layer_id = ( int ) this - > layer ( ) - > id ( ) ;
g . ext_perimeter_flow = this - > flow ( frExternalPerimeter ) ;
2022-07-22 09:46:10 +00:00
g . overhang_flow = this - > bridging_flow ( frPerimeter , object_config . thick_bridges ) ;
2022-07-15 15:37:19 +00:00
g . solid_infill_flow = this - > flow ( frSolidInfill ) ;
2022-11-30 13:14:55 +00:00
if ( this - > layer ( ) - > object ( ) - > config ( ) . wall_generator . value = = PerimeterGeneratorType : : Arachne & & ! spiral_mode )
g . process_arachne ( ) ;
else
g . process_classic ( ) ;
2022-07-15 15:37:19 +00:00
}
2024-03-29 07:37:11 +00:00
# if 1
// Extract surfaces of given type from surfaces, extract fill (layer) thickness of one of the surfaces.
static ExPolygons fill_surfaces_extract_expolygons ( Surfaces & surfaces , std : : initializer_list < SurfaceType > surface_types , double & thickness )
{
size_t cnt = 0 ;
for ( const Surface & surface : surfaces )
if ( std : : find ( surface_types . begin ( ) , surface_types . end ( ) , surface . surface_type ) ! = surface_types . end ( ) ) {
+ + cnt ;
thickness = surface . thickness ;
}
if ( cnt = = 0 )
return { } ;
ExPolygons out ;
out . reserve ( cnt ) ;
for ( Surface & surface : surfaces )
if ( std : : find ( surface_types . begin ( ) , surface_types . end ( ) , surface . surface_type ) ! = surface_types . end ( ) )
out . emplace_back ( std : : move ( surface . expolygon ) ) ;
return out ;
}
2024-05-10 04:20:33 +00:00
// Cache for detecting bridge orientation and merging regions with overlapping expansions.
struct Bridge {
ExPolygon expolygon ;
uint32_t group_id ;
std : : vector < Algorithm : : RegionExpansionEx > : : const_iterator bridge_expansion_begin ;
std : : optional < double > angle { std : : nullopt } ;
} ;
// Group the bridge surfaces by overlaps.
uint32_t group_id ( std : : vector < Bridge > & bridges , uint32_t src_id ) {
uint32_t group_id = bridges [ src_id ] . group_id ;
while ( group_id ! = src_id ) {
src_id = group_id ;
group_id = bridges [ src_id ] . group_id ;
2024-03-29 07:37:11 +00:00
}
2024-05-10 04:20:33 +00:00
bridges [ src_id ] . group_id = group_id ;
return group_id ;
} ;
2024-03-29 07:37:11 +00:00
2024-05-10 04:20:33 +00:00
std : : vector < Bridge > get_grouped_bridges (
ExPolygons & & bridge_expolygons ,
const std : : vector < Algorithm : : RegionExpansionEx > & bridge_expansions
) {
using namespace Algorithm ;
std : : vector < Bridge > result ;
2024-03-29 07:37:11 +00:00
{
2024-05-10 04:20:33 +00:00
result . reserve ( bridge_expansions . size ( ) ) ;
2024-03-29 07:37:11 +00:00
uint32_t group_id = 0 ;
2024-05-10 04:20:33 +00:00
using std : : move_iterator ;
for ( ExPolygon & expolygon : bridge_expolygons )
result . push_back ( { std : : move ( expolygon ) , group_id + + , bridge_expansions . end ( ) } ) ;
2024-03-29 07:37:11 +00:00
}
2024-05-10 04:20:33 +00:00
// Detect overlaps of bridge anchors inside their respective shell regions.
// bridge_expansions are sorted by boundary id and source id.
for ( auto expansion_iterator = bridge_expansions . begin ( ) ; expansion_iterator ! = bridge_expansions . end ( ) ; ) {
auto boundary_region_begin = expansion_iterator ;
auto boundary_region_end = std : : find_if (
next ( expansion_iterator ) ,
bridge_expansions . end ( ) ,
[ & ] ( const RegionExpansionEx & expansion ) {
return expansion . boundary_id ! = expansion_iterator - > boundary_id ;
}
) ;
2024-03-29 07:37:11 +00:00
// Cache of bboxes per expansion boundary.
2024-05-10 04:20:33 +00:00
std : : vector < BoundingBox > bounding_boxes ;
bounding_boxes . reserve ( std : : distance ( boundary_region_begin , boundary_region_end ) ) ;
std : : transform (
boundary_region_begin ,
boundary_region_end ,
std : : back_inserter ( bounding_boxes ) ,
[ ] ( const RegionExpansionEx & expansion ) {
return get_extents ( expansion . expolygon . contour ) ;
}
) ;
// For each bridge anchor of the current source:
for ( ; expansion_iterator ! = boundary_region_end ; + + expansion_iterator ) {
auto candidate_iterator = std : : next ( expansion_iterator ) ;
for ( ; candidate_iterator ! = boundary_region_end ; + + candidate_iterator ) {
const BoundingBox & current_bounding_box {
bounding_boxes [ expansion_iterator - boundary_region_begin ]
} ;
const BoundingBox & candidate_bounding_box {
bounding_boxes [ candidate_iterator - boundary_region_begin ]
} ;
if (
expansion_iterator - > src_id ! = candidate_iterator - > src_id
& & current_bounding_box . overlap ( candidate_bounding_box )
// One may ignore holes, they are irrelevant for intersection test.
& & ! intersection ( expansion_iterator - > expolygon . contour , candidate_iterator - > expolygon . contour ) . empty ( )
) {
// The two bridge regions intersect. Give them the same (lower) group id.
uint32_t id = group_id ( result , expansion_iterator - > src_id ) ;
uint32_t id2 = group_id ( result , candidate_iterator - > src_id ) ;
if ( id < id2 )
result [ id2 ] . group_id = id ;
else
result [ id ] . group_id = id2 ;
}
2024-03-29 07:37:11 +00:00
}
}
}
2024-05-10 04:20:33 +00:00
return result ;
}
2024-03-29 07:37:11 +00:00
2024-05-10 04:20:33 +00:00
void detect_bridge_directions (
const Algorithm : : WaveSeeds & bridge_anchors ,
std : : vector < Bridge > & bridges ,
const std : : vector < ExpansionZone > & expansion_zones
) {
if ( expansion_zones . empty ( ) ) {
throw std : : runtime_error ( " At least one expansion zone must exist! " ) ;
}
auto it_bridge_anchor = bridge_anchors . begin ( ) ;
for ( uint32_t bridge_id = 0 ; bridge_id < uint32_t ( bridges . size ( ) ) ; + + bridge_id ) {
Bridge & bridge = bridges [ bridge_id ] ;
2024-03-29 07:37:11 +00:00
Polygons anchor_areas ;
2024-05-10 04:20:33 +00:00
int32_t last_anchor_id = - 1 ;
for ( ; it_bridge_anchor ! = bridge_anchors . end ( ) & & it_bridge_anchor - > src = = bridge_id ; + + it_bridge_anchor ) {
if ( last_anchor_id ! = int ( it_bridge_anchor - > boundary ) ) {
last_anchor_id = int ( it_bridge_anchor - > boundary ) ;
unsigned start_index { } ;
unsigned end_index { } ;
for ( const ExpansionZone & expansion_zone : expansion_zones ) {
end_index + = expansion_zone . expolygons . size ( ) ;
if ( last_anchor_id < static_cast < int64_t > ( end_index ) ) {
append ( anchor_areas , to_polygons ( expansion_zone . expolygons [ last_anchor_id - start_index ] ) ) ;
break ;
}
start_index + = expansion_zone . expolygons . size ( ) ;
2024-03-29 07:37:11 +00:00
}
}
2024-05-10 04:20:33 +00:00
}
Lines lines { to_lines ( diff_pl ( to_polylines ( bridge . expolygon ) , expand ( anchor_areas , float ( SCALED_EPSILON ) ) ) ) } ;
auto [ bridging_dir , unsupported_dist ] = detect_bridging_direction ( lines , to_polygons ( bridge . expolygon ) ) ;
bridge . angle = M_PI + std : : atan2 ( bridging_dir . y ( ) , bridging_dir . x ( ) ) ;
if constexpr ( false ) {
2024-03-29 07:37:11 +00:00
coordf_t stroke_width = scale_ ( 0.06 ) ;
BoundingBox bbox = get_extents ( anchor_areas ) ;
bbox . merge ( get_extents ( bridge . expolygon ) ) ;
bbox . offset ( scale_ ( 1. ) ) ;
: : Slic3r : : SVG
2024-05-10 04:20:33 +00:00
svg ( debug_out_path ( ( " bridge " + std : : to_string ( * bridge . angle ) + " _ " /* + std::to_string(this->layer()->bottom_z())*/ ) . c_str ( ) ) ,
2024-03-29 07:37:11 +00:00
bbox ) ;
svg . draw ( bridge . expolygon , " cyan " ) ;
svg . draw ( lines , " green " , stroke_width ) ;
svg . draw ( anchor_areas , " red " ) ;
}
}
2024-05-10 04:20:33 +00:00
}
2024-03-29 07:37:11 +00:00
2024-05-10 04:20:33 +00:00
Surfaces merge_bridges (
std : : vector < Bridge > & bridges ,
const std : : vector < Algorithm : : RegionExpansionEx > & bridge_expansions ,
const float closing_radius
) {
for ( auto it = bridge_expansions . begin ( ) ; it ! = bridge_expansions . end ( ) ; ) {
bridges [ it - > src_id ] . bridge_expansion_begin = it ;
uint32_t src_id = it - > src_id ;
for ( + + it ; it ! = bridge_expansions . end ( ) & & it - > src_id = = src_id ; + + it ) ;
}
Surfaces result ;
for ( uint32_t bridge_id = 0 ; bridge_id < uint32_t ( bridges . size ( ) ) ; + + bridge_id ) {
if ( group_id ( bridges , bridge_id ) = = bridge_id ) {
// Head of the group.
Polygons acc ;
for ( uint32_t bridge_id2 = bridge_id ; bridge_id2 < uint32_t ( bridges . size ( ) ) ; + + bridge_id2 )
if ( group_id ( bridges , bridge_id2 ) = = bridge_id ) {
append ( acc , to_polygons ( std : : move ( bridges [ bridge_id2 ] . expolygon ) ) ) ;
auto it_bridge_expansion = bridges [ bridge_id2 ] . bridge_expansion_begin ;
assert ( it_bridge_expansion = = bridge_expansions . end ( ) | | it_bridge_expansion - > src_id = = bridge_id2 ) ;
for ( ; it_bridge_expansion ! = bridge_expansions . end ( ) & & it_bridge_expansion - > src_id = = bridge_id2 ; + + it_bridge_expansion )
append ( acc , to_polygons ( it_bridge_expansion - > expolygon ) ) ;
}
//FIXME try to be smart and pick the best bridging angle for all?
if ( ! bridges [ bridge_id ] . angle ) {
assert ( false & & " Bridge angle must be pre-calculated! " ) ;
2024-03-29 07:37:11 +00:00
}
2024-05-10 04:20:33 +00:00
Surface templ { stBottomBridge , { } } ;
templ . bridge_angle = bridges [ bridge_id ] . angle ? * bridges [ bridge_id ] . angle : - 1 ;
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
ExPolygons final = closing_ex ( acc , closing_radius ) ;
// without safety offset, artifacts are generated (GH #2494)
// union_safety_offset_ex(acc)
for ( ExPolygon & ex : final )
result . emplace_back ( templ , std : : move ( ex ) ) ;
}
}
return result ;
}
struct ExpansionResult {
Algorithm : : WaveSeeds anchors ;
std : : vector < Algorithm : : RegionExpansionEx > expansions ;
} ;
ExpansionResult expand_expolygons (
const ExPolygons & expolygons ,
std : : vector < ExpansionZone > & expansion_zones
) {
using namespace Algorithm ;
WaveSeeds bridge_anchors ;
std : : vector < RegionExpansionEx > bridge_expansions ;
unsigned processed_bridges_count = 0 ;
for ( ExpansionZone & expansion_zone : expansion_zones ) {
WaveSeeds seeds { wave_seeds (
expolygons ,
expansion_zone . expolygons ,
expansion_zone . parameters . tiny_expansion ,
true
) } ;
std : : vector < RegionExpansionEx > expansions { propagate_waves_ex (
seeds ,
expansion_zone . expolygons ,
expansion_zone . parameters
) } ;
for ( WaveSeed & seed : seeds )
seed . boundary + = processed_bridges_count ;
for ( RegionExpansionEx & expansion : expansions )
expansion . boundary_id + = processed_bridges_count ;
expansion_zone . expanded_into = ! expansions . empty ( ) ;
append ( bridge_anchors , std : : move ( seeds ) ) ;
append ( bridge_expansions , std : : move ( expansions ) ) ;
processed_bridges_count + = expansion_zone . expolygons . size ( ) ;
2024-03-29 07:37:11 +00:00
}
2024-05-10 04:20:33 +00:00
return { bridge_anchors , bridge_expansions } ;
}
// Extract bridging surfaces from "surfaces", expand them into "shells" using expansion_params,
// detect bridges.
// Trim "shells" by the expanded bridges.
Surfaces expand_bridges_detect_orientations (
Surfaces & surfaces ,
std : : vector < ExpansionZone > & expansion_zones ,
const float closing_radius
)
{
using namespace Slic3r : : Algorithm ;
double thickness ;
ExPolygons bridge_expolygons = fill_surfaces_extract_expolygons ( surfaces , { stBottomBridge } , thickness ) ;
if ( bridge_expolygons . empty ( ) )
return { } ;
// Calculate bridge anchors and their expansions in their respective shell region.
ExpansionResult expansion_result { expand_expolygons (
bridge_expolygons ,
expansion_zones
) } ;
std : : vector < Bridge > bridges { get_grouped_bridges (
std : : move ( bridge_expolygons ) ,
expansion_result . expansions
) } ;
bridge_expolygons . clear ( ) ;
std : : sort ( expansion_result . anchors . begin ( ) , expansion_result . anchors . end ( ) , Algorithm : : lower_by_src_and_boundary ) ;
detect_bridge_directions ( expansion_result . anchors , bridges , expansion_zones ) ;
// Merge the groups with the same group id, produce surfaces by merging source overhangs with their newly expanded anchors.
std : : sort ( expansion_result . expansions . begin ( ) , expansion_result . expansions . end ( ) , [ ] ( auto & l , auto & r ) {
return l . src_id < r . src_id | | ( l . src_id = = r . src_id & & l . boundary_id < r . boundary_id ) ;
} ) ;
Surfaces out { merge_bridges ( bridges , expansion_result . expansions , closing_radius ) } ;
2024-03-29 07:37:11 +00:00
// Clip by the expanded bridges.
2024-05-10 04:20:33 +00:00
for ( ExpansionZone & expansion_zone : expansion_zones )
if ( expansion_zone . expanded_into )
expansion_zone . expolygons = diff_ex ( expansion_zone . expolygons , out ) ;
2024-03-29 07:37:11 +00:00
return out ;
}
2024-05-10 04:20:33 +00:00
Surfaces expand_merge_surfaces (
Surfaces & surfaces ,
SurfaceType surface_type ,
std : : vector < ExpansionZone > & expansion_zones ,
const float closing_radius ,
const double bridge_angle
)
2024-03-29 07:37:11 +00:00
{
using namespace Slic3r : : Algorithm ;
double thickness ;
ExPolygons src = fill_surfaces_extract_expolygons ( surfaces , { surface_type } , thickness ) ;
if ( src . empty ( ) )
return { } ;
2024-05-10 04:20:33 +00:00
unsigned processed_expolygons_count = 0 ;
std : : vector < RegionExpansion > expansions ;
for ( ExpansionZone & expansion_zone : expansion_zones ) {
std : : vector < RegionExpansion > zone_expansions = propagate_waves ( src , expansion_zone . expolygons , expansion_zone . parameters ) ;
expansion_zone . expanded_into = ! zone_expansions . empty ( ) ;
for ( RegionExpansion & expansion : zone_expansions )
expansion . boundary_id + = processed_expolygons_count ;
processed_expolygons_count + = expansion_zone . expolygons . size ( ) ;
append ( expansions , std : : move ( zone_expansions ) ) ;
2024-03-29 07:37:11 +00:00
}
std : : vector < ExPolygon > expanded = merge_expansions_into_expolygons ( std : : move ( src ) , std : : move ( expansions ) ) ;
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
expanded = closing_ex ( expanded , closing_radius ) ;
2024-05-10 04:20:33 +00:00
// Trim the zones by the expanded expolygons.
for ( ExpansionZone & expansion_zone : expansion_zones )
if ( expansion_zone . expanded_into )
expansion_zone . expolygons = diff_ex ( expansion_zone . expolygons , expanded ) ;
2024-03-29 07:37:11 +00:00
Surface templ { surface_type , { } } ;
templ . bridge_angle = bridge_angle ;
Surfaces out ;
out . reserve ( expanded . size ( ) ) ;
for ( auto & expoly : expanded )
out . emplace_back ( templ , std : : move ( expoly ) ) ;
return out ;
}
void LayerRegion : : process_external_surfaces ( const Layer * lower_layer , const Polygons * lower_layer_covered )
{
using namespace Slic3r : : Algorithm ;
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug ( " 4_process_external_surfaces-initial " ) ;
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Width of the perimeters.
float shell_width = 0 ;
float expansion_min = 0 ;
if ( int num_perimeters = this - > region ( ) . config ( ) . wall_loops ; num_perimeters > 0 ) {
Flow external_perimeter_flow = this - > flow ( frExternalPerimeter ) ;
Flow perimeter_flow = this - > flow ( frPerimeter ) ;
shell_width = 0.5f * external_perimeter_flow . scaled_width ( ) + external_perimeter_flow . scaled_spacing ( ) ;
shell_width + = perimeter_flow . scaled_spacing ( ) * ( num_perimeters - 1 ) ;
expansion_min = perimeter_flow . scaled_spacing ( ) ;
} else {
// TODO: Maybe there is better solution when printing with zero perimeters, but this works reasonably well, given the situation
shell_width = float ( SCALED_EPSILON ) ;
expansion_min = float ( SCALED_EPSILON ) ; ;
}
// Scaled expansions of the respective external surfaces.
float expansion_top = shell_width * sqrt ( 2. ) ;
float expansion_bottom = expansion_top ;
float expansion_bottom_bridge = expansion_top ;
// Expand by waves of expansion_step size (expansion_step is scaled), but with no more steps than max_nr_expansion_steps.
static constexpr const float expansion_step = scaled < float > ( 0.1 ) ;
// Don't take more than max_nr_steps for small expansion_step.
static constexpr const size_t max_nr_expansion_steps = 5 ;
// Radius (with added epsilon) to absorb empty regions emering from regularization of ensuring, viz const float narrow_ensure_vertical_wall_thickness_region_radius = 0.5f * 0.65f * min_perimeter_infill_spacing;
const float closing_radius = 0.55f * 0.65f * 1.05f * this - > flow ( frSolidInfill ) . scaled_spacing ( ) ;
// Expand the top / bottom / bridge surfaces into the shell thickness solid infills.
double layer_thickness ;
ExPolygons shells = union_ex ( fill_surfaces_extract_expolygons ( fill_surfaces . surfaces , { stInternalSolid } , layer_thickness ) ) ;
ExPolygons sparse = union_ex ( fill_surfaces_extract_expolygons ( fill_surfaces . surfaces , { stInternal } , layer_thickness ) ) ;
2024-05-10 04:20:33 +00:00
ExPolygons top_expolygons = union_ex ( fill_surfaces_extract_expolygons ( fill_surfaces . surfaces , { stTop } , layer_thickness ) ) ;
const auto expansion_params_into_sparse_infill = RegionExpansionParameters : : build ( expansion_min , expansion_step , max_nr_expansion_steps ) ;
const auto expansion_params_into_solid_infill = RegionExpansionParameters : : build ( expansion_bottom_bridge , expansion_step , max_nr_expansion_steps ) ;
std : : vector < ExpansionZone > expansion_zones {
ExpansionZone { std : : move ( shells ) , expansion_params_into_solid_infill } ,
ExpansionZone { std : : move ( sparse ) , expansion_params_into_sparse_infill } ,
ExpansionZone { std : : move ( top_expolygons ) , expansion_params_into_solid_infill } ,
} ;
2024-03-29 07:37:11 +00:00
SurfaceCollection bridges ;
{
BOOST_LOG_TRIVIAL ( trace ) < < " Processing external surface, detecting bridges. layer " < < this - > layer ( ) - > print_z ;
const double custom_angle = this - > region ( ) . config ( ) . bridge_angle . value ;
bridges . surfaces = custom_angle > 0 ?
2024-05-10 04:20:33 +00:00
expand_merge_surfaces ( fill_surfaces . surfaces , stBottomBridge , expansion_zones , closing_radius , Geometry : : deg2rad ( custom_angle ) ) :
expand_bridges_detect_orientations ( fill_surfaces . surfaces , expansion_zones , closing_radius ) ;
2024-03-29 07:37:11 +00:00
BOOST_LOG_TRIVIAL ( trace ) < < " Processing external surface, detecting bridges - done " ;
#if 0
{
static int iRun = 0 ;
bridges . export_to_svg ( debug_out_path ( " bridges-after-grouping-%d.svg " , iRun + + ) , true ) ;
}
# endif
}
2024-05-10 04:20:33 +00:00
fill_surfaces . remove_types ( { stTop } ) ;
{
Surface top_templ ( stTop , { } ) ;
top_templ . thickness = layer_thickness ;
fill_surfaces . append ( std : : move ( expansion_zones . back ( ) . expolygons ) , top_templ ) ;
}
expansion_zones . pop_back ( ) ;
expansion_zones . at ( 0 ) . parameters = RegionExpansionParameters : : build ( expansion_bottom , expansion_step , max_nr_expansion_steps ) ;
Surfaces bottoms = expand_merge_surfaces ( fill_surfaces . surfaces , stBottom , expansion_zones , closing_radius ) ;
expansion_zones . at ( 0 ) . parameters = RegionExpansionParameters : : build ( expansion_top , expansion_step , max_nr_expansion_steps ) ;
Surfaces tops = expand_merge_surfaces ( fill_surfaces . surfaces , stTop , expansion_zones , closing_radius ) ;
2024-03-29 07:37:11 +00:00
2024-05-11 03:28:19 +00:00
//expansion_zone[0]: shell , expansion_zone[1]: sparse
//apply minimu sparse infill area logic, this should also be added in bridge_over_infill
if ( ! this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) . spiral_mode & & this - > region ( ) . config ( ) . sparse_infill_density . value > 0 ) {
auto & sparse = expansion_zones [ 1 ] . expolygons ;
auto & shells = expansion_zones [ 0 ] . expolygons ;
double min_area = scale_ ( scale_ ( this - > region ( ) . config ( ) . minimum_sparse_infill_area . value ) ) ;
ExPolygons areas_to_be_solid { } ;
sparse . erase ( std : : remove_if ( sparse . begin ( ) , sparse . end ( ) , [ min_area , & areas_to_be_solid ] ( ExPolygon & expoly ) {
if ( expoly . area ( ) < = min_area ) {
areas_to_be_solid . push_back ( expoly ) ;
return true ;
}
return false ;
} ) , sparse . end ( ) ) ;
if ( ! areas_to_be_solid . empty ( ) )
shells = union_ex ( shells , areas_to_be_solid ) ;
}
2024-03-29 07:37:11 +00:00
// m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
fill_surfaces . clear ( ) ;
2024-05-10 04:20:33 +00:00
unsigned zones_expolygons_count = 0 ;
for ( const ExpansionZone & zone : expansion_zones )
zones_expolygons_count + = zone . expolygons . size ( ) ;
reserve_more ( fill_surfaces . surfaces , zones_expolygons_count + bridges . size ( ) + bottoms . size ( ) + tops . size ( ) ) ;
2024-03-29 07:37:11 +00:00
{
Surface solid_templ ( stInternalSolid , { } ) ;
solid_templ . thickness = layer_thickness ;
2024-05-10 04:20:33 +00:00
fill_surfaces . append ( std : : move ( expansion_zones [ 0 ] . expolygons ) , solid_templ ) ;
2024-03-29 07:37:11 +00:00
}
{
Surface sparse_templ ( stInternal , { } ) ;
sparse_templ . thickness = layer_thickness ;
2024-05-10 04:20:33 +00:00
fill_surfaces . append ( std : : move ( expansion_zones [ 1 ] . expolygons ) , sparse_templ ) ;
2024-03-29 07:37:11 +00:00
}
fill_surfaces . append ( std : : move ( bridges . surfaces ) ) ;
fill_surfaces . append ( std : : move ( bottoms ) ) ;
fill_surfaces . append ( std : : move ( tops ) ) ;
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug ( " 4_process_external_surfaces-final " ) ;
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
2024-05-10 04:20:33 +00:00
2024-03-29 07:37:11 +00:00
# else
2022-07-15 15:37:19 +00:00
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
# define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
void LayerRegion : : process_external_surfaces ( const Layer * lower_layer , const Polygons * lower_layer_covered )
{
const bool has_infill = this - > region ( ) . config ( ) . sparse_infill_density . value > 0. ;
2022-09-19 10:44:04 +00:00
//BBS
auto nozzle_diameter = this - > region ( ) . nozzle_dmr_avg ( this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) ) ;
2022-10-11 14:12:58 +00:00
const float margin = float ( scale_ ( EXTERNAL_INFILL_MARGIN ) ) ;
const float bridge_margin = std : : min ( float ( scale_ ( BRIDGE_INFILL_MARGIN ) ) , float ( scale_ ( nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4 ) ) ) ;
2022-07-15 15:37:19 +00:00
2022-07-22 09:46:10 +00:00
// BBS
const PrintObjectConfig & object_config = this - > layer ( ) - > object ( ) - > config ( ) ;
2022-07-15 15:37:19 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug ( " 3_process_external_surfaces-initial " ) ;
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// 1) Collect bottom and bridge surfaces, each of them grown by a fixed 3mm offset
// for better anchoring.
// Bottom surfaces, grown.
Surfaces bottom ;
// Bridge surfaces, initialy not grown.
Surfaces bridges ;
// Top surfaces, grown.
Surfaces top ;
// Internal surfaces, not grown.
Surfaces internal ;
// Areas, where an infill of various types (top, bottom, bottom bride, sparse, void) could be placed.
Polygons fill_boundaries = to_polygons ( this - > fill_expolygons ) ;
Polygons lower_layer_covered_tmp ;
// Collect top surfaces and internal surfaces.
// Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
// This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
{
// Voids are sparse infills if infill rate is zero.
Polygons voids ;
2022-12-12 02:32:26 +00:00
double max_grid_area = - 1 ;
if ( this - > layer ( ) - > lower_layer ! = nullptr )
max_grid_area = this - > layer ( ) - > lower_layer - > get_sparse_infill_max_void_area ( ) ;
2022-07-15 15:37:19 +00:00
for ( const Surface & surface : this - > fill_surfaces . surfaces ) {
if ( surface . is_top ( ) ) {
// Collect the top surfaces, inflate them and trim them by the bottom surfaces.
// This gives the priority to bottom surfaces.
2022-12-12 02:32:26 +00:00
if ( max_grid_area < 0 | | surface . expolygon . area ( ) < max_grid_area )
surfaces_append ( top , offset_ex ( surface . expolygon , margin , EXTERNAL_SURFACES_OFFSET_PARAMETERS ) , surface ) ;
else
//BBS: Don't need to expand too much in this situation. Expand 3mm to eliminate hole and 1mm for contour
surfaces_append ( top , intersection_ex ( offset ( surface . expolygon . contour , margin / 3.0 , EXTERNAL_SURFACES_OFFSET_PARAMETERS ) ,
offset_ex ( surface . expolygon , margin , EXTERNAL_SURFACES_OFFSET_PARAMETERS ) ) , surface ) ;
2022-07-15 15:37:19 +00:00
} else if ( surface . surface_type = = stBottom | | ( surface . surface_type = = stBottomBridge & & lower_layer = = nullptr ) ) {
// Grown by 3mm.
surfaces_append ( bottom , offset_ex ( surface . expolygon , margin , EXTERNAL_SURFACES_OFFSET_PARAMETERS ) , surface ) ;
} else if ( surface . surface_type = = stBottomBridge ) {
if ( ! surface . empty ( ) )
bridges . emplace_back ( surface ) ;
}
if ( surface . is_internal ( ) ) {
assert ( surface . surface_type = = stInternal | | surface . surface_type = = stInternalSolid ) ;
if ( ! has_infill & & lower_layer ! = nullptr )
polygons_append ( voids , surface . expolygon ) ;
internal . emplace_back ( std : : move ( surface ) ) ;
}
}
if ( ! has_infill & & lower_layer ! = nullptr & & ! voids . empty ( ) ) {
// Remove voids from fill_boundaries, that are not supported by the layer below.
if ( lower_layer_covered = = nullptr ) {
lower_layer_covered = & lower_layer_covered_tmp ;
lower_layer_covered_tmp = to_polygons ( lower_layer - > lslices ) ;
}
if ( ! lower_layer_covered - > empty ( ) )
voids = diff ( voids , * lower_layer_covered ) ;
fill_boundaries = diff ( fill_boundaries , voids ) ;
}
}
#if 0
{
static int iRun = 0 ;
bridges . export_to_svg ( debug_out_path ( " bridges-before-grouping-%d.svg " , iRun + + ) , true ) ;
}
# endif
if ( bridges . empty ( ) )
{
fill_boundaries = union_safety_offset ( fill_boundaries ) ;
} else
{
// 1) Calculate the inflated bridge regions, each constrained to its island.
ExPolygons fill_boundaries_ex = union_safety_offset_ex ( fill_boundaries ) ;
std : : vector < Polygons > bridges_grown ;
std : : vector < BoundingBox > bridge_bboxes ;
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0 ;
SVG svg ( debug_out_path ( " 3_process_external_surfaces-fill_regions-%d.svg " , iRun + + ) . c_str ( ) , get_extents ( fill_boundaries_ex ) ) ;
svg . draw ( fill_boundaries_ex ) ;
2024-11-22 10:32:47 +00:00
svg . draw_outline ( fill_boundaries_ex , " black " , " blue " , scale_ ( 0.05 ) ) ;
2022-07-15 15:37:19 +00:00
svg . Close ( ) ;
}
// export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2024-11-22 10:32:47 +00:00
2022-07-15 15:37:19 +00:00
{
// Bridge expolygons, grown, to be tested for intersection with other bridge regions.
std : : vector < BoundingBox > fill_boundaries_ex_bboxes = get_extents_vector ( fill_boundaries_ex ) ;
bridges_grown . reserve ( bridges . size ( ) ) ;
bridge_bboxes . reserve ( bridges . size ( ) ) ;
for ( size_t i = 0 ; i < bridges . size ( ) ; + + i ) {
// Find the island of this bridge.
const Point pt = bridges [ i ] . expolygon . contour . points . front ( ) ;
int idx_island = - 1 ;
for ( int j = 0 ; j < int ( fill_boundaries_ex . size ( ) ) ; + + j )
2024-11-22 10:32:47 +00:00
if ( fill_boundaries_ex_bboxes [ j ] . contains ( pt ) & &
2022-07-15 15:37:19 +00:00
fill_boundaries_ex [ j ] . contains ( pt ) ) {
idx_island = j ;
break ;
}
// Grown by 3mm.
2022-11-28 09:22:26 +00:00
//BBS: eliminate too narrow area to avoid generating bridge on top layer when wall loop is 1
//Polygons polys = offset(bridges[i].expolygon, bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
Polygons polys = offset2 ( { bridges [ i ] . expolygon } , - scale_ ( nozzle_diameter * 0.1 ) , bridge_margin , EXTERNAL_SURFACES_OFFSET_PARAMETERS ) ;
2022-07-15 15:37:19 +00:00
if ( idx_island = = - 1 ) {
BOOST_LOG_TRIVIAL ( trace ) < < " Bridge did not fall into the source region! " ;
} else {
// Found an island, to which this bridge region belongs. Trim it,
polys = intersection ( polys , fill_boundaries_ex [ idx_island ] ) ;
}
bridge_bboxes . push_back ( get_extents ( polys ) ) ;
bridges_grown . push_back ( std : : move ( polys ) ) ;
}
}
// 2) Group the bridge surfaces by overlaps.
std : : vector < size_t > bridge_group ( bridges . size ( ) , ( size_t ) - 1 ) ;
2024-11-22 10:32:47 +00:00
size_t n_groups = 0 ;
2022-07-15 15:37:19 +00:00
for ( size_t i = 0 ; i < bridges . size ( ) ; + + i ) {
// A grup id for this bridge.
size_t group_id = ( bridge_group [ i ] = = size_t ( - 1 ) ) ? ( n_groups + + ) : bridge_group [ i ] ;
bridge_group [ i ] = group_id ;
// For all possibly overlaping bridges:
for ( size_t j = i + 1 ; j < bridges . size ( ) ; + + j ) {
if ( ! bridge_bboxes [ i ] . overlap ( bridge_bboxes [ j ] ) )
continue ;
if ( intersection ( bridges_grown [ i ] , bridges_grown [ j ] ) . empty ( ) )
continue ;
// The two bridge regions intersect. Give them the same group id.
if ( bridge_group [ j ] ! = size_t ( - 1 ) ) {
// The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group [ j ] ;
for ( size_t k = 0 ; k < j ; + + k )
if ( bridge_group [ k ] = = group_id )
bridge_group [ k ] = group_id_new ;
group_id = group_id_new ;
}
bridge_group [ j ] = group_id ;
}
}
// 3) Merge the groups with the same group id, detect bridges.
{
BOOST_LOG_TRIVIAL ( trace ) < < " Processing external surface, detecting bridges. layer " < < this - > layer ( ) - > print_z < < " , bridge groups: " < < n_groups ;
for ( size_t group_id = 0 ; group_id < n_groups ; + + group_id ) {
size_t n_bridges_merged = 0 ;
size_t idx_last = ( size_t ) - 1 ;
for ( size_t i = 0 ; i < bridges . size ( ) ; + + i ) {
if ( bridge_group [ i ] = = group_id ) {
+ + n_bridges_merged ;
idx_last = i ;
}
}
if ( n_bridges_merged = = 0 )
// This group has no regions assigned as these were moved into another group.
continue ;
// Collect the initial ungrown regions and the grown polygons.
ExPolygons initial ;
Polygons grown ;
for ( size_t i = 0 ; i < bridges . size ( ) ; + + i ) {
if ( bridge_group [ i ] ! = group_id )
continue ;
initial . push_back ( std : : move ( bridges [ i ] . expolygon ) ) ;
polygons_append ( grown , bridges_grown [ i ] ) ;
}
// detect bridge direction before merging grown surfaces otherwise adjacent bridges
// would get merged into a single one while they need different directions
// also, supply the original expolygon instead of the grown one, because in case
// of very thin (but still working) anchors, the grown expolygon would go beyond them
2023-03-10 12:44:07 +00:00
double custom_angle = Geometry : : deg2rad ( this - > region ( ) . config ( ) . bridge_angle . value ) ;
if ( custom_angle > 0.0 ) {
bridges [ idx_last ] . bridge_angle = custom_angle ;
} else {
auto [ bridging_dir , unsupported_dist ] = detect_bridging_direction ( to_polygons ( initial ) , to_polygons ( lower_layer - > lslices ) ) ;
bridges [ idx_last ] . bridge_angle = PI + std : : atan2 ( bridging_dir . y ( ) , bridging_dir . x ( ) ) ;
}
/*
2022-07-22 09:46:10 +00:00
BridgeDetector bd ( initial , lower_layer - > lslices , this - > bridging_flow ( frInfill , object_config . thick_bridges ) . scaled_width ( ) ) ;
2022-07-15 15:37:19 +00:00
# ifdef SLIC3R_DEBUG
printf ( " Processing bridge at layer %zu: \n " , this - > layer ( ) - > id ( ) ) ;
# endif
2022-10-26 07:03:16 +00:00
double custom_angle = Geometry : : deg2rad ( this - > region ( ) . config ( ) . bridge_angle . value ) ;
2022-07-15 15:37:19 +00:00
if ( bd . detect_angle ( custom_angle ) ) {
bridges [ idx_last ] . bridge_angle = bd . angle ;
if ( this - > layer ( ) - > object ( ) - > has_support ( ) ) {
// polygons_append(this->bridged, bd.coverage());
append ( this - > unsupported_bridge_edges , bd . unsupported_edges ( ) ) ;
}
} else if ( custom_angle > 0 ) {
// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
// using a bridging flow, therefore it makes sense to respect the custom bridging direction.
bridges [ idx_last ] . bridge_angle = custom_angle ;
}
2023-03-10 12:44:07 +00:00
*/
2022-07-15 15:37:19 +00:00
// without safety offset, artifacts are generated (GH #2494)
surfaces_append ( bottom , union_safety_offset_ex ( grown ) , bridges [ idx_last ] ) ;
}
fill_boundaries = to_polygons ( fill_boundaries_ex ) ;
BOOST_LOG_TRIVIAL ( trace ) < < " Processing external surface, detecting bridges - done " ;
}
#if 0
{
static int iRun = 0 ;
bridges . export_to_svg ( debug_out_path ( " bridges-after-grouping-%d.svg " , iRun + + ) , true ) ;
}
# endif
}
Surfaces new_surfaces ;
{
// Intersect the grown surfaces with the actual fill boundaries.
Polygons bottom_polygons = to_polygons ( bottom ) ;
2024-03-04 07:45:28 +00:00
// Merge top and bottom in a single collection.
surfaces_append ( top , std : : move ( bottom ) ) ;
2022-07-15 15:37:19 +00:00
for ( size_t i = 0 ; i < top . size ( ) ; + + i ) {
Surface & s1 = top [ i ] ;
if ( s1 . empty ( ) )
continue ;
Polygons polys ;
polygons_append ( polys , to_polygons ( std : : move ( s1 ) ) ) ;
for ( size_t j = i + 1 ; j < top . size ( ) ; + + j ) {
Surface & s2 = top [ j ] ;
if ( ! s2 . empty ( ) & & surfaces_could_merge ( s1 , s2 ) ) {
polygons_append ( polys , to_polygons ( std : : move ( s2 ) ) ) ;
s2 . clear ( ) ;
}
}
if ( s1 . is_top ( ) )
// Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
polys = diff ( polys , bottom_polygons ) ;
surfaces_append (
new_surfaces ,
// Don't use a safety offset as fill_boundaries were already united using the safety offset.
intersection_ex ( polys , fill_boundaries ) ,
s1 ) ;
}
}
2024-11-22 10:32:47 +00:00
2022-07-15 15:37:19 +00:00
// Subtract the new top surfaces from the other non-top surfaces and re-add them.
Polygons new_polygons = to_polygons ( new_surfaces ) ;
for ( size_t i = 0 ; i < internal . size ( ) ; + + i ) {
Surface & s1 = internal [ i ] ;
if ( s1 . empty ( ) )
continue ;
Polygons polys ;
polygons_append ( polys , to_polygons ( std : : move ( s1 ) ) ) ;
for ( size_t j = i + 1 ; j < internal . size ( ) ; + + j ) {
Surface & s2 = internal [ j ] ;
if ( ! s2 . empty ( ) & & surfaces_could_merge ( s1 , s2 ) ) {
polygons_append ( polys , to_polygons ( std : : move ( s2 ) ) ) ;
s2 . clear ( ) ;
}
}
ExPolygons new_expolys = diff_ex ( polys , new_polygons ) ;
polygons_append ( new_polygons , to_polygons ( new_expolys ) ) ;
surfaces_append ( new_surfaces , std : : move ( new_expolys ) , s1 ) ;
}
2024-11-22 10:32:47 +00:00
2022-07-15 15:37:19 +00:00
this - > fill_surfaces . surfaces = std : : move ( new_surfaces ) ;
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug ( " 3_process_external_surfaces-final " ) ;
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
2024-03-29 07:37:11 +00:00
# endif
2022-07-15 15:37:19 +00:00
void LayerRegion : : prepare_fill_surfaces ( )
{
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_slices_to_svg_debug ( " 2_prepare_fill_surfaces-initial " ) ;
export_region_fill_surfaces_to_svg_debug ( " 2_prepare_fill_surfaces-initial " ) ;
2024-11-22 10:32:47 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2022-07-15 15:37:19 +00:00
/* Note: in order to make the psPrepareInfill step idempotent, we should never
alter fill_surfaces boundaries on which our idempotency relies since that ' s
the only meaningful information returned by psPerimeters . */
2024-11-22 10:32:47 +00:00
2022-07-15 15:37:19 +00:00
bool spiral_mode = this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) . spiral_mode ;
2024-03-04 07:45:28 +00:00
#if 0
2022-07-15 15:37:19 +00:00
// if no solid layers are requested, turn top/bottom surfaces to internal
if ( ! spiral_mode & & this - > region ( ) . config ( ) . top_shell_layers = = 0 ) {
for ( Surface & surface : this - > fill_surfaces . surfaces )
if ( surface . is_top ( ) )
//BBS
//surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
surface . surface_type = PrintObject : : infill_only_where_needed ? stInternalVoid : stInternal ;
}
if ( this - > region ( ) . config ( ) . bottom_shell_layers = = 0 ) {
for ( Surface & surface : this - > fill_surfaces . surfaces )
if ( surface . is_bottom ( ) ) // (surface.surface_type == stBottom)
surface . surface_type = stInternal ;
}
2024-03-04 07:45:28 +00:00
# endif
2022-07-15 15:37:19 +00:00
// turn too small internal regions into solid regions according to the user setting
if ( ! spiral_mode & & this - > region ( ) . config ( ) . sparse_infill_density . value > 0 ) {
// scaling an area requires two calls!
double min_area = scale_ ( scale_ ( this - > region ( ) . config ( ) . minimum_sparse_infill_area . value ) ) ;
for ( Surface & surface : this - > fill_surfaces . surfaces )
if ( surface . surface_type = = stInternal & & surface . area ( ) < = min_area )
surface . surface_type = stInternalSolid ;
}
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_slices_to_svg_debug ( " 2_prepare_fill_surfaces-final " ) ;
export_region_fill_surfaces_to_svg_debug ( " 2_prepare_fill_surfaces-final " ) ;
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
double LayerRegion : : infill_area_threshold ( ) const
{
double ss = this - > flow ( frSolidInfill ) . scaled_spacing ( ) ;
return ss * ss ;
}
void LayerRegion : : trim_surfaces ( const Polygons & trimming_polygons )
{
# ifndef NDEBUG
for ( const Surface & surface : this - > slices . surfaces )
assert ( surface . surface_type = = stInternal ) ;
# endif /* NDEBUG */
this - > slices . set ( intersection_ex ( this - > slices . surfaces , trimming_polygons ) , stInternal ) ;
}
void LayerRegion : : elephant_foot_compensation_step ( const float elephant_foot_compensation_perimeter_step , const Polygons & trimming_polygons )
{
# ifndef NDEBUG
for ( const Surface & surface : this - > slices . surfaces )
assert ( surface . surface_type = = stInternal ) ;
# endif /* NDEBUG */
Polygons tmp = intersection ( this - > slices . surfaces , trimming_polygons ) ;
append ( tmp , diff ( this - > slices . surfaces , opening ( this - > slices . surfaces , elephant_foot_compensation_perimeter_step ) ) ) ;
this - > slices . set ( union_ex ( tmp ) , stInternal ) ;
}
void LayerRegion : : export_region_slices_to_svg ( const char * path ) const
{
BoundingBox bbox ;
for ( Surfaces : : const_iterator surface = this - > slices . surfaces . begin ( ) ; surface ! = this - > slices . surfaces . end ( ) ; + + surface )
bbox . merge ( get_extents ( surface - > expolygon ) ) ;
Point legend_size = export_surface_type_legend_to_svg_box_size ( ) ;
Point legend_pos ( bbox . min ( 0 ) , bbox . max ( 1 ) ) ;
bbox . merge ( Point ( std : : max ( bbox . min ( 0 ) + legend_size ( 0 ) , bbox . max ( 0 ) ) , bbox . max ( 1 ) + legend_size ( 1 ) ) ) ;
SVG svg ( path , bbox ) ;
const float transparency = 0.5f ;
for ( Surfaces : : const_iterator surface = this - > slices . surfaces . begin ( ) ; surface ! = this - > slices . surfaces . end ( ) ; + + surface )
svg . draw ( surface - > expolygon , surface_type_to_color_name ( surface - > surface_type ) , transparency ) ;
for ( Surfaces : : const_iterator surface = this - > fill_surfaces . surfaces . begin ( ) ; surface ! = this - > fill_surfaces . surfaces . end ( ) ; + + surface )
svg . draw ( surface - > expolygon . lines ( ) , surface_type_to_color_name ( surface - > surface_type ) ) ;
export_surface_type_legend_to_svg ( svg , legend_pos ) ;
svg . Close ( ) ;
}
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void LayerRegion : : export_region_slices_to_svg_debug ( const char * name ) const
{
static std : : map < std : : string , size_t > idx_map ;
size_t & idx = idx_map [ name ] ;
this - > export_region_slices_to_svg ( debug_out_path ( " LayerRegion-slices-%s-%d.svg " , name , idx + + ) . c_str ( ) ) ;
}
void LayerRegion : : export_region_fill_surfaces_to_svg ( const char * path ) const
{
BoundingBox bbox ;
for ( Surfaces : : const_iterator surface = this - > fill_surfaces . surfaces . begin ( ) ; surface ! = this - > fill_surfaces . surfaces . end ( ) ; + + surface )
bbox . merge ( get_extents ( surface - > expolygon ) ) ;
Point legend_size = export_surface_type_legend_to_svg_box_size ( ) ;
Point legend_pos ( bbox . min ( 0 ) , bbox . max ( 1 ) ) ;
bbox . merge ( Point ( std : : max ( bbox . min ( 0 ) + legend_size ( 0 ) , bbox . max ( 0 ) ) , bbox . max ( 1 ) + legend_size ( 1 ) ) ) ;
SVG svg ( path , bbox ) ;
const float transparency = 0.5f ;
for ( const Surface & surface : this - > fill_surfaces . surfaces ) {
svg . draw ( surface . expolygon , surface_type_to_color_name ( surface . surface_type ) , transparency ) ;
2024-11-22 10:32:47 +00:00
svg . draw_outline ( surface . expolygon , " black " , " blue " , scale_ ( 0.05 ) ) ;
2022-07-15 15:37:19 +00:00
}
export_surface_type_legend_to_svg ( svg , legend_pos ) ;
svg . Close ( ) ;
}
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void LayerRegion : : export_region_fill_surfaces_to_svg_debug ( const char * name ) const
{
static std : : map < std : : string , size_t > idx_map ;
size_t & idx = idx_map [ name ] ;
this - > export_region_fill_surfaces_to_svg ( debug_out_path ( " LayerRegion-fill_surfaces-%s-%d.svg " , name , idx + + ) . c_str ( ) ) ;
}
void LayerRegion : : simplify_entity_collection ( ExtrusionEntityCollection * entity_collection )
{
for ( size_t i = 0 ; i < entity_collection - > entities . size ( ) ; i + + ) {
if ( ExtrusionEntityCollection * collection = dynamic_cast < ExtrusionEntityCollection * > ( entity_collection - > entities [ i ] ) )
this - > simplify_entity_collection ( collection ) ;
else if ( ExtrusionPath * path = dynamic_cast < ExtrusionPath * > ( entity_collection - > entities [ i ] ) )
this - > simplify_path ( path ) ;
else if ( ExtrusionMultiPath * multipath = dynamic_cast < ExtrusionMultiPath * > ( entity_collection - > entities [ i ] ) )
this - > simplify_multi_path ( multipath ) ;
else if ( ExtrusionLoop * loop = dynamic_cast < ExtrusionLoop * > ( entity_collection - > entities [ i ] ) )
this - > simplify_loop ( loop ) ;
else
throw Slic3r : : InvalidArgument ( " Invalid extrusion entity supplied to simplify_entity_collection() " ) ;
}
}
void LayerRegion : : simplify_path ( ExtrusionPath * path )
{
const auto print_config = this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) ;
const bool spiral_mode = print_config . spiral_mode ;
const bool enable_arc_fitting = print_config . enable_arc_fitting ;
const auto scaled_resolution = scaled < double > ( print_config . resolution . value ) ;
if ( enable_arc_fitting & &
! spiral_mode ) {
if ( path - > role ( ) = = erInternalInfill )
path - > simplify_by_fitting_arc ( SCALED_SPARSE_INFILL_RESOLUTION ) ;
else
path - > simplify_by_fitting_arc ( scaled_resolution ) ;
} else {
path - > simplify ( scaled_resolution ) ;
}
}
void LayerRegion : : simplify_multi_path ( ExtrusionMultiPath * multipath )
{
const auto print_config = this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) ;
const bool spiral_mode = print_config . spiral_mode ;
const bool enable_arc_fitting = print_config . enable_arc_fitting ;
const auto scaled_resolution = scaled < double > ( print_config . resolution . value ) ;
for ( size_t i = 0 ; i < multipath - > paths . size ( ) ; + + i ) {
if ( enable_arc_fitting & &
! spiral_mode ) {
if ( multipath - > paths [ i ] . role ( ) = = erInternalInfill )
multipath - > paths [ i ] . simplify_by_fitting_arc ( SCALED_SPARSE_INFILL_RESOLUTION ) ;
else
multipath - > paths [ i ] . simplify_by_fitting_arc ( scaled_resolution ) ;
} else {
multipath - > paths [ i ] . simplify ( scaled_resolution ) ;
}
}
}
void LayerRegion : : simplify_loop ( ExtrusionLoop * loop )
{
const auto print_config = this - > layer ( ) - > object ( ) - > print ( ) - > config ( ) ;
const bool spiral_mode = print_config . spiral_mode ;
const bool enable_arc_fitting = print_config . enable_arc_fitting ;
const auto scaled_resolution = scaled < double > ( print_config . resolution . value ) ;
for ( size_t i = 0 ; i < loop - > paths . size ( ) ; + + i ) {
if ( enable_arc_fitting & &
! spiral_mode ) {
if ( loop - > paths [ i ] . role ( ) = = erInternalInfill )
loop - > paths [ i ] . simplify_by_fitting_arc ( SCALED_SPARSE_INFILL_RESOLUTION ) ;
else
loop - > paths [ i ] . simplify_by_fitting_arc ( scaled_resolution ) ;
} else {
loop - > paths [ i ] . simplify ( scaled_resolution ) ;
}
}
}
}