Implemented wipe tower print path preview.

This commit is contained in:
bubnikv 2017-05-25 22:27:53 +02:00
parent 7d64c465c0
commit e000b22578
19 changed files with 615 additions and 190 deletions

View file

@ -1,3 +1,4 @@
#include "Print.hpp"
#include "ToolOrdering.hpp"
#include <assert.h>

View file

@ -4,10 +4,12 @@
#define slic3r_ToolOrdering_hpp_
#include "libslic3r.h"
#include "Print.hpp"
namespace Slic3r {
class Print;
class PrintObject;
class ToolOrdering
{
public:
@ -47,6 +49,8 @@ public:
// (print.config.complete_objects is false).
ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1);
void clear() { m_layer_tools.clear(); }
// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed.
unsigned int first_extruder() const;
@ -61,6 +65,7 @@ public:
const LayerTools& front() const { return m_layer_tools.front(); }
const LayerTools& back() const { return m_layer_tools.back(); }
bool empty() const { return m_layer_tools.empty(); }
const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
private:
void initialize_layers(std::vector<coordf_t> &zs);

View file

@ -3,6 +3,7 @@
#include <utility>
#include <string>
#include <vector>
namespace Slic3r
{
@ -20,6 +21,8 @@ public:
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
bool operator==(const xy &rhs) { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) { return x != rhs.x || y != rhs.y; }
float x;
float y;
};
@ -55,18 +58,50 @@ public:
PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE,
};
// Extrusion path of the wipe tower, for 3D preview of the generated tool paths.
struct Extrusion
{
Extrusion(const xy &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
// End position of this extrusion.
xy pos;
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width;
// Current extruder index.
unsigned int tool;
};
struct ToolChangeResult
{
// Print heigh of this tool change.
float print_z;
// G-code section to be directly included into the output G-code.
std::string gcode;
// For path preview.
std::vector<Extrusion> extrusions;
// Initial position, at which the wipe tower starts its action.
// At this position the extruder is loaded and there is no Z-hop applied.
xy start_pos;
// Last point, at which the normal G-code generator of Slic3r shall continue.
// At this position the extruder is loaded and there is no Z-hop applied.
xy end_pos;
// Time elapsed over this tool change.
// This is useful not only for the print time estimation, but also for the control of layer cooling.
float elapsed_time;
};
// 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, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
virtual ToolChangeResult 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(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
virtual ToolChangeResult 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,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const = 0;
virtual bool layer_finished() const = 0;
};
}; // namespace Slic3r

View file

@ -4,6 +4,7 @@
#include <math.h>
#include <fstream>
#include <iostream>
#include <vector>
#ifdef __linux
#include <strings.h>
@ -25,16 +26,34 @@ public:
m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
m_current_z(0.f),
m_current_feedrate(0.f),
m_extrusion_flow(0.f) {}
m_extrusion_flow(0.f),
m_layer_height(0.f),
m_preview_suppressed(false),
m_elapsed_time(0.f) {}
Writer& set_initial_position(const WipeTower::xy &pos) { m_current_pos = pos; return *this; }
Writer& set_initial_position(const WipeTower::xy &pos) {
m_start_pos = pos;
m_current_pos = pos;
return *this;
}
Writer& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; }
Writer& set_z(float z)
{ m_current_z = z; return *this; }
Writer& set_layer_height(float layer_height)
{ m_layer_height = layer_height; return *this; }
Writer& set_extrusion_flow(float flow)
{ m_extrusion_flow = flow; return *this; }
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
// is asked to suppres output of some lines, which look like extrusions.
Writer& suppress_preview() { m_preview_suppressed = true; return *this; }
Writer& resume_preview() { m_preview_suppressed = false; return *this; }
Writer& feedrate(float f)
{
if (f != m_current_feedrate)
@ -43,24 +62,48 @@ public:
}
const std::string& gcode() const { return m_gcode; }
const std::vector<WipeTower::Extrusion>& extrusions() const { return m_extrusions; }
float x() const { return m_current_pos.x; }
float y() const { return m_current_pos.y; }
const WipeTower::xy& start_pos() const { return m_start_pos; }
const WipeTower::xy& pos() const { return m_current_pos; }
float elapsed_time() const { return m_elapsed_time; }
// Extrude with an explicitely provided amount of extrusion.
Writer& extrude_explicit(float x, float y, float e, float f = 0.f)
{
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
return *this;
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
double len = sqrt(dx*dx+dy*dy);
if (! m_preview_suppressed && e > 0.f && len > 0.) {
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width = float(double(e) * m_filament_area / (len * m_layer_height));
// Correct for the roundings of a squished extrusion.
width += float(m_layer_height * (1. - M_PI / 4.));
if (m_extrusions.empty() || m_extrusions.back().pos != m_current_pos)
m_extrusions.emplace_back(WipeTower::Extrusion(m_current_pos, 0, m_current_tool));
m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(x, y), width, m_current_tool));
}
m_gcode += "G1";
if (x != m_current_pos.x)
m_gcode += set_format_X(x);
if (y != m_current_pos.y)
m_gcode += set_format_Y(y);
if (e != 0.f)
m_gcode += set_format_E(e);
if (f != 0.f && f != m_current_feedrate)
m_gcode += set_format_F(f);
// Update the elapsed time with a rough estimate.
m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
m_gcode += "\n";
return *this;
}
@ -146,6 +189,7 @@ public:
char buf[64];
sprintf(buf, "T%d\n", tool);
m_gcode += buf;
m_current_tool = tool;
return *this;
}
@ -222,11 +266,18 @@ public:
Writer& append(const char *text) { m_gcode += text; return *this; }
private:
WipeTower::xy m_start_pos;
WipeTower::xy m_current_pos;
float m_current_z;
float m_current_feedrate;
unsigned int m_current_tool;
float m_layer_height;
float m_extrusion_flow;
bool m_preview_suppressed;
std::string m_gcode;
std::vector<WipeTower::Extrusion> m_extrusions;
float m_elapsed_time;
const double m_filament_area = 0.25*M_PI*1.75*1.75;
std::string set_format_X(float x) {
char buf[64];
@ -260,6 +311,8 @@ private:
m_current_feedrate = f;
return buf;
}
Writer& operator=(const Writer &rhs);
};
} // namespace PrusaMultiMaterial
@ -287,7 +340,7 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
return INVALID;
}
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Purpose purpose)
WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(int tool, Purpose purpose)
{
// Either it is the last tool unload,
// or there must be a nonzero wipe tower partitions available.
@ -306,10 +359,12 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
PrusaMultiMaterial::Writer writer;
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_layer_height(m_layer_height)
.set_initial_tool(m_current_tool)
.append(";--------------------\n"
"; CP TOOLCHANGE START\n")
.comment_with_value(" toolchange #", m_num_tool_changes)
.comment_material(m_current_material)
.comment_material(m_material[m_current_tool])
.append(";--------------------\n")
.speed_override(100);
@ -318,7 +373,7 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
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;
float initial_retract = ((m_material[m_current_tool] == SCAFF) ? 1.f : 0.5f) * m_retract;
writer // Lift for a Z hop.
.z_hop(m_zhop, 7200)
// Additional retract on move to tower.
@ -339,7 +394,7 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
// Increase the extruder driver current to allow fast ramming.
writer.set_extruder_trimpot(750);
// 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_material[m_current_tool],
m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
if (tool >= 0) {
@ -379,10 +434,17 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
m_current_wipe_start_y += m_wipe_area;
}
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
ToolChangeResult result;
result.print_z = this->m_z_pos;
result.gcode = writer.gcode();
result.elapsed_time = writer.elapsed_time();
result.extrusions = writer.extrusions();
result.start_pos = writer.start_pos();
result.end_pos = writer.pos();
return result;
}
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
{
const box_coordinates wipeTower_box(
m_wipe_tower_pos,
@ -393,6 +455,8 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
// Let the writer know the current Z position as a base for Z-hop.
.set_z(m_z_pos)
.set_layer_height(m_layer_height)
.set_initial_tool(m_current_tool)
.append(
";-------------------------------------\n"
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
@ -453,7 +517,14 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose
m_idx_tool_change_in_layer = 0;
}
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
ToolChangeResult result;
result.print_z = this->m_z_pos;
result.gcode = writer.gcode();
result.elapsed_time = writer.elapsed_time();
result.extrusions = writer.extrusions();
result.start_pos = writer.start_pos();
result.end_pos = writer.pos();
return result;
}
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
@ -510,7 +581,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
std::swap(xl, xr);
// Horizontal cooling moves will be performed at the following Y coordinate:
writer.travel(xr, writer.y() + y_step * 0.8f, 7200);
writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
.suppress_preview();
switch (current_material)
{
case ABS:
@ -541,7 +613,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
.cool(xl, xr, 5, -3, 2400);
}
writer.flush_planner_queue();
writer.resume_preview()
.flush_planner_queue();
}
// Change the tool, set a speed override for solube and flex materials.
@ -561,7 +634,7 @@ void WipeTowerPrusaMM::toolchange_Change(
writer.set_tool(new_tool)
.speed_override(speed_override)
.flush_planner_queue();
m_current_material = new_material;
m_current_tool = new_tool;
}
void WipeTowerPrusaMM::toolchange_Load(
@ -574,10 +647,12 @@ void WipeTowerPrusaMM::toolchange_Load(
writer.append("; CP TOOLCHANGE LOAD\n")
// Load the filament while moving left / right,
// so the excess material will not create a blob at a single position.
.suppress_preview()
.load_move_x(xr, 20, 1400)
.load_move_x(xl, 40, 3000)
.load_move_x(xr, 20, 1600)
.load_move_x(xl, 10, 1000);
.load_move_x(xl, 10, 1000)
.resume_preview();
// Extrude first five lines (just three lines if colorInit is set).
writer.extrude(xr, writer.y(), 1600);
@ -634,7 +709,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
writer.set_extrusion_flow(m_extrusion_flow);
}
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose purpose)
WipeTower::ToolChangeResult 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.
@ -643,6 +718,8 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
PrusaMultiMaterial::Writer writer;
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_layer_height(m_layer_height)
.set_initial_tool(m_current_tool)
.append(";--------------------\n"
"; CP EMPTY GRID START\n")
// m_num_layer_changes is incremented by set_z, so it is 1 based.
@ -676,7 +753,7 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
} 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);
writer.set_initial_position(fill_box.ld);
}
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
@ -734,7 +811,14 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;
}
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
ToolChangeResult result;
result.print_z = this->m_z_pos;
result.gcode = writer.gcode();
result.elapsed_time = writer.elapsed_time();
result.extrusions = writer.extrusions();
result.start_pos = writer.start_pos();
result.end_pos = writer.pos();
return result;
}
}; // namespace Slic3r

View file

@ -39,11 +39,13 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTowerPrusaMM(float x, float y, float width, float wipe_area) :
WipeTowerPrusaMM(float x, float y, float width, float wipe_area, unsigned int initial_tool) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_area(wipe_area),
m_z_pos(0.f) {
m_z_pos(0.f),
m_current_tool(initial_tool)
{
for (size_t i = 0; i < 4; ++ i) {
// Extruder specific parameters.
m_material[i] = PLA;
@ -51,6 +53,7 @@ public:
m_first_layer_temperature[i] = 0;
}
}
virtual ~WipeTowerPrusaMM() {}
// _retract - retract value in mm
@ -81,6 +84,7 @@ public:
bool is_last_layer)
{
m_z_pos = print_z;
m_layer_height = layer_height;
m_max_color_changes = max_tool_changes;
m_is_first_layer = is_first_layer;
m_is_last_layer = is_last_layer;
@ -105,24 +109,24 @@ 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; }
// Return the wipe tower width.
virtual float width() const { return m_wipe_tower_width; }
virtual float width() const { return m_wipe_tower_width; }
// 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, Purpose purpose);
virtual ToolChangeResult 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(Purpose purpose);
virtual ToolChangeResult 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,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const
virtual bool layer_finished() const
{ return m_idx_tool_change_in_layer == m_max_color_changes; }
private:
@ -143,6 +147,8 @@ private:
float m_wipe_area;
// Current Z position.
float m_z_pos = 0.f;
// Current layer height.
float m_layer_height = 0.f;
// Maximum number of color changes per layer.
size_t m_max_color_changes = 0;
// Is this the 1st layer of the print? If so, print the brim around the waste tower.
@ -172,7 +178,7 @@ private:
unsigned int m_idx_tool_change_in_layer = 0;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
material_type m_current_material = PLA;
unsigned int m_current_tool = 0;
// Current y position at the wipe tower.
float m_current_wipe_start_y = 0.f;
@ -210,7 +216,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(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
ToolChangeResult toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload(
PrusaMultiMaterial::Writer &writer,