diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index dc52257aa..ec4d85ddb 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -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 diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5af90bc3f..47cc61882 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.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 @@ -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()); 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); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 21957d32c..4b81b42aa 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -83,7 +83,7 @@ class WipeTowerIntegration { public: WipeTowerIntegration( const PrintConfig &print_config, - const WipeTower::ToolChangeResult &priming, + const std::vector &priming, const std::vector> &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 &m_priming; const std::vector> &m_tool_changes; const WipeTower::ToolChangeResult &m_final_purge; // Current layer index. diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 92a58fdf0..07a71a0ea 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -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(); + Vec2d p2 = trafo * e.pos.cast(); 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(); + const Vec2d& p2 = e.pos.cast(); + 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); + } } } } diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTower.cpp similarity index 66% rename from src/libslic3r/GCode/WipeTowerPrusaMM.cpp rename to src/libslic3r/GCode/WipeTower.cpp index edfe475b5..37e4040d1 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -13,7 +13,7 @@ TODO LIST */ -#include "WipeTowerPrusaMM.hpp" +#include "WipeTower.hpp" #include #include @@ -35,12 +35,23 @@ TODO LIST namespace Slic3r { -namespace PrusaMultiMaterial { +// Rotate the point around center of the wipe tower about given angle (in degrees) +static Vec2f rotate(const Vec2f& pt, float width, float depth, float angle) +{ + Vec2f out(0,0); + float temp_x = pt(0) - width / 2.f; + float temp_y = pt(1) - 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; +} -class Writer + +class WipeTowerWriter { public: - Writer(float layer_height, float line_width, GCodeFlavor flavor) : + WipeTowerWriter(float layer_height, float line_width, GCodeFlavor flavor, const std::vector& filament_parameters) : m_current_pos(std::numeric_limits::max(), std::numeric_limits::max()), m_current_z(0.f), m_current_feedrate(0.f), @@ -49,7 +60,8 @@ public: m_preview_suppressed(false), m_elapsed_time(0.f), m_default_analyzer_line_width(line_width), - m_gcode_flavor(flavor) + m_gcode_flavor(flavor), + m_filpar(filament_parameters) { // adds tag for analyzer: char buf[64]; @@ -60,7 +72,7 @@ public: change_analyzer_line_width(line_width); } - Writer& change_analyzer_line_width(float line_width) { + WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: char buf[64]; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); @@ -68,25 +80,35 @@ public: return *this; } - Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { + WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { + static const float area = M_PI * 1.75f * 1.75f / 4.f; + float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); + // adds tag for analyzer: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); + m_gcode += buf; + return *this; + } + + WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { m_wipe_tower_width = width; m_wipe_tower_depth = depth; m_internal_angle = internal_angle; - m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); + m_start_pos = rotate(pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); m_current_pos = pos; return *this; } - Writer& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; } + WipeTowerWriter& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; } - Writer& set_z(float z) + WipeTowerWriter& set_z(float z) { m_current_z = z; return *this; } - Writer& set_extrusion_flow(float flow) + WipeTowerWriter& set_extrusion_flow(float flow) { m_extrusion_flow = flow; return *this; } - Writer& set_y_shift(float shift) { - m_current_pos.y -= shift-m_y_shift; + WipeTowerWriter& set_y_shift(float shift) { + m_current_pos.y() -= shift-m_y_shift; m_y_shift = shift; return (*this); } @@ -94,10 +116,10 @@ public: // 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() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } - Writer& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } + WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } + WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } - Writer& feedrate(float f) + WipeTowerWriter& feedrate(float f) { if (f != m_current_feedrate) m_gcode += "G1" + set_format_F(f) + "\n"; @@ -106,33 +128,33 @@ public: const std::string& gcode() const { return m_gcode; } const std::vector& extrusions() const { return m_extrusions; } - float x() const { return m_current_pos.x; } - float y() const { return m_current_pos.y; } - const WipeTower::xy& pos() const { return m_current_pos; } - const WipeTower::xy start_pos_rotated() const { return m_start_pos; } - const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); } + float x() const { return m_current_pos.x(); } + float y() const { return m_current_pos.y(); } + const Vec2f& pos() const { return m_current_pos; } + const Vec2f start_pos_rotated() const { return m_start_pos; } + const Vec2f pos_rotated() const { return rotate(m_current_pos + Vec2f(0.f, m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); } float elapsed_time() const { return m_elapsed_time; } float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; } // Extrude with an explicitely provided amount of extrusion. - Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false) + WipeTowerWriter& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) { - if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate)) + 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; + float dx = x - m_current_pos.x(); + float dy = y - m_current_pos.y(); double len = sqrt(dx*dx+dy*dy); if (record_length) m_used_filament_length += e; - // Now do the "internal rotation" with respect to the wipe tower center - WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are - WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go + Vec2f rotated_current_pos(rotate(m_current_pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are + Vec2f rot(rotate(Vec2f(x,y+m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go if (! m_preview_suppressed && e > 0.f && len > 0.) { + change_analyzer_mm3_per_mm(len, e); // 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) * /*Filament_Area*/2.40528 / (len * m_layer_height)); @@ -140,25 +162,30 @@ public: width += m_layer_height * float(1. - M_PI / 4.); if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos) m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool)); - m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool)); + m_extrusions.emplace_back(WipeTower::Extrusion(rot, width, m_current_tool)); } m_gcode += "G1"; - if (std::abs(rot.x - rotated_current_pos.x) > EPSILON) - m_gcode += set_format_X(rot.x); + if (std::abs(rot.x() - rotated_current_pos.x()) > EPSILON) + m_gcode += set_format_X(rot.x()); - if (std::abs(rot.y - rotated_current_pos.y) > EPSILON) - m_gcode += set_format_Y(rot.y); + if (std::abs(rot.y() - rotated_current_pos.y()) > EPSILON) + m_gcode += set_format_Y(rot.y()); if (e != 0.f) m_gcode += set_format_E(e); - if (f != 0.f && f != m_current_feedrate) + if (f != 0.f && f != m_current_feedrate) { + if (limit_volumetric_flow) { + float e_speed = e / (((len == 0) ? std::abs(e) : len) / f * 60.f); + f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed); + } m_gcode += set_format_F(f); + } - m_current_pos.x = x; - m_current_pos.y = y; + m_current_pos.x() = x; + m_current_pos.y() = y; // Update the elapsed time with a rough estimate. m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f; @@ -166,42 +193,42 @@ public: return *this; } - Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false) - { return extrude_explicit(dest.x, dest.y, e, f, record_length); } + WipeTowerWriter& extrude_explicit(const Vec2f &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) + { return extrude_explicit(dest.x(), dest.y(), e, f, record_length); } // Travel to a new XY position. f=0 means use the current value. - Writer& travel(float x, float y, float f = 0.f) + WipeTowerWriter& travel(float x, float y, float f = 0.f) { return extrude_explicit(x, y, 0.f, f); } - Writer& travel(const WipeTower::xy &dest, float f = 0.f) - { return extrude_explicit(dest.x, dest.y, 0.f, f); } + WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f) + { return extrude_explicit(dest.x(), dest.y(), 0.f, f); } // Extrude a line from current position to x, y with the extrusion amount given by m_extrusion_flow. - Writer& extrude(float x, float y, float f = 0.f) + WipeTowerWriter& extrude(float x, float y, float f = 0.f) { - float dx = x - m_current_pos.x; - float dy = y - m_current_pos.y; + float dx = x - m_current_pos.x(); + float dy = y - m_current_pos.y(); return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); } - Writer& extrude(const WipeTower::xy &dest, const float f = 0.f) - { return extrude(dest.x, dest.y, f); } + WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f) + { return extrude(dest.x(), dest.y(), f); } - Writer& rectangle(const WipeTower::xy& ld,float width,float height,const float f = 0.f) + WipeTowerWriter& rectangle(const Vec2f& ld,float width,float height,const float f = 0.f) { - WipeTower::xy corners[4]; + Vec2f corners[4]; corners[0] = ld; - corners[1] = WipeTower::xy(ld,width,0.f); - corners[2] = WipeTower::xy(ld,width,height); - corners[3] = WipeTower::xy(ld,0.f,height); + corners[1] = ld + Vec2f(width,0.f); + corners[2] = ld + Vec2f(width,height); + corners[3] = ld + Vec2f(0.f,height); int index_of_closest = 0; - if (x()-ld.x > ld.x+width-x()) // closer to the right + if (x()-ld.x() > ld.x()+width-x()) // closer to the right index_of_closest = 1; - if (y()-ld.y > ld.y+height-y()) // closer to the top + if (y()-ld.y() > ld.y()+height-y()) // closer to the top index_of_closest = (index_of_closest==0 ? 3 : 2); - travel(corners[index_of_closest].x, y()); // travel to the closest corner - travel(x(),corners[index_of_closest].y); + travel(corners[index_of_closest].x(), y()); // travel to the closest corner + travel(x(),corners[index_of_closest].y()); int i = index_of_closest; do { @@ -212,7 +239,7 @@ public: return (*this); } - Writer& load(float e, float f = 0.f) + WipeTowerWriter& load(float e, float f = 0.f) { if (e == 0.f && (f == 0.f || f == m_current_feedrate)) return *this; @@ -224,29 +251,29 @@ public: m_gcode += "\n"; return *this; } - - // Derectract while moving in the X direction. - // If |x| > 0, the feed rate relates to the x distance, - // otherwise the feed rate relates to the e distance. - Writer& load_move_x(float x, float e, float f = 0.f) - { return extrude_explicit(x, m_current_pos.y, e, f); } - Writer& retract(float e, float f = 0.f) + WipeTowerWriter& retract(float e, float f = 0.f) { return load(-e, f); } // Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary) - Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f) + WipeTowerWriter& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f) { - float time = std::abs(loading_dist / loading_speed); - float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time); - float feedrate = 60.f * std::hypot(x_speed, loading_speed); + float time = std::abs(loading_dist / loading_speed); // time that the move must take + float x_distance = std::abs(farthest_x - x()); // max x-distance that we can travel + float x_speed = x_distance / time; // x-speed to do it in that time - float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time; - return extrude_explicit(end_point, y(), loading_dist, feedrate); + if (x_speed > max_x_speed) { + // Necessary x_speed is too high - we must shorten the distance to achieve max_x_speed and still respect the time. + x_distance = max_x_speed * time; + x_speed = max_x_speed; + } + + float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_distance; + return extrude_explicit(end_point, y(), loading_dist, x_speed * 60.f, false, false); } // Elevate the extruder head above the current print_z position. - Writer& z_hop(float hop, float f = 0.f) + WipeTowerWriter& z_hop(float hop, float f = 0.f) { m_gcode += std::string("G1") + set_format_Z(m_current_z + hop); if (f != 0 && f != m_current_feedrate) @@ -256,39 +283,36 @@ public: } // Lower the extruder head back to the current print_z position. - Writer& z_hop_reset(float f = 0.f) + WipeTowerWriter& z_hop_reset(float f = 0.f) { return z_hop(0, f); } // Move to x1, +y_increment, // extrude quickly amount e to x2 with feed f. - Writer& ram(float x1, float x2, float dy, float e0, float e, float f) + WipeTowerWriter& ram(float x1, float x2, float dy, float e0, float e, float f) { - extrude_explicit(x1, m_current_pos.y + dy, e0, f, true); - extrude_explicit(x2, m_current_pos.y, e, 0.f, true); + extrude_explicit(x1, m_current_pos.y() + dy, e0, f, true, false); + extrude_explicit(x2, m_current_pos.y(), e, 0.f, true, false); return *this; } // Let the end of the pulled out filament cool down in the cooling tube // by moving up and down and moving the print head left / right // at the current Y position to spread the leaking material. - Writer& cool(float x1, float x2, float e1, float e2, float f) + WipeTowerWriter& cool(float x1, float x2, float e1, float e2, float f) { - extrude_explicit(x1, m_current_pos.y, e1, f); - extrude_explicit(x2, m_current_pos.y, e2); + extrude_explicit(x1, m_current_pos.y(), e1, f, false, false); + extrude_explicit(x2, m_current_pos.y(), e2, false, false); return *this; } - Writer& set_tool(int tool) + WipeTowerWriter& set_tool(int tool) { - char buf[64]; - sprintf(buf, "T%d\n", tool); - m_gcode += buf; m_current_tool = tool; return *this; } // Set extruder temperature, don't wait by default. - Writer& set_extruder_temp(int temperature, bool wait = false) + WipeTowerWriter& set_extruder_temp(int temperature, bool wait = false) { char buf[128]; sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature); @@ -297,7 +321,7 @@ public: }; // Wait for a period of time (seconds). - Writer& wait(float time) + WipeTowerWriter& wait(float time) { if (time==0) return *this; @@ -308,7 +332,7 @@ public: }; // Set speed factor override percentage. - Writer& speed_override(int speed) + WipeTowerWriter& speed_override(int speed) { char buf[128]; sprintf(buf, "M220 S%d\n", speed); @@ -317,21 +341,21 @@ public: }; // Let the firmware back up the active speed override value. - Writer& speed_override_backup() + WipeTowerWriter& speed_override_backup() { m_gcode += "M220 B\n"; return *this; }; // Let the firmware restore the active speed override value. - Writer& speed_override_restore() + WipeTowerWriter& speed_override_restore() { m_gcode += "M220 R\n"; return *this; }; // Set digital trimpot motor - Writer& set_extruder_trimpot(int current) + WipeTowerWriter& set_extruder_trimpot(int current) { char buf[128]; if (m_gcode_flavor == gcfRepRap) @@ -342,20 +366,20 @@ public: return *this; }; - Writer& flush_planner_queue() + WipeTowerWriter& flush_planner_queue() { m_gcode += "G4 S0\n"; return *this; } // Reset internal extruder counter. - Writer& reset_extruder() + WipeTowerWriter& reset_extruder() { m_gcode += "G92 E0\n"; return *this; } - Writer& comment_with_value(const char *comment, int value) + WipeTowerWriter& comment_with_value(const char *comment, int value) { char strvalue[64]; sprintf(strvalue, "%d", value); @@ -364,7 +388,7 @@ public: }; - Writer& set_fan(unsigned int speed) + WipeTowerWriter& set_fan(unsigned int speed) { if (speed == m_last_fan_speed) return *this; @@ -382,18 +406,11 @@ public: return *this; } - Writer& comment_material(WipeTowerPrusaMM::material_type material) - { - m_gcode += "; material : "; - m_gcode += WipeTowerPrusaMM::to_string(material) + "\n"; - return *this; - }; - - Writer& append(const char *text) { m_gcode += text; return *this; } + WipeTowerWriter& append(const std::string& text) { m_gcode += text; return *this; } private: - WipeTower::xy m_start_pos; - WipeTower::xy m_current_pos; + Vec2f m_start_pos; + Vec2f m_current_pos; float m_current_z; float m_current_feedrate; unsigned int m_current_tool; @@ -412,19 +429,20 @@ private: const float m_default_analyzer_line_width; float m_used_filament_length = 0.f; GCodeFlavor m_gcode_flavor; + const std::vector& m_filpar; std::string set_format_X(float x) { char buf[64]; sprintf(buf, " X%.3f", x); - m_current_pos.x = x; + m_current_pos.x() = x; return buf; } std::string set_format_Y(float y) { char buf[64]; sprintf(buf, " Y%.3f", y); - m_current_pos.y = y; + m_current_pos.y() = y; return buf; } @@ -447,58 +465,13 @@ private: return buf; } - Writer& operator=(const Writer &rhs); -}; // class Writer - -}; // namespace PrusaMultiMaterial + WipeTowerWriter& operator=(const WipeTowerWriter &rhs); +}; // class WipeTowerWriter -WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name) -{ - if (strcasecmp(name, "PLA") == 0) - return PLA; - if (strcasecmp(name, "ABS") == 0) - return ABS; - if (strcasecmp(name, "PET") == 0) - return PET; - if (strcasecmp(name, "HIPS") == 0) - return HIPS; - if (strcasecmp(name, "FLEX") == 0) - return FLEX; - if (strcasecmp(name, "SCAFF") == 0) - return SCAFF; - if (strcasecmp(name, "EDGE") == 0) - return EDGE; - if (strcasecmp(name, "NGEN") == 0) - return NGEN; - if (strcasecmp(name, "PVA") == 0) - return PVA; - if (strcasecmp(name, "PC") == 0) - return PC; - return INVALID; -} - -std::string WipeTowerPrusaMM::to_string(material_type material) -{ - switch (material) { - case PLA: return "PLA"; - case ABS: return "ABS"; - case PET: return "PET"; - case HIPS: return "HIPS"; - case FLEX: return "FLEX"; - case SCAFF: return "SCAFF"; - case EDGE: return "EDGE"; - case NGEN: return "NGEN"; - case PVA: return "PVA"; - case PC: return "PC"; - case INVALID: - default: return "INVALID"; - } -} - // Returns gcode to prime the nozzles at the front edge of the print bed. -WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( +std::vector WipeTower::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. @@ -516,25 +489,36 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area); const float prime_section_width = std::min(240.f / tools.size(), 60.f); - box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); + box_coordinates cleaning_box(Vec2f(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); - writer.set_extrusion_flow(m_extrusion_flow) - .set_z(m_z_pos) - .set_initial_tool(m_current_tool) - .append(";--------------------\n" - "; CP PRIMING START\n") - .append(";--------------------\n"); - if (m_retain_speed_override) - writer.speed_override_backup(); - writer.speed_override(100); - writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position - .travel(cleaning_box.ld, 7200); - if (m_set_extruder_trimpot) - writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. + std::vector results; + // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector. for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) { + int old_tool = m_current_tool; + + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); + writer.set_extrusion_flow(m_extrusion_flow) + .set_z(m_z_pos) + .set_initial_tool(m_current_tool); + + // This is the first toolchange - initiate priming + if (idx_tool == 0) { + writer.append(";--------------------\n" + "; CP PRIMING START\n") + .append(";--------------------\n") + .speed_override_backup() + .speed_override(100) + .set_initial_position(Vec2f::Zero()) // Always move to the starting position + .travel(cleaning_box.ld, 7200); + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. + } + else + writer.set_initial_position(results.back().end_pos); + + unsigned int tool = tools[idx_tool]; m_left_to_right = true; toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials. @@ -547,53 +531,63 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200); toolchange_Wipe(writer, cleaning_box , 20.f); box_coordinates box = cleaning_box; - box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width); + box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width); toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature); cleaning_box.translate(prime_section_width, 0.f); writer.travel(cleaning_box.ld, 7200); } ++ m_num_tool_changes; + + + // Ask our writer about how much material was consumed: + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + + ToolChangeResult result; + result.priming = true; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; + result.print_z = this->m_z_pos; + result.layer_height = this->m_layer_height; + result.gcode = writer.gcode(); + result.elapsed_time = writer.elapsed_time(); + result.extrusions = writer.extrusions(); + result.start_pos = writer.start_pos_rotated(); + result.end_pos = writer.pos_rotated(); + + results.push_back(std::move(result)); + + // This is the last priming toolchange - finish priming + if (idx_tool+1 == tools.size()) { + // Reset the extruder current to a normal value. + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); + writer.speed_override_restore() + .feedrate(6000) + .flush_planner_queue() + .reset_extruder() + .append("; CP PRIMING END\n" + ";------------------\n" + "\n\n"); + } } m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear // in the output gcode - we should not remember emitting them (we will output them twice in the worst case) - // Reset the extruder current to a normal value. - if (m_set_extruder_trimpot) - writer.set_extruder_trimpot(550); - if (m_retain_speed_override) - writer.speed_override_restore(); - writer.feedrate(6000) - .flush_planner_queue() - .reset_extruder() - .append("; CP PRIMING END\n" - ";------------------\n" - "\n\n"); - // so that tool_change() will know to extrude the wipe tower brim: m_print_brim = true; - // Ask our writer about how much material was consumed: - if (m_current_tool < m_used_filament_length.size()) - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - - ToolChangeResult result; - result.priming = true; - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; - result.gcode = writer.gcode(); - result.elapsed_time = writer.elapsed_time(); - result.extrusions = writer.extrusions(); - result.start_pos = writer.start_pos_rotated(); - result.end_pos = writer.pos_rotated(); - return result; + return results; } -WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer) +WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_in_layer) { if ( m_print_brim ) return toolchange_Brim(); + int old_tool = m_current_tool; + float wipe_area = 0.f; bool last_change_in_layer = false; float wipe_volume = 0.f; @@ -615,26 +609,28 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo } box_coordinates cleaning_box( - xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f), + Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f), m_wipe_tower_width - m_perimeter_width, (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width : m_wipe_tower_depth-m_perimeter_width)); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) .set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f)) .append(";--------------------\n" "; CP TOOLCHANGE START\n") - .comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based - .comment_material(m_filpar[m_current_tool].material) - .append(";--------------------\n"); - if (m_retain_speed_override) - writer.speed_override_backup(); + .comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based + + if (tool != (unsigned)(-1)) + writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str()) + .append(";--------------------\n"); + + writer.speed_override_backup(); writer.speed_override(100); - xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed); + Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed); writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); // Increase the extruder driver current to allow fast ramming. @@ -658,9 +654,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo if (last_change_in_layer) {// draw perimeter line writer.set_y_shift(m_y_shift); if (m_peters_wipe_tower) - writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth); + writer.rectangle(Vec2f::Zero(), m_layer_info->depth + 3*m_perimeter_width, m_wipe_tower_depth); else { - writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width); + writer.rectangle(Vec2f::Zero(), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width); if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y()); } @@ -669,8 +665,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo if (m_set_extruder_trimpot) writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value. - if (m_retain_speed_override) - writer.speed_override_restore(); + writer.speed_override_restore(); writer.feedrate(6000) .flush_planner_queue() .reset_extruder() @@ -684,6 +679,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo ToolChangeResult result; result.priming = false; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -694,25 +691,27 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo return result; } -WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset) +WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_offset) { + int old_tool = m_current_tool; + const box_coordinates wipeTower_box( - WipeTower::xy(0.f, 0.f), + Vec2f::Zero(), m_wipe_tower_width, m_wipe_tower_depth); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow * 1.1f) .set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop. .set_initial_tool(m_current_tool) .append(";-------------------------------------\n" "; CP WIPE TOWER FIRST LAYER BRIM START\n"); - xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0); + Vec2f initial_position = wipeTower_box.lu - Vec2f(m_perimeter_width * 6.f, 0); writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); - writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower. - 1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400); + writer.extrude_explicit(wipeTower_box.ld - Vec2f(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower. + 1.5f * m_extrusion_flow * (wipeTower_box.lu.y() - wipeTower_box.ld.y()), 2400); // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded. // Extrude 4 rounds of a brim around the future wipe tower. @@ -738,6 +737,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo ToolChangeResult result; result.priming = false; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -751,14 +752,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. -void WipeTowerPrusaMM::toolchange_Unload( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Unload( + WipeTowerWriter &writer, const box_coordinates &cleaning_box, - const material_type current_material, + const std::string& current_material, const int new_temperature) { - float xl = cleaning_box.ld.x + 1.f * m_perimeter_width; - float xr = cleaning_box.rd.x - 1.f * m_perimeter_width; + float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width; + float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width; const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm @@ -771,7 +772,7 @@ void WipeTowerPrusaMM::toolchange_Unload( float remaining = xr - xl ; // keeps track of distance to the next turnaround float e_done = 0; // measures E move done from each segment - writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position + writer.travel(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f ); // move to starting position // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower: if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) { @@ -824,7 +825,7 @@ void WipeTowerPrusaMM::toolchange_Unload( const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move; const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround const float actual_time = dist/x * 0.25; - writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.)); + writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), dist / (actual_time / 60.)); remaining -= dist; if (remaining < WT_EPSILON) { // we reached a turning point @@ -838,25 +839,27 @@ void WipeTowerPrusaMM::toolchange_Unload( e_done = 0; } } - WipeTower::xy end_of_ramming(writer.x(),writer.y()); + Vec2f end_of_ramming(writer.x(),writer.y()); writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier // Retraction: float old_x = writer.x(); float turning_point = (!m_left_to_right ? xl : xr ); - float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming - writer.suppress_preview() - .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s - .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f) - .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f) - .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f) - - /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed - .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed) - .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed) - .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed) - .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/ - .resume_preview(); + if (m_semm && (m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0)) { + float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming + writer.suppress_preview() + .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s + .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f) + .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f) + .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f) + + /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed + .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed) + .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed) + .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed) + .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/ + .resume_preview(); + } if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait. // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset) // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off). @@ -891,73 +894,72 @@ void WipeTowerPrusaMM::toolchange_Unload( // this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start: // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material - writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f); + writer.travel(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f); writer.resume_preview() .flush_planner_queue(); } // Change the tool, set a speed override for soluble and flex materials. -void WipeTowerPrusaMM::toolchange_Change( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Change( + WipeTowerWriter &writer, const unsigned int new_tool, - material_type new_material) + const std::string& new_material) { // Ask the writer about how much of the old filament we consumed: if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - // Speed override for the material. Go slow for flex and soluble materials. - int speed_override; - switch (new_material) { - case PVA: speed_override = (m_z_pos < 0.80f) ? 60 : 80; break; - case SCAFF: speed_override = 35; break; - case FLEX: speed_override = 35; break; - default: speed_override = 100; - } - writer.set_tool(new_tool); - if (m_retain_speed_override) - assert(speed_override == 100); - else - writer.speed_override(speed_override); + // This is where we want to place the custom gcodes. We will use placeholders for this. + // These will be substituted by the actual gcodes when the gcode is generated. + writer.append("[end_filament_gcode]\n"); + writer.append("[toolchange_gcode]\n"); + + // The toolchange Tn command will be inserted later, only in case that the user does + // not provide a custom toolchange gcode. + writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed. + writer.append("[start_filament_gcode]\n"); + writer.flush_planner_queue(); m_current_tool = new_tool; } -void WipeTowerPrusaMM::toolchange_Load( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Load( + WipeTowerWriter &writer, const box_coordinates &cleaning_box) -{ - float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f; - float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f; - float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position +{ + if (m_semm && (m_parking_pos_retraction != 0 || m_extra_loading_move != 0)) { + float xl = cleaning_box.ld.x() + m_perimeter_width * 0.75f; + float xr = cleaning_box.rd.x() - m_perimeter_width * 0.75f; + float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position - // Load the filament while moving left / right, so the excess material will not create a blob at a single position. - float turning_point = ( oldx-xl < xr-oldx ? xr : xl ); - float edist = m_parking_pos_retraction+m_extra_loading_move; + // Load the filament while moving left / right, so the excess material will not create a blob at a single position. + float turning_point = ( oldx-xl < xr-oldx ? xr : xl ); + float edist = m_parking_pos_retraction+m_extra_loading_move; - writer.append("; CP TOOLCHANGE LOAD\n") - .suppress_preview() - /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration - .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase - .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down - .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ + writer.append("; CP TOOLCHANGE LOAD\n") + .suppress_preview() + /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration + .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase + .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down + .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ - .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start) - .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase - .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ + .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start) + .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase + .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ - .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate - .resume_preview(); + .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate + .resume_preview(); - // Reset the extruder current to the normal value. - if (m_set_extruder_trimpot) - writer.set_extruder_trimpot(550); + // Reset the extruder current to the normal value. + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); + } } // Wipe the newly loaded filament until the end of the assigned wipe area. -void WipeTowerPrusaMM::toolchange_Wipe( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Wipe( + WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_volume) { @@ -965,8 +967,8 @@ void WipeTowerPrusaMM::toolchange_Wipe( writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f)) .append("; CP TOOLCHANGE WIPE\n"); float wipe_coeff = m_is_first_layer ? 0.5f : 1.f; - const float& xl = cleaning_box.ld.x; - const float& xr = cleaning_box.rd.x; + const float& xl = cleaning_box.ld.x(); + const float& xr = cleaning_box.rd.x(); // Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least // the ordered volume, even if it means violating the box. This can later be removed and simply @@ -997,7 +999,7 @@ void WipeTowerPrusaMM::toolchange_Wipe( else writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); - if (writer.y()+EPSILON > cleaning_box.lu.y-0.5f*m_perimeter_width) + if (writer.y()+EPSILON > cleaning_box.lu.y()-0.5f*m_perimeter_width) break; // in case next line would not fit traversed_x -= writer.x(); @@ -1024,13 +1026,15 @@ void WipeTowerPrusaMM::toolchange_Wipe( -WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() +WipeTower::ToolChangeResult WipeTower::finish_layer() { // This should only be called if the layer is not finished yet. // Otherwise the caller would likely travel to the wipe tower in vain. assert(! this->layer_finished()); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + int old_tool = m_current_tool; + + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) @@ -1042,7 +1046,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() // Slow down on the 1st layer. float speed_factor = m_is_first_layer ? 0.5f : 1.f; float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth(); - box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width), + box_coordinates fill_box(Vec2f(m_perimeter_width, m_depth_traversed + m_perimeter_width), m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width); @@ -1056,44 +1060,44 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() else box.expand(-m_perimeter_width); } else i=2; // only draw the inner perimeter, outer has been already drawn by tool_change(...) - writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y,2900*speed_factor); + writer.rectangle(box.ld, box.rd.x()-box.ld.x(), box.ru.y()-box.rd.y(), 2900*speed_factor); } // we are in one of the corners, travel to ld along the perimeter: - if (writer.x() > fill_box.ld.x+EPSILON) writer.travel(fill_box.ld.x,writer.y()); - if (writer.y() > fill_box.ld.y+EPSILON) writer.travel(writer.x(),fill_box.ld.y); + if (writer.x() > fill_box.ld.x()+EPSILON) writer.travel(fill_box.ld.x(),writer.y()); + if (writer.y() > fill_box.ld.y()+EPSILON) writer.travel(writer.x(),fill_box.ld.y()); if (m_is_first_layer && m_adhesion) { // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower. box.expand(-m_perimeter_width/2.f); - int nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width))); - float step = (box.lu.y - box.ld.y) / nsteps; - writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f)); + int nsteps = int(floor((box.lu.y() - box.ld.y()) / (2*m_perimeter_width))); + float step = (box.lu.y() - box.ld.y()) / nsteps; + writer.travel(box.ld - Vec2f(m_perimeter_width/2.f, m_perimeter_width/2.f)); if (nsteps >= 0) for (int i = 0; i < nsteps; ++i) { - writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step); - writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y()); - writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step); - writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y()); + writer.extrude(box.ld.x()+m_perimeter_width/2.f, writer.y() + 0.5f * step); + writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y()); + writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step); + writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y()); } - writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle + writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle } else { // Extrude a sparse infill to support the material to be printed above. - const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width); - const float left = fill_box.lu.x+2*m_perimeter_width; - const float right = fill_box.ru.x - 2 * m_perimeter_width; + const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width); + const float left = fill_box.lu.x() + 2*m_perimeter_width; + const float right = fill_box.ru.x() - 2 * m_perimeter_width; if (dy > m_perimeter_width) { // Extrude an inverse U at the left of the region. - writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f)) - .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor); + writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f)) + .extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor); const int n = 1+(right-left)/(m_bridging); const float dx = (right-left)/n; for (int i=1;i<=n;++i) { float x=left+dx*i; writer.travel(x,writer.y()); - writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y); + writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y()); } writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower } @@ -1111,6 +1115,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() ToolChangeResult result; result.priming = false; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -1122,7 +1128,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() } // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box -void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume) +void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume) { assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON); // refuses to add a layer below the last one @@ -1158,7 +1164,7 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi -void WipeTowerPrusaMM::plan_tower() +void WipeTower::plan_tower() { // Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards m_wipe_tower_depth = 0.f; @@ -1181,7 +1187,7 @@ void WipeTowerPrusaMM::plan_tower() } } -void WipeTowerPrusaMM::save_on_last_wipe() +void WipeTower::save_on_last_wipe() { for (m_layer_info=m_plan.begin();m_layer_infoz, m_layer_info->height, 0, m_layer_info->z == m_plan.front().z, m_layer_info->z == m_plan.back().z); @@ -1206,7 +1212,7 @@ void WipeTowerPrusaMM::save_on_last_wipe() // Processes vector m_plan and calls respective functions to generate G-code for the wipe tower // Resulting ToolChangeResults are appended into vector "result" -void WipeTowerPrusaMM::generate(std::vector> &result) +void WipeTower::generate(std::vector> &result) { if (m_plan.empty()) @@ -1224,7 +1230,15 @@ void WipeTowerPrusaMM::generate(std::vectordepth < m_wipe_tower_depth - m_perimeter_width) m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f; - for (const auto &toolchange : layer.tool_changes) { - if (m_current_tool == (unsigned int)(-2)) - m_current_tool = toolchange.old_tool; + for (const auto &toolchange : layer.tool_changes) layer_result.emplace_back(tool_change(toolchange.new_tool, false)); - } if (! layer_finished()) { auto finish_layer_toolchange = finish_layer(); @@ -1252,7 +1263,7 @@ void WipeTowerPrusaMM::generate(std::vector -#include +#include #include -#include +#include +#include +#include + +#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 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>& 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> &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 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 &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 get_used_filament() const = 0; + std::vector 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::max(); + std::vector 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 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> 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 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 m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...)) + std::vector::iterator m_layer_info = m_plan.end(); + + // Stores information about used filament length per extruder: + std::vector 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_ diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp deleted file mode 100644 index f8adf4c5f..000000000 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ /dev/null @@ -1,388 +0,0 @@ -#ifndef WipeTowerPrusaMM_hpp_ -#define WipeTowerPrusaMM_hpp_ - -#include -#include -#include -#include -#include - -#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>& 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> &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 &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 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 ramming_speed; - float nozzle_diameter; - }; - - // Extruder specific parameters. - std::vector 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> 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 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 m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...)) - std::vector::iterator m_layer_info = m_plan.end(); - - // Stores information about used filament length per extruder: - std::vector 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_ */ diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 60bd18a59..7034d8dc0 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -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::vectorhas_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 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( + m_wipe_tower_data.priming = Slic3r::make_unique>( 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 diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 24dc839bf..a9593b6f0 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -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 priming; + std::unique_ptr> priming; std::vector> tool_changes; std::unique_ptr final_purge; std::vector used_filament; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ec7746092..64a734f81 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -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(m_config->option("nozzle_diameter"))->values.size(); - bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; bool co = dynamic_cast(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& str_ { const Print *print; const std::vector *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& 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; iwipe_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& 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. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 164381308..7ab564beb 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -874,11 +874,10 @@ void Tab::update_wiping_button_visibility() { return; // ys_FIXME bool wipe_tower_enabled = dynamic_cast( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value; bool multiple_extruders = dynamic_cast((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1; - bool single_extruder_mm = dynamic_cast( (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) { diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 4c2b2480e..894b1ee62 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -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);