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;
}
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)
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.
if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value &&
! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0) {
// 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_wipe_tower.reset(wipe_tower);
}
! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0)
m_wipe_tower.reset(new WipeTowerIntegration(print.config));
// Extrude the layers.
for (auto &layer : layers) {
// 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
if (m_wipe_tower) {
// Unload the current filament over the purge tower.
if (tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished()) {
// 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
if (m_wipe_tower)
write(file, m_wipe_tower->finalize(*this, print, tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished()));
else
write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully
write(file, m_writer.set_fan(false));
writeln(file, m_placeholder_parser.process(print.config.end_gcode));
@ -711,24 +772,16 @@ void GCode::process_layer(
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) {
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
// 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) {
int temperature = print.config.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);
}
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);
if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(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)
gcode += m_writer.set_bed_temperature(print.config.bed_temperature);
@ -736,9 +789,6 @@ void GCode::process_layer(
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
// not at the print_z of the interlaced support material layers.
bool extrude_skirt =
@ -913,21 +963,10 @@ void GCode::process_layer(
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
{
gcode += this->set_extruder(extruder_id);
if (m_wipe_tower && ! m_wipe_tower->layer_finished() && extruder_id == layer_tools.extruders.back()) {
// Last extruder change on the layer or no extruder change at all.
// 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));
}
{
gcode += m_wipe_tower ?
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
this->set_extruder(extruder_id);
if (extrude_skirt) {
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 (! *lower_layer_edge_grid) {
// 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)->create(m_layer->lower_layer->slices, distance_field_resolution);
(*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.
// 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.
const float penaltyConvexVertex = 1.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[1]
);
double distance = std::min(
double distance = std::min<double>(
scale_(EXTRUDER_CONFIG(nozzle_diameter)),
first_segment.length()
);
@ -1791,8 +1830,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
// This method accepts &point in print coordinates.
std::string
GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
std::string 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.
This is expressed in print coordinates, so it will need to be translated by
@ -1924,50 +1962,35 @@ std::string GCode::set_extruder(unsigned int extruder_id)
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
// standby point and set it to the standby temperature
if (m_ooze_prevention.enable && m_writer.extruder() != NULL)
gcode += m_ooze_prevention.pre_toolchange(*this);
// append the toolchange command
gcode += m_writer.toolchange(extruder_id);
// set the new extruder to the operating temperature
if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this);
}
// if ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature
if (m_ooze_prevention.enable && m_writer.extruder() != NULL)
gcode += m_ooze_prevention.pre_toolchange(*this);
// append the toolchange command
gcode += m_writer.toolchange(extruder_id);
// set the new extruder to the operating temperature
if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this);
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
Pointf GCode::point_to_gcode(const Point &point) const
{
Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return Pointf(
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);
};
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 {
public:
GCode() :
@ -101,6 +115,7 @@ public:
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; }
Pointf point_to_gcode(const Point &point) const;
Point gcode_to_point(const Pointf &point) const;
const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; }
@ -114,7 +129,7 @@ public:
void set_elapsed_time(float value) { m_elapsed_time = value; }
void apply_print_config(const PrintConfig &print_config);
private:
protected:
// Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint
{
@ -175,7 +190,6 @@ private:
std::string retract(bool toolchange = false);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
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.
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<SpiralVase> m_spiral_vase;
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.
std::vector<coordf_t> m_skirt_done;
@ -251,6 +265,8 @@ private:
size_t object_idx,
size_t num_objects,
size_t num_islands);
friend class WipeTowerIntegration;
};
}

View file

@ -46,13 +46,19 @@ public:
// Is this the last layer of the wipe tower?
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.
// 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.
// 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
// 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_extrusion_flow(0.f) {}
Writer& set_initial_position(const WipeTower::xy &pos) { m_current_pos = pos; return *this; }
Writer& set_z(float z)
{ m_current_z = z; return *this; }
@ -283,17 +285,15 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
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,
// or there must be a nonzero wipe tower partitions available.
assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0);
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.
return toolchange_Brim(tool);
return toolchange_Brim(purpose);
}
box_coordinates cleaning_box(
@ -302,61 +302,70 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool)
m_wipe_area - m_perimeter_width);
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)
.set_z(m_z_pos)
.append(";--------------------\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)
.append(";--------------------\n")
.speed_override(100)
// Lift for a Z hop.
.z_hop(m_zhop, 7200)
// Additional retract on move to tower.
.retract(initial_retract, 3600)
// Move to a starting position, one perimeter width inside the cleaning box.
.travel(((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), 7200)
// Unlift for a Z hop.
.z_hop_reset(7200)
// Additional retract on move to tower.
.load(initial_retract, 3600)
.load(m_retract, 1500)
// Increase extruder current for ramming.
.set_extruder_trimpot(750)
.flush_planner_queue();
.speed_override(100);
// 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,
m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
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 (tool >= 0) {
// This is not the last change.
// Change the tool, set a speed override for soluble and flex materials.
toolchange_Change(writer, tool, m_material[tool]);
toolchange_Load(writer, cleaning_box);
// Wipe the newly loaded filament until the end of the assigned wipe area.
toolchange_Wipe(writer, cleaning_box);
// Draw a perimeter around cleaning_box and wipe.
toolchange_Done(writer, cleaning_box);
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)
// Additional retract on move to tower.
.retract(initial_retract, 3600)
// Move to a starting position, one perimeter width inside the cleaning box.
.travel(initial_position, 7200)
// Unlift for a Z hop.
.z_hop_reset(7200)
// Additional retract on move to tower.
.load(initial_retract, 3600)
.load(m_retract, 1500);
} else {
// Already at the initial position.
writer.set_initial_position(initial_position);
}
// Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550)
.flush_planner_queue()
.reset_extruder()
.append("; CP TOOLCHANGE END\n"
";------------------\n"
"\n\n");
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.
toolchange_Unload(writer, cleaning_box, m_current_material,
m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
if (tool >= 0) {
// This is not the last change.
// Change the tool, set a speed override for soluble and flex materials.
toolchange_Change(writer, tool, m_material[tool]);
toolchange_Load(writer, cleaning_box);
// Wipe the newly loaded filament until the end of the assigned wipe area.
toolchange_Wipe(writer, cleaning_box);
// Draw a perimeter around cleaning_box and wipe.
toolchange_Done(writer, cleaning_box);
}
// Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550)
.flush_planner_queue()
.reset_extruder()
.append("; CP TOOLCHANGE END\n"
";------------------\n"
"\n\n");
++ m_num_tool_changes;
++ m_idx_tool_change_in_layer;
m_current_wipe_start_y += m_wipe_area;
}
++ m_idx_tool_change_in_layer;
m_current_wipe_start_y += m_wipe_area;
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(
m_wipe_tower_pos,
@ -371,45 +380,57 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(size_t /
";-------------------------------------\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.
writer.z_hop(m_zhop, 7200)
.travel(wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0), 6000)
.z_hop_reset(7200)
.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400);
xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0);
// 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]);
if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
// Move with Z hop.
writer.z_hop(m_zhop, 7200)
.travel(initial_position, 6000)
.z_hop_reset(7200);
else
writer.set_initial_position(initial_position);
if (sideOnly) {
float x_offset = m_perimeter_width;
for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset), 7000)
.extrude(wipeTower_box.lu + xy(- x_offset, - y_offset), 2100);
writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000);
x_offset = m_perimeter_width;
for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
writer.travel (wipeTower_box.rd + xy(x_offset, y_offset), 7000)
.extrude(wipeTower_box.ru + xy(x_offset, - y_offset), 2100);
} else {
// Extrude 4 rounds of a brim around the future wipe tower.
box_coordinates box(wipeTower_box);
//FIXME why is the box shifted in +Y by 0.5f * m_perimeter_width?
box.translate(0.f, 0.5f * m_perimeter_width);
box.expand(0.5f * m_perimeter_width);
for (size_t i = 0; i < 4; ++ i) {
writer.travel (box.ld, 7000)
.extrude(box.lu, 2100).extrude(box.ru)
.extrude(box.rd ).extrude(box.ld);
box.expand(m_perimeter_width);
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.
// toolchange_Change(writer, int(tool), m_material[tool]);
if (sideOnly) {
float x_offset = m_perimeter_width;
for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset), 7000)
.extrude(wipeTower_box.lu + xy(- x_offset, - y_offset), 2100);
writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000);
x_offset = m_perimeter_width;
for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
writer.travel (wipeTower_box.rd + xy(x_offset, y_offset), 7000)
.extrude(wipeTower_box.ru + xy(x_offset, - y_offset), 2100);
} else {
// Extrude 4 rounds of a brim around the future wipe tower.
box_coordinates box(wipeTower_box);
//FIXME why is the box shifted in +Y by 0.5f * m_perimeter_width?
box.translate(0.f, 0.5f * m_perimeter_width);
box.expand(0.5f * m_perimeter_width);
for (size_t i = 0; i < 4; ++ i) {
writer.travel (box.ld, 7000)
.extrude(box.lu, 2100).extrude(box.ru)
.extrude(box.rd ).extrude(box.ld);
box.expand(m_perimeter_width);
}
}
}
// Move to the front left corner and wipe along the front edge.
writer.travel(wipeTower_box.ld, 7000)
.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld)
.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n");
// Move to the front left corner and wipe along the front edge.
writer.travel(wipeTower_box.ld, 7000)
.travel(wipeTower_box.rd)
.travel(wipeTower_box.ld)
.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n");
// Mark the brim as extruded.
m_idx_tool_change_in_layer = 0;
}
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 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.
// 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);
}
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.
// 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)
.append(";--------------------\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.
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
@ -638,60 +660,74 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer()
fill_box.rd.y += firstLayerOffset;
}
if (m_idx_tool_change_in_layer == 0)
// There were no tool changes at all in this layer.
writer.retract(m_retract * 1.5f, 3600)
// Jump with retract to fill_box.ld + a random shift in +x.
.z_hop(m_zhop, 7200)
.travel(fill_box.ld + xy(5.f + 15.f * float(rand()) / RAND_MAX, 0.f), 7000)
.z_hop_reset(7200)
// Prime the extruder.
.load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600);
// Extrude the first perimeter.
box_coordinates box = fill_box;
writer.extrude(box.lu, 2400 * speed_factor)
.extrude(box.ru)
.extrude(box.rd)
.extrude(box.ld + xy(m_perimeter_width / 2, 0));
// Extrude second perimeter.
box.expand(- m_perimeter_width / 2);
writer.extrude(box.lu, 3200 * speed_factor)
.extrude(box.ru)
.extrude(box.rd)
.extrude(box.ld + xy(m_perimeter_width / 2, 0))
.extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2));
// Extrude an inverse U at the left of the region.
writer.extrude(fill_box.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor)
.extrude(fill_box.lu + xy(m_perimeter_width * 3, - m_perimeter_width))
.extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width))
.extrude(fill_box.ld + xy(m_perimeter_width * 6, m_perimeter_width));
if (fill_box.lu.y - fill_box.ld.y > 4.f) {
// Extrude three zig-zags.
float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f;
for (size_t i = 0; i < 3; ++ i) {
writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor);
writer.extrude(writer.x() , fill_box.lu.y - m_perimeter_width * 8);
writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width );
writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width * 8);
writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width * 8);
writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width );
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.
writer.retract(m_retract * 1.5f, 3600)
// Jump with retract to fill_box.ld + a random shift in +x.
.z_hop(m_zhop, 7200)
.travel(fill_box.ld + xy(5.f + 15.f * float(rand()) / RAND_MAX, 0.f), 7000)
.z_hop_reset(7200)
// Prime the extruder.
.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);
}
// Extrude an inverse U at the left of the region.
writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor)
.extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width))
.extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width))
.extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width))
// Wipe along the front side of the current wiping box.
.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200)
.travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2))
.append("; CP EMPTY GRID END\n"
";------------------\n\n\n\n\n\n\n");
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
// Extrude the first perimeter.
box_coordinates box = fill_box;
writer.extrude(box.lu, 2400 * speed_factor)
.extrude(box.ru)
.extrude(box.rd)
.extrude(box.ld + xy(m_perimeter_width / 2, 0));
// Extrude second perimeter.
box.expand(- m_perimeter_width / 2);
writer.extrude(box.lu, 3200 * speed_factor)
.extrude(box.ru)
.extrude(box.rd)
.extrude(box.ld + xy(m_perimeter_width / 2, 0))
.extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2));
// Extrude an inverse U at the left of the region.
writer.extrude(fill_box.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor)
.extrude(fill_box.lu + xy(m_perimeter_width * 3, - m_perimeter_width))
.extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width))
.extrude(fill_box.ld + xy(m_perimeter_width * 6, m_perimeter_width));
if (fill_box.lu.y - fill_box.ld.y > 4.f) {
// Extrude three zig-zags.
float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f;
for (size_t i = 0; i < 3; ++ i) {
writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor);
writer.extrude(writer.x() , fill_box.lu.y - m_perimeter_width * 8);
writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width );
writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width * 8);
writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width * 8);
writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width );
}
}
// Extrude an inverse U at the left of the region.
writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor)
.extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width))
.extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width))
.extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width))
// Wipe along the front side of the current wiping box.
.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200)
.travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2))
.append("; CP EMPTY GRID END\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());
}

View file

@ -88,6 +88,7 @@ public:
m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
m_current_wipe_start_y = 0.f;
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);
switch (layer_idx)
@ -103,17 +104,17 @@ public:
}
// Return the wipe tower position.
virtual const xy& position() const { return m_wipe_tower_pos; }
virtual const xy& position() const { return m_wipe_tower_pos; }
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
virtual bool finished() const { return m_max_color_changes == 0; }
virtual bool finished() const { return m_max_color_changes == 0; }
// Returns gcode for a toolchange and a final print head position.
// 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.
// 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
// the wipe tower has been completely covered by the tool change extrusions,
@ -132,27 +133,27 @@ private:
};
// Left front corner of the wipe tower in mm.
xy m_wipe_tower_pos;
xy m_wipe_tower_pos;
// Width of the wipe tower.
float m_wipe_tower_width;
float m_wipe_tower_width;
// Per color Y span.
float m_wipe_area;
float m_wipe_area;
// Current Z position.
float m_z_pos = 0.f;
float m_z_pos = 0.f;
// Maximum number of color changes per layer.
size_t m_max_color_changes = 0;
size_t m_max_color_changes = 0;
// Is this the 1st layer of the print? If so, print the brim around the waste tower.
bool m_is_first_layer = false;
bool m_is_first_layer = false;
// Is this the last layer of this waste tower?
bool m_is_last_layer = false;
bool m_is_last_layer = false;
// G-code generator parameters.
float m_zhop = 0.5f;
float m_retract = 4.f;
float m_zhop = 0.5f;
float m_retract = 4.f;
// Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_perimeter_width = 0.5f;
float m_perimeter_width = 0.5f;
// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
float m_extrusion_flow = 0.029f;
float m_extrusion_flow = 0.029f;
// Extruder specific parameters.
material_type m_material[4];
@ -206,7 +207,7 @@ private:
// Returns gcode for wipe tower brim
// 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
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(
PrusaMultiMaterial::Writer &writer,

View file

@ -23,12 +23,12 @@ public:
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
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 new_X() const { return this->has('X') ? 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_Z() const { return this->has('Z') ? 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_F() const { return this->has('F') ? atof(this->args.at('F').c_str()) : this->reader->F; };
float get_float(char arg) const { return float(atof(this->args.at(arg).c_str())); };
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') ? float(atof(this->args.at('Y').c_str())) : this->reader->Y; };
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') ? float(atof(this->args.at('E').c_str())) : this->reader->E; };
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_Y() const { return this->new_Y() - this->reader->Y; };
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_BUILD "UNKNOWN"
typedef long coord_t;
typedef double coordf_t;
//FIXME This epsilon value is used for many non-related purposes:
// For a threshold of a squared Euclidean distance,
// for a trheshold in a difference of radians,
@ -39,12 +42,11 @@
// 3mm ring around the top / bottom / bridging areas.
//FIXME This is quite a lot.
#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 unscale(val) ((val) * SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON)
typedef long coord_t;
typedef double coordf_t;
/* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)