Merge branch 'lm_improving_wipe_tower'. Summary of changes:
- wipe tower respects max volumetric flow, slowing down with PVA etc is not hardcoded anymore
- wipe tower is now allowed for multiple-extruder printers. single extruder stuff is not used in that case (ramming, cooling, etc.)
- start/end filament and toolchange custom gcodes are now inserted differently than before - see 41164a9
- some refactoring, e.g. the abstract WipeTower class was eradicated
This commit is contained in:
commit
e674c586b0
12 changed files with 897 additions and 942 deletions
|
@ -81,9 +81,8 @@ add_library(libslic3r STATIC
|
|||
GCode/SpiralVase.hpp
|
||||
GCode/ToolOrdering.cpp
|
||||
GCode/ToolOrdering.hpp
|
||||
GCode/WipeTower.cpp
|
||||
GCode/WipeTower.hpp
|
||||
GCode/WipeTowerPrusaMM.cpp
|
||||
GCode/WipeTowerPrusaMM.hpp
|
||||
GCode.cpp
|
||||
GCode.hpp
|
||||
GCodeReader.cpp
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "EdgeGrid.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "GCode/PrintExtents.hpp"
|
||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -162,9 +162,9 @@ std::string Wipe::wipe(GCode &gcodegen, bool toolchange)
|
|||
return gcode;
|
||||
}
|
||||
|
||||
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
|
||||
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt)
|
||||
{
|
||||
return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1)));
|
||||
return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1)));
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
|
||||
|
@ -174,47 +174,97 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin
|
||||
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
|
||||
float alpha = m_wipe_tower_rotation/180.f * float(M_PI);
|
||||
WipeTower::xy start_pos = tcr.start_pos;
|
||||
WipeTower::xy end_pos = tcr.end_pos;
|
||||
start_pos.rotate(alpha);
|
||||
start_pos.translate(m_wipe_tower_pos);
|
||||
end_pos.rotate(alpha);
|
||||
end_pos.translate(m_wipe_tower_pos);
|
||||
std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
|
||||
Vec2f start_pos = tcr.start_pos;
|
||||
Vec2f end_pos = tcr.end_pos;
|
||||
if (!tcr.priming) {
|
||||
start_pos = Eigen::Rotation2Df(alpha) * start_pos;
|
||||
start_pos += m_wipe_tower_pos;
|
||||
end_pos = Eigen::Rotation2Df(alpha) * end_pos;
|
||||
end_pos += m_wipe_tower_pos;
|
||||
}
|
||||
std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
|
||||
|
||||
|
||||
// Disable linear advance for the wipe tower operations.
|
||||
gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
|
||||
// Move over the wipe tower.
|
||||
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
||||
gcode += gcodegen.retract(true);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
||||
erMixed,
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += tcr_rotated_gcode;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
|
||||
gcodegen.writer().toolchange(new_extruder_id);
|
||||
if (!tcr.priming) {
|
||||
// Move over the wipe tower.
|
||||
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
||||
gcode += gcodegen.retract(true);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
||||
erMixed,
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
}
|
||||
|
||||
|
||||
// Process the end filament gcode.
|
||||
std::string end_filament_gcode_str;
|
||||
if (gcodegen.writer().extruder() != nullptr) {
|
||||
// Process the custom end_filament_gcode in case of single_extruder_multi_material.
|
||||
unsigned int old_extruder_id = gcodegen.writer().extruder()->id();
|
||||
const std::string &end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id);
|
||||
if (gcodegen.writer().extruder() != nullptr && ! end_filament_gcode.empty()) {
|
||||
end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
|
||||
check_add_eol(end_filament_gcode_str);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament.
|
||||
// Otherwise, leave control to the user completely.
|
||||
std::string toolchange_gcode_str;
|
||||
if (true /*gcodegen.writer().extruder() != nullptr*/) {
|
||||
const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value;
|
||||
if (!toolchange_gcode.empty()) {
|
||||
DynamicConfig config;
|
||||
int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1;
|
||||
config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id));
|
||||
config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id));
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
|
||||
toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
|
||||
check_add_eol(toolchange_gcode_str);
|
||||
}
|
||||
|
||||
std::string toolchange_command;
|
||||
if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)))
|
||||
toolchange_command = gcodegen.writer().toolchange(new_extruder_id);
|
||||
if (toolchange_gcode.empty())
|
||||
toolchange_gcode_str = toolchange_command;
|
||||
else {
|
||||
// We have informed the m_writer about the current extruder_id, we can ignore the generated G-code.
|
||||
}
|
||||
}
|
||||
|
||||
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
|
||||
|
||||
// Always append the filament start G-code even if the extruder did not switch,
|
||||
// because the wipe tower resets the linear advance and we want it to be re-enabled.
|
||||
// Process the start filament gcode.
|
||||
std::string start_filament_gcode_str;
|
||||
const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id);
|
||||
if (! start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the active filament only.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
|
||||
gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
|
||||
check_add_eol(gcode);
|
||||
start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
|
||||
check_add_eol(start_filament_gcode_str);
|
||||
}
|
||||
|
||||
// Insert the end filament, toolchange, and start filament gcode into the generated gcode.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str));
|
||||
config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str));
|
||||
config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str));
|
||||
std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config);
|
||||
unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
|
||||
gcode += tcr_gcode;
|
||||
check_add_eol(toolchange_gcode_str);
|
||||
|
||||
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y));
|
||||
gcodegen.writer().travel_to_xy(end_pos.cast<double>());
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
|
||||
// Prepare a future wipe.
|
||||
|
@ -224,8 +274,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
|
||||
end_pos.y)));
|
||||
Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left,
|
||||
end_pos.y())));
|
||||
}
|
||||
|
||||
// Let the planner know we are traveling between objects.
|
||||
|
@ -235,14 +285,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
|
||||
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
|
||||
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
|
||||
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
|
||||
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const
|
||||
{
|
||||
std::istringstream gcode_str(gcode_original);
|
||||
std::string gcode_out;
|
||||
std::string line;
|
||||
WipeTower::xy pos = start_pos;
|
||||
WipeTower::xy transformed_pos;
|
||||
WipeTower::xy old_pos(-1000.1f, -1000.1f);
|
||||
Vec2f pos = start_pos;
|
||||
Vec2f transformed_pos;
|
||||
Vec2f old_pos(-1000.1f, -1000.1f);
|
||||
|
||||
while (gcode_str) {
|
||||
std::getline(gcode_str, line); // we read the gcode line by line
|
||||
|
@ -253,25 +303,25 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
|
|||
char ch = 0;
|
||||
while (line_str >> ch) {
|
||||
if (ch == 'X')
|
||||
line_str >> pos.x;
|
||||
line_str >> pos.x();
|
||||
else
|
||||
if (ch == 'Y')
|
||||
line_str >> pos.y;
|
||||
line_str >> pos.y();
|
||||
else
|
||||
line_out << ch;
|
||||
}
|
||||
|
||||
transformed_pos = pos;
|
||||
transformed_pos.rotate(angle);
|
||||
transformed_pos.translate(translation);
|
||||
transformed_pos = Eigen::Rotation2Df(angle) * transformed_pos;
|
||||
transformed_pos += translation;
|
||||
|
||||
if (transformed_pos != old_pos) {
|
||||
line = line_out.str();
|
||||
char buf[2048] = "G1";
|
||||
if (transformed_pos.x != old_pos.x)
|
||||
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
|
||||
if (transformed_pos.y != old_pos.y)
|
||||
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
|
||||
if (transformed_pos.x() != old_pos.x())
|
||||
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x());
|
||||
if (transformed_pos.y() != old_pos.y())
|
||||
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y());
|
||||
|
||||
line.replace(line.find("G1 "), 3, buf);
|
||||
old_pos = transformed_pos;
|
||||
|
@ -288,27 +338,36 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
|||
assert(m_layer_idx == 0);
|
||||
std::string gcode;
|
||||
|
||||
if (&m_priming != nullptr && ! m_priming.extrusions.empty()) {
|
||||
if (&m_priming != nullptr) {
|
||||
// Disable linear advance for the wipe tower operations.
|
||||
gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += m_priming.gcode;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
unsigned int current_extruder_id = m_priming.extrusions.back().tool;
|
||||
gcodegen.writer().toolchange(current_extruder_id);
|
||||
gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
|
||||
//gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
|
||||
|
||||
for (const WipeTower::ToolChangeResult& tcr : m_priming) {
|
||||
if (!tcr.extrusions.empty())
|
||||
gcode += append_tcr(gcodegen, tcr, tcr.new_tool);
|
||||
|
||||
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
//gcode += tcr.gcode;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
// unsigned int current_extruder_id = tcr.extrusions.back().tool;
|
||||
// gcodegen.writer().toolchange(current_extruder_id);
|
||||
// gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
|
||||
|
||||
}
|
||||
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
|
||||
/* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
|
||||
// Prepare a future wipe.
|
||||
gcodegen.m_wipe.path.points.clear();
|
||||
// Start the wipe at the current position.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
|
||||
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(m_left - m_priming.end_pos.x) < std::abs(m_right - m_priming.end_pos.x)) ? m_right : m_left,
|
||||
m_priming.end_pos.y)));
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left,
|
||||
m_priming.back().end_pos.y)));*/
|
||||
}
|
||||
return gcode;
|
||||
}
|
||||
|
@ -807,24 +866,16 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
|
||||
// Write the custom start G-code
|
||||
_writeln(file, start_gcode);
|
||||
// Process filament-specific gcode in extruder order.
|
||||
if (print.config().single_extruder_multi_material) {
|
||||
if (has_wipe_tower) {
|
||||
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
|
||||
} else {
|
||||
// Only initialize the initial extruder.
|
||||
|
||||
// Process filament-specific gcode.
|
||||
/* if (has_wipe_tower) {
|
||||
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
|
||||
} else {
|
||||
DynamicConfig config;
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
|
||||
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
|
||||
}
|
||||
} else {
|
||||
DynamicConfig config;
|
||||
for (const std::string &start_gcode : print.config().start_filament_gcode.values) {
|
||||
int extruder_id = (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front());
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
|
||||
_writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, extruder_id, &config));
|
||||
}
|
||||
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
|
||||
}
|
||||
*/
|
||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
|
||||
print.throw_if_canceled();
|
||||
|
||||
|
@ -2563,7 +2614,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
gcode += buf;
|
||||
}
|
||||
|
||||
if (m_last_mm3_per_mm != path.mm3_per_mm)
|
||||
if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm))
|
||||
{
|
||||
m_last_mm3_per_mm = path.mm3_per_mm;
|
||||
|
||||
|
@ -2741,38 +2792,58 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
|
|||
m_wipe.reset_path();
|
||||
|
||||
if (m_writer.extruder() != nullptr) {
|
||||
// Process the custom end_filament_gcode in case of single_extruder_multi_material.
|
||||
// Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower
|
||||
// so it should not be injected twice.
|
||||
unsigned int old_extruder_id = m_writer.extruder()->id();
|
||||
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
|
||||
if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
|
||||
if (! end_filament_gcode.empty()) {
|
||||
gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
}
|
||||
|
||||
m_placeholder_parser.set("current_extruder", extruder_id);
|
||||
|
||||
if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) {
|
||||
// Process the custom toolchange_gcode.
|
||||
DynamicConfig config;
|
||||
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
|
||||
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||
gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
|
||||
// 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() != nullptr)
|
||||
gcode += m_ooze_prevention.pre_toolchange(*this);
|
||||
// Append the toolchange command.
|
||||
gcode += m_writer.toolchange(extruder_id);
|
||||
// Append the filament start G-code for single_extruder_multi_material.
|
||||
|
||||
const std::string& toolchange_gcode = m_config.toolchange_gcode.value;
|
||||
if (m_writer.extruder() != nullptr) {
|
||||
// Process the custom toolchange_gcode. If it is empty, insert just a Tn command.
|
||||
if (!toolchange_gcode.empty()) {
|
||||
DynamicConfig config;
|
||||
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
|
||||
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||
gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config);
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
}
|
||||
|
||||
// We inform the writer about what is happening, but we may not use the resulting gcode.
|
||||
std::string toolchange_command = m_writer.toolchange(extruder_id);
|
||||
if (toolchange_gcode.empty())
|
||||
gcode += toolchange_command;
|
||||
else {
|
||||
// user provided his own toolchange gcode, no need to do anything
|
||||
}
|
||||
|
||||
// Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
|
||||
if (m_config.single_extruder_multi_material && !m_config.wipe_tower) {
|
||||
int temp = (m_layer_index == 0 ? m_config.first_layer_temperature.get_at(extruder_id) :
|
||||
m_config.temperature.get_at(extruder_id));
|
||||
|
||||
gcode += m_writer.set_temperature(temp, false);
|
||||
}
|
||||
|
||||
m_placeholder_parser.set("current_extruder", extruder_id);
|
||||
|
||||
// Append the filament start G-code.
|
||||
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
|
||||
if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the active filament only.
|
||||
if (! start_filament_gcode.empty()) {
|
||||
// Process the start_filament_gcode for the new filament.
|
||||
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
|
||||
check_add_eol(gcode);
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class WipeTowerIntegration {
|
|||
public:
|
||||
WipeTowerIntegration(
|
||||
const PrintConfig &print_config,
|
||||
const WipeTower::ToolChangeResult &priming,
|
||||
const std::vector<WipeTower::ToolChangeResult> &priming,
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
|
||||
const WipeTower::ToolChangeResult &final_purge) :
|
||||
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
|
||||
|
@ -108,15 +108,15 @@ private:
|
|||
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
|
||||
|
||||
// Postprocesses gcode: rotates and moves all G1 extrusions and returns result
|
||||
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
|
||||
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const;
|
||||
|
||||
// Left / right edges of the wipe tower, for the planning of wipe moves.
|
||||
const float m_left;
|
||||
const float m_right;
|
||||
const WipeTower::xy m_wipe_tower_pos;
|
||||
const Vec2f m_wipe_tower_pos;
|
||||
const float m_wipe_tower_rotation;
|
||||
// Reference to cached values at the Printer class.
|
||||
const WipeTower::ToolChangeResult &m_priming;
|
||||
const std::vector<WipeTower::ToolChangeResult> &m_priming;
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
|
||||
const WipeTower::ToolChangeResult &m_final_purge;
|
||||
// Current layer index.
|
||||
|
|
|
@ -149,8 +149,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
|
|||
const WipeTower::Extrusion &e = tcr.extrusions[i];
|
||||
if (e.width > 0) {
|
||||
Vec2d delta = 0.5 * Vec2d(e.width, e.width);
|
||||
Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y);
|
||||
Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y);
|
||||
Vec2d p1 = trafo * (&e - 1)->pos.cast<double>();
|
||||
Vec2d p2 = trafo * e.pos.cast<double>();
|
||||
bbox.merge(p1.cwiseMin(p2) - delta);
|
||||
bbox.merge(p1.cwiseMax(p2) + delta);
|
||||
}
|
||||
|
@ -165,18 +165,19 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
|
|||
{
|
||||
BoundingBoxf bbox;
|
||||
if (print.wipe_tower_data().priming != nullptr) {
|
||||
const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming;
|
||||
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
|
||||
const WipeTower::Extrusion &e = tcr.extrusions[i];
|
||||
if (e.width > 0) {
|
||||
Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y);
|
||||
Vec2d p2(e.pos.x, e.pos.y);
|
||||
bbox.merge(p1);
|
||||
coordf_t radius = 0.5 * e.width;
|
||||
bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
|
||||
bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
|
||||
bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
|
||||
bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
|
||||
for (const WipeTower::ToolChangeResult &tcr : *print.wipe_tower_data().priming) {
|
||||
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
|
||||
const WipeTower::Extrusion &e = tcr.extrusions[i];
|
||||
if (e.width > 0) {
|
||||
const Vec2d& p1 = (&e - 1)->pos.cast<double>();
|
||||
const Vec2d& p2 = e.pos.cast<double>();
|
||||
bbox.merge(p1);
|
||||
coordf_t radius = 0.5 * e.width;
|
||||
bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
|
||||
bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
|
||||
bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
|
||||
bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,95 +1,30 @@
|
|||
#ifndef slic3r_WipeTower_hpp_
|
||||
#define slic3r_WipeTower_hpp_
|
||||
#ifndef WipeTower_
|
||||
#define WipeTower_
|
||||
|
||||
#include <math.h>
|
||||
#include <utility>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
// A pure virtual WipeTower definition.
|
||||
class WipeTowerWriter;
|
||||
|
||||
|
||||
|
||||
class WipeTower
|
||||
{
|
||||
public:
|
||||
// Internal point class, to make the wipe tower independent from other slic3r modules.
|
||||
// This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r.
|
||||
struct xy
|
||||
struct Extrusion
|
||||
{
|
||||
xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
|
||||
xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
|
||||
xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
|
||||
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) const { return x == rhs.x && y == rhs.y; }
|
||||
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
|
||||
|
||||
// Rotate the point around center of the wipe tower about given angle (in degrees)
|
||||
xy rotate(float width, float depth, float angle) const {
|
||||
xy out(0,0);
|
||||
float temp_x = x - width / 2.f;
|
||||
float temp_y = y - depth / 2.f;
|
||||
angle *= float(M_PI/180.);
|
||||
out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
|
||||
out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Rotate the point around origin about given angle in degrees
|
||||
void rotate(float angle) {
|
||||
float temp_x = x * cos(angle) - y * sin(angle);
|
||||
y = x * sin(angle) + y * cos(angle);
|
||||
x = temp_x;
|
||||
}
|
||||
|
||||
void translate(const xy& vect) {
|
||||
x += vect.x;
|
||||
y += vect.y;
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
WipeTower() {}
|
||||
virtual ~WipeTower() {}
|
||||
|
||||
// Return the wipe tower position.
|
||||
virtual const xy& position() const = 0;
|
||||
|
||||
// Return the wipe tower width.
|
||||
virtual float width() const = 0;
|
||||
|
||||
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
|
||||
virtual bool finished() const = 0;
|
||||
|
||||
// Switch to a next layer.
|
||||
virtual void set_layer(
|
||||
// Print height of this layer.
|
||||
float print_z,
|
||||
// Layer height, used to calculate extrusion the rate.
|
||||
float layer_height,
|
||||
// Maximum number of tool changes on this layer or the layers below.
|
||||
size_t max_tool_changes,
|
||||
// Is this the first layer of the print? In that case print the brim first.
|
||||
bool is_first_layer,
|
||||
// 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,
|
||||
};
|
||||
|
||||
// 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) {}
|
||||
Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
|
||||
// End position of this extrusion.
|
||||
xy pos;
|
||||
Vec2f 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;
|
||||
|
@ -108,10 +43,10 @@ public:
|
|||
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;
|
||||
Vec2f 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;
|
||||
Vec2f 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;
|
||||
|
@ -119,50 +54,375 @@ public:
|
|||
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
|
||||
bool priming;
|
||||
|
||||
// Initial tool
|
||||
int initial_tool;
|
||||
|
||||
// New tool
|
||||
int new_tool;
|
||||
|
||||
// Sum the total length of the extrusion.
|
||||
float total_extrusion_length_in_plane() {
|
||||
float e_length = 0.f;
|
||||
for (size_t i = 1; i < this->extrusions.size(); ++ i) {
|
||||
const Extrusion &e = this->extrusions[i];
|
||||
if (e.width > 0) {
|
||||
xy v = e.pos - (&e - 1)->pos;
|
||||
e_length += sqrt(v.x*v.x+v.y*v.y);
|
||||
Vec2f v = e.pos - (&e - 1)->pos;
|
||||
e_length += v.norm();
|
||||
}
|
||||
}
|
||||
return e_length;
|
||||
}
|
||||
};
|
||||
|
||||
// x -- x coordinates of wipe tower in mm ( left bottom corner )
|
||||
// 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
|
||||
WipeTower(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
||||
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
|
||||
float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
|
||||
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
||||
m_semm(semm),
|
||||
m_wipe_tower_pos(x, y),
|
||||
m_wipe_tower_width(width),
|
||||
m_wipe_tower_rotation_angle(rotation_angle),
|
||||
m_y_shift(0.f),
|
||||
m_z_pos(0.f),
|
||||
m_is_first_layer(false),
|
||||
m_gcode_flavor(flavor),
|
||||
m_bridging(bridging),
|
||||
m_current_tool(initial_tool),
|
||||
wipe_volumes(wiping_matrix)
|
||||
{
|
||||
// If this is a single extruder MM printer, we will use all the SE-specific config values.
|
||||
// Otherwise, the defaults will be used to turn off the SE stuff.
|
||||
if (m_semm) {
|
||||
m_cooling_tube_retraction = cooling_tube_retraction;
|
||||
m_cooling_tube_length = cooling_tube_length;
|
||||
m_parking_pos_retraction = parking_pos_retraction;
|
||||
m_extra_loading_move = extra_loading_move;
|
||||
m_set_extruder_trimpot = set_extruder_trimpot;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~WipeTower() {}
|
||||
|
||||
|
||||
// Set the extruder properties.
|
||||
void set_extruder(size_t idx, std::string material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
|
||||
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
|
||||
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed, float nozzle_diameter)
|
||||
{
|
||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = material;
|
||||
m_filpar[idx].temperature = temp;
|
||||
m_filpar[idx].first_layer_temperature = first_layer_temp;
|
||||
|
||||
// If this is a single extruder MM printer, we will use all the SE-specific config values.
|
||||
// Otherwise, the defaults will be used to turn off the SE stuff.
|
||||
if (m_semm) {
|
||||
m_filpar[idx].loading_speed = loading_speed;
|
||||
m_filpar[idx].loading_speed_start = loading_speed_start;
|
||||
m_filpar[idx].unloading_speed = unloading_speed;
|
||||
m_filpar[idx].unloading_speed_start = unloading_speed_start;
|
||||
m_filpar[idx].delay = delay;
|
||||
m_filpar[idx].cooling_moves = cooling_moves;
|
||||
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
|
||||
m_filpar[idx].cooling_final_speed = cooling_final_speed;
|
||||
}
|
||||
|
||||
if (max_volumetric_speed != 0.f)
|
||||
m_filpar[idx].max_e_speed = (max_volumetric_speed / Filament_Area);
|
||||
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
||||
|
||||
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
|
||||
|
||||
std::stringstream stream{m_semm ? ramming_parameters : std::string()};
|
||||
float speed = 0.f;
|
||||
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
|
||||
m_filpar[idx].ramming_line_width_multiplicator /= 100;
|
||||
m_filpar[idx].ramming_step_multiplicator /= 100;
|
||||
while (stream >> speed)
|
||||
m_filpar[idx].ramming_speed.push_back(speed);
|
||||
|
||||
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
|
||||
}
|
||||
|
||||
|
||||
// Appends into internal structure m_plan containing info about the future wipe tower
|
||||
// to be used before building begins. The entries must be added ordered in z.
|
||||
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
|
||||
|
||||
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
||||
void generate(std::vector<std::vector<ToolChangeResult>> &result);
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
|
||||
|
||||
|
||||
// Switch to a next layer.
|
||||
void set_layer(
|
||||
// Print height of this layer.
|
||||
float print_z,
|
||||
// Layer height, used to calculate extrusion the rate.
|
||||
float layer_height,
|
||||
// Maximum number of tool changes on this layer or the layers below.
|
||||
size_t max_tool_changes,
|
||||
// Is this the first layer of the print? In that case print the brim first.
|
||||
bool is_first_layer,
|
||||
// Is this the last layer of the waste tower?
|
||||
bool is_last_layer)
|
||||
{
|
||||
m_z_pos = print_z;
|
||||
m_layer_height = layer_height;
|
||||
m_is_first_layer = is_first_layer;
|
||||
m_print_brim = is_first_layer;
|
||||
m_depth_traversed = 0.f;
|
||||
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
|
||||
if (is_first_layer) {
|
||||
this->m_num_layer_changes = 0;
|
||||
this->m_num_tool_changes = 0;
|
||||
}
|
||||
else
|
||||
++ m_num_layer_changes;
|
||||
|
||||
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
|
||||
m_extrusion_flow = extrusion_flow(layer_height);
|
||||
|
||||
// Advance m_layer_info iterator, making sure we got it right
|
||||
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
|
||||
++m_layer_info;
|
||||
}
|
||||
|
||||
// Return the wipe tower position.
|
||||
const Vec2f& position() const { return m_wipe_tower_pos; }
|
||||
// Return the wipe tower width.
|
||||
float width() const { return m_wipe_tower_width; }
|
||||
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
|
||||
bool finished() const { return m_max_color_changes == 0; }
|
||||
|
||||
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
||||
virtual ToolChangeResult prime(
|
||||
std::vector<ToolChangeResult> prime(
|
||||
// print_z of the first layer.
|
||||
float first_layer_height,
|
||||
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
||||
const std::vector<unsigned int> &tools,
|
||||
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
|
||||
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||
bool last_wipe_inside_wipe_tower) = 0;
|
||||
bool last_wipe_inside_wipe_tower);
|
||||
|
||||
// Returns gcode for toolchange and the end position.
|
||||
// if new_tool == -1, just unload the current filament over the wipe tower.
|
||||
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 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.
|
||||
ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
|
||||
|
||||
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
|
||||
// Fill the unfilled space with a sparse infill.
|
||||
// Call this method only if layer_finished() is false.
|
||||
virtual ToolChangeResult finish_layer() = 0;
|
||||
ToolChangeResult finish_layer();
|
||||
|
||||
// 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;
|
||||
// Is the current layer finished?
|
||||
bool layer_finished() const {
|
||||
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
|
||||
}
|
||||
|
||||
// Returns used filament length per extruder:
|
||||
virtual std::vector<float> get_used_filament() const = 0;
|
||||
std::vector<float> get_used_filament() const { return m_used_filament_length; }
|
||||
int get_number_of_toolchanges() const { return m_num_tool_changes; }
|
||||
|
||||
// Returns total number of toolchanges:
|
||||
virtual int get_number_of_toolchanges() const = 0;
|
||||
struct FilamentParameters {
|
||||
std::string material = "PLA";
|
||||
int temperature = 0;
|
||||
int first_layer_temperature = 0;
|
||||
float loading_speed = 0.f;
|
||||
float loading_speed_start = 0.f;
|
||||
float unloading_speed = 0.f;
|
||||
float unloading_speed_start = 0.f;
|
||||
float delay = 0.f ;
|
||||
int cooling_moves = 0;
|
||||
float cooling_initial_speed = 0.f;
|
||||
float cooling_final_speed = 0.f;
|
||||
float ramming_line_width_multiplicator = 0.f;
|
||||
float ramming_step_multiplicator = 0.f;
|
||||
float max_e_speed = std::numeric_limits<float>::max();
|
||||
std::vector<float> ramming_speed;
|
||||
float nozzle_diameter;
|
||||
};
|
||||
|
||||
private:
|
||||
WipeTower();
|
||||
|
||||
enum wipe_shape // A fill-in direction
|
||||
{
|
||||
SHAPE_NORMAL = 1,
|
||||
SHAPE_REVERSED = -1
|
||||
};
|
||||
|
||||
|
||||
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
|
||||
const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
|
||||
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
|
||||
const float WT_EPSILON = 1e-3f;
|
||||
|
||||
|
||||
bool m_semm = true; // Are we using a single extruder multimaterial printer?
|
||||
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
|
||||
float m_wipe_tower_width; // Width of the wipe tower.
|
||||
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
||||
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
|
||||
float m_internal_rotation = 0.f;
|
||||
float m_y_shift = 0.f; // y shift passed to writer
|
||||
float m_z_pos = 0.f; // Current Z position.
|
||||
float m_layer_height = 0.f; // Current layer height.
|
||||
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
|
||||
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
|
||||
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
|
||||
|
||||
// G-code generator parameters.
|
||||
float m_cooling_tube_retraction = 0.f;
|
||||
float m_cooling_tube_length = 0.f;
|
||||
float m_parking_pos_retraction = 0.f;
|
||||
float m_extra_loading_move = 0.f;
|
||||
float m_bridging = 0.f;
|
||||
bool m_set_extruder_trimpot = false;
|
||||
bool m_adhesion = true;
|
||||
GCodeFlavor m_gcode_flavor;
|
||||
|
||||
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
|
||||
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
|
||||
|
||||
// Extruder specific parameters.
|
||||
std::vector<FilamentParameters> m_filpar;
|
||||
|
||||
|
||||
// State of the wipe tower generator.
|
||||
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
|
||||
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
|
||||
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
|
||||
bool m_print_brim = true;
|
||||
// A fill-in direction (positive Y, negative Y) alternates with each layer.
|
||||
wipe_shape m_current_shape = SHAPE_NORMAL;
|
||||
unsigned int m_current_tool = 0;
|
||||
const std::vector<std::vector<float>> wipe_volumes;
|
||||
|
||||
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
|
||||
bool m_left_to_right = true;
|
||||
float m_extra_spacing = 1.f;
|
||||
|
||||
// Calculates extrusion flow needed to produce required line width for given layer height
|
||||
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
||||
{
|
||||
if ( layer_height < 0 )
|
||||
return m_extrusion_flow;
|
||||
return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
|
||||
}
|
||||
|
||||
// Calculates length of extrusion line to extrude given volume
|
||||
float volume_to_length(float volume, float line_width, float layer_height) const {
|
||||
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
|
||||
}
|
||||
|
||||
// Calculates depth for all layers and propagates them downwards
|
||||
void plan_tower();
|
||||
|
||||
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
|
||||
void make_wipe_tower_square();
|
||||
|
||||
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
|
||||
void save_on_last_wipe();
|
||||
|
||||
|
||||
struct box_coordinates
|
||||
{
|
||||
box_coordinates(float left, float bottom, float width, float height) :
|
||||
ld(left , bottom ),
|
||||
lu(left , bottom + height),
|
||||
rd(left + width, bottom ),
|
||||
ru(left + width, bottom + height) {}
|
||||
box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
|
||||
void translate(const Vec2f &shift) {
|
||||
ld += shift; lu += shift;
|
||||
rd += shift; ru += shift;
|
||||
}
|
||||
void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
|
||||
void expand(const float offset) {
|
||||
ld += Vec2f(- offset, - offset);
|
||||
lu += Vec2f(- offset, offset);
|
||||
rd += Vec2f( offset, - offset);
|
||||
ru += Vec2f( offset, offset);
|
||||
}
|
||||
void expand(const float offset_x, const float offset_y) {
|
||||
ld += Vec2f(- offset_x, - offset_y);
|
||||
lu += Vec2f(- offset_x, offset_y);
|
||||
rd += Vec2f( offset_x, - offset_y);
|
||||
ru += Vec2f( offset_x, offset_y);
|
||||
}
|
||||
Vec2f ld; // left down
|
||||
Vec2f lu; // left upper
|
||||
Vec2f rd; // right lower
|
||||
Vec2f ru; // right upper
|
||||
};
|
||||
|
||||
|
||||
// to store information about tool changes for a given layer
|
||||
struct WipeTowerInfo{
|
||||
struct ToolChange {
|
||||
unsigned int old_tool;
|
||||
unsigned int new_tool;
|
||||
float required_depth;
|
||||
float ramming_depth;
|
||||
float first_wipe_line;
|
||||
float wipe_volume;
|
||||
ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
|
||||
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
|
||||
};
|
||||
float z; // z position of the layer
|
||||
float height; // layer height
|
||||
float depth; // depth of the layer based on all layers above
|
||||
float extra_spacing;
|
||||
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
|
||||
|
||||
std::vector<ToolChange> tool_changes;
|
||||
|
||||
WipeTowerInfo(float z_par, float layer_height_par)
|
||||
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
|
||||
};
|
||||
|
||||
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
|
||||
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
|
||||
|
||||
// Stores information about used filament length per extruder:
|
||||
std::vector<float> m_used_filament_length;
|
||||
|
||||
|
||||
// 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
|
||||
ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
|
||||
|
||||
void toolchange_Unload(
|
||||
WipeTowerWriter &writer,
|
||||
const box_coordinates &cleaning_box,
|
||||
const std::string& current_material,
|
||||
const int new_temperature);
|
||||
|
||||
void toolchange_Change(
|
||||
WipeTowerWriter &writer,
|
||||
const unsigned int new_tool,
|
||||
const std::string& new_material);
|
||||
|
||||
void toolchange_Load(
|
||||
WipeTowerWriter &writer,
|
||||
const box_coordinates &cleaning_box);
|
||||
|
||||
void toolchange_Wipe(
|
||||
WipeTowerWriter &writer,
|
||||
const box_coordinates &cleaning_box,
|
||||
float wipe_volume);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_WipeTower_hpp_ */
|
||||
#endif // WipeTowerPrusaMM_hpp_
|
||||
|
|
|
@ -1,388 +0,0 @@
|
|||
#ifndef WipeTowerPrusaMM_hpp_
|
||||
#define WipeTowerPrusaMM_hpp_
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include "WipeTower.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
namespace PrusaMultiMaterial {
|
||||
class Writer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class WipeTowerPrusaMM : public WipeTower
|
||||
{
|
||||
public:
|
||||
enum material_type
|
||||
{
|
||||
INVALID = -1,
|
||||
PLA = 0, // E:210C B:55C
|
||||
ABS = 1, // E:255C B:100C
|
||||
PET = 2, // E:240C B:90C
|
||||
HIPS = 3, // E:220C B:100C
|
||||
FLEX = 4, // E:245C B:80C
|
||||
SCAFF = 5, // E:215C B:55C
|
||||
EDGE = 6, // E:240C B:80C
|
||||
NGEN = 7, // E:230C B:80C
|
||||
PVA = 8, // E:210C B:80C
|
||||
PC = 9
|
||||
};
|
||||
|
||||
// Parse material name into material_type.
|
||||
static material_type parse_material(const char *name);
|
||||
static std::string to_string(material_type material);
|
||||
|
||||
// x -- x coordinates of wipe tower in mm ( left bottom corner )
|
||||
// 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 rotation_angle, float cooling_tube_retraction,
|
||||
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
|
||||
float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
|
||||
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
||||
m_wipe_tower_pos(x, y),
|
||||
m_wipe_tower_width(width),
|
||||
m_wipe_tower_rotation_angle(rotation_angle),
|
||||
m_y_shift(0.f),
|
||||
m_z_pos(0.f),
|
||||
m_is_first_layer(false),
|
||||
m_cooling_tube_retraction(cooling_tube_retraction),
|
||||
m_cooling_tube_length(cooling_tube_length),
|
||||
m_parking_pos_retraction(parking_pos_retraction),
|
||||
m_extra_loading_move(extra_loading_move),
|
||||
m_bridging(bridging),
|
||||
m_set_extruder_trimpot(set_extruder_trimpot),
|
||||
m_gcode_flavor(flavor),
|
||||
m_current_tool(initial_tool),
|
||||
wipe_volumes(wiping_matrix)
|
||||
{}
|
||||
|
||||
virtual ~WipeTowerPrusaMM() {}
|
||||
|
||||
|
||||
// Set the extruder properties.
|
||||
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
|
||||
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
|
||||
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
|
||||
{
|
||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = material;
|
||||
if (material == FLEX || material == SCAFF || material == PVA) {
|
||||
// MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials.
|
||||
// Therefore it does not make sense to use the new M220 B and M220 R (backup / restore).
|
||||
m_retain_speed_override = false;
|
||||
}
|
||||
m_filpar[idx].temperature = temp;
|
||||
m_filpar[idx].first_layer_temperature = first_layer_temp;
|
||||
m_filpar[idx].loading_speed = loading_speed;
|
||||
m_filpar[idx].loading_speed_start = loading_speed_start;
|
||||
m_filpar[idx].unloading_speed = unloading_speed;
|
||||
m_filpar[idx].unloading_speed_start = unloading_speed_start;
|
||||
m_filpar[idx].delay = delay;
|
||||
m_filpar[idx].cooling_moves = cooling_moves;
|
||||
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
|
||||
m_filpar[idx].cooling_final_speed = cooling_final_speed;
|
||||
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
||||
|
||||
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
|
||||
|
||||
std::stringstream stream{ramming_parameters};
|
||||
float speed = 0.f;
|
||||
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
|
||||
m_filpar[idx].ramming_line_width_multiplicator /= 100;
|
||||
m_filpar[idx].ramming_step_multiplicator /= 100;
|
||||
while (stream >> speed)
|
||||
m_filpar[idx].ramming_speed.push_back(speed);
|
||||
|
||||
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
|
||||
}
|
||||
|
||||
|
||||
// Appends into internal structure m_plan containing info about the future wipe tower
|
||||
// to be used before building begins. The entries must be added ordered in z.
|
||||
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
|
||||
|
||||
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
||||
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
|
||||
|
||||
|
||||
// Switch to a next layer.
|
||||
virtual void set_layer(
|
||||
// Print height of this layer.
|
||||
float print_z,
|
||||
// Layer height, used to calculate extrusion the rate.
|
||||
float layer_height,
|
||||
// Maximum number of tool changes on this layer or the layers below.
|
||||
size_t max_tool_changes,
|
||||
// Is this the first layer of the print? In that case print the brim first.
|
||||
bool is_first_layer,
|
||||
// Is this the last layer of the waste tower?
|
||||
bool is_last_layer)
|
||||
{
|
||||
m_z_pos = print_z;
|
||||
m_layer_height = layer_height;
|
||||
m_is_first_layer = is_first_layer;
|
||||
m_print_brim = is_first_layer;
|
||||
m_depth_traversed = 0.f;
|
||||
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
|
||||
if (is_first_layer) {
|
||||
this->m_num_layer_changes = 0;
|
||||
this->m_num_tool_changes = 0;
|
||||
}
|
||||
else
|
||||
++ m_num_layer_changes;
|
||||
|
||||
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
|
||||
m_extrusion_flow = extrusion_flow(layer_height);
|
||||
|
||||
// Advance m_layer_info iterator, making sure we got it right
|
||||
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
|
||||
++m_layer_info;
|
||||
}
|
||||
|
||||
// Return the wipe tower position.
|
||||
virtual const xy& position() const { return m_wipe_tower_pos; }
|
||||
// Return the 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; }
|
||||
|
||||
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
||||
virtual ToolChangeResult prime(
|
||||
// print_z of the first layer.
|
||||
float first_layer_height,
|
||||
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
||||
const std::vector<unsigned int> &tools,
|
||||
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
|
||||
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||
bool last_wipe_inside_wipe_tower);
|
||||
|
||||
// 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 ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
|
||||
|
||||
// Fill the unfilled space with a sparse infill.
|
||||
// Call this method only if layer_finished() is false.
|
||||
virtual ToolChangeResult finish_layer();
|
||||
|
||||
// Is the current layer finished?
|
||||
virtual bool layer_finished() const {
|
||||
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
|
||||
}
|
||||
|
||||
virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
|
||||
virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
|
||||
|
||||
|
||||
private:
|
||||
WipeTowerPrusaMM();
|
||||
|
||||
enum wipe_shape // A fill-in direction
|
||||
{
|
||||
SHAPE_NORMAL = 1,
|
||||
SHAPE_REVERSED = -1
|
||||
};
|
||||
|
||||
|
||||
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
|
||||
const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
|
||||
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
|
||||
const float WT_EPSILON = 1e-3f;
|
||||
|
||||
|
||||
xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
|
||||
float m_wipe_tower_width; // Width of the wipe tower.
|
||||
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
||||
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
|
||||
float m_internal_rotation = 0.f;
|
||||
float m_y_shift = 0.f; // y shift passed to writer
|
||||
float m_z_pos = 0.f; // Current Z position.
|
||||
float m_layer_height = 0.f; // Current layer height.
|
||||
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
|
||||
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
|
||||
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
|
||||
|
||||
// G-code generator parameters.
|
||||
float m_cooling_tube_retraction = 0.f;
|
||||
float m_cooling_tube_length = 0.f;
|
||||
float m_parking_pos_retraction = 0.f;
|
||||
float m_extra_loading_move = 0.f;
|
||||
float m_bridging = 0.f;
|
||||
bool m_set_extruder_trimpot = false;
|
||||
bool m_retain_speed_override = true;
|
||||
bool m_adhesion = true;
|
||||
GCodeFlavor m_gcode_flavor;
|
||||
|
||||
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
|
||||
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
|
||||
|
||||
|
||||
struct FilamentParameters {
|
||||
material_type material = PLA;
|
||||
int temperature = 0;
|
||||
int first_layer_temperature = 0;
|
||||
float loading_speed = 0.f;
|
||||
float loading_speed_start = 0.f;
|
||||
float unloading_speed = 0.f;
|
||||
float unloading_speed_start = 0.f;
|
||||
float delay = 0.f ;
|
||||
int cooling_moves = 0;
|
||||
float cooling_initial_speed = 0.f;
|
||||
float cooling_final_speed = 0.f;
|
||||
float ramming_line_width_multiplicator = 0.f;
|
||||
float ramming_step_multiplicator = 0.f;
|
||||
std::vector<float> ramming_speed;
|
||||
float nozzle_diameter;
|
||||
};
|
||||
|
||||
// Extruder specific parameters.
|
||||
std::vector<FilamentParameters> m_filpar;
|
||||
|
||||
|
||||
// State of the wipe tower generator.
|
||||
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
|
||||
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
|
||||
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
|
||||
bool m_print_brim = true;
|
||||
// A fill-in direction (positive Y, negative Y) alternates with each layer.
|
||||
wipe_shape m_current_shape = SHAPE_NORMAL;
|
||||
unsigned int m_current_tool = 0;
|
||||
const std::vector<std::vector<float>> wipe_volumes;
|
||||
|
||||
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
|
||||
bool m_left_to_right = true;
|
||||
float m_extra_spacing = 1.f;
|
||||
|
||||
// Calculates extrusion flow needed to produce required line width for given layer height
|
||||
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
||||
{
|
||||
if ( layer_height < 0 )
|
||||
return m_extrusion_flow;
|
||||
return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
|
||||
}
|
||||
|
||||
// Calculates length of extrusion line to extrude given volume
|
||||
float volume_to_length(float volume, float line_width, float layer_height) const {
|
||||
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
|
||||
}
|
||||
|
||||
// Calculates depth for all layers and propagates them downwards
|
||||
void plan_tower();
|
||||
|
||||
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
|
||||
void make_wipe_tower_square();
|
||||
|
||||
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
|
||||
void save_on_last_wipe();
|
||||
|
||||
|
||||
struct box_coordinates
|
||||
{
|
||||
box_coordinates(float left, float bottom, float width, float height) :
|
||||
ld(left , bottom ),
|
||||
lu(left , bottom + height),
|
||||
rd(left + width, bottom ),
|
||||
ru(left + width, bottom + height) {}
|
||||
box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {}
|
||||
void translate(const xy &shift) {
|
||||
ld += shift; lu += shift;
|
||||
rd += shift; ru += shift;
|
||||
}
|
||||
void translate(const float dx, const float dy) { translate(xy(dx, dy)); }
|
||||
void expand(const float offset) {
|
||||
ld += xy(- offset, - offset);
|
||||
lu += xy(- offset, offset);
|
||||
rd += xy( offset, - offset);
|
||||
ru += xy( offset, offset);
|
||||
}
|
||||
void expand(const float offset_x, const float offset_y) {
|
||||
ld += xy(- offset_x, - offset_y);
|
||||
lu += xy(- offset_x, offset_y);
|
||||
rd += xy( offset_x, - offset_y);
|
||||
ru += xy( offset_x, offset_y);
|
||||
}
|
||||
xy ld; // left down
|
||||
xy lu; // left upper
|
||||
xy rd; // right lower
|
||||
xy ru; // right upper
|
||||
};
|
||||
|
||||
|
||||
// to store information about tool changes for a given layer
|
||||
struct WipeTowerInfo{
|
||||
struct ToolChange {
|
||||
unsigned int old_tool;
|
||||
unsigned int new_tool;
|
||||
float required_depth;
|
||||
float ramming_depth;
|
||||
float first_wipe_line;
|
||||
float wipe_volume;
|
||||
ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
|
||||
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
|
||||
};
|
||||
float z; // z position of the layer
|
||||
float height; // layer height
|
||||
float depth; // depth of the layer based on all layers above
|
||||
float extra_spacing;
|
||||
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
|
||||
|
||||
std::vector<ToolChange> tool_changes;
|
||||
|
||||
WipeTowerInfo(float z_par, float layer_height_par)
|
||||
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
|
||||
};
|
||||
|
||||
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
|
||||
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
|
||||
|
||||
// Stores information about used filament length per extruder:
|
||||
std::vector<float> m_used_filament_length;
|
||||
|
||||
|
||||
// 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
|
||||
ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
|
||||
|
||||
void toolchange_Unload(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
const box_coordinates &cleaning_box,
|
||||
const material_type current_material,
|
||||
const int new_temperature);
|
||||
|
||||
void toolchange_Change(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
const unsigned int new_tool,
|
||||
material_type new_material);
|
||||
|
||||
void toolchange_Load(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
const box_coordinates &cleaning_box);
|
||||
|
||||
void toolchange_Wipe(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
const box_coordinates &cleaning_box,
|
||||
float wipe_volume);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* WipeTowerPrusaMM_hpp_ */
|
|
@ -7,7 +7,7 @@
|
|||
#include "I18N.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "GCode.hpp"
|
||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
//#include "PrintExport.hpp"
|
||||
|
@ -93,7 +93,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"filament_density",
|
||||
"filament_notes",
|
||||
"filament_cost",
|
||||
"filament_max_volumetric_speed",
|
||||
"first_layer_acceleration",
|
||||
"first_layer_bed_temperature",
|
||||
"first_layer_speed",
|
||||
|
@ -188,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
|| opt_key == "filament_cooling_initial_speed"
|
||||
|| opt_key == "filament_cooling_final_speed"
|
||||
|| opt_key == "filament_ramming_parameters"
|
||||
|| opt_key == "filament_max_volumetric_speed"
|
||||
|| opt_key == "gcode_flavor"
|
||||
|| opt_key == "high_current_on_filament_swap"
|
||||
|| opt_key == "infill_first"
|
||||
|
@ -1091,17 +1091,15 @@ std::string Print::validate() const
|
|||
return L("The Spiral Vase option can only be used when printing single material objects.");
|
||||
}
|
||||
|
||||
if (m_config.single_extruder_multi_material) {
|
||||
for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
|
||||
if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
|
||||
return L("All extruders must have the same diameter for single extruder multimaterial printer.");
|
||||
}
|
||||
|
||||
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
||||
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
|
||||
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
|
||||
if (! m_config.use_relative_e_distances)
|
||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||
|
||||
for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
|
||||
if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
|
||||
return L("All extruders must have the same diameter for the Wipe Tower.");
|
||||
|
||||
if (m_objects.size() > 1) {
|
||||
bool has_custom_layering = false;
|
||||
|
@ -1610,7 +1608,6 @@ void Print::_make_brim()
|
|||
bool Print::has_wipe_tower() const
|
||||
{
|
||||
return
|
||||
m_config.single_extruder_multi_material.value &&
|
||||
! m_config.spiral_vase.value &&
|
||||
m_config.wipe_tower.value &&
|
||||
m_config.nozzle_diameter.values.size() > 1;
|
||||
|
@ -1674,12 +1671,13 @@ void Print::_make_wipe_tower()
|
|||
this->throw_if_canceled();
|
||||
|
||||
// Initialize the wipe tower.
|
||||
WipeTowerPrusaMM wipe_tower(
|
||||
WipeTower wipe_tower(
|
||||
m_config.single_extruder_multi_material.value,
|
||||
float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value),
|
||||
float(m_config.wipe_tower_width.value),
|
||||
float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
|
||||
float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
|
||||
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
|
||||
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
|
||||
m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes,
|
||||
m_wipe_tower_data.tool_ordering.first_extruder());
|
||||
|
||||
|
@ -1688,9 +1686,10 @@ void Print::_make_wipe_tower()
|
|||
|
||||
// Set the extruder & material properties at the wipe tower object.
|
||||
for (size_t i = 0; i < number_of_extruders; ++ i)
|
||||
|
||||
wipe_tower.set_extruder(
|
||||
i,
|
||||
WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
|
||||
m_config.filament_type.get_at(i),
|
||||
m_config.temperature.get_at(i),
|
||||
m_config.first_layer_temperature.get_at(i),
|
||||
(float)m_config.filament_loading_speed.get_at(i),
|
||||
|
@ -1702,9 +1701,10 @@ void Print::_make_wipe_tower()
|
|||
(float)m_config.filament_cooling_initial_speed.get_at(i),
|
||||
(float)m_config.filament_cooling_final_speed.get_at(i),
|
||||
m_config.filament_ramming_parameters.get_at(i),
|
||||
(float)m_config.nozzle_diameter.get_at(i));
|
||||
m_config.filament_max_volumetric_speed.get_at(i),
|
||||
m_config.nozzle_diameter.get_at(i));
|
||||
|
||||
m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
||||
m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
|
||||
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
|
||||
|
||||
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
||||
|
|
|
@ -216,7 +216,7 @@ struct WipeTowerData
|
|||
// Cache it here, so it does not need to be recalculated during the G-code generation.
|
||||
ToolOrdering tool_ordering;
|
||||
// Cache of tool changes per print layer.
|
||||
std::unique_ptr<WipeTower::ToolChangeResult> priming;
|
||||
std::unique_ptr<std::vector<WipeTower::ToolChangeResult>> priming;
|
||||
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
|
||||
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
|
||||
std::vector<float> used_filament;
|
||||
|
|
|
@ -2038,11 +2038,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
// Should the wipe tower be visualized ?
|
||||
unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size();
|
||||
|
||||
bool semm = dynamic_cast<const ConfigOptionBool*>(m_config->option("single_extruder_multi_material"))->value;
|
||||
bool wt = dynamic_cast<const ConfigOptionBool*>(m_config->option("wipe_tower"))->value;
|
||||
bool co = dynamic_cast<const ConfigOptionBool*>(m_config->option("complete_objects"))->value;
|
||||
|
||||
if ((extruders_count > 1) && semm && wt && !co)
|
||||
if ((extruders_count > 1) && wt && !co)
|
||||
{
|
||||
// Height of a print (Show at least a slab)
|
||||
double height = std::max(m_model->bounding_box().max(2), 10.0);
|
||||
|
@ -4617,7 +4616,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
|
|||
{
|
||||
const Print *print;
|
||||
const std::vector<float> *tool_colors;
|
||||
WipeTower::xy wipe_tower_pos;
|
||||
Vec2f wipe_tower_pos;
|
||||
float wipe_tower_angle;
|
||||
|
||||
// Number of vertices (each vertex is 6x4=24 bytes long)
|
||||
|
@ -4648,12 +4647,13 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
|
|||
ctxt.print = print;
|
||||
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
|
||||
if (print->wipe_tower_data().priming && print->config().single_extruder_multi_material_priming)
|
||||
ctxt.priming.emplace_back(*print->wipe_tower_data().priming.get());
|
||||
for (int i=0; i<print->wipe_tower_data().priming.get()->size(); ++i)
|
||||
ctxt.priming.emplace_back(print->wipe_tower_data().priming.get()->at(i));
|
||||
if (print->wipe_tower_data().final_purge)
|
||||
ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get());
|
||||
|
||||
ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI;
|
||||
ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
|
||||
ctxt.wipe_tower_pos = Vec2f(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
|
||||
|
||||
|
@ -4715,19 +4715,19 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
|
|||
WipeTower::Extrusion e_prev = extrusions.extrusions[i-1];
|
||||
|
||||
if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
|
||||
e_prev.pos.rotate(ctxt.wipe_tower_angle);
|
||||
e_prev.pos.translate(ctxt.wipe_tower_pos);
|
||||
e_prev.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e_prev.pos;
|
||||
e_prev.pos += ctxt.wipe_tower_pos;
|
||||
}
|
||||
|
||||
for (; i < j; ++i) {
|
||||
WipeTower::Extrusion e = extrusions.extrusions[i];
|
||||
assert(e.width > 0.f);
|
||||
if (!extrusions.priming) {
|
||||
e.pos.rotate(ctxt.wipe_tower_angle);
|
||||
e.pos.translate(ctxt.wipe_tower_pos);
|
||||
e.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e.pos;
|
||||
e.pos += ctxt.wipe_tower_pos;
|
||||
}
|
||||
|
||||
lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
|
||||
lines.emplace_back(Point::new_scale(e_prev.pos.x(), e_prev.pos.y()), Point::new_scale(e.pos.x(), e.pos.y()));
|
||||
widths.emplace_back(e.width);
|
||||
|
||||
e_prev = e;
|
||||
|
@ -5271,7 +5271,7 @@ void GLCanvas3D::_load_fff_shells()
|
|||
double max_z = print->objects()[0]->model_object()->get_model()->bounding_box().max(2);
|
||||
const PrintConfig& config = print->config();
|
||||
unsigned int extruders_count = config.nozzle_diameter.size();
|
||||
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
|
||||
if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) {
|
||||
float depth = print->get_wipe_tower_depth();
|
||||
|
||||
// Calculate wipe tower brim spacing.
|
||||
|
|
|
@ -874,11 +874,10 @@ void Tab::update_wiping_button_visibility() {
|
|||
return; // ys_FIXME
|
||||
bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value;
|
||||
bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1;
|
||||
bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value;
|
||||
|
||||
auto wiping_dialog_button = wxGetApp().sidebar().get_wiping_dialog_button();
|
||||
if (wiping_dialog_button) {
|
||||
wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm);
|
||||
wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders);
|
||||
wiping_dialog_button->GetParent()->Layout();
|
||||
}
|
||||
}
|
||||
|
@ -1557,6 +1556,9 @@ void TabFilament::build()
|
|||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Wipe tower parameters")));
|
||||
optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
|
||||
optgroup->append_single_option_line("filament_loading_speed_start");
|
||||
optgroup->append_single_option_line("filament_loading_speed");
|
||||
|
@ -1568,7 +1570,6 @@ void TabFilament::build()
|
|||
optgroup->append_single_option_line("filament_cooling_moves");
|
||||
optgroup->append_single_option_line("filament_cooling_initial_speed");
|
||||
optgroup->append_single_option_line("filament_cooling_final_speed");
|
||||
optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
|
||||
|
||||
line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" };
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
|
|
|
@ -172,7 +172,7 @@ void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_
|
|||
wxSize text_size = GetTextExtent(info);
|
||||
auto info_str = new wxStaticText(page, wxID_ANY, info ,wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
info_str->Wrap(int(0.6*text_size.x));
|
||||
sizer->Add( info_str, 0, wxALIGN_CENTER_HORIZONTAL | wxEXPAND);
|
||||
sizer->Add( info_str, 0, wxEXPAND);
|
||||
auto table_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift);
|
||||
table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50);
|
||||
|
|
Loading…
Reference in a new issue