Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::WipeTower Class Reference

#include <src/libslic3r/GCode/WipeTower.hpp>

+ Collaboration diagram for Slic3r::WipeTower:

Classes

struct  box_coordinates
 
struct  Extrusion
 
struct  FilamentParameters
 
struct  ToolChangeResult
 
struct  WipeTowerInfo
 

Public Member Functions

ToolChangeResult construct_tcr (WipeTowerWriter &writer, bool priming, size_t old_tool) const
 
 WipeTower (const PrintConfig &config, const std::vector< std::vector< float > > &wiping_matrix, size_t initial_tool)
 
void set_extruder (size_t idx, const PrintConfig &config)
 
void plan_toolchange (float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume=0.f)
 
void generate (std::vector< std::vector< ToolChangeResult > > &result)
 
float get_depth () const
 
std::vector< std::pair< float, float > > get_z_and_depth_pairs () const
 
float get_brim_width () const
 
float get_wipe_tower_height () const
 
void set_layer (float print_z, float layer_height, size_t max_tool_changes, bool, bool is_last_layer)
 
const Vec2fposition () const
 
float width () const
 
bool finished () const
 
std::vector< ToolChangeResultprime (float first_layer_height, const std::vector< unsigned int > &tools, bool last_wipe_inside_wipe_tower)
 
ToolChangeResult tool_change (size_t new_tool)
 
ToolChangeResult finish_layer ()
 
bool layer_finished () const
 
std::vector< float > get_used_filament () const
 
int get_number_of_toolchanges () const
 

Static Public Member Functions

static const std::string never_skip_tag ()
 
static std::pair< double, double > get_wipe_tower_cone_base (double width, double height, double depth, double angle_deg)
 
static std::vector< std::vector< float > > extract_wipe_volumes (const PrintConfig &config)
 

Private Types

enum  wipe_shape { SHAPE_NORMAL = 1 , SHAPE_REVERSED = -1 }
 
enum  { RectangularBed , CircularBed , CustomBed }
 

Private Member Functions

float filament_area () const
 
bool is_first_layer () const
 
float extrusion_flow (float layer_height=-1.f) const
 
float volume_to_length (float volume, float line_width, float layer_height) const
 
void plan_tower ()
 
void make_wipe_tower_square ()
 
void save_on_last_wipe ()
 
int first_toolchange_to_nonsoluble (const std::vector< WipeTowerInfo::ToolChange > &tool_changes) const
 
void toolchange_Unload (WipeTowerWriter &writer, const box_coordinates &cleaning_box, const std::string &current_material, const int new_temperature)
 
void toolchange_Change (WipeTowerWriter &writer, const size_t new_tool, const std::string &new_material)
 
void toolchange_Load (WipeTowerWriter &writer, const box_coordinates &cleaning_box)
 
void toolchange_Wipe (WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_volume)
 

Private Attributes

const float Width_To_Nozzle_Ratio = 1.25f
 
const float WT_EPSILON = 1e-3f
 
bool m_semm = true
 
Vec2f m_wipe_tower_pos
 
float m_wipe_tower_width
 
float m_wipe_tower_depth = 0.f
 
float m_wipe_tower_height = 0.f
 
float m_wipe_tower_cone_angle = 0.f
 
float m_wipe_tower_brim_width = 0.f
 
float m_wipe_tower_brim_width_real = 0.f
 
float m_wipe_tower_rotation_angle = 0.f
 
float m_internal_rotation = 0.f
 
float m_y_shift = 0.f
 
float m_z_pos = 0.f
 
float m_layer_height = 0.f
 
size_t m_max_color_changes = 0
 
int m_old_temperature = -1
 
float m_travel_speed = 0.f
 
float m_first_layer_speed = 0.f
 
size_t m_first_layer_idx = size_t(-1)
 
float m_cooling_tube_retraction = 0.f
 
float m_cooling_tube_length = 0.f
 
float m_parking_pos_retraction = 0.f
 
float m_extra_loading_move = 0.f
 
float m_bridging = 0.f
 
bool m_no_sparse_layers = false
 
bool m_set_extruder_trimpot = false
 
bool m_adhesion = true
 
GCodeFlavor m_gcode_flavor
 
enum Slic3r::WipeTower:: { ... }  m_bed_shape
 
float m_bed_width
 
Vec2f m_bed_bottom_left
 
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio
 
float m_extrusion_flow = 0.038f
 
std::vector< FilamentParametersm_filpar
 
unsigned int m_num_layer_changes = 0
 
unsigned int m_num_tool_changes = 0
 
bool m_print_brim = true
 unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
 
wipe_shape m_current_shape = SHAPE_NORMAL
 
size_t m_current_tool = 0
 
const std::vector< std::vector< float > > wipe_volumes
 
float m_depth_traversed = 0.f
 
bool m_current_layer_finished = false
 
bool m_left_to_right = true
 
float m_extra_spacing = 1.f
 
std::vector< WipeTowerInfom_plan
 
std::vector< WipeTowerInfo >::iterator m_layer_info = m_plan.end()
 
float m_current_height = 0.f
 
std::vector< float > m_used_filament_length
 

Detailed Description


Class Documentation

◆ Slic3r::WipeTower::FilamentParameters

struct Slic3r::WipeTower::FilamentParameters
+ Collaboration diagram for Slic3r::WipeTower::FilamentParameters:
Class Members
float cooling_final_speed = 0.f
float cooling_initial_speed = 0.f
int cooling_moves = 0
float delay = 0.f
float filament_area
int first_layer_temperature = 0
bool is_soluble = false
float loading_speed = 0.f
float loading_speed_start = 0.f
string material = "PLA"
float max_e_speed = std::numeric_limits<float>::max()
float nozzle_diameter
float ramming_line_width_multiplicator = 1.f
vector< float > ramming_speed
float ramming_step_multiplicator = 1.f
int temperature = 0
float unloading_speed = 0.f
float unloading_speed_start = 0.f

Member Enumeration Documentation

◆ anonymous enum

anonymous enum
private
Enumerator
RectangularBed 
CircularBed 
CustomBed 
287 {
291 } m_bed_shape;
@ CustomBed
Definition WipeTower.hpp:290
@ RectangularBed
Definition WipeTower.hpp:288
@ CircularBed
Definition WipeTower.hpp:289
enum Slic3r::WipeTower::@681 m_bed_shape

◆ wipe_shape

Enumerator
SHAPE_NORMAL 
SHAPE_REVERSED 
244 {
245 SHAPE_NORMAL = 1,
246 SHAPE_REVERSED = -1
247 };
@ SHAPE_NORMAL
Definition WipeTower.hpp:245
@ SHAPE_REVERSED
Definition WipeTower.hpp:246

Constructor & Destructor Documentation

◆ WipeTower()

Slic3r::WipeTower::WipeTower ( const PrintConfig &  config,
const std::vector< std::vector< float > > &  wiping_matrix,
size_t  initial_tool 
)
519 :
520 m_semm(config.single_extruder_multi_material.value),
521 m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y),
522 m_wipe_tower_width(float(config.wipe_tower_width)),
523 m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)),
524 m_wipe_tower_brim_width(float(config.wipe_tower_brim_width)),
525 m_wipe_tower_cone_angle(float(config.wipe_tower_cone_angle)),
526 m_extra_spacing(float(config.wipe_tower_extra_spacing/100.)),
527 m_y_shift(0.f),
528 m_z_pos(0.f),
529 m_bridging(float(config.wipe_tower_bridging)),
530 m_no_sparse_layers(config.wipe_tower_no_sparse_layers),
531 m_gcode_flavor(config.gcode_flavor),
532 m_travel_speed(config.travel_speed),
533 m_current_tool(initial_tool),
534 wipe_volumes(wiping_matrix)
535{
536 // Read absolute value of first layer speed, if given as percentage,
537 // it is taken over following default. Speeds from config are not
538 // easily accessible here.
539 const float default_speed = 60.f;
540 m_first_layer_speed = config.get_abs_value("first_layer_speed", default_speed);
541 if (m_first_layer_speed == 0.f) // just to make sure autospeed doesn't break it.
542 m_first_layer_speed = default_speed / 2.f;
543
544 // If this is a single extruder MM printer, we will use all the SE-specific config values.
545 // Otherwise, the defaults will be used to turn off the SE stuff.
546 if (m_semm) {
547 m_cooling_tube_retraction = float(config.cooling_tube_retraction);
548 m_cooling_tube_length = float(config.cooling_tube_length);
549 m_parking_pos_retraction = float(config.parking_pos_retraction);
550 m_extra_loading_move = float(config.extra_loading_move);
551 m_set_extruder_trimpot = config.high_current_on_filament_swap;
552 }
553 // Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
554 const std::vector<Vec2d>& bed_points = config.bed_shape.values;
555 BoundingBoxf bb(bed_points);
556 m_bed_width = float(bb.size().x());
557 m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
558
559 if (m_bed_shape == CircularBed) {
560 // this may still be a custom bed, check that the points are roughly on a circle
561 double r2 = std::pow(m_bed_width/2., 2.);
562 double lim2 = std::pow(m_bed_width/10., 2.);
563 Vec2d center = bb.center();
564 for (const Vec2d& pt : bed_points)
565 if (std::abs(std::pow(pt.x()-center.x(), 2.) + std::pow(pt.y()-center.y(), 2.) - r2) > lim2) {
567 break;
568 }
569 }
570
572 ? Vec2f(bed_points.front().x(), bed_points.front().y())
573 : Vec2f::Zero();
574}
Vec2f m_wipe_tower_pos
Definition WipeTower.hpp:257
float m_wipe_tower_brim_width
Definition WipeTower.hpp:262
float m_y_shift
Definition WipeTower.hpp:266
bool m_semm
Definition WipeTower.hpp:256
float m_cooling_tube_retraction
Definition WipeTower.hpp:276
GCodeFlavor m_gcode_flavor
Definition WipeTower.hpp:284
bool m_set_extruder_trimpot
Definition WipeTower.hpp:282
float m_wipe_tower_cone_angle
Definition WipeTower.hpp:261
float m_cooling_tube_length
Definition WipeTower.hpp:277
const std::vector< std::vector< float > > wipe_volumes
Definition WipeTower.hpp:309
float m_wipe_tower_rotation_angle
Definition WipeTower.hpp:264
float m_z_pos
Definition WipeTower.hpp:267
float m_extra_loading_move
Definition WipeTower.hpp:279
Vec2f m_bed_bottom_left
Definition WipeTower.hpp:293
float m_parking_pos_retraction
Definition WipeTower.hpp:278
size_t m_current_tool
Definition WipeTower.hpp:308
bool m_no_sparse_layers
Definition WipeTower.hpp:281
float m_bed_width
Definition WipeTower.hpp:292
float m_travel_speed
Definition WipeTower.hpp:271
float m_extra_spacing
Definition WipeTower.hpp:314
float m_bridging
Definition WipeTower.hpp:280
float m_first_layer_speed
Definition WipeTower.hpp:272
float m_wipe_tower_width
Definition WipeTower.hpp:258
if(!(yy_init))
Definition lexer.c:1190
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half abs(const half &a)
Definition Half.h:445
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half pow(const half &a, const half &b)
Definition Half.h:477
const Scalar & y
Definition MathFunctions.h:552
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:51
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:48
TCoord< P > x(const P &p)
Definition geometry_traits.hpp:297
STL namespace.

References Slic3r::BoundingBoxBase< PointType, APointsType >::center(), CircularBed, CustomBed, m_bed_bottom_left, m_bed_shape, m_bed_width, m_cooling_tube_length, m_cooling_tube_retraction, m_extra_loading_move, m_first_layer_speed, m_parking_pos_retraction, m_semm, m_set_extruder_trimpot, RectangularBed, and Slic3r::BoundingBoxBase< PointType, APointsType >::size().

+ Here is the call graph for this function:

Member Function Documentation

◆ construct_tcr()

WipeTower::ToolChangeResult Slic3r::WipeTower::construct_tcr ( WipeTowerWriter writer,
bool  priming,
size_t  old_tool 
) const
501{
502 ToolChangeResult result;
503 result.priming = priming;
504 result.initial_tool = int(old_tool);
505 result.new_tool = int(m_current_tool);
506 result.print_z = m_z_pos;
507 result.layer_height = m_layer_height;
508 result.elapsed_time = writer.elapsed_time();
509 result.start_pos = writer.start_pos_rotated();
510 result.end_pos = priming ? writer.pos() : writer.pos_rotated();
511 result.gcode = std::move(writer.gcode());
512 result.extrusions = std::move(writer.extrusions());
513 result.wipe_path = std::move(writer.wipe_path());
514 return result;
515}
float m_layer_height
Definition WipeTower.hpp:268

References Slic3r::WipeTowerWriter::elapsed_time(), Slic3r::WipeTower::ToolChangeResult::elapsed_time, Slic3r::WipeTower::ToolChangeResult::end_pos, Slic3r::WipeTowerWriter::extrusions(), Slic3r::WipeTower::ToolChangeResult::extrusions, Slic3r::WipeTowerWriter::gcode(), Slic3r::WipeTower::ToolChangeResult::gcode, Slic3r::WipeTower::ToolChangeResult::initial_tool, Slic3r::WipeTower::ToolChangeResult::layer_height, m_current_tool, m_layer_height, m_z_pos, Slic3r::WipeTower::ToolChangeResult::new_tool, Slic3r::WipeTowerWriter::pos(), Slic3r::WipeTowerWriter::pos_rotated(), Slic3r::WipeTower::ToolChangeResult::priming, Slic3r::WipeTower::ToolChangeResult::print_z, Slic3r::WipeTower::ToolChangeResult::start_pos, Slic3r::WipeTowerWriter::start_pos_rotated(), Slic3r::WipeTowerWriter::wipe_path(), and Slic3r::WipeTower::ToolChangeResult::wipe_path.

Referenced by finish_layer(), prime(), and tool_change().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ extract_wipe_volumes()

std::vector< std::vector< float > > Slic3r::WipeTower::extract_wipe_volumes ( const PrintConfig &  config)
static
1350{
1351 // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
1352 std::vector<float> wiping_matrix(cast<float>(config.wiping_volumes_matrix.values));
1353
1354 // The values shall only be used when SEMM is enabled. The purging for other printers
1355 // is determined by filament_minimal_purge_on_wipe_tower.
1356 if (! config.single_extruder_multi_material.value)
1357 std::fill(wiping_matrix.begin(), wiping_matrix.end(), 0.f);
1358
1359 // Extract purging volumes for each extruder pair:
1360 std::vector<std::vector<float>> wipe_volumes;
1361 const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
1362 for (unsigned int i = 0; i<number_of_extruders; ++i)
1363 wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
1364
1365 // Also include filament_minimal_purge_on_wipe_tower. This is needed for the preview.
1366 for (unsigned int i = 0; i<number_of_extruders; ++i)
1367 for (unsigned int j = 0; j<number_of_extruders; ++j)
1368 wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j], config.filament_minimal_purge_on_wipe_tower.get_at(j));
1369
1370 return wipe_volumes;
1371}
EIGEN_DEVICE_FUNC const SqrtReturnType sqrt() const
Definition ArrayCwiseUnaryOps.h:152
static constexpr double EPSILON
Definition libslic3r.h:51

References EPSILON, sqrt(), and wipe_volumes.

Referenced by Slic3r::Print::_make_wipe_tower(), and Slic3r::Print::wipe_tower_data().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ extrusion_flow()

float Slic3r::WipeTower::extrusion_flow ( float  layer_height = -1.f) const
inlineprivate
320 {
321 if ( layer_height < 0 )
322 return m_extrusion_flow;
323 return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / filament_area();
324 }
#define M_PI
Definition ExtrusionSimulator.cpp:20
float m_extrusion_flow
Definition WipeTower.hpp:296
float filament_area() const
Definition WipeTower.hpp:251
float m_perimeter_width
Definition WipeTower.hpp:295
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat

References filament_area(), Slic3r::layer_height(), m_extrusion_flow, m_perimeter_width, and M_PI.

Referenced by set_layer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ filament_area()

float Slic3r::WipeTower::filament_area ( ) const
inlineprivate
251 {
252 return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point
253 }
std::vector< FilamentParameters > m_filpar
Definition WipeTower.hpp:299

References m_filpar.

Referenced by extrusion_flow(), set_extruder(), and toolchange_Unload().

+ Here is the caller graph for this function:

◆ finish_layer()

WipeTower::ToolChangeResult Slic3r::WipeTower::finish_layer ( )
1091{
1092 assert(! this->layer_finished());
1094
1095 size_t old_tool = m_current_tool;
1096
1097 WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
1098 writer.set_extrusion_flow(m_extrusion_flow)
1099 .set_z(m_z_pos)
1100 .set_initial_tool(m_current_tool)
1101 .set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f));
1102
1103
1104 // Slow down on the 1st layer.
1105 bool first_layer = is_first_layer();
1106 float feedrate = first_layer ? m_first_layer_speed * 60.f : 2900.f;
1107 float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
1108 box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth-(current_depth-m_perimeter_width)),
1110
1111
1112 writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
1114
1115 bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON;
1116 box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)),
1118
1119 // inner perimeter of the sparse section, if there is space for it:
1120 if (fill_box.ru.y() - fill_box.rd.y() > m_perimeter_width - WT_EPSILON)
1121 writer.rectangle(fill_box.ld, fill_box.rd.x()-fill_box.ld.x(), fill_box.ru.y()-fill_box.rd.y(), feedrate);
1122
1123 // we are in one of the corners, travel to ld along the perimeter:
1124 if (writer.x() > fill_box.ld.x()+EPSILON) writer.travel(fill_box.ld.x(),writer.y());
1125 if (writer.y() > fill_box.ld.y()+EPSILON) writer.travel(writer.x(),fill_box.ld.y());
1126
1127 // Extrude infill to support the material to be printed above.
1128 const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
1129 float left = fill_box.lu.x() + 2*m_perimeter_width;
1130 float right = fill_box.ru.x() - 2 * m_perimeter_width;
1131 if (dy > m_perimeter_width)
1132 {
1133 writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f))
1134 .append(";--------------------\n"
1135 "; CP EMPTY GRID START\n")
1136 .comment_with_value(" layer #", m_num_layer_changes + 1);
1137
1138 // Is there a soluble filament wiped/rammed at the next layer?
1139 // If so, the infill should not be sparse.
1140 bool solid_infill = m_layer_info+1 == m_plan.end()
1141 ? false
1142 : std::any_of((m_layer_info+1)->tool_changes.begin(),
1143 (m_layer_info+1)->tool_changes.end(),
1144 [this](const WipeTowerInfo::ToolChange& tch) {
1145 return m_filpar[tch.new_tool].is_soluble
1146 || m_filpar[tch.old_tool].is_soluble;
1147 });
1148 solid_infill |= first_layer && m_adhesion;
1149
1150 if (solid_infill) {
1151 float sparse_factor = 1.5f; // 1=solid, 2=every other line, etc.
1152 if (first_layer) { // the infill should touch perimeters
1155 sparse_factor = 1.f;
1156 }
1157 float y = fill_box.ld.y() + m_perimeter_width;
1158 int n = dy / (m_perimeter_width * sparse_factor);
1159 float spacing = (dy-m_perimeter_width)/(n-1);
1160 int i=0;
1161 for (i=0; i<n; ++i) {
1162 writer.extrude(writer.x(), y, feedrate)
1163 .extrude(i%2 ? left : right, y);
1164 y = y + spacing;
1165 }
1166 writer.extrude(writer.x(), fill_box.lu.y());
1167 } else {
1168 // Extrude an inverse U at the left of the region and the sparse infill.
1169 writer.extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), feedrate);
1170
1171 const int n = 1+int((right-left)/m_bridging);
1172 const float dx = (right-left)/n;
1173 for (int i=1;i<=n;++i) {
1174 float x=left+dx*i;
1175 writer.travel(x,writer.y());
1176 writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y());
1177 }
1178 }
1179
1180 writer.append("; CP EMPTY GRID END\n"
1181 ";------------------\n\n\n\n\n\n\n");
1182 }
1183
1184 const float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4);
1185
1186 // This block creates the stabilization cone.
1187 // First define a lambda to draw the rectangle with stabilization.
1188 auto supported_rectangle = [this, &writer, spacing](const box_coordinates& wt_box, double feedrate, bool infill_cone) -> Polygon {
1190
1191 double z = m_no_sparse_layers ? (m_current_height + m_layer_info->height) : m_layer_info->z; // the former should actually work in both cases, but let's stay on the safe side (the 2.6.0 is close)
1192
1193 double r = std::tan(Geometry::deg2rad(m_wipe_tower_cone_angle/2.f)) * (m_wipe_tower_height - z);
1194 Vec2f center = (wt_box.lu + wt_box.rd) / 2.;
1195 double w = wt_box.lu.y() - wt_box.ld.y();
1196 enum Type {
1197 Arc,
1198 Corner,
1199 ArcStart,
1200 ArcEnd
1201 };
1202
1203 // First generate vector of annotated point which form the boundary.
1204 std::vector<std::pair<Vec2f, Type>> pts = {{wt_box.ru, Corner}};
1205 if (double alpha_start = std::asin((0.5*w)/r); ! std::isnan(alpha_start) && r > 0.5*w+0.01) {
1206 for (double alpha = alpha_start; alpha < M_PI-alpha_start+0.001; alpha+=(M_PI-2*alpha_start) / 20.)
1207 pts.emplace_back(Vec2f(center.x() + r*std::cos(alpha)/support_scale, center.y() + r*std::sin(alpha)), alpha == alpha_start ? ArcStart : Arc);
1208 pts.back().second = ArcEnd;
1209 }
1210 pts.emplace_back(wt_box.lu, Corner);
1211 pts.emplace_back(wt_box.ld, Corner);
1212 for (int i=int(pts.size())-3; i>0; --i)
1213 pts.emplace_back(Vec2f(pts[i].first.x(), 2*center.y()-pts[i].first.y()), i == int(pts.size())-3 ? ArcStart : i == 1 ? ArcEnd : Arc);
1214 pts.emplace_back(wt_box.rd, Corner);
1215
1216 // Create a Polygon from the points.
1217 Polygon poly;
1218 for (const auto& [pt, tag] : pts)
1219 poly.points.push_back(Point::new_scale(pt));
1220
1221 // Prepare polygons to be filled by infill.
1222 Polylines polylines;
1223 if (infill_cone && m_wipe_tower_width > 2*spacing && m_wipe_tower_depth > 2*spacing) {
1224 ExPolygons infill_areas;
1225 ExPolygon wt_contour(poly);
1226 Polygon wt_rectangle(Points{Point::new_scale(wt_box.ld), Point::new_scale(wt_box.rd), Point::new_scale(wt_box.ru), Point::new_scale(wt_box.lu)});
1227 wt_rectangle = offset(wt_rectangle, scale_(-spacing/2.)).front();
1228 wt_contour = offset_ex(wt_contour, scale_(-spacing/2.)).front();
1229 infill_areas = diff_ex(wt_contour, wt_rectangle);
1230 if (infill_areas.size() == 2) {
1231 ExPolygon& bottom_expoly = infill_areas.front().contour.points.front().y() < infill_areas.back().contour.points.front().y() ? infill_areas[0] : infill_areas[1];
1232 std::unique_ptr<Fill> filler(Fill::new_from_type(ipMonotonicLines));
1233 filler->angle = Geometry::deg2rad(45.f);
1234 filler->spacing = spacing;
1235 FillParams params;
1236 params.density = 1.f;
1237 Surface surface(stBottom, bottom_expoly);
1238 filler->bounding_box = get_extents(bottom_expoly);
1239 polylines = filler->fill_surface(&surface, params);
1240 if (! polylines.empty()) {
1241 if (polylines.front().points.front().x() > polylines.back().points.back().x()) {
1242 std::reverse(polylines.begin(), polylines.end());
1243 for (Polyline& p : polylines)
1244 p.reverse();
1245 }
1246 }
1247 }
1248 }
1249
1250 // Find the closest corner and travel to it.
1251 int start_i = 0;
1252 double min_dist = std::numeric_limits<double>::max();
1253 for (int i=0; i<int(pts.size()); ++i) {
1254 if (pts[i].second == Corner) {
1255 double dist = (pts[i].first - Vec2f(writer.x(), writer.y())).squaredNorm();
1256 if (dist < min_dist) {
1257 min_dist = dist;
1258 start_i = i;
1259 }
1260 }
1261 }
1262 writer.travel(pts[start_i].first);
1263
1264 // Now actually extrude the boundary (and possibly infill):
1265 int i = start_i+1 == int(pts.size()) ? 0 : start_i + 1;
1266 while (i != start_i) {
1267 writer.extrude(pts[i].first, feedrate);
1268 if (pts[i].second == ArcEnd) {
1269 // Extrude the infill.
1270 if (! polylines.empty()) {
1271 // Extrude the infill and travel back to where we were.
1272 bool mirror = ((pts[i].first.y() - center.y()) * (unscale(polylines.front().points.front()).y() - center.y())) < 0.;
1273 for (const Polyline& line : polylines) {
1274 writer.travel(center - (mirror ? 1.f : -1.f) * (unscale(line.points.front()).cast<float>() - center));
1275 for (size_t i=0; i<line.points.size(); ++i)
1276 writer.extrude(center - (mirror ? 1.f : -1.f) * (unscale(line.points[i]).cast<float>() - center));
1277 }
1278 writer.travel(pts[i].first);
1279 }
1280 }
1281 if (++i == int(pts.size()))
1282 i = 0;
1283 }
1284 writer.extrude(pts[start_i].first, feedrate);
1285 return poly;
1286 };
1287
1288 // outer contour (always)
1289 bool infill_cone = first_layer && m_wipe_tower_width > 2*spacing && m_wipe_tower_depth > 2*spacing;
1290 Polygon poly = supported_rectangle(wt_box, feedrate, infill_cone);
1291
1292
1293 // brim (first layer only)
1294 if (first_layer) {
1295 size_t loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing;
1296
1297 for (size_t i = 0; i < loops_num; ++ i) {
1298 poly = offset(poly, scale_(spacing)).front();
1299 int cp = poly.closest_point_index(Point::new_scale(writer.x(), writer.y()));
1300 writer.travel(unscale(poly.points[cp]).cast<float>());
1301 for (int i=cp+1; true; ++i ) {
1302 if (i==int(poly.points.size()))
1303 i = 0;
1304 writer.extrude(unscale(poly.points[i]).cast<float>());
1305 if (i == cp)
1306 break;
1307 }
1308 }
1309
1310 // Save actual brim width to be later passed to the Print object, which will use it
1311 // for skirt calculation and pass it to GLCanvas for precise preview box
1312 m_wipe_tower_brim_width_real = loops_num * spacing;
1313 }
1314
1315 // Now prepare future wipe.
1316 int i = poly.closest_point_index(Point::new_scale(writer.x(), writer.y()));
1317 writer.add_wipe_point(writer.pos());
1318 writer.add_wipe_point(unscale(poly.points[i==0 ? int(poly.points.size())-1 : i-1]).cast<float>());
1319
1320 // Ask our writer about how much material was consumed.
1321 // Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
1322 if (! m_no_sparse_layers || toolchanges_on_layer || first_layer) {
1324 m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
1325 m_current_height += m_layer_info->height;
1326 }
1327
1328 return construct_tcr(writer, false, old_tool);
1329}
EIGEN_DEVICE_FUNC CastXpr< NewType >::Type cast() const
Definition CommonCwiseUnaryOps.h:62
static Fill * new_from_type(const InfillPattern type)
Definition FillBase.cpp:31
ToolChangeResult construct_tcr(WipeTowerWriter &writer, bool priming, size_t old_tool) const
Definition WipeTower.cpp:498
bool m_left_to_right
Definition WipeTower.hpp:313
const float WT_EPSILON
Definition WipeTower.hpp:250
std::vector< float > m_used_filament_length
Definition WipeTower.hpp:373
std::vector< WipeTowerInfo >::iterator m_layer_info
Definition WipeTower.hpp:366
float m_internal_rotation
Definition WipeTower.hpp:265
float m_wipe_tower_height
Definition WipeTower.hpp:260
std::vector< WipeTowerInfo > m_plan
Definition WipeTower.hpp:365
unsigned int m_num_layer_changes
Definition WipeTower.hpp:302
bool is_first_layer() const
Definition WipeTower.hpp:316
bool m_adhesion
Definition WipeTower.hpp:283
float m_current_height
Definition WipeTower.hpp:370
bool m_current_layer_finished
Definition WipeTower.hpp:312
bool layer_finished() const
Definition WipeTower.hpp:214
float m_wipe_tower_brim_width_real
Definition WipeTower.hpp:263
wipe_shape m_current_shape
Definition WipeTower.hpp:307
static std::pair< double, double > get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg)
Definition WipeTower.cpp:1332
float m_wipe_tower_depth
Definition WipeTower.hpp:259
#define scale_(val)
Definition libslic3r.h:69
Type
Definition CustomGCode.hpp:14
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:280
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:289
@ ipMonotonicLines
Definition PrintConfig.hpp:61
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:726
std::vector< Polyline > Polylines
Definition Polyline.hpp:14
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:13
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:421
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:416
BoundingBox get_extents(const ExPolygon &expolygon)
Definition ExPolygon.cpp:352
T unscale(Q v)
Definition libslic3r.h:95
@ stBottom
Definition Surface.hpp:13
void append(std::vector< T, Alloc > &dest, const std::vector< T, Alloc2 > &src)
Definition libslic3r.h:110
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:58
TPoint< P > front(const P &p)
Definition geometry_traits.hpp:872
Slic3r::Polygon Polygon
Definition Emboss.cpp:34
Kernel::Point_2 Point
Definition point_areas.cpp:20

References Slic3r::WipeTowerWriter::add_wipe_point(), Slic3r::WipeTowerWriter::append(), Slic3r::append(), Slic3r::MultiPoint::closest_point_index(), construct_tcr(), Slic3r::ExPolygon::contour, Slic3r::Geometry::deg2rad(), Slic3r::FillParams::density, Slic3r::diff_ex(), EPSILON, Slic3r::WipeTowerWriter::extrude(), Slic3r::WipeTowerWriter::get_and_reset_used_filament_length(), Slic3r::get_extents(), get_wipe_tower_cone_base(), Slic3r::ipMonotonicLines, is_first_layer(), layer_finished(), Slic3r::WipeTower::box_coordinates::ld, Slic3r::WipeTower::box_coordinates::lu, m_adhesion, m_bridging, m_current_height, m_current_layer_finished, m_current_shape, m_current_tool, m_extrusion_flow, m_filpar, m_first_layer_speed, m_gcode_flavor, m_internal_rotation, m_layer_height, m_layer_info, m_left_to_right, m_no_sparse_layers, m_num_layer_changes, m_perimeter_width, M_PI, m_plan, m_used_filament_length, m_wipe_tower_brim_width, m_wipe_tower_brim_width_real, m_wipe_tower_cone_angle, m_wipe_tower_depth, m_wipe_tower_height, m_wipe_tower_width, m_y_shift, m_z_pos, Slic3r::Fill::new_from_type(), Slic3r::offset(), Slic3r::offset_ex(), Slic3r::MultiPoint::points, Slic3r::WipeTowerWriter::pos(), Slic3r::WipeTower::box_coordinates::rd, Slic3r::WipeTowerWriter::rectangle(), Slic3r::WipeTower::box_coordinates::ru, scale_, Slic3r::WipeTowerWriter::set_extrusion_flow(), Slic3r::WipeTowerWriter::set_initial_position(), Slic3r::WipeTowerWriter::set_initial_tool(), Slic3r::WipeTowerWriter::set_y_shift(), Slic3r::WipeTowerWriter::set_z(), SHAPE_REVERSED, Slic3r::stBottom, Slic3r::WipeTowerWriter::travel(), Slic3r::unscale(), WT_EPSILON, Slic3r::WipeTowerWriter::x(), and Slic3r::WipeTowerWriter::y().

Referenced by generate(), and save_on_last_wipe().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ finished()

bool Slic3r::WipeTower::finished ( ) const
inline
193{ return m_max_color_changes == 0; }
size_t m_max_color_changes
Definition WipeTower.hpp:269

References m_max_color_changes.

◆ first_toolchange_to_nonsoluble()

int Slic3r::WipeTower::first_toolchange_to_nonsoluble ( const std::vector< WipeTowerInfo::ToolChange > &  tool_changes) const
private
1468{
1469 for (size_t idx=0; idx<tool_changes.size(); ++idx)
1470 if (! m_filpar[tool_changes[idx].new_tool].is_soluble)
1471 return idx;
1472 return -1;
1473}

References m_filpar.

Referenced by generate(), and save_on_last_wipe().

+ Here is the caller graph for this function:

◆ generate()

void Slic3r::WipeTower::generate ( std::vector< std::vector< ToolChangeResult > > &  result)
1497{
1498 if (m_plan.empty())
1499 return;
1500
1501 plan_tower();
1502 for (int i=0;i<5;++i) {
1504 plan_tower();
1505 }
1506
1507 m_layer_info = m_plan.begin();
1508 m_current_height = 0.f;
1509
1510 // we don't know which extruder to start with - we'll set it according to the first toolchange
1511 for (const auto& layer : m_plan) {
1512 if (!layer.tool_changes.empty()) {
1513 m_current_tool = layer.tool_changes.front().old_tool;
1514 break;
1515 }
1516 }
1517
1518 for (auto& used : m_used_filament_length) // reset used filament stats
1519 used = 0.f;
1520
1521 m_old_temperature = -1; // reset last temperature written in the gcode
1522
1523 std::vector<WipeTower::ToolChangeResult> layer_result;
1524 for (const WipeTower::WipeTowerInfo& layer : m_plan)
1525 {
1526 set_layer(layer.z, layer.height, 0, false/*layer.z == m_plan.front().z*/, layer.z == m_plan.back().z);
1527 m_internal_rotation += 180.f;
1528
1531
1532 int idx = first_toolchange_to_nonsoluble(layer.tool_changes);
1533 ToolChangeResult finish_layer_tcr;
1534
1535 if (idx == -1) {
1536 // if there is no toolchange switching to non-soluble, finish layer
1537 // will be called at the very beginning. That's the last possibility
1538 // where a nonsoluble tool can be.
1539 finish_layer_tcr = finish_layer();
1540 }
1541
1542 for (int i=0; i<int(layer.tool_changes.size()); ++i) {
1543 layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool));
1544 if (i == idx) // finish_layer will be called after this toolchange
1545 finish_layer_tcr = finish_layer();
1546 }
1547
1548 if (layer_result.empty()) {
1549 // there is nothing to merge finish_layer with
1550 layer_result.emplace_back(std::move(finish_layer_tcr));
1551 }
1552 else {
1553 if (idx == -1) {
1554 layer_result[0] = merge_tcr(finish_layer_tcr, layer_result[0]);
1555 layer_result[0].force_travel = true;
1556 }
1557 else
1558 layer_result[idx] = merge_tcr(layer_result[idx], finish_layer_tcr);
1559 }
1560
1561 result.emplace_back(std::move(layer_result));
1562 }
1563}
int m_old_temperature
Definition WipeTower.hpp:270
void plan_tower()
Definition WipeTower.cpp:1409
void set_layer(float print_z, float layer_height, size_t max_tool_changes, bool, bool is_last_layer)
Definition WipeTower.hpp:155
ToolChangeResult finish_layer()
Definition WipeTower.cpp:1090
void save_on_last_wipe()
Definition WipeTower.cpp:1434
ToolChangeResult tool_change(size_t new_tool)
Definition WipeTower.cpp:728
int first_toolchange_to_nonsoluble(const std::vector< WipeTowerInfo::ToolChange > &tool_changes) const
Definition WipeTower.cpp:1466
static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult &first, WipeTower::ToolChangeResult &second)
Definition WipeTower.cpp:1475

References finish_layer(), first_toolchange_to_nonsoluble(), m_current_height, m_current_tool, m_internal_rotation, m_layer_info, m_old_temperature, m_perimeter_width, m_plan, m_used_filament_length, m_wipe_tower_depth, m_y_shift, Slic3r::merge_tcr(), plan_tower(), save_on_last_wipe(), set_layer(), and tool_change().

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_brim_width()

float Slic3r::WipeTower::get_brim_width ( ) const
inline

References m_wipe_tower_brim_width_real.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ get_depth()

float Slic3r::WipeTower::get_depth ( ) const
inline
145{ return m_wipe_tower_depth; }

References m_wipe_tower_depth.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ get_number_of_toolchanges()

int Slic3r::WipeTower::get_number_of_toolchanges ( ) const
inline
219{ return m_num_tool_changes; }
unsigned int m_num_tool_changes
Definition WipeTower.hpp:303

References m_num_tool_changes.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ get_used_filament()

std::vector< float > Slic3r::WipeTower::get_used_filament ( ) const
inline
218{ return m_used_filament_length; }

References m_used_filament_length.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ get_wipe_tower_cone_base()

std::pair< double, double > Slic3r::WipeTower::get_wipe_tower_cone_base ( double  width,
double  height,
double  depth,
double  angle_deg 
)
static
1333{
1334 double R = std::tan(Geometry::deg2rad(angle_deg/2.)) * height;
1335 double fake_width = 0.66 * width;
1336 double diag = std::hypot(fake_width / 2., depth / 2.);
1337 double support_scale = 1.;
1338 if (R > diag) {
1339 double w = fake_width;
1340 double sin = 0.5 * depth / diag;
1341 double tan = depth / w;
1342 double t = (R - diag) * sin;
1343 support_scale = (w / 2. + t / tan + t * tan) / (w / 2.);
1344 }
1345 return std::make_pair(R, support_scale);
1346}
EIGEN_DEVICE_FUNC const TanReturnType tan() const
Definition ArrayCwiseUnaryOps.h:234
EIGEN_DEVICE_FUNC const SinReturnType sin() const
Definition ArrayCwiseUnaryOps.h:220
float width() const
Definition WipeTower.hpp:191
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:540
IGL_INLINE void diag(const Eigen::SparseMatrix< T > &X, Eigen::SparseVector< T > &V)
Definition diag.cpp:17

References Slic3r::Geometry::deg2rad(), sin(), tan(), and width().

Referenced by finish_layer(), Slic3r::Print::first_layer_wipe_tower_corners(), Slic3r::FakeWipeTower::getFakeExtrusionPathsFromWipeTower(), and Slic3r::GLVolumeCollection::load_wipe_tower_preview().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_wipe_tower_height()

float Slic3r::WipeTower::get_wipe_tower_height ( ) const
inline
148{ return m_wipe_tower_height; }

References m_wipe_tower_height.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ get_z_and_depth_pairs()

std::vector< std::pair< float, float > > Slic3r::WipeTower::get_z_and_depth_pairs ( ) const
1568{
1569 std::vector<std::pair<float, float>> out = {{0.f, m_wipe_tower_depth}};
1570 for (const WipeTowerInfo& wti : m_plan) {
1571 assert(wti.depth < wti.depth + WT_EPSILON);
1572 if (wti.depth < out.back().second - WT_EPSILON)
1573 out.emplace_back(wti.z, wti.depth);
1574 }
1575 if (out.back().first < m_wipe_tower_height - WT_EPSILON)
1576 out.emplace_back(m_wipe_tower_height, 0.f);
1577 return out;
1578}

References m_plan, m_wipe_tower_depth, m_wipe_tower_height, and WT_EPSILON.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ is_first_layer()

bool Slic3r::WipeTower::is_first_layer ( ) const
inlineprivate
316{ return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
size_t m_first_layer_idx
Definition WipeTower.hpp:273

References m_first_layer_idx, m_layer_info, and m_plan.

Referenced by finish_layer(), set_layer(), tool_change(), toolchange_Unload(), and toolchange_Wipe().

+ Here is the caller graph for this function:

◆ layer_finished()

bool Slic3r::WipeTower::layer_finished ( ) const
inline
214 {
216 }

References m_current_layer_finished.

Referenced by Slic3r::Print::_make_wipe_tower(), and finish_layer().

+ Here is the caller graph for this function:

◆ make_wipe_tower_square()

void Slic3r::WipeTower::make_wipe_tower_square ( )
private

◆ never_skip_tag()

static const std::string Slic3r::WipeTower::never_skip_tag ( )
inlinestatic
24{ return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; }

Referenced by Slic3r::WipeTowerIntegration::post_process_wipe_tower_moves(), and toolchange_Change().

+ Here is the caller graph for this function:

◆ plan_toolchange()

void Slic3r::WipeTower::plan_toolchange ( float  z_par,
float  layer_height_par,
unsigned int  old_tool,
unsigned int  new_tool,
float  wipe_volume = 0.f 
)
1376{
1377 assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON); // refuses to add a layer below the last one
1378
1379 if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
1380 m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
1381
1382 if (m_first_layer_idx == size_t(-1) && (! m_no_sparse_layers || old_tool != new_tool || m_plan.size() == 1))
1383 m_first_layer_idx = m_plan.size() - 1;
1384
1385 if (old_tool == new_tool) // new layer without toolchanges - we are done
1386 return;
1387
1388 // this is an actual toolchange - let's calculate depth to reserve on the wipe tower
1389 float depth = 0.f;
1391 float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
1392 m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
1393 layer_height_par);
1394 depth = (int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator);
1395 float ramming_depth = depth;
1396 length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
1397 float first_wipe_line = -length_to_extrude;
1398 length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
1399 length_to_extrude = std::max(length_to_extrude,0.f);
1400
1401 depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
1402 depth *= m_extra_spacing;
1403
1404 m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
1405}
float volume_to_length(float volume, float line_width, float layer_height) const
Definition WipeTower.hpp:327

References m_extra_spacing, m_filpar, m_first_layer_idx, m_no_sparse_layers, m_perimeter_width, m_plan, m_wipe_tower_width, volume_to_length(), width(), and WT_EPSILON.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ plan_tower()

void Slic3r::WipeTower::plan_tower ( )
private
1410{
1411 // Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
1412 m_wipe_tower_depth = 0.f;
1413 for (auto& layer : m_plan)
1414 layer.depth = 0.f;
1415 m_wipe_tower_height = m_plan.empty() ? 0.f : m_plan.back().z;
1416 m_current_height = 0.f;
1417
1418 for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index)
1419 {
1420 float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
1421 m_plan[layer_index].depth = this_layer_depth;
1422
1423 if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
1424 m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
1425
1426 for (int i = layer_index - 1; i >= 0 ; i--)
1427 {
1428 if (m_plan[i].depth - this_layer_depth < 2*m_perimeter_width )
1429 m_plan[i].depth = this_layer_depth;
1430 }
1431 }
1432}

References m_current_height, m_perimeter_width, m_plan, m_wipe_tower_depth, and m_wipe_tower_height.

Referenced by generate().

+ Here is the caller graph for this function:

◆ position()

const Vec2f & Slic3r::WipeTower::position ( ) const
inline
189{ return m_wipe_tower_pos; }

References m_wipe_tower_pos.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the caller graph for this function:

◆ prime()

std::vector< WipeTower::ToolChangeResult > Slic3r::WipeTower::prime ( float  first_layer_height,
const std::vector< unsigned int > &  tools,
bool  last_wipe_inside_wipe_tower 
)
635{
636 this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
637 m_current_tool = tools.front();
638
639 // The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
640 // Due to the XYZ calibration, this working space may shrink slightly from all directions,
641 // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
642// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
643
644 float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f);
645 box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
646 if (m_bed_shape == CircularBed) {
647 cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f);
648 float total_width_half = tools.size() * prime_section_width / 2.f;
649 cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f))));
650 }
651 else
652 cleaning_box.translate(m_bed_bottom_left);
653
654 std::vector<ToolChangeResult> results;
655
656 // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector.
657 for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
658 size_t old_tool = m_current_tool;
659
660 WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
661 writer.set_extrusion_flow(m_extrusion_flow)
662 .set_z(m_z_pos)
663 .set_initial_tool(m_current_tool);
664
665 // This is the first toolchange - initiate priming
666 if (idx_tool == 0) {
667 writer.append(";--------------------\n"
668 "; CP PRIMING START\n")
669 .append(";--------------------\n")
670 .speed_override_backup()
671 .speed_override(100)
672 .set_initial_position(Vec2f::Zero()) // Always move to the starting position
673 .travel(cleaning_box.ld, 7200);
675 writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
676 }
677 else
678 writer.set_initial_position(results.back().end_pos);
679
680
681 unsigned int tool = tools[idx_tool];
682 m_left_to_right = true;
683 toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
684 toolchange_Load(writer, cleaning_box); // Prime the tool.
685 if (idx_tool + 1 == tools.size()) {
686 // Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
687 toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool]);
688 } else {
689 // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
690 //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
691 toolchange_Wipe(writer, cleaning_box , 20.f);
692 box_coordinates box = cleaning_box;
693 box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width);
694 toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
695 cleaning_box.translate(prime_section_width, 0.f);
696 writer.travel(cleaning_box.ld, 7200);
697 }
699
700
701 // Ask our writer about how much material was consumed:
703 m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
704
705 // This is the last priming toolchange - finish priming
706 if (idx_tool+1 == tools.size()) {
707 // Reset the extruder current to a normal value.
709 writer.set_extruder_trimpot(550);
710 writer.speed_override_restore()
711 .feedrate(m_travel_speed * 60.f)
712 .flush_planner_queue()
713 .reset_extruder()
714 .append("; CP PRIMING END\n"
715 ";------------------\n"
716 "\n\n");
717 }
718
719 results.emplace_back(construct_tcr(writer, true, old_tool));
720 }
721
722 m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
723 // in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
724
725 return results;
726}
void toolchange_Change(WipeTowerWriter &writer, const size_t new_tool, const std::string &new_material)
Definition WipeTower.cpp:957
void toolchange_Load(WipeTowerWriter &writer, const box_coordinates &cleaning_box)
Definition WipeTower.cpp:990
void toolchange_Wipe(WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_volume)
Definition WipeTower.cpp:1019
void toolchange_Unload(WipeTowerWriter &writer, const box_coordinates &cleaning_box, const std::string &current_material, const int new_temperature)
Definition WipeTower.cpp:811

References Slic3r::WipeTowerWriter::append(), CircularBed, construct_tcr(), Slic3r::WipeTowerWriter::feedrate(), Slic3r::WipeTowerWriter::flush_planner_queue(), Slic3r::WipeTowerWriter::get_and_reset_used_filament_length(), Slic3r::WipeTower::box_coordinates::ld, m_bed_bottom_left, m_bed_shape, m_bed_width, m_current_tool, m_extrusion_flow, m_filpar, m_gcode_flavor, m_layer_height, m_left_to_right, m_num_tool_changes, m_old_temperature, m_perimeter_width, m_set_extruder_trimpot, m_travel_speed, m_used_filament_length, m_z_pos, Slic3r::WipeTowerWriter::reset_extruder(), Slic3r::WipeTowerWriter::set_extruder_trimpot(), Slic3r::WipeTowerWriter::set_extrusion_flow(), Slic3r::WipeTowerWriter::set_initial_position(), Slic3r::WipeTowerWriter::set_initial_tool(), set_layer(), Slic3r::WipeTowerWriter::set_z(), Slic3r::WipeTowerWriter::speed_override(), Slic3r::WipeTowerWriter::speed_override_backup(), Slic3r::WipeTowerWriter::speed_override_restore(), toolchange_Change(), toolchange_Load(), toolchange_Unload(), toolchange_Wipe(), Slic3r::WipeTower::box_coordinates::translate(), Slic3r::WipeTowerWriter::travel(), wipe_volumes, and Slic3r::WipeTowerWriter::y().

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ save_on_last_wipe()

void Slic3r::WipeTower::save_on_last_wipe ( )
private
1435{
1436 for (m_layer_info=m_plan.begin();m_layer_info<m_plan.end();++m_layer_info) {
1437 set_layer(m_layer_info->z, m_layer_info->height, 0, m_layer_info->z == m_plan.front().z, m_layer_info->z == m_plan.back().z);
1438 if (m_layer_info->tool_changes.size()==0) // we have no way to save anything on an empty layer
1439 continue;
1440
1441 // Which toolchange will finish_layer extrusions be subtracted from?
1442 int idx = first_toolchange_to_nonsoluble(m_layer_info->tool_changes);
1443
1444 for (int i=0; i<int(m_layer_info->tool_changes.size()); ++i) {
1445 auto& toolchange = m_layer_info->tool_changes[i];
1446 tool_change(toolchange.new_tool);
1447
1448 if (i == idx) {
1449 float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
1450 float length_to_save = finish_layer().total_extrusion_length_in_plane();
1451 float length_to_wipe = volume_to_length(toolchange.wipe_volume,
1452 m_perimeter_width, m_layer_info->height) - toolchange.first_wipe_line - length_to_save;
1453
1454 length_to_wipe = std::max(length_to_wipe,0.f);
1455 float depth_to_wipe = m_perimeter_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
1456
1457 toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
1458 }
1459 }
1460 }
1461}
float total_extrusion_length_in_plane()
Definition WipeTower.hpp:76

References finish_layer(), first_toolchange_to_nonsoluble(), m_extra_spacing, m_layer_info, m_perimeter_width, m_plan, m_wipe_tower_width, set_layer(), tool_change(), Slic3r::WipeTower::ToolChangeResult::total_extrusion_length_in_plane(), volume_to_length(), and width().

Referenced by generate().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_extruder()

void Slic3r::WipeTower::set_extruder ( size_t  idx,
const PrintConfig &  config 
)
579{
580 //while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
581 m_filpar.push_back(FilamentParameters());
582
583 m_filpar[idx].material = config.filament_type.get_at(idx);
584 m_filpar[idx].is_soluble = config.wipe_tower_extruder == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_extruder - 1));
585 m_filpar[idx].temperature = config.temperature.get_at(idx);
586 m_filpar[idx].first_layer_temperature = config.first_layer_temperature.get_at(idx);
587
588 // If this is a single extruder MM printer, we will use all the SE-specific config values.
589 // Otherwise, the defaults will be used to turn off the SE stuff.
590 if (m_semm) {
591 m_filpar[idx].loading_speed = float(config.filament_loading_speed.get_at(idx));
592 m_filpar[idx].loading_speed_start = float(config.filament_loading_speed_start.get_at(idx));
593 m_filpar[idx].unloading_speed = float(config.filament_unloading_speed.get_at(idx));
594 m_filpar[idx].unloading_speed_start = float(config.filament_unloading_speed_start.get_at(idx));
595 m_filpar[idx].delay = float(config.filament_toolchange_delay.get_at(idx));
596 m_filpar[idx].cooling_moves = config.filament_cooling_moves.get_at(idx);
597 m_filpar[idx].cooling_initial_speed = float(config.filament_cooling_initial_speed.get_at(idx));
598 m_filpar[idx].cooling_final_speed = float(config.filament_cooling_final_speed.get_at(idx));
599 }
600
601 m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point
602 float nozzle_diameter = float(config.nozzle_diameter.get_at(idx));
603 m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
604
605 float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx));
606 if (max_vol_speed!= 0.f)
607 m_filpar[idx].max_e_speed = (max_vol_speed / filament_area());
608
609 m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
610
611 if (m_semm) {
612 std::istringstream stream{config.filament_ramming_parameters.get_at(idx)};
613 float speed = 0.f;
614 stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
615 m_filpar[idx].ramming_line_width_multiplicator /= 100;
616 m_filpar[idx].ramming_step_multiplicator /= 100;
617 while (stream >> speed)
618 m_filpar[idx].ramming_speed.push_back(speed);
619 }
620
621 m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
622}
const float Width_To_Nozzle_Ratio
Definition WipeTower.hpp:249

References filament_area(), m_filpar, m_perimeter_width, M_PI, m_semm, m_used_filament_length, and Width_To_Nozzle_Ratio.

Referenced by Slic3r::Print::_make_wipe_tower().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_layer()

void Slic3r::WipeTower::set_layer ( float  print_z,
float  layer_height,
size_t  max_tool_changes,
bool  ,
bool  is_last_layer 
)
inline
166 {
167 m_z_pos = print_z;
169 m_depth_traversed = 0.f;
171
172
173 // Advance m_layer_info iterator, making sure we got it right
174 while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
175 ++m_layer_info;
176
178 if (this->is_first_layer()) {
181 } else
183
184 // Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
186 }
float m_depth_traversed
Definition WipeTower.hpp:311
float extrusion_flow(float layer_height=-1.f) const
Definition WipeTower.hpp:319

References extrusion_flow(), is_first_layer(), Slic3r::layer_height(), m_current_layer_finished, m_current_shape, m_depth_traversed, m_extrusion_flow, m_layer_height, m_layer_info, m_num_layer_changes, m_num_tool_changes, m_plan, m_z_pos, SHAPE_NORMAL, SHAPE_REVERSED, and WT_EPSILON.

Referenced by Slic3r::Print::_make_wipe_tower(), generate(), prime(), and save_on_last_wipe().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tool_change()

WipeTower::ToolChangeResult Slic3r::WipeTower::tool_change ( size_t  new_tool)
729{
730 size_t old_tool = m_current_tool;
731
732 float wipe_area = 0.f;
733 float wipe_volume = 0.f;
734
735 // Finds this toolchange info
736 if (tool != (unsigned int)(-1))
737 {
738 for (const auto &b : m_layer_info->tool_changes)
739 if ( b.new_tool == tool ) {
740 wipe_volume = b.wipe_volume;
741 wipe_area = b.required_depth * m_layer_info->extra_spacing;
742 break;
743 }
744 }
745 else {
746 // Otherwise we are going to Unload only. And m_layer_info would be invalid.
747 }
748
749 box_coordinates cleaning_box(
752 (tool != (unsigned int)(-1) ? wipe_area+m_depth_traversed-0.5f*m_perimeter_width
754
755 WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
756 writer.set_extrusion_flow(m_extrusion_flow)
757 .set_z(m_z_pos)
758 .set_initial_tool(m_current_tool)
759 .set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
760 .append(";--------------------\n"
761 "; CP TOOLCHANGE START\n")
762 .comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based
763
764 if (tool != (unsigned)(-1))
765 writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str())
766 .append(";--------------------\n");
767
768 writer.speed_override_backup();
769 writer.speed_override(100);
770
771 Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
772 writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
773
774 // Increase the extruder driver current to allow fast ramming.
776 writer.set_extruder_trimpot(750);
777
778 // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
779 if (tool != (unsigned int)-1){ // This is not the last change.
780 toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
781 is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
782 toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
783 toolchange_Load(writer, cleaning_box);
784 writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
785 toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
787 } else
788 toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
789
790 m_depth_traversed += wipe_area;
791
793 writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value.
794 writer.speed_override_restore();
795 writer.feedrate(m_travel_speed * 60.f)
796 .flush_planner_queue()
797 .reset_extruder()
798 .append("; CP TOOLCHANGE END\n"
799 ";------------------\n"
800 "\n\n");
801
802 // Ask our writer about how much material was consumed:
804 m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
805
806 return construct_tcr(writer, false, old_tool);
807}

References Slic3r::WipeTowerWriter::append(), Slic3r::append(), Slic3r::WipeTowerWriter::comment_with_value(), construct_tcr(), Slic3r::WipeTowerWriter::feedrate(), Slic3r::WipeTowerWriter::flush_planner_queue(), Slic3r::WipeTowerWriter::get_and_reset_used_filament_length(), is_first_layer(), Slic3r::WipeTower::box_coordinates::ld, m_current_shape, m_current_tool, m_depth_traversed, m_extrusion_flow, m_filpar, m_gcode_flavor, m_internal_rotation, m_layer_height, m_layer_info, m_num_tool_changes, m_perimeter_width, m_set_extruder_trimpot, m_travel_speed, m_used_filament_length, m_wipe_tower_depth, m_wipe_tower_width, m_y_shift, m_z_pos, Slic3r::WipeTowerWriter::reset_extruder(), Slic3r::WipeTowerWriter::set_extruder_trimpot(), Slic3r::WipeTowerWriter::set_extrusion_flow(), Slic3r::WipeTowerWriter::set_initial_position(), Slic3r::WipeTowerWriter::set_initial_tool(), Slic3r::WipeTowerWriter::set_y_shift(), Slic3r::WipeTowerWriter::set_z(), SHAPE_REVERSED, Slic3r::WipeTowerWriter::speed_override(), Slic3r::WipeTowerWriter::speed_override_backup(), Slic3r::WipeTowerWriter::speed_override_restore(), toolchange_Change(), toolchange_Load(), toolchange_Unload(), toolchange_Wipe(), Slic3r::WipeTowerWriter::travel(), Slic3r::WipeTowerWriter::x(), and Slic3r::WipeTowerWriter::y().

Referenced by Slic3r::Print::_make_wipe_tower(), generate(), and save_on_last_wipe().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ toolchange_Change()

void Slic3r::WipeTower::toolchange_Change ( WipeTowerWriter writer,
const size_t  new_tool,
const std::string &  new_material 
)
private
961{
962 // Ask the writer about how much of the old filament we consumed:
964 m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
965
966 // This is where we want to place the custom gcodes. We will use placeholders for this.
967 // These will be substituted by the actual gcodes when the gcode is generated.
968 //writer.append("[end_filament_gcode]\n");
969 writer.append("[toolchange_gcode]\n");
970
971 // Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc)
972 // gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the
973 // postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before.
974 Vec2f current_pos = writer.pos_rotated();
975 writer.feedrate(m_travel_speed * 60.f) // see https://github.com/prusa3d/PrusaSlicer/issues/5483
976 .append(std::string("G1 X") + Slic3r::float_to_string_decimal_point(current_pos.x())
977 + " Y" + Slic3r::float_to_string_decimal_point(current_pos.y())
978 + never_skip_tag() + "\n");
979 writer.append("[deretraction_from_wipe_tower_generator]");
980
981 // The toolchange Tn command will be inserted later, only in case that the user does
982 // not provide a custom toolchange gcode.
983 writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed.
984 //writer.append("[start_filament_gcode]\n");
985
986 writer.flush_planner_queue();
987 m_current_tool = new_tool;
988}
static const std::string never_skip_tag()
Definition WipeTower.hpp:24
std::string float_to_string_decimal_point(double value, int precision)
Definition LocalesUtils.cpp:74

References Slic3r::WipeTowerWriter::append(), Slic3r::WipeTowerWriter::feedrate(), Slic3r::float_to_string_decimal_point(), Slic3r::WipeTowerWriter::flush_planner_queue(), Slic3r::WipeTowerWriter::get_and_reset_used_filament_length(), m_current_tool, m_travel_speed, m_used_filament_length, never_skip_tag(), Slic3r::WipeTowerWriter::pos_rotated(), and Slic3r::WipeTowerWriter::set_tool().

Referenced by prime(), and tool_change().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ toolchange_Load()

void Slic3r::WipeTower::toolchange_Load ( WipeTowerWriter writer,
const box_coordinates cleaning_box 
)
private
993{
995 float xl = cleaning_box.ld.x() + m_perimeter_width * 0.75f;
996 float xr = cleaning_box.rd.x() - m_perimeter_width * 0.75f;
997 float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
998
999 // Load the filament while moving left / right, so the excess material will not create a blob at a single position.
1000 float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
1002
1003 writer.append("; CP TOOLCHANGE LOAD\n")
1004 .suppress_preview()
1005 .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
1006 .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
1007 .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
1008
1009 .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
1010 .resume_preview();
1011
1012 // Reset the extruder current to the normal value.
1014 writer.set_extruder_trimpot(550);
1015 }
1016}

References Slic3r::WipeTowerWriter::append(), Slic3r::WipeTower::box_coordinates::ld, Slic3r::WipeTowerWriter::load(), Slic3r::WipeTowerWriter::load_move_x_advanced(), m_current_tool, m_extra_loading_move, m_filpar, m_parking_pos_retraction, m_perimeter_width, m_semm, m_set_extruder_trimpot, Slic3r::WipeTower::box_coordinates::rd, Slic3r::WipeTowerWriter::set_extruder_trimpot(), Slic3r::WipeTowerWriter::suppress_preview(), Slic3r::WipeTowerWriter::travel(), Slic3r::WipeTowerWriter::x(), and Slic3r::WipeTowerWriter::y().

Referenced by prime(), and tool_change().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ toolchange_Unload()

void Slic3r::WipeTower::toolchange_Unload ( WipeTowerWriter writer,
const box_coordinates cleaning_box,
const std::string &  current_material,
const int  new_temperature 
)
private
816{
817 float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width;
818 float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width;
819
820 const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
821 const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
822
823 const Vec2f ramming_start_pos = Vec2f(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f);
824
825 writer.append("; CP TOOLCHANGE UNLOAD\n")
826 .change_analyzer_line_width(line_width);
827
828 unsigned i = 0; // iterates through ramming_speed
829 m_left_to_right = true; // current direction of ramming
830 float remaining = xr - xl ; // keeps track of distance to the next turnaround
831 float e_done = 0; // measures E move done from each segment
832
833 if (m_semm)
834 writer.travel(ramming_start_pos); // move to starting position
835 else
836 writer.set_position(ramming_start_pos);
837 // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
838 if (m_semm && (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion ))) {
839
840 // this is y of the center of previous sparse infill border
841 float sparse_beginning_y = 0.f;
843 sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
844 - ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
845 else
846 sparse_beginning_y += (m_layer_info-1)->toolchanges_depth() + m_perimeter_width;
847
848 float sum_of_depths = 0.f;
849 for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
850 if (tch.old_tool == m_current_tool) {
851 sum_of_depths += tch.ramming_depth;
852 float ramming_end_y = sum_of_depths;
853 ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
854
855 if ( (m_current_shape == SHAPE_REVERSED && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width ) ||
856 (m_current_shape == SHAPE_NORMAL && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width ) )
857 {
858 writer.extrude(xl + tch.first_wipe_line-1.f*m_perimeter_width,writer.y());
859 remaining -= tch.first_wipe_line-1.f*m_perimeter_width;
860 }
861 break;
862 }
863 sum_of_depths += tch.required_depth;
864 }
865 }
866
867 writer.disable_linear_advance();
868
869 // now the ramming itself:
870 while (m_semm && i < m_filpar[m_current_tool].ramming_speed.size())
871 {
872 const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height);
873 const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / filament_area(); // transform volume per sec to E move;
874 const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround
875 const float actual_time = dist/x * 0.25f;
876 writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0.f, 0.f, e * (dist / x), dist / (actual_time / 60.f));
877 remaining -= dist;
878
879 if (remaining < WT_EPSILON) { // we reached a turning point
880 writer.travel(writer.x(), writer.y() + y_step, 7200);
882 remaining = xr - xl;
883 }
884 e_done += dist; // subtract what was actually done
885 if (e_done > x - WT_EPSILON) { // current segment finished
886 ++i;
887 e_done = 0;
888 }
889 }
890 Vec2f end_of_ramming(writer.x(),writer.y());
891 writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
892
893 // Retraction:
894 float old_x = writer.x();
895 float turning_point = (!m_left_to_right ? xl : xr );
897 float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
898 writer.suppress_preview()
899 .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
900 .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
901 .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
902 .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
903 .resume_preview();
904 }
905 // Wipe tower should only change temperature with single extruder MM. Otherwise, all temperatures should
906 // be already set and there is no need to change anything. Also, the temperature could be changed
907 // for wrong extruder.
908 if (m_semm) {
909 if (new_temperature != 0 && (new_temperature != m_old_temperature || is_first_layer()) ) { // Set the extruder temperature, but don't wait.
910 // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
911 // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
912 writer.set_extruder_temp(new_temperature, false);
913 m_old_temperature = new_temperature;
914 }
915 }
916
917 // Cooling:
918 const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
919 if (m_semm && number_of_moves > 0) {
920 const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
921 const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
922
923 float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
924
925 writer.suppress_preview()
926 .travel(writer.x(), writer.y() + y_step);
927 old_x = writer.x();
928 turning_point = xr-old_x > old_x-xl ? xr : xl;
929 for (int i=0; i<number_of_moves; ++i) {
930 float speed = initial_speed + speed_inc * 2*i;
931 writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
932 speed += speed_inc;
933 writer.load_move_x_advanced(old_x, -m_cooling_tube_length, speed);
934 }
935 }
936
937 if (m_semm) {
938 // let's wait is necessary:
939 writer.wait(m_filpar[m_current_tool].delay);
940 // we should be at the beginning of the cooling tube again - let's move to parking position:
942 }
943
944 // this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
945 // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
946 Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width);
947 if (m_semm)
948 writer.travel(pos, 2400.f);
949 else
950 writer.set_position(pos);
951
952 writer.resume_preview()
953 .flush_planner_queue();
954}
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:14

References Slic3r::WipeTowerWriter::append(), Slic3r::WipeTowerWriter::change_analyzer_line_width(), Slic3r::WipeTowerWriter::disable_linear_advance(), Slic3r::WipeTowerWriter::extrude(), filament_area(), Slic3r::WipeTowerWriter::flush_planner_queue(), is_first_layer(), Slic3r::WipeTower::box_coordinates::ld, Slic3r::WipeTowerWriter::load_move_x_advanced(), m_adhesion, m_cooling_tube_length, m_cooling_tube_retraction, m_current_shape, m_current_tool, m_depth_traversed, m_extra_spacing, m_filpar, m_layer_height, m_layer_info, m_left_to_right, m_old_temperature, m_parking_pos_retraction, m_perimeter_width, m_plan, m_semm, Slic3r::WipeTowerWriter::ram(), Slic3r::WipeTower::box_coordinates::rd, Slic3r::WipeTowerWriter::resume_preview(), Slic3r::WipeTowerWriter::retract(), Slic3r::WipeTowerWriter::set_extruder_temp(), Slic3r::WipeTowerWriter::set_position(), SHAPE_NORMAL, SHAPE_REVERSED, Slic3r::WipeTowerWriter::suppress_preview(), Slic3r::WipeTowerWriter::travel(), volume_to_length(), Slic3r::WipeTowerWriter::wait(), WT_EPSILON, Slic3r::WipeTowerWriter::x(), and Slic3r::WipeTowerWriter::y().

Referenced by prime(), and tool_change().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ toolchange_Wipe()

void Slic3r::WipeTower::toolchange_Wipe ( WipeTowerWriter writer,
const box_coordinates cleaning_box,
float  wipe_volume 
)
private
1023{
1024 // Increase flow on first layer, slow down print.
1025 writer.set_extrusion_flow(m_extrusion_flow * (is_first_layer() ? 1.18f : 1.f))
1026 .append("; CP TOOLCHANGE WIPE\n");
1027 const float& xl = cleaning_box.ld.x();
1028 const float& xr = cleaning_box.rd.x();
1029
1030 // Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
1031 // the ordered volume, even if it means violating the box. This can later be removed and simply
1032 // wipe until the end of the assigned area.
1033
1034 float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height);
1036
1037 const float target_speed = is_first_layer() ? m_first_layer_speed * 60.f : 4800.f;
1038 float wipe_speed = 0.33f * target_speed;
1039
1040 // if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
1041 if ((m_left_to_right ? xr-writer.x() : writer.x()-xl) < 2.5f*m_perimeter_width) {
1042 writer.travel((m_left_to_right ? xr-m_perimeter_width : xl+m_perimeter_width),writer.y()+dy);
1044 }
1045
1046 // now the wiping itself:
1047 for (int i = 0; true; ++i) {
1048 if (i!=0) {
1049 if (wipe_speed < 0.34f * target_speed) wipe_speed = 0.375f * target_speed;
1050 else if (wipe_speed < 0.377 * target_speed) wipe_speed = 0.458f * target_speed;
1051 else if (wipe_speed < 0.46f * target_speed) wipe_speed = 0.875f * target_speed;
1052 else wipe_speed = std::min(target_speed, wipe_speed + 50.f);
1053 }
1054
1055 float traversed_x = writer.x();
1056 if (m_left_to_right)
1057 writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed);
1058 else
1059 writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed);
1060
1061 if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*m_perimeter_width)
1062 break; // in case next line would not fit
1063
1064 traversed_x -= writer.x();
1065 x_to_wipe -= std::abs(traversed_x);
1066 if (x_to_wipe < WT_EPSILON) {
1067 writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200);
1068 break;
1069 }
1070 // stepping to the next line:
1071 writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5f*m_perimeter_width, writer.y() + dy);
1073 }
1074
1075 // We may be going back to the model - wipe the nozzle. If this is followed
1076 // by finish_layer, this wipe path will be overwritten.
1077 writer.add_wipe_point(writer.x(), writer.y())
1078 .add_wipe_point(writer.x(), writer.y() - dy)
1079 .add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy);
1080
1081 if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool)
1083
1084 writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
1085}

References Slic3r::WipeTowerWriter::add_wipe_point(), Slic3r::WipeTowerWriter::append(), EPSILON, Slic3r::WipeTowerWriter::extrude(), is_first_layer(), Slic3r::WipeTower::box_coordinates::ld, Slic3r::WipeTower::box_coordinates::lu, m_current_tool, m_extra_spacing, m_extrusion_flow, m_first_layer_speed, m_layer_height, m_layer_info, m_left_to_right, m_perimeter_width, m_plan, m_wipe_tower_width, Slic3r::WipeTower::box_coordinates::rd, Slic3r::WipeTowerWriter::set_extrusion_flow(), Slic3r::WipeTowerWriter::travel(), volume_to_length(), WT_EPSILON, Slic3r::WipeTowerWriter::x(), and Slic3r::WipeTowerWriter::y().

Referenced by prime(), and tool_change().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ volume_to_length()

float Slic3r::WipeTower::volume_to_length ( float  volume,
float  line_width,
float  layer_height 
) const
inlineprivate
327 {
328 return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
329 }

References Slic3r::layer_height(), and M_PI.

Referenced by plan_toolchange(), save_on_last_wipe(), toolchange_Unload(), and toolchange_Wipe().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ width()

float Slic3r::WipeTower::width ( ) const
inline
191{ return m_wipe_tower_width; }

References m_wipe_tower_width.

Referenced by Slic3r::Print::_make_wipe_tower(), get_wipe_tower_cone_base(), plan_toolchange(), and save_on_last_wipe().

+ Here is the caller graph for this function:

Member Data Documentation

◆ m_adhesion

bool Slic3r::WipeTower::m_adhesion = true
private

Referenced by finish_layer(), and toolchange_Unload().

◆ m_bed_bottom_left

Vec2f Slic3r::WipeTower::m_bed_bottom_left
private

Referenced by WipeTower(), and prime().

◆ []

enum { ... } Slic3r::WipeTower::m_bed_shape

Referenced by WipeTower(), and prime().

◆ m_bed_width

float Slic3r::WipeTower::m_bed_width
private

Referenced by WipeTower(), and prime().

◆ m_bridging

float Slic3r::WipeTower::m_bridging = 0.f
private

Referenced by finish_layer().

◆ m_cooling_tube_length

float Slic3r::WipeTower::m_cooling_tube_length = 0.f
private

Referenced by WipeTower(), and toolchange_Unload().

◆ m_cooling_tube_retraction

float Slic3r::WipeTower::m_cooling_tube_retraction = 0.f
private

Referenced by WipeTower(), and toolchange_Unload().

◆ m_current_height

float Slic3r::WipeTower::m_current_height = 0.f
private

Referenced by finish_layer(), generate(), and plan_tower().

◆ m_current_layer_finished

bool Slic3r::WipeTower::m_current_layer_finished = false
private

◆ m_current_shape

wipe_shape Slic3r::WipeTower::m_current_shape = SHAPE_NORMAL
private

◆ m_current_tool

size_t Slic3r::WipeTower::m_current_tool = 0
private

◆ m_depth_traversed

float Slic3r::WipeTower::m_depth_traversed = 0.f
private

◆ m_extra_loading_move

float Slic3r::WipeTower::m_extra_loading_move = 0.f
private

Referenced by WipeTower(), and toolchange_Load().

◆ m_extra_spacing

float Slic3r::WipeTower::m_extra_spacing = 1.f
private

◆ m_extrusion_flow

float Slic3r::WipeTower::m_extrusion_flow = 0.038f
private

◆ m_filpar

◆ m_first_layer_idx

size_t Slic3r::WipeTower::m_first_layer_idx = size_t(-1)
private

Referenced by is_first_layer(), and plan_toolchange().

◆ m_first_layer_speed

float Slic3r::WipeTower::m_first_layer_speed = 0.f
private

◆ m_gcode_flavor

GCodeFlavor Slic3r::WipeTower::m_gcode_flavor
private

Referenced by finish_layer(), prime(), and tool_change().

◆ m_internal_rotation

float Slic3r::WipeTower::m_internal_rotation = 0.f
private

Referenced by finish_layer(), generate(), and tool_change().

◆ m_layer_height

float Slic3r::WipeTower::m_layer_height = 0.f
private

◆ m_layer_info

std::vector<WipeTowerInfo>::iterator Slic3r::WipeTower::m_layer_info = m_plan.end()
private

◆ m_left_to_right

bool Slic3r::WipeTower::m_left_to_right = true
private

◆ m_max_color_changes

size_t Slic3r::WipeTower::m_max_color_changes = 0
private

Referenced by finished().

◆ m_no_sparse_layers

bool Slic3r::WipeTower::m_no_sparse_layers = false
private

Referenced by finish_layer(), and plan_toolchange().

◆ m_num_layer_changes

unsigned int Slic3r::WipeTower::m_num_layer_changes = 0
private

Referenced by finish_layer(), and set_layer().

◆ m_num_tool_changes

unsigned int Slic3r::WipeTower::m_num_tool_changes = 0
private

◆ m_old_temperature

int Slic3r::WipeTower::m_old_temperature = -1
private

Referenced by generate(), prime(), and toolchange_Unload().

◆ m_parking_pos_retraction

float Slic3r::WipeTower::m_parking_pos_retraction = 0.f
private

◆ m_perimeter_width

◆ m_plan

◆ m_print_brim

bool Slic3r::WipeTower::m_print_brim = true
private

unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.

◆ m_semm

bool Slic3r::WipeTower::m_semm = true
private

◆ m_set_extruder_trimpot

bool Slic3r::WipeTower::m_set_extruder_trimpot = false
private

◆ m_travel_speed

float Slic3r::WipeTower::m_travel_speed = 0.f
private

◆ m_used_filament_length

std::vector<float> Slic3r::WipeTower::m_used_filament_length
private

◆ m_wipe_tower_brim_width

float Slic3r::WipeTower::m_wipe_tower_brim_width = 0.f
private

Referenced by finish_layer().

◆ m_wipe_tower_brim_width_real

float Slic3r::WipeTower::m_wipe_tower_brim_width_real = 0.f
private

Referenced by finish_layer(), and get_brim_width().

◆ m_wipe_tower_cone_angle

float Slic3r::WipeTower::m_wipe_tower_cone_angle = 0.f
private

Referenced by finish_layer().

◆ m_wipe_tower_depth

float Slic3r::WipeTower::m_wipe_tower_depth = 0.f
private

◆ m_wipe_tower_height

float Slic3r::WipeTower::m_wipe_tower_height = 0.f
private

◆ m_wipe_tower_pos

Vec2f Slic3r::WipeTower::m_wipe_tower_pos
private

Referenced by position().

◆ m_wipe_tower_rotation_angle

float Slic3r::WipeTower::m_wipe_tower_rotation_angle = 0.f
private

◆ m_wipe_tower_width

float Slic3r::WipeTower::m_wipe_tower_width
private

◆ m_y_shift

float Slic3r::WipeTower::m_y_shift = 0.f
private

Referenced by finish_layer(), generate(), and tool_change().

◆ m_z_pos

float Slic3r::WipeTower::m_z_pos = 0.f
private

◆ Width_To_Nozzle_Ratio

const float Slic3r::WipeTower::Width_To_Nozzle_Ratio = 1.25f
private

Referenced by set_extruder().

◆ wipe_volumes

const std::vector<std::vector<float> > Slic3r::WipeTower::wipe_volumes
private

Referenced by extract_wipe_volumes(), and prime().

◆ WT_EPSILON

const float Slic3r::WipeTower::WT_EPSILON = 1e-3f
private

The documentation for this class was generated from the following files: