2016-10-13 16:00:22 +02:00
#include "ClipperUtils.hpp"
#include "ExtrusionEntityCollection.hpp"
2016-10-16 16:30:56 +02:00
#include "Layer.hpp"
2016-11-23 15:51:47 +01:00
#include "Print.hpp"
2016-10-16 16:30:56 +02:00
#include "SupportMaterial.hpp"
#include "Fill/FillBase.hpp"
2017-01-25 18:33:05 +01:00
#include "Geometry.hpp"
2021-02-23 14:43:47 +01:00
#include "Point.hpp"
2021-03-23 11:06:31 +01:00
#include "MutablePolygon.hpp"
2016-10-16 16:30:56 +02:00
2016-10-13 16:00:22 +02:00
#include <cmath>
2016-10-17 09:27:20 +02:00
#include <memory>
2016-11-24 13:44:51 +01:00
#include <boost/log/trivial.hpp>
2021-03-10 12:14:45 +01:00
#include <boost/container/static_vector.hpp>
2016-10-13 16:00:22 +02:00
2017-03-22 15:35:50 +01:00
#include <tbb/parallel_for.h>
#include <tbb/atomic.h>
#include <tbb/spin_mutex.h>
#include <tbb/task_group.h>
2021-02-19 18:50:44 +01:00
#define SUPPORT_USE_AGG_RASTERIZER
#ifdef SUPPORT_USE_AGG_RASTERIZER
#include <agg/agg_pixfmt_gray.h>
#include <agg/agg_renderer_scanline.h>
#include <agg/agg_scanline_p.h>
#include <agg/agg_rasterizer_scanline_aa.h>
#include <agg/agg_path_storage.h>
#include "PNGReadWrite.hpp"
#else
#include "EdgeGrid.hpp"
#endif // SUPPORT_USE_AGG_RASTERIZER
2016-11-24 11:29:31 +01:00
// #define SLIC3R_DEBUG
2016-11-23 15:51:47 +01:00
// Make assert active if SLIC3R_DEBUG
2017-03-28 13:46:31 +02:00
#ifdef SLIC3R_DEBUG
2017-03-22 15:35:50 +01:00
#define DEBUG
#define _DEBUG
2016-11-23 15:51:47 +01:00
#undef NDEBUG
#include "SVG.hpp"
#endif
2016-11-02 10:47:00 +01:00
2017-01-16 10:56:40 +01:00
// #undef NDEBUG
2016-12-08 19:02:16 +01:00
#include <cassert>
2016-10-13 16:00:22 +02:00
namespace Slic3r {
2021-02-24 09:22:31 +01:00
// how much we extend support around the actual contact area
//FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5
2016-10-16 16:30:56 +02:00
// Increment used to reach MARGIN in steps to avoid trespassing thin objects
#define NUM_MARGIN_STEPS 3
// Dimensions of a tree-like structure to save material
2016-10-13 16:00:22 +02:00
#define PILLAR_SIZE (2.5)
#define PILLAR_SPACING 10
2017-01-05 09:14:59 +01:00
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
2017-01-11 14:37:53 +01:00
#ifdef SLIC3R_DEBUG
2017-01-11 13:42:41 +01:00
const char * support_surface_type_to_color_name ( const PrintObjectSupportMaterial :: SupporLayerType surface_type )
{
switch ( surface_type ) {
case PrintObjectSupportMaterial :: sltTopContact : return "rgb(255,0,0)" ; // "red";
case PrintObjectSupportMaterial :: sltTopInterface : return "rgb(0,255,0)" ; // "green";
case PrintObjectSupportMaterial :: sltBase : return "rgb(0,0,255)" ; // "blue";
case PrintObjectSupportMaterial :: sltBottomInterface : return "rgb(255,255,128)" ; // yellow
case PrintObjectSupportMaterial :: sltBottomContact : return "rgb(255,0,255)" ; // magenta
case PrintObjectSupportMaterial :: sltRaftInterface : return "rgb(0,255,255)" ;
case PrintObjectSupportMaterial :: sltRaftBase : return "rgb(128,128,128)" ;
case PrintObjectSupportMaterial :: sltUnknown : return "rgb(128,0,0)" ; // maroon
default : return "rgb(64,64,64)" ;
};
}
Point export_support_surface_type_legend_to_svg_box_size ()
{
return Point ( scale_ ( 1. + 10. * 8. ), scale_ ( 3. ));
}
void export_support_surface_type_legend_to_svg ( SVG & svg , const Point & pos )
{
// 1st row
2018-08-17 15:53:43 +02:00
coord_t pos_x0 = pos ( 0 ) + scale_ ( 1. );
2017-01-11 13:42:41 +01:00
coord_t pos_x = pos_x0 ;
2018-08-17 15:53:43 +02:00
coord_t pos_y = pos ( 1 ) + scale_ ( 1.5 );
2017-01-11 13:42:41 +01:00
coord_t step_x = scale_ ( 10. );
svg . draw_legend ( Point ( pos_x , pos_y ), "top contact" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltTopContact ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "top iface" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltTopInterface ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "base" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltBase ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "bottom iface" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltBottomInterface ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "bottom contact" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltBottomContact ));
// 2nd row
pos_x = pos_x0 ;
2018-08-17 15:53:43 +02:00
pos_y = pos ( 1 ) + scale_ ( 2.8 );
2017-01-11 13:42:41 +01:00
svg . draw_legend ( Point ( pos_x , pos_y ), "raft interface" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltRaftInterface ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "raft base" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltRaftBase ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "unknown" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltUnknown ));
pos_x += step_x ;
svg . draw_legend ( Point ( pos_x , pos_y ), "intermediate" , support_surface_type_to_color_name ( PrintObjectSupportMaterial :: sltIntermediate ));
}
void export_print_z_polygons_to_svg ( const char * path , PrintObjectSupportMaterial :: MyLayer ** const layers , size_t n_layers )
{
BoundingBox bbox ;
for ( int i = 0 ; i < n_layers ; ++ i )
bbox . merge ( get_extents ( layers [ i ] -> polygons ));
Point legend_size = export_support_surface_type_legend_to_svg_box_size ();
2018-08-17 15:53:43 +02:00
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 )));
2017-01-11 13:42:41 +01:00
SVG svg ( path , bbox );
const float transparency = 0.5f ;
for ( int i = 0 ; i < n_layers ; ++ i )
svg . draw ( union_ex ( layers [ i ] -> polygons ), support_surface_type_to_color_name ( layers [ i ] -> layer_type ), transparency );
for ( int i = 0 ; i < n_layers ; ++ i )
2017-01-19 13:47:06 +01:00
svg . draw ( to_polylines ( layers [ i ] -> polygons ), support_surface_type_to_color_name ( layers [ i ] -> layer_type ));
2017-01-11 13:42:41 +01:00
export_support_surface_type_legend_to_svg ( svg , legend_pos );
svg . Close ();
}
2017-01-20 15:21:05 +01:00
void export_print_z_polygons_and_extrusions_to_svg (
const char * path ,
PrintObjectSupportMaterial :: MyLayer ** const layers ,
size_t n_layers ,
SupportLayer & support_layer )
{
BoundingBox bbox ;
for ( int i = 0 ; i < n_layers ; ++ i )
bbox . merge ( get_extents ( layers [ i ] -> polygons ));
Point legend_size = export_support_surface_type_legend_to_svg_box_size ();
2018-08-17 15:53:43 +02:00
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 )));
2017-01-20 15:21:05 +01:00
SVG svg ( path , bbox );
const float transparency = 0.5f ;
for ( int i = 0 ; i < n_layers ; ++ i )
svg . draw ( union_ex ( layers [ i ] -> polygons ), support_surface_type_to_color_name ( layers [ i ] -> layer_type ), transparency );
for ( int i = 0 ; i < n_layers ; ++ i )
svg . draw ( to_polylines ( layers [ i ] -> polygons ), support_surface_type_to_color_name ( layers [ i ] -> layer_type ));
Polygons polygons_support , polygons_interface ;
2021-02-19 18:50:44 +01:00
support_layer . support_fills . polygons_covered_by_width ( polygons_support , float ( SCALED_EPSILON ));
2017-04-07 17:44:51 +02:00
// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON);
2017-01-20 15:21:05 +01:00
svg . draw ( union_ex ( polygons_support ), "brown" );
svg . draw ( union_ex ( polygons_interface ), "black" );
export_support_surface_type_legend_to_svg ( svg , legend_pos );
svg . Close ();
}
2017-01-11 14:37:53 +01:00
#endif /* SLIC3R_DEBUG */
2017-01-11 13:42:41 +01:00
2021-02-19 18:50:44 +01:00
#ifdef SUPPORT_USE_AGG_RASTERIZER
static std :: vector < unsigned char > rasterize_polygons ( const Vec2i & grid_size , const double pixel_size , const Point & left_bottom , const Polygons & polygons )
{
std :: vector < unsigned char > data ( grid_size . x () * grid_size . y ());
agg :: rendering_buffer rendering_buffer ( data . data (), unsigned ( grid_size . x ()), unsigned ( grid_size . y ()), grid_size . x ());
agg :: pixfmt_gray8 pixel_renderer ( rendering_buffer );
agg :: renderer_base < agg :: pixfmt_gray8 > raw_renderer ( pixel_renderer );
agg :: renderer_scanline_aa_solid < agg :: renderer_base < agg :: pixfmt_gray8 >> renderer ( raw_renderer );
renderer . color ( agg :: pixfmt_gray8 :: color_type ( 255 ));
raw_renderer . clear ( agg :: pixfmt_gray8 :: color_type ( 0 ));
agg :: scanline_p8 scanline ;
agg :: rasterizer_scanline_aa <> rasterizer ;
auto convert_pt = [ left_bottom , pixel_size ]( const Point & pt ) {
return Vec2d (( pt . x () - left_bottom . x ()) / pixel_size , ( pt . y () - left_bottom . y ()) / pixel_size );
};
rasterizer . reset ();
for ( const Polygon & polygon : polygons ) {
agg :: path_storage path ;
auto it = polygon . points . begin ();
Vec2d pt_front = convert_pt ( * it );
path . move_to ( pt_front . x (), pt_front . y ());
while ( ++ it != polygon . points . end ()) {
Vec2d pt = convert_pt ( * it );
path . line_to ( pt . x (), pt . y ());
}
path . line_to ( pt_front . x (), pt_front . y ());
rasterizer . add_path ( std :: move ( path ));
}
agg :: render_scanlines ( rasterizer , scanline , renderer );
return data ;
}
// Grid has to have the boundary pixels unset.
static Polygons contours_simplified ( const Vec2i & grid_size , const double pixel_size , Point left_bottom , const std :: vector < unsigned char > & grid , coord_t offset , bool fill_holes )
{
assert ( std :: abs ( 2 * offset ) < pixel_size - 10 );
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
2021-02-19 19:00:43 +01:00
std :: vector < unsigned char > cell_inside_data ;
const std :: vector < unsigned char > & cell_inside = fill_holes ? cell_inside_data : grid ;
2021-02-19 18:50:44 +01:00
if ( fill_holes ) {
cell_inside_data = grid ;
for ( int r = 1 ; r + 1 < grid_size . y (); ++ r ) {
for ( int c = 1 ; c + 1 < grid_size . x (); ++ c ) {
int addr = r * grid_size . x () + c ;
if (( grid [ addr - 1 ] != 0 && grid [ addr + 1 ] != 0 ) ||
( grid [ addr - grid_size . x ()] != 0 && grid [ addr + grid_size . x ()] != 0 ))
cell_inside_data [ addr ] = true ;
}
}
}
// 1) Collect the lines.
std :: vector < Line > lines ;
std :: vector < std :: pair < Point , int >> start_point_to_line_idx ;
for ( int r = 1 ; r < grid_size . y (); ++ r ) {
for ( int c = 1 ; c < grid_size . x (); ++ c ) {
int addr = r * grid_size . x () + c ;
bool left = cell_inside [ addr - 1 ] != 0 ;
bool top = cell_inside [ addr - grid_size . x ()] != 0 ;
bool current = cell_inside [ addr ] != 0 ;
if ( left != current ) {
lines . push_back (
left ?
Line ( Point ( c , r + 1 ), Point ( c , r )) :
Line ( Point ( c , r ), Point ( c , r + 1 )));
start_point_to_line_idx . emplace_back ( lines . back (). a , int ( lines . size ()) - 1 );
}
if ( top != current ) {
lines . push_back (
top ?
Line ( Point ( c , r ), Point ( c + 1 , r )) :
Line ( Point ( c + 1 , r ), Point ( c , r )));
start_point_to_line_idx . emplace_back ( lines . back (). a , int ( lines . size ()) - 1 );
}
}
}
std :: sort ( start_point_to_line_idx . begin (), start_point_to_line_idx . end (), []( const auto & l , const auto & r ){ return l . first < r . first ; });
// 2) Chain the lines.
std :: vector < char > line_processed ( lines . size (), false );
Polygons out ;
for ( int i_candidate = 0 ; i_candidate < int ( lines . size ()); ++ i_candidate ) {
if ( line_processed [ i_candidate ])
continue ;
Polygon poly ;
line_processed [ i_candidate ] = true ;
poly . points . push_back ( lines [ i_candidate ]. b );
int i_line_current = i_candidate ;
for (;;) {
auto line_range = std :: equal_range ( std :: begin ( start_point_to_line_idx ), std :: end ( start_point_to_line_idx ),
std :: make_pair ( lines [ i_line_current ]. b , 0 ), []( const auto & l , const auto & r ) { return l . first < r . first ; });
// The interval has to be non empty, there shall be at least one line continuing the current one.
assert ( line_range . first != line_range . second );
int i_next = - 1 ;
for ( auto it = line_range . first ; it != line_range . second ; ++ it ) {
if ( it -> second == i_candidate ) {
// closing the loop.
goto end_of_poly ;
}
if ( line_processed [ it -> second ])
continue ;
if ( i_next == - 1 ) {
i_next = it -> second ;
} else {
// This is a corner, where two lines meet exactly. Pick the line, which encloses a smallest angle with
// the current edge.
const Line & line_current = lines [ i_line_current ];
const Line & line_next = lines [ it -> second ];
const Vector v1 = line_current . vector ();
const Vector v2 = line_next . vector ();
int64_t cross = int64_t ( v1 ( 0 )) * int64_t ( v2 ( 1 )) - int64_t ( v2 ( 0 )) * int64_t ( v1 ( 1 ));
if ( cross > 0 ) {
// This has to be a convex right angle. There is no better next line.
i_next = it -> second ;
break ;
}
}
}
line_processed [ i_next ] = true ;
i_line_current = i_next ;
poly . points . push_back ( lines [ i_line_current ]. b );
}
end_of_poly :
out . push_back ( std :: move ( poly ));
}
// 3) Scale the polygons back into world, shrink slightly and remove collinear points.
for ( Polygon & poly : out ) {
for ( Point & p : poly . points ) {
#if 0
p.x() = (p.x() + 1) * pixel_size + left_bottom.x();
p.y() = (p.y() + 1) * pixel_size + left_bottom.y();
#else
p *= pixel_size ;
p += left_bottom ;
#endif
}
// Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result.
// Remove collinear points.
Points pts ;
pts . reserve ( poly . points . size ());
for ( size_t j = 0 ; j < poly . points . size (); ++ j ) {
size_t j0 = ( j == 0 ) ? poly . points . size () - 1 : j - 1 ;
size_t j2 = ( j + 1 == poly . points . size ()) ? 0 : j + 1 ;
Point v = poly . points [ j2 ] - poly . points [ j0 ];
if ( v ( 0 ) != 0 && v ( 1 ) != 0 ) {
// This is a corner point. Copy it to the output contour.
Point p = poly . points [ j ];
p ( 1 ) += ( v ( 0 ) < 0 ) ? - offset : offset ;
p ( 0 ) += ( v ( 1 ) > 0 ) ? - offset : offset ;
pts . push_back ( p );
}
}
poly . points = std :: move ( pts );
}
return out ;
}
#endif // SUPPORT_USE_AGG_RASTERIZER
2016-12-20 12:19:13 +01:00
PrintObjectSupportMaterial :: PrintObjectSupportMaterial ( const PrintObject * object , const SlicingParameters & slicing_params ) :
2016-10-20 13:04:23 +02:00
m_object ( object ),
2018-09-11 14:04:47 +02:00
m_print_config ( & object -> print () -> config ()),
m_object_config ( & object -> config ()),
2021-03-19 11:21:29 +01:00
m_slicing_params ( slicing_params )
2016-10-20 13:04:23 +02:00
{
2021-03-19 11:21:29 +01:00
m_support_params . first_layer_flow = support_material_1st_layer_flow ( object , float ( slicing_params . first_print_layer_height ));
m_support_params . support_material_flow = support_material_flow ( object , float ( slicing_params . layer_height ));
m_support_params . support_material_interface_flow = support_material_interface_flow ( object , float ( slicing_params . layer_height ));
m_support_params . support_layer_height_min = 0.01 ;
2017-03-28 13:46:31 +02:00
2021-03-19 11:21:29 +01:00
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
m_support_params . support_layer_height_min = 1000000. ;
for ( auto lh : m_print_config -> min_layer_height . values )
m_support_params . support_layer_height_min = std :: min ( m_support_params . support_layer_height_min , std :: max ( 0.01 , lh ));
if ( m_object_config -> support_material_interface_layers . value == 0 ) {
2017-01-05 09:14:59 +01:00
// No interface layers allowed, print everything with the base support pattern.
2021-03-19 11:21:29 +01:00
m_support_params . support_material_interface_flow = m_support_params . support_material_flow ;
2017-01-05 09:14:59 +01:00
}
2019-04-18 15:08:11 +02:00
// Evaluate the XY gap between the object outer perimeters and the support structures.
2017-01-05 09:14:59 +01:00
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0. ;
2021-03-09 12:30:31 +01:00
coordf_t bridge_flow_ratio = 0 ;
2021-05-05 16:21:55 +02:00
for ( size_t region_id = 0 ; region_id < object -> num_printing_regions (); ++ region_id ) {
const PrintRegion & region = object -> printing_region ( region_id );
external_perimeter_width = std :: max ( external_perimeter_width , coordf_t ( region . flow ( * object , frExternalPerimeter , slicing_params . layer_height ). width ()));
bridge_flow_ratio += region . config (). bridge_flow_ratio ;
}
2021-03-19 11:21:29 +01:00
m_support_params . gap_xy = m_object_config -> support_material_xy_spacing . get_abs_value ( external_perimeter_width );
2021-05-05 16:21:55 +02:00
bridge_flow_ratio /= object -> num_printing_regions ();
2021-03-09 12:30:31 +01:00
2021-03-19 11:21:29 +01:00
m_support_params . support_material_bottom_interface_flow = m_slicing_params . soluble_interface || ! m_object_config -> thick_bridges ?
m_support_params . support_material_interface_flow . with_flow_ratio ( bridge_flow_ratio ) :
Flow :: bridging_flow ( bridge_flow_ratio * m_support_params . support_material_interface_flow . nozzle_diameter (), m_support_params . support_material_interface_flow . nozzle_diameter ());
2017-04-07 17:44:51 +02:00
2021-03-19 11:21:29 +01:00
m_support_params . can_merge_support_regions = m_object_config -> support_material_extruder . value == m_object_config -> support_material_interface_extruder . value ;
if ( ! m_support_params . can_merge_support_regions && ( m_object_config -> support_material_extruder . value == 0 || m_object_config -> support_material_interface_extruder . value == 0 )) {
2017-04-07 17:44:51 +02:00
// One of the support extruders is of "don't care" type.
2021-05-06 14:43:36 +02:00
auto object_extruders = m_object -> object_extruders ();
2017-04-07 17:44:51 +02:00
if ( object_extruders . size () == 1 &&
2017-05-12 11:18:32 +02:00
* object_extruders . begin () == std :: max < unsigned int > ( m_object_config -> support_material_extruder . value , m_object_config -> support_material_interface_extruder . value ))
2017-04-07 17:44:51 +02:00
// Object is printed with the same extruder as the support.
2021-03-19 11:21:29 +01:00
m_support_params . can_merge_support_regions = true ;
2017-04-07 17:44:51 +02:00
}
2016-10-20 13:04:23 +02:00
}
2016-10-16 16:30:56 +02:00
// Using the std::deque as an allocator.
2016-10-20 13:04:23 +02:00
inline PrintObjectSupportMaterial :: MyLayer & layer_allocate (
std :: deque < PrintObjectSupportMaterial :: MyLayer > & layer_storage ,
PrintObjectSupportMaterial :: SupporLayerType layer_type )
2016-10-13 16:00:22 +02:00
{
2016-10-20 13:04:23 +02:00
layer_storage . push_back ( PrintObjectSupportMaterial :: MyLayer ());
2016-10-16 16:30:56 +02:00
layer_storage . back (). layer_type = layer_type ;
return layer_storage . back ();
2016-10-13 16:00:22 +02:00
}
2017-03-22 15:35:50 +01:00
inline PrintObjectSupportMaterial :: MyLayer & layer_allocate (
std :: deque < PrintObjectSupportMaterial :: MyLayer > & layer_storage ,
tbb :: spin_mutex & layer_storage_mutex ,
PrintObjectSupportMaterial :: SupporLayerType layer_type )
{
layer_storage_mutex . lock ();
layer_storage . push_back ( PrintObjectSupportMaterial :: MyLayer ());
PrintObjectSupportMaterial :: MyLayer * layer_new = & layer_storage . back ();
layer_storage_mutex . unlock ();
layer_new -> layer_type = layer_type ;
return * layer_new ;
}
2016-10-20 13:04:23 +02:00
inline void layers_append ( PrintObjectSupportMaterial :: MyLayersPtr & dst , const PrintObjectSupportMaterial :: MyLayersPtr & src )
2016-10-16 16:30:56 +02:00
{
dst . insert ( dst . end (), src . begin (), src . end ());
}
2016-10-20 13:04:23 +02:00
void PrintObjectSupportMaterial :: generate ( PrintObject & object )
2016-10-13 16:00:22 +02:00
{
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Start" ;
2016-10-13 16:00:22 +02:00
coordf_t max_object_layer_height = 0. ;
2016-10-16 16:30:56 +02:00
for ( size_t i = 0 ; i < object . layer_count (); ++ i )
2018-09-11 14:04:47 +02:00
max_object_layer_height = std :: max ( max_object_layer_height , object . layers ()[ i ] -> height );
2016-10-13 16:00:22 +02:00
// Layer instances will be allocated by std::deque and they will be kept until the end of this function call.
2016-10-16 16:30:56 +02:00
// The layers will be referenced by various LayersPtr (of type std::vector<Layer*>)
MyLayerStorage layer_storage ;
2016-10-13 16:00:22 +02:00
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating top contacts" ;
2021-03-19 11:21:29 +01:00
std :: vector < Polygons > buildplate_covered = this -> buildplate_covered ( object );
2016-10-16 16:30:56 +02:00
// Determine the top contact surfaces of the support, defined as:
2016-10-13 16:00:22 +02:00
// contact = overhangs - clearance + margin
// This method is responsible for identifying what contact surfaces
// should the support material expose to the object in order to guarantee
// that it will be effective, regardless of how it's built below.
2016-10-20 13:04:23 +02:00
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette without holes.
2021-03-19 11:21:29 +01:00
MyLayersPtr top_contacts = this -> top_contact_layers ( object , buildplate_covered , layer_storage );
2016-10-13 16:00:22 +02:00
if ( top_contacts . empty ())
2016-10-16 16:30:56 +02:00
// Nothing is supported, no supports are generated.
2016-10-13 16:00:22 +02:00
return ;
2016-11-02 10:47:00 +01:00
#ifdef SLIC3R_DEBUG
static int iRun = 0 ;
iRun ++ ;
2018-09-17 15:12:13 +02:00
for ( const MyLayer * layer : top_contacts )
2017-01-05 09:14:59 +01:00
Slic3r :: SVG :: export_expolygons (
2018-09-17 15:12:13 +02:00
debug_out_path ( "support-top-contacts-%d-%lf.svg" , iRun , layer -> print_z ),
union_ex ( layer -> polygons , false ));
2016-11-02 10:47:00 +01:00
#endif /* SLIC3R_DEBUG */
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating bottom contacts" ;
2016-10-16 16:30:56 +02:00
// Determine the bottom contact surfaces of the supports over the top surfaces of the object.
// Depending on whether the support is soluble or not, the contact layer thickness is decided.
2017-01-05 09:14:59 +01:00
// layer_support_areas contains the per object layer support areas. These per object layer support areas
// may get merged and trimmed by this->generate_base_layers() if the support layers are not synchronized with object layers.
2016-11-29 19:30:59 +01:00
std :: vector < Polygons > layer_support_areas ;
MyLayersPtr bottom_contacts = this -> bottom_contact_layers_and_layer_support_areas (
2021-03-19 11:21:29 +01:00
object , top_contacts , buildplate_covered ,
layer_storage , layer_support_areas );
2016-10-13 16:00:22 +02:00
2017-01-05 09:14:59 +01:00
#ifdef SLIC3R_DEBUG
2018-09-11 14:04:47 +02:00
for ( size_t layer_id = 0 ; layer_id < object . layers (). size (); ++ layer_id )
2017-01-05 09:14:59 +01:00
Slic3r :: SVG :: export_expolygons (
2018-09-11 14:04:47 +02:00
debug_out_path ( "support-areas-%d-%lf.svg" , iRun , object . layers ()[ layer_id ] -> print_z ),
2017-01-05 09:14:59 +01:00
union_ex ( layer_support_areas [ layer_id ], false ));
#endif /* SLIC3R_DEBUG */
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating intermediate layers - indices" ;
2017-01-05 09:14:59 +01:00
// Allocate empty layers between the top / bottom support contact layers
// as placeholders for the base and intermediate support layers.
2016-10-16 16:30:56 +02:00
// The layers may or may not be synchronized with the object layers, depending on the configuration.
// For example, a single nozzle multi material printing will need to generate a waste tower, which in turn
2017-01-05 09:14:59 +01:00
// wastes less material, if there are as little tool changes as possible.
2016-10-16 16:30:56 +02:00
MyLayersPtr intermediate_layers = this -> raft_and_intermediate_support_layers (
2017-03-22 15:35:50 +01:00
object , bottom_contacts , top_contacts , layer_storage );
2016-10-13 16:00:22 +02:00
2021-03-19 11:21:29 +01:00
this -> trim_support_layers_by_object ( object , top_contacts , m_slicing_params . gap_support_object , m_slicing_params . gap_object_support , m_support_params . gap_xy );
2018-09-17 15:12:13 +02:00
#ifdef SLIC3R_DEBUG
for ( const MyLayer * layer : top_contacts )
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-top-contacts-trimmed-by-object-%d-%lf.svg" , iRun , layer -> print_z ),
union_ex ( layer -> polygons , false ));
#endif
2017-01-05 09:14:59 +01:00
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating base layers" ;
2021-02-15 18:07:44 +01:00
// Fill in intermediate layers between the top / bottom support contact layers, trim them by the object.
2016-11-29 19:30:59 +01:00
this -> generate_base_layers ( object , bottom_contacts , top_contacts , intermediate_layers , layer_support_areas );
2016-10-20 13:04:23 +02:00
2016-11-02 10:47:00 +01:00
#ifdef SLIC3R_DEBUG
2017-01-05 09:14:59 +01:00
for ( MyLayersPtr :: const_iterator it = intermediate_layers . begin (); it != intermediate_layers . end (); ++ it )
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-base-layers-%d-%lf.svg" , iRun , ( * it ) -> print_z ),
union_ex (( * it ) -> polygons , false ));
2016-11-02 10:47:00 +01:00
#endif /* SLIC3R_DEBUG */
2017-01-25 18:33:05 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Trimming top contacts by bottom contacts" ;
// Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion
// and unwanted strong bonds to the object.
// Rather trim the top contacts by their overlapping bottom contacts to leave a gap instead of over extruding
// top contacts over the bottom contacts.
this -> trim_top_contacts_by_bottom_contacts ( object , bottom_contacts , top_contacts );
2016-10-13 16:00:22 +02:00
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating interfaces" ;
2021-02-15 18:07:44 +01:00
// Propagate top / bottom contact layers to generate interface layers
// and base interface layers (for soluble interface / non souble base only)
auto [ interface_layers , base_interface_layers ] = this -> generate_interface_layers ( bottom_contacts , top_contacts , intermediate_layers , layer_storage );
2016-10-16 16:30:56 +02:00
2017-02-14 19:49:30 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating raft" ;
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
// There is also a 1st intermediate layer containing bases of support columns.
// Inflate the bases of the support columns and create the raft base under the object.
2021-02-25 14:50:35 +01:00
MyLayersPtr raft_layers = this -> generate_raft_base ( object , top_contacts , interface_layers , intermediate_layers , base_interface_layers , layer_storage );
2017-02-14 19:49:30 +01:00
2016-11-02 10:47:00 +01:00
#ifdef SLIC3R_DEBUG
2021-02-15 18:07:44 +01:00
for ( const MyLayer * l : interface_layers )
2017-01-05 09:14:59 +01:00
Slic3r :: SVG :: export_expolygons (
2021-02-15 18:07:44 +01:00
debug_out_path ( "support-interface-layers-%d-%lf.svg" , iRun , l -> print_z ),
union_ex ( l -> polygons , false ));
for ( const MyLayer * l : base_interface_layers )
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-base-interface-layers-%d-%lf.svg" , iRun , l -> print_z ),
union_ex ( l -> polygons , false ));
#endif // SLIC3R_DEBUG
2016-11-02 10:47:00 +01:00
2016-10-16 16:30:56 +02:00
/*
// Clip with the pillars.
if (! shape.empty()) {
2016-10-13 16:00:22 +02:00
this->clip_with_shape(interface, shape);
this->clip_with_shape(base, shape);
2016-10-16 16:30:56 +02:00
}
*/
2016-10-13 16:00:22 +02:00
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Creating layers" ;
2017-01-19 13:47:06 +01:00
// For debugging purposes, one may want to show only some of the support extrusions.
2017-01-05 09:14:59 +01:00
// raft_layers.clear();
// bottom_contacts.clear();
// top_contacts.clear();
// intermediate_layers.clear();
// interface_layers.clear();
2016-10-16 16:30:56 +02:00
// Install support layers into the object.
2017-01-05 09:14:59 +01:00
// A support layer installed on a PrintObject has a unique print_z.
2016-10-16 16:30:56 +02:00
MyLayersPtr layers_sorted ;
2021-02-15 18:07:44 +01:00
layers_sorted . reserve ( raft_layers . size () + bottom_contacts . size () + top_contacts . size () + intermediate_layers . size () + interface_layers . size () + base_interface_layers . size ());
2016-12-20 12:19:13 +01:00
layers_append ( layers_sorted , raft_layers );
2016-10-16 16:30:56 +02:00
layers_append ( layers_sorted , bottom_contacts );
layers_append ( layers_sorted , top_contacts );
layers_append ( layers_sorted , intermediate_layers );
layers_append ( layers_sorted , interface_layers );
2021-02-15 18:07:44 +01:00
layers_append ( layers_sorted , base_interface_layers );
2017-01-05 09:14:59 +01:00
// Sort the layers lexicographically by a raising print_z and a decreasing height.
2021-03-10 12:14:45 +01:00
std :: sort ( layers_sorted . begin (), layers_sorted . end (), []( auto * l1 , auto * l2 ) { return * l1 < * l2 ; });
2016-10-16 16:30:56 +02:00
int layer_id = 0 ;
2018-09-11 14:04:47 +02:00
assert ( object . support_layers (). empty ());
2019-06-25 13:06:04 +02:00
for ( size_t i = 0 ; i < layers_sorted . size ();) {
2017-01-11 13:42:41 +01:00
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
2017-05-12 11:18:32 +02:00
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
2019-06-25 13:06:04 +02:00
size_t j = i + 1 ;
2017-05-12 11:18:32 +02:00
coordf_t zmax = layers_sorted [ i ] -> print_z + EPSILON ;
for (; j < layers_sorted . size () && layers_sorted [ j ] -> print_z <= zmax ; ++ j ) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
coordf_t zavg = 0.5 * ( layers_sorted [ i ] -> print_z + layers_sorted [ j - 1 ] -> print_z );
coordf_t height_min = layers_sorted [ i ] -> height ;
bool empty = true ;
2019-06-25 13:06:04 +02:00
for ( size_t u = i ; u < j ; ++ u ) {
2017-05-12 11:18:32 +02:00
MyLayer & layer = * layers_sorted [ u ];
if ( ! layer . polygons . empty ())
empty = false ;
layer . print_z = zavg ;
height_min = std :: min ( height_min , layer . height );
}
if ( ! empty ) {
2017-12-11 17:19:55 +01:00
// Here the upper_layer and lower_layer pointers are left to null at the support layers,
// as they are never used. These pointers are candidates for removal.
object . add_support_layer ( layer_id ++ , height_min , zavg );
2017-05-12 11:18:32 +02:00
}
i = j ;
2016-10-13 16:00:22 +02:00
}
2017-01-25 18:33:05 +01:00
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - Generating tool paths" ;
2021-02-24 17:34:51 +01:00
#if 0 // #ifdef SLIC3R_DEBUG
{
size_t layer_id = 0;
for (int i = 0; i < int(layers_sorted.size());) {
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
int j = i + 1;
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
bool empty = true;
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j)
if (!layers_sorted[j]->polygons.empty())
empty = false;
if (!empty) {
export_print_z_polygons_to_svg(
debug_out_path("support-%d-%lf-before.svg", iRun, layers_sorted[i]->print_z).c_str(),
layers_sorted.data() + i, j - i);
export_print_z_polygons_and_extrusions_to_svg(
debug_out_path("support-w-fills-%d-%lf-before.svg", iRun, layers_sorted[i]->print_z).c_str(),
layers_sorted.data() + i, j - i,
*object.support_layers()[layer_id]);
++layer_id;
}
i = j;
}
}
#endif /* SLIC3R_DEBUG */
2016-10-13 16:00:22 +02:00
// Generate the actual toolpaths and save them into each layer.
2021-02-15 18:07:44 +01:00
this -> generate_toolpaths ( object . support_layers (), raft_layers , bottom_contacts , top_contacts , intermediate_layers , interface_layers , base_interface_layers );
2016-11-24 13:44:51 +01:00
2017-01-20 15:21:05 +01:00
#ifdef SLIC3R_DEBUG
{
size_t layer_id = 0 ;
for ( int i = 0 ; i < int ( layers_sorted . size ());) {
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
int j = i + 1 ;
coordf_t zmax = layers_sorted [ i ] -> print_z + EPSILON ;
2017-05-12 11:18:32 +02:00
bool empty = true ;
for (; j < layers_sorted . size () && layers_sorted [ j ] -> print_z <= zmax ; ++ j )
if ( ! layers_sorted [ j ] -> polygons . empty ())
empty = false ;
if ( ! empty ) {
export_print_z_polygons_to_svg (
debug_out_path ( "support-%d-%lf.svg" , iRun , layers_sorted [ i ] -> print_z ). c_str (),
layers_sorted . data () + i , j - i );
export_print_z_polygons_and_extrusions_to_svg (
debug_out_path ( "support-w-fills-%d-%lf.svg" , iRun , layers_sorted [ i ] -> print_z ). c_str (),
layers_sorted . data () + i , j - i ,
2020-08-12 10:47:36 +02:00
* object . support_layers ()[ layer_id ]);
2017-05-12 11:18:32 +02:00
++ layer_id ;
}
i = j ;
2017-01-20 15:21:05 +01:00
}
}
#endif /* SLIC3R_DEBUG */
2016-11-24 13:44:51 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Support generator - End" ;
2016-10-13 16:00:22 +02:00
}
2017-01-05 09:14:59 +01:00
// Collect all polygons of all regions in a layer with a given surface type.
Polygons collect_region_slices_by_type ( const Layer & layer , SurfaceType surface_type )
2016-10-13 16:00:22 +02:00
{
// 1) Count the new polygons first.
size_t n_polygons_new = 0 ;
2018-09-17 15:12:13 +02:00
for ( const LayerRegion * region : layer . regions ())
for ( const Surface & surface : region -> slices . surfaces )
2016-10-13 16:00:22 +02:00
if ( surface . surface_type == surface_type )
n_polygons_new += surface . expolygon . holes . size () + 1 ;
// 2) Collect the new polygons.
2017-01-05 09:14:59 +01:00
Polygons out ;
out . reserve ( n_polygons_new );
2018-09-17 15:12:13 +02:00
for ( const LayerRegion * region : layer . regions ())
for ( const Surface & surface : region -> slices . surfaces )
2016-11-02 10:47:00 +01:00
if ( surface . surface_type == surface_type )
polygons_append ( out , surface . expolygon );
2016-10-13 16:00:22 +02:00
return out ;
}
2017-01-05 09:14:59 +01:00
// Collect outer contours of all slices of this layer.
// This is useful for calculating the support base with holes filled.
2016-11-02 10:47:00 +01:00
Polygons collect_slices_outer ( const Layer & layer )
{
Polygons out ;
2020-01-03 14:05:56 +01:00
out . reserve ( out . size () + layer . lslices . size ());
for ( const ExPolygon & expoly : layer . lslices )
2018-09-17 15:12:13 +02:00
out . emplace_back ( expoly . contour );
2016-11-02 10:47:00 +01:00
return out ;
}
2021-03-19 11:21:29 +01:00
struct SupportGridParams {
SupportGridParams ( const PrintObjectConfig & object_config , const Flow & support_material_flow ) :
2021-03-23 11:06:31 +01:00
style ( object_config . support_material_style . value ),
2021-03-19 11:21:29 +01:00
grid_resolution ( object_config . support_material_spacing . value + support_material_flow . spacing ()),
support_angle ( Geometry :: deg2rad ( object_config . support_material_angle . value )),
extrusion_width ( support_material_flow . spacing ()),
2021-04-12 14:56:30 +02:00
support_material_closing_radius ( object_config . support_material_closing_radius . value ),
2021-03-19 11:21:29 +01:00
expansion_to_slice ( coord_t ( support_material_flow . scaled_spacing () / 2 + 5 )),
expansion_to_propagate ( - 3 ) {}
2021-03-23 11:06:31 +01:00
SupportMaterialStyle style ;
double grid_resolution ;
double support_angle ;
double extrusion_width ;
2021-04-12 14:56:30 +02:00
double support_material_closing_radius ;
2021-03-23 11:06:31 +01:00
coord_t expansion_to_slice ;
coord_t expansion_to_propagate ;
2021-03-19 11:21:29 +01:00
};
2017-04-05 09:56:59 +02:00
class SupportGridPattern
{
public :
2017-06-08 14:02:37 +02:00
SupportGridPattern (
2018-09-17 15:12:13 +02:00
// Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
2021-03-19 11:21:29 +01:00
const Polygons * support_polygons ,
2018-09-17 15:12:13 +02:00
// Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
2021-03-19 11:21:29 +01:00
const Polygons * trimming_polygons ,
const SupportGridParams & params ) :
2021-03-23 11:06:31 +01:00
m_style ( params . style ),
2021-03-17 12:25:43 +01:00
m_support_polygons ( support_polygons ), m_trimming_polygons ( trimming_polygons ),
2021-04-12 14:56:30 +02:00
m_support_spacing ( params . grid_resolution ), m_support_angle ( params . support_angle ),
m_extrusion_width ( params . extrusion_width ),
m_support_material_closing_radius ( params . support_material_closing_radius )
2017-04-05 09:56:59 +02:00
{
2021-03-23 11:06:31 +01:00
switch ( m_style ) {
case smsGrid :
2021-02-19 18:50:44 +01:00
{
2021-03-23 11:06:31 +01:00
// Prepare the grid data, it will be reused when extracting support structures.
if ( m_support_angle != 0. ) {
// Create a copy of the rotated contours.
m_support_polygons_rotated = * support_polygons ;
m_trimming_polygons_rotated = * trimming_polygons ;
m_support_polygons = & m_support_polygons_rotated ;
m_trimming_polygons = & m_trimming_polygons_rotated ;
polygons_rotate ( m_support_polygons_rotated , - params . support_angle );
polygons_rotate ( m_trimming_polygons_rotated , - params . support_angle );
}
2021-02-19 18:50:44 +01:00
2021-03-23 11:06:31 +01:00
// Resolution of the sparse support grid.
coord_t grid_resolution = coord_t ( scale_ ( m_support_spacing ));
BoundingBox bbox = get_extents ( * m_support_polygons );
bbox . offset ( 20 );
// Align the bounding box with the sparse support grid.
bbox . align_to_grid ( grid_resolution );
#ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox ;
// Oversample the grid to avoid leaking of supports through or around the object walls.
int oversampling = std :: min ( 8 , int ( scale_ ( m_support_spacing ) / ( scale_ ( params . extrusion_width ) + 100 )));
m_pixel_size = scale_ ( m_support_spacing / oversampling );
assert ( scale_ ( params . extrusion_width ) + 20 < m_pixel_size );
// Add one empty column / row boundaries.
m_bbox . offset ( m_pixel_size );
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
Vec2i grid_size_raw ( int ( ceil (( m_bbox . max . x () - m_bbox . min . x ()) / m_pixel_size )),
int ( ceil (( m_bbox . max . y () - m_bbox . min . y ()) / m_pixel_size )));
// Overlay macro blocks of (oversampling x oversampling) over the grid.
Vec2i grid_blocks (( grid_size_raw . x () + oversampling - 1 - 2 ) / oversampling ,
( grid_size_raw . y () + oversampling - 1 - 2 ) / oversampling );
// and resize the grid to fit the macro blocks + one pixel boundary.
m_grid_size = grid_blocks * oversampling + Vec2i ( 2 , 2 );
assert ( m_grid_size . x () >= grid_size_raw . x ());
assert ( m_grid_size . y () >= grid_size_raw . y ());
m_grid2 = rasterize_polygons ( m_grid_size , m_pixel_size , m_bbox . min , * m_support_polygons );
seed_fill_block ( m_grid2 , m_grid_size ,
dilate_trimming_region ( rasterize_polygons ( m_grid_size , m_pixel_size , m_bbox . min , * m_trimming_polygons ), m_grid_size ),
grid_blocks , oversampling );
#ifdef SLIC3R_DEBUG
{
static int irun ;
Slic3r :: png :: write_gray_to_file_scaled ( debug_out_path ( "support-rasterizer-%d.png" , irun ++ ), m_grid_size . x (), m_grid_size . y (), m_grid2 . data (), 4 );
}
#endif // SLIC3R_DEBUG
#else // SUPPORT_USE_AGG_RASTERIZER
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
2018-12-12 10:12:35 +01:00
m_grid . set_bbox ( bbox );
m_grid . create ( * m_support_polygons , grid_resolution );
2021-03-23 11:06:31 +01:00
#if 0
if (m_grid.has_intersecting_edges()) {
// EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
m_support_polygons = &m_support_polygons_rotated;
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
// assert(! m_grid.has_intersecting_edges());
printf("SupportGridPattern: fixing polygons with intersection %s\n",
m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
}
#endif
m_grid . calculate_sdf ();
#endif // SUPPORT_USE_AGG_RASTERIZER
break ;
}
case smsSnug :
default :
// nothing to prepare
break ;
2018-12-12 10:12:35 +01:00
}
2017-04-05 09:56:59 +02:00
}
// Extract polygons from the grid, offsetted by offset_in_grid,
// and trim the extracted polygons by trimming_polygons.
// Trimming by the trimming_polygons may split the extracted polygons into pieces.
// Remove all the pieces, which do not contain any of the island_samples.
2021-03-17 12:25:43 +01:00
Polygons extract_support ( const coord_t offset_in_grid , bool fill_holes
#ifdef SLIC3R_DEBUG
, const char * step_name , int iRun , size_t layer_id , double print_z
#endif
)
2017-04-05 09:56:59 +02:00
{
2021-03-23 11:06:31 +01:00
switch ( m_style ) {
case smsGrid :
{
#ifdef SUPPORT_USE_AGG_RASTERIZER
Polygons support_polygons_simplified = contours_simplified ( m_grid_size , m_pixel_size , m_bbox . min , m_grid2 , offset_in_grid , fill_holes );
#else // SUPPORT_USE_AGG_RASTERIZER
// Generate islands, so each island may be tested for overlap with island_samples.
assert ( std :: abs ( 2 * offset_in_grid ) < m_grid . resolution ());
Polygons support_polygons_simplified = m_grid . contours_simplified ( offset_in_grid , fill_holes );
#endif // SUPPORT_USE_AGG_RASTERIZER
2021-02-19 18:50:44 +01:00
2021-05-05 12:16:40 +02:00
ExPolygons islands = diff_ex ( support_polygons_simplified , * m_trimming_polygons );
2017-04-05 09:56:59 +02:00
2021-03-23 11:06:31 +01:00
// Extract polygons, which contain some of the island_samples.
Polygons out ;
2021-03-17 12:25:43 +01:00
2021-03-23 11:06:31 +01:00
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
// polygons if ever these polygons get split into parts by the trimming polygons.
// As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands.
// Trim ti with islands.
Points samples = island_samples (
offset_in_grid > 0 ?
// Expanding, thus m_support_polygons are all inside islands.
union_ex ( * m_support_polygons ) :
// Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands.
2021-05-03 11:39:53 +02:00
intersection_ex ( * m_support_polygons , islands ));
2021-03-17 12:25:43 +01:00
2021-03-23 11:06:31 +01:00
std :: vector < std :: pair < Point , bool >> samples_inside ;
for ( ExPolygon & island : islands ) {
BoundingBox bbox = get_extents ( island . contour );
// Samples are sorted lexicographically.
auto it_lower = std :: lower_bound ( samples . begin (), samples . end (), Point ( bbox . min - Point ( 1 , 1 )));
auto it_upper = std :: upper_bound ( samples . begin (), samples . end (), Point ( bbox . max + Point ( 1 , 1 )));
samples_inside . clear ();
for ( auto it = it_lower ; it != it_upper ; ++ it )
if ( bbox . contains ( * it ))
samples_inside . push_back ( std :: make_pair ( * it , false ));
if ( ! samples_inside . empty ()) {
// For all samples_inside count the boundary crossing.
for ( size_t i_contour = 0 ; i_contour <= island . holes . size (); ++ i_contour ) {
Polygon & contour = ( i_contour == 0 ) ? island . contour : island . holes [ i_contour - 1 ];
Points :: const_iterator i = contour . points . begin ();
Points :: const_iterator j = contour . points . end () - 1 ;
for (; i != contour . points . end (); j = i ++ ) {
//FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
// Does the ray with y == point(1) intersect this line segment?
for ( auto & sample_inside : samples_inside ) {
if ((( * i )( 1 ) > sample_inside . first ( 1 )) != (( * j )( 1 ) > sample_inside . first ( 1 ))) {
double x1 = ( double ) sample_inside . first ( 0 );
double x2 = ( double )( * i )( 0 ) + ( double )(( * j )( 0 ) - ( * i )( 0 )) * ( double )( sample_inside . first ( 1 ) - ( * i )( 1 )) / ( double )(( * j )( 1 ) - ( * i )( 1 ));
if ( x1 < x2 )
sample_inside . second = ! sample_inside . second ;
}
2017-04-05 09:56:59 +02:00
}
}
}
2021-03-23 11:06:31 +01:00
// If any of the sample is inside this island, add this island to the output.
for ( auto & sample_inside : samples_inside )
if ( sample_inside . second ) {
polygons_append ( out , std :: move ( island ));
island . clear ();
break ;
}
2017-04-05 09:56:59 +02:00
}
}
2021-03-23 11:06:31 +01:00
#ifdef SLIC3R_DEBUG
BoundingBox bbox = get_extents ( * m_trimming_polygons );
if ( ! islands . empty ())
bbox . merge ( get_extents ( islands ));
if ( ! out . empty ())
bbox . merge ( get_extents ( out ));
if ( ! support_polygons_simplified . empty ())
bbox . merge ( get_extents ( support_polygons_simplified ));
SVG svg ( debug_out_path ( "extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg" , step_name , iRun , layer_id , print_z ). c_str (), bbox );
svg . draw ( union_ex ( support_polygons_simplified ), "gray" , 0.25f );
svg . draw ( islands , "red" , 0.5f );
svg . draw ( union_ex ( out ), "green" , 0.5f );
svg . draw ( union_ex ( * m_support_polygons ), "blue" , 0.5f );
svg . draw_outline ( islands , "red" , "red" , scale_ ( 0.05 ));
svg . draw_outline ( union_ex ( out ), "green" , "green" , scale_ ( 0.05 ));
svg . draw_outline ( union_ex ( * m_support_polygons ), "blue" , "blue" , scale_ ( 0.05 ));
for ( const Point & pt : samples )
svg . draw ( pt , "black" , coord_t ( scale_ ( 0.15 )));
svg . Close ();
#endif /* SLIC3R_DEBUG */
if ( m_support_angle != 0. )
polygons_rotate ( out , m_support_angle );
return out ;
}
case smsSnug :
2021-04-12 14:56:30 +02:00
// Merge the support polygons by applying morphological closing and inwards smoothing.
auto closing_distance = scaled < float > ( m_support_material_closing_radius );
auto smoothing_distance = scaled < float > ( m_extrusion_width );
return smooth_outward ( offset ( offset_ex ( * m_support_polygons , closing_distance ), - closing_distance ), smoothing_distance );
2017-04-05 09:56:59 +02:00
}
2021-03-23 00:19:45 +01:00
assert ( false );
return Polygons ();
2017-04-05 09:56:59 +02:00
}
2021-02-19 18:50:44 +01:00
#if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER)
2018-12-12 10:12:35 +01:00
void serialize ( const std :: string & path )
{
FILE * file = :: fopen ( path . c_str (), "wb" );
:: fwrite ( & m_support_spacing , 8 , 1 , file );
:: fwrite ( & m_support_angle , 8 , 1 , file );
uint32_t n_polygons = m_support_polygons -> size ();
:: fwrite ( & n_polygons , 4 , 1 , file );
for ( uint32_t i = 0 ; i < n_polygons ; ++ i ) {
const Polygon & poly = ( * m_support_polygons )[ i ];
uint32_t n_points = poly . size ();
:: fwrite ( & n_points , 4 , 1 , file );
for ( uint32_t j = 0 ; j < n_points ; ++ j ) {
const Point & pt = poly . points [ j ];
2020-08-12 10:47:36 +02:00
:: fwrite ( & pt . x (), sizeof ( coord_t ), 1 , file );
:: fwrite ( & pt . y (), sizeof ( coord_t ), 1 , file );
2018-12-12 10:12:35 +01:00
}
}
n_polygons = m_trimming_polygons -> size ();
:: fwrite ( & n_polygons , 4 , 1 , file );
for ( uint32_t i = 0 ; i < n_polygons ; ++ i ) {
const Polygon & poly = ( * m_trimming_polygons )[ i ];
uint32_t n_points = poly . size ();
:: fwrite ( & n_points , 4 , 1 , file );
for ( uint32_t j = 0 ; j < n_points ; ++ j ) {
const Point & pt = poly . points [ j ];
2020-08-12 10:47:36 +02:00
:: fwrite ( & pt . x (), sizeof ( coord_t ), 1 , file );
:: fwrite ( & pt . y (), sizeof ( coord_t ), 1 , file );
2018-12-12 10:12:35 +01:00
}
}
:: fclose ( file );
}
static SupportGridPattern deserialize ( const std :: string & path , int which = - 1 )
{
SupportGridPattern out ;
out . deserialize_ ( path , which );
return out ;
}
// Deserialization constructor
2021-05-03 11:39:53 +02:00
bool deserialize_ ( const std :: string & path , int which = - 1 )
2018-12-12 10:12:35 +01:00
{
FILE * file = :: fopen ( path . c_str (), "rb" );
if ( file == nullptr )
return false ;
m_support_polygons = & m_support_polygons_deserialized ;
m_trimming_polygons = & m_trimming_polygons_deserialized ;
:: fread ( & m_support_spacing , 8 , 1 , file );
:: fread ( & m_support_angle , 8 , 1 , file );
//FIXME
//m_support_spacing *= 0.01 / 2;
uint32_t n_polygons ;
:: fread ( & n_polygons , 4 , 1 , file );
m_support_polygons_deserialized . reserve ( n_polygons );
int32_t scale = 1 ;
for ( uint32_t i = 0 ; i < n_polygons ; ++ i ) {
Polygon poly ;
uint32_t n_points ;
:: fread ( & n_points , 4 , 1 , file );
poly . points . reserve ( n_points );
for ( uint32_t j = 0 ; j < n_points ; ++ j ) {
coord_t x , y ;
:: fread ( & x , sizeof ( coord_t ), 1 , file );
:: fread ( & y , sizeof ( coord_t ), 1 , file );
poly . points . emplace_back ( Point ( x * scale , y * scale ));
}
if ( which == - 1 || which == i )
2021-05-03 11:39:53 +02:00
m_support_polygons_deserialized . emplace_back ( std :: move ( poly ));
2018-12-12 10:12:35 +01:00
printf ( "Polygon %d, area: %lf \n " , i , area ( poly . points ));
}
:: fread ( & n_polygons , 4 , 1 , file );
m_trimming_polygons_deserialized . reserve ( n_polygons );
for ( uint32_t i = 0 ; i < n_polygons ; ++ i ) {
Polygon poly ;
uint32_t n_points ;
:: fread ( & n_points , 4 , 1 , file );
poly . points . reserve ( n_points );
for ( uint32_t j = 0 ; j < n_points ; ++ j ) {
coord_t x , y ;
:: fread ( & x , sizeof ( coord_t ), 1 , file );
:: fread ( & y , sizeof ( coord_t ), 1 , file );
poly . points . emplace_back ( Point ( x * scale , y * scale ));
}
m_trimming_polygons_deserialized . emplace_back ( std :: move ( poly ));
}
:: fclose ( file );
m_support_polygons_deserialized = simplify_polygons ( m_support_polygons_deserialized , false );
//m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false));
2021-05-03 11:39:53 +02:00
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
coord_t grid_resolution = coord_t ( scale_ ( m_support_spacing ));
BoundingBox bbox = get_extents ( * m_support_polygons );
2018-12-12 10:12:35 +01:00
bbox . offset ( 20 );
2021-05-03 11:39:53 +02:00
bbox . align_to_grid ( grid_resolution );
m_grid . set_bbox ( bbox );
m_grid . create ( * m_support_polygons , grid_resolution );
m_grid . calculate_sdf ();
2018-12-12 10:12:35 +01:00
return true ;
}
const Polygons & support_polygons () const { return * m_support_polygons ; }
const Polygons & trimming_polygons () const { return * m_trimming_polygons ; }
const EdgeGrid :: Grid & grid () const { return m_grid ; }
2021-02-19 18:50:44 +01:00
#endif // defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER)
2018-12-12 10:12:35 +01:00
2017-04-05 09:56:59 +02:00
private :
2018-12-12 10:12:35 +01:00
SupportGridPattern () {}
2017-05-12 11:18:32 +02:00
SupportGridPattern & operator = ( const SupportGridPattern & rhs );
2021-02-19 18:50:44 +01:00
#ifdef SUPPORT_USE_AGG_RASTERIZER
// Dilate the trimming region (unmask the boundary pixels).
static std :: vector < unsigned char > dilate_trimming_region ( const std :: vector < unsigned char > & trimming , const Vec2i & grid_size )
{
std :: vector < unsigned char > dilated ( trimming . size (), 0 );
for ( int r = 1 ; r + 1 < grid_size . y (); ++ r )
for ( int c = 1 ; c + 1 < grid_size . x (); ++ c ) {
//int addr = c + r * m_grid_size.x();
// 4-neighborhood is not sufficient.
// dilated[addr] = trimming[addr] != 0 && trimming[addr - 1] != 0 && trimming[addr + 1] != 0 && trimming[addr - m_grid_size.x()] != 0 && trimming[addr + m_grid_size.x()] != 0;
// 8-neighborhood
int addr = c + ( r - 1 ) * grid_size . x ();
bool b = trimming [ addr - 1 ] != 0 && trimming [ addr ] != 0 && trimming [ addr + 1 ] != 0 ;
addr += grid_size . x ();
b = b && trimming [ addr - 1 ] != 0 && trimming [ addr ] != 0 && trimming [ addr + 1 ] != 0 ;
addr += grid_size . x ();
b = b && trimming [ addr - 1 ] != 0 && trimming [ addr ] != 0 && trimming [ addr + 1 ] != 0 ;
dilated [ addr - grid_size . x ()] = b ;
}
return dilated ;
}
// Seed fill each of the (oversampling x oversampling) block up to the dilated trimming region.
static void seed_fill_block ( std :: vector < unsigned char > & grid , Vec2i grid_size , const std :: vector < unsigned char > & trimming , const Vec2i & grid_blocks , int oversampling )
{
int size = oversampling ;
int stride = grid_size . x ();
for ( int block_r = 0 ; block_r < grid_blocks . y (); ++ block_r )
for ( int block_c = 0 ; block_c < grid_blocks . x (); ++ block_c ) {
// Propagate the support pixels over the macro cell up to the trimming mask.
int addr = block_c * size + 1 + ( block_r * size + 1 ) * stride ;
unsigned char * grid_data = grid . data () + addr ;
const unsigned char * mask_data = trimming . data () + addr ;
// Top to bottom propagation.
#define PROPAGATION_STEP(offset) \
do { \
int addr = r * stride + c; \
int addr2 = addr + offset; \
if (grid_data[addr2] && ! mask_data[addr] && ! mask_data[addr2]) \
grid_data[addr] = 1; \
} while (0);
for ( int r = 0 ; r < size ; ++ r ) {
if ( r > 0 )
for ( int c = 0 ; c < size ; ++ c )
PROPAGATION_STEP ( - stride );
for ( int c = 1 ; c < size ; ++ c )
PROPAGATION_STEP ( - 1 );
for ( int c = size - 2 ; c >= 0 ; -- c )
PROPAGATION_STEP ( + 1 );
}
// Bottom to top propagation.
for ( int r = size - 2 ; r >= 0 ; -- r ) {
for ( int c = 0 ; c < size ; ++ c )
PROPAGATION_STEP ( + stride );
for ( int c = 1 ; c < size ; ++ c )
PROPAGATION_STEP ( - 1 );
for ( int c = size - 2 ; c >= 0 ; -- c )
PROPAGATION_STEP ( + 1 );
}
#undef PROPAGATION_STEP
}
}
#endif // SUPPORT_USE_AGG_RASTERIZER
2018-09-17 15:12:13 +02:00
#if 0
2017-04-05 09:56:59 +02:00
// Get some internal point of an expolygon, to be used as a representative
// sample to test, whether this island is inside another island.
2018-09-17 15:12:13 +02:00
//FIXME this was quick, but not sufficiently robust.
2017-04-05 09:56:59 +02:00
static Point island_sample(const ExPolygon &expoly)
{
// Find the lowest point lexicographically.
const Point *pt_min = &expoly.contour.points.front();
for (size_t i = 1; i < expoly.contour.points.size(); ++ i)
if (expoly.contour.points[i] < *pt_min)
pt_min = &expoly.contour.points[i];
// Lowest corner will always be convex, in worst case denegenerate with zero angle.
const Point &p1 = (pt_min == &expoly.contour.points.front()) ? expoly.contour.points.back() : *(pt_min - 1);
const Point &p2 = *pt_min;
const Point &p3 = (pt_min == &expoly.contour.points.back()) ? expoly.contour.points.front() : *(pt_min + 1);
Vector v = (p3 - p2) + (p1 - p2);
2018-08-17 15:53:43 +02:00
double l2 = double(v(0))*double(v(0))+double(v(1))*double(v(1));
2017-04-05 09:56:59 +02:00
if (l2 == 0.)
return p2;
double coef = 20. / sqrt(l2);
2018-08-17 15:53:43 +02:00
return Point(p2(0) + coef * v(0), p2(1) + coef * v(1));
2017-04-05 09:56:59 +02:00
}
2018-09-17 15:12:13 +02:00
#endif
2017-04-05 09:56:59 +02:00
2018-09-17 15:12:13 +02:00
// Sample one internal point per expolygon.
// FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
2017-04-05 09:56:59 +02:00
static Points island_samples ( const ExPolygons & expolygons )
{
Points pts ;
pts . reserve ( expolygons . size ());
for ( const ExPolygon & expoly : expolygons )
if ( expoly . contour . points . size () > 2 ) {
#if 0
pts.push_back(island_sample(expoly));
#else
Polygons polygons = offset ( expoly , - 20.f );
for ( const Polygon & poly : polygons )
if ( ! poly . points . empty ()) {
2021-02-19 18:50:44 +01:00
// Take a small fixed number of samples of this polygon for robustness.
int num_points = int ( poly . points . size ());
int num_samples = std :: min ( num_points , 4 );
int stride = num_points / num_samples ;
for ( int i = 0 ; i < num_points ; i += stride )
pts . push_back ( poly . points [ i ]);
2017-04-05 09:56:59 +02:00
break ;
}
#endif
}
// Sort the points lexicographically, so a binary search could be used to locate points inside a bounding box.
std :: sort ( pts . begin (), pts . end ());
return pts ;
}
2021-03-23 11:06:31 +01:00
SupportMaterialStyle m_style ;
2017-06-08 14:02:37 +02:00
const Polygons * m_support_polygons ;
const Polygons * m_trimming_polygons ;
Polygons m_support_polygons_rotated ;
Polygons m_trimming_polygons_rotated ;
// Angle in radians, by which the whole support is rotated.
coordf_t m_support_angle ;
// X spacing of the support lines parallel with the Y axis.
2017-04-05 09:56:59 +02:00
coordf_t m_support_spacing ;
2021-04-12 14:56:30 +02:00
coordf_t m_extrusion_width ;
// For snug supports: Morphological closing of support areas.
coordf_t m_support_material_closing_radius ;
2017-04-05 09:56:59 +02:00
2021-02-19 18:50:44 +01:00
#ifdef SUPPORT_USE_AGG_RASTERIZER
Vec2i m_grid_size ;
double m_pixel_size ;
BoundingBox m_bbox ;
std :: vector < unsigned char > m_grid2 ;
#else // SUPPORT_USE_AGG_RASTERIZER
Slic3r :: EdgeGrid :: Grid m_grid ;
#endif // SUPPORT_USE_AGG_RASTERIZER
2018-12-12 10:12:35 +01:00
#ifdef SLIC3R_DEBUG
// support for deserialization of m_support_polygons, m_trimming_polygons
Polygons m_support_polygons_deserialized ;
Polygons m_trimming_polygons_deserialized ;
#endif /* SLIC3R_DEBUG */
2017-04-05 09:56:59 +02:00
};
2018-09-17 15:12:13 +02:00
namespace SupportMaterialInternal {
static inline bool has_bridging_perimeters ( const ExtrusionLoop & loop )
{
for ( const ExtrusionPath & ep : loop . paths )
if ( ep . role () == erOverhangPerimeter && ! ep . polyline . empty ())
2021-02-11 08:42:45 +01:00
return int ( ep . size ()) >= ( ep . is_closed () ? 3 : 2 );
2019-09-24 16:01:01 +02:00
return false ;
2018-09-17 15:12:13 +02:00
}
static bool has_bridging_perimeters ( const ExtrusionEntityCollection & perimeters )
{
for ( const ExtrusionEntity * ee : perimeters . entities ) {
if ( ee -> is_collection ()) {
for ( const ExtrusionEntity * ee2 : static_cast < const ExtrusionEntityCollection *> ( ee ) -> entities ) {
assert ( ! ee2 -> is_collection ());
if ( ee2 -> is_loop ())
if ( has_bridging_perimeters ( * static_cast < const ExtrusionLoop *> ( ee2 )))
return true ;
}
} else if ( ee -> is_loop () && has_bridging_perimeters ( * static_cast < const ExtrusionLoop *> ( ee )))
return true ;
}
return false ;
}
static bool has_bridging_fills ( const ExtrusionEntityCollection & fills )
{
for ( const ExtrusionEntity * ee : fills . entities ) {
assert ( ee -> is_collection ());
for ( const ExtrusionEntity * ee2 : static_cast < const ExtrusionEntityCollection *> ( ee ) -> entities ) {
assert ( ! ee2 -> is_collection ());
assert ( ! ee2 -> is_loop ());
if ( ee2 -> role () == erBridgeInfill )
return true ;
}
}
return false ;
}
static bool has_bridging_extrusions ( const Layer & layer )
{
for ( const LayerRegion * region : layer . regions ()) {
if ( SupportMaterialInternal :: has_bridging_perimeters ( region -> perimeters ))
return true ;
if ( region -> fill_surfaces . has ( stBottomBridge ) && has_bridging_fills ( region -> fills ))
return true ;
}
return false ;
}
static inline void collect_bridging_perimeter_areas ( const ExtrusionLoop & loop , const float expansion_scaled , Polygons & out )
{
assert ( expansion_scaled >= 0.f );
for ( const ExtrusionPath & ep : loop . paths )
if ( ep . role () == erOverhangPerimeter && ! ep . polyline . empty ()) {
2019-06-20 16:15:09 +02:00
float exp = 0.5f * ( float ) scale_ ( ep . width ) + expansion_scaled ;
2018-09-17 15:12:13 +02:00
if ( ep . is_closed ()) {
if ( ep . size () >= 3 ) {
// This is a complete loop.
// Add the outer contour first.
Polygon poly ;
poly . points = ep . polyline . points ;
poly . points . pop_back ();
if ( poly . area () < 0 )
poly . reverse ();
polygons_append ( out , offset ( poly , exp , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
Polygons holes = offset ( poly , - exp , SUPPORT_SURFACES_OFFSET_PARAMETERS );
polygons_reverse ( holes );
polygons_append ( out , holes );
}
} else if ( ep . size () >= 2 ) {
// Offset the polyline.
polygons_append ( out , offset ( ep . polyline , exp , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
}
}
}
static void collect_bridging_perimeter_areas ( const ExtrusionEntityCollection & perimeters , const float expansion_scaled , Polygons & out )
{
for ( const ExtrusionEntity * ee : perimeters . entities ) {
if ( ee -> is_collection ()) {
for ( const ExtrusionEntity * ee2 : static_cast < const ExtrusionEntityCollection *> ( ee ) -> entities ) {
assert ( ! ee2 -> is_collection ());
if ( ee2 -> is_loop ())
collect_bridging_perimeter_areas ( * static_cast < const ExtrusionLoop *> ( ee2 ), expansion_scaled , out );
}
} else if ( ee -> is_loop ())
collect_bridging_perimeter_areas ( * static_cast < const ExtrusionLoop *> ( ee ), expansion_scaled , out );
}
}
static void remove_bridges_from_contacts (
const PrintConfig & print_config ,
const Layer & lower_layer ,
const Polygons & lower_layer_polygons ,
LayerRegion * layerm ,
float fw ,
Polygons & contact_polygons )
{
// compute the area of bridging perimeters
Polygons bridges ;
{
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
Polygons lower_grown_slices = offset ( lower_layer_polygons ,
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
2021-05-05 18:13:58 +02:00
0.5f * float ( scale_ ( print_config . nozzle_diameter . get_at ( layerm -> region (). config (). perimeter_extruder - 1 ))),
2018-09-17 15:12:13 +02:00
SUPPORT_SURFACES_OFFSET_PARAMETERS );
// Collect perimeters of this layer.
//FIXME split_at_first_point() could split a bridge mid-way
#if 0
Polylines overhang_perimeters = layerm->perimeters.as_polylines();
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
for (Polyline &polyline : overhang_perimeters)
polyline.points[0].x += 1;
// Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
#else
Polylines overhang_perimeters = diff_pl ( layerm -> perimeters . as_polylines (), lower_grown_slices );
#endif
// only consider straight overhangs
// only consider overhangs having endpoints inside layer's slices
// convert bridging polylines into polygons by inflating them with their thickness
// since we're dealing with bridges, we can't assume width is larger than spacing,
// so we take the largest value and also apply safety offset to be ensure no gaps
// are left in between
2021-03-09 12:30:31 +01:00
Flow perimeter_bridge_flow = layerm -> bridging_flow ( frPerimeter );
float w = float ( std :: max ( perimeter_bridge_flow . scaled_width (), perimeter_bridge_flow . scaled_spacing ()));
2018-09-17 15:12:13 +02:00
for ( Polyline & polyline : overhang_perimeters )
if ( polyline . is_straight ()) {
// This is a bridge
polyline . extend_start ( fw );
polyline . extend_end ( fw );
// Is the straight perimeter segment supported at both sides?
2020-12-11 15:02:53 +01:00
Point pts [ 2 ] = { polyline . first_point (), polyline . last_point () };
bool supported [ 2 ] = { false , false };
2021-05-03 11:39:53 +02:00
for ( size_t i = 0 ; i < lower_layer . lslices . size () && ! ( supported [ 0 ] && supported [ 1 ]); ++ i )
2020-12-11 15:02:53 +01:00
for ( int j = 0 ; j < 2 ; ++ j )
if ( ! supported [ j ] && lower_layer . lslices_bboxes [ i ]. contains ( pts [ j ]) && lower_layer . lslices [ i ]. contains ( pts [ j ]))
supported [ j ] = true ;
if ( supported [ 0 ] && supported [ 1 ])
// Offset a polyline into a thick line.
polygons_append ( bridges , offset ( polyline , 0.5f * w + 10.f ));
2018-09-17 15:12:13 +02:00
}
bridges = union_ ( bridges );
}
// remove the entire bridges and only support the unsupported edges
//FIXME the brided regions are already collected as layerm->bridged. Use it?
for ( const Surface & surface : layerm -> fill_surfaces . surfaces )
if ( surface . surface_type == stBottomBridge && surface . bridge_angle != - 1 )
polygons_append ( bridges , surface . expolygon );
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
2018-12-12 10:12:35 +01:00
// Remove the unsupported ends of the bridges from the bridged areas.
2018-09-17 15:12:13 +02:00
//FIXME add supports at regular intervals to support long bridges!
2018-12-12 10:12:35 +01:00
bridges = diff ( bridges ,
2018-09-17 15:12:13 +02:00
// Offset unsupported edges into polygons.
2019-09-27 18:17:21 +02:00
offset ( layerm -> unsupported_bridge_edges , scale_ ( SUPPORT_MATERIAL_MARGIN ), SUPPORT_SURFACES_OFFSET_PARAMETERS ));
2018-12-12 10:12:35 +01:00
// Remove bridged areas from the supported areas.
2021-05-05 12:16:40 +02:00
contact_polygons = diff ( contact_polygons , bridges , ApplySafetyOffset :: Yes );
2021-02-25 12:29:08 +01:00
#ifdef SLIC3R_DEBUG
static int iRun = 0 ;
SVG :: export_expolygons ( debug_out_path ( "support-top-contacts-remove-bridges-run%d.svg" , iRun ++ ),
{ { { union_ex ( offset ( layerm -> unsupported_bridge_edges , scale_ ( SUPPORT_MATERIAL_MARGIN ), SUPPORT_SURFACES_OFFSET_PARAMETERS ), false ) }, { "unsupported_bridge_edges" , "orange" , 0.5f } },
{ { union_ex ( contact_polygons , false ) }, { "contact_polygons" , "blue" , 0.5f } },
{ { union_ex ( bridges , false ) }, { "bridges" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
2018-09-17 15:12:13 +02:00
}
}
2021-03-19 11:21:29 +01:00
std :: vector < Polygons > PrintObjectSupportMaterial :: buildplate_covered ( const PrintObject & object ) const
2018-12-12 10:12:35 +01:00
{
2017-03-22 15:35:50 +01:00
// Build support on a build plate only? If so, then collect and union all the surfaces below the current layer.
// Unfortunately this is an inherently serial process.
const bool buildplate_only = this -> build_plate_only ();
std :: vector < Polygons > buildplate_covered ;
if ( buildplate_only ) {
2021-03-19 11:21:29 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::buildplate_covered() - start" ;
2018-09-11 14:04:47 +02:00
buildplate_covered . assign ( object . layers (). size (), Polygons ());
for ( size_t layer_id = 1 ; layer_id < object . layers (). size (); ++ layer_id ) {
const Layer & lower_layer = * object . layers ()[ layer_id - 1 ];
2017-03-22 15:35:50 +01:00
// Merge the new slices with the preceding slices.
// Apply the safety offset to the newly added polygons, so they will connect
// with the polygons collected before,
// but don't apply the safety offset during the union operation as it would
// inflate the polygons over and over.
Polygons & covered = buildplate_covered [ layer_id ];
covered = buildplate_covered [ layer_id - 1 ];
2020-01-03 14:05:56 +01:00
polygons_append ( covered , offset ( lower_layer . lslices , scale_ ( 0.01 )));
2021-05-05 12:16:40 +02:00
covered = union_ ( covered );
2017-03-22 15:35:50 +01:00
}
2021-03-19 11:21:29 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::buildplate_covered() - end" ;
2017-03-22 15:35:50 +01:00
}
2021-03-19 11:21:29 +01:00
return buildplate_covered ;
}
struct SupportAnnotations
{
SupportAnnotations ( const PrintObject & object , const std :: vector < Polygons > & buildplate_covered ) :
enforcers_layers ( object . slice_support_enforcers ()),
blockers_layers ( object . slice_support_blockers ()),
buildplate_covered ( buildplate_covered )
{
// Append custom supports.
object . project_and_append_custom_facets ( false , EnforcerBlockerType :: ENFORCER , enforcers_layers );
object . project_and_append_custom_facets ( false , EnforcerBlockerType :: BLOCKER , blockers_layers );
}
std :: vector < ExPolygons > enforcers_layers ;
std :: vector < ExPolygons > blockers_layers ;
const std :: vector < Polygons >& buildplate_covered ;
};
struct SlicesMarginCache
{
float offset { - 1 };
// Trimming polygons, including possibly the "build plate only" mask.
Polygons polygons ;
// Trimming polygons, without the "build plate only" mask. If empty, use polygons.
Polygons all_polygons ;
};
static inline std :: tuple < Polygons , Polygons , Polygons , float > detect_overhangs (
const Layer & layer ,
const size_t layer_id ,
const Polygons & lower_layer_polygons ,
const PrintConfig & print_config ,
const PrintObjectConfig & object_config ,
SupportAnnotations & annotations ,
SlicesMarginCache & slices_margin ,
const double gap_xy
#ifdef SLIC3R_DEBUG
, size_t iRun
#endif // SLIC3R_DEBUG
)
{
// Snug overhang polygons.
Polygons overhang_polygons ;
// Expanded for stability, trimmed by gap_xy.
Polygons contact_polygons ;
// Enforcers projected to overhangs, trimmed
Polygons enforcer_polygons ;
const bool support_auto = object_config . support_material . value && object_config . support_material_auto . value ;
const bool buildplate_only = ! annotations . buildplate_covered . empty ();
// If user specified a custom angle threshold, convert it to radians.
// Zero means automatic overhang detection.
const double threshold_rad = ( object_config . support_material_threshold . value > 0 ) ?
M_PI * double ( object_config . support_material_threshold . value + 1 ) / 180. : // +1 makes the threshold inclusive
0. ;
float no_interface_offset = 0.f ;
if ( layer_id == 0 )
{
// This is the first object layer, so the object is being printed on a raft and
// we're here just to get the object footprint for the raft.
#if 0
// The following line was filling excessive holes in the raft, see GH #430
overhang_polygons = collect_slices_outer(layer);
#else
// Don't fill in the holes. The user may apply a higher raft_expansion if one wants a better 1st layer adhesion.
overhang_polygons = to_polygons ( layer . lslices );
#endif
// Expand for better stability.
contact_polygons = offset ( overhang_polygons , scaled < float > ( object_config . raft_expansion . value ));
}
else
{
// Generate overhang / contact_polygons for non-raft layers.
const Layer & lower_layer = * layer . lower_layer ;
const bool has_enforcer = ! annotations . enforcers_layers . empty () && ! annotations . enforcers_layers [ layer_id ]. empty ();
2021-03-19 13:39:20 +01:00
float fw = 0 ;
2021-03-19 11:21:29 +01:00
for ( LayerRegion * layerm : layer . regions ()) {
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
fw = float ( layerm -> flow ( frExternalPerimeter ). scaled_width ());
no_interface_offset = ( no_interface_offset == 0.f ) ? fw : std :: min ( no_interface_offset , fw );
float lower_layer_offset =
( layer_id < ( size_t ) object_config . support_material_enforce_layers . value ) ?
// Enforce a full possible support, ignore the overhang angle.
0.f :
( threshold_rad > 0. ?
// Overhang defined by an angle.
float ( scale_ ( lower_layer . height / tan ( threshold_rad ))) :
// Overhang defined by half the extrusion width.
0.5f * fw );
// Overhang polygons for this layer and region.
Polygons diff_polygons ;
2021-05-03 11:39:53 +02:00
Polygons layerm_polygons = to_polygons ( layerm -> slices . surfaces );
2021-03-19 11:21:29 +01:00
if ( lower_layer_offset == 0.f ) {
// Support everything.
diff_polygons = diff ( layerm_polygons , lower_layer_polygons );
if ( buildplate_only ) {
// Don't support overhangs above the top surfaces.
// This step is done before the contact surface is calculated by growing the overhang region.
diff_polygons = diff ( diff_polygons , annotations . buildplate_covered [ layer_id ]);
}
} else {
if ( support_auto ) {
// Get the regions needing a suport, collapse very tiny spots.
//FIXME cache the lower layer offset if this layer has multiple regions.
#if 1
//FIXME this solution will trigger stupid supports for sharp corners, see GH #4874
diff_polygons = offset2 (
diff ( layerm_polygons ,
offset2 ( lower_layer_polygons , - 0.5f * fw , lower_layer_offset + 0.5f * fw , SUPPORT_SURFACES_OFFSET_PARAMETERS )),
//FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
// no support at all for not so steep overhangs.
- 0.1f * fw , 0.1f * fw );
#else
diff_polygons =
diff ( layerm_polygons ,
offset ( lower_layer_polygons , lower_layer_offset , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
#endif
if ( buildplate_only && ! annotations . buildplate_covered [ layer_id ]. empty ()) {
// Don't support overhangs above the top surfaces.
// This step is done before the contact surface is calculated by growing the overhang region.
diff_polygons = diff ( diff_polygons , annotations . buildplate_covered [ layer_id ]);
}
if ( ! diff_polygons . empty ()) {
2021-05-03 11:39:53 +02:00
// Offset the support regions back to a full overhang, restrict them to the full overhang.
// This is done to increase size of the supporting columns below, as they are calculated by
// propagating these contact surfaces downwards.
diff_polygons = diff (
intersection ( offset ( diff_polygons , lower_layer_offset , SUPPORT_SURFACES_OFFSET_PARAMETERS ), layerm_polygons ),
lower_layer_polygons );
}
2021-03-19 11:21:29 +01:00
}
}
if ( diff_polygons . empty ())
continue ;
// Apply the "support blockers".
if ( ! annotations . blockers_layers . empty () && ! annotations . blockers_layers [ layer_id ]. empty ()) {
// Expand the blocker a bit. Custom blockers produce strips
// spanning just the projection between the two slices.
// Subtracting them as they are may leave unwanted narrow
// residues of diff_polygons that would then be supported.
diff_polygons = diff ( diff_polygons ,
2021-05-03 11:39:53 +02:00
offset ( union_ ( annotations . blockers_layers [ layer_id ]), float ( 1000. * SCALED_EPSILON )));
2021-03-19 11:21:29 +01:00
}
#ifdef SLIC3R_DEBUG
{
:: Slic3r :: SVG svg ( debug_out_path ( "support-top-contacts-raw-run%d-layer%d-region%d.svg" ,
iRun , layer_id ,
std :: find_if ( layer . regions (). begin (), layer . regions (). end (), [ layerm ]( const LayerRegion * other ){ return other == layerm ;}) - layer . regions (). begin ()),
get_extents ( diff_polygons ));
Slic3r :: ExPolygons expolys = union_ex ( diff_polygons , false );
svg . draw ( expolys );
}
#endif /* SLIC3R_DEBUG */
if ( object_config . dont_support_bridges )
SupportMaterialInternal :: remove_bridges_from_contacts (
print_config , lower_layer , lower_layer_polygons , layerm , fw , diff_polygons );
if ( diff_polygons . empty ())
continue ;
#ifdef SLIC3R_DEBUG
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg" ,
iRun , layer_id ,
std :: find_if ( layer . regions (). begin (), layer . regions (). end (), [ layerm ]( const LayerRegion * other ){ return other == layerm ;}) - layer . regions (). begin (),
layer . print_z ),
union_ex ( diff_polygons , false ));
#endif /* SLIC3R_DEBUG */
//FIXME the overhang_polygons are used to construct the support towers as well.
//if (this->has_contact_loops())
// Store the exact contour of the overhang for the contact loops.
polygons_append ( overhang_polygons , diff_polygons );
// Let's define the required contact area by using a max gap of half the upper
// extrusion width and extending the area according to the configured margin.
// We increment the area in steps because we don't want our support to overflow
// on the other side of the object (if it's very thin).
{
//FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
//FIXME one should trim with the layer span colliding with the support layer, this layer
// may be lower than lower_layer, so the support area needed may need to be actually bigger!
// For the same reason, the non-bridging support area may be smaller than the bridging support area!
float slices_margin_offset = std :: min ( lower_layer_offset , float ( scale_ ( gap_xy )));
if ( slices_margin . offset != slices_margin_offset ) {
slices_margin . offset = slices_margin_offset ;
slices_margin . polygons = ( slices_margin_offset == 0.f ) ?
lower_layer_polygons :
2021-05-03 11:39:53 +02:00
offset2 ( lower_layer . lslices , - no_interface_offset * 0.5f , slices_margin_offset + no_interface_offset * 0.5f , SUPPORT_SURFACES_OFFSET_PARAMETERS );
2021-03-19 11:21:29 +01:00
if ( buildplate_only && ! annotations . buildplate_covered [ layer_id ]. empty ()) {
if ( has_enforcer )
// Make a backup of trimming polygons before enforcing "on build plate only".
slices_margin . all_polygons = slices_margin . polygons ;
// Trim the inflated contact surfaces by the top surfaces as well.
slices_margin . polygons = union_ ( slices_margin . polygons , annotations . buildplate_covered [ layer_id ]);
}
}
// Offset the contact polygons outside.
#if 0
for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) {
diff_polygons = diff(
offset(
diff_polygons,
scaled<float>(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS),
ClipperLib::jtRound,
// round mitter limit
scale_(0.05)),
2021-03-23 11:06:31 +01:00
slices_margin.polygons);
2021-03-19 11:21:29 +01:00
}
#else
diff_polygons = diff ( diff_polygons , slices_margin . polygons );
#endif
}
polygons_append ( contact_polygons , diff_polygons );
} // for each layer.region
if ( has_enforcer ) {
// Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
#ifdef SLIC3R_DEBUG
2021-05-03 11:39:53 +02:00
ExPolygons enforcers_united = union_ex ( annotations . enforcers_layers [ layer_id ]);
2021-03-19 11:21:29 +01:00
#endif // SLIC3R_DEBUG
2021-05-03 11:39:53 +02:00
enforcer_polygons = diff ( intersection ( layer . lslices , annotations . enforcers_layers [ layer_id ]),
2021-03-19 11:21:29 +01:00
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
offset ( lower_layer_polygons , 0.05f * fw , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-top-contacts-enforcers-run%d-layer%d-z%f.svg" , iRun , layer_id , layer . print_z ),
{ { layer . lslices , { "layer.lslices" , "gray" , 0.2f } },
{ { union_ex ( lower_layer_polygons , false ) }, { "lower_layer_polygons" , "green" , 0.5f } },
{ enforcers_united , { "enforcers" , "blue" , 0.5f } },
{ { union_ex ( enforcer_polygons , true ) }, { "new_contacts" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
polygons_append ( overhang_polygons , enforcer_polygons );
polygons_append ( contact_polygons , diff ( enforcer_polygons , slices_margin . all_polygons . empty () ? slices_margin . polygons : slices_margin . all_polygons ));
}
}
return std :: make_tuple ( std :: move ( overhang_polygons ), std :: move ( contact_polygons ), std :: move ( enforcer_polygons ), no_interface_offset );
}
static inline std :: pair < PrintObjectSupportMaterial :: MyLayer * , PrintObjectSupportMaterial :: MyLayer *> new_contact_layer (
const PrintConfig & print_config ,
const PrintObjectConfig & object_config ,
const SlicingParameters & slicing_params ,
const Layer & layer ,
std :: deque < PrintObjectSupportMaterial :: MyLayer > & layer_storage ,
tbb :: spin_mutex & layer_storage_mutex )
{
double print_z , bottom_z , height ;
PrintObjectSupportMaterial :: MyLayer * bridging_layer = nullptr ;
assert ( layer . id () >= slicing_params . raft_layers ());
size_t layer_id = layer . id () - slicing_params . raft_layers ();
if ( layer_id == 0 ) {
// This is a raft contact layer sitting directly on the print bed.
assert ( slicing_params . has_raft ());
print_z = slicing_params . raft_contact_top_z ;
bottom_z = slicing_params . raft_interface_top_z ;
height = slicing_params . contact_raft_layer_height ;
} else if ( slicing_params . soluble_interface ) {
// Align the contact surface height with a layer immediately below the supported layer.
// Interface layer will be synchronized with the object.
print_z = layer . bottom_z ();
height = layer . lower_layer -> height ;
bottom_z = ( layer_id == 1 ) ? slicing_params . object_print_z_min : layer . lower_layer -> lower_layer -> print_z ;
} else {
2021-04-08 15:29:40 +02:00
print_z = layer . bottom_z () - slicing_params . gap_support_object ;
2021-03-19 11:21:29 +01:00
bottom_z = print_z ;
height = 0. ;
// Ignore this contact area if it's too low.
// Don't want to print a layer below the first layer height as it may not stick well.
//FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
// and it may actually make sense to do it with a thinner layer than the first layer height.
if ( print_z < slicing_params . first_print_layer_height - EPSILON ) {
// This contact layer is below the first layer height, therefore not printable. Don't support this surface.
return std :: pair < PrintObjectSupportMaterial :: MyLayer * , PrintObjectSupportMaterial :: MyLayer *> ( nullptr , nullptr );
} else if ( print_z < slicing_params . first_print_layer_height + EPSILON ) {
// Align the layer with the 1st layer height.
print_z = slicing_params . first_print_layer_height ;
bottom_z = 0 ;
height = slicing_params . first_print_layer_height ;
} else {
// Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
// its height will be set adaptively later on.
}
// Contact layer will be printed with a normal flow, but
// it will support layers printed with a bridging flow.
if ( object_config . thick_bridges && SupportMaterialInternal :: has_bridging_extrusions ( layer )) {
coordf_t bridging_height = 0. ;
for ( const LayerRegion * region : layer . regions ())
2021-05-05 18:13:58 +02:00
bridging_height += region -> region (). bridging_height_avg ( print_config );
2021-03-19 11:21:29 +01:00
bridging_height /= coordf_t ( layer . regions (). size ());
coordf_t bridging_print_z = layer . print_z - bridging_height - slicing_params . gap_support_object ;
if ( bridging_print_z >= slicing_params . first_print_layer_height - EPSILON ) {
// Not below the first layer height means this layer is printable.
if ( print_z < slicing_params . first_print_layer_height + EPSILON ) {
// Align the layer with the 1st layer height.
bridging_print_z = slicing_params . first_print_layer_height ;
}
if ( bridging_print_z < print_z - EPSILON ) {
// Allocate the new layer.
bridging_layer = & layer_allocate ( layer_storage , layer_storage_mutex , PrintObjectSupportMaterial :: sltTopContact );
bridging_layer -> idx_object_layer_above = layer_id ;
bridging_layer -> print_z = bridging_print_z ;
if ( bridging_print_z == slicing_params . first_print_layer_height ) {
bridging_layer -> bottom_z = 0 ;
bridging_layer -> height = slicing_params . first_print_layer_height ;
}
else {
// Don't know the height yet.
bridging_layer -> bottom_z = bridging_print_z ;
bridging_layer -> height = 0 ;
}
}
}
}
}
PrintObjectSupportMaterial :: MyLayer & new_layer = layer_allocate ( layer_storage , layer_storage_mutex , PrintObjectSupportMaterial :: sltTopContact );
new_layer . idx_object_layer_above = layer_id ;
new_layer . print_z = print_z ;
new_layer . bottom_z = bottom_z ;
new_layer . height = height ;
return std :: make_pair ( & new_layer , bridging_layer );
}
static inline void fill_contact_layer (
PrintObjectSupportMaterial :: MyLayer & new_layer ,
size_t layer_id ,
const SlicingParameters & slicing_params ,
const PrintObjectConfig & object_config ,
const SlicesMarginCache & slices_margin ,
const Polygons & overhang_polygons ,
const Polygons & contact_polygons ,
const Polygons & enforcer_polygons ,
const Polygons & lower_layer_polygons ,
const Flow & support_material_flow ,
float no_interface_offset
#ifdef SLIC3R_DEBUG
, size_t iRun ,
const Layer & layer
#endif // SLIC3R_DEBUG
)
{
const SupportGridParams grid_params ( object_config , support_material_flow );
Polygons lower_layer_polygons_for_dense_interface_cache ;
auto lower_layer_polygons_for_dense_interface = [ & lower_layer_polygons_for_dense_interface_cache , & lower_layer_polygons , no_interface_offset ]() -> const Polygons & {
if ( lower_layer_polygons_for_dense_interface_cache . empty ())
lower_layer_polygons_for_dense_interface_cache =
offset2 ( lower_layer_polygons , - no_interface_offset * 0.5f , no_interface_offset * ( 0.6f + 0.5f ), SUPPORT_SURFACES_OFFSET_PARAMETERS );
return lower_layer_polygons_for_dense_interface_cache ;
};
// Stretch support islands into a grid, trim them.
SupportGridPattern support_grid_pattern ( & contact_polygons , & slices_margin . polygons , grid_params );
// 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
new_layer . contact_polygons = std :: make_unique < Polygons > ( support_grid_pattern . extract_support ( grid_params . expansion_to_propagate , true
#ifdef SLIC3R_DEBUG
, "top_contact_polygons" , iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
));
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
if ( layer_id == 0 || slicing_params . soluble_interface ) {
// if (no_interface_offset == 0.f) {
new_layer . polygons = support_grid_pattern . extract_support ( grid_params . expansion_to_slice , true
#ifdef SLIC3R_DEBUG
, "top_contact_polygons2" , iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
);
} else {
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
Polygons dense_interface_polygons = diff ( overhang_polygons , lower_layer_polygons_for_dense_interface ());
if ( ! dense_interface_polygons . empty ()) {
dense_interface_polygons =
diff (
// Regularize the contour.
offset ( dense_interface_polygons , no_interface_offset * 0.1f ),
slices_margin . polygons );
// Support islands, to be stretched into a grid.
//FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons,
// thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line.
// See for example GH #4874.
Polygons dense_interface_polygons_trimmed = intersection ( dense_interface_polygons , * new_layer . contact_polygons );
// Stretch support islands into a grid, trim them.
SupportGridPattern support_grid_pattern ( & dense_interface_polygons_trimmed , & slices_margin . polygons , grid_params );
new_layer . polygons = support_grid_pattern . extract_support ( grid_params . expansion_to_slice , false
#ifdef SLIC3R_DEBUG
, "top_contact_polygons3" , iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
);
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-top-contacts-final0-run%d-layer%d-z%f.svg" , iRun , layer_id , layer . print_z ),
{ { { union_ex ( lower_layer_polygons , false ) }, { "lower_layer_polygons" , "gray" , 0.2f } },
{ { union_ex ( * new_layer . contact_polygons , false ) }, { "new_layer.contact_polygons" , "yellow" , 0.5f } },
{ { union_ex ( slices_margin . polygons , false ) }, { "slices_margin_cached" , "blue" , 0.5f } },
{ { union_ex ( dense_interface_polygons , false ) }, { "dense_interface_polygons" , "green" , 0.5f } },
{ { union_ex ( new_layer . polygons , true ) }, { "new_layer.polygons" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
//support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z));
SVG :: export_expolygons ( debug_out_path ( "support-top-contacts-final0-run%d-layer%d-z%f.svg" , iRun , layer_id , layer . print_z ),
{ { { union_ex ( lower_layer_polygons , false ) }, { "lower_layer_polygons" , "gray" , 0.2f } },
{ { union_ex ( * new_layer . contact_polygons , false ) }, { "new_layer.contact_polygons" , "yellow" , 0.5f } },
{ { union_ex ( contact_polygons , false ) }, { "contact_polygons" , "blue" , 0.5f } },
{ { union_ex ( dense_interface_polygons , false ) }, { "dense_interface_polygons" , "green" , 0.5f } },
{ { union_ex ( new_layer . polygons , true ) }, { "new_layer.polygons" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
}
}
if ( ! enforcer_polygons . empty () && ! slices_margin . all_polygons . empty () && layer_id > 0 ) {
// Support enforcers used together with support enforcers. The support enforcers need to be handled separately from the rest of the support.
{
SupportGridPattern support_grid_pattern ( & enforcer_polygons , & slices_margin . all_polygons , grid_params );
// 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
new_layer . enforcer_polygons = std :: make_unique < Polygons > ( support_grid_pattern . extract_support ( grid_params . expansion_to_propagate , true
#ifdef SLIC3R_DEBUG
, "top_contact_polygons4" , iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
));
}
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
Polygons dense_interface_polygons = diff ( enforcer_polygons , lower_layer_polygons_for_dense_interface ());
if ( ! dense_interface_polygons . empty ()) {
dense_interface_polygons =
diff (
// Regularize the contour.
offset ( dense_interface_polygons , no_interface_offset * 0.1f ),
slices_margin . all_polygons );
// Support islands, to be stretched into a grid.
//FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons,
// thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line.
// See for example GH #4874.
Polygons dense_interface_polygons_trimmed = intersection ( dense_interface_polygons , * new_layer . enforcer_polygons );
SupportGridPattern support_grid_pattern ( & dense_interface_polygons_trimmed , & slices_margin . all_polygons , grid_params );
// Extend the polygons to extrude with the contact polygons of support enforcers.
bool needs_union = ! new_layer . polygons . empty ();
append ( new_layer . polygons , support_grid_pattern . extract_support ( grid_params . expansion_to_slice , false
#ifdef SLIC3R_DEBUG
, "top_contact_polygons5" , iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
));
if ( needs_union )
new_layer . polygons = union_ ( new_layer . polygons );
}
}
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-top-contacts-final0-run%d-layer%d-z%f.svg" , iRun , layer_id , layer . print_z ),
{ { { union_ex ( lower_layer_polygons , false ) }, { "lower_layer_polygons" , "gray" , 0.2f } },
{ { union_ex ( * new_layer . contact_polygons , false ) }, { "new_layer.contact_polygons" , "yellow" , 0.5f } },
{ { union_ex ( contact_polygons , false ) }, { "contact_polygons" , "blue" , 0.5f } },
{ { union_ex ( overhang_polygons , false ) }, { "overhang_polygons" , "green" , 0.5f } },
{ { union_ex ( new_layer . polygons , true ) }, { "new_layer.polygons" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
// Store the overhang polygons.
// The overhang polygons are used in the path generator for planning of the contact loops.
// if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
new_layer . overhang_polygons = std :: make_unique < Polygons > ( std :: move ( overhang_polygons ));
if ( ! enforcer_polygons . empty ())
new_layer . enforcer_polygons = std :: make_unique < Polygons > ( std :: move ( enforcer_polygons ));
}
// Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
// the top contact layer is merged into the bottom contact layer.
static void merge_contact_layers ( const SlicingParameters & slicing_params , double support_layer_height_min , PrintObjectSupportMaterial :: MyLayersPtr & layers )
{
// Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z.
std :: sort ( layers . begin (), layers . end (), []( const PrintObjectSupportMaterial :: MyLayer * l1 , const PrintObjectSupportMaterial :: MyLayer * l2 ) { return l1 -> print_z < l2 -> print_z ; });
int i = 0 ;
int k = 0 ;
{
// Find the span of layers, which are to be printed at the first layer height.
int j = 0 ;
for (; j < ( int ) layers . size () && layers [ j ] -> print_z < slicing_params . first_print_layer_height + support_layer_height_min - EPSILON ; ++ j );
if ( j > 0 ) {
// Merge the layers layers (0) to (j - 1) into the layers[0].
PrintObjectSupportMaterial :: MyLayer & dst = * layers . front ();
for ( int u = 1 ; u < j ; ++ u )
dst . merge ( std :: move ( * layers [ u ]));
// Snap the first layer to the 1st layer height.
dst . print_z = slicing_params . first_print_layer_height ;
dst . height = slicing_params . first_print_layer_height ;
dst . bottom_z = 0 ;
++ k ;
}
i = j ;
}
for (; i < int ( layers . size ()); ++ k ) {
// Find the span of layers closer than m_support_layer_height_min.
int j = i + 1 ;
coordf_t zmax = layers [ i ] -> print_z + support_layer_height_min + EPSILON ;
for (; j < ( int ) layers . size () && layers [ j ] -> print_z < zmax ; ++ j ) ;
if ( i + 1 < j ) {
// Merge the layers layers (i + 1) to (j - 1) into the layers[i].
PrintObjectSupportMaterial :: MyLayer & dst = * layers [ i ];
for ( int u = i + 1 ; u < j ; ++ u )
dst . merge ( std :: move ( * layers [ u ]));
}
if ( k < i )
layers [ k ] = layers [ i ];
i = j ;
}
if ( k < ( int ) layers . size ())
layers . erase ( layers . begin () + k , layers . end ());
}
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
PrintObjectSupportMaterial :: MyLayersPtr PrintObjectSupportMaterial :: top_contact_layers (
const PrintObject & object , const std :: vector < Polygons > & buildplate_covered , MyLayerStorage & layer_storage ) const
{
#ifdef SLIC3R_DEBUG
static int iRun = 0 ;
++ iRun ;
# define SLIC3R_IRUN , iRun
#endif /* SLIC3R_DEBUG */
// Slice support enforcers / support blockers.
SupportAnnotations annotations ( object , buildplate_covered );
// Output layers, sorted by top Z.
MyLayersPtr contact_out ;
2017-03-22 15:35:50 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - start" ;
2016-10-16 16:30:56 +02:00
// Determine top contact areas.
2017-01-05 09:14:59 +01:00
// If generating raft only (no support), only calculate top contact areas for the 0th layer.
// If having a raft, start with 0th layer, otherwise with 1st layer.
// Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
// So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
2017-03-22 15:35:50 +01:00
size_t num_layers = this -> has_support () ? object . layer_count () : 1 ;
2018-09-17 15:12:13 +02:00
// For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow,
// and the other for the overhangs extruded with a normal flow.
contact_out . assign ( num_layers * 2 , nullptr );
2017-03-22 15:35:50 +01:00
tbb :: spin_mutex layer_storage_mutex ;
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( this -> has_raft () ? 0 : 1 , num_layers ),
2021-03-19 13:39:20 +01:00
[ this , & object , & annotations , & layer_storage , & layer_storage_mutex , & contact_out ]
2018-09-17 15:12:13 +02:00
( const tbb :: blocked_range < size_t >& range ) {
2017-03-22 15:35:50 +01:00
for ( size_t layer_id = range . begin (); layer_id < range . end (); ++ layer_id )
2017-01-05 09:14:59 +01:00
{
2021-03-19 11:21:29 +01:00
const Layer & layer = * object . layers ()[ layer_id ];
Polygons lower_layer_polygons = ( layer_id == 0 ) ? Polygons () : to_polygons ( object . layers ()[ layer_id - 1 ] -> lslices );
SlicesMarginCache slices_margin ;
2017-03-22 15:35:50 +01:00
2021-03-19 11:21:29 +01:00
auto [ overhang_polygons , contact_polygons , enforcer_polygons , no_interface_offset ] =
detect_overhangs ( layer , layer_id , lower_layer_polygons , * m_print_config , * m_object_config , annotations , slices_margin , m_support_params . gap_xy
#ifdef SLIC3R_DEBUG
, iRun
#endif // SLIC3R_DEBUG
);
2020-04-27 17:43:34 +02:00
2018-09-17 15:12:13 +02:00
// Now apply the contact areas to the layer where they need to be made.
2017-03-22 15:35:50 +01:00
if ( ! contact_polygons . empty ()) {
2021-03-19 11:21:29 +01:00
auto [ new_layer , bridging_layer ] = new_contact_layer ( * m_print_config , * m_object_config , m_slicing_params , layer , layer_storage , layer_storage_mutex );
if ( new_layer ) {
fill_contact_layer ( * new_layer , layer_id , m_slicing_params ,
* m_object_config , slices_margin , overhang_polygons , contact_polygons , enforcer_polygons , lower_layer_polygons ,
m_support_params . support_material_flow , no_interface_offset
2018-12-12 10:12:35 +01:00
#ifdef SLIC3R_DEBUG
2021-03-19 11:21:29 +01:00
, iRun , layer
#endif // SLIC3R_DEBUG
);
contact_out [ layer_id * 2 ] = new_layer ;
if ( bridging_layer != nullptr ) {
bridging_layer -> polygons = new_layer -> polygons ;
bridging_layer -> contact_polygons = std :: make_unique < Polygons > ( * new_layer -> contact_polygons );
bridging_layer -> overhang_polygons = std :: make_unique < Polygons > ( * new_layer -> overhang_polygons );
if ( new_layer -> enforcer_polygons )
bridging_layer -> enforcer_polygons = std :: make_unique < Polygons > ( * new_layer -> enforcer_polygons );
contact_out [ layer_id * 2 + 1 ] = bridging_layer ;
2018-09-17 15:12:13 +02:00
}
}
2017-03-22 15:35:50 +01:00
}
2016-10-13 16:00:22 +02:00
}
2017-03-22 15:35:50 +01:00
});
2018-09-17 15:12:13 +02:00
2017-03-22 15:35:50 +01:00
// Compress contact_out, remove the nullptr items.
remove_nulls ( contact_out );
2018-09-17 15:12:13 +02:00
2021-03-19 11:21:29 +01:00
merge_contact_layers ( m_slicing_params , m_support_params . support_layer_height_min , contact_out );
2018-09-17 15:12:13 +02:00
2017-03-22 15:35:50 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end" ;
2016-10-13 16:00:22 +02:00
return contact_out ;
}
2021-03-19 11:21:29 +01:00
// Find the bottom contact layers above the top surfaces of this layer.
static inline PrintObjectSupportMaterial :: MyLayer * detect_bottom_contacts (
const SlicingParameters & slicing_params ,
const PrintObjectSupportMaterial :: SupportParams & support_params ,
const PrintObject & object ,
const Layer & layer ,
// Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height.
const PrintObjectSupportMaterial :: MyLayersPtr & top_contacts ,
// First top contact layer index overlapping with this new bottom interface layer.
size_t contact_idx ,
// To allocate a new layer from.
std :: deque < PrintObjectSupportMaterial :: MyLayer > & layer_storage ,
// To trim the support areas above this bottom interface layer with this newly created bottom interface layer.
std :: vector < Polygons > & layer_support_areas ,
// Support areas projected from top to bottom, starting with top support interfaces.
const Polygons & supports_projected
#ifdef SLIC3R_DEBUG
, size_t iRun
, const Polygons & polygons_new
#endif // SLIC3R_DEBUG
)
{
Polygons top = collect_region_slices_by_type ( layer , stTop );
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-bottom-layers-raw-%d-%lf.svg" , iRun , layer . print_z ),
{ { { union_ex ( top , false ) }, { "top" , "blue" , 0.5f } },
{ { union_ex ( supports_projected , true ) }, { "overhangs" , "magenta" , 0.5f } },
{ layer . lslices , { "layer.lslices" , "green" , 0.5f } },
{ { union_ex ( polygons_new , true ) }, { "polygons_new" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
// Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any
// top surfaces above layer.print_z falls onto this top surface.
// Touching are the contact surfaces supported exclusively by this top surfaces.
// Don't use a safety offset as it has been applied during insertion of polygons.
if ( top . empty ())
return nullptr ;
2021-05-05 12:16:40 +02:00
Polygons touching = intersection ( top , supports_projected );
2021-03-19 11:21:29 +01:00
if ( touching . empty ())
return nullptr ;
assert ( layer . id () >= slicing_params . raft_layers ());
size_t layer_id = layer . id () - slicing_params . raft_layers ();
// Allocate a new bottom contact layer.
PrintObjectSupportMaterial :: MyLayer & layer_new = layer_allocate ( layer_storage , PrintObjectSupportMaterial :: sltBottomContact );
// Grow top surfaces so that interface and support generation are generated
// with some spacing from object - it looks we don't need the actual
// top shapes so this can be done here
//FIXME calculate layer height based on the actual thickness of the layer:
// If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new . height = slicing_params . soluble_interface ?
// Align the interface layer with the object's layer height.
layer . upper_layer -> height :
// Place a bridge flow interface layer or the normal flow interface layer over the top surface.
support_params . support_material_bottom_interface_flow . height ();
layer_new . print_z = slicing_params . soluble_interface ? layer . upper_layer -> print_z :
layer . print_z + layer_new . height + slicing_params . gap_object_support ;
layer_new . bottom_z = layer . print_z ;
layer_new . idx_object_layer_below = layer_id ;
layer_new . bridging = ! slicing_params . soluble_interface && object . config (). thick_bridges ;
//FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
//FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
layer_new . polygons = offset ( touching , float ( support_params . support_material_flow . scaled_width ()), SUPPORT_SURFACES_OFFSET_PARAMETERS );
if ( ! slicing_params . soluble_interface ) {
// Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
// so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
for ( size_t top_idx = size_t ( std :: max < int > ( 0 , contact_idx ));
top_idx < top_contacts . size () && top_contacts [ top_idx ] -> print_z < layer_new . print_z + support_params . support_layer_height_min + EPSILON ;
++ top_idx ) {
if ( top_contacts [ top_idx ] -> print_z > layer_new . print_z - support_params . support_layer_height_min - EPSILON ) {
// A top layer has been found, which is close to the new bottom layer.
coordf_t diff = layer_new . print_z - top_contacts [ top_idx ] -> print_z ;
assert ( std :: abs ( diff ) <= support_params . support_layer_height_min + EPSILON );
if ( diff > 0. ) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert ( diff < layer_new . height + EPSILON );
assert ( layer_new . height - diff >= support_params . support_layer_height_min - EPSILON );
layer_new . print_z = top_contacts [ top_idx ] -> print_z ;
layer_new . height -= diff ;
}
else {
// The top contact layer is above this layer. One may either make this layer thicker or thinner.
// By making the layer thicker, one will decrease the number of discrete layers with the price of extruding a bit too thick bridges.
// By making the layer thinner, one adds one more discrete layer.
layer_new . print_z = top_contacts [ top_idx ] -> print_z ;
layer_new . height -= diff ;
}
break ;
}
}
}
#ifdef SLIC3R_DEBUG
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-bottom-contacts-%d-%lf.svg" , iRun , layer_new . print_z ),
union_ex ( layer_new . polygons , false ));
#endif /* SLIC3R_DEBUG */
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
//FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset ( touching , float ( SCALED_EPSILON ));
for ( int layer_id_above = layer_id + 1 ; layer_id_above < int ( object . total_layer_count ()); ++ layer_id_above ) {
const Layer & layer_above = * object . layers ()[ layer_id_above ];
if ( layer_above . print_z > layer_new . print_z - EPSILON )
break ;
if ( ! layer_support_areas [ layer_id_above ]. empty ()) {
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg" , iRun , layer . print_z , layer_above . print_z ),
{ { { union_ex ( touching , false ) }, { "touching" , "blue" , 0.5f } },
{ { union_ex ( layer_support_areas [ layer_id_above ], true ) }, { "above" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
layer_support_areas [ layer_id_above ] = diff ( layer_support_areas [ layer_id_above ], touching );
#ifdef SLIC3R_DEBUG
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg" , iRun , layer . print_z , layer_above . print_z ),
union_ex ( layer_support_areas [ layer_id_above ], false ));
#endif /* SLIC3R_DEBUG */
}
}
return & layer_new ;
}
// Returns polygons to print + polygons to propagate downwards.
// Called twice: First for normal supports, possibly trimmed by "on build plate only", second for support enforcers not trimmed by "on build plate only".
static inline std :: pair < Polygons , Polygons > project_support_to_grid ( const Layer & layer , const SupportGridParams & grid_params , const Polygons & overhangs , Polygons * layer_buildplate_covered
#ifdef SLIC3R_DEBUG
, size_t iRun , size_t layer_id , const char * debug_name
#endif /* SLIC3R_DEBUG */
)
{
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
// Polygons trimming = union_(to_polygons(layer.slices), touching, true);
Polygons trimming = layer_buildplate_covered ? std :: move ( * layer_buildplate_covered ) : offset ( layer . lslices , float ( SCALED_EPSILON ));
2021-05-05 12:16:40 +02:00
Polygons overhangs_projection = diff ( overhangs , trimming );
2021-03-19 11:21:29 +01:00
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-support-areas-%s-raw-%d-%lf.svg" , debug_name , iRun , layer . print_z ),
{ { { union_ex ( trimming , false ) }, { "trimming" , "blue" , 0.5f } },
{ { union_ex ( overhangs_projection , true ) }, { "overhangs_projection" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
remove_sticks ( overhangs_projection );
remove_degenerate ( overhangs_projection );
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-support-areas-%s-raw-cleaned-%d-%lf.svg" , debug_name , iRun , layer . print_z ),
{ { { union_ex ( trimming , false ) }, { "trimming" , "blue" , 0.5f } },
{ { union_ex ( overhangs_projection , false ) }, { "overhangs_projection" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
SupportGridPattern support_grid_pattern ( & overhangs_projection , & trimming , grid_params );
tbb :: task_group task_group_inner ;
std :: pair < Polygons , Polygons > out ;
// 1) Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing
// to allow a placement of suppot zig-zag snake along the grid lines.
task_group_inner . run ([ & grid_params , & support_grid_pattern , & out
#ifdef SLIC3R_DEBUG
, & layer , layer_id , iRun , debug_name
#endif /* SLIC3R_DEBUG */
] {
out . first = support_grid_pattern . extract_support ( grid_params . expansion_to_slice , true
#ifdef SLIC3R_DEBUG
, ( std :: string ( debug_name ) + "_support_area" ). c_str (), iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
);
#ifdef SLIC3R_DEBUG
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-layer_support_area-gridded-%s-%d-%lf.svg" , debug_name , iRun , layer . print_z ),
union_ex ( out . first , false ));
#endif /* SLIC3R_DEBUG */
});
// 2) Support polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
task_group_inner . run ([ & grid_params , & support_grid_pattern , & out
#ifdef SLIC3R_DEBUG
, & layer , layer_id , & overhangs_projection , & trimming , iRun , debug_name
#endif /* SLIC3R_DEBUG */
] {
out . second = support_grid_pattern . extract_support ( grid_params . expansion_to_propagate , true
#ifdef SLIC3R_DEBUG
, "support_projection" , iRun , layer_id , layer . print_z
#endif // SLIC3R_DEBUG
);
#ifdef SLIC3R_DEBUG
Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-projection_new-gridded-%d-%lf.svg" , iRun , layer . print_z ),
union_ex ( out . second , false ));
#endif /* SLIC3R_DEBUG */
#ifdef SLIC3R_DEBUG
SVG :: export_expolygons ( debug_out_path ( "support-projection_new-gridded-%d-%lf.svg" , iRun , layer . print_z ),
{ { { union_ex ( trimming , false ) }, { "trimming" , "gray" , 0.5f } },
{ { union_ex ( overhangs_projection , true ) }, { "overhangs_projection" , "blue" , 0.5f } },
{ { union_ex ( out . second , true ) }, { "projection_new" , "red" , "black" , "" , scaled < coord_t > ( 0.1f ), 0.5f } } });
#endif /* SLIC3R_DEBUG */
});
task_group_inner . wait ();
return out ;
}
2017-01-05 09:14:59 +01:00
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
2016-11-29 19:30:59 +01:00
PrintObjectSupportMaterial :: MyLayersPtr PrintObjectSupportMaterial :: bottom_contact_layers_and_layer_support_areas (
2021-03-19 11:21:29 +01:00
const PrintObject & object , const MyLayersPtr & top_contacts , std :: vector < Polygons > & buildplate_covered ,
MyLayerStorage & layer_storage , std :: vector < Polygons > & layer_support_areas ) const
2016-10-13 16:00:22 +02:00
{
2021-03-19 11:21:29 +01:00
if ( top_contacts . empty ())
return MyLayersPtr ();
2016-11-28 17:36:50 +01:00
#ifdef SLIC3R_DEBUG
2021-03-19 11:21:29 +01:00
static size_t s_iRun = 0 ;
size_t iRun = s_iRun ++ ;
2016-11-28 17:36:50 +01:00
#endif /* SLIC3R_DEBUG */
2021-03-19 11:21:29 +01:00
//FIXME higher expansion_to_slice here? why?
//const auto expansion_to_slice = m_support_material_flow.scaled_spacing() / 2 + 25;
const SupportGridParams grid_params ( * m_object_config , m_support_params . support_material_flow );
const bool buildplate_only = ! buildplate_covered . empty ();
2016-11-29 19:30:59 +01:00
// Allocate empty surface areas, one per object layer.
layer_support_areas . assign ( object . total_layer_count (), Polygons ());
2016-10-13 16:00:22 +02:00
// find object top surfaces
// we'll use them to clip our support and detect where does it stick
2016-10-16 16:30:56 +02:00
MyLayersPtr bottom_contacts ;
2016-11-29 19:30:59 +01:00
2021-03-19 11:21:29 +01:00
// There is some support to be built, if there are non-empty top surfaces detected.
// Sum of unsupported contact areas above the current layer.print_z.
Polygons overhangs_projection ;
// Sum of unsupported enforcer contact areas above the current layer.print_z.
// Only used if "supports on build plate only" is enabled and both automatic and support enforcers are enabled.
Polygons enforcers_projection ;
// Last top contact layer visited when collecting the projection of contact areas.
int contact_idx = int ( top_contacts . size ()) - 1 ;
for ( int layer_id = int ( object . total_layer_count ()) - 2 ; layer_id >= 0 ; -- layer_id ) {
BOOST_LOG_TRIVIAL ( trace ) << "Support generator - bottom_contact_layers - layer " << layer_id ;
const Layer & layer = * object . get_layer ( layer_id );
// Collect projections of all contact areas above or at the same level as this top surface.
2021-02-19 18:50:44 +01:00
#ifdef SLIC3R_DEBUG
2021-03-19 11:21:29 +01:00
Polygons polygons_new ;
Polygons enforcers_new ;
2021-02-19 18:50:44 +01:00
#endif // SLIC3R_DEBUG
2021-03-19 11:21:29 +01:00
for (; contact_idx >= 0 && top_contacts [ contact_idx ] -> print_z > layer . print_z - EPSILON ; -- contact_idx ) {
MyLayer & top_contact = * top_contacts [ contact_idx ];
2021-02-19 18:50:44 +01:00
#ifndef SLIC3R_DEBUG
2021-03-19 11:21:29 +01:00
Polygons polygons_new ;
Polygons enforcers_new ;
2021-02-19 18:50:44 +01:00
#endif // SLIC3R_DEBUG
2021-03-19 11:21:29 +01:00
// Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions.
2017-01-20 15:21:05 +01:00
#if 0
2021-03-19 11:21:29 +01:00
// Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
polygons_append(polygons_new, offset(*top_contact.contact_polygons, SCALED_EPSILON));
if (top_contact.enforcer_polygons)
polygons_append(enforcers_new, offset(*top_contact.enforcer_polygons, SCALED_EPSILON));
2017-01-20 15:21:05 +01:00
#else
2021-03-19 11:21:29 +01:00
// Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
// than the grid cells.
polygons_append ( polygons_new , std :: move ( * top_contact . contact_polygons ));
if ( top_contact . enforcer_polygons )
polygons_append ( enforcers_new , std :: move ( * top_contact . enforcer_polygons ));
2017-01-20 15:21:05 +01:00
#endif
2021-03-19 11:21:29 +01:00
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
// Use a slight positive offset to overlap the touching regions.
polygons_append ( polygons_new , offset ( * top_contact . overhang_polygons , float ( SCALED_EPSILON )));
polygons_append ( overhangs_projection , union_ ( polygons_new ));
polygons_append ( enforcers_projection , enforcers_new );
2016-10-13 16:00:22 +02:00
}
2021-03-19 11:21:29 +01:00
if ( overhangs_projection . empty () && enforcers_projection . empty ())
continue ;
2016-11-23 15:51:47 +01:00
2021-03-19 11:21:29 +01:00
// Overhangs_projection will be filled in asynchronously, move it away.
Polygons overhangs_projection_raw = union_ ( std :: move ( overhangs_projection ));
Polygons enforcers_projection_raw = union_ ( std :: move ( enforcers_projection ));
tbb :: task_group task_group ;
const Polygons & overhangs_for_bottom_contacts = buildplate_only ? enforcers_projection_raw : overhangs_projection_raw ;
if ( ! overhangs_for_bottom_contacts . empty ())
// Find the bottom contact layers above the top surfaces of this layer.
task_group . run ([ this , & object , & layer , & top_contacts , contact_idx , & layer_storage , & layer_support_areas , & bottom_contacts , & overhangs_for_bottom_contacts
#ifdef SLIC3R_DEBUG
, iRun , & polygons_new
#endif // SLIC3R_DEBUG
] {
// Find the bottom contact layers above the top surfaces of this layer.
MyLayer * layer_new = detect_bottom_contacts (
m_slicing_params , m_support_params , object , layer , top_contacts , contact_idx , layer_storage , layer_support_areas , overhangs_for_bottom_contacts
#ifdef SLIC3R_DEBUG
, iRun , polygons_new
#endif // SLIC3R_DEBUG
);
if ( layer_new )
bottom_contacts . push_back ( layer_new );
});
Polygons & layer_support_area = layer_support_areas [ layer_id ];
Polygons * layer_buildplate_covered = buildplate_covered . empty () ? nullptr : & buildplate_covered [ layer_id ];
task_group . run ([ & grid_params , & overhangs_projection , & overhangs_projection_raw , & layer , & layer_support_area , layer_buildplate_covered
#ifdef SLIC3R_DEBUG
, iRun , layer_id
#endif /* SLIC3R_DEBUG */
] {
// buildplate_covered[layer_id] will be consumed here.
std :: tie ( layer_support_area , overhangs_projection ) = project_support_to_grid ( layer , grid_params , overhangs_projection_raw , layer_buildplate_covered
#ifdef SLIC3R_DEBUG
, iRun , layer_id , "general"
#endif /* SLIC3R_DEBUG */
);
});
Polygons layer_support_area_enforcers ;
if ( ! enforcers_projection . empty ())
// Project the enforcers polygons downwards, don't trim them with the "buildplate only" polygons.
task_group . run ([ & grid_params , & enforcers_projection , & enforcers_projection_raw , & layer , & layer_support_area_enforcers
#ifdef SLIC3R_DEBUG
, iRun , layer_id
#endif /* SLIC3R_DEBUG */
]{
std :: tie ( layer_support_area_enforcers , enforcers_projection ) = project_support_to_grid ( layer , grid_params , enforcers_projection_raw , nullptr
#ifdef SLIC3R_DEBUG
, iRun , layer_id , "enforcers"
#endif /* SLIC3R_DEBUG */
);
});
task_group . wait ();
if ( ! layer_support_area_enforcers . empty ()) {
if ( layer_support_area . empty ())
layer_support_area = std :: move ( layer_support_area_enforcers );
else
layer_support_area = union_ ( layer_support_area , layer_support_area_enforcers );
}
} // over all layers downwards
std :: reverse ( bottom_contacts . begin (), bottom_contacts . end ());
trim_support_layers_by_object ( object , bottom_contacts , m_slicing_params . gap_support_object , m_slicing_params . gap_object_support , m_support_params . gap_xy );
2016-10-16 16:30:56 +02:00
return bottom_contacts ;
2016-10-13 16:00:22 +02:00
}
2017-03-22 15:35:50 +01:00
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
// If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards.
2021-02-15 18:07:44 +01:00
template < typename IteratorType , typename IndexType , typename FN_HIGHER_EQUAL >
IndexType idx_higher_or_equal ( IteratorType begin , IteratorType end , IndexType idx , FN_HIGHER_EQUAL fn_higher_equal )
2017-03-22 15:35:50 +01:00
{
2021-02-03 15:12:53 +01:00
auto size = int ( end - begin );
if ( size == 0 ) {
2017-05-12 11:18:32 +02:00
idx = 0 ;
2021-02-15 18:07:44 +01:00
} else if ( idx == IndexType ( - 1 )) {
2017-03-22 15:35:50 +01:00
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0 ;
2021-02-03 15:12:53 +01:00
int idx_high = std :: max ( 0 , size - 1 );
2017-03-22 15:35:50 +01:00
while ( idx_low + 1 < idx_high ) {
int idx_mid = ( idx_low + idx_high ) / 2 ;
2021-02-03 15:12:53 +01:00
if ( fn_higher_equal ( begin [ idx_mid ]))
2017-03-22 15:35:50 +01:00
idx_high = idx_mid ;
else
idx_low = idx_mid ;
}
2021-02-03 15:12:53 +01:00
idx = fn_higher_equal ( begin [ idx_low ]) ? idx_low :
( fn_higher_equal ( begin [ idx_high ]) ? idx_high : size );
2017-03-22 15:35:50 +01:00
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
2021-02-08 11:58:03 +01:00
while ( int ( idx ) < size && ! fn_higher_equal ( begin [ idx ]))
2017-03-22 15:35:50 +01:00
++ idx ;
}
return idx ;
}
2021-02-15 18:07:44 +01:00
template < typename T , typename IndexType , typename FN_HIGHER_EQUAL >
IndexType idx_higher_or_equal ( const std :: vector < T >& vec , IndexType idx , FN_HIGHER_EQUAL fn_higher_equal )
2021-02-03 15:12:53 +01:00
{
return idx_higher_or_equal ( vec . begin (), vec . end (), idx , fn_higher_equal );
}
2017-03-22 15:35:50 +01:00
// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold.
// Find the first item with Z value <= of an internal threshold of fn_lower_equal.
// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1.
// If the initial idx is < -1, then use binary search.
// Otherwise search linearly downwards.
2021-02-03 15:12:53 +01:00
template < typename IT , typename FN_LOWER_EQUAL >
int idx_lower_or_equal ( IT begin , IT end , int idx , FN_LOWER_EQUAL fn_lower_equal )
2017-03-22 15:35:50 +01:00
{
2021-02-03 15:12:53 +01:00
auto size = int ( end - begin );
if ( size == 0 ) {
2017-05-12 11:18:32 +02:00
idx = - 1 ;
} else if ( idx < - 1 ) {
2017-03-22 15:35:50 +01:00
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0 ;
2021-02-03 15:12:53 +01:00
int idx_high = std :: max ( 0 , size - 1 );
2017-03-22 15:35:50 +01:00
while ( idx_low + 1 < idx_high ) {
int idx_mid = ( idx_low + idx_high ) / 2 ;
2021-02-03 15:12:53 +01:00
if ( fn_lower_equal ( begin [ idx_mid ]))
2017-03-22 15:35:50 +01:00
idx_low = idx_mid ;
else
idx_high = idx_mid ;
}
2021-02-03 15:12:53 +01:00
idx = fn_lower_equal ( begin [ idx_high ]) ? idx_high :
( fn_lower_equal ( begin [ idx_low ]) ? idx_low : - 1 );
2017-03-22 15:35:50 +01:00
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
2021-02-03 15:12:53 +01:00
while ( idx >= 0 && ! fn_lower_equal ( begin [ idx ]))
2017-03-22 15:35:50 +01:00
-- idx ;
}
return idx ;
}
2021-02-03 15:12:53 +01:00
template < typename T , typename FN_LOWER_EQUAL >
int idx_lower_or_equal ( const std :: vector < T *> & vec , int idx , FN_LOWER_EQUAL fn_lower_equal )
{
return idx_lower_or_equal ( vec . begin (), vec . end (), idx , fn_lower_equal );
}
2017-03-22 15:35:50 +01:00
2016-10-13 16:00:22 +02:00
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
2016-10-20 13:04:23 +02:00
void PrintObjectSupportMaterial :: trim_top_contacts_by_bottom_contacts (
2016-10-16 16:30:56 +02:00
const PrintObject & object , const MyLayersPtr & bottom_contacts , MyLayersPtr & top_contacts ) const
2016-10-13 16:00:22 +02:00
{
2017-03-22 15:35:50 +01:00
tbb :: parallel_for ( tbb :: blocked_range < int > ( 0 , int ( top_contacts . size ())),
2021-01-29 17:12:06 +01:00
[ & bottom_contacts , & top_contacts ]( const tbb :: blocked_range < int >& range ) {
2017-03-22 15:35:50 +01:00
int idx_bottom_overlapping_first = - 2 ;
// For all top contact layers, counting downwards due to the way idx_higher_or_equal caches the last index to avoid repeated binary search.
for ( int idx_top = range . end () - 1 ; idx_top >= range . begin (); -- idx_top ) {
MyLayer & layer_top = * top_contacts [ idx_top ];
// Find the first bottom layer overlapping with layer_top.
idx_bottom_overlapping_first = idx_lower_or_equal ( bottom_contacts , idx_bottom_overlapping_first , [ & layer_top ]( const MyLayer * layer_bottom ){ return layer_bottom -> bottom_print_z () - EPSILON <= layer_top . bottom_z ; });
// For all top contact layers overlapping with the thick bottom contact layer:
for ( int idx_bottom_overlapping = idx_bottom_overlapping_first ; idx_bottom_overlapping >= 0 ; -- idx_bottom_overlapping ) {
const MyLayer & layer_bottom = * bottom_contacts [ idx_bottom_overlapping ];
assert ( layer_bottom . bottom_print_z () - EPSILON <= layer_top . bottom_z );
if ( layer_top . print_z < layer_bottom . print_z + EPSILON ) {
// Layers overlap. Trim layer_top with layer_bottom.
layer_top . polygons = diff ( layer_top . polygons , layer_bottom . polygons );
} else
break ;
}
}
});
2016-10-13 16:00:22 +02:00
}
2016-10-20 13:04:23 +02:00
PrintObjectSupportMaterial :: MyLayersPtr PrintObjectSupportMaterial :: raft_and_intermediate_support_layers (
2016-10-13 16:00:22 +02:00
const PrintObject & object ,
2016-10-16 16:30:56 +02:00
const MyLayersPtr & bottom_contacts ,
const MyLayersPtr & top_contacts ,
2017-03-22 15:35:50 +01:00
MyLayerStorage & layer_storage ) const
2016-10-13 16:00:22 +02:00
{
2016-10-20 13:04:23 +02:00
MyLayersPtr intermediate_layers ;
2016-10-13 16:00:22 +02:00
// Collect and sort the extremes (bottoms of the top contacts and tops of the bottom contacts).
2017-03-28 13:46:31 +02:00
MyLayersPtr extremes ;
2016-10-13 16:00:22 +02:00
extremes . reserve ( top_contacts . size () + bottom_contacts . size ());
for ( size_t i = 0 ; i < top_contacts . size (); ++ i )
2016-10-20 13:04:23 +02:00
// Bottoms of the top contact layers. In case of non-soluble supports,
// the top contact layer thickness is not known yet.
2017-03-28 13:46:31 +02:00
extremes . push_back ( top_contacts [ i ]);
2016-10-13 16:00:22 +02:00
for ( size_t i = 0 ; i < bottom_contacts . size (); ++ i )
2016-10-20 13:04:23 +02:00
// Tops of the bottom contact layers.
2017-03-28 13:46:31 +02:00
extremes . push_back ( bottom_contacts [ i ]);
2016-10-20 13:04:23 +02:00
if ( extremes . empty ())
return intermediate_layers ;
2017-03-28 13:46:31 +02:00
auto layer_extreme_lower = []( const MyLayer * l1 , const MyLayer * l2 ) {
coordf_t z1 = l1 -> extreme_z ();
coordf_t z2 = l2 -> extreme_z ();
// If the layers are aligned, return the top contact surface first.
return z1 < z2 || ( z1 == z2 && l1 -> layer_type == PrintObjectSupportMaterial :: sltTopContact && l2 -> layer_type == PrintObjectSupportMaterial :: sltBottomContact );
};
std :: sort ( extremes . begin (), extremes . end (), layer_extreme_lower );
2016-10-13 16:00:22 +02:00
2017-05-12 11:18:32 +02:00
assert ( extremes . empty () ||
( extremes . front () -> extreme_z () > m_slicing_params . raft_interface_top_z - EPSILON &&
( m_slicing_params . raft_layers () == 1 || // only raft contact layer
extremes . front () -> layer_type == sltTopContact || // first extreme is a top contact layer
extremes . front () -> extreme_z () > m_slicing_params . first_print_layer_height - EPSILON )));
2016-10-20 13:04:23 +02:00
2017-01-11 18:05:03 +01:00
bool synchronize = this -> synchronize_layers ();
2017-01-05 09:14:59 +01:00
2017-03-28 13:46:31 +02:00
#ifdef _DEBUG
// Verify that the extremes are separated by m_support_layer_height_min.
for ( size_t i = 1 ; i < extremes . size (); ++ i ) {
assert ( extremes [ i ] -> extreme_z () - extremes [ i - 1 ] -> extreme_z () == 0. ||
2021-03-19 11:21:29 +01:00
extremes [ i ] -> extreme_z () - extremes [ i - 1 ] -> extreme_z () > m_support_params . support_layer_height_min - EPSILON );
2017-03-28 13:46:31 +02:00
assert ( extremes [ i ] -> extreme_z () - extremes [ i - 1 ] -> extreme_z () > 0. ||
2017-05-12 11:18:32 +02:00
extremes [ i ] -> layer_type == extremes [ i - 1 ] -> layer_type ||
( extremes [ i ] -> layer_type == sltBottomContact && extremes [ i - 1 ] -> layer_type == sltTopContact ));
2017-03-28 13:46:31 +02:00
}
#endif
2016-10-13 16:00:22 +02:00
// Generate intermediate layers.
2016-10-20 13:04:23 +02:00
// The first intermediate layer is the same as the 1st layer if there is no raft,
// or the bottom of the first intermediate layer is aligned with the bottom of the raft contact layer.
// Intermediate layers are always printed with a normal etrusion flow (non-bridging).
2017-01-05 09:14:59 +01:00
size_t idx_layer_object = 0 ;
2016-10-20 13:04:23 +02:00
for ( size_t idx_extreme = 0 ; idx_extreme < extremes . size (); ++ idx_extreme ) {
2017-05-12 11:18:32 +02:00
MyLayer * extr2 = extremes [ idx_extreme ];
coordf_t extr2z = extr2 -> extreme_z ();
if ( std :: abs ( extr2z - m_slicing_params . raft_interface_top_z ) < EPSILON ) {
// This is a raft contact layer, its height has been decided in this->top_contact_layers().
2017-03-28 13:46:31 +02:00
assert ( extr2 -> layer_type == sltTopContact );
2017-05-12 11:18:32 +02:00
continue ;
2017-03-27 14:34:07 +02:00
}
if ( std :: abs ( extr2z - m_slicing_params . first_print_layer_height ) < EPSILON ) {
2017-04-07 17:44:51 +02:00
// This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers().
2017-03-28 13:46:31 +02:00
assert ( extr2 -> layer_type == sltTopContact );
2017-05-12 11:18:32 +02:00
assert ( extr2 -> bottom_z == m_slicing_params . first_print_layer_height );
2021-03-19 11:21:29 +01:00
assert ( extr2 -> print_z >= m_slicing_params . first_print_layer_height + m_support_params . support_layer_height_min - EPSILON );
2017-05-12 11:18:32 +02:00
if ( intermediate_layers . empty () || intermediate_layers . back () -> print_z < m_slicing_params . first_print_layer_height ) {
MyLayer & layer_new = layer_allocate ( layer_storage , sltIntermediate );
layer_new . bottom_z = 0. ;
layer_new . print_z = m_slicing_params . first_print_layer_height ;
layer_new . height = m_slicing_params . first_print_layer_height ;
intermediate_layers . push_back ( & layer_new );
}
2017-03-27 14:34:07 +02:00
continue ;
}
assert ( extr2z >= m_slicing_params . raft_interface_top_z + EPSILON );
assert ( extr2z >= m_slicing_params . first_print_layer_height + EPSILON );
2017-05-12 11:18:32 +02:00
MyLayer * extr1 = ( idx_extreme == 0 ) ? nullptr : extremes [ idx_extreme - 1 ];
2017-03-27 14:34:07 +02:00
// Fuse a support layer firmly to the raft top interface (not to the raft contacts).
2017-03-28 13:46:31 +02:00
coordf_t extr1z = ( extr1 == nullptr ) ? m_slicing_params . raft_interface_top_z : extr1 -> extreme_z ();
2017-05-12 11:18:32 +02:00
assert ( extr2z >= extr1z );
2017-03-28 13:46:31 +02:00
assert ( extr2z > extr1z || ( extr1 != nullptr && extr2 -> layer_type == sltBottomContact ));
2017-05-12 11:18:32 +02:00
if ( std :: abs ( extr1z ) < EPSILON ) {
// This layer interval starts with the 1st layer. Print the 1st layer using the prescribed 1st layer thickness.
2021-02-24 09:22:31 +01:00
// assert(! m_slicing_params.has_raft()); RaftingEdition: unclear where the issue is: assert fails with 1-layer raft & base supports
2017-03-27 14:34:07 +02:00
assert ( intermediate_layers . empty () || intermediate_layers . back () -> print_z <= m_slicing_params . first_print_layer_height );
// At this point only layers above first_print_layer_heigth + EPSILON are expected as the other cases were captured earlier.
2017-05-12 11:18:32 +02:00
assert ( extr2z >= m_slicing_params . first_print_layer_height + EPSILON );
// Generate a new intermediate layer.
MyLayer & layer_new = layer_allocate ( layer_storage , sltIntermediate );
layer_new . bottom_z = 0. ;
layer_new . print_z = extr1z = m_slicing_params . first_print_layer_height ;
layer_new . height = extr1z ;
intermediate_layers . push_back ( & layer_new );
// Continue printing the other layers up to extr2z.
}
2016-10-20 13:04:23 +02:00
coordf_t dist = extr2z - extr1z ;
2017-01-05 09:14:59 +01:00
assert ( dist >= 0. );
2017-05-12 11:18:32 +02:00
if ( dist == 0. )
continue ;
2017-03-28 13:46:31 +02:00
// The new layers shall be at least m_support_layer_height_min thick.
2021-03-19 11:21:29 +01:00
assert ( dist >= m_support_params . support_layer_height_min - EPSILON );
2017-01-05 09:14:59 +01:00
if ( synchronize ) {
2017-04-10 12:00:07 +02:00
// Emit support layers synchronized with the object layers.
// Find the first object layer, which has its print_z in this support Z range.
2018-09-11 14:04:47 +02:00
while ( idx_layer_object < object . layers (). size () && object . layers ()[ idx_layer_object ] -> print_z < extr1z + EPSILON )
2017-04-10 12:00:07 +02:00
++ idx_layer_object ;
2017-06-13 19:29:15 +02:00
if ( idx_layer_object == 0 && extr1z == m_slicing_params . raft_interface_top_z ) {
// Insert one base support layer below the object.
MyLayer & layer_new = layer_allocate ( layer_storage , sltIntermediate );
layer_new . print_z = m_slicing_params . object_print_z_min ;
layer_new . bottom_z = m_slicing_params . raft_interface_top_z ;
layer_new . height = layer_new . print_z - layer_new . bottom_z ;
intermediate_layers . push_back ( & layer_new );
}
2017-04-10 12:00:07 +02:00
// Emit all intermediate support layers synchronized with object layers up to extr2z.
2018-09-11 14:04:47 +02:00
for (; idx_layer_object < object . layers (). size () && object . layers ()[ idx_layer_object ] -> print_z < extr2z + EPSILON ; ++ idx_layer_object ) {
2017-04-10 12:00:07 +02:00
MyLayer & layer_new = layer_allocate ( layer_storage , sltIntermediate );
2018-09-11 14:04:47 +02:00
layer_new . print_z = object . layers ()[ idx_layer_object ] -> print_z ;
layer_new . height = object . layers ()[ idx_layer_object ] -> height ;
layer_new . bottom_z = ( idx_layer_object > 0 ) ? object . layers ()[ idx_layer_object - 1 ] -> print_z : ( layer_new . print_z - layer_new . height );
2017-04-10 12:00:07 +02:00
assert ( intermediate_layers . empty () || intermediate_layers . back () -> print_z < layer_new . print_z + EPSILON );
intermediate_layers . push_back ( & layer_new );
}
} else {
// Insert intermediate layers.
size_t n_layers_extra = size_t ( ceil ( dist / m_slicing_params . max_suport_layer_height ));
assert ( n_layers_extra > 0 );
coordf_t step = dist / coordf_t ( n_layers_extra );
if ( extr1 != nullptr && extr1 -> layer_type == sltTopContact &&
2021-03-19 11:21:29 +01:00
extr1 -> print_z + m_support_params . support_layer_height_min > extr1 -> bottom_z + step ) {
2017-04-10 12:00:07 +02:00
// The bottom extreme is a bottom of a top surface. Ensure that the gap
// between the 1st intermediate layer print_z and extr1->print_z is not too small.
2021-03-19 11:21:29 +01:00
assert ( extr1 -> bottom_z + m_support_params . support_layer_height_min < extr1 -> print_z + EPSILON );
2017-04-10 12:00:07 +02:00
// Generate the first intermediate layer.
MyLayer & layer_new = layer_allocate ( layer_storage , sltIntermediate );
layer_new . bottom_z = extr1 -> bottom_z ;
layer_new . print_z = extr1z = extr1 -> print_z ;
layer_new . height = extr1 -> height ;
intermediate_layers . push_back ( & layer_new );
2017-05-12 11:18:32 +02:00
dist = extr2z - extr1z ;
2017-02-09 16:19:14 +01:00
n_layers_extra = size_t ( ceil ( dist / m_slicing_params . max_suport_layer_height ));
2017-04-10 12:00:07 +02:00
if ( n_layers_extra == 0 )
continue ;
// Continue printing the other layers up to extr2z.
2017-01-05 09:14:59 +01:00
step = dist / coordf_t ( n_layers_extra );
}
2017-05-12 11:18:32 +02:00
if ( ! m_slicing_params . soluble_interface && extr2 -> layer_type == sltTopContact ) {
2017-04-10 12:00:07 +02:00
// This is a top interface layer, which does not have a height assigned yet. Do it now.
assert ( extr2 -> height == 0. );
assert ( extr1z > m_slicing_params . first_print_layer_height - EPSILON );
extr2 -> height = step ;
extr2 -> bottom_z = extr2z = extr2 -> print_z - step ;
if ( -- n_layers_extra == 0 )
continue ;
}
coordf_t extr2z_large_steps = extr2z ;
// Take the largest allowed step in the Z axis until extr2z_large_steps is reached.
for ( size_t i = 0 ; i < n_layers_extra ; ++ i ) {
MyLayer & layer_new = layer_allocate ( layer_storage , sltIntermediate );
2017-05-12 11:18:32 +02:00
if ( i + 1 == n_layers_extra ) {
// Last intermediate layer added. Align the last entered layer with extr2z_large_steps exactly.
layer_new . bottom_z = ( i == 0 ) ? extr1z : intermediate_layers . back () -> print_z ;
layer_new . print_z = extr2z_large_steps ;
layer_new . height = layer_new . print_z - layer_new . bottom_z ;
}
else {
// Intermediate layer, not the last added.
layer_new . height = step ;
layer_new . bottom_z = extr1z + i * step ;
layer_new . print_z = layer_new . bottom_z + step ;
}
assert ( intermediate_layers . empty () || intermediate_layers . back () -> print_z <= layer_new . print_z );
intermediate_layers . push_back ( & layer_new );
2017-01-05 09:14:59 +01:00
}
}
2016-10-13 16:00:22 +02:00
}
2017-01-05 09:14:59 +01:00
#ifdef _DEBUG
2017-05-12 11:18:32 +02:00
for ( size_t i = 0 ; i < top_contacts . size (); ++ i )
assert ( top_contacts [ i ] -> height > 0. );
2017-01-05 09:14:59 +01:00
#endif /* _DEBUG */
2016-10-16 16:30:56 +02:00
return intermediate_layers ;
2016-10-13 16:00:22 +02:00
}
// At this stage there shall be intermediate_layers allocated between bottom_contacts and top_contacts, but they have no polygons assigned.
2017-01-05 09:14:59 +01:00
// Also the bottom/top_contacts shall have a layer thickness assigned already.
2016-10-20 13:04:23 +02:00
void PrintObjectSupportMaterial :: generate_base_layers (
2016-10-13 16:00:22 +02:00
const PrintObject & object ,
2016-10-16 16:30:56 +02:00
const MyLayersPtr & bottom_contacts ,
const MyLayersPtr & top_contacts ,
2016-11-29 19:30:59 +01:00
MyLayersPtr & intermediate_layers ,
2017-03-22 15:35:50 +01:00
const std :: vector < Polygons > & layer_support_areas ) const
2016-10-13 16:00:22 +02:00
{
2016-11-24 10:43:47 +01:00
#ifdef SLIC3R_DEBUG
static int iRun = 0 ;
#endif /* SLIC3R_DEBUG */
2016-10-13 16:00:22 +02:00
if ( top_contacts . empty ())
// No top contacts -> no intermediate layers will be produced.
return ;
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
2017-03-22 15:35:50 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start" ;
tbb :: parallel_for (
tbb :: blocked_range < size_t > ( 0 , intermediate_layers . size ()),
2021-01-29 17:12:06 +01:00
[ & object , & bottom_contacts , & top_contacts , & intermediate_layers , & layer_support_areas ]( const tbb :: blocked_range < size_t >& range ) {
2017-03-22 15:35:50 +01:00
// index -2 means not initialized yet, -1 means intialized and decremented to 0 and then -1.
int idx_top_contact_above = - 2 ;
int idx_bottom_contact_overlapping = - 2 ;
int idx_object_layer_above = - 2 ;
// Counting down due to the way idx_lower_or_equal caches indices to avoid repeated binary search over the complete sequence.
for ( int idx_intermediate = int ( range . end ()) - 1 ; idx_intermediate >= int ( range . begin ()); -- idx_intermediate )
{
BOOST_LOG_TRIVIAL ( trace ) << "Support generator - generate_base_layers - creating layer " <<
idx_intermediate << " of " << intermediate_layers . size ();
MyLayer & layer_intermediate = * intermediate_layers [ idx_intermediate ];
2017-05-12 11:18:32 +02:00
// Layers must be sorted by print_z.
assert ( idx_intermediate == 0 || layer_intermediate . print_z >= intermediate_layers [ idx_intermediate - 1 ] -> print_z );
2016-11-29 19:30:59 +01:00
2017-03-22 15:35:50 +01:00
// Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
// New polygons for layer_intermediate.
Polygons polygons_new ;
2017-01-05 09:14:59 +01:00
2017-03-22 15:35:50 +01:00
// Use the precomputed layer_support_areas.
2021-02-15 18:07:44 +01:00
idx_object_layer_above = std :: max ( 0 , idx_lower_or_equal ( object . layers (). begin (), object . layers (). end (), idx_object_layer_above ,
2017-05-12 11:09:24 +02:00
[ & layer_intermediate ]( const Layer * layer ){ return layer -> print_z <= layer_intermediate . print_z + EPSILON ; }));
2017-03-22 15:35:50 +01:00
polygons_new = layer_support_areas [ idx_object_layer_above ];
2016-10-13 16:00:22 +02:00
2017-03-22 15:35:50 +01:00
// Polygons to trim polygons_new.
Polygons polygons_trimming ;
2016-10-13 16:00:22 +02:00
2017-03-22 15:35:50 +01:00
// Trimming the base layer with any overlapping top layer.
// Following cases are recognized:
// 1) top.bottom_z >= base.top_z -> No overlap, no trimming needed.
// 2) base.bottom_z >= top.print_z -> No overlap, no trimming needed.
// 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
// 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
// 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
2018-09-17 15:12:13 +02:00
idx_top_contact_above = idx_lower_or_equal ( top_contacts , idx_top_contact_above ,
[ & layer_intermediate ]( const MyLayer * layer ){ return layer -> bottom_z <= layer_intermediate . print_z - EPSILON ; });
2017-03-22 15:35:50 +01:00
// Collect all the top_contact layer intersecting with this layer.
2018-09-17 15:12:13 +02:00
for ( int idx_top_contact_overlapping = idx_top_contact_above ; idx_top_contact_overlapping >= 0 ; -- idx_top_contact_overlapping ) {
2017-05-12 11:18:32 +02:00
MyLayer & layer_top_overlapping = * top_contacts [ idx_top_contact_overlapping ];
2017-03-22 15:35:50 +01:00
if ( layer_top_overlapping . print_z < layer_intermediate . bottom_z + EPSILON )
break ;
// Base must not overlap with top.bottom_z.
assert ( ! ( layer_intermediate . print_z > layer_top_overlapping . bottom_z + EPSILON && layer_intermediate . bottom_z < layer_top_overlapping . bottom_z - EPSILON ));
if ( layer_intermediate . print_z <= layer_top_overlapping . print_z + EPSILON && layer_intermediate . bottom_z >= layer_top_overlapping . bottom_z - EPSILON )
// Base is fully inside top. Trim base by top.
polygons_append ( polygons_trimming , layer_top_overlapping . polygons );
}
2016-10-13 16:00:22 +02:00
2017-03-22 15:35:50 +01:00
// Trimming the base layer with any overlapping bottom layer.
// Following cases are recognized:
// 1) bottom.bottom_z >= base.top_z -> No overlap, no trimming needed.
// 2) base.bottom_z >= bottom.print_z -> No overlap, no trimming needed.
// 3) base.print_z > bottom.bottom_z && base.bottom_z < bottom.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the bottom layer height where it overlaps the base layer. No trimming needed here.
// 4) base.print_z > bottom.print_z && base.bottom_z >= bottom.print_z -> Base overlaps with bottom.print_z. This must not happen.
// 5) base.print_z <= bottom.print_z && base.bottom_z >= bottom.bottom_z -> Base is fully inside top. Trim base by top.
idx_bottom_contact_overlapping = idx_lower_or_equal ( bottom_contacts , idx_bottom_contact_overlapping ,
[ & layer_intermediate ]( const MyLayer * layer ){ return layer -> bottom_print_z () <= layer_intermediate . print_z - EPSILON ; });
// Collect all the bottom_contacts layer intersecting with this layer.
for ( int i = idx_bottom_contact_overlapping ; i >= 0 ; -- i ) {
2017-05-12 11:18:32 +02:00
MyLayer & layer_bottom_overlapping = * bottom_contacts [ i ];
2017-03-22 15:35:50 +01:00
if ( layer_bottom_overlapping . print_z < layer_intermediate . bottom_print_z () + EPSILON )
break ;
// Base must not overlap with bottom.top_z.
assert ( ! ( layer_intermediate . print_z > layer_bottom_overlapping . print_z + EPSILON && layer_intermediate . bottom_z < layer_bottom_overlapping . print_z - EPSILON ));
if ( layer_intermediate . print_z <= layer_bottom_overlapping . print_z + EPSILON && layer_intermediate . bottom_z >= layer_bottom_overlapping . bottom_print_z () - EPSILON )
// Base is fully inside bottom. Trim base by bottom.
polygons_append ( polygons_trimming , layer_bottom_overlapping . polygons );
}
2016-11-24 10:43:47 +01:00
2017-03-22 15:35:50 +01:00
#ifdef SLIC3R_DEBUG
{
BoundingBox bbox = get_extents ( polygons_new );
bbox . merge ( get_extents ( polygons_trimming ));
:: Slic3r :: SVG svg ( debug_out_path ( "support-intermediate-layers-raw-%d-%lf.svg" , iRun , layer_intermediate . print_z ), bbox );
svg . draw ( union_ex ( polygons_new , false ), "blue" , 0.5f );
2017-05-12 11:18:32 +02:00
svg . draw ( to_polylines ( polygons_new ), "blue" );
svg . draw ( union_ex ( polygons_trimming , true ), "red" , 0.5f );
svg . draw ( to_polylines ( polygons_trimming ), "red" );
}
2017-03-22 15:35:50 +01:00
#endif /* SLIC3R_DEBUG */
// Trim the polygons, store them.
if ( polygons_trimming . empty ())
layer_intermediate . polygons = std :: move ( polygons_new );
else
2017-05-12 11:18:32 +02:00
layer_intermediate . polygons = diff (
2017-03-22 15:35:50 +01:00
polygons_new ,
polygons_trimming ,
2021-05-05 12:16:40 +02:00
ApplySafetyOffset :: Yes ); // safety offset to merge the touching source polygons
2017-03-22 15:35:50 +01:00
layer_intermediate . layer_type = sltBase ;
#if 0
// Fillet the base polygons and trim them again with the top, interface and contact layers.
$base->{$i} = diff(
offset2(
$base->{$i},
$fillet_radius_scaled,
-$fillet_radius_scaled,
# Use a geometric offsetting for filleting.
JT_ROUND,
0.2*$fillet_radius_scaled),
$trim_polygons,
false); // don't apply the safety offset.
}
#endif
}
});
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - end" ;
2016-10-13 16:00:22 +02:00
2016-11-02 10:47:00 +01:00
#ifdef SLIC3R_DEBUG
2017-05-12 11:18:32 +02:00
for ( MyLayersPtr :: const_iterator it = intermediate_layers . begin (); it != intermediate_layers . end (); ++ it )
2017-01-05 09:14:59 +01:00
:: Slic3r :: SVG :: export_expolygons (
debug_out_path ( "support-intermediate-layers-untrimmed-%d-%lf.svg" , iRun , ( * it ) -> print_z ),
union_ex (( * it ) -> polygons , false ));
++ iRun ;
2016-11-02 10:47:00 +01:00
#endif /* SLIC3R_DEBUG */
2021-03-19 11:21:29 +01:00
this -> trim_support_layers_by_object ( object , intermediate_layers , m_slicing_params . gap_support_object , m_slicing_params . gap_object_support , m_support_params . gap_xy );
2017-01-05 09:14:59 +01:00
}
void PrintObjectSupportMaterial :: trim_support_layers_by_object (
const PrintObject & object ,
MyLayersPtr & support_layers ,
const coordf_t gap_extra_above ,
const coordf_t gap_extra_below ,
const coordf_t gap_xy ) const
{
2017-03-22 15:35:50 +01:00
const float gap_xy_scaled = float ( scale_ ( gap_xy ));
2016-11-29 19:30:59 +01:00
2017-03-22 15:35:50 +01:00
// Collect non-empty layers to be processed in parallel.
// This is a good idea as pulling a thread from a thread pool for an empty task is expensive.
MyLayersPtr nonempty_layers ;
nonempty_layers . reserve ( support_layers . size ());
for ( size_t idx_layer = 0 ; idx_layer < support_layers . size (); ++ idx_layer ) {
MyLayer * support_layer = support_layers [ idx_layer ];
if ( ! support_layer -> polygons . empty () && support_layer -> print_z >= m_slicing_params . raft_contact_top_z + EPSILON )
// Non-empty support layer and not a raft layer.
nonempty_layers . push_back ( support_layer );
2016-10-13 16:00:22 +02:00
}
2017-03-22 15:35:50 +01:00
// For all intermediate support layers:
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - start" ;
tbb :: parallel_for (
tbb :: blocked_range < size_t > ( 0 , nonempty_layers . size ()),
[ this , & object , & nonempty_layers , gap_extra_above , gap_extra_below , gap_xy_scaled ]( const tbb :: blocked_range < size_t >& range ) {
size_t idx_object_layer_overlapping = size_t ( - 1 );
for ( size_t idx_layer = range . begin (); idx_layer < range . end (); ++ idx_layer ) {
MyLayer & support_layer = * nonempty_layers [ idx_layer ];
// BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming non-empty layer " << idx_layer << " of " << nonempty_layers.size();
assert ( ! support_layer . polygons . empty () && support_layer . print_z >= m_slicing_params . raft_contact_top_z + EPSILON );
// Find the overlapping object layers including the extra above / below gap.
2021-03-10 12:14:45 +01:00
coordf_t z_threshold = support_layer . bottom_print_z () - gap_extra_below + EPSILON ;
2017-03-22 15:35:50 +01:00
idx_object_layer_overlapping = idx_higher_or_equal (
2021-02-03 15:12:53 +01:00
object . layers (). begin (), object . layers (). end (), idx_object_layer_overlapping ,
2017-03-22 15:35:50 +01:00
[ z_threshold ]( const Layer * layer ){ return layer -> print_z >= z_threshold ; });
// Collect all the object layers intersecting with this layer.
Polygons polygons_trimming ;
2017-03-28 13:46:31 +02:00
size_t i = idx_object_layer_overlapping ;
2018-09-11 14:04:47 +02:00
for (; i < object . layers (). size (); ++ i ) {
const Layer & object_layer = * object . layers ()[ i ];
2021-03-10 12:14:45 +01:00
if ( object_layer . bottom_z () > support_layer . print_z + gap_extra_above - EPSILON )
2017-03-22 15:35:50 +01:00
break ;
2020-01-03 14:05:56 +01:00
polygons_append ( polygons_trimming , offset ( object_layer . lslices , gap_xy_scaled , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
2017-03-22 15:35:50 +01:00
}
2021-03-08 13:44:00 +01:00
if ( ! m_slicing_params . soluble_interface && m_object_config -> thick_bridges ) {
2017-03-28 13:46:31 +02:00
// Collect all bottom surfaces, which will be extruded with a bridging flow.
2018-09-11 14:04:47 +02:00
for (; i < object . layers (). size (); ++ i ) {
const Layer & object_layer = * object . layers ()[ i ];
2017-03-28 13:46:31 +02:00
bool some_region_overlaps = false ;
2018-09-17 15:12:13 +02:00
for ( LayerRegion * region : object_layer . regions ()) {
2021-05-06 14:43:36 +02:00
coordf_t bridging_height = region -> region (). bridging_height_avg ( * m_print_config );
2018-09-17 15:12:13 +02:00
if ( object_layer . print_z - bridging_height > support_layer . print_z + gap_extra_above - EPSILON )
2017-03-28 13:46:31 +02:00
break ;
some_region_overlaps = true ;
2018-09-17 15:12:13 +02:00
polygons_append ( polygons_trimming ,
2021-05-03 11:39:53 +02:00
offset ( region -> fill_surfaces . filter_by_type ( stBottomBridge ), gap_xy_scaled , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
2021-05-05 18:13:58 +02:00
if ( region -> region (). config (). overhangs . value )
2021-03-10 12:14:45 +01:00
// Add bridging perimeters.
2018-09-17 15:12:13 +02:00
SupportMaterialInternal :: collect_bridging_perimeter_areas ( region -> perimeters , gap_xy_scaled , polygons_trimming );
2017-03-28 13:46:31 +02:00
}
if ( ! some_region_overlaps )
break ;
}
}
2017-03-22 15:35:50 +01:00
// $layer->slices contains the full shape of layer, thus including
// perimeter's width. $support contains the full shape of support
2019-09-24 16:01:01 +02:00
// material, thus including the width of its foremost extrusion.
2017-03-22 15:35:50 +01:00
// We leave a gap equal to a full extrusion width.
2018-09-17 15:12:13 +02:00
support_layer . polygons = diff ( support_layer . polygons , polygons_trimming );
2017-03-22 15:35:50 +01:00
}
});
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end" ;
2016-10-13 16:00:22 +02:00
}
2016-12-20 12:19:13 +01:00
PrintObjectSupportMaterial :: MyLayersPtr PrintObjectSupportMaterial :: generate_raft_base (
2021-02-23 14:43:47 +01:00
const PrintObject & object ,
2016-12-20 12:19:13 +01:00
const MyLayersPtr & top_contacts ,
2017-02-14 19:49:30 +01:00
const MyLayersPtr & interface_layers ,
2021-02-25 14:50:35 +01:00
const MyLayersPtr & base_interface_layers ,
2017-02-14 19:49:30 +01:00
const MyLayersPtr & base_layers ,
2016-12-20 12:19:13 +01:00
MyLayerStorage & layer_storage ) const
2016-10-20 13:04:23 +02:00
{
2021-02-23 14:43:47 +01:00
// If there is brim to be generated, calculate the trimming regions.
Polygons brim ;
if ( object . has_brim ()) {
// Calculate the area covered by the brim.
const BrimType brim_type = object . config (). brim_type ;
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner ;
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner ;
const auto brim_offset = scaled < float > ( object . config (). brim_offset . value + object . config (). brim_width . value );
for ( const ExPolygon & ex : object . layers (). front () -> lslices ) {
if ( brim_outer && brim_inner )
polygons_append ( brim , offset ( ex , brim_offset ));
else {
if ( brim_outer )
polygons_append ( brim , offset ( ex . contour , brim_offset , ClipperLib :: jtRound , float ( scale_ ( 0.1 ))));
else
brim . emplace_back ( ex . contour );
if ( brim_inner ) {
Polygons holes = ex . holes ;
polygons_reverse ( holes );
holes = offset ( holes , - brim_offset , ClipperLib :: jtRound , float ( scale_ ( 0.1 )));
polygons_reverse ( holes );
polygons_append ( brim , std :: move ( holes ));
} else
polygons_append ( brim , ex . holes );
}
}
brim = union_ ( brim );
}
2016-12-20 12:19:13 +01:00
// How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed.
2017-04-07 17:44:51 +02:00
const float inflate_factor_fine = float ( scale_ (( m_slicing_params . raft_layers () > 1 ) ? 0.5 : EPSILON ));
2021-02-24 08:48:33 +01:00
const float inflate_factor_1st_layer = std :: max ( 0.f , float ( scale_ ( object . config (). raft_first_layer_expansion )) - inflate_factor_fine );
2021-02-25 14:50:35 +01:00
MyLayer * contacts = top_contacts . empty () ? nullptr : top_contacts . front ();
MyLayer * interfaces = interface_layers . empty () ? nullptr : interface_layers . front ();
MyLayer * base_interfaces = base_interface_layers . empty () ? nullptr : base_interface_layers . front ();
MyLayer * columns_base = base_layers . empty () ? nullptr : base_layers . front ();
2017-05-12 11:18:32 +02:00
if ( contacts != nullptr && contacts -> print_z > std :: max ( m_slicing_params . first_print_layer_height , m_slicing_params . raft_contact_top_z ) + EPSILON )
// This is not the raft contact layer.
contacts = nullptr ;
2017-02-14 19:49:30 +01:00
if ( interfaces != nullptr && interfaces -> bottom_print_z () > m_slicing_params . raft_interface_top_z + EPSILON )
// This is not the raft column base layer.
interfaces = nullptr ;
2021-02-25 14:50:35 +01:00
if ( base_interfaces != nullptr && base_interfaces -> bottom_print_z () > m_slicing_params . raft_interface_top_z + EPSILON )
// This is not the raft column base layer.
base_interfaces = nullptr ;
2017-02-14 19:49:30 +01:00
if ( columns_base != nullptr && columns_base -> bottom_print_z () > m_slicing_params . raft_interface_top_z + EPSILON )
// This is not the raft interface layer.
columns_base = nullptr ;
2016-12-20 12:19:13 +01:00
2017-02-14 19:49:30 +01:00
Polygons interface_polygons ;
if ( contacts != nullptr && ! contacts -> polygons . empty ())
polygons_append ( interface_polygons , offset ( contacts -> polygons , inflate_factor_fine , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
if ( interfaces != nullptr && ! interfaces -> polygons . empty ())
polygons_append ( interface_polygons , offset ( interfaces -> polygons , inflate_factor_fine , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
2021-02-25 14:50:35 +01:00
if ( base_interfaces != nullptr && ! base_interfaces -> polygons . empty ())
polygons_append ( interface_polygons , offset ( base_interfaces -> polygons , inflate_factor_fine , SUPPORT_SURFACES_OFFSET_PARAMETERS ));
2017-02-14 19:49:30 +01:00
2017-05-12 11:18:32 +02:00
// Output vector.
2016-12-20 12:19:13 +01:00
MyLayersPtr raft_layers ;
2017-05-12 11:18:32 +02:00
if ( m_slicing_params . raft_layers () > 1 ) {
2017-02-14 19:49:30 +01:00
Polygons base ;
Polygons columns ;
if ( columns_base != nullptr ) {
base = columns_base -> polygons ;
columns = base ;
if ( ! interface_polygons . empty ())
// Trim the 1st layer columns with the inflated interface polygons.
columns = diff ( columns , interface_polygons );
}
if ( ! interface_polygons . empty ()) {
// Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface.
base = union_ ( base , interface_polygons );
}
2016-12-20 12:19:13 +01:00
// Do not add the raft contact layer, only add the raft layers below the contact layer.
// Insert the 1st layer.
{
MyLayer & new_layer = layer_allocate ( layer_storage , ( m_slicing_params . base_raft_layers > 0 ) ? sltRaftBase : sltRaftInterface );
raft_layers . push_back ( & new_layer );
new_layer . print_z = m_slicing_params . first_print_layer_height ;
new_layer . height = m_slicing_params . first_print_layer_height ;
new_layer . bottom_z = 0. ;
2021-02-24 08:48:33 +01:00
new_layer . polygons = inflate_factor_1st_layer > 0 ? offset ( base , inflate_factor_1st_layer ) : base ;
2016-12-20 12:19:13 +01:00
}
// Insert the base layers.
for ( size_t i = 1 ; i < m_slicing_params . base_raft_layers ; ++ i ) {
2017-05-12 11:18:32 +02:00
coordf_t print_z = raft_layers . back () -> print_z ;
2016-12-20 12:19:13 +01:00
MyLayer & new_layer = layer_allocate ( layer_storage , sltRaftBase );
raft_layers . push_back ( & new_layer );
2017-05-12 11:18:32 +02:00
new_layer . print_z = print_z + m_slicing_params . base_raft_layer_height ;
2016-12-20 12:19:13 +01:00
new_layer . height = m_slicing_params . base_raft_layer_height ;
2017-05-12 11:18:32 +02:00
new_layer . bottom_z = print_z ;
new_layer . polygons = base ;
}
2016-12-20 12:19:13 +01:00
// Insert the interface layers.
for ( size_t i = 1 ; i < m_slicing_params . interface_raft_layers ; ++ i ) {
2017-05-12 11:18:32 +02:00
coordf_t print_z = raft_layers . back () -> print_z ;
MyLayer & new_layer = layer_allocate ( layer_storage , sltRaftInterface );
2016-12-20 12:19:13 +01:00
raft_layers . push_back ( & new_layer );
2017-05-12 11:18:32 +02:00
new_layer . print_z = print_z + m_slicing_params . interface_raft_layer_height ;
2016-12-20 12:19:13 +01:00
new_layer . height = m_slicing_params . interface_raft_layer_height ;
2017-05-12 11:18:32 +02:00
new_layer . bottom_z = print_z ;
new_layer . polygons = interface_polygons ;
2017-02-14 19:49:30 +01:00
//FIXME misusing contact_polygons for support columns.
2021-03-19 11:21:29 +01:00
new_layer . contact_polygons = std :: make_unique < Polygons > ( columns );
2017-05-12 11:18:32 +02:00
}
2021-04-08 16:36:52 +02:00
} else {
if ( columns_base != nullptr ) {
2017-02-14 19:49:30 +01:00
// Expand the bases of the support columns in the 1st layer.
2021-02-24 19:49:38 +01:00
Polygons & raft = columns_base -> polygons ;
2021-03-19 11:21:29 +01:00
Polygons trimming = offset ( m_object -> layers (). front () -> lslices , ( float ) scale_ ( m_support_params . gap_xy ), SUPPORT_SURFACES_OFFSET_PARAMETERS );
2021-02-24 19:49:38 +01:00
if ( inflate_factor_1st_layer > SCALED_EPSILON ) {
// Inflate in multiple steps to avoid leaking of the support 1st layer through object walls.
2021-03-19 11:21:29 +01:00
auto nsteps = std :: max ( 5 , int ( ceil ( inflate_factor_1st_layer / m_support_params . first_layer_flow . scaled_width ())));
2021-02-24 19:49:38 +01:00
float step = inflate_factor_1st_layer / nsteps ;
for ( int i = 0 ; i < nsteps ; ++ i )
raft = diff ( offset ( raft , step ), trimming );
} else
raft = diff ( raft , trimming );
2021-04-08 16:36:52 +02:00
if ( contacts != nullptr )
columns_base -> polygons = diff ( columns_base -> polygons , interface_polygons );
2021-02-24 19:49:38 +01:00
}
2021-02-23 14:43:47 +01:00
if ( ! brim . empty ()) {
2021-04-08 16:36:52 +02:00
if ( columns_base )
columns_base -> polygons = diff ( columns_base -> polygons , brim );
2021-02-23 14:43:47 +01:00
if ( contacts )
contacts -> polygons = diff ( contacts -> polygons , brim );
if ( interfaces )
interfaces -> polygons = diff ( interfaces -> polygons , brim );
2021-02-25 14:50:35 +01:00
if ( base_interfaces )
base_interfaces -> polygons = diff ( base_interfaces -> polygons , brim );
2021-02-23 14:43:47 +01:00
}
2016-12-20 12:19:13 +01:00
}
return raft_layers ;
2016-10-20 13:04:23 +02:00
}
2021-02-15 18:07:44 +01:00
// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers.
std :: pair < PrintObjectSupportMaterial :: MyLayersPtr , PrintObjectSupportMaterial :: MyLayersPtr > PrintObjectSupportMaterial :: generate_interface_layers (
2016-10-16 16:30:56 +02:00
const MyLayersPtr & bottom_contacts ,
const MyLayersPtr & top_contacts ,
MyLayersPtr & intermediate_layers ,
MyLayerStorage & layer_storage ) const
2016-10-13 16:00:22 +02:00
{
// my $area_threshold = $self->interface_flow->scaled_spacing ** 2;
2021-02-15 18:07:44 +01:00
std :: pair < MyLayersPtr , MyLayersPtr > base_and_interface_layers ;
MyLayersPtr & interface_layers = base_and_interface_layers . first ;
MyLayersPtr & base_interface_layers = base_and_interface_layers . second ;
// distinguish between interface and base interface layers
2016-10-13 16:00:22 +02:00
// Contact layer is considered an interface layer, therefore run the following block only if support_material_interface_layers > 1.
2021-02-15 18:07:44 +01:00
// Contact layer needs a base_interface layer, therefore run the following block if support_material_interface_layers > 0, has soluble support and extruders are different.
bool soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
m_slicing_params . soluble_interface &&
// Interface extruder soluble.
m_object_config -> support_material_interface_extruder . value > 0 && m_print_config -> filament_soluble . get_at ( m_object_config -> support_material_interface_extruder . value - 1 ) &&
// Base extruder: Either "print with active extruder" not soluble.
( m_object_config -> support_material_extruder . value == 0 || ! m_print_config -> filament_soluble . get_at ( m_object_config -> support_material_extruder . value - 1 ));
2021-03-09 15:29:13 +01:00
int num_interface_layers_top = m_object_config -> support_material_interface_layers ;
int num_interface_layers_bottom = m_object_config -> support_material_bottom_interface_layers ;
if ( num_interface_layers_bottom < 0 )
num_interface_layers_bottom = num_interface_layers_top ;
int num_base_interface_layers_top = soluble_interface_non_soluble_base ? std :: min ( num_interface_layers_top / 2 , 2 ) : 0 ;
int num_base_interface_layers_bottom = soluble_interface_non_soluble_base ? std :: min ( num_interface_layers_bottom / 2 , 2 ) : 0 ;
2021-02-15 18:07:44 +01:00
2021-03-09 15:29:13 +01:00
if ( ! intermediate_layers . empty () && ( num_interface_layers_top > 1 || num_interface_layers_bottom > 1 )) {
2016-10-13 16:00:22 +02:00
// For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
2017-03-22 15:35:50 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start" ;
2021-02-15 18:07:44 +01:00
// Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1.
2021-03-09 15:29:13 +01:00
-- num_interface_layers_top ;
-- num_interface_layers_bottom ;
int num_interface_layers_only_top = num_interface_layers_top - num_base_interface_layers_top ;
int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom ;
2017-03-22 15:35:50 +01:00
interface_layers . assign ( intermediate_layers . size (), nullptr );
2021-03-09 15:29:13 +01:00
if ( num_base_interface_layers_top || num_base_interface_layers_bottom )
2021-02-15 18:07:44 +01:00
base_interface_layers . assign ( intermediate_layers . size (), nullptr );
2017-03-22 15:35:50 +01:00
tbb :: spin_mutex layer_storage_mutex ;
2021-02-15 18:07:44 +01:00
// Insert a new layer into base_interface_layers, if intersection with base exists.
auto insert_layer = [ & layer_storage , & layer_storage_mutex ]( MyLayer & intermediate_layer , Polygons & bottom , Polygons && top , const Polygons * subtract , SupporLayerType type ) {
assert ( ! bottom . empty () || ! top . empty ());
MyLayer & layer_new = layer_allocate ( layer_storage , layer_storage_mutex , type );
layer_new . print_z = intermediate_layer . print_z ;
layer_new . bottom_z = intermediate_layer . bottom_z ;
layer_new . height = intermediate_layer . height ;
layer_new . bridging = intermediate_layer . bridging ;
// Merge top into bottom, unite them with a safety offset.
append ( bottom , std :: move ( top ));
2021-05-05 12:16:40 +02:00
layer_new . polygons = intersection ( union_safety_offset ( std :: move ( bottom )), intermediate_layer . polygons );
2021-02-15 18:07:44 +01:00
// Subtract the interface from the base regions.
2021-05-05 12:16:40 +02:00
intermediate_layer . polygons = diff ( intermediate_layer . polygons , layer_new . polygons );
2021-02-15 18:07:44 +01:00
if ( subtract )
// Trim the base interface layer with the interface layer.
layer_new . polygons = diff ( std :: move ( layer_new . polygons ), * subtract );
//FIXME filter layer_new.polygons islands by a minimum area?
// $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
return & layer_new ;
};
tbb :: parallel_for ( tbb :: blocked_range < int > ( 0 , int ( intermediate_layers . size ())),
2021-03-09 15:29:13 +01:00
[ & bottom_contacts , & top_contacts , & intermediate_layers , & insert_layer ,
num_interface_layers_top , num_interface_layers_bottom , num_base_interface_layers_top , num_base_interface_layers_bottom , num_interface_layers_only_top , num_interface_layers_only_bottom ,
2021-02-15 18:07:44 +01:00
& interface_layers , & base_interface_layers ]( const tbb :: blocked_range < int >& range ) {
// Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
// this intermediate layer.
2017-03-22 15:35:50 +01:00
// Index of the first top contact layer intersecting the current intermediate layer.
2021-02-15 18:07:44 +01:00
auto idx_top_contact_first = - 1 ;
2017-03-22 15:35:50 +01:00
// Index of the first bottom contact layer intersecting the current intermediate layer.
2021-02-15 18:07:44 +01:00
auto idx_bottom_contact_first = - 1 ;
auto num_intermediate = int ( intermediate_layers . size ());
for ( int idx_intermediate_layer = range . begin (); idx_intermediate_layer < range . end (); ++ idx_intermediate_layer ) {
2017-03-22 15:35:50 +01:00
MyLayer & intermediate_layer = * intermediate_layers [ idx_intermediate_layer ];
2021-02-15 18:07:44 +01:00
Polygons polygons_top_contact_projected_interface ;
Polygons polygons_top_contact_projected_base ;
Polygons polygons_bottom_contact_projected_interface ;
Polygons polygons_bottom_contact_projected_base ;
2021-03-11 15:01:24 +01:00
if ( num_interface_layers_top > 0 ) {
2021-03-09 15:29:13 +01:00
// Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
coordf_t top_z = intermediate_layers [ std :: min ( num_intermediate - 1 , idx_intermediate_layer + num_interface_layers_top - 1 )] -> print_z ;
coordf_t top_inteface_z = std :: numeric_limits < coordf_t >:: max ();
if ( num_base_interface_layers_top > 0 )
// Some top base interface layers will be generated.
top_inteface_z = num_interface_layers_only_top == 0 ?
// Only base interface layers to generate.
- std :: numeric_limits < coordf_t >:: max () :
intermediate_layers [ std :: min ( num_intermediate - 1 , idx_intermediate_layer + num_interface_layers_only_top - 1 )] -> print_z ;
// Move idx_top_contact_first up until above the current print_z.
idx_top_contact_first = idx_higher_or_equal ( top_contacts , idx_top_contact_first , [ & intermediate_layer ]( const MyLayer * layer ){ return layer -> print_z >= intermediate_layer . print_z ; }); // - EPSILON
// Collect the top contact areas above this intermediate layer, below top_z.
for ( int idx_top_contact = idx_top_contact_first ; idx_top_contact < int ( top_contacts . size ()); ++ idx_top_contact ) {
const MyLayer & top_contact_layer = * top_contacts [ idx_top_contact ];
//FIXME maybe this adds one interface layer in excess?
if ( top_contact_layer . bottom_z - EPSILON > top_z )
break ;
polygons_append ( top_contact_layer . bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface , top_contact_layer . polygons );
}
}
2021-03-11 15:01:24 +01:00
if ( num_interface_layers_bottom > 0 ) {
2021-03-09 15:29:13 +01:00
// Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
coordf_t bottom_z = intermediate_layers [ std :: max ( 0 , idx_intermediate_layer - num_interface_layers_bottom + 1 )] -> bottom_z ;
coordf_t bottom_interface_z = - std :: numeric_limits < coordf_t >:: max ();
if ( num_base_interface_layers_bottom > 0 )
// Some bottom base interface layers will be generated.
bottom_interface_z = num_interface_layers_only_bottom == 0 ?
// Only base interface layers to generate.
std :: numeric_limits < coordf_t >:: max () :
intermediate_layers [ std :: max ( 0 , idx_intermediate_layer - num_interface_layers_only_bottom )] -> bottom_z ;
// Move idx_bottom_contact_first up until touching bottom_z.
idx_bottom_contact_first = idx_higher_or_equal ( bottom_contacts , idx_bottom_contact_first , [ bottom_z ]( const MyLayer * layer ){ return layer -> print_z >= bottom_z - EPSILON ; });
// Collect the top contact areas above this intermediate layer, below top_z.
for ( int idx_bottom_contact = idx_bottom_contact_first ; idx_bottom_contact < int ( bottom_contacts . size ()); ++ idx_bottom_contact ) {
const MyLayer & bottom_contact_layer = * bottom_contacts [ idx_bottom_contact ];
if ( bottom_contact_layer . print_z - EPSILON > intermediate_layer . bottom_z )
break ;
polygons_append ( bottom_contact_layer . print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base , bottom_contact_layer . polygons );
}
2017-03-22 15:35:50 +01:00
}
2021-02-15 18:07:44 +01:00
MyLayer * interface_layer = nullptr ;
if ( ! polygons_bottom_contact_projected_interface . empty () || ! polygons_top_contact_projected_interface . empty ()) {
interface_layer = insert_layer (
intermediate_layer , polygons_bottom_contact_projected_interface , std :: move ( polygons_top_contact_projected_interface ), nullptr ,
polygons_top_contact_projected_interface . empty () ? sltBottomInterface : sltTopInterface );
interface_layers [ idx_intermediate_layer ] = interface_layer ;
}
if ( ! polygons_bottom_contact_projected_base . empty () || ! polygons_top_contact_projected_base . empty ())
base_interface_layers [ idx_intermediate_layer ] = insert_layer (
intermediate_layer , polygons_bottom_contact_projected_base , std :: move ( polygons_top_contact_projected_base ),
interface_layer ? & interface_layer -> polygons : nullptr , sltBase );
2017-03-22 15:35:50 +01:00
}
});
// Compress contact_out, remove the nullptr items.
remove_nulls ( interface_layers );
2021-02-15 18:07:44 +01:00
remove_nulls ( base_interface_layers );
2021-03-08 13:44:00 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end" ;
2016-10-13 16:00:22 +02:00
}
2021-02-15 18:07:44 +01:00
return base_and_interface_layers ;
2016-10-13 16:00:22 +02:00
}
2021-02-24 08:48:33 +01:00
static inline void fill_expolygon_generate_paths (
ExtrusionEntitiesPtr & dst ,
ExPolygon && expolygon ,
Fill * filler ,
const FillParams & fill_params ,
float density ,
ExtrusionRole role ,
const Flow & flow )
{
Surface surface ( stInternal , std :: move ( expolygon ));
Polylines polylines ;
try {
polylines = filler -> fill_surface ( & surface , fill_params );
2021-05-03 11:39:53 +02:00
} catch ( InfillFailedException & ) {
}
2021-02-24 08:48:33 +01:00
extrusion_entities_append_paths (
dst ,
std :: move ( polylines ),
role ,
2021-03-08 13:44:00 +01:00
flow . mm3_per_mm (), flow . width (), flow . height ());
2021-02-24 08:48:33 +01:00
}
static inline void fill_expolygons_generate_paths (
ExtrusionEntitiesPtr & dst ,
ExPolygons && expolygons ,
Fill * filler ,
const FillParams & fill_params ,
float density ,
ExtrusionRole role ,
const Flow & flow )
{
for ( ExPolygon & expoly : expolygons )
fill_expolygon_generate_paths ( dst , std :: move ( expoly ), filler , fill_params , density , role , flow );
}
2016-11-23 15:51:47 +01:00
static inline void fill_expolygons_generate_paths (
ExtrusionEntitiesPtr & dst ,
ExPolygons && expolygons ,
Fill * filler ,
float density ,
ExtrusionRole role ,
const Flow & flow )
{
FillParams fill_params ;
2021-02-24 08:48:33 +01:00
fill_params . density = density ;
2016-12-20 12:19:13 +01:00
fill_params . dont_adjust = true ;
2021-02-24 08:48:33 +01:00
fill_expolygons_generate_paths ( dst , std :: move ( expolygons ), filler , fill_params , density , role , flow );
}
static inline void fill_expolygons_with_sheath_generate_paths (
ExtrusionEntitiesPtr & dst ,
const Polygons & polygons ,
Fill * filler ,
float density ,
ExtrusionRole role ,
const Flow & flow ,
2021-03-10 12:14:45 +01:00
bool with_sheath ,
bool no_sort )
2021-02-24 08:48:33 +01:00
{
if ( polygons . empty ())
return ;
if ( ! with_sheath ) {
fill_expolygons_generate_paths ( dst , offset2_ex ( polygons , float ( SCALED_EPSILON ), float ( - SCALED_EPSILON )), filler , density , role , flow );
return ;
}
FillParams fill_params ;
fill_params . density = density ;
fill_params . dont_adjust = true ;
double spacing = flow . scaled_spacing ();
// Clip the sheath path to avoid the extruder to get exactly on the first point of the loop.
double clip_length = spacing * 0.15 ;
for ( ExPolygon & expoly : offset2_ex ( polygons , float ( SCALED_EPSILON ), float ( - SCALED_EPSILON - 0.5 * flow . scaled_width ()))) {
// Don't reorder the skirt and its infills.
2021-03-10 12:14:45 +01:00
std :: unique_ptr < ExtrusionEntityCollection > eec ;
if ( no_sort ) {
eec = std :: make_unique < ExtrusionEntityCollection > ();
eec -> no_sort = true ;
}
ExtrusionEntitiesPtr & out = no_sort ? eec -> entities : dst ;
2021-02-24 08:48:33 +01:00
// Draw the perimeters.
2020-04-25 08:15:04 +02:00
Polylines polylines ;
2021-02-24 08:48:33 +01:00
polylines . reserve ( expoly . holes . size () + 1 );
for ( size_t i = 0 ; i <= expoly . holes . size (); ++ i ) {
Polyline pl ( i == 0 ? expoly . contour . points : expoly . holes [ i - 1 ]. points );
pl . points . emplace_back ( pl . points . front ());
pl . clip_end ( clip_length );
polylines . emplace_back ( std :: move ( pl ));
}
2021-03-10 12:14:45 +01:00
extrusion_entities_append_paths ( out , polylines , erSupportMaterial , flow . mm3_per_mm (), flow . width (), flow . height ());
2021-02-24 08:48:33 +01:00
// Fill in the rest.
2021-03-10 12:14:45 +01:00
fill_expolygons_generate_paths ( out , offset_ex ( expoly , float ( - 0.4 * spacing )), filler , fill_params , density , role , flow );
2021-04-08 15:29:40 +02:00
if ( no_sort && ! eec -> empty ())
2021-03-10 12:14:45 +01:00
dst . emplace_back ( eec . release ());
2016-11-23 15:51:47 +01:00
}
}
// Support layers, partially processed.
struct MyLayerExtruded
{
2021-04-08 15:29:40 +02:00
MyLayerExtruded & operator = ( MyLayerExtruded && rhs ) {
this -> layer = rhs . layer ;
this -> extrusions = std :: move ( rhs . extrusions );
2021-05-06 14:43:36 +02:00
m_polygons_to_extrude = std :: move ( rhs . m_polygons_to_extrude );
2021-04-08 15:29:40 +02:00
rhs . layer = nullptr ;
return * this ;
}
2016-11-23 15:51:47 +01:00
bool empty () const {
return layer == nullptr || layer -> polygons . empty ();
}
2017-01-25 18:33:05 +01:00
void set_polygons_to_extrude ( Polygons && polygons ) {
if ( m_polygons_to_extrude == nullptr )
2021-04-08 15:29:40 +02:00
m_polygons_to_extrude = std :: make_unique < Polygons > ( std :: move ( polygons ));
2017-01-25 18:33:05 +01:00
else
* m_polygons_to_extrude = std :: move ( polygons );
}
2018-09-11 14:04:47 +02:00
Polygons & polygons_to_extrude () { return ( m_polygons_to_extrude == nullptr ) ? layer -> polygons : * m_polygons_to_extrude ; }
const Polygons & polygons_to_extrude () const { return ( m_polygons_to_extrude == nullptr ) ? layer -> polygons : * m_polygons_to_extrude ; }
2017-01-25 18:33:05 +01:00
2016-11-23 15:51:47 +01:00
bool could_merge ( const MyLayerExtruded & other ) const {
2021-02-15 18:07:44 +01:00
return ! this -> empty () && ! other . empty () &&
2017-01-20 15:21:05 +01:00
std :: abs ( this -> layer -> height - other . layer -> height ) < EPSILON &&
2021-02-15 18:07:44 +01:00
this -> layer -> bridging == other . layer -> bridging ;
2016-11-23 15:51:47 +01:00
}
2017-01-05 09:14:59 +01:00
// Merge regions, perform boolean union over the merged polygons.
2016-11-23 15:51:47 +01:00
void merge ( MyLayerExtruded && other ) {
2017-01-25 18:33:05 +01:00
assert ( this -> could_merge ( other ));
// 1) Merge the rest polygons to extrude, if there are any.
if ( other . m_polygons_to_extrude != nullptr ) {
2018-09-11 14:04:47 +02:00
if ( m_polygons_to_extrude == nullptr ) {
2017-01-25 18:33:05 +01:00
// This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert ( this -> extrusions . empty ());
2021-04-08 15:29:40 +02:00
m_polygons_to_extrude = std :: make_unique < Polygons > ( this -> layer -> polygons );
2017-01-25 18:33:05 +01:00
}
2018-09-11 14:04:47 +02:00
Slic3r :: polygons_append ( * m_polygons_to_extrude , std :: move ( * other . m_polygons_to_extrude ));
2021-05-05 12:16:40 +02:00
* m_polygons_to_extrude = union_safety_offset ( * m_polygons_to_extrude );
2021-04-08 15:29:40 +02:00
other . m_polygons_to_extrude . reset ();
2018-09-11 14:04:47 +02:00
} else if ( m_polygons_to_extrude != nullptr ) {
2017-01-25 18:33:05 +01:00
assert ( other . m_polygons_to_extrude == nullptr );
// The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert ( other . extrusions . empty ());
2018-09-11 14:04:47 +02:00
Slic3r :: polygons_append ( * m_polygons_to_extrude , other . layer -> polygons );
2021-05-05 12:16:40 +02:00
* m_polygons_to_extrude = union_safety_offset ( * m_polygons_to_extrude );
2017-01-25 18:33:05 +01:00
}
// 2) Merge the extrusions.
this -> extrusions . insert ( this -> extrusions . end (), other . extrusions . begin (), other . extrusions . end ());
other . extrusions . clear ();
// 3) Merge the infill polygons.
Slic3r :: polygons_append ( this -> layer -> polygons , std :: move ( other . layer -> polygons ));
2021-05-05 12:16:40 +02:00
this -> layer -> polygons = union_safety_offset ( this -> layer -> polygons );
2016-11-23 15:51:47 +01:00
other . layer -> polygons . clear ();
}
void polygons_append ( Polygons & dst ) const {
if ( layer != NULL && ! layer -> polygons . empty ())
Slic3r :: polygons_append ( dst , layer -> polygons );
}
// The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height).
2021-04-08 15:29:40 +02:00
PrintObjectSupportMaterial :: MyLayer * layer { nullptr };
2016-11-23 15:51:47 +01:00
// Collect extrusions. They will be exported sorted by the bottom height.
2017-03-16 14:02:28 +01:00
ExtrusionEntitiesPtr extrusions ;
2021-04-08 15:29:40 +02:00
private :
2017-01-25 18:33:05 +01:00
// In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support.
// This is useful mainly for the loop interfaces, which are generated before the zig-zag infills.
2021-04-08 15:29:40 +02:00
std :: unique_ptr < Polygons > m_polygons_to_extrude ;
2016-11-23 15:51:47 +01:00
};
typedef std :: vector < MyLayerExtruded *> MyLayerExtrudedPtrs ;
struct LoopInterfaceProcessor
{
LoopInterfaceProcessor ( coordf_t circle_r ) :
2017-01-05 09:14:59 +01:00
n_contact_loops ( 0 ),
2016-11-23 15:51:47 +01:00
circle_radius ( circle_r ),
circle_distance ( circle_r * 3. )
{
// Shape of the top contact area.
circle . points . reserve ( 6 );
for ( size_t i = 0 ; i < 6 ; ++ i ) {
double angle = double ( i ) * M_PI / 3. ;
circle . points . push_back ( Point ( circle_radius * cos ( angle ), circle_radius * sin ( angle )));
}
}
// Generate loop contacts at the top_contact_layer,
// trim the top_contact_layer->polygons with the areas covered by the loops.
2017-03-22 15:35:50 +01:00
void generate ( MyLayerExtruded & top_contact_layer , const Flow & interface_flow_src ) const ;
2016-11-23 15:51:47 +01:00
int n_contact_loops ;
coordf_t circle_radius ;
coordf_t circle_distance ;
Polygon circle ;
};
2017-03-22 15:35:50 +01:00
void LoopInterfaceProcessor :: generate ( MyLayerExtruded & top_contact_layer , const Flow & interface_flow_src ) const
2016-11-23 15:51:47 +01:00
{
if ( n_contact_loops == 0 || top_contact_layer . empty ())
return ;
2021-03-08 13:44:00 +01:00
Flow flow = interface_flow_src . with_height ( top_contact_layer . layer -> height );
2016-11-23 15:51:47 +01:00
Polygons overhang_polygons ;
2017-01-25 18:33:05 +01:00
if ( top_contact_layer . layer -> overhang_polygons != nullptr )
2017-01-20 15:21:05 +01:00
overhang_polygons = std :: move ( * top_contact_layer . layer -> overhang_polygons );
2016-11-23 15:51:47 +01:00
// Generate the outermost loop.
// Find centerline of the external loop (or any other kind of extrusions should the loop be skipped)
2017-01-25 18:33:05 +01:00
ExPolygons top_contact_expolygons = offset_ex ( union_ex ( top_contact_layer . layer -> polygons ), - 0.5f * flow . scaled_width ());
// Grid size and bit shifts for quick and exact to/from grid coordinates manipulation.
coord_t circle_grid_resolution = 1 ;
coord_t circle_grid_powerof2 = 0 ;
{
// epsilon to account for rounding errors
coord_t circle_grid_resolution_non_powerof2 = coord_t ( 2. * circle_distance + 3. );
while ( circle_grid_resolution < circle_grid_resolution_non_powerof2 ) {
circle_grid_resolution <<= 1 ;
++ circle_grid_powerof2 ;
}
}
struct PointAccessor {
const Point * operator ()( const Point & pt ) const { return & pt ; }
};
typedef ClosestPointInRadiusLookup < Point , PointAccessor > ClosestPointLookupType ;
2016-11-23 15:51:47 +01:00
Polygons loops0 ;
{
// find centerline of the external loop of the contours
2017-01-25 18:33:05 +01:00
// Only consider the loops facing the overhang.
2016-11-23 15:51:47 +01:00
Polygons external_loops ;
2017-01-25 18:33:05 +01:00
// Holes in the external loops.
2016-11-23 15:51:47 +01:00
Polygons circles ;
2017-01-25 18:33:05 +01:00
Polygons overhang_with_margin = offset ( union_ex ( overhang_polygons ), 0.5f * flow . scaled_width ());
for ( ExPolygons :: iterator it_contact_expoly = top_contact_expolygons . begin (); it_contact_expoly != top_contact_expolygons . end (); ++ it_contact_expoly ) {
// Store the circle centers placed for an expolygon into a regular grid, hashed by the circle centers.
ClosestPointLookupType circle_centers_lookup ( coord_t ( circle_distance - SCALED_EPSILON ));
Points circle_centers ;
Point center_last ;
// For each contour of the expolygon, start with the outer contour, continue with the holes.
for ( size_t i_contour = 0 ; i_contour <= it_contact_expoly -> holes . size (); ++ i_contour ) {
Polygon & contour = ( i_contour == 0 ) ? it_contact_expoly -> contour : it_contact_expoly -> holes [ i_contour - 1 ];
const Point * seg_current_pt = nullptr ;
coordf_t seg_current_t = 0. ;
2020-11-24 16:00:46 +01:00
if ( ! intersection_pl (( Polylines ) contour . split_at_first_point (), overhang_with_margin ). empty ()) {
2017-01-25 18:33:05 +01:00
// The contour is below the overhang at least to some extent.
//FIXME ideally one would place the circles below the overhang only.
// Walk around the contour and place circles so their centers are not closer than circle_distance from each other.
if ( circle_centers . empty ()) {
// Place the first circle.
seg_current_pt = & contour . points . front ();
seg_current_t = 0. ;
center_last = * seg_current_pt ;
circle_centers_lookup . insert ( center_last );
circle_centers . push_back ( center_last );
}
for ( Points :: const_iterator it = contour . points . begin () + 1 ; it != contour . points . end (); ++ it ) {
// Is it possible to place a circle on this segment? Is it not too close to any of the circles already placed on this contour?
const Point & p1 = * ( it - 1 );
const Point & p2 = * it ;
// Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance.
2018-08-21 21:05:24 +02:00
const Vec2d v_seg ( coordf_t ( p2 ( 0 )) - coordf_t ( p1 ( 0 )), coordf_t ( p2 ( 1 )) - coordf_t ( p1 ( 1 )));
const Vec2d v_cntr ( coordf_t ( p1 ( 0 ) - center_last ( 0 )), coordf_t ( p1 ( 1 ) - center_last ( 1 )));
2018-08-17 14:14:24 +02:00
coordf_t a = v_seg . squaredNorm ();
coordf_t b = 2. * v_seg . dot ( v_cntr );
coordf_t c = v_cntr . squaredNorm () - circle_distance * circle_distance ;
2017-01-25 18:33:05 +01:00
coordf_t disc = b * b - 4. * a * c ;
if ( disc > 0. ) {
// The circle intersects a ray. Avoid the parts of the segment inside the circle.
coordf_t t1 = ( - b - sqrt ( disc )) / ( 2. * a );
coordf_t t2 = ( - b + sqrt ( disc )) / ( 2. * a );
coordf_t t0 = ( seg_current_pt == & p1 ) ? seg_current_t : 0. ;
// Take the lowest t in <t0, 1.>, excluding <t1, t2>.
coordf_t t ;
if ( t0 <= t1 )
t = t0 ;
else if ( t2 <= 1. )
t = t2 ;
else {
// Try the following segment.
seg_current_pt = nullptr ;
continue ;
}
seg_current_pt = & p1 ;
seg_current_t = t ;
2018-08-17 15:53:43 +02:00
center_last = Point ( p1 ( 0 ) + coord_t ( v_seg ( 0 ) * t ), p1 ( 1 ) + coord_t ( v_seg ( 1 ) * t ));
2017-01-25 18:33:05 +01:00
// It has been verified that the new point is far enough from center_last.
// Ensure, that it is far enough from all the centers.
std :: pair < const Point * , coordf_t > circle_closest = circle_centers_lookup . find ( center_last );
if ( circle_closest . first != nullptr ) {
-- it ;
continue ;
}
} else {
// All of the segment is outside the circle. Take the first point.
seg_current_pt = & p1 ;
seg_current_t = 0. ;
center_last = p1 ;
}
// Place the first circle.
circle_centers_lookup . insert ( center_last );
circle_centers . push_back ( center_last );
}
external_loops . push_back ( std :: move ( contour ));
2018-08-17 14:14:24 +02:00
for ( const Point & center : circle_centers ) {
2017-01-25 18:33:05 +01:00
circles . push_back ( circle );
2018-08-17 14:14:24 +02:00
circles . back (). translate ( center );
2017-01-25 18:33:05 +01:00
}
2016-11-23 15:51:47 +01:00
}
}
2017-01-25 18:33:05 +01:00
}
// Apply a pattern to the external loops.
2016-11-23 15:51:47 +01:00
loops0 = diff ( external_loops , circles );
}
Polylines loop_lines ;
{
// make more loops
Polygons loop_polygons = loops0 ;
2019-06-25 13:06:04 +02:00
for ( int i = 1 ; i < n_contact_loops ; ++ i )
2016-11-23 15:51:47 +01:00
polygons_append ( loop_polygons ,
offset2 (
loops0 ,
2019-06-25 13:06:04 +02:00
- i * flow . scaled_spacing () - 0.5f * flow . scaled_spacing (),
2016-11-23 15:51:47 +01:00
0.5f * flow . scaled_spacing ()));
2017-01-25 18:33:05 +01:00
// Clip such loops to the side oriented towards the object.
// Collect split points, so they will be recognized after the clipping.
// At the split points the clipped pieces will be stitched back together.
2016-11-23 15:51:47 +01:00
loop_lines . reserve ( loop_polygons . size ());
2017-01-25 18:33:05 +01:00
std :: unordered_map < Point , int , PointHash > map_split_points ;
for ( Polygons :: const_iterator it = loop_polygons . begin (); it != loop_polygons . end (); ++ it ) {
assert ( map_split_points . find ( it -> first_point ()) == map_split_points . end ());
map_split_points [ it -> first_point ()] = - 1 ;
2016-11-23 15:51:47 +01:00
loop_lines . push_back ( it -> split_at_first_point ());
2017-01-25 18:33:05 +01:00
}
2016-12-13 19:22:23 +01:00
loop_lines = intersection_pl ( loop_lines , offset ( overhang_polygons , scale_ ( SUPPORT_MATERIAL_MARGIN )));
2017-01-25 18:33:05 +01:00
// Because a closed loop has been split to a line, loop_lines may contain continuous segments split to 2 pieces.
// Try to connect them.
for ( int i_line = 0 ; i_line < int ( loop_lines . size ()); ++ i_line ) {
Polyline & polyline = loop_lines [ i_line ];
auto it = map_split_points . find ( polyline . first_point ());
if ( it != map_split_points . end ()) {
// This is a stitching point.
// If this assert triggers, multiple source polygons likely intersected at this point.
assert ( it -> second != - 2 );
if ( it -> second < 0 ) {
// First occurence.
it -> second = i_line ;
} else {
// Second occurence. Join the lines.
Polyline & polyline_1st = loop_lines [ it -> second ];
assert ( polyline_1st . first_point () == it -> first || polyline_1st . last_point () == it -> first );
if ( polyline_1st . first_point () == it -> first )
polyline_1st . reverse ();
polyline_1st . append ( std :: move ( polyline ));
it -> second = - 2 ;
}
continue ;
}
it = map_split_points . find ( polyline . last_point ());
if ( it != map_split_points . end ()) {
// This is a stitching point.
// If this assert triggers, multiple source polygons likely intersected at this point.
assert ( it -> second != - 2 );
if ( it -> second < 0 ) {
// First occurence.
it -> second = i_line ;
} else {
// Second occurence. Join the lines.
Polyline & polyline_1st = loop_lines [ it -> second ];
assert ( polyline_1st . first_point () == it -> first || polyline_1st . last_point () == it -> first );
if ( polyline_1st . first_point () == it -> first )
polyline_1st . reverse ();
polyline . reverse ();
polyline_1st . append ( std :: move ( polyline ));
it -> second = - 2 ;
}
}
}
// Remove empty lines.
remove_degenerate ( loop_lines );
2016-11-23 15:51:47 +01:00
}
// add the contact infill area to the interface area
// note that growing loops by $circle_radius ensures no tiny
// extrusions are left inside the circles; however it creates
// a very large gap between loops and contact_infill_polygons, so maybe another
// solution should be found to achieve both goals
2017-01-25 18:33:05 +01:00
// Store the trimmed polygons into a separate polygon set, so the original infill area remains intact for
// "modulate by layer thickness".
top_contact_layer . set_polygons_to_extrude ( diff ( top_contact_layer . layer -> polygons , offset ( loop_lines , float ( circle_radius * 1.1 ))));
2016-11-23 15:51:47 +01:00
// Transform loops into ExtrusionPath objects.
extrusion_entities_append_paths (
top_contact_layer . extrusions ,
2018-11-02 20:45:23 +01:00
std :: move ( loop_lines ),
2021-03-08 13:44:00 +01:00
erSupportMaterialInterface , flow . mm3_per_mm (), flow . width (), flow . height ());
2016-11-23 15:51:47 +01:00
}
2017-01-19 13:47:06 +01:00
#ifdef SLIC3R_DEBUG
static std :: string dbg_index_to_color ( int idx )
{
if ( idx < 0 )
return "yellow" ;
idx = idx % 3 ;
switch ( idx ) {
case 0 : return "red" ;
case 1 : return "green" ;
default : return "blue" ;
}
}
#endif /* SLIC3R_DEBUG */
// When extruding a bottom interface layer over an object, the bottom interface layer is extruded in a thin air, therefore
// it is being extruded with a bridging flow to not shrink excessively (the die swell effect).
// Tiny extrusions are better avoided and it is always better to anchor the thread to an existing support structure if possible.
// Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers,
// leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers
// to stick too firmly to the object.
2021-03-10 12:14:45 +01:00
//
// Modulate thickness (increase bottom_z) of extrusions_in_out generated for this_layer
// if they overlap with overlapping_layers, whose print_z is above this_layer.bottom_z() and below this_layer.print_z.
2017-01-19 13:47:06 +01:00
void modulate_extrusion_by_overlapping_layers (
// Extrusions generated for this_layer.
ExtrusionEntitiesPtr & extrusions_in_out ,
2017-05-12 11:18:32 +02:00
const PrintObjectSupportMaterial :: MyLayer & this_layer ,
2017-01-19 13:47:06 +01:00
// Multiple layers overlapping with this_layer, sorted bottom up.
2017-01-20 15:21:05 +01:00
const PrintObjectSupportMaterial :: MyLayersPtr & overlapping_layers )
2017-01-19 13:47:06 +01:00
{
2017-05-12 11:18:32 +02:00
size_t n_overlapping_layers = overlapping_layers . size ();
2017-01-20 15:21:05 +01:00
if ( n_overlapping_layers == 0 || extrusions_in_out . empty ())
2017-01-19 13:47:06 +01:00
// The extrusions do not overlap with any other extrusion.
return ;
// Get the initial extrusion parameters.
ExtrusionPath * extrusion_path_template = dynamic_cast < ExtrusionPath *> ( extrusions_in_out . front ());
assert ( extrusion_path_template != nullptr );
2017-04-07 17:44:51 +02:00
ExtrusionRole extrusion_role = extrusion_path_template -> role ();
2017-01-19 13:47:06 +01:00
float extrusion_width = extrusion_path_template -> width ;
struct ExtrusionPathFragment
{
ExtrusionPathFragment () : mm3_per_mm ( - 1 ), width ( - 1 ), height ( - 1 ) {};
ExtrusionPathFragment ( double mm3_per_mm , float width , float height ) : mm3_per_mm ( mm3_per_mm ), width ( width ), height ( height ) {};
Polylines polylines ;
double mm3_per_mm ;
float width ;
float height ;
};
// Split the extrusions by the overlapping layers, reduce their extrusion rate.
// The last path_fragment is from this_layer.
std :: vector < ExtrusionPathFragment > path_fragments (
2017-01-20 15:21:05 +01:00
n_overlapping_layers + 1 ,
2017-01-19 13:47:06 +01:00
ExtrusionPathFragment ( extrusion_path_template -> mm3_per_mm , extrusion_path_template -> width , extrusion_path_template -> height ));
// Don't use it, it will be released.
extrusion_path_template = nullptr ;
#ifdef SLIC3R_DEBUG
static int iRun = 0 ;
++ iRun ;
BoundingBox bbox ;
2017-01-20 15:21:05 +01:00
for ( size_t i_overlapping_layer = 0 ; i_overlapping_layer < n_overlapping_layers ; ++ i_overlapping_layer ) {
2017-01-19 13:47:06 +01:00
const PrintObjectSupportMaterial :: MyLayer & overlapping_layer = * overlapping_layers [ i_overlapping_layer ];
bbox . merge ( get_extents ( overlapping_layer . polygons ));
}
for ( ExtrusionEntitiesPtr :: const_iterator it = extrusions_in_out . begin (); it != extrusions_in_out . end (); ++ it ) {
ExtrusionPath * path = dynamic_cast < ExtrusionPath *> ( * it );
assert ( path != nullptr );
bbox . merge ( get_extents ( path -> polyline ));
}
SVG svg ( debug_out_path ( "support-fragments-%d-%lf.svg" , iRun , this_layer . print_z ). c_str (), bbox );
const float transparency = 0.5f ;
// Filled polygons for the overlapping regions.
svg . draw ( union_ex ( this_layer . polygons ), dbg_index_to_color ( - 1 ), transparency );
2017-01-20 15:21:05 +01:00
for ( size_t i_overlapping_layer = 0 ; i_overlapping_layer < n_overlapping_layers ; ++ i_overlapping_layer ) {
2017-01-19 13:47:06 +01:00
const PrintObjectSupportMaterial :: MyLayer & overlapping_layer = * overlapping_layers [ i_overlapping_layer ];
svg . draw ( union_ex ( overlapping_layer . polygons ), dbg_index_to_color ( int ( i_overlapping_layer )), transparency );
}
// Contours of the overlapping regions.
svg . draw ( to_polylines ( this_layer . polygons ), dbg_index_to_color ( - 1 ), scale_ ( 0.2 ));
2017-01-20 15:21:05 +01:00
for ( size_t i_overlapping_layer = 0 ; i_overlapping_layer < n_overlapping_layers ; ++ i_overlapping_layer ) {
2017-01-19 13:47:06 +01:00
const PrintObjectSupportMaterial :: MyLayer & overlapping_layer = * overlapping_layers [ i_overlapping_layer ];
svg . draw ( to_polylines ( overlapping_layer . polygons ), dbg_index_to_color ( int ( i_overlapping_layer )), scale_ ( 0.1 ));
}
// Fill extrusion, the source.
for ( ExtrusionEntitiesPtr :: const_iterator it = extrusions_in_out . begin (); it != extrusions_in_out . end (); ++ it ) {
ExtrusionPath * path = dynamic_cast < ExtrusionPath *> ( * it );
2017-01-25 18:33:05 +01:00
std :: string color_name ;
switch (( it - extrusions_in_out . begin ()) % 9 ) {
case 0 : color_name = "magenta" ; break ;
case 1 : color_name = "deepskyblue" ; break ;
case 2 : color_name = "coral" ; break ;
case 3 : color_name = "goldenrod" ; break ;
case 4 : color_name = "orange" ; break ;
case 5 : color_name = "olivedrab" ; break ;
case 6 : color_name = "blueviolet" ; break ;
case 7 : color_name = "brown" ; break ;
default : color_name = "orchid" ; break ;
}
svg . draw ( path -> polyline , color_name , scale_ ( 0.2 ));
2017-01-19 13:47:06 +01:00
}
#endif /* SLIC3R_DEBUG */
// End points of the original paths.
2017-01-25 18:33:05 +01:00
std :: vector < std :: pair < Point , Point >> path_ends ;
2017-01-19 13:47:06 +01:00
// Collect the paths of this_layer.
{
Polylines & polylines = path_fragments . back (). polylines ;
2021-03-10 12:14:45 +01:00
for ( ExtrusionEntity * ee : extrusions_in_out ) {
ExtrusionPath * path = dynamic_cast < ExtrusionPath *> ( ee );
2017-01-19 13:47:06 +01:00
assert ( path != nullptr );
polylines . emplace_back ( Polyline ( std :: move ( path -> polyline )));
path_ends . emplace_back ( std :: pair < Point , Point > ( polylines . back (). points . front (), polylines . back (). points . back ()));
}
}
// Destroy the original extrusion paths, their polylines were moved to path_fragments already.
// This will be the destination for the new paths.
extrusions_in_out . clear ();
// Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z.
// Trim by the highest overlapping layer first.
2017-01-20 15:21:05 +01:00
for ( int i_overlapping_layer = int ( n_overlapping_layers ) - 1 ; i_overlapping_layer >= 0 ; -- i_overlapping_layer ) {
2017-01-19 13:47:06 +01:00
const PrintObjectSupportMaterial :: MyLayer & overlapping_layer = * overlapping_layers [ i_overlapping_layer ];
ExtrusionPathFragment & frag = path_fragments [ i_overlapping_layer ];
2017-05-12 11:18:32 +02:00
Polygons polygons_trimming = offset ( union_ex ( overlapping_layer . polygons ), float ( scale_ ( 0.5 * extrusion_width )));
2021-05-05 12:16:40 +02:00
frag . polylines = intersection_pl ( path_fragments . back (). polylines , polygons_trimming );
path_fragments . back (). polylines = diff_pl ( path_fragments . back (). polylines , polygons_trimming );
2017-01-19 13:47:06 +01:00
// Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter).
2017-05-12 11:18:32 +02:00
assert ( this_layer . print_z > overlapping_layer . print_z );
frag . height = float ( this_layer . print_z - overlapping_layer . print_z );
2021-03-08 13:44:00 +01:00
frag . mm3_per_mm = Flow ( frag . width , frag . height , - 1.f ). mm3_per_mm ();
2017-01-19 13:47:06 +01:00
#ifdef SLIC3R_DEBUG
svg . draw ( frag . polylines , dbg_index_to_color ( i_overlapping_layer ), scale_ ( 0.1 ));
#endif /* SLIC3R_DEBUG */
}
#ifdef SLIC3R_DEBUG
svg . draw ( path_fragments . back (). polylines , dbg_index_to_color ( - 1 ), scale_ ( 0.1 ));
2017-05-12 11:18:32 +02:00
svg . Close ();
2017-01-19 13:47:06 +01:00
#endif /* SLIC3R_DEBUG */
// Now chain the split segments using hashing and a nearly exact match, maintaining the order of segments.
// Create a single ExtrusionPath or ExtrusionEntityCollection per source ExtrusionPath.
// Map of fragment start/end points to a pair of <i_overlapping_layer, i_polyline_in_layer>
// Because a non-exact matching is used for the end points, a multi-map is used.
// As the clipper library may reverse the order of some clipped paths, store both ends into the map.
struct ExtrusionPathFragmentEnd
{
ExtrusionPathFragmentEnd ( size_t alayer_idx , size_t apolyline_idx , bool ais_start ) :
layer_idx ( alayer_idx ), polyline_idx ( apolyline_idx ), is_start ( ais_start ) {}
size_t layer_idx ;
size_t polyline_idx ;
bool is_start ;
};
2017-01-25 18:33:05 +01:00
class ExtrusionPathFragmentEndPointAccessor {
public :
ExtrusionPathFragmentEndPointAccessor ( const std :: vector < ExtrusionPathFragment > & path_fragments ) : m_path_fragments ( path_fragments ) {}
// Return an end point of a fragment, or nullptr if the fragment has been consumed already.
const Point * operator ()( const ExtrusionPathFragmentEnd & fragment_end ) const {
const Polyline & polyline = m_path_fragments [ fragment_end . layer_idx ]. polylines [ fragment_end . polyline_idx ];
return polyline . points . empty () ? nullptr :
( fragment_end . is_start ? & polyline . points . front () : & polyline . points . back ());
}
private :
2018-09-19 10:34:21 +02:00
ExtrusionPathFragmentEndPointAccessor & operator = ( const ExtrusionPathFragmentEndPointAccessor & ) {
return * this ;
}
2017-01-25 18:33:05 +01:00
const std :: vector < ExtrusionPathFragment > & m_path_fragments ;
};
const coord_t search_radius = 7 ;
ClosestPointInRadiusLookup < ExtrusionPathFragmentEnd , ExtrusionPathFragmentEndPointAccessor > map_fragment_starts (
search_radius , ExtrusionPathFragmentEndPointAccessor ( path_fragments ));
2017-01-20 15:21:05 +01:00
for ( size_t i_overlapping_layer = 0 ; i_overlapping_layer <= n_overlapping_layers ; ++ i_overlapping_layer ) {
2017-01-19 13:47:06 +01:00
const Polylines & polylines = path_fragments [ i_overlapping_layer ]. polylines ;
for ( size_t i_polyline = 0 ; i_polyline < polylines . size (); ++ i_polyline ) {
// Map a starting point of a polyline to a pair of <layer, polyline>
if ( polylines [ i_polyline ]. points . size () >= 2 ) {
2017-01-25 18:33:05 +01:00
map_fragment_starts . insert ( ExtrusionPathFragmentEnd ( i_overlapping_layer , i_polyline , true ));
map_fragment_starts . insert ( ExtrusionPathFragmentEnd ( i_overlapping_layer , i_polyline , false ));
2017-01-19 13:47:06 +01:00
}
}
}
// For each source path:
for ( size_t i_path = 0 ; i_path < path_ends . size (); ++ i_path ) {
const Point & pt_start = path_ends [ i_path ]. first ;
const Point & pt_end = path_ends [ i_path ]. second ;
Point pt_current = pt_start ;
// Find a chain of fragments with the original / reduced print height.
2017-05-12 11:18:32 +02:00
ExtrusionMultiPath multipath ;
2017-01-19 13:47:06 +01:00
for (;;) {
2017-01-25 18:33:05 +01:00
// Find a closest end point to pt_current.
std :: pair < const ExtrusionPathFragmentEnd * , coordf_t > end_and_dist2 = map_fragment_starts . find ( pt_current );
2017-05-12 11:18:32 +02:00
// There may be a bug in Clipper flipping the order of two last points in a fragment?
2017-01-25 18:33:05 +01:00
// assert(end_and_dist2.first != nullptr);
2017-05-12 11:18:32 +02:00
assert ( end_and_dist2 . first == nullptr || end_and_dist2 . second < search_radius * search_radius );
2017-01-25 18:33:05 +01:00
if ( end_and_dist2 . first == nullptr ) {
2017-01-19 13:47:06 +01:00
// New fragment connecting to pt_current was not found.
// Verify that the last point found is close to the original end point of the unfragmented path.
2019-03-11 17:18:38 +01:00
//const double d2 = (pt_end - pt_current).cast<double>.squaredNorm();
2017-01-25 18:33:05 +01:00
//assert(d2 < coordf_t(search_radius * search_radius));
2017-01-19 13:47:06 +01:00
// End of the path.
break ;
}
2017-01-25 18:33:05 +01:00
const ExtrusionPathFragmentEnd & fragment_end_min = * end_and_dist2 . first ;
2017-01-19 13:47:06 +01:00
// Fragment to consume.
ExtrusionPathFragment & frag = path_fragments [ fragment_end_min . layer_idx ];
Polyline & frag_polyline = frag . polylines [ fragment_end_min . polyline_idx ];
// Path to append the fragment to.
2017-05-12 11:18:32 +02:00
ExtrusionPath * path = multipath . paths . empty () ? nullptr : & multipath . paths . back ();
2017-01-19 13:47:06 +01:00
if ( path != nullptr ) {
2017-01-25 18:33:05 +01:00
// Verify whether the path is compatible with the current fragment.
2017-05-12 11:18:32 +02:00
assert ( this_layer . layer_type == PrintObjectSupportMaterial :: sltBottomContact || path -> height != frag . height || path -> mm3_per_mm != frag . mm3_per_mm );
if ( path -> height != frag . height || path -> mm3_per_mm != frag . mm3_per_mm ) {
path = nullptr ;
}
// Merging with the previous path. This can only happen if the current layer was reduced by a base layer, which was split into a base and interface layer.
2017-01-19 13:47:06 +01:00
}
if ( path == nullptr ) {
// Allocate a new path.
2017-05-12 11:18:32 +02:00
multipath . paths . push_back ( ExtrusionPath ( extrusion_role , frag . mm3_per_mm , frag . width , frag . height ));
2017-01-20 15:21:05 +01:00
path = & multipath . paths . back ();
2017-01-19 13:47:06 +01:00
}
// The Clipper library may flip the order of the clipped polylines arbitrarily.
// Reverse the source polyline, if connecting to the end.
if ( ! fragment_end_min . is_start )
frag_polyline . reverse ();
// Enforce exact overlap of the end points of successive fragments.
2017-05-12 11:18:32 +02:00
assert ( frag_polyline . points . front () == pt_current );
frag_polyline . points . front () = pt_current ;
2017-01-19 13:47:06 +01:00
// Don't repeat the first point.
if ( ! path -> polyline . points . empty ())
path -> polyline . points . pop_back ();
// Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time.
path -> polyline . append ( std :: move ( frag_polyline ));
frag_polyline . points . clear ();
pt_current = path -> polyline . points . back ();
if ( pt_current == pt_end ) {
// End of the path.
break ;
}
}
2017-05-12 11:18:32 +02:00
if ( ! multipath . paths . empty ()) {
if ( multipath . paths . size () == 1 ) {
2017-01-19 13:47:06 +01:00
// This path was not fragmented.
2017-05-12 11:18:32 +02:00
extrusions_in_out . push_back ( new ExtrusionPath ( std :: move ( multipath . paths . front ())));
2017-01-19 13:47:06 +01:00
} else {
// This path was fragmented. Copy the collection as a whole object, so the order inside the collection will not be changed
// during the chaining of extrusions_in_out.
2017-05-12 11:18:32 +02:00
extrusions_in_out . push_back ( new ExtrusionMultiPath ( std :: move ( multipath )));
2017-01-19 13:47:06 +01:00
}
}
}
2017-05-12 11:18:32 +02:00
// If there are any non-consumed fragments, add them separately.
//FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected.
for ( auto it_fragment = path_fragments . begin (); it_fragment != path_fragments . end (); ++ it_fragment )
extrusion_entities_append_paths ( extrusions_in_out , std :: move ( it_fragment -> polylines ), extrusion_role , it_fragment -> mm3_per_mm , it_fragment -> width , it_fragment -> height );
2017-01-19 13:47:06 +01:00
}
2016-10-20 13:04:23 +02:00
void PrintObjectSupportMaterial :: generate_toolpaths (
2021-02-03 15:12:53 +01:00
SupportLayerPtrs & support_layers ,
2016-12-20 12:19:13 +01:00
const MyLayersPtr & raft_layers ,
2016-10-13 16:00:22 +02:00
const MyLayersPtr & bottom_contacts ,
const MyLayersPtr & top_contacts ,
const MyLayersPtr & intermediate_layers ,
2021-02-15 18:07:44 +01:00
const MyLayersPtr & interface_layers ,
const MyLayersPtr & base_interface_layers ) const
2016-10-13 16:00:22 +02:00
{
2016-11-23 15:51:47 +01:00
// loop_interface_processor with a given circle radius.
2021-03-19 11:21:29 +01:00
LoopInterfaceProcessor loop_interface_processor ( 1.5 * m_support_params . support_material_interface_flow . scaled_width ());
2017-01-25 18:33:05 +01:00
loop_interface_processor . n_contact_loops = this -> has_contact_loops () ? 1 : 0 ;
2016-10-13 16:00:22 +02:00
2017-10-17 16:01:18 +02:00
float base_angle = Geometry :: deg2rad ( float ( m_object_config -> support_material_angle . value ));
float interface_angle = Geometry :: deg2rad ( float ( m_object_config -> support_material_angle . value + 90. ));
2021-03-19 11:21:29 +01:00
coordf_t interface_spacing = m_object_config -> support_material_interface_spacing . value + m_support_params . support_material_interface_flow . spacing ();
coordf_t interface_density = std :: min ( 1. , m_support_params . support_material_interface_flow . spacing () / interface_spacing );
coordf_t support_spacing = m_object_config -> support_material_spacing . value + m_support_params . support_material_flow . spacing ();
coordf_t support_density = std :: min ( 1. , m_support_params . support_material_flow . spacing () / support_spacing );
if ( m_object_config -> support_material_interface_layers . value == 0 ) {
2017-02-14 19:49:30 +01:00
// No interface layers allowed, print everything with the base support pattern.
interface_spacing = support_spacing ;
interface_density = support_density ;
}
2016-10-13 16:00:22 +02:00
// Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config -> support_material_pattern ;
bool with_sheath = m_object_config -> support_material_with_sheath ;
2021-04-08 15:29:40 +02:00
InfillPattern infill_pattern = ( support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase );
2017-02-14 19:49:30 +01:00
std :: vector < float > angles ;
angles . push_back ( base_angle );
2019-09-24 18:09:48 +02:00
if ( support_pattern == smpRectilinearGrid )
2017-02-14 19:49:30 +01:00
angles . push_back ( interface_angle );
2019-09-24 18:09:48 +02:00
2017-03-22 15:35:50 +01:00
BoundingBox bbox_object ( Point ( - scale_ ( 1. ), - scale_ ( 1.0 )), Point ( scale_ ( 1. ), scale_ ( 1. )));
2016-10-13 16:00:22 +02:00
2017-01-20 15:21:05 +01:00
// const coordf_t link_max_length_factor = 3.;
const coordf_t link_max_length_factor = 0. ;
2017-02-14 19:49:30 +01:00
float raft_angle_1st_layer = 0.f ;
float raft_angle_base = 0.f ;
float raft_angle_interface = 0.f ;
if ( m_slicing_params . base_raft_layers > 1 ) {
// There are all raft layer types (1st layer, base, interface & contact layers) available.
raft_angle_1st_layer = interface_angle ;
raft_angle_base = base_angle ;
raft_angle_interface = interface_angle ;
} else if ( m_slicing_params . base_raft_layers == 1 || m_slicing_params . interface_raft_layers > 1 ) {
// 1st layer, interface & contact layers available.
raft_angle_1st_layer = base_angle ;
if ( this -> has_support ())
// Print 1st layer at 45 degrees from both the interface and base angles as both can land on the 1st layer.
raft_angle_1st_layer += 0.7854f ;
raft_angle_interface = interface_angle ;
} else if ( m_slicing_params . interface_raft_layers == 1 ) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
2017-05-12 11:18:32 +02:00
assert ( m_slicing_params . base_raft_layers == 0 );
assert ( m_slicing_params . interface_raft_layers == 1 );
assert ( m_slicing_params . raft_layers () == 1 && raft_layers . size () == 0 );
2017-02-14 19:49:30 +01:00
} else {
// No raft.
assert ( m_slicing_params . base_raft_layers == 0 );
assert ( m_slicing_params . interface_raft_layers == 0 );
assert ( m_slicing_params . raft_layers () == 0 && raft_layers . size () == 0 );
}
2016-12-20 12:19:13 +01:00
// Insert the raft base layers.
2017-03-22 15:35:50 +01:00
size_t n_raft_layers = size_t ( std :: max ( 0 , int ( m_slicing_params . raft_layers ()) - 1 ));
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( 0 , n_raft_layers ),
2021-02-15 18:07:44 +01:00
[ this , & support_layers , & raft_layers ,
2017-03-22 15:35:50 +01:00
infill_pattern , & bbox_object , support_density , interface_density , raft_angle_1st_layer , raft_angle_base , raft_angle_interface , link_max_length_factor , with_sheath ]
( const tbb :: blocked_range < size_t >& range ) {
for ( size_t support_layer_id = range . begin (); support_layer_id < range . end (); ++ support_layer_id )
{
assert ( support_layer_id < raft_layers . size ());
2021-02-03 15:12:53 +01:00
SupportLayer & support_layer = * support_layers [ support_layer_id ];
2017-03-22 15:35:50 +01:00
assert ( support_layer . support_fills . entities . empty ());
MyLayer & raft_layer = * raft_layers [ support_layer_id ];
2017-02-14 19:49:30 +01:00
2017-03-22 15:35:50 +01:00
std :: unique_ptr < Fill > filler_interface = std :: unique_ptr < Fill > ( Fill :: new_from_type ( ipRectilinear ));
std :: unique_ptr < Fill > filler_support = std :: unique_ptr < Fill > ( Fill :: new_from_type ( infill_pattern ));
filler_interface -> set_bounding_box ( bbox_object );
filler_support -> set_bounding_box ( bbox_object );
// Print the support base below the support columns, or the support base for the support columns plus the contacts.
if ( support_layer_id > 0 ) {
2021-02-24 08:48:33 +01:00
const Polygons & to_infill_polygons = ( support_layer_id < m_slicing_params . base_raft_layers ) ?
2017-03-22 15:35:50 +01:00
raft_layer . polygons :
//FIXME misusing contact_polygons for support columns.
(( raft_layer . contact_polygons == nullptr ) ? Polygons () : * raft_layer . contact_polygons );
if ( ! to_infill_polygons . empty ()) {
2021-03-08 13:44:00 +01:00
assert ( ! raft_layer . bridging );
2021-03-19 11:21:29 +01:00
Flow flow ( float ( m_support_params . support_material_flow . width ()), float ( raft_layer . height ), m_support_params . support_material_flow . nozzle_diameter ());
2021-02-24 08:48:33 +01:00
Fill * filler = filler_support . get ();
filler -> angle = raft_angle_base ;
2021-03-19 11:21:29 +01:00
filler -> spacing = m_support_params . support_material_flow . spacing ();
2021-02-24 08:48:33 +01:00
filler -> link_max_length = coord_t ( scale_ ( filler -> spacing * link_max_length_factor / support_density ));
fill_expolygons_with_sheath_generate_paths (
// Destination
support_layer . support_fills . entities ,
// Regions to fill
to_infill_polygons ,
// Filler and its parameters
filler , float ( support_density ),
// Extrusion parameters
erSupportMaterial , flow ,
2021-03-10 12:14:45 +01:00
with_sheath , false );
2017-02-14 19:49:30 +01:00
}
}
2017-03-22 15:35:50 +01:00
Fill * filler = filler_interface . get ();
2021-03-19 11:21:29 +01:00
Flow flow = m_support_params . first_layer_flow ;
2017-03-22 15:35:50 +01:00
float density = 0.f ;
if ( support_layer_id == 0 ) {
// Base flange.
filler -> angle = raft_angle_1st_layer ;
2021-03-19 11:21:29 +01:00
filler -> spacing = m_support_params . first_layer_flow . spacing ();
2021-02-25 12:29:08 +01:00
density = float ( m_object_config -> raft_first_layer_density . value * 0.01 );
2017-03-22 15:35:50 +01:00
} else if ( support_layer_id >= m_slicing_params . base_raft_layers ) {
filler -> angle = raft_angle_interface ;
// We don't use $base_flow->spacing because we need a constant spacing
// value that guarantees that all layers are correctly aligned.
2021-03-19 11:21:29 +01:00
filler -> spacing = m_support_params . support_material_flow . spacing ();
2021-03-08 13:44:00 +01:00
assert ( ! raft_layer . bridging );
2021-03-19 11:21:29 +01:00
flow = Flow ( float ( m_support_params . support_material_interface_flow . width ()), float ( raft_layer . height ), m_support_params . support_material_flow . nozzle_diameter ());
2017-03-22 15:35:50 +01:00
density = float ( interface_density );
} else
2017-01-25 18:33:05 +01:00
continue ;
2017-03-22 15:35:50 +01:00
filler -> link_max_length = coord_t ( scale_ ( filler -> spacing * link_max_length_factor / density ));
2021-02-24 09:22:31 +01:00
fill_expolygons_with_sheath_generate_paths (
2016-11-23 15:51:47 +01:00
// Destination
2017-03-22 15:35:50 +01:00
support_layer . support_fills . entities ,
2016-11-23 15:51:47 +01:00
// Regions to fill
2021-02-24 09:22:31 +01:00
raft_layer . polygons ,
2016-11-23 15:51:47 +01:00
// Filler and its parameters
filler , density ,
// Extrusion parameters
2021-02-24 09:22:31 +01:00
( support_layer_id < m_slicing_params . base_raft_layers ) ? erSupportMaterial : erSupportMaterialInterface , flow ,
// sheath at first layer
2021-03-10 12:14:45 +01:00
support_layer_id == 0 , support_layer_id == 0 );
2016-11-23 15:51:47 +01:00
}
2017-03-22 15:35:50 +01:00
});
2016-11-23 15:51:47 +01:00
2017-03-22 15:35:50 +01:00
struct LayerCacheItem {
LayerCacheItem ( MyLayerExtruded * layer_extruded = nullptr ) : layer_extruded ( layer_extruded ) {}
MyLayerExtruded * layer_extruded ;
std :: vector < MyLayer *> overlapping ;
};
struct LayerCache {
2021-03-10 12:14:45 +01:00
MyLayerExtruded bottom_contact_layer ;
MyLayerExtruded top_contact_layer ;
MyLayerExtruded base_layer ;
MyLayerExtruded interface_layer ;
MyLayerExtruded base_interface_layer ;
boost :: container :: static_vector < LayerCacheItem , 5 > nonempty ;
void add_nonempty_and_sort () {
for ( MyLayerExtruded * item : { & bottom_contact_layer , & top_contact_layer , & interface_layer , & base_interface_layer , & base_layer })
if ( ! item -> empty ())
this -> nonempty . emplace_back ( item );
// Sort the layers with the same print_z coordinate by their heights, thickest first.
std :: stable_sort ( this -> nonempty . begin (), this -> nonempty . end (), []( const LayerCacheItem & lc1 , const LayerCacheItem & lc2 ) { return lc1 . layer_extruded -> layer -> height > lc2 . layer_extruded -> layer -> height ; });
}
2017-03-22 15:35:50 +01:00
};
2021-04-08 15:29:40 +02:00
std :: vector < LayerCache > layer_caches ( support_layers . size ());
2017-04-07 17:44:51 +02:00
2021-02-24 15:59:09 +01:00
const auto fill_type_interface =
( m_object_config -> support_material_interface_pattern == smipAuto && m_slicing_params . soluble_interface ) ||
m_object_config -> support_material_interface_pattern == smipConcentric ?
ipConcentric : ipRectilinear ;
2021-02-03 15:12:53 +01:00
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( n_raft_layers , support_layers . size ()),
2021-02-15 18:07:44 +01:00
[ this , & support_layers , & bottom_contacts , & top_contacts , & intermediate_layers , & interface_layers , & base_interface_layers , & layer_caches , & loop_interface_processor ,
2021-02-24 15:59:09 +01:00
infill_pattern , & bbox_object , support_density , fill_type_interface , interface_density , interface_angle , & angles , link_max_length_factor , with_sheath ]
2017-03-22 15:35:50 +01:00
( const tbb :: blocked_range < size_t >& range ) {
// Indices of the 1st layer in their respective container at the support layer height.
2017-05-12 11:14:25 +02:00
size_t idx_layer_bottom_contact = size_t ( - 1 );
size_t idx_layer_top_contact = size_t ( - 1 );
size_t idx_layer_intermediate = size_t ( - 1 );
2021-02-24 08:48:33 +01:00
size_t idx_layer_interface = size_t ( - 1 );
size_t idx_layer_base_interface = size_t ( - 1 );
const auto fill_type_first_layer = ipRectilinear ;
auto filler_interface = std :: unique_ptr < Fill > ( Fill :: new_from_type ( fill_type_interface ));
// Filler for the 1st layer interface, if different from filler_interface.
auto filler_first_layer_ptr = std :: unique_ptr < Fill > ( range . begin () == 0 && fill_type_interface != fill_type_first_layer ? Fill :: new_from_type ( fill_type_first_layer ) : nullptr );
// Pointer to the 1st layer interface filler.
auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr . get () : filler_interface . get ();
// Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer).
2021-02-15 18:07:44 +01:00
auto filler_base_interface = std :: unique_ptr < Fill > ( base_interface_layers . empty () ? nullptr : Fill :: new_from_type ( ipRectilinear ));
auto filler_support = std :: unique_ptr < Fill > ( Fill :: new_from_type ( infill_pattern ));
2017-03-22 15:35:50 +01:00
filler_interface -> set_bounding_box ( bbox_object );
2021-02-24 08:48:33 +01:00
if ( filler_first_layer_ptr )
filler_first_layer_ptr -> set_bounding_box ( bbox_object );
2021-02-15 18:07:44 +01:00
if ( filler_base_interface )
filler_base_interface -> set_bounding_box ( bbox_object );
2017-03-22 15:35:50 +01:00
filler_support -> set_bounding_box ( bbox_object );
for ( size_t support_layer_id = range . begin (); support_layer_id < range . end (); ++ support_layer_id )
{
2021-02-03 15:12:53 +01:00
SupportLayer & support_layer = * support_layers [ support_layer_id ];
2017-03-22 15:35:50 +01:00
LayerCache & layer_cache = layer_caches [ support_layer_id ];
// Find polygons with the same print_z.
MyLayerExtruded & bottom_contact_layer = layer_cache . bottom_contact_layer ;
MyLayerExtruded & top_contact_layer = layer_cache . top_contact_layer ;
MyLayerExtruded & base_layer = layer_cache . base_layer ;
MyLayerExtruded & interface_layer = layer_cache . interface_layer ;
2021-02-15 18:07:44 +01:00
MyLayerExtruded & base_interface_layer = layer_cache . base_interface_layer ;
2017-03-22 15:35:50 +01:00
// Increment the layer indices to find a layer at support_layer.print_z.
{
auto fun = [ & support_layer ]( const MyLayer * l ){ return l -> print_z >= support_layer . print_z - EPSILON ; };
idx_layer_bottom_contact = idx_higher_or_equal ( bottom_contacts , idx_layer_bottom_contact , fun );
idx_layer_top_contact = idx_higher_or_equal ( top_contacts , idx_layer_top_contact , fun );
idx_layer_intermediate = idx_higher_or_equal ( intermediate_layers , idx_layer_intermediate , fun );
2021-02-15 18:07:44 +01:00
idx_layer_interface = idx_higher_or_equal ( interface_layers , idx_layer_interface , fun );
idx_layer_base_interface = idx_higher_or_equal ( base_interface_layers , idx_layer_base_interface , fun );
2017-03-22 15:35:50 +01:00
}
// Copy polygons from the layers.
if ( idx_layer_bottom_contact < bottom_contacts . size () && bottom_contacts [ idx_layer_bottom_contact ] -> print_z < support_layer . print_z + EPSILON )
bottom_contact_layer . layer = bottom_contacts [ idx_layer_bottom_contact ];
if ( idx_layer_top_contact < top_contacts . size () && top_contacts [ idx_layer_top_contact ] -> print_z < support_layer . print_z + EPSILON )
top_contact_layer . layer = top_contacts [ idx_layer_top_contact ];
2021-02-15 18:07:44 +01:00
if ( idx_layer_interface < interface_layers . size () && interface_layers [ idx_layer_interface ] -> print_z < support_layer . print_z + EPSILON )
interface_layer . layer = interface_layers [ idx_layer_interface ];
if ( idx_layer_base_interface < base_interface_layers . size () && base_interface_layers [ idx_layer_base_interface ] -> print_z < support_layer . print_z + EPSILON )
base_interface_layer . layer = base_interface_layers [ idx_layer_base_interface ];
2017-03-22 15:35:50 +01:00
if ( idx_layer_intermediate < intermediate_layers . size () && intermediate_layers [ idx_layer_intermediate ] -> print_z < support_layer . print_z + EPSILON )
base_layer . layer = intermediate_layers [ idx_layer_intermediate ];
if ( m_object_config -> support_material_interface_layers == 0 ) {
2021-03-19 13:39:20 +01:00
// If no top interface layers were requested, we treat the contact layer exactly as a generic base layer.
2021-03-19 11:21:29 +01:00
if ( m_support_params . can_merge_support_regions ) {
2017-05-12 11:18:32 +02:00
if ( base_layer . could_merge ( top_contact_layer ))
base_layer . merge ( std :: move ( top_contact_layer ));
2021-03-19 13:39:20 +01:00
else if ( base_layer . empty ())
base_layer = std :: move ( top_contact_layer );
2017-04-07 17:44:51 +02:00
}
2017-03-22 15:35:50 +01:00
} else {
2021-03-19 11:21:29 +01:00
loop_interface_processor . generate ( top_contact_layer , m_support_params . support_material_interface_flow );
2017-03-22 15:35:50 +01:00
// If no loops are allowed, we treat the contact layer exactly as a generic interface layer.
// Merge interface_layer into top_contact_layer, as the top_contact_layer is not synchronized and therefore it will be used
// to trim other layers.
if ( top_contact_layer . could_merge ( interface_layer ))
top_contact_layer . merge ( std :: move ( interface_layer ));
}
2021-03-19 11:21:29 +01:00
if (( m_object_config -> support_material_interface_layers == 0 || m_object_config -> support_material_bottom_interface_layers == 0 ) && m_support_params . can_merge_support_regions ) {
2021-03-09 15:29:13 +01:00
if ( base_layer . could_merge ( bottom_contact_layer ))
base_layer . merge ( std :: move ( bottom_contact_layer ));
2021-03-19 13:39:20 +01:00
else if ( base_layer . empty () && ! bottom_contact_layer . empty () && ! bottom_contact_layer . layer -> bridging )
base_layer = std :: move ( bottom_contact_layer );
2021-03-09 15:29:13 +01:00
}
2017-03-22 15:35:50 +01:00
2021-02-24 17:34:51 +01:00
#if 0
2021-02-15 18:07:44 +01:00
if ( ! interface_layer.empty() && ! base_layer.empty()) {
2017-03-22 15:35:50 +01:00
// turn base support into interface when it's contained in our holes
// (this way we get wider interface anchoring)
2021-02-24 17:34:51 +01:00
//FIXME The intention of the code below is unclear. One likely wanted to just merge small islands of base layers filling in the holes
// inside interface layers, but the code below fills just too much, see GH #4570
2017-03-22 15:35:50 +01:00
Polygons islands = top_level_islands(interface_layer.layer->polygons);
polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands));
base_layer.layer->polygons = diff(base_layer.layer->polygons, islands);
}
2021-02-24 17:34:51 +01:00
#endif
2017-03-22 15:35:50 +01:00
2017-05-12 11:18:32 +02:00
// Top and bottom contacts, interface layers.
2017-03-22 15:35:50 +01:00
for ( size_t i = 0 ; i < 3 ; ++ i ) {
MyLayerExtruded & layer_ex = ( i == 0 ) ? top_contact_layer : ( i == 1 ? bottom_contact_layer : interface_layer );
if ( layer_ex . empty () || layer_ex . polygons_to_extrude (). empty ())
continue ;
2021-03-19 13:39:20 +01:00
bool interface_as_base = m_object_config -> support_material_interface_layers . value == 0 ||
( m_object_config -> support_material_bottom_interface_layers == 0 && & layer_ex == & bottom_contact_layer );
2018-09-17 15:12:13 +02:00
//FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
// the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
2021-03-08 13:44:00 +01:00
auto interface_flow = layer_ex . layer -> bridging ?
2021-03-19 11:21:29 +01:00
Flow :: bridging_flow ( layer_ex . layer -> height , m_support_params . support_material_bottom_interface_flow . nozzle_diameter ()) :
( interface_as_base ? & m_support_params . support_material_flow : & m_support_params . support_material_interface_flow ) -> with_height ( float ( layer_ex . layer -> height ));
2017-05-12 11:18:32 +02:00
filler_interface -> angle = interface_as_base ?
2017-03-22 15:35:50 +01:00
// If zero interface layers are configured, use the same angle as for the base layers.
angles [ support_layer_id % angles . size ()] :
// Use interface angle for the interface layers.
interface_angle ;
2021-03-19 13:39:20 +01:00
double density = interface_as_base ? support_density : interface_density ;
filler_interface -> spacing = interface_as_base ? m_support_params . support_material_flow . spacing () : m_support_params . support_material_interface_flow . spacing ();
filler_interface -> link_max_length = coord_t ( scale_ ( filler_interface -> spacing * link_max_length_factor / density ));
2017-05-12 11:18:32 +02:00
fill_expolygons_generate_paths (
// Destination
layer_ex . extrusions ,
// Regions to fill
2021-05-05 12:16:40 +02:00
union_safety_offset_ex ( layer_ex . polygons_to_extrude ()),
2017-05-12 11:18:32 +02:00
// Filler and its parameters
2021-03-19 13:39:20 +01:00
filler_interface . get (), float ( density ),
2017-05-12 11:18:32 +02:00
// Extrusion parameters
erSupportMaterialInterface , interface_flow );
}
2017-03-22 15:35:50 +01:00
2021-02-15 18:07:44 +01:00
// Base interface layers under soluble interfaces
2021-03-19 13:39:20 +01:00
if ( ! base_interface_layer . empty () && ! base_interface_layer . polygons_to_extrude (). empty ()) {
2021-02-15 18:07:44 +01:00
Fill * filler = filler_base_interface . get ();
//FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
// the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
2021-03-08 13:44:00 +01:00
assert ( ! base_interface_layer . layer -> bridging );
2021-03-19 11:21:29 +01:00
Flow interface_flow = m_support_params . support_material_flow . with_height ( float ( base_interface_layer . layer -> height ));
2021-02-15 18:07:44 +01:00
filler -> angle = interface_angle ;
2021-03-19 11:21:29 +01:00
filler -> spacing = m_support_params . support_material_interface_flow . spacing ();
2021-02-15 18:07:44 +01:00
filler -> link_max_length = coord_t ( scale_ ( filler -> spacing * link_max_length_factor / interface_density ));
fill_expolygons_generate_paths (
// Destination
base_interface_layer . extrusions ,
//base_layer_interface.extrusions,
// Regions to fill
2021-05-05 12:16:40 +02:00
union_safety_offset_ex ( base_interface_layer . polygons_to_extrude ()),
2021-02-15 18:07:44 +01:00
// Filler and its parameters
filler , float ( interface_density ),
// Extrusion parameters
erSupportMaterial , interface_flow );
}
2017-03-22 15:35:50 +01:00
// Base support or flange.
if ( ! base_layer . empty () && ! base_layer . polygons_to_extrude (). empty ()) {
Fill * filler = filler_support . get ();
filler -> angle = angles [ support_layer_id % angles . size ()];
// We don't use $base_flow->spacing because we need a constant spacing
// value that guarantees that all layers are correctly aligned.
2021-03-08 13:44:00 +01:00
assert ( ! base_layer . layer -> bridging );
2021-03-19 11:21:29 +01:00
auto flow = m_support_params . support_material_flow . with_height ( float ( base_layer . layer -> height ));
filler -> spacing = m_support_params . support_material_flow . spacing ();
2017-03-22 15:35:50 +01:00
filler -> link_max_length = coord_t ( scale_ ( filler -> spacing * link_max_length_factor / support_density ));
float density = float ( support_density );
2021-02-24 08:48:33 +01:00
bool sheath = with_sheath ;
2021-03-10 12:14:45 +01:00
bool no_sort = false ;
2017-03-22 15:35:50 +01:00
if ( base_layer . layer -> bottom_z < EPSILON ) {
// Base flange (the 1st layer).
2021-02-24 08:48:33 +01:00
filler = filler_first_layer ;
2017-10-17 16:01:18 +02:00
filler -> angle = Geometry :: deg2rad ( float ( m_object_config -> support_material_angle . value + 90. ));
2021-02-24 08:48:33 +01:00
density = float ( m_object_config -> raft_first_layer_density . value * 0.01 );
2021-03-19 11:21:29 +01:00
flow = m_support_params . first_layer_flow ;
2017-03-22 15:35:50 +01:00
// use the proper spacing for first layer as we don't need to align
2019-09-24 16:01:01 +02:00
// its pattern to the other layers
2017-03-22 15:35:50 +01:00
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
filler -> spacing = flow . spacing ();
filler -> link_max_length = coord_t ( scale_ ( filler -> spacing * link_max_length_factor / density ));
2021-03-10 12:14:45 +01:00
sheath = true ;
no_sort = true ;
2017-03-22 15:35:50 +01:00
}
2021-02-24 08:48:33 +01:00
fill_expolygons_with_sheath_generate_paths (
2017-03-22 15:35:50 +01:00
// Destination
2021-02-24 08:48:33 +01:00
base_layer . extrusions ,
2017-03-22 15:35:50 +01:00
// Regions to fill
2021-02-24 08:48:33 +01:00
base_layer . polygons_to_extrude (),
2017-03-22 15:35:50 +01:00
// Filler and its parameters
filler , density ,
// Extrusion parameters
2021-02-24 08:48:33 +01:00
erSupportMaterial , flow ,
2021-03-10 12:14:45 +01:00
sheath , no_sort );
2021-02-24 08:48:33 +01:00
2017-03-22 15:35:50 +01:00
}
2021-02-15 18:07:44 +01:00
// Merge base_interface_layers to base_layers to avoid unneccessary retractions
if ( ! base_layer . empty () && ! base_interface_layer . empty () && ! base_layer . polygons_to_extrude (). empty () && ! base_interface_layer . polygons_to_extrude (). empty () &&
base_layer . could_merge ( base_interface_layer ))
base_layer . merge ( std :: move ( base_interface_layer ));
2021-03-10 12:14:45 +01:00
layer_cache . add_nonempty_and_sort ();
2017-03-22 15:35:50 +01:00
// Collect the support areas with this print_z into islands, as there is no need
// for retraction over these islands.
Polygons polys ;
// Collect the extrusions, sorted by the bottom extrusion height.
2021-03-10 12:14:45 +01:00
for ( LayerCacheItem & layer_cache_item : layer_cache . nonempty ) {
2017-03-22 15:35:50 +01:00
// Collect islands to polys.
layer_cache_item . layer_extruded -> polygons_append ( polys );
// The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free"
// in a sense that they are not synchronized with other support layers. As the top and bottom contact surfaces
// are inflated to achieve a better anchoring, it may happen, that these surfaces will at least partially
// overlap in Z with another support layers, leading to over-extrusion.
// Mitigate the over-extrusion by modulating the extrusion rate over these regions.
// The print head will follow the same print_z, but the layer thickness will be reduced
// where it overlaps with another support layer.
//FIXME When printing a briging path, what is an equivalent height of the squished extrudate of the same width?
// Collect overlapping top/bottom surfaces.
2021-02-15 18:07:44 +01:00
layer_cache_item . overlapping . reserve ( 20 );
2017-03-22 15:35:50 +01:00
coordf_t bottom_z = layer_cache_item . layer_extruded -> layer -> bottom_print_z () + EPSILON ;
2021-03-10 12:14:45 +01:00
auto add_overlapping = [ & layer_cache_item , bottom_z ]( const MyLayersPtr & layers , size_t idx_top ) {
for ( int i = int ( idx_top ) - 1 ; i >= 0 && layers [ i ] -> print_z > bottom_z ; -- i )
layer_cache_item . overlapping . push_back ( layers [ i ]);
};
add_overlapping ( top_contacts , idx_layer_top_contact );
2017-03-22 15:35:50 +01:00
if ( layer_cache_item . layer_extruded -> layer -> layer_type == sltBottomContact ) {
// Bottom contact layer may overlap with a base layer, which may be changed to interface layer.
2021-03-10 12:14:45 +01:00
add_overlapping ( intermediate_layers , idx_layer_intermediate );
add_overlapping ( interface_layers , idx_layer_interface );
add_overlapping ( base_interface_layers , idx_layer_base_interface );
2017-03-22 15:35:50 +01:00
}
2021-03-10 12:14:45 +01:00
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
std :: stable_sort ( layer_cache_item . overlapping . begin (), layer_cache_item . overlapping . end (), []( auto * l1 , auto * l2 ) { return * l1 < * l2 ; });
2017-03-22 15:35:50 +01:00
}
if ( ! polys . empty ())
expolygons_append ( support_layer . support_islands . expolygons , union_ex ( polys ));
} // for each support_layer_id
});
// Now modulate the support layer height in parallel.
2021-02-03 15:12:53 +01:00
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( n_raft_layers , support_layers . size ()),
2021-02-09 19:23:58 +01:00
[ & support_layers , & layer_caches ]
2017-03-22 15:35:50 +01:00
( const tbb :: blocked_range < size_t >& range ) {
for ( size_t support_layer_id = range . begin (); support_layer_id < range . end (); ++ support_layer_id ) {
2021-02-03 15:12:53 +01:00
SupportLayer & support_layer = * support_layers [ support_layer_id ];
2017-03-22 15:35:50 +01:00
LayerCache & layer_cache = layer_caches [ support_layer_id ];
2021-03-10 12:14:45 +01:00
// For all extrusion types at this print_z, ordered by decreasing layer height:
for ( LayerCacheItem & layer_cache_item : layer_cache . nonempty ) {
// Trim the extrusion height from the bottom by the overlapping layers.
2017-03-22 15:35:50 +01:00
modulate_extrusion_by_overlapping_layers ( layer_cache_item . layer_extruded -> extrusions , * layer_cache_item . layer_extruded -> layer , layer_cache_item . overlapping );
support_layer . support_fills . append ( std :: move ( layer_cache_item . layer_extruded -> extrusions ));
2017-01-20 15:21:05 +01:00
}
2016-10-13 16:00:22 +02:00
}
2017-03-22 15:35:50 +01:00
});
2021-04-08 15:29:40 +02:00
#ifndef NDEBUG
struct Test {
static bool verify_nonempty ( const ExtrusionEntityCollection * collection ) {
for ( const ExtrusionEntity * ee : collection -> entities ) {
if ( const ExtrusionPath * path = dynamic_cast < const ExtrusionPath *> ( ee ))
assert ( ! path -> empty ());
else if ( const ExtrusionMultiPath * multipath = dynamic_cast < const ExtrusionMultiPath *> ( ee ))
assert ( ! multipath -> empty ());
else if ( const ExtrusionEntityCollection * eecol = dynamic_cast < const ExtrusionEntityCollection *> ( ee )) {
assert ( ! eecol -> empty ());
return verify_nonempty ( eecol );
} else
assert ( false );
}
return true ;
}
};
for ( const SupportLayer * support_layer : support_layers )
assert ( Test :: verify_nonempty ( & support_layer -> support_fills ));
#endif // NDEBUG
2016-10-13 16:00:22 +02:00
}
/*
2016-10-20 13:04:23 +02:00
void PrintObjectSupportMaterial::clip_by_pillars(
2016-10-13 16:00:22 +02:00
const PrintObject &object,
LayersPtr &bottom_contacts,
LayersPtr &top_contacts,
LayersPtr &intermediate_contacts);
{
// this prevents supplying an empty point set to BoundingBox constructor
if (top_contacts.empty())
return;
coord_t pillar_size = scale_(PILLAR_SIZE);
coord_t pillar_spacing = scale_(PILLAR_SPACING);
// A regular grid of pillars, filling the 2D bounding box.
Polygons grid;
{
// Rectangle with a side of 2.5x2.5mm.
Polygon pillar;
pillar.points.push_back(Point(0, 0));
pillar.points.push_back(Point(pillar_size, 0));
pillar.points.push_back(Point(pillar_size, pillar_size));
pillar.points.push_back(Point(0, pillar_size));
// 2D bounding box of the projection of all contact polygons.
BoundingBox bbox;
for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
bbox.merge(get_extents((*it)->polygons));
2018-08-17 15:53:43 +02:00
grid.reserve(size_t(ceil(bb.size()(0) / pillar_spacing)) * size_t(ceil(bb.size()(1) / pillar_spacing)));
for (coord_t x = bb.min(0); x <= bb.max(0) - pillar_size; x += pillar_spacing) {
for (coord_t y = bb.min(1); y <= bb.max(1) - pillar_size; y += pillar_spacing) {
2016-10-13 16:00:22 +02:00
grid.push_back(pillar);
for (size_t i = 0; i < pillar.points.size(); ++ i)
grid.back().points[i].translate(Point(x, y));
}
}
}
// add pillars to every layer
for my $i (0..n_support_z) {
$shape->[$i] = [ @$grid ];
}
// build capitals
for my $i (0..n_support_z) {
my $z = $support_z->[$i];
my $capitals = intersection(
$grid,
$contact->{$z} // [],
);
// work on one pillar at time (if any) to prevent the capitals from being merged
// but store the contact area supported by the capital because we need to make
// sure nothing is left
my $contact_supported_by_capitals = [];
foreach my $capital (@$capitals) {
// enlarge capital tops
$capital = offset([$capital], +($pillar_spacing - $pillar_size)/2);
push @$contact_supported_by_capitals, @$capital;
for (my $j = $i-1; $j >= 0; $j--) {
my $jz = $support_z->[$j];
$capital = offset($capital, -$self->interface_flow->scaled_width/2);
last if !@$capitals;
push @{ $shape->[$j] }, @$capital;
}
}
// Capitals will not generally cover the whole contact area because there will be
// remainders. For now we handle this situation by projecting such unsupported
// areas to the ground, just like we would do with a normal support.
my $contact_not_supported_by_capitals = diff(
$contact->{$z} // [],
$contact_supported_by_capitals,
);
if (@$contact_not_supported_by_capitals) {
for (my $j = $i-1; $j >= 0; $j--) {
push @{ $shape->[$j] }, @$contact_not_supported_by_capitals;
}
}
}
}
sub clip_with_shape {
my ($self, $support, $shape) = @_;
foreach my $i (keys %$support) {
// don't clip bottom layer with shape so that we
2021-02-15 18:07:44 +01:00
// can generate a continuous base flange
2016-10-13 16:00:22 +02:00
// also don't clip raft layers
next if $i == 0;
next if $i < $self->object_config->raft_layers;
$support->{$i} = intersection(
$support->{$i},
$shape->[$i],
);
}
}
*/
} // namespace Slic3r