2014-08-03 19:28:40 +02:00
#include "Print.hpp"
#include "BoundingBox.hpp"
2014-12-24 10:20:55 +01:00
#include "ClipperUtils.hpp"
2014-11-09 12:25:59 +01:00
#include "Geometry.hpp"
2014-08-03 19:28:40 +02:00
2016-11-29 19:27:23 +01:00
#include <boost/log/trivial.hpp>
2016-11-16 22:16:20 +01:00
#include <Shiny/Shiny.h>
2016-12-12 17:53:38 +01:00
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#undef NDEBUG
#define DEBUG
#define _DEBUG
#include "SVG.hpp"
#undef assert
#include <cassert>
#endif
2014-08-03 19:28:40 +02:00
namespace Slic3r {
PrintObject :: PrintObject ( Print * print , ModelObject * model_object , const BoundingBoxf3 & modobj_bbox )
2015-07-29 00:29:25 +03:00
: typed_slices ( false ),
_print ( print ),
_model_object ( model_object )
2014-08-03 19:28:40 +02:00
{
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
{
// Translate meshes so that our toolpath generation algorithms work with smaller
// XY coordinates; this translation is an optimization and not strictly required.
// A cloned mesh will be aligned to 0 before slicing in _slice_region() since we
// don't assume it's already aligned and we don't alter the original position in model.
// We store the XY translation so that we can place copies correctly in the output G-code
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
this -> _copies_shift = Point (
scale_ ( modobj_bbox . min . x ), scale_ ( modobj_bbox . min . y ));
// Scale the object size and store it
Pointf3 size = modobj_bbox . size ();
this -> size = Point3 ( scale_ ( size . x ), scale_ ( size . y ), scale_ ( size . z ));
}
2014-11-12 23:28:42 +01:00
this -> reload_model_instances ();
this -> layer_height_ranges = model_object -> layer_height_ranges ;
2014-08-03 19:28:40 +02:00
}
2014-11-13 00:34:56 +01:00
bool
PrintObject :: add_copy ( const Pointf & point )
{
Points points = this -> _copies ;
points . push_back ( Point :: new_scale ( point . x , point . y ));
return this -> set_copies ( points );
}
bool
PrintObject :: delete_last_copy ()
{
Points points = this -> _copies ;
points . pop_back ();
return this -> set_copies ( points );
}
bool
PrintObject :: delete_all_copies ()
{
Points points ;
return this -> set_copies ( points );
}
2014-11-12 23:28:42 +01:00
bool
2014-11-09 12:25:59 +01:00
PrintObject :: set_copies ( const Points & points )
{
this -> _copies = points ;
// order copies with a nearest neighbor search and translate them by _copies_shift
this -> _shifted_copies . clear ();
this -> _shifted_copies . reserve ( points . size ());
// order copies with a nearest-neighbor search
std :: vector < Points :: size_type > ordered_copies ;
Slic3r :: Geometry :: chained_path ( points , ordered_copies );
for ( std :: vector < Points :: size_type >:: const_iterator it = ordered_copies . begin (); it != ordered_copies . end (); ++ it ) {
Point copy = points [ * it ];
copy . translate ( this -> _copies_shift );
this -> _shifted_copies . push_back ( copy );
}
2014-11-12 23:28:42 +01:00
bool invalidated = false ;
if ( this -> _print -> invalidate_step ( psSkirt )) invalidated = true ;
if ( this -> _print -> invalidate_step ( psBrim )) invalidated = true ;
return invalidated ;
}
bool
PrintObject :: reload_model_instances ()
{
Points copies ;
for ( ModelInstancePtrs :: const_iterator i = this -> _model_object -> instances . begin (); i != this -> _model_object -> instances . end (); ++ i ) {
copies . push_back ( Point :: new_scale (( * i ) -> offset . x , ( * i ) -> offset . y ));
}
return this -> set_copies ( copies );
2014-11-09 12:25:59 +01:00
}
2014-08-03 19:28:40 +02:00
void
PrintObject :: add_region_volume ( int region_id , int volume_id )
{
region_volumes [ region_id ]. push_back ( volume_id );
}
2014-11-30 22:01:46 +01:00
/* This is the *total* layer count (including support layers)
this value is not supposed to be compared with Layer::id
since they have different semantics */
2014-08-03 19:28:40 +02:00
size_t
2014-11-30 22:01:46 +01:00
PrintObject :: total_layer_count () const
{
return this -> layer_count () + this -> support_layer_count ();
}
size_t
PrintObject :: layer_count () const
2014-08-03 19:28:40 +02:00
{
return this -> layers . size ();
}
void
PrintObject :: clear_layers ()
{
for ( int i = this -> layers . size () - 1 ; i >= 0 ; -- i )
this -> delete_layer ( i );
}
Layer *
PrintObject :: add_layer ( int id , coordf_t height , coordf_t print_z , coordf_t slice_z )
{
Layer * layer = new Layer ( id , this , height , print_z , slice_z );
layers . push_back ( layer );
return layer ;
}
void
PrintObject :: delete_layer ( int idx )
{
LayerPtrs :: iterator i = this -> layers . begin () + idx ;
delete * i ;
this -> layers . erase ( i );
}
size_t
2014-11-30 22:01:46 +01:00
PrintObject :: support_layer_count () const
2014-08-03 19:28:40 +02:00
{
return this -> support_layers . size ();
}
void
PrintObject :: clear_support_layers ()
{
for ( int i = this -> support_layers . size () - 1 ; i >= 0 ; -- i )
this -> delete_support_layer ( i );
}
SupportLayer *
PrintObject :: get_support_layer ( int idx )
{
return this -> support_layers . at ( idx );
}
SupportLayer *
2015-01-30 18:45:30 +01:00
PrintObject :: add_support_layer ( int id , coordf_t height , coordf_t print_z )
2014-08-03 19:28:40 +02:00
{
2015-01-30 18:45:30 +01:00
SupportLayer * layer = new SupportLayer ( id , this , height , print_z , - 1 );
2014-08-03 19:28:40 +02:00
support_layers . push_back ( layer );
return layer ;
}
void
PrintObject :: delete_support_layer ( int idx )
{
SupportLayerPtrs :: iterator i = this -> support_layers . begin () + idx ;
delete * i ;
this -> support_layers . erase ( i );
}
bool
PrintObject :: invalidate_state_by_config_options ( const std :: vector < t_config_option_key > & opt_keys )
{
std :: set < PrintObjectStep > steps ;
// this method only accepts PrintObjectConfig and PrintRegionConfig option keys
for ( std :: vector < t_config_option_key >:: const_iterator opt_key = opt_keys . begin (); opt_key != opt_keys . end (); ++ opt_key ) {
if ( * opt_key == "perimeters"
|| * opt_key == "extra_perimeters"
|| * opt_key == "gap_fill_speed"
|| * opt_key == "overhangs"
2014-11-30 20:38:42 +01:00
|| * opt_key == "first_layer_extrusion_width"
2014-08-03 19:28:40 +02:00
|| * opt_key == "perimeter_extrusion_width"
2015-02-01 12:08:25 +01:00
|| * opt_key == "infill_overlap"
2014-08-03 19:28:40 +02:00
|| * opt_key == "thin_walls"
|| * opt_key == "external_perimeters_first" ) {
steps . insert ( posPerimeters );
2015-04-29 19:22:44 +02:00
} else if ( * opt_key == "layer_height"
2014-08-03 19:28:40 +02:00
|| * opt_key == "first_layer_height"
|| * opt_key == "xy_size_compensation"
|| * opt_key == "raft_layers" ) {
steps . insert ( posSlice );
} else if ( * opt_key == "support_material"
|| * opt_key == "support_material_angle"
|| * opt_key == "support_material_extruder"
|| * opt_key == "support_material_extrusion_width"
|| * opt_key == "support_material_interface_layers"
|| * opt_key == "support_material_interface_extruder"
|| * opt_key == "support_material_interface_spacing"
|| * opt_key == "support_material_interface_speed"
2016-10-04 13:54:10 +02:00
|| * opt_key == "support_material_buildplate_only"
2014-08-03 19:28:40 +02:00
|| * opt_key == "support_material_pattern"
|| * opt_key == "support_material_spacing"
|| * opt_key == "support_material_threshold"
2016-10-04 14:38:13 +02:00
|| * opt_key == "support_material_with_sheath"
2014-11-30 20:38:42 +01:00
|| * opt_key == "dont_support_bridges"
|| * opt_key == "first_layer_extrusion_width" ) {
2014-08-03 19:28:40 +02:00
steps . insert ( posSupportMaterial );
} else if ( * opt_key == "interface_shells"
|| * opt_key == "infill_only_where_needed"
2014-12-08 21:23:42 +01:00
|| * opt_key == "infill_every_layers"
|| * opt_key == "solid_infill_every_layers"
2014-08-03 19:28:40 +02:00
|| * opt_key == "bottom_solid_layers"
|| * opt_key == "top_solid_layers"
2014-11-07 00:53:15 +01:00
|| * opt_key == "solid_infill_below_area"
2014-08-03 19:28:40 +02:00
|| * opt_key == "infill_extruder"
2014-12-17 00:34:00 +01:00
|| * opt_key == "solid_infill_extruder"
2016-10-16 22:11:19 +02:00
|| * opt_key == "infill_extrusion_width"
|| * opt_key == "ensure_vertical_shell_thickness" ) {
2014-08-03 19:28:40 +02:00
steps . insert ( posPrepareInfill );
2014-11-27 00:38:05 +01:00
} else if ( * opt_key == "external_fill_pattern"
2016-11-02 10:47:00 +01:00
|| * opt_key == "external_fill_link_max_length"
2014-11-27 00:38:05 +01:00
|| * opt_key == "fill_angle"
2014-08-03 19:28:40 +02:00
|| * opt_key == "fill_pattern"
2016-11-02 10:47:00 +01:00
|| * opt_key == "fill_link_max_length"
2014-11-30 20:38:42 +01:00
|| * opt_key == "top_infill_extrusion_width"
|| * opt_key == "first_layer_extrusion_width" ) {
2014-08-03 19:28:40 +02:00
steps . insert ( posInfill );
} else if ( * opt_key == "fill_density"
|| * opt_key == "solid_infill_extrusion_width" ) {
steps . insert ( posPerimeters );
steps . insert ( posPrepareInfill );
} else if ( * opt_key == "external_perimeter_extrusion_width"
|| * opt_key == "perimeter_extruder" ) {
steps . insert ( posPerimeters );
steps . insert ( posSupportMaterial );
} else if ( * opt_key == "bridge_flow_ratio" ) {
steps . insert ( posPerimeters );
steps . insert ( posInfill );
} else if ( * opt_key == "seam_position"
2016-09-13 13:30:00 +02:00
|| * opt_key == "seam_preferred_direction"
|| * opt_key == "seam_preferred_direction_jitter"
2014-08-03 19:28:40 +02:00
|| * opt_key == "support_material_speed"
|| * opt_key == "bridge_speed"
|| * opt_key == "external_perimeter_speed"
|| * opt_key == "infill_speed"
|| * opt_key == "perimeter_speed"
|| * opt_key == "small_perimeter_speed"
|| * opt_key == "solid_infill_speed"
|| * opt_key == "top_solid_infill_speed" ) {
// these options only affect G-code export, so nothing to invalidate
} else {
// for legacy, if we can't handle this option let's invalidate all steps
return this -> invalidate_all_steps ();
}
}
bool invalidated = false ;
for ( std :: set < PrintObjectStep >:: const_iterator step = steps . begin (); step != steps . end (); ++ step ) {
if ( this -> invalidate_step ( * step )) invalidated = true ;
}
return invalidated ;
}
bool
PrintObject :: invalidate_step ( PrintObjectStep step )
{
bool invalidated = this -> state . invalidate ( step );
// propagate to dependent steps
if ( step == posPerimeters ) {
this -> invalidate_step ( posPrepareInfill );
this -> _print -> invalidate_step ( psSkirt );
this -> _print -> invalidate_step ( psBrim );
} else if ( step == posPrepareInfill ) {
this -> invalidate_step ( posInfill );
} else if ( step == posInfill ) {
this -> _print -> invalidate_step ( psSkirt );
this -> _print -> invalidate_step ( psBrim );
} else if ( step == posSlice ) {
this -> invalidate_step ( posPerimeters );
this -> invalidate_step ( posSupportMaterial );
2014-08-08 01:37:39 +02:00
} else if ( step == posSupportMaterial ) {
this -> _print -> invalidate_step ( psSkirt );
this -> _print -> invalidate_step ( psBrim );
2014-08-03 19:28:40 +02:00
}
return invalidated ;
}
bool
PrintObject :: invalidate_all_steps ()
{
// make a copy because when invalidating steps the iterators are not working anymore
std :: set < PrintObjectStep > steps = this -> state . started ;
bool invalidated = false ;
for ( std :: set < PrintObjectStep >:: const_iterator step = steps . begin (); step != steps . end (); ++ step ) {
if ( this -> invalidate_step ( * step )) invalidated = true ;
}
return invalidated ;
}
2015-03-06 09:56:58 +01:00
bool
PrintObject :: has_support_material () const
{
return this -> config . support_material
|| this -> config . raft_layers > 0
|| this -> config . support_material_enforce_layers > 0 ;
}
2016-11-10 19:23:01 +01:00
// This function analyzes slices of a region (SurfaceCollection slices).
2016-11-17 23:22:59 +01:00
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
2016-11-10 19:23:01 +01:00
// Initially all slices are of type S_TYPE_INTERNAL.
// Slices are compared against the top / bottom slices and regions and classified to the following groups:
// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill.
// S_TYPE_BOTTOMBRIDGE - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft.
// S_TYPE_BOTTOM - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer.
// S_TYPE_INTERNAL - Part of a region, which is supported by the same region type.
// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
void PrintObject :: detect_surfaces_type ()
{
2016-11-29 19:27:23 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Detecting solid surfaces..." ;
2016-11-10 19:23:01 +01:00
for ( int idx_region = 0 ; idx_region < this -> _print -> regions . size (); ++ idx_region ) {
2016-11-17 23:22:59 +01:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2016-11-10 19:23:01 +01:00
for ( int idx_layer = 0 ; idx_layer < int ( this -> layer_count ()); ++ idx_layer ) {
LayerRegion * layerm = this -> layers [ idx_layer ] -> get_region ( idx_region );
layerm -> export_region_fill_surfaces_to_svg_debug ( "1_detect_surfaces_type-initial" );
}
2016-11-17 23:22:59 +01:00
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-10 19:23:01 +01:00
for ( int idx_layer = 0 ; idx_layer < int ( this -> layer_count ()); ++ idx_layer ) {
Layer * layer = this -> layers [ idx_layer ];
LayerRegion * layerm = layer -> get_region ( idx_region );
// comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested
Layer * upper_layer = idx_layer + 1 < this -> layer_count () ? this -> get_layer ( idx_layer + 1 ) : NULL ;
Layer * lower_layer = idx_layer > 0 ? this -> get_layer ( idx_layer - 1 ) : NULL ;
// collapse very narrow parts (using the safety offset in the diff is not enough)
float offset = layerm -> flow ( frExternalPerimeter ). scaled_width () / 10.f ;
Polygons layerm_slices_surfaces = to_polygons ( layerm -> slices . surfaces );
// find top surfaces (difference between current surfaces
// of current layer and upper one)
Surfaces top ;
if ( upper_layer ) {
// Config value $self->config->interface_shells is true, if a support is separated from the object
// by a soluble material (for example a PVA plastic).
Polygons upper_slices = this -> config . interface_shells . value ?
to_polygons ( upper_layer -> get_region ( idx_region ) -> slices . surfaces ) :
to_polygons ( upper_layer -> slices );
surfaces_append ( top ,
offset2_ex ( diff ( layerm_slices_surfaces , upper_slices , true ), - offset , offset ),
stTop );
} else {
// if no upper layer, all surfaces of this one are solid
// we clone surfaces because we're going to clear the slices collection
top = layerm -> slices . surfaces ;
for ( Surfaces :: iterator it = top . begin (); it != top . end (); ++ it )
it -> surface_type = stTop ;
}
// find bottom surfaces (difference between current surfaces
// of current layer and lower one)
Surfaces bottom ;
if ( lower_layer ) {
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print.
SurfaceType surface_type_bottom =
( this -> config . support_material . value && this -> config . support_material_contact_distance . value == 0 ) ?
stBottom : stBottomBridge ;
// Any surface lying on the void is a true bottom bridge (an overhang)
surfaces_append (
bottom ,
offset2_ex (
diff ( layerm_slices_surfaces , to_polygons ( lower_layer -> slices ), true ),
- offset , offset ),
surface_type_bottom );
// if user requested internal shells, we need to identify surfaces
// lying on other slices not belonging to this region
//FIXME Vojtech: config.internal_shells or config.interface_shells? Is it some legacy code?
// Why shall multiple regions over soluble support be treated specially?
if ( this -> config . interface_shells . value ) {
// non-bridging bottom surfaces: any part of this layer lying
// on something else, excluding those lying on our own region
surfaces_append (
bottom ,
offset2_ex (
diff (
intersection ( layerm_slices_surfaces , to_polygons ( lower_layer -> slices )), // supported
to_polygons ( lower_layer -> get_region ( idx_region ) -> slices . surfaces ),
true ),
- offset , offset ),
stBottom );
}
} else {
// if no lower layer, all surfaces of this one are solid
// we clone surfaces because we're going to clear the slices collection
bottom = layerm -> slices . surfaces ;
// if we have raft layers, consider bottom layer as a bridge
// just like any other bottom surface lying on the void
SurfaceType surface_type_bottom =
( this -> config . raft_layers . value > 0 && this -> config . support_material_contact_distance . value > 0 ) ?
stBottomBridge : stBottom ;
for ( Surfaces :: iterator it = bottom . begin (); it != bottom . end (); ++ it )
it -> surface_type = surface_type_bottom ;
}
// now, if the object contained a thin membrane, we could have overlapping bottom
// and top surfaces; let's do an intersection to discover them and consider them
// as bottom surfaces (to allow for bridge detection)
if ( ! top . empty () && ! bottom . empty ()) {
// Polygons overlapping = intersection(to_polygons(top), to_polygons(bottom));
// Slic3r::debugf " layer %d contains %d membrane(s)\n", $layerm->layer->id, scalar(@$overlapping)
// if $Slic3r::debug;
Polygons top_polygons = to_polygons ( STDMOVE ( top ));
top . clear ();
surfaces_append ( top ,
#if 0
offset2_ex(diff(top_polygons, to_polygons(bottom), true), -offset, offset),
#else
diff_ex ( top_polygons , to_polygons ( bottom ), false ),
#endif
stTop );
}
// save surfaces to layer
layerm -> slices . surfaces . clear ();
// find internal surfaces (difference between top/bottom surfaces and others)
{
Polygons topbottom = to_polygons ( top );
polygons_append ( topbottom , to_polygons ( bottom ));
surfaces_append ( layerm -> slices . surfaces ,
#if 0
offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset),
#else
diff_ex ( layerm_slices_surfaces , topbottom , false ),
#endif
stInternal );
}
surfaces_append ( layerm -> slices . surfaces , STDMOVE ( top ));
surfaces_append ( layerm -> slices . surfaces , STDMOVE ( bottom ));
// Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
// $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm -> export_region_slices_to_svg_debug ( "detect_surfaces_type-final" );
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each layer of a region
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
for ( int idx_layer = 0 ; idx_layer < int ( this -> layer_count ()); ++ idx_layer ) {
LayerRegion * layerm = this -> layers [ idx_layer ] -> get_region ( idx_region );
layerm -> slices_to_fill_surfaces_clipped ();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm -> export_region_fill_surfaces_to_svg_debug ( "1_detect_surfaces_type-final" );
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each layer of a region
} // for each $self->print->region_count
}
2015-10-26 23:23:03 +01:00
void
PrintObject :: process_external_surfaces ()
{
2016-11-29 19:27:23 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Processing external surfaces..." ;
2015-10-26 23:23:03 +01:00
FOREACH_REGION ( this -> _print , region ) {
size_t region_id = region - this -> _print -> regions . begin ();
FOREACH_LAYER ( this , layer_it ) {
const Layer * lower_layer = ( layer_it == this -> layers . begin ())
? NULL
: * ( layer_it - 1 );
( * layer_it ) -> get_region ( region_id ) -> process_external_surfaces ( lower_layer );
}
}
}
2016-11-16 18:04:47 +01:00
struct DiscoverVerticalShellsCacheEntry
{
DiscoverVerticalShellsCacheEntry () : valid ( false ) {}
// Collected polygons, offsetted
Polygons slices ;
Polygons fill_surfaces ;
// Is this cache entry valid?
bool valid ;
};
2016-09-26 13:56:24 +02:00
void
PrintObject :: discover_vertical_shells ()
{
2016-11-16 22:16:20 +01:00
PROFILE_FUNC ();
2016-11-29 19:27:23 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Discovering vertical shells..." ;
2016-11-16 18:04:47 +01:00
const SurfaceType surfaces_bottom [ 2 ] = { stBottom , stBottomBridge };
2016-09-26 13:56:24 +02:00
for ( size_t idx_region = 0 ; idx_region < this -> _print -> regions . size (); ++ idx_region ) {
2016-11-16 22:16:20 +01:00
PROFILE_BLOCK ( discover_vertical_shells_region );
2016-11-16 18:04:47 +01:00
const PrintRegion & region = * this -> _print -> get_region ( idx_region );
if ( ! region . config . ensure_vertical_shell_thickness . value )
// This region will be handled by discover_horizontal_shells().
2016-10-16 22:11:19 +02:00
continue ;
2016-11-16 18:04:47 +01:00
int n_extra_top_layers = std :: max ( 0 , region . config . top_solid_layers . value - 1 );
int n_extra_bottom_layers = std :: max ( 0 , region . config . bottom_solid_layers . value - 1 );
if ( n_extra_top_layers + n_extra_bottom_layers == 0 )
// Zero or 1 layer, there is no additional vertical wall thickness enforced.
continue ;
// Cyclic buffers of pre-calculated offsetted top/bottom surfaces.
std :: vector < DiscoverVerticalShellsCacheEntry > cache_top_regions ( n_extra_top_layers , DiscoverVerticalShellsCacheEntry ());
std :: vector < DiscoverVerticalShellsCacheEntry > cache_bottom_regions ( n_extra_bottom_layers , DiscoverVerticalShellsCacheEntry ());
2016-11-16 22:16:20 +01:00
for ( size_t idx_layer = 0 ; idx_layer < this -> layers . size (); ++ idx_layer )
{
PROFILE_BLOCK ( discover_vertical_shells_region_layer );
2016-10-22 22:25:00 +02:00
Layer * layer = this -> layers [ idx_layer ];
LayerRegion * layerm = layer -> get_region ( idx_region );
Flow solid_infill_flow = layerm -> flow ( frSolidInfill );
coord_t infill_line_spacing = solid_infill_flow . scaled_spacing ();
2016-09-26 13:56:24 +02:00
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
Polygons shell ;
2016-11-17 23:22:59 +01:00
Polygons holes ;
2016-10-22 22:25:00 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2016-09-26 13:56:24 +02:00
ExPolygons shell_ex ;
2016-10-22 22:25:00 +02:00
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-11 11:13:36 +01:00
float min_perimeter_infill_spacing = float ( infill_line_spacing ) * 1.05f ;
2016-09-26 13:56:24 +02:00
if ( 1 )
{
2016-11-16 22:16:20 +01:00
PROFILE_BLOCK ( discover_vertical_shells_region_layer_collect );
2016-09-26 13:56:24 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG svg_cummulative ( debug_out_path ( "discover_vertical_shells-perimeters-before-union-run%d.svg" , idx ), this -> bounding_box ());
2016-11-16 18:04:47 +01:00
for ( int n = ( int ) idx_layer - n_extra_bottom_layers ; n <= ( int ) idx_layer + n_extra_top_layers ; ++ n ) {
2016-09-26 13:56:24 +02:00
if ( n < 0 || n >= ( int ) this -> layers . size ())
continue ;
ExPolygons & expolys = this -> layers [ n ] -> perimeter_expolygons ;
for ( size_t i = 0 ; i < expolys . size (); ++ i ) {
2016-10-21 10:18:01 +02:00
SVG svg ( debug_out_path ( "discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg" , idx , n , i ), get_extents ( expolys [ i ]));
2016-09-26 13:56:24 +02:00
svg . draw ( expolys [ i ]);
svg . draw_outline ( expolys [ i ]. contour , "black" , scale_ ( 0.05 ));
svg . draw_outline ( expolys [ i ]. holes , "blue" , scale_ ( 0.05 ));
svg . Close ();
svg_cummulative . draw ( expolys [ i ]);
svg_cummulative . draw_outline ( expolys [ i ]. contour , "black" , scale_ ( 0.05 ));
svg_cummulative . draw_outline ( expolys [ i ]. holes , "blue" , scale_ ( 0.05 ));
}
}
++ idx ;
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-16 18:04:47 +01:00
// Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
if ( n_extra_top_layers > 0 )
cache_top_regions [ idx_layer % n_extra_top_layers ]. valid = false ;
if ( n_extra_bottom_layers > 0 && idx_layer > 0 )
cache_bottom_regions [( idx_layer - 1 ) % n_extra_bottom_layers ]. valid = false ;
2016-11-17 23:22:59 +01:00
bool hole_first = true ;
2016-11-16 18:04:47 +01:00
for ( int n = ( int ) idx_layer - n_extra_bottom_layers ; n <= ( int ) idx_layer + n_extra_top_layers ; ++ n )
2016-11-10 19:23:01 +01:00
if ( n >= 0 && n < ( int ) this -> layers . size ()) {
Layer & neighbor_layer = * this -> layers [ n ];
LayerRegion & neighbor_region = * neighbor_layer . get_region ( int ( idx_region ));
2016-11-17 23:22:59 +01:00
Polygons newholes ;
for ( size_t idx_region = 0 ; idx_region < this -> _print -> regions . size (); ++ idx_region )
polygons_append ( newholes , to_polygons ( neighbor_layer . get_region ( idx_region ) -> fill_expolygons ));
if ( hole_first ) {
hole_first = false ;
polygons_append ( holes , STDMOVE ( newholes ));
}
else if ( ! holes . empty ()) {
holes = intersection ( holes , newholes );
}
size_t n_shell_old = shell . size ();
2016-11-10 19:23:01 +01:00
if ( n > int ( idx_layer )) {
// Collect top surfaces.
2016-11-16 18:04:47 +01:00
DiscoverVerticalShellsCacheEntry & cache = cache_top_regions [ n % n_extra_top_layers ];
if ( ! cache . valid ) {
cache . valid = true ;
2016-11-17 23:22:59 +01:00
// neighbor_region.slices contain the source top regions,
// so one would think that they encompass the top fill_surfaces. But the fill_surfaces could have been
// expanded before, therefore they may protrude out of neighbor_region.slices's top surfaces.
//FIXME one should probably use the cummulative top surfaces over all regions here.
2016-11-16 18:04:47 +01:00
cache . slices = offset ( to_expolygons ( neighbor_region . slices . filter_by_type ( stTop )), min_perimeter_infill_spacing );
cache . fill_surfaces = offset ( to_expolygons ( neighbor_region . fill_surfaces . filter_by_type ( stTop )), min_perimeter_infill_spacing );
}
polygons_append ( shell , cache . slices );
polygons_append ( shell , cache . fill_surfaces );
2016-11-10 19:23:01 +01:00
}
else if ( n < int ( idx_layer )) {
// Collect bottom and bottom bridge surfaces.
2016-11-16 18:04:47 +01:00
DiscoverVerticalShellsCacheEntry & cache = cache_bottom_regions [ n % n_extra_bottom_layers ];
if ( ! cache . valid ) {
cache . valid = true ;
2016-11-17 23:22:59 +01:00
//FIXME one should probably use the cummulative top surfaces over all regions here.
2016-11-16 18:04:47 +01:00
cache . slices = offset ( to_expolygons ( neighbor_region . slices . filter_by_types ( surfaces_bottom , 2 )), min_perimeter_infill_spacing );
cache . fill_surfaces = offset ( to_expolygons ( neighbor_region . fill_surfaces . filter_by_types ( surfaces_bottom , 2 )), min_perimeter_infill_spacing );
}
polygons_append ( shell , cache . slices );
polygons_append ( shell , cache . fill_surfaces );
2016-11-10 19:23:01 +01:00
}
2016-11-16 22:16:20 +01:00
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
2016-11-17 23:22:59 +01:00
if ( n_shell_old < shell . size ())
shell = union_ ( shell , false );
2016-11-10 19:23:01 +01:00
}
2016-09-26 13:56:24 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG svg ( debug_out_path ( "discover_vertical_shells-perimeters-before-union-%d.svg" , idx ++ ), get_extents ( shell ));
2016-09-26 13:56:24 +02:00
svg . draw ( shell );
svg . draw_outline ( shell , "black" , scale_ ( 0.05 ));
svg . Close ();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-16 22:16:20 +01:00
#if 0
{
PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_);
// shell = union_(shell, true);
shell = union_(shell, false);
}
#endif
2016-09-26 13:56:24 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
shell_ex = union_ex ( shell , true );
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
2016-11-17 23:22:59 +01:00
//if (shell.empty())
// continue;
2016-10-22 22:25:00 +02:00
2016-09-26 13:56:24 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG svg ( debug_out_path ( "discover_vertical_shells-perimeters-after-union-%d.svg" , idx ++ ), get_extents ( shell ));
2016-09-26 13:56:24 +02:00
svg . draw ( shell_ex );
svg . draw_outline ( shell_ex , "black" , "blue" , scale_ ( 0.05 ));
svg . Close ();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG svg ( debug_out_path ( "discover_vertical_shells-internal-wshell-%d.svg" , idx ++ ), get_extents ( shell ));
2016-09-26 13:56:24 +02:00
svg . draw ( layerm -> fill_surfaces . filter_by_type ( stInternal ), "yellow" , 0.5 );
svg . draw_outline ( layerm -> fill_surfaces . filter_by_type ( stInternal ), "black" , "blue" , scale_ ( 0.05 ));
svg . draw ( shell_ex , "blue" , 0.5 );
svg . draw_outline ( shell_ex , "black" , "blue" , scale_ ( 0.05 ));
svg . Close ();
}
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG svg ( debug_out_path ( "discover_vertical_shells-internalvoid-wshell-%d.svg" , idx ++ ), get_extents ( shell ));
2016-09-26 13:56:24 +02:00
svg . draw ( layerm -> fill_surfaces . filter_by_type ( stInternalVoid ), "yellow" , 0.5 );
svg . draw_outline ( layerm -> fill_surfaces . filter_by_type ( stInternalVoid ), "black" , "blue" , scale_ ( 0.05 ));
svg . draw ( shell_ex , "blue" , 0.5 );
svg . draw_outline ( shell_ex , "black" , "blue" , scale_ ( 0.05 ));
svg . Close ();
}
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG svg ( debug_out_path ( "discover_vertical_shells-internalvoid-wshell-%d.svg" , idx ++ ), get_extents ( shell ));
2016-09-26 13:56:24 +02:00
svg . draw ( layerm -> fill_surfaces . filter_by_type ( stInternalVoid ), "yellow" , 0.5 );
svg . draw_outline ( layerm -> fill_surfaces . filter_by_type ( stInternalVoid ), "black" , "blue" , scale_ ( 0.05 ));
svg . draw ( shell_ex , "blue" , 0.5 );
svg . draw_outline ( shell_ex , "black" , "blue" , scale_ ( 0.05 ));
svg . Close ();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-04 00:55:43 +01:00
// Trim the shells region by the internal & internal void surfaces.
2016-11-10 19:23:01 +01:00
const SurfaceType surfaceTypesInternal [] = { stInternal , stInternalVoid , stInternalSolid };
2016-11-04 00:55:43 +01:00
const Polygons polygonsInternal = to_polygons ( layerm -> fill_surfaces . filter_by_types ( surfaceTypesInternal , 2 ));
shell = intersection ( shell , polygonsInternal , true );
2016-11-17 23:22:59 +01:00
polygons_append ( shell , diff ( polygonsInternal , holes ));
2016-11-04 00:55:43 +01:00
if ( shell . empty ())
continue ;
2016-11-10 19:23:01 +01:00
// Append the internal solids, so they will be merged with the new ones.
polygons_append ( shell , to_polygons ( layerm -> fill_surfaces . filter_by_type ( stInternalSolid )));
2016-11-04 00:55:43 +01:00
// These regions will be filled by a rectilinear full infill. Currently this type of infill
// only fills regions, which fit at least a single line. To avoid gaps in the sparse infill,
// make sure that this region does not contain parts narrower than the infill spacing width.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
Polygons shell_before = shell ;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-10 19:23:01 +01:00
#if 1
2016-11-04 00:55:43 +01:00
// Intentionally inflate a bit more than how much the region has been shrunk,
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
2016-11-28 17:33:17 +01:00
shell = offset2 ( shell , - 0.5f * min_perimeter_infill_spacing , 0.8f * min_perimeter_infill_spacing , ClipperLib :: jtSquare );
2016-11-04 00:55:43 +01:00
if ( shell . empty ())
continue ;
2016-11-10 19:23:01 +01:00
#else
// Ensure each region is at least 3x infill line width wide, so it could be filled in.
// float margin = float(infill_line_spacing) * 3.f;
float margin = float ( infill_line_spacing ) * 1.5f ;
// we use a higher miterLimit here to handle areas with acute angles
// in those cases, the default miterLimit would cut the corner and we'd
// get a triangle in $too_narrow; if we grow it below then the shell
// would have a different shape from the external surface and we'd still
// have the same angle, so the next shell would be grown even more and so on.
2016-11-28 17:33:17 +01:00
Polygons too_narrow = diff ( shell , offset2 ( shell , - margin , margin , ClipperLib :: jtMiter , 5. ), true );
2016-11-10 19:23:01 +01:00
if ( ! too_narrow . empty ()) {
// grow the collapsing parts and add the extra area to the neighbor layer
// as well as to our original surfaces so that we support this
// additional area in the next shell too
// make sure our grown surfaces don't exceed the fill area
polygons_append ( shell , intersection ( offset ( too_narrow , margin ), polygonsInternal ));
}
#endif
2016-11-04 00:55:43 +01:00
ExPolygons new_internal_solid = intersection_ex ( polygonsInternal , shell , false );
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0 ;
SVG svg ( debug_out_path ( "discover_vertical_shells-regularized-%d.svg" , idx ++ ), get_extents ( shell_before ));
// Source shell.
svg . draw ( union_ex ( shell_before , true ));
// Shell trimmed to the internal surfaces.
svg . draw_outline ( union_ex ( shell , true ), "black" , "blue" , scale_ ( 0.05 ));
// Regularized infill region.
svg . draw_outline ( new_internal_solid , "red" , "magenta" , scale_ ( 0.05 ));
svg . Close ();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Trim the internal & internalvoid by the shell.
2016-09-26 13:56:24 +02:00
Slic3r :: ExPolygons new_internal = diff_ex (
to_polygons ( layerm -> fill_surfaces . filter_by_type ( stInternal )),
shell ,
false
);
Slic3r :: ExPolygons new_internal_void = diff_ex (
to_polygons ( layerm -> fill_surfaces . filter_by_type ( stInternalVoid )),
shell ,
false
);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0 ;
2016-10-21 10:18:01 +02:00
SVG :: export_expolygons ( debug_out_path ( "discover_vertical_shells-new_internal-%d.svg" , idx ), get_extents ( shell ), new_internal , "black" , "blue" , scale_ ( 0.05 ));
SVG :: export_expolygons ( debug_out_path ( "discover_vertical_shells-new_internal_void-%d.svg" , idx ), get_extents ( shell ), new_internal_void , "black" , "blue" , scale_ ( 0.05 ));
SVG :: export_expolygons ( debug_out_path ( "discover_vertical_shells-new_internal_solid-%d.svg" , idx ), get_extents ( shell ), new_internal_solid , "black" , "blue" , scale_ ( 0.05 ));
2016-09-26 13:56:24 +02:00
++ idx ;
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Assign resulting internal surfaces to layer.
2016-11-10 19:23:01 +01:00
const SurfaceType surfaceTypesKeep [] = { stTop , stBottom , stBottomBridge };
2016-09-26 13:56:24 +02:00
layerm -> fill_surfaces . keep_types ( surfaceTypesKeep , sizeof ( surfaceTypesKeep ) / sizeof ( SurfaceType ));
2016-12-12 17:53:38 +01:00
layerm -> fill_surfaces . append ( new_internal , stInternal );
layerm -> fill_surfaces . append ( new_internal_void , stInternalVoid );
layerm -> fill_surfaces . append ( new_internal_solid , stInternalSolid );
2016-09-26 13:56:24 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm -> export_region_slices_to_svg_debug ( "4_discover_vertical_shells" );
layerm -> export_region_fill_surfaces_to_svg_debug ( "4_discover_vertical_shells" );
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each layer
} // for each region
2016-11-16 22:16:20 +01:00
// Write the profiler measurements to file
PROFILE_UPDATE ();
PROFILE_OUTPUT ( debug_out_path ( "discover_vertical_shells-profile.txt" ). c_str ());
2016-09-26 13:56:24 +02:00
}
2015-10-26 23:23:03 +01:00
/* This method applies bridge flow to the first internal solid layer above
sparse infill */
2014-12-24 10:20:55 +01:00
void
PrintObject :: bridge_over_infill ()
{
2016-11-29 19:27:23 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Bridge over infill..." ;
2014-12-24 10:20:55 +01:00
FOREACH_REGION ( this -> _print , region ) {
size_t region_id = region - this -> _print -> regions . begin ();
2015-10-26 23:23:03 +01:00
// skip bridging in case there are no voids
if (( * region ) -> config . fill_density . value == 100 ) continue ;
2014-12-24 10:20:55 +01:00
2015-06-13 19:48:46 +02:00
// get bridge flow
Flow bridge_flow = ( * region ) -> flow (
frSolidInfill ,
- 1 , // layer height, not relevant for bridge flow
true , // bridge
false , // first layer
- 1 , // custom width, not relevant for bridge flow
* this
);
2014-12-24 10:20:55 +01:00
FOREACH_LAYER ( this , layer_it ) {
2015-10-26 23:23:03 +01:00
// skip first layer
2014-12-24 10:20:55 +01:00
if ( layer_it == this -> layers . begin ()) continue ;
Layer * layer = * layer_it ;
LayerRegion * layerm = layer -> get_region ( region_id );
2015-06-13 19:48:46 +02:00
// extract the stInternalSolid surfaces that might be transformed into bridges
Polygons internal_solid ;
2014-12-24 10:20:55 +01:00
layerm -> fill_surfaces . filter_by_type ( stInternalSolid , & internal_solid );
2015-06-13 19:48:46 +02:00
// check whether the lower area is deep enough for absorbing the extra flow
// (for obvious physical reasons but also for preventing the bridge extrudates
// from overflowing in 3D preview)
2014-12-24 10:20:55 +01:00
ExPolygons to_bridge ;
2015-06-13 19:48:46 +02:00
{
Polygons to_bridge_pp = internal_solid ;
// iterate through lower layers spanned by bridge_flow
double bottom_z = layer -> print_z - bridge_flow . height ;
for ( int i = ( layer_it - this -> layers . begin ()) - 1 ; i >= 0 ; -- i ) {
2015-10-26 23:23:03 +01:00
const Layer * lower_layer = this -> layers [ i ];
2015-06-13 19:48:46 +02:00
// stop iterating if layer is lower than bottom_z
if ( lower_layer -> print_z < bottom_z ) break ;
// iterate through regions and collect internal surfaces
Polygons lower_internal ;
FOREACH_LAYERREGION ( lower_layer , lower_layerm_it )
( * lower_layerm_it ) -> fill_surfaces . filter_by_type ( stInternal , & lower_internal );
// intersect such lower internal surfaces with the candidate solid surfaces
2015-10-26 23:23:03 +01:00
to_bridge_pp = intersection ( to_bridge_pp , lower_internal );
2015-06-13 19:48:46 +02:00
}
// there's no point in bridging too thin/short regions
2016-09-26 13:56:24 +02:00
//FIXME Vojtech: The offset2 function is not a geometric offset,
// therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour.
// The gaps will be filled by a separate region, which makes the infill less stable and it takes longer.
2015-06-13 19:48:46 +02:00
{
double min_width = bridge_flow . scaled_width () * 3 ;
2015-10-26 23:23:03 +01:00
to_bridge_pp = offset2 ( to_bridge_pp , - min_width , + min_width );
2015-06-13 19:48:46 +02:00
}
if ( to_bridge_pp . empty ()) continue ;
// convert into ExPolygons
2015-10-26 23:23:03 +01:00
to_bridge = union_ex ( to_bridge_pp );
2015-06-13 19:48:46 +02:00
}
2014-12-24 10:20:55 +01:00
#ifdef SLIC3R_DEBUG
2016-11-02 10:47:00 +01:00
printf ( "Bridging " PRINTF_ZU " internal areas at layer " PRINTF_ZU " \n " , to_bridge . size (), layer -> id ());
2014-12-24 10:20:55 +01:00
#endif
2015-06-13 19:48:46 +02:00
// compute the remaning internal solid surfaces as difference
2015-10-26 23:23:03 +01:00
ExPolygons not_to_bridge = diff_ex ( internal_solid , to_bridge , true );
2016-09-26 13:56:24 +02:00
to_bridge = intersection_ex ( to_polygons ( to_bridge ), internal_solid , true );
2015-06-13 19:48:46 +02:00
2014-12-24 10:20:55 +01:00
// build the new collection of fill_surfaces
{
2016-09-26 13:56:24 +02:00
layerm -> fill_surfaces . remove_type ( stInternalSolid );
2014-12-24 10:20:55 +01:00
for ( ExPolygons :: const_iterator ex = to_bridge . begin (); ex != to_bridge . end (); ++ ex )
2016-09-26 13:56:24 +02:00
layerm -> fill_surfaces . surfaces . push_back ( Surface ( stInternalBridge , * ex ));
2014-12-24 10:20:55 +01:00
for ( ExPolygons :: const_iterator ex = not_to_bridge . begin (); ex != not_to_bridge . end (); ++ ex )
2016-09-26 13:56:24 +02:00
layerm -> fill_surfaces . surfaces . push_back ( Surface ( stInternalSolid , * ex ));
2014-12-24 10:20:55 +01:00
}
/*
# exclude infill from the layers below if needed
# see discussion at https://github.com/alexrj/Slic3r/issues/240
# Update: do not exclude any infill. Sparse infill is able to absorb the excess material.
if (0) {
my $excess = $layerm->extruders->{infill}->bridge_flow->width - $layerm->height;
for (my $i = $layer_id-1; $excess >= $self->get_layer($i)->height; $i--) {
Slic3r::debugf " skipping infill below those areas at layer %d\n", $i;
foreach my $lower_layerm (@{$self->get_layer($i)->regions}) {
my @new_surfaces = ();
# subtract the area from all types of surfaces
foreach my $group (@{$lower_layerm->fill_surfaces->group}) {
push @new_surfaces, map $group->[0]->clone(expolygon => $_),
@{diff_ex(
[ map $_->p, @$group ],
[ map @$_, @$to_bridge ],
)};
push @new_surfaces, map Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNALVOID,
), @{intersection_ex(
[ map $_->p, @$group ],
[ map @$_, @$to_bridge ],
)};
}
$lower_layerm->fill_surfaces->clear;
$lower_layerm->fill_surfaces->append($_) for @new_surfaces;
}
$excess -= $self->get_layer($i)->height;
}
}
*/
2016-09-26 13:56:24 +02:00
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm -> export_region_slices_to_svg_debug ( "7_bridge_over_infill" );
layerm -> export_region_fill_surfaces_to_svg_debug ( "7_bridge_over_infill" );
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2014-12-24 10:20:55 +01:00
}
}
}
2016-12-12 17:53:38 +01:00
SlicingParameters PrintObject :: slicing_parameters () const
{
return SlicingParameters :: create_from_config (
this -> print () -> config , this -> config ,
unscale ( this -> size . z ), this -> print () -> object_extruders ());
}
void PrintObject :: update_layer_height_profile ()
{
if ( this -> layer_height_profile . empty ()) {
if ( 0 )
// if (this->layer_height_profile.empty())
this -> layer_height_profile = layer_height_profile_adaptive ( this -> slicing_parameters (), this -> layer_height_ranges ,
this -> model_object () -> volumes );
else
this -> layer_height_profile = layer_height_profile_from_ranges ( this -> slicing_parameters (), this -> layer_height_ranges );
}
}
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
// 3) Slices the object meshes
// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
// 5) Applies size compensation (offsets the slices in XY plane)
// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
// Resulting expolygons of layer regions are marked as Internal.
//
// this should be idempotent
void PrintObject :: _slice ()
{
SlicingParameters slicing_params = this -> slicing_parameters ();
// 1) Initialize layers and their slice heights.
std :: vector < float > slice_zs ;
{
this -> clear_layers ();
// Object layers (pairs of bottom/top Z coordinate), without the raft.
this -> update_layer_height_profile ();
std :: vector < coordf_t > object_layers = generate_object_layers ( slicing_params , this -> layer_height_profile );
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
int id = int ( slicing_params . raft_layers ());
slice_zs . reserve ( object_layers . size ());
Layer * prev = nullptr ;
for ( size_t i_layer = 0 ; i_layer < object_layers . size (); i_layer += 2 ) {
coordf_t lo = object_layers [ i_layer ];
coordf_t hi = object_layers [ i_layer + 1 ];
coordf_t slice_z = 0.5 * ( lo + hi );
Layer * layer = this -> add_layer ( id ++ , hi - lo , hi + slicing_params . object_print_z_min , slice_z );
slice_zs . push_back ( float ( slice_z ));
if ( prev != nullptr ) {
prev -> upper_layer = layer ;
layer -> lower_layer = prev ;
}
// Make sure all layers contain layer region objects for all regions.
for ( size_t region_id = 0 ; region_id < this -> print () -> regions . size (); ++ region_id )
layer -> add_region ( this -> print () -> regions [ region_id ]);
prev = layer ;
}
}
if ( this -> print () -> regions . size () == 1 ) {
// Optimized for a single region. Slice the single non-modifier mesh.
std :: vector < ExPolygons > expolygons_by_layer = this -> _slice_region ( 0 , slice_zs , false );
for ( size_t layer_id = 0 ; layer_id < expolygons_by_layer . size (); ++ layer_id )
this -> layers [ layer_id ] -> regions . front () -> slices . append ( std :: move ( expolygons_by_layer [ layer_id ]), stInternal );
} else {
// Slice all non-modifier volumes.
for ( size_t region_id = 0 ; region_id < this -> print () -> regions . size (); ++ region_id ) {
std :: vector < ExPolygons > expolygons_by_layer = this -> _slice_region ( region_id , slice_zs , false );
for ( size_t layer_id = 0 ; layer_id < expolygons_by_layer . size (); ++ layer_id )
this -> layers [ layer_id ] -> regions [ region_id ] -> slices . append ( std :: move ( expolygons_by_layer [ layer_id ]), stInternal );
}
// Slice all modifier volumes.
for ( size_t region_id = 0 ; region_id < this -> print () -> regions . size (); ++ region_id ) {
std :: vector < ExPolygons > expolygons_by_layer = this -> _slice_region ( region_id , slice_zs , true );
// loop through the other regions and 'steal' the slices belonging to this one
for ( size_t other_region_id = 0 ; other_region_id < this -> print () -> regions . size (); ++ other_region_id ) {
if ( region_id == other_region_id )
continue ;
for ( size_t layer_id = 0 ; layer_id < expolygons_by_layer . size (); ++ layer_id ) {
Layer * layer = layers [ layer_id ];
LayerRegion * layerm = layer -> regions [ region_id ];
LayerRegion * other_layerm = layer -> regions [ other_region_id ];
if ( layerm == nullptr || other_layerm == nullptr )
continue ;
Polygons other_slices = to_polygons ( other_layerm -> slices );
ExPolygons my_parts = intersection_ex ( other_slices , to_polygons ( expolygons_by_layer [ layer_id ]));
if ( my_parts . empty ())
continue ;
// Remove such parts from original region.
other_layerm -> slices . set ( diff_ex ( other_slices , my_parts ), stInternal );
// Append new parts to our region.
layerm -> slices . append ( std :: move ( my_parts ), stInternal );
}
}
}
}
// remove last layer(s) if empty
while ( ! this -> layers . empty ()) {
const Layer * layer = this -> layers . back ();
for ( size_t region_id = 0 ; region_id < this -> print () -> regions . size (); ++ region_id )
if ( layer -> regions [ region_id ] != nullptr && ! layer -> regions [ region_id ] -> slices . empty ())
// Non empty layer.
goto end ;
this -> delete_layer ( int ( this -> layers . size ()) - 1 );
}
end :
;
for ( size_t layer_id = 0 ; layer_id < layers . size (); ++ layer_id ) {
Layer * layer = this -> layers [ layer_id ];
// apply size compensation
if ( this -> config . xy_size_compensation . value != 0. ) {
float delta = float ( scale_ ( this -> config . xy_size_compensation . value ));
if ( layer -> regions . size () == 1 ) {
// single region
LayerRegion * layerm = layer -> regions . front ();
layerm -> slices . set ( offset_ex ( to_polygons ( std :: move ( layerm -> slices . surfaces )), delta ), stInternal );
} else {
if ( delta < 0 ) {
// multiple regions, shrinking
// we apply the offset to the combined shape, then intersect it
// with the original slices for each region
Polygons region_slices ;
for ( size_t region_id = 0 ; region_id < layer -> regions . size (); ++ region_id )
polygons_append ( region_slices , layer -> regions [ region_id ] -> slices . surfaces );
Polygons slices = offset ( union_ ( region_slices ), delta );
for ( size_t region_id = 0 ; region_id < layer -> regions . size (); ++ region_id ) {
LayerRegion * layerm = layer -> regions [ region_id ];
layerm -> slices . set ( std :: move ( intersection_ex ( slices , to_polygons ( std :: move ( layerm -> slices . surfaces )))), stInternal );
}
} else {
// multiple regions, growing
// this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
// so we give priority to the first one and so on
Polygons processed ;
for ( size_t region_id = 0 ;; ++ region_id ) {
LayerRegion * layerm = layer -> regions [ region_id ];
ExPolygons slices = offset_ex ( to_polygons ( layerm -> slices . surfaces ), delta );
if ( region_id > 0 )
// Trim by the slices of already processed regions.
slices = diff_ex ( to_polygons ( std :: move ( slices )), processed );
if ( region_id + 1 == layer -> regions . size ()) {
layerm -> slices . set ( std :: move ( slices ), stInternal );
break ;
}
polygons_append ( processed , slices );
layerm -> slices . set ( std :: move ( slices ), stInternal );
}
}
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
layer -> make_slices ();
}
}
std :: vector < ExPolygons > PrintObject :: _slice_region ( size_t region_id , const std :: vector < float > & z , bool modifier )
{
std :: vector < ExPolygons > layers ;
assert ( region_id < this -> region_volumes . size ());
std :: vector < int > & volumes = this -> region_volumes [ region_id ];
if ( ! volumes . empty ()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh ;
for ( std :: vector < int >:: const_iterator it_volume = volumes . begin (); it_volume != volumes . end (); ++ it_volume ) {
ModelVolume * volume = this -> model_object () -> volumes [ * it_volume ];
if ( volume -> modifier == modifier )
mesh . merge ( volume -> mesh );
}
if ( mesh . stl . stats . number_of_facets > 0 ) {
// transform mesh
// we ignore the per-instance transformations currently and only
// consider the first one
this -> model_object () -> instances . front () -> transform_mesh ( & mesh , true );
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
mesh . translate ( - unscale ( this -> _copies_shift . x ), - unscale ( this -> _copies_shift . y ), - this -> model_object () -> bounding_box (). min . z );
// perform actual slicing
TriangleMeshSlicer mslicer ( & mesh );
mslicer . slice ( z , & layers );
}
}
return layers ;
}
2016-11-26 12:28:39 +01:00
void
PrintObject :: _make_perimeters ()
{
if ( this -> state . is_done ( posPerimeters )) return ;
this -> state . set_started ( posPerimeters );
// merge slices if they were split into types
if ( this -> typed_slices ) {
FOREACH_LAYER ( this , layer_it )
( * layer_it ) -> merge_slices ();
this -> typed_slices = false ;
this -> state . invalidate ( posPrepareInfill );
}
// compare each layer to the one below, and mark those slices needing
// one additional inner perimeter, like the top of domed objects-
// this algorithm makes sure that at least one perimeter is overlapping
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
2016-12-12 17:53:38 +01:00
// hollow objects
2016-11-26 12:28:39 +01:00
FOREACH_REGION ( this -> _print , region_it ) {
size_t region_id = region_it - this -> _print -> regions . begin ();
const PrintRegion & region = ** region_it ;
if ( ! region . config . extra_perimeters
|| region . config . perimeters == 0
2016-11-26 12:47:11 +01:00
|| region . config . fill_density == 0
|| this -> layer_count () < 2 ) continue ;
2016-11-26 12:28:39 +01:00
2016-12-12 17:53:38 +01:00
for ( int i = 0 ; i < int ( this -> layer_count ()) - 1 ; ++ i ) {
2016-11-26 12:28:39 +01:00
LayerRegion & layerm = * this -> get_layer ( i ) -> get_region ( region_id );
const LayerRegion & upper_layerm = * this -> get_layer ( i + 1 ) -> get_region ( region_id );
const Polygons upper_layerm_polygons = upper_layerm . slices ;
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
double total_loop_length = 0 ;
for ( Polygons :: const_iterator it = upper_layerm_polygons . begin (); it != upper_layerm_polygons . end (); ++ it )
total_loop_length += it -> length ();
const coord_t perimeter_spacing = layerm . flow ( frPerimeter ). scaled_spacing ();
const Flow ext_perimeter_flow = layerm . flow ( frExternalPerimeter );
const coord_t ext_perimeter_width = ext_perimeter_flow . scaled_width ();
const coord_t ext_perimeter_spacing = ext_perimeter_flow . scaled_spacing ();
for ( Surfaces :: iterator slice = layerm . slices . surfaces . begin ();
slice != layerm . slices . surfaces . end (); ++ slice ) {
while ( true ) {
// compute the total thickness of perimeters
const coord_t perimeters_thickness = ext_perimeter_width / 2 + ext_perimeter_spacing / 2
+ ( region . config . perimeters - 1 + region . config . extra_perimeters ) * perimeter_spacing ;
// define a critical area where we don't want the upper slice to fall into
// (it should either lay over our perimeters or outside this area)
const coord_t critical_area_depth = perimeter_spacing * 1.5 ;
const Polygons critical_area = diff (
offset ( slice -> expolygon , - perimeters_thickness ),
offset ( slice -> expolygon , - ( perimeters_thickness + critical_area_depth ))
);
// check whether a portion of the upper slices falls inside the critical area
const Polylines intersection = intersection_pl (
2016-12-08 19:02:16 +01:00
to_polylines ( upper_layerm_polygons ),
2016-11-26 12:28:39 +01:00
critical_area
);
// only add an additional loop if at least 30% of the slice loop would benefit from it
{
double total_intersection_length = 0 ;
for ( Polylines :: const_iterator it = intersection . begin (); it != intersection . end (); ++ it )
total_intersection_length += it -> length ();
if ( total_intersection_length <= total_loop_length * 0.3 ) break ;
}
/*
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"extra.svg",
no_arrows => 1,
expolygons => union_ex($critical_area),
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
);
}
*/
slice -> extra_perimeters ++ ;
}
#ifdef DEBUG
if ( slice -> extra_perimeters > 0 )
2016-11-26 12:47:11 +01:00
printf ( " adding %d more perimeter(s) at layer %zu \n " , slice -> extra_perimeters , i );
2016-11-26 12:28:39 +01:00
#endif
}
}
}
2016-11-26 16:07:36 +01:00
parallelize < Layer *> (
std :: queue < Layer *> ( std :: deque < Layer *> ( this -> layers . begin (), this -> layers . end ())), // cast LayerPtrs to std::queue<Layer*>
boost :: bind ( & Slic3r :: Layer :: make_perimeters , _1 ),
this -> _print -> config . threads . value
);
2016-11-26 12:28:39 +01:00
/*
simplify slices (both layer and region slices),
we only need the max resolution for perimeters
### This makes this method not-idempotent, so we keep it disabled for now.
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
*/
this -> state . set_done ( posPerimeters );
}
void
PrintObject :: _infill ()
{
if ( this -> state . is_done ( posInfill )) return ;
this -> state . set_started ( posInfill );
2016-11-26 16:07:36 +01:00
parallelize < Layer *> (
std :: queue < Layer *> ( std :: deque < Layer *> ( this -> layers . begin (), this -> layers . end ())), // cast LayerPtrs to std::queue<Layer*>
boost :: bind ( & Slic3r :: Layer :: make_fills , _1 ),
this -> _print -> config . threads . value
);
2016-11-26 12:28:39 +01:00
/* we could free memory now, but this would make this step not idempotent
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
*/
this -> state . set_done ( posInfill );
}
2016-12-12 17:53:38 +01:00
} // namespace Slic3r