2017-05-25 20:27:53 +00:00
# include "Print.hpp"
2017-05-16 11:45:28 +00:00
# include "ToolOrdering.hpp"
2017-12-11 16:19:55 +00:00
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
# ifdef SLIC3R_DEBUG
# define DEBUG
# define _DEBUG
# undef NDEBUG
# endif
# include <cassert>
2017-06-09 12:24:00 +00:00
# include <limits>
2017-05-23 13:00:01 +00:00
2017-05-16 11:45:28 +00:00
namespace Slic3r {
2017-05-23 13:00:01 +00:00
2018-07-13 09:25:22 +00:00
// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
bool LayerTools : : is_extruder_order ( unsigned int a , unsigned int b ) const
{
if ( a = = b )
return false ;
for ( auto extruder : extruders ) {
if ( extruder = = a )
return true ;
if ( extruder = = b )
return false ;
}
return false ;
}
2017-05-23 13:00:01 +00:00
// For the use case when each object is printed separately
2018-09-11 12:04:47 +00:00
// (print.config().complete_objects is true).
2017-09-01 15:30:18 +00:00
ToolOrdering : : ToolOrdering ( const PrintObject & object , unsigned int first_extruder , bool prime_multi_material )
2017-05-23 13:00:01 +00:00
{
2018-09-11 12:04:47 +00:00
if ( object . layers ( ) . empty ( ) )
2017-06-08 14:58:29 +00:00
return ;
2017-05-23 13:00:01 +00:00
// Initialize the print layers for just a single object.
{
std : : vector < coordf_t > zs ;
2018-09-11 12:04:47 +00:00
zs . reserve ( zs . size ( ) + object . layers ( ) . size ( ) + object . support_layers ( ) . size ( ) ) ;
for ( auto layer : object . layers ( ) )
2017-05-23 13:00:01 +00:00
zs . emplace_back ( layer - > print_z ) ;
2018-09-11 12:04:47 +00:00
for ( auto layer : object . support_layers ( ) )
2017-05-23 13:00:01 +00:00
zs . emplace_back ( layer - > print_z ) ;
this - > initialize_layers ( zs ) ;
}
// Collect extruders reuqired to print the layers.
this - > collect_extruders ( object ) ;
// Reorder the extruders to minimize tool switches.
this - > reorder_extruders ( first_extruder ) ;
2018-09-11 12:04:47 +00:00
this - > fill_wipe_tower_partitions ( object . print ( ) - > config ( ) , object . layers ( ) . front ( ) - > print_z - object . layers ( ) . front ( ) - > height ) ;
2017-09-01 15:30:18 +00:00
this - > collect_extruder_statistics ( prime_multi_material ) ;
2017-05-23 13:00:01 +00:00
}
// For the use case when all objects are printed at once.
2018-09-11 12:04:47 +00:00
// (print.config().complete_objects is false).
2017-09-01 15:30:18 +00:00
ToolOrdering : : ToolOrdering ( const Print & print , unsigned int first_extruder , bool prime_multi_material )
2017-05-23 13:00:01 +00:00
{
2018-09-12 09:59:02 +00:00
m_print_config_ptr = & print . config ( ) ;
2018-07-18 07:37:25 +00:00
2017-09-01 15:30:18 +00:00
// Initialize the print layers for all objects and all layers.
2017-06-08 14:58:29 +00:00
coordf_t object_bottom_z = 0. ;
2017-05-23 13:00:01 +00:00
{
std : : vector < coordf_t > zs ;
2018-11-08 13:23:17 +00:00
for ( auto object : print . objects ( ) ) {
2018-09-11 12:04:47 +00:00
zs . reserve ( zs . size ( ) + object - > layers ( ) . size ( ) + object - > support_layers ( ) . size ( ) ) ;
for ( auto layer : object - > layers ( ) )
2017-05-23 13:00:01 +00:00
zs . emplace_back ( layer - > print_z ) ;
2018-09-11 12:04:47 +00:00
for ( auto layer : object - > support_layers ( ) )
2017-05-23 13:00:01 +00:00
zs . emplace_back ( layer - > print_z ) ;
2019-08-02 14:49:22 +00:00
// Find first object layer that is not empty and save its print_z
for ( const Layer * layer : object - > layers ( ) )
if ( layer - > has_extrusions ( ) ) {
object_bottom_z = layer - > print_z - layer - > height ;
break ;
}
2017-05-23 13:00:01 +00:00
}
this - > initialize_layers ( zs ) ;
}
// Collect extruders reuqired to print the layers.
2018-11-08 13:23:17 +00:00
for ( auto object : print . objects ( ) )
2017-05-23 13:00:01 +00:00
this - > collect_extruders ( * object ) ;
// Reorder the extruders to minimize tool switches.
this - > reorder_extruders ( first_extruder ) ;
2018-09-11 12:04:47 +00:00
this - > fill_wipe_tower_partitions ( print . config ( ) , object_bottom_z ) ;
2017-05-23 13:00:01 +00:00
2017-09-01 15:30:18 +00:00
this - > collect_extruder_statistics ( prime_multi_material ) ;
2017-05-23 13:00:01 +00:00
}
void ToolOrdering : : initialize_layers ( std : : vector < coordf_t > & zs )
{
sort_remove_duplicates ( zs ) ;
// Merge numerically very close Z values.
for ( size_t i = 0 ; i < zs . size ( ) ; ) {
// Find the last layer with roughly the same print_z.
size_t j = i + 1 ;
coordf_t zmax = zs [ i ] + EPSILON ;
for ( ; j < zs . size ( ) & & zs [ j ] < = zmax ; + + j ) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
2018-06-26 12:12:25 +00:00
m_layer_tools . emplace_back ( LayerTools ( 0.5 * ( zs [ i ] + zs [ j - 1 ] ) , m_print_config_ptr ) ) ;
2017-05-23 13:00:01 +00:00
i = j ;
}
}
2017-05-16 11:45:28 +00:00
// Collect extruders reuqired to print layers.
2017-05-23 13:00:01 +00:00
void ToolOrdering : : collect_extruders ( const PrintObject & object )
2017-05-16 11:45:28 +00:00
{
// Collect the support extruders.
2018-09-11 12:04:47 +00:00
for ( auto support_layer : object . support_layers ( ) ) {
2017-05-23 13:00:01 +00:00
LayerTools & layer_tools = this - > tools_for_layer ( support_layer - > print_z ) ;
2017-05-16 11:45:28 +00:00
ExtrusionRole role = support_layer - > support_fills . role ( ) ;
bool has_support = role = = erMixed | | role = = erSupportMaterial ;
bool has_interface = role = = erMixed | | role = = erSupportMaterialInterface ;
2018-09-11 12:04:47 +00:00
unsigned int extruder_support = object . config ( ) . support_material_extruder . value ;
unsigned int extruder_interface = object . config ( ) . support_material_interface_extruder . value ;
2017-05-16 11:45:28 +00:00
if ( has_support )
2017-05-23 13:00:01 +00:00
layer_tools . extruders . push_back ( extruder_support ) ;
2017-05-16 11:45:28 +00:00
if ( has_interface )
2017-05-23 13:00:01 +00:00
layer_tools . extruders . push_back ( extruder_interface ) ;
2017-05-19 17:24:21 +00:00
if ( has_support | | has_interface )
2017-05-23 13:00:01 +00:00
layer_tools . has_support = true ;
2017-05-16 11:45:28 +00:00
}
// Collect the object extruders.
2018-09-11 12:04:47 +00:00
for ( auto layer : object . layers ( ) ) {
2017-05-23 13:00:01 +00:00
LayerTools & layer_tools = this - > tools_for_layer ( layer - > print_z ) ;
2017-05-16 11:45:28 +00:00
// What extruders are required to print this object layer?
2018-11-06 14:31:26 +00:00
for ( size_t region_id = 0 ; region_id < object . region_volumes . size ( ) ; + + region_id ) {
2019-08-16 14:31:05 +00:00
const LayerRegion * layerm = ( region_id < layer - > regions ( ) . size ( ) ) ? layer - > regions ( ) [ region_id ] : nullptr ;
2017-05-16 11:45:28 +00:00
if ( layerm = = nullptr )
continue ;
2018-09-11 12:04:47 +00:00
const PrintRegion & region = * object . print ( ) - > regions ( ) [ region_id ] ;
2018-06-26 12:12:25 +00:00
2017-05-19 17:24:21 +00:00
if ( ! layerm - > perimeters . entities . empty ( ) ) {
2018-06-27 13:49:02 +00:00
bool something_nonoverriddable = true ;
if ( m_print_config_ptr ) { // in this case complete_objects is false (see ToolOrdering constructors)
something_nonoverriddable = false ;
for ( const auto & eec : layerm - > perimeters . entities ) // let's check if there are nonoverriddable entities
2018-07-11 12:46:13 +00:00
if ( ! layer_tools . wiping_extrusions ( ) . is_overriddable ( dynamic_cast < const ExtrusionEntityCollection & > ( * eec ) , * m_print_config_ptr , object , region ) ) {
2018-06-27 13:49:02 +00:00
something_nonoverriddable = true ;
break ;
}
}
2018-06-26 12:12:25 +00:00
if ( something_nonoverriddable )
2018-09-12 09:59:02 +00:00
layer_tools . extruders . push_back ( region . config ( ) . perimeter_extruder . value ) ;
2018-06-26 12:12:25 +00:00
2017-05-23 13:00:01 +00:00
layer_tools . has_object = true ;
2017-05-19 17:24:21 +00:00
}
2018-06-26 12:12:25 +00:00
2017-05-19 17:24:21 +00:00
bool has_infill = false ;
2017-05-16 11:45:28 +00:00
bool has_solid_infill = false ;
2018-06-26 12:12:25 +00:00
bool something_nonoverriddable = false ;
2017-05-16 11:45:28 +00:00
for ( const ExtrusionEntity * ee : layerm - > fills . entities ) {
// fill represents infill extrusions of a single island.
const auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
ExtrusionRole role = fill - > entities . empty ( ) ? erNone : fill - > entities . front ( ) - > role ( ) ;
if ( is_solid_infill ( role ) )
has_solid_infill = true ;
else if ( role ! = erNone )
has_infill = true ;
2018-06-26 12:12:25 +00:00
2018-06-27 13:49:02 +00:00
if ( m_print_config_ptr ) {
2018-07-11 12:46:13 +00:00
if ( ! something_nonoverriddable & & ! layer_tools . wiping_extrusions ( ) . is_overriddable ( * fill , * m_print_config_ptr , object , region ) )
2018-06-27 13:49:02 +00:00
something_nonoverriddable = true ;
}
2018-06-26 12:12:25 +00:00
}
2018-06-27 13:49:02 +00:00
if ( something_nonoverriddable | | ! m_print_config_ptr )
2018-06-26 12:12:25 +00:00
{
if ( has_solid_infill )
2018-09-12 09:59:02 +00:00
layer_tools . extruders . push_back ( region . config ( ) . solid_infill_extruder ) ;
2018-06-26 12:12:25 +00:00
if ( has_infill )
2018-09-12 09:59:02 +00:00
layer_tools . extruders . push_back ( region . config ( ) . infill_extruder ) ;
2017-05-16 11:45:28 +00:00
}
2017-05-19 17:24:21 +00:00
if ( has_solid_infill | | has_infill )
2017-05-23 13:00:01 +00:00
layer_tools . has_object = true ;
2017-05-16 11:45:28 +00:00
}
}
2018-06-27 12:08:46 +00:00
for ( auto & layer : m_layer_tools ) {
// Sort and remove duplicates
sort_remove_duplicates ( layer . extruders ) ;
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
if ( layer . extruders . empty ( ) & & layer . has_object )
layer . extruders . push_back ( 0 ) ; // 0="dontcare" extruder - it will be taken care of in reorder_extruders
2018-06-26 12:12:25 +00:00
}
2017-05-16 11:45:28 +00:00
}
// Reorder extruders to minimize layer changes.
2017-05-23 13:00:01 +00:00
void ToolOrdering : : reorder_extruders ( unsigned int last_extruder_id )
2017-05-16 11:45:28 +00:00
{
2017-05-23 13:00:01 +00:00
if ( m_layer_tools . empty ( ) )
2017-05-16 11:45:28 +00:00
return ;
2017-05-16 13:30:03 +00:00
if ( last_extruder_id = = ( unsigned int ) - 1 ) {
// The initial print extruder has not been decided yet.
// Initialize the last_extruder_id with the first non-zero extruder id used for the print.
last_extruder_id = 0 ;
2017-05-23 13:00:01 +00:00
for ( size_t i = 0 ; i < m_layer_tools . size ( ) & & last_extruder_id = = 0 ; + + i ) {
const LayerTools & lt = m_layer_tools [ i ] ;
2017-05-16 13:30:03 +00:00
for ( unsigned int extruder_id : lt . extruders )
if ( extruder_id > 0 ) {
last_extruder_id = extruder_id ;
break ;
}
}
if ( last_extruder_id = = 0 )
// Nothing to extrude.
return ;
} else
// 1 based index
+ + last_extruder_id ;
2017-05-16 11:45:28 +00:00
2017-05-23 13:00:01 +00:00
for ( LayerTools & lt : m_layer_tools ) {
2017-05-16 11:45:28 +00:00
if ( lt . extruders . empty ( ) )
continue ;
if ( lt . extruders . size ( ) = = 1 & & lt . extruders . front ( ) = = 0 )
lt . extruders . front ( ) = last_extruder_id ;
else {
if ( lt . extruders . front ( ) = = 0 )
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
lt . extruders . erase ( lt . extruders . begin ( ) ) ;
// Reorder the extruders to start with the last one.
for ( size_t i = 1 ; i < lt . extruders . size ( ) ; + + i )
if ( lt . extruders [ i ] = = last_extruder_id ) {
// Move the last extruder to the front.
memmove ( lt . extruders . data ( ) + 1 , lt . extruders . data ( ) , i * sizeof ( unsigned int ) ) ;
lt . extruders . front ( ) = last_extruder_id ;
break ;
}
}
last_extruder_id = lt . extruders . back ( ) ;
}
// Reindex the extruders, so they are zero based, not 1 based.
2017-05-23 13:00:01 +00:00
for ( LayerTools & lt : m_layer_tools )
2017-05-16 11:45:28 +00:00
for ( unsigned int & extruder_id : lt . extruders ) {
assert ( extruder_id > 0 ) ;
- - extruder_id ;
}
}
2018-05-24 15:24:37 +00:00
2017-06-08 14:58:29 +00:00
void ToolOrdering : : fill_wipe_tower_partitions ( const PrintConfig & config , coordf_t object_bottom_z )
2017-05-16 11:45:28 +00:00
{
2017-05-23 13:00:01 +00:00
if ( m_layer_tools . empty ( ) )
2017-05-16 11:45:28 +00:00
return ;
// Count the minimum number of tool changes per layer.
2017-05-16 13:30:03 +00:00
size_t last_extruder = size_t ( - 1 ) ;
2017-05-23 13:00:01 +00:00
for ( LayerTools & lt : m_layer_tools ) {
2017-05-17 14:45:37 +00:00
lt . wipe_tower_partitions = lt . extruders . size ( ) ;
2017-05-16 13:30:03 +00:00
if ( ! lt . extruders . empty ( ) ) {
if ( last_extruder = = size_t ( - 1 ) | | last_extruder = = lt . extruders . front ( ) )
// The first extruder on this layer is equal to the current one, no need to do an initial tool change.
- - lt . wipe_tower_partitions ;
last_extruder = lt . extruders . back ( ) ;
}
}
2017-05-16 11:45:28 +00:00
// Propagate the wipe tower partitions down to support the upper partitions by the lower partitions.
2017-05-23 13:00:01 +00:00
for ( int i = int ( m_layer_tools . size ( ) ) - 2 ; i > = 0 ; - - i )
m_layer_tools [ i ] . wipe_tower_partitions = std : : max ( m_layer_tools [ i + 1 ] . wipe_tower_partitions , m_layer_tools [ i ] . wipe_tower_partitions ) ;
2017-05-19 17:24:21 +00:00
//FIXME this is a hack to get the ball rolling.
2017-05-23 13:00:01 +00:00
for ( LayerTools & lt : m_layer_tools )
2017-06-08 14:58:29 +00:00
lt . has_wipe_tower = ( lt . has_object & & lt . wipe_tower_partitions > 0 ) | | lt . print_z < object_bottom_z + EPSILON ;
// Test for a raft, insert additional wipe tower layer to fill in the raft separation gap.
2017-06-09 12:24:00 +00:00
double max_layer_height = std : : numeric_limits < double > : : max ( ) ;
2017-06-08 14:58:29 +00:00
for ( size_t i = 0 ; i < config . nozzle_diameter . values . size ( ) ; + + i ) {
double mlh = config . max_layer_height . values [ i ] ;
if ( mlh = = 0. )
mlh = 0.75 * config . nozzle_diameter . values [ i ] ;
max_layer_height = std : : min ( max_layer_height , mlh ) ;
}
for ( size_t i = 0 ; i + 1 < m_layer_tools . size ( ) ; + + i ) {
const LayerTools & lt = m_layer_tools [ i ] ;
const LayerTools & lt_next = m_layer_tools [ i + 1 ] ;
if ( lt . print_z < object_bottom_z + EPSILON & & lt_next . print_z > = object_bottom_z + EPSILON ) {
// lt is the last raft layer. Find the 1st object layer.
size_t j = i + 1 ;
for ( ; j < m_layer_tools . size ( ) & & ! m_layer_tools [ j ] . has_wipe_tower ; + + j ) ;
if ( j < m_layer_tools . size ( ) ) {
const LayerTools & lt_object = m_layer_tools [ j ] ;
coordf_t gap = lt_object . print_z - lt . print_z ;
assert ( gap > 0.f ) ;
if ( gap > max_layer_height + EPSILON ) {
// Insert one additional wipe tower layer between lh.print_z and lt_object.print_z.
LayerTools lt_new ( 0.5f * ( lt . print_z + lt_object . print_z ) ) ;
// Find the 1st layer above lt_new.
2017-12-13 09:32:25 +00:00
for ( j = i + 1 ; j < m_layer_tools . size ( ) & & m_layer_tools [ j ] . print_z < lt_new . print_z - EPSILON ; + + j ) ;
if ( std : : abs ( m_layer_tools [ j ] . print_z - lt_new . print_z ) < EPSILON ) {
2017-12-11 16:19:55 +00:00
m_layer_tools [ j ] . has_wipe_tower = true ;
} else {
LayerTools & lt_extra = * m_layer_tools . insert ( m_layer_tools . begin ( ) + j , lt_new ) ;
2019-08-02 14:49:22 +00:00
//LayerTools <_prev = m_layer_tools[j];
2017-12-11 16:19:55 +00:00
LayerTools & lt_next = m_layer_tools [ j + 1 ] ;
2019-06-25 11:06:04 +00:00
assert ( ! m_layer_tools [ j - 1 ] . extruders . empty ( ) & & ! lt_next . extruders . empty ( ) ) ;
2019-05-22 14:43:14 +00:00
// FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now.
// If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might
// still be worth looking into it more and decide if it is a bug or an obsolete assert.
//assert(lt_prev.extruders.back() == lt_next.extruders.front());
2019-08-16 14:31:05 +00:00
lt_extra . has_wipe_tower = true ;
2017-12-11 16:19:55 +00:00
lt_extra . extruders . push_back ( lt_next . extruders . front ( ) ) ;
2019-08-16 14:31:05 +00:00
lt_extra . wipe_tower_partitions = lt_next . wipe_tower_partitions ;
}
2017-06-08 14:58:29 +00:00
}
}
break ;
}
}
// Calculate the wipe_tower_layer_height values.
coordf_t wipe_tower_print_z_last = 0. ;
for ( LayerTools & lt : m_layer_tools )
if ( lt . has_wipe_tower ) {
lt . wipe_tower_layer_height = lt . print_z - wipe_tower_print_z_last ;
wipe_tower_print_z_last = lt . print_z ;
}
2017-05-16 11:45:28 +00:00
}
2017-09-01 15:30:18 +00:00
void ToolOrdering : : collect_extruder_statistics ( bool prime_multi_material )
{
m_first_printing_extruder = ( unsigned int ) - 1 ;
for ( const auto & lt : m_layer_tools )
if ( ! lt . extruders . empty ( ) ) {
m_first_printing_extruder = lt . extruders . front ( ) ;
break ;
}
m_last_printing_extruder = ( unsigned int ) - 1 ;
for ( auto lt_it = m_layer_tools . rbegin ( ) ; lt_it ! = m_layer_tools . rend ( ) ; + + lt_it )
if ( ! lt_it - > extruders . empty ( ) ) {
m_last_printing_extruder = lt_it - > extruders . back ( ) ;
break ;
}
m_all_printing_extruders . clear ( ) ;
for ( const auto & lt : m_layer_tools ) {
append ( m_all_printing_extruders , lt . extruders ) ;
sort_remove_duplicates ( m_all_printing_extruders ) ;
}
if ( prime_multi_material & & ! m_all_printing_extruders . empty ( ) ) {
// Reorder m_all_printing_extruders in the sequence they will be primed, the last one will be m_first_printing_extruder.
// Then set m_first_printing_extruder to the 1st extruder primed.
m_all_printing_extruders . erase (
2019-08-16 14:31:05 +00:00
std : : remove_if ( m_all_printing_extruders . begin ( ) , m_all_printing_extruders . end ( ) ,
2017-09-01 15:30:18 +00:00
[ this ] ( const unsigned int eid ) { return eid = = m_first_printing_extruder ; } ) ,
m_all_printing_extruders . end ( ) ) ;
m_all_printing_extruders . emplace_back ( m_first_printing_extruder ) ;
m_first_printing_extruder = m_all_printing_extruders . front ( ) ;
}
}
2018-06-20 10:52:00 +00:00
2018-06-21 08:16:52 +00:00
// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
2018-06-27 12:08:46 +00:00
void WipingExtrusions : : set_extruder_override ( const ExtrusionEntity * entity , unsigned int copy_id , int extruder , unsigned int num_of_copies )
{
2018-06-21 08:16:52 +00:00
something_overridden = true ;
2018-06-20 10:52:00 +00:00
2018-06-21 08:16:52 +00:00
auto entity_map_it = ( entity_map . insert ( std : : make_pair ( entity , std : : vector < int > ( ) ) ) ) . first ; // (add and) return iterator
auto & copies_vector = entity_map_it - > second ;
if ( copies_vector . size ( ) < num_of_copies )
copies_vector . resize ( num_of_copies , - 1 ) ;
2018-06-20 10:52:00 +00:00
2018-06-21 08:16:52 +00:00
if ( copies_vector [ copy_id ] ! = - 1 )
std : : cout < < " ERROR: Entity extruder overriden multiple times!!! \n " ; // A debugging message - this must never happen.
2018-06-20 10:52:00 +00:00
2018-06-21 08:16:52 +00:00
copies_vector [ copy_id ] = extruder ;
}
2018-06-20 10:52:00 +00:00
2018-06-29 10:26:22 +00:00
// Finds first non-soluble extruder on the layer
2018-07-11 12:46:13 +00:00
int WipingExtrusions : : first_nonsoluble_extruder_on_layer ( const PrintConfig & print_config ) const
2018-06-29 10:26:22 +00:00
{
2018-07-11 12:46:13 +00:00
const LayerTools & lt = * m_layer_tools ;
2018-06-29 10:26:22 +00:00
for ( auto extruders_it = lt . extruders . begin ( ) ; extruders_it ! = lt . extruders . end ( ) ; + + extruders_it )
if ( ! print_config . filament_soluble . get_at ( * extruders_it ) )
return ( * extruders_it ) ;
return ( - 1 ) ;
}
2018-06-26 12:12:25 +00:00
// Finds last non-soluble extruder on the layer
2018-07-11 12:46:13 +00:00
int WipingExtrusions : : last_nonsoluble_extruder_on_layer ( const PrintConfig & print_config ) const
2018-06-27 12:08:46 +00:00
{
2018-07-11 12:46:13 +00:00
const LayerTools & lt = * m_layer_tools ;
2018-06-26 12:12:25 +00:00
for ( auto extruders_it = lt . extruders . rbegin ( ) ; extruders_it ! = lt . extruders . rend ( ) ; + + extruders_it )
if ( ! print_config . filament_soluble . get_at ( * extruders_it ) )
2018-06-29 10:26:22 +00:00
return ( * extruders_it ) ;
return ( - 1 ) ;
2018-06-26 12:12:25 +00:00
}
// Decides whether this entity could be overridden
2018-06-27 12:08:46 +00:00
bool WipingExtrusions : : is_overriddable ( const ExtrusionEntityCollection & eec , const PrintConfig & print_config , const PrintObject & object , const PrintRegion & region ) const
{
2018-07-11 12:46:13 +00:00
if ( print_config . filament_soluble . get_at ( Print : : get_extruder ( eec , region ) ) )
2018-06-27 12:08:46 +00:00
return false ;
2018-09-12 09:59:02 +00:00
if ( object . config ( ) . wipe_into_objects )
2018-06-27 12:08:46 +00:00
return true ;
2018-09-12 09:59:02 +00:00
if ( ! region . config ( ) . wipe_into_infill | | eec . role ( ) ! = erInternalInfill )
2018-06-27 12:08:46 +00:00
return false ;
2018-06-26 12:12:25 +00:00
return true ;
}
// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
// and returns volume that is left to be wiped on the wipe tower.
2018-07-18 09:05:39 +00:00
float WipingExtrusions : : mark_wiping_extrusions ( const Print & print , unsigned int old_extruder , unsigned int new_extruder , float volume_to_wipe )
2018-06-26 12:12:25 +00:00
{
2018-07-13 09:25:22 +00:00
const LayerTools & lt = * m_layer_tools ;
2018-06-26 12:12:25 +00:00
const float min_infill_volume = 0.f ; // ignore infill with smaller volume than this
2018-09-12 09:59:02 +00:00
if ( print . config ( ) . filament_soluble . get_at ( old_extruder ) | | print . config ( ) . filament_soluble . get_at ( new_extruder ) )
2018-07-18 09:05:39 +00:00
return volume_to_wipe ; // Soluble filament cannot be wiped in a random infill, neither the filament after it
2018-06-26 12:12:25 +00:00
// we will sort objects so that dedicated for wiping are at the beginning:
2018-11-08 13:23:17 +00:00
PrintObjectPtrs object_list = print . objects ( ) ;
2018-09-12 09:59:02 +00:00
std : : sort ( object_list . begin ( ) , object_list . end ( ) , [ ] ( const PrintObject * a , const PrintObject * b ) { return a - > config ( ) . wipe_into_objects ; } ) ;
2018-06-26 12:12:25 +00:00
// We will now iterate through
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
// - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
// - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
// this is controlled by the following variable:
bool perimeters_done = false ;
2018-06-29 10:26:22 +00:00
for ( int i = 0 ; i < ( int ) object_list . size ( ) + ( perimeters_done ? 0 : 1 ) ; + + i ) {
2018-09-12 09:59:02 +00:00
if ( ! perimeters_done & & ( i = = ( int ) object_list . size ( ) | | ! object_list [ i ] - > config ( ) . wipe_into_objects ) ) { // we passed the last dedicated object in list
2018-06-26 12:12:25 +00:00
perimeters_done = true ;
i = - 1 ; // let's go from the start again
continue ;
}
2018-06-29 10:26:22 +00:00
const auto & object = object_list [ i ] ;
2018-06-26 12:12:25 +00:00
// Finds this layer:
2018-09-12 09:59:02 +00:00
auto this_layer_it = std : : find_if ( object - > layers ( ) . begin ( ) , object - > layers ( ) . end ( ) , [ & lt ] ( const Layer * lay ) { return std : : abs ( lt . print_z - lay - > print_z ) < EPSILON ; } ) ;
if ( this_layer_it = = object - > layers ( ) . end ( ) )
2018-06-26 12:12:25 +00:00
continue ;
const Layer * this_layer = * this_layer_it ;
2018-09-12 09:59:02 +00:00
unsigned int num_of_copies = object - > copies ( ) . size ( ) ;
2018-06-26 12:12:25 +00:00
for ( unsigned int copy = 0 ; copy < num_of_copies ; + + copy ) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
2018-11-07 14:17:29 +00:00
for ( size_t region_id = 0 ; region_id < object - > region_volumes . size ( ) ; + + region_id ) {
2018-09-12 09:59:02 +00:00
const auto & region = * object - > print ( ) - > regions ( ) [ region_id ] ;
2018-06-26 12:12:25 +00:00
2018-09-12 09:59:02 +00:00
if ( ! region . config ( ) . wipe_into_infill & & ! object - > config ( ) . wipe_into_objects )
2018-06-26 12:12:25 +00:00
continue ;
2018-09-12 09:59:02 +00:00
if ( ( ! print . config ( ) . infill_first ? perimeters_done : ! perimeters_done ) | | ( ! object - > config ( ) . wipe_into_objects & & region . config ( ) . wipe_into_infill ) ) {
for ( const ExtrusionEntity * ee : this_layer - > regions ( ) [ region_id ] - > fills . entities ) { // iterate through all infill Collections
2018-06-26 12:12:25 +00:00
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
2018-09-12 09:59:02 +00:00
if ( ! is_overriddable ( * fill , print . config ( ) , * object , region ) )
2018-06-26 12:12:25 +00:00
continue ;
2018-06-29 10:26:22 +00:00
if ( volume_to_wipe < = 0 )
2018-06-26 12:12:25 +00:00
continue ;
2018-09-12 09:59:02 +00:00
if ( ! object - > config ( ) . wipe_into_objects & & ! print . config ( ) . infill_first & & region . config ( ) . wipe_into_infill )
2018-06-26 12:12:25 +00:00
// In this case we must check that the original extruder is used on this layer before the one we are overridding
// (and the perimeters will be finished before the infill is printed):
2018-09-12 09:59:02 +00:00
if ( ! lt . is_extruder_order ( region . config ( ) . perimeter_extruder - 1 , new_extruder ) )
2018-07-13 09:25:22 +00:00
continue ;
2018-06-26 12:12:25 +00:00
2018-06-29 10:26:22 +00:00
if ( ( ! is_entity_overridden ( fill , copy ) & & fill - > total_volume ( ) > min_infill_volume ) ) { // this infill will be used to wipe this extruder
2018-06-26 12:12:25 +00:00
set_extruder_override ( fill , copy , new_extruder , num_of_copies ) ;
volume_to_wipe - = fill - > total_volume ( ) ;
}
}
}
2018-06-27 13:07:37 +00:00
// Now the same for perimeters - see comments above for explanation:
2018-09-12 09:59:02 +00:00
if ( object - > config ( ) . wipe_into_objects & & ( print . config ( ) . infill_first ? perimeters_done : ! perimeters_done ) )
2018-06-26 12:12:25 +00:00
{
2018-09-12 09:59:02 +00:00
for ( const ExtrusionEntity * ee : this_layer - > regions ( ) [ region_id ] - > perimeters . entities ) {
2018-06-26 12:12:25 +00:00
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
2018-09-12 09:59:02 +00:00
if ( ! is_overriddable ( * fill , print . config ( ) , * object , region ) )
2018-06-27 13:07:37 +00:00
continue ;
2018-06-29 10:26:22 +00:00
if ( volume_to_wipe < = 0 )
2018-06-26 12:12:25 +00:00
continue ;
2018-06-29 10:26:22 +00:00
if ( ( ! is_entity_overridden ( fill , copy ) & & fill - > total_volume ( ) > min_infill_volume ) ) {
2018-06-26 12:12:25 +00:00
set_extruder_override ( fill , copy , new_extruder , num_of_copies ) ;
volume_to_wipe - = fill - > total_volume ( ) ;
}
}
}
}
}
}
return std : : max ( 0.f , volume_to_wipe ) ;
}
2018-06-29 10:26:22 +00:00
// Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
// that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
// they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
// them again and make sure we override it.
2018-07-11 12:46:13 +00:00
void WipingExtrusions : : ensure_perimeters_infills_order ( const Print & print )
2018-06-29 10:26:22 +00:00
{
2018-07-13 09:25:22 +00:00
const LayerTools & lt = * m_layer_tools ;
2018-09-12 09:59:02 +00:00
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer ( print . config ( ) ) ;
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer ( print . config ( ) ) ;
2018-06-29 10:26:22 +00:00
2018-11-08 13:23:17 +00:00
for ( const PrintObject * object : print . objects ( ) ) {
2018-06-29 10:26:22 +00:00
// Finds this layer:
2018-09-12 09:59:02 +00:00
auto this_layer_it = std : : find_if ( object - > layers ( ) . begin ( ) , object - > layers ( ) . end ( ) , [ & lt ] ( const Layer * lay ) { return std : : abs ( lt . print_z - lay - > print_z ) < EPSILON ; } ) ;
if ( this_layer_it = = object - > layers ( ) . end ( ) )
2018-06-29 10:26:22 +00:00
continue ;
const Layer * this_layer = * this_layer_it ;
2018-09-12 09:59:02 +00:00
unsigned int num_of_copies = object - > copies ( ) . size ( ) ;
2018-06-29 10:26:22 +00:00
for ( unsigned int copy = 0 ; copy < num_of_copies ; + + copy ) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
2018-11-07 14:17:29 +00:00
for ( size_t region_id = 0 ; region_id < object - > region_volumes . size ( ) ; + + region_id ) {
2018-09-12 09:59:02 +00:00
const auto & region = * object - > print ( ) - > regions ( ) [ region_id ] ;
2018-06-29 10:26:22 +00:00
2018-09-12 09:59:02 +00:00
if ( ! region . config ( ) . wipe_into_infill & & ! object - > config ( ) . wipe_into_objects )
2018-06-29 10:26:22 +00:00
continue ;
2018-09-12 09:59:02 +00:00
for ( const ExtrusionEntity * ee : this_layer - > regions ( ) [ region_id ] - > fills . entities ) { // iterate through all infill Collections
2018-06-29 10:26:22 +00:00
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
2018-09-12 09:59:02 +00:00
if ( ! is_overriddable ( * fill , print . config ( ) , * object , region )
2018-06-29 10:26:22 +00:00
| | is_entity_overridden ( fill , copy ) )
continue ;
2018-07-13 09:25:22 +00:00
// This infill could have been overridden but was not - unless we do something, it could be
2018-06-29 10:26:22 +00:00
// printed before its perimeter, or not be printed at all (in case its original extruder has
// not been added to LayerTools
// Either way, we will now force-override it with something suitable:
2018-09-12 09:59:02 +00:00
if ( print . config ( ) . infill_first
| | object - > config ( ) . wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
| | lt . is_extruder_order ( region . config ( ) . perimeter_extruder - 1 , last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
| | std : : find ( lt . extruders . begin ( ) , lt . extruders . end ( ) , region . config ( ) . infill_extruder - 1 ) = = lt . extruders . end ( ) ) // we have to force override - this could violate infill_first (FIXME)
2018-07-13 09:25:22 +00:00
)
2018-09-12 09:59:02 +00:00
set_extruder_override ( fill , copy , ( print . config ( ) . infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder ) , num_of_copies ) ;
2018-07-13 09:25:22 +00:00
else {
// In this case we can (and should) leave it to be printed normally.
// Force overriding would mean it gets printed before its perimeter.
}
2018-06-29 10:26:22 +00:00
}
// Now the same for perimeters - see comments above for explanation:
2018-09-12 09:59:02 +00:00
for ( const ExtrusionEntity * ee : this_layer - > regions ( ) [ region_id ] - > perimeters . entities ) { // iterate through all perimeter Collections
2018-06-29 10:26:22 +00:00
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
2018-09-12 09:59:02 +00:00
if ( ! is_overriddable ( * fill , print . config ( ) , * object , region )
2018-06-29 10:26:22 +00:00
| | is_entity_overridden ( fill , copy ) )
continue ;
2018-09-12 09:59:02 +00:00
set_extruder_override ( fill , copy , ( print . config ( ) . infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder ) , num_of_copies ) ;
2018-06-29 10:26:22 +00:00
}
}
}
}
}
2018-06-20 10:52:00 +00:00
2018-06-21 08:16:52 +00:00
// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
// so -1 was used as "print as usual".
// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
2018-06-27 12:08:46 +00:00
const std : : vector < int > * WipingExtrusions : : get_extruder_overrides ( const ExtrusionEntity * entity , int correct_extruder_id , int num_of_copies )
{
2018-06-21 08:16:52 +00:00
auto entity_map_it = entity_map . find ( entity ) ;
if ( entity_map_it = = entity_map . end ( ) )
entity_map_it = ( entity_map . insert ( std : : make_pair ( entity , std : : vector < int > ( ) ) ) ) . first ;
2018-06-20 10:52:00 +00:00
2018-06-21 08:16:52 +00:00
// Now the entity_map_it should be valid, let's make sure the vector is long enough:
entity_map_it - > second . resize ( num_of_copies , - 1 ) ;
// Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
std : : replace ( entity_map_it - > second . begin ( ) , entity_map_it - > second . end ( ) , - 1 , - correct_extruder_id - 1 ) ;
return & ( entity_map_it - > second ) ;
}
2019-08-16 14:31:05 +00:00
2018-06-20 10:52:00 +00:00
2017-05-16 11:45:28 +00:00
} // namespace Slic3r