2013-12-20 00:36:42 +00:00
# include "Print.hpp"
2014-05-06 08:07:18 +00:00
# include "BoundingBox.hpp"
2014-11-09 14:27:34 +00:00
# include "ClipperUtils.hpp"
2017-02-15 10:05:52 +00:00
# include "Extruder.hpp"
2014-12-12 18:14:52 +00:00
# include "Flow.hpp"
2014-11-11 20:17:02 +00:00
# include "Geometry.hpp"
2014-12-12 18:14:52 +00:00
# include "SupportMaterial.hpp"
2017-05-25 20:27:53 +00:00
# include "GCode/WipeTowerPrusaMM.hpp"
2014-08-03 16:41:09 +00:00
# include <algorithm>
2017-05-31 15:02:23 +00:00
# include <unordered_set>
2016-12-20 18:26:23 +00:00
# include <boost/filesystem.hpp>
# include <boost/lexical_cast.hpp>
2013-12-20 00:36:42 +00:00
namespace Slic3r {
2017-05-30 15:17:26 +00:00
template class PrintState < PrintStep , psCount > ;
template class PrintState < PrintObjectStep , posCount > ;
2014-05-06 08:07:18 +00:00
2017-05-30 18:09:34 +00:00
void Print : : clear_objects ( )
2014-05-06 08:07:18 +00:00
{
2017-05-03 16:28:22 +00:00
for ( int i = int ( this - > objects . size ( ) ) - 1 ; i > = 0 ; - - i )
2014-05-06 08:07:18 +00:00
this - > delete_object ( i ) ;
2017-05-31 15:02:23 +00:00
for ( PrintRegion * region : this - > regions )
delete region ;
this - > regions . clear ( ) ;
2014-05-06 08:07:18 +00:00
}
2017-05-30 18:09:34 +00:00
void Print : : delete_object ( size_t idx )
2014-05-06 08:07:18 +00:00
{
2014-12-12 19:16:32 +00:00
// destroy object and remove it from our container
2017-05-31 10:55:59 +00:00
delete this - > objects [ idx ] ;
2017-05-30 18:09:34 +00:00
this - > objects . erase ( this - > objects . begin ( ) + idx ) ;
2017-05-31 10:55:59 +00:00
this - > invalidate_all_steps ( ) ;
2014-05-06 08:07:18 +00:00
// TODO: purge unused regions
}
2017-05-31 15:02:23 +00:00
void Print : : reload_object ( size_t /* idx */ )
2014-11-07 19:25:05 +00:00
{
/* TODO: this method should check whether the per-object config and per-material configs
have changed in such a way that regions need to be rearranged or we can just apply
the diff and invalidate something . Same logic as apply_config ( )
For now we just re - add all objects since we haven ' t implemented this incremental logic yet .
This should also check whether object volumes ( parts ) have changed . */
// collect all current model objects
ModelObjectPtrs model_objects ;
2017-05-30 18:09:34 +00:00
model_objects . reserve ( this - > objects . size ( ) ) ;
for ( PrintObject * object : this - > objects )
model_objects . push_back ( object - > model_object ( ) ) ;
2014-11-07 19:25:05 +00:00
// remove our print objects
2014-11-12 21:51:48 +00:00
this - > clear_objects ( ) ;
2014-11-07 19:25:05 +00:00
// re-add model objects
2017-05-30 18:09:34 +00:00
for ( ModelObject * mo : model_objects )
this - > add_model_object ( mo ) ;
2014-11-07 19:25:05 +00:00
}
2017-09-01 15:30:18 +00:00
// Reloads the model instances into the print class.
// The slicing shall not be running as the modified model instances at the print
// are used for the brim & skirt calculation.
// Returns true if the brim or skirt have been invalidated.
2017-05-30 18:09:34 +00:00
bool Print : : reload_model_instances ( )
2014-12-29 21:29:24 +00:00
{
bool invalidated = false ;
2017-05-30 18:09:34 +00:00
for ( PrintObject * object : this - > objects )
invalidated | = object - > reload_model_instances ( ) ;
2014-12-29 21:29:24 +00:00
return invalidated ;
}
2017-05-30 18:09:34 +00:00
PrintRegion * Print : : add_region ( )
2014-05-06 08:07:18 +00:00
{
2017-05-30 18:09:34 +00:00
regions . push_back ( new PrintRegion ( this ) ) ;
return regions . back ( ) ;
2014-05-06 08:07:18 +00:00
}
2017-05-30 18:09:34 +00:00
// Called by Print::apply_config().
// This method only accepts PrintConfig option keys.
bool Print : : invalidate_state_by_config_options ( const std : : vector < t_config_option_key > & opt_keys )
2014-05-06 08:07:18 +00:00
{
2017-05-30 18:09:34 +00:00
if ( opt_keys . empty ( ) )
return false ;
2014-05-06 08:07:18 +00:00
2017-05-31 15:02:23 +00:00
// Cache the plenty of parameters, which influence the G-code generator only,
// or they are only notes not influencing the generated G-code.
2017-11-28 14:19:57 +00:00
static std : : unordered_set < std : : string > steps_ignore = {
" avoid_crossing_perimeters " ,
" bed_shape " ,
" bed_temperature " ,
" before_layer_gcode " ,
" between_objects_gcode " ,
" bridge_acceleration " ,
" bridge_fan_speed " ,
" cooling " ,
" default_acceleration " ,
" deretract_speed " ,
" disable_fan_first_layers " ,
" duplicate_distance " ,
" end_gcode " ,
" end_filament_gcode " ,
" extrusion_axis " ,
" extruder_clearance_height " ,
" extruder_clearance_radius " ,
" extruder_colour " ,
" extruder_offset " ,
" extrusion_multiplier " ,
" fan_always_on " ,
" fan_below_layer_time " ,
" filament_colour " ,
" filament_diameter " ,
" filament_density " ,
" filament_notes " ,
" filament_cost " ,
" filament_max_volumetric_speed " ,
" first_layer_acceleration " ,
" first_layer_bed_temperature " ,
" first_layer_speed " ,
" gcode_comments " ,
" gcode_flavor " ,
" infill_acceleration " ,
" infill_first " ,
" layer_gcode " ,
" min_fan_speed " ,
" max_fan_speed " ,
2018-03-09 09:40:42 +00:00
" max_print_height " ,
2017-11-28 14:19:57 +00:00
" min_print_speed " ,
" max_print_speed " ,
" max_volumetric_speed " ,
" max_volumetric_extrusion_rate_slope_positive " ,
" max_volumetric_extrusion_rate_slope_negative " ,
" notes " ,
" only_retract_when_crossing_perimeters " ,
" output_filename_format " ,
" perimeter_acceleration " ,
" post_process " ,
" printer_notes " ,
" retract_before_travel " ,
" retract_before_wipe " ,
" retract_layer_change " ,
" retract_length " ,
" retract_length_toolchange " ,
" retract_lift " ,
" retract_lift_above " ,
" retract_lift_below " ,
" retract_restart_extra " ,
" retract_restart_extra_toolchange " ,
" retract_speed " ,
" slowdown_below_layer_time " ,
" standby_temperature_delta " ,
" start_gcode " ,
" start_filament_gcode " ,
" toolchange_gcode " ,
" threads " ,
" travel_speed " ,
" use_firmware_retraction " ,
" use_relative_e_distances " ,
" use_volumetric_e " ,
" variable_layer_height " ,
" wipe "
} ;
2017-05-31 15:02:23 +00:00
2017-05-30 15:17:26 +00:00
std : : vector < PrintStep > steps ;
std : : vector < PrintObjectStep > osteps ;
2017-05-31 15:02:23 +00:00
bool invalidated = false ;
2017-05-30 15:17:26 +00:00
for ( const t_config_option_key & opt_key : opt_keys ) {
2017-05-31 15:02:23 +00:00
if ( steps_ignore . find ( opt_key ) ! = steps_ignore . 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.
} else if (
opt_key = = " skirts "
2017-05-30 15:17:26 +00:00
| | opt_key = = " skirt_height "
| | opt_key = = " skirt_distance "
| | opt_key = = " min_skirt_length "
| | opt_key = = " ooze_prevention " ) {
steps . emplace_back ( psSkirt ) ;
} else if ( opt_key = = " brim_width " ) {
steps . emplace_back ( psBrim ) ;
steps . emplace_back ( psSkirt ) ;
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " nozzle_diameter "
2017-05-30 15:17:26 +00:00
| | opt_key = = " resolution " ) {
osteps . emplace_back ( posSlice ) ;
2017-05-25 20:27:53 +00:00
} else if (
2017-05-30 15:17:26 +00:00
opt_key = = " complete_objects "
| | opt_key = = " filament_type "
2017-05-31 15:02:23 +00:00
| | opt_key = = " filament_soluble "
2017-05-30 15:17:26 +00:00
| | opt_key = = " first_layer_temperature "
2018-03-05 09:45:35 +00:00
| | opt_key = = " filament_loading_speed "
| | opt_key = = " filament_unloading_speed "
| | opt_key = = " filament_toolchange_delay "
2018-04-24 11:02:08 +00:00
| | opt_key = = " filament_cooling_moves "
| | opt_key = = " filament_cooling_initial_speed "
| | opt_key = = " filament_cooling_final_speed "
2018-03-15 13:04:12 +00:00
| | opt_key = = " filament_ramming_parameters "
2017-05-30 15:17:26 +00:00
| | opt_key = = " gcode_flavor "
| | opt_key = = " single_extruder_multi_material "
| | opt_key = = " spiral_vase "
| | opt_key = = " temperature "
| | opt_key = = " wipe_tower "
| | opt_key = = " wipe_tower_x "
| | opt_key = = " wipe_tower_y "
| | opt_key = = " wipe_tower_width "
2017-11-30 11:08:22 +00:00
| | opt_key = = " wipe_tower_rotation_angle "
2018-03-12 14:41:25 +00:00
| | opt_key = = " wipe_tower_bridging "
2018-03-20 14:07:18 +00:00
| | opt_key = = " wiping_volumes_matrix "
2018-04-12 13:38:05 +00:00
| | opt_key = = " parking_pos_retraction "
| | opt_key = = " cooling_tube_retraction "
| | opt_key = = " cooling_tube_length "
2018-04-16 12:26:57 +00:00
| | opt_key = = " extra_loading_move "
2017-05-30 15:17:26 +00:00
| | opt_key = = " z_offset " ) {
steps . emplace_back ( psWipeTower ) ;
2017-05-31 15:02:23 +00:00
} else if (
opt_key = = " first_layer_extrusion_width "
| | opt_key = = " min_layer_height "
| | opt_key = = " max_layer_height " ) {
2017-05-30 15:17:26 +00:00
osteps . emplace_back ( posPerimeters ) ;
osteps . emplace_back ( posInfill ) ;
osteps . emplace_back ( posSupportMaterial ) ;
steps . emplace_back ( psSkirt ) ;
steps . emplace_back ( psBrim ) ;
steps . emplace_back ( psWipeTower ) ;
2014-06-10 22:15:02 +00:00
} else {
2014-06-13 09:19:53 +00:00
// for legacy, if we can't handle this option let's invalidate all steps
2017-05-31 10:55:59 +00:00
//FIXME invalidate all steps of all objects as well?
2017-05-31 15:02:23 +00:00
invalidated | = this - > invalidate_all_steps ( ) ;
// Continue with the other opt_keys to possibly invalidate any object specific steps.
2014-06-10 22:15:02 +00:00
}
}
2017-05-31 15:02:23 +00:00
2017-05-30 15:17:26 +00:00
sort_remove_duplicates ( steps ) ;
for ( PrintStep step : steps )
invalidated | = this - > invalidate_step ( step ) ;
sort_remove_duplicates ( osteps ) ;
for ( PrintObjectStep ostep : osteps )
2017-05-31 10:55:59 +00:00
for ( PrintObject * object : this - > objects )
invalidated | = object - > invalidate_step ( ostep ) ;
2014-06-13 09:19:53 +00:00
return invalidated ;
2014-06-10 22:15:02 +00:00
}
2017-05-30 15:17:26 +00:00
bool Print : : invalidate_step ( PrintStep step )
2014-06-10 22:15:02 +00:00
{
2014-06-13 09:19:53 +00:00
bool invalidated = this - > state . invalidate ( step ) ;
2017-05-30 15:17:26 +00:00
// Propagate to dependent steps.
2017-05-31 15:02:23 +00:00
//FIXME Why should skirt invalidate brim? Shouldn't it be vice versa?
2017-05-25 20:27:53 +00:00
if ( step = = psSkirt )
2017-05-30 15:17:26 +00:00
invalidated | = this - > state . invalidate ( psBrim ) ;
2014-06-13 09:19:53 +00:00
return invalidated ;
2014-06-10 22:15:02 +00:00
}
2014-11-30 19:18:09 +00:00
// returns true if an object step is done on all objects
// and there's at least one object
2017-05-30 15:17:26 +00:00
bool Print : : step_done ( PrintObjectStep step ) const
2014-11-30 19:18:09 +00:00
{
2017-05-30 15:17:26 +00:00
if ( this - > objects . empty ( ) )
return false ;
for ( const PrintObject * object : this - > objects )
if ( ! object - > state . is_done ( step ) )
2014-11-30 19:18:09 +00:00
return false ;
return true ;
}
2014-08-03 16:41:09 +00:00
// returns 0-based indices of used extruders
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > Print : : object_extruders ( ) const
2014-08-03 16:41:09 +00:00
{
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > extruders ;
2014-08-03 16:41:09 +00:00
2017-05-30 15:17:26 +00:00
for ( PrintRegion * region : this - > regions ) {
2015-03-06 20:35:00 +00:00
// these checks reflect the same logic used in the GUI for enabling/disabling
// extruder selection fields
2017-05-30 15:17:26 +00:00
if ( region - > config . perimeters . value > 0 | | this - > config . brim_width . value > 0 )
extruders . push_back ( region - > config . perimeter_extruder - 1 ) ;
if ( region - > config . fill_density . value > 0 )
extruders . push_back ( region - > config . infill_extruder - 1 ) ;
if ( region - > config . top_solid_layers . value > 0 | | region - > config . bottom_solid_layers . value > 0 )
extruders . push_back ( region - > config . solid_infill_extruder - 1 ) ;
2014-08-03 16:41:09 +00:00
}
2015-03-09 18:27:57 +00:00
2017-05-15 09:32:59 +00:00
sort_remove_duplicates ( extruders ) ;
2015-03-09 18:27:57 +00:00
return extruders ;
}
// returns 0-based indices of used extruders
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > Print : : support_material_extruders ( ) const
2015-03-09 18:27:57 +00:00
{
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > extruders ;
2017-01-30 18:56:46 +00:00
bool support_uses_current_extruder = false ;
2017-05-03 16:28:22 +00:00
for ( PrintObject * object : this - > objects ) {
if ( object - > has_support_material ( ) ) {
if ( object - > config . support_material_extruder = = 0 )
2017-01-30 18:56:46 +00:00
support_uses_current_extruder = true ;
else
2017-05-03 16:28:22 +00:00
extruders . push_back ( object - > config . support_material_extruder - 1 ) ;
if ( object - > config . support_material_interface_extruder = = 0 )
2017-01-30 18:56:46 +00:00
support_uses_current_extruder = true ;
else
2017-05-03 16:28:22 +00:00
extruders . push_back ( object - > config . support_material_interface_extruder - 1 ) ;
2015-03-06 08:56:58 +00:00
}
2014-08-03 16:41:09 +00:00
}
2017-01-30 18:56:46 +00:00
2017-05-03 16:28:22 +00:00
if ( support_uses_current_extruder )
2017-01-30 18:56:46 +00:00
// Add all object extruders to the support extruders as it is not know which one will be used to print supports.
2017-05-03 16:28:22 +00:00
append ( extruders , this - > object_extruders ( ) ) ;
2014-08-03 16:41:09 +00:00
2017-05-15 09:32:59 +00:00
sort_remove_duplicates ( extruders ) ;
2014-08-03 16:41:09 +00:00
return extruders ;
}
2015-03-09 18:36:23 +00:00
// returns 0-based indices of used extruders
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > Print : : extruders ( ) const
2015-03-09 18:36:23 +00:00
{
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > extruders = this - > object_extruders ( ) ;
append ( extruders , this - > support_material_extruders ( ) ) ;
2017-05-15 09:32:59 +00:00
sort_remove_duplicates ( extruders ) ;
2015-03-09 18:36:23 +00:00
return extruders ;
}
2017-05-03 16:28:22 +00:00
void Print : : _simplify_slices ( double distance )
2014-08-03 16:41:09 +00:00
{
2017-05-30 18:09:34 +00:00
for ( PrintObject * object : this - > objects ) {
for ( Layer * layer : object - > layers ) {
layer - > slices . simplify ( distance ) ;
for ( LayerRegion * layerm : layer - > regions )
layerm - > slices . simplify ( distance ) ;
2014-08-03 16:41:09 +00:00
}
}
}
2017-05-03 16:28:22 +00:00
double Print : : max_allowed_layer_height ( ) const
2014-08-03 16:41:09 +00:00
{
2017-05-03 16:28:22 +00:00
double nozzle_diameter_max = 0. ;
for ( unsigned int extruder_id : this - > extruders ( ) )
nozzle_diameter_max = std : : max ( nozzle_diameter_max , this - > config . nozzle_diameter . get_at ( extruder_id ) ) ;
return nozzle_diameter_max ;
2014-08-03 16:41:09 +00:00
}
2017-05-31 10:55:59 +00:00
// Caller is responsible for supplying models whose objects don't collide
// and have explicit instance positions.
2017-05-03 16:28:22 +00:00
void Print : : add_model_object ( ModelObject * model_object , int idx )
2014-11-09 11:25:59 +00:00
{
2017-05-31 10:55:59 +00:00
// Initialize a new print object and store it at the given position.
PrintObject * object = new PrintObject ( this , model_object , model_object - > raw_bounding_box ( ) ) ;
if ( idx ! = - 1 ) {
delete this - > objects [ idx ] ;
this - > objects [ idx ] = object ;
} else
this - > objects . emplace_back ( object ) ;
// Invalidate all print steps.
this - > invalidate_all_steps ( ) ;
2014-11-09 11:25:59 +00:00
2017-05-30 15:17:26 +00:00
for ( size_t volume_id = 0 ; volume_id < model_object - > volumes . size ( ) ; + + volume_id ) {
2017-05-31 10:55:59 +00:00
// Get the config applied to this volume.
PrintRegionConfig config = this - > _region_config_from_model_volume ( * model_object - > volumes [ volume_id ] ) ;
// Find an existing print region with the same config.
2017-05-30 15:17:26 +00:00
size_t region_id = size_t ( - 1 ) ;
for ( size_t i = 0 ; i < this - > regions . size ( ) ; + + i )
if ( config . equals ( this - > regions [ i ] - > config ) ) {
region_id = i ;
2014-11-09 11:25:59 +00:00
break ;
}
2017-05-31 10:55:59 +00:00
// If no region exists with the same config, create a new one.
2017-05-30 15:17:26 +00:00
if ( region_id = = size_t ( - 1 ) ) {
region_id = this - > regions . size ( ) ;
this - > add_region ( ) - > config . apply ( config ) ;
2014-11-09 11:25:59 +00:00
}
2017-05-31 10:55:59 +00:00
// Assign volume to a region.
object - > add_region_volume ( region_id , volume_id ) ;
2014-11-09 11:25:59 +00:00
}
2017-05-31 10:55:59 +00:00
// Apply config to print object.
object - > config . apply ( this - > default_object_config ) ;
normalize_and_apply_config ( object - > config , model_object - > config ) ;
2016-12-20 18:26:23 +00:00
// update placeholders
{
// get the first input file name
std : : string input_file ;
std : : vector < std : : string > v_scale ;
for ( const PrintObject * object : this - > objects ) {
const ModelObject & mobj = * object - > model_object ( ) ;
2017-05-31 10:55:59 +00:00
v_scale . push_back ( boost : : lexical_cast < std : : string > ( mobj . instances [ 0 ] - > scaling_factor * 100 ) + " % " ) ;
2016-12-20 18:26:23 +00:00
if ( input_file . empty ( ) )
input_file = mobj . input_file ;
}
PlaceholderParser & pp = this - > placeholder_parser ;
pp . set ( " scale " , v_scale ) ;
2017-05-31 10:55:59 +00:00
if ( ! input_file . empty ( ) ) {
2016-12-20 18:26:23 +00:00
// get basename with and without suffix
const std : : string input_basename = boost : : filesystem : : path ( input_file ) . filename ( ) . string ( ) ;
pp . set ( " input_filename " , input_basename ) ;
const std : : string input_basename_base = input_basename . substr ( 0 , input_basename . find_last_of ( " . " ) ) ;
pp . set ( " input_filename_base " , input_basename_base ) ;
}
}
2014-11-09 11:25:59 +00:00
}
2017-05-30 18:09:34 +00:00
bool Print : : apply_config ( DynamicPrintConfig config )
2014-11-09 11:25:59 +00:00
{
// we get a copy of the config object so we can modify it safely
config . normalize ( ) ;
// apply variables to placeholder parser
this - > placeholder_parser . apply_config ( config ) ;
// handle changes to print config
t_config_option_keys print_diff = this - > config . diff ( config ) ;
2017-10-17 14:01:18 +00:00
this - > config . apply_only ( config , print_diff , true ) ;
2017-05-31 10:55:59 +00:00
bool invalidated = this - > invalidate_state_by_config_options ( print_diff ) ;
2014-11-09 11:25:59 +00:00
// handle changes to object config defaults
this - > default_object_config . apply ( config , true ) ;
2017-05-30 18:09:34 +00:00
for ( PrintObject * object : this - > objects ) {
2014-11-09 11:25:59 +00:00
// we don't assume that config contains a full ObjectConfig,
// so we base it on the current print-wise default
PrintObjectConfig new_config = this - > default_object_config ;
// we override the new config with object-specific options
2017-05-31 10:55:59 +00:00
normalize_and_apply_config ( new_config , object - > model_object ( ) - > config ) ;
2017-06-09 11:27:35 +00:00
// Force a refresh of a variable layer height profile at the PrintObject if it is not valid.
if ( ! object - > layer_height_profile_valid ) {
// The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change).
// Invalidate the slicing step, which in turn invalidates everything.
object - > invalidate_step ( posSlice ) ;
// Trigger recalculation.
invalidated = true ;
}
2014-11-09 11:25:59 +00:00
// check whether the new config is different from the current one
2017-05-30 18:09:34 +00:00
t_config_option_keys diff = object - > config . diff ( new_config ) ;
2017-10-17 14:01:18 +00:00
object - > config . apply_only ( new_config , diff , true ) ;
2017-05-30 18:09:34 +00:00
invalidated | = object - > invalidate_state_by_config_options ( diff ) ;
2014-11-09 11:25:59 +00:00
}
// handle changes to regions config defaults
this - > default_region_config . apply ( config , true ) ;
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
bool rearrange_regions = false ;
2017-05-30 18:09:34 +00:00
{
2017-05-31 10:55:59 +00:00
// Collect the already visited region configs into other_region_configs,
// so one may check for duplicates.
std : : vector < PrintRegionConfig > other_region_configs ;
for ( size_t region_id = 0 ; region_id < this - > regions . size ( ) ; + + region_id ) {
PrintRegion & region = * this - > regions [ region_id ] ;
PrintRegionConfig this_region_config ;
bool this_region_config_set = false ;
for ( PrintObject * object : this - > objects ) {
if ( region_id < object - > region_volumes . size ( ) ) {
for ( int volume_id : object - > region_volumes [ region_id ] ) {
const ModelVolume & volume = * object - > model_object ( ) - > volumes [ volume_id ] ;
if ( this_region_config_set ) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if ( ! this_region_config . equals ( this - > _region_config_from_model_volume ( volume ) ) ) {
rearrange_regions = true ;
goto exit_for_rearrange_regions ;
}
} else {
this_region_config = this - > _region_config_from_model_volume ( volume ) ;
this_region_config_set = true ;
}
for ( const PrintRegionConfig & cfg : other_region_configs ) {
// If the new config for this volume equals any of the other
// volume configs that are not currently associated to this
// region, it means the region subdivision does not make
// sense anymore.
if ( cfg . equals ( this_region_config ) ) {
rearrange_regions = true ;
goto exit_for_rearrange_regions ;
}
}
2014-11-09 11:25:59 +00:00
}
}
2017-05-31 10:55:59 +00:00
}
if ( this_region_config_set ) {
t_config_option_keys diff = region . config . diff ( this_region_config ) ;
2017-05-30 18:09:34 +00:00
if ( ! diff . empty ( ) ) {
2017-10-17 14:01:18 +00:00
region . config . apply_only ( this_region_config , diff ) ;
2017-05-30 18:09:34 +00:00
for ( PrintObject * object : this - > objects )
2017-05-31 10:55:59 +00:00
if ( region_id < object - > region_volumes . size ( ) & & ! object - > region_volumes [ region_id ] . empty ( ) )
invalidated | = object - > invalidate_state_by_config_options ( diff ) ;
2014-11-09 11:25:59 +00:00
}
2017-09-01 15:30:18 +00:00
other_region_configs . emplace_back ( std : : move ( this_region_config ) ) ;
2014-11-09 11:25:59 +00:00
}
}
}
2017-05-31 10:55:59 +00:00
exit_for_rearrange_regions :
2014-11-09 11:25:59 +00:00
if ( rearrange_regions ) {
2017-05-30 18:09:34 +00:00
// The current subdivision of regions does not make sense anymore.
// We need to remove all objects and re-add them.
2014-11-09 11:25:59 +00:00
ModelObjectPtrs model_objects ;
2017-05-30 18:09:34 +00:00
model_objects . reserve ( this - > objects . size ( ) ) ;
for ( PrintObject * object : this - > objects )
model_objects . push_back ( object - > model_object ( ) ) ;
2014-11-09 11:25:59 +00:00
this - > clear_objects ( ) ;
2017-06-15 18:15:53 +00:00
for ( ModelObject * mo : model_objects )
2017-05-30 18:09:34 +00:00
this - > add_model_object ( mo ) ;
2014-11-09 11:25:59 +00:00
invalidated = true ;
}
2017-06-15 18:15:53 +00:00
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
for ( PrintObject * object : this - > objects )
if ( ! object - > layer_height_profile_valid )
object - > update_layer_height_profile ( ) ;
2014-11-09 11:25:59 +00:00
return invalidated ;
}
2015-03-06 08:56:58 +00:00
bool Print : : has_infinite_skirt ( ) const
2014-08-03 16:41:09 +00:00
{
2015-03-06 08:56:58 +00:00
return ( this - > config . skirt_height = = - 1 & & this - > config . skirts > 0 )
| | ( this - > config . ooze_prevention & & this - > extruders ( ) . size ( ) > 1 ) ;
}
bool Print : : has_skirt ( ) const
{
return ( this - > config . skirt_height > 0 & & this - > config . skirts > 0 )
| | this - > has_infinite_skirt ( ) ;
2014-08-03 16:41:09 +00:00
}
2017-05-30 18:09:34 +00:00
std : : string Print : : validate ( ) const
2014-11-09 14:27:34 +00:00
{
2018-03-14 15:11:57 +00:00
BoundingBox bed_box_2D = get_extents ( Polygon : : new_scale ( config . bed_shape . values ) ) ;
BoundingBoxf3 print_volume ( Pointf3 ( unscale ( bed_box_2D . min . x ) , unscale ( bed_box_2D . min . y ) , 0.0 ) , Pointf3 ( unscale ( bed_box_2D . max . x ) , unscale ( bed_box_2D . max . y ) , config . max_print_height ) ) ;
2018-03-14 17:16:17 +00:00
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume . min . z = - 1e10 ;
2018-03-14 15:11:57 +00:00
for ( PrintObject * po : this - > objects ) {
2018-04-24 07:00:33 +00:00
if ( ! print_volume . contains ( po - > model_object ( ) - > tight_bounding_box ( false ) ) )
2018-03-14 15:11:57 +00:00
return " Some objects are outside of the print volume. " ;
}
2014-11-09 14:27:34 +00:00
if ( this - > config . complete_objects ) {
2017-05-31 10:55:59 +00:00
// Check horizontal clearance.
2014-11-09 14:27:34 +00:00
{
2017-05-31 10:55:59 +00:00
Polygons convex_hulls_other ;
for ( PrintObject * object : this - > objects ) {
2017-06-13 09:35:24 +00:00
// Get convex hull of all meshes assigned to this print object.
2015-01-07 20:57:22 +00:00
Polygon convex_hull ;
{
Polygons mesh_convex_hulls ;
2017-05-31 10:55:59 +00:00
for ( const std : : vector < int > & volumes : object - > region_volumes )
for ( int volume_id : volumes )
mesh_convex_hulls . emplace_back ( object - > model_object ( ) - > volumes [ volume_id ] - > mesh . convex_hull ( ) ) ;
2015-01-07 20:57:22 +00:00
// make a single convex hull for all of them
2015-01-19 17:53:04 +00:00
convex_hull = Slic3r : : Geometry : : convex_hull ( mesh_convex_hulls ) ;
2015-01-07 20:57:22 +00:00
}
2017-05-31 10:55:59 +00:00
// Apply the same transformations we apply to the actual meshes when slicing them.
2014-11-09 14:27:34 +00:00
object - > model_object ( ) - > instances . front ( ) - > transform_polygon ( & convex_hull ) ;
2017-05-31 10:55:59 +00:00
// Grow convex hull with the clearance margin.
2016-12-13 18:22:23 +00:00
convex_hull = offset ( convex_hull , scale_ ( this - > config . extruder_clearance_radius . value ) / 2 , jtRound , scale_ ( 0.1 ) ) . front ( ) ;
2017-05-31 10:55:59 +00:00
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
2017-05-03 16:28:22 +00:00
for ( const Point & copy : object - > _shifted_copies ) {
2014-11-09 14:27:34 +00:00
Polygon p = convex_hull ;
2017-05-03 16:28:22 +00:00
p . translate ( copy ) ;
2017-05-31 10:55:59 +00:00
if ( ! intersection ( convex_hulls_other , p ) . empty ( ) )
2016-11-05 01:23:46 +00:00
return " Some objects are too close; your extruder will collide with them. " ;
2017-05-31 10:55:59 +00:00
polygons_append ( convex_hulls_other , p ) ;
2014-11-09 14:27:34 +00:00
}
}
}
2017-05-31 10:55:59 +00:00
// Check vertical clearance.
2014-11-09 14:27:34 +00:00
{
std : : vector < coord_t > object_height ;
2017-05-31 10:55:59 +00:00
for ( const PrintObject * object : this - > objects )
2014-11-09 14:27:34 +00:00
object_height . insert ( object_height . end ( ) , object - > copies ( ) . size ( ) , object - > size . z ) ;
std : : sort ( object_height . begin ( ) , object_height . end ( ) ) ;
2017-05-31 10:55:59 +00:00
// Ignore the tallest *copy* (this is why we repeat height for all of them):
// it will be printed as last one so its height doesn't matter.
2014-11-09 14:27:34 +00:00
object_height . pop_back ( ) ;
2017-05-31 10:55:59 +00:00
if ( ! object_height . empty ( ) & & object_height . back ( ) > scale_ ( this - > config . extruder_clearance_height . value ) )
2016-11-05 01:23:46 +00:00
return " Some objects are too tall and cannot be printed without extruder collisions. " ;
2014-11-09 14:27:34 +00:00
}
2016-09-13 11:30:00 +00:00
} // end if (this->config.complete_objects)
2017-05-31 10:55:59 +00:00
2014-11-09 14:27:34 +00:00
if ( this - > config . spiral_vase ) {
size_t total_copies_count = 0 ;
2017-05-31 10:55:59 +00:00
for ( const PrintObject * object : this - > objects )
total_copies_count + = object - > copies ( ) . size ( ) ;
2017-06-23 08:13:09 +00:00
// #4043
if ( total_copies_count > 1 & & ! this - > config . complete_objects . value )
2016-11-05 01:23:46 +00:00
return " The Spiral Vase option can only be used when printing a single object. " ;
2014-11-09 14:27:34 +00:00
if ( this - > regions . size ( ) > 1 )
2016-11-05 01:23:46 +00:00
return " The Spiral Vase option can only be used when printing single material objects. " ;
2014-11-09 14:27:34 +00:00
}
2017-06-06 08:36:14 +00:00
2018-03-29 13:32:09 +00:00
if ( this - > config . single_extruder_multi_material ) {
for ( size_t i = 1 ; i < this - > config . nozzle_diameter . values . size ( ) ; + + i )
if ( this - > config . nozzle_diameter . values [ i ] ! = this - > config . nozzle_diameter . values [ i - 1 ] )
return " All extruders must have the same diameter for single extruder multimaterial printer. " ;
}
2017-07-17 07:07:18 +00:00
if ( this - > has_wipe_tower ( ) & & ! this - > objects . empty ( ) ) {
2017-08-28 12:57:00 +00:00
#if 0
2017-06-06 08:36:14 +00:00
for ( auto dmr : this - > config . nozzle_diameter . values )
if ( std : : abs ( dmr - 0.4 ) > EPSILON )
return " The Wipe Tower is currently only supported for the 0.4mm nozzle diameter. " ;
2017-08-28 12:57:00 +00:00
# endif
2018-01-06 17:49:28 +00:00
if ( this - > config . gcode_flavor ! = gcfRepRap & & this - > config . gcode_flavor ! = gcfMarlin )
return " The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors. " ;
2017-06-06 08:36:14 +00:00
if ( ! this - > config . use_relative_e_distances )
return " The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1). " ;
2017-06-08 14:58:29 +00:00
SlicingParameters slicing_params0 = this - > objects . front ( ) - > slicing_parameters ( ) ;
2018-04-16 09:47:35 +00:00
2018-04-26 09:19:51 +00:00
const PrintObject * tallest_object = this - > objects . front ( ) ; // let's find the tallest object
2018-04-16 09:47:35 +00:00
for ( const auto * object : objects )
2018-04-26 09:19:51 +00:00
if ( * ( object - > layer_height_profile . end ( ) - 2 ) > * ( tallest_object - > layer_height_profile . end ( ) - 2 ) )
tallest_object = object ;
2018-04-16 09:47:35 +00:00
2017-06-06 09:40:35 +00:00
for ( PrintObject * object : this - > objects ) {
2017-06-08 14:58:29 +00:00
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 )
2017-06-06 09:40:35 +00:00
return " The Wipe Tower is only supported for multiple objects if they have equal layer heigths " ;
2017-06-08 14:58:29 +00:00
if ( slicing_params . raft_layers ( ) ! = slicing_params0 . raft_layers ( ) )
return " The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers " ;
if ( object - > config . support_material_contact_distance ! = this - > objects . front ( ) - > config . support_material_contact_distance )
return " The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance " ;
if ( ! equal_layering ( slicing_params , slicing_params0 ) )
return " The Wipe Tower is only supported for multiple objects if they are sliced equally. " ;
2017-06-09 11:27:35 +00:00
bool was_layer_height_profile_valid = object - > layer_height_profile_valid ;
2017-06-06 09:40:35 +00:00
object - > update_layer_height_profile ( ) ;
2017-06-09 11:27:35 +00:00
object - > layer_height_profile_valid = was_layer_height_profile_valid ;
2018-04-03 11:51:12 +00:00
2018-04-26 09:19:51 +00:00
if ( this - > config . variable_layer_height ) { // comparing layer height profiles
bool failed = false ;
if ( tallest_object - > layer_height_profile . size ( ) > = object - > layer_height_profile . size ( ) ) {
int i = 0 ;
while ( i < object - > layer_height_profile . size ( ) & & i < tallest_object - > layer_height_profile . size ( ) ) {
if ( std : : abs ( tallest_object - > layer_height_profile [ i ] - object - > layer_height_profile [ i ] ) ) {
failed = true ;
break ;
}
+ + i ;
if ( i = = object - > layer_height_profile . size ( ) - 2 ) // this element contains this objects max z
if ( tallest_object - > layer_height_profile [ i ] > object - > layer_height_profile [ i ] ) // the difference does not matter in this case
+ + i ;
}
2018-04-04 07:59:41 +00:00
}
2018-04-26 09:19:51 +00:00
else
failed = true ;
if ( failed )
return " The Wipe tower is only supported if all objects have the same layer height profile " ;
2018-04-03 11:51:12 +00:00
}
2018-03-29 13:32:09 +00:00
/*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
2017-06-08 14:58:29 +00:00
if ( object - > layer_height_profile [ i - 1 ] > slicing_params . object_print_z_min + EPSILON & &
std : : abs ( object - > layer_height_profile [ i ] - object - > config . layer_height ) > EPSILON )
2018-03-29 13:32:09 +00:00
return " The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed. " ; */
2017-06-06 09:40:35 +00:00
}
2017-06-06 08:36:14 +00:00
}
2014-11-09 14:27:34 +00:00
{
2015-03-06 20:35:00 +00:00
// find the smallest nozzle diameter
2017-05-03 16:28:22 +00:00
std : : vector < unsigned int > extruders = this - > extruders ( ) ;
2015-03-06 20:35:00 +00:00
if ( extruders . empty ( ) )
2016-11-05 01:23:46 +00:00
return " The supplied settings will cause an empty print. " ;
2015-03-06 20:35:00 +00:00
2017-05-03 16:28:22 +00:00
std : : vector < double > nozzle_diameters ;
for ( unsigned int extruder_id : extruders )
nozzle_diameters . push_back ( this - > config . nozzle_diameter . get_at ( extruder_id ) ) ;
2015-03-06 20:35:00 +00:00
double min_nozzle_diameter = * std : : min_element ( nozzle_diameters . begin ( ) , nozzle_diameters . end ( ) ) ;
2018-03-21 15:01:31 +00:00
unsigned int total_extruders_count = this - > config . nozzle_diameter . size ( ) ;
for ( const auto & extruder_idx : extruders )
if ( extruder_idx > = total_extruders_count )
2018-04-13 11:43:53 +00:00
return " One or more object were assigned an extruder that the printer does not have. " ;
2018-03-21 15:01:31 +00:00
2017-05-31 10:55:59 +00:00
for ( PrintObject * object : this - > objects ) {
2017-01-30 18:56:46 +00:00
if ( ( object - > config . support_material_extruder = = - 1 | | object - > config . support_material_interface_extruder = = - 1 ) & &
( object - > config . raft_layers > 0 | | object - > config . support_material . value ) ) {
// The object has some form of support and either support_material_extruder or support_material_interface_extruder
// will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
// are of the same diameter.
if ( nozzle_diameters . size ( ) > 1 )
return " Printing with multiple extruders of differing nozzle diameters. "
2017-02-11 23:51:53 +00:00
" If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), "
2017-01-30 18:56:46 +00:00
" all nozzles have to be of the same diameter. " ;
}
2015-03-06 20:35:00 +00:00
// validate first_layer_height
double first_layer_height = object - > config . get_abs_value ( " first_layer_height " ) ;
double first_layer_min_nozzle_diameter ;
if ( object - > config . raft_layers > 0 ) {
// 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_material_interface_extruder - 1
: object - > config . support_material_extruder - 1 ;
2017-01-30 18:56:46 +00:00
first_layer_min_nozzle_diameter = ( first_layer_extruder = = size_t ( - 1 ) ) ?
min_nozzle_diameter :
this - > config . nozzle_diameter . get_at ( first_layer_extruder ) ;
2015-03-06 20:35:00 +00:00
} 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 ( first_layer_height > first_layer_min_nozzle_diameter )
2016-11-05 01:23:46 +00:00
return " First layer height can't be greater than nozzle diameter " ;
2015-03-06 20:35:00 +00:00
// validate layer_height
if ( object - > config . layer_height . value > min_nozzle_diameter )
2016-11-05 01:23:46 +00:00
return " Layer height can't be greater than nozzle diameter " ;
2014-11-09 14:27:34 +00:00
}
}
2016-11-05 01:23:46 +00:00
return std : : string ( ) ;
2014-11-09 14:27:34 +00:00
}
2014-12-12 18:14:52 +00:00
// the bounding box of objects placed in copies position
// (without taking skirt/brim/support material into account)
2017-05-30 18:09:34 +00:00
BoundingBox Print : : bounding_box ( ) const
2014-12-12 18:14:52 +00:00
{
BoundingBox bb ;
2017-05-31 10:55:59 +00:00
for ( const PrintObject * object : this - > objects )
for ( Point copy : object - > _shifted_copies ) {
bb . merge ( copy ) ;
copy . translate ( object - > size ) ;
bb . merge ( copy ) ;
2014-12-12 18:14:52 +00:00
}
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
2017-05-30 18:09:34 +00:00
BoundingBox Print : : total_bounding_box ( ) const
2014-12-12 18:14:52 +00:00
{
// get objects bounding box
BoundingBox bb = this - > bounding_box ( ) ;
2014-12-12 18:25:50 +00:00
// we need to offset the objects bounding box by at least half the perimeters extrusion width
Flow perimeter_flow = this - > 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 ( this - > config . brim_width . value > 0 ) {
2014-12-16 23:45:05 +00:00
Flow brim_flow = this - > brim_flow ( ) ;
extra = std : : max ( extra , this - > config . brim_width . value + brim_flow . width / 2 ) ;
2014-12-12 18:25:50 +00:00
}
2015-03-06 08:56:58 +00:00
if ( this - > has_skirt ( ) ) {
int skirts = this - > config . skirts . value ;
if ( skirts = = 0 & & this - > has_infinite_skirt ( ) ) skirts = 1 ;
2014-12-16 23:45:05 +00:00
Flow skirt_flow = this - > skirt_flow ( ) ;
2014-12-12 18:14:52 +00:00
extra = std : : max (
extra ,
2014-12-12 18:25:50 +00:00
this - > config . brim_width . value
+ this - > config . skirt_distance . value
2015-03-06 08:56:58 +00:00
+ skirts * skirt_flow . spacing ( )
2014-12-12 18:25:50 +00:00
+ skirt_flow . width / 2
2014-12-12 18:14:52 +00:00
) ;
}
if ( extra > 0 )
bb . offset ( scale_ ( extra ) ) ;
return bb ;
}
2017-05-30 18:09:34 +00:00
double Print : : skirt_first_layer_height ( ) const
2014-12-12 18:14:52 +00:00
{
if ( this - > objects . empty ( ) ) CONFESS ( " skirt_first_layer_height() can't be called without PrintObjects " ) ;
return this - > objects . front ( ) - > config . get_abs_value ( " first_layer_height " ) ;
}
2017-05-30 18:09:34 +00:00
Flow Print : : brim_flow ( ) const
2014-12-16 23:45:05 +00:00
{
ConfigOptionFloatOrPercent width = this - > config . first_layer_extrusion_width ;
2017-11-09 09:48:06 +00:00
if ( width . value = = 0 )
width = this - > regions . front ( ) - > config . perimeter_extrusion_width ;
if ( width . value = = 0 )
width = this - > objects . front ( ) - > config . extrusion_width ;
2014-12-16 23:45:05 +00:00
/* 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 ,
this - > config . nozzle_diameter . get_at ( this - > regions . front ( ) - > config . perimeter_extruder - 1 ) ,
this - > skirt_first_layer_height ( ) ,
0
) ;
}
2017-05-30 18:09:34 +00:00
Flow Print : : skirt_flow ( ) const
2014-12-12 18:14:52 +00:00
{
ConfigOptionFloatOrPercent width = this - > config . first_layer_extrusion_width ;
2017-11-09 09:48:06 +00:00
if ( width . value = = 0 )
width = this - > regions . front ( ) - > config . perimeter_extrusion_width ;
if ( width . value = = 0 )
width = this - > objects . front ( ) - > config . extrusion_width ;
2014-12-12 18:14:52 +00:00
2014-12-16 23:45:05 +00:00
/* 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 . */
2014-12-12 18:14:52 +00:00
return Flow : : new_from_config_width (
frPerimeter ,
width ,
this - > config . nozzle_diameter . get_at ( this - > objects . front ( ) - > config . support_material_extruder - 1 ) ,
this - > skirt_first_layer_height ( ) ,
0
) ;
}
2017-05-30 18:09:34 +00:00
PrintRegionConfig Print : : _region_config_from_model_volume ( const ModelVolume & volume )
2014-11-09 11:25:59 +00:00
{
PrintRegionConfig config = this - > default_region_config ;
2017-05-31 10:55:59 +00:00
normalize_and_apply_config ( config , volume . get_object ( ) - > config ) ;
normalize_and_apply_config ( config , volume . config ) ;
if ( ! volume . material_id ( ) . empty ( ) )
normalize_and_apply_config ( config , volume . material ( ) - > config ) ;
2014-11-09 11:25:59 +00:00
return config ;
}
2017-05-30 18:09:34 +00:00
bool Print : : has_support_material ( ) const
2014-08-03 16:41:09 +00:00
{
2017-05-30 18:09:34 +00:00
for ( const PrintObject * object : this - > objects )
if ( object - > has_support_material ( ) )
return true ;
2014-08-03 16:41:09 +00:00
return false ;
}
2015-12-02 17:29:33 +00:00
/* This method assigns extruders to the volumes having a material
but not having extruders set in the volume config . */
2017-05-30 18:09:34 +00:00
void Print : : auto_assign_extruders ( ModelObject * model_object ) const
2015-12-02 17:29:33 +00:00
{
// only assign extruders if object has more than one volume
2017-05-30 18:09:34 +00:00
if ( model_object - > volumes . size ( ) < 2 )
return ;
2015-12-02 17:29:33 +00:00
2017-05-30 18:09:34 +00:00
// size_t extruders = this->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 - > material_id ( ) . empty ( ) & & ! volume - > config . has ( " extruder " ) )
volume - > config . opt < ConfigOptionInt > ( " extruder " , true ) - > value = int ( volume_id + 1 ) ;
2015-12-02 17:29:33 +00:00
}
}
2017-02-15 10:05:52 +00:00
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 : this - > objects ) {
size_t skirt_layers = this - > has_infinite_skirt ( ) ?
object - > layer_count ( ) :
std : : min ( size_t ( this - > config . skirt_height . value ) , object - > layer_count ( ) ) ;
skirt_height_z = std : : max ( skirt_height_z , object - > layers [ skirt_layers - 1 ] - > print_z ) ;
}
// Collect points from all layers contained in skirt height.
Points points ;
for ( const PrintObject * object : this - > objects ) {
Points object_points ;
// Get object layers up to skirt_height_z.
for ( const Layer * layer : object - > layers ) {
if ( layer - > print_z > skirt_height_z )
break ;
for ( const ExPolygon & expoly : layer - > slices . expolygons )
// 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 ;
for ( const ExtrusionEntity * extrusion_entity : layer - > support_fills . entities )
append ( object_points , extrusion_entity - > as_polyline ( ) . points ) ;
}
// Repeat points for each object copy.
for ( const Point & shift : object - > _shifted_copies ) {
Points copy_points = object_points ;
for ( Point & pt : copy_points )
pt . translate ( shift ) ;
append ( points , copy_points ) ;
}
}
if ( points . size ( ) < 3 )
// At least three points required for a convex hull.
return ;
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 first_layer_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 ) ;
2017-10-17 17:41:04 +00:00
extruders_e_per_mm . push_back ( Extruder ( ( unsigned int ) extruder_id , & this - > config ) . e_per_mm ( mm3_per_mm ) ) ;
2017-02-15 10:05:52 +00:00
}
}
// Number of skirt loops per skirt layer.
int n_skirts = this - > config . skirts . 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.
2018-05-18 10:19:54 +00:00
Flow brim_flow = this - > brim_flow ( ) ;
double actual_brim_width = brim_flow . spacing ( ) * floor ( this - > config . brim_width . value / brim_flow . spacing ( ) ) ;
2018-05-22 08:50:38 +00:00
coord_t distance = scale_ ( std : : max ( this - > config . skirt_distance . value , actual_brim_width ) - spacing / 2. ) ;
2017-02-15 10:05:52 +00:00
// 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 ( int i = n_skirts , extruder_idx = 0 ; i > 0 ; - - i ) {
// Offset the skirt outside.
distance + = coord_t ( scale_ ( spacing ) ) ;
// Generate the skirt centerline.
2017-03-23 11:35:00 +00:00
Polygon loop ;
{
Polygons loops = offset ( convex_hull , distance , ClipperLib : : jtRound , scale_ ( 0.1 ) ) ;
Geometry : : simplify_polygons ( loops , scale_ ( 0.05 ) , & loops ) ;
loop = loops . front ( ) ;
}
2017-02-15 10:05:52 +00:00
// Extrude the skirt loop.
ExtrusionLoop eloop ( elrSkirt ) ;
eloop . paths . emplace_back ( ExtrusionPath (
ExtrusionPath (
erSkirt ,
mm3_per_mm , // this will be overridden at G-code export time
flow . width ,
first_layer_height // this will be overridden at G-code export time
) ) ) ;
eloop . paths . back ( ) . polyline = loop . split_at_first_point ( ) ;
this - > skirt . append ( eloop ) ;
if ( this - > config . min_skirt_length . value > 0 ) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
extruded_length [ extruder_idx ] + = unscale ( loop . length ( ) ) * extruders_e_per_mm [ extruder_idx ] ;
if ( extruded_length [ extruder_idx ] < this - > config . min_skirt_length . value ) {
// Not extruded enough yet with the current extruder. Add another loop.
if ( i = = 1 )
+ + i ;
} else {
assert ( extruded_length [ extruder_idx ] > = this - > config . min_skirt_length . value ) ;
// 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.
this - > skirt . reverse ( ) ;
}
2017-07-07 14:40:23 +00:00
void Print : : _make_brim ( )
{
// Brim is only printed on first layer and uses perimeter extruder.
Flow flow = this - > brim_flow ( ) ;
Polygons islands ;
for ( PrintObject * object : this - > objects ) {
Polygons object_islands ;
for ( ExPolygon & expoly : object - > layers . front ( ) - > slices . expolygons )
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 ) ) ;
islands . reserve ( islands . size ( ) + object_islands . size ( ) * object - > _shifted_copies . size ( ) ) ;
for ( const Point & pt : object - > _shifted_copies )
for ( Polygon & poly : object_islands ) {
islands . push_back ( poly ) ;
islands . back ( ) . translate ( pt ) ;
}
}
Polygons loops ;
2018-05-18 10:19:54 +00:00
size_t num_loops = size_t ( floor ( this - > config . brim_width . value / flow . spacing ( ) ) ) ;
2017-07-07 14:40:23 +00:00
for ( size_t i = 0 ; i < num_loops ; + + i ) {
islands = offset ( islands , float ( flow . scaled_spacing ( ) ) , jtSquare ) ;
for ( Polygon & poly : islands ) {
// poly.simplify(SCALED_RESOLUTION);
poly . points . push_back ( poly . points . front ( ) ) ;
Points p = MultiPoint : : _douglas_peucker ( poly . points , SCALED_RESOLUTION ) ;
p . pop_back ( ) ;
poly . points = std : : move ( p ) ;
}
polygons_append ( loops , offset ( islands , - 0.5f * float ( flow . scaled_spacing ( ) ) ) ) ;
}
loops = union_pt_chained ( loops , false ) ;
std : : reverse ( loops . begin ( ) , loops . end ( ) ) ;
extrusion_entities_append_loops ( this - > brim . entities , std : : move ( loops ) , erSkirt , float ( flow . mm3_per_mm ( ) ) , float ( flow . width ) , float ( this - > skirt_first_layer_height ( ) ) ) ;
}
2017-05-25 20:27:53 +00:00
// Wipe tower support.
2017-07-17 07:07:18 +00:00
bool Print : : has_wipe_tower ( ) const
2017-05-25 20:27:53 +00:00
{
return
this - > config . single_extruder_multi_material . value & &
! this - > config . spiral_vase . value & &
this - > config . wipe_tower . value & &
this - > config . nozzle_diameter . values . size ( ) > 1 ;
}
void Print : : _clear_wipe_tower ( )
{
m_tool_ordering . clear ( ) ;
2017-09-01 15:30:18 +00:00
m_wipe_tower_priming . reset ( nullptr ) ;
2017-05-25 20:27:53 +00:00
m_wipe_tower_tool_changes . clear ( ) ;
m_wipe_tower_final_purge . reset ( nullptr ) ;
}
void Print : : _make_wipe_tower ( )
{
this - > _clear_wipe_tower ( ) ;
if ( ! this - > has_wipe_tower ( ) )
return ;
2018-05-24 15:24:37 +00:00
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
std : : vector < float > wiping_matrix ( ( this - > config . wiping_volumes_matrix . values ) . begin ( ) , ( this - > config . wiping_volumes_matrix . values ) . end ( ) ) ;
// Extract purging volumes for each extruder pair:
std : : vector < std : : vector < float > > wipe_volumes ;
const unsigned int number_of_extruders = ( unsigned int ) ( sqrt ( wiping_matrix . size ( ) ) + EPSILON ) ;
for ( unsigned int i = 0 ; i < number_of_extruders ; + + i )
wipe_volumes . push_back ( std : : vector < float > ( wiping_matrix . begin ( ) + i * number_of_extruders , wiping_matrix . begin ( ) + ( i + 1 ) * number_of_extruders ) ) ;
2018-05-24 12:05:51 +00:00
2017-09-01 15:30:18 +00:00
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_tool_ordering = ToolOrdering ( * this , ( unsigned int ) - 1 , true ) ;
2017-09-04 11:51:05 +00:00
if ( ! m_tool_ordering . has_wipe_tower ( ) )
2017-05-30 07:25:34 +00:00
// Don't generate any wipe tower.
2017-05-25 20:27:53 +00:00
return ;
2017-12-11 16:19:55 +00:00
// 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/Slic3r/issues/607
{
size_t idx_begin = size_t ( - 1 ) ;
size_t idx_end = m_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 ToolOrdering : : LayerTools & lt = m_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 this->objects.first()->support_layers to insert these new support layers.
double wipe_tower_new_layer_print_z_first = m_tool_ordering . layer_tools ( ) [ idx_begin ] . print_z ;
SupportLayerPtrs : : iterator it_layer = this - > objects . front ( ) - > support_layers . begin ( ) ;
2018-02-13 10:18:58 +00:00
SupportLayerPtrs : : iterator it_end = this - > objects . front ( ) - > support_layers . end ( ) ;
for ( ; it_layer ! = it_end & & ( * it_layer ) - > print_z - EPSILON < wipe_tower_new_layer_print_z_first ; + + it_layer ) ;
2017-12-11 16:19:55 +00:00
// 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 ) {
ToolOrdering : : LayerTools & lt = const_cast < ToolOrdering : : LayerTools & > ( m_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 - m_tool_ordering . layer_tools ( ) [ i - 1 ] . print_z ;
2018-02-13 10:18:58 +00:00
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
auto * new_layer = new SupportLayer ( size_t ( - 1 ) , this - > objects . front ( ) ,
2017-12-11 16:19:55 +00:00
height , lt . print_z , lt . print_z - 0.5 * height ) ;
it_layer = this - > objects . front ( ) - > support_layers . insert ( it_layer , new_layer ) ;
+ + it_layer ;
}
}
}
2017-05-25 20:27:53 +00:00
// Initialize the wipe tower.
WipeTowerPrusaMM wipe_tower (
float ( this - > config . wipe_tower_x . value ) , float ( this - > config . wipe_tower_y . value ) ,
2018-03-22 12:37:01 +00:00
float ( this - > config . wipe_tower_width . value ) ,
2018-03-01 15:15:00 +00:00
float ( this - > config . wipe_tower_rotation_angle . value ) , float ( this - > config . cooling_tube_retraction . value ) ,
float ( this - > config . cooling_tube_length . value ) , float ( this - > config . parking_pos_retraction . value ) ,
2018-05-24 15:24:37 +00:00
float ( this - > config . extra_loading_move . value ) , float ( this - > config . wipe_tower_bridging ) , wipe_volumes ,
2018-04-16 12:26:57 +00:00
m_tool_ordering . first_extruder ( ) ) ;
2018-03-20 14:45:11 +00:00
2017-05-25 20:27:53 +00:00
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object.
2018-05-24 15:24:37 +00:00
for ( size_t i = 0 ; i < number_of_extruders ; + + i )
2017-05-25 20:27:53 +00:00
wipe_tower . set_extruder (
i ,
WipeTowerPrusaMM : : parse_material ( this - > config . filament_type . get_at ( i ) . c_str ( ) ) ,
this - > config . temperature . get_at ( i ) ,
2018-03-05 09:45:35 +00:00
this - > config . first_layer_temperature . get_at ( i ) ,
this - > config . filament_loading_speed . get_at ( i ) ,
this - > config . filament_unloading_speed . get_at ( i ) ,
2018-03-12 14:41:25 +00:00
this - > config . filament_toolchange_delay . get_at ( i ) ,
2018-04-24 11:02:08 +00:00
this - > config . filament_cooling_moves . get_at ( i ) ,
this - > config . filament_cooling_initial_speed . get_at ( i ) ,
this - > config . filament_cooling_final_speed . get_at ( i ) ,
2018-03-29 13:32:09 +00:00
this - > config . filament_ramming_parameters . get_at ( i ) ,
this - > config . nozzle_diameter . get_at ( i ) ) ;
2017-05-25 20:27:53 +00:00
2017-09-12 13:55:38 +00:00
// When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
// Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer.
// The following variable is true if the last priming section cannot be squeezed inside the wipe tower.
bool last_priming_wipe_full = m_tool_ordering . front ( ) . extruders . size ( ) > m_tool_ordering . front ( ) . wipe_tower_partitions ;
2017-09-01 15:30:18 +00:00
m_wipe_tower_priming = Slic3r : : make_unique < WipeTower : : ToolChangeResult > (
2018-03-08 15:44:52 +00:00
wipe_tower . prime ( this - > skirt_first_layer_height ( ) , m_tool_ordering . all_extruders ( ) , ! last_priming_wipe_full ) ) ;
2017-09-01 15:30:18 +00:00
2017-12-21 12:28:26 +00:00
// 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)
{
2017-12-21 13:24:47 +00:00
unsigned int current_extruder_id = m_tool_ordering . all_extruders ( ) . back ( ) ;
2017-12-21 12:28:26 +00:00
for ( const auto & layer_tools : m_tool_ordering . layer_tools ( ) ) { // for all layers
if ( ! layer_tools . has_wipe_tower ) continue ;
bool first_layer = & layer_tools = = & m_tool_ordering . front ( ) ;
2017-12-22 10:26:43 +00:00
wipe_tower . plan_toolchange ( layer_tools . print_z , layer_tools . wipe_tower_layer_height , current_extruder_id , current_extruder_id , false ) ;
2017-12-21 12:28:26 +00:00
for ( const auto extruder_id : layer_tools . extruders ) {
2017-12-22 10:26:43 +00:00
if ( ( first_layer & & extruder_id = = m_tool_ordering . all_extruders ( ) . back ( ) ) | | extruder_id ! = current_extruder_id ) {
2018-05-30 09:56:30 +00:00
float volume_to_wipe = wipe_volumes [ current_extruder_id ] [ extruder_id ] ; // total volume to wipe after this toolchange
2018-05-24 15:24:37 +00:00
2018-05-30 09:56:30 +00:00
if ( config . wipe_into_infill & & ! first_layer )
volume_to_wipe = mark_wiping_infill ( layer_tools , extruder_id , wipe_volumes [ current_extruder_id ] [ extruder_id ] ) ;
2018-05-24 15:24:37 +00:00
2018-05-30 09:56:30 +00:00
wipe_tower . plan_toolchange ( layer_tools . print_z , layer_tools . wipe_tower_layer_height , current_extruder_id , extruder_id , first_layer & & extruder_id = = m_tool_ordering . all_extruders ( ) . back ( ) , volume_to_wipe ) ;
2017-12-21 12:28:26 +00:00
current_extruder_id = extruder_id ;
}
}
if ( & layer_tools = = & m_tool_ordering . back ( ) | | ( & layer_tools + 1 ) - > wipe_tower_partitions = = 0 )
break ;
}
}
2017-05-25 20:27:53 +00:00
// Generate the wipe tower layers.
m_wipe_tower_tool_changes . reserve ( m_tool_ordering . layer_tools ( ) . size ( ) ) ;
2017-12-21 12:28:26 +00:00
wipe_tower . generate ( m_wipe_tower_tool_changes ) ;
2017-05-25 20:27:53 +00:00
// Unload the current filament over the purge tower.
2017-05-30 07:25:34 +00:00
coordf_t layer_height = this - > objects . front ( ) - > config . layer_height . value ;
if ( m_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_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_tool_ordering . back ( ) . wipe_tower_partitions = = 0 ) ;
wipe_tower . set_layer ( float ( m_tool_ordering . back ( ) . print_z ) , float ( layer_height ) , 0 , false , true ) ;
2017-05-25 20:27:53 +00:00
}
m_wipe_tower_final_purge = Slic3r : : make_unique < WipeTower : : ToolChangeResult > (
2018-03-08 15:44:52 +00:00
wipe_tower . tool_change ( ( unsigned int ) - 1 , false ) ) ;
2017-05-25 20:27:53 +00:00
}
2018-05-30 09:56:30 +00:00
float Print : : mark_wiping_infill ( const ToolOrdering : : LayerTools & layer_tools , unsigned int new_extruder , float volume_to_wipe )
{
const float min_infill_volume = 0.f ; // ignore infill with smaller volume than this
if ( ! config . filament_soluble . get_at ( new_extruder ) ) { // Soluble filament cannot be wiped in a random infill
for ( size_t i = 0 ; i < objects . size ( ) ; + + i ) { // Let's iterate through all objects...
Layer * this_layer = nullptr ;
for ( unsigned int a = 0 ; a < objects [ i ] - > layers . size ( ) ; this_layer = objects [ i ] - > layers [ + + a ] ) // Finds this layer
if ( std : : abs ( layer_tools . print_z - objects [ i ] - > layers [ a ] - > print_z ) < EPSILON )
break ;
for ( size_t region_id = 0 ; region_id < objects [ i ] - > print ( ) - > regions . size ( ) ; + + region_id ) {
unsigned int region_extruder = objects [ i ] - > print ( ) - > regions [ region_id ] - > config . infill_extruder - 1 ; // config value is 1-based
if ( config . filament_soluble . get_at ( region_extruder ) ) // if this infill is meant to be soluble, keep it that way
continue ;
if ( ! config . infill_first ) { // in this case we must verify that region_extruder was already used at this layer (and perimeters of the infill are therefore extruded)
bool unused_yet = false ;
for ( unsigned i = 0 ; i < layer_tools . extruders . size ( ) ; + + i ) {
if ( layer_tools . extruders [ i ] = = new_extruder )
unused_yet = true ;
if ( layer_tools . extruders [ i ] = = region_extruder )
break ;
}
if ( unused_yet )
continue ;
}
ExtrusionEntityCollection & eec = this_layer - > regions [ region_id ] - > fills ;
for ( ExtrusionEntity * ee : eec . entities ) { // iterate through all infill Collections
auto * fill = dynamic_cast < ExtrusionEntityCollection * > ( ee ) ;
if ( fill - > role ( ) = = erTopSolidInfill ) continue ; // color of TopSolidInfill cannot be changed - it is visible
if ( volume_to_wipe > 0.f & & ! fill - > is_extruder_overridden ( ) & & fill - > total_volume ( ) > min_infill_volume ) { // this infill will be used to wipe this extruder
fill - > set_extruder_override ( new_extruder ) ;
volume_to_wipe - = fill - > total_volume ( ) ;
}
}
}
}
}
return std : : max ( 0.f , volume_to_wipe ) ;
}
2017-05-25 20:27:53 +00:00
std : : string Print : : output_filename ( )
2016-12-20 18:01:51 +00:00
{
this - > placeholder_parser . update_timestamp ( ) ;
2017-12-05 14:54:24 +00:00
try {
return this - > placeholder_parser . process ( this - > config . output_filename_format . value , 0 ) ;
} catch ( std : : runtime_error & err ) {
throw std : : runtime_error ( std : : string ( " Failed processing of the output_filename_format template. \n " ) + err . what ( ) ) ;
}
2016-12-20 18:01:51 +00:00
}
2017-05-25 20:27:53 +00:00
std : : string Print : : output_filepath ( const std : : string & path )
2016-12-20 18:01:51 +00:00
{
// if we were supplied no path, generate an automatic one based on our first object's input file
if ( path . empty ( ) ) {
// get the first input file name
std : : string input_file ;
2017-05-31 10:55:59 +00:00
for ( const PrintObject * object : this - > objects ) {
input_file = object - > model_object ( ) - > input_file ;
if ( ! input_file . empty ( ) )
break ;
2016-12-20 18:01:51 +00:00
}
2017-10-27 20:49:59 +00:00
return ( boost : : filesystem : : path ( input_file ) . parent_path ( ) / this - > output_filename ( ) ) . make_preferred ( ) . string ( ) ;
2016-12-20 18:01:51 +00:00
}
// if we were supplied a directory, use it and append our automatically generated filename
boost : : filesystem : : path p ( path ) ;
if ( boost : : filesystem : : is_directory ( p ) )
2017-10-27 20:49:59 +00:00
return ( p / this - > output_filename ( ) ) . make_preferred ( ) . string ( ) ;
2016-12-20 18:01:51 +00:00
// if we were supplied a file which is not a directory, use it
return path ;
}
2017-09-01 15:30:18 +00:00
void Print : : set_status ( int percent , const std : : string & message )
{
printf ( " Print::status %d => %s \n " , percent , message . c_str ( ) ) ;
}
2013-12-20 00:36:42 +00:00
}