2020-09-14 14:27:55 +00:00
# include "Exception.hpp"
2014-08-03 17:28:40 +00:00
# include "Print.hpp"
# include "BoundingBox.hpp"
2014-12-24 09:20:55 +00:00
# include "ClipperUtils.hpp"
2019-11-01 18:59:09 +00:00
# include "ElephantFootCompensation.hpp"
2014-11-09 11:25:59 +00:00
# include "Geometry.hpp"
2019-05-04 00:07:07 +00:00
# include "I18N.hpp"
2020-05-26 09:09:38 +00:00
# include "Layer.hpp"
2016-12-20 11:19:13 +00:00
# include "SupportMaterial.hpp"
2017-08-02 12:24:32 +00:00
# include "Surface.hpp"
2018-05-30 13:18:45 +00:00
# include "Slicing.hpp"
2020-09-22 06:53:45 +00:00
# include "Tesselate.hpp"
2018-12-18 10:31:41 +00:00
# include "Utils.hpp"
2020-08-26 16:15:59 +00:00
# include "Fill/FillAdaptive.hpp"
2020-09-08 09:49:26 +00:00
# include "Format/STL.hpp"
2014-08-03 17:28:40 +00:00
2017-03-02 15:52:24 +00:00
# include <utility>
2016-11-29 18:27:23 +00:00
# include <boost/log/trivial.hpp>
2017-06-01 14:43:21 +00:00
# include <float.h>
2016-11-29 18:27:23 +00:00
2017-03-07 12:03:14 +00:00
# include <tbb/parallel_for.h>
# include <tbb/atomic.h>
2016-11-16 21:16:20 +00:00
# include <Shiny/Shiny.h>
2019-06-25 11:06:04 +00:00
//! macro used to mark string used at localization,
2019-05-04 00:07:07 +00:00
//! return same string
# define L(s) Slic3r::I18N::translate(s)
2017-01-03 09:51:19 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
# define SLIC3R_DEBUG
# endif
2016-12-12 16:53:38 +00: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 17:28:40 +00:00
namespace Slic3r {
2020-02-07 13:10:18 +00:00
// Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid.
PrintObject : : PrintObject ( Print * print , ModelObject * model_object , const Transform3d & trafo , PrintInstances & & instances ) :
2018-11-16 17:28:50 +00:00
PrintObjectBaseWithState ( print , model_object ) ,
2020-02-07 13:10:18 +00:00
m_trafo ( trafo )
2014-08-03 17:28:40 +00:00
{
2020-02-07 13:10:18 +00:00
// Compute centering offet to be applied to our meshes so that we work with smaller coordinates
// requiring less bits to represent Clipper coordinates.
// Snug bounding box of a rotated and scaled object by the 1st instantion, without the instance translation applied.
// All the instances share the transformation matrix with the exception of translation in XY and rotation by Z,
// therefore a bounding box from 1st instance of a ModelObject is good enough for calculating the object center,
// snug height and an approximate bounding box in XY.
BoundingBoxf3 bbox = model_object - > raw_bounding_box ( ) ;
Vec3d bbox_center = bbox . center ( ) ;
// We may need to rotate the bbox / bbox_center from the original instance to the current instance.
double z_diff = Geometry : : rotation_diff_z ( model_object - > instances . front ( ) - > get_rotation ( ) , instances . front ( ) . model_instance - > get_rotation ( ) ) ;
if ( std : : abs ( z_diff ) > EPSILON ) {
auto z_rot = Eigen : : AngleAxisd ( z_diff , Vec3d : : UnitZ ( ) ) ;
bbox = bbox . transformed ( Transform3d ( z_rot ) ) ;
bbox_center = ( z_rot * bbox_center ) . eval ( ) ;
}
// Center of the transformed mesh (without translation).
m_center_offset = Point : : new_scale ( bbox_center . x ( ) , bbox_center . y ( ) ) ;
// Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
m_size = ( bbox . size ( ) * ( 1. / SCALING_FACTOR ) ) . cast < coord_t > ( ) ;
this - > set_instances ( std : : move ( instances ) ) ;
2014-08-03 17:28:40 +00:00
}
2020-01-23 08:53:06 +00:00
PrintBase : : ApplyStatus PrintObject : : set_instances ( PrintInstances & & instances )
2014-11-09 11:25:59 +00:00
{
2020-01-24 16:10:59 +00:00
for ( PrintInstance & i : instances )
2020-02-07 13:10:18 +00:00
// Add the center offset, which will be subtracted from the mesh when slicing.
i . shift + = m_center_offset ;
2018-10-23 13:27:31 +00:00
// Invalidate and set copies.
2018-12-22 09:02:42 +00:00
PrintBase : : ApplyStatus status = PrintBase : : APPLY_STATUS_UNCHANGED ;
2020-01-23 08:53:06 +00:00
bool equal_length = instances . size ( ) = = m_instances . size ( ) ;
bool equal = equal_length & & std : : equal ( instances . begin ( ) , instances . end ( ) , m_instances . begin ( ) ,
[ ] ( const PrintInstance & lhs , const PrintInstance & rhs ) { return lhs . model_instance = = rhs . model_instance & & lhs . shift = = rhs . shift ; } ) ;
if ( ! equal ) {
2018-12-22 09:02:42 +00:00
status = PrintBase : : APPLY_STATUS_CHANGED ;
if ( m_print - > invalidate_steps ( { psSkirt , psBrim , psGCodeExport } ) | |
2020-01-23 08:53:06 +00:00
( ! equal_length & & m_print - > invalidate_step ( psWipeTower ) ) )
2018-12-22 09:02:42 +00:00
status = PrintBase : : APPLY_STATUS_INVALIDATED ;
2020-01-24 16:10:59 +00:00
m_instances = std : : move ( instances ) ;
2020-01-23 08:53:06 +00:00
for ( PrintInstance & i : m_instances )
i . print_object = this ;
2018-10-23 13:27:31 +00:00
}
2018-12-22 09:02:42 +00:00
return status ;
2014-11-12 22:28:42 +00:00
}
2020-12-11 11:21:07 +00:00
// Called by make_perimeters()
2018-03-23 10:41:20 +00:00
// 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.
void PrintObject : : slice ( )
{
2018-11-16 17:28:50 +00:00
if ( ! this - > set_started ( posSlice ) )
2018-03-23 10:41:20 +00:00
return ;
2019-05-04 00:07:07 +00:00
m_print - > set_status ( 10 , L ( " Processing triangulated mesh " ) ) ;
2019-01-23 13:00:03 +00:00
std : : vector < coordf_t > layer_height_profile ;
2019-03-04 14:28:04 +00:00
this - > update_layer_height_profile ( * this - > model_object ( ) , m_slicing_params , layer_height_profile ) ;
2019-01-21 09:06:51 +00:00
m_print - > throw_if_canceled ( ) ;
2019-01-23 13:00:03 +00:00
this - > _slice ( layer_height_profile ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
// Fix the model.
//FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
std : : string warning = this - > _fix_slicing_errors ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
if ( ! warning . empty ( ) )
BOOST_LOG_TRIVIAL ( info ) < < warning ;
// Simplify slices if required.
2018-09-11 12:04:47 +00:00
if ( m_print - > config ( ) . resolution )
2020-01-03 13:05:56 +00:00
this - > simplify_slices ( scale_ ( this - > print ( ) - > config ( ) . resolution ) ) ;
2020-12-11 11:21:07 +00:00
// Update bounding boxes, back up raw slices of complex models.
2019-10-01 15:17:08 +00:00
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
[ this ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
m_print - > throw_if_canceled ( ) ;
Layer & layer = * m_layers [ layer_idx ] ;
2020-01-03 13:05:56 +00:00
layer . lslices_bboxes . clear ( ) ;
layer . lslices_bboxes . reserve ( layer . lslices . size ( ) ) ;
for ( const ExPolygon & expoly : layer . lslices )
layer . lslices_bboxes . emplace_back ( get_extents ( expoly ) ) ;
2020-12-11 11:21:07 +00:00
layer . backup_untyped_slices ( ) ;
2019-10-01 15:17:08 +00:00
}
} ) ;
2018-09-11 12:04:47 +00:00
if ( m_layers . empty ( ) )
2020-09-14 16:01:25 +00:00
throw Slic3r : : SlicingError ( " No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry. \n " ) ;
2018-09-11 12:04:47 +00:00
this - > set_done ( posSlice ) ;
2018-03-23 10:41:20 +00:00
}
// 1) Merges typed region slices into stInternal type.
// 2) Increases an "extra perimeters" counter at region slices where needed.
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
void PrintObject : : make_perimeters ( )
{
// prerequisites
this - > slice ( ) ;
2018-11-16 17:28:50 +00:00
if ( ! this - > set_started ( posPerimeters ) )
2018-03-23 10:41:20 +00:00
return ;
2019-05-04 00:07:07 +00:00
m_print - > set_status ( 20 , L ( " Generating perimeters " ) ) ;
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Generating perimeters... " < < log_memory_info ( ) ;
2018-03-23 10:41:20 +00:00
2020-12-11 11:21:07 +00:00
// Revert the typed slices into untyped slices.
2020-02-07 13:10:18 +00:00
if ( m_typed_slices ) {
2018-11-02 19:41:49 +00:00
for ( Layer * layer : m_layers ) {
2020-12-11 11:21:07 +00:00
layer - > restore_untyped_slices ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-28 15:05:31 +00:00
}
2020-02-07 13:10:18 +00:00
m_typed_slices = false ;
2018-03-23 10:41:20 +00:00
}
// 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
// hollow objects
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
const PrintRegion & region = * m_print - > regions ( ) [ region_id ] ;
2018-09-12 09:59:02 +00:00
if ( ! region . config ( ) . extra_perimeters | | region . config ( ) . perimeters = = 0 | | region . config ( ) . fill_density = = 0 | | this - > layer_count ( ) < 2 )
2018-03-23 10:41:20 +00:00
continue ;
2019-08-08 13:17:17 +00:00
2018-03-23 10:41:20 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Generating extra perimeters for region " < < region_id < < " in parallel - start " ;
tbb : : parallel_for (
2018-09-11 12:04:47 +00:00
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) - 1 ) ,
2018-03-23 10:41:20 +00:00
[ this , & region , region_id ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
LayerRegion & layerm = * m_layers [ layer_idx ] - > m_regions [ region_id ] ;
const LayerRegion & upper_layerm = * m_layers [ layer_idx + 1 ] - > m_regions [ region_id ] ;
2018-03-23 10:41:20 +00:00
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} ];
const double total_loop_length = total_length ( upper_layerm_polygons ) ;
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 ( Surface & slice : layerm . slices . surfaces ) {
for ( ; ; ) {
// compute the total thickness of perimeters
const coord_t perimeters_thickness = ext_perimeter_width / 2 + ext_perimeter_spacing / 2
2018-09-11 12:04:47 +00:00
+ ( region . config ( ) . perimeters - 1 + slice . extra_perimeters ) * perimeter_spacing ;
2018-03-23 10:41:20 +00:00
// 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 = coord_t ( perimeter_spacing * 1.5 ) ;
const Polygons critical_area = diff (
offset ( slice . expolygon , float ( - perimeters_thickness ) ) ,
offset ( slice . expolygon , float ( - perimeters_thickness - critical_area_depth ) )
) ;
// check whether a portion of the upper slices falls inside the critical area
const Polylines intersection = intersection_pl ( to_polylines ( upper_layerm_polygons ) , critical_area ) ;
// only add an additional loop if at least 30% of the slice loop would benefit from it
if ( total_length ( intersection ) < = 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 )
printf ( " adding %d more perimeter(s) at layer %zu \n " , slice . extra_perimeters , layer_idx ) ;
# endif
}
}
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Generating extra perimeters for region " < < region_id < < " in parallel - end " ;
}
BOOST_LOG_TRIVIAL ( debug ) < < " Generating perimeters in parallel - start " ;
tbb : : parallel_for (
2018-09-11 12:04:47 +00:00
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
2018-03-23 10:41:20 +00:00
[ this ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
m_layers [ layer_idx ] - > make_perimeters ( ) ;
2018-03-23 10:41:20 +00:00
}
}
) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Generating perimeters in parallel - end " ;
2018-09-11 12:04:47 +00:00
this - > set_done ( posPerimeters ) ;
2018-03-23 10:41:20 +00:00
}
void PrintObject : : prepare_infill ( )
{
2018-11-16 17:28:50 +00:00
if ( ! this - > set_started ( posPrepareInfill ) )
2018-03-23 10:41:20 +00:00
return ;
2019-05-04 00:07:07 +00:00
m_print - > set_status ( 30 , L ( " Preparing infill " ) ) ;
2018-03-23 10:41:20 +00:00
// This will assign a type (top/bottom/internal) to $layerm->slices.
// Then the classifcation of $layerm->slices is transfered onto
// the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
// by the cummulative area of the previous $layerm->fill_surfaces.
this - > detect_surfaces_type ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
// Decide what surfaces are to be filled.
2019-09-09 14:47:15 +00:00
// Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured.
// Also tiny stInternal surfaces are turned to stInternalSolid.
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Preparing fill surfaces... " < < log_memory_info ( ) ;
2018-09-11 12:04:47 +00:00
for ( auto * layer : m_layers )
for ( auto * region : layer - > m_regions ) {
2018-03-23 10:41:20 +00:00
region - > prepare_fill_surfaces ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-28 15:05:31 +00:00
}
2018-03-23 10:41:20 +00:00
// this will detect bridges and reverse bridges
// and rearrange top/bottom/internal surfaces
// It produces enlarged overlapping bridging areas.
//
2019-09-09 14:47:15 +00:00
// 1) stBottomBridge / stBottom infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap.
// 2) stTop is grown by 3mm and clipped by the grown bottom areas. The areas may overlap.
2018-03-23 10:41:20 +00:00
// 3) Clip the internal surfaces by the grown top/bottom surfaces.
// 4) Merge surfaces with the same style. This will mostly get rid of the overlaps.
//FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties.
this - > process_external_surfaces ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
// Add solid fills to ensure the shell vertical thickness.
this - > discover_vertical_shells ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
// Debugging output.
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
for ( const Layer * layer : m_layers ) {
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2018-03-23 10:41:20 +00:00
layerm - > export_region_slices_to_svg_debug ( " 6_discover_vertical_shells-final " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 6_discover_vertical_shells-final " ) ;
} // for each layer
} // for each region
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Detect, which fill surfaces are near external layers.
// They will be split in internal and internal-solid surfaces.
// The purpose is to add a configurable number of solid layers to support the TOP surfaces
// and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces
// to close these surfaces reliably.
//FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters?
this - > discover_horizontal_shells ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
for ( const Layer * layer : m_layers ) {
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2018-03-23 10:41:20 +00:00
layerm - > export_region_slices_to_svg_debug ( " 7_discover_horizontal_shells-final " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 7_discover_horizontal_shells-final " ) ;
} // for each layer
} // for each region
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
// so it acts as an internal support. It maintains all other infill types intact.
// Here the internal surfaces and perimeters have to be supported by the sparse infill.
//FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
// Likely the sparse infill will not be anchored correctly, so it will not work as intended.
// Also one wishes the perimeters to be supported by a full infill.
this - > clip_fill_surfaces ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
for ( const Layer * layer : m_layers ) {
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2018-03-23 10:41:20 +00:00
layerm - > export_region_slices_to_svg_debug ( " 8_clip_surfaces-final " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 8_clip_surfaces-final " ) ;
} // for each layer
} // for each region
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// the following step needs to be done before combination because it may need
// to remove only half of the combined infill
this - > bridge_over_infill ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
// combine fill surfaces to honor the "infill every N layers" option
this - > combine_infill ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
for ( const Layer * layer : m_layers ) {
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2018-03-23 10:41:20 +00:00
layerm - > export_region_slices_to_svg_debug ( " 9_prepare_infill-final " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 9_prepare_infill-final " ) ;
} // for each layer
} // for each region
2018-09-11 12:04:47 +00:00
for ( const Layer * layer : m_layers ) {
2018-03-23 10:41:20 +00:00
layer - > export_region_slices_to_svg_debug ( " 9_prepare_infill-final " ) ;
layer - > export_region_fill_surfaces_to_svg_debug ( " 9_prepare_infill-final " ) ;
} // for each layer
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2018-09-11 12:04:47 +00:00
this - > set_done ( posPrepareInfill ) ;
2018-03-23 10:41:20 +00:00
}
void PrintObject : : infill ( )
{
// prerequisites
this - > prepare_infill ( ) ;
2018-11-16 17:28:50 +00:00
if ( this - > set_started ( posInfill ) ) {
2020-09-10 14:53:08 +00:00
auto [ adaptive_fill_octree , support_fill_octree ] = this - > prepare_adaptive_infill_data ( ) ;
2020-09-02 20:53:10 +00:00
2018-03-23 10:41:20 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Filling layers in parallel - start " ;
tbb : : parallel_for (
2018-09-11 12:04:47 +00:00
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
2020-09-10 20:57:58 +00:00
[ this , & adaptive_fill_octree = adaptive_fill_octree , & support_fill_octree = support_fill_octree ] ( const tbb : : blocked_range < size_t > & range ) {
2018-03-23 10:41:20 +00:00
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2020-09-10 14:53:08 +00:00
m_layers [ layer_idx ] - > make_fills ( adaptive_fill_octree . get ( ) , support_fill_octree . get ( ) ) ;
2018-03-23 10:41:20 +00:00
}
}
) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-03-23 10:41:20 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Filling layers in parallel - end " ;
/* we could free memory now, but this would make this step not idempotent
# ## $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
*/
2018-09-11 12:04:47 +00:00
this - > set_done ( posInfill ) ;
2018-03-23 10:41:20 +00:00
}
}
2020-04-14 09:53:28 +00:00
void PrintObject : : ironing ( )
{
if ( this - > set_started ( posIroning ) ) {
BOOST_LOG_TRIVIAL ( debug ) < < " Ironing in parallel - start " ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 1 , m_layers . size ( ) ) ,
[ this ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
m_print - > throw_if_canceled ( ) ;
m_layers [ layer_idx ] - > make_ironing ( ) ;
}
}
) ;
m_print - > throw_if_canceled ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Ironing in parallel - end " ;
this - > set_done ( posIroning ) ;
}
}
2018-03-23 10:41:20 +00:00
void PrintObject : : generate_support_material ( )
{
2018-11-16 17:28:50 +00:00
if ( this - > set_started ( posSupportMaterial ) ) {
2018-03-23 10:41:20 +00:00
this - > clear_support_layers ( ) ;
2018-09-11 12:04:47 +00:00
if ( ( m_config . support_material | | m_config . raft_layers > 0 ) & & m_layers . size ( ) > 1 ) {
2019-05-04 00:07:07 +00:00
m_print - > set_status ( 85 , L ( " Generating support material " ) ) ;
2018-03-23 10:41:20 +00:00
this - > _generate_support_material ( ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2018-12-14 16:17:51 +00:00
} else {
2018-12-14 18:40:02 +00:00
#if 0
2018-12-14 16:17:51 +00:00
// Printing without supports. Empty layer means some objects or object parts are levitating,
// therefore they cannot be printed without supports.
for ( const Layer * layer : m_layers )
if ( layer - > empty ( ) )
2020-09-14 16:01:25 +00:00
throw Slic3r : : SlicingError ( " Levitating objects cannot be printed without supports. " ) ;
2018-12-14 18:40:02 +00:00
# endif
2018-03-23 10:41:20 +00:00
}
2018-09-11 12:04:47 +00:00
this - > set_done ( posSupportMaterial ) ;
2018-03-23 10:41:20 +00:00
}
}
2020-09-18 08:53:50 +00:00
std : : pair < FillAdaptive : : OctreePtr , FillAdaptive : : OctreePtr > PrintObject : : prepare_adaptive_infill_data ( )
2020-08-26 16:15:59 +00:00
{
2020-09-18 08:53:50 +00:00
using namespace FillAdaptive ;
2020-09-10 14:53:08 +00:00
2020-09-09 13:55:06 +00:00
auto [ adaptive_line_spacing , support_line_spacing ] = adaptive_fill_line_spacing ( * this ) ;
2020-09-24 18:32:52 +00:00
if ( ( adaptive_line_spacing = = 0. & & support_line_spacing = = 0. ) | | this - > layers ( ) . empty ( ) )
2020-09-17 16:39:28 +00:00
return std : : make_pair ( OctreePtr ( ) , OctreePtr ( ) ) ;
2020-08-26 16:15:59 +00:00
2020-09-17 16:39:28 +00:00
indexed_triangle_set mesh = this - > model_object ( ) - > raw_indexed_triangle_set ( ) ;
2020-09-18 08:53:50 +00:00
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes.
Transform3d m = m_trafo ;
2020-09-22 06:53:45 +00:00
m . pretranslate ( Vec3d ( - unscale < float > ( m_center_offset . x ( ) ) , - unscale < float > ( m_center_offset . y ( ) ) , 0 ) ) ;
auto to_octree = transform_to_octree ( ) . toRotationMatrix ( ) ;
its_transform ( mesh , to_octree * m , true ) ;
// Triangulate internal bridging surfaces.
std : : vector < std : : vector < Vec3d > > overhangs ( this - > layers ( ) . size ( ) ) ;
tbb : : parallel_for (
tbb : : blocked_range < int > ( 0 , int ( m_layers . size ( ) ) - 1 ) ,
[ this , & to_octree , & overhangs ] ( const tbb : : blocked_range < int > & range ) {
std : : vector < Vec3d > & out = overhangs [ range . begin ( ) ] ;
for ( int idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
m_print - > throw_if_canceled ( ) ;
const Layer * layer = this - > layers ( ) [ idx_layer ] ;
for ( const LayerRegion * layerm : layer - > regions ( ) )
for ( const Surface & surface : layerm - > fill_surfaces . surfaces )
if ( surface . surface_type = = stInternalBridge )
append ( out , triangulate_expolygon_3d ( surface . expolygon , layer - > bottom_z ( ) ) ) ;
}
for ( Vec3d & p : out )
p = ( to_octree * p ) . eval ( ) ;
} ) ;
// and gather them.
for ( size_t i = 1 ; i < overhangs . size ( ) ; + + i )
append ( overhangs . front ( ) , std : : move ( overhangs [ i ] ) ) ;
2020-09-17 16:39:28 +00:00
return std : : make_pair (
2020-09-22 06:53:45 +00:00
adaptive_line_spacing ? build_octree ( mesh , overhangs . front ( ) , adaptive_line_spacing , false ) : OctreePtr ( ) ,
support_line_spacing ? build_octree ( mesh , overhangs . front ( ) , support_line_spacing , true ) : OctreePtr ( ) ) ;
2020-08-26 16:15:59 +00:00
}
2017-05-30 16:33:17 +00:00
void PrintObject : : clear_layers ( )
2014-08-03 17:28:40 +00:00
{
2018-09-11 12:04:47 +00:00
for ( Layer * l : m_layers )
2017-05-30 16:33:17 +00:00
delete l ;
2018-09-11 12:04:47 +00:00
m_layers . clear ( ) ;
2014-08-03 17:28:40 +00:00
}
2017-05-30 16:33:17 +00:00
Layer * PrintObject : : add_layer ( int id , coordf_t height , coordf_t print_z , coordf_t slice_z )
2014-08-03 17:28:40 +00:00
{
2018-09-11 12:04:47 +00:00
m_layers . emplace_back ( new Layer ( id , this , height , print_z , slice_z ) ) ;
return m_layers . back ( ) ;
2014-08-03 17:28:40 +00:00
}
2017-05-30 16:33:17 +00:00
void PrintObject : : clear_support_layers ( )
2014-08-03 17:28:40 +00:00
{
2018-09-11 12:04:47 +00:00
for ( Layer * l : m_support_layers )
2017-05-30 16:33:17 +00:00
delete l ;
2018-09-11 12:04:47 +00:00
m_support_layers . clear ( ) ;
2014-08-03 17:28:40 +00:00
}
2017-05-30 16:33:17 +00:00
SupportLayer * PrintObject : : add_support_layer ( int id , coordf_t height , coordf_t print_z )
2014-08-03 17:28:40 +00:00
{
2018-09-11 12:04:47 +00:00
m_support_layers . emplace_back ( new SupportLayer ( id , this , height , print_z , - 1 ) ) ;
return m_support_layers . back ( ) ;
}
2019-06-25 11:06:04 +00:00
SupportLayerPtrs : : const_iterator PrintObject : : insert_support_layer ( SupportLayerPtrs : : const_iterator pos , size_t id , coordf_t height , coordf_t print_z , coordf_t slice_z )
2018-09-11 12:04:47 +00:00
{
return m_support_layers . insert ( pos , new SupportLayer ( id , this , height , print_z , slice_z ) ) ;
2014-08-03 17:28:40 +00:00
}
2019-05-22 14:43:14 +00:00
// Called by Print::apply().
2017-05-30 18:09:34 +00:00
// This method only accepts PrintObjectConfig and PrintRegionConfig option keys.
2017-05-30 15:17:26 +00:00
bool PrintObject : : invalidate_state_by_config_options ( const std : : vector < t_config_option_key > & opt_keys )
2014-08-03 17:28:40 +00:00
{
2017-05-30 18:09:34 +00:00
if ( opt_keys . empty ( ) )
return false ;
2017-05-30 15:17:26 +00:00
std : : vector < PrintObjectStep > steps ;
2017-06-08 15:46:28 +00:00
bool invalidated = false ;
2017-05-30 15:17:26 +00:00
for ( const t_config_option_key & opt_key : opt_keys ) {
2017-05-31 15:02:23 +00:00
if ( opt_key = = " perimeters "
2017-05-30 15:17:26 +00:00
| | opt_key = = " extra_perimeters "
| | opt_key = = " gap_fill_speed "
| | opt_key = = " overhangs "
| | opt_key = = " first_layer_extrusion_width "
| | opt_key = = " perimeter_extrusion_width "
| | opt_key = = " infill_overlap "
| | opt_key = = " thin_walls "
| | opt_key = = " external_perimeters_first " ) {
steps . emplace_back ( posPerimeters ) ;
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " layer_height "
2017-05-30 15:17:26 +00:00
| | opt_key = = " first_layer_height "
2019-03-01 16:53:02 +00:00
| | opt_key = = " raft_layers "
2020-02-09 12:56:42 +00:00
| | opt_key = = " slice_closing_radius " ) {
2017-05-30 15:17:26 +00:00
steps . emplace_back ( posSlice ) ;
2020-02-08 20:36:29 +00:00
} else if (
2017-06-13 17:29:15 +00:00
opt_key = = " clip_multipart_objects "
2017-06-26 14:28:10 +00:00
| | opt_key = = " elefant_foot_compensation "
2017-06-13 17:29:15 +00:00
| | opt_key = = " support_material_contact_distance "
2017-05-30 15:17:26 +00:00
| | opt_key = = " xy_size_compensation " ) {
steps . emplace_back ( posSlice ) ;
2019-09-09 15:47:29 +00:00
} else if ( opt_key = = " support_material " ) {
steps . emplace_back ( posSupportMaterial ) ;
if ( m_config . support_material_contact_distance = = 0. ) {
// Enabling / disabling supports while soluble support interface is enabled.
// This changes the bridging logic (bridging enabled without supports, disabled with supports).
// Reset everything.
// See GH #1482 for details.
steps . emplace_back ( posSlice ) ;
}
2017-05-31 15:02:23 +00:00
} else if (
2019-09-09 15:47:29 +00:00
opt_key = = " support_material_auto "
2017-05-30 15:17:26 +00:00
| | opt_key = = " support_material_angle "
2017-05-31 15:02:23 +00:00
| | opt_key = = " support_material_buildplate_only "
| | opt_key = = " support_material_enforce_layers "
2017-05-30 15:17:26 +00:00
| | opt_key = = " support_material_extruder "
| | opt_key = = " support_material_extrusion_width "
| | opt_key = = " support_material_interface_layers "
| | opt_key = = " support_material_interface_contact_loops "
| | opt_key = = " support_material_interface_extruder "
| | opt_key = = " support_material_interface_spacing "
| | opt_key = = " support_material_pattern "
| | opt_key = = " support_material_xy_spacing "
| | opt_key = = " support_material_spacing "
| | opt_key = = " support_material_synchronize_layers "
| | opt_key = = " support_material_threshold "
| | opt_key = = " support_material_with_sheath "
| | opt_key = = " dont_support_bridges "
| | opt_key = = " first_layer_extrusion_width " ) {
steps . emplace_back ( posSupportMaterial ) ;
2020-12-21 08:15:33 +00:00
} else if ( opt_key = = " bottom_solid_layers " ) {
steps . emplace_back ( posPrepareInfill ) ;
if ( m_print - > config ( ) . spiral_vase ) {
// Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again.
// Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528.
steps . emplace_back ( posSlice ) ;
}
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " interface_shells "
2017-05-30 15:17:26 +00:00
| | opt_key = = " infill_only_where_needed "
| | opt_key = = " infill_every_layers "
| | opt_key = = " solid_infill_every_layers "
2020-02-05 15:53:26 +00:00
| | opt_key = = " bottom_solid_min_thickness "
2017-05-30 15:17:26 +00:00
| | opt_key = = " top_solid_layers "
2020-02-05 15:53:26 +00:00
| | opt_key = = " top_solid_min_thickness "
2017-05-30 15:17:26 +00:00
| | opt_key = = " solid_infill_below_area "
| | opt_key = = " infill_extruder "
| | opt_key = = " solid_infill_extruder "
| | opt_key = = " infill_extrusion_width "
2017-07-31 14:23:52 +00:00
| | opt_key = = " ensure_vertical_shell_thickness "
| | opt_key = = " bridge_angle " ) {
2017-05-30 15:17:26 +00:00
steps . emplace_back ( posPrepareInfill ) ;
2017-05-31 15:02:23 +00:00
} else if (
2019-02-22 14:25:35 +00:00
opt_key = = " top_fill_pattern "
| | opt_key = = " bottom_fill_pattern "
2017-05-30 15:17:26 +00:00
| | opt_key = = " external_fill_link_max_length "
| | opt_key = = " fill_angle "
| | opt_key = = " fill_pattern "
2020-11-16 09:54:00 +00:00
| | opt_key = = " infill_anchor "
2020-11-24 15:00:46 +00:00
| | opt_key = = " infill_anchor_max "
2017-05-30 15:17:26 +00:00
| | opt_key = = " top_infill_extrusion_width "
| | opt_key = = " first_layer_extrusion_width " ) {
steps . emplace_back ( posInfill ) ;
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " fill_density "
2017-05-30 15:17:26 +00:00
| | opt_key = = " solid_infill_extrusion_width " ) {
steps . emplace_back ( posPerimeters ) ;
steps . emplace_back ( posPrepareInfill ) ;
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " external_perimeter_extrusion_width "
2017-05-30 15:17:26 +00:00
| | opt_key = = " perimeter_extruder " ) {
steps . emplace_back ( posPerimeters ) ;
steps . emplace_back ( posSupportMaterial ) ;
} else if ( opt_key = = " bridge_flow_ratio " ) {
2019-09-09 16:09:40 +00:00
if ( m_config . support_material_contact_distance > 0. ) {
// Only invalidate due to bridging if bridging is enabled.
// If later "support_material_contact_distance" is modified, the complete PrintObject is invalidated anyway.
steps . emplace_back ( posPerimeters ) ;
steps . emplace_back ( posInfill ) ;
steps . emplace_back ( posSupportMaterial ) ;
}
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " seam_position "
2017-05-30 15:17:26 +00:00
| | opt_key = = " seam_preferred_direction "
| | opt_key = = " seam_preferred_direction_jitter "
| | opt_key = = " support_material_speed "
2017-05-31 15:02:23 +00:00
| | opt_key = = " support_material_interface_speed "
2017-05-30 15:17:26 +00:00
| | 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 "
2018-12-14 16:17:51 +00:00
| | opt_key = = " top_solid_infill_speed " ) {
invalidated | = m_print - > invalidate_step ( psGCodeExport ) ;
} else if (
opt_key = = " wipe_into_infill "
| | opt_key = = " wipe_into_objects " ) {
invalidated | = m_print - > invalidate_step ( psWipeTower ) ;
invalidated | = m_print - > invalidate_step ( psGCodeExport ) ;
2014-08-03 17:28:40 +00:00
} else {
// for legacy, if we can't handle this option let's invalidate all steps
2018-09-11 12:04:47 +00:00
this - > invalidate_all_steps ( ) ;
2017-06-08 15:46:28 +00:00
invalidated = true ;
2014-08-03 17:28:40 +00:00
}
}
2017-06-08 15:46:28 +00:00
2017-05-30 15:17:26 +00:00
sort_remove_duplicates ( steps ) ;
for ( PrintObjectStep step : steps )
invalidated | = this - > invalidate_step ( step ) ;
2014-08-03 17:28:40 +00:00
return invalidated ;
}
2017-05-30 15:17:26 +00:00
bool PrintObject : : invalidate_step ( PrintObjectStep step )
2014-08-03 17:28:40 +00:00
{
2018-11-08 13:23:17 +00:00
bool invalidated = Inherited : : invalidate_step ( step ) ;
2014-08-03 17:28:40 +00:00
// propagate to dependent steps
if ( step = = posPerimeters ) {
2020-10-22 15:36:51 +00:00
invalidated | = this - > invalidate_steps ( { posPrepareInfill , posInfill , posIroning } ) ;
2018-10-31 18:21:00 +00:00
invalidated | = m_print - > invalidate_steps ( { psSkirt , psBrim } ) ;
2014-08-03 17:28:40 +00:00
} else if ( step = = posPrepareInfill ) {
2020-10-22 15:54:32 +00:00
invalidated | = this - > invalidate_steps ( { posInfill , posIroning } ) ;
2014-08-03 17:28:40 +00:00
} else if ( step = = posInfill ) {
2020-12-18 10:14:26 +00:00
invalidated | = this - > invalidate_steps ( { posIroning } ) ;
2018-10-31 18:21:00 +00:00
invalidated | = m_print - > invalidate_steps ( { psSkirt , psBrim } ) ;
2014-08-03 17:28:40 +00:00
} else if ( step = = posSlice ) {
2020-10-22 15:36:51 +00:00
invalidated | = this - > invalidate_steps ( { posPerimeters , posPrepareInfill , posInfill , posIroning , posSupportMaterial } ) ;
2018-12-18 08:57:19 +00:00
invalidated | = m_print - > invalidate_steps ( { psSkirt , psBrim } ) ;
2019-03-05 13:05:58 +00:00
this - > m_slicing_params . valid = false ;
} else if ( step = = posSupportMaterial ) {
2018-10-31 18:21:00 +00:00
invalidated | = m_print - > invalidate_steps ( { psSkirt , psBrim } ) ;
2019-03-05 13:05:58 +00:00
this - > m_slicing_params . valid = false ;
}
2014-08-03 17:28:40 +00:00
2017-05-31 15:02:23 +00:00
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
2018-06-07 14:19:57 +00:00
// It also decides about what the wipe_into_infill / wipe_into_object features will do,
// and that too depends on many of the settings.
2018-09-11 12:04:47 +00:00
invalidated | = m_print - > invalidate_step ( psWipeTower ) ;
2018-11-12 15:28:27 +00:00
// Invalidate G-code export in any case.
invalidated | = m_print - > invalidate_step ( psGCodeExport ) ;
2014-08-03 17:28:40 +00:00
return invalidated ;
}
2018-11-12 15:28:27 +00:00
bool PrintObject : : invalidate_all_steps ( )
{
2019-06-20 14:15:09 +00:00
// First call the "invalidate" functions, which may cancel background processing.
bool result = Inherited : : invalidate_all_steps ( ) | m_print - > invalidate_all_steps ( ) ;
// Then reset some of the depending values.
this - > m_slicing_params . valid = false ;
this - > region_volumes . clear ( ) ;
return result ;
2018-11-12 15:28:27 +00:00
}
2017-05-30 15:17:26 +00:00
bool PrintObject : : has_support_material ( ) const
2015-03-06 08:56:58 +00:00
{
2018-09-11 12:04:47 +00:00
return m_config . support_material
| | m_config . raft_layers > 0
| | m_config . support_material_enforce_layers > 0 ;
2015-03-06 08:56:58 +00:00
}
2020-02-08 20:36:29 +00:00
static const PrintRegion * first_printing_region ( const PrintObject & print_object )
{
for ( size_t idx_region = 0 ; idx_region < print_object . region_volumes . size ( ) ; + + idx_region )
if ( ! print_object . region_volumes . empty ( ) )
return print_object . print ( ) - > regions ( ) [ idx_region ] ;
return nullptr ;
}
2016-11-10 18:23:01 +00:00
// This function analyzes slices of a region (SurfaceCollection slices).
2016-11-17 22:22:59 +00:00
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
2017-06-01 14:31:29 +00:00
// Initially all slices are of type stInternal.
2016-11-10 18:23:01 +00:00
// Slices are compared against the top / bottom slices and regions and classified to the following groups:
2017-06-01 14:31:29 +00:00
// stTop - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill.
// stBottomBridge - 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.
// stBottom - 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.
// stInternal - Part of a region, which is supported by the same region type.
// If a part of a region is of stBottom and stTop, the stBottom wins.
2016-11-10 18:23:01 +00:00
void PrintObject : : detect_surfaces_type ( )
{
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Detecting solid surfaces... " < < log_memory_info ( ) ;
2016-11-29 18:27:23 +00:00
2017-03-28 11:25:10 +00:00
// Interface shells: the intersecting parts are treated as self standing objects supporting each other.
// Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers
// are completely hidden inside a collective body of intersecting parts.
// This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells
// should be visible.
2020-02-08 20:36:29 +00:00
bool spiral_vase = this - > print ( ) - > config ( ) . spiral_vase . value ;
bool interface_shells = ! spiral_vase & & m_config . interface_shells . value ;
2020-12-09 06:21:14 +00:00
size_t num_layers = spiral_vase ? std : : min ( size_t ( first_printing_region ( * this ) - > config ( ) . bottom_solid_layers ) , m_layers . size ( ) ) : m_layers . size ( ) ;
2017-03-07 20:46:45 +00:00
2019-06-25 11:06:04 +00:00
for ( size_t idx_region = 0 ; idx_region < this - > region_volumes . size ( ) ; + + idx_region ) {
2017-03-07 20:46:45 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Detecting solid surfaces for region " < < idx_region < < " in parallel - start " ;
2016-11-17 22:22:59 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-09-11 12:04:47 +00:00
for ( Layer * layer : m_layers )
layer - > m_regions [ idx_region ] - > export_region_fill_surfaces_to_svg_debug ( " 1_detect_surfaces_type-initial " ) ;
2016-11-17 22:22:59 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-10 18:23:01 +00:00
2017-03-28 11:25:10 +00:00
// If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads.
// Cache the result of the following parallel_loop.
2017-03-07 20:46:45 +00:00
std : : vector < Surfaces > surfaces_new ;
if ( interface_shells )
2020-02-08 20:36:29 +00:00
surfaces_new . assign ( num_layers , Surfaces ( ) ) ;
2017-03-02 15:52:24 +00:00
2017-03-07 20:46:45 +00:00
tbb : : parallel_for (
2020-02-08 20:36:29 +00:00
tbb : : blocked_range < size_t > ( 0 ,
spiral_vase ?
// In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom.
( ( num_layers > 1 ) ? num_layers - 1 : num_layers ) :
// In non-spiral vase mode, go over all layers.
m_layers . size ( ) ) ,
2017-03-07 20:46:45 +00:00
[ this , idx_region , interface_shells , & surfaces_new ] ( const tbb : : blocked_range < size_t > & range ) {
2017-03-28 11:25:10 +00:00
// 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_1st =
2018-09-11 12:04:47 +00:00
( m_config . raft_layers . value > 0 & & m_config . support_material_contact_distance . value > 0 ) ?
2017-03-28 11:25:10 +00:00
stBottomBridge : stBottom ;
// 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_other =
2018-09-11 12:04:47 +00:00
( m_config . support_material . value & & m_config . support_material_contact_distance . value = = 0 ) ?
2017-03-28 11:25:10 +00:00
stBottom : stBottomBridge ;
2017-03-07 20:46:45 +00:00
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-07 20:46:45 +00:00
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
2018-09-11 12:04:47 +00:00
Layer * layer = m_layers [ idx_layer ] ;
2020-01-23 08:53:06 +00:00
LayerRegion * layerm = layer - > m_regions [ idx_region ] ;
2017-03-07 20:46:45 +00:00
// comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested
2018-09-11 12:04:47 +00:00
Layer * upper_layer = ( idx_layer + 1 < this - > layer_count ( ) ) ? m_layers [ idx_layer + 1 ] : nullptr ;
Layer * lower_layer = ( idx_layer > 0 ) ? m_layers [ idx_layer - 1 ] : nullptr ;
2017-03-07 20:46:45 +00:00
// 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 ) {
Polygons upper_slices = interface_shells ?
2020-01-23 08:53:06 +00:00
to_polygons ( upper_layer - > m_regions [ idx_region ] - > slices . surfaces ) :
2020-01-03 13:05:56 +00:00
to_polygons ( upper_layer - > lslices ) ;
2017-03-07 20:46:45 +00:00
surfaces_append ( top ,
2017-12-14 12:26:44 +00:00
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
offset_ex ( offset_ex ( diff_ex ( layerm_slices_surfaces , upper_slices , true ) , - offset ) , offset ) ,
2017-03-07 20:46:45 +00:00
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 ;
2017-03-28 11:25:10 +00:00
for ( Surface & surface : top )
surface . surface_type = stTop ;
2017-03-07 20:46:45 +00:00
}
2017-03-28 11:25:10 +00:00
// Find bottom surfaces (difference between current surfaces of current layer and lower one).
2017-03-07 20:46:45 +00:00
Surfaces bottom ;
if ( lower_layer ) {
2017-03-28 12:29:27 +00:00
#if 0
//FIXME Why is this branch failing t\multi.t ?
2017-03-28 11:25:10 +00:00
Polygons lower_slices = interface_shells ?
to_polygons ( lower_layer - > get_region ( idx_region ) - > slices . surfaces ) :
to_polygons ( lower_layer - > slices ) ;
surfaces_append ( bottom ,
offset2_ex ( diff ( layerm_slices_surfaces , lower_slices , true ) , - offset , offset ) ,
surface_type_bottom_other ) ;
2017-03-28 12:29:27 +00:00
# else
// Any surface lying on the void is a true bottom bridge (an overhang)
surfaces_append (
bottom ,
offset2_ex (
2020-01-03 13:05:56 +00:00
diff ( layerm_slices_surfaces , to_polygons ( lower_layer - > lslices ) , true ) ,
2017-03-28 12:29:27 +00:00
- offset , offset ) ,
surface_type_bottom_other ) ;
// if user requested internal shells, we need to identify surfaces
// lying on other slices not belonging to this region
if ( interface_shells ) {
// 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 (
2020-01-03 13:05:56 +00:00
intersection ( layerm_slices_surfaces , to_polygons ( lower_layer - > lslices ) ) , // supported
2020-01-23 08:53:06 +00:00
to_polygons ( lower_layer - > m_regions [ idx_region ] - > slices . surfaces ) ,
2017-03-28 12:29:27 +00:00
true ) ,
- offset , offset ) ,
stBottom ) ;
}
# endif
2017-03-07 20:46:45 +00:00
} 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 ;
2017-03-28 11:25:10 +00:00
for ( Surface & surface : bottom )
surface . surface_type = surface_type_bottom_1st ;
2017-03-07 20:46:45 +00:00
}
// 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 ( std : : move ( top ) ) ;
top . clear ( ) ;
surfaces_append ( top ,
diff_ex ( top_polygons , to_polygons ( bottom ) , false ) ,
stTop ) ;
}
2016-11-10 18:23:01 +00:00
2017-03-07 20:46:45 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0 ;
std : : vector < std : : pair < Slic3r : : ExPolygons , SVG : : ExPolygonAttributes > > expolygons_with_attributes ;
expolygons_with_attributes . emplace_back ( std : : make_pair ( union_ex ( top ) , SVG : : ExPolygonAttributes ( " green " ) ) ) ;
expolygons_with_attributes . emplace_back ( std : : make_pair ( union_ex ( bottom ) , SVG : : ExPolygonAttributes ( " brown " ) ) ) ;
expolygons_with_attributes . emplace_back ( std : : make_pair ( to_expolygons ( layerm - > slices . surfaces ) , SVG : : ExPolygonAttributes ( " black " ) ) ) ;
SVG : : export_expolygons ( debug_out_path ( " 1_detect_surfaces_type_%d_region%d-layer_%f.svg " , iRun + + , idx_region , layer - > print_z ) . c_str ( ) , expolygons_with_attributes ) ;
}
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// save surfaces to layer
Surfaces & surfaces_out = interface_shells ? surfaces_new [ idx_layer ] : layerm - > slices . surfaces ;
surfaces_out . clear ( ) ;
// find internal surfaces (difference between top/bottom surfaces and others)
{
Polygons topbottom = to_polygons ( top ) ;
polygons_append ( topbottom , to_polygons ( bottom ) ) ;
surfaces_append ( surfaces_out ,
diff_ex ( layerm_slices_surfaces , topbottom , false ) ,
stInternal ) ;
}
surfaces_append ( surfaces_out , std : : move ( top ) ) ;
surfaces_append ( surfaces_out , std : : move ( 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 */
}
2016-11-10 18:23:01 +00:00
}
2017-03-07 20:46:45 +00:00
) ; // for each layer of a region
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2016-11-10 18:23:01 +00:00
2017-03-07 20:46:45 +00:00
if ( interface_shells ) {
// Move surfaces_new to layerm->slices.surfaces
2020-02-08 20:36:29 +00:00
for ( size_t idx_layer = 0 ; idx_layer < num_layers ; + + idx_layer )
2020-01-23 08:53:06 +00:00
m_layers [ idx_layer ] - > m_regions [ idx_region ] - > slices . surfaces = std : : move ( surfaces_new [ idx_layer ] ) ;
2017-03-07 20:46:45 +00:00
}
2016-11-10 18:23:01 +00:00
2020-03-14 10:59:50 +00:00
if ( spiral_vase ) {
if ( num_layers > 1 )
// Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
m_layers [ num_layers - 1 ] - > m_regions [ idx_region ] - > slices . set_type ( stTop ) ;
for ( size_t i = num_layers ; i < m_layers . size ( ) ; + + i )
m_layers [ i ] - > m_regions [ idx_region ] - > slices . set_type ( stInternal ) ;
2020-02-08 20:36:29 +00:00
}
2017-03-08 22:02:27 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Detecting solid surfaces for region " < < idx_region < < " - clipping in parallel - start " ;
2016-11-10 18:23:01 +00:00
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
2017-03-08 22:02:27 +00:00
tbb : : parallel_for (
2018-09-11 12:04:47 +00:00
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
2020-02-09 08:43:15 +00:00
[ this , idx_region , interface_shells ] ( const tbb : : blocked_range < size_t > & range ) {
2017-03-08 22:02:27 +00:00
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2020-01-23 08:53:06 +00:00
LayerRegion * layerm = m_layers [ idx_layer ] - > m_regions [ idx_region ] ;
2017-03-08 22:02:27 +00:00
layerm - > slices_to_fill_surfaces_clipped ( ) ;
2016-11-10 18:23:01 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 22:02:27 +00:00
layerm - > export_region_fill_surfaces_to_svg_debug ( " 1_detect_surfaces_type-final " ) ;
2016-11-10 18:23:01 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 22:02:27 +00:00
} // for each layer of a region
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-08 22:02:27 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Detecting solid surfaces for region " < < idx_region < < " - clipping in parallel - end " ;
2017-08-02 12:24:32 +00:00
} // for each this->print->region_count
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
2020-02-07 13:10:18 +00:00
m_typed_slices = true ;
2016-11-10 18:23:01 +00:00
}
2017-05-30 16:33:17 +00:00
void PrintObject : : process_external_surfaces ( )
2015-10-26 22:23:03 +00:00
{
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Processing external surfaces... " < < log_memory_info ( ) ;
2016-11-29 18:27:23 +00:00
2019-09-06 13:03:49 +00:00
// Cached surfaces covered by some extrusion, defining regions, over which the from the surfaces one layer higher are allowed to expand.
std : : vector < Polygons > surfaces_covered ;
// Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
// over voids, which are supported by the layer below.
bool has_voids = false ;
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id )
if ( ! this - > region_volumes . empty ( ) & & this - > print ( ) - > regions ( ) [ region_id ] - > config ( ) . fill_density = = 0 ) {
has_voids = true ;
break ;
}
if ( has_voids & & m_layers . size ( ) > 1 ) {
// All but stInternal fill surfaces will get expanded and possibly trimmed.
std : : vector < unsigned char > layer_expansions_and_voids ( m_layers . size ( ) , false ) ;
for ( size_t layer_idx = 0 ; layer_idx < m_layers . size ( ) ; + + layer_idx ) {
const Layer * layer = m_layers [ layer_idx ] ;
bool expansions = false ;
bool voids = false ;
for ( const LayerRegion * layerm : layer - > regions ( ) ) {
for ( const Surface & surface : layerm - > fill_surfaces . surfaces ) {
if ( surface . surface_type = = stInternal )
voids = true ;
else
expansions = true ;
if ( voids & & expansions ) {
layer_expansions_and_voids [ layer_idx ] = true ;
goto end ;
}
}
}
end : ;
}
BOOST_LOG_TRIVIAL ( debug ) < < " Collecting surfaces covered with extrusions in parallel - start " ;
surfaces_covered . resize ( m_layers . size ( ) - 1 , Polygons ( ) ) ;
auto unsupported_width = - float ( scale_ ( 0.3 * EXTERNAL_INFILL_MARGIN ) ) ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) - 1 ) ,
[ this , & surfaces_covered , & layer_expansions_and_voids , unsupported_width ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx )
if ( layer_expansions_and_voids [ layer_idx + 1 ] ) {
m_print - > throw_if_canceled ( ) ;
Polygons voids ;
for ( const LayerRegion * layerm : m_layers [ layer_idx ] - > regions ( ) ) {
if ( layerm - > region ( ) - > config ( ) . fill_density . value = = 0. )
for ( const Surface & surface : layerm - > fill_surfaces . surfaces )
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
polygons_append ( voids , offset ( surface . expolygon , unsupported_width ) ) ;
}
2020-01-03 13:05:56 +00:00
surfaces_covered [ layer_idx ] = diff ( to_polygons ( this - > m_layers [ layer_idx ] - > lslices ) , voids ) ;
2019-09-06 13:03:49 +00:00
}
}
) ;
m_print - > throw_if_canceled ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Collecting surfaces covered with extrusions in parallel - end " ;
}
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2017-03-07 16:43:43 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Processing external surfaces for region " < < region_id < < " in parallel - start " ;
tbb : : parallel_for (
2018-09-11 12:04:47 +00:00
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
2019-09-06 13:03:49 +00:00
[ this , & surfaces_covered , region_id ] ( const tbb : : blocked_range < size_t > & range ) {
2017-03-07 16:43:43 +00:00
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
2019-09-06 13:03:49 +00:00
m_layers [ layer_idx ] - > get_region ( ( int ) region_id ) - > process_external_surfaces (
( layer_idx = = 0 ) ? nullptr : m_layers [ layer_idx - 1 ] ,
( layer_idx = = 0 | | surfaces_covered . empty ( ) | | surfaces_covered [ layer_idx - 1 ] . empty ( ) ) ? nullptr : & surfaces_covered [ layer_idx - 1 ] ) ;
2017-03-07 16:43:43 +00:00
}
}
) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-07 16:43:43 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Processing external surfaces for region " < < region_id < < " in parallel - end " ;
2015-10-26 22:23:03 +00:00
}
}
2017-05-30 16:33:17 +00:00
void PrintObject : : discover_vertical_shells ( )
2016-09-26 11:56:24 +00:00
{
2016-11-16 21:16:20 +00:00
PROFILE_FUNC ( ) ;
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Discovering vertical shells... " < < log_memory_info ( ) ;
2016-11-29 18:27:23 +00:00
2017-06-01 14:31:29 +00:00
struct DiscoverVerticalShellsCacheEntry
{
// Collected polygons, offsetted
Polygons top_surfaces ;
Polygons bottom_surfaces ;
Polygons holes ;
} ;
2020-02-08 20:36:29 +00:00
bool spiral_vase = this - > print ( ) - > config ( ) . spiral_vase . value ;
2020-12-09 06:21:14 +00:00
size_t num_layers = spiral_vase ? std : : min ( size_t ( first_printing_region ( * this ) - > config ( ) . bottom_solid_layers ) , m_layers . size ( ) ) : m_layers . size ( ) ;
2020-02-05 15:53:26 +00:00
coordf_t min_layer_height = this - > slicing_parameters ( ) . min_layer_height ;
// Does this region possibly produce more than 1 top or bottom layer?
auto has_extra_layers_fn = [ min_layer_height ] ( const PrintRegionConfig & config ) {
auto num_extra_layers = [ min_layer_height ] ( int num_solid_layers , coordf_t min_shell_thickness ) {
if ( num_solid_layers = = 0 )
return 0 ;
int n = num_solid_layers - 1 ;
int n2 = int ( ceil ( min_shell_thickness / min_layer_height ) ) ;
return std : : max ( n , n2 - 1 ) ;
} ;
return num_extra_layers ( config . top_solid_layers , config . top_solid_min_thickness ) +
num_extra_layers ( config . bottom_solid_layers , config . bottom_solid_min_thickness ) > 0 ;
} ;
2020-02-08 20:36:29 +00:00
std : : vector < DiscoverVerticalShellsCacheEntry > cache_top_botom_regions ( num_layers , DiscoverVerticalShellsCacheEntry ( ) ) ;
2018-11-06 14:31:26 +00:00
bool top_bottom_surfaces_all_regions = this - > region_volumes . size ( ) > 1 & & ! m_config . interface_shells . value ;
2017-06-01 14:31:29 +00:00
if ( top_bottom_surfaces_all_regions ) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false ;
2020-02-05 15:53:26 +00:00
for ( size_t idx_region = 0 ; idx_region < this - > region_volumes . size ( ) ; + + idx_region ) {
const PrintRegionConfig & config = m_print - > get_region ( idx_region ) - > config ( ) ;
if ( config . ensure_vertical_shell_thickness . value & & has_extra_layers_fn ( config ) ) {
2017-06-01 14:31:29 +00:00
has_extra_layers = true ;
2020-02-05 15:53:26 +00:00
break ;
2017-06-01 14:31:29 +00:00
}
}
if ( ! has_extra_layers )
// The "ensure vertical wall thickness" feature is not applicable to any of the regions. Quit.
return ;
BOOST_LOG_TRIVIAL ( debug ) < < " Discovering vertical shells in parallel - start : cache top / bottom " ;
//FIXME Improve the heuristics for a grain size.
2020-02-08 20:36:29 +00:00
size_t grain_size = std : : max ( num_layers / 16 , size_t ( 1 ) ) ;
2017-06-01 14:31:29 +00:00
tbb : : parallel_for (
2020-02-08 20:36:29 +00:00
tbb : : blocked_range < size_t > ( 0 , num_layers , grain_size ) ,
2017-06-01 14:31:29 +00:00
[ this , & cache_top_botom_regions ] ( const tbb : : blocked_range < size_t > & range ) {
const SurfaceType surfaces_bottom [ 2 ] = { stBottom , stBottomBridge } ;
2018-11-06 14:31:26 +00:00
const size_t num_regions = this - > region_volumes . size ( ) ;
2017-06-01 14:31:29 +00:00
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
const Layer & layer = * m_layers [ idx_layer ] ;
2017-06-01 14:31:29 +00:00
DiscoverVerticalShellsCacheEntry & cache = cache_top_botom_regions [ idx_layer ] ;
// Simulate single set of perimeters over all merged regions.
float perimeter_offset = 0.f ;
float perimeter_min_spacing = FLT_MAX ;
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static size_t debug_idx = 0 ;
+ + debug_idx ;
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
for ( size_t idx_region = 0 ; idx_region < num_regions ; + + idx_region ) {
2018-09-11 12:04:47 +00:00
LayerRegion & layerm = * layer . m_regions [ idx_region ] ;
2017-06-01 14:31:29 +00:00
float min_perimeter_infill_spacing = float ( layerm . flow ( frSolidInfill ) . scaled_spacing ( ) ) * 1.05f ;
// Top surfaces.
append ( cache . top_surfaces , offset ( to_expolygons ( layerm . slices . filter_by_type ( stTop ) ) , min_perimeter_infill_spacing ) ) ;
append ( cache . top_surfaces , offset ( to_expolygons ( layerm . fill_surfaces . filter_by_type ( stTop ) ) , min_perimeter_infill_spacing ) ) ;
// Bottom surfaces.
append ( cache . bottom_surfaces , offset ( to_expolygons ( layerm . slices . filter_by_types ( surfaces_bottom , 2 ) ) , min_perimeter_infill_spacing ) ) ;
append ( cache . bottom_surfaces , offset ( to_expolygons ( layerm . fill_surfaces . filter_by_types ( surfaces_bottom , 2 ) ) , min_perimeter_infill_spacing ) ) ;
// Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only.
// First find the maxium number of perimeters per region slice.
unsigned int perimeters = 0 ;
for ( Surface & s : layerm . slices . surfaces )
perimeters = std : : max < unsigned int > ( perimeters , s . extra_perimeters ) ;
2018-09-11 12:04:47 +00:00
perimeters + = layerm . region ( ) - > config ( ) . perimeters . value ;
2017-06-01 14:31:29 +00:00
// Then calculate the infill offset.
if ( perimeters > 0 ) {
Flow extflow = layerm . flow ( frExternalPerimeter ) ;
Flow flow = layerm . flow ( frPerimeter ) ;
perimeter_offset = std : : max ( perimeter_offset ,
0.5f * float ( extflow . scaled_width ( ) + extflow . scaled_spacing ( ) ) + ( float ( perimeters ) - 1.f ) * flow . scaled_spacing ( ) ) ;
perimeter_min_spacing = std : : min ( perimeter_min_spacing , float ( std : : min ( extflow . scaled_spacing ( ) , flow . scaled_spacing ( ) ) ) ) ;
}
polygons_append ( cache . holes , to_polygons ( layerm . fill_expolygons ) ) ;
}
// Save some computing time by reducing the number of polygons.
cache . top_surfaces = union_ ( cache . top_surfaces , false ) ;
cache . bottom_surfaces = union_ ( cache . bottom_surfaces , false ) ;
// For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print.
if ( perimeter_offset > 0. ) {
2020-01-03 13:05:56 +00:00
// The layer.lslices are forced to merge by expanding them first.
polygons_append ( cache . holes , offset ( offset_ex ( layer . lslices , 0.3f * perimeter_min_spacing ) , - perimeter_offset - 0.3f * perimeter_min_spacing ) ) ;
2017-06-01 14:31:29 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
2020-02-08 20:36:29 +00:00
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-extra-holes-%d.svg " , debug_idx ) , get_extents ( layer . lslices ) ) ;
svg . draw ( layer . lslices , " blue " ) ;
2017-06-01 14:31:29 +00:00
svg . draw ( union_ex ( cache . holes ) , " red " ) ;
svg . draw_outline ( union_ex ( cache . holes ) , " black " , " blue " , scale_ ( 0.05 ) ) ;
svg . Close ( ) ;
}
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
cache . holes = union_ ( cache . holes , false ) ;
}
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-06-01 14:31:29 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Discovering vertical shells in parallel - end : cache top / bottom " ;
}
2018-11-06 14:31:26 +00:00
for ( size_t idx_region = 0 ; idx_region < this - > region_volumes . size ( ) ; + + idx_region ) {
2016-11-16 21:16:20 +00:00
PROFILE_BLOCK ( discover_vertical_shells_region ) ;
2018-09-11 12:04:47 +00:00
const PrintRegion & region = * m_print - > get_region ( idx_region ) ;
if ( ! region . config ( ) . ensure_vertical_shell_thickness . value )
2016-11-16 17:04:47 +00:00
// This region will be handled by discover_horizontal_shells().
continue ;
2020-02-05 15:53:26 +00:00
if ( ! has_extra_layers_fn ( region . config ( ) ) )
2016-11-16 17:04:47 +00:00
// Zero or 1 layer, there is no additional vertical wall thickness enforced.
2016-10-16 20:11:19 +00:00
continue ;
2017-03-08 13:54:04 +00:00
//FIXME Improve the heuristics for a grain size.
2020-02-08 20:36:29 +00:00
size_t grain_size = std : : max ( num_layers / 16 , size_t ( 1 ) ) ;
2017-06-01 14:31:29 +00:00
if ( ! top_bottom_surfaces_all_regions ) {
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
// is calculated over a single material.
BOOST_LOG_TRIVIAL ( debug ) < < " Discovering vertical shells for region " < < idx_region < < " in parallel - start : cache top / bottom " ;
tbb : : parallel_for (
2020-02-08 20:36:29 +00:00
tbb : : blocked_range < size_t > ( 0 , num_layers , grain_size ) ,
2017-06-01 14:31:29 +00:00
[ this , idx_region , & cache_top_botom_regions ] ( const tbb : : blocked_range < size_t > & range ) {
2017-03-08 17:10:39 +00:00
const SurfaceType surfaces_bottom [ 2 ] = { stBottom , stBottomBridge } ;
2017-06-01 14:31:29 +00:00
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
Layer & layer = * m_layers [ idx_layer ] ;
LayerRegion & layerm = * layer . m_regions [ idx_region ] ;
2017-06-01 14:31:29 +00:00
float min_perimeter_infill_spacing = float ( layerm . flow ( frSolidInfill ) . scaled_spacing ( ) ) * 1.05f ;
// Top surfaces.
auto & cache = cache_top_botom_regions [ idx_layer ] ;
cache . top_surfaces = offset ( to_expolygons ( layerm . slices . filter_by_type ( stTop ) ) , min_perimeter_infill_spacing ) ;
append ( cache . top_surfaces , offset ( to_expolygons ( layerm . fill_surfaces . filter_by_type ( stTop ) ) , min_perimeter_infill_spacing ) ) ;
// Bottom surfaces.
cache . bottom_surfaces = offset ( to_expolygons ( layerm . slices . filter_by_types ( surfaces_bottom , 2 ) ) , min_perimeter_infill_spacing ) ;
append ( cache . bottom_surfaces , offset ( to_expolygons ( layerm . fill_surfaces . filter_by_types ( surfaces_bottom , 2 ) ) , min_perimeter_infill_spacing ) ) ;
// Holes over all regions. Only collect them once, they are valid for all idx_region iterations.
if ( cache . holes . empty ( ) ) {
2018-09-11 12:04:47 +00:00
for ( size_t idx_region = 0 ; idx_region < layer . regions ( ) . size ( ) ; + + idx_region )
polygons_append ( cache . holes , to_polygons ( layer . regions ( ) [ idx_region ] - > fill_expolygons ) ) ;
2017-06-01 14:31:29 +00:00
}
}
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-06-01 14:31:29 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Discovering vertical shells for region " < < idx_region < < " in parallel - end : cache top / bottom " ;
}
2017-03-08 17:10:39 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Discovering vertical shells for region " < < idx_region < < " in parallel - start : ensure vertical wall thickness " ;
tbb : : parallel_for (
2020-02-08 20:36:29 +00:00
tbb : : blocked_range < size_t > ( 0 , num_layers , grain_size ) ,
2020-02-05 15:53:26 +00:00
[ this , idx_region , & cache_top_botom_regions ]
2017-03-08 17:10:39 +00:00
( const tbb : : blocked_range < size_t > & range ) {
2017-03-08 13:54:04 +00:00
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for ( size_t idx_layer = range . begin ( ) ; idx_layer < range . end ( ) ; + + idx_layer ) {
PROFILE_BLOCK ( discover_vertical_shells_region_layer ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-01-03 09:51:19 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
static size_t debug_idx = 0 ;
+ + debug_idx ;
2017-01-03 09:51:19 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2020-02-05 15:53:26 +00:00
Layer * layer = m_layers [ idx_layer ] ;
LayerRegion * layerm = layer - > m_regions [ idx_region ] ;
const PrintRegionConfig & region_config = layerm - > region ( ) - > config ( ) ;
2017-01-03 09:51:19 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
layerm - > export_region_slices_to_svg_debug ( " 4_discover_vertical_shells-initial " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 4_discover_vertical_shells-initial " ) ;
2017-01-03 09:51:19 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 13:54:04 +00:00
Flow solid_infill_flow = layerm - > flow ( frSolidInfill ) ;
coord_t infill_line_spacing = solid_infill_flow . scaled_spacing ( ) ;
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
Polygons shell ;
Polygons holes ;
2016-10-22 20:25:00 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
ExPolygons shell_ex ;
2016-10-22 20:25:00 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 13:54:04 +00:00
float min_perimeter_infill_spacing = float ( infill_line_spacing ) * 1.05f ;
{
PROFILE_BLOCK ( discover_vertical_shells_region_layer_collect ) ;
2017-01-03 09:51:19 +00:00
#if 0
// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
{
Slic3r : : SVG svg_cummulative ( debug_out_path ( " discover_vertical_shells-perimeters-before-union-run%d.svg " , debug_idx ) , this - > bounding_box ( ) ) ;
for ( int n = ( int ) idx_layer - n_extra_bottom_layers ; n < = ( int ) idx_layer + n_extra_top_layers ; + + n ) {
2018-09-11 12:04:47 +00:00
if ( n < 0 | | n > = ( int ) m_layers . size ( ) )
2017-03-08 13:54:04 +00:00
continue ;
2018-09-11 12:04:47 +00:00
ExPolygons & expolys = m_layers [ n ] - > perimeter_expolygons ;
2017-03-08 13:54:04 +00:00
for ( size_t i = 0 ; i < expolys . size ( ) ; + + i ) {
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg " , debug_idx , n , i ) , get_extents ( expolys [ i ] ) ) ;
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 ) ) ;
}
2016-11-16 17:04:47 +00:00
}
2016-11-10 18:23:01 +00:00
}
2017-03-08 13:54:04 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2020-02-05 15:53:26 +00:00
polygons_append ( holes , cache_top_botom_regions [ idx_layer ] . holes ) ;
2020-02-18 14:44:01 +00:00
if ( int n_top_layers = region_config . top_solid_layers . value ; n_top_layers > 0 ) {
2020-02-05 15:53:26 +00:00
// Gather top regions projected to this layer.
2020-02-18 14:44:01 +00:00
coordf_t print_z = layer - > print_z ;
2020-02-05 15:53:26 +00:00
for ( int i = int ( idx_layer ) + 1 ;
2020-02-09 08:43:15 +00:00
i < int ( cache_top_botom_regions . size ( ) ) & &
2020-02-05 15:53:26 +00:00
( i < int ( idx_layer ) + n_top_layers | |
m_layers [ i ] - > print_z - print_z < region_config . top_solid_min_thickness - EPSILON ) ;
+ + i ) {
const DiscoverVerticalShellsCacheEntry & cache = cache_top_botom_regions [ i ] ;
if ( ! holes . empty ( ) )
holes = intersection ( holes , cache . holes ) ;
if ( ! cache . top_surfaces . empty ( ) ) {
polygons_append ( shell , cache . top_surfaces ) ;
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_ ( shell , false ) ;
}
}
}
2020-02-18 14:44:01 +00:00
if ( int n_bottom_layers = region_config . bottom_solid_layers . value ; n_bottom_layers > 0 ) {
2020-02-05 15:53:26 +00:00
// Gather bottom regions projected to this layer.
2020-02-18 14:44:01 +00:00
coordf_t bottom_z = layer - > bottom_z ( ) ;
2020-02-05 15:53:26 +00:00
for ( int i = int ( idx_layer ) - 1 ;
i > = 0 & &
( i > int ( idx_layer ) - n_bottom_layers | |
bottom_z - m_layers [ i ] - > bottom_z ( ) < region_config . bottom_solid_min_thickness - EPSILON ) ;
- - i ) {
const DiscoverVerticalShellsCacheEntry & cache = cache_top_botom_regions [ i ] ;
if ( ! holes . empty ( ) )
holes = intersection ( holes , cache . holes ) ;
if ( ! cache . bottom_surfaces . empty ( ) ) {
polygons_append ( shell , cache . bottom_surfaces ) ;
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_ ( shell , false ) ;
}
}
}
2016-09-26 11:56:24 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
{
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-perimeters-before-union-%d.svg " , debug_idx ) , get_extents ( shell ) ) ;
svg . draw ( shell ) ;
svg . draw_outline ( shell , " black " , scale_ ( 0.05 ) ) ;
svg . Close ( ) ;
}
2016-09-26 11:56:24 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-16 21:16:20 +00:00
#if 0
2017-03-08 13:54:04 +00:00
{
PROFILE_BLOCK ( discover_vertical_shells_region_layer_shell_ ) ;
// shell = union_(shell, true);
shell = union_ ( shell , false ) ;
}
2016-11-16 21:16:20 +00:00
# endif
2016-09-26 11:56:24 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
shell_ex = union_ex ( shell , true ) ;
2016-09-26 11:56:24 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 13:54:04 +00:00
}
2016-09-26 11:56:24 +00:00
2017-03-08 13:54:04 +00:00
//if (shell.empty())
// continue;
2016-10-22 20:25:00 +00:00
2016-09-26 11:56:24 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
{
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-perimeters-after-union-%d.svg " , debug_idx ) , get_extents ( shell ) ) ;
svg . draw ( shell_ex ) ;
svg . draw_outline ( shell_ex , " black " , " blue " , scale_ ( 0.05 ) ) ;
svg . Close ( ) ;
}
2016-09-26 11:56:24 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
{
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-internal-wshell-%d.svg " , debug_idx ) , get_extents ( shell ) ) ;
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 ( ) ;
}
{
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-internalvoid-wshell-%d.svg " , debug_idx ) , get_extents ( shell ) ) ;
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 ( ) ;
}
{
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-internalvoid-wshell-%d.svg " , debug_idx ) , get_extents ( shell ) ) ;
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 ( ) ;
}
2016-09-26 11:56:24 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 13:54:04 +00:00
// Trim the shells region by the internal & internal void surfaces.
const SurfaceType surfaceTypesInternal [ ] = { stInternal , stInternalVoid , stInternalSolid } ;
const Polygons polygonsInternal = to_polygons ( layerm - > fill_surfaces . filter_by_types ( surfaceTypesInternal , 3 ) ) ;
shell = intersection ( shell , polygonsInternal , true ) ;
polygons_append ( shell , diff ( polygonsInternal , holes ) ) ;
if ( shell . empty ( ) )
continue ;
2016-11-03 23:55:43 +00:00
2017-03-08 13:54:04 +00: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-10 18:23:01 +00:00
2017-03-08 13:54:04 +00: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.
2016-11-03 23:55:43 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
Polygons shell_before = shell ;
2016-11-03 23:55:43 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2016-11-10 18:23:01 +00:00
# if 1
2017-03-08 13:54:04 +00: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).
2018-12-11 15:33:43 +00:00
shell = offset ( offset_ex ( union_ex ( shell ) , - 0.5f * min_perimeter_infill_spacing ) , 0.8f * min_perimeter_infill_spacing , ClipperLib : : jtSquare ) ;
2017-03-08 13:54:04 +00:00
if ( shell . empty ( ) )
continue ;
2016-11-10 18:23:01 +00:00
# else
2017-03-08 13:54:04 +00:00
// 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.
Polygons too_narrow = diff ( shell , offset2 ( shell , - margin , margin , ClipperLib : : jtMiter , 5. ) , true ) ;
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 ) ) ;
}
2016-11-10 18:23:01 +00:00
# endif
2017-03-08 13:54:04 +00:00
ExPolygons new_internal_solid = intersection_ex ( polygonsInternal , shell , false ) ;
2016-11-03 23:55:43 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
{
Slic3r : : SVG svg ( debug_out_path ( " discover_vertical_shells-regularized-%d.svg " , debug_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 ( ) ;
}
2016-11-03 23:55:43 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 13:54:04 +00:00
// Trim the internal & internalvoid by the shell.
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
) ;
2016-09-26 11:56:24 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2017-03-08 13:54:04 +00:00
{
SVG : : export_expolygons ( debug_out_path ( " discover_vertical_shells-new_internal-%d.svg " , debug_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 " , debug_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 " , debug_idx ) , get_extents ( shell ) , new_internal_solid , " black " , " blue " , scale_ ( 0.05 ) ) ;
}
2016-09-26 11:56:24 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2017-03-08 13:54:04 +00:00
// Assign resulting internal surfaces to layer.
const SurfaceType surfaceTypesKeep [ ] = { stTop , stBottom , stBottomBridge } ;
layerm - > fill_surfaces . keep_types ( surfaceTypesKeep , sizeof ( surfaceTypesKeep ) / sizeof ( SurfaceType ) ) ;
layerm - > fill_surfaces . append ( new_internal , stInternal ) ;
layerm - > fill_surfaces . append ( new_internal_void , stInternalVoid ) ;
layerm - > fill_surfaces . append ( new_internal_solid , stInternalSolid ) ;
} // for each layer
2018-03-28 15:05:31 +00:00
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-08 13:54:04 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Discovering vertical shells for region " < < idx_region < < " in parallel - end " ;
2016-09-26 11:56:24 +00:00
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-09-11 12:04:47 +00:00
for ( size_t idx_layer = 0 ; idx_layer < m_layers . size ( ) ; + + idx_layer ) {
LayerRegion * layerm = m_layers [ idx_layer ] - > get_region ( idx_region ) ;
2017-01-03 09:51:19 +00:00
layerm - > export_region_slices_to_svg_debug ( " 4_discover_vertical_shells-final " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 4_discover_vertical_shells-final " ) ;
}
2016-09-26 11:56:24 +00:00
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each region
2016-11-16 21:16:20 +00:00
// Write the profiler measurements to file
2017-03-03 11:53:05 +00:00
// PROFILE_UPDATE();
// PROFILE_OUTPUT(debug_out_path("discover_vertical_shells-profile.txt").c_str());
2016-09-26 11:56:24 +00:00
}
2015-10-26 22:23:03 +00:00
/* This method applies bridge flow to the first internal solid layer above
sparse infill */
2017-08-02 12:24:32 +00:00
void PrintObject : : bridge_over_infill ( )
2014-12-24 09:20:55 +00:00
{
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Bridge over infill... " < < log_memory_info ( ) ;
2016-11-29 18:27:23 +00:00
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
const PrintRegion & region = * m_print - > regions ( ) [ region_id ] ;
2014-12-24 09:20:55 +00:00
2015-10-26 22:23:03 +00:00
// skip bridging in case there are no voids
2018-09-11 12:04:47 +00:00
if ( region . config ( ) . fill_density . value = = 100 ) continue ;
2014-12-24 09:20:55 +00:00
2015-06-13 17:48:46 +00:00
// get bridge flow
2018-09-11 12:04:47 +00:00
Flow bridge_flow = region . flow (
2015-06-13 17:48:46 +00:00
frSolidInfill ,
- 1 , // layer height, not relevant for bridge flow
true , // bridge
false , // first layer
- 1 , // custom width, not relevant for bridge flow
* this
) ;
2018-11-02 19:41:49 +00:00
for ( LayerPtrs : : iterator layer_it = m_layers . begin ( ) ; layer_it ! = m_layers . end ( ) ; + + layer_it ) {
2015-10-26 22:23:03 +00:00
// skip first layer
2018-11-02 19:41:49 +00:00
if ( layer_it = = m_layers . begin ( ) )
continue ;
2014-12-24 09:20:55 +00:00
Layer * layer = * layer_it ;
2018-09-11 12:04:47 +00:00
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2014-12-24 09:20:55 +00:00
2015-06-13 17:48:46 +00:00
// extract the stInternalSolid surfaces that might be transformed into bridges
Polygons internal_solid ;
2014-12-24 09:20:55 +00:00
layerm - > fill_surfaces . filter_by_type ( stInternalSolid , & internal_solid ) ;
2015-06-13 17:48:46 +00: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 09:20:55 +00:00
ExPolygons to_bridge ;
2015-06-13 17:48:46 +00:00
{
Polygons to_bridge_pp = internal_solid ;
// iterate through lower layers spanned by bridge_flow
double bottom_z = layer - > print_z - bridge_flow . height ;
2018-09-11 12:04:47 +00:00
for ( int i = int ( layer_it - m_layers . begin ( ) ) - 1 ; i > = 0 ; - - i ) {
const Layer * lower_layer = m_layers [ i ] ;
2015-06-13 17:48:46 +00: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 ;
2018-11-02 19:41:49 +00:00
for ( LayerRegion * lower_layerm : lower_layer - > m_regions )
lower_layerm - > fill_surfaces . filter_by_type ( stInternal , & lower_internal ) ;
2015-06-13 17:48:46 +00:00
// intersect such lower internal surfaces with the candidate solid surfaces
2015-10-26 22:23:03 +00:00
to_bridge_pp = intersection ( to_bridge_pp , lower_internal ) ;
2015-06-13 17:48:46 +00:00
}
// there's no point in bridging too thin/short regions
2016-09-26 11:56:24 +00: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 17:48:46 +00:00
{
2017-08-02 12:24:32 +00:00
float min_width = float ( bridge_flow . scaled_width ( ) ) * 3.f ;
2015-10-26 22:23:03 +00:00
to_bridge_pp = offset2 ( to_bridge_pp , - min_width , + min_width ) ;
2015-06-13 17:48:46 +00:00
}
if ( to_bridge_pp . empty ( ) ) continue ;
// convert into ExPolygons
2015-10-26 22:23:03 +00:00
to_bridge = union_ex ( to_bridge_pp ) ;
2015-06-13 17:48:46 +00:00
}
2014-12-24 09:20:55 +00:00
# ifdef SLIC3R_DEBUG
2020-06-16 11:13:51 +00:00
printf ( " Bridging %zu internal areas at layer %zu \n " , to_bridge . size ( ) , layer - > id ( ) ) ;
2014-12-24 09:20:55 +00:00
# endif
2015-06-13 17:48:46 +00:00
// compute the remaning internal solid surfaces as difference
2016-12-13 18:22:23 +00:00
ExPolygons not_to_bridge = diff_ex ( internal_solid , to_polygons ( to_bridge ) , true ) ;
2016-09-26 11:56:24 +00:00
to_bridge = intersection_ex ( to_polygons ( to_bridge ) , internal_solid , true ) ;
2014-12-24 09:20:55 +00:00
// build the new collection of fill_surfaces
2017-03-13 15:03:44 +00:00
layerm - > fill_surfaces . remove_type ( stInternalSolid ) ;
for ( ExPolygon & ex : to_bridge )
layerm - > fill_surfaces . surfaces . push_back ( Surface ( stInternalBridge , ex ) ) ;
for ( ExPolygon & ex : not_to_bridge )
layerm - > fill_surfaces . surfaces . push_back ( Surface ( stInternalSolid , ex ) ) ;
2014-12-24 09:20:55 +00: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 = > $ _ ,
2019-09-09 14:47:15 +00:00
surface_type = > stInternalVoid ,
2014-12-24 09:20:55 +00:00
) , @ { 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 11:56:24 +00: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 */
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2014-12-24 09:20:55 +00:00
}
}
}
2019-01-21 09:06:51 +00:00
static void clamp_exturder_to_default ( ConfigOptionInt & opt , size_t num_extruders )
{
if ( opt . value > ( int ) num_extruders )
// assign the default extruder
opt . value = 1 ;
}
PrintObjectConfig PrintObject : : object_config_from_model_object ( const PrintObjectConfig & default_object_config , const ModelObject & object , size_t num_extruders )
{
PrintObjectConfig config = default_object_config ;
2020-09-24 17:03:09 +00:00
{
DynamicPrintConfig src_normalized ( object . config . get ( ) ) ;
src_normalized . normalize_fdm ( ) ;
config . apply ( src_normalized , true ) ;
}
2019-01-21 09:06:51 +00:00
// Clamp invalid extruders to the default extruder (with index 1).
clamp_exturder_to_default ( config . support_material_extruder , num_extruders ) ;
clamp_exturder_to_default ( config . support_material_interface_extruder , num_extruders ) ;
return config ;
}
2020-09-24 17:03:09 +00:00
static void apply_to_print_region_config ( PrintRegionConfig & out , const DynamicPrintConfig & in )
{
// 1) Copy the "extruder key to infill_extruder and perimeter_extruder.
std : : string sextruder = " extruder " ;
auto * opt_extruder = in . opt < ConfigOptionInt > ( sextruder ) ;
if ( opt_extruder ) {
2020-09-24 18:32:52 +00:00
int extruder = opt_extruder - > value ;
if ( extruder ! = 0 ) {
out . infill_extruder . value = extruder ;
out . solid_infill_extruder . value = extruder ;
out . perimeter_extruder . value = extruder ;
2020-09-24 17:03:09 +00:00
}
}
// 2) Copy the rest of the values.
for ( auto it = in . cbegin ( ) ; it ! = in . cend ( ) ; + + it )
if ( it - > first ! = sextruder ) {
ConfigOption * my_opt = out . option ( it - > first , false ) ;
if ( my_opt )
my_opt - > set ( it - > second . get ( ) ) ;
}
}
2019-06-20 14:15:09 +00:00
PrintRegionConfig PrintObject : : region_config_from_model_volume ( const PrintRegionConfig & default_region_config , const DynamicPrintConfig * layer_range_config , const ModelVolume & volume , size_t num_extruders )
2019-01-21 09:06:51 +00:00
{
PrintRegionConfig config = default_region_config ;
2020-09-24 17:03:09 +00:00
apply_to_print_region_config ( config , volume . get_object ( ) - > config . get ( ) ) ;
2019-06-20 14:15:09 +00:00
if ( layer_range_config ! = nullptr )
2020-09-24 17:03:09 +00:00
apply_to_print_region_config ( config , * layer_range_config ) ;
apply_to_print_region_config ( config , volume . config . get ( ) ) ;
2019-01-21 09:06:51 +00:00
if ( ! volume . material_id ( ) . empty ( ) )
2020-09-24 17:03:09 +00:00
apply_to_print_region_config ( config , volume . material ( ) - > config . get ( ) ) ;
2019-01-21 09:06:51 +00:00
// Clamp invalid extruders to the default extruder (with index 1).
clamp_exturder_to_default ( config . infill_extruder , num_extruders ) ;
clamp_exturder_to_default ( config . perimeter_extruder , num_extruders ) ;
clamp_exturder_to_default ( config . solid_infill_extruder , num_extruders ) ;
return config ;
}
2019-03-05 13:05:58 +00:00
void PrintObject : : update_slicing_parameters ( )
2019-03-04 14:28:04 +00:00
{
2019-03-05 13:05:58 +00:00
if ( ! m_slicing_params . valid )
m_slicing_params = SlicingParameters : : create_from_config (
2020-02-07 13:10:18 +00:00
this - > print ( ) - > config ( ) , m_config , unscale < double > ( this - > height ( ) ) , this - > object_extruders ( ) ) ;
2019-03-04 14:28:04 +00:00
}
2019-06-20 14:15:09 +00:00
SlicingParameters PrintObject : : slicing_parameters ( const DynamicPrintConfig & full_config , const ModelObject & model_object , float object_max_z )
2019-01-21 09:06:51 +00:00
{
2019-06-20 14:15:09 +00:00
PrintConfig print_config ;
PrintObjectConfig object_config ;
PrintRegionConfig default_region_config ;
print_config . apply ( full_config , true ) ;
object_config . apply ( full_config , true ) ;
default_region_config . apply ( full_config , true ) ;
size_t num_extruders = print_config . nozzle_diameter . size ( ) ;
object_config = object_config_from_model_object ( object_config , model_object , num_extruders ) ;
std : : vector < unsigned int > object_extruders ;
for ( const ModelVolume * model_volume : model_object . volumes )
if ( model_volume - > is_model_part ( ) ) {
PrintRegion : : collect_object_printing_extruders (
print_config ,
region_config_from_model_volume ( default_region_config , nullptr , * model_volume , num_extruders ) ,
object_extruders ) ;
2020-09-24 13:34:13 +00:00
for ( const std : : pair < const t_layer_height_range , ModelConfig > & range_and_config : model_object . layer_config_ranges )
2019-06-20 14:15:09 +00:00
if ( range_and_config . second . has ( " perimeter_extruder " ) | |
range_and_config . second . has ( " infill_extruder " ) | |
range_and_config . second . has ( " solid_infill_extruder " ) )
PrintRegion : : collect_object_printing_extruders (
print_config ,
2020-09-24 13:34:13 +00:00
region_config_from_model_volume ( default_region_config , & range_and_config . second . get ( ) , * model_volume , num_extruders ) ,
2019-06-20 14:15:09 +00:00
object_extruders ) ;
}
2019-01-21 09:06:51 +00:00
sort_remove_duplicates ( object_extruders ) ;
2019-04-13 12:15:54 +00:00
if ( object_max_z < = 0.f )
2019-06-20 14:15:09 +00:00
object_max_z = ( float ) model_object . raw_bounding_box ( ) . size ( ) . z ( ) ;
2019-09-24 14:01:01 +00:00
return SlicingParameters : : create_from_config ( print_config , object_config , object_max_z , object_extruders ) ;
2019-01-21 09:06:51 +00:00
}
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
std : : vector < unsigned int > PrintObject : : object_extruders ( ) const
{
std : : vector < unsigned int > extruders ;
extruders . reserve ( this - > region_volumes . size ( ) * 3 ) ;
for ( size_t idx_region = 0 ; idx_region < this - > region_volumes . size ( ) ; + + idx_region )
if ( ! this - > region_volumes [ idx_region ] . empty ( ) )
m_print - > get_region ( idx_region ) - > collect_object_printing_extruders ( extruders ) ;
sort_remove_duplicates ( extruders ) ;
return extruders ;
}
bool PrintObject : : update_layer_height_profile ( const ModelObject & model_object , const SlicingParameters & slicing_parameters , std : : vector < coordf_t > & layer_height_profile )
2016-12-12 16:53:38 +00:00
{
2017-02-07 17:17:12 +00:00
bool updated = false ;
2017-02-09 13:56:13 +00:00
2018-12-11 15:33:43 +00:00
if ( layer_height_profile . empty ( ) ) {
2020-07-13 11:16:18 +00:00
// use the constructor because the assignement is crashing on ASAN OsX
2020-09-24 13:34:13 +00:00
layer_height_profile = std : : vector < coordf_t > ( model_object . layer_height_profile . get ( ) ) ;
2020-07-13 11:16:18 +00:00
// layer_height_profile = model_object.layer_height_profile;
2017-02-09 13:56:13 +00:00
updated = true ;
}
// Verify the layer_height_profile.
if ( ! layer_height_profile . empty ( ) & &
// Must not be of even length.
( ( layer_height_profile . size ( ) & 1 ) ! = 0 | |
// Last entry must be at the top of the object.
2019-01-21 09:06:51 +00:00
std : : abs ( layer_height_profile [ layer_height_profile . size ( ) - 2 ] - slicing_parameters . object_print_z_height ( ) ) > 1e-3 ) )
2017-02-09 13:56:13 +00:00
layer_height_profile . clear ( ) ;
if ( layer_height_profile . empty ( ) ) {
2019-11-12 13:18:43 +00:00
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
2019-11-07 14:55:45 +00:00
layer_height_profile = layer_height_profile_from_ranges ( slicing_parameters , model_object . layer_config_ranges ) ;
updated = true ;
2016-12-12 16:53:38 +00:00
}
2017-02-07 17:17:12 +00:00
return updated ;
2016-12-12 16:53:38 +00:00
}
// 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
2019-01-23 13:00:03 +00:00
void PrintObject : : _slice ( const std : : vector < coordf_t > & layer_height_profile )
2016-12-12 16:53:38 +00:00
{
2018-12-18 10:31:41 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Slicing objects... " < < log_memory_info ( ) ;
2017-03-02 15:52:24 +00:00
2020-02-07 13:10:18 +00:00
m_typed_slices = false ;
2017-08-02 12:24:32 +00:00
2016-12-12 16:53:38 +00:00
// 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.
2019-03-04 14:28:04 +00:00
std : : vector < coordf_t > object_layers = generate_object_layers ( m_slicing_params , layer_height_profile ) ;
2016-12-12 16:53:38 +00:00
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
2019-03-04 14:28:04 +00:00
int id = int ( m_slicing_params . raft_layers ( ) ) ;
2016-12-12 16:53:38 +00:00
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 ) ;
2019-03-04 14:28:04 +00:00
Layer * layer = this - > add_layer ( id + + , hi - lo , hi + m_slicing_params . object_print_z_min , slice_z ) ;
2016-12-12 16:53:38 +00:00
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.
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id )
2018-09-11 12:04:47 +00:00
layer - > add_region ( this - > print ( ) - > regions ( ) [ region_id ] ) ;
2016-12-12 16:53:38 +00:00
prev = layer ;
}
}
2019-03-06 09:21:10 +00:00
// Count model parts and modifier meshes, check whether the model parts are of the same region.
2019-06-20 14:15:09 +00:00
int all_volumes_single_region = - 2 ; // not set yet
bool has_z_ranges = false ;
2019-03-06 09:21:10 +00:00
size_t num_volumes = 0 ;
size_t num_modifiers = 0 ;
for ( int region_id = 0 ; region_id < ( int ) this - > region_volumes . size ( ) ; + + region_id ) {
2019-06-20 14:15:09 +00:00
int last_volume_id = - 1 ;
for ( const std : : pair < t_layer_height_range , int > & volume_and_range : this - > region_volumes [ region_id ] ) {
const int volume_id = volume_and_range . second ;
2019-03-06 09:21:10 +00:00
const ModelVolume * model_volume = this - > model_object ( ) - > volumes [ volume_id ] ;
if ( model_volume - > is_model_part ( ) ) {
2019-06-20 14:15:09 +00:00
if ( last_volume_id = = volume_id ) {
has_z_ranges = true ;
} else {
last_volume_id = volume_id ;
if ( all_volumes_single_region = = - 2 )
// first model volume met
all_volumes_single_region = region_id ;
else if ( all_volumes_single_region ! = region_id )
// multiple volumes met and they are not equal
all_volumes_single_region = - 1 ;
+ + num_volumes ;
}
2019-03-06 09:21:10 +00:00
} else if ( model_volume - > is_modifier ( ) )
+ + num_modifiers ;
}
}
assert ( num_volumes > 0 ) ;
2016-12-12 16:53:38 +00:00
2017-03-08 14:58:40 +00:00
// Slice all non-modifier volumes.
2019-03-06 09:21:10 +00:00
bool clipped = false ;
bool upscaled = false ;
2020-12-09 13:07:22 +00:00
bool spiral_vase = this - > print ( ) - > config ( ) . spiral_vase ;
auto slicing_mode = spiral_vase ? SlicingMode : : PositiveLargestContour : SlicingMode : : Regular ;
2019-06-20 14:15:09 +00:00
if ( ! has_z_ranges & & ( ! m_config . clip_multipart_objects . value | | all_volumes_single_region > = 0 ) ) {
2019-03-06 09:21:10 +00:00
// Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - region " < < region_id ;
// slicing in parallel
2020-12-09 13:07:22 +00:00
size_t slicing_mode_normal_below_layer = 0 ;
if ( spiral_vase ) {
// Slice the bottom layers with SlicingMode::Regular.
// This needs to be in sync with LayerRegion::make_perimeters() spiral_vase!
const PrintRegionConfig & config = this - > print ( ) - > regions ( ) [ region_id ] - > config ( ) ;
slicing_mode_normal_below_layer = size_t ( config . bottom_solid_layers . value ) ;
for ( ; slicing_mode_normal_below_layer < slice_zs . size ( ) & & slice_zs [ slicing_mode_normal_below_layer ] < config . bottom_solid_min_thickness - EPSILON ;
+ + slicing_mode_normal_below_layer ) ;
}
std : : vector < ExPolygons > expolygons_by_layer = this - > slice_region ( region_id , slice_zs , slicing_mode , slicing_mode_normal_below_layer , SlicingMode : : Regular ) ;
2019-03-06 09:21:10 +00:00
m_print - > throw_if_canceled ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - append slices " < < region_id < < " start " ;
for ( size_t layer_id = 0 ; layer_id < expolygons_by_layer . size ( ) ; + + layer_id )
m_layers [ layer_id ] - > regions ( ) [ region_id ] - > slices . append ( std : : move ( expolygons_by_layer [ layer_id ] ) , stInternal ) ;
m_print - > throw_if_canceled ( ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - append slices " < < region_id < < " end " ;
}
} else {
// Expensive path: Slice one volume after the other in the order they are presented at the user interface,
// clip the last volumes with the first.
// First slice the volumes.
struct SlicedVolume {
SlicedVolume ( int volume_id , int region_id , std : : vector < ExPolygons > & & expolygons_by_layer ) :
volume_id ( volume_id ) , region_id ( region_id ) , expolygons_by_layer ( std : : move ( expolygons_by_layer ) ) { }
int volume_id ;
int region_id ;
std : : vector < ExPolygons > expolygons_by_layer ;
} ;
std : : vector < SlicedVolume > sliced_volumes ;
sliced_volumes . reserve ( num_volumes ) ;
2019-06-20 14:15:09 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
const std : : vector < std : : pair < t_layer_height_range , int > > & volumes_and_ranges = this - > region_volumes [ region_id ] ;
for ( size_t i = 0 ; i < volumes_and_ranges . size ( ) ; ) {
int volume_id = volumes_and_ranges [ i ] . second ;
2019-03-06 09:21:10 +00:00
const ModelVolume * model_volume = this - > model_object ( ) - > volumes [ volume_id ] ;
if ( model_volume - > is_model_part ( ) ) {
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - volume " < < volume_id ;
2019-06-20 14:15:09 +00:00
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std : : vector < t_layer_height_range > ranges ;
ranges . emplace_back ( volumes_and_ranges [ i ] . first ) ;
size_t j = i + 1 ;
for ( ; j < volumes_and_ranges . size ( ) & & volume_id = = volumes_and_ranges [ j ] . second ; + + j )
if ( ! ranges . empty ( ) & & std : : abs ( ranges . back ( ) . second - volumes_and_ranges [ j ] . first . first ) < EPSILON )
ranges . back ( ) . second = volumes_and_ranges [ j ] . first . second ;
else
ranges . emplace_back ( volumes_and_ranges [ j ] . first ) ;
2019-03-06 09:21:10 +00:00
// slicing in parallel
2020-02-08 20:36:29 +00:00
sliced_volumes . emplace_back ( volume_id , ( int ) region_id , this - > slice_volume ( slice_zs , ranges , slicing_mode , * model_volume ) ) ;
2019-06-20 14:15:09 +00:00
i = j ;
} else
+ + i ;
2019-03-06 09:21:10 +00:00
}
2019-06-20 14:15:09 +00:00
}
2019-03-06 09:21:10 +00:00
// Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - parallel clipping - start " ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , slice_zs . size ( ) ) ,
[ this , & sliced_volumes , num_modifiers ] ( const tbb : : blocked_range < size_t > & range ) {
float delta = float ( scale_ ( m_config . xy_size_compensation . value ) ) ;
// Only upscale together with clipping if there are no modifiers, as the modifiers shall be applied before upscaling
// (upscaling may grow the object outside of the modifier mesh).
bool upscale = delta > 0 & & num_modifiers = = 0 ;
for ( size_t layer_id = range . begin ( ) ; layer_id < range . end ( ) ; + + layer_id ) {
m_print - > throw_if_canceled ( ) ;
// Trim volumes in a single layer, one by the other, possibly apply upscaling.
{
Polygons processed ;
2019-10-01 11:41:22 +00:00
for ( SlicedVolume & sliced_volume : sliced_volumes )
if ( ! sliced_volume . expolygons_by_layer . empty ( ) ) {
ExPolygons slices = std : : move ( sliced_volume . expolygons_by_layer [ layer_id ] ) ;
if ( upscale )
slices = offset_ex ( std : : move ( slices ) , delta ) ;
if ( ! processed . empty ( ) )
// Trim by the slices of already processed regions.
slices = diff_ex ( to_polygons ( std : : move ( slices ) ) , processed ) ;
if ( size_t ( & sliced_volume - & sliced_volumes . front ( ) ) + 1 < sliced_volumes . size ( ) )
// Collect the already processed regions to trim the to be processed regions.
polygons_append ( processed , slices ) ;
sliced_volume . expolygons_by_layer [ layer_id ] = std : : move ( slices ) ;
}
2019-03-06 09:21:10 +00:00
}
// Collect and union volumes of a single region.
for ( int region_id = 0 ; region_id < ( int ) this - > region_volumes . size ( ) ; + + region_id ) {
ExPolygons expolygons ;
size_t num_volumes = 0 ;
for ( SlicedVolume & sliced_volume : sliced_volumes )
2019-10-01 11:41:22 +00:00
if ( sliced_volume . region_id = = region_id & & ! sliced_volume . expolygons_by_layer . empty ( ) & & ! sliced_volume . expolygons_by_layer [ layer_id ] . empty ( ) ) {
2019-03-06 09:21:10 +00:00
+ + num_volumes ;
append ( expolygons , std : : move ( sliced_volume . expolygons_by_layer [ layer_id ] ) ) ;
}
if ( num_volumes > 1 )
// Merge the islands using a positive / negative offset.
expolygons = offset_ex ( offset_ex ( expolygons , float ( scale_ ( EPSILON ) ) ) , - float ( scale_ ( EPSILON ) ) ) ;
m_layers [ layer_id ] - > regions ( ) [ region_id ] - > slices . append ( std : : move ( expolygons ) , stInternal ) ;
}
}
} ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - parallel clipping - end " ;
clipped = true ;
upscaled = m_config . xy_size_compensation . value > 0 & & num_modifiers = = 0 ;
2017-03-08 14:58:40 +00:00
}
// Slice all modifier volumes.
2018-11-06 14:31:26 +00:00
if ( this - > region_volumes . size ( ) > 1 ) {
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2017-03-08 14:58:40 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing modifier volumes - region " < < region_id ;
2019-03-06 09:21:10 +00:00
// slicing in parallel
2019-06-20 14:15:09 +00:00
std : : vector < ExPolygons > expolygons_by_layer = this - > slice_modifiers ( region_id , slice_zs ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2019-03-06 09:21:10 +00:00
if ( expolygons_by_layer . empty ( ) )
continue ;
2016-12-12 16:53:38 +00:00
// loop through the other regions and 'steal' the slices belonging to this one
2017-03-08 14:58:40 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing modifier volumes - stealing " < < region_id < < " start " ;
2019-03-06 09:21:10 +00:00
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
[ this , & expolygons_by_layer , region_id ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_id = range . begin ( ) ; layer_id < range . end ( ) ; + + layer_id ) {
for ( size_t other_region_id = 0 ; other_region_id < this - > region_volumes . size ( ) ; + + other_region_id ) {
if ( region_id = = other_region_id )
continue ;
Layer * layer = m_layers [ layer_id ] ;
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
LayerRegion * other_layerm = layer - > m_regions [ other_region_id ] ;
2019-06-20 14:15:09 +00:00
if ( layerm = = nullptr | | other_layerm = = nullptr | | other_layerm - > slices . empty ( ) | | expolygons_by_layer [ layer_id ] . empty ( ) )
2019-03-06 09:21:10 +00:00
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 , to_polygons ( my_parts ) ) , stInternal ) ;
// Append new parts to our region.
layerm - > slices . append ( std : : move ( my_parts ) , stInternal ) ;
}
}
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-08 14:58:40 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing modifier volumes - stealing " < < region_id < < " end " ;
2016-12-12 16:53:38 +00:00
}
}
2017-03-08 12:43:49 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - removing top empty layers " ;
2018-09-11 12:04:47 +00:00
while ( ! m_layers . empty ( ) ) {
const Layer * layer = m_layers . back ( ) ;
2018-12-14 16:17:51 +00:00
if ( ! layer - > empty ( ) )
goto end ;
2017-05-30 16:33:17 +00:00
delete layer ;
2018-09-11 12:04:47 +00:00
m_layers . pop_back ( ) ;
if ( ! m_layers . empty ( ) )
m_layers . back ( ) - > upper_layer = nullptr ;
2016-12-12 16:53:38 +00:00
}
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2016-12-12 16:53:38 +00:00
end :
;
2017-03-08 10:56:42 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - make_slices in parallel - begin " ;
2020-01-03 13:05:56 +00:00
{
// Compensation value, scaled.
const float xy_compensation_scaled = float ( scale_ ( m_config . xy_size_compensation . value ) ) ;
const float elephant_foot_compensation_scaled = ( m_config . raft_layers = = 0 ) ?
// Only enable Elephant foot compensation if printing directly on the print bed.
float ( scale_ ( m_config . elefant_foot_compensation . value ) ) :
0.f ;
// Uncompensated slices for the first layer in case the Elephant foot compensation is applied.
ExPolygons lslices_1st_layer ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
[ this , upscaled , clipped , xy_compensation_scaled , elephant_foot_compensation_scaled , & lslices_1st_layer ]
( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_id = range . begin ( ) ; layer_id < range . end ( ) ; + + layer_id ) {
m_print - > throw_if_canceled ( ) ;
Layer * layer = m_layers [ layer_id ] ;
// Apply size compensation and perform clipping of multi-part objects.
float elfoot = ( layer_id = = 0 ) ? elephant_foot_compensation_scaled : 0.f ;
if ( layer - > m_regions . size ( ) = = 1 ) {
assert ( ! upscaled ) ;
assert ( ! clipped ) ;
// Optimized version for a single region layer.
// Single region, growing or shrinking.
LayerRegion * layerm = layer - > m_regions . front ( ) ;
if ( elfoot > 0 ) {
// Apply the elephant foot compensation and store the 1st layer slices without the Elephant foot compensation applied.
lslices_1st_layer = to_expolygons ( std : : move ( layerm - > slices . surfaces ) ) ;
float delta = xy_compensation_scaled ;
if ( delta > elfoot ) {
delta - = elfoot ;
elfoot = 0.f ;
} else if ( delta > 0 )
elfoot - = delta ;
layerm - > slices . set (
union_ex (
Slic3r : : elephant_foot_compensation (
( delta = = 0.f ) ? lslices_1st_layer : offset_ex ( lslices_1st_layer , delta ) ,
layerm - > flow ( frExternalPerimeter ) , unscale < double > ( elfoot ) ) ) ,
stInternal ) ;
if ( xy_compensation_scaled ! = 0.f )
lslices_1st_layer = offset_ex ( std : : move ( lslices_1st_layer ) , xy_compensation_scaled ) ;
} else if ( xy_compensation_scaled ! = 0.f ) {
// Apply the XY compensation.
layerm - > slices . set (
offset_ex ( to_expolygons ( std : : move ( layerm - > slices . surfaces ) ) , xy_compensation_scaled ) ,
stInternal ) ;
}
} else {
bool upscale = ! upscaled & & xy_compensation_scaled > 0.f ;
bool clip = ! clipped & & m_config . clip_multipart_objects . value ;
if ( upscale | | clip ) {
// Multiple regions, growing or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions.
Polygons processed ;
for ( size_t region_id = 0 ; region_id < layer - > m_regions . size ( ) ; + + region_id ) {
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
ExPolygons slices = to_expolygons ( std : : move ( layerm - > slices . surfaces ) ) ;
if ( upscale )
slices = offset_ex ( std : : move ( slices ) , xy_compensation_scaled ) ;
if ( region_id > 0 & & clip )
// Trim by the slices of already processed regions.
slices = diff_ex ( to_polygons ( std : : move ( slices ) ) , processed ) ;
if ( clip & & ( region_id + 1 < layer - > m_regions . size ( ) ) )
// Collect the already processed regions to trim the to be processed regions.
polygons_append ( processed , slices ) ;
layerm - > slices . set ( std : : move ( slices ) , stInternal ) ;
}
}
if ( xy_compensation_scaled < 0.f | | elfoot > 0.f ) {
// Apply the negative XY compensation.
Polygons trimming ;
static const float eps = float ( scale_ ( m_config . slice_closing_radius . value ) * 1.5 ) ;
if ( elfoot > 0.f ) {
lslices_1st_layer = offset_ex ( layer - > merged ( eps ) , std : : min ( xy_compensation_scaled , 0.f ) - eps ) ;
trimming = to_polygons ( Slic3r : : elephant_foot_compensation ( lslices_1st_layer ,
layer - > m_regions . front ( ) - > flow ( frExternalPerimeter ) , unscale < double > ( elfoot ) ) ) ;
} else
trimming = offset ( layer - > merged ( float ( SCALED_EPSILON ) ) , xy_compensation_scaled - float ( SCALED_EPSILON ) ) ;
for ( size_t region_id = 0 ; region_id < layer - > m_regions . size ( ) ; + + region_id )
layer - > m_regions [ region_id ] - > trim_surfaces ( trimming ) ;
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
layer - > make_slices ( ) ;
}
} ) ;
if ( elephant_foot_compensation_scaled > 0.f ) {
// The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value.
// Store the uncompensated value there.
assert ( ! m_layers . empty ( ) ) ;
assert ( m_layers . front ( ) - > id ( ) = = 0 ) ;
m_layers . front ( ) - > lslices = std : : move ( lslices_1st_layer ) ;
}
}
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-08 10:56:42 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - make_slices in parallel - end " ;
2016-12-12 16:53:38 +00:00
}
2019-06-20 14:15:09 +00:00
// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
2020-12-09 13:07:22 +00:00
std : : vector < ExPolygons > PrintObject : : slice_region ( size_t region_id , const std : : vector < float > & z , SlicingMode mode , size_t slicing_mode_normal_below_layer , SlicingMode mode_below ) const
2016-12-12 16:53:38 +00:00
{
2019-06-20 14:15:09 +00:00
std : : vector < const ModelVolume * > volumes ;
2017-05-31 10:55:59 +00:00
if ( region_id < this - > region_volumes . size ( ) ) {
2019-06-20 14:15:09 +00:00
for ( const std : : pair < t_layer_height_range , int > & volume_and_range : this - > region_volumes [ region_id ] ) {
const ModelVolume * volume = this - > model_object ( ) - > volumes [ volume_and_range . second ] ;
if ( volume - > is_model_part ( ) )
volumes . emplace_back ( volume ) ;
}
2018-09-17 13:12:13 +00:00
}
2020-12-09 13:07:22 +00:00
return this - > slice_volumes ( z , mode , slicing_mode_normal_below_layer , mode_below , volumes ) ;
2018-09-17 13:12:13 +00:00
}
2020-12-09 06:28:18 +00:00
// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_and_range at most once.
2019-06-20 14:15:09 +00:00
std : : vector < ExPolygons > PrintObject : : slice_modifiers ( size_t region_id , const std : : vector < float > & slice_zs ) const
2018-09-17 13:12:13 +00:00
{
2019-06-20 14:15:09 +00:00
std : : vector < ExPolygons > out ;
if ( region_id < this - > region_volumes . size ( ) )
{
std : : vector < std : : vector < t_layer_height_range > > volume_ranges ;
const std : : vector < std : : pair < t_layer_height_range , int > > & volumes_and_ranges = this - > region_volumes [ region_id ] ;
volume_ranges . reserve ( volumes_and_ranges . size ( ) ) ;
for ( size_t i = 0 ; i < volumes_and_ranges . size ( ) ; ) {
int volume_id = volumes_and_ranges [ i ] . second ;
const ModelVolume * model_volume = this - > model_object ( ) - > volumes [ volume_id ] ;
if ( model_volume - > is_modifier ( ) ) {
std : : vector < t_layer_height_range > ranges ;
ranges . emplace_back ( volumes_and_ranges [ i ] . first ) ;
size_t j = i + 1 ;
for ( ; j < volumes_and_ranges . size ( ) & & volume_id = = volumes_and_ranges [ j ] . second ; + + j ) {
if ( ! ranges . empty ( ) & & std : : abs ( ranges . back ( ) . second - volumes_and_ranges [ j ] . first . first ) < EPSILON )
ranges . back ( ) . second = volumes_and_ranges [ j ] . first . second ;
else
ranges . emplace_back ( volumes_and_ranges [ j ] . first ) ;
}
volume_ranges . emplace_back ( std : : move ( ranges ) ) ;
i = j ;
} else
+ + i ;
}
if ( ! volume_ranges . empty ( ) )
{
bool equal_ranges = true ;
for ( size_t i = 1 ; i < volume_ranges . size ( ) ; + + i ) {
assert ( ! volume_ranges [ i ] . empty ( ) ) ;
if ( volume_ranges . front ( ) ! = volume_ranges [ i ] ) {
equal_ranges = false ;
break ;
}
}
if ( equal_ranges & & volume_ranges . front ( ) . size ( ) = = 1 & & volume_ranges . front ( ) . front ( ) = = t_layer_height_range ( 0 , DBL_MAX ) ) {
// No modifier in this region was split to layer spans.
std : : vector < const ModelVolume * > volumes ;
for ( const std : : pair < t_layer_height_range , int > & volume_and_range : this - > region_volumes [ region_id ] ) {
const ModelVolume * volume = this - > model_object ( ) - > volumes [ volume_and_range . second ] ;
if ( volume - > is_modifier ( ) )
volumes . emplace_back ( volume ) ;
}
2020-02-08 20:36:29 +00:00
out = this - > slice_volumes ( slice_zs , SlicingMode : : Regular , volumes ) ;
2019-06-20 14:15:09 +00:00
} else {
// Some modifier in this region was split to layer spans.
std : : vector < char > merge ;
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
const std : : vector < std : : pair < t_layer_height_range , int > > & volumes_and_ranges = this - > region_volumes [ region_id ] ;
for ( size_t i = 0 ; i < volumes_and_ranges . size ( ) ; ) {
int volume_id = volumes_and_ranges [ i ] . second ;
const ModelVolume * model_volume = this - > model_object ( ) - > volumes [ volume_id ] ;
if ( model_volume - > is_modifier ( ) ) {
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing modifiers - volume " < < volume_id ;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std : : vector < t_layer_height_range > ranges ;
ranges . emplace_back ( volumes_and_ranges [ i ] . first ) ;
size_t j = i + 1 ;
for ( ; j < volumes_and_ranges . size ( ) & & volume_id = = volumes_and_ranges [ j ] . second ; + + j )
ranges . emplace_back ( volumes_and_ranges [ j ] . first ) ;
// slicing in parallel
2020-02-08 20:36:29 +00:00
std : : vector < ExPolygons > this_slices = this - > slice_volume ( slice_zs , ranges , SlicingMode : : Regular , * model_volume ) ;
2020-12-09 06:28:18 +00:00
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
2019-06-20 14:15:09 +00:00
if ( out . empty ( ) ) {
out = std : : move ( this_slices ) ;
merge . assign ( out . size ( ) , false ) ;
2020-12-09 06:28:18 +00:00
} else if ( ! this_slices . empty ( ) ) {
assert ( out . size ( ) = = this_slices . size ( ) ) ;
2019-06-20 14:15:09 +00:00
for ( size_t i = 0 ; i < out . size ( ) ; + + i )
2019-08-29 11:17:10 +00:00
if ( ! this_slices [ i ] . empty ( ) ) {
2019-06-20 14:15:09 +00:00
if ( ! out [ i ] . empty ( ) ) {
append ( out [ i ] , this_slices [ i ] ) ;
merge [ i ] = true ;
} else
out [ i ] = std : : move ( this_slices [ i ] ) ;
2019-08-29 11:17:10 +00:00
}
2019-06-20 14:15:09 +00:00
}
i = j ;
} else
+ + i ;
}
}
for ( size_t i = 0 ; i < merge . size ( ) ; + + i )
if ( merge [ i ] )
out [ i ] = union_ex ( out [ i ] ) ;
}
}
}
return out ;
2018-09-17 13:12:13 +00:00
}
2019-06-20 14:15:09 +00:00
std : : vector < ExPolygons > PrintObject : : slice_support_volumes ( const ModelVolumeType & model_volume_type ) const
2018-09-17 13:12:13 +00:00
{
std : : vector < const ModelVolume * > volumes ;
for ( const ModelVolume * volume : this - > model_object ( ) - > volumes )
2019-06-20 14:15:09 +00:00
if ( volume - > type ( ) = = model_volume_type )
2018-09-17 13:12:13 +00:00
volumes . emplace_back ( volume ) ;
std : : vector < float > zs ;
zs . reserve ( this - > layers ( ) . size ( ) ) ;
for ( const Layer * l : this - > layers ( ) )
2018-09-20 14:48:13 +00:00
zs . emplace_back ( ( float ) l - > slice_z ) ;
2020-02-08 20:36:29 +00:00
return this - > slice_volumes ( zs , SlicingMode : : Regular , volumes ) ;
2018-09-17 13:12:13 +00:00
}
2020-12-09 13:07:22 +00:00
std : : vector < ExPolygons > PrintObject : : slice_volumes (
const std : : vector < float > & z ,
SlicingMode mode , size_t slicing_mode_normal_below_layer , SlicingMode mode_below ,
const std : : vector < const ModelVolume * > & volumes ) const
2018-09-17 13:12:13 +00:00
{
std : : vector < ExPolygons > layers ;
if ( ! volumes . empty ( ) ) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
2019-06-11 15:08:47 +00:00
TriangleMesh mesh ( volumes . front ( ) - > mesh ( ) ) ;
2019-04-03 09:12:03 +00:00
mesh . transform ( volumes . front ( ) - > get_matrix ( ) , true ) ;
2019-04-04 14:16:57 +00:00
assert ( mesh . repaired ) ;
if ( volumes . size ( ) = = 1 & & mesh . repaired ) {
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
stl_check_facets_exact ( & mesh . stl ) ;
}
2019-04-03 09:12:03 +00:00
for ( size_t idx_volume = 1 ; idx_volume < volumes . size ( ) ; + + idx_volume ) {
const ModelVolume & model_volume = * volumes [ idx_volume ] ;
2019-06-11 15:08:47 +00:00
TriangleMesh vol_mesh ( model_volume . mesh ( ) ) ;
2019-04-03 09:12:03 +00:00
vol_mesh . transform ( model_volume . get_matrix ( ) , true ) ;
2018-11-02 11:11:28 +00:00
mesh . merge ( vol_mesh ) ;
}
2018-09-17 13:12:13 +00:00
if ( mesh . stl . stats . number_of_facets > 0 ) {
2019-04-03 09:12:03 +00:00
mesh . transform ( m_trafo , true ) ;
2018-10-31 19:02:07 +00:00
// apply XY shift
2020-02-07 13:10:18 +00:00
mesh . translate ( - unscale < float > ( m_center_offset . x ( ) ) , - unscale < float > ( m_center_offset . y ( ) ) , 0 ) ;
2018-09-17 13:12:13 +00:00
// perform actual slicing
const Print * print = this - > print ( ) ;
auto callback = TriangleMeshSlicer : : throw_on_cancel_callback_type ( [ print ] ( ) { print - > throw_if_canceled ( ) ; } ) ;
2019-06-11 15:08:47 +00:00
// TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
mesh . require_shared_vertices ( ) ;
TriangleMeshSlicer mslicer ;
2018-09-17 13:12:13 +00:00
mslicer . init ( & mesh , callback ) ;
2020-12-09 13:07:22 +00:00
mslicer . slice ( z , mode , slicing_mode_normal_below_layer , mode_below , float ( m_config . slice_closing_radius . value ) , & layers , callback ) ;
2018-09-17 13:12:13 +00:00
m_print - > throw_if_canceled ( ) ;
2016-12-12 16:53:38 +00:00
}
}
return layers ;
}
2020-02-08 20:36:29 +00:00
std : : vector < ExPolygons > PrintObject : : slice_volume ( const std : : vector < float > & z , SlicingMode mode , const ModelVolume & volume ) const
2019-03-06 09:21:10 +00:00
{
std : : vector < ExPolygons > layers ;
2019-06-20 14:15:09 +00:00
if ( ! z . empty ( ) ) {
// Compose mesh.
//FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
TriangleMesh mesh ( volume . mesh ( ) ) ;
mesh . transform ( volume . get_matrix ( ) , true ) ;
if ( mesh . repaired ) {
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
stl_check_facets_exact ( & mesh . stl ) ;
}
if ( mesh . stl . stats . number_of_facets > 0 ) {
mesh . transform ( m_trafo , true ) ;
// apply XY shift
2020-02-07 13:10:18 +00:00
mesh . translate ( - unscale < float > ( m_center_offset . x ( ) ) , - unscale < float > ( m_center_offset . y ( ) ) , 0 ) ;
2019-06-20 14:15:09 +00:00
// perform actual slicing
TriangleMeshSlicer mslicer ;
const Print * print = this - > print ( ) ;
auto callback = TriangleMeshSlicer : : throw_on_cancel_callback_type ( [ print ] ( ) { print - > throw_if_canceled ( ) ; } ) ;
// TriangleMeshSlicer needs the shared vertices.
mesh . require_shared_vertices ( ) ;
mslicer . init ( & mesh , callback ) ;
2020-02-08 20:36:29 +00:00
mslicer . slice ( z , mode , float ( m_config . slice_closing_radius . value ) , & layers , callback ) ;
2019-06-20 14:15:09 +00:00
m_print - > throw_if_canceled ( ) ;
}
2019-04-04 14:16:57 +00:00
}
2019-03-06 09:21:10 +00:00
return layers ;
}
2020-12-09 06:28:18 +00:00
// Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping.
2020-02-08 20:36:29 +00:00
std : : vector < ExPolygons > PrintObject : : slice_volume ( const std : : vector < float > & z , const std : : vector < t_layer_height_range > & ranges , SlicingMode mode , const ModelVolume & volume ) const
2019-06-20 14:15:09 +00:00
{
std : : vector < ExPolygons > out ;
if ( ! z . empty ( ) & & ! ranges . empty ( ) ) {
if ( ranges . size ( ) = = 1 & & z . front ( ) > = ranges . front ( ) . first & & z . back ( ) < ranges . front ( ) . second ) {
// All layers fit into a single range.
2020-02-08 20:36:29 +00:00
out = this - > slice_volume ( z , mode , volume ) ;
2019-06-20 14:15:09 +00:00
} else {
std : : vector < float > z_filtered ;
std : : vector < std : : pair < size_t , size_t > > n_filtered ;
z_filtered . reserve ( z . size ( ) ) ;
n_filtered . reserve ( 2 * ranges . size ( ) ) ;
size_t i = 0 ;
for ( const t_layer_height_range & range : ranges ) {
for ( ; i < z . size ( ) & & z [ i ] < range . first ; + + i ) ;
size_t first = i ;
for ( ; i < z . size ( ) & & z [ i ] < range . second ; + + i )
z_filtered . emplace_back ( z [ i ] ) ;
if ( i > first )
n_filtered . emplace_back ( std : : make_pair ( first , i ) ) ;
}
if ( ! n_filtered . empty ( ) ) {
2020-02-08 20:36:29 +00:00
std : : vector < ExPolygons > layers = this - > slice_volume ( z_filtered , mode , volume ) ;
2019-06-20 14:15:09 +00:00
out . assign ( z . size ( ) , ExPolygons ( ) ) ;
i = 0 ;
for ( const std : : pair < size_t , size_t > & span : n_filtered )
for ( size_t j = span . first ; j < span . second ; + + j )
out [ j ] = std : : move ( layers [ i + + ] ) ;
}
}
}
return out ;
}
2017-03-08 10:56:42 +00:00
std : : string PrintObject : : _fix_slicing_errors ( )
{
// Collect layers with slicing errors.
// These layers will be fixed in parallel.
std : : vector < size_t > buggy_layers ;
2018-09-11 12:04:47 +00:00
buggy_layers . reserve ( m_layers . size ( ) ) ;
for ( size_t idx_layer = 0 ; idx_layer < m_layers . size ( ) ; + + idx_layer )
if ( m_layers [ idx_layer ] - > slicing_errors )
2017-03-08 10:56:42 +00:00
buggy_layers . push_back ( idx_layer ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - fixing slicing errors in parallel - begin " ;
tbb : : parallel_for (
tbb : : blocked_range < size_t > ( 0 , buggy_layers . size ( ) ) ,
[ this , & buggy_layers ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t buggy_layer_idx = range . begin ( ) ; buggy_layer_idx < range . end ( ) ; + + buggy_layer_idx ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-08 10:56:42 +00:00
size_t idx_layer = buggy_layers [ buggy_layer_idx ] ;
2018-09-11 12:04:47 +00:00
Layer * layer = m_layers [ idx_layer ] ;
2017-03-08 10:56:42 +00:00
assert ( layer - > slicing_errors ) ;
// Try to repair the layer surfaces by merging all contours and all holes from neighbor layers.
// BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer;
2018-09-11 12:04:47 +00:00
for ( size_t region_id = 0 ; region_id < layer - > m_regions . size ( ) ; + + region_id ) {
LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2017-03-08 10:56:42 +00:00
// Find the first valid layer below / above the current layer.
const Surfaces * upper_surfaces = nullptr ;
const Surfaces * lower_surfaces = nullptr ;
2018-09-11 12:04:47 +00:00
for ( size_t j = idx_layer + 1 ; j < m_layers . size ( ) ; + + j )
if ( ! m_layers [ j ] - > slicing_errors ) {
upper_surfaces = & m_layers [ j ] - > regions ( ) [ region_id ] - > slices . surfaces ;
2017-03-08 10:56:42 +00:00
break ;
}
for ( int j = int ( idx_layer ) - 1 ; j > = 0 ; - - j )
2018-09-11 12:04:47 +00:00
if ( ! m_layers [ j ] - > slicing_errors ) {
lower_surfaces = & m_layers [ j ] - > regions ( ) [ region_id ] - > slices . surfaces ;
2017-03-08 10:56:42 +00:00
break ;
}
// Collect outer contours and holes from the valid layers above & below.
Polygons outer ;
outer . reserve (
( ( upper_surfaces = = nullptr ) ? 0 : upper_surfaces - > size ( ) ) +
( ( lower_surfaces = = nullptr ) ? 0 : lower_surfaces - > size ( ) ) ) ;
size_t num_holes = 0 ;
if ( upper_surfaces )
for ( const auto & surface : * upper_surfaces ) {
outer . push_back ( surface . expolygon . contour ) ;
num_holes + = surface . expolygon . holes . size ( ) ;
}
if ( lower_surfaces )
for ( const auto & surface : * lower_surfaces ) {
outer . push_back ( surface . expolygon . contour ) ;
num_holes + = surface . expolygon . holes . size ( ) ;
}
Polygons holes ;
holes . reserve ( num_holes ) ;
if ( upper_surfaces )
for ( const auto & surface : * upper_surfaces )
polygons_append ( holes , surface . expolygon . holes ) ;
if ( lower_surfaces )
for ( const auto & surface : * lower_surfaces )
polygons_append ( holes , surface . expolygon . holes ) ;
layerm - > slices . set ( diff_ex ( union_ ( outer ) , holes , false ) , stInternal ) ;
}
// Update layer slices after repairing the single regions.
layer - > make_slices ( ) ;
}
} ) ;
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-03-08 10:56:42 +00:00
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - fixing slicing errors in parallel - end " ;
// remove empty layers from bottom
2020-01-03 13:05:56 +00:00
while ( ! m_layers . empty ( ) & & ( m_layers . front ( ) - > lslices . empty ( ) | | m_layers . front ( ) - > empty ( ) ) ) {
2018-09-11 12:04:47 +00:00
delete m_layers . front ( ) ;
m_layers . erase ( m_layers . begin ( ) ) ;
m_layers . front ( ) - > lower_layer = nullptr ;
for ( size_t i = 0 ; i < m_layers . size ( ) ; + + i )
m_layers [ i ] - > set_id ( m_layers [ i ] - > id ( ) - 1 ) ;
2017-03-08 10:56:42 +00:00
}
return buggy_layers . empty ( ) ? " " :
" The model has overlapping or self-intersecting facets. I tried to repair it, "
" however you might want to check the results or repair the input file and retry. \n " ;
}
2017-03-08 12:43:49 +00:00
// Simplify the sliced model, if "resolution" configuration parameter > 0.
// The simplification is problematic, because it simplifies the slices independent from each other,
// which makes the simplified discretization visible on the object surface.
2020-01-03 13:05:56 +00:00
void PrintObject : : simplify_slices ( double distance )
2017-03-08 12:43:49 +00:00
{
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - siplifying slices in parallel - begin " ;
tbb : : parallel_for (
2018-09-11 12:04:47 +00:00
tbb : : blocked_range < size_t > ( 0 , m_layers . size ( ) ) ,
2017-03-08 12:43:49 +00:00
[ this , distance ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t layer_idx = range . begin ( ) ; layer_idx < range . end ( ) ; + + layer_idx ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
Layer * layer = m_layers [ layer_idx ] ;
for ( size_t region_idx = 0 ; region_idx < layer - > m_regions . size ( ) ; + + region_idx )
layer - > m_regions [ region_idx ] - > slices . simplify ( distance ) ;
2019-10-01 15:17:08 +00:00
{
ExPolygons simplified ;
2020-01-03 13:05:56 +00:00
for ( const ExPolygon & expoly : layer - > lslices )
2019-10-01 15:17:08 +00:00
expoly . simplify ( distance , & simplified ) ;
2020-01-03 13:05:56 +00:00
layer - > lslices = std : : move ( simplified ) ;
2019-10-01 15:17:08 +00:00
}
2017-03-08 12:43:49 +00:00
}
} ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " Slicing objects - siplifying slices in parallel - end " ;
}
2017-08-02 12:24:32 +00:00
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
// so it acts as an internal support. It maintains all other infill types intact.
// Here the internal surfaces and perimeters have to be supported by the sparse infill.
//FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
// Likely the sparse infill will not be anchored correctly, so it will not work as intended.
// Also one wishes the perimeters to be supported by a full infill.
// Idempotence of this method is guaranteed by the fact that we don't remove things from
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
void PrintObject : : clip_fill_surfaces ( )
{
2018-09-11 12:04:47 +00:00
if ( ! m_config . infill_only_where_needed . value | |
! std : : any_of ( this - > print ( ) - > regions ( ) . begin ( ) , this - > print ( ) - > regions ( ) . end ( ) ,
[ ] ( const PrintRegion * region ) { return region - > config ( ) . fill_density > 0 ; } ) )
2017-08-02 12:24:32 +00:00
return ;
// We only want infill under ceilings; this is almost like an
// internal support material.
// Proceed top-down, skipping the bottom layer.
Polygons upper_internal ;
2018-09-11 12:04:47 +00:00
for ( int layer_id = int ( m_layers . size ( ) ) - 1 ; layer_id > 0 ; - - layer_id ) {
Layer * layer = m_layers [ layer_id ] ;
Layer * lower_layer = m_layers [ layer_id - 1 ] ;
2017-08-02 12:24:32 +00:00
// Detect things that we need to support.
// Cummulative slices.
Polygons slices ;
2020-01-03 13:05:56 +00:00
polygons_append ( slices , layer - > lslices ) ;
2017-08-02 12:24:32 +00:00
// Cummulative fill surfaces.
Polygons fill_surfaces ;
// Solid surfaces to be supported.
Polygons overhangs ;
2018-09-11 12:04:47 +00:00
for ( const LayerRegion * layerm : layer - > m_regions )
2017-08-02 12:24:32 +00:00
for ( const Surface & surface : layerm - > fill_surfaces . surfaces ) {
Polygons polygons = to_polygons ( surface . expolygon ) ;
if ( surface . is_solid ( ) )
polygons_append ( overhangs , polygons ) ;
polygons_append ( fill_surfaces , std : : move ( polygons ) ) ;
}
Polygons lower_layer_fill_surfaces ;
Polygons lower_layer_internal_surfaces ;
2018-09-11 12:04:47 +00:00
for ( const LayerRegion * layerm : lower_layer - > m_regions )
2017-08-02 12:24:32 +00:00
for ( const Surface & surface : layerm - > fill_surfaces . surfaces ) {
Polygons polygons = to_polygons ( surface . expolygon ) ;
if ( surface . surface_type = = stInternal | | surface . surface_type = = stInternalVoid )
polygons_append ( lower_layer_internal_surfaces , polygons ) ;
polygons_append ( lower_layer_fill_surfaces , std : : move ( polygons ) ) ;
}
// We also need to support perimeters when there's at least one full unsupported loop
{
// Get perimeters area as the difference between slices and fill_surfaces
// Only consider the area that is not supported by lower perimeters
Polygons perimeters = intersection ( diff ( slices , fill_surfaces ) , lower_layer_fill_surfaces ) ;
// Only consider perimeter areas that are at least one extrusion width thick.
//FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
//Should the pw not be half of the current value?
float pw = FLT_MAX ;
2018-09-11 12:04:47 +00:00
for ( const LayerRegion * layerm : layer - > m_regions )
2019-06-20 14:15:09 +00:00
pw = std : : min ( pw , ( float ) layerm - > flow ( frPerimeter ) . scaled_width ( ) ) ;
2017-08-02 12:24:32 +00:00
// Append such thick perimeters to the areas that need support
polygons_append ( overhangs , offset2 ( perimeters , - pw , + pw ) ) ;
}
// Find new internal infill.
polygons_append ( overhangs , std : : move ( upper_internal ) ) ;
upper_internal = intersection ( overhangs , lower_layer_internal_surfaces ) ;
// Apply new internal infill to regions.
2018-09-11 12:04:47 +00:00
for ( LayerRegion * layerm : lower_layer - > m_regions ) {
if ( layerm - > region ( ) - > config ( ) . fill_density . value = = 0 )
2017-08-02 12:24:32 +00:00
continue ;
SurfaceType internal_surface_types [ ] = { stInternal , stInternalVoid } ;
Polygons internal ;
for ( Surface & surface : layerm - > fill_surfaces . surfaces )
if ( surface . surface_type = = stInternal | | surface . surface_type = = stInternalVoid )
polygons_append ( internal , std : : move ( surface . expolygon ) ) ;
layerm - > fill_surfaces . remove_types ( internal_surface_types , 2 ) ;
layerm - > fill_surfaces . append ( intersection_ex ( internal , upper_internal , true ) , stInternal ) ;
layerm - > fill_surfaces . append ( diff_ex ( internal , upper_internal , true ) , stInternalVoid ) ;
// If there are voids it means that our internal infill is not adjacent to
// perimeters. In this case it would be nice to add a loop around infill to
// make it more robust and nicer. TODO.
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm - > export_region_fill_surfaces_to_svg_debug ( " 6_clip_fill_surfaces " ) ;
# endif
}
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-08-02 12:24:32 +00:00
}
}
void PrintObject : : discover_horizontal_shells ( )
{
BOOST_LOG_TRIVIAL ( trace ) < < " discover_horizontal_shells() " ;
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2019-06-25 11:06:04 +00:00
for ( size_t i = 0 ; i < m_layers . size ( ) ; + + i ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2020-02-05 15:53:26 +00:00
Layer * layer = m_layers [ i ] ;
LayerRegion * layerm = layer - > regions ( ) [ region_id ] ;
2018-09-11 12:04:47 +00:00
const PrintRegionConfig & region_config = layerm - > region ( ) - > config ( ) ;
2017-08-02 12:24:32 +00:00
if ( region_config . solid_infill_every_layers . value > 0 & & region_config . fill_density . value > 0 & &
( i % region_config . solid_infill_every_layers ) = = 0 ) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
SurfaceType type = ( region_config . fill_density = = 100 ) ? stInternalSolid : stInternalBridge ;
for ( Surface & surface : layerm - > fill_surfaces . surfaces )
if ( surface . surface_type = = stInternal )
surface . surface_type = type ;
}
// If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
if ( region_config . ensure_vertical_shell_thickness . value )
continue ;
2020-02-05 15:53:26 +00:00
coordf_t print_z = layer - > print_z ;
coordf_t bottom_z = layer - > bottom_z ( ) ;
2019-06-25 11:06:04 +00:00
for ( size_t idx_surface_type = 0 ; idx_surface_type < 3 ; + + idx_surface_type ) {
2018-09-11 12:04:47 +00:00
m_print - > throw_if_canceled ( ) ;
2017-08-02 12:24:32 +00:00
SurfaceType type = ( idx_surface_type = = 0 ) ? stTop : ( idx_surface_type = = 1 ) ? stBottom : stBottomBridge ;
2020-02-18 14:44:01 +00:00
int num_solid_layers = ( type = = stTop ) ? region_config . top_solid_layers . value : region_config . bottom_solid_layers . value ;
if ( num_solid_layers = = 0 )
continue ;
2017-08-02 12:24:32 +00:00
// Find slices of current type for current layer.
// Use slices instead of fill_surfaces, because they also include the perimeter area,
// which needs to be propagated in shells; we need to grow slices like we did for
// fill_surfaces though. Using both ungrown slices and grown fill_surfaces will
// not work in some situations, as there won't be any grown region in the perimeter
// area (this was seen in a model where the top layer had one extra perimeter, thus
// its fill_surfaces were thinner than the lower layer's infill), however it's the best
// solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put
// too much solid infill inside nearly-vertical slopes.
// Surfaces including the area of perimeters. Everything, that is visible from the top / bottom
// (not covered by a layer above / below).
// This does not contain the areas covered by perimeters!
Polygons solid ;
for ( const Surface & surface : layerm - > slices . surfaces )
if ( surface . surface_type = = type )
polygons_append ( solid , to_polygons ( surface . expolygon ) ) ;
// Infill areas (slices without the perimeters).
for ( const Surface & surface : layerm - > fill_surfaces . surfaces )
if ( surface . surface_type = = type )
polygons_append ( solid , to_polygons ( surface . expolygon ) ) ;
if ( solid . empty ( ) )
continue ;
2019-09-09 14:47:15 +00:00
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
2017-08-02 12:24:32 +00:00
2020-02-05 15:53:26 +00:00
// Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
for ( int n = ( type = = stTop ) ? int ( i ) - 1 : int ( i ) + 1 ;
( type = = stTop ) ?
2020-02-18 14:44:01 +00:00
( n > = 0 & & ( int ( i ) - n < num_solid_layers | |
2020-02-05 15:53:26 +00:00
print_z - m_layers [ n ] - > print_z < region_config . top_solid_min_thickness . value - EPSILON ) ) :
2020-02-18 14:44:01 +00:00
( n < int ( m_layers . size ( ) ) & & ( n - int ( i ) < num_solid_layers | |
2020-02-05 15:53:26 +00:00
m_layers [ n ] - > bottom_z ( ) - bottom_z < region_config . bottom_solid_min_thickness . value - EPSILON ) ) ;
( type = = stTop ) ? - - n : + + n )
{
2017-08-02 12:24:32 +00:00
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
// Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
2018-09-11 12:04:47 +00:00
LayerRegion * neighbor_layerm = m_layers [ n ] - > regions ( ) [ region_id ] ;
2017-08-02 12:24:32 +00:00
// find intersection between neighbor and current layer's surfaces
// intersections have contours and holes
// we update $solid so that we limit the next neighbor layer to the areas that were
// found on this one - in other words, solid shells on one layer (for a given external surface)
// are always a subset of the shells found on the previous shell layer
// this approach allows for DWIM in hollow sloping vases, where we want bottom
// shells to be generated in the base but not in the walls (where there are many
// narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the
// upper perimeter as an obstacle and shell will not be propagated to more upper layers
2019-09-09 14:47:15 +00:00
//FIXME How does it work for stInternalBRIDGE? This is set for sparse infill. Likely this does not work.
2017-08-02 12:24:32 +00:00
Polygons new_internal_solid ;
{
Polygons internal ;
for ( const Surface & surface : neighbor_layerm - > fill_surfaces . surfaces )
if ( surface . surface_type = = stInternal | | surface . surface_type = = stInternalSolid )
polygons_append ( internal , to_polygons ( surface . expolygon ) ) ;
new_internal_solid = intersection ( solid , internal , true ) ;
}
if ( new_internal_solid . empty ( ) ) {
// No internal solid needed on this layer. In order to decide whether to continue
// searching on the next neighbor (thus enforcing the configured number of solid
2019-08-08 13:17:17 +00:00
// layers, use different strategies according to configured infill density:
2017-08-02 12:24:32 +00:00
if ( region_config . fill_density . value = = 0 ) {
// If user expects the object to be void (for example a hollow sloping vase),
// don't continue the search. In this case, we only generate the external solid
// shell if the object would otherwise show a hole (gap between perimeters of
// the two layers), and internal solid shells are a subset of the shells found
// on each previous layer.
goto EXTERNAL ;
} else {
// If we have internal infill, we can generate internal solid shells freely.
continue ;
}
}
if ( region_config . fill_density . value = = 0 ) {
// if we're printing a hollow object we discard any solid shell thinner
// than a perimeter width, since it's probably just crossing a sloping wall
// and it's not wanted in a hollow print even if it would make sense when
// obeying the solid shell count option strictly (DWIM!)
float margin = float ( neighbor_layerm - > flow ( frExternalPerimeter ) . scaled_width ( ) ) ;
Polygons too_narrow = diff (
new_internal_solid ,
offset2 ( new_internal_solid , - margin , + margin , jtMiter , 5 ) ,
true ) ;
// Trim the regularized region by the original region.
if ( ! too_narrow . empty ( ) )
new_internal_solid = solid = diff ( new_internal_solid , too_narrow ) ;
}
// make sure the new internal solid is wide enough, as it might get collapsed
// when spacing is added in Fill.pm
{
//FIXME Vojtech: Disable this and you will be sorry.
2019-05-14 17:46:01 +00:00
// https://github.com/prusa3d/PrusaSlicer/issues/26 bottom
2017-08-02 12:24:32 +00:00
float margin = 3.f * layerm - > flow ( frSolidInfill ) . scaled_width ( ) ; // require at least this size
// 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.
Polygons too_narrow = diff (
new_internal_solid ,
offset2 ( new_internal_solid , - margin , + margin , ClipperLib : : jtMiter , 5 ) ,
true ) ;
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 internal ;
for ( const Surface & surface : neighbor_layerm - > fill_surfaces . surfaces )
if ( surface . is_internal ( ) & & ! surface . is_bridge ( ) )
polygons_append ( internal , to_polygons ( surface . expolygon ) ) ;
polygons_append ( new_internal_solid ,
intersection (
offset ( too_narrow , + margin ) ,
// Discard bridges as they are grown for anchoring and we can't
// remove such anchors. (This may happen when a bridge is being
// anchored onto a wall where little space remains after the bridge
// is grown, and that little space is an internal solid shell so
// it triggers this too_narrow logic.)
internal ) ) ;
2020-02-08 20:36:29 +00:00
// see https://github.com/prusa3d/PrusaSlicer/pull/3426
// solid = new_internal_solid;
2017-08-02 12:24:32 +00:00
}
}
// internal-solid are the union of the existing internal-solid surfaces
// and new ones
SurfaceCollection backup = std : : move ( neighbor_layerm - > fill_surfaces ) ;
polygons_append ( new_internal_solid , to_polygons ( backup . filter_by_type ( stInternalSolid ) ) ) ;
ExPolygons internal_solid = union_ex ( new_internal_solid , false ) ;
// assign new internal-solid surfaces to layer
neighbor_layerm - > fill_surfaces . set ( internal_solid , stInternalSolid ) ;
// subtract intersections from layer surfaces to get resulting internal surfaces
Polygons polygons_internal = to_polygons ( std : : move ( internal_solid ) ) ;
ExPolygons internal = diff_ex (
to_polygons ( backup . filter_by_type ( stInternal ) ) ,
polygons_internal ,
true ) ;
// assign resulting internal surfaces to layer
neighbor_layerm - > fill_surfaces . append ( internal , stInternal ) ;
polygons_append ( polygons_internal , to_polygons ( std : : move ( internal ) ) ) ;
// assign top and bottom surfaces to layer
SurfaceType surface_types_solid [ ] = { stTop , stBottom , stBottomBridge } ;
backup . keep_types ( surface_types_solid , 3 ) ;
std : : vector < SurfacesPtr > top_bottom_groups ;
backup . group ( & top_bottom_groups ) ;
for ( SurfacesPtr & group : top_bottom_groups )
neighbor_layerm - > fill_surfaces . append (
diff_ex ( to_polygons ( group ) , polygons_internal ) ,
2017-09-14 11:15:32 +00:00
// Use an existing surface as a template, it carries the bridge angle etc.
* group . front ( ) ) ;
2017-08-02 12:24:32 +00:00
}
EXTERNAL : ;
} // foreach type (stTop, stBottom, stBottomBridge)
} // for each layer
} // for each region
# ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
for ( const Layer * layer : m_layers ) {
const LayerRegion * layerm = layer - > m_regions [ region_id ] ;
2017-08-02 12:24:32 +00:00
layerm - > export_region_slices_to_svg_debug ( " 5_discover_horizontal_shells " ) ;
layerm - > export_region_fill_surfaces_to_svg_debug ( " 5_discover_horizontal_shells " ) ;
} // for each layer
} // for each region
# endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
// combine fill surfaces across layers to honor the "infill every N layers" option
// Idempotence of this method is guaranteed by the fact that we don't remove things from
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
void PrintObject : : combine_infill ( )
{
// Work on each region separately.
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < this - > region_volumes . size ( ) ; + + region_id ) {
2018-09-11 12:04:47 +00:00
const PrintRegion * region = this - > print ( ) - > regions ( ) [ region_id ] ;
2019-06-25 11:06:04 +00:00
const size_t every = region - > config ( ) . infill_every_layers . value ;
2018-09-11 12:04:47 +00:00
if ( every < 2 | | region - > config ( ) . fill_density = = 0. )
2017-08-02 12:24:32 +00:00
continue ;
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height
double nozzle_diameter = std : : min (
2018-09-11 12:04:47 +00:00
this - > print ( ) - > config ( ) . nozzle_diameter . get_at ( region - > config ( ) . infill_extruder . value - 1 ) ,
this - > print ( ) - > config ( ) . nozzle_diameter . get_at ( region - > config ( ) . solid_infill_extruder . value - 1 ) ) ;
2017-08-02 12:24:32 +00:00
// define the combinations
2018-09-11 12:04:47 +00:00
std : : vector < size_t > combine ( m_layers . size ( ) , 0 ) ;
2017-08-02 12:24:32 +00:00
{
double current_height = 0. ;
size_t num_layers = 0 ;
2018-09-11 12:04:47 +00:00
for ( size_t layer_idx = 0 ; layer_idx < m_layers . size ( ) ; + + layer_idx ) {
m_print - > throw_if_canceled ( ) ;
const Layer * layer = m_layers [ layer_idx ] ;
2017-08-02 12:24:32 +00:00
if ( layer - > id ( ) = = 0 )
// Skip first print layer (which may not be first layer in array because of raft).
continue ;
// Check whether the combination of this layer with the lower layers' buffer
// would exceed max layer height or max combined layer count.
if ( current_height + layer - > height > = nozzle_diameter + EPSILON | | num_layers > = every ) {
// Append combination to lower layer.
combine [ layer_idx - 1 ] = num_layers ;
current_height = 0. ;
num_layers = 0 ;
}
current_height + = layer - > height ;
+ + num_layers ;
}
// Append lower layers (if any) to uppermost layer.
2018-09-11 12:04:47 +00:00
combine [ m_layers . size ( ) - 1 ] = num_layers ;
2017-08-02 12:24:32 +00:00
}
// loop through layers to which we have assigned layers to combine
2018-09-11 12:04:47 +00:00
for ( size_t layer_idx = 0 ; layer_idx < m_layers . size ( ) ; + + layer_idx ) {
m_print - > throw_if_canceled ( ) ;
2017-08-02 12:24:32 +00:00
size_t num_layers = combine [ layer_idx ] ;
if ( num_layers < = 1 )
continue ;
// Get all the LayerRegion objects to be combined.
std : : vector < LayerRegion * > layerms ;
layerms . reserve ( num_layers ) ;
for ( size_t i = layer_idx + 1 - num_layers ; i < = layer_idx ; + + i )
2018-09-11 12:04:47 +00:00
layerms . emplace_back ( m_layers [ i ] - > regions ( ) [ region_id ] ) ;
2017-08-02 12:24:32 +00:00
// We need to perform a multi-layer intersection, so let's split it in pairs.
// Initialize the intersection with the candidates of the lowest layer.
ExPolygons intersection = to_expolygons ( layerms . front ( ) - > fill_surfaces . filter_by_type ( stInternal ) ) ;
// Start looping from the second layer and intersect the current intersection with it.
for ( size_t i = 1 ; i < layerms . size ( ) ; + + i )
intersection = intersection_ex (
to_polygons ( intersection ) ,
to_polygons ( layerms [ i ] - > fill_surfaces . filter_by_type ( stInternal ) ) ,
false ) ;
double area_threshold = layerms . front ( ) - > infill_area_threshold ( ) ;
if ( ! intersection . empty ( ) & & area_threshold > 0. )
intersection . erase ( std : : remove_if ( intersection . begin ( ) , intersection . end ( ) ,
[ area_threshold ] ( const ExPolygon & expoly ) { return expoly . area ( ) < = area_threshold ; } ) ,
intersection . end ( ) ) ;
if ( intersection . empty ( ) )
continue ;
// Slic3r::debugf " combining %d %s regions from layers %d-%d\n",
// scalar(@$intersection),
2019-09-09 14:47:15 +00:00
// ($type == stInternal ? 'internal' : 'internal-solid'),
2017-08-02 12:24:32 +00:00
// $layer_idx-($every-1), $layer_idx;
// intersection now contains the regions that can be combined across the full amount of layers,
// so let's remove those areas from all layers.
Polygons intersection_with_clearance ;
intersection_with_clearance . reserve ( intersection . size ( ) ) ;
float clearance_offset =
0.5f * layerms . back ( ) - > flow ( frPerimeter ) . scaled_width ( ) +
// Because fill areas for rectilinear and honeycomb are grown
// later to overlap perimeters, we need to counteract that too.
2018-09-11 12:04:47 +00:00
( ( region - > config ( ) . fill_pattern = = ipRectilinear | |
2020-10-05 14:38:28 +00:00
region - > config ( ) . fill_pattern = = ipMonotonic | |
2018-09-11 12:04:47 +00:00
region - > config ( ) . fill_pattern = = ipGrid | |
region - > config ( ) . fill_pattern = = ipLine | |
region - > config ( ) . fill_pattern = = ipHoneycomb ) ? 1.5f : 0.5f ) *
2017-08-02 12:24:32 +00:00
layerms . back ( ) - > flow ( frSolidInfill ) . scaled_width ( ) ;
for ( ExPolygon & expoly : intersection )
polygons_append ( intersection_with_clearance , offset ( expoly , clearance_offset ) ) ;
for ( LayerRegion * layerm : layerms ) {
Polygons internal = to_polygons ( layerm - > fill_surfaces . filter_by_type ( stInternal ) ) ;
layerm - > fill_surfaces . remove_type ( stInternal ) ;
layerm - > fill_surfaces . append ( diff_ex ( internal , intersection_with_clearance , false ) , stInternal ) ;
if ( layerm = = layerms . back ( ) ) {
// Apply surfaces back with adjusted depth to the uppermost layer.
Surface templ ( stInternal , ExPolygon ( ) ) ;
templ . thickness = 0. ;
for ( LayerRegion * layerm2 : layerms )
templ . thickness + = layerm2 - > layer ( ) - > height ;
templ . thickness_layers = ( unsigned short ) layerms . size ( ) ;
layerm - > fill_surfaces . append ( intersection , templ ) ;
} else {
// Save void surfaces.
layerm - > fill_surfaces . append (
intersection_ex ( internal , intersection_with_clearance , false ) ,
stInternalVoid ) ;
}
}
}
}
}
2016-12-20 11:19:13 +00:00
void PrintObject : : _generate_support_material ( )
{
2019-03-04 14:28:04 +00:00
PrintObjectSupportMaterial support_material ( this , m_slicing_params ) ;
2016-12-20 11:19:13 +00:00
support_material . generate ( * this ) ;
}
2020-04-21 11:50:47 +00:00
2020-08-31 05:25:43 +00:00
void PrintObject : : project_and_append_custom_facets (
bool seam , EnforcerBlockerType type , std : : vector < ExPolygons > & expolys ) const
2020-04-21 11:50:47 +00:00
{
for ( const ModelVolume * mv : this - > model_object ( ) - > volumes ) {
2020-08-31 05:25:43 +00:00
const indexed_triangle_set custom_facets = seam
2020-10-09 11:09:03 +00:00
? mv - > seam_facets . get_facets ( * mv , type )
: mv - > supported_facets . get_facets ( * mv , type ) ;
2020-08-31 05:25:43 +00:00
if ( ! mv - > is_model_part ( ) | | custom_facets . indices . empty ( ) )
2020-04-23 23:01:47 +00:00
continue ;
2020-04-21 11:50:47 +00:00
const Transform3f & tr1 = mv - > get_matrix ( ) . cast < float > ( ) ;
const Transform3f & tr2 = this - > trafo ( ) . cast < float > ( ) ;
2020-04-23 23:01:47 +00:00
const Transform3f tr = tr2 * tr1 ;
2020-07-23 06:17:04 +00:00
const float tr_det_sign = ( tr . matrix ( ) . determinant ( ) > 0. ? 1.f : - 1.f ) ;
2020-04-23 23:01:47 +00:00
// The projection will be at most a pentagon. Let's minimize heap
// reallocations by saving in in the following struct.
// Points are used so that scaling can be done in parallel
// and they can be moved from to create an ExPolygon later.
struct LightPolygon {
LightPolygon ( ) { pts . reserve ( 5 ) ; }
Points pts ;
void add ( const Vec2f & pt ) {
pts . emplace_back ( scale_ ( pt . x ( ) ) , scale_ ( pt . y ( ) ) ) ;
2020-04-27 10:15:45 +00:00
assert ( pts . size ( ) < = 5 ) ;
2020-04-23 23:01:47 +00:00
}
} ;
2020-04-21 11:50:47 +00:00
2020-04-22 14:08:36 +00:00
// Structure to collect projected polygons. One element for each triangle.
2020-04-23 23:01:47 +00:00
// Saves vector of polygons and layer_id of the first one.
struct TriangleProjections {
size_t first_layer_id ;
std : : vector < LightPolygon > polygons ;
} ;
2020-04-22 14:08:36 +00:00
2020-04-23 23:01:47 +00:00
// Vector to collect resulting projections from each triangle.
2020-07-15 08:28:20 +00:00
std : : vector < TriangleProjections > projections_of_triangles ( custom_facets . indices . size ( ) ) ;
2020-04-21 11:50:47 +00:00
// Iterate over all triangles.
2020-04-22 14:08:36 +00:00
tbb : : parallel_for (
2020-07-15 08:28:20 +00:00
tbb : : blocked_range < size_t > ( 0 , custom_facets . indices . size ( ) ) ,
2020-04-22 14:08:36 +00:00
[ & ] ( const tbb : : blocked_range < size_t > & range ) {
for ( size_t idx = range . begin ( ) ; idx < range . end ( ) ; + + idx ) {
2020-04-21 11:50:47 +00:00
std : : array < Vec3f , 3 > facet ;
// Transform the triangle into worlds coords.
for ( int i = 0 ; i < 3 ; + + i )
2020-07-15 08:28:20 +00:00
facet [ i ] = tr * custom_facets . vertices [ custom_facets . indices [ idx ] ( i ) ] ;
2020-04-21 11:50:47 +00:00
2020-07-23 06:17:04 +00:00
// Ignore triangles with upward-pointing normal. Don't forget about mirroring.
float z_comp = ( facet [ 1 ] - facet [ 0 ] ) . cross ( facet [ 2 ] - facet [ 0 ] ) . z ( ) ;
2020-08-31 05:25:43 +00:00
if ( ! seam & & tr_det_sign * z_comp > 0. )
2020-04-22 12:01:29 +00:00
continue ;
2020-04-21 11:50:47 +00:00
// Sort the three vertices according to z-coordinate.
std : : sort ( facet . begin ( ) , facet . end ( ) ,
[ ] ( const Vec3f & pt1 , const Vec3f & pt2 ) {
return pt1 . z ( ) < pt2 . z ( ) ;
} ) ;
2020-04-22 07:24:52 +00:00
std : : array < Vec2f , 3 > trianglef ;
2020-04-21 11:50:47 +00:00
for ( int i = 0 ; i < 3 ; + + i ) {
2020-04-22 07:24:52 +00:00
trianglef [ i ] = Vec2f ( facet [ i ] . x ( ) , facet [ i ] . y ( ) ) ;
2020-08-12 09:28:30 +00:00
trianglef [ i ] - = Vec2f ( unscale < float > ( this - > center_offset ( ) . x ( ) ) ,
2020-04-22 07:24:52 +00:00
unscale < float > ( this - > center_offset ( ) . y ( ) ) ) ;
2020-04-21 11:50:47 +00:00
}
// Find lowest slice not below the triangle.
2020-04-22 07:24:52 +00:00
auto it = std : : lower_bound ( layers ( ) . begin ( ) , layers ( ) . end ( ) , facet [ 0 ] . z ( ) + EPSILON ,
2020-04-21 11:50:47 +00:00
[ ] ( const Layer * l1 , float z ) {
return l1 - > slice_z < z ;
} ) ;
2020-04-23 23:01:47 +00:00
// Count how many projections will be generated for this triangle
// and allocate respective amount in projections_of_triangles.
projections_of_triangles [ idx ] . first_layer_id = it - layers ( ) . begin ( ) ;
size_t last_layer_id = projections_of_triangles [ idx ] . first_layer_id ;
// The cast in the condition below is important. The comparison must
// be an exact opposite of the one lower in the code where
// the polygons are appended. And that one is on floats.
while ( last_layer_id + 1 < layers ( ) . size ( )
& & float ( layers ( ) [ last_layer_id ] - > slice_z ) < = facet [ 2 ] . z ( ) )
+ + last_layer_id ;
projections_of_triangles [ idx ] . polygons . resize (
last_layer_id - projections_of_triangles [ idx ] . first_layer_id + 1 ) ;
2020-04-21 11:50:47 +00:00
// Calculate how to move points on triangle sides per unit z increment.
2020-04-22 07:24:52 +00:00
Vec2f ta ( trianglef [ 1 ] - trianglef [ 0 ] ) ;
Vec2f tb ( trianglef [ 2 ] - trianglef [ 0 ] ) ;
2020-09-22 06:53:45 +00:00
ta * = 1.f / ( facet [ 1 ] . z ( ) - facet [ 0 ] . z ( ) ) ;
tb * = 1.f / ( facet [ 2 ] . z ( ) - facet [ 0 ] . z ( ) ) ;
2020-04-22 07:24:52 +00:00
2020-04-23 23:01:47 +00:00
// Projection on current slice will be build directly in place.
LightPolygon * proj = & projections_of_triangles [ idx ] . polygons [ 0 ] ;
proj - > add ( trianglef [ 0 ] ) ;
2020-04-22 07:24:52 +00:00
2020-04-21 11:50:47 +00:00
bool passed_first = false ;
bool stop = false ;
2020-04-23 23:01:47 +00:00
// Project a sub-polygon on all slices intersecting the triangle.
2020-04-22 07:24:52 +00:00
while ( it ! = layers ( ) . end ( ) ) {
2020-09-22 06:53:45 +00:00
const float z = float ( ( * it ) - > slice_z ) ;
2020-04-21 11:50:47 +00:00
2020-04-23 23:01:47 +00:00
// Projections of triangle sides intersections with slices.
// a moves along one side, b tracks the other.
Vec2f a ;
Vec2f b ;
2020-04-22 07:24:52 +00:00
// If the middle vertex was already passed, append the vertex
2020-04-23 23:01:47 +00:00
// and use ta for tracking the remaining side.
2020-04-22 07:24:52 +00:00
if ( z > facet [ 1 ] . z ( ) & & ! passed_first ) {
2020-04-23 23:01:47 +00:00
proj - > add ( trianglef [ 1 ] ) ;
2020-04-22 07:24:52 +00:00
ta = trianglef [ 2 ] - trianglef [ 1 ] ;
2020-09-22 06:53:45 +00:00
ta * = 1.f / ( facet [ 2 ] . z ( ) - facet [ 1 ] . z ( ) ) ;
2020-04-21 11:50:47 +00:00
passed_first = true ;
}
2020-04-22 07:24:52 +00:00
// This slice is above the triangle already.
if ( z > facet [ 2 ] . z ( ) | | it + 1 = = layers ( ) . end ( ) ) {
2020-04-23 23:01:47 +00:00
proj - > add ( trianglef [ 2 ] ) ;
2020-04-21 11:50:47 +00:00
stop = true ;
}
else {
2020-04-23 23:01:47 +00:00
// Move a, b along the side it currently tracks to get
// projected intersection with current slice.
a = passed_first ? ( trianglef [ 1 ] + ta * ( z - facet [ 1 ] . z ( ) ) )
: ( trianglef [ 0 ] + ta * ( z - facet [ 0 ] . z ( ) ) ) ;
2020-04-22 07:24:52 +00:00
b = trianglef [ 0 ] + tb * ( z - facet [ 0 ] . z ( ) ) ;
2020-04-23 23:01:47 +00:00
proj - > add ( a ) ;
proj - > add ( b ) ;
2020-04-22 07:24:52 +00:00
}
2020-04-21 11:50:47 +00:00
2020-04-23 23:01:47 +00:00
if ( stop )
2020-04-21 11:50:47 +00:00
break ;
2020-04-23 23:01:47 +00:00
// Advance to the next layer.
2020-04-21 11:50:47 +00:00
+ + it ;
2020-04-23 23:01:47 +00:00
+ + proj ;
assert ( proj < = & projections_of_triangles [ idx ] . polygons . back ( ) ) ;
// a, b are first two points of the polygon for the next layer.
proj - > add ( b ) ;
proj - > add ( a ) ;
2020-04-21 11:50:47 +00:00
}
}
2020-04-22 14:08:36 +00:00
} ) ; // end of parallel_for
2020-04-23 23:01:47 +00:00
// Make sure that the output vector can be used.
expolys . resize ( layers ( ) . size ( ) ) ;
2020-04-22 14:08:36 +00:00
// Now append the collected polygons to respective layers.
2020-04-23 23:01:47 +00:00
for ( auto & trg : projections_of_triangles ) {
2020-09-24 13:34:13 +00:00
int layer_id = int ( trg . first_layer_id ) ;
2020-04-23 23:01:47 +00:00
for ( const LightPolygon & poly : trg . polygons ) {
2020-08-11 10:15:44 +00:00
if ( layer_id > = int ( expolys . size ( ) ) )
break ; // part of triangle could be projected above top layer
2020-04-27 15:43:34 +00:00
expolys [ layer_id ] . emplace_back ( std : : move ( poly . pts ) ) ;
2020-04-23 23:01:47 +00:00
+ + layer_id ;
2020-04-22 14:08:36 +00:00
}
}
} // loop over ModelVolumes
2020-04-21 11:50:47 +00:00
}
2020-05-26 09:09:38 +00:00
const Layer * PrintObject : : get_layer_at_printz ( coordf_t print_z ) const {
auto it = Slic3r : : lower_bound_by_predicate ( m_layers . begin ( ) , m_layers . end ( ) , [ print_z ] ( const Layer * layer ) { return layer - > print_z < print_z ; } ) ;
return ( it = = m_layers . end ( ) | | ( * it ) - > print_z ! = print_z ) ? nullptr : * it ;
}
Layer * PrintObject : : get_layer_at_printz ( coordf_t print_z ) { return const_cast < Layer * > ( std : : as_const ( * this ) . get_layer_at_printz ( print_z ) ) ; }
// Get a layer approximately at print_z.
const Layer * PrintObject : : get_layer_at_printz ( coordf_t print_z , coordf_t epsilon ) const {
coordf_t limit = print_z - epsilon ;
auto it = Slic3r : : lower_bound_by_predicate ( m_layers . begin ( ) , m_layers . end ( ) , [ limit ] ( const Layer * layer ) { return layer - > print_z < limit ; } ) ;
return ( it = = m_layers . end ( ) | | ( * it ) - > print_z > print_z + epsilon ) ? nullptr : * it ;
}
Layer * PrintObject : : get_layer_at_printz ( coordf_t print_z , coordf_t epsilon ) { return const_cast < Layer * > ( std : : as_const ( * this ) . get_layer_at_printz ( print_z , epsilon ) ) ; }
2020-12-15 05:46:44 +00:00
const Layer * PrintObject : : get_first_layer_bellow_printz ( coordf_t print_z , coordf_t epsilon ) const
{
coordf_t limit = print_z + epsilon ;
auto it = Slic3r : : lower_bound_by_predicate ( m_layers . begin ( ) , m_layers . end ( ) , [ limit ] ( const Layer * layer ) { return layer - > print_z < limit ; } ) ;
return ( it = = m_layers . begin ( ) ) ? nullptr : * ( - - it ) ;
}
2016-12-12 16:53:38 +00:00
} // namespace Slic3r