Prusa Multi Material: Improved path planning when jumping to the wipe tower.

This commit is contained in:
bubnikv 2017-05-18 16:53:19 +02:00
parent e75d851bc4
commit 81701b400c
7 changed files with 348 additions and 264 deletions

View file

@ -140,6 +140,90 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
return gcode; return gcode;
} }
WipeTowerIntegration::WipeTowerIntegration(const PrintConfig &print_config)
{
// Initialize the wipe tower.
auto *wipe_tower = new WipeTowerPrusaMM(
float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value),
float(print_config.wipe_tower_width.value), float(print_config.wipe_tower_per_color_wipe.value));
//wipe_tower->set_retract();
//wipe_tower->set_zhop();
//wipe_tower->set_zhop();
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < 4; ++ i)
wipe_tower->set_extruder(
i,
WipeTowerPrusaMM::parse_material(print_config.filament_type.get_at(i).c_str()),
print_config.temperature.get_at(i),
print_config.first_layer_temperature.get_at(i));
m_impl.reset(wipe_tower);
}
std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
{
bool over_wipe_tower = false;
std::string gcode;
if (gcodegen.writer().need_toolchange(extruder_id)) {
// Move over the wipe tower.
gcode += this->travel_to(gcodegen, m_impl->tool_change(extruder_id, WipeTower::PURPOSE_MOVE_TO_TOWER).second);
// Let the tool change be executed by the wipe tower class.
std::pair<std::string, WipeTower::xy> code_and_pos = m_impl->tool_change(extruder_id, WipeTower::PURPOSE_EXTRUDE);
// Inform the G-code writer about the changes done behind its back.
gcode += code_and_pos.first;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
gcodegen.writer().toolchange(extruder_id);
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
over_wipe_tower = true;
}
if (finish_layer && ! m_impl->layer_finished()) {
// Last extruder change on the layer or no extruder change at all.
if (! over_wipe_tower)
gcode += this->travel_to(gcodegen, m_impl->finish_layer(WipeTower::PURPOSE_MOVE_TO_TOWER).second);
// Let the tool change be executed by the wipe tower class.
std::pair<std::string, WipeTower::xy> code_and_pos = m_impl->finish_layer(WipeTower::PURPOSE_EXTRUDE);
// Inform the G-code writer about the changes done behind its back.
gcode += code_and_pos.first;
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
}
return gcode;
}
// Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower.
std::string WipeTowerIntegration::finalize(GCode &gcodegen, const Print &print, bool current_layer_full)
{
std::string gcode;
// Unload the current filament over the purge tower.
if (current_layer_full) {
// There is not enough space on the wipe tower to purge the nozzle into. Lift Z to the next layer.
coordf_t new_print_z = gcodegen.writer().get_position().z + print.objects.front()->config.layer_height.value;
gcode += gcodegen.change_layer(new_print_z);
m_impl->set_layer(float(new_print_z), float(print.objects.front()->config.layer_height.value), 0, false, true);
}
gcode += this->tool_change(gcodegen, -1, false);
m_impl.release();
return gcode;
}
std::string WipeTowerIntegration::travel_to(GCode &gcodegen, const WipeTower::xy &dest)
{
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
std::string gcode = gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to(
Point(scale_(dest.x - gcodegen.origin().x), scale_(dest.y - gcodegen.origin().y)),
erMixed,
"Travel to a Wipe Tower");
gcode += gcodegen.unretract();
return gcode;
}
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id) #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id)
inline void write(FILE *file, const std::string &what) inline void write(FILE *file, const std::string &what)
@ -484,23 +568,8 @@ bool GCode::do_export(FILE *file, Print &print)
} }
// Prusa Multi-Material wipe tower. // Prusa Multi-Material wipe tower.
if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value && if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value &&
! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0) { ! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0)
// Initialize the wipe tower. m_wipe_tower.reset(new WipeTowerIntegration(print.config));
auto *wipe_tower = new WipeTowerPrusaMM(
float(print.config.wipe_tower_x.value), float(print.config.wipe_tower_y.value),
float(print.config.wipe_tower_width.value), float(print.config.wipe_tower_per_color_wipe.value));
//wipe_tower->set_retract();
//wipe_tower->set_zhop();
//wipe_tower->set_zhop();
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < 4; ++ i)
wipe_tower->set_extruder(
i,
WipeTowerPrusaMM::parse_material(print.config.filament_type.get_at(i).c_str()),
print.config.temperature.get_at(i),
print.config.first_layer_temperature.get_at(i));
m_wipe_tower.reset(wipe_tower);
}
// Extrude the layers. // Extrude the layers.
for (auto &layer : layers) { for (auto &layer : layers) {
// layer.second is of type std::vector<LayerToPrint>, // layer.second is of type std::vector<LayerToPrint>,
@ -526,17 +595,9 @@ bool GCode::do_export(FILE *file, Print &print)
} }
// write end commands to file // write end commands to file
if (m_wipe_tower) { if (m_wipe_tower)
// Unload the current filament over the purge tower. write(file, m_wipe_tower->finalize(*this, print, tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished()));
if (tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished()) { else
// There is not enough space on the wipe tower to purge the nozzle into. Lift Z to the next layer.
coordf_t new_print_z = tool_ordering.back().print_z + print.objects.front()->config.layer_height.value;
write(file, this->change_layer(new_print_z));
m_wipe_tower->set_layer(new_print_z, print.objects.front()->config.layer_height.value, 0, false, true);
}
write(file, this->wipe_tower_tool_change(-1));
m_wipe_tower.release();
} else
write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully
write(file, m_writer.set_fan(false)); write(file, m_writer.set_fan(false));
writeln(file, m_placeholder_parser.process(print.config.end_gcode)); writeln(file, m_placeholder_parser.process(print.config.end_gcode));
@ -711,34 +772,23 @@ void GCode::process_layer(
gcode += pp.process(print.config.layer_gcode.value) + "\n"; gcode += pp.process(print.config.layer_gcode.value) + "\n";
} }
// Trigger the tool change explicitely to draw the wipe tower brim always.
bool wipe_tower_extrude_brim_explicit = m_wipe_tower && ! m_wipe_tower->finished() && first_layer && m_writer.extruder()->id == first_extruder_id;
if (! first_layer && ! m_second_layer_things_done) { if (! first_layer && ! m_second_layer_things_done) {
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
// first_layer_temperature vs. temperature settings. // first_layer_temperature vs. temperature settings.
if (print.config.single_extruder_multi_material.value) {
// Switch the extruder before setting the 2nd layer temperature.
this->set_extruder(first_extruder_id);
int temperature = print.config.temperature.get_at(first_extruder_id);
if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(m_writer.extruder()->id))
gcode += m_writer.set_temperature(temperature, false, first_extruder_id);
} else {
for (const Extruder &extruder : m_writer.extruders) { for (const Extruder &extruder : m_writer.extruders) {
if (print.config.single_extruder_multi_material.value && extruder.id != m_writer.extruder()->id)
// In single extruder multi material mode, set the temperature for the current extruder only.
continue;
int temperature = print.config.temperature.get_at(extruder.id); int temperature = print.config.temperature.get_at(extruder.id);
if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id)) if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id))
gcode += m_writer.set_temperature(temperature, false, extruder.id); gcode += m_writer.set_temperature(temperature, false, extruder.id);
} }
}
if (print.config.bed_temperature.value > 0 && print.config.bed_temperature != print.config.first_layer_bed_temperature.value) if (print.config.bed_temperature.value > 0 && print.config.bed_temperature != print.config.first_layer_bed_temperature.value)
gcode += m_writer.set_bed_temperature(print.config.bed_temperature); gcode += m_writer.set_bed_temperature(print.config.bed_temperature);
// Mark the temperature transition from 1st to 2nd layer to be finished. // Mark the temperature transition from 1st to 2nd layer to be finished.
m_second_layer_things_done = true; m_second_layer_things_done = true;
} }
if (wipe_tower_extrude_brim_explicit)
gcode += this->wipe_tower_tool_change(first_extruder_id);
// Extrude skirt at the print_z of the raft layers and normal object layers // Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers. // not at the print_z of the interlaced support material layers.
bool extrude_skirt = bool extrude_skirt =
@ -914,20 +964,9 @@ void GCode::process_layer(
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size()); std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders) for (unsigned int extruder_id : layer_tools.extruders)
{ {
gcode += this->set_extruder(extruder_id); gcode += m_wipe_tower ?
if (m_wipe_tower && ! m_wipe_tower->layer_finished() && extruder_id == layer_tools.extruders.back()) { m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
// Last extruder change on the layer or no extruder change at all. this->set_extruder(extruder_id);
// Move over the wipe tower.
gcode += m_writer.travel_to_xy(Pointf3(m_wipe_tower->position().x, m_wipe_tower->position().y));
gcode += m_writer.unlift();
gcode += m_writer.unretract();
// Let the tool change be executed by the wipe tower class.
std::pair<std::string, WipeTower::xy> code_and_pos = m_wipe_tower->finish_layer();
// Inform the G-code writer about the changes done behind its back.
gcode += code_and_pos.first;
// A phony move to the end position at the wipe tower.
m_writer.travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
}
if (extrude_skirt) { if (extrude_skirt) {
auto loops_it = skirt_loops_per_extruder.find(extruder_id); auto loops_it = skirt_loops_per_extruder.find(extruder_id);
@ -1317,7 +1356,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) { if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) {
if (! *lower_layer_edge_grid) { if (! *lower_layer_edge_grid) {
// Create the distance field for a layer below. // Create the distance field for a layer below.
const coord_t distance_field_resolution = scale_(1.f); const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>(); *lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
(*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution); (*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution);
(*lower_layer_edge_grid)->calculate_sdf(); (*lower_layer_edge_grid)->calculate_sdf();
@ -1381,7 +1420,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// For each polygon point, store a penalty. // For each polygon point, store a penalty.
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r. // First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, nozzle_r); std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, float(nozzle_r));
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces. // No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
const float penaltyConvexVertex = 1.f; const float penaltyConvexVertex = 1.f;
const float penaltyFlatSurface = 5.f; const float penaltyFlatSurface = 5.f;
@ -1558,7 +1597,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
paths.front().polyline.points[0], paths.front().polyline.points[0],
paths.front().polyline.points[1] paths.front().polyline.points[1]
); );
double distance = std::min( double distance = std::min<double>(
scale_(EXTRUDER_CONFIG(nozzle_diameter)), scale_(EXTRUDER_CONFIG(nozzle_diameter)),
first_segment.length() first_segment.length()
); );
@ -1791,8 +1830,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} }
// This method accepts &point in print coordinates. // This method accepts &point in print coordinates.
std::string std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
{ {
/* Define the travel move as a line between current position and the taget point. /* Define the travel move as a line between current position and the taget point.
This is expressed in print coordinates, so it will need to be translated by This is expressed in print coordinates, so it will need to be translated by
@ -1924,11 +1962,6 @@ std::string GCode::set_extruder(unsigned int extruder_id)
gcode += pp.process(m_config.toolchange_gcode.value) + '\n'; gcode += pp.process(m_config.toolchange_gcode.value) + '\n';
} }
if (m_wipe_tower) {
assert(! m_wipe_tower->finished());
if (! m_wipe_tower->finished())
gcode += this->wipe_tower_tool_change(extruder_id);
} else {
// if ooze prevention is enabled, park current extruder in the nearest // if ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature // standby point and set it to the standby temperature
if (m_ooze_prevention.enable && m_writer.extruder() != NULL) if (m_ooze_prevention.enable && m_writer.extruder() != NULL)
@ -1938,36 +1971,26 @@ std::string GCode::set_extruder(unsigned int extruder_id)
// set the new extruder to the operating temperature // set the new extruder to the operating temperature
if (m_ooze_prevention.enable) if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this); gcode += m_ooze_prevention.post_toolchange(*this);
}
return gcode; return gcode;
} }
std::string GCode::wipe_tower_tool_change(int extruder_id)
{
// Move over the wipe tower.
std::string gcode = m_writer.travel_to_xy(Pointf3(m_wipe_tower->position().x, m_wipe_tower->position().y));
gcode += m_writer.unlift();
gcode += m_writer.unretract();
// Let the tool change be executed by the wipe tower class.
std::pair<std::string, WipeTower::xy> code_and_pos = m_wipe_tower->tool_change(extruder_id);
// Inform the G-code writer about the changes done behind its back.
gcode += code_and_pos.first;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
m_writer.toolchange(extruder_id);
// A phony move to the end position at the wipe tower.
m_writer.travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
return gcode;
}
// convert a model-space scaled point into G-code coordinates // convert a model-space scaled point into G-code coordinates
Pointf GCode::point_to_gcode(const Point &point) const Pointf GCode::point_to_gcode(const Point &point) const
{ {
Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return Pointf( return Pointf(
unscale(point.x) + m_origin.x - extruder_offset.x, unscale(point.x) + m_origin.x - extruder_offset.x,
unscale(point.y) + m_origin.y - extruder_offset.y unscale(point.y) + m_origin.y - extruder_offset.y);
); }
// convert a model-space scaled point into G-code coordinates
Point GCode::gcode_to_point(const Pointf &point) const
{
Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return Point(
scale_(point.x - m_origin.x + extruder_offset.x),
scale_(point.y - m_origin.y + extruder_offset.y));
} }
} }

View file

@ -73,6 +73,20 @@ public:
std::string wipe(GCode &gcodegen, bool toolchange = false); std::string wipe(GCode &gcodegen, bool toolchange = false);
}; };
class WipeTowerIntegration {
public:
WipeTowerIntegration(const PrintConfig &config);
void set_layer(coordf_t print_z, coordf_t layer_height, size_t max_tool_changes, bool is_first_layer, bool is_last_layer)
{ m_impl->set_layer(float(print_z), float(layer_height), max_tool_changes, is_first_layer, is_last_layer); }
bool layer_finished() const { return m_impl->layer_finished(); }
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
std::string finalize(GCode &gcodegen, const Print &print, bool current_layer_full);
private:
std::string travel_to(GCode &codegen, const WipeTower::xy &dest);
std::unique_ptr<WipeTower> m_impl;
};
class GCode { class GCode {
public: public:
GCode() : GCode() :
@ -101,6 +115,7 @@ public:
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); } void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); }
const Point& last_pos() const { return m_last_pos; } const Point& last_pos() const { return m_last_pos; }
Pointf point_to_gcode(const Point &point) const; Pointf point_to_gcode(const Point &point) const;
Point gcode_to_point(const Pointf &point) const;
const FullPrintConfig &config() const { return m_config; } const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; } const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; } GCodeWriter& writer() { return m_writer; }
@ -114,7 +129,7 @@ public:
void set_elapsed_time(float value) { m_elapsed_time = value; } void set_elapsed_time(float value) { m_elapsed_time = value; }
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
private: protected:
// Object and support extrusions of the same PrintObject at the same print_z. // Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint struct LayerToPrint
{ {
@ -175,7 +190,6 @@ private:
std::string retract(bool toolchange = false); std::string retract(bool toolchange = false);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id); std::string set_extruder(unsigned int extruder_id);
std::string wipe_tower_tool_change(int extruder_id);
/* Origin of print coordinates expressed in unscaled G-code coordinates. /* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to() This affects the input arguments supplied to the extrude*() and travel_to()
@ -222,7 +236,7 @@ private:
std::unique_ptr<CoolingBuffer> m_cooling_buffer; std::unique_ptr<CoolingBuffer> m_cooling_buffer;
std::unique_ptr<SpiralVase> m_spiral_vase; std::unique_ptr<SpiralVase> m_spiral_vase;
std::unique_ptr<PressureEqualizer> m_pressure_equalizer; std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
std::unique_ptr<WipeTower> m_wipe_tower; std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
// Heights at which the skirt has already been extruded. // Heights at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done; std::vector<coordf_t> m_skirt_done;
@ -251,6 +265,8 @@ private:
size_t object_idx, size_t object_idx,
size_t num_objects, size_t num_objects,
size_t num_islands); size_t num_islands);
friend class WipeTowerIntegration;
}; };
} }

View file

@ -46,13 +46,19 @@ public:
// Is this the last layer of the wipe tower? // Is this the last layer of the wipe tower?
bool is_last_layer) = 0; bool is_last_layer) = 0;
enum Purpose {
PURPOSE_MOVE_TO_TOWER,
PURPOSE_EXTRUDE,
PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE,
};
// Returns gcode for toolchange and the end position. // Returns gcode for toolchange and the end position.
// if new_tool == -1, just unload the current filament over the wipe tower. // if new_tool == -1, just unload the current filament over the wipe tower.
virtual std::pair<std::string, xy> tool_change(int new_tool) = 0; virtual std::pair<std::string, xy> tool_change(int new_tool, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
// Call this method only if layer_finished() is false. // Call this method only if layer_finished() is false.
virtual std::pair<std::string, xy> finish_layer() = 0; virtual std::pair<std::string, xy> finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or // Is the current layer finished? A layer is finished if either the wipe tower is finished, or
// the wipe tower has been completely covered by the tool change extrusions, // the wipe tower has been completely covered by the tool change extrusions,

View file

@ -27,6 +27,8 @@ public:
m_current_feedrate(0.f), m_current_feedrate(0.f),
m_extrusion_flow(0.f) {} m_extrusion_flow(0.f) {}
Writer& set_initial_position(const WipeTower::xy &pos) { m_current_pos = pos; return *this; }
Writer& set_z(float z) Writer& set_z(float z)
{ m_current_z = z; return *this; } { m_current_z = z; return *this; }
@ -283,17 +285,15 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
return INVALID; return INVALID;
} }
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool) std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Purpose purpose)
{ {
// Either it is the last tool unload, // Either it is the last tool unload,
// or there must be a nonzero wipe tower partitions available. // or there must be a nonzero wipe tower partitions available.
assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0); assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0);
if (m_idx_tool_change_in_layer == (unsigned int)(-1)) { if (m_idx_tool_change_in_layer == (unsigned int)(-1)) {
// Mark the brim as extruded.
m_idx_tool_change_in_layer = 0;
// First layer, prime the extruder. // First layer, prime the extruder.
return toolchange_Brim(tool); return toolchange_Brim(purpose);
} }
box_coordinates cleaning_box( box_coordinates cleaning_box(
@ -302,32 +302,38 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool)
m_wipe_area - m_perimeter_width); m_wipe_area - m_perimeter_width);
PrusaMultiMaterial::Writer writer; PrusaMultiMaterial::Writer writer;
// Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
float initial_retract = ((m_current_material == SCAFF) ? 1.f : 0.5f) * m_retract;
writer.set_extrusion_flow(m_extrusion_flow) writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos) .set_z(m_z_pos)
.append(";--------------------\n" .append(";--------------------\n"
"; CP TOOLCHANGE START\n") "; CP TOOLCHANGE START\n")
.comment_with_value(" toolchange #", m_num_tool_changes ++) .comment_with_value(" toolchange #", m_num_tool_changes)
.comment_material(m_current_material) .comment_material(m_current_material)
.append(";--------------------\n") .append(";--------------------\n")
.speed_override(100) .speed_override(100);
// Lift for a Z hop.
xy initial_position = ((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) +
xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width);
if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
// Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
float initial_retract = ((m_current_material == SCAFF) ? 1.f : 0.5f) * m_retract;
writer // Lift for a Z hop.
.z_hop(m_zhop, 7200) .z_hop(m_zhop, 7200)
// Additional retract on move to tower. // Additional retract on move to tower.
.retract(initial_retract, 3600) .retract(initial_retract, 3600)
// Move to a starting position, one perimeter width inside the cleaning box. // Move to a starting position, one perimeter width inside the cleaning box.
.travel(((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) + .travel(initial_position, 7200)
xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width), 7200)
// Unlift for a Z hop. // Unlift for a Z hop.
.z_hop_reset(7200) .z_hop_reset(7200)
// Additional retract on move to tower. // Additional retract on move to tower.
.load(initial_retract, 3600) .load(initial_retract, 3600)
.load(m_retract, 1500) .load(m_retract, 1500);
// Increase extruder current for ramming. } else {
.set_extruder_trimpot(750) // Already at the initial position.
.flush_planner_queue(); writer.set_initial_position(initial_position);
}
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
toolchange_Unload(writer, cleaning_box, m_current_material, toolchange_Unload(writer, cleaning_box, m_current_material,
m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]); m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
@ -351,12 +357,15 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool)
";------------------\n" ";------------------\n"
"\n\n"); "\n\n");
++ m_num_tool_changes;
++ m_idx_tool_change_in_layer; ++ m_idx_tool_change_in_layer;
m_current_wipe_start_y += m_wipe_area; m_current_wipe_start_y += m_wipe_area;
}
return std::pair<std::string, xy>(writer.gcode(), writer.pos()); return std::pair<std::string, xy>(writer.gcode(), writer.pos());
} }
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(size_t /* tool */, bool sideOnly, float y_offset) std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
{ {
const box_coordinates wipeTower_box( const box_coordinates wipeTower_box(
m_wipe_tower_pos, m_wipe_tower_pos,
@ -371,11 +380,19 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(size_t /
";-------------------------------------\n" ";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n"); "; CP WIPE TOWER FIRST LAYER BRIM START\n");
// Move with Z hop and prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower. xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0);
if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
// Move with Z hop.
writer.z_hop(m_zhop, 7200) writer.z_hop(m_zhop, 7200)
.travel(wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0), 6000) .travel(initial_position, 6000)
.z_hop_reset(7200) .z_hop_reset(7200);
.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400); else
writer.set_initial_position(initial_position);
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
// Prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower.
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400);
// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded. // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
// toolchange_Change(writer, int(tool), m_material[tool]); // toolchange_Change(writer, int(tool), m_material[tool]);
@ -411,6 +428,10 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(size_t /
.append("; CP WIPE TOWER FIRST LAYER BRIM END\n" .append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n"); ";-----------------------------------\n");
// Mark the brim as extruded.
m_idx_tool_change_in_layer = 0;
}
return std::pair<std::string, xy>(writer.gcode(), writer.pos()); return std::pair<std::string, xy>(writer.gcode(), writer.pos());
} }
@ -425,7 +446,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
float xr = cleaning_box.rd.x - 0.5f * m_perimeter_width; float xr = cleaning_box.rd.x - 0.5f * m_perimeter_width;
float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width; float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width;
writer.append("; CP TOOLCHANGE UNLOAD"); writer.append("; CP TOOLCHANGE UNLOAD\n");
// Ram the hot material out of the extruder melt zone. // Ram the hot material out of the extruder melt zone.
// Current extruder position is on the left, one perimeter inside the cleaning box in both X and Y. // Current extruder position is on the left, one perimeter inside the cleaning box in both X and Y.
@ -613,7 +634,7 @@ void WipeTowerPrusaMM::toolchange_Done(
.feedrate(6000); .feedrate(6000);
} }
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer() std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose purpose)
{ {
// This should only be called if the layer is not finished yet. // This should only be called if the layer is not finished yet.
// Otherwise the caller would likely travel to the wipe tower in vain. // Otherwise the caller would likely travel to the wipe tower in vain.
@ -624,7 +645,8 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer()
.set_z(m_z_pos) .set_z(m_z_pos)
.append(";--------------------\n" .append(";--------------------\n"
"; CP EMPTY GRID START\n") "; CP EMPTY GRID START\n")
.comment_with_value(" layer #", m_num_layer_changes ++); // m_num_layer_changes is incremented by set_z, so it is 1 based.
.comment_with_value(" layer #", m_num_layer_changes - 1);
// Slow down on the 1st layer. // Slow down on the 1st layer.
float speed_factor = m_is_first_layer ? 0.5f : 1.f; float speed_factor = m_is_first_layer ? 0.5f : 1.f;
@ -638,7 +660,8 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer()
fill_box.rd.y += firstLayerOffset; fill_box.rd.y += firstLayerOffset;
} }
if (m_idx_tool_change_in_layer == 0) if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
if (m_idx_tool_change_in_layer == 0) {
// There were no tool changes at all in this layer. // There were no tool changes at all in this layer.
writer.retract(m_retract * 1.5f, 3600) writer.retract(m_retract * 1.5f, 3600)
// Jump with retract to fill_box.ld + a random shift in +x. // Jump with retract to fill_box.ld + a random shift in +x.
@ -647,7 +670,16 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer()
.z_hop_reset(7200) .z_hop_reset(7200)
// Prime the extruder. // Prime the extruder.
.load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600); .load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600);
} else {
// Otherwise the extruder is already over the wipe tower.
}
} else {
// The print head is inside the wipe tower. Rather move to the start of the following extrusion.
// writer.set_initial_position(fill_box.ld);
writer.travel(fill_box.ld, 7000);
}
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
// Extrude the first perimeter. // Extrude the first perimeter.
box_coordinates box = fill_box; box_coordinates box = fill_box;
writer.extrude(box.lu, 2400 * speed_factor) writer.extrude(box.lu, 2400 * speed_factor)
@ -693,6 +725,10 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer()
.append("; CP EMPTY GRID END\n" .append("; CP EMPTY GRID END\n"
";------------------\n\n\n\n\n\n\n"); ";------------------\n\n\n\n\n\n\n");
// Indicate that this wipe tower layer is fully covered.
m_idx_tool_change_in_layer = m_max_color_changes;
}
return std::pair<std::string, xy>(writer.gcode(), writer.pos()); return std::pair<std::string, xy>(writer.gcode(), writer.pos());
} }

View file

@ -88,6 +88,7 @@ public:
m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0; m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
m_current_wipe_start_y = 0.f; m_current_wipe_start_y = 0.f;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL; m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
++ m_num_layer_changes;
int layer_idx = int(std::floor(layer_height * 100) + 0.5f); int layer_idx = int(std::floor(layer_height * 100) + 0.5f);
switch (layer_idx) switch (layer_idx)
@ -109,11 +110,11 @@ public:
// Returns gcode for a toolchange and a final print head position. // Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first. // On the first layer, extrude a brim around the future wipe tower first.
virtual std::pair<std::string, xy> tool_change(int new_tool); virtual std::pair<std::string, xy> tool_change(int new_tool, Purpose purpose);
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
// Call this method only if layer_finished() is false. // Call this method only if layer_finished() is false.
virtual std::pair<std::string, xy> finish_layer(); virtual std::pair<std::string, xy> finish_layer(Purpose purpose);
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or // Is the current layer finished? A layer is finished if either the wipe tower is finished, or
// the wipe tower has been completely covered by the tool change extrusions, // the wipe tower has been completely covered by the tool change extrusions,
@ -206,7 +207,7 @@ private:
// Returns gcode for wipe tower brim // Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower // offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
std::pair<std::string, WipeTower::xy> toolchange_Brim(size_t tool, bool sideOnly = false, float y_offset = 0.f); std::pair<std::string, WipeTower::xy> toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload( void toolchange_Unload(
PrusaMultiMaterial::Writer &writer, PrusaMultiMaterial::Writer &writer,

View file

@ -23,12 +23,12 @@ public:
GCodeLine(GCodeReader* _reader) : reader(_reader) {}; GCodeLine(GCodeReader* _reader) : reader(_reader) {};
bool has(char arg) const { return this->args.count(arg) > 0; }; bool has(char arg) const { return this->args.count(arg) > 0; };
float get_float(char arg) const { return atof(this->args.at(arg).c_str()); }; float get_float(char arg) const { return float(atof(this->args.at(arg).c_str())); };
float new_X() const { return this->has('X') ? atof(this->args.at('X').c_str()) : this->reader->X; }; float new_X() const { return this->has('X') ? float(atof(this->args.at('X').c_str())) : this->reader->X; };
float new_Y() const { return this->has('Y') ? atof(this->args.at('Y').c_str()) : this->reader->Y; }; float new_Y() const { return this->has('Y') ? float(atof(this->args.at('Y').c_str())) : this->reader->Y; };
float new_Z() const { return this->has('Z') ? atof(this->args.at('Z').c_str()) : this->reader->Z; }; float new_Z() const { return this->has('Z') ? float(atof(this->args.at('Z').c_str())) : this->reader->Z; };
float new_E() const { return this->has('E') ? atof(this->args.at('E').c_str()) : this->reader->E; }; float new_E() const { return this->has('E') ? float(atof(this->args.at('E').c_str())) : this->reader->E; };
float new_F() const { return this->has('F') ? atof(this->args.at('F').c_str()) : this->reader->F; }; float new_F() const { return this->has('F') ? float(atof(this->args.at('F').c_str())) : this->reader->F; };
float dist_X() const { return this->new_X() - this->reader->X; }; float dist_X() const { return this->new_X() - this->reader->X; };
float dist_Y() const { return this->new_Y() - this->reader->Y; }; float dist_Y() const { return this->new_Y() - this->reader->Y; };
float dist_Z() const { return this->new_Z() - this->reader->Z; }; float dist_Z() const { return this->new_Z() - this->reader->Z; };

View file

@ -17,6 +17,9 @@
#define SLIC3R_VERSION "1.33.8.devel" #define SLIC3R_VERSION "1.33.8.devel"
#define SLIC3R_BUILD "UNKNOWN" #define SLIC3R_BUILD "UNKNOWN"
typedef long coord_t;
typedef double coordf_t;
//FIXME This epsilon value is used for many non-related purposes: //FIXME This epsilon value is used for many non-related purposes:
// For a threshold of a squared Euclidean distance, // For a threshold of a squared Euclidean distance,
// for a trheshold in a difference of radians, // for a trheshold in a difference of radians,
@ -39,12 +42,11 @@
// 3mm ring around the top / bottom / bridging areas. // 3mm ring around the top / bottom / bridging areas.
//FIXME This is quite a lot. //FIXME This is quite a lot.
#define EXTERNAL_INFILL_MARGIN 3. #define EXTERNAL_INFILL_MARGIN 3.
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR) #define scale_(val) ((val) / SCALING_FACTOR)
#define unscale(val) ((val) * SCALING_FACTOR) #define unscale(val) ((val) * SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON) #define SCALED_EPSILON scale_(EPSILON)
typedef long coord_t;
typedef double coordf_t;
/* Implementation of CONFESS("foo"): */ /* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER #ifdef _MSC_VER
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)