2022-07-15 15:37:19 +00:00
# include "Print.hpp"
# include "ToolOrdering.hpp"
# include "Layer.hpp"
2023-12-21 09:09:19 +00:00
# include "ClipperUtils.hpp"
2024-02-27 02:55:45 +00:00
# include "ParameterUtils.hpp"
2022-07-15 15:37:19 +00:00
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
# ifdef SLIC3R_DEBUG
# define DEBUG
# define _DEBUG
# undef NDEBUG
# endif
# include <cassert>
# include <limits>
2022-11-07 07:09:35 +00:00
# include <algorithm>
2024-07-15 12:00:03 +00:00
# include <unordered_map>
2022-07-15 15:37:19 +00:00
# include <libslic3r.h>
2024-07-15 12:00:03 +00:00
# include <boost/multiprecision/cpp_int.hpp>
2022-07-15 15:37:19 +00:00
namespace Slic3r {
const static bool g_wipe_into_objects = false ;
2023-11-29 02:54:40 +00:00
2024-07-15 12:00:03 +00:00
2024-07-18 08:38:26 +00:00
static std : : vector < unsigned int > solve_extruder_order_with_greedy ( const std : : vector < std : : vector < float > > & wipe_volumes ,
const std : : vector < unsigned int > curr_layer_extruders ,
const std : : optional < unsigned int > & start_extruder_id ,
float * min_cost )
{
float cost = 0 ;
std : : vector < unsigned int > best_seq ;
std : : vector < bool > is_visited ( curr_layer_extruders . size ( ) , false ) ;
std : : optional < unsigned int > prev_filament = start_extruder_id ;
int idx = curr_layer_extruders . size ( ) ;
while ( idx > 0 ) {
if ( ! prev_filament ) {
auto iter = std : : find_if ( is_visited . begin ( ) , is_visited . end ( ) , [ ] ( auto item ) { return item = = 0 ; } ) ;
assert ( iter ! = is_visited . end ( ) ) ;
prev_filament = curr_layer_extruders [ iter - is_visited . begin ( ) ] ;
}
int target_idx = - 1 ;
int target_cost = std : : numeric_limits < int > : : max ( ) ;
for ( size_t k = 0 ; k < is_visited . size ( ) ; + + k ) {
if ( ! is_visited [ k ] ) {
if ( wipe_volumes [ * prev_filament ] [ curr_layer_extruders [ k ] ] < target_cost ) {
target_idx = k ;
target_cost = wipe_volumes [ * prev_filament ] [ curr_layer_extruders [ k ] ] ;
}
}
}
assert ( target_idx ! = - 1 ) ;
cost + = target_cost ;
best_seq . emplace_back ( curr_layer_extruders [ target_idx ] ) ;
prev_filament = curr_layer_extruders [ target_idx ] ;
is_visited [ target_idx ] = true ;
idx - = 1 ;
}
if ( min_cost )
* min_cost = cost ;
return best_seq ;
}
2024-07-15 12:00:03 +00:00
//solve the probleme by forcasting one layer
2024-07-18 08:38:26 +00:00
static std : : vector < unsigned int > solve_extruder_order_with_forcast ( const std : : vector < std : : vector < float > > & wipe_volumes ,
2024-07-15 12:00:03 +00:00
std : : vector < unsigned int > curr_layer_extruders ,
std : : vector < unsigned int > next_layer_extruders ,
const std : : optional < unsigned int > & start_extruder_id ,
float * min_cost )
{
std : : sort ( curr_layer_extruders . begin ( ) , curr_layer_extruders . end ( ) ) ;
std : : sort ( next_layer_extruders . begin ( ) , next_layer_extruders . end ( ) ) ;
float best_cost = std : : numeric_limits < float > : : max ( ) ;
std : : vector < unsigned int > best_seq ;
do {
std : : optional < unsigned int > prev_extruder_1 = start_extruder_id ;
float curr_layer_cost = 0 ;
for ( size_t idx = 0 ; idx < curr_layer_extruders . size ( ) ; + + idx ) {
if ( prev_extruder_1 )
curr_layer_cost + = wipe_volumes [ * prev_extruder_1 ] [ curr_layer_extruders [ idx ] ] ;
prev_extruder_1 = curr_layer_extruders [ idx ] ;
}
if ( curr_layer_cost > best_cost )
continue ;
do {
std : : optional < unsigned int > prev_extruder_2 = prev_extruder_1 ;
float total_cost = curr_layer_cost ;
for ( size_t idx = 0 ; idx < next_layer_extruders . size ( ) ; + + idx ) {
if ( prev_extruder_2 )
total_cost + = wipe_volumes [ * prev_extruder_2 ] [ next_layer_extruders [ idx ] ] ;
prev_extruder_2 = next_layer_extruders [ idx ] ;
}
if ( total_cost < best_cost ) {
best_cost = total_cost ;
best_seq = curr_layer_extruders ;
}
} while ( std : : next_permutation ( next_layer_extruders . begin ( ) , next_layer_extruders . end ( ) ) ) ;
} while ( std : : next_permutation ( curr_layer_extruders . begin ( ) , curr_layer_extruders . end ( ) ) ) ;
if ( min_cost ) {
float real_cost = 0 ;
std : : optional < unsigned int > prev_extruder = start_extruder_id ;
for ( size_t idx = 0 ; idx < best_seq . size ( ) ; + + idx ) {
if ( prev_extruder )
real_cost + = wipe_volumes [ * prev_extruder ] [ best_seq [ idx ] ] ;
prev_extruder = best_seq [ idx ] ;
}
* min_cost = real_cost ;
}
return best_seq ;
}
2023-11-29 02:54:40 +00:00
// Shortest hamilton path problem
2024-06-26 11:30:37 +00:00
static std : : vector < unsigned int > solve_extruder_order ( const std : : vector < std : : vector < float > > & wipe_volumes , std : : vector < unsigned int > all_extruders , std : : optional < unsigned int > start_extruder_id , float * min_cost )
2023-11-29 02:54:40 +00:00
{
bool add_start_extruder_flag = false ;
2024-05-17 05:11:26 +00:00
if ( start_extruder_id ) {
auto start_iter = std : : find ( all_extruders . begin ( ) , all_extruders . end ( ) , start_extruder_id ) ;
if ( start_iter = = all_extruders . end ( ) )
all_extruders . insert ( all_extruders . begin ( ) , * start_extruder_id ) , add_start_extruder_flag = true ;
else
std : : swap ( * all_extruders . begin ( ) , * start_iter ) ;
}
else {
2024-06-26 11:30:37 +00:00
start_extruder_id = all_extruders . front ( ) ;
2024-05-17 05:11:26 +00:00
}
2023-11-29 02:54:40 +00:00
unsigned int iterations = ( 1 < < all_extruders . size ( ) ) ;
unsigned int final_state = iterations - 1 ;
std : : vector < std : : vector < float > > cache ( iterations , std : : vector < float > ( all_extruders . size ( ) , 0x7fffffff ) ) ;
std : : vector < std : : vector < int > > prev ( iterations , std : : vector < int > ( all_extruders . size ( ) , - 1 ) ) ;
cache [ 1 ] [ 0 ] = 0. ;
for ( unsigned int state = 0 ; state < iterations ; + + state ) {
if ( state & 1 ) {
for ( unsigned int target = 0 ; target < all_extruders . size ( ) ; + + target ) {
if ( state > > target & 1 ) {
for ( unsigned int mid_point = 0 ; mid_point < all_extruders . size ( ) ; + + mid_point ) {
if ( state > > mid_point & 1 ) {
auto tmp = cache [ state - ( 1 < < target ) ] [ mid_point ] + wipe_volumes [ all_extruders [ mid_point ] ] [ all_extruders [ target ] ] ;
if ( cache [ state ] [ target ] > tmp ) {
cache [ state ] [ target ] = tmp ;
prev [ state ] [ target ] = mid_point ;
}
}
}
}
}
}
}
//get res
float cost = std : : numeric_limits < float > : : max ( ) ;
int final_dst = 0 ;
for ( unsigned int dst = 0 ; dst < all_extruders . size ( ) ; + + dst ) {
if ( all_extruders [ dst ] ! = start_extruder_id & & cost > cache [ final_state ] [ dst ] ) {
cost = cache [ final_state ] [ dst ] ;
2024-06-26 11:30:37 +00:00
if ( min_cost )
* min_cost = cost ;
2023-11-29 02:54:40 +00:00
final_dst = dst ;
}
}
std : : vector < unsigned int > path ;
unsigned int curr_state = final_state ;
int curr_point = final_dst ;
while ( curr_point ! = - 1 ) {
path . emplace_back ( all_extruders [ curr_point ] ) ;
auto mid_point = prev [ curr_state ] [ curr_point ] ;
curr_state - = ( 1 < < curr_point ) ;
curr_point = mid_point ;
} ;
if ( add_start_extruder_flag )
path . pop_back ( ) ;
std : : reverse ( path . begin ( ) , path . end ( ) ) ;
return path ;
}
2024-07-15 12:00:03 +00:00
std : : vector < unsigned int > get_extruders_order ( const std : : vector < std : : vector < float > > & wipe_volumes ,
const std : : vector < unsigned int > & curr_layer_extruders ,
const std : : vector < unsigned int > & next_layer_extruders ,
const std : : optional < unsigned int > & start_extruder_id ,
bool use_forcast = false ,
float * cost = nullptr )
2022-09-30 03:54:44 +00:00
{
2024-07-15 12:00:03 +00:00
if ( curr_layer_extruders . size ( ) < = 1 ) {
2024-06-26 11:30:37 +00:00
if ( cost )
* cost = 0 ;
2024-07-15 12:00:03 +00:00
return curr_layer_extruders ;
2024-06-26 11:30:37 +00:00
}
2024-07-15 12:00:03 +00:00
if ( use_forcast )
return solve_extruder_order_with_forcast ( wipe_volumes , curr_layer_extruders , next_layer_extruders , start_extruder_id , cost ) ;
2024-07-18 08:38:26 +00:00
else if ( curr_layer_extruders . size ( ) < = 20 )
2024-07-15 12:00:03 +00:00
return solve_extruder_order ( wipe_volumes , curr_layer_extruders , start_extruder_id , cost ) ;
2024-07-18 08:38:26 +00:00
else
return solve_extruder_order_with_greedy ( wipe_volumes , curr_layer_extruders , start_extruder_id , cost ) ;
2022-09-30 03:54:44 +00:00
}
2024-07-08 13:31:28 +00:00
int reorder_filaments_for_minimum_flush_volume ( const std : : vector < unsigned int > & filament_lists ,
const std : : vector < int > & filament_maps ,
const std : : vector < std : : vector < unsigned int > > & layer_filaments ,
const std : : vector < FlushMatrix > & flush_matrix ,
std : : optional < std : : function < bool ( int , std : : vector < int > & ) > > get_custom_seq ,
std : : vector < std : : vector < unsigned int > > * filament_sequences )
{
2024-07-15 12:00:03 +00:00
constexpr int max_n_with_forcast = 5 ;
2024-07-08 13:31:28 +00:00
int cost = 0 ;
2024-07-15 12:00:03 +00:00
std : : vector < std : : set < unsigned int > > groups ( 2 ) ; //save the grouped filaments
std : : vector < std : : vector < std : : vector < unsigned int > > > layer_sequences ( 2 ) ; //save the reordered filament sequence by group
2024-07-18 04:03:08 +00:00
std : : map < size_t , std : : vector < int > > custom_layer_filament_map ; //save the custom layers,second key stores the last extruder of that layer by group
std : : map < size_t , std : : vector < unsigned int > > custom_layer_sequence_map ; // save the filament sequences of custom layer
2024-07-08 13:31:28 +00:00
2024-07-15 12:00:03 +00:00
// group the filament
2024-07-08 13:31:28 +00:00
for ( int i = 0 ; i < filament_maps . size ( ) ; + + i ) {
if ( filament_maps [ i ] = = 0 )
groups [ 0 ] . insert ( filament_lists [ i ] ) ;
if ( filament_maps [ i ] = = 1 )
groups [ 1 ] . insert ( filament_lists [ i ] ) ;
}
2024-07-15 12:00:03 +00:00
// store custom layer sequence
for ( size_t layer = 0 ; layer < layer_filaments . size ( ) ; + + layer ) {
const auto & curr_lf = layer_filaments [ layer ] ;
std : : vector < int > custom_filament_seq ;
if ( get_custom_seq & & ( * get_custom_seq ) ( layer , custom_filament_seq ) & & ! custom_filament_seq . empty ( ) ) {
std : : vector < unsigned int > unsign_custom_extruder_seq ;
for ( int extruder : custom_filament_seq ) {
unsigned int unsign_extruder = static_cast < unsigned int > ( extruder ) - 1 ;
auto it = std : : find ( curr_lf . begin ( ) , curr_lf . end ( ) , unsign_extruder ) ;
if ( it ! = curr_lf . end ( ) )
unsign_custom_extruder_seq . emplace_back ( unsign_extruder ) ;
}
assert ( curr_lf . size ( ) = = unsign_custom_extruder_seq . size ( ) ) ;
custom_layer_sequence_map [ layer ] = unsign_custom_extruder_seq ;
custom_layer_filament_map [ layer ] . resize ( 2 , - 1 ) ;
for ( auto iter = unsign_custom_extruder_seq . rbegin ( ) ; iter ! = unsign_custom_extruder_seq . rend ( ) ; + + iter ) {
if ( groups [ 0 ] . find ( * iter ) ! = groups [ 0 ] . end ( ) & & custom_layer_filament_map [ layer ] [ 0 ] = = - 1 )
custom_layer_filament_map [ layer ] [ 0 ] = * iter ;
if ( groups [ 1 ] . find ( * iter ) ! = groups [ 1 ] . end ( ) & & custom_layer_filament_map [ layer ] [ 1 ] = = - 1 )
custom_layer_filament_map [ layer ] [ 1 ] = * iter ;
}
}
}
using uint128_t = boost : : multiprecision : : uint128_t ;
auto extruders_to_hash_key = [ ] ( const std : : vector < unsigned int > & curr_layer_extruders ,
const std : : vector < unsigned int > & next_layer_extruders ,
const std : : optional < unsigned int > & prev_extruder ,
bool use_forcast ) - > uint128_t
{
uint128_t hash_key = 0 ;
//31-0 bit define current layer extruder,63-32 bit define next layer extruder,95~64 define prev extruder
if ( prev_extruder )
hash_key | = ( uint128_t ( 1 ) < < ( 64 + * prev_extruder ) ) ;
if ( use_forcast ) {
for ( auto item : next_layer_extruders )
hash_key | = ( uint128_t ( 1 ) < < ( 32 + item ) ) ;
}
for ( auto item : curr_layer_extruders )
hash_key | = ( uint128_t ( 1 ) < < item ) ;
return hash_key ;
} ;
// get best layer sequence by group
2024-07-08 13:31:28 +00:00
for ( size_t idx = 0 ; idx < groups . size ( ) ; + + idx ) {
2024-07-12 06:40:25 +00:00
// case with one group
if ( groups [ idx ] . empty ( ) )
continue ;
2024-07-15 12:00:03 +00:00
bool use_forcast = groups [ idx ] . size ( ) < = max_n_with_forcast ;
2024-07-08 13:31:28 +00:00
std : : optional < unsigned int > current_extruder_id ;
2024-07-15 12:00:03 +00:00
std : : unordered_map < uint128_t , std : : pair < float , std : : vector < unsigned int > > > caches ;
for ( size_t layer = 0 ; layer < layer_filaments . size ( ) ; + + layer ) {
const auto & curr_lf = layer_filaments [ layer ] ;
2024-07-08 13:31:28 +00:00
std : : vector < int > custom_filament_seq ;
if ( get_custom_seq & & ( * get_custom_seq ) ( layer , custom_filament_seq ) & & ! custom_filament_seq . empty ( ) ) {
2024-07-15 12:00:03 +00:00
if ( custom_layer_filament_map [ layer ] [ idx ] ! = - 1 )
current_extruder_id = ( unsigned int ) ( custom_layer_filament_map [ layer ] [ idx ] ) ;
//insert an empty array
2024-07-08 13:31:28 +00:00
if ( filament_sequences )
2024-07-15 12:00:03 +00:00
layer_sequences [ idx ] . emplace_back ( std : : vector < unsigned int > ( ) ) ;
2024-07-08 13:31:28 +00:00
continue ;
}
2024-07-15 12:00:03 +00:00
std : : vector < unsigned int > filament_used_in_group ;
for ( const auto & filament : curr_lf ) {
2024-07-08 13:31:28 +00:00
if ( groups [ idx ] . find ( filament ) ! = groups [ idx ] . end ( ) )
filament_used_in_group . emplace_back ( filament ) ;
}
2024-07-15 12:00:03 +00:00
std : : vector < unsigned int > filament_used_in_group_next_layer ;
{
std : : vector < unsigned int > next_lf ;
if ( layer + 1 < layer_filaments . size ( ) )
next_lf = layer_filaments [ layer + 1 ] ;
for ( const auto & filament : next_lf ) {
if ( groups [ idx ] . find ( filament ) ! = groups [ idx ] . end ( ) )
filament_used_in_group_next_layer . emplace_back ( filament ) ;
}
}
2024-07-08 13:31:28 +00:00
float tmp_cost = 0 ;
2024-07-15 12:00:03 +00:00
std : : vector < unsigned int > sequence ;
uint128_t hash_key = extruders_to_hash_key ( filament_used_in_group , filament_used_in_group_next_layer , current_extruder_id , use_forcast ) ;
if ( auto iter = caches . find ( hash_key ) ; iter ! = caches . end ( ) ) {
tmp_cost = iter - > second . first ;
sequence = iter - > second . second ;
}
else {
sequence = get_extruders_order ( flush_matrix [ idx ] , filament_used_in_group , filament_used_in_group_next_layer , current_extruder_id , use_forcast , & tmp_cost ) ;
caches [ hash_key ] = { tmp_cost , sequence } ;
}
2024-07-08 13:31:28 +00:00
assert ( sequence . size ( ) = = filament_used_in_group . size ( ) ) ;
if ( filament_sequences )
2024-07-10 10:40:52 +00:00
layer_sequences [ idx ] . emplace_back ( sequence ) ;
2024-07-08 13:31:28 +00:00
if ( ! sequence . empty ( ) )
current_extruder_id = sequence . back ( ) ;
cost + = tmp_cost ;
}
}
2024-07-15 12:00:03 +00:00
// get the final layer sequences
2024-07-12 06:40:25 +00:00
// if only have one group,we need to check whether layer sequence[idx] is valid
2024-07-10 10:40:52 +00:00
if ( filament_sequences ) {
filament_sequences - > clear ( ) ;
filament_sequences - > resize ( layer_filaments . size ( ) ) ;
2024-07-18 04:03:08 +00:00
bool last_group = 0 ;
//if last_group == 0,print group 0 first ,else print group 1 first
if ( ! custom_layer_sequence_map . empty ( ) ) {
int custom_first_layer = custom_layer_sequence_map . begin ( ) - > first ;
bool custom_first_group = groups [ 0 ] . count ( custom_first_layer ) ? 0 : 1 ;
last_group = ( custom_first_layer & 1 ) ? ! custom_first_group : custom_first_group ;
}
2024-07-10 10:40:52 +00:00
for ( size_t layer = 0 ; layer < layer_filaments . size ( ) ; + + layer ) {
auto & curr_layer_seq = ( * filament_sequences ) [ layer ] ;
2024-07-15 12:00:03 +00:00
if ( custom_layer_sequence_map . find ( layer ) ! = custom_layer_sequence_map . end ( ) ) {
curr_layer_seq = custom_layer_sequence_map [ layer ] ;
2024-07-18 04:03:08 +00:00
if ( ! curr_layer_seq . empty ( ) ) {
last_group = groups [ 0 ] . count ( curr_layer_seq . back ( ) ) ? 0 : 1 ;
}
2024-07-15 12:00:03 +00:00
continue ;
}
2024-07-18 04:03:08 +00:00
if ( last_group ) {
2024-07-12 06:40:25 +00:00
if ( ! layer_sequences [ 1 ] . empty ( ) )
curr_layer_seq . insert ( curr_layer_seq . end ( ) , layer_sequences [ 1 ] [ layer ] . begin ( ) , layer_sequences [ 1 ] [ layer ] . end ( ) ) ;
if ( ! layer_sequences [ 0 ] . empty ( ) )
curr_layer_seq . insert ( curr_layer_seq . end ( ) , layer_sequences [ 0 ] [ layer ] . begin ( ) , layer_sequences [ 0 ] [ layer ] . end ( ) ) ;
2024-07-10 10:40:52 +00:00
}
else {
2024-07-12 06:40:25 +00:00
if ( ! layer_sequences [ 0 ] . empty ( ) )
curr_layer_seq . insert ( curr_layer_seq . end ( ) , layer_sequences [ 0 ] [ layer ] . begin ( ) , layer_sequences [ 0 ] [ layer ] . end ( ) ) ;
if ( ! layer_sequences [ 1 ] . empty ( ) )
curr_layer_seq . insert ( curr_layer_seq . end ( ) , layer_sequences [ 1 ] [ layer ] . begin ( ) , layer_sequences [ 1 ] [ layer ] . end ( ) ) ;
2024-07-10 10:40:52 +00:00
}
2024-07-18 04:03:08 +00:00
last_group = ! last_group ;
2024-07-10 10:40:52 +00:00
}
}
2024-07-08 13:31:28 +00:00
return cost ;
}
2022-09-30 03:54:44 +00:00
2022-07-15 15:37:19 +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 ;
}
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools : : wall_filament ( const PrintRegion & region ) const
{
assert ( region . config ( ) . wall_filament . value > 0 ) ;
return ( ( this - > extruder_override = = 0 ) ? region . config ( ) . wall_filament . value : this - > extruder_override ) - 1 ;
}
unsigned int LayerTools : : sparse_infill_filament ( const PrintRegion & region ) const
{
assert ( region . config ( ) . sparse_infill_filament . value > 0 ) ;
return ( ( this - > extruder_override = = 0 ) ? region . config ( ) . sparse_infill_filament . value : this - > extruder_override ) - 1 ;
}
unsigned int LayerTools : : solid_infill_filament ( const PrintRegion & region ) const
{
assert ( region . config ( ) . solid_infill_filament . value > 0 ) ;
return ( ( this - > extruder_override = = 0 ) ? region . config ( ) . solid_infill_filament . value : this - > extruder_override ) - 1 ;
}
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int LayerTools : : extruder ( const ExtrusionEntityCollection & extrusions , const PrintRegion & region ) const
{
assert ( region . config ( ) . wall_filament . value > 0 ) ;
assert ( region . config ( ) . sparse_infill_filament . value > 0 ) ;
assert ( region . config ( ) . solid_infill_filament . value > 0 ) ;
// 1 based extruder ID.
unsigned int extruder = ( ( this - > extruder_override = = 0 ) ?
( is_infill ( extrusions . role ( ) ) ?
( is_solid_infill ( extrusions . entities . front ( ) - > role ( ) ) ? region . config ( ) . solid_infill_filament : region . config ( ) . sparse_infill_filament ) :
region . config ( ) . wall_filament . value ) :
this - > extruder_override ) ;
return ( extruder = = 0 ) ? 0 : extruder - 1 ;
}
static double calc_max_layer_height ( const PrintConfig & config , double max_object_layer_height )
{
double max_layer_height = std : : numeric_limits < double > : : max ( ) ;
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 ) ;
}
// The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed
// by the nozzle. This is a hack and it works by increasing extrusion width. See GH #3919.
return std : : max ( max_layer_height , max_object_layer_height ) ;
}
// For the use case when each object is printed separately
// (print->config().print_sequence == PrintSequence::ByObject is true).
ToolOrdering : : ToolOrdering ( const PrintObject & object , unsigned int first_extruder , bool prime_multi_material )
{
2023-12-12 14:36:48 +00:00
m_print_object_ptr = & object ;
2024-07-10 09:48:00 +00:00
m_print = const_cast < Print * > ( object . print ( ) ) ;
2022-07-15 15:37:19 +00:00
if ( object . layers ( ) . empty ( ) )
return ;
// Initialize the print layers for just a single object.
{
std : : vector < coordf_t > zs ;
zs . reserve ( zs . size ( ) + object . layers ( ) . size ( ) + object . support_layers ( ) . size ( ) ) ;
for ( auto layer : object . layers ( ) )
zs . emplace_back ( layer - > print_z ) ;
for ( auto layer : object . support_layers ( ) )
zs . emplace_back ( layer - > print_z ) ;
this - > initialize_layers ( zs ) ;
}
double max_layer_height = calc_max_layer_height ( object . print ( ) - > config ( ) , object . config ( ) . layer_height ) ;
// Collect extruders reuqired to print the layers.
this - > collect_extruders ( object , std : : vector < std : : pair < double , unsigned int > > ( ) ) ;
// BBS
// Reorder the extruders to minimize tool switches.
if ( first_extruder = = ( unsigned int ) - 1 ) {
this - > reorder_extruders ( generate_first_layer_tool_order ( object ) ) ;
}
else {
this - > reorder_extruders ( first_extruder ) ;
}
this - > fill_wipe_tower_partitions ( object . print ( ) - > config ( ) , object . layers ( ) . front ( ) - > print_z - object . layers ( ) . front ( ) - > height , max_layer_height ) ;
this - > collect_extruder_statistics ( prime_multi_material ) ;
this - > mark_skirt_layers ( object . print ( ) - > config ( ) , max_layer_height ) ;
}
// For the use case when all objects are printed at once.
// (print->config().print_sequence == PrintSequence::ByObject is false).
ToolOrdering : : ToolOrdering ( const Print & print , unsigned int first_extruder , bool prime_multi_material )
{
2024-07-02 11:26:52 +00:00
m_print = const_cast < Print * > ( & print ) ; // for update the context of print
2022-07-15 15:37:19 +00:00
m_print_config_ptr = & print . config ( ) ;
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0. ;
coordf_t max_layer_height = 0. ;
{
std : : vector < coordf_t > zs ;
for ( auto object : print . objects ( ) ) {
zs . reserve ( zs . size ( ) + object - > layers ( ) . size ( ) + object - > support_layers ( ) . size ( ) ) ;
for ( auto layer : object - > layers ( ) )
zs . emplace_back ( layer - > print_z ) ;
for ( auto layer : object - > support_layers ( ) )
zs . emplace_back ( layer - > print_z ) ;
// 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 ;
}
max_layer_height = std : : max ( max_layer_height , object - > config ( ) . layer_height . value ) ;
}
this - > initialize_layers ( zs ) ;
}
max_layer_height = calc_max_layer_height ( print . config ( ) , max_layer_height ) ;
// Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object.
// Do it only if all the objects were configured to be printed with a single extruder.
std : : vector < std : : pair < double , unsigned int > > per_layer_extruder_switches ;
// BBS
if ( auto num_filaments = unsigned ( print . config ( ) . filament_diameter . size ( ) ) ;
num_filaments > 1 & & print . object_extruders ( ) . size ( ) = = 1 & & // the current Print's configuration is CustomGCode::MultiAsSingle
2023-02-01 09:38:02 +00:00
//BBS: replace model custom gcode with current plate custom gcode
print . model ( ) . get_curr_plate_custom_gcodes ( ) . mode = = CustomGCode : : MultiAsSingle ) {
2022-07-15 15:37:19 +00:00
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model.
2023-02-01 09:38:02 +00:00
per_layer_extruder_switches = custom_tool_changes ( print . model ( ) . get_curr_plate_custom_gcodes ( ) , num_filaments ) ;
2022-07-15 15:37:19 +00:00
}
// Collect extruders reuqired to print the layers.
for ( auto object : print . objects ( ) )
this - > collect_extruders ( * object , per_layer_extruder_switches ) ;
// Reorder the extruders to minimize tool switches.
2022-09-02 08:51:17 +00:00
std : : vector < unsigned int > first_layer_tool_order ;
2022-07-15 15:37:19 +00:00
if ( first_extruder = = ( unsigned int ) - 1 ) {
2022-09-02 08:51:17 +00:00
first_layer_tool_order = generate_first_layer_tool_order ( print ) ;
}
if ( ! first_layer_tool_order . empty ( ) ) {
this - > reorder_extruders ( first_layer_tool_order ) ;
2022-07-15 15:37:19 +00:00
}
else {
this - > reorder_extruders ( first_extruder ) ;
}
this - > fill_wipe_tower_partitions ( print . config ( ) , object_bottom_z , max_layer_height ) ;
this - > collect_extruder_statistics ( prime_multi_material ) ;
this - > mark_skirt_layers ( print . config ( ) , max_layer_height ) ;
}
// BBS
std : : vector < unsigned int > ToolOrdering : : generate_first_layer_tool_order ( const Print & print )
{
std : : vector < unsigned int > tool_order ;
int initial_extruder_id = - 1 ;
std : : map < int , double > min_areas_per_extruder ;
for ( auto object : print . objects ( ) ) {
auto first_layer = object - > get_layer ( 0 ) ;
for ( auto layerm : first_layer - > regions ( ) ) {
int extruder_id = layerm - > region ( ) . config ( ) . option ( " wall_filament " ) - > getInt ( ) ;
2024-07-02 11:26:52 +00:00
2022-07-15 15:37:19 +00:00
for ( auto expoly : layerm - > raw_slices ) {
if ( offset_ex ( expoly , - 0.2 * scale_ ( print . config ( ) . initial_layer_line_width ) ) . empty ( ) )
continue ;
double contour_area = expoly . contour . area ( ) ;
auto iter = min_areas_per_extruder . find ( extruder_id ) ;
if ( iter = = min_areas_per_extruder . end ( ) ) {
min_areas_per_extruder . insert ( { extruder_id , contour_area } ) ;
}
else {
if ( contour_area < min_areas_per_extruder . at ( extruder_id ) ) {
min_areas_per_extruder [ extruder_id ] = contour_area ;
}
}
}
}
}
double max_minimal_area = 0. ;
for ( auto ape : min_areas_per_extruder ) {
auto iter = tool_order . begin ( ) ;
for ( ; iter ! = tool_order . end ( ) ; iter + + ) {
if ( min_areas_per_extruder . at ( * iter ) < min_areas_per_extruder . at ( ape . first ) )
break ;
}
tool_order . insert ( iter , ape . first ) ;
}
2023-08-08 02:54:07 +00:00
const ConfigOptionInts * first_layer_print_sequence_op = print . full_print_config ( ) . option < ConfigOptionInts > ( " first_layer_print_sequence " ) ;
if ( first_layer_print_sequence_op ) {
const std : : vector < int > & print_sequence_1st = first_layer_print_sequence_op - > values ;
if ( print_sequence_1st . size ( ) > = tool_order . size ( ) ) {
std : : sort ( tool_order . begin ( ) , tool_order . end ( ) , [ & print_sequence_1st ] ( int lh , int rh ) {
auto lh_it = std : : find ( print_sequence_1st . begin ( ) , print_sequence_1st . end ( ) , lh ) ;
auto rh_it = std : : find ( print_sequence_1st . begin ( ) , print_sequence_1st . end ( ) , rh ) ;
if ( lh_it = = print_sequence_1st . end ( ) | | rh_it = = print_sequence_1st . end ( ) )
return false ;
return lh_it < rh_it ;
} ) ;
}
}
2022-07-15 15:37:19 +00:00
return tool_order ;
}
std : : vector < unsigned int > ToolOrdering : : generate_first_layer_tool_order ( const PrintObject & object )
{
std : : vector < unsigned int > tool_order ;
int initial_extruder_id = - 1 ;
std : : map < int , double > min_areas_per_extruder ;
auto first_layer = object . get_layer ( 0 ) ;
for ( auto layerm : first_layer - > regions ( ) ) {
int extruder_id = layerm - > region ( ) . config ( ) . option ( " wall_filament " ) - > getInt ( ) ;
for ( auto expoly : layerm - > raw_slices ) {
if ( offset_ex ( expoly , - 0.2 * scale_ ( object . config ( ) . line_width ) ) . empty ( ) )
continue ;
double contour_area = expoly . contour . area ( ) ;
auto iter = min_areas_per_extruder . find ( extruder_id ) ;
if ( iter = = min_areas_per_extruder . end ( ) ) {
min_areas_per_extruder . insert ( { extruder_id , contour_area } ) ;
}
else {
if ( contour_area < min_areas_per_extruder . at ( extruder_id ) ) {
min_areas_per_extruder [ extruder_id ] = contour_area ;
}
}
}
}
double max_minimal_area = 0. ;
for ( auto ape : min_areas_per_extruder ) {
auto iter = tool_order . begin ( ) ;
for ( ; iter ! = tool_order . end ( ) ; iter + + ) {
if ( min_areas_per_extruder . at ( * iter ) < min_areas_per_extruder . at ( ape . first ) )
break ;
}
tool_order . insert ( iter , ape . first ) ;
}
2023-08-08 02:54:07 +00:00
const ConfigOptionInts * first_layer_print_sequence_op = object . print ( ) - > full_print_config ( ) . option < ConfigOptionInts > ( " first_layer_print_sequence " ) ;
if ( first_layer_print_sequence_op ) {
const std : : vector < int > & print_sequence_1st = first_layer_print_sequence_op - > values ;
if ( print_sequence_1st . size ( ) > = tool_order . size ( ) ) {
std : : sort ( tool_order . begin ( ) , tool_order . end ( ) , [ & print_sequence_1st ] ( int lh , int rh ) {
auto lh_it = std : : find ( print_sequence_1st . begin ( ) , print_sequence_1st . end ( ) , lh ) ;
auto rh_it = std : : find ( print_sequence_1st . begin ( ) , print_sequence_1st . end ( ) , rh ) ;
if ( lh_it = = print_sequence_1st . end ( ) | | rh_it = = print_sequence_1st . end ( ) )
return false ;
return lh_it < rh_it ;
} ) ;
}
}
2022-07-15 15:37:19 +00:00
return tool_order ;
}
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.
m_layer_tools . emplace_back ( LayerTools ( 0.5 * ( zs [ i ] + zs [ j - 1 ] ) ) ) ;
i = j ;
}
}
// Collect extruders reuqired to print layers.
void ToolOrdering : : collect_extruders ( const PrintObject & object , const std : : vector < std : : pair < double , unsigned int > > & per_layer_extruder_switches )
{
// Collect the support extruders.
for ( auto support_layer : object . support_layers ( ) ) {
LayerTools & layer_tools = this - > tools_for_layer ( support_layer - > print_z ) ;
ExtrusionRole role = support_layer - > support_fills . role ( ) ;
bool has_support = role = = erMixed | | role = = erSupportMaterial | | role = = erSupportTransition ;
bool has_interface = role = = erMixed | | role = = erSupportMaterialInterface ;
unsigned int extruder_support = object . config ( ) . support_filament . value ;
unsigned int extruder_interface = object . config ( ) . support_interface_filament . value ;
if ( has_support )
layer_tools . extruders . push_back ( extruder_support ) ;
if ( has_interface )
layer_tools . extruders . push_back ( extruder_interface ) ;
if ( has_support | | has_interface ) {
layer_tools . has_support = true ;
2022-07-22 09:46:10 +00:00
layer_tools . wiping_extrusions ( ) . is_support_overriddable_and_mark ( role , object ) ;
2022-07-15 15:37:19 +00:00
}
}
// Extruder overrides are ordered by print_z.
std : : vector < std : : pair < double , unsigned int > > : : const_iterator it_per_layer_extruder_override ;
it_per_layer_extruder_override = per_layer_extruder_switches . begin ( ) ;
unsigned int extruder_override = 0 ;
2023-09-04 10:28:06 +00:00
// BBS: collect first layer extruders of an object's wall, which will be used by brim generator
int layerCount = 0 ;
std : : vector < int > firstLayerExtruders ;
firstLayerExtruders . clear ( ) ;
2022-07-15 15:37:19 +00:00
// Collect the object extruders.
for ( auto layer : object . layers ( ) ) {
LayerTools & layer_tools = this - > tools_for_layer ( layer - > print_z ) ;
2024-07-02 11:26:52 +00:00
// Override extruder with the next
2022-07-15 15:37:19 +00:00
for ( ; it_per_layer_extruder_override ! = per_layer_extruder_switches . end ( ) & & it_per_layer_extruder_override - > first < layer - > print_z + EPSILON ; + + it_per_layer_extruder_override )
extruder_override = ( int ) it_per_layer_extruder_override - > second ;
// Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it.
layer_tools . extruder_override = extruder_override ;
// What extruders are required to print this object layer?
for ( const LayerRegion * layerm : layer - > regions ( ) ) {
const PrintRegion & region = layerm - > region ( ) ;
if ( ! layerm - > perimeters . entities . empty ( ) ) {
bool something_nonoverriddable = true ;
if ( m_print_config_ptr ) { // in this case print->config().print_sequence != PrintSequence::ByObject (see ToolOrdering constructors)
something_nonoverriddable = false ;
for ( const auto & eec : layerm - > perimeters . entities ) // let's check if there are nonoverriddable entities
if ( ! layer_tools . wiping_extrusions ( ) . is_overriddable_and_mark ( dynamic_cast < const ExtrusionEntityCollection & > ( * eec ) , * m_print_config_ptr , object , region ) )
something_nonoverriddable = true ;
}
2023-09-04 10:28:06 +00:00
if ( something_nonoverriddable ) {
2022-07-15 15:37:19 +00:00
layer_tools . extruders . emplace_back ( ( extruder_override = = 0 ) ? region . config ( ) . wall_filament . value : extruder_override ) ;
2023-09-04 10:28:06 +00:00
if ( layerCount = = 0 ) {
firstLayerExtruders . emplace_back ( ( extruder_override = = 0 ) ? region . config ( ) . wall_filament . value : extruder_override ) ;
}
}
2022-07-15 15:37:19 +00:00
layer_tools . has_object = true ;
}
bool has_infill = false ;
bool has_solid_infill = false ;
bool something_nonoverriddable = false ;
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 ;
if ( m_print_config_ptr ) {
if ( ! layer_tools . wiping_extrusions ( ) . is_overriddable_and_mark ( * fill , * m_print_config_ptr , object , region ) )
something_nonoverriddable = true ;
}
}
if ( something_nonoverriddable | | ! m_print_config_ptr ) {
if ( extruder_override = = 0 ) {
if ( has_solid_infill )
layer_tools . extruders . emplace_back ( region . config ( ) . solid_infill_filament ) ;
if ( has_infill )
layer_tools . extruders . emplace_back ( region . config ( ) . sparse_infill_filament ) ;
} else if ( has_solid_infill | | has_infill )
layer_tools . extruders . emplace_back ( extruder_override ) ;
}
if ( has_solid_infill | | has_infill )
layer_tools . has_object = true ;
}
2023-09-04 10:28:06 +00:00
layerCount + + ;
2022-07-15 15:37:19 +00:00
}
2023-09-04 10:28:06 +00:00
sort_remove_duplicates ( firstLayerExtruders ) ;
const_cast < PrintObject & > ( object ) . object_first_layer_wall_extruders = firstLayerExtruders ;
2024-07-02 11:26:52 +00:00
2022-07-15 15:37:19 +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 . emplace_back ( 0 ) ; // 0="dontcare" extruder - it will be taken care of in reorder_extruders
}
}
// Reorder extruders to minimize layer changes.
void ToolOrdering : : reorder_extruders ( unsigned int last_extruder_id )
{
if ( m_layer_tools . empty ( ) )
return ;
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 ;
for ( size_t i = 0 ; i < m_layer_tools . size ( ) & & last_extruder_id = = 0 ; + + i ) {
const LayerTools & lt = m_layer_tools [ i ] ;
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 ;
for ( LayerTools & lt : m_layer_tools ) {
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 ;
}
// On first layer with wipe tower, prefer a soluble extruder
// at the beginning, so it is not wiped on the first layer.
if ( lt = = m_layer_tools [ 0 ] & & m_print_config_ptr & & m_print_config_ptr - > enable_prime_tower ) {
for ( size_t i = 0 ; i < lt . extruders . size ( ) ; + + i )
if ( m_print_config_ptr - > filament_soluble . get_at ( lt . extruders [ i ] - 1 ) ) { // 1-based...
std : : swap ( lt . extruders [ i ] , lt . extruders . front ( ) ) ;
break ;
}
}
}
last_extruder_id = lt . extruders . back ( ) ;
}
// Reindex the extruders, so they are zero based, not 1 based.
for ( LayerTools & lt : m_layer_tools )
for ( unsigned int & extruder_id : lt . extruders ) {
assert ( extruder_id > 0 ) ;
- - extruder_id ;
}
2022-09-30 03:54:44 +00:00
// reorder the extruders for minimum flush volume
2024-07-18 04:03:08 +00:00
reorder_extruders_for_minimum_flush_volume ( true ) ;
2022-07-15 15:37:19 +00:00
}
// BBS
void ToolOrdering : : reorder_extruders ( std : : vector < unsigned int > tool_order_layer0 )
{
if ( m_layer_tools . empty ( ) )
return ;
// Reorder the extruders of first layer
{
LayerTools & lt = m_layer_tools [ 0 ] ;
std : : vector < unsigned int > layer0_extruders = lt . extruders ;
lt . extruders . clear ( ) ;
for ( unsigned int extruder_id : tool_order_layer0 ) {
auto iter = std : : find ( layer0_extruders . begin ( ) , layer0_extruders . end ( ) , extruder_id ) ;
if ( iter ! = layer0_extruders . end ( ) ) {
lt . extruders . push_back ( extruder_id ) ;
* iter = ( unsigned int ) - 1 ;
}
}
for ( unsigned int extruder_id : layer0_extruders ) {
if ( extruder_id = = 0 )
continue ;
if ( extruder_id ! = ( unsigned int ) - 1 )
lt . extruders . push_back ( extruder_id ) ;
}
// all extruders are zero
2024-11-26 01:59:16 +00:00
if ( lt . extruders . empty ( ) & & ! tool_order_layer0 . empty ( ) ) {
2022-07-15 15:37:19 +00:00
lt . extruders . push_back ( tool_order_layer0 [ 0 ] ) ;
}
}
2024-11-26 01:59:16 +00:00
int last_extruder_id = 0 ;
// BBS: fist layer may be empty or only has defult extrude id
if ( m_layer_tools [ 0 ] . extruders . empty ( ) ) {
// search for first extrude filament id
for ( size_t layer_id = 1 ; layer_id < m_layer_tools . size ( ) ; layer_id + + ) {
for ( auto const & extrude : m_layer_tools [ layer_id ] . extruders ) {
if ( extrude ! = 0 ) {
last_extruder_id = extrude ;
break ;
}
}
if ( last_extruder_id ! = 0 )
break ;
}
} else {
//use first layer last extruder
last_extruder_id = m_layer_tools [ 0 ] . extruders . back ( ) ;
}
2022-07-15 15:37:19 +00:00
for ( int i = 1 ; i < m_layer_tools . size ( ) ; i + + ) {
LayerTools & lt = m_layer_tools [ i ] ;
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.
for ( LayerTools & lt : m_layer_tools )
for ( unsigned int & extruder_id : lt . extruders ) {
assert ( extruder_id > 0 ) ;
- - extruder_id ;
}
2022-09-30 03:54:44 +00:00
// reorder the extruders for minimum flush volume
2024-07-18 04:03:08 +00:00
reorder_extruders_for_minimum_flush_volume ( false ) ;
2022-07-15 15:37:19 +00:00
}
void ToolOrdering : : fill_wipe_tower_partitions ( const PrintConfig & config , coordf_t object_bottom_z , coordf_t max_layer_height )
{
if ( m_layer_tools . empty ( ) )
return ;
// Count the minimum number of tool changes per layer.
size_t last_extruder = size_t ( - 1 ) ;
for ( LayerTools & lt : m_layer_tools ) {
lt . wipe_tower_partitions = lt . extruders . size ( ) ;
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 ( ) ;
}
}
// Propagate the wipe tower partitions down to support the upper partitions by the lower partitions.
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 ) ;
//FIXME this is a hack to get the ball rolling.
for ( LayerTools & lt : m_layer_tools )
2022-09-15 03:18:29 +00:00
lt . has_wipe_tower = ( lt . has_object & & ( config . timelapse_type = = TimelapseType : : tlSmooth | | lt . wipe_tower_partitions > 0 ) )
| | lt . print_z < object_bottom_z + EPSILON ;
2022-07-15 15:37:19 +00:00
// Test for a raft, insert additional wipe tower layer to fill in the raft separation gap.
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.
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 ) {
m_layer_tools [ j ] . has_wipe_tower = true ;
} else {
LayerTools & lt_extra = * m_layer_tools . insert ( m_layer_tools . begin ( ) + j , lt_new ) ;
//LayerTools <_prev = m_layer_tools[j];
LayerTools & lt_next = m_layer_tools [ j + 1 ] ;
assert ( ! m_layer_tools [ j - 1 ] . extruders . empty ( ) & & ! lt_next . extruders . empty ( ) ) ;
// 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());
lt_extra . has_wipe_tower = true ;
lt_extra . extruders . push_back ( lt_next . extruders . front ( ) ) ;
lt_extra . wipe_tower_partitions = lt_next . wipe_tower_partitions ;
}
}
}
break ;
}
}
// If the model contains empty layers (such as https://github.com/prusa3d/Slic3r/issues/1266), there might be layers
// that were not marked as has_wipe_tower, even when they should have been. This produces a crash with soluble supports
// and maybe other problems. We will therefore go through layer_tools and detect and fix this.
// So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder),
// we'll mark it with has_wipe tower.
for ( unsigned int i = 0 ; i + 1 < m_layer_tools . size ( ) ; + + i ) {
LayerTools & lt = m_layer_tools [ i ] ;
LayerTools & lt_next = m_layer_tools [ i + 1 ] ;
if ( lt . extruders . empty ( ) | | lt_next . extruders . empty ( ) )
break ;
if ( ! lt_next . has_wipe_tower & & ( lt_next . extruders . front ( ) ! = lt . extruders . back ( ) | | lt_next . extruders . size ( ) > 1 ) )
lt_next . has_wipe_tower = true ;
// We should also check that the next wipe tower layer is no further than max_layer_height:
unsigned int j = i + 1 ;
double last_wipe_tower_print_z = lt_next . print_z ;
while ( + + j < m_layer_tools . size ( ) - 1 & & ! m_layer_tools [ j ] . has_wipe_tower )
if ( m_layer_tools [ j + 1 ] . print_z - last_wipe_tower_print_z > max_layer_height + EPSILON ) {
m_layer_tools [ j ] . has_wipe_tower = true ;
last_wipe_tower_print_z = m_layer_tools [ j ] . print_z ;
}
}
// 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 ;
}
}
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 (
std : : remove_if ( m_all_printing_extruders . begin ( ) , m_all_printing_extruders . end ( ) ,
[ 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 ( ) ;
}
}
2024-06-26 11:30:37 +00:00
std : : set < std : : pair < std : : vector < unsigned int > , std : : vector < unsigned int > > > generate_combinations ( const std : : vector < unsigned int > & extruders )
2022-09-30 03:54:44 +00:00
{
2024-06-26 11:30:37 +00:00
int n = extruders . size ( ) ;
std : : vector < bool > flags ( n ) ;
std : : set < std : : pair < std : : vector < unsigned int > , std : : vector < unsigned int > > > unique_combinations ;
if ( extruders . empty ( ) )
return unique_combinations ;
for ( int i = 1 ; i < = n / 2 ; + + i ) {
std : : fill ( flags . begin ( ) , flags . begin ( ) + i , true ) ;
std : : fill ( flags . begin ( ) + i , flags . end ( ) , false ) ;
do {
std : : vector < unsigned int > group1 , group2 ;
for ( int j = 0 ; j < n ; + + j ) {
if ( flags [ j ] ) {
group1 . push_back ( extruders [ j ] ) ;
} else {
group2 . push_back ( extruders [ j ] ) ;
}
}
if ( group1 . size ( ) > group2 . size ( ) ) { std : : swap ( group1 , group2 ) ; }
unique_combinations . insert ( { group1 , group2 } ) ;
} while ( std : : prev_permutation ( flags . begin ( ) , flags . end ( ) ) ) ;
}
return unique_combinations ;
}
float get_flush_volume ( const std : : vector < int > & filament_maps , const std : : vector < unsigned int > & extruders , const std : : vector < FlushMatrix > & matrix , size_t nozzle_nums )
{
std : : vector < std : : vector < unsigned int > > nozzle_filaments ;
nozzle_filaments . resize ( nozzle_nums ) ;
for ( unsigned int filament_id : extruders ) {
nozzle_filaments [ filament_maps [ filament_id ] ] . emplace_back ( filament_id ) ;
}
float flush_volume = 0 ;
for ( size_t nozzle_id = 0 ; nozzle_id < nozzle_nums ; + + nozzle_id ) {
for ( size_t i = 0 ; i + 1 < nozzle_filaments [ nozzle_id ] . size ( ) ; + + i ) {
flush_volume + = matrix [ nozzle_id ] [ nozzle_filaments [ nozzle_id ] [ i ] ] [ nozzle_filaments [ nozzle_id ] [ i + 1 ] ] ;
}
}
return flush_volume ;
}
2024-07-10 09:48:00 +00:00
std : : vector < int > ToolOrdering : : get_recommended_filament_maps ( const std : : vector < std : : vector < unsigned int > > & layer_filaments , const PrintConfig * print_config )
2024-06-26 11:30:37 +00:00
{
2024-07-10 09:48:00 +00:00
if ( ! print_config | | layer_filaments . empty ( ) )
2024-06-26 11:30:37 +00:00
return std : : vector < int > ( ) ;
const unsigned int number_of_extruders = ( unsigned int ) ( print_config - > filament_colour . values . size ( ) + EPSILON ) ;
// get flush matrix
std : : vector < FlushMatrix > nozzle_flush_mtx ;
size_t nozzle_nums = print_config - > nozzle_diameter . values . size ( ) ;
for ( size_t nozzle_id = 0 ; nozzle_id < nozzle_nums ; + + nozzle_id ) {
std : : vector < float > flush_matrix ( cast < float > ( get_flush_volumes_matrix ( print_config - > flush_volumes_matrix . values , nozzle_id , nozzle_nums ) ) ) ;
std : : vector < std : : vector < float > > wipe_volumes ;
for ( unsigned int i = 0 ; i < number_of_extruders ; + + i )
wipe_volumes . push_back ( std : : vector < float > ( flush_matrix . begin ( ) + i * number_of_extruders , flush_matrix . begin ( ) + ( i + 1 ) * number_of_extruders ) ) ;
nozzle_flush_mtx . emplace_back ( wipe_volumes ) ;
}
std : : vector < LayerPrintSequence > other_layers_seqs ;
const ConfigOptionInts * other_layers_print_sequence_op = print_config - > option < ConfigOptionInts > ( " other_layers_print_sequence " ) ;
const ConfigOptionInt * other_layers_print_sequence_nums_op = print_config - > option < ConfigOptionInt > ( " other_layers_print_sequence_nums " ) ;
if ( other_layers_print_sequence_op & & other_layers_print_sequence_nums_op ) {
const std : : vector < int > & print_sequence = other_layers_print_sequence_op - > values ;
int sequence_nums = other_layers_print_sequence_nums_op - > value ;
other_layers_seqs = get_other_layers_print_sequence ( sequence_nums , print_sequence ) ;
2024-06-17 12:34:18 +00:00
}
2024-06-26 11:30:37 +00:00
// other_layers_seq: the layer_idx and extruder_idx are base on 1
auto get_custom_seq = [ & other_layers_seqs ] ( int layer_idx , std : : vector < int > & out_seq ) - > bool {
for ( size_t idx = other_layers_seqs . size ( ) - 1 ; idx ! = size_t ( - 1 ) ; - - idx ) {
const auto & other_layers_seq = other_layers_seqs [ idx ] ;
if ( layer_idx + 1 > = other_layers_seq . first . first & & layer_idx + 1 < = other_layers_seq . first . second ) {
out_seq = other_layers_seq . second ;
return true ;
}
}
return false ;
} ;
2024-07-08 13:31:28 +00:00
std : : vector < unsigned int > used_filaments ;
2024-07-10 09:48:00 +00:00
for ( auto & one_layer_filaments : layer_filaments ) {
for ( unsigned int filament : one_layer_filaments ) {
if ( std : : find ( used_filaments . begin ( ) , used_filaments . end ( ) , filament ) = = used_filaments . end ( ) )
used_filaments . emplace_back ( filament ) ;
2024-06-26 11:30:37 +00:00
}
}
2024-07-08 13:31:28 +00:00
std : : sort ( used_filaments . begin ( ) , used_filaments . end ( ) ) ;
2024-07-12 06:40:25 +00:00
std : : vector < int > ret ( number_of_extruders , 0 ) ;
// if mutli_extruder, calc group,otherwise set to 0
if ( nozzle_nums = = 2 )
{
2024-07-17 09:19:27 +00:00
std : : vector < std : : string > extruder_ams_count_str = print_config - > extruder_ams_count . values ;
auto extruder_ams_counts = get_extruder_ams_count ( extruder_ams_count_str ) ;
std : : vector < int > group_size = { 16 , 16 } ;
if ( extruder_ams_counts . size ( ) > 0 ) {
assert ( extruder_ams_counts . size ( ) = = 2 ) ;
for ( int i = 0 ; i < extruder_ams_counts . size ( ) ; + + i ) {
group_size [ i ] = 0 ;
const auto & ams_count = extruder_ams_counts [ i ] ;
for ( auto iter = ams_count . begin ( ) ; iter ! = ams_count . end ( ) ; + + iter ) {
group_size [ i ] + = iter - > first * iter - > second ;
}
}
}
2024-07-12 06:40:25 +00:00
FilamentGroup fg (
nozzle_flush_mtx ,
used_filaments . size ( ) ,
2024-07-17 09:19:27 +00:00
group_size
2024-07-12 06:40:25 +00:00
) ;
fg . get_custom_seq = get_custom_seq ;
2024-07-12 10:04:01 +00:00
fg . calc_filament_group ( layer_filaments , FGStrategy : : BestFit ) ;
2024-07-12 06:40:25 +00:00
auto filament_map = fg . get_filament_map ( ) ;
for ( size_t idx = 0 ; idx < filament_map . size ( ) ; + + idx ) {
if ( filament_map [ idx ] )
ret [ used_filaments [ idx ] ] = 1 ;
}
2024-07-08 13:31:28 +00:00
}
2024-07-12 06:40:25 +00:00
2024-07-08 13:31:28 +00:00
return ret ;
2024-06-26 11:30:37 +00:00
}
2024-07-18 04:03:08 +00:00
void ToolOrdering : : reorder_extruders_for_minimum_flush_volume ( bool reorder_first_layer )
2024-06-26 11:30:37 +00:00
{
2024-07-15 12:00:03 +00:00
const PrintConfig * print_config = m_print_config_ptr ;
2023-12-12 14:36:48 +00:00
if ( ! print_config & & m_print_object_ptr ) {
print_config = & ( m_print_object_ptr - > print ( ) - > config ( ) ) ;
}
2024-06-17 12:34:18 +00:00
if ( ! print_config | | m_layer_tools . empty ( ) )
return ;
2024-07-15 12:00:03 +00:00
const unsigned int number_of_extruders = ( unsigned int ) ( print_config - > filament_colour . values . size ( ) + EPSILON ) ;
2024-06-17 12:34:18 +00:00
using FlushMatrix = std : : vector < std : : vector < float > > ;
size_t nozzle_nums = print_config - > nozzle_diameter . values . size ( ) ;
std : : vector < FlushMatrix > nozzle_flush_mtx ;
for ( size_t nozzle_id = 0 ; nozzle_id < nozzle_nums ; + + nozzle_id ) {
std : : vector < float > flush_matrix ( cast < float > ( get_flush_volumes_matrix ( print_config - > flush_volumes_matrix . values , nozzle_id , nozzle_nums ) ) ) ;
std : : vector < std : : vector < float > > wipe_volumes ;
for ( unsigned int i = 0 ; i < number_of_extruders ; + + i )
wipe_volumes . push_back ( std : : vector < float > ( flush_matrix . begin ( ) + i * number_of_extruders , flush_matrix . begin ( ) + ( i + 1 ) * number_of_extruders ) ) ;
nozzle_flush_mtx . emplace_back ( wipe_volumes ) ;
}
2024-07-10 09:48:00 +00:00
2024-07-08 13:31:28 +00:00
std : : vector < int > filament_maps ( number_of_extruders , 0 ) ;
if ( nozzle_nums > 1 ) {
filament_maps = m_print - > get_filament_maps ( ) ;
2024-07-17 02:51:02 +00:00
if ( m_print - > get_filament_map_mode ( ) = = FilamentMapMode : : fmmAuto
& & ( print_config - > print_sequence ! = PrintSequence : : ByObject | | m_print - > objects ( ) . size ( ) = = 1 ) ) {
2024-07-15 12:00:03 +00:00
const PrintConfig * print_config = m_print_config_ptr ;
2024-07-10 09:48:00 +00:00
if ( ! print_config & & m_print_object_ptr ) {
print_config = & ( m_print_object_ptr - > print ( ) - > config ( ) ) ;
}
std : : vector < std : : vector < unsigned int > > layer_filaments ;
for ( auto & lt : m_layer_tools ) {
2024-07-15 12:00:03 +00:00
layer_filaments . emplace_back ( lt . extruders ) ;
2024-07-10 09:48:00 +00:00
}
filament_maps = ToolOrdering : : get_recommended_filament_maps ( layer_filaments , print_config ) ;
2024-07-08 13:31:28 +00:00
if ( filament_maps . empty ( ) )
return ;
std : : transform ( filament_maps . begin ( ) , filament_maps . end ( ) , filament_maps . begin ( ) , [ ] ( int value ) { return value + 1 ; } ) ;
m_print - > update_filament_maps_to_config ( filament_maps ) ;
}
std : : transform ( filament_maps . begin ( ) , filament_maps . end ( ) , filament_maps . begin ( ) , [ ] ( int value ) { return value - 1 ; } ) ;
}
2024-07-02 11:26:52 +00:00
2024-07-08 13:31:28 +00:00
std : : vector < std : : vector < unsigned int > > filament_sequences ;
std : : vector < unsigned int > filament_lists ( number_of_extruders ) ;
2024-07-15 12:00:03 +00:00
std : : iota ( filament_lists . begin ( ) , filament_lists . end ( ) , 0 ) ;
2024-07-08 13:31:28 +00:00
std : : vector < std : : vector < unsigned int > > layer_filaments ;
for ( auto & lt : m_layer_tools ) {
layer_filaments . emplace_back ( lt . extruders ) ;
}
2024-06-17 12:34:18 +00:00
std : : vector < LayerPrintSequence > other_layers_seqs ;
2024-07-15 12:00:03 +00:00
const ConfigOptionInts * other_layers_print_sequence_op = print_config - > option < ConfigOptionInts > ( " other_layers_print_sequence " ) ;
const ConfigOptionInt * other_layers_print_sequence_nums_op = print_config - > option < ConfigOptionInt > ( " other_layers_print_sequence_nums " ) ;
2024-06-17 12:34:18 +00:00
if ( other_layers_print_sequence_op & & other_layers_print_sequence_nums_op ) {
2024-07-15 12:00:03 +00:00
const std : : vector < int > & print_sequence = other_layers_print_sequence_op - > values ;
int sequence_nums = other_layers_print_sequence_nums_op - > value ;
other_layers_seqs = get_other_layers_print_sequence ( sequence_nums , print_sequence ) ;
2024-06-17 12:34:18 +00:00
}
2024-07-18 04:03:08 +00:00
std : : vector < unsigned int > first_layer_filaments ;
if ( ! m_layer_tools . empty ( ) )
first_layer_filaments = m_layer_tools [ 0 ] . extruders ;
2024-06-17 12:34:18 +00:00
// other_layers_seq: the layer_idx and extruder_idx are base on 1
2024-07-18 04:03:08 +00:00
auto get_custom_seq = [ & other_layers_seqs , & reorder_first_layer , & first_layer_filaments ] ( int layer_idx , std : : vector < int > & out_seq ) - > bool {
if ( ! reorder_first_layer & & layer_idx = = 0 ) {
out_seq . resize ( first_layer_filaments . size ( ) ) ;
std : : transform ( first_layer_filaments . begin ( ) , first_layer_filaments . end ( ) , out_seq . begin ( ) , [ ] ( auto item ) { return item + 1 ; } ) ;
return true ;
}
2024-06-17 12:34:18 +00:00
for ( size_t idx = other_layers_seqs . size ( ) - 1 ; idx ! = size_t ( - 1 ) ; - - idx ) {
2024-07-15 12:00:03 +00:00
const auto & other_layers_seq = other_layers_seqs [ idx ] ;
2024-06-17 12:34:18 +00:00
if ( layer_idx + 1 > = other_layers_seq . first . first & & layer_idx + 1 < = other_layers_seq . first . second ) {
out_seq = other_layers_seq . second ;
return true ;
}
}
return false ;
2024-07-08 13:31:28 +00:00
} ;
reorder_filaments_for_minimum_flush_volume (
filament_lists ,
filament_maps ,
layer_filaments ,
nozzle_flush_mtx ,
get_custom_seq ,
& filament_sequences
) ;
for ( size_t i = 0 ; i < filament_sequences . size ( ) ; + + i )
m_layer_tools [ i ] . extruders = std : : move ( filament_sequences [ i ] ) ;
2024-06-17 12:34:18 +00:00
}
2022-07-15 15:37:19 +00:00
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
void ToolOrdering : : mark_skirt_layers ( const PrintConfig & config , coordf_t max_layer_height )
{
if ( m_layer_tools . empty ( ) )
return ;
if ( m_layer_tools . front ( ) . extruders . empty ( ) ) {
// Empty first layer, no skirt will be printed.
//FIXME throw an exception?
return ;
}
size_t i = 0 ;
for ( ; ; ) {
m_layer_tools [ i ] . has_skirt = true ;
size_t j = i + 1 ;
for ( ; j < m_layer_tools . size ( ) & & ! m_layer_tools [ j ] . has_object ; + + j ) ;
// i and j are two successive layers printing an object.
if ( j = = m_layer_tools . size ( ) )
// Don't print skirt above the last object layer.
break ;
// Mark some printing intermediate layers as having skirt.
double last_z = m_layer_tools [ i ] . print_z ;
for ( size_t k = i + 1 ; k < j ; + + k ) {
if ( m_layer_tools [ k + 1 ] . print_z - last_z > max_layer_height + EPSILON ) {
// Layer k is the last one not violating the maximum layer height.
// Don't extrude skirt on empty layers.
while ( m_layer_tools [ k ] . extruders . empty ( ) )
- - k ;
if ( m_layer_tools [ k ] . has_skirt ) {
// Skirt cannot be generated due to empty layers, there would be a missing layer in the skirt.
//FIXME throw an exception?
break ;
}
m_layer_tools [ k ] . has_skirt = true ;
last_z = m_layer_tools [ k ] . print_z ;
}
}
i = j ;
}
}
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
// If multiple events are planned over a span of a single layer, use the last one.
2023-02-01 09:38:02 +00:00
// BBS: replace model custom gcode with current plate custom gcode
static CustomGCode : : Info custom_gcode_per_print_z ;
2024-11-15 04:19:50 +00:00
void ToolOrdering : : assign_custom_gcodes ( const Print & print )
2022-07-15 15:37:19 +00:00
{
2024-11-15 04:19:50 +00:00
// Only valid for non-sequential print.
assert ( print . config ( ) . print_sequence = = PrintSequence : : ByLayer ) ;
2022-07-15 15:37:19 +00:00
2023-02-01 09:38:02 +00:00
custom_gcode_per_print_z = print . model ( ) . get_curr_plate_custom_gcodes ( ) ;
2024-11-15 04:19:50 +00:00
if ( custom_gcode_per_print_z . gcodes . empty ( ) )
return ;
2022-07-15 15:37:19 +00:00
// BBS
2024-11-15 04:19:50 +00:00
auto num_filaments = unsigned ( print . config ( ) . filament_diameter . size ( ) ) ;
CustomGCode : : Mode mode =
( num_filaments = = 1 ) ? CustomGCode : : SingleExtruder :
print . object_extruders ( ) . size ( ) = = 1 ? CustomGCode : : MultiAsSingle : CustomGCode : : MultiExtruder ;
CustomGCode : : Mode model_mode = print . model ( ) . get_curr_plate_custom_gcodes ( ) . mode ;
auto custom_gcode_it = custom_gcode_per_print_z . gcodes . rbegin ( ) ;
// Tool changes and color changes will be ignored, if the model's tool/color changes were entered in mm mode and the print is in non mm mode
// or vice versa.
bool ignore_tool_and_color_changes = ( mode = = CustomGCode : : MultiExtruder ) ! = ( model_mode = = CustomGCode : : MultiExtruder ) ;
// If printing on a single extruder machine, make the tool changes trigger color change (M600) events.
bool tool_changes_as_color_changes = mode = = CustomGCode : : SingleExtruder & & model_mode = = CustomGCode : : MultiAsSingle ;
auto apply_custom_gcode_to_layer = [ mode ,
ignore_tool_and_color_changes ,
tool_changes_as_color_changes ,
num_filaments ] ( LayerTools & lt , const std : : vector < unsigned char > & extruder_printing_above , const CustomGCode : : Item & item )
{
bool color_change = item . type = = CustomGCode : : ColorChange ;
bool tool_change = item . type = = CustomGCode : : ToolChange ;
bool pause_or_custom_gcode = ! color_change & & ! tool_change ;
bool apply_color_change = ! ignore_tool_and_color_changes & &
// If it is color change, it will actually be useful as the exturder above will print.
// BBS
( color_change ?
mode = = CustomGCode : : SingleExtruder | |
( item . extruder < = int ( num_filaments ) & & extruder_printing_above [ unsigned ( item . extruder - 1 ) ] ) :
tool_change & & tool_changes_as_color_changes ) ;
if ( pause_or_custom_gcode | | apply_color_change )
lt . custom_gcode = & item ;
} ;
std : : unordered_map < int , std : : vector < unsigned char > > extruder_print_above_by_layer ;
{
std : : vector < unsigned char > extruder_printing_above ( num_filaments , false ) ;
for ( auto iter = m_layer_tools . rbegin ( ) ; iter ! = m_layer_tools . rend ( ) ; + + iter ) {
for ( unsigned int i : iter - > extruders )
extruder_printing_above [ i ] = true ;
2024-11-15 07:59:10 +00:00
int layer_idx = m_layer_tools . rend ( ) - iter - 1 ;
2024-11-15 04:19:50 +00:00
extruder_print_above_by_layer . emplace ( layer_idx , extruder_printing_above ) ;
}
}
2024-10-09 04:15:46 +00:00
for ( auto custom_gcode_it = custom_gcode_per_print_z . gcodes . rbegin ( ) ; custom_gcode_it ! = custom_gcode_per_print_z . gcodes . rend ( ) ; + + custom_gcode_it ) {
if ( custom_gcode_it - > type = = CustomGCode : : ToolChange )
continue ;
2024-11-15 04:19:50 +00:00
auto layer_it_upper = std : : upper_bound ( m_layer_tools . begin ( ) , m_layer_tools . end ( ) , custom_gcode_it - > print_z , [ ] ( double z , const LayerTools & lt ) {
return z < lt . print_z ;
} ) ;
int upper_layer_idx = layer_it_upper - m_layer_tools . begin ( ) ;
if ( layer_it_upper = = m_layer_tools . begin ( ) ) {
apply_custom_gcode_to_layer ( * layer_it_upper , extruder_print_above_by_layer [ 0 ] , * custom_gcode_it ) ;
}
else if ( layer_it_upper = = m_layer_tools . end ( ) ) {
auto layer_it_lower = std : : prev ( layer_it_upper ) ;
int lower_layer_idx = layer_it_lower - m_layer_tools . begin ( ) ;
apply_custom_gcode_to_layer ( * layer_it_lower , extruder_print_above_by_layer [ lower_layer_idx ] , * custom_gcode_it ) ;
}
else {
auto layer_it_lower = std : : prev ( layer_it_upper ) ;
int lower_layer_idx = layer_it_lower - m_layer_tools . begin ( ) ;
double gap_to_lower = std : : fabs ( custom_gcode_it - > print_z - layer_it_lower - > print_z ) ;
double gap_to_upper = std : : fabs ( custom_gcode_it - > print_z - layer_it_upper - > print_z ) ;
if ( gap_to_lower < gap_to_upper )
apply_custom_gcode_to_layer ( * layer_it_lower , extruder_print_above_by_layer [ lower_layer_idx ] , * custom_gcode_it ) ;
else
apply_custom_gcode_to_layer ( * layer_it_upper , extruder_print_above_by_layer [ upper_layer_idx ] , * custom_gcode_it ) ;
2024-10-09 04:15:46 +00:00
}
}
2022-07-15 15:37:19 +00:00
}
const LayerTools & ToolOrdering : : tools_for_layer ( coordf_t print_z ) const
{
auto it_layer_tools = std : : lower_bound ( m_layer_tools . begin ( ) , m_layer_tools . end ( ) , LayerTools ( print_z - EPSILON ) ) ;
assert ( it_layer_tools ! = m_layer_tools . end ( ) ) ;
coordf_t dist_min = std : : abs ( it_layer_tools - > print_z - print_z ) ;
for ( + + it_layer_tools ; it_layer_tools ! = m_layer_tools . end ( ) ; + + it_layer_tools ) {
coordf_t d = std : : abs ( it_layer_tools - > print_z - print_z ) ;
if ( d > = dist_min )
break ;
dist_min = d ;
}
- - it_layer_tools ;
assert ( dist_min < EPSILON ) ;
return * it_layer_tools ;
}
// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
2022-10-19 08:30:08 +00:00
void WipingExtrusions : : set_extruder_override ( const ExtrusionEntity * entity , const PrintObject * object , size_t copy_id , int extruder , size_t num_of_copies )
2022-07-15 15:37:19 +00:00
{
something_overridden = true ;
2022-10-19 08:30:08 +00:00
auto entity_map_it = ( entity_map . emplace ( std : : make_tuple ( entity , object ) , ExtruderPerCopy ( ) ) ) . first ; // (add and) return iterator
2022-07-15 15:37:19 +00:00
ExtruderPerCopy & copies_vector = entity_map_it - > second ;
copies_vector . resize ( num_of_copies , - 1 ) ;
if ( copies_vector [ copy_id ] ! = - 1 )
std : : cout < < " ERROR: Entity extruder overriden multiple times!!! \n " ; // A debugging message - this must never happen.
copies_vector [ copy_id ] = extruder ;
}
2022-07-22 09:46:10 +00:00
// BBS
void WipingExtrusions : : set_support_extruder_override ( const PrintObject * object , size_t copy_id , int extruder , size_t num_of_copies )
{
something_overridden = true ;
support_map . emplace ( object , extruder ) ;
}
void WipingExtrusions : : set_support_interface_extruder_override ( const PrintObject * object , size_t copy_id , int extruder , size_t num_of_copies )
{
something_overridden = true ;
support_intf_map . emplace ( object , extruder ) ;
}
2022-07-15 15:37:19 +00:00
// Finds first non-soluble extruder on the layer
int WipingExtrusions : : first_nonsoluble_extruder_on_layer ( const PrintConfig & print_config ) const
{
const LayerTools & lt = * m_layer_tools ;
for ( auto extruders_it = lt . extruders . begin ( ) ; extruders_it ! = lt . extruders . end ( ) ; + + extruders_it )
2023-01-31 03:57:39 +00:00
if ( ! print_config . filament_soluble . get_at ( * extruders_it ) & & ! print_config . filament_is_support . get_at ( * extruders_it ) )
2022-07-15 15:37:19 +00:00
return ( * extruders_it ) ;
return ( - 1 ) ;
}
// Finds last non-soluble extruder on the layer
int WipingExtrusions : : last_nonsoluble_extruder_on_layer ( const PrintConfig & print_config ) const
{
const LayerTools & lt = * m_layer_tools ;
for ( auto extruders_it = lt . extruders . rbegin ( ) ; extruders_it ! = lt . extruders . rend ( ) ; + + extruders_it )
2023-01-31 03:57:39 +00:00
if ( ! print_config . filament_soluble . get_at ( * extruders_it ) & & ! print_config . filament_is_support . get_at ( * extruders_it ) )
2022-07-15 15:37:19 +00:00
return ( * extruders_it ) ;
return ( - 1 ) ;
}
// Decides whether this entity could be overridden
bool WipingExtrusions : : is_overriddable ( const ExtrusionEntityCollection & eec , const PrintConfig & print_config , const PrintObject & object , const PrintRegion & region ) const
{
if ( print_config . filament_soluble . get_at ( m_layer_tools - > extruder ( eec , region ) ) )
return false ;
if ( object . config ( ) . flush_into_objects )
return true ;
if ( ! object . config ( ) . flush_into_infill | | eec . role ( ) ! = erInternalInfill )
return false ;
return true ;
}
2022-07-22 09:46:10 +00:00
// BBS
bool WipingExtrusions : : is_support_overriddable ( const ExtrusionRole role , const PrintObject & object ) const
{
if ( ! object . config ( ) . flush_into_support )
return false ;
if ( role = = erMixed ) {
return object . config ( ) . support_filament = = 0 | | object . config ( ) . support_interface_filament = = 0 ;
}
else if ( role = = erSupportMaterial | | role = = erSupportTransition ) {
return object . config ( ) . support_filament = = 0 ;
}
else if ( role = = erSupportMaterialInterface ) {
return object . config ( ) . support_interface_filament = = 0 ;
}
return false ;
}
2022-07-15 15:37:19 +00:00
// 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.
float WipingExtrusions : : mark_wiping_extrusions ( const Print & print , unsigned int old_extruder , unsigned int new_extruder , float volume_to_wipe )
{
const LayerTools & lt = * m_layer_tools ;
const float min_infill_volume = 0.f ; // ignore infill with smaller volume than this
if ( ! this - > something_overridable | | volume_to_wipe < = 0. | | print . config ( ) . filament_soluble . get_at ( old_extruder ) | | print . config ( ) . filament_soluble . get_at ( new_extruder ) )
return std : : max ( 0.f , volume_to_wipe ) ; // Soluble filament cannot be wiped in a random infill, neither the filament after it
2022-07-22 09:46:10 +00:00
// BBS
2022-10-10 09:35:28 +00:00
if ( print . config ( ) . filament_is_support . get_at ( old_extruder ) | | print . config ( ) . filament_is_support . get_at ( new_extruder ) )
2022-07-22 09:46:10 +00:00
return std : : max ( 0.f , volume_to_wipe ) ; // Support filament cannot be used to print support, infill, wipe_tower, etc.
2022-07-15 15:37:19 +00:00
// we will sort objects so that dedicated for wiping are at the beginning:
ConstPrintObjectPtrs object_list = print . objects ( ) . vector ( ) ;
2022-10-19 01:47:46 +00:00
// BBS: fix the exception caused by not fixed order between different objects
std : : sort ( object_list . begin ( ) , object_list . end ( ) , [ object_list ] ( const PrintObject * a , const PrintObject * b ) {
if ( a - > config ( ) . flush_into_objects ! = b - > config ( ) . flush_into_objects ) {
return a - > config ( ) . flush_into_objects . getBool ( ) ;
}
else {
return a - > id ( ) < b - > id ( ) ;
}
} ) ;
2022-07-15 15:37:19 +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 ;
for ( int i = 0 ; i < ( int ) object_list . size ( ) + ( perimeters_done ? 0 : 1 ) ; + + i ) {
if ( ! perimeters_done & & ( i = = ( int ) object_list . size ( ) | | ! object_list [ i ] - > config ( ) . flush_into_objects ) ) { // we passed the last dedicated object in list
perimeters_done = true ;
i = - 1 ; // let's go from the start again
continue ;
}
const PrintObject * object = object_list [ i ] ;
// Finds this layer:
const Layer * this_layer = object - > get_layer_at_printz ( lt . print_z , EPSILON ) ;
if ( this_layer = = nullptr )
continue ;
size_t num_of_copies = object - > instances ( ) . size ( ) ;
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for ( unsigned int copy = 0 ; copy < num_of_copies ; + + copy ) {
for ( const LayerRegion * layerm : this_layer - > regions ( ) ) {
const auto & region = layerm - > region ( ) ;
2022-07-22 09:46:10 +00:00
if ( ! object - > config ( ) . flush_into_infill & & ! object - > config ( ) . flush_into_objects & & ! object - > config ( ) . flush_into_support )
2022-07-15 15:37:19 +00:00
continue ;
bool wipe_into_infill_only = ! object - > config ( ) . flush_into_objects & & object - > config ( ) . flush_into_infill ;
2023-09-21 07:32:18 +00:00
bool is_infill_first = print . config ( ) . is_infill_first ;
2022-07-15 15:37:19 +00:00
if ( is_infill_first ! = perimeters_done | | wipe_into_infill_only ) {
for ( const ExtrusionEntity * ee : layerm - > fills . entities ) { // iterate through all infill Collections
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
if ( ! is_overriddable ( * fill , print . config ( ) , * object , region ) )
continue ;
if ( wipe_into_infill_only & & ! is_infill_first )
// 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):
if ( ! lt . is_extruder_order ( lt . wall_filament ( region ) , new_extruder ) )
continue ;
2022-10-19 08:30:08 +00:00
if ( ( ! is_entity_overridden ( fill , object , copy ) & & fill - > total_volume ( ) > min_infill_volume ) )
2022-07-15 15:37:19 +00:00
{ // this infill will be used to wipe this extruder
2022-10-19 08:30:08 +00:00
set_extruder_override ( fill , object , copy , new_extruder , num_of_copies ) ;
2022-07-15 15:37:19 +00:00
if ( ( volume_to_wipe - = float ( fill - > total_volume ( ) ) ) < = 0.f )
// More material was purged already than asked for.
return 0.f ;
}
}
}
// Now the same for perimeters - see comments above for explanation:
if ( object - > config ( ) . flush_into_objects & & is_infill_first = = perimeters_done )
{
for ( const ExtrusionEntity * ee : layerm - > perimeters . entities ) {
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
2022-10-19 08:30:08 +00:00
if ( is_overriddable ( * fill , print . config ( ) , * object , region ) & & ! is_entity_overridden ( fill , object , copy ) & & fill - > total_volume ( ) > min_infill_volume ) {
set_extruder_override ( fill , object , copy , new_extruder , num_of_copies ) ;
2022-07-15 15:37:19 +00:00
if ( ( volume_to_wipe - = float ( fill - > total_volume ( ) ) ) < = 0.f )
// More material was purged already than asked for.
return 0.f ;
}
}
}
}
2022-07-22 09:46:10 +00:00
// BBS
if ( object - > config ( ) . flush_into_support ) {
auto & object_config = object - > config ( ) ;
const SupportLayer * this_support_layer = object - > get_support_layer_at_printz ( lt . print_z , EPSILON ) ;
do {
2022-12-09 06:08:30 +00:00
if ( this_support_layer = = nullptr )
2022-07-22 09:46:10 +00:00
break ;
bool support_overriddable = object_config . support_filament = = 0 ;
bool support_intf_overriddable = object_config . support_interface_filament = = 0 ;
if ( ! support_overriddable & & ! support_intf_overriddable )
break ;
2022-12-09 06:08:30 +00:00
auto & entities = this_support_layer - > support_fills . entities ;
2023-10-23 09:59:01 +00:00
if ( support_overriddable & & ! is_support_overridden ( object ) & & ! ( object_config . support_interface_not_for_body . value & & ! support_intf_overriddable & & ( new_extruder = = object_config . support_interface_filament - 1 | | old_extruder = = object_config . support_interface_filament - 1 ) ) ) {
2022-07-22 09:46:10 +00:00
set_support_extruder_override ( object , copy , new_extruder , num_of_copies ) ;
for ( const ExtrusionEntity * ee : entities ) {
if ( ee - > role ( ) = = erSupportMaterial | | ee - > role ( ) = = erSupportTransition )
volume_to_wipe - = ee - > total_volume ( ) ;
if ( volume_to_wipe < = 0.f )
return 0.f ;
}
}
if ( support_intf_overriddable & & ! is_support_interface_overridden ( object ) ) {
set_support_interface_extruder_override ( object , copy , new_extruder , num_of_copies ) ;
for ( const ExtrusionEntity * ee : entities ) {
if ( ee - > role ( ) = = erSupportMaterialInterface )
volume_to_wipe - = ee - > total_volume ( ) ;
if ( volume_to_wipe < = 0.f )
return 0.f ;
}
}
} while ( 0 ) ;
}
2022-07-15 15:37:19 +00:00
}
}
// Some purge remains to be done on the Wipe Tower.
assert ( volume_to_wipe > 0. ) ;
return volume_to_wipe ;
}
// 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.
void WipingExtrusions : : ensure_perimeters_infills_order ( const Print & print )
{
if ( ! this - > something_overridable )
return ;
const LayerTools & lt = * m_layer_tools ;
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer ( print . config ( ) ) ;
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer ( print . config ( ) ) ;
for ( const PrintObject * object : print . objects ( ) ) {
// Finds this layer:
const Layer * this_layer = object - > get_layer_at_printz ( lt . print_z , EPSILON ) ;
if ( this_layer = = nullptr )
continue ;
size_t num_of_copies = object - > instances ( ) . size ( ) ;
for ( size_t copy = 0 ; copy < num_of_copies ; + + copy ) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for ( const LayerRegion * layerm : this_layer - > regions ( ) ) {
const auto & region = layerm - > region ( ) ;
//BBS
if ( ! object - > config ( ) . flush_into_infill & & ! object - > config ( ) . flush_into_objects )
continue ;
2023-09-21 07:32:18 +00:00
bool is_infill_first = print . config ( ) . is_infill_first ;
2022-07-15 15:37:19 +00:00
for ( const ExtrusionEntity * ee : layerm - > fills . entities ) { // iterate through all infill Collections
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
if ( ! is_overriddable ( * fill , print . config ( ) , * object , region )
2022-10-19 08:30:08 +00:00
| | is_entity_overridden ( fill , object , copy ) )
2022-07-15 15:37:19 +00:00
continue ;
// This infill could have been overridden but was not - unless we do something, it could be
// 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:
//BBS
if ( is_infill_first
//BBS
//|| object->config().flush_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
| | lt . is_extruder_order ( lt . wall_filament ( region ) , last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
| | ! lt . has_extruder ( lt . sparse_infill_filament ( region ) ) ) ) // we have to force override - this could violate infill_first (FIXME)
2022-10-19 08:30:08 +00:00
set_extruder_override ( fill , object , copy , ( is_infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder ) , num_of_copies ) ;
2022-07-15 15:37:19 +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.
}
}
// Now the same for perimeters - see comments above for explanation:
for ( const ExtrusionEntity * ee : layerm - > perimeters . entities ) { // iterate through all perimeter Collections
auto * fill = dynamic_cast < const ExtrusionEntityCollection * > ( ee ) ;
2022-10-19 08:30:08 +00:00
if ( is_overriddable ( * fill , print . config ( ) , * object , region ) & & ! is_entity_overridden ( fill , object , copy ) )
set_extruder_override ( fill , object , copy , ( is_infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder ) , num_of_copies ) ;
2022-07-15 15:37:19 +00:00
}
}
}
}
}
// Following function is called from GCode::process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
// If this extrusion does not have any override, nullptr is returned.
// Otherwise it 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 therefore keeps track of which extrusions are the ones that were overridden and which were not. If the extruder used is overridden,
// its number is saved as is (zero-based index). Regular extrusions are saved as -number-1 (unfortunately there is no negative zero).
2022-10-19 08:30:08 +00:00
const WipingExtrusions : : ExtruderPerCopy * WipingExtrusions : : get_extruder_overrides ( const ExtrusionEntity * entity , const PrintObject * object , int correct_extruder_id , size_t num_of_copies )
2022-07-15 15:37:19 +00:00
{
ExtruderPerCopy * overrides = nullptr ;
2022-10-19 08:30:08 +00:00
auto entity_map_it = entity_map . find ( std : : make_tuple ( entity , object ) ) ;
2022-07-15 15:37:19 +00:00
if ( entity_map_it ! = entity_map . end ( ) ) {
overrides = & entity_map_it - > second ;
overrides - > 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 ( overrides - > begin ( ) , overrides - > end ( ) , - 1 , - correct_extruder_id - 1 ) ;
}
return overrides ;
}
2022-07-22 09:46:10 +00:00
// BBS
int WipingExtrusions : : get_support_extruder_overrides ( const PrintObject * object )
{
auto iter = support_map . find ( object ) ;
if ( iter ! = support_map . end ( ) )
return iter - > second ;
return - 1 ;
}
int WipingExtrusions : : get_support_interface_extruder_overrides ( const PrintObject * object )
{
auto iter = support_intf_map . find ( object ) ;
if ( iter ! = support_intf_map . end ( ) )
return iter - > second ;
return - 1 ;
}
2022-07-15 15:37:19 +00:00
} // namespace Slic3r