2022-07-15 15:37:19 +00:00
# include "Exception.hpp"
# include "Print.hpp"
# include "BoundingBox.hpp"
# include "Brim.hpp"
# include "ClipperUtils.hpp"
# include "Extruder.hpp"
# include "Flow.hpp"
# include "Geometry/ConvexHull.hpp"
# include "I18N.hpp"
# include "ShortestPath.hpp"
# include "SupportMaterial.hpp"
# include "Thread.hpp"
# include "GCode.hpp"
# include "GCode/WipeTower.hpp"
# include "Utils.hpp"
# include "PrintConfig.hpp"
# include "Model.hpp"
# include <float.h>
# include <algorithm>
# include <limits>
# include <unordered_set>
# include <boost/filesystem/path.hpp>
# include <boost/format.hpp>
# include <boost/log/trivial.hpp>
// Mark string for localization and translate.
# define L(s) Slic3r::I18N::translate(s)
namespace Slic3r {
template class PrintState < PrintStep , psCount > ;
template class PrintState < PrintObjectStep , posCount > ;
PrintRegion : : PrintRegion ( const PrintRegionConfig & config ) : PrintRegion ( config , config . hash ( ) ) { }
PrintRegion : : PrintRegion ( PrintRegionConfig & & config ) : PrintRegion ( std : : move ( config ) , config . hash ( ) ) { }
//BBS
float Print : : min_skirt_length = 0 ;
void Print : : clear ( )
{
std : : scoped_lock < std : : mutex > lock ( this - > state_mutex ( ) ) ;
// The following call should stop background processing if it is running.
this - > invalidate_all_steps ( ) ;
for ( PrintObject * object : m_objects )
delete object ;
m_objects . clear ( ) ;
m_print_regions . clear ( ) ;
m_model . clear_objects ( ) ;
}
// Called by Print::apply().
// This method only accepts PrintConfig option keys.
bool Print : : invalidate_state_by_config_options ( const ConfigOptionResolver & /* new_config */ , const std : : vector < t_config_option_key > & opt_keys )
{
if ( opt_keys . empty ( ) )
return false ;
// Cache the plenty of parameters, which influence the G-code generator only,
// or they are only notes not influencing the generated G-code.
static std : : unordered_set < std : : string > steps_gcode = {
//BBS
" additional_cooling_fan_speed " ,
" reduce_crossing_wall " ,
" max_travel_detour_distance " ,
" printable_area " ,
//BBS: add bed_exclude_area
" bed_exclude_area " ,
" before_layer_change_gcode " ,
" enable_overhang_bridge_fan "
" overhang_fan_speed " ,
" overhang_fan_threshold " ,
" slow_down_for_layer_cooling " ,
" default_acceleration " ,
" deretraction_speed " ,
" close_fan_the_first_x_layers " ,
" machine_end_gcode " ,
" filament_end_gcode " ,
" extruder_clearance_height_to_rod " ,
" extruder_clearance_height_to_lid " ,
" extruder_clearance_radius " ,
2022-10-22 15:31:02 +00:00
" extruder_clearance_max_radius " ,
2022-07-15 15:37:19 +00:00
" extruder_colour " ,
" extruder_offset " ,
" filament_flow_ratio " ,
" reduce_fan_stop_start_freq " ,
" fan_cooling_layer_time " ,
" full_fan_speed_layer " ,
" filament_colour " ,
" filament_diameter " ,
" filament_density " ,
" filament_cost " ,
" initial_layer_acceleration " ,
" top_surface_acceleration " ,
// BBS
" cool_plate_temp_initial_layer " ,
" eng_plate_temp_initial_layer " ,
" hot_plate_temp_initial_layer " ,
2022-08-24 07:06:25 +00:00
" textured_plate_temp_initial_layer " ,
2022-07-15 15:37:19 +00:00
" gcode_add_line_number " ,
" layer_change_gcode " ,
" fan_min_speed " ,
" fan_max_speed " ,
" printable_height " ,
" slow_down_min_speed " ,
# ifdef HAS_PRESSURE_EQUALIZER
" max_volumetric_extrusion_rate_slope_positive " ,
" max_volumetric_extrusion_rate_slope_negative " ,
# endif /* HAS_PRESSURE_EQUALIZER */
" reduce_infill_retraction " ,
" filename_format " ,
" retraction_minimum_travel " ,
" retract_before_wipe " ,
" retract_when_changing_layer " ,
" retraction_length " ,
" retract_length_toolchange " ,
" z_hop " ,
" retract_restart_extra " ,
" retract_restart_extra_toolchange " ,
" retraction_speed " ,
" slow_down_layer_time " ,
" standby_temperature_delta " ,
" machine_start_gcode " ,
" filament_start_gcode " ,
" change_filament_gcode " ,
" wipe " ,
// BBS
" wipe_distance " ,
" curr_bed_type " ,
" nozzle_volume "
} ;
static std : : unordered_set < std : : string > steps_ignore ;
std : : vector < PrintStep > steps ;
std : : vector < PrintObjectStep > osteps ;
bool invalidated = false ;
for ( const t_config_option_key & opt_key : opt_keys ) {
if ( steps_gcode . find ( opt_key ) ! = steps_gcode . end ( ) ) {
// These options only affect G-code export or they are just notes without influence on the generated G-code,
// so there is nothing to invalidate.
steps . emplace_back ( psGCodeExport ) ;
} else if ( steps_ignore . find ( opt_key ) ! = steps_ignore . end ( ) ) {
// These steps have no influence on the G-code whatsoever. Just ignore them.
} else if (
opt_key = = " skirt_loops "
| | opt_key = = " skirt_height "
| | opt_key = = " draft_shield "
| | opt_key = = " skirt_distance "
| | opt_key = = " ooze_prevention "
| | opt_key = = " wipe_tower_x "
| | opt_key = = " wipe_tower_y "
| | opt_key = = " wipe_tower_rotation_angle " ) {
steps . emplace_back ( psSkirtBrim ) ;
} else if (
opt_key = = " initial_layer_print_height "
| | opt_key = = " nozzle_diameter "
// Spiral Vase forces different kind of slicing than the normal model:
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
| | opt_key = = " spiral_mode " ) {
osteps . emplace_back ( posSlice ) ;
} else if (
opt_key = = " print_sequence "
| | opt_key = = " filament_type "
| | opt_key = = " nozzle_temperature_initial_layer "
| | opt_key = = " filament_minimal_purge_on_wipe_tower "
| | opt_key = = " filament_max_volumetric_speed "
| | opt_key = = " gcode_flavor "
| | opt_key = = " single_extruder_multi_material "
| | opt_key = = " nozzle_temperature "
// BBS
| | opt_key = = " cool_plate_temp "
| | opt_key = = " eng_plate_temp "
| | opt_key = = " hot_plate_temp "
2022-08-24 07:06:25 +00:00
| | opt_key = = " textured_plate_temp "
2022-07-15 15:37:19 +00:00
| | opt_key = = " enable_prime_tower "
| | opt_key = = " prime_tower_width "
| | opt_key = = " prime_tower_brim_width "
//|| opt_key == "wipe_tower_bridging"
| | opt_key = = " wipe_tower_no_sparse_layers "
| | opt_key = = " flush_volumes_matrix "
| | opt_key = = " prime_volume "
| | opt_key = = " flush_into_infill "
2022-07-22 09:46:10 +00:00
| | opt_key = = " flush_into_support "
2022-07-15 15:37:19 +00:00
| | opt_key = = " initial_layer_infill_speed "
| | opt_key = = " travel_speed "
| | opt_key = = " travel_speed_z "
| | opt_key = = " initial_layer_speed " ) {
//|| opt_key == "z_offset") {
steps . emplace_back ( psWipeTower ) ;
steps . emplace_back ( psSkirtBrim ) ;
} else if ( opt_key = = " filament_soluble "
| | opt_key = = " filament_is_support " ) {
steps . emplace_back ( psWipeTower ) ;
// Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers.
// Thus switching between soluble / non-soluble interface layer material may require recalculation of supports.
//FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary.
osteps . emplace_back ( posSupportMaterial ) ;
osteps . emplace_back ( posSimplifySupportPath ) ;
} else if (
opt_key = = " initial_layer_line_width "
| | opt_key = = " min_layer_height "
| | opt_key = = " max_layer_height "
| | opt_key = = " resolution "
//BBS: when enable arc fitting, we must re-generate perimeter
| | opt_key = = " enable_arc_fitting "
| | opt_key = = " wall_infill_order " ) {
osteps . emplace_back ( posPerimeters ) ;
osteps . emplace_back ( posInfill ) ;
osteps . emplace_back ( posSupportMaterial ) ;
osteps . emplace_back ( posSimplifyPath ) ;
osteps . emplace_back ( posSimplifySupportPath ) ;
steps . emplace_back ( psSkirtBrim ) ;
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?
invalidated | = this - > invalidate_all_steps ( ) ;
// Continue with the other opt_keys to possibly invalidate any object specific steps.
}
}
sort_remove_duplicates ( steps ) ;
for ( PrintStep step : steps )
invalidated | = this - > invalidate_step ( step ) ;
sort_remove_duplicates ( osteps ) ;
for ( PrintObjectStep ostep : osteps )
for ( PrintObject * object : m_objects )
invalidated | = object - > invalidate_step ( ostep ) ;
return invalidated ;
}
bool Print : : invalidate_step ( PrintStep step )
{
bool invalidated = Inherited : : invalidate_step ( step ) ;
// Propagate to dependent steps.
if ( step ! = psGCodeExport )
invalidated | = Inherited : : invalidate_step ( psGCodeExport ) ;
return invalidated ;
}
// returns true if an object step is done on all objects
// and there's at least one object
bool Print : : is_step_done ( PrintObjectStep step ) const
{
if ( m_objects . empty ( ) )
return false ;
std : : scoped_lock < std : : mutex > lock ( this - > state_mutex ( ) ) ;
for ( const PrintObject * object : m_objects )
if ( ! object - > is_step_done_unguarded ( step ) )
return false ;
return true ;
}
// returns 0-based indices of used extruders
std : : vector < unsigned int > Print : : object_extruders ( ) const
{
std : : vector < unsigned int > extruders ;
extruders . reserve ( m_print_regions . size ( ) * m_objects . size ( ) * 3 ) ;
// BBS
#if 0
for ( const PrintObject * object : m_objects )
for ( const PrintRegion & region : object - > all_regions ( ) )
region . collect_object_printing_extruders ( * this , extruders ) ;
# else
for ( const PrintObject * object : m_objects ) {
const ModelObject * mo = object - > model_object ( ) ;
for ( const ModelVolume * mv : mo - > volumes ) {
std : : vector < int > volume_extruders = mv - > get_extruders ( ) ;
for ( int extruder : volume_extruders ) {
assert ( extruder > 0 ) ;
extruders . push_back ( extruder - 1 ) ;
}
}
}
# endif
sort_remove_duplicates ( extruders ) ;
return extruders ;
}
// returns 0-based indices of used extruders
std : : vector < unsigned int > Print : : support_material_extruders ( ) const
{
std : : vector < unsigned int > extruders ;
bool support_uses_current_extruder = false ;
// BBS
auto num_extruders = ( unsigned int ) m_config . filament_diameter . size ( ) ;
for ( PrintObject * object : m_objects ) {
if ( object - > has_support_material ( ) ) {
assert ( object - > config ( ) . support_filament > = 0 ) ;
if ( object - > config ( ) . support_filament = = 0 )
support_uses_current_extruder = true ;
else {
unsigned int i = ( unsigned int ) object - > config ( ) . support_filament - 1 ;
extruders . emplace_back ( ( i > = num_extruders ) ? 0 : i ) ;
}
assert ( object - > config ( ) . support_interface_filament > = 0 ) ;
if ( object - > config ( ) . support_interface_filament = = 0 )
support_uses_current_extruder = true ;
else {
unsigned int i = ( unsigned int ) object - > config ( ) . support_interface_filament - 1 ;
extruders . emplace_back ( ( i > = num_extruders ) ? 0 : i ) ;
}
}
}
if ( support_uses_current_extruder )
// Add all object extruders to the support extruders as it is not know which one will be used to print supports.
append ( extruders , this - > object_extruders ( ) ) ;
sort_remove_duplicates ( extruders ) ;
return extruders ;
}
// returns 0-based indices of used extruders
std : : vector < unsigned int > Print : : extruders ( ) const
{
std : : vector < unsigned int > extruders = this - > object_extruders ( ) ;
append ( extruders , this - > support_material_extruders ( ) ) ;
sort_remove_duplicates ( extruders ) ;
return extruders ;
}
unsigned int Print : : num_object_instances ( ) const
{
unsigned int instances = 0 ;
for ( const PrintObject * print_object : m_objects )
instances + = ( unsigned int ) print_object - > instances ( ) . size ( ) ;
return instances ;
}
double Print : : max_allowed_layer_height ( ) const
{
double nozzle_diameter_max = 0. ;
for ( unsigned int extruder_id : this - > extruders ( ) )
nozzle_diameter_max = std : : max ( nozzle_diameter_max , m_config . nozzle_diameter . get_at ( extruder_id ) ) ;
return nozzle_diameter_max ;
}
std : : vector < ObjectID > Print : : print_object_ids ( ) const
{
std : : vector < ObjectID > out ;
// Reserve one more for the caller to append the ID of the Print itself.
out . reserve ( m_objects . size ( ) + 1 ) ;
for ( const PrintObject * print_object : m_objects )
out . emplace_back ( print_object - > id ( ) ) ;
return out ;
}
bool Print : : has_infinite_skirt ( ) const
{
return ( m_config . draft_shield = = dsEnabled & & m_config . skirt_loops > 0 ) | | ( m_config . ooze_prevention & & this - > extruders ( ) . size ( ) > 1 ) ;
}
bool Print : : has_skirt ( ) const
{
return ( m_config . skirt_height > 0 & & m_config . skirt_loops > 0 ) | | m_config . draft_shield ! = dsDisabled ;
}
bool Print : : has_brim ( ) const
{
return std : : any_of ( m_objects . begin ( ) , m_objects . end ( ) , [ ] ( PrintObject * object ) { return object - > has_brim ( ) ; } ) ;
}
//BBS
StringObjectException Print : : sequential_print_clearance_valid ( const Print & print , Polygons * polygons , std : : vector < std : : pair < Polygon , float > > * height_polygons )
{
StringObjectException single_object_exception ;
auto print_config = print . config ( ) ;
Pointfs excluse_area_points = print_config . bed_exclude_area . values ;
Polygons exclude_polys ;
Polygon exclude_poly ;
const Vec3d print_origin = print . get_plate_origin ( ) ;
for ( int i = 0 ; i < excluse_area_points . size ( ) ; i + + ) {
auto pt = excluse_area_points [ i ] ;
exclude_poly . points . emplace_back ( scale_ ( pt . x ( ) + print_origin . x ( ) ) , scale_ ( pt . y ( ) + print_origin . y ( ) ) ) ;
if ( i % 4 = = 3 ) { // exclude areas are always rectangle
exclude_polys . push_back ( exclude_poly ) ;
exclude_poly . points . clear ( ) ;
}
}
std : : map < ObjectID , Polygon > map_model_object_to_convex_hull ;
struct print_instance_info
{
const PrintInstance * print_instance ;
BoundingBox bounding_box ;
Polygon hull_polygon ;
2022-10-22 15:31:02 +00:00
int index ;
double arrange_score ;
2022-10-26 13:12:19 +00:00
double height ;
2022-07-15 15:37:19 +00:00
} ;
std : : vector < struct print_instance_info > print_instance_with_bounding_box ;
{
// sequential_print_horizontal_clearance_valid
Polygons convex_hulls_other ;
if ( polygons ! = nullptr )
polygons - > clear ( ) ;
std : : vector < size_t > intersecting_idxs ;
for ( const PrintObject * print_object : print . objects ( ) ) {
assert ( ! print_object - > model_object ( ) - > instances . empty ( ) ) ;
assert ( ! print_object - > instances ( ) . empty ( ) ) ;
ObjectID model_object_id = print_object - > model_object ( ) - > id ( ) ;
auto it_convex_hull = map_model_object_to_convex_hull . find ( model_object_id ) ;
// Get convex hull of all printable volumes assigned to this print object.
ModelInstance * model_instance0 = print_object - > model_object ( ) - > instances . front ( ) ;
if ( it_convex_hull = = map_model_object_to_convex_hull . end ( ) ) {
// Calculate the convex hull of a printable object.
// Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
it_convex_hull = map_model_object_to_convex_hull . emplace_hint ( it_convex_hull , model_object_id ,
print_object - > model_object ( ) - > convex_hull_2d ( Geometry : : assemble_transform (
{ 0.0 , 0.0 , model_instance0 - > get_offset ( ) . z ( ) } , model_instance0 - > get_rotation ( ) , model_instance0 - > get_scaling_factor ( ) , model_instance0 - > get_mirror ( ) ) ) ) ;
}
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull - > second ;
const double z_diff = Geometry : : rotation_diff_z ( model_instance0 - > get_rotation ( ) , print_object - > instances ( ) . front ( ) . model_instance - > get_rotation ( ) ) ;
if ( std : : abs ( z_diff ) > EPSILON )
convex_hull0 . rotate ( z_diff ) ;
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for ( const PrintInstance & instance : print_object - > instances ( ) ) {
Polygon convex_hull_no_offset = convex_hull0 , convex_hull ;
convex_hull = offset ( convex_hull_no_offset ,
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float ( scale_ ( 0.5 * print . config ( ) . extruder_clearance_radius . value - EPSILON ) ) ,
jtRound , scale_ ( 0.1 ) ) . front ( ) ;
// instance.shift is a position of a centered object, while model object may not be centered.
// Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
convex_hull . translate ( instance . shift - print_object - > center_offset ( ) ) ;
convex_hull_no_offset . translate ( instance . shift - print_object - > center_offset ( ) ) ;
//juedge the exclude area
if ( ! intersection ( exclude_polys , convex_hull_no_offset ) . empty ( ) ) {
if ( single_object_exception . string . empty ( ) ) {
2022-09-29 06:59:21 +00:00
single_object_exception . string = ( boost : : format ( L ( " %1% is too close to exclusion area, there may be collisions when printing. " ) ) % instance . model_instance - > get_object ( ) - > name ) . str ( ) ;
2022-07-15 15:37:19 +00:00
single_object_exception . object = instance . model_instance - > get_object ( ) ;
}
else {
2022-10-24 01:35:09 +00:00
single_object_exception . string + = " \n " + ( boost : : format ( L ( " %1% is too close to exclusion area, there may be collisions when printing. " ) ) % instance . model_instance - > get_object ( ) - > name ) . str ( ) ;
2022-07-15 15:37:19 +00:00
single_object_exception . object = nullptr ;
}
//if (polygons) {
// intersecting_idxs.emplace_back(convex_hulls_other.size());
//}
}
// if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
for ( size_t i = 0 ; i < convex_hulls_other . size ( ) ; + + i ) {
if ( ! intersection ( convex_hulls_other [ i ] , convex_hull ) . empty ( ) ) {
if ( single_object_exception . string . empty ( ) ) {
single_object_exception . string = ( boost : : format ( L ( " %1% is too close to others, and collisions may be caused. " ) ) % instance . model_instance - > get_object ( ) - > name ) . str ( ) ;
single_object_exception . object = instance . model_instance - > get_object ( ) ;
}
else {
single_object_exception . string + = " \n " + ( boost : : format ( L ( " %1% is too close to others, and collisions may be caused. " ) ) % instance . model_instance - > get_object ( ) - > name ) . str ( ) ;
single_object_exception . object = nullptr ;
}
if ( polygons ) {
intersecting_idxs . emplace_back ( i ) ;
intersecting_idxs . emplace_back ( convex_hulls_other . size ( ) ) ;
}
}
}
struct print_instance_info print_info { & instance , convex_hull . bounding_box ( ) , convex_hull } ;
2022-10-26 13:12:19 +00:00
print_info . height = instance . print_object - > height ( ) ;
2022-07-15 15:37:19 +00:00
print_instance_with_bounding_box . push_back ( std : : move ( print_info ) ) ;
convex_hulls_other . emplace_back ( std : : move ( convex_hull ) ) ;
}
}
if ( ! intersecting_idxs . empty ( ) ) {
// use collected indices (inside convex_hulls_other) to update output
std : : sort ( intersecting_idxs . begin ( ) , intersecting_idxs . end ( ) ) ;
intersecting_idxs . erase ( std : : unique ( intersecting_idxs . begin ( ) , intersecting_idxs . end ( ) ) , intersecting_idxs . end ( ) ) ;
for ( size_t i : intersecting_idxs ) {
polygons - > emplace_back ( std : : move ( convex_hulls_other [ i ] ) ) ;
}
}
}
2022-10-22 15:31:02 +00:00
// calc sort order
2022-10-27 13:15:46 +00:00
double hc1 = scale_ ( print . config ( ) . extruder_clearance_height_to_lid ) ; // height to lid
double hc2 = scale_ ( print . config ( ) . extruder_clearance_height_to_rod ) ; // height to rod
2022-10-26 13:12:19 +00:00
double printable_height = scale_ ( print . config ( ) . printable_height ) ;
2022-10-22 15:31:02 +00:00
auto bed_points = get_bed_shape ( print_config ) ;
float bed_width = bed_points [ 1 ] . x ( ) - bed_points [ 0 ] . x ( ) ;
// 如果扩大以后的多边形的距离小于这个值,就需要严格保证从左到右的打印顺序,否则会撞工具头右侧
float unsafe_dist = scale_ ( print_config . extruder_clearance_max_radius . value - print_config . extruder_clearance_radius . value ) ;
2022-10-27 13:15:46 +00:00
struct VecHash
{
size_t operator ( ) ( const Vec2i & n1 ) const
{
return std : : hash < coord_t > ( ) ( int ( n1 ( 0 ) * 100 + 100 ) ) + std : : hash < coord_t > ( ) ( int ( n1 ( 1 ) * 100 + 100 ) ) * 101 ;
}
} ;
std : : unordered_set < Vec2i , VecHash > left_right_pair ; // pairs in this vector must strictly obey the left-right order
2022-10-22 15:31:02 +00:00
for ( size_t i = 0 ; i < print_instance_with_bounding_box . size ( ) ; i + + ) {
auto & inst = print_instance_with_bounding_box [ i ] ;
inst . index = i ;
Point pt = inst . bounding_box . center ( ) ;
inst . arrange_score = pt . x ( ) / 2 + pt . y ( ) ; // we prefer print row-by-row, so cost on x-direction is smaller
}
for ( size_t i = 0 ; i < print_instance_with_bounding_box . size ( ) ; i + + ) {
auto & inst = print_instance_with_bounding_box [ i ] ;
2022-10-26 13:12:19 +00:00
auto & l = print_instance_with_bounding_box [ i ] ;
2022-10-22 15:31:02 +00:00
for ( size_t j = 0 ; j < print_instance_with_bounding_box . size ( ) ; j + + ) {
if ( j ! = i ) {
auto & r = print_instance_with_bounding_box [ j ] ;
auto ly1 = l . bounding_box . min . y ( ) ;
auto ly2 = l . bounding_box . max . y ( ) ;
auto ry1 = r . bounding_box . min . y ( ) ;
auto ry2 = r . bounding_box . max . y ( ) ;
auto lx1 = l . bounding_box . min . x ( ) ;
auto rx1 = r . bounding_box . min . x ( ) ;
auto lx2 = l . bounding_box . max . x ( ) ;
auto rx2 = r . bounding_box . max . x ( ) ;
auto inter_min = std : : max ( ly1 , ry1 ) ;
auto inter_max = std : : min ( ly2 , ry2 ) ;
auto inter_y = inter_max - inter_min ;
inter_min = std : : max ( lx1 , rx1 ) ;
inter_max = std : : min ( lx2 , rx2 ) ;
auto inter_x = inter_max - inter_min ;
2022-10-26 13:12:19 +00:00
// 如果y方向的重合超过轮廓的膨胀量, 说明两个物体在一行, 应该先打左边的物体, 即先比较二者的x坐标。
2022-10-27 13:15:46 +00:00
if ( inter_y > scale_ ( 1 ) ) {
2022-10-22 15:31:02 +00:00
if ( std : : max ( rx1 - lx2 , lx1 - rx2 ) < unsafe_dist ) {
if ( lx1 > rx1 ) {
2022-10-27 13:15:46 +00:00
left_right_pair . insert ( { j , i } ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " in-a-row, print_instance " < < r . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < r . arrange_score < < " ) "
< < " -> " < < l . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < l . arrange_score < < " ) " ;
2022-10-22 15:31:02 +00:00
} else {
2022-10-27 13:15:46 +00:00
left_right_pair . insert ( { i , j } ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " in-a-row, print_instance " < < l . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < l . arrange_score < < " ) "
< < " -> " < < r . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < r . arrange_score < < " ) " ;
2022-10-22 15:31:02 +00:00
}
}
2022-10-26 13:12:19 +00:00
}
2022-10-27 13:15:46 +00:00
if ( l . height > hc1 & & r . height < hc1 ) {
// 当前物体超过了顶盖高度,必须后打
left_right_pair . insert ( { j , i } ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " height>hc1, print_instance " < < r . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < r . arrange_score < < " ) "
< < " -> " < < l . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < l . arrange_score < < " ) " ;
}
else if ( l . height > hc2 & & l . height > r . height & & l . arrange_score < r . arrange_score ) {
// 如果当前物体的高度超过滑杆, 且比r高, 就给它加一点代价, 尽量让高的物体后打( 只有物体高度超过滑杆时才有必要按高度来)
l . arrange_score = std : : max ( l . arrange_score , r . arrange_score + bed_width / 2 ) ;
BOOST_LOG_TRIVIAL ( debug ) < < " height>hc2, print_instance " < < inst . print_instance - > model_instance - > get_object ( ) - > name
2022-10-26 13:12:19 +00:00
< < " , right= " < < r . print_instance - > model_instance - > get_object ( ) - > name < < " , l.score: " < < l . arrange_score
< < " , r.score: " < < r . arrange_score ;
2022-10-22 15:31:02 +00:00
}
}
}
}
BOOST_LOG_TRIVIAL ( debug ) < < " bed width: " < < bed_width < < " , unsafe_dist: " < < unsafe_dist ;
2022-10-27 13:15:46 +00:00
// 多做几次代价传播,因为前一次有些值没有更新。
// TODO 更好的办法是建立一颗树,一步到位。不过我暂时没精力搞,先就这样吧
for ( int k = 0 ; k < 5 ; k + + )
for ( auto p : left_right_pair ) {
auto & l = print_instance_with_bounding_box [ p ( 0 ) ] ;
auto & r = print_instance_with_bounding_box [ p ( 1 ) ] ;
if ( r . arrange_score < l . arrange_score )
r . arrange_score = l . arrange_score + bed_width / 2 ;
}
for ( auto p : left_right_pair ) {
auto & l = print_instance_with_bounding_box [ p ( 0 ) ] ;
auto & r = print_instance_with_bounding_box [ p ( 1 ) ] ;
BOOST_LOG_TRIVIAL ( debug ) < < " print_instance " < < l . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < l . arrange_score < < " ) "
< < " -> " < < r . print_instance - > model_instance - > get_object ( ) - > name < < " ( " < < r . arrange_score < < " ) " ;
2022-10-22 15:31:02 +00:00
}
// sort the print instance
2022-07-15 15:37:19 +00:00
std : : sort ( print_instance_with_bounding_box . begin ( ) , print_instance_with_bounding_box . end ( ) ,
2022-10-27 13:15:46 +00:00
[ ] ( print_instance_info & l , print_instance_info & r ) { return l . arrange_score < r . arrange_score ; } ) ;
2022-07-15 15:37:19 +00:00
2022-10-26 13:12:19 +00:00
for ( auto & inst : print_instance_with_bounding_box )
BOOST_LOG_TRIVIAL ( debug ) < < " after sorting print_instance " < < inst . print_instance - > model_instance - > get_object ( ) - > name < < " , score: " < < inst . arrange_score
< < " , height: " < < inst . height ;
2022-07-15 15:37:19 +00:00
// sequential_print_vertical_clearance_valid
{
// Ignore the last instance printed.
//print_instance_with_bounding_box.pop_back();
/*bool has_interlaced_objects = false;
for ( int k = 0 ; k < print_instance_count ; k + + )
{
auto inst = print_instance_with_bounding_box [ k ] . print_instance ;
auto bbox = print_instance_with_bounding_box [ k ] . bounding_box ;
auto iy1 = bbox . min . y ( ) ;
auto iy2 = bbox . max . y ( ) ;
for ( int i = 0 ; i < k ; i + + )
{
auto & p = print_instance_with_bounding_box [ i ] . print_instance ;
auto bbox2 = print_instance_with_bounding_box [ i ] . bounding_box ;
auto py1 = bbox2 . min . y ( ) ;
auto py2 = bbox2 . max . y ( ) ;
auto inter_min = std : : max ( iy1 , py1 ) ; // min y of intersection
auto inter_max = std : : min ( iy2 , py2 ) ; // max y of intersection. length=max_y-min_y>0 means intersection exists
if ( inter_max - inter_min > 0 ) {
has_interlaced_objects = true ;
break ;
}
}
if ( has_interlaced_objects )
break ;
} */
// if objects are not overlapped on y-axis, they will not collide even if they are taller than extruder_clearance_height_to_rod
int print_instance_count = print_instance_with_bounding_box . size ( ) ;
std : : map < const PrintInstance * , std : : pair < Polygon , float > > too_tall_instances ;
for ( int k = 0 ; k < print_instance_count ; k + + )
{
auto inst = print_instance_with_bounding_box [ k ] . print_instance ;
2022-10-27 13:15:46 +00:00
// 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
auto bbox = print_instance_with_bounding_box [ k ] . bounding_box . inflated ( - scale_ ( 0.5 * print . config ( ) . extruder_clearance_radius . value ) ) ;
2022-07-15 15:37:19 +00:00
auto iy1 = bbox . min . y ( ) ;
auto iy2 = bbox . max . y ( ) ;
( const_cast < ModelInstance * > ( inst - > model_instance ) ) - > arrange_order = k + 1 ;
double height = ( k = = ( print_instance_count - 1 ) ) ? printable_height : hc1 ;
/*if (has_interlaced_objects) {
if ( ( k < ( print_instance_count - 1 ) ) & & ( inst - > print_object - > height ( ) > hc2 ) ) {
too_tall_instances [ inst ] = std : : make_pair ( print_instance_with_bounding_box [ k ] . hull_polygon , unscaled < double > ( hc2 ) ) ;
}
}
else {
if ( ( k < ( print_instance_count - 1 ) ) & & ( inst - > print_object - > height ( ) > hc1 ) ) {
too_tall_instances [ inst ] = std : : make_pair ( print_instance_with_bounding_box [ k ] . hull_polygon , unscaled < double > ( hc1 ) ) ;
}
} */
for ( int i = k + 1 ; i < print_instance_count ; i + + )
{
auto & p = print_instance_with_bounding_box [ i ] . print_instance ;
auto bbox2 = print_instance_with_bounding_box [ i ] . bounding_box ;
auto py1 = bbox2 . min . y ( ) ;
auto py2 = bbox2 . max . y ( ) ;
auto inter_min = std : : max ( iy1 , py1 ) ; // min y of intersection
auto inter_max = std : : min ( iy2 , py2 ) ; // max y of intersection. length=max_y-min_y>0 means intersection exists
if ( inter_max - inter_min > 0 ) {
height = hc2 ;
break ;
}
}
if ( height < inst - > print_object - > height ( ) )
too_tall_instances [ inst ] = std : : make_pair ( print_instance_with_bounding_box [ k ] . hull_polygon , unscaled < double > ( height ) ) ;
}
if ( too_tall_instances . size ( ) > 0 ) {
//return {, inst->model_instance->get_object()};
for ( auto & iter : too_tall_instances ) {
if ( single_object_exception . string . empty ( ) ) {
single_object_exception . string = ( boost : : format ( L ( " %1% is too tall, and collisions will be caused. " ) ) % iter . first - > model_instance - > get_object ( ) - > name ) . str ( ) ;
single_object_exception . object = iter . first - > model_instance - > get_object ( ) ;
}
else {
single_object_exception . string + = " \n " + ( boost : : format ( L ( " %1% is too tall, and collisions will be caused. " ) ) % iter . first - > model_instance - > get_object ( ) - > name ) . str ( ) ;
single_object_exception . object = nullptr ;
}
if ( height_polygons )
height_polygons - > emplace_back ( std : : move ( iter . second ) ) ;
}
}
}
return single_object_exception ;
}
//BBS
static StringObjectException layered_print_cleareance_valid ( const Print & print , StringObjectException * warning )
{
std : : vector < const PrintInstance * > print_instances_ordered = sort_object_instances_by_model_order ( print , true ) ;
if ( print_instances_ordered . size ( ) < 1 )
return { } ;
auto print_config = print . config ( ) ;
Pointfs excluse_area_points = print_config . bed_exclude_area . values ;
Polygons exclude_polys ;
Polygon exclude_poly ;
const Vec3d print_origin = print . get_plate_origin ( ) ;
for ( int i = 0 ; i < excluse_area_points . size ( ) ; i + + ) {
auto pt = excluse_area_points [ i ] ;
exclude_poly . points . emplace_back ( scale_ ( pt . x ( ) + print_origin . x ( ) ) , scale_ ( pt . y ( ) + print_origin . y ( ) ) ) ;
if ( i % 4 = = 3 ) { // exclude areas are always rectangle
exclude_polys . push_back ( exclude_poly ) ;
exclude_poly . points . clear ( ) ;
}
}
std : : map < const PrintInstance * , Polygon > map_model_object_to_convex_hull ;
// sequential_print_horizontal_clearance_valid
Polygons convex_hulls_other ;
for ( int k = 0 ; k < print_instances_ordered . size ( ) ; k + + )
{
auto & inst = print_instances_ordered [ k ] ;
auto it_convex_hull = map_model_object_to_convex_hull . find ( inst ) ;
// Get convex hull of all printable volumes assigned to this print object.
const ModelInstance * model_instance0 = inst - > model_instance ;
if ( it_convex_hull = = map_model_object_to_convex_hull . end ( ) ) {
// Calculate the convex hull of a printable object.
auto convex_hull0 = inst - > print_object - > model_object ( ) - > convex_hull_2d (
Geometry : : assemble_transform ( Vec3d : : Zero ( ) , model_instance0 - > get_rotation ( ) , model_instance0 - > get_scaling_factor ( ) , model_instance0 - > get_mirror ( ) ) ) ;
double z_diff = Geometry : : rotation_diff_z ( model_instance0 - > get_rotation ( ) , inst - > model_instance - > get_rotation ( ) ) ;
if ( std : : abs ( z_diff ) > EPSILON )
convex_hull0 . rotate ( z_diff ) ;
// instance.shift is a position of a centered object, while model object may not be centered.
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
convex_hull0 . translate ( inst - > shift - inst - > print_object - > center_offset ( ) ) ;
it_convex_hull = map_model_object_to_convex_hull . emplace_hint ( it_convex_hull , inst , convex_hull0 ) ;
}
Polygon & convex_hull = it_convex_hull - > second ;
Polygons convex_hulls_temp ;
convex_hulls_temp . push_back ( convex_hull ) ;
if ( ! intersection ( convex_hulls_other , convex_hulls_temp ) . empty ( ) ) {
if ( warning ) {
2022-09-29 06:59:21 +00:00
warning - > string = inst - > model_instance - > get_object ( ) - > name + L ( " is too close to others, there may be collisions when printing. \n " ) ;
2022-07-15 15:37:19 +00:00
warning - > object = inst - > model_instance - > get_object ( ) ;
}
}
if ( ! intersection ( exclude_polys , convex_hull ) . empty ( ) ) {
2022-09-29 06:59:21 +00:00
return { inst - > model_instance - > get_object ( ) - > name + L ( " is too close to exclusion area, there may be collisions when printing. \n " ) , inst - > model_instance - > get_object ( ) } ;
2022-07-15 15:37:19 +00:00
/*if (warning) {
2022-09-29 06:59:21 +00:00
warning - > string = inst - > model_instance - > get_object ( ) - > name + L ( " is too close to exclusion area, there may be collisions when printing. \n " ) ;
2022-07-15 15:37:19 +00:00
warning - > object = inst - > model_instance - > get_object ( ) ;
} */
}
convex_hulls_other . emplace_back ( convex_hull ) ;
}
//BBS: add the wipe tower check logic
const PrintConfig & config = print . config ( ) ;
int filaments_count = print . extruders ( ) . size ( ) ;
int plate_index = print . get_plate_index ( ) ;
const Vec3d plate_origin = print . get_plate_origin ( ) ;
float x = config . wipe_tower_x . get_at ( plate_index ) + plate_origin ( 0 ) ;
float y = config . wipe_tower_y . get_at ( plate_index ) + plate_origin ( 1 ) ;
float width = config . prime_tower_width . value ;
float a = config . wipe_tower_rotation_angle . value ;
//float v = config.wiping_volume.value;
float depth = print . wipe_tower_data ( filaments_count ) . depth ;
//float brim_width = print.wipe_tower_data(filaments_count).brim_width;
Polygons convex_hulls_temp ;
2022-09-27 02:14:04 +00:00
if ( print . has_wipe_tower ( ) ) {
Polygon wipe_tower_convex_hull ;
wipe_tower_convex_hull . points . emplace_back ( scale_ ( x ) , scale_ ( y ) ) ;
wipe_tower_convex_hull . points . emplace_back ( scale_ ( x + width ) , scale_ ( y ) ) ;
wipe_tower_convex_hull . points . emplace_back ( scale_ ( x + width ) , scale_ ( y + depth ) ) ;
wipe_tower_convex_hull . points . emplace_back ( scale_ ( x ) , scale_ ( y + depth ) ) ;
wipe_tower_convex_hull . rotate ( a ) ;
convex_hulls_temp . push_back ( wipe_tower_convex_hull ) ;
}
2022-07-15 15:37:19 +00:00
if ( ! intersection ( convex_hulls_other , convex_hulls_temp ) . empty ( ) ) {
if ( warning ) {
warning - > string + = L ( " Prime Tower " ) + L ( " is too close to others, and collisions may be caused. \n " ) ;
}
}
if ( ! intersection ( exclude_polys , convex_hulls_temp ) . empty ( ) ) {
/*if (warning) {
2022-09-29 06:59:21 +00:00
warning - > string + = L ( " Prime Tower is too close to exclusion area, there may be collisions when printing. \n " ) ;
2022-07-15 15:37:19 +00:00
} */
return { L ( " Prime Tower " ) + L ( " is too close to exclusion area, and collisions will be caused. \n " ) } ;
}
return { } ;
}
//BBS
static std : : map < std : : string , bool > filament_is_high_temp {
{ " PLA " , false } ,
2022-08-02 04:16:51 +00:00
{ " PLA-CF " , false } ,
{ " PETG " , true } ,
2022-07-15 15:37:19 +00:00
{ " ABS " , true } ,
{ " TPU " , false } ,
{ " PA " , true } ,
2022-07-27 08:01:28 +00:00
{ " PA-CF " , true } ,
2022-07-15 15:37:19 +00:00
{ " PET-CF " , true } ,
{ " PC " , true } ,
2022-08-02 04:16:51 +00:00
{ " ASA " , true }
2022-07-15 15:37:19 +00:00
} ;
//BBS: this function is used to check whether multi filament can be printed
StringObjectException Print : : check_multi_filament_valid ( const Print & print )
{
bool has_high_temperature_filament = false ;
bool has_low_temperature_filament = false ;
auto print_config = print . config ( ) ;
std : : vector < unsigned int > extruders = print . extruders ( ) ;
for ( const auto & extruder_idx : extruders ) {
std : : string filament_type = print_config . filament_type . get_at ( extruder_idx ) ;
if ( filament_is_high_temp . find ( filament_type ) ! = filament_is_high_temp . end ( ) ) {
if ( filament_is_high_temp [ filament_type ] )
has_high_temperature_filament = true ;
else
has_low_temperature_filament = true ;
}
}
if ( has_high_temperature_filament & & has_low_temperature_filament )
return { L ( " Can not print multiple filaments which have large difference of temperature together. Otherwise, the extruder and nozzle may be blocked or damaged during printing " ) } ;
return { std : : string ( ) } ;
}
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
//BBS: refine seq-print validation logic
StringObjectException Print : : validate ( StringObjectException * warning , Polygons * collison_polygons , std : : vector < std : : pair < Polygon , float > > * height_polygons ) const
{
std : : vector < unsigned int > extruders = this - > extruders ( ) ;
if ( m_objects . empty ( ) )
return { std : : string ( ) } ;
if ( extruders . empty ( ) )
return { L ( " No extrusions under current settings. " ) } ;
if ( extruders . size ( ) > 1 ) {
auto ret = check_multi_filament_valid ( * this ) ;
if ( ! ret . string . empty ( ) )
return ret ;
}
if ( m_config . print_sequence = = PrintSequence : : ByObject ) {
2022-09-14 04:21:17 +00:00
if ( m_config . timelapse_type = = TimelapseType : : tlSmooth )
return { L ( " Smooth mode of timelapse is not supported when \" by object \" sequence is enabled. " ) } ;
2022-07-15 15:37:19 +00:00
//BBS: refine seq-print validation logic
auto ret = sequential_print_clearance_valid ( * this , collison_polygons , height_polygons ) ;
if ( ! ret . string . empty ( ) )
return ret ;
}
else {
//BBS
auto ret = layered_print_cleareance_valid ( * this , warning ) ;
if ( ! ret . string . empty ( ) ) {
return ret ;
}
}
if ( m_config . spiral_mode ) {
size_t total_copies_count = 0 ;
for ( const PrintObject * object : m_objects )
total_copies_count + = object - > instances ( ) . size ( ) ;
// #4043
if ( total_copies_count > 1 & & m_config . print_sequence ! = PrintSequence : : ByObject )
return { L ( " Please select \" By object \" print sequence to print multiple objects in spiral vase mode. " ) , nullptr , " spiral_mode " } ;
assert ( m_objects . size ( ) = = 1 ) ;
if ( m_objects . front ( ) - > all_regions ( ) . size ( ) > 1 )
return { L ( " The spiral vase mode does not work when an object contains more than one materials. " ) , nullptr , " spiral_mode " } ;
}
if ( this - > has_wipe_tower ( ) & & ! m_objects . empty ( ) ) {
// Make sure all extruders use same diameter filament and have the same nozzle diameter
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
double first_nozzle_diam = m_config . nozzle_diameter . get_at ( extruders . front ( ) ) ;
double first_filament_diam = m_config . filament_diameter . get_at ( extruders . front ( ) ) ;
for ( const auto & extruder_idx : extruders ) {
double nozzle_diam = m_config . nozzle_diameter . get_at ( extruder_idx ) ;
double filament_diam = m_config . filament_diameter . get_at ( extruder_idx ) ;
if ( nozzle_diam - EPSILON > first_nozzle_diam | | nozzle_diam + EPSILON < first_nozzle_diam
| | std : : abs ( ( filament_diam - first_filament_diam ) / first_filament_diam ) > 0.1 )
// BBS: remove L()
return { ( " Different nozzle diameters and different filament diameters is not allowed when prime tower is enabled. " ) } ;
}
if ( m_config . ooze_prevention )
return { ( " Ooze prevention is currently not supported with the prime tower enabled. " ) } ;
// BBS: remove following logic and _L()
#if 0
if ( m_config . gcode_flavor ! = gcfRepRapSprinter & & m_config . gcode_flavor ! = gcfRepRapFirmware & &
m_config . gcode_flavor ! = gcfRepetier & & m_config . gcode_flavor ! = gcfMarlinLegacy & & m_config . gcode_flavor ! = gcfMarlinFirmware )
return { ( " The prime tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors. " ) } ;
if ( ( m_config . print_sequence = = PrintSequence : : ByObject ) & & extruders . size ( ) > 1 )
return { L ( " The prime tower is not supported in \" By object \" print. " ) , nullptr , " enable_prime_tower " } ;
// BBS: When prime tower is on, object layer and support layer must be aligned. So support gap should be multiple of object layer height.
for ( size_t i = 0 ; i < m_objects . size ( ) ; i + + ) {
const PrintObject * object = m_objects [ i ] ;
const SlicingParameters & slicing_params = object - > slicing_parameters ( ) ;
if ( object - > config ( ) . adaptive_layer_height ) {
return { L ( " The prime tower is not supported when adaptive layer height is on. It requires that all objects have the same layer height. " ) , object , " adaptive_layer_height " } ;
}
if ( ! object - > config ( ) . enable_support )
continue ;
double gap_layers = slicing_params . gap_object_support / slicing_params . layer_height ;
if ( gap_layers - ( int ) gap_layers > EPSILON ) {
return { L ( " The prime tower requires \" support gap \" to be multiple of layer height " ) , object } ;
}
}
# endif
if ( m_objects . size ( ) > 1 ) {
bool has_custom_layering = false ;
std : : vector < std : : vector < coordf_t > > layer_height_profiles ;
for ( const PrintObject * object : m_objects ) {
has_custom_layering = ! object - > model_object ( ) - > layer_config_ranges . empty ( ) | | ! object - > model_object ( ) - > layer_height_profile . empty ( ) ;
if ( has_custom_layering ) {
layer_height_profiles . assign ( m_objects . size ( ) , std : : vector < coordf_t > ( ) ) ;
break ;
}
}
const SlicingParameters & slicing_params0 = m_objects . front ( ) - > slicing_parameters ( ) ;
size_t tallest_object_idx = 0 ;
if ( has_custom_layering )
PrintObject : : update_layer_height_profile ( * m_objects . front ( ) - > model_object ( ) , slicing_params0 , layer_height_profiles . front ( ) ) ;
for ( size_t i = 1 ; i < m_objects . size ( ) ; + + i ) {
const PrintObject * object = m_objects [ i ] ;
const SlicingParameters & slicing_params = object - > slicing_parameters ( ) ;
if ( std : : abs ( slicing_params . first_print_layer_height - slicing_params0 . first_print_layer_height ) > EPSILON | |
std : : abs ( slicing_params . layer_height - slicing_params0 . layer_height ) > EPSILON )
return { L ( " The prime tower requires that all objects have the same layer heights " ) , object , " initial_layer_print_height " } ;
if ( slicing_params . raft_layers ( ) ! = slicing_params0 . raft_layers ( ) )
return { L ( " The prime tower requires that all objects are printed over the same number of raft layers " ) , object , " raft_layers " } ;
// BBS: support gap can be multiple of object layer height, remove _L()
#if 0
if ( slicing_params0 . gap_object_support ! = slicing_params . gap_object_support | |
slicing_params0 . gap_support_object ! = slicing_params . gap_support_object )
return { ( " The prime tower is only supported for multiple objects if they are printed with the same support_top_z_distance " ) , object } ;
# endif
if ( ! equal_layering ( slicing_params , slicing_params0 ) )
return { L ( " The prime tower requires that all objects are sliced with the same layer heights. " ) , object } ;
if ( has_custom_layering ) {
PrintObject : : update_layer_height_profile ( * object - > model_object ( ) , slicing_params , layer_height_profiles [ i ] ) ;
if ( * ( layer_height_profiles [ i ] . end ( ) - 2 ) > * ( layer_height_profiles [ tallest_object_idx ] . end ( ) - 2 ) )
tallest_object_idx = i ;
}
}
// BBS: remove obsolete logics and _L()
#if 0
if ( has_custom_layering ) {
for ( size_t idx_object = 0 ; idx_object < m_objects . size ( ) ; + + idx_object ) {
if ( idx_object = = tallest_object_idx )
continue ;
if ( layer_height_profiles [ idx_object ] ! = layer_height_profiles [ tallest_object_idx ] )
return { ( " The prime tower is only supported if all objects have the same variable layer height " ) , m_objects [ idx_object ] } ;
}
}
# endif
}
}
{
// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
double min_nozzle_diameter = std : : numeric_limits < double > : : max ( ) ;
double max_nozzle_diameter = 0 ;
for ( unsigned int extruder_id : extruders ) {
double dmr = m_config . nozzle_diameter . get_at ( extruder_id ) ;
min_nozzle_diameter = std : : min ( min_nozzle_diameter , dmr ) ;
max_nozzle_diameter = std : : max ( max_nozzle_diameter , dmr ) ;
}
// BBS: remove L()
#if 0
// We currently allow one to assign extruders with a higher index than the number
// of physical extruders the machine is equipped with, as the Printer::apply() clamps them.
unsigned int total_extruders_count = m_config . nozzle_diameter . size ( ) ;
for ( const auto & extruder_idx : extruders )
if ( extruder_idx > = total_extruders_count )
return ( " One or more object were assigned an extruder that the printer does not have. " ) ;
# endif
auto validate_extrusion_width = [ /*min_nozzle_diameter,*/ max_nozzle_diameter ] ( const ConfigBase & config , const char * opt_key , double layer_height , std : : string & err_msg ) - > bool {
// This may change in the future, if we switch to "extrusion width wrt. nozzle diameter"
// instead of currently used logic "extrusion width wrt. layer height", see GH issues #1923 #2829.
// double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
// double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
double extrusion_width_min = config . get_abs_value ( opt_key ) ;
double extrusion_width_max = config . get_abs_value ( opt_key ) ;
if ( extrusion_width_min = = 0 ) {
// Default "auto-generated" extrusion width is always valid.
} else if ( extrusion_width_min < = layer_height ) {
err_msg = L ( " Too small line width " ) ;
return false ;
} else if ( extrusion_width_max > = max_nozzle_diameter * 2.5 ) {
err_msg = L ( " Too large line width " ) ;
return false ;
}
return true ;
} ;
for ( PrintObject * object : m_objects ) {
if ( object - > has_support_material ( ) ) {
// BBS: remove useless logics and L()
#if 0
if ( ( object - > config ( ) . support_filament = = 0 | | object - > config ( ) . support_interface_filament = = 0 ) & & max_nozzle_diameter - min_nozzle_diameter > EPSILON ) {
// The object has some form of support and either support_filament or support_interface_filament
// will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
// are of the same diameter.
return { ( " Printing with multiple extruders of differing nozzle diameters. "
" If support is to be printed with the current filament (support_filament == 0 or support_interface_filament == 0), "
" all nozzles have to be of the same diameter. " ) , object , " support_filament " } ;
}
# endif
// BBS
#if 0
if ( this - > has_wipe_tower ( ) & & object - > config ( ) . independent_support_layer_height ) {
return { L ( " The prime tower requires that support has the same layer height with object. " ) , object , " support_filament " } ;
}
# endif
}
// Do we have custom support data that would not be used?
// Notify the user in that case.
if ( ! object - > has_support ( ) & & warning ) {
for ( const ModelVolume * mv : object - > model_object ( ) - > volumes ) {
bool has_enforcers = mv - > is_support_enforcer ( ) | |
( mv - > is_model_part ( ) & & mv - > supported_facets . has_facets ( * mv , EnforcerBlockerType : : ENFORCER ) ) ;
if ( has_enforcers ) {
warning - > string = L ( " Support enforcers are used but support is not enabled. Please enable support. " ) ;
warning - > object = object ;
break ;
}
}
}
double initial_layer_print_height = m_config . initial_layer_print_height . value ;
double first_layer_min_nozzle_diameter ;
if ( object - > has_raft ( ) ) {
// if we have raft layers, only support material extruder is used on first layer
size_t first_layer_extruder = object - > config ( ) . raft_layers = = 1
? object - > config ( ) . support_interface_filament - 1
: object - > config ( ) . support_filament - 1 ;
first_layer_min_nozzle_diameter = ( first_layer_extruder = = size_t ( - 1 ) ) ?
min_nozzle_diameter :
m_config . nozzle_diameter . get_at ( first_layer_extruder ) ;
} else {
// if we don't have raft layers, any nozzle diameter is potentially used in first layer
first_layer_min_nozzle_diameter = min_nozzle_diameter ;
}
if ( initial_layer_print_height > first_layer_min_nozzle_diameter )
return { L ( " Layer height cannot exceed nozzle diameter " ) , object , " initial_layer_print_height " } ;
// validate layer_height
double layer_height = object - > config ( ) . layer_height . value ;
if ( layer_height > min_nozzle_diameter )
return { L ( " Layer height cannot exceed nozzle diameter " ) , object , " layer_height " } ;
// Validate extrusion widths.
std : : string err_msg ;
if ( ! validate_extrusion_width ( object - > config ( ) , " line_width " , layer_height , err_msg ) )
return { err_msg , object , " line_width " } ;
if ( object - > has_support ( ) | | object - > has_raft ( ) ) {
if ( ! validate_extrusion_width ( object - > config ( ) , " support_line_width " , layer_height , err_msg ) )
return { err_msg , object , " support_line_width " } ;
}
for ( const char * opt_key : { " inner_wall_line_width " , " outer_wall_line_width " , " sparse_infill_line_width " , " internal_solid_infill_line_width " , " top_surface_line_width " } )
for ( const PrintRegion & region : object - > all_regions ( ) )
if ( ! validate_extrusion_width ( region . config ( ) , opt_key , layer_height , err_msg ) )
return { err_msg , object , opt_key } ;
}
}
const ConfigOptionDef * bed_type_def = print_config_def . get ( " curr_bed_type " ) ;
assert ( bed_type_def ! = nullptr ) ;
const t_config_enum_values * bed_type_keys_map = bed_type_def - > enum_keys_map ;
for ( unsigned int extruder_id : extruders ) {
const ConfigOptionInts * bed_temp_opt = m_config . option < ConfigOptionInts > ( get_bed_temp_key ( m_config . curr_bed_type ) ) ;
for ( unsigned int extruder_id : extruders ) {
int curr_bed_temp = bed_temp_opt - > get_at ( extruder_id ) ;
if ( curr_bed_temp = = 0 & & bed_type_keys_map ! = nullptr ) {
std : : string bed_type_name ;
for ( auto item : * bed_type_keys_map ) {
if ( item . second = = m_config . curr_bed_type ) {
bed_type_name = item . first ;
break ;
}
}
return { format ( L ( " Plate %d: %s does not support filament %s. \n " ) , this - > get_plate_index ( ) + 1 ,
L ( bed_type_name ) , extruder_id + 1 ) } ;
}
}
}
return { } ;
}
#if 0
// the bounding box of objects placed in copies position
// (without taking skirt/brim/support material into account)
BoundingBox Print : : bounding_box ( ) const
{
BoundingBox bb ;
for ( const PrintObject * object : m_objects )
for ( const PrintInstance & instance : object - > instances ( ) ) {
BoundingBox bb2 ( object - > bounding_box ( ) ) ;
bb . merge ( bb2 . min + instance . shift ) ;
bb . merge ( bb2 . max + instance . shift ) ;
}
return bb ;
}
// the total bounding box of extrusions, including skirt/brim/support material
// this methods needs to be called even when no steps were processed, so it should
// only use configuration values
BoundingBox Print : : total_bounding_box ( ) const
{
// get objects bounding box
BoundingBox bb = this - > bounding_box ( ) ;
// we need to offset the objects bounding box by at least half the perimeters extrusion width
Flow perimeter_flow = m_objects . front ( ) - > get_layer ( 0 ) - > get_region ( 0 ) - > flow ( frPerimeter ) ;
double extra = perimeter_flow . width / 2 ;
// consider support material
if ( this - > has_support_material ( ) ) {
extra = std : : max ( extra , SUPPORT_MATERIAL_MARGIN ) ;
}
// consider brim and skirt
if ( m_config . brim_width . value > 0 ) {
Flow brim_flow = this - > brim_flow ( ) ;
extra = std : : max ( extra , m_config . brim_width . value + brim_flow . width / 2 ) ;
}
if ( this - > has_skirt ( ) ) {
int skirts = m_config . skirt_loops . value ;
if ( skirts = = 0 & & this - > has_infinite_skirt ( ) ) skirts = 1 ;
Flow skirt_flow = this - > skirt_flow ( ) ;
extra = std : : max (
extra ,
m_config . brim_width . value
+ m_config . skirt_distance . value
+ skirts * skirt_flow . spacing ( )
+ skirt_flow . width / 2
) ;
}
if ( extra > 0 )
bb . offset ( scale_ ( extra ) ) ;
return bb ;
}
# endif
double Print : : skirt_first_layer_height ( ) const
{
return m_config . initial_layer_print_height . value ;
}
Flow Print : : brim_flow ( ) const
{
ConfigOptionFloat width = m_config . initial_layer_line_width ;
if ( width . value = = 0 )
width = m_print_regions . front ( ) - > config ( ) . inner_wall_line_width ;
if ( width . value = = 0 )
width = m_objects . front ( ) - > config ( ) . line_width ;
/* We currently use a random region's perimeter extruder.
While this works for most cases , we should probably consider all of the perimeter
extruders and take the one with , say , the smallest index .
The same logic should be applied to the code that selects the extruder during G - code
generation as well . */
return Flow : : new_from_config_width (
frPerimeter ,
width ,
( float ) m_config . nozzle_diameter . get_at ( m_print_regions . front ( ) - > config ( ) . wall_filament - 1 ) ,
( float ) this - > skirt_first_layer_height ( ) ) ;
}
Flow Print : : skirt_flow ( ) const
{
ConfigOptionFloat width = m_config . initial_layer_line_width ;
if ( width . value = = 0 )
width = m_objects . front ( ) - > config ( ) . line_width ;
/* We currently use a random object's support material extruder.
While this works for most cases , we should probably consider all of the support material
extruders and take the one with , say , the smallest index ;
The same logic should be applied to the code that selects the extruder during G - code
generation as well . */
return Flow : : new_from_config_width (
frPerimeter ,
width ,
( float ) m_config . nozzle_diameter . get_at ( m_objects . front ( ) - > config ( ) . support_filament - 1 ) ,
( float ) this - > skirt_first_layer_height ( ) ) ;
}
bool Print : : has_support_material ( ) const
{
for ( const PrintObject * object : m_objects )
if ( object - > has_support_material ( ) )
return true ;
return false ;
}
/* This method assigns extruders to the volumes having a material
but not having extruders set in the volume config . */
void Print : : auto_assign_extruders ( ModelObject * model_object ) const
{
// only assign extruders if object has more than one volume
if ( model_object - > volumes . size ( ) < 2 )
return ;
// size_t extruders = m_config.nozzle_diameter.values.size();
for ( size_t volume_id = 0 ; volume_id < model_object - > volumes . size ( ) ; + + volume_id ) {
ModelVolume * volume = model_object - > volumes [ volume_id ] ;
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
if ( ( volume - > is_model_part ( ) | | volume - > is_modifier ( ) ) & & ! volume - > material_id ( ) . empty ( ) & & ! volume - > config . has ( " extruder " ) )
volume - > config . set ( " extruder " , int ( volume_id + 1 ) ) ;
}
}
2022-08-18 07:29:33 +00:00
void PrintObject : : set_shared_object ( PrintObject * object )
{
m_shared_object = object ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : this=%1%, found shared object from %2% " ) % this % m_shared_object ;
}
void PrintObject : : clear_shared_object ( )
{
if ( m_shared_object ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : this=%1%, clear previous shared object data %2% " ) % this % m_shared_object ;
m_layers . clear ( ) ;
m_support_layers . clear ( ) ;
m_tree_support_layers . clear ( ) ;
m_shared_object = nullptr ;
invalidate_all_steps_without_cancel ( ) ;
}
}
void PrintObject : : copy_layers_from_shared_object ( )
{
if ( m_shared_object ) {
m_layers . clear ( ) ;
m_support_layers . clear ( ) ;
m_tree_support_layers . clear ( ) ;
firstLayerObjSliceByVolume . clear ( ) ;
firstLayerObjSliceByGroups . clear ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : this=%1%, copied layers from object %2% " ) % this % m_shared_object ;
m_layers = m_shared_object - > layers ( ) ;
m_support_layers = m_shared_object - > support_layers ( ) ;
m_tree_support_layers = m_shared_object - > tree_support_layers ( ) ;
firstLayerObjSliceByVolume = m_shared_object - > firstLayerObjSlice ( ) ;
firstLayerObjSliceByGroups = m_shared_object - > firstLayerObjGroups ( ) ;
}
}
2022-07-15 15:37:19 +00:00
// BBS
BoundingBox PrintObject : : get_first_layer_bbox ( float & a , float & layer_height , std : : string & name )
{
BoundingBox bbox ;
a = 0 ;
name = this - > model_object ( ) - > name ;
if ( layer_count ( ) > 0 ) {
auto layer = get_layer ( 0 ) ;
layer_height = layer - > height ;
// only work for object with single instance
auto shift = instances ( ) [ 0 ] . shift ;
for ( auto bb : layer - > lslices_bboxes )
{
bb . translate ( shift . x ( ) , shift . y ( ) ) ;
bbox . merge ( bb ) ;
}
for ( auto slice : layer - > lslices ) {
a + = area ( slice ) ;
}
}
if ( has_brim ( ) )
bbox = firstLayerObjectBrimBoundingBox ;
return bbox ;
}
// BBS: map print object with its first layer's first extruder
std : : map < ObjectID , unsigned int > getObjectExtruderMap ( const Print & print ) {
std : : map < ObjectID , unsigned int > objectExtruderMap ;
for ( const PrintObject * object : print . objects ( ) ) {
// BBS
unsigned int objectFirstLayerFirstExtruder = print . config ( ) . filament_diameter . size ( ) ;
auto firstLayerRegions = object - > layers ( ) . front ( ) - > regions ( ) ;
if ( ! firstLayerRegions . empty ( ) ) {
for ( const LayerRegion * regionPtr : firstLayerRegions ) {
if ( regionPtr - > has_extrusions ( ) )
objectFirstLayerFirstExtruder = std : : min ( objectFirstLayerFirstExtruder ,
regionPtr - > region ( ) . extruder ( frExternalPerimeter ) ) ;
}
}
objectExtruderMap . insert ( std : : make_pair ( object - > id ( ) , objectFirstLayerFirstExtruder ) ) ;
}
return objectExtruderMap ;
}
// Slicing process, running at a background thread.
void Print : : process ( )
{
name_tbb_thread_pool_threads_set_locale ( ) ;
2022-08-18 07:29:33 +00:00
//compute the PrintObject with the same geometries
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : this=%1%, enter " ) % this ;
2022-07-15 15:37:19 +00:00
for ( PrintObject * obj : m_objects )
2022-08-18 07:29:33 +00:00
obj - > clear_shared_object ( ) ;
//add the print_object share check logic
auto is_print_object_the_same = [ this ] ( const PrintObject * object1 , const PrintObject * object2 ) - > bool {
if ( object1 - > trafo ( ) . matrix ( ) ! = object2 - > trafo ( ) . matrix ( ) )
return false ;
const ModelObject * model_obj1 = object1 - > model_object ( ) ;
const ModelObject * model_obj2 = object2 - > model_object ( ) ;
if ( model_obj1 - > volumes . size ( ) ! = model_obj2 - > volumes . size ( ) )
return false ;
bool has_extruder1 = model_obj1 - > config . has ( " extruder " ) ;
bool has_extruder2 = model_obj2 - > config . has ( " extruder " ) ;
if ( ( has_extruder1 ! = has_extruder2 )
| | ( has_extruder1 & & model_obj1 - > config . extruder ( ) ! = model_obj2 - > config . extruder ( ) ) )
return false ;
for ( int index = 0 ; index < model_obj1 - > volumes . size ( ) ; index + + ) {
const ModelVolume & model_volume1 = * model_obj1 - > volumes [ index ] ;
const ModelVolume & model_volume2 = * model_obj2 - > volumes [ index ] ;
if ( model_volume1 . type ( ) ! = model_volume2 . type ( ) )
return false ;
if ( model_volume1 . mesh_ptr ( ) ! = model_volume2 . mesh_ptr ( ) )
return false ;
has_extruder1 = model_volume1 . config . has ( " extruder " ) ;
has_extruder2 = model_volume2 . config . has ( " extruder " ) ;
if ( ( has_extruder1 ! = has_extruder2 )
| | ( has_extruder1 & & model_volume1 . config . extruder ( ) ! = model_volume2 . config . extruder ( ) ) )
return false ;
if ( ! model_volume1 . supported_facets . equals ( model_volume2 . supported_facets ) )
return false ;
if ( ! model_volume1 . seam_facets . equals ( model_volume2 . seam_facets ) )
return false ;
if ( ! model_volume1 . mmu_segmentation_facets . equals ( model_volume2 . mmu_segmentation_facets ) )
return false ;
if ( model_volume1 . config . get ( ) ! = model_volume2 . config . get ( ) )
return false ;
}
//if (!object1->config().equals(object2->config()))
// return false;
if ( model_obj1 - > config . get ( ) ! = model_obj2 - > config . get ( ) )
return false ;
return true ;
} ;
int object_count = m_objects . size ( ) ;
std : : set < PrintObject * > need_slicing_objects ;
for ( int index = 0 ; index < object_count ; index + + )
{
PrintObject * obj = m_objects [ index ] ;
for ( PrintObject * slicing_obj : need_slicing_objects )
{
if ( is_print_object_the_same ( obj , slicing_obj ) ) {
obj - > set_shared_object ( slicing_obj ) ;
break ;
}
}
if ( ! obj - > get_shared_object ( ) )
need_slicing_objects . insert ( obj ) ;
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : total object counts %1% in current print, need to slice %2% " ) % m_objects . size ( ) % need_slicing_objects . size ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < " Starting the slicing process. " < < log_memory_info ( ) ;
for ( PrintObject * obj : m_objects ) {
if ( need_slicing_objects . count ( obj ) ! = 0 ) {
obj - > make_perimeters ( ) ;
}
else {
if ( obj - > set_started ( posSlice ) )
obj - > set_done ( posSlice ) ;
if ( obj - > set_started ( posPerimeters ) )
obj - > set_done ( posPerimeters ) ;
}
}
for ( PrintObject * obj : m_objects ) {
if ( need_slicing_objects . count ( obj ) ! = 0 ) {
obj - > infill ( ) ;
}
else {
if ( obj - > set_started ( posPrepareInfill ) )
obj - > set_done ( posPrepareInfill ) ;
if ( obj - > set_started ( posInfill ) )
obj - > set_done ( posInfill ) ;
}
}
for ( PrintObject * obj : m_objects ) {
if ( need_slicing_objects . count ( obj ) ! = 0 ) {
obj - > ironing ( ) ;
}
else {
if ( obj - > set_started ( posIroning ) )
obj - > set_done ( posIroning ) ;
}
}
for ( PrintObject * obj : m_objects ) {
if ( need_slicing_objects . count ( obj ) ! = 0 ) {
obj - > generate_support_material ( ) ;
}
else {
if ( obj - > set_started ( posSupportMaterial ) )
obj - > set_done ( posSupportMaterial ) ;
}
}
2022-07-15 15:37:19 +00:00
for ( PrintObject * obj : m_objects )
2022-08-18 07:29:33 +00:00
{
if ( need_slicing_objects . count ( obj ) = = 0 )
obj - > copy_layers_from_shared_object ( ) ;
}
2022-07-15 15:37:19 +00:00
if ( this - > set_started ( psWipeTower ) ) {
m_wipe_tower_data . clear ( ) ;
m_tool_ordering . clear ( ) ;
if ( this - > has_wipe_tower ( ) ) {
this - > _make_wipe_tower ( ) ;
} else if ( this - > config ( ) . print_sequence ! = PrintSequence : : ByObject ) {
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
m_tool_ordering = ToolOrdering ( * this , - 1 , false ) ;
if ( m_tool_ordering . empty ( ) | | m_tool_ordering . last_extruder ( ) = = unsigned ( - 1 ) )
throw Slic3r : : SlicingError ( " The print is empty. The model is not printable with current print settings. " ) ;
}
this - > set_done ( psWipeTower ) ;
}
if ( this - > set_started ( psSkirtBrim ) ) {
this - > set_status ( 70 , L ( " Generating skirt & brim " ) ) ;
m_skirt . clear ( ) ;
m_skirt_convex_hull . clear ( ) ;
m_first_layer_convex_hull . points . clear ( ) ;
const bool draft_shield = config ( ) . draft_shield ! = dsDisabled ;
if ( this - > has_skirt ( ) & & draft_shield ) {
// In case that draft shield is active, generate skirt first so brim
// can be trimmed to make room for it.
_make_skirt ( ) ;
}
//BBS: get the objects' indices when GCodes are generated
ToolOrdering tool_ordering ;
unsigned int initial_extruder_id = ( unsigned int ) - 1 ;
unsigned int final_extruder_id = ( unsigned int ) - 1 ;
bool has_wipe_tower = false ;
std : : vector < const PrintInstance * > print_object_instances_ordering ;
std : : vector < const PrintInstance * > : : const_iterator print_object_instance_sequential_active ;
std : : vector < std : : pair < coordf_t , std : : vector < GCode : : LayerToPrint > > > layers_to_print = GCode : : collect_layers_to_print ( * this ) ;
std : : vector < unsigned int > printExtruders ;
if ( this - > config ( ) . print_sequence = = PrintSequence : : ByObject ) {
// Order object instances for sequential print.
print_object_instances_ordering = sort_object_instances_by_model_order ( * this ) ;
// print_object_instances_ordering = sort_object_instances_by_max_z(print);
print_object_instance_sequential_active = print_object_instances_ordering . begin ( ) ;
for ( ; print_object_instance_sequential_active ! = print_object_instances_ordering . end ( ) ; + + print_object_instance_sequential_active ) {
tool_ordering = ToolOrdering ( * ( * print_object_instance_sequential_active ) - > print_object , initial_extruder_id ) ;
if ( ( initial_extruder_id = tool_ordering . first_extruder ( ) ) ! = static_cast < unsigned int > ( - 1 ) ) {
append ( printExtruders , tool_ordering . tools_for_layer ( layers_to_print . front ( ) . first ) . extruders ) ;
}
}
}
else {
tool_ordering = this - > tool_ordering ( ) ;
tool_ordering . assign_custom_gcodes ( * this ) ;
has_wipe_tower = this - > has_wipe_tower ( ) & & tool_ordering . has_wipe_tower ( ) ;
//BBS: have no single_extruder_multi_material_priming
#if 0
initial_extruder_id = ( has_wipe_tower & & ! this - > config ( ) . single_extruder_multi_material_priming ) ?
// The priming towers will be skipped.
tool_ordering . all_extruders ( ) . back ( ) :
// Don't skip the priming towers.
tool_ordering . first_extruder ( ) ;
# endif
initial_extruder_id = tool_ordering . first_extruder ( ) ;
print_object_instances_ordering = chain_print_object_instances ( * this ) ;
append ( printExtruders , tool_ordering . tools_for_layer ( layers_to_print . front ( ) . first ) . extruders ) ;
}
auto objectExtruderMap = getObjectExtruderMap ( * this ) ;
std : : vector < std : : pair < ObjectID , unsigned int > > objPrintVec ;
for ( const PrintInstance * instance : print_object_instances_ordering ) {
const ObjectID & print_object_ID = instance - > print_object - > id ( ) ;
bool existObject = false ;
for ( auto & objIDPair : objPrintVec ) {
if ( print_object_ID = = objIDPair . first ) existObject = true ;
}
if ( ! existObject & & objectExtruderMap . find ( print_object_ID ) ! = objectExtruderMap . end ( ) )
objPrintVec . push_back ( std : : make_pair ( print_object_ID , objectExtruderMap . at ( print_object_ID ) ) ) ;
}
// BBS: m_brimMap and m_supportBrimMap are used instead of m_brim to generate brim of objs and supports seperately
m_brimMap . clear ( ) ;
m_supportBrimMap . clear ( ) ;
m_first_layer_convex_hull . points . clear ( ) ;
if ( this - > has_brim ( ) ) {
Polygons islands_area ;
make_brim ( * this , this - > make_try_cancel ( ) , islands_area , m_brimMap ,
m_supportBrimMap , objPrintVec , printExtruders ) ;
for ( Polygon & poly_ex : islands_area )
poly_ex . douglas_peucker ( SCALED_RESOLUTION ) ;
for ( Polygon & poly : union_ ( this - > first_layer_islands ( ) , islands_area ) )
append ( m_first_layer_convex_hull . points , std : : move ( poly . points ) ) ;
}
if ( has_skirt ( ) & & ! draft_shield ) {
// In case that draft shield is NOT active, generate skirt now.
// It will be placed around the brim, so brim has to be ready.
assert ( m_skirt . empty ( ) ) ;
_make_skirt ( ) ;
}
this - > finalize_first_layer_convex_hull ( ) ;
this - > set_done ( psSkirtBrim ) ;
}
//BBS
2022-08-18 07:29:33 +00:00
for ( PrintObject * obj : m_objects ) {
if ( need_slicing_objects . count ( obj ) ! = 0 ) {
obj - > simplify_extrusion_path ( ) ;
}
else {
if ( obj - > set_started ( posSimplifyPath ) )
obj - > set_done ( posSimplifyPath ) ;
if ( obj - > set_started ( posSimplifySupportPath ) )
obj - > set_done ( posSimplifySupportPath ) ;
}
}
2022-07-15 15:37:19 +00:00
BOOST_LOG_TRIVIAL ( info ) < < " Slicing process finished. " < < log_memory_info ( ) ;
}
// G-code export process, running at a background thread.
// The export_gcode may die for various reasons (fails to process filename_format,
// write error into the G-code, cannot execute post-processing scripts).
// It is up to the caller to show an error message.
std : : string Print : : export_gcode ( const std : : string & path_template , GCodeProcessorResult * result , ThumbnailsGeneratorCallback thumbnail_cb )
{
// output everything to a G-code file
// The following call may die if the filename_format template substitution fails.
std : : string path = this - > output_filepath ( path_template ) ;
std : : string message ;
if ( ! path . empty ( ) & & result = = nullptr ) {
// Only show the path if preview_data is not set -> running from command line.
message = L ( " Exporting G-code " ) ;
message + = " to " ;
message + = path ;
} else
message = L ( " Generating G-code " ) ;
this - > set_status ( 80 , message ) ;
// The following line may die for multiple reasons.
GCode gcode ;
//BBS: compute plate offset for gcode-generator
const Vec3d origin = this - > get_plate_origin ( ) ;
gcode . set_gcode_offset ( origin ( 0 ) , origin ( 1 ) ) ;
gcode . do_export ( this , path . c_str ( ) , result , thumbnail_cb ) ;
return path . c_str ( ) ;
}
void Print : : _make_skirt ( )
{
// First off we need to decide how tall the skirt must be.
// The skirt_height option from config is expressed in layers, but our
// object might have different layer heights, so we need to find the print_z
// of the highest layer involved.
// Note that unless has_infinite_skirt() == true
// the actual skirt might not reach this $skirt_height_z value since the print
// order of objects on each layer is not guaranteed and will not generally
// include the thickest object first. It is just guaranteed that a skirt is
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
coordf_t skirt_height_z = 0. ;
for ( const PrintObject * object : m_objects ) {
size_t skirt_layers = this - > has_infinite_skirt ( ) ?
object - > layer_count ( ) :
std : : min ( size_t ( m_config . skirt_height . value ) , object - > layer_count ( ) ) ;
skirt_height_z = std : : max ( skirt_height_z , object - > m_layers [ skirt_layers - 1 ] - > print_z ) ;
}
// Collect points from all layers contained in skirt height.
Points points ;
// BBS
std : : map < PrintObject * , Polygon > object_convex_hulls ;
for ( PrintObject * object : m_objects ) {
Points object_points ;
// Get object layers up to skirt_height_z.
for ( const Layer * layer : object - > m_layers ) {
if ( layer - > print_z > skirt_height_z )
break ;
for ( const ExPolygon & expoly : layer - > lslices )
// Collect the outer contour points only, ignore holes for the calculation of the convex hull.
append ( object_points , expoly . contour . points ) ;
}
// Get support layers up to skirt_height_z.
for ( const SupportLayer * layer : object - > support_layers ( ) ) {
if ( layer - > print_z > skirt_height_z )
break ;
layer - > support_fills . collect_points ( object_points ) ;
}
// BBS
for ( const TreeSupportLayer * layer : object - > m_tree_support_layers ) {
if ( layer - > print_z > skirt_height_z )
break ;
layer - > support_fills . collect_points ( object_points ) ;
}
object_convex_hulls . insert ( { object , Slic3r : : Geometry : : convex_hull ( object_points ) } ) ;
// Repeat points for each object copy.
for ( const PrintInstance & instance : object - > instances ( ) ) {
Points copy_points = object_points ;
for ( Point & pt : copy_points )
pt + = instance . shift ;
append ( points , copy_points ) ;
}
}
// Include the wipe tower.
append ( points , this - > first_layer_wipe_tower_corners ( ) ) ;
// Unless draft shield is enabled, include all brims as well.
if ( config ( ) . draft_shield = = dsDisabled )
append ( points , m_first_layer_convex_hull . points ) ;
if ( points . size ( ) < 3 )
// At least three points required for a convex hull.
return ;
this - > throw_if_canceled ( ) ;
Polygon convex_hull = Slic3r : : Geometry : : convex_hull ( points ) ;
// Skirt may be printed on several layers, having distinct layer heights,
// but loops must be aligned so can't vary width/spacing
// TODO: use each extruder's own flow
double initial_layer_print_height = this - > skirt_first_layer_height ( ) ;
Flow flow = this - > skirt_flow ( ) ;
float spacing = flow . spacing ( ) ;
double mm3_per_mm = flow . mm3_per_mm ( ) ;
std : : vector < size_t > extruders ;
std : : vector < double > extruders_e_per_mm ;
{
auto set_extruders = this - > extruders ( ) ;
extruders . reserve ( set_extruders . size ( ) ) ;
extruders_e_per_mm . reserve ( set_extruders . size ( ) ) ;
for ( auto & extruder_id : set_extruders ) {
extruders . push_back ( extruder_id ) ;
extruders_e_per_mm . push_back ( Extruder ( ( unsigned int ) extruder_id , & m_config , m_config . single_extruder_multi_material ) . e_per_mm ( mm3_per_mm ) ) ;
}
}
// Number of skirt loops per skirt layer.
size_t n_skirts = m_config . skirt_loops . value ;
if ( this - > has_infinite_skirt ( ) & & n_skirts = = 0 )
n_skirts = 1 ;
// Initial offset of the brim inner edge from the object (possible with a support & raft).
// The skirt will touch the brim if the brim is extruded.
auto distance = float ( scale_ ( m_config . skirt_distance . value ) - spacing / 2. ) ;
// Draw outlines from outside to inside.
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
std : : vector < coordf_t > extruded_length ( extruders . size ( ) , 0. ) ;
for ( size_t i = n_skirts , extruder_idx = 0 ; i > 0 ; - - i ) {
this - > throw_if_canceled ( ) ;
// Offset the skirt outside.
distance + = float ( scale_ ( spacing ) ) ;
// Generate the skirt centerline.
Polygon loop ;
{
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
Polygons loops = offset ( convex_hull , distance , ClipperLib : : jtRound , float ( scale_ ( 0.1 ) ) ) ;
Geometry : : simplify_polygons ( loops , scale_ ( 0.05 ) , & loops ) ;
if ( loops . empty ( ) )
break ;
loop = loops . front ( ) ;
}
// Extrude the skirt loop.
ExtrusionLoop eloop ( elrSkirt ) ;
eloop . paths . emplace_back ( ExtrusionPath (
ExtrusionPath (
erSkirt ,
( float ) mm3_per_mm , // this will be overridden at G-code export time
flow . width ( ) ,
( float ) initial_layer_print_height // this will be overridden at G-code export time
) ) ) ;
eloop . paths . back ( ) . polyline = loop . split_at_first_point ( ) ;
m_skirt . append ( eloop ) ;
if ( Print : : min_skirt_length > 0 ) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
extruded_length [ extruder_idx ] + = unscale < double > ( loop . length ( ) ) * extruders_e_per_mm [ extruder_idx ] ;
if ( extruded_length [ extruder_idx ] < Print : : min_skirt_length ) {
// Not extruded enough yet with the current extruder. Add another loop.
if ( i = = 1 )
+ + i ;
} else {
assert ( extruded_length [ extruder_idx ] > = Print : : min_skirt_length ) ;
// Enough extruded with the current extruder. Extrude with the next one,
// until the prescribed number of skirt loops is extruded.
if ( extruder_idx + 1 < extruders . size ( ) )
+ + extruder_idx ;
}
} else {
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
}
}
// Brims were generated inside out, reverse to print the outmost contour first.
m_skirt . reverse ( ) ;
// Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull.
for ( Polygon & poly : offset ( convex_hull , distance + 0.5f * float ( scale_ ( spacing ) ) , ClipperLib : : jtRound , float ( scale_ ( 0.1 ) ) ) )
append ( m_skirt_convex_hull , std : : move ( poly . points ) ) ;
// BBS
const int n_object_skirts = 1 ;
const double object_skirt_distance = scale_ ( 1.0 ) ;
for ( auto obj_cvx_hull : object_convex_hulls ) {
PrintObject * object = obj_cvx_hull . first ;
for ( int i = 0 ; i < n_object_skirts ; i + + ) {
distance + = float ( scale_ ( spacing ) ) ;
Polygon loop ;
{
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
Polygons loops = offset ( obj_cvx_hull . second , object_skirt_distance , ClipperLib : : jtRound , float ( scale_ ( 0.1 ) ) ) ;
Geometry : : simplify_polygons ( loops , scale_ ( 0.05 ) , & loops ) ;
if ( loops . empty ( ) )
break ;
loop = loops . front ( ) ;
}
// Extrude the skirt loop.
ExtrusionLoop eloop ( elrSkirt ) ;
eloop . paths . emplace_back ( ExtrusionPath (
ExtrusionPath (
erSkirt ,
( float ) mm3_per_mm , // this will be overridden at G-code export time
flow . width ( ) ,
( float ) initial_layer_print_height // this will be overridden at G-code export time
) ) ) ;
eloop . paths . back ( ) . polyline = loop . split_at_first_point ( ) ;
object - > m_skirt . append ( std : : move ( eloop ) ) ;
}
}
}
Polygons Print : : first_layer_islands ( ) const
{
Polygons islands ;
for ( PrintObject * object : m_objects ) {
Polygons object_islands ;
for ( ExPolygon & expoly : object - > m_layers . front ( ) - > lslices )
object_islands . push_back ( expoly . contour ) ;
if ( ! object - > support_layers ( ) . empty ( ) )
object - > support_layers ( ) . front ( ) - > support_fills . polygons_covered_by_spacing ( object_islands , float ( SCALED_EPSILON ) ) ;
if ( ! object - > tree_support_layers ( ) . empty ( ) ) {
ExPolygons & expolys_first_layer = object - > m_tree_support_layers . front ( ) - > lslices ;
for ( ExPolygon & expoly : expolys_first_layer ) {
object_islands . push_back ( expoly . contour ) ;
}
}
islands . reserve ( islands . size ( ) + object_islands . size ( ) * object - > instances ( ) . size ( ) ) ;
for ( const PrintInstance & instance : object - > instances ( ) )
for ( Polygon & poly : object_islands ) {
islands . push_back ( poly ) ;
islands . back ( ) . translate ( instance . shift ) ;
}
}
return islands ;
}
std : : vector < Point > Print : : first_layer_wipe_tower_corners ( bool check_wipe_tower_existance ) const
{
std : : vector < Point > corners ;
if ( check_wipe_tower_existance & & ( ! has_wipe_tower ( ) | | m_wipe_tower_data . tool_changes . empty ( ) ) )
return corners ;
{
double width = m_config . prime_tower_width + 2 * m_wipe_tower_data . brim_width ;
double depth = m_wipe_tower_data . depth + 2 * m_wipe_tower_data . brim_width ;
Vec2d pt0 ( - m_wipe_tower_data . brim_width , - m_wipe_tower_data . brim_width ) ;
for ( Vec2d pt : {
pt0 ,
Vec2d ( pt0 . x ( ) + width , pt0 . y ( ) ) ,
Vec2d ( pt0 . x ( ) + width , pt0 . y ( ) + depth ) ,
Vec2d ( pt0 . x ( ) , pt0 . y ( ) + depth )
} ) {
pt = Eigen : : Rotation2Dd ( Geometry : : deg2rad ( m_config . wipe_tower_rotation_angle . value ) ) * pt ;
// BBS: add partplate logic
pt + = Vec2d ( m_config . wipe_tower_x . get_at ( m_plate_index ) + m_origin ( 0 ) , m_config . wipe_tower_y . get_at ( m_plate_index ) + m_origin ( 1 ) ) ;
corners . emplace_back ( Point ( scale_ ( pt . x ( ) ) , scale_ ( pt . y ( ) ) ) ) ;
}
}
return corners ;
}
void Print : : finalize_first_layer_convex_hull ( )
{
append ( m_first_layer_convex_hull . points , m_skirt_convex_hull ) ;
if ( m_first_layer_convex_hull . empty ( ) ) {
// Neither skirt nor brim was extruded. Collect points of printed objects from 1st layer.
for ( Polygon & poly : this - > first_layer_islands ( ) )
append ( m_first_layer_convex_hull . points , std : : move ( poly . points ) ) ;
}
append ( m_first_layer_convex_hull . points , this - > first_layer_wipe_tower_corners ( ) ) ;
m_first_layer_convex_hull = Geometry : : convex_hull ( m_first_layer_convex_hull . points ) ;
}
// Wipe tower support.
bool Print : : has_wipe_tower ( ) const
{
2022-10-13 05:23:08 +00:00
if ( m_config . enable_prime_tower . value = = true ) {
if ( enable_timelapse_print ( ) )
return true ;
2022-08-15 06:41:28 +00:00
2022-10-13 05:23:08 +00:00
return ! m_config . spiral_mode . value & & m_config . filament_diameter . values . size ( ) > 1 ;
}
return false ;
2022-07-15 15:37:19 +00:00
}
const WipeTowerData & Print : : wipe_tower_data ( size_t filaments_cnt ) const
{
// If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
if ( ! is_step_done ( psWipeTower ) & & filaments_cnt ! = 0 ) {
// BBS
double width = m_config . prime_tower_width ;
double layer_height = 0.2 ; // hard code layer height
double wipe_volume = m_config . prime_volume ;
2022-08-15 06:41:28 +00:00
if ( filaments_cnt = = 1 & & enable_timelapse_print ( ) ) {
const_cast < Print * > ( this ) - > m_wipe_tower_data . depth = wipe_volume / ( layer_height * width ) ;
} else {
const_cast < Print * > ( this ) - > m_wipe_tower_data . depth = wipe_volume * ( filaments_cnt - 1 ) / ( layer_height * width ) ;
}
2022-07-15 15:37:19 +00:00
const_cast < Print * > ( this ) - > m_wipe_tower_data . brim_width = m_config . prime_tower_brim_width ;
}
return m_wipe_tower_data ;
}
2022-08-15 06:41:28 +00:00
bool Print : : enable_timelapse_print ( ) const
{
2022-09-07 06:15:14 +00:00
return m_config . timelapse_type . value = = TimelapseType : : tlSmooth ;
2022-08-15 06:41:28 +00:00
}
2022-07-15 15:37:19 +00:00
void Print : : _make_wipe_tower ( )
{
m_wipe_tower_data . clear ( ) ;
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
std : : vector < float > flush_matrix ( cast < float > ( m_config . flush_volumes_matrix . values ) ) ;
// BBS
const unsigned int number_of_extruders = ( unsigned int ) ( sqrt ( flush_matrix . size ( ) ) + EPSILON ) ;
// Extract purging volumes for each extruder pair:
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 ) ) ;
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
// BBS: priming logic is removed, so don't consider it in tool ordering
m_wipe_tower_data . tool_ordering = ToolOrdering ( * this , ( unsigned int ) - 1 , false ) ;
2022-08-15 06:41:28 +00:00
if ( ! m_wipe_tower_data . tool_ordering . has_wipe_tower ( ) )
2022-07-15 15:37:19 +00:00
// Don't generate any wipe tower.
return ;
// Check whether there are any layers in m_tool_ordering, which are marked with has_wipe_tower,
// they print neither object, nor support. These layers are above the raft and below the object, and they
// shall be added to the support layers to be printed.
// see https://github.com/prusa3d/PrusaSlicer/issues/607
{
size_t idx_begin = size_t ( - 1 ) ;
size_t idx_end = m_wipe_tower_data . tool_ordering . layer_tools ( ) . size ( ) ;
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
for ( size_t i = 0 ; i < idx_end ; + + i ) {
const LayerTools & lt = m_wipe_tower_data . tool_ordering . layer_tools ( ) [ i ] ;
if ( lt . has_wipe_tower & & ! lt . has_object & & ! lt . has_support ) {
idx_begin = i ;
break ;
}
}
if ( idx_begin ! = size_t ( - 1 ) ) {
// Find the position in m_objects.first()->support_layers to insert these new support layers.
double wipe_tower_new_layer_print_z_first = m_wipe_tower_data . tool_ordering . layer_tools ( ) [ idx_begin ] . print_z ;
auto it_layer = m_objects . front ( ) - > support_layers ( ) . begin ( ) ;
auto it_end = m_objects . front ( ) - > support_layers ( ) . end ( ) ;
for ( ; it_layer ! = it_end & & ( * it_layer ) - > print_z - EPSILON < wipe_tower_new_layer_print_z_first ; + + it_layer ) ;
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
for ( size_t i = idx_begin ; i < idx_end ; + + i ) {
LayerTools & lt = const_cast < LayerTools & > ( m_wipe_tower_data . tool_ordering . layer_tools ( ) [ i ] ) ;
if ( ! ( lt . has_wipe_tower & & ! lt . has_object & & ! lt . has_support ) )
break ;
lt . has_support = true ;
// Insert the new support layer.
double height = lt . print_z - ( i = = 0 ? 0. : m_wipe_tower_data . tool_ordering . layer_tools ( ) [ i - 1 ] . print_z ) ;
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
it_layer = m_objects . front ( ) - > insert_support_layer ( it_layer , - 1 , 0 , height , lt . print_z , lt . print_z - 0.5 * height ) ;
+ + it_layer ;
}
}
}
this - > throw_if_canceled ( ) ;
// Initialize the wipe tower.
// BBS: in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
WipeTower wipe_tower ( m_config , m_plate_index , m_origin , m_config . prime_volume , m_wipe_tower_data . tool_ordering . first_extruder ( ) ,
m_wipe_tower_data . tool_ordering . empty ( ) ? 0.f : m_wipe_tower_data . tool_ordering . back ( ) . print_z ) ;
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object.
for ( size_t i = 0 ; i < number_of_extruders ; + + i )
wipe_tower . set_extruder ( i , m_config ) ;
// BBS: remove priming logic
//m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
// wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
{
// BBS: priming logic is removed, so get the initial extruder by first_extruder()
unsigned int current_extruder_id = m_wipe_tower_data . tool_ordering . first_extruder ( ) ;
for ( auto & layer_tools : m_wipe_tower_data . tool_ordering . layer_tools ( ) ) { // for all layers
if ( ! layer_tools . has_wipe_tower ) continue ;
bool first_layer = & layer_tools = = & m_wipe_tower_data . tool_ordering . front ( ) ;
wipe_tower . plan_toolchange ( ( float ) layer_tools . print_z , ( float ) layer_tools . wipe_tower_layer_height , current_extruder_id , current_extruder_id , false ) ;
2022-09-15 07:46:21 +00:00
2022-07-15 15:37:19 +00:00
for ( const auto extruder_id : layer_tools . extruders ) {
// BBS: priming logic is removed, so no need to do toolchange for first extruder
if ( /*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */ extruder_id ! = current_extruder_id ) {
float volume_to_purge = wipe_volumes [ current_extruder_id ] [ extruder_id ] ;
// Not all of that can be used for infill purging:
//volume_to_purge -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
volume_to_purge - = ( float ) m_config . nozzle_volume ;
// try to assign some infills/objects for the wiping:
volume_to_purge = layer_tools . wiping_extrusions ( ) . mark_wiping_extrusions ( * this , current_extruder_id , extruder_id , volume_to_purge ) ;
// add back the minimal amount toforce on the wipe tower:
//volume_to_purge += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
volume_to_purge + = ( float ) m_config . nozzle_volume ;
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
wipe_tower . plan_toolchange ( ( float ) layer_tools . print_z , ( float ) layer_tools . wipe_tower_layer_height ,
current_extruder_id , extruder_id , m_config . prime_volume , volume_to_purge ) ;
current_extruder_id = extruder_id ;
}
}
layer_tools . wiping_extrusions ( ) . ensure_perimeters_infills_order ( * this ) ;
2022-08-15 06:41:28 +00:00
// if enable timelapse, slice all layer
2022-09-15 03:18:29 +00:00
if ( enable_timelapse_print ( ) ) {
if ( layer_tools . wipe_tower_partitions = = 0 )
wipe_tower . set_last_layer_extruder_fill ( false ) ;
2022-08-15 06:41:28 +00:00
continue ;
2022-09-15 03:18:29 +00:00
}
2022-08-15 06:41:28 +00:00
2022-07-15 15:37:19 +00:00
if ( & layer_tools = = & m_wipe_tower_data . tool_ordering . back ( ) | | ( & layer_tools + 1 ) - > wipe_tower_partitions = = 0 )
break ;
}
}
// Generate the wipe tower layers.
m_wipe_tower_data . tool_changes . reserve ( m_wipe_tower_data . tool_ordering . layer_tools ( ) . size ( ) ) ;
wipe_tower . generate ( m_wipe_tower_data . tool_changes ) ;
m_wipe_tower_data . depth = wipe_tower . get_depth ( ) ;
m_wipe_tower_data . brim_width = wipe_tower . get_brim_width ( ) ;
// Unload the current filament over the purge tower.
coordf_t layer_height = m_objects . front ( ) - > config ( ) . layer_height . value ;
if ( m_wipe_tower_data . tool_ordering . back ( ) . wipe_tower_partitions > 0 ) {
// The wipe tower goes up to the last layer of the print.
if ( wipe_tower . layer_finished ( ) ) {
// The wipe tower is printed to the top of the print and it has no space left for the final extruder purge.
// Lift Z to the next layer.
wipe_tower . set_layer ( float ( m_wipe_tower_data . tool_ordering . back ( ) . print_z + layer_height ) , float ( layer_height ) , 0 , false , true ) ;
} else {
// There is yet enough space at this layer of the wipe tower for the final purge.
}
} else {
// The wipe tower does not reach the last print layer, perform the pruge at the last print layer.
assert ( m_wipe_tower_data . tool_ordering . back ( ) . wipe_tower_partitions = = 0 ) ;
wipe_tower . set_layer ( float ( m_wipe_tower_data . tool_ordering . back ( ) . print_z ) , float ( layer_height ) , 0 , false , true ) ;
}
m_wipe_tower_data . final_purge = Slic3r : : make_unique < WipeTower : : ToolChangeResult > (
wipe_tower . tool_change ( ( unsigned int ) ( - 1 ) ) ) ;
m_wipe_tower_data . used_filament = wipe_tower . get_used_filament ( ) ;
m_wipe_tower_data . number_of_toolchanges = wipe_tower . get_number_of_toolchanges ( ) ;
}
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized).
std : : string Print : : output_filename ( const std : : string & filename_base ) const
{
// Set the placeholders for the data know first after the G-code export is finished.
// These values will be just propagated into the output file name.
DynamicConfig config = this - > finished ( ) ? this - > print_statistics ( ) . config ( ) : this - > print_statistics ( ) . placeholders ( ) ;
config . set_key_value ( " num_filaments " , new ConfigOptionInt ( ( int ) m_config . nozzle_diameter . size ( ) ) ) ;
return this - > PrintBase : : output_filename ( m_config . filename_format . value , " .gcode " , filename_base , & config ) ;
}
//BBS: add gcode file preload logic
void Print : : set_gcode_file_ready ( )
{
this - > set_started ( psGCodeExport ) ;
this - > set_done ( psGCodeExport ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : done " ) ;
}
//BBS: add gcode file preload logic
void Print : : set_gcode_file_invalidated ( )
{
this - > invalidate_step ( psGCodeExport ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : done " ) ;
}
//BBS: add gcode file preload logic
void Print : : export_gcode_from_previous_file ( const std : : string & file , GCodeProcessorResult * result , ThumbnailsGeneratorCallback thumbnail_cb )
{
try {
GCodeProcessor processor ;
const Vec3d origin = this - > get_plate_origin ( ) ;
processor . set_xy_offset ( origin ( 0 ) , origin ( 1 ) ) ;
//processor.enable_producers(true);
processor . process_file ( file ) ;
* result = std : : move ( processor . extract_result ( ) ) ;
} catch ( std : : exception & /* ex */ ) {
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < boost : : format ( " : found errors when process gcode file %1% " ) % file . c_str ( ) ;
throw Slic3r : : RuntimeError (
std : : string ( " Failed to process the G-code file " ) + file + " from previous 3mf \n " ) ;
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : process the G-code file %1% successfully " ) % file . c_str ( ) ;
}
DynamicConfig PrintStatistics : : config ( ) const
{
DynamicConfig config ;
std : : string normal_print_time = short_time ( this - > estimated_normal_print_time ) ;
std : : string silent_print_time = short_time ( this - > estimated_silent_print_time ) ;
config . set_key_value ( " print_time " , new ConfigOptionString ( normal_print_time ) ) ;
config . set_key_value ( " normal_print_time " , new ConfigOptionString ( normal_print_time ) ) ;
config . set_key_value ( " silent_print_time " , new ConfigOptionString ( silent_print_time ) ) ;
config . set_key_value ( " used_filament " , new ConfigOptionFloat ( this - > total_used_filament / 1000. ) ) ;
config . set_key_value ( " extruded_volume " , new ConfigOptionFloat ( this - > total_extruded_volume ) ) ;
config . set_key_value ( " total_cost " , new ConfigOptionFloat ( this - > total_cost ) ) ;
config . set_key_value ( " total_toolchanges " , new ConfigOptionInt ( this - > total_toolchanges ) ) ;
config . set_key_value ( " total_weight " , new ConfigOptionFloat ( this - > total_weight ) ) ;
config . set_key_value ( " total_wipe_tower_cost " , new ConfigOptionFloat ( this - > total_wipe_tower_cost ) ) ;
config . set_key_value ( " total_wipe_tower_filament " , new ConfigOptionFloat ( this - > total_wipe_tower_filament ) ) ;
return config ;
}
DynamicConfig PrintStatistics : : placeholders ( )
{
DynamicConfig config ;
for ( const std : : string & key : {
" print_time " , " normal_print_time " , " silent_print_time " ,
" used_filament " , " extruded_volume " , " total_cost " , " total_weight " ,
" total_toolchanges " , " total_wipe_tower_cost " , " total_wipe_tower_filament " } )
config . set_key_value ( key , new ConfigOptionString ( std : : string ( " { " ) + key + " } " ) ) ;
return config ;
}
std : : string PrintStatistics : : finalize_output_path ( const std : : string & path_in ) const
{
std : : string final_path ;
try {
boost : : filesystem : : path path ( path_in ) ;
DynamicConfig cfg = this - > config ( ) ;
PlaceholderParser pp ;
std : : string new_stem = pp . process ( path . stem ( ) . string ( ) , 0 , & cfg ) ;
final_path = ( path . parent_path ( ) / ( new_stem + path . extension ( ) . string ( ) ) ) . string ( ) ;
} catch ( const std : : exception & ex ) {
BOOST_LOG_TRIVIAL ( error ) < < " Failed to apply the print statistics to the export file name: " < < ex . what ( ) ;
final_path = path_in ;
}
return final_path ;
}
} // namespace Slic3r