diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 6e03cff047..4a01e27ca7 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -140,6 +140,90 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) return gcode; } +WipeTowerIntegration::WipeTowerIntegration(const PrintConfig &print_config) +{ + // Initialize the wipe tower. + auto *wipe_tower = new WipeTowerPrusaMM( + float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value), + float(print_config.wipe_tower_width.value), float(print_config.wipe_tower_per_color_wipe.value)); + //wipe_tower->set_retract(); + //wipe_tower->set_zhop(); + //wipe_tower->set_zhop(); + // Set the extruder & material properties at the wipe tower object. + for (size_t i = 0; i < 4; ++ i) + wipe_tower->set_extruder( + i, + WipeTowerPrusaMM::parse_material(print_config.filament_type.get_at(i).c_str()), + print_config.temperature.get_at(i), + print_config.first_layer_temperature.get_at(i)); + m_impl.reset(wipe_tower); +} + +std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) +{ + bool over_wipe_tower = false; + std::string gcode; + + if (gcodegen.writer().need_toolchange(extruder_id)) { + // Move over the wipe tower. + gcode += this->travel_to(gcodegen, m_impl->tool_change(extruder_id, WipeTower::PURPOSE_MOVE_TO_TOWER).second); + // Let the tool change be executed by the wipe tower class. + std::pair code_and_pos = m_impl->tool_change(extruder_id, WipeTower::PURPOSE_EXTRUDE); + // Inform the G-code writer about the changes done behind its back. + gcode += code_and_pos.first; + // Let the m_writer know the current extruder_id, but ignore the generated G-code. + gcodegen.writer().toolchange(extruder_id); + // A phony move to the end position at the wipe tower. + gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + over_wipe_tower = true; + } + + if (finish_layer && ! m_impl->layer_finished()) { + // Last extruder change on the layer or no extruder change at all. + if (! over_wipe_tower) + gcode += this->travel_to(gcodegen, m_impl->finish_layer(WipeTower::PURPOSE_MOVE_TO_TOWER).second); + // Let the tool change be executed by the wipe tower class. + std::pair code_and_pos = m_impl->finish_layer(WipeTower::PURPOSE_EXTRUDE); + // Inform the G-code writer about the changes done behind its back. + gcode += code_and_pos.first; + // A phony move to the end position at the wipe tower. + gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + } + + return gcode; +} + +// Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. +std::string WipeTowerIntegration::finalize(GCode &gcodegen, const Print &print, bool current_layer_full) +{ + std::string gcode; + // Unload the current filament over the purge tower. + if (current_layer_full) { + // There is not enough space on the wipe tower to purge the nozzle into. Lift Z to the next layer. + coordf_t new_print_z = gcodegen.writer().get_position().z + print.objects.front()->config.layer_height.value; + gcode += gcodegen.change_layer(new_print_z); + m_impl->set_layer(float(new_print_z), float(print.objects.front()->config.layer_height.value), 0, false, true); + } + gcode += this->tool_change(gcodegen, -1, false); + m_impl.release(); + return gcode; +} + +std::string WipeTowerIntegration::travel_to(GCode &gcodegen, const WipeTower::xy &dest) +{ + // Retract for a tool change, using the toolchange retract value and setting the priming extra length. + std::string gcode = gcodegen.retract(true); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + gcode += gcodegen.travel_to( + Point(scale_(dest.x - gcodegen.origin().x), scale_(dest.y - gcodegen.origin().y)), + erMixed, + "Travel to a Wipe Tower"); + gcode += gcodegen.unretract(); + return gcode; +} + #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id) inline void write(FILE *file, const std::string &what) @@ -484,23 +568,8 @@ bool GCode::do_export(FILE *file, Print &print) } // Prusa Multi-Material wipe tower. if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value && - ! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0) { - // Initialize the wipe tower. - auto *wipe_tower = new WipeTowerPrusaMM( - float(print.config.wipe_tower_x.value), float(print.config.wipe_tower_y.value), - float(print.config.wipe_tower_width.value), float(print.config.wipe_tower_per_color_wipe.value)); - //wipe_tower->set_retract(); - //wipe_tower->set_zhop(); - //wipe_tower->set_zhop(); - // Set the extruder & material properties at the wipe tower object. - for (size_t i = 0; i < 4; ++ i) - wipe_tower->set_extruder( - i, - WipeTowerPrusaMM::parse_material(print.config.filament_type.get_at(i).c_str()), - print.config.temperature.get_at(i), - print.config.first_layer_temperature.get_at(i)); - m_wipe_tower.reset(wipe_tower); - } + ! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0) + m_wipe_tower.reset(new WipeTowerIntegration(print.config)); // Extrude the layers. for (auto &layer : layers) { // layer.second is of type std::vector, @@ -526,17 +595,9 @@ bool GCode::do_export(FILE *file, Print &print) } // write end commands to file - if (m_wipe_tower) { - // Unload the current filament over the purge tower. - if (tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished()) { - // There is not enough space on the wipe tower to purge the nozzle into. Lift Z to the next layer. - coordf_t new_print_z = tool_ordering.back().print_z + print.objects.front()->config.layer_height.value; - write(file, this->change_layer(new_print_z)); - m_wipe_tower->set_layer(new_print_z, print.objects.front()->config.layer_height.value, 0, false, true); - } - write(file, this->wipe_tower_tool_change(-1)); - m_wipe_tower.release(); - } else + if (m_wipe_tower) + write(file, m_wipe_tower->finalize(*this, print, tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished())); + else write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully write(file, m_writer.set_fan(false)); writeln(file, m_placeholder_parser.process(print.config.end_gcode)); @@ -711,24 +772,16 @@ void GCode::process_layer( gcode += pp.process(print.config.layer_gcode.value) + "\n"; } - // Trigger the tool change explicitely to draw the wipe tower brim always. - bool wipe_tower_extrude_brim_explicit = m_wipe_tower && ! m_wipe_tower->finished() && first_layer && m_writer.extruder()->id == first_extruder_id; - if (! first_layer && ! m_second_layer_things_done) { // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // first_layer_temperature vs. temperature settings. - if (print.config.single_extruder_multi_material.value) { - // Switch the extruder before setting the 2nd layer temperature. - this->set_extruder(first_extruder_id); - int temperature = print.config.temperature.get_at(first_extruder_id); - if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(m_writer.extruder()->id)) - gcode += m_writer.set_temperature(temperature, false, first_extruder_id); - } else { - for (const Extruder &extruder : m_writer.extruders) { - int temperature = print.config.temperature.get_at(extruder.id); - if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id)) - gcode += m_writer.set_temperature(temperature, false, extruder.id); - } + for (const Extruder &extruder : m_writer.extruders) { + if (print.config.single_extruder_multi_material.value && extruder.id != m_writer.extruder()->id) + // In single extruder multi material mode, set the temperature for the current extruder only. + continue; + int temperature = print.config.temperature.get_at(extruder.id); + if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id)) + gcode += m_writer.set_temperature(temperature, false, extruder.id); } if (print.config.bed_temperature.value > 0 && print.config.bed_temperature != print.config.first_layer_bed_temperature.value) gcode += m_writer.set_bed_temperature(print.config.bed_temperature); @@ -736,9 +789,6 @@ void GCode::process_layer( m_second_layer_things_done = true; } - if (wipe_tower_extrude_brim_explicit) - gcode += this->wipe_tower_tool_change(first_extruder_id); - // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. bool extrude_skirt = @@ -913,21 +963,10 @@ void GCode::process_layer( // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. std::vector> lower_layer_edge_grids(layers.size()); for (unsigned int extruder_id : layer_tools.extruders) - { - gcode += this->set_extruder(extruder_id); - if (m_wipe_tower && ! m_wipe_tower->layer_finished() && extruder_id == layer_tools.extruders.back()) { - // Last extruder change on the layer or no extruder change at all. - // Move over the wipe tower. - gcode += m_writer.travel_to_xy(Pointf3(m_wipe_tower->position().x, m_wipe_tower->position().y)); - gcode += m_writer.unlift(); - gcode += m_writer.unretract(); - // Let the tool change be executed by the wipe tower class. - std::pair code_and_pos = m_wipe_tower->finish_layer(); - // Inform the G-code writer about the changes done behind its back. - gcode += code_and_pos.first; - // A phony move to the end position at the wipe tower. - m_writer.travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); - } + { + gcode += m_wipe_tower ? + m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : + this->set_extruder(extruder_id); if (extrude_skirt) { auto loops_it = skirt_loops_per_extruder.find(extruder_id); @@ -1317,7 +1356,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) { if (! *lower_layer_edge_grid) { // Create the distance field for a layer below. - const coord_t distance_field_resolution = scale_(1.f); + const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); *lower_layer_edge_grid = make_unique(); (*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution); (*lower_layer_edge_grid)->calculate_sdf(); @@ -1381,7 +1420,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // For each polygon point, store a penalty. // First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r. - std::vector penalties = polygon_angles_at_vertices(polygon, lengths, nozzle_r); + std::vector penalties = polygon_angles_at_vertices(polygon, lengths, float(nozzle_r)); // No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces. const float penaltyConvexVertex = 1.f; const float penaltyFlatSurface = 5.f; @@ -1558,7 +1597,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou paths.front().polyline.points[0], paths.front().polyline.points[1] ); - double distance = std::min( + double distance = std::min( scale_(EXTRUDER_CONFIG(nozzle_diameter)), first_segment.length() ); @@ -1791,8 +1830,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // This method accepts &point in print coordinates. -std::string -GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) +std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) { /* Define the travel move as a line between current position and the taget point. This is expressed in print coordinates, so it will need to be translated by @@ -1924,50 +1962,35 @@ std::string GCode::set_extruder(unsigned int extruder_id) gcode += pp.process(m_config.toolchange_gcode.value) + '\n'; } - if (m_wipe_tower) { - assert(! m_wipe_tower->finished()); - if (! m_wipe_tower->finished()) - gcode += this->wipe_tower_tool_change(extruder_id); - } else { - // if ooze prevention is enabled, park current extruder in the nearest - // standby point and set it to the standby temperature - if (m_ooze_prevention.enable && m_writer.extruder() != NULL) - gcode += m_ooze_prevention.pre_toolchange(*this); - // append the toolchange command - gcode += m_writer.toolchange(extruder_id); - // set the new extruder to the operating temperature - if (m_ooze_prevention.enable) - gcode += m_ooze_prevention.post_toolchange(*this); - } + // if ooze prevention is enabled, park current extruder in the nearest + // standby point and set it to the standby temperature + if (m_ooze_prevention.enable && m_writer.extruder() != NULL) + gcode += m_ooze_prevention.pre_toolchange(*this); + // append the toolchange command + gcode += m_writer.toolchange(extruder_id); + // set the new extruder to the operating temperature + if (m_ooze_prevention.enable) + gcode += m_ooze_prevention.post_toolchange(*this); return gcode; } -std::string GCode::wipe_tower_tool_change(int extruder_id) -{ - // Move over the wipe tower. - std::string gcode = m_writer.travel_to_xy(Pointf3(m_wipe_tower->position().x, m_wipe_tower->position().y)); - gcode += m_writer.unlift(); - gcode += m_writer.unretract(); - // Let the tool change be executed by the wipe tower class. - std::pair code_and_pos = m_wipe_tower->tool_change(extruder_id); - // Inform the G-code writer about the changes done behind its back. - gcode += code_and_pos.first; - // Let the m_writer know the current extruder_id, but ignore the generated G-code. - m_writer.toolchange(extruder_id); - // A phony move to the end position at the wipe tower. - m_writer.travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); - return gcode; -} - // convert a model-space scaled point into G-code coordinates Pointf GCode::point_to_gcode(const Point &point) const { Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); return Pointf( unscale(point.x) + m_origin.x - extruder_offset.x, - unscale(point.y) + m_origin.y - extruder_offset.y - ); + unscale(point.y) + m_origin.y - extruder_offset.y); +} + +// convert a model-space scaled point into G-code coordinates +Point GCode::gcode_to_point(const Pointf &point) const +{ + Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); + return Point( + scale_(point.x - m_origin.x + extruder_offset.x), + scale_(point.y - m_origin.y + extruder_offset.y)); } } diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 96b1360f1b..09b526353e 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -73,6 +73,20 @@ public: std::string wipe(GCode &gcodegen, bool toolchange = false); }; +class WipeTowerIntegration { +public: + WipeTowerIntegration(const PrintConfig &config); + void set_layer(coordf_t print_z, coordf_t layer_height, size_t max_tool_changes, bool is_first_layer, bool is_last_layer) + { m_impl->set_layer(float(print_z), float(layer_height), max_tool_changes, is_first_layer, is_last_layer); } + bool layer_finished() const { return m_impl->layer_finished(); } + std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); + std::string finalize(GCode &gcodegen, const Print &print, bool current_layer_full); + +private: + std::string travel_to(GCode &codegen, const WipeTower::xy &dest); + std::unique_ptr m_impl; +}; + class GCode { public: GCode() : @@ -101,6 +115,7 @@ public: void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); } const Point& last_pos() const { return m_last_pos; } Pointf point_to_gcode(const Point &point) const; + Point gcode_to_point(const Pointf &point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } @@ -114,7 +129,7 @@ public: void set_elapsed_time(float value) { m_elapsed_time = value; } void apply_print_config(const PrintConfig &print_config); -private: +protected: // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint { @@ -175,7 +190,6 @@ private: std::string retract(bool toolchange = false); std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id); - std::string wipe_tower_tool_change(int extruder_id); /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() @@ -222,7 +236,7 @@ private: std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; std::unique_ptr m_pressure_equalizer; - std::unique_ptr m_wipe_tower; + std::unique_ptr m_wipe_tower; // Heights at which the skirt has already been extruded. std::vector m_skirt_done; @@ -251,6 +265,8 @@ private: size_t object_idx, size_t num_objects, size_t num_islands); + + friend class WipeTowerIntegration; }; } diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp index a6378efa94..728ab10c5f 100644 --- a/xs/src/libslic3r/GCode/WipeTower.hpp +++ b/xs/src/libslic3r/GCode/WipeTower.hpp @@ -46,13 +46,19 @@ public: // Is this the last layer of the wipe tower? bool is_last_layer) = 0; + enum Purpose { + PURPOSE_MOVE_TO_TOWER, + PURPOSE_EXTRUDE, + PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE, + }; + // Returns gcode for toolchange and the end position. // if new_tool == -1, just unload the current filament over the wipe tower. - virtual std::pair tool_change(int new_tool) = 0; + virtual std::pair tool_change(int new_tool, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0; // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. // Call this method only if layer_finished() is false. - virtual std::pair finish_layer() = 0; + virtual std::pair finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0; // Is the current layer finished? A layer is finished if either the wipe tower is finished, or // the wipe tower has been completely covered by the tool change extrusions, diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index e04be0e762..f2bb515a06 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -27,6 +27,8 @@ public: m_current_feedrate(0.f), m_extrusion_flow(0.f) {} + Writer& set_initial_position(const WipeTower::xy &pos) { m_current_pos = pos; return *this; } + Writer& set_z(float z) { m_current_z = z; return *this; } @@ -283,17 +285,15 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam return INVALID; } -std::pair WipeTowerPrusaMM::tool_change(int tool) +std::pair WipeTowerPrusaMM::tool_change(int tool, Purpose purpose) { // Either it is the last tool unload, // or there must be a nonzero wipe tower partitions available. assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0); if (m_idx_tool_change_in_layer == (unsigned int)(-1)) { - // Mark the brim as extruded. - m_idx_tool_change_in_layer = 0; // First layer, prime the extruder. - return toolchange_Brim(tool); + return toolchange_Brim(purpose); } box_coordinates cleaning_box( @@ -302,61 +302,70 @@ std::pair WipeTowerPrusaMM::tool_change(int tool) m_wipe_area - m_perimeter_width); PrusaMultiMaterial::Writer writer; - // Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower. - float initial_retract = ((m_current_material == SCAFF) ? 1.f : 0.5f) * m_retract; writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .append(";--------------------\n" "; CP TOOLCHANGE START\n") - .comment_with_value(" toolchange #", m_num_tool_changes ++) + .comment_with_value(" toolchange #", m_num_tool_changes) .comment_material(m_current_material) .append(";--------------------\n") - .speed_override(100) - // Lift for a Z hop. - .z_hop(m_zhop, 7200) - // Additional retract on move to tower. - .retract(initial_retract, 3600) - // Move to a starting position, one perimeter width inside the cleaning box. - .travel(((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) + - xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width), 7200) - // Unlift for a Z hop. - .z_hop_reset(7200) - // Additional retract on move to tower. - .load(initial_retract, 3600) - .load(m_retract, 1500) - // Increase extruder current for ramming. - .set_extruder_trimpot(750) - .flush_planner_queue(); + .speed_override(100); - // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. - toolchange_Unload(writer, cleaning_box, m_current_material, - m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]); + xy initial_position = ((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) + + xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width); - if (tool >= 0) { - // This is not the last change. - // Change the tool, set a speed override for soluble and flex materials. - toolchange_Change(writer, tool, m_material[tool]); - toolchange_Load(writer, cleaning_box); - // Wipe the newly loaded filament until the end of the assigned wipe area. - toolchange_Wipe(writer, cleaning_box); - // Draw a perimeter around cleaning_box and wipe. - toolchange_Done(writer, cleaning_box); + if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) { + // Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower. + float initial_retract = ((m_current_material == SCAFF) ? 1.f : 0.5f) * m_retract; + writer // Lift for a Z hop. + .z_hop(m_zhop, 7200) + // Additional retract on move to tower. + .retract(initial_retract, 3600) + // Move to a starting position, one perimeter width inside the cleaning box. + .travel(initial_position, 7200) + // Unlift for a Z hop. + .z_hop_reset(7200) + // Additional retract on move to tower. + .load(initial_retract, 3600) + .load(m_retract, 1500); + } else { + // Already at the initial position. + writer.set_initial_position(initial_position); } - // Reset the extruder current to a normal value. - writer.set_extruder_trimpot(550) - .flush_planner_queue() - .reset_extruder() - .append("; CP TOOLCHANGE END\n" - ";------------------\n" - "\n\n"); + if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) { + // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. + toolchange_Unload(writer, cleaning_box, m_current_material, + m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]); + + if (tool >= 0) { + // This is not the last change. + // Change the tool, set a speed override for soluble and flex materials. + toolchange_Change(writer, tool, m_material[tool]); + toolchange_Load(writer, cleaning_box); + // Wipe the newly loaded filament until the end of the assigned wipe area. + toolchange_Wipe(writer, cleaning_box); + // Draw a perimeter around cleaning_box and wipe. + toolchange_Done(writer, cleaning_box); + } + + // Reset the extruder current to a normal value. + writer.set_extruder_trimpot(550) + .flush_planner_queue() + .reset_extruder() + .append("; CP TOOLCHANGE END\n" + ";------------------\n" + "\n\n"); + + ++ m_num_tool_changes; + ++ m_idx_tool_change_in_layer; + m_current_wipe_start_y += m_wipe_area; + } - ++ m_idx_tool_change_in_layer; - m_current_wipe_start_y += m_wipe_area; return std::pair(writer.gcode(), writer.pos()); } -std::pair WipeTowerPrusaMM::toolchange_Brim(size_t /* tool */, bool sideOnly, float y_offset) +std::pair WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset) { const box_coordinates wipeTower_box( m_wipe_tower_pos, @@ -371,45 +380,57 @@ std::pair WipeTowerPrusaMM::toolchange_Brim(size_t / ";-------------------------------------\n" "; CP WIPE TOWER FIRST LAYER BRIM START\n"); - // Move with Z hop and prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower. - writer.z_hop(m_zhop, 7200) - .travel(wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0), 6000) - .z_hop_reset(7200) - .extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400); + xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0); - // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded. - // toolchange_Change(writer, int(tool), m_material[tool]); + if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) + // Move with Z hop. + writer.z_hop(m_zhop, 7200) + .travel(initial_position, 6000) + .z_hop_reset(7200); + else + writer.set_initial_position(initial_position); - if (sideOnly) { - float x_offset = m_perimeter_width; - for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width) - writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset), 7000) - .extrude(wipeTower_box.lu + xy(- x_offset, - y_offset), 2100); - writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000); - x_offset = m_perimeter_width; - for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width) - writer.travel (wipeTower_box.rd + xy(x_offset, y_offset), 7000) - .extrude(wipeTower_box.ru + xy(x_offset, - y_offset), 2100); - } else { - // Extrude 4 rounds of a brim around the future wipe tower. - box_coordinates box(wipeTower_box); - //FIXME why is the box shifted in +Y by 0.5f * m_perimeter_width? - box.translate(0.f, 0.5f * m_perimeter_width); - box.expand(0.5f * m_perimeter_width); - for (size_t i = 0; i < 4; ++ i) { - writer.travel (box.ld, 7000) - .extrude(box.lu, 2100).extrude(box.ru) - .extrude(box.rd ).extrude(box.ld); - box.expand(m_perimeter_width); + if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) { + // Prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower. + writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400); + + // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded. + // toolchange_Change(writer, int(tool), m_material[tool]); + + if (sideOnly) { + float x_offset = m_perimeter_width; + for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width) + writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset), 7000) + .extrude(wipeTower_box.lu + xy(- x_offset, - y_offset), 2100); + writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000); + x_offset = m_perimeter_width; + for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width) + writer.travel (wipeTower_box.rd + xy(x_offset, y_offset), 7000) + .extrude(wipeTower_box.ru + xy(x_offset, - y_offset), 2100); + } else { + // Extrude 4 rounds of a brim around the future wipe tower. + box_coordinates box(wipeTower_box); + //FIXME why is the box shifted in +Y by 0.5f * m_perimeter_width? + box.translate(0.f, 0.5f * m_perimeter_width); + box.expand(0.5f * m_perimeter_width); + for (size_t i = 0; i < 4; ++ i) { + writer.travel (box.ld, 7000) + .extrude(box.lu, 2100).extrude(box.ru) + .extrude(box.rd ).extrude(box.ld); + box.expand(m_perimeter_width); + } } - } - // Move to the front left corner and wipe along the front edge. - writer.travel(wipeTower_box.ld, 7000) - .travel(wipeTower_box.rd) - .travel(wipeTower_box.ld) - .append("; CP WIPE TOWER FIRST LAYER BRIM END\n" - ";-----------------------------------\n"); + // Move to the front left corner and wipe along the front edge. + writer.travel(wipeTower_box.ld, 7000) + .travel(wipeTower_box.rd) + .travel(wipeTower_box.ld) + .append("; CP WIPE TOWER FIRST LAYER BRIM END\n" + ";-----------------------------------\n"); + + // Mark the brim as extruded. + m_idx_tool_change_in_layer = 0; + } return std::pair(writer.gcode(), writer.pos()); } @@ -425,7 +446,7 @@ void WipeTowerPrusaMM::toolchange_Unload( float xr = cleaning_box.rd.x - 0.5f * m_perimeter_width; float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width; - writer.append("; CP TOOLCHANGE UNLOAD"); + writer.append("; CP TOOLCHANGE UNLOAD\n"); // Ram the hot material out of the extruder melt zone. // Current extruder position is on the left, one perimeter inside the cleaning box in both X and Y. @@ -613,7 +634,7 @@ void WipeTowerPrusaMM::toolchange_Done( .feedrate(6000); } -std::pair WipeTowerPrusaMM::finish_layer() +std::pair WipeTowerPrusaMM::finish_layer(Purpose purpose) { // This should only be called if the layer is not finished yet. // Otherwise the caller would likely travel to the wipe tower in vain. @@ -624,7 +645,8 @@ std::pair WipeTowerPrusaMM::finish_layer() .set_z(m_z_pos) .append(";--------------------\n" "; CP EMPTY GRID START\n") - .comment_with_value(" layer #", m_num_layer_changes ++); + // m_num_layer_changes is incremented by set_z, so it is 1 based. + .comment_with_value(" layer #", m_num_layer_changes - 1); // Slow down on the 1st layer. float speed_factor = m_is_first_layer ? 0.5f : 1.f; @@ -638,60 +660,74 @@ std::pair WipeTowerPrusaMM::finish_layer() fill_box.rd.y += firstLayerOffset; } - if (m_idx_tool_change_in_layer == 0) - // There were no tool changes at all in this layer. - writer.retract(m_retract * 1.5f, 3600) - // Jump with retract to fill_box.ld + a random shift in +x. - .z_hop(m_zhop, 7200) - .travel(fill_box.ld + xy(5.f + 15.f * float(rand()) / RAND_MAX, 0.f), 7000) - .z_hop_reset(7200) - // Prime the extruder. - .load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600); - - // Extrude the first perimeter. - box_coordinates box = fill_box; - writer.extrude(box.lu, 2400 * speed_factor) - .extrude(box.ru) - .extrude(box.rd) - .extrude(box.ld + xy(m_perimeter_width / 2, 0)); - - // Extrude second perimeter. - box.expand(- m_perimeter_width / 2); - writer.extrude(box.lu, 3200 * speed_factor) - .extrude(box.ru) - .extrude(box.rd) - .extrude(box.ld + xy(m_perimeter_width / 2, 0)) - .extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2)); - - // Extrude an inverse U at the left of the region. - writer.extrude(fill_box.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor) - .extrude(fill_box.lu + xy(m_perimeter_width * 3, - m_perimeter_width)) - .extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width)) - .extrude(fill_box.ld + xy(m_perimeter_width * 6, m_perimeter_width)); - - if (fill_box.lu.y - fill_box.ld.y > 4.f) { - // Extrude three zig-zags. - float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f; - for (size_t i = 0; i < 3; ++ i) { - writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor); - writer.extrude(writer.x() , fill_box.lu.y - m_perimeter_width * 8); - writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width ); - writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width * 8); - writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width * 8); - writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width ); + if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) { + if (m_idx_tool_change_in_layer == 0) { + // There were no tool changes at all in this layer. + writer.retract(m_retract * 1.5f, 3600) + // Jump with retract to fill_box.ld + a random shift in +x. + .z_hop(m_zhop, 7200) + .travel(fill_box.ld + xy(5.f + 15.f * float(rand()) / RAND_MAX, 0.f), 7000) + .z_hop_reset(7200) + // Prime the extruder. + .load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600); + } else { + // Otherwise the extruder is already over the wipe tower. } + } else { + // The print head is inside the wipe tower. Rather move to the start of the following extrusion. + // writer.set_initial_position(fill_box.ld); + writer.travel(fill_box.ld, 7000); } - // Extrude an inverse U at the left of the region. - writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor) - .extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width)) - .extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width)) - .extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width)) - // Wipe along the front side of the current wiping box. - .travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200) - .travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2)) - .append("; CP EMPTY GRID END\n" - ";------------------\n\n\n\n\n\n\n"); + if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) { + // Extrude the first perimeter. + box_coordinates box = fill_box; + writer.extrude(box.lu, 2400 * speed_factor) + .extrude(box.ru) + .extrude(box.rd) + .extrude(box.ld + xy(m_perimeter_width / 2, 0)); + + // Extrude second perimeter. + box.expand(- m_perimeter_width / 2); + writer.extrude(box.lu, 3200 * speed_factor) + .extrude(box.ru) + .extrude(box.rd) + .extrude(box.ld + xy(m_perimeter_width / 2, 0)) + .extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2)); + + // Extrude an inverse U at the left of the region. + writer.extrude(fill_box.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor) + .extrude(fill_box.lu + xy(m_perimeter_width * 3, - m_perimeter_width)) + .extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width)) + .extrude(fill_box.ld + xy(m_perimeter_width * 6, m_perimeter_width)); + + if (fill_box.lu.y - fill_box.ld.y > 4.f) { + // Extrude three zig-zags. + float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f; + for (size_t i = 0; i < 3; ++ i) { + writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor); + writer.extrude(writer.x() , fill_box.lu.y - m_perimeter_width * 8); + writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width ); + writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width * 8); + writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width * 8); + writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width ); + } + } + + // Extrude an inverse U at the left of the region. + writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor) + .extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width)) + .extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width)) + .extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width)) + // Wipe along the front side of the current wiping box. + .travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200) + .travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2)) + .append("; CP EMPTY GRID END\n" + ";------------------\n\n\n\n\n\n\n"); + + // Indicate that this wipe tower layer is fully covered. + m_idx_tool_change_in_layer = m_max_color_changes; + } return std::pair(writer.gcode(), writer.pos()); } diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index e44056d4cc..c8eb6c5438 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -88,6 +88,7 @@ public: m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0; m_current_wipe_start_y = 0.f; m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL; + ++ m_num_layer_changes; int layer_idx = int(std::floor(layer_height * 100) + 0.5f); switch (layer_idx) @@ -103,17 +104,17 @@ public: } // Return the wipe tower position. - virtual const xy& position() const { return m_wipe_tower_pos; } + virtual const xy& position() const { return m_wipe_tower_pos; } // The wipe tower is finished, there should be no more tool changes or wipe tower prints. - virtual bool finished() const { return m_max_color_changes == 0; } + virtual bool finished() const { return m_max_color_changes == 0; } // Returns gcode for a toolchange and a final print head position. // On the first layer, extrude a brim around the future wipe tower first. - virtual std::pair tool_change(int new_tool); + virtual std::pair tool_change(int new_tool, Purpose purpose); // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. // Call this method only if layer_finished() is false. - virtual std::pair finish_layer(); + virtual std::pair finish_layer(Purpose purpose); // Is the current layer finished? A layer is finished if either the wipe tower is finished, or // the wipe tower has been completely covered by the tool change extrusions, @@ -132,27 +133,27 @@ private: }; // Left front corner of the wipe tower in mm. - xy m_wipe_tower_pos; + xy m_wipe_tower_pos; // Width of the wipe tower. - float m_wipe_tower_width; + float m_wipe_tower_width; // Per color Y span. - float m_wipe_area; + float m_wipe_area; // Current Z position. - float m_z_pos = 0.f; + float m_z_pos = 0.f; // Maximum number of color changes per layer. - size_t m_max_color_changes = 0; + size_t m_max_color_changes = 0; // Is this the 1st layer of the print? If so, print the brim around the waste tower. - bool m_is_first_layer = false; + bool m_is_first_layer = false; // Is this the last layer of this waste tower? - bool m_is_last_layer = false; + bool m_is_last_layer = false; // G-code generator parameters. - float m_zhop = 0.5f; - float m_retract = 4.f; + float m_zhop = 0.5f; + float m_retract = 4.f; // Width of an extrusion line, also a perimeter spacing for 100% infill. - float m_perimeter_width = 0.5f; + float m_perimeter_width = 0.5f; // Extrusion flow is derived from m_perimeter_width, layer height and filament diameter. - float m_extrusion_flow = 0.029f; + float m_extrusion_flow = 0.029f; // Extruder specific parameters. material_type m_material[4]; @@ -206,7 +207,7 @@ private: // Returns gcode for wipe tower brim // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower // offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower - std::pair toolchange_Brim(size_t tool, bool sideOnly = false, float y_offset = 0.f); + std::pair toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f); void toolchange_Unload( PrusaMultiMaterial::Writer &writer, diff --git a/xs/src/libslic3r/GCodeReader.hpp b/xs/src/libslic3r/GCodeReader.hpp index 8506a49d23..03380f32e9 100644 --- a/xs/src/libslic3r/GCodeReader.hpp +++ b/xs/src/libslic3r/GCodeReader.hpp @@ -23,12 +23,12 @@ public: GCodeLine(GCodeReader* _reader) : reader(_reader) {}; bool has(char arg) const { return this->args.count(arg) > 0; }; - float get_float(char arg) const { return atof(this->args.at(arg).c_str()); }; - float new_X() const { return this->has('X') ? atof(this->args.at('X').c_str()) : this->reader->X; }; - float new_Y() const { return this->has('Y') ? atof(this->args.at('Y').c_str()) : this->reader->Y; }; - float new_Z() const { return this->has('Z') ? atof(this->args.at('Z').c_str()) : this->reader->Z; }; - float new_E() const { return this->has('E') ? atof(this->args.at('E').c_str()) : this->reader->E; }; - float new_F() const { return this->has('F') ? atof(this->args.at('F').c_str()) : this->reader->F; }; + float get_float(char arg) const { return float(atof(this->args.at(arg).c_str())); }; + float new_X() const { return this->has('X') ? float(atof(this->args.at('X').c_str())) : this->reader->X; }; + float new_Y() const { return this->has('Y') ? float(atof(this->args.at('Y').c_str())) : this->reader->Y; }; + float new_Z() const { return this->has('Z') ? float(atof(this->args.at('Z').c_str())) : this->reader->Z; }; + float new_E() const { return this->has('E') ? float(atof(this->args.at('E').c_str())) : this->reader->E; }; + float new_F() const { return this->has('F') ? float(atof(this->args.at('F').c_str())) : this->reader->F; }; float dist_X() const { return this->new_X() - this->reader->X; }; float dist_Y() const { return this->new_Y() - this->reader->Y; }; float dist_Z() const { return this->new_Z() - this->reader->Z; }; diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index d0bcfee374..b69d9c564c 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -17,6 +17,9 @@ #define SLIC3R_VERSION "1.33.8.devel" #define SLIC3R_BUILD "UNKNOWN" +typedef long coord_t; +typedef double coordf_t; + //FIXME This epsilon value is used for many non-related purposes: // For a threshold of a squared Euclidean distance, // for a trheshold in a difference of radians, @@ -39,12 +42,11 @@ // 3mm ring around the top / bottom / bridging areas. //FIXME This is quite a lot. #define EXTERNAL_INFILL_MARGIN 3. +//FIXME Better to use an inline function with an explicit return type. +//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } #define scale_(val) ((val) / SCALING_FACTOR) #define unscale(val) ((val) * SCALING_FACTOR) #define SCALED_EPSILON scale_(EPSILON) -typedef long coord_t; -typedef double coordf_t; - /* Implementation of CONFESS("foo"): */ #ifdef _MSC_VER #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)