From 82336a0616a555574fc553fa4d0dccaa00e8c9be Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:47:16 -0500 Subject: [PATCH 01/27] Create PurgeLinesAndUnload.py This script has 4 options. Add Purge Lines will draw lines left, right, top, or bottom of the build plate and either fill length of half length. If a print takes up the entire width then the purge lines could be moved to the bottom. --- .../scripts/PurgeLinesAndUnload.py | 745 ++++++++++++++++++ 1 file changed, 745 insertions(+) create mode 100644 plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py new file mode 100644 index 0000000000..1e606cf17b --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -0,0 +1,745 @@ +# August 2024 - GregValiant (Greg Foresi) +# +# NOTE: You may have purge lines in your startup, or you may use this script, you should not do both. The script will attempt to comment out existing StartUp purge lines. +# 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface. Any Purge lines currently in the StartUp should be removed before using this script. +# The setting 'Purge Line Length' is only avaialble for rectangular beds because I was too lazy to calculate the 45° arcs. +# 'Move to Start' takes an orthogonal path around the periphery before moving in to the print start location. It eliminates strings across the print area. +# 'Adjust Starting E' is a correction in the E location before the skirt/brim starts. The user can make an adjustment so that the skirt / brim / raft starts where it should. +# 'Unload' adds code to the Ending Gcode that will unload the filament from the machine. The unlaod distance is broken into chunks to avoid overly long E distances. +# Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface. + +from ..Script import Script +from UM.Application import Application +from UM.Message import Message +import re +import os +from UM.Logger import Logger + +class PurgeLinesAndUnload(Script): + + def initialize(self) -> None: + super().initialize() + # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. + curaApp = Application.getInstance().getGlobalContainerStack() + startup_gcode = curaApp.getProperty("machine_start_gcode", "value") + start_lines = startup_gcode.splitlines() + for line in start_lines: + if line.startswith("G1") and " E" in line and (" X" in line or " Y" in line): + Message(title = "[Purge Lines and Unload]", text = "It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() + break + self._instance.setProperty("is_rectangular", "value", True if curaApp.getProperty("machine_shape", "value") == "rectangular" else False) + self._extruder = curaApp.extruderList + #This is set in 'Add Purge Lines' and is used by 'Move to Start' to indicate which corner the nozzle is in after the purge lines + self._purge_end_loc = None + # Set the default E adjustment + self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self._extruder[0].getProperty("retraction_amount", "value")), 1))) + + def getSettingDataString(self): + return """{ + "name": "Purge Lines and Unload Filament", + "key": "PurgeLinesAndUnload", + "metadata": {}, + "version": 2, + "settings": + { + "add_purge_lines": + { + "label": "Add Purge Lines to StartUp", + "description": "The purge lines can be left, right, front or back. If there are purge lines present in the StartUp Gcode remove them or comment them out before using this script. You don't want to double dip.", + "type": "bool", + "default_value": false, + "value": false, + "enabled": true + }, + "purge_line_location": + { + "label": " Purge Line Location", + "description": "What edge of the build plate should have the purge lines. If the printer is 'Elliptical' then it is assumed to be an 'Origin At Center' printer and the purge lines are 90° arcs.", + "type": "enum", + "options": { + "purge_left": "On left edge (Xmin)", + "purge_right": "On right edge (Xmax)", + "purge_bottom": "On front edge (Ymin)", + "purge_top": "On back edge (Ymax)"}, + "default_value": "purge_left", + "enabled": "add_purge_lines" + }, + "purge_line_length": + { + "label": " Purge Line Length", + "description": "Select 'Full' for the entire Height or Width of the build plate. Select 'Half' for shorter purge lines. NOTE: This has no effect on elliptic beds.", + "type": "enum", + "options": { + "purge_full": "Full", + "purge_half": "Half"}, + "default_value": "purge_full", + "enabled": "add_purge_lines and is_rectangular" + }, + "move_to_start": + { + "label": "Circle around to layer start", + "description": "Depending on where the 'Layer Start X' and 'Layer Start Y' are for the print, the opening travel move can pass across the print area and leave a string there. This option will generate an orthogonal path that moves the nozzle around the edges of the build plate and then comes in to the Start Point. The nozzle will drop and touch the build plate at each stop in order to nail down the string so it doesn't follow in a straight line.", + "type": "bool", + "default_value": false, + "enabled": true + }, + "adjust_starting_e": + { + "label": "Adjust Starting E location", + "description": "If there is a retraction after the purge lines in the Startup Gcode (like the 'Add Purge Lines' script here does) then often the skirt does not start where the nozzle starts. It is because Cura always adds a retraction prior to the print starting which results in a double retraction. Enabling this will allow you to adjust the starting E location and tune it so the skirt/brim/model starts right where it should. To fix a blob enter a positive number. To fix a 'dry start' enter a negative number.", + "type": "bool", + "default_value": false, + "value": false, + "enabled": true + }, + "adjust_e_loc_to": + { + "label": " Starting E location", + "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", + "type": "float", + "unit": "mm ", + "default_value": -6.5, + "enabled": "adjust_starting_e" + }, + "enable_unload": + { + "label": "Unload filament at print end", + "description": "Adds an unload script to the Ending Gcode section. It goes in just ahead of the M104 S0. This scripts always unloads the active extruder. If the unload distance is greater than 150mm it will be broken into chunks to avoid tripping the excessive extrusion warning in some firmware.", + "type": "bool", + "default_value": false, + "enabled": true + }, + "unload_distance": + { + "label": " Unload Distance", + "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", + "type": "int", + "default_value": 440, + "unit": "mm ", + "enabled": "enable_unload" + }, + "is_rectangular": + { + "label": "Bed is rectangular", + "description": "Hidden setting that disables 'purge line length' for elliptical beds.", + "type": "bool", + "default_value": false, + "enabled": false + } + } + }""" + + def execute(self, data): + # Run the selected procedures + if self.getSettingValueByKey("add_purge_lines"): + self._add_purge_lines(data) + if self.getSettingValueByKey("move_to_start"): + self._move_to_start(data) + if self.getSettingValueByKey("adjust_starting_e"): + self._adjust_starting_e(data) + if self.getSettingValueByKey("enable_unload"): + self._unload_filament(data) + # Format the startup and ending gcodes + data[1] = self._format_string(data[1]) + data[len(data) - 1] = self._format_string(data[len(data) - 1]) + return data + + # Add Purge Lines to the user defined position on the build plate + def _add_purge_lines(self, data: str): + curaApp = Application.getInstance().getGlobalContainerStack() + retract_dist = self._extruder[0].getProperty("retraction_amount", "value") + retract_enable = self._extruder[0].getProperty("retraction_enable", "value") + retract_speed = self._extruder[0].getProperty("retraction_retract_speed", "value") * 60 + bed_shape = str(curaApp.getProperty("machine_shape", "value")) + origin_at_center = bool(curaApp.getProperty("machine_center_is_zero", "value")) + machine_width = curaApp.getProperty("machine_width", "value") + machine_depth = curaApp.getProperty("machine_depth", "value") + material_diameter = self._extruder[0].getProperty("material_diameter", "value") + mm3_per_mm = (material_diameter / 2)**2 * 3.14159 + init_line_width = self._extruder[0].getProperty("skirt_brim_line_width", "value") + where_at = self.getSettingValueByKey("purge_line_location") + travel_speed = self._extruder[0].getProperty("speed_travel", "value") * 60 + print_speed = round(self._extruder[0].getProperty("speed_print", "value") * 60 * .75) + purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False + purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" + + # Normal cartesian printer with origin at the left front corner + if bed_shape == "rectangular" and not origin_at_center: + if where_at == "purge_left": + purge_len = int(machine_depth) - 20 if purge_extrusion_full else int(machine_depth / 2) + y_stop = int(machine_depth - 10) if purge_extrusion_full else int(machine_depth / 2) + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str = purge_str.replace("Lines", "Lines at MinX") + purge_str += f"G0 F{travel_speed} X0 Y10 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X0 Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X3 Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X3 Y10 E{round(purge_volume * 2,5)} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X3 Y20 Z0.3 ; Slide over and down\n" + purge_str += "G0 X3 Y35 ; Wipe\n" + self._purge_end_loc = "LF" + elif where_at == "purge_right": + purge_len = int(machine_depth) - 20 if purge_extrusion_full else int(machine_depth / 2) + y_stop = 10 if purge_extrusion_full else int(machine_depth / 2) + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str = purge_str.replace("Lines", "Lines at MaxX") + purge_str += f"G0 F{travel_speed} X{machine_width} ; Move\nG0 Y{machine_depth - 10} ; Move\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{machine_width} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{machine_width - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{machine_width - 3} Y{machine_depth - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{machine_width - 3} Y{machine_depth - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{machine_width - 3} Y{machine_depth - 35} ; Wipe\n" + self._purge_end_loc = "RR" + elif where_at == "purge_bottom": + purge_len = int(machine_width) - 20 if purge_extrusion_full else int(machine_width / 2) + x_stop = int(machine_width - 10) if purge_extrusion_full else int(machine_width / 2) + purge_str = purge_str.replace("Lines", "Lines at MinY") + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{travel_speed} X10 Y0 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y0 E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y3 ; Move over\n" + purge_str += f"G1 F{print_speed} X10 Y3 E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X20 Y3 Z0.3 ; Slide over and down\n" + purge_str += "G0 X35 Y3 ; Wipe\n" + self._purge_end_loc = "LF" + elif where_at == "purge_top": + purge_len = int(machine_width - 20) if purge_extrusion_full else int(machine_width / 2) + x_stop = 10 if purge_extrusion_full else int(machine_width / 2) + purge_str = purge_str.replace("Lines", "Lines at MaxY") + purge_len = int(machine_width) - 20 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{travel_speed} Y{machine_depth} ; Ortho Move to back\n" + purge_str += f"G0 X{machine_width - 10} ; Ortho move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{machine_depth} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{machine_depth - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{machine_width - 10} Y{machine_depth - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" + purge_str += f"G0 F{print_speed} X{machine_width - 20} Y{machine_depth - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{machine_width - 35} Y{machine_depth - 3} ; Wipe\n" + self._purge_end_loc = "RR" + + # Some cartesian printers (BIBO, Weedo, etc.) are Origin at Center + elif bed_shape == "rectangular" and origin_at_center: + if where_at == "purge_left": + purge_len = int(machine_depth - 20) if purge_extrusion_full else int(machine_depth / 2) + y_stop = int((machine_depth / 2) - 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{travel_speed} X-{machine_width / 2} Y-{(machine_depth / 2) - 10} ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X-{machine_width / 2} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X-{(machine_width / 2) - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X-{(machine_width / 2) - 3} Y-{(machine_depth / 2) - 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X-{(machine_width / 2) - 3} Y-{(machine_depth / 2) - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X-{(machine_width / 2) - 3} Y-{(machine_depth / 2) - 35} ; Wipe\n" + self._purge_end_loc = "LF" + elif where_at == "purge_right": + purge_len = int(machine_depth - 20) if purge_extrusion_full else int(machine_depth / 2) + y_stop = int((machine_depth / 2) - 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{travel_speed} X{machine_width / 2} Z2 ; Move\nG0 Y{(machine_depth / 2) - 10} Z2 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{machine_width / 2} Y-{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{(machine_width / 2) - 3} Y-{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{(machine_width / 2) - 3} Y{(machine_depth / 2) - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{(machine_width / 2) - 3} Y{(machine_depth / 2) - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{travel_speed} X{(machine_width / 2) - 3} Y{(machine_depth / 2) - 35} ; Wipe\n" + self._purge_end_loc = "RR" + elif where_at == "purge_bottom": + purge_len = int(machine_width - 20) if purge_extrusion_full else int(machine_width / 2) + x_stop = int((machine_width / 2) - 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{travel_speed} X-{machine_width / 2 - 10} Z2 ; Move\nG0 Y-{machine_depth / 2} Z2 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y-{machine_depth / 2} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y-{machine_depth / 2 - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X-{machine_width / 2 - 10} Y-{machine_depth / 2 - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X-{(machine_width / 2) - 20} Y-{(machine_depth / 2) - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X-{(machine_width / 2) - 35} Y-{(machine_depth / 2) - 3} ; Wipe\n" + self._purge_end_loc = "LF" + elif where_at == "purge_top": + purge_len = int(machine_width - 20) if purge_extrusion_full else int(machine_width / 2) + x_stop = int((machine_width / 2) - 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{travel_speed} Y{machine_depth / 2} Z2; Ortho Move to back\n" + purge_str += f"G0 X{machine_width / 2 - 10} Z2 ; Ortho Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X-{x_stop} Y{machine_depth / 2} E{purge_volume} ; First line\n" + purge_str += f"G0 X-{x_stop} Y{machine_depth / 2 - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{machine_width / 2 - 10} Y{machine_depth / 2 - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{machine_width / 2 - 20} Y{machine_depth / 2 - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X{machine_width / 2 - 35} Y{machine_depth / 2 - 3} ; Wipe\n" + self._purge_end_loc = "RR" + + # Elliptic printers with Origin at Center + elif bed_shape == "elliptic": + if where_at in ["purge_left","purge_right"]: + radius_1 = round((machine_width / 2) - 1,2) + elif where_at in ["purge_bottom", "purge_top"]: + radius_1 = round((machine_depth / 2) - 1,2) + purge_len = int(radius_1) * 3.14159 / 4 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + if where_at == "purge_left": + purge_str += f"G0 F{travel_speed} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X-{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" + purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} I{round((radius_1 - 3) * .707,2)} J-{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1,5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707,2)} ; Wipe\n" + self.purge_end_loc = "LF" + elif where_at == "purge_right": + purge_str += f"G0 F{travel_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G3 F{print_speed} X{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I-{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} I-{round((radius_1 - 3) * .707,2)} J-{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1,5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707,2)}\n" + self.purge_end_loc = "RR" + elif where_at == "purge_bottom": + purge_str += f"G0 F{travel_speed} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G3 F{print_speed} X{round(radius_1 * .707,2)} Y-{round(radius_1 * .707,2)} I{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} I-{round((radius_1 - 3) * .707,2)} J{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707,2)}\n" + self.purge_end_loc = "LF" + elif where_at == "purge_top": + purge_str += f"G0 F{travel_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I-{round(radius_1 * .707,2)} J-{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X-{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} I{round((radius_1 - 3) * .707,2)} J-{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1,5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5\nG4 S1\n" + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707,2)}\n" + self.purge_end_loc = "RR" + + # Common ending for purge_str + purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" + + # If there is a move to the prime tower location after purging then it needs to be accounted for + if curaApp.getProperty("machine_extruder_count", "value") > 1: + data[1] = self._move_to_prime_tower(data[1], bed_shape, origin_at_center, machine_width, machine_depth, travel_speed) + + # Comment out any existing purge lines in Data[1] + startup = data[1].split("\n") + for index, line in enumerate(startup): + if line.startswith("G1") and " E" in line and (" X" in line or " Y" in line): + next_line = index + try: + while not startup[next_line].startswith ("G92 E0"): + startup[next_line] = ";" + startup[next_line] + next_line += 1 + except: + break + data[1] = "\n".join(startup) + + # Find the insertion location in data[1] + purge_str = self._format_string(purge_str) + startup_section = data[1].split("\n") + for num in range(len(startup_section) - 1, 0, -1): + # In Absolute Extrusion mode - insert above the last G92 E0 line + if "G92 E0" in startup_section[num]: + insert_index = num + break + # In Relative Extrusion mode - insert above the M83 line + elif "M83" in startup_section[num]: + insert_index = num + break + startup_section.insert(insert_index, purge_str) + data[1] = "\n".join(startup_section) + return + + # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. + def _move_to_start(self, data: str) -> str: + curaApp = Application.getInstance().getGlobalContainerStack() + bed_shape = str(curaApp.getProperty("machine_shape", "value")) + origin_at_center = bool(curaApp.getProperty("machine_center_is_zero", "value")) + machine_width = curaApp.getProperty("machine_width", "value") + machine_depth = curaApp.getProperty("machine_depth", "value") + if curaApp.getProperty("machine_extruder_count", "value") > 1: + self._purge_end_loc = self._get_real_start_point(data[1], bed_shape, origin_at_center, machine_width, machine_depth) + layer = data[2].split("\n") + start_x = None + start_y = None + for line in layer: + if line.startswith("G0") and " X" in line and " Y" in line: + start_x = self.getValue(line, "X") + start_y = self.getValue(line, "Y") + break + if start_x == None: start_x = 0 + if start_y == None: start_y = 0 + if self._purge_end_loc == None: + purge_end_loc = "LF" + else: + purge_end_loc = self._purge_end_loc + travel_speed = round(self._extruder[0].getProperty("speed_travel", "value") * 60) + move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" + midpoint_x = machine_width / 2 + midpoint_y = machine_depth / 2 + if not origin_at_center: + if float(start_x) <= float(midpoint_x): + goto_str = "Lt" + else: + goto_str = "Rt" + if float(start_y) <= float(midpoint_y): + goto_str += "Frt" + else: + goto_str += "Bk" + else: + if float(start_x) <= 0: + goto_str = "Lt" + else: + goto_str = "Rt" + if float(start_y) <= 0: + goto_str += "Frt" + else: + goto_str += "Bk" + + # If purge lines was not selected, and the printer is multi-extruder, there may be a move to the purge tower that needs to be considered + if not self.getSettingValueByKey("add_purge_lines"): + if curaApp.getProperty("machine_extruder_count", "value") > 1: + data[1] = self._move_to_prime_tower(data[1], bed_shape, origin_at_center, machine_width, machine_depth, travel_speed) + + # Depending on which quadrant the XY layer start is, move around the periphery before coming in to the start position + if bed_shape == "rectangular" and not origin_at_center: + if purge_end_loc == "LF": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X5 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y5 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X5 Z2; Ortho Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y5 Z2 ; Ortho Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} X{start_x} ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{travel_speed} X5 ; Ortho Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y5 Z2 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X5 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y5 Z2 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} X{machine_width - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif purge_end_loc == "RR": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X5 Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y5 Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X{start_x} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y5 Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{travel_speed} X5 Z2 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X{machine_width - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + + elif bed_shape == "rectangular" and origin_at_center: + if purge_end_loc == "LF": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X-{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{travel_speed} X-{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif purge_end_loc == "RR": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{travel_speed} X-{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + + elif bed_shape == "elliptic" and origin_at_center: + radius = machine_width / 2 + offset_sin = round(2**.5 / 2 * radius, 2) + if purge_end_loc == "LR": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif purge_end_loc == "RR": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif purge_end_loc == "LF": + if goto_str == "LtFrt": + move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + move_str += ";---------------------[End of layer start travels]" + # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. + startup = data[1].split("\n") + for index, line in enumerate(startup): + if "LAYER_COUNT" in line: + lay_count = startup.pop(index) + "\n" + break + move_str = self._format_string(move_str) + if move_str.startswith("\n"): + move_str = move_str[1:] + startup.append(move_str) + startup.append(lay_count) + data[1] = "\n".join(startup) + # Remove any double spaced lines + data[1] = data[1].replace("\n\n", "\n") + return + + # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into chunks. + def _unload_filament(self, data: str) -> str: + extrude_speed = 3000 + unload_distance = self.getSettingValueByKey("unload_distance") + lines = data[len(data) - 1].split("\n") + for index, line in enumerate(lines): + # Unload the filament just before the hot end turns off. + if line.startswith("M104") and "S0" in line: + filament_str = "M83 ; [Unload] Relative extrusion\nM400 ; Complete all moves\n" + if unload_distance > 150: + temp_unload = unload_distance + while temp_unload > 150: + filament_str += "G1 F" + str(int(extrude_speed)) + " E-150 ; Unload some\n" + temp_unload -= 150 + if 0 < temp_unload <= 150: + filament_str += "G1 F" + str(int(extrude_speed)) + " E-" + str(temp_unload) + " ; Unload the remainder\nM82 ; Absolute Extrusion\nG92 E0 ; Reset Extruder\n" + else: + filament_str += "G1 F" + str(int(extrude_speed)) + " E-" + str(unload_distance) + " ; Unload\nM82 ; Absolute Extrusion\nG92 E0 ; Reset Extruder\n" + break + lines[index] = filament_str + lines[index] + data[len(data) - 1] = "\n".join(lines) + return + + # Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out. + def _adjust_starting_e(self, data: str) -> str: + curaApp = Application.getInstance().getGlobalContainerStack() + retract_enabled = self._extruder[0].getProperty("retraction_enable", "value") + if not retract_enabled: + return + adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") + lines = data[1].split("\n") + lines.reverse() + for index, line in enumerate(lines): + if re.search("G1 F(\d*) E-(\d.*)", line) is not None: + lines[index] = re.sub("G1 F(\d*) E-(\d.*)", f"G92 E{adjust_amt}", line) + lines.reverse() + data[1] = "\n".join(lines) + break + return + + # Format the purge or travel-to-start strings. No reason they shouldn't look nice. + def _format_string(self, any_gcode_str: str): + temp_lines = any_gcode_str.split("\n") + gap_len = 0 + for temp_line in temp_lines: + if ";" in temp_line and not temp_line.startswith(";"): + if gap_len - len(temp_line.split(";")[0]) + 1 < 0: + gap_len = len(temp_line.split(";")[0]) + 1 + if gap_len < 30: gap_len = 30 + for temp_index, temp_line in enumerate(temp_lines): + if ";" in temp_line and not temp_line.startswith(";"): + temp_lines[temp_index] = temp_line.replace(temp_line.split(";")[0], temp_line.split(";")[0] + str(" " * (gap_len - len(temp_line.split(";")[0]))),1) + # This formats lines that are commented out but contain additional comments Ex: ;M420 ; leveling mesh + elif temp_line.startswith(";") and ";" in temp_line[1:]: + temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], ";" + temp_line[1:].split(";")[0] + str(" " * (gap_len - 1 - len(temp_line[1:].split(";")[0]))),1) + any_gcode_str = "\n".join(temp_lines) + return any_gcode_str + + # Get the actual layer start point of the print before adding movements + def _get_real_start_point(self, first_section: str, bed_shape: str, origin_at_center: bool, machine_width: int, machine_depth: int): + startup = first_section.split("\n") + last_line = startup[len(startup) - 1] + last_x = None + if last_line[:3] in ("G0 ", "G1 ") and " X" in last_line and " Y" in last_line: + last_x = self.getValue(last_line, "X") + last_y = self.getValue(last_line, "Y") + if last_x == None: + return self._purge_end_loc + else: + if bed_shape == "rectangular" and not origin_at_center: + midpoint_x = machine_width / 2 + midpoint_y = machine_depth / 2 + elif bed_shape in ("rectangular", "elliptic") and origin_at_center: + midpoint_x = 0 + midpoint_y = 0 + if last_x < midpoint_x and last_y < midpoint_y: + return "LF" + if last_x > midpoint_x and last_y < midpoint_y: + return "RF" + if last_x > midpoint_x and last_y > midpoint_y: + return "RR" + if last_x < midpoint_x and last_y > midpoint_y: + return "LR" + + # Multi-extruders may get a move to the prime tower just before layer 0 starts. The adjusted lines move around the periphery instead of across the middle. + def _get_adjustment_lines(self, prime_tower_loc: str, purge_end_loc: str, bed_shape: str, origin_at_center: bool, machine_width: int, machine_depth: int, travel_speed: int): + adj_lines = "" + if not origin_at_center: + midpoint_x = machine_width / 2 + midpoint_y = machine_depth / 2 + max_x = machine_width - 7 + min_x = 7 + max_y = machine_depth - 7 + min_y = 7 + elif origin_at_center: + midpoint_x = 0 + midpoint_y = 0 + max_x = (machine_width / 2) - 7 + min_x = -abs((machine_width / 2) - 7) + max_y = (machine_depth / 2) - 7 + min_y = -abs((machine_depth / 2) - 7) + if purge_end_loc == "LF": + if prime_tower_loc == "LF": + adj_lines = "" + if prime_tower_loc == "RF": + adj_lines = f"G0 F{travel_speed} X{max_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" + if prime_tower_loc == "RR": + adj_lines = f"G0 F{travel_speed} X{max_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" + if prime_tower_loc == "LR": + adj_lines = f"G0 F{travel_speed} Y{max_y} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" + elif purge_end_loc == "RR": + if prime_tower_loc == "LF": + adj_lines = f"G0 F{travel_speed} X{min_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" + if prime_tower_loc == "RF": + adj_lines = f"G0 F{travel_speed} Y{min_y} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" + if prime_tower_loc == "RR": + adj_lines = "" + if prime_tower_loc == "LR": + adj_lines = f"G0 F{travel_speed} X{min_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" + return adj_lines + + # Determine the quadrant that the prime tower rests in so the adjustments can be calculated + def _prime_tower_quadrant(self, prime_tower_x, prime_tower_y, bed_shape, origin_at_center, machine_width, machine_depth): + if not origin_at_center: + midpoint_x = machine_width / 2 + midpoint_y = machine_depth / 2 + max_x = machine_width - 7 + min_x = 7 + max_y = machine_depth - 7 + min_y = 7 + elif origin_at_center: + midpoint_x = 0 + midpoint_y = 0 + max_x = (machine_width / 2) - 7 + min_x = -abs((machine_width / 2) - 7) + max_y = (machine_depth / 2) - 7 + min_y = -abs((machine_depth / 2) - 7) + if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: + prime_tower_location = "LF" + elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: + prime_tower_location = "RF" + elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: + prime_tower_location = "RR" + elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: + prime_tower_location = "LR" + return prime_tower_location + + # For some multi-extruder printers. Takes into account a 'Move to Prime Tower' if there is one and adds orthononal travel moves to get there. + def _move_to_prime_tower(self, startup_gcode: str, bed_shape: str, origin_at_center: bool, machine_width: int, machine_depth: int, travel_speed: int): + adjustment_lines = "" + curaApp = Application.getInstance().getGlobalContainerStack() + prime_tower_x = curaApp.getProperty("prime_tower_position_x", "value") + prime_tower_y = curaApp.getProperty("prime_tower_position_y", "value") + prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y, bed_shape, origin_at_center, machine_width, machine_depth) + if self._purge_end_loc == None: + self._purge_end_loc = "LF" + if prime_tower_loc != self._purge_end_loc: + startup = startup_gcode.split("\n") + for index, line in enumerate(startup): + if ";LAYER_COUNT:" in line: + try: + if startup[index + 1].startswith("G0"): + prime_move = startup[index + 1] + " ; move to Prime Tower" + adjustment_lines = self._get_adjustment_lines(prime_tower_loc, self._purge_end_loc, bed_shape, origin_at_center, machine_width, machine_depth, travel_speed) + startup[index + 1] = adjustment_lines + prime_move + startup_gcode = "\n".join(startup) + except: + pass + return startup_gcode \ No newline at end of file From 035ae14eb765e8b06aa3eb7ce85817daa3932b7c Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:43:03 -0500 Subject: [PATCH 02/27] Update PurgeLinesAndUnload.py Changed 'Execute' procedure per suggestion. Add 'G10' firmware retraction support to 'Adjust Starting E'. --- .../scripts/PurgeLinesAndUnload.py | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 1e606cf17b..407201f4ef 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -131,17 +131,20 @@ class PurgeLinesAndUnload(Script): def execute(self, data): # Run the selected procedures - if self.getSettingValueByKey("add_purge_lines"): - self._add_purge_lines(data) - if self.getSettingValueByKey("move_to_start"): - self._move_to_start(data) - if self.getSettingValueByKey("adjust_starting_e"): - self._adjust_starting_e(data) - if self.getSettingValueByKey("enable_unload"): - self._unload_filament(data) + # Mapping settings to corresponding methods + procedures = { + "add_purge_lines": self._add_purge_lines, + "move_to_start": self._move_to_start, + "adjust_starting_e": self._adjust_starting_e, + "enable_unload": self._unload_filament + } + # Run selected procedures + for setting, method in procedures.items(): + if self.getSettingValueByKey(setting): + method(data) # Format the startup and ending gcodes data[1] = self._format_string(data[1]) - data[len(data) - 1] = self._format_string(data[len(data) - 1]) + data[-1] = self._format_string(data[-1]) return data # Add Purge Lines to the user defined position on the build plate @@ -606,9 +609,13 @@ class PurgeLinesAndUnload(Script): adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") lines = data[1].split("\n") lines.reverse() + if curaApp.getProperty("machine_firmware_retract", "value"): + search_pattern = "G10" + else: + search_pattern = "G1 F(\d*) E-(\d.*)" for index, line in enumerate(lines): - if re.search("G1 F(\d*) E-(\d.*)", line) is not None: - lines[index] = re.sub("G1 F(\d*) E-(\d.*)", f"G92 E{adjust_amt}", line) + if re.search(search_pattern, line): + lines[index] = re.sub(search_pattern, f"G92 E{adjust_amt}", line) lines.reverse() data[1] = "\n".join(lines) break From 60b64d70beb37d3c29afdf0a5012a9d9f30346ab Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 21 Dec 2024 09:21:44 -0500 Subject: [PATCH 03/27] Update PurgeLinesAndUnload.py Added a "quick purge" option before the actual unload to insure the filament is free to pull back. Made adjustments for "Machine Disallowed Areas". Added some comments. Re-ordered some of the code. --- .../scripts/PurgeLinesAndUnload.py | 721 ++++++++++-------- 1 file changed, 403 insertions(+), 318 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 407201f4ef..ac848f706b 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -1,18 +1,19 @@ # August 2024 - GregValiant (Greg Foresi) # # NOTE: You may have purge lines in your startup, or you may use this script, you should not do both. The script will attempt to comment out existing StartUp purge lines. -# 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface. Any Purge lines currently in the StartUp should be removed before using this script. +# 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface. +# This script will attempt to recognize and comment out purge lines in the StartUp Gcode but they should be removed if using this script. # The setting 'Purge Line Length' is only avaialble for rectangular beds because I was too lazy to calculate the 45° arcs. # 'Move to Start' takes an orthogonal path around the periphery before moving in to the print start location. It eliminates strings across the print area. # 'Adjust Starting E' is a correction in the E location before the skirt/brim starts. The user can make an adjustment so that the skirt / brim / raft starts where it should. # 'Unload' adds code to the Ending Gcode that will unload the filament from the machine. The unlaod distance is broken into chunks to avoid overly long E distances. # Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface. +# Added ability to take 'disallowed areas' into account. from ..Script import Script from UM.Application import Application from UM.Message import Message import re -import os from UM.Logger import Logger class PurgeLinesAndUnload(Script): @@ -20,19 +21,21 @@ class PurgeLinesAndUnload(Script): def initialize(self) -> None: super().initialize() # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. - curaApp = Application.getInstance().getGlobalContainerStack() - startup_gcode = curaApp.getProperty("machine_start_gcode", "value") + self.curaApp = Application.getInstance().getGlobalContainerStack() + startup_gcode = self.curaApp.getProperty("machine_start_gcode", "value") start_lines = startup_gcode.splitlines() for line in start_lines: - if line.startswith("G1") and " E" in line and (" X" in line or " Y" in line): + if "G1" in line and " E" in line and (" X" in line or " Y" in line): Message(title = "[Purge Lines and Unload]", text = "It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() break - self._instance.setProperty("is_rectangular", "value", True if curaApp.getProperty("machine_shape", "value") == "rectangular" else False) - self._extruder = curaApp.extruderList + # 'is rectangular' is used to disable half-length purge lines for elliptic beds. + self._instance.setProperty("is_rectangular", "value", True if self.curaApp.getProperty("machine_shape", "value") == "rectangular" else False) + self._instance.setProperty("move_to_prime_tower", "value", True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) + self.extruder = self.curaApp.extruderList #This is set in 'Add Purge Lines' and is used by 'Move to Start' to indicate which corner the nozzle is in after the purge lines - self._purge_end_loc = None + self.start_location = "LF" # Set the default E adjustment - self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self._extruder[0].getProperty("retraction_amount", "value")), 1))) + self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) def getSettingDataString(self): return """{ @@ -118,6 +121,22 @@ class PurgeLinesAndUnload(Script): "unit": "mm ", "enabled": "enable_unload" }, + "unload_quick_purge": + { + "label": " Quick purge before unload", + "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle.", + "type": "bool", + "default_value": false, + "enabled": "enable_unload" + }, + "move_to_prime_tower": + { + "label": "Hidden setting", + "description": "Hidden setting that enables 'move_to_prime_tower' for multi extruder machines.", + "type": "bool", + "default_value": false, + "enabled": false + }, "is_rectangular": { "label": "Bed is rectangular", @@ -130,10 +149,39 @@ class PurgeLinesAndUnload(Script): }""" def execute(self, data): + # Exit if the Gcode has already been processed. + for num in range(0, len(data)): + layer = data[num].split("\n") + for line in layer: + if ";LAYER:" in line: + break + elif "PurgeLinesAndUnload" in line: + Logger.log("i", "[Add Purge Lines and Unload Filament] has already run on this gcode.") + return data + # This will be True when there are more than 4 'machine_disallowed_areas' + self.show_warning = False + self.disallowed_areas = self.curaApp.getProperty("machine_disallowed_areas", "value") + self.extruder = self.curaApp.extruderList + self.extruder_count = self.curaApp.getProperty("machine_extruder_count", "value") + self.bed_shape = self.curaApp.getProperty("machine_shape", "value") + self.origin_at_center = self.curaApp.getProperty("machine_center_is_zero", "value") + self.machine_width = self.curaApp.getProperty("machine_width", "value") + self.machine_depth = self.curaApp.getProperty("machine_depth", "value") + self.machine_left = 1.0 + self.machine_right = self.machine_width - 1.0 + self.machine_front = 1.0 + self.machine_back = self.machine_depth - 1.0 + # Adjust the usable size of the bed per any 'disallowed areas' + max_print_size = self._get_build_plate_extents() + self.speed_travel = self.extruder[0].getProperty("speed_travel", "value") * 60 + # The start location changes according to which quadrant the nozzle is in at the beginning + self.start_location = self._get_real_start_point(data[1]) + # Run the selected procedures # Mapping settings to corresponding methods procedures = { "add_purge_lines": self._add_purge_lines, + "move_to_prime_tower": self._move_to_prime_tower, "move_to_start": self._move_to_start, "adjust_starting_e": self._adjust_starting_e, "enable_unload": self._unload_filament @@ -145,162 +193,321 @@ class PurgeLinesAndUnload(Script): # Format the startup and ending gcodes data[1] = self._format_string(data[1]) data[-1] = self._format_string(data[-1]) + if self.getSettingValueByKey("add_purge_lines"): + if self.show_warning: + msg_text = "The printer has ( " + str(len(self.disallowed_areas)) + " ) 'disallowed areas'. That can cause the area available for the purge lines to be small.\nOpen the Gcode file for preview in Cura and check the purge line location to insure it is acceptable." + else: + msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." + Message(title = "[Purge Lines and Unload]", text = msg_text).show() return data + def _get_real_start_point(self, first_section: str) -> str: + last_x = 0.0 + last_y = 0.0 + start_quadrant = "LF" + startup = first_section.split("\n") + for line in startup: + if (line.startswith(";") and not line.startswith(";LAYER_COUNT")) or line == "": + continue + if line.startswith("G28"): + last_x = 0 + last_y = 0 + elif line[:3] in ["G0 ", "G1 "]: + if " X" in line: + last_x = self.getValue(line, "X") + if " Y" in line: + last_y = self.getValue(line, "Y") + # Stop at the Layer Count line to exclude a possible move to the prime tower + elif "LAYER_COUNT" in line: + break + if self.bed_shape == "rectangular" and not self.origin_at_center: + midpoint_x = self.machine_width / 2 + midpoint_y = self.machine_depth / 2 + elif self.origin_at_center: + midpoint_x = 0.0 + midpoint_y = 0.0 + if last_x <= midpoint_x and last_y <= midpoint_y: + start_quadrant = "LF" + elif last_x > midpoint_x and last_y < midpoint_y: + start_quadrant = "RF" + elif last_x > midpoint_x and last_y > midpoint_y: + start_quadrant = "RR" + elif last_x < midpoint_x and last_y > midpoint_y: + start_quadrant = "LR" + return start_quadrant + + # For some multi-extruder printers. Takes into account a 'Move to Prime Tower' if there is one and adds orthogonal travel moves to get there. + # 'Move to Prime Tower' does not require that the prime tower is enabled, only that 'machine_extruder_start_position_?' is in the definition file. + def _move_to_prime_tower(self, first_section: str) -> str: + if self.extruder_count == 1: + return first_section + adjustment_lines = "" + move_to_prime_present = False + prime_tower_x = self.curaApp.getProperty("prime_tower_position_x", "value") + prime_tower_y = self.curaApp.getProperty("prime_tower_position_y", "value") + prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y) + # Shortstop an error if Start Location comes through as None + if self.start_location == None: + self.start_location = "LF" + if prime_tower_loc != self.start_location: + startup = first_section[1].split("\n") + for index, line in enumerate(startup): + if ";LAYER_COUNT:" in line: + try: + if startup[index + 1].startswith("G0"): + prime_move = startup[index + 1] + " ; Move to Prime Tower" + adjustment_lines = self._get_adjustment_lines(prime_tower_loc) + startup[index + 1] = adjustment_lines + prime_move + "\n" + startup[index] + startup.pop(index) + first_section[1] = "\n".join(startup) + move_to_prime_present = True + except: + pass + # The start_location changes to the prime tower location in case 'Move to Start' is enabled. + if move_to_prime_present: + self.start_location = prime_tower_loc + return first_section + + # Determine the quadrant that the prime tower rests in so the orthogonal moves can be calculated + def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float): + if not self.origin_at_center: + midpoint_x = self.machine_width / 2 + midpoint_y = self.machine_depth / 2 + max_x = self.machine_width - 1 + min_x = 1 + max_y = self.machine_depth - 1 + min_y = 1 + elif self.origin_at_center: + midpoint_x = 0 + midpoint_y = 0 + max_x = (self.machine_width / 2) - 1 + min_x = -abs((self.machine_width / 2) - 1) + max_y = (self.machine_depth / 2) - 1 + min_y = -abs((self.machine_depth / 2) - 1) + if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: + self.prime_tower_location = "LF" + elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: + self.prime_tower_location = "RF" + elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: + self.prime_tower_location = "RR" + elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: + self.prime_tower_location = "LR" + return self.prime_tower_location + + # This puts the 'Move to Prime' tower lines together when they are required + def _get_adjustment_lines(self, prime_tower_loc: str): + adj_lines = ";MESH:NONMESH---------[Move to Prime Tower]" + if self.start_location == "LF": + if prime_tower_loc == "RF": + adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_right} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" + if prime_tower_loc == "RR": + adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_right} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" + if prime_tower_loc == "LR": + adj_lines += f"\nG0 F{self.speed_travel} Y{self.machine_back} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" + elif self.start_location == "RR": + if prime_tower_loc == "LF": + adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_left} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" + if prime_tower_loc == "RF": + adj_lines += f"\nG0 F{self.speed_travel} Y{self.machine_front} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" + if prime_tower_loc == "LR": + adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_left} ; Start move to Prime Tower\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" + return adj_lines + + def _get_build_plate_extents(self) -> float: + # Machine disallwed areas can be ordered at the whim of the definition author and cannot be counted on when parsed + # This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. + if self.bed_shape == "rectangular": + if self.disallowed_areas != []: + if len(self.disallowed_areas) > 4: + self.show_warning = True + mid_x = 0 + mid_y = 0 + left_x = -(self.machine_width / 2) + right_x = (self.machine_width / 2) + front_y = (self.machine_depth / 2) + back_y = -(self.machine_depth / 2) + for rect in self.disallowed_areas: + for corner in rect: + x = corner[0] + if x < mid_x and x > left_x: + left_x = x + if x > mid_x and x < right_x: + right_x = x + y = corner[1] + if y > mid_y and y < front_y: + front_y = y + if y < mid_y and y > back_y: + back_y = y + if self.origin_at_center: + self.machine_left = round(left_x + 1, 2) + self.machine_right = round(right_x - 1, 2) + self.machine_front = round(front_y - 1, 2) + self.machine_back = round(back_y + 1, 2) + else: + self.machine_left = round(left_x + 1 + self.machine_width / 2, 2) + self.machine_right = round(right_x - 1 + self.machine_width / 2, 2) + self.machine_front = round((self.machine_depth / 2) - front_y - 1, 2) + self.machine_back = round((self.machine_depth / 2) - back_y + 1 , 2) + else: + if self.origin_at_center: + self.machine_left = round(-(self.machine_width/2) + 1, 2) + self.machine_right = round((self.machine_width/2) - 1, 2) + self.machine_front = round(-(self.machine_depth/2) + 1, 2) + self.machine_back = round((self.machine_depth/2) - 1, 2) + else: + self.machine_left = 1 + self.machine_right = self.machine_width - 1 + self.machine_front = 1 + self.machine_back = self.machine_depth - 1 + return + # Add Purge Lines to the user defined position on the build plate - def _add_purge_lines(self, data: str): - curaApp = Application.getInstance().getGlobalContainerStack() - retract_dist = self._extruder[0].getProperty("retraction_amount", "value") - retract_enable = self._extruder[0].getProperty("retraction_enable", "value") - retract_speed = self._extruder[0].getProperty("retraction_retract_speed", "value") * 60 - bed_shape = str(curaApp.getProperty("machine_shape", "value")) - origin_at_center = bool(curaApp.getProperty("machine_center_is_zero", "value")) - machine_width = curaApp.getProperty("machine_width", "value") - machine_depth = curaApp.getProperty("machine_depth", "value") - material_diameter = self._extruder[0].getProperty("material_diameter", "value") + def _add_purge_lines(self, data_1: str): + retract_dist = self.extruder[0].getProperty("retraction_amount", "value") + retract_enable = self.extruder[0].getProperty("retraction_enable", "value") + retract_speed = self.extruder[0].getProperty("retraction_retract_speed", "value") * 60 + material_diameter = self.extruder[0].getProperty("material_diameter", "value") mm3_per_mm = (material_diameter / 2)**2 * 3.14159 - init_line_width = self._extruder[0].getProperty("skirt_brim_line_width", "value") + init_line_width = self.extruder[0].getProperty("skirt_brim_line_width", "value") where_at = self.getSettingValueByKey("purge_line_location") - travel_speed = self._extruder[0].getProperty("speed_travel", "value") * 60 - print_speed = round(self._extruder[0].getProperty("speed_print", "value") * 60 * .75) + print_speed = round(self.extruder[0].getProperty("speed_print", "value") * 60 * .75) purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" # Normal cartesian printer with origin at the left front corner - if bed_shape == "rectangular" and not origin_at_center: + if self.bed_shape == "rectangular" and not self.origin_at_center: if where_at == "purge_left": - purge_len = int(machine_depth) - 20 if purge_extrusion_full else int(machine_depth / 2) - y_stop = int(machine_depth - 10) if purge_extrusion_full else int(machine_depth / 2) + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) purge_str = purge_str.replace("Lines", "Lines at MinX") - purge_str += f"G0 F{travel_speed} X0 Y10 ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X0 Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X3 Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X3 Y10 E{round(purge_volume * 2,5)} ; Second line\n" + purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2,5)} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X3 Y20 Z0.3 ; Slide over and down\n" - purge_str += "G0 X3 Y35 ; Wipe\n" - self._purge_end_loc = "LF" + purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + self.start_location = "LF" elif where_at == "purge_right": - purge_len = int(machine_depth) - 20 if purge_extrusion_full else int(machine_depth / 2) - y_stop = 10 if purge_extrusion_full else int(machine_depth / 2) + purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) purge_str = purge_str.replace("Lines", "Lines at MaxX") - purge_str += f"G0 F{travel_speed} X{machine_width} ; Move\nG0 Y{machine_depth - 10} ; Move\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{machine_width} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{machine_width - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{machine_width - 3} Y{machine_depth - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X{machine_width - 3} Y{machine_depth - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{machine_width - 3} Y{machine_depth - 35} ; Wipe\n" - self._purge_end_loc = "RR" + purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + self.start_location = "RR" elif where_at == "purge_bottom": - purge_len = int(machine_width) - 20 if purge_extrusion_full else int(machine_width / 2) - x_stop = int(machine_width - 10) if purge_extrusion_full else int(machine_width / 2) + purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int((self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width/2) purge_str = purge_str.replace("Lines", "Lines at MinY") purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{travel_speed} X10 Y0 ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{x_stop} Y0 E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y3 ; Move over\n" - purge_str += f"G1 F{print_speed} X10 Y3 E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X20 Y3 Z0.3 ; Slide over and down\n" - purge_str += "G0 X35 Y3 ; Wipe\n" - self._purge_end_loc = "LF" + purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + self.start_location = "LF" elif where_at == "purge_top": - purge_len = int(machine_width - 20) if purge_extrusion_full else int(machine_width / 2) - x_stop = 10 if purge_extrusion_full else int(machine_width / 2) + purge_len = int(self.machine_width - 20) if purge_extrusion_full else int((self.machine_right - self.machine_left)/2) + x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width/2) purge_str = purge_str.replace("Lines", "Lines at MaxY") - purge_len = int(machine_width) - 20 purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{travel_speed} Y{machine_depth} ; Ortho Move to back\n" - purge_str += f"G0 X{machine_width - 10} ; Ortho move to start\n" + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" + purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{x_stop} Y{machine_depth} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{machine_depth - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{machine_width - 10} Y{machine_depth - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" - purge_str += f"G0 F{print_speed} X{machine_width - 20} Y{machine_depth - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{machine_width - 35} Y{machine_depth - 3} ; Wipe\n" - self._purge_end_loc = "RR" + purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + self.start_location = "RR" - # Some cartesian printers (BIBO, Weedo, etc.) are Origin at Center - elif bed_shape == "rectangular" and origin_at_center: + # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center + elif self.bed_shape == "rectangular" and self.origin_at_center: if where_at == "purge_left": - purge_len = int(machine_depth - 20) if purge_extrusion_full else int(machine_depth / 2) - y_stop = int((machine_depth / 2) - 10) if purge_extrusion_full else 0 + purge_len = int(self.machine_back - self.machine_front-20) if purge_extrusion_full else abs(int(self.machine_front - 10)) + y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{travel_speed} X-{machine_width / 2} Y-{(machine_depth / 2) - 10} ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X-{machine_width / 2} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X-{(machine_width / 2) - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X-{(machine_width / 2) - 3} Y-{(machine_depth / 2) - 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X-{(machine_width / 2) - 3} Y-{(machine_depth / 2) - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X-{(machine_width / 2) - 3} Y-{(machine_depth / 2) - 35} ; Wipe\n" - self._purge_end_loc = "LF" + purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + self.start_location = "LF" elif where_at == "purge_right": - purge_len = int(machine_depth - 20) if purge_extrusion_full else int(machine_depth / 2) - y_stop = int((machine_depth / 2) - 10) if purge_extrusion_full else 0 + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front)/2) + y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{travel_speed} X{machine_width / 2} Z2 ; Move\nG0 Y{(machine_depth / 2) - 10} Z2 ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{machine_width / 2} Y-{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{(machine_width / 2) - 3} Y-{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{(machine_width / 2) - 3} Y{(machine_depth / 2) - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X{(machine_width / 2) - 3} Y{(machine_depth / 2) - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{travel_speed} X{(machine_width / 2) - 3} Y{(machine_depth / 2) - 35} ; Wipe\n" - self._purge_end_loc = "RR" + purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + self.start_location = "RR" elif where_at == "purge_bottom": - purge_len = int(machine_width - 20) if purge_extrusion_full else int(machine_width / 2) - x_stop = int((machine_width / 2) - 10) if purge_extrusion_full else 0 + purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int((self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{travel_speed} X-{machine_width / 2 - 10} Z2 ; Move\nG0 Y-{machine_depth / 2} Z2 ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{x_stop} Y-{machine_depth / 2} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y-{machine_depth / 2 - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X-{machine_width / 2 - 10} Y-{machine_depth / 2 - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X-{(machine_width / 2) - 20} Y-{(machine_depth / 2) - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X-{(machine_width / 2) - 35} Y-{(machine_depth / 2) - 3} ; Wipe\n" - self._purge_end_loc = "LF" + purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + self.start_location = "LF" elif where_at == "purge_top": - purge_len = int(machine_width - 20) if purge_extrusion_full else int(machine_width / 2) - x_stop = int((machine_width / 2) - 10) if purge_extrusion_full else 0 + purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs(int(self.machine_right - 10)) + x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{travel_speed} Y{machine_depth / 2} Z2; Ortho Move to back\n" - purge_str += f"G0 X{machine_width / 2 - 10} Z2 ; Ortho Move to start\n" + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" + purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X-{x_stop} Y{machine_depth / 2} E{purge_volume} ; First line\n" - purge_str += f"G0 X-{x_stop} Y{machine_depth / 2 - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{machine_width / 2 - 10} Y{machine_depth / 2 - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X{machine_width / 2 - 20} Y{machine_depth / 2 - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X{machine_width / 2 - 35} Y{machine_depth / 2 - 3} ; Wipe\n" - self._purge_end_loc = "RR" + purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + self.start_location = "RR" # Elliptic printers with Origin at Center - elif bed_shape == "elliptic": + elif self.bed_shape == "elliptic": if where_at in ["purge_left","purge_right"]: - radius_1 = round((machine_width / 2) - 1,2) + radius_1 = round((self.machine_width / 2) - 1,2) elif where_at in ["purge_bottom", "purge_top"]: - radius_1 = round((machine_depth / 2) - 1,2) + radius_1 = round((self.machine_depth / 2) - 1,2) purge_len = int(radius_1) * 3.14159 / 4 purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) if where_at == "purge_left": - purge_str += f"G0 F{travel_speed} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X-{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" @@ -310,9 +517,9 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707,2)} ; Wipe\n" - self.purge_end_loc = "LF" + self.start_location = "LF" elif where_at == "purge_right": - purge_str += f"G0 F{travel_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" purge_str += f"G3 F{print_speed} X{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I-{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" @@ -322,9 +529,9 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707,2)}\n" - self.purge_end_loc = "RR" + self.start_location = "RR" elif where_at == "purge_bottom": - purge_str += f"G0 F{travel_speed} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" purge_str += f"G3 F{print_speed} X{round(radius_1 * .707,2)} Y-{round(radius_1 * .707,2)} I{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} ; Move Over\n" @@ -334,9 +541,9 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707,2)}\n" - self.purge_end_loc = "LF" + self.start_location = "LF" elif where_at == "purge_top": - purge_str += f"G0 F{travel_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707,2)} ; Travel\n" + purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707,2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I-{round(radius_1 * .707,2)} J-{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X-{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" @@ -346,19 +553,15 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z5\nG4 S1\n" purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707,2)}\n" - self.purge_end_loc = "RR" + self.start_location = "RR" # Common ending for purge_str purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" - # If there is a move to the prime tower location after purging then it needs to be accounted for - if curaApp.getProperty("machine_extruder_count", "value") > 1: - data[1] = self._move_to_prime_tower(data[1], bed_shape, origin_at_center, machine_width, machine_depth, travel_speed) - - # Comment out any existing purge lines in Data[1] - startup = data[1].split("\n") + # Comment out any existing purge lines in data_1 + startup = data_1[1].split("\n") for index, line in enumerate(startup): - if line.startswith("G1") and " E" in line and (" X" in line or " Y" in line): + if "G1"in line and " E" in line and (" X" in line or " Y" in line): next_line = index try: while not startup[next_line].startswith ("G92 E0"): @@ -366,11 +569,12 @@ class PurgeLinesAndUnload(Script): next_line += 1 except: break - data[1] = "\n".join(startup) + data_1[1] = "\n".join(startup) - # Find the insertion location in data[1] + # Find the insertion location in data_1 purge_str = self._format_string(purge_str) - startup_section = data[1].split("\n") + startup_section = data_1[1].split("\n") + insert_index = len(startup_section)-1 for num in range(len(startup_section) - 1, 0, -1): # In Absolute Extrusion mode - insert above the last G92 E0 line if "G92 E0" in startup_section[num]: @@ -381,21 +585,14 @@ class PurgeLinesAndUnload(Script): insert_index = num break startup_section.insert(insert_index, purge_str) - data[1] = "\n".join(startup_section) - return + data_1[1] = "\n".join(startup_section) + return data_1 # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. def _move_to_start(self, data: str) -> str: - curaApp = Application.getInstance().getGlobalContainerStack() - bed_shape = str(curaApp.getProperty("machine_shape", "value")) - origin_at_center = bool(curaApp.getProperty("machine_center_is_zero", "value")) - machine_width = curaApp.getProperty("machine_width", "value") - machine_depth = curaApp.getProperty("machine_depth", "value") - if curaApp.getProperty("machine_extruder_count", "value") > 1: - self._purge_end_loc = self._get_real_start_point(data[1], bed_shape, origin_at_center, machine_width, machine_depth) - layer = data[2].split("\n") start_x = None start_y = None + layer = data[2].split("\n") for line in layer: if line.startswith("G0") and " X" in line and " Y" in line: start_x = self.getValue(line, "X") @@ -403,15 +600,12 @@ class PurgeLinesAndUnload(Script): break if start_x == None: start_x = 0 if start_y == None: start_y = 0 - if self._purge_end_loc == None: - purge_end_loc = "LF" - else: - purge_end_loc = self._purge_end_loc - travel_speed = round(self._extruder[0].getProperty("speed_travel", "value") * 60) + if self.start_location == None: + self.start_location = "LF" move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" - midpoint_x = machine_width / 2 - midpoint_y = machine_depth / 2 - if not origin_at_center: + midpoint_x = self.machine_width / 2 + midpoint_y = self.machine_depth / 2 + if not self.origin_at_center: if float(start_x) <= float(midpoint_x): goto_str = "Lt" else: @@ -430,136 +624,131 @@ class PurgeLinesAndUnload(Script): else: goto_str += "Bk" - # If purge lines was not selected, and the printer is multi-extruder, there may be a move to the purge tower that needs to be considered - if not self.getSettingValueByKey("add_purge_lines"): - if curaApp.getProperty("machine_extruder_count", "value") > 1: - data[1] = self._move_to_prime_tower(data[1], bed_shape, origin_at_center, machine_width, machine_depth, travel_speed) - # Depending on which quadrant the XY layer start is, move around the periphery before coming in to the start position - if bed_shape == "rectangular" and not origin_at_center: - if purge_end_loc == "LF": + if self.bed_shape == "rectangular" and not self.origin_at_center: + if self.start_location == "LF": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X5 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y5 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X5 Z2; Ortho Move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2; Ortho Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y5 Z2 ; Ortho Move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} X{start_x} ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{start_x} ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "LtBk": - move_str += f"G0 F{travel_speed} X5 ; Ortho Move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Ortho Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y5 Z2 ; Move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X5 ; Move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y5 Z2 ; Move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} X{machine_width - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif purge_end_loc == "RR": + elif self.start_location == "RR": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X5 Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y5 Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X{start_x} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{start_x} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y5 Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "LtBk": - move_str += f"G0 F{travel_speed} X5 Z2 ; Move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X{machine_width - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif bed_shape == "rectangular" and origin_at_center: - if purge_end_loc == "LF": + elif self.bed_shape == "rectangular" and self.origin_at_center: + if self.start_location == "LF": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X-{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "LtBk": - move_str += f"G0 F{travel_speed} X-{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif purge_end_loc == "RR": + elif self.start_location == "RR": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y-{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "LtBk": - move_str += f"G0 F{travel_speed} X-{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X{machine_width / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{travel_speed} Y{machine_depth / 2 - 5} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif bed_shape == "elliptic" and origin_at_center: - radius = machine_width / 2 + elif self.bed_shape == "elliptic" and self.origin_at_center: + radius = self.machine_width / 2 offset_sin = round(2**.5 / 2 * radius, 2) - if purge_end_loc == "LR": + if self.start_location == "LR": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" elif goto_str == "LtBk": move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif purge_end_loc == "RR": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif self.start_location == "RR": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" elif goto_str == "LtBk": - move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif purge_end_loc == "LF": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif self.start_location == "LF": if goto_str == "LtFrt": - move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" elif goto_str == "LtBk": - move_str += f"G0 F{travel_speed} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" elif goto_str == "RtFrt": - move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" elif goto_str == "RtBk": - move_str += f"G0 F{travel_speed} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" move_str += ";---------------------[End of layer start travels]" # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. startup = data[1].split("\n") @@ -577,15 +766,24 @@ class PurgeLinesAndUnload(Script): data[1] = data[1].replace("\n\n", "\n") return - # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into chunks. + # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments. def _unload_filament(self, data: str) -> str: extrude_speed = 3000 + quick_purge_speed = 240 + retract_amount = self.extruder[0].getProperty("retraction_amount", "value") + if retract_amount < 2.0: + quick_purge_amount = retract_amount + 5 + else: + quick_purge_amount = retract_amount * 2 unload_distance = self.getSettingValueByKey("unload_distance") + quick_purge = self.getSettingValueByKey("unload_quick_purge") lines = data[len(data) - 1].split("\n") for index, line in enumerate(lines): # Unload the filament just before the hot end turns off. if line.startswith("M104") and "S0" in line: filament_str = "M83 ; [Unload] Relative extrusion\nM400 ; Complete all moves\n" + if quick_purge: + filament_str += f"G1 F{quick_purge_speed} E{quick_purge_amount} ; Quick Purge before unload\n" if unload_distance > 150: temp_unload = unload_distance while temp_unload > 150: @@ -602,14 +800,13 @@ class PurgeLinesAndUnload(Script): # Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out. def _adjust_starting_e(self, data: str) -> str: - curaApp = Application.getInstance().getGlobalContainerStack() - retract_enabled = self._extruder[0].getProperty("retraction_enable", "value") + retract_enabled = self.extruder[0].getProperty("retraction_enable", "value") if not retract_enabled: return adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") lines = data[1].split("\n") lines.reverse() - if curaApp.getProperty("machine_firmware_retract", "value"): + if self.curaApp.getProperty("machine_firmware_retract", "value"): search_pattern = "G10" else: search_pattern = "G1 F(\d*) E-(\d.*)" @@ -637,116 +834,4 @@ class PurgeLinesAndUnload(Script): elif temp_line.startswith(";") and ";" in temp_line[1:]: temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], ";" + temp_line[1:].split(";")[0] + str(" " * (gap_len - 1 - len(temp_line[1:].split(";")[0]))),1) any_gcode_str = "\n".join(temp_lines) - return any_gcode_str - - # Get the actual layer start point of the print before adding movements - def _get_real_start_point(self, first_section: str, bed_shape: str, origin_at_center: bool, machine_width: int, machine_depth: int): - startup = first_section.split("\n") - last_line = startup[len(startup) - 1] - last_x = None - if last_line[:3] in ("G0 ", "G1 ") and " X" in last_line and " Y" in last_line: - last_x = self.getValue(last_line, "X") - last_y = self.getValue(last_line, "Y") - if last_x == None: - return self._purge_end_loc - else: - if bed_shape == "rectangular" and not origin_at_center: - midpoint_x = machine_width / 2 - midpoint_y = machine_depth / 2 - elif bed_shape in ("rectangular", "elliptic") and origin_at_center: - midpoint_x = 0 - midpoint_y = 0 - if last_x < midpoint_x and last_y < midpoint_y: - return "LF" - if last_x > midpoint_x and last_y < midpoint_y: - return "RF" - if last_x > midpoint_x and last_y > midpoint_y: - return "RR" - if last_x < midpoint_x and last_y > midpoint_y: - return "LR" - - # Multi-extruders may get a move to the prime tower just before layer 0 starts. The adjusted lines move around the periphery instead of across the middle. - def _get_adjustment_lines(self, prime_tower_loc: str, purge_end_loc: str, bed_shape: str, origin_at_center: bool, machine_width: int, machine_depth: int, travel_speed: int): - adj_lines = "" - if not origin_at_center: - midpoint_x = machine_width / 2 - midpoint_y = machine_depth / 2 - max_x = machine_width - 7 - min_x = 7 - max_y = machine_depth - 7 - min_y = 7 - elif origin_at_center: - midpoint_x = 0 - midpoint_y = 0 - max_x = (machine_width / 2) - 7 - min_x = -abs((machine_width / 2) - 7) - max_y = (machine_depth / 2) - 7 - min_y = -abs((machine_depth / 2) - 7) - if purge_end_loc == "LF": - if prime_tower_loc == "LF": - adj_lines = "" - if prime_tower_loc == "RF": - adj_lines = f"G0 F{travel_speed} X{max_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" - if prime_tower_loc == "RR": - adj_lines = f"G0 F{travel_speed} X{max_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" - if prime_tower_loc == "LR": - adj_lines = f"G0 F{travel_speed} Y{max_y} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" - elif purge_end_loc == "RR": - if prime_tower_loc == "LF": - adj_lines = f"G0 F{travel_speed} X{min_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" - if prime_tower_loc == "RF": - adj_lines = f"G0 F{travel_speed} Y{min_y} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" - if prime_tower_loc == "RR": - adj_lines = "" - if prime_tower_loc == "LR": - adj_lines = f"G0 F{travel_speed} X{min_x} ; Move to edge\nG0 F600 Z0 ; nail down the string\nG0 F600 Z2 ; move up\n" - return adj_lines - - # Determine the quadrant that the prime tower rests in so the adjustments can be calculated - def _prime_tower_quadrant(self, prime_tower_x, prime_tower_y, bed_shape, origin_at_center, machine_width, machine_depth): - if not origin_at_center: - midpoint_x = machine_width / 2 - midpoint_y = machine_depth / 2 - max_x = machine_width - 7 - min_x = 7 - max_y = machine_depth - 7 - min_y = 7 - elif origin_at_center: - midpoint_x = 0 - midpoint_y = 0 - max_x = (machine_width / 2) - 7 - min_x = -abs((machine_width / 2) - 7) - max_y = (machine_depth / 2) - 7 - min_y = -abs((machine_depth / 2) - 7) - if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: - prime_tower_location = "LF" - elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: - prime_tower_location = "RF" - elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: - prime_tower_location = "RR" - elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: - prime_tower_location = "LR" - return prime_tower_location - - # For some multi-extruder printers. Takes into account a 'Move to Prime Tower' if there is one and adds orthononal travel moves to get there. - def _move_to_prime_tower(self, startup_gcode: str, bed_shape: str, origin_at_center: bool, machine_width: int, machine_depth: int, travel_speed: int): - adjustment_lines = "" - curaApp = Application.getInstance().getGlobalContainerStack() - prime_tower_x = curaApp.getProperty("prime_tower_position_x", "value") - prime_tower_y = curaApp.getProperty("prime_tower_position_y", "value") - prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y, bed_shape, origin_at_center, machine_width, machine_depth) - if self._purge_end_loc == None: - self._purge_end_loc = "LF" - if prime_tower_loc != self._purge_end_loc: - startup = startup_gcode.split("\n") - for index, line in enumerate(startup): - if ";LAYER_COUNT:" in line: - try: - if startup[index + 1].startswith("G0"): - prime_move = startup[index + 1] + " ; move to Prime Tower" - adjustment_lines = self._get_adjustment_lines(prime_tower_loc, self._purge_end_loc, bed_shape, origin_at_center, machine_width, machine_depth, travel_speed) - startup[index + 1] = adjustment_lines + prime_move - startup_gcode = "\n".join(startup) - except: - pass - return startup_gcode \ No newline at end of file + return any_gcode_str \ No newline at end of file From 3eb6db1a7b6d73700a5336f8df78ab05ef847a50 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 23 Dec 2024 18:14:31 +0100 Subject: [PATCH 04/27] Temp v2 --- .../scripts/PurgeLinesAndUnload_v2.py | 871 ++++++++++++++++++ 1 file changed, 871 insertions(+) create mode 100644 plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py new file mode 100644 index 0000000000..6ff567974a --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py @@ -0,0 +1,871 @@ +# August 2024 - GregValiant (Greg Foresi) +# +# NOTE: You may have purge lines in your startup, or you may use this script, you should not do both. The script will attempt to comment out existing StartUp purge lines. +# 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface. +# This script will attempt to recognize and comment out purge lines in the StartUp Gcode but they should be removed if using this script. +# The setting 'Purge Line Length' is only avaialble for rectangular beds because I was too lazy to calculate the 45° arcs. +# 'Move to Start' takes an orthogonal path around the periphery before moving in to the print start location. It eliminates strings across the print area. +# 'Adjust Starting E' is a correction in the E location before the skirt/brim starts. The user can make an adjustment so that the skirt / brim / raft starts where it should. +# 'Unload' adds code to the Ending Gcode that will unload the filament from the machine. The unlaod distance is broken into chunks to avoid overly long E distances. +# Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface. +# Added ability to take 'disallowed areas' into account. + +from ..Script import Script +from UM.Application import Application +from UM.Message import Message +import re +from UM.Logger import Logger +from enum import Enum + + +class Location(str, Enum): + LEFT_FRONT = "LF" + RIGHT_FRONT = "RF" + LEFT_REAR = "LR" + RIGHT_REAR = "RR" + LEFT = "left" + RIGHT = "right" + TOP = "top" + BOTTOM = "bottom" + + +class PurgeLinesAndUnload(Script): + + def __init__(self): + super().__init__() + self.curaApp = Application.getInstance().getGlobalContainerStack() + self.extruder = self.curaApp.extruderList + self.start_location = None + self.speed_travel = None + # This will be True when there are more than 4 'machine_disallowed_areas' + self.show_warning = False + self.disallowed_areas = self.curaApp.getProperty("machine_disallowed_areas", "value") + self.extruder = self.curaApp.extruderList + self.extruder_count = self.curaApp.getProperty("machine_extruder_count", "value") + self.bed_shape = self.curaApp.getProperty("machine_shape", "value") + self.origin_at_center = self.curaApp.getProperty("machine_center_is_zero", "value") + self.machine_width = self.curaApp.getProperty("machine_width", "value") + self.machine_depth = self.curaApp.getProperty("machine_depth", "value") + self.machine_left = 1.0 + self.machine_right = self.machine_width - 1.0 + self.machine_front = 1.0 + self.machine_back = self.machine_depth - 1.0 + + def initialize(self) -> None: + super().initialize() + # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. + startup_gcode = self.curaApp.getProperty("machine_start_gcode", "value") + start_lines = startup_gcode.splitlines() + for line in start_lines: + if "G1" in line and " E" in line and (" X" in line or " Y" in line): + Message(title="[Purge Lines and Unload]", + text="It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() + break + # 'is_rectangular' is used to disable half-length purge lines for elliptic beds. + self._instance.setProperty("is_rectangular", "value", + True if self.curaApp.getProperty("machine_shape", + "value") == "rectangular" else False) + self._instance.setProperty("move_to_prime_tower", "value", + True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) + # Set the default E adjustment + self._instance.setProperty("adjust_e_loc_to", "value", + -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) + + def getSettingDataString(self): + return """{ + "name": "Purge Lines and Unload Filament", + "key": "PurgeLinesAndUnload", + "metadata": {}, + "version": 2, + "settings": + { + "add_purge_lines": + { + "label": "Add Purge Lines to StartUp", + "description": "The purge lines can be left, right, front or back. If there are purge lines present in the StartUp Gcode remove them or comment them out before using this script. You don't want to double dip.", + "type": "bool", + "default_value": false, + "value": false, + "enabled": true + }, + "purge_line_location": + { + "label": " Purge Line Location", + "description": "What edge of the build plate should have the purge lines. If the printer is 'Elliptical' then it is assumed to be an 'Origin At Center' printer and the purge lines are 90° arcs.", + "type": "enum", + "options": { + "left": "On left edge (Xmin)", + "right": "On right edge (Xmax)", + "bottom": "On front edge (Ymin)", + "top": "On back edge (Ymax)"}, + "default_value": "left", + "enabled": "add_purge_lines" + }, + "purge_line_length": + { + "label": " Purge Line Length", + "description": "Select 'Full' for the entire Height or Width of the build plate. Select 'Half' for shorter purge lines. NOTE: This has no effect on elliptic beds.", + "type": "enum", + "options": { + "purge_full": "Full", + "purge_half": "Half"}, + "default_value": "purge_full", + "enabled": "add_purge_lines and is_rectangular" + }, + "move_to_start": + { + "label": "Circle around to layer start", + "description": "Depending on where the 'Layer Start X' and 'Layer Start Y' are for the print, the opening travel move can pass across the print area and leave a string there. This option will generate an orthogonal path that moves the nozzle around the edges of the build plate and then comes in to the Start Point. The nozzle will drop and touch the build plate at each stop in order to nail down the string so it doesn't follow in a straight line.", + "type": "bool", + "default_value": false, + "enabled": true + }, + "adjust_starting_e": + { + "label": "Adjust Starting E location", + "description": "If there is a retraction after the purge lines in the Startup Gcode (like the 'Add Purge Lines' script here does) then often the skirt does not start where the nozzle starts. It is because Cura always adds a retraction prior to the print starting which results in a double retraction. Enabling this will allow you to adjust the starting E location and tune it so the skirt/brim/model starts right where it should. To fix a blob enter a positive number. To fix a 'dry start' enter a negative number.", + "type": "bool", + "default_value": false, + "value": false, + "enabled": true + }, + "adjust_e_loc_to": + { + "label": " Starting E location", + "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", + "type": "float", + "unit": "mm ", + "default_value": -6.5, + "enabled": "adjust_starting_e" + }, + "enable_unload": + { + "label": "Unload filament at print end", + "description": "Adds an unload script to the Ending Gcode section. It goes in just ahead of the M104 S0. This scripts always unloads the active extruder. If the unload distance is greater than 150mm it will be broken into chunks to avoid tripping the excessive extrusion warning in some firmware.", + "type": "bool", + "default_value": false, + "enabled": true + }, + "unload_distance": + { + "label": " Unload Distance", + "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", + "type": "int", + "default_value": 440, + "unit": "mm ", + "enabled": "enable_unload" + }, + "unload_quick_purge": + { + "label": " Quick purge before unload", + "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle.", + "type": "bool", + "default_value": false, + "enabled": "enable_unload" + }, + "move_to_prime_tower": + { + "label": "Hidden setting", + "description": "Hidden setting that enables 'move_to_prime_tower' for multi extruder machines.", + "type": "bool", + "default_value": false, + "enabled": false + }, + "is_rectangular": + { + "label": "Bed is rectangular", + "description": "Hidden setting that disables 'purge line length' for elliptical beds.", + "type": "bool", + "default_value": false, + "enabled": false + } + } + }""" + + def execute(self, data): + # Exit if the Gcode has already been processed. + for num in range(0, len(data)): + layer = data[num].split("\n") + for line in layer: + if ";LAYER:" in line: + break + elif "PurgeLinesAndUnload" in line: + Logger.log("i", "[Add Purge Lines and Unload Filament] has already run on this gcode.") + return data + + # Adjust the usable size of the bed per any 'disallowed areas' + self._get_build_plate_extents() + self.speed_travel = self.extruder[0].getProperty("speed_travel", "value") * 60 + # The start location changes according to which quadrant the nozzle is in at the beginning + self.start_location = self._get_real_start_point(data[1]) + + # Mapping settings to corresponding methods + procedures = { + "add_purge_lines": self._add_purge_lines, + "move_to_prime_tower": self._move_to_prime_tower, + "move_to_start": self._move_to_start, + "adjust_starting_e": self._adjust_starting_e, + "enable_unload": self._unload_filament + } + # Run selected procedures + for setting, method in procedures.items(): + if self.getSettingValueByKey(setting): + method(data) + # Format the startup and ending gcodes + data[1] = self._format_string(data[1]) + data[-1] = self._format_string(data[-1]) + if self.getSettingValueByKey("add_purge_lines"): + if self.show_warning: + msg_text = ("The printer has ( " + str(len(self.disallowed_areas)) + + " ) 'disallowed areas'. That can cause the area available for the purge lines to be small.\nOpen the Gcode file for preview in Cura and check the purge line location to insure it is acceptable.") + else: + msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." + Message(title="[Purge Lines and Unload]", text=msg_text).show() + return data + + def _get_real_start_point(self, first_section: str) -> str: + last_x, last_y = 0.0, 0.0 + start_quadrant = Location.LEFT_FRONT + + for line in first_section.split("\n"): + if line.startswith(";") and not line.startswith(";LAYER_COUNT") or not line: + continue + + if line.startswith("G28"): + last_x, last_y = 0.0, 0.0 + elif line[:3] in {"G0 ", "G1 "}: + last_x = self.getValue(line, "X") if " X" in line else last_x + last_y = self.getValue(line, "Y") if " Y" in line else last_y + elif "LAYER_COUNT" in line: + break + + midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( + self.machine_width / 2, self.machine_depth / 2) + + if last_x <= midpoint_x and last_y <= midpoint_y: + start_quadrant = Location.LEFT_FRONT + elif last_x > midpoint_x and last_y <= midpoint_y: + start_quadrant = Location.RIGHT_FRONT + elif last_x > midpoint_x and last_y > midpoint_y: + start_quadrant = Location.RIGHT_REAR + elif last_x <= midpoint_x and last_y > midpoint_y: + start_quadrant = Location.LEFT_REAR + + return start_quadrant + + """ + For some multi-extruder printers. + Takes into account a 'Move to Prime Tower' if there is one and adds orthogonal travel moves to get there. + 'Move to Prime Tower' does not require that the prime tower is enabled, + only that 'machine_extruder_start_position_?' is in the definition file. + """ + + def _move_to_prime_tower(self, first_section: str) -> str: + if self.extruder_count == 1: + return first_section + adjustment_lines = "" + move_to_prime_present = False + prime_tower_x = self.curaApp.getProperty("prime_tower_position_x", "value") + prime_tower_y = self.curaApp.getProperty("prime_tower_position_y", "value") + prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y) + # Shortstop an error if Start Location comes through as None + if self.start_location is None: + self.start_location = Location.LEFT_FRONT + if prime_tower_loc != self.start_location: + startup = first_section[1].split("\n") + for index, line in enumerate(startup): + if ";LAYER_COUNT:" in line: + try: + if startup[index + 1].startswith("G0"): + prime_move = startup[index + 1] + " ; Move to Prime Tower" + adjustment_lines = self._get_adjustment_lines(prime_tower_loc) + startup[index + 1] = adjustment_lines + prime_move + "\n" + startup[index] + startup.pop(index) + first_section[1] = "\n".join(startup) + move_to_prime_present = True + except IndexError: + pass + # The start_location changes to the prime tower location in case 'Move to Start' is enabled. + if move_to_prime_present: + self.start_location = prime_tower_loc + return first_section + + # Determine the quadrant that the prime tower rests in so the orthogonal moves can be calculated + def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float) -> str: + midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( + self.machine_width / 2, self.machine_depth / 2) + + if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: + return Location.LEFT_FRONT + elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: + return Location.RIGHT_FRONT + elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: + return Location.RIGHT_REAR + elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: + return Location.LEFT_REAR + else: + return Location.LEFT_FRONT # return Default in case of no match + + # This puts the 'Move to Prime' tower lines together when they are required + def _get_adjustment_lines(self, prime_tower_loc: str): + adj_lines = ";MESH:NONMESH---------[Move to Prime Tower]" + # Move commands linked to keys (Start location, Prime tower location) + move_commands = { + (Location.LEFT_FRONT, Location.RIGHT_FRONT): f"G0 F{self.speed_travel} X{self.machine_right}", + (Location.LEFT_FRONT, Location.RIGHT_REAR): f"G0 F{self.speed_travel} X{self.machine_right}", + (Location.LEFT_FRONT, Location.LEFT_REAR): f"G0 F{self.speed_travel} Y{self.machine_back}", + (Location.RIGHT_REAR, Location.LEFT_FRONT): f"G0 F{self.speed_travel} X{self.machine_left}", + (Location.RIGHT_REAR, Location.RIGHT_FRONT): f"G0 F{self.speed_travel} Y{self.machine_front}", + (Location.RIGHT_REAR, Location.LEFT_REAR): f"G0 F{self.speed_travel} X{self.machine_left}" + } + + key = (self.start_location, prime_tower_loc) + if key in move_commands: + adj_lines += f"\n{move_commands[key]} ; Start move" + adj_lines += "\nG0 F600 Z0 ; Nail down the string" + adj_lines += "\nG0 F600 Z2 ; Move up" + + return adj_lines + + def _get_build_plate_extents(self): + # Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed + # This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. + if self.bed_shape == "rectangular": + if self.disallowed_areas: + if len(self.disallowed_areas) > 4: + self.show_warning = True + mid_x = 0 + mid_y = 0 + left_x = -(self.machine_width / 2) + right_x = (self.machine_width / 2) + front_y = (self.machine_depth / 2) + back_y = -(self.machine_depth / 2) + for rect in self.disallowed_areas: + for corner in rect: + x = corner[0] + if mid_x > x > left_x: + left_x = x + if mid_x < x < right_x: + right_x = x + y = corner[1] + if mid_y < y < front_y: + front_y = y + if mid_y > y > back_y: + back_y = y + if self.origin_at_center: + self.machine_left = round(left_x + 1, 2) + self.machine_right = round(right_x - 1, 2) + self.machine_front = round(front_y - 1, 2) + self.machine_back = round(back_y + 1, 2) + else: + self.machine_left = round(left_x + 1 + self.machine_width / 2, 2) + self.machine_right = round(right_x - 1 + self.machine_width / 2, 2) + self.machine_front = round((self.machine_depth / 2) - front_y - 1, 2) + self.machine_back = round((self.machine_depth / 2) - back_y + 1, 2) + else: + if self.origin_at_center: + self.machine_left = round(-(self.machine_width / 2) + 1, 2) + self.machine_right = round((self.machine_width / 2) - 1, 2) + self.machine_front = round(-(self.machine_depth / 2) + 1, 2) + self.machine_back = round((self.machine_depth / 2) - 1, 2) + else: + self.machine_left = 1 + self.machine_right = self.machine_width - 1 + self.machine_front = 1 + self.machine_back = self.machine_depth - 1 + return + + # Add Purge Lines to the user defined position on the build plate + def _add_purge_lines(self, data_1: str): + retract_dist = self.extruder[0].getProperty("retraction_amount", "value") + retract_enable = self.extruder[0].getProperty("retraction_enable", "value") + retract_speed = self.extruder[0].getProperty("retraction_retract_speed", "value") * 60 + material_diameter = self.extruder[0].getProperty("material_diameter", "value") + mm3_per_mm = (material_diameter / 2) ** 2 * 3.14159 + init_line_width = self.extruder[0].getProperty("skirt_brim_line_width", "value") + where_at = self.getSettingValueByKey("purge_line_location") + print_speed = round(self.extruder[0].getProperty("speed_print", "value") * 60 * .75) + purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False + purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" + + # Normal cartesian printer with origin at the left front corner + if self.bed_shape == "rectangular" and not self.origin_at_center: + if where_at == Location.LEFT: + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( + (self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str = purge_str.replace("Lines", "Lines at MinX") + purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + self.start_location = Location.LEFT_FRONT + elif where_at == Location.RIGHT: + purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( + (self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str = purge_str.replace("Lines", "Lines at MaxX") + purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + self.start_location = Location.RIGHT_REAR + elif where_at == Location.BOTTOM: + purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int( + (self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) + purge_str = purge_str.replace("Lines", "Lines at MinY") + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + self.start_location = Location.LEFT_FRONT + elif where_at == Location.TOP: + purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( + (self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) + purge_str = purge_str.replace("Lines", "Lines at MaxY") + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" + purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + self.start_location = Location.RIGHT_REAR + + # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center + elif self.bed_shape == "rectangular" and self.origin_at_center: + if where_at == Location.LEFT: + purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs( + int(self.machine_front - 10)) + y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + self.start_location = Location.LEFT_FRONT + elif where_at == Location.RIGHT: + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( + (self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + self.start_location = Location.RIGHT_REAR + elif where_at == Location.BOTTOM: + purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( + (self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + self.start_location = Location.LEFT_FRONT + elif where_at == Location.TOP: + purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( + int(self.machine_right - 10)) + x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" + purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + self.start_location = Location.RIGHT_REAR + + # Elliptic printers with Origin at Center + elif self.bed_shape == "elliptic": + if where_at in [Location.LEFT, Location.RIGHT]: + radius_1 = round((self.machine_width / 2) - 1, 2) + elif where_at in [Location.BOTTOM, Location.TOP]: + radius_1 = round((self.machine_depth / 2) - 1, 2) + purge_len = int(radius_1) * 3.14159 / 4 + purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + if where_at == Location.LEFT: + purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + self.start_location = Location.LEFT_FRONT + elif where_at == Location.RIGHT: + purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)}\n" + self.start_location = Location.RIGHT_REAR + elif where_at == Location.BOTTOM: + purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)}\n" + self.start_location = Location.LEFT_FRONT + elif where_at == Location.TOP: + purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5\nG4 S1\n" + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)}\n" + self.start_location = Location.RIGHT_REAR + + # Common ending for purge_str + purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" + + # Comment out any existing purge lines in data_1 + startup = data_1[1].split("\n") + for index, line in enumerate(startup): + if "G1" in line and " E" in line and (" X" in line or " Y" in line): + next_line = index + try: + while not startup[next_line].startswith("G92 E0"): + startup[next_line] = ";" + startup[next_line] + next_line += 1 + except IndexError: + break + data_1[1] = "\n".join(startup) + + # Find the insertion location in data_1 + purge_str = self._format_string(purge_str) + startup_section = data_1[1].split("\n") + insert_index = len(startup_section) - 1 + for num in range(len(startup_section) - 1, 0, -1): + # In Absolute Extrusion mode - insert above the last G92 E0 line + if "G92 E0" in startup_section[num]: + insert_index = num + break + # In Relative Extrusion mode - insert above the M83 line + elif "M83" in startup_section[num]: + insert_index = num + break + startup_section.insert(insert_index, purge_str) + data_1[1] = "\n".join(startup_section) + return data_1 + + # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. + def _move_to_start(self, data: str) -> str: + start_x = None + start_y = None + layer = data[2].split("\n") + for line in layer: + if line.startswith("G0") and " X" in line and " Y" in line: + start_x = self.getValue(line, "X") + start_y = self.getValue(line, "Y") + break + start_x = start_x or 0 + start_y = start_y or 0 + if self.start_location is None: + self.start_location = Location.LEFT_FRONT + move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" + midpoint_x = self.machine_width / 2 + midpoint_y = self.machine_depth / 2 + if not self.origin_at_center: + if float(start_x) <= float(midpoint_x): + goto_str = "Lt" + else: + goto_str = "Rt" + if float(start_y) <= float(midpoint_y): + goto_str += "Frt" + else: + goto_str += "Bk" + else: + if float(start_x) <= 0: + goto_str = "Lt" + else: + goto_str = "Rt" + if float(start_y) <= 0: + goto_str += "Frt" + else: + goto_str += "Bk" + + # Depending on which quadrant the XY layer start is, move around the periphery before coming in to the start position + if self.bed_shape == "rectangular" and not self.origin_at_center: + if self.start_location == Location.LEFT_FRONT: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2; Ortho Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} X{start_x} ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Ortho Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif self.start_location == Location.RIGHT_REAR: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{start_x} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + + elif self.bed_shape == "rectangular" and self.origin_at_center: + if self.start_location == Location.LEFT_FRONT: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif self.start_location == Location.RIGHT_REAR: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + + elif self.bed_shape == "elliptic" and self.origin_at_center: + radius = self.machine_width / 2 + offset_sin = round(2 ** .5 / 2 * radius, 2) + if self.start_location == Location.LEFT_REAR: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif self.start_location == Location.RIGHT_REAR: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif self.start_location == Location.LEFT_FRONT: + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + move_str += ";---------------------[End of layer start travels]" + # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. + startup = data[1].split("\n") + for index, line in enumerate(startup): + if "LAYER_COUNT" in line: + lay_count = startup.pop(index) + "\n" + break + move_str = self._format_string(move_str) + if move_str.startswith("\n"): + move_str = move_str[1:] + startup.append(move_str) + startup.append(lay_count) + data[1] = "\n".join(startup) + # Remove any double-spaced lines + data[1] = data[1].replace("\n\n", "\n") + return + + # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments. + def _unload_filament(self, data: str) -> str: + extrude_speed = 3000 + quick_purge_speed = 240 + retract_amount = self.extruder[0].getProperty("retraction_amount", "value") + quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2 + unload_distance = self.getSettingValueByKey("unload_distance") + quick_purge = self.getSettingValueByKey("unload_quick_purge") + + lines = data[-1].split("\n") + for index, line in enumerate(lines): + # Unload the filament just before the hot end turns off. + if line.startswith("M104") and "S0" in line: + filament_str = ( + "M83 ; [Unload] Relative extrusion\n" + "M400 ; Complete all moves\n" + ) + if quick_purge: + filament_str += f"G1 F{quick_purge_speed} E{quick_purge_amount} ; Quick Purge before unload\n" + + if unload_distance > 150: + filament_str += "".join( + f"G1 F{extrude_speed} E-150 ; Unload some\n" + for _ in range(unload_distance // 150) + ) + remaining_unload = unload_distance % 150 + if remaining_unload > 0: + filament_str += f"G1 F{extrude_speed} E-{remaining_unload} ; Unload the remainder\n" + else: + filament_str += f"G1 F{extrude_speed} E-{unload_distance} ; Unload\n" + + filament_str += ( + "M82 ; Absolute Extrusion\n" + "G92 E0 ; Reset Extruder\n" + ) + lines[index] = filament_str + line + break + + data[-1] = "\n".join(lines) + return data + + # Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out. + def _adjust_starting_e(self, data: str) -> str: + if not self.extruder[0].getProperty("retraction_enable", "value"): + return + adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") + lines = data[1].split("\n") + lines.reverse() + if self.curaApp.getProperty("machine_firmware_retract", "value"): + search_pattern = "G10" + else: + search_pattern = "G1 F(\d*) E-(\d.*)" + for index, line in enumerate(lines): + if re.search(search_pattern, line): + lines[index] = re.sub(search_pattern, f"G92 E{adjust_amt}", line) + lines.reverse() + data[1] = "\n".join(lines) + break + return + + # Format the purge or travel-to-start strings. No reason they shouldn't look nice. + def _format_string(self, any_gcode_str: str): + temp_lines = any_gcode_str.split("\n") + gap_len = 0 + for temp_line in temp_lines: + if ";" in temp_line and not temp_line.startswith(";"): + if gap_len - len(temp_line.split(";")[0]) + 1 < 0: + gap_len = len(temp_line.split(";")[0]) + 1 + if gap_len < 30: gap_len = 30 + for temp_index, temp_line in enumerate(temp_lines): + if ";" in temp_line and not temp_line.startswith(";"): + temp_lines[temp_index] = temp_line.replace(temp_line.split(";")[0], temp_line.split(";")[0] + str( + " " * (gap_len - len(temp_line.split(";")[0]))), 1) + # This formats lines that are commented out but contain additional comments Ex: ;M420 ; leveling mesh + elif temp_line.startswith(";") and ";" in temp_line[1:]: + temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], + ";" + temp_line[1:].split(";")[0] + str(" " * ( + gap_len - 1 - len( + temp_line[1:].split(";")[0]))), 1) + any_gcode_str = "\n".join(temp_lines) + return any_gcode_str From 79ec59578541385c62b4c623415be627104d90a4 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Tue, 24 Dec 2024 19:21:40 +0100 Subject: [PATCH 05/27] v2 update --- .../scripts/PurgeLinesAndUnload_v2.py | 262 ++++++++---------- 1 file changed, 120 insertions(+), 142 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py index 6ff567974a..545f3a2c68 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py @@ -9,6 +9,7 @@ # 'Unload' adds code to the Ending Gcode that will unload the filament from the machine. The unlaod distance is broken into chunks to avoid overly long E distances. # Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface. # Added ability to take 'disallowed areas' into account. +import math from ..Script import Script from UM.Application import Application @@ -377,6 +378,9 @@ class PurgeLinesAndUnload(Script): # Add Purge Lines to the user defined position on the build plate def _add_purge_lines(self, data_1: str): + def calculate_purge_volume(line_width, purge_length, volume_per_mm): + return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5) + retract_dist = self.extruder[0].getProperty("retraction_amount", "value") retract_enable = self.extruder[0].getProperty("retraction_enable", "value") retract_speed = self.extruder[0].getProperty("retraction_retract_speed", "value") * 60 @@ -394,187 +398,249 @@ class PurgeLinesAndUnload(Script): purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_str = purge_str.replace("Lines", "Lines at MinX") + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + self.start_location = Location.LEFT_FRONT elif where_at == Location.RIGHT: purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_str = purge_str.replace("Lines", "Lines at MaxX") + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + self.start_location = Location.RIGHT_REAR elif where_at == Location.BOTTOM: purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_str = purge_str.replace("Lines", "Lines at MinY") - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + self.start_location = Location.LEFT_FRONT elif where_at == Location.TOP: purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_str = purge_str.replace("Lines", "Lines at MaxY") - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.start_location = Location.RIGHT_REAR # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center elif self.bed_shape == "rectangular" and self.origin_at_center: if where_at == Location.LEFT: purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs( int(self.machine_front - 10)) y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + self.start_location = Location.LEFT_FRONT elif where_at == Location.RIGHT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + self.start_location = Location.RIGHT_REAR elif where_at == Location.BOTTOM: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + self.start_location = Location.LEFT_FRONT elif where_at == Location.TOP: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( int(self.machine_right - 10)) x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.start_location = Location.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": if where_at in [Location.LEFT, Location.RIGHT]: radius_1 = round((self.machine_width / 2) - 1, 2) - elif where_at in [Location.BOTTOM, Location.TOP]: + else: # For where_at in [Location.BOTTOM, Location.TOP] radius_1 = round((self.machine_depth / 2) - 1, 2) - purge_len = int(radius_1) * 3.14159 / 4 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + + purge_len = int(radius_1) * math.pi / 4 + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + if where_at == Location.LEFT: + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two arcs purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + # Wipe purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + self.start_location = Location.LEFT_FRONT elif where_at == Location.RIGHT: + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two arcs purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + # Wipe purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)}\n" + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + self.start_location = Location.RIGHT_REAR elif where_at == Location.BOTTOM: + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two arcs purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n" purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" + # Wipe purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)}\n" + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.start_location = Location.LEFT_FRONT elif where_at == Location.TOP: + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two arcs purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z5\nG4 S1\n" + purge_str += "G0 F600 Z5\nG4 S1 ; Wait 1 Second\n" + # Wipe purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)}\n" + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.start_location = Location.RIGHT_REAR # Common ending for purge_str @@ -646,143 +712,55 @@ class PurgeLinesAndUnload(Script): else: goto_str += "Bk" - # Depending on which quadrant the XY layer start is, move around the periphery before coming in to the start position - if self.bed_shape == "rectangular" and not self.origin_at_center: - if self.start_location == Location.LEFT_FRONT: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2; Ortho Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} X{start_x} ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Ortho Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif self.start_location == Location.RIGHT_REAR: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{start_x} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - - elif self.bed_shape == "rectangular" and self.origin_at_center: - if self.start_location == Location.LEFT_FRONT: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif self.start_location == Location.RIGHT_REAR: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - + if self.bed_shape == "rectangular": + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "LtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" + move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" elif self.bed_shape == "elliptic" and self.origin_at_center: radius = self.machine_width / 2 offset_sin = round(2 ** .5 / 2 * radius, 2) - if self.start_location == Location.LEFT_REAR: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": + if goto_str == "LtFrt": + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif goto_str == "LtBk": + if self.start_location == Location.LEFT_REAR: move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif self.start_location == Location.RIGHT_REAR: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": + else: move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif self.start_location == Location.LEFT_FRONT: - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtFrt": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif goto_str == "RtBk": + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" move_str += ";---------------------[End of layer start travels]" # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. startup = data[1].split("\n") - for index, line in enumerate(startup): - if "LAYER_COUNT" in line: - lay_count = startup.pop(index) + "\n" - break move_str = self._format_string(move_str) if move_str.startswith("\n"): move_str = move_str[1:] startup.append(move_str) - startup.append(lay_count) + + for index, line in enumerate(startup): + if "LAYER_COUNT" in line: + lay_count = startup.pop(index) + "\n" + startup.append(lay_count) + break + data[1] = "\n".join(startup) # Remove any double-spaced lines data[1] = data[1].replace("\n\n", "\n") From d80f291dc57a751200b5832156ec89a7be1eba04 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Thu, 26 Dec 2024 12:40:06 +0100 Subject: [PATCH 06/27] More V2 shenanigans - Rename the start location to end purge location to match with what it represents - Added location and position enums - Rewrite the _get_adjustment_lines function as _move_to_location (generic) - Replace goto_str with a position tuple for consistency and error prevention (typo prevention) --- .../scripts/PurgeLinesAndUnload_v2.py | 199 ++++++++++-------- 1 file changed, 106 insertions(+), 93 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py index 545f3a2c68..ed2c2d2c3d 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py @@ -20,14 +20,19 @@ from enum import Enum class Location(str, Enum): - LEFT_FRONT = "LF" - RIGHT_FRONT = "RF" - LEFT_REAR = "LR" - RIGHT_REAR = "RR" LEFT = "left" RIGHT = "right" TOP = "top" BOTTOM = "bottom" + REAR = "rear" + FRONT = "front" + + +class Position(tuple, Enum): + LEFT_FRONT = ("left", "front") + RIGHT_FRONT = ("right", "front") + LEFT_REAR = ("left", "rear") + RIGHT_REAR = ("right", "rear") class PurgeLinesAndUnload(Script): @@ -36,7 +41,7 @@ class PurgeLinesAndUnload(Script): super().__init__() self.curaApp = Application.getInstance().getGlobalContainerStack() self.extruder = self.curaApp.extruderList - self.start_location = None + self.end_purge_location = None self.speed_travel = None # This will be True when there are more than 4 'machine_disallowed_areas' self.show_warning = False @@ -198,7 +203,7 @@ class PurgeLinesAndUnload(Script): self._get_build_plate_extents() self.speed_travel = self.extruder[0].getProperty("speed_travel", "value") * 60 # The start location changes according to which quadrant the nozzle is in at the beginning - self.start_location = self._get_real_start_point(data[1]) + self.end_purge_location = self._get_real_start_point(data[1]) # Mapping settings to corresponding methods procedures = { @@ -224,9 +229,9 @@ class PurgeLinesAndUnload(Script): Message(title="[Purge Lines and Unload]", text=msg_text).show() return data - def _get_real_start_point(self, first_section: str) -> str: + def _get_real_start_point(self, first_section: str) -> tuple: last_x, last_y = 0.0, 0.0 - start_quadrant = Location.LEFT_FRONT + start_quadrant = Position.LEFT_FRONT for line in first_section.split("\n"): if line.startswith(";") and not line.startswith(";LAYER_COUNT") or not line: @@ -244,13 +249,13 @@ class PurgeLinesAndUnload(Script): self.machine_width / 2, self.machine_depth / 2) if last_x <= midpoint_x and last_y <= midpoint_y: - start_quadrant = Location.LEFT_FRONT + start_quadrant = Position.LEFT_FRONT elif last_x > midpoint_x and last_y <= midpoint_y: - start_quadrant = Location.RIGHT_FRONT + start_quadrant = Position.RIGHT_FRONT elif last_x > midpoint_x and last_y > midpoint_y: - start_quadrant = Location.RIGHT_REAR + start_quadrant = Position.RIGHT_REAR elif last_x <= midpoint_x and last_y > midpoint_y: - start_quadrant = Location.LEFT_REAR + start_quadrant = Position.LEFT_REAR return start_quadrant @@ -270,16 +275,16 @@ class PurgeLinesAndUnload(Script): prime_tower_y = self.curaApp.getProperty("prime_tower_position_y", "value") prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y) # Shortstop an error if Start Location comes through as None - if self.start_location is None: - self.start_location = Location.LEFT_FRONT - if prime_tower_loc != self.start_location: + if self.end_purge_location is None: + self.end_purge_location = Position.LEFT_FRONT + if prime_tower_loc != self.end_purge_location: startup = first_section[1].split("\n") for index, line in enumerate(startup): if ";LAYER_COUNT:" in line: try: if startup[index + 1].startswith("G0"): prime_move = startup[index + 1] + " ; Move to Prime Tower" - adjustment_lines = self._get_adjustment_lines(prime_tower_loc) + adjustment_lines = self._move_to_location("Prime Tower", prime_tower_loc) startup[index + 1] = adjustment_lines + prime_move + "\n" + startup[index] startup.pop(index) first_section[1] = "\n".join(startup) @@ -288,45 +293,70 @@ class PurgeLinesAndUnload(Script): pass # The start_location changes to the prime tower location in case 'Move to Start' is enabled. if move_to_prime_present: - self.start_location = prime_tower_loc + self.end_purge_location = prime_tower_loc return first_section # Determine the quadrant that the prime tower rests in so the orthogonal moves can be calculated - def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float) -> str: + def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float) -> tuple: midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( self.machine_width / 2, self.machine_depth / 2) if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: - return Location.LEFT_FRONT + return Position.LEFT_FRONT elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: - return Location.RIGHT_FRONT + return Position.RIGHT_FRONT elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: - return Location.RIGHT_REAR + return Position.RIGHT_REAR elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: - return Location.LEFT_REAR + return Position.LEFT_REAR else: - return Location.LEFT_FRONT # return Default in case of no match + return Position.LEFT_FRONT # return Default in case of no match - # This puts the 'Move to Prime' tower lines together when they are required - def _get_adjustment_lines(self, prime_tower_loc: str): - adj_lines = ";MESH:NONMESH---------[Move to Prime Tower]" - # Move commands linked to keys (Start location, Prime tower location) - move_commands = { - (Location.LEFT_FRONT, Location.RIGHT_FRONT): f"G0 F{self.speed_travel} X{self.machine_right}", - (Location.LEFT_FRONT, Location.RIGHT_REAR): f"G0 F{self.speed_travel} X{self.machine_right}", - (Location.LEFT_FRONT, Location.LEFT_REAR): f"G0 F{self.speed_travel} Y{self.machine_back}", - (Location.RIGHT_REAR, Location.LEFT_FRONT): f"G0 F{self.speed_travel} X{self.machine_left}", - (Location.RIGHT_REAR, Location.RIGHT_FRONT): f"G0 F{self.speed_travel} Y{self.machine_front}", - (Location.RIGHT_REAR, Location.LEFT_REAR): f"G0 F{self.speed_travel} X{self.machine_left}" - } + def _move_to_location(self, location_name: str, location: tuple) -> str: + """ + Compare the input tuple (B) with the end purge location (A) and describe the move from A to B. - key = (self.start_location, prime_tower_loc) - if key in move_commands: - adj_lines += f"\n{move_commands[key]} ; Start move" - adj_lines += "\nG0 F600 Z0 ; Nail down the string" - adj_lines += "\nG0 F600 Z2 ; Move up" + Parameters: + location_name (str): A descriptive name for the target location. + location (tuple): The target tuple (e.g., ("right", "front")). - return adj_lines + Returns: + str: G-code for the move from A to B or an empty string if no move is required. + """ + # Validate input + if len(self.end_purge_location) != 2 or len(location) != 2: + raise ValueError("Both locations must be tuples of length 2.") + + # Extract components + start_side, start_depth = self.end_purge_location + target_side, target_depth = location + + moves = [f";MESH:NONMESH---------[Move to {location_name}]\n G0 F600 Z2 ; Move up\n"] + + # Helper function to add G-code for moves + def add_move(axis: str, position: float) -> None: + moves.append( + f"G0 F{self.speed_travel} {axis}{position} ; Start move\n" + f"G0 F600 Z0 ; Nail down the string\n" + f"G0 F600 Z2 ; Move up\n" + ) + + # Compare sides + if start_side != target_side: + if target_side == Location.RIGHT: + add_move("X", self.machine_right) + else: + add_move("X", self.machine_left) + + # Compare positions + if start_depth != target_depth: + if target_depth == Location.REAR: + add_move("Y", self.machine_back) + else: + add_move("Y", self.machine_front) + + # Combine moves into a single G-code string or return empty if no movement is needed + return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" def _get_build_plate_extents(self): # Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed @@ -415,7 +445,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - self.start_location = Location.LEFT_FRONT + self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.RIGHT: purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) @@ -437,7 +467,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.end_purge_location = Position.RIGHT_REAR elif where_at == Location.BOTTOM: purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) @@ -459,7 +489,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - self.start_location = Location.LEFT_FRONT + self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.TOP: purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) @@ -482,7 +512,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.end_purge_location = Position.RIGHT_REAR # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center elif self.bed_shape == "rectangular" and self.origin_at_center: if where_at == Location.LEFT: @@ -504,7 +534,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - self.start_location = Location.LEFT_FRONT + self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.RIGHT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) @@ -524,7 +554,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.end_purge_location = Position.RIGHT_REAR elif where_at == Location.BOTTOM: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) @@ -544,7 +574,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - self.start_location = Location.LEFT_FRONT + self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.TOP: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( int(self.machine_right - 10)) @@ -565,12 +595,12 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": if where_at in [Location.LEFT, Location.RIGHT]: radius_1 = round((self.machine_width / 2) - 1, 2) - else: # For where_at in [Location.BOTTOM, Location.TOP] + else: # For where_at in [Location.BOTTOM, Location.TOP] radius_1 = round((self.machine_depth / 2) - 1, 2) purge_len = int(radius_1) * math.pi / 4 @@ -592,7 +622,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - self.start_location = Location.LEFT_FRONT + self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.RIGHT: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" @@ -609,7 +639,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.end_purge_location = Position.RIGHT_REAR elif where_at == Location.BOTTOM: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" @@ -625,7 +655,7 @@ class PurgeLinesAndUnload(Script): # Wipe purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - self.start_location = Location.LEFT_FRONT + self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.TOP: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" @@ -641,7 +671,7 @@ class PurgeLinesAndUnload(Script): # Wipe purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - self.start_location = Location.RIGHT_REAR + self.end_purge_location = Position.RIGHT_REAR # Common ending for purge_str purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" @@ -680,6 +710,7 @@ class PurgeLinesAndUnload(Script): def _move_to_start(self, data: str) -> str: start_x = None start_y = None + move_str = None layer = data[2].split("\n") for line in layer: if line.startswith("G0") and " X" in line and " Y" in line: @@ -688,64 +719,46 @@ class PurgeLinesAndUnload(Script): break start_x = start_x or 0 start_y = start_y or 0 - if self.start_location is None: - self.start_location = Location.LEFT_FRONT - move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" + if self.end_purge_location is None: + self.end_purge_location = Position.LEFT_FRONT midpoint_x = self.machine_width / 2 midpoint_y = self.machine_depth / 2 if not self.origin_at_center: if float(start_x) <= float(midpoint_x): - goto_str = "Lt" + x_target = Location.LEFT else: - goto_str = "Rt" + x_target = Location.RIGHT if float(start_y) <= float(midpoint_y): - goto_str += "Frt" + y_target = Location.FRONT else: - goto_str += "Bk" + y_target = Location.REAR else: if float(start_x) <= 0: - goto_str = "Lt" + x_target = Location.LEFT else: - goto_str = "Rt" + x_target = Location.RIGHT if float(start_y) <= 0: - goto_str += "Frt" + y_target = Location.FRONT else: - goto_str += "Bk" - + y_target = Location.REAR + target_location = (x_target, y_target) if self.bed_shape == "rectangular": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" + move_str = self._move_to_location("Layer Start", target_location) elif self.bed_shape == "elliptic" and self.origin_at_center: + move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" + radius = self.machine_width / 2 offset_sin = round(2 ** .5 / 2 * radius, 2) - if goto_str == "LtFrt": + if target_location == Position.LEFT_FRONT: move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": - if self.start_location == Location.LEFT_REAR: + elif target_location == Position.LEFT_REAR: + if self.end_purge_location == Position.LEFT_REAR: move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" else: move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtFrt": + elif target_location == Position.RIGHT_FRONT: move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": + elif target_location == Position.RIGHT_REAR: move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" move_str += ";---------------------[End of layer start travels]" # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. @@ -843,7 +856,7 @@ class PurgeLinesAndUnload(Script): elif temp_line.startswith(";") and ";" in temp_line[1:]: temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], ";" + temp_line[1:].split(";")[0] + str(" " * ( - gap_len - 1 - len( - temp_line[1:].split(";")[0]))), 1) + gap_len - 1 - len( + temp_line[1:].split(";")[0]))), 1) any_gcode_str = "\n".join(temp_lines) return any_gcode_str From b872610275da313d4a2cfd080664ac3a141267c8 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Thu, 26 Dec 2024 12:42:55 +0100 Subject: [PATCH 07/27] comment fix for v2 --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py index ed2c2d2c3d..268a676f85 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py @@ -355,7 +355,7 @@ class PurgeLinesAndUnload(Script): else: add_move("Y", self.machine_front) - # Combine moves into a single G-code string or return empty if no movement is needed + # Combine moves into a single G-code string or return a comment if no movement is needed return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" def _get_build_plate_extents(self): From e9f1be7b6ba0e13a21502b94fcd654cbbd4b0917 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Thu, 26 Dec 2024 16:41:07 +0100 Subject: [PATCH 08/27] Greg's changes --- .../scripts/PurgeLinesAndUnload_v2.py | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py index 268a676f85..f276f56e71 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py @@ -35,7 +35,7 @@ class Position(tuple, Enum): RIGHT_REAR = ("right", "rear") -class PurgeLinesAndUnload(Script): +class PurgeLinesAndUnload_V2(Script): def __init__(self): super().__init__() @@ -79,8 +79,8 @@ class PurgeLinesAndUnload(Script): def getSettingDataString(self): return """{ - "name": "Purge Lines and Unload Filament", - "key": "PurgeLinesAndUnload", + "name": "Purge Lines and Unload Filament V2", + "key": "PurgeLinesAndUnload_V2", "metadata": {}, "version": 2, "settings": @@ -315,11 +315,9 @@ class PurgeLinesAndUnload(Script): def _move_to_location(self, location_name: str, location: tuple) -> str: """ Compare the input tuple (B) with the end purge location (A) and describe the move from A to B. - Parameters: location_name (str): A descriptive name for the target location. location (tuple): The target tuple (e.g., ("right", "front")). - Returns: str: G-code for the move from A to B or an empty string if no move is required. """ @@ -331,7 +329,7 @@ class PurgeLinesAndUnload(Script): start_side, start_depth = self.end_purge_location target_side, target_depth = location - moves = [f";MESH:NONMESH---------[Move to {location_name}]\n G0 F600 Z2 ; Move up\n"] + moves = [f";MESH:NONMESH---------[Move to {location_name}]\nG0 F600 Z2 ; Move up\n"] # Helper function to add G-code for moves def add_move(axis: str, position: float) -> None: @@ -354,7 +352,8 @@ class PurgeLinesAndUnload(Script): add_move("Y", self.machine_back) else: add_move("Y", self.machine_front) - + if len(moves) <= 1: + moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") # Combine moves into a single G-code string or return a comment if no movement is needed return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" @@ -708,36 +707,36 @@ class PurgeLinesAndUnload(Script): # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. def _move_to_start(self, data: str) -> str: - start_x = None - start_y = None + self.start_x = None + self.start_y = None move_str = None layer = data[2].split("\n") for line in layer: if line.startswith("G0") and " X" in line and " Y" in line: - start_x = self.getValue(line, "X") - start_y = self.getValue(line, "Y") + self.start_x = self.getValue(line, "X") + self.start_y = self.getValue(line, "Y") break - start_x = start_x or 0 - start_y = start_y or 0 + self.start_x = self.start_x or 0 + self.start_y = self.start_y or 0 if self.end_purge_location is None: self.end_purge_location = Position.LEFT_FRONT midpoint_x = self.machine_width / 2 midpoint_y = self.machine_depth / 2 if not self.origin_at_center: - if float(start_x) <= float(midpoint_x): + if float(self.start_x) <= float(midpoint_x): x_target = Location.LEFT else: x_target = Location.RIGHT - if float(start_y) <= float(midpoint_y): + if float(self.start_y) <= float(midpoint_y): y_target = Location.FRONT else: y_target = Location.REAR else: - if float(start_x) <= 0: + if float(self.start_x) <= 0: x_target = Location.LEFT else: x_target = Location.RIGHT - if float(start_y) <= 0: + if float(self.start_y) <= 0: y_target = Location.FRONT else: y_target = Location.REAR @@ -859,4 +858,4 @@ class PurgeLinesAndUnload(Script): gap_len - 1 - len( temp_line[1:].split(";")[0]))), 1) any_gcode_str = "\n".join(temp_lines) - return any_gcode_str + return any_gcode_str \ No newline at end of file From 20847a0b33a9f31da2e1d3a464976f88669437ef Mon Sep 17 00:00:00 2001 From: HellAholic Date: Thu, 26 Dec 2024 16:54:26 +0100 Subject: [PATCH 09/27] Final adjustments to V2 - Add condition to check for self.start_y before appending undefined/non to the gcode - Move self.start_x and self.start_y def to the init() --- .../PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py index f276f56e71..81dc05507a 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py @@ -56,6 +56,8 @@ class PurgeLinesAndUnload_V2(Script): self.machine_right = self.machine_width - 1.0 self.machine_front = 1.0 self.machine_back = self.machine_depth - 1.0 + self.start_x = None + self.start_y = None def initialize(self) -> None: super().initialize() @@ -352,7 +354,7 @@ class PurgeLinesAndUnload_V2(Script): add_move("Y", self.machine_back) else: add_move("Y", self.machine_front) - if len(moves) <= 1: + if len(moves) == 1 and self.start_y: moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") # Combine moves into a single G-code string or return a comment if no movement is needed return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" @@ -707,8 +709,6 @@ class PurgeLinesAndUnload_V2(Script): # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. def _move_to_start(self, data: str) -> str: - self.start_x = None - self.start_y = None move_str = None layer = data[2].split("\n") for line in layer: From f08873fa3528bd9e558d345760f51cb858e404d2 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Thu, 26 Dec 2024 11:19:12 -0500 Subject: [PATCH 10/27] Update PurgeLinesAndUnload.py Updated with the combined changes and ready for review. --- .../scripts/PurgeLinesAndUnload.py | 892 +++++++++--------- 1 file changed, 458 insertions(+), 434 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index ac848f706b..386e518c29 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -1,4 +1,4 @@ -# August 2024 - GregValiant (Greg Foresi) +# August 2024 - Designed by: GregValiant (Greg Foresi). Straightened out by: Hellaholic # # NOTE: You may have purge lines in your startup, or you may use this script, you should not do both. The script will attempt to comment out existing StartUp purge lines. # 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface. @@ -10,32 +10,72 @@ # Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface. # Added ability to take 'disallowed areas' into account. +import math from ..Script import Script from UM.Application import Application from UM.Message import Message import re from UM.Logger import Logger +from enum import Enum + + +class Location(str, Enum): + LEFT = "left" + RIGHT = "right" + TOP = "top" + BOTTOM = "bottom" + REAR = "rear" + FRONT = "front" + + +class Position(tuple, Enum): + LEFT_FRONT = ("left", "front") + RIGHT_FRONT = ("right", "front") + LEFT_REAR = ("left", "rear") + RIGHT_REAR = ("right", "rear") + class PurgeLinesAndUnload(Script): + def __init__(self): + super().__init__() + self.curaApp = Application.getInstance().getGlobalContainerStack() + self.extruder = self.curaApp.extruderList + self.end_purge_location = None + self.speed_travel = None + # This will be True when there are more than 4 'machine_disallowed_areas' + self.show_warning = False + self.disallowed_areas = self.curaApp.getProperty("machine_disallowed_areas", "value") + self.extruder = self.curaApp.extruderList + self.extruder_count = self.curaApp.getProperty("machine_extruder_count", "value") + self.bed_shape = self.curaApp.getProperty("machine_shape", "value") + self.origin_at_center = self.curaApp.getProperty("machine_center_is_zero", "value") + self.machine_width = self.curaApp.getProperty("machine_width", "value") + self.machine_depth = self.curaApp.getProperty("machine_depth", "value") + self.machine_left = 1.0 + self.machine_right = self.machine_width - 1.0 + self.machine_front = 1.0 + self.machine_back = self.machine_depth - 1.0 + def initialize(self) -> None: super().initialize() # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. - self.curaApp = Application.getInstance().getGlobalContainerStack() startup_gcode = self.curaApp.getProperty("machine_start_gcode", "value") start_lines = startup_gcode.splitlines() for line in start_lines: if "G1" in line and " E" in line and (" X" in line or " Y" in line): - Message(title = "[Purge Lines and Unload]", text = "It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() + Message(title="[Purge Lines and Unload]", + text="It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() break - # 'is rectangular' is used to disable half-length purge lines for elliptic beds. - self._instance.setProperty("is_rectangular", "value", True if self.curaApp.getProperty("machine_shape", "value") == "rectangular" else False) - self._instance.setProperty("move_to_prime_tower", "value", True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) - self.extruder = self.curaApp.extruderList - #This is set in 'Add Purge Lines' and is used by 'Move to Start' to indicate which corner the nozzle is in after the purge lines - self.start_location = "LF" + # 'is_rectangular' is used to disable half-length purge lines for elliptic beds. + self._instance.setProperty("is_rectangular", "value", + True if self.curaApp.getProperty("machine_shape", + "value") == "rectangular" else False) + self._instance.setProperty("move_to_prime_tower", "value", + True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) # Set the default E adjustment - self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) + self._instance.setProperty("adjust_e_loc_to", "value", + -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) def getSettingDataString(self): return """{ @@ -60,11 +100,11 @@ class PurgeLinesAndUnload(Script): "description": "What edge of the build plate should have the purge lines. If the printer is 'Elliptical' then it is assumed to be an 'Origin At Center' printer and the purge lines are 90° arcs.", "type": "enum", "options": { - "purge_left": "On left edge (Xmin)", - "purge_right": "On right edge (Xmax)", - "purge_bottom": "On front edge (Ymin)", - "purge_top": "On back edge (Ymax)"}, - "default_value": "purge_left", + "left": "On left edge (Xmin)", + "right": "On right edge (Xmax)", + "bottom": "On front edge (Ymin)", + "top": "On back edge (Ymax)"}, + "default_value": "left", "enabled": "add_purge_lines" }, "purge_line_length": @@ -158,26 +198,13 @@ class PurgeLinesAndUnload(Script): elif "PurgeLinesAndUnload" in line: Logger.log("i", "[Add Purge Lines and Unload Filament] has already run on this gcode.") return data - # This will be True when there are more than 4 'machine_disallowed_areas' - self.show_warning = False - self.disallowed_areas = self.curaApp.getProperty("machine_disallowed_areas", "value") - self.extruder = self.curaApp.extruderList - self.extruder_count = self.curaApp.getProperty("machine_extruder_count", "value") - self.bed_shape = self.curaApp.getProperty("machine_shape", "value") - self.origin_at_center = self.curaApp.getProperty("machine_center_is_zero", "value") - self.machine_width = self.curaApp.getProperty("machine_width", "value") - self.machine_depth = self.curaApp.getProperty("machine_depth", "value") - self.machine_left = 1.0 - self.machine_right = self.machine_width - 1.0 - self.machine_front = 1.0 - self.machine_back = self.machine_depth - 1.0 + # Adjust the usable size of the bed per any 'disallowed areas' - max_print_size = self._get_build_plate_extents() + self._get_build_plate_extents() self.speed_travel = self.extruder[0].getProperty("speed_travel", "value") * 60 # The start location changes according to which quadrant the nozzle is in at the beginning - self.start_location = self._get_real_start_point(data[1]) + self.end_purge_location = self._get_real_start_point(data[1]) - # Run the selected procedures # Mapping settings to corresponding methods procedures = { "add_purge_lines": self._add_purge_lines, @@ -195,49 +222,50 @@ class PurgeLinesAndUnload(Script): data[-1] = self._format_string(data[-1]) if self.getSettingValueByKey("add_purge_lines"): if self.show_warning: - msg_text = "The printer has ( " + str(len(self.disallowed_areas)) + " ) 'disallowed areas'. That can cause the area available for the purge lines to be small.\nOpen the Gcode file for preview in Cura and check the purge line location to insure it is acceptable." + msg_text = ("The printer has ( " + str(len(self.disallowed_areas)) + + " ) 'disallowed areas'. That can cause the area available for the purge lines to be small.\nOpen the Gcode file for preview in Cura and check the purge line location to insure it is acceptable.") else: - msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." - Message(title = "[Purge Lines and Unload]", text = msg_text).show() + msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." + Message(title="[Purge Lines and Unload]", text=msg_text).show() return data - def _get_real_start_point(self, first_section: str) -> str: - last_x = 0.0 - last_y = 0.0 - start_quadrant = "LF" - startup = first_section.split("\n") - for line in startup: - if (line.startswith(";") and not line.startswith(";LAYER_COUNT")) or line == "": + def _get_real_start_point(self, first_section: str) -> tuple: + last_x, last_y = 0.0, 0.0 + start_quadrant = Position.LEFT_FRONT + + for line in first_section.split("\n"): + if line.startswith(";") and not line.startswith(";LAYER_COUNT") or not line: continue + if line.startswith("G28"): - last_x = 0 - last_y = 0 - elif line[:3] in ["G0 ", "G1 "]: - if " X" in line: - last_x = self.getValue(line, "X") - if " Y" in line: - last_y = self.getValue(line, "Y") - # Stop at the Layer Count line to exclude a possible move to the prime tower + last_x, last_y = 0.0, 0.0 + elif line[:3] in {"G0 ", "G1 "}: + last_x = self.getValue(line, "X") if " X" in line else last_x + last_y = self.getValue(line, "Y") if " Y" in line else last_y elif "LAYER_COUNT" in line: break - if self.bed_shape == "rectangular" and not self.origin_at_center: - midpoint_x = self.machine_width / 2 - midpoint_y = self.machine_depth / 2 - elif self.origin_at_center: - midpoint_x = 0.0 - midpoint_y = 0.0 + + midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( + self.machine_width / 2, self.machine_depth / 2) + if last_x <= midpoint_x and last_y <= midpoint_y: - start_quadrant = "LF" - elif last_x > midpoint_x and last_y < midpoint_y: - start_quadrant = "RF" + start_quadrant = Position.LEFT_FRONT + elif last_x > midpoint_x and last_y <= midpoint_y: + start_quadrant = Position.RIGHT_FRONT elif last_x > midpoint_x and last_y > midpoint_y: - start_quadrant = "RR" - elif last_x < midpoint_x and last_y > midpoint_y: - start_quadrant = "LR" + start_quadrant = Position.RIGHT_REAR + elif last_x <= midpoint_x and last_y > midpoint_y: + start_quadrant = Position.LEFT_REAR + return start_quadrant - # For some multi-extruder printers. Takes into account a 'Move to Prime Tower' if there is one and adds orthogonal travel moves to get there. - # 'Move to Prime Tower' does not require that the prime tower is enabled, only that 'machine_extruder_start_position_?' is in the definition file. + """ + For some multi-extruder printers. + Takes into account a 'Move to Prime Tower' if there is one and adds orthogonal travel moves to get there. + 'Move to Prime Tower' does not require that the prime tower is enabled, + only that 'machine_extruder_start_position_?' is in the definition file. + """ + def _move_to_prime_tower(self, first_section: str) -> str: if self.extruder_count == 1: return first_section @@ -247,77 +275,93 @@ class PurgeLinesAndUnload(Script): prime_tower_y = self.curaApp.getProperty("prime_tower_position_y", "value") prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y) # Shortstop an error if Start Location comes through as None - if self.start_location == None: - self.start_location = "LF" - if prime_tower_loc != self.start_location: + if self.end_purge_location is None: + self.end_purge_location = Position.LEFT_FRONT + if prime_tower_loc != self.end_purge_location: startup = first_section[1].split("\n") for index, line in enumerate(startup): if ";LAYER_COUNT:" in line: try: if startup[index + 1].startswith("G0"): prime_move = startup[index + 1] + " ; Move to Prime Tower" - adjustment_lines = self._get_adjustment_lines(prime_tower_loc) + adjustment_lines = self._move_to_location("Prime Tower", prime_tower_loc) startup[index + 1] = adjustment_lines + prime_move + "\n" + startup[index] startup.pop(index) first_section[1] = "\n".join(startup) move_to_prime_present = True - except: + except IndexError: pass # The start_location changes to the prime tower location in case 'Move to Start' is enabled. if move_to_prime_present: - self.start_location = prime_tower_loc + self.end_purge_location = prime_tower_loc return first_section # Determine the quadrant that the prime tower rests in so the orthogonal moves can be calculated - def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float): - if not self.origin_at_center: - midpoint_x = self.machine_width / 2 - midpoint_y = self.machine_depth / 2 - max_x = self.machine_width - 1 - min_x = 1 - max_y = self.machine_depth - 1 - min_y = 1 - elif self.origin_at_center: - midpoint_x = 0 - midpoint_y = 0 - max_x = (self.machine_width / 2) - 1 - min_x = -abs((self.machine_width / 2) - 1) - max_y = (self.machine_depth / 2) - 1 - min_y = -abs((self.machine_depth / 2) - 1) + def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float) -> tuple: + midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( + self.machine_width / 2, self.machine_depth / 2) + if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: - self.prime_tower_location = "LF" + return Position.LEFT_FRONT elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: - self.prime_tower_location = "RF" + return Position.RIGHT_FRONT elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: - self.prime_tower_location = "RR" + return Position.RIGHT_REAR elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: - self.prime_tower_location = "LR" - return self.prime_tower_location + return Position.LEFT_REAR + else: + return Position.LEFT_FRONT # return Default in case of no match - # This puts the 'Move to Prime' tower lines together when they are required - def _get_adjustment_lines(self, prime_tower_loc: str): - adj_lines = ";MESH:NONMESH---------[Move to Prime Tower]" - if self.start_location == "LF": - if prime_tower_loc == "RF": - adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_right} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" - if prime_tower_loc == "RR": - adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_right} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" - if prime_tower_loc == "LR": - adj_lines += f"\nG0 F{self.speed_travel} Y{self.machine_back} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" - elif self.start_location == "RR": - if prime_tower_loc == "LF": - adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_left} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" - if prime_tower_loc == "RF": - adj_lines += f"\nG0 F{self.speed_travel} Y{self.machine_front} ; Start move\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" - if prime_tower_loc == "LR": - adj_lines += f"\nG0 F{self.speed_travel} X{self.machine_left} ; Start move to Prime Tower\nG0 F600 Z0 ; Nail down the string\nG0 F600 Z2 ; Move up\n" - return adj_lines + def _move_to_location(self, location_name: str, location: tuple) -> str: + """ + Compare the input tuple (B) with the end purge location (A) and describe the move from A to B. + Parameters: + location_name (str): A descriptive name for the target location. + location (tuple): The target tuple (e.g., ("right", "front")). + Returns: + str: G-code for the move from A to B or an empty string if no move is required. + """ + # Validate input + if len(self.end_purge_location) != 2 or len(location) != 2: + raise ValueError("Both locations must be tuples of length 2.") - def _get_build_plate_extents(self) -> float: - # Machine disallwed areas can be ordered at the whim of the definition author and cannot be counted on when parsed + # Extract components + start_side, start_depth = self.end_purge_location + target_side, target_depth = location + + moves = [f";MESH:NONMESH---------[Move to {location_name}]\nG0 F600 Z2 ; Move up\n"] + + # Helper function to add G-code for moves + def add_move(axis: str, position: float) -> None: + moves.append( + f"G0 F{self.speed_travel} {axis}{position} ; Start move\n" + f"G0 F600 Z0 ; Nail down the string\n" + f"G0 F600 Z2 ; Move up\n" + ) + + # Compare sides + if start_side != target_side: + if target_side == Location.RIGHT: + add_move("X", self.machine_right) + else: + add_move("X", self.machine_left) + + # Compare positions + if start_depth != target_depth: + if target_depth == Location.REAR: + add_move("Y", self.machine_back) + else: + add_move("Y", self.machine_front) + if len(moves) <= 1: + moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") + # Combine moves into a single G-code string or return a comment if no movement is needed + return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" + + def _get_build_plate_extents(self): + # Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed # This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. if self.bed_shape == "rectangular": - if self.disallowed_areas != []: + if self.disallowed_areas: if len(self.disallowed_areas) > 4: self.show_warning = True mid_x = 0 @@ -329,14 +373,14 @@ class PurgeLinesAndUnload(Script): for rect in self.disallowed_areas: for corner in rect: x = corner[0] - if x < mid_x and x > left_x: + if mid_x > x > left_x: left_x = x - if x > mid_x and x < right_x: + if mid_x < x < right_x: right_x = x y = corner[1] - if y > mid_y and y < front_y: + if mid_y < y < front_y: front_y = y - if y < mid_y and y > back_y: + if mid_y > y > back_y: back_y = y if self.origin_at_center: self.machine_left = round(left_x + 1, 2) @@ -347,27 +391,30 @@ class PurgeLinesAndUnload(Script): self.machine_left = round(left_x + 1 + self.machine_width / 2, 2) self.machine_right = round(right_x - 1 + self.machine_width / 2, 2) self.machine_front = round((self.machine_depth / 2) - front_y - 1, 2) - self.machine_back = round((self.machine_depth / 2) - back_y + 1 , 2) + self.machine_back = round((self.machine_depth / 2) - back_y + 1, 2) else: if self.origin_at_center: - self.machine_left = round(-(self.machine_width/2) + 1, 2) - self.machine_right = round((self.machine_width/2) - 1, 2) - self.machine_front = round(-(self.machine_depth/2) + 1, 2) - self.machine_back = round((self.machine_depth/2) - 1, 2) + self.machine_left = round(-(self.machine_width / 2) + 1, 2) + self.machine_right = round((self.machine_width / 2) - 1, 2) + self.machine_front = round(-(self.machine_depth / 2) + 1, 2) + self.machine_back = round((self.machine_depth / 2) - 1, 2) else: self.machine_left = 1 self.machine_right = self.machine_width - 1 self.machine_front = 1 - self.machine_back = self.machine_depth - 1 + self.machine_back = self.machine_depth - 1 return # Add Purge Lines to the user defined position on the build plate def _add_purge_lines(self, data_1: str): + def calculate_purge_volume(line_width, purge_length, volume_per_mm): + return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5) + retract_dist = self.extruder[0].getProperty("retraction_amount", "value") retract_enable = self.extruder[0].getProperty("retraction_enable", "value") retract_speed = self.extruder[0].getProperty("retraction_retract_speed", "value") * 60 material_diameter = self.extruder[0].getProperty("material_diameter", "value") - mm3_per_mm = (material_diameter / 2)**2 * 3.14159 + mm3_per_mm = (material_diameter / 2) ** 2 * 3.14159 init_line_width = self.extruder[0].getProperty("skirt_brim_line_width", "value") where_at = self.getSettingValueByKey("purge_line_location") print_speed = round(self.extruder[0].getProperty("speed_print", "value") * 60 * .75) @@ -376,184 +423,254 @@ class PurgeLinesAndUnload(Script): # Normal cartesian printer with origin at the left front corner if self.bed_shape == "rectangular" and not self.origin_at_center: - if where_at == "purge_left": - purge_len = int(self.machine_back - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) + if where_at == Location.LEFT: + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( + (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str = purge_str.replace("Lines", "Lines at MinX") - purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2,5)} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - self.start_location = "LF" - elif where_at == "purge_right": - purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) - y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str = purge_str.replace("Lines", "Lines at MaxX") - purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - self.start_location = "RR" - elif where_at == "purge_bottom": - purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int((self.machine_right - self.machine_left) / 2) - x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width/2) - purge_str = purge_str.replace("Lines", "Lines at MinY") - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - self.start_location = "LF" - elif where_at == "purge_top": - purge_len = int(self.machine_width - 20) if purge_extrusion_full else int((self.machine_right - self.machine_left)/2) - x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width/2) - purge_str = purge_str.replace("Lines", "Lines at MaxY") - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" - purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" - purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.start_location = "RR" + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center - elif self.bed_shape == "rectangular" and self.origin_at_center: - if where_at == "purge_left": - purge_len = int(self.machine_back - self.machine_front-20) if purge_extrusion_full else abs(int(self.machine_front - 10)) - y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) + purge_str = purge_str.replace("Lines", "Lines at MinX") + # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + # Retract if enabled purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - self.start_location = "LF" - elif where_at == "purge_right": - purge_len = int(self.machine_back - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front)/2) - y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" + + self.end_purge_location = Position.LEFT_FRONT + elif where_at == Location.RIGHT: + purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( + (self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + + purge_str = purge_str.replace("Lines", "Lines at MaxX") + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - self.start_location = "RR" - elif where_at == "purge_bottom": - purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int((self.machine_right - self.machine_left) / 2) - x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" + purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + + self.end_purge_location = Position.RIGHT_REAR + elif where_at == Location.BOTTOM: + purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int( + (self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + + purge_str = purge_str.replace("Lines", "Lines at MinY") + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - self.start_location = "LF" - elif where_at == "purge_top": - purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs(int(self.machine_right - 10)) - x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" - purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" + purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + + self.end_purge_location = Position.LEFT_FRONT + elif where_at == Location.TOP: + purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( + (self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + + purge_str = purge_str.replace("Lines", "Lines at MaxY") + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" + purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist,5)} ; Retract\n" if retract_enable else "" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" + # Wipe + purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + + self.end_purge_location = Position.RIGHT_REAR + # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center + elif self.bed_shape == "rectangular" and self.origin_at_center: + if where_at == Location.LEFT: + purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs( + int(self.machine_front - 10)) + y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines + purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe + purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + + self.end_purge_location = Position.LEFT_FRONT + elif where_at == Location.RIGHT: + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( + (self.machine_back - self.machine_front) / 2) + y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines + purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe + purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + + self.end_purge_location = Position.RIGHT_REAR + elif where_at == Location.BOTTOM: + purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( + (self.machine_right - self.machine_left) / 2) + x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe + purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + + self.end_purge_location = Position.LEFT_FRONT + elif where_at == Location.TOP: + purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( + int(self.machine_right - 10)) + x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" + purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" + purge_str += f"G0 F600 Z0.3 ; Move down\n" + # Purge two lines + purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" + purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" + # Wipe purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.start_location = "RR" + self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": - if where_at in ["purge_left","purge_right"]: - radius_1 = round((self.machine_width / 2) - 1,2) - elif where_at in ["purge_bottom", "purge_top"]: - radius_1 = round((self.machine_depth / 2) - 1,2) - purge_len = int(radius_1) * 3.14159 / 4 - purge_volume = round((init_line_width * 0.3 * purge_len) * 1.25 / mm3_per_mm, 5) - if where_at == "purge_left": - purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + if where_at in [Location.LEFT, Location.RIGHT]: + radius_1 = round((self.machine_width / 2) - 1, 2) + else: # For where_at in [Location.BOTTOM, Location.TOP] + radius_1 = round((self.machine_depth / 2) - 1, 2) + + purge_len = int(radius_1) * math.pi / 4 + purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + + if where_at == Location.LEFT: + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X-{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" - purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} I{round((radius_1 - 3) * .707,2)} J-{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1,5)} ; Move Over\n" - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + # Purge two arcs + purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" - purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707,2)} ; Wipe\n" - self.start_location = "LF" - elif where_at == "purge_right": - purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + # Wipe + purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + + self.end_purge_location = Position.LEFT_FRONT + elif where_at == Location.RIGHT: + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G3 F{print_speed} X{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I-{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} I-{round((radius_1 - 3) * .707,2)} J-{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1,5)} ; Move Over\n" - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + # Purge two arcs + purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707,2)}\n" - self.start_location = "RR" - elif where_at == "purge_bottom": - purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707,2)} ; Travel\n" + # Wipe + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + + self.end_purge_location = Position.RIGHT_REAR + elif where_at == Location.BOTTOM: + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G3 F{print_speed} X{round(radius_1 * .707,2)} Y-{round(radius_1 * .707,2)} I{round(radius_1 * .707,2)} J{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707,2)} Y-{round((radius_1 - 3) * .707,2)} I-{round((radius_1 - 3) * .707,2)} J{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" + # Purge two arcs + purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707,2)}\n" - self.start_location = "LF" - elif where_at == "purge_top": - purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707,2)} ; Travel\n" + # Wipe + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + self.end_purge_location = Position.LEFT_FRONT + elif where_at == Location.TOP: + # Travel to the purge start + purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" - purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707,2)} Y{round(radius_1 * .707,2)} I-{round(radius_1 * .707,2)} J-{round(radius_1 * .707,2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X-{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707,2)} Y{round((radius_1 - 3) * .707,2)} I{round((radius_1 - 3) * .707,2)} J-{round((radius_1 - 3) * .707,2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25,2)} E{round(purge_volume * 2 + 1,5)} ; Move Over\n" - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist,5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z5\nG4 S1\n" - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15,2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707,2)}\n" - self.start_location = "RR" + # Purge two arcs + purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" + purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" + # Retract if enabled + purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += "G0 F600 Z5\nG4 S1 ; Wait 1 Second\n" + # Wipe + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + self.end_purge_location = Position.RIGHT_REAR # Common ending for purge_str purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" @@ -561,20 +678,20 @@ class PurgeLinesAndUnload(Script): # Comment out any existing purge lines in data_1 startup = data_1[1].split("\n") for index, line in enumerate(startup): - if "G1"in line and " E" in line and (" X" in line or " Y" in line): + if "G1" in line and " E" in line and (" X" in line or " Y" in line): next_line = index try: - while not startup[next_line].startswith ("G92 E0"): + while not startup[next_line].startswith("G92 E0"): startup[next_line] = ";" + startup[next_line] next_line += 1 - except: + except IndexError: break data_1[1] = "\n".join(startup) # Find the insertion location in data_1 purge_str = self._format_string(purge_str) startup_section = data_1[1].split("\n") - insert_index = len(startup_section)-1 + insert_index = len(startup_section) - 1 for num in range(len(startup_section) - 1, 0, -1): # In Absolute Extrusion mode - insert above the last G92 E0 line if "G92 E0" in startup_section[num]: @@ -590,218 +707,121 @@ class PurgeLinesAndUnload(Script): # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. def _move_to_start(self, data: str) -> str: - start_x = None - start_y = None + self.start_x = None + self.start_y = None + move_str = None layer = data[2].split("\n") for line in layer: if line.startswith("G0") and " X" in line and " Y" in line: - start_x = self.getValue(line, "X") - start_y = self.getValue(line, "Y") + self.start_x = self.getValue(line, "X") + self.start_y = self.getValue(line, "Y") break - if start_x == None: start_x = 0 - if start_y == None: start_y = 0 - if self.start_location == None: - self.start_location = "LF" - move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" + self.start_x = self.start_x or 0 + self.start_y = self.start_y or 0 + if self.end_purge_location is None: + self.end_purge_location = Position.LEFT_FRONT midpoint_x = self.machine_width / 2 midpoint_y = self.machine_depth / 2 if not self.origin_at_center: - if float(start_x) <= float(midpoint_x): - goto_str = "Lt" + if float(self.start_x) <= float(midpoint_x): + x_target = Location.LEFT else: - goto_str = "Rt" - if float(start_y) <= float(midpoint_y): - goto_str += "Frt" + x_target = Location.RIGHT + if float(self.start_y) <= float(midpoint_y): + y_target = Location.FRONT else: - goto_str += "Bk" + y_target = Location.REAR else: - if float(start_x) <= 0: - goto_str = "Lt" + if float(self.start_x) <= 0: + x_target = Location.LEFT else: - goto_str = "Rt" - if float(start_y) <= 0: - goto_str += "Frt" + x_target = Location.RIGHT + if float(self.start_y) <= 0: + y_target = Location.FRONT else: - goto_str += "Bk" - - # Depending on which quadrant the XY layer start is, move around the periphery before coming in to the start position - if self.bed_shape == "rectangular" and not self.origin_at_center: - if self.start_location == "LF": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2; Ortho Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} X{start_x} ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Ortho Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif self.start_location == "RR": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{start_x} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_front + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{start_y} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - - elif self.bed_shape == "rectangular" and self.origin_at_center: - if self.start_location == "LF": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_left + 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif self.start_location == "RR": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{self.machine_right - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - move_str += f"G0 F{self.speed_travel} Y{self.machine_back - 5} Z2 ; Ortho move\n" - move_str += f"G0 F600 Z0 ; Nail down the string\nG0 Z2 ; Move up\n" - + y_target = Location.REAR + target_location = (x_target, y_target) + if self.bed_shape == "rectangular": + move_str = self._move_to_location("Layer Start", target_location) elif self.bed_shape == "elliptic" and self.origin_at_center: + move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" + radius = self.machine_width / 2 - offset_sin = round(2**.5 / 2 * radius, 2) - if self.start_location == "LR": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": + offset_sin = round(2 ** .5 / 2 * radius, 2) + if target_location == Position.LEFT_FRONT: + move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" + elif target_location == Position.LEFT_REAR: + if self.end_purge_location == Position.LEFT_REAR: move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif self.start_location == "RR": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": + else: move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif self.start_location == "LF": - if goto_str == "LtFrt": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif goto_str == "LtBk": - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtFrt": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif goto_str == "RtBk": - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" + elif target_location == Position.RIGHT_FRONT: + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" + elif target_location == Position.RIGHT_REAR: + move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" move_str += ";---------------------[End of layer start travels]" # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. startup = data[1].split("\n") - for index, line in enumerate(startup): - if "LAYER_COUNT" in line: - lay_count = startup.pop(index) + "\n" - break move_str = self._format_string(move_str) if move_str.startswith("\n"): move_str = move_str[1:] startup.append(move_str) - startup.append(lay_count) + + for index, line in enumerate(startup): + if "LAYER_COUNT" in line: + lay_count = startup.pop(index) + "\n" + startup.append(lay_count) + break + data[1] = "\n".join(startup) - # Remove any double spaced lines + # Remove any double-spaced lines data[1] = data[1].replace("\n\n", "\n") - return + return data # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments. def _unload_filament(self, data: str) -> str: extrude_speed = 3000 quick_purge_speed = 240 retract_amount = self.extruder[0].getProperty("retraction_amount", "value") - if retract_amount < 2.0: - quick_purge_amount = retract_amount + 5 - else: - quick_purge_amount = retract_amount * 2 + quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2 unload_distance = self.getSettingValueByKey("unload_distance") quick_purge = self.getSettingValueByKey("unload_quick_purge") - lines = data[len(data) - 1].split("\n") + + lines = data[-1].split("\n") for index, line in enumerate(lines): # Unload the filament just before the hot end turns off. if line.startswith("M104") and "S0" in line: - filament_str = "M83 ; [Unload] Relative extrusion\nM400 ; Complete all moves\n" + filament_str = ( + "M83 ; [Unload] Relative extrusion\n" + "M400 ; Complete all moves\n" + ) if quick_purge: filament_str += f"G1 F{quick_purge_speed} E{quick_purge_amount} ; Quick Purge before unload\n" + if unload_distance > 150: - temp_unload = unload_distance - while temp_unload > 150: - filament_str += "G1 F" + str(int(extrude_speed)) + " E-150 ; Unload some\n" - temp_unload -= 150 - if 0 < temp_unload <= 150: - filament_str += "G1 F" + str(int(extrude_speed)) + " E-" + str(temp_unload) + " ; Unload the remainder\nM82 ; Absolute Extrusion\nG92 E0 ; Reset Extruder\n" + filament_str += "".join( + f"G1 F{extrude_speed} E-150 ; Unload some\n" + for _ in range(unload_distance // 150) + ) + remaining_unload = unload_distance % 150 + if remaining_unload > 0: + filament_str += f"G1 F{extrude_speed} E-{remaining_unload} ; Unload the remainder\n" else: - filament_str += "G1 F" + str(int(extrude_speed)) + " E-" + str(unload_distance) + " ; Unload\nM82 ; Absolute Extrusion\nG92 E0 ; Reset Extruder\n" + filament_str += f"G1 F{extrude_speed} E-{unload_distance} ; Unload\n" + + filament_str += ( + "M82 ; Absolute Extrusion\n" + "G92 E0 ; Reset Extruder\n" + ) + lines[index] = filament_str + line break - lines[index] = filament_str + lines[index] - data[len(data) - 1] = "\n".join(lines) - return + + data[-1] = "\n".join(lines) + return data # Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out. def _adjust_starting_e(self, data: str) -> str: - retract_enabled = self.extruder[0].getProperty("retraction_enable", "value") - if not retract_enabled: + if not self.extruder[0].getProperty("retraction_enable", "value"): return adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") lines = data[1].split("\n") @@ -816,7 +836,7 @@ class PurgeLinesAndUnload(Script): lines.reverse() data[1] = "\n".join(lines) break - return + return data # Format the purge or travel-to-start strings. No reason they shouldn't look nice. def _format_string(self, any_gcode_str: str): @@ -829,9 +849,13 @@ class PurgeLinesAndUnload(Script): if gap_len < 30: gap_len = 30 for temp_index, temp_line in enumerate(temp_lines): if ";" in temp_line and not temp_line.startswith(";"): - temp_lines[temp_index] = temp_line.replace(temp_line.split(";")[0], temp_line.split(";")[0] + str(" " * (gap_len - len(temp_line.split(";")[0]))),1) + temp_lines[temp_index] = temp_line.replace(temp_line.split(";")[0], temp_line.split(";")[0] + str( + " " * (gap_len - len(temp_line.split(";")[0]))), 1) # This formats lines that are commented out but contain additional comments Ex: ;M420 ; leveling mesh elif temp_line.startswith(";") and ";" in temp_line[1:]: - temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], ";" + temp_line[1:].split(";")[0] + str(" " * (gap_len - 1 - len(temp_line[1:].split(";")[0]))),1) + temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], + ";" + temp_line[1:].split(";")[0] + str(" " * ( + gap_len - 1 - len( + temp_line[1:].split(";")[0]))), 1) any_gcode_str = "\n".join(temp_lines) return any_gcode_str \ No newline at end of file From 7e4b6a63f77f1a0e0ed8de0506c1e64ca692f9d3 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Thu, 26 Dec 2024 17:35:01 +0100 Subject: [PATCH 11/27] Remove the temp V2 --- .../scripts/PurgeLinesAndUnload_v2.py | 861 ------------------ 1 file changed, 861 deletions(-) delete mode 100644 plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py deleted file mode 100644 index 81dc05507a..0000000000 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload_v2.py +++ /dev/null @@ -1,861 +0,0 @@ -# August 2024 - GregValiant (Greg Foresi) -# -# NOTE: You may have purge lines in your startup, or you may use this script, you should not do both. The script will attempt to comment out existing StartUp purge lines. -# 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface. -# This script will attempt to recognize and comment out purge lines in the StartUp Gcode but they should be removed if using this script. -# The setting 'Purge Line Length' is only avaialble for rectangular beds because I was too lazy to calculate the 45° arcs. -# 'Move to Start' takes an orthogonal path around the periphery before moving in to the print start location. It eliminates strings across the print area. -# 'Adjust Starting E' is a correction in the E location before the skirt/brim starts. The user can make an adjustment so that the skirt / brim / raft starts where it should. -# 'Unload' adds code to the Ending Gcode that will unload the filament from the machine. The unlaod distance is broken into chunks to avoid overly long E distances. -# Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface. -# Added ability to take 'disallowed areas' into account. -import math - -from ..Script import Script -from UM.Application import Application -from UM.Message import Message -import re -from UM.Logger import Logger -from enum import Enum - - -class Location(str, Enum): - LEFT = "left" - RIGHT = "right" - TOP = "top" - BOTTOM = "bottom" - REAR = "rear" - FRONT = "front" - - -class Position(tuple, Enum): - LEFT_FRONT = ("left", "front") - RIGHT_FRONT = ("right", "front") - LEFT_REAR = ("left", "rear") - RIGHT_REAR = ("right", "rear") - - -class PurgeLinesAndUnload_V2(Script): - - def __init__(self): - super().__init__() - self.curaApp = Application.getInstance().getGlobalContainerStack() - self.extruder = self.curaApp.extruderList - self.end_purge_location = None - self.speed_travel = None - # This will be True when there are more than 4 'machine_disallowed_areas' - self.show_warning = False - self.disallowed_areas = self.curaApp.getProperty("machine_disallowed_areas", "value") - self.extruder = self.curaApp.extruderList - self.extruder_count = self.curaApp.getProperty("machine_extruder_count", "value") - self.bed_shape = self.curaApp.getProperty("machine_shape", "value") - self.origin_at_center = self.curaApp.getProperty("machine_center_is_zero", "value") - self.machine_width = self.curaApp.getProperty("machine_width", "value") - self.machine_depth = self.curaApp.getProperty("machine_depth", "value") - self.machine_left = 1.0 - self.machine_right = self.machine_width - 1.0 - self.machine_front = 1.0 - self.machine_back = self.machine_depth - 1.0 - self.start_x = None - self.start_y = None - - def initialize(self) -> None: - super().initialize() - # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. - startup_gcode = self.curaApp.getProperty("machine_start_gcode", "value") - start_lines = startup_gcode.splitlines() - for line in start_lines: - if "G1" in line and " E" in line and (" X" in line or " Y" in line): - Message(title="[Purge Lines and Unload]", - text="It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() - break - # 'is_rectangular' is used to disable half-length purge lines for elliptic beds. - self._instance.setProperty("is_rectangular", "value", - True if self.curaApp.getProperty("machine_shape", - "value") == "rectangular" else False) - self._instance.setProperty("move_to_prime_tower", "value", - True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) - # Set the default E adjustment - self._instance.setProperty("adjust_e_loc_to", "value", - -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) - - def getSettingDataString(self): - return """{ - "name": "Purge Lines and Unload Filament V2", - "key": "PurgeLinesAndUnload_V2", - "metadata": {}, - "version": 2, - "settings": - { - "add_purge_lines": - { - "label": "Add Purge Lines to StartUp", - "description": "The purge lines can be left, right, front or back. If there are purge lines present in the StartUp Gcode remove them or comment them out before using this script. You don't want to double dip.", - "type": "bool", - "default_value": false, - "value": false, - "enabled": true - }, - "purge_line_location": - { - "label": " Purge Line Location", - "description": "What edge of the build plate should have the purge lines. If the printer is 'Elliptical' then it is assumed to be an 'Origin At Center' printer and the purge lines are 90° arcs.", - "type": "enum", - "options": { - "left": "On left edge (Xmin)", - "right": "On right edge (Xmax)", - "bottom": "On front edge (Ymin)", - "top": "On back edge (Ymax)"}, - "default_value": "left", - "enabled": "add_purge_lines" - }, - "purge_line_length": - { - "label": " Purge Line Length", - "description": "Select 'Full' for the entire Height or Width of the build plate. Select 'Half' for shorter purge lines. NOTE: This has no effect on elliptic beds.", - "type": "enum", - "options": { - "purge_full": "Full", - "purge_half": "Half"}, - "default_value": "purge_full", - "enabled": "add_purge_lines and is_rectangular" - }, - "move_to_start": - { - "label": "Circle around to layer start", - "description": "Depending on where the 'Layer Start X' and 'Layer Start Y' are for the print, the opening travel move can pass across the print area and leave a string there. This option will generate an orthogonal path that moves the nozzle around the edges of the build plate and then comes in to the Start Point. The nozzle will drop and touch the build plate at each stop in order to nail down the string so it doesn't follow in a straight line.", - "type": "bool", - "default_value": false, - "enabled": true - }, - "adjust_starting_e": - { - "label": "Adjust Starting E location", - "description": "If there is a retraction after the purge lines in the Startup Gcode (like the 'Add Purge Lines' script here does) then often the skirt does not start where the nozzle starts. It is because Cura always adds a retraction prior to the print starting which results in a double retraction. Enabling this will allow you to adjust the starting E location and tune it so the skirt/brim/model starts right where it should. To fix a blob enter a positive number. To fix a 'dry start' enter a negative number.", - "type": "bool", - "default_value": false, - "value": false, - "enabled": true - }, - "adjust_e_loc_to": - { - "label": " Starting E location", - "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", - "type": "float", - "unit": "mm ", - "default_value": -6.5, - "enabled": "adjust_starting_e" - }, - "enable_unload": - { - "label": "Unload filament at print end", - "description": "Adds an unload script to the Ending Gcode section. It goes in just ahead of the M104 S0. This scripts always unloads the active extruder. If the unload distance is greater than 150mm it will be broken into chunks to avoid tripping the excessive extrusion warning in some firmware.", - "type": "bool", - "default_value": false, - "enabled": true - }, - "unload_distance": - { - "label": " Unload Distance", - "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", - "type": "int", - "default_value": 440, - "unit": "mm ", - "enabled": "enable_unload" - }, - "unload_quick_purge": - { - "label": " Quick purge before unload", - "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle.", - "type": "bool", - "default_value": false, - "enabled": "enable_unload" - }, - "move_to_prime_tower": - { - "label": "Hidden setting", - "description": "Hidden setting that enables 'move_to_prime_tower' for multi extruder machines.", - "type": "bool", - "default_value": false, - "enabled": false - }, - "is_rectangular": - { - "label": "Bed is rectangular", - "description": "Hidden setting that disables 'purge line length' for elliptical beds.", - "type": "bool", - "default_value": false, - "enabled": false - } - } - }""" - - def execute(self, data): - # Exit if the Gcode has already been processed. - for num in range(0, len(data)): - layer = data[num].split("\n") - for line in layer: - if ";LAYER:" in line: - break - elif "PurgeLinesAndUnload" in line: - Logger.log("i", "[Add Purge Lines and Unload Filament] has already run on this gcode.") - return data - - # Adjust the usable size of the bed per any 'disallowed areas' - self._get_build_plate_extents() - self.speed_travel = self.extruder[0].getProperty("speed_travel", "value") * 60 - # The start location changes according to which quadrant the nozzle is in at the beginning - self.end_purge_location = self._get_real_start_point(data[1]) - - # Mapping settings to corresponding methods - procedures = { - "add_purge_lines": self._add_purge_lines, - "move_to_prime_tower": self._move_to_prime_tower, - "move_to_start": self._move_to_start, - "adjust_starting_e": self._adjust_starting_e, - "enable_unload": self._unload_filament - } - # Run selected procedures - for setting, method in procedures.items(): - if self.getSettingValueByKey(setting): - method(data) - # Format the startup and ending gcodes - data[1] = self._format_string(data[1]) - data[-1] = self._format_string(data[-1]) - if self.getSettingValueByKey("add_purge_lines"): - if self.show_warning: - msg_text = ("The printer has ( " + str(len(self.disallowed_areas)) - + " ) 'disallowed areas'. That can cause the area available for the purge lines to be small.\nOpen the Gcode file for preview in Cura and check the purge line location to insure it is acceptable.") - else: - msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." - Message(title="[Purge Lines and Unload]", text=msg_text).show() - return data - - def _get_real_start_point(self, first_section: str) -> tuple: - last_x, last_y = 0.0, 0.0 - start_quadrant = Position.LEFT_FRONT - - for line in first_section.split("\n"): - if line.startswith(";") and not line.startswith(";LAYER_COUNT") or not line: - continue - - if line.startswith("G28"): - last_x, last_y = 0.0, 0.0 - elif line[:3] in {"G0 ", "G1 "}: - last_x = self.getValue(line, "X") if " X" in line else last_x - last_y = self.getValue(line, "Y") if " Y" in line else last_y - elif "LAYER_COUNT" in line: - break - - midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( - self.machine_width / 2, self.machine_depth / 2) - - if last_x <= midpoint_x and last_y <= midpoint_y: - start_quadrant = Position.LEFT_FRONT - elif last_x > midpoint_x and last_y <= midpoint_y: - start_quadrant = Position.RIGHT_FRONT - elif last_x > midpoint_x and last_y > midpoint_y: - start_quadrant = Position.RIGHT_REAR - elif last_x <= midpoint_x and last_y > midpoint_y: - start_quadrant = Position.LEFT_REAR - - return start_quadrant - - """ - For some multi-extruder printers. - Takes into account a 'Move to Prime Tower' if there is one and adds orthogonal travel moves to get there. - 'Move to Prime Tower' does not require that the prime tower is enabled, - only that 'machine_extruder_start_position_?' is in the definition file. - """ - - def _move_to_prime_tower(self, first_section: str) -> str: - if self.extruder_count == 1: - return first_section - adjustment_lines = "" - move_to_prime_present = False - prime_tower_x = self.curaApp.getProperty("prime_tower_position_x", "value") - prime_tower_y = self.curaApp.getProperty("prime_tower_position_y", "value") - prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y) - # Shortstop an error if Start Location comes through as None - if self.end_purge_location is None: - self.end_purge_location = Position.LEFT_FRONT - if prime_tower_loc != self.end_purge_location: - startup = first_section[1].split("\n") - for index, line in enumerate(startup): - if ";LAYER_COUNT:" in line: - try: - if startup[index + 1].startswith("G0"): - prime_move = startup[index + 1] + " ; Move to Prime Tower" - adjustment_lines = self._move_to_location("Prime Tower", prime_tower_loc) - startup[index + 1] = adjustment_lines + prime_move + "\n" + startup[index] - startup.pop(index) - first_section[1] = "\n".join(startup) - move_to_prime_present = True - except IndexError: - pass - # The start_location changes to the prime tower location in case 'Move to Start' is enabled. - if move_to_prime_present: - self.end_purge_location = prime_tower_loc - return first_section - - # Determine the quadrant that the prime tower rests in so the orthogonal moves can be calculated - def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float) -> tuple: - midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else ( - self.machine_width / 2, self.machine_depth / 2) - - if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y: - return Position.LEFT_FRONT - elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y: - return Position.RIGHT_FRONT - elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y: - return Position.RIGHT_REAR - elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y: - return Position.LEFT_REAR - else: - return Position.LEFT_FRONT # return Default in case of no match - - def _move_to_location(self, location_name: str, location: tuple) -> str: - """ - Compare the input tuple (B) with the end purge location (A) and describe the move from A to B. - Parameters: - location_name (str): A descriptive name for the target location. - location (tuple): The target tuple (e.g., ("right", "front")). - Returns: - str: G-code for the move from A to B or an empty string if no move is required. - """ - # Validate input - if len(self.end_purge_location) != 2 or len(location) != 2: - raise ValueError("Both locations must be tuples of length 2.") - - # Extract components - start_side, start_depth = self.end_purge_location - target_side, target_depth = location - - moves = [f";MESH:NONMESH---------[Move to {location_name}]\nG0 F600 Z2 ; Move up\n"] - - # Helper function to add G-code for moves - def add_move(axis: str, position: float) -> None: - moves.append( - f"G0 F{self.speed_travel} {axis}{position} ; Start move\n" - f"G0 F600 Z0 ; Nail down the string\n" - f"G0 F600 Z2 ; Move up\n" - ) - - # Compare sides - if start_side != target_side: - if target_side == Location.RIGHT: - add_move("X", self.machine_right) - else: - add_move("X", self.machine_left) - - # Compare positions - if start_depth != target_depth: - if target_depth == Location.REAR: - add_move("Y", self.machine_back) - else: - add_move("Y", self.machine_front) - if len(moves) == 1 and self.start_y: - moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") - # Combine moves into a single G-code string or return a comment if no movement is needed - return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" - - def _get_build_plate_extents(self): - # Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed - # This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. - if self.bed_shape == "rectangular": - if self.disallowed_areas: - if len(self.disallowed_areas) > 4: - self.show_warning = True - mid_x = 0 - mid_y = 0 - left_x = -(self.machine_width / 2) - right_x = (self.machine_width / 2) - front_y = (self.machine_depth / 2) - back_y = -(self.machine_depth / 2) - for rect in self.disallowed_areas: - for corner in rect: - x = corner[0] - if mid_x > x > left_x: - left_x = x - if mid_x < x < right_x: - right_x = x - y = corner[1] - if mid_y < y < front_y: - front_y = y - if mid_y > y > back_y: - back_y = y - if self.origin_at_center: - self.machine_left = round(left_x + 1, 2) - self.machine_right = round(right_x - 1, 2) - self.machine_front = round(front_y - 1, 2) - self.machine_back = round(back_y + 1, 2) - else: - self.machine_left = round(left_x + 1 + self.machine_width / 2, 2) - self.machine_right = round(right_x - 1 + self.machine_width / 2, 2) - self.machine_front = round((self.machine_depth / 2) - front_y - 1, 2) - self.machine_back = round((self.machine_depth / 2) - back_y + 1, 2) - else: - if self.origin_at_center: - self.machine_left = round(-(self.machine_width / 2) + 1, 2) - self.machine_right = round((self.machine_width / 2) - 1, 2) - self.machine_front = round(-(self.machine_depth / 2) + 1, 2) - self.machine_back = round((self.machine_depth / 2) - 1, 2) - else: - self.machine_left = 1 - self.machine_right = self.machine_width - 1 - self.machine_front = 1 - self.machine_back = self.machine_depth - 1 - return - - # Add Purge Lines to the user defined position on the build plate - def _add_purge_lines(self, data_1: str): - def calculate_purge_volume(line_width, purge_length, volume_per_mm): - return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5) - - retract_dist = self.extruder[0].getProperty("retraction_amount", "value") - retract_enable = self.extruder[0].getProperty("retraction_enable", "value") - retract_speed = self.extruder[0].getProperty("retraction_retract_speed", "value") * 60 - material_diameter = self.extruder[0].getProperty("material_diameter", "value") - mm3_per_mm = (material_diameter / 2) ** 2 * 3.14159 - init_line_width = self.extruder[0].getProperty("skirt_brim_line_width", "value") - where_at = self.getSettingValueByKey("purge_line_location") - print_speed = round(self.extruder[0].getProperty("speed_print", "value") * 60 * .75) - purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False - purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" - - # Normal cartesian printer with origin at the left front corner - if self.bed_shape == "rectangular" and not self.origin_at_center: - if where_at == Location.LEFT: - purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( - (self.machine_back - self.machine_front) / 2) - y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - - purge_str = purge_str.replace("Lines", "Lines at MinX") - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - - self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.RIGHT: - purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( - (self.machine_back - self.machine_front) / 2) - y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - - purge_str = purge_str.replace("Lines", "Lines at MaxX") - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - - self.end_purge_location = Position.RIGHT_REAR - elif where_at == Location.BOTTOM: - purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int( - (self.machine_right - self.machine_left) / 2) - x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - - purge_str = purge_str.replace("Lines", "Lines at MinY") - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - - self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.TOP: - purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( - (self.machine_right - self.machine_left) / 2) - x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - - purge_str = purge_str.replace("Lines", "Lines at MaxY") - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" - purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - - self.end_purge_location = Position.RIGHT_REAR - # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center - elif self.bed_shape == "rectangular" and self.origin_at_center: - if where_at == Location.LEFT: - purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs( - int(self.machine_front - 10)) - y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - - self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.RIGHT: - purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( - (self.machine_back - self.machine_front) / 2) - y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - - self.end_purge_location = Position.RIGHT_REAR - elif where_at == Location.BOTTOM: - purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( - (self.machine_right - self.machine_left) / 2) - x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - - self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.TOP: - purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( - int(self.machine_right - 10)) - x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" - purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - - self.end_purge_location = Position.RIGHT_REAR - # Elliptic printers with Origin at Center - elif self.bed_shape == "elliptic": - if where_at in [Location.LEFT, Location.RIGHT]: - radius_1 = round((self.machine_width / 2) - 1, 2) - else: # For where_at in [Location.BOTTOM, Location.TOP] - radius_1 = round((self.machine_depth / 2) - 1, 2) - - purge_len = int(radius_1) * math.pi / 4 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - - if where_at == Location.LEFT: - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two arcs - purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" - # Wipe - purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - - self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.RIGHT: - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two arcs - purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" - # Wipe - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - - self.end_purge_location = Position.RIGHT_REAR - elif where_at == Location.BOTTOM: - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two arcs - purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" - # Wipe - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.TOP: - # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" - purge_str += f"G0 F600 Z0.3 ; Move down\n" - # Purge two arcs - purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" - purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" - purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" - # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" - purge_str += "G0 F600 Z5\nG4 S1 ; Wait 1 Second\n" - # Wipe - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - self.end_purge_location = Position.RIGHT_REAR - - # Common ending for purge_str - purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" - - # Comment out any existing purge lines in data_1 - startup = data_1[1].split("\n") - for index, line in enumerate(startup): - if "G1" in line and " E" in line and (" X" in line or " Y" in line): - next_line = index - try: - while not startup[next_line].startswith("G92 E0"): - startup[next_line] = ";" + startup[next_line] - next_line += 1 - except IndexError: - break - data_1[1] = "\n".join(startup) - - # Find the insertion location in data_1 - purge_str = self._format_string(purge_str) - startup_section = data_1[1].split("\n") - insert_index = len(startup_section) - 1 - for num in range(len(startup_section) - 1, 0, -1): - # In Absolute Extrusion mode - insert above the last G92 E0 line - if "G92 E0" in startup_section[num]: - insert_index = num - break - # In Relative Extrusion mode - insert above the M83 line - elif "M83" in startup_section[num]: - insert_index = num - break - startup_section.insert(insert_index, purge_str) - data_1[1] = "\n".join(startup_section) - return data_1 - - # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. - def _move_to_start(self, data: str) -> str: - move_str = None - layer = data[2].split("\n") - for line in layer: - if line.startswith("G0") and " X" in line and " Y" in line: - self.start_x = self.getValue(line, "X") - self.start_y = self.getValue(line, "Y") - break - self.start_x = self.start_x or 0 - self.start_y = self.start_y or 0 - if self.end_purge_location is None: - self.end_purge_location = Position.LEFT_FRONT - midpoint_x = self.machine_width / 2 - midpoint_y = self.machine_depth / 2 - if not self.origin_at_center: - if float(self.start_x) <= float(midpoint_x): - x_target = Location.LEFT - else: - x_target = Location.RIGHT - if float(self.start_y) <= float(midpoint_y): - y_target = Location.FRONT - else: - y_target = Location.REAR - else: - if float(self.start_x) <= 0: - x_target = Location.LEFT - else: - x_target = Location.RIGHT - if float(self.start_y) <= 0: - y_target = Location.FRONT - else: - y_target = Location.REAR - target_location = (x_target, y_target) - if self.bed_shape == "rectangular": - move_str = self._move_to_location("Layer Start", target_location) - elif self.bed_shape == "elliptic" and self.origin_at_center: - move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" - - radius = self.machine_width / 2 - offset_sin = round(2 ** .5 / 2 * radius, 2) - if target_location == Position.LEFT_FRONT: - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n" - elif target_location == Position.LEFT_REAR: - if self.end_purge_location == Position.LEFT_REAR: - move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n" - else: - move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - elif target_location == Position.RIGHT_FRONT: - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n" - elif target_location == Position.RIGHT_REAR: - move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n" - move_str += ";---------------------[End of layer start travels]" - # Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end. - startup = data[1].split("\n") - move_str = self._format_string(move_str) - if move_str.startswith("\n"): - move_str = move_str[1:] - startup.append(move_str) - - for index, line in enumerate(startup): - if "LAYER_COUNT" in line: - lay_count = startup.pop(index) + "\n" - startup.append(lay_count) - break - - data[1] = "\n".join(startup) - # Remove any double-spaced lines - data[1] = data[1].replace("\n\n", "\n") - return - - # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments. - def _unload_filament(self, data: str) -> str: - extrude_speed = 3000 - quick_purge_speed = 240 - retract_amount = self.extruder[0].getProperty("retraction_amount", "value") - quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2 - unload_distance = self.getSettingValueByKey("unload_distance") - quick_purge = self.getSettingValueByKey("unload_quick_purge") - - lines = data[-1].split("\n") - for index, line in enumerate(lines): - # Unload the filament just before the hot end turns off. - if line.startswith("M104") and "S0" in line: - filament_str = ( - "M83 ; [Unload] Relative extrusion\n" - "M400 ; Complete all moves\n" - ) - if quick_purge: - filament_str += f"G1 F{quick_purge_speed} E{quick_purge_amount} ; Quick Purge before unload\n" - - if unload_distance > 150: - filament_str += "".join( - f"G1 F{extrude_speed} E-150 ; Unload some\n" - for _ in range(unload_distance // 150) - ) - remaining_unload = unload_distance % 150 - if remaining_unload > 0: - filament_str += f"G1 F{extrude_speed} E-{remaining_unload} ; Unload the remainder\n" - else: - filament_str += f"G1 F{extrude_speed} E-{unload_distance} ; Unload\n" - - filament_str += ( - "M82 ; Absolute Extrusion\n" - "G92 E0 ; Reset Extruder\n" - ) - lines[index] = filament_str + line - break - - data[-1] = "\n".join(lines) - return data - - # Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out. - def _adjust_starting_e(self, data: str) -> str: - if not self.extruder[0].getProperty("retraction_enable", "value"): - return - adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") - lines = data[1].split("\n") - lines.reverse() - if self.curaApp.getProperty("machine_firmware_retract", "value"): - search_pattern = "G10" - else: - search_pattern = "G1 F(\d*) E-(\d.*)" - for index, line in enumerate(lines): - if re.search(search_pattern, line): - lines[index] = re.sub(search_pattern, f"G92 E{adjust_amt}", line) - lines.reverse() - data[1] = "\n".join(lines) - break - return - - # Format the purge or travel-to-start strings. No reason they shouldn't look nice. - def _format_string(self, any_gcode_str: str): - temp_lines = any_gcode_str.split("\n") - gap_len = 0 - for temp_line in temp_lines: - if ";" in temp_line and not temp_line.startswith(";"): - if gap_len - len(temp_line.split(";")[0]) + 1 < 0: - gap_len = len(temp_line.split(";")[0]) + 1 - if gap_len < 30: gap_len = 30 - for temp_index, temp_line in enumerate(temp_lines): - if ";" in temp_line and not temp_line.startswith(";"): - temp_lines[temp_index] = temp_line.replace(temp_line.split(";")[0], temp_line.split(";")[0] + str( - " " * (gap_len - len(temp_line.split(";")[0]))), 1) - # This formats lines that are commented out but contain additional comments Ex: ;M420 ; leveling mesh - elif temp_line.startswith(";") and ";" in temp_line[1:]: - temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0], - ";" + temp_line[1:].split(";")[0] + str(" " * ( - gap_len - 1 - len( - temp_line[1:].split(";")[0]))), 1) - any_gcode_str = "\n".join(temp_lines) - return any_gcode_str \ No newline at end of file From 703028d00d23286b73ca0cf18f624fda6a8810b6 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Thu, 26 Dec 2024 17:44:09 +0100 Subject: [PATCH 12/27] Adjustment bit for self.start_x, self.start_y --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 386e518c29..88cc8a5050 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -56,6 +56,8 @@ class PurgeLinesAndUnload(Script): self.machine_right = self.machine_width - 1.0 self.machine_front = 1.0 self.machine_back = self.machine_depth - 1.0 + self.start_x = None + self.start_y = None def initialize(self) -> None: super().initialize() @@ -352,7 +354,7 @@ class PurgeLinesAndUnload(Script): add_move("Y", self.machine_back) else: add_move("Y", self.machine_front) - if len(moves) <= 1: + if len(moves) == 1 and self.start_y: moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") # Combine moves into a single G-code string or return a comment if no movement is needed return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" From 8c28eecd91123738947e3a344c829db131f4314e Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:54:02 -0500 Subject: [PATCH 13/27] Update PurgeLinesAndUnload.py Added consideration for Disallowed Areas and Tool Offsets. This also includes a lot of variables moved into "self". --- .../scripts/PurgeLinesAndUnload.py | 264 ++++++++++-------- 1 file changed, 141 insertions(+), 123 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 88cc8a5050..151c030f60 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -56,8 +56,6 @@ class PurgeLinesAndUnload(Script): self.machine_right = self.machine_width - 1.0 self.machine_front = 1.0 self.machine_back = self.machine_depth - 1.0 - self.start_x = None - self.start_y = None def initialize(self) -> None: super().initialize() @@ -70,14 +68,10 @@ class PurgeLinesAndUnload(Script): text="It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() break # 'is_rectangular' is used to disable half-length purge lines for elliptic beds. - self._instance.setProperty("is_rectangular", "value", - True if self.curaApp.getProperty("machine_shape", - "value") == "rectangular" else False) - self._instance.setProperty("move_to_prime_tower", "value", - True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) + self._instance.setProperty("is_rectangular", "value", True if self.curaApp.getProperty("machine_shape", "value") == "rectangular" else False) + self._instance.setProperty("move_to_prime_tower", "value", True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) # Set the default E adjustment - self._instance.setProperty("adjust_e_loc_to", "value", - -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) + self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) def getSettingDataString(self): return """{ @@ -163,7 +157,7 @@ class PurgeLinesAndUnload(Script): "unit": "mm ", "enabled": "enable_unload" }, - "unload_quick_purge": + "unload_quick_purge": { "label": " Quick purge before unload", "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle.", @@ -200,10 +194,12 @@ class PurgeLinesAndUnload(Script): elif "PurgeLinesAndUnload" in line: Logger.log("i", "[Add Purge Lines and Unload Filament] has already run on this gcode.") return data - + # The function also retrieves extruder settings used later in the script + # 't0_has_offsets' is used to exit 'Add Purge Lines' and 'Circle around...' because the script is not compatible with machines with the right nozzle as the primary nozzle. + self.t0_has_offsets = False + self.init_ext_nr = self._get_initial_tool() # Adjust the usable size of the bed per any 'disallowed areas' self._get_build_plate_extents() - self.speed_travel = self.extruder[0].getProperty("speed_travel", "value") * 60 # The start location changes according to which quadrant the nozzle is in at the beginning self.end_purge_location = self._get_real_start_point(data[1]) @@ -229,6 +225,7 @@ class PurgeLinesAndUnload(Script): else: msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." Message(title="[Purge Lines and Unload]", text=msg_text).show() + data[1] += self.data_str return data def _get_real_start_point(self, first_section: str) -> tuple: @@ -341,20 +338,29 @@ class PurgeLinesAndUnload(Script): f"G0 F600 Z2 ; Move up\n" ) + # Move to a corner + if start_side == Location.LEFT: + moves.append(f"G0 F{self.speed_travel} X{self.machine_left + 6} ; Init move\n") + elif start_side == Location.RIGHT: + moves.append(f"G0 F{self.speed_travel} X{self.machine_right - 6} ; Init move\n") + if start_depth == Location.FRONT: + add_move("Y", self.machine_front + 6) + elif start_depth == Location.REAR: + add_move("Y", self.machine_back - 6) # Compare sides if start_side != target_side: if target_side == Location.RIGHT: add_move("X", self.machine_right) else: add_move("X", self.machine_left) - + self.data_str = "; start_side: " + str(start_side) + " | Start Depth: " + str(start_depth) + " | target_side: " + str(target_side) + " | target depth: " + str(target_depth) + "\n" # Compare positions if start_depth != target_depth: if target_depth == Location.REAR: add_move("Y", self.machine_back) else: add_move("Y", self.machine_front) - if len(moves) == 1 and self.start_y: + if len(moves) <= 1: moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") # Combine moves into a single G-code string or return a comment if no movement is needed return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" @@ -362,6 +368,7 @@ class PurgeLinesAndUnload(Script): def _get_build_plate_extents(self): # Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed # This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. + # If there are "extruder offsets" then use them to adjust the 'machine_right' and 'machine_back' independent of any disallowed areas. if self.bed_shape == "rectangular": if self.disallowed_areas: if len(self.disallowed_areas) > 4: @@ -397,122 +404,115 @@ class PurgeLinesAndUnload(Script): else: if self.origin_at_center: self.machine_left = round(-(self.machine_width / 2) + 1, 2) - self.machine_right = round((self.machine_width / 2) - 1, 2) - self.machine_front = round(-(self.machine_depth / 2) + 1, 2) - self.machine_back = round((self.machine_depth / 2) - 1, 2) + self.machine_right = round((self.machine_width / 2) - 1 - self.nozzle_offset_x, 2) + self.machine_front = round(-(self.machine_depth / 2) + 1 + self.nozzle_offset_y, 2) + self.machine_back = round((self.machine_depth / 2) - 1 - self.nozzle_offset_y, 2) else: self.machine_left = 1 - self.machine_right = self.machine_width - 1 - self.machine_front = 1 - self.machine_back = self.machine_depth - 1 + self.machine_right = self.machine_width - 1 - self.nozzle_offset_x + if self.nozzle_offset_y >= 0: + self.machine_front = 1 + self.machine_back = self.machine_depth - 1 - self.nozzle_offset_y + elif self.nozzle_offset_y < 0: + self.machine_front = 1 + abs(self.nozzle_offset_y) + self.machine_back = self.machine_depth - 1 return # Add Purge Lines to the user defined position on the build plate - def _add_purge_lines(self, data_1: str): + def _add_purge_lines(self, data: str): + if self.t0_has_offsets: + data[0] += "; [Purge Lines and Unload] 'Add Purge Lines' did not run because the assumed primary nozzle (T0) has tool offsets.\n" + Message(title = "[Purge Lines and Unload]", text = "'Add Purge Lines' did not run because the assumed primary nozzle (T0) has tool offsets").show() + return data + def calculate_purge_volume(line_width, purge_length, volume_per_mm): return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5) - retract_dist = self.extruder[0].getProperty("retraction_amount", "value") - retract_enable = self.extruder[0].getProperty("retraction_enable", "value") - retract_speed = self.extruder[0].getProperty("retraction_retract_speed", "value") * 60 - material_diameter = self.extruder[0].getProperty("material_diameter", "value") - mm3_per_mm = (material_diameter / 2) ** 2 * 3.14159 - init_line_width = self.extruder[0].getProperty("skirt_brim_line_width", "value") where_at = self.getSettingValueByKey("purge_line_location") - print_speed = round(self.extruder[0].getProperty("speed_print", "value") * 60 * .75) purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" - # Normal cartesian printer with origin at the left front corner if self.bed_shape == "rectangular" and not self.origin_at_center: if where_at == Location.LEFT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MinX") # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.RIGHT: purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MaxX") # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - self.end_purge_location = Position.RIGHT_REAR elif where_at == Location.BOTTOM: - purge_len = int(self.machine_width) - 20 if purge_extrusion_full else int( + purge_len = int(self.machine_width) - self.nozzle_offset_x - 20 if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MinY") # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.TOP: purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MaxY") # Travel to the purge start purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - self.end_purge_location = Position.RIGHT_REAR # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center elif self.bed_shape == "rectangular" and self.origin_at_center: @@ -520,82 +520,78 @@ class PurgeLinesAndUnload(Script): purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs( int(self.machine_front - 10)) y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" - self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.RIGHT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" - self.end_purge_location = Position.RIGHT_REAR elif where_at == Location.BOTTOM: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" - + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.TOP: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( int(self.machine_right - 10)) x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round(purge_volume * 2 - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" - + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": @@ -603,82 +599,78 @@ class PurgeLinesAndUnload(Script): radius_1 = round((self.machine_width / 2) - 1, 2) else: # For where_at in [Location.BOTTOM, Location.TOP] radius_1 = round((self.machine_depth / 2) - 1, 2) - purge_len = int(radius_1) * math.pi / 4 - purge_volume = calculate_purge_volume(init_line_width, purge_len, mm3_per_mm) - + purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) if where_at == Location.LEFT: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two arcs - purge_str += f"G2 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G2 F{self.print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G3 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G3 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" # Wipe - purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - + purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.RIGHT: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two arcs - purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G3 F{self.print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G2 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" # Wipe - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" - + purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR elif where_at == Location.BOTTOM: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two arcs - purge_str += f"G3 F{print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G3 F{self.print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G2 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n" # Wipe - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif where_at == Location.TOP: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two arcs - purge_str += f"G3 F{print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" + purge_str += f"G3 F{self.print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n" purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n" - purge_str += f"G2 F{print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" + purge_str += f"G2 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n" purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n" # Retract if enabled - purge_str += f"G1 F{int(retract_speed)} E{round((purge_volume * 2 + 1) - retract_dist, 5)} ; Retract\n" if retract_enable else "" + purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z5\nG4 S1 ; Wait 1 Second\n" # Wipe - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" - purge_str += f"G0 F{print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" + purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Common ending for purge_str purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]" - # Comment out any existing purge lines in data_1 - startup = data_1[1].split("\n") + # Comment out any existing purge lines in data + startup = data[1].split("\n") for index, line in enumerate(startup): if "G1" in line and " E" in line and (" X" in line or " Y" in line): next_line = index @@ -688,11 +680,11 @@ class PurgeLinesAndUnload(Script): next_line += 1 except IndexError: break - data_1[1] = "\n".join(startup) + data[1] = "\n".join(startup) - # Find the insertion location in data_1 + # Find the insertion location in data purge_str = self._format_string(purge_str) - startup_section = data_1[1].split("\n") + startup_section = data[1].split("\n") insert_index = len(startup_section) - 1 for num in range(len(startup_section) - 1, 0, -1): # In Absolute Extrusion mode - insert above the last G92 E0 line @@ -704,11 +696,15 @@ class PurgeLinesAndUnload(Script): insert_index = num break startup_section.insert(insert_index, purge_str) - data_1[1] = "\n".join(startup_section) - return data_1 + data[1] = "\n".join(startup_section) + return data # Travel moves around the bed periphery to keep strings from crossing the footprint of the model. def _move_to_start(self, data: str) -> str: + if self.t0_has_offsets: + data[0] += "; [Purge Lines and Unload] 'Circle Around to Layer Start' did not run because the assumed primary nozzle (T0) has tool offsets.\n" + Message(title = "[Purge Lines and Unload]", text = "'Circle Around to Layer Start' did not run because the assumed primary nozzle (T0) has tool offsets.").show() + return data self.start_x = None self.start_y = None move_str = None @@ -747,7 +743,6 @@ class PurgeLinesAndUnload(Script): move_str = self._move_to_location("Layer Start", target_location) elif self.bed_shape == "elliptic" and self.origin_at_center: move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n" - radius = self.machine_width / 2 offset_sin = round(2 ** .5 / 2 * radius, 2) if target_location == Position.LEFT_FRONT: @@ -768,7 +763,7 @@ class PurgeLinesAndUnload(Script): if move_str.startswith("\n"): move_str = move_str[1:] startup.append(move_str) - + # Move the 'LAYER_COUNT' line so it's at the end of data[1] for index, line in enumerate(startup): if "LAYER_COUNT" in line: lay_count = startup.pop(index) + "\n" @@ -788,7 +783,6 @@ class PurgeLinesAndUnload(Script): quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2 unload_distance = self.getSettingValueByKey("unload_distance") quick_purge = self.getSettingValueByKey("unload_quick_purge") - lines = data[-1].split("\n") for index, line in enumerate(lines): # Unload the filament just before the hot end turns off. @@ -799,7 +793,6 @@ class PurgeLinesAndUnload(Script): ) if quick_purge: filament_str += f"G1 F{quick_purge_speed} E{quick_purge_amount} ; Quick Purge before unload\n" - if unload_distance > 150: filament_str += "".join( f"G1 F{extrude_speed} E-150 ; Unload some\n" @@ -810,14 +803,12 @@ class PurgeLinesAndUnload(Script): filament_str += f"G1 F{extrude_speed} E-{remaining_unload} ; Unload the remainder\n" else: filament_str += f"G1 F{extrude_speed} E-{unload_distance} ; Unload\n" - filament_str += ( "M82 ; Absolute Extrusion\n" "G92 E0 ; Reset Extruder\n" ) lines[index] = filament_str + line break - data[-1] = "\n".join(lines) return data @@ -860,4 +851,31 @@ class PurgeLinesAndUnload(Script): gap_len - 1 - len( temp_line[1:].split(";")[0]))), 1) any_gcode_str = "\n".join(temp_lines) - return any_gcode_str \ No newline at end of file + return any_gcode_str + + def _get_initial_tool(self) -> int: + # Get the Initial Extruder + num = Application.getInstance().getExtruderManager().getInitialExtruderNr() + if num == None or num == -1: + num = 0 + # If there is an extruder offset X then it will be used to adjust the "machine_right" and a Y offset will adjust the "machine_back" + if self.extruder_count > 1 and bool(self.curaApp.getProperty("machine_use_extruder_offset_to_offset_coords", "value")): + self.nozzle_offset_x = self.extruder[1].getProperty("machine_nozzle_offset_x", "value") + self.nozzle_offset_y = self.extruder[1].getProperty("machine_nozzle_offset_y", "value") + else: + self.nozzle_offset_x = 0.0 + self.nozzle_offset_y = 0.0 + material_diameter = self.extruder[num].getProperty("material_diameter", "value") + self.init_line_width = self.extruder[num].getProperty("skirt_brim_line_width", "value") + self.print_speed = round(self.extruder[num].getProperty("speed_print", "value") * 60 * .75) + self.speed_travel = round(self.extruder[num].getProperty("speed_travel", "value") * 60) + self.retract_dist = self.extruder[num].getProperty("retraction_amount", "value") + self.retraction_enable = self.extruder[num].getProperty("retraction_enable", "value") + self.retract_speed = self.extruder[num].getProperty("retraction_retract_speed", "value") * 60 + self.mm3_per_mm = (material_diameter / 2) ** 2 * math.pi + # Don't add purge lines if 'T0' has offsets. + t0_x_offset = self.extruder[0].getProperty("machine_nozzle_offset_x", "value") + t0_y_offset = self.extruder[0].getProperty("machine_nozzle_offset_y", "value") + if t0_x_offset or t0_y_offset: + self.t0_has_offsets = True + return num \ No newline at end of file From e7546031d3fe4fb07a9889f938e899bfb0ec69dd Mon Sep 17 00:00:00 2001 From: HellAholic Date: Tue, 7 Jan 2025 23:50:01 +0100 Subject: [PATCH 14/27] Some cleanup - variable names updated to reflect their use more clearly - removed obsolete if/else for move (the length of the list will be always 2) --- .../scripts/PurgeLinesAndUnload.py | 91 ++++++++++--------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 151c030f60..7cacc8762b 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -39,19 +39,19 @@ class PurgeLinesAndUnload(Script): def __init__(self): super().__init__() - self.curaApp = Application.getInstance().getGlobalContainerStack() - self.extruder = self.curaApp.extruderList + self.global_stack = Application.getInstance().getGlobalContainerStack() + self.extruder = self.global_stack.extruderList self.end_purge_location = None self.speed_travel = None # This will be True when there are more than 4 'machine_disallowed_areas' self.show_warning = False - self.disallowed_areas = self.curaApp.getProperty("machine_disallowed_areas", "value") - self.extruder = self.curaApp.extruderList - self.extruder_count = self.curaApp.getProperty("machine_extruder_count", "value") - self.bed_shape = self.curaApp.getProperty("machine_shape", "value") - self.origin_at_center = self.curaApp.getProperty("machine_center_is_zero", "value") - self.machine_width = self.curaApp.getProperty("machine_width", "value") - self.machine_depth = self.curaApp.getProperty("machine_depth", "value") + self.disallowed_areas = self.global_stack.getProperty("machine_disallowed_areas", "value") + self.extruder = self.global_stack.extruderList + self.extruder_count = self.global_stack.getProperty("machine_extruder_count", "value") + self.bed_shape = self.global_stack.getProperty("machine_shape", "value") + self.origin_at_center = self.global_stack.getProperty("machine_center_is_zero", "value") + self.machine_width = self.global_stack.getProperty("machine_width", "value") + self.machine_depth = self.global_stack.getProperty("machine_depth", "value") self.machine_left = 1.0 self.machine_right = self.machine_width - 1.0 self.machine_front = 1.0 @@ -60,7 +60,7 @@ class PurgeLinesAndUnload(Script): def initialize(self) -> None: super().initialize() # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. - startup_gcode = self.curaApp.getProperty("machine_start_gcode", "value") + startup_gcode = self.global_stack.getProperty("machine_start_gcode", "value") start_lines = startup_gcode.splitlines() for line in start_lines: if "G1" in line and " E" in line and (" X" in line or " Y" in line): @@ -68,8 +68,8 @@ class PurgeLinesAndUnload(Script): text="It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show() break # 'is_rectangular' is used to disable half-length purge lines for elliptic beds. - self._instance.setProperty("is_rectangular", "value", True if self.curaApp.getProperty("machine_shape", "value") == "rectangular" else False) - self._instance.setProperty("move_to_prime_tower", "value", True if self.curaApp.getProperty("machine_extruder_count", "value") > 1 else False) + self._instance.setProperty("is_rectangular", "value", True if self.global_stack.getProperty("machine_shape", "value") == "rectangular" else False) + self._instance.setProperty("move_to_prime_tower", "value", True if self.global_stack.getProperty("machine_extruder_count", "value") > 1 else False) # Set the default E adjustment self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) @@ -270,8 +270,8 @@ class PurgeLinesAndUnload(Script): return first_section adjustment_lines = "" move_to_prime_present = False - prime_tower_x = self.curaApp.getProperty("prime_tower_position_x", "value") - prime_tower_y = self.curaApp.getProperty("prime_tower_position_y", "value") + prime_tower_x = self.global_stack.getProperty("prime_tower_position_x", "value") + prime_tower_y = self.global_stack.getProperty("prime_tower_position_y", "value") prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y) # Shortstop an error if Start Location comes through as None if self.end_purge_location is None: @@ -353,6 +353,7 @@ class PurgeLinesAndUnload(Script): add_move("X", self.machine_right) else: add_move("X", self.machine_left) + # Add a comment to highlight the move self.data_str = "; start_side: " + str(start_side) + " | Start Depth: " + str(start_depth) + " | target_side: " + str(target_side) + " | target depth: " + str(target_depth) + "\n" # Compare positions if start_depth != target_depth: @@ -360,15 +361,17 @@ class PurgeLinesAndUnload(Script): add_move("Y", self.machine_back) else: add_move("Y", self.machine_front) - if len(moves) <= 1: + if len(moves) == 1: moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n") - # Combine moves into a single G-code string or return a comment if no movement is needed - return "".join(moves) if len(moves) > 1 else f";----------[Already at {location_name}, No Moves necessary]\n" + # Combine moves into a single G-code string and return + return "".join(moves) def _get_build_plate_extents(self): - # Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed - # This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. - # If there are "extruder offsets" then use them to adjust the 'machine_right' and 'machine_back' independent of any disallowed areas. + """ + Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed + This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle. + If there are "extruder offsets" then use them to adjust the 'machine_right' and 'machine_back' independent of any disallowed areas. + """ if self.bed_shape == "rectangular": if self.disallowed_areas: if len(self.disallowed_areas) > 4: @@ -428,12 +431,12 @@ class PurgeLinesAndUnload(Script): def calculate_purge_volume(line_width, purge_length, volume_per_mm): return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5) - where_at = self.getSettingValueByKey("purge_line_location") + purge_location = self.getSettingValueByKey("purge_line_location") purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" # Normal cartesian printer with origin at the left front corner if self.bed_shape == "rectangular" and not self.origin_at_center: - if where_at == Location.LEFT: + if purge_location == Location.LEFT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) @@ -453,7 +456,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.RIGHT: + elif purge_location == Location.RIGHT: purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) @@ -473,7 +476,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR - elif where_at == Location.BOTTOM: + elif purge_location == Location.BOTTOM: purge_len = int(self.machine_width) - self.nozzle_offset_x - 20 if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) @@ -493,7 +496,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.TOP: + elif purge_location == Location.TOP: purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) @@ -516,7 +519,7 @@ class PurgeLinesAndUnload(Script): self.end_purge_location = Position.RIGHT_REAR # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center elif self.bed_shape == "rectangular" and self.origin_at_center: - if where_at == Location.LEFT: + if purge_location == Location.LEFT: purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs( int(self.machine_front - 10)) y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 @@ -535,7 +538,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.RIGHT: + elif purge_location == Location.RIGHT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( (self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 @@ -554,7 +557,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR - elif where_at == Location.BOTTOM: + elif purge_location == Location.BOTTOM: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 @@ -573,7 +576,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.TOP: + elif purge_location == Location.TOP: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( int(self.machine_right - 10)) x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 @@ -595,13 +598,13 @@ class PurgeLinesAndUnload(Script): self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": - if where_at in [Location.LEFT, Location.RIGHT]: + if purge_location in [Location.LEFT, Location.RIGHT]: radius_1 = round((self.machine_width / 2) - 1, 2) - else: # For where_at in [Location.BOTTOM, Location.TOP] + else: # For purge_location in [Location.BOTTOM, Location.TOP] radius_1 = round((self.machine_depth / 2) - 1, 2) purge_len = int(radius_1) * math.pi / 4 purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) - if where_at == Location.LEFT: + if purge_location == Location.LEFT: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" @@ -617,7 +620,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.RIGHT: + elif purge_location == Location.RIGHT: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" @@ -633,7 +636,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR - elif where_at == Location.BOTTOM: + elif purge_location == Location.BOTTOM: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" @@ -649,7 +652,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif where_at == Location.TOP: + elif purge_location == Location.TOP: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" @@ -815,17 +818,17 @@ class PurgeLinesAndUnload(Script): # Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out. def _adjust_starting_e(self, data: str) -> str: if not self.extruder[0].getProperty("retraction_enable", "value"): - return - adjust_amt = self.getSettingValueByKey("adjust_e_loc_to") + return data + adjust_amount = self.getSettingValueByKey("adjust_e_loc_to") lines = data[1].split("\n") lines.reverse() - if self.curaApp.getProperty("machine_firmware_retract", "value"): - search_pattern = "G10" + if self.global_stack.getProperty("machine_firmware_retract", "value"): + search_pattern = r"G10" else: - search_pattern = "G1 F(\d*) E-(\d.*)" + search_pattern = r"G1 F(\d*) E-(\d.*)" for index, line in enumerate(lines): if re.search(search_pattern, line): - lines[index] = re.sub(search_pattern, f"G92 E{adjust_amt}", line) + lines[index] = re.sub(search_pattern, f"G92 E{adjust_amount}", line) lines.reverse() data[1] = "\n".join(lines) break @@ -856,10 +859,10 @@ class PurgeLinesAndUnload(Script): def _get_initial_tool(self) -> int: # Get the Initial Extruder num = Application.getInstance().getExtruderManager().getInitialExtruderNr() - if num == None or num == -1: + if num is None or num == -1: num = 0 # If there is an extruder offset X then it will be used to adjust the "machine_right" and a Y offset will adjust the "machine_back" - if self.extruder_count > 1 and bool(self.curaApp.getProperty("machine_use_extruder_offset_to_offset_coords", "value")): + if self.extruder_count > 1 and bool(self.global_stack.getProperty("machine_use_extruder_offset_to_offset_coords", "value")): self.nozzle_offset_x = self.extruder[1].getProperty("machine_nozzle_offset_x", "value") self.nozzle_offset_y = self.extruder[1].getProperty("machine_nozzle_offset_y", "value") else: @@ -878,4 +881,4 @@ class PurgeLinesAndUnload(Script): t0_y_offset = self.extruder[0].getProperty("machine_nozzle_offset_y", "value") if t0_x_offset or t0_y_offset: self.t0_has_offsets = True - return num \ No newline at end of file + return num From 28f8e2af7996f13e5e414b4ce0c4babf725cba4b Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:14:09 -0500 Subject: [PATCH 15/27] Update PurgeLinesAndUnload.py Change the comment line location from the end of data[1] to the first line of the "moves". Update PurgeLinesAndUnload.py Move the "start from" comment from the end of data[1] to the first line of the "moves" list. Update PurgeLinesAndUnload.py Change the comment line location from the end of data[1] to the first line of the "moves". --- .../PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 7cacc8762b..852b941d67 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -225,7 +225,6 @@ class PurgeLinesAndUnload(Script): else: msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable." Message(title="[Purge Lines and Unload]", text=msg_text).show() - data[1] += self.data_str return data def _get_real_start_point(self, first_section: str) -> tuple: @@ -327,8 +326,8 @@ class PurgeLinesAndUnload(Script): # Extract components start_side, start_depth = self.end_purge_location target_side, target_depth = location - - moves = [f";MESH:NONMESH---------[Move to {location_name}]\nG0 F600 Z2 ; Move up\n"] + # Start of the moves and a comment to highlight the move + moves = [f";MESH:NONMESH---------[Circle around to {location_name}] Start from: {str(start_side)} {str(start_depth)} Go to: {target_side} {target_depth}\nG0 F600 Z2 ; Move up\n"] # Helper function to add G-code for moves def add_move(axis: str, position: float) -> None: @@ -353,8 +352,6 @@ class PurgeLinesAndUnload(Script): add_move("X", self.machine_right) else: add_move("X", self.machine_left) - # Add a comment to highlight the move - self.data_str = "; start_side: " + str(start_side) + " | Start Depth: " + str(start_depth) + " | target_side: " + str(target_side) + " | target depth: " + str(target_depth) + "\n" # Compare positions if start_depth != target_depth: if target_depth == Location.REAR: From 94c64a031ec76da0c8bc39803daba5e5a7704a8b Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Mon, 10 Feb 2025 10:46:05 -0500 Subject: [PATCH 16/27] Update PurgeLinesAndUnload.py Changes made per wawanbreton suggestions. "border distance" setting is not included. --- .../scripts/PurgeLinesAndUnload.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 852b941d67..5a0ca43c2a 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -116,8 +116,8 @@ class PurgeLinesAndUnload(Script): }, "move_to_start": { - "label": "Circle around to layer start", - "description": "Depending on where the 'Layer Start X' and 'Layer Start Y' are for the print, the opening travel move can pass across the print area and leave a string there. This option will generate an orthogonal path that moves the nozzle around the edges of the build plate and then comes in to the Start Point. The nozzle will drop and touch the build plate at each stop in order to nail down the string so it doesn't follow in a straight line.", + "label": "Circle around to layer start ⚠️​", + "description": "Depending on where the 'Layer Start X' and 'Layer Start Y' are for the print, the opening travel move can pass across the print area and leave a string there. This option will generate an orthogonal path that moves the nozzle around the edges of the build plate and then comes in to the Start Point. || ⚠️​ || The nozzle will drop to Z0.0 and touch the build plate at each stop in order to 'nail down the string' so it doesn't follow in a straight line.", "type": "bool", "default_value": false, "enabled": true @@ -434,8 +434,7 @@ class PurgeLinesAndUnload(Script): # Normal cartesian printer with origin at the left front corner if self.bed_shape == "rectangular" and not self.origin_at_center: if purge_location == Location.LEFT: - purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( - (self.machine_back - self.machine_front) / 2) + purge_len = int(self.machine_back - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2) purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MinX") @@ -454,8 +453,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.RIGHT: - purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int( - (self.machine_back - self.machine_front) / 2) + purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2) purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MaxX") @@ -778,7 +776,7 @@ class PurgeLinesAndUnload(Script): # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments. def _unload_filament(self, data: str) -> str: extrude_speed = 3000 - quick_purge_speed = 240 + quick_purge_speed = round(float(self.extruder[0].getProperty("machine_nozzle_size", "value")) * 500) retract_amount = self.extruder[0].getProperty("retraction_amount", "value") quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2 unload_distance = self.getSettingValueByKey("unload_distance") From 1728db94273aa91e9497c0b5af02464b983e18c7 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 10 Feb 2025 16:57:55 +0100 Subject: [PATCH 17/27] Top/Bottom -> Rear/Front Top -> Rear Bottom -> Front --- .../scripts/PurgeLinesAndUnload.py | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 5a0ca43c2a..a5e4ddf874 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -22,8 +22,6 @@ from enum import Enum class Location(str, Enum): LEFT = "left" RIGHT = "right" - TOP = "top" - BOTTOM = "bottom" REAR = "rear" FRONT = "front" @@ -98,8 +96,8 @@ class PurgeLinesAndUnload(Script): "options": { "left": "On left edge (Xmin)", "right": "On right edge (Xmax)", - "bottom": "On front edge (Ymin)", - "top": "On back edge (Ymax)"}, + "front": "On front edge (Ymin)", + "rear": "On back edge (Ymax)"}, "default_value": "left", "enabled": "add_purge_lines" }, @@ -160,7 +158,7 @@ class PurgeLinesAndUnload(Script): "unload_quick_purge": { "label": " Quick purge before unload", - "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle.", + "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.", "type": "bool", "default_value": false, "enabled": "enable_unload" @@ -471,7 +469,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR - elif purge_location == Location.BOTTOM: + elif purge_location == Location.FRONT: purge_len = int(self.machine_width) - self.nozzle_offset_x - 20 if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2) @@ -491,7 +489,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif purge_location == Location.TOP: + elif purge_location == Location.REAR: purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2) @@ -552,7 +550,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR - elif purge_location == Location.BOTTOM: + elif purge_location == Location.FRONT: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( (self.machine_right - self.machine_left) / 2) x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 @@ -571,7 +569,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif purge_location == Location.TOP: + elif purge_location == Location.REAR: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( int(self.machine_right - 10)) x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 @@ -595,7 +593,7 @@ class PurgeLinesAndUnload(Script): elif self.bed_shape == "elliptic": if purge_location in [Location.LEFT, Location.RIGHT]: radius_1 = round((self.machine_width / 2) - 1, 2) - else: # For purge_location in [Location.BOTTOM, Location.TOP] + else: # For purge_location in [Location.FRONT, Location.REAR] radius_1 = round((self.machine_depth / 2) - 1, 2) purge_len = int(radius_1) * math.pi / 4 purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) @@ -631,7 +629,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR - elif purge_location == Location.BOTTOM: + elif purge_location == Location.FRONT: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" @@ -647,7 +645,7 @@ class PurgeLinesAndUnload(Script): purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n" purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT - elif purge_location == Location.TOP: + elif purge_location == Location.REAR: # Travel to the purge start purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" From 1dc8dd8a808214e9b368fef0a91e82561f2a5778 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Mon, 10 Feb 2025 12:40:59 -0500 Subject: [PATCH 18/27] Update PurgeLinesAndUnload Removed the default border of 1mm and added a setting "Border Distance". --- .../scripts/PurgeLinesAndUnload.py | 150 ++++++++++-------- 1 file changed, 81 insertions(+), 69 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 5a0ca43c2a..088d2f6e30 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -56,7 +56,7 @@ class PurgeLinesAndUnload(Script): self.machine_right = self.machine_width - 1.0 self.machine_front = 1.0 self.machine_back = self.machine_depth - 1.0 - + def initialize(self) -> None: super().initialize() # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. @@ -72,7 +72,7 @@ class PurgeLinesAndUnload(Script): self._instance.setProperty("move_to_prime_tower", "value", True if self.global_stack.getProperty("machine_extruder_count", "value") > 1 else False) # Set the default E adjustment self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) - + def getSettingDataString(self): return """{ "name": "Purge Lines and Unload Filament", @@ -114,6 +114,17 @@ class PurgeLinesAndUnload(Script): "default_value": "purge_full", "enabled": "add_purge_lines and is_rectangular" }, + "border_distance": + { + "label": " Border Distance", + "description": "This is the distance from the build plate edge to the first purge line. '0' works for most printers but you might want the lines further inboard. The allowable range is -12 to 12. ⚠️ Negative numbers are allowed for printers that have 'Disallowed Areas'. You must use due caution when using a negative value.", + "type": "int", + "unit": "mm ", + "default_value": 0, + "minimum_value": -12, + "maximum_value": 12, + "enabled": "add_purge_lines" + }, "move_to_start": { "label": "Circle around to layer start ⚠️​", @@ -160,7 +171,7 @@ class PurgeLinesAndUnload(Script): "unload_quick_purge": { "label": " Quick purge before unload", - "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle.", + "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle. If there is no 'Present Print' move in the Ending Gcode then the Quick Purge can be over the print.", "type": "bool", "default_value": false, "enabled": "enable_unload" @@ -202,6 +213,7 @@ class PurgeLinesAndUnload(Script): self._get_build_plate_extents() # The start location changes according to which quadrant the nozzle is in at the beginning self.end_purge_location = self._get_real_start_point(data[1]) + self.border_distance = self.getSettingValueByKey("border_distance") # Mapping settings to corresponding methods procedures = { @@ -392,30 +404,30 @@ class PurgeLinesAndUnload(Script): if mid_y > y > back_y: back_y = y if self.origin_at_center: - self.machine_left = round(left_x + 1, 2) - self.machine_right = round(right_x - 1, 2) - self.machine_front = round(front_y - 1, 2) - self.machine_back = round(back_y + 1, 2) + self.machine_left = round(left_x, 2) + self.machine_right = round(right_x, 2) + self.machine_front = round(front_y, 2) + self.machine_back = round(back_y, 2) else: - self.machine_left = round(left_x + 1 + self.machine_width / 2, 2) - self.machine_right = round(right_x - 1 + self.machine_width / 2, 2) - self.machine_front = round((self.machine_depth / 2) - front_y - 1, 2) - self.machine_back = round((self.machine_depth / 2) - back_y + 1, 2) + self.machine_left = round(left_x + self.machine_width / 2, 2) + self.machine_right = round(right_x + self.machine_width / 2, 2) + self.machine_front = round((self.machine_depth / 2) - front_y, 2) + self.machine_back = round((self.machine_depth / 2) - back_y, 2) else: if self.origin_at_center: - self.machine_left = round(-(self.machine_width / 2) + 1, 2) - self.machine_right = round((self.machine_width / 2) - 1 - self.nozzle_offset_x, 2) - self.machine_front = round(-(self.machine_depth / 2) + 1 + self.nozzle_offset_y, 2) - self.machine_back = round((self.machine_depth / 2) - 1 - self.nozzle_offset_y, 2) + self.machine_left = round(-(self.machine_width / 2), 2) + self.machine_right = round((self.machine_width / 2) - self.nozzle_offset_x, 2) + self.machine_front = round(-(self.machine_depth / 2) + self.nozzle_offset_y, 2) + self.machine_back = round((self.machine_depth / 2) - self.nozzle_offset_y, 2) else: - self.machine_left = 1 - self.machine_right = self.machine_width - 1 - self.nozzle_offset_x + self.machine_left = 0 + self.machine_right = self.machine_width - self.nozzle_offset_x if self.nozzle_offset_y >= 0: - self.machine_front = 1 - self.machine_back = self.machine_depth - 1 - self.nozzle_offset_y + self.machine_front = 0 + self.machine_back = self.machine_depth - self.nozzle_offset_y elif self.nozzle_offset_y < 0: - self.machine_front = 1 + abs(self.nozzle_offset_y) - self.machine_back = self.machine_depth - 1 + self.machine_front = abs(self.nozzle_offset_y) + self.machine_back = self.machine_depth return # Add Purge Lines to the user defined position on the build plate @@ -439,18 +451,18 @@ class PurgeLinesAndUnload(Script): purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MinX") # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + self.border_distance} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + self.border_distance} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 35} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.RIGHT: purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2) @@ -458,18 +470,18 @@ class PurgeLinesAndUnload(Script): purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MaxX") # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_right} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - self.border_distance} ; Move\nG0 Y{self.machine_back - 10} ; Move\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - self.border_distance} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR elif purge_location == Location.BOTTOM: purge_len = int(self.machine_width) - self.nozzle_offset_x - 20 if purge_extrusion_full else int( @@ -478,18 +490,18 @@ class PurgeLinesAndUnload(Script): purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MinY") # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front} ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front + self.border_distance} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front + self.border_distance} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3 + self.border_distance} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3 + self.border_distance} E{purge_volume * 2} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.TOP: purge_len = int(self.machine_width - 20) if purge_extrusion_full else int( @@ -498,19 +510,19 @@ class PurgeLinesAndUnload(Script): purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) purge_str = purge_str.replace("Lines", "Lines at MaxY") # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} ; Ortho Move to back\n" + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back - self.border_distance} ; Ortho Move to back\n" purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back - self.border_distance} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3 - self.border_distance} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3 - self.border_distance} E{purge_volume * 2} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center elif self.bed_shape == "rectangular" and self.origin_at_center: @@ -520,18 +532,18 @@ class PurgeLinesAndUnload(Script): y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0 purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left} Y{self.machine_front + 10} ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + self.border_distance} Y{self.machine_front + 10} ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{self.machine_left} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_left + 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + self.border_distance} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 3} Y{self.machine_front + 35} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 35} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.RIGHT: purge_len = int(self.machine_back - 20) if purge_extrusion_full else int( @@ -539,18 +551,18 @@ class PurgeLinesAndUnload(Script): y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0 purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_right} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - self.border_distance} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{self.machine_right} Y{y_stop} E{purge_volume} ; First line\n" - purge_str += f"G0 X{self.machine_right - 3} Y{y_stop} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - self.border_distance} Y{y_stop} E{purge_volume} ; First line\n" + purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{y_stop} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3} Y{self.machine_back - 35} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR elif purge_location == Location.BOTTOM: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( @@ -558,18 +570,18 @@ class PurgeLinesAndUnload(Script): x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0 purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front} Z2 ; Move to start\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front + self.border_distance} Z2 ; Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_front + 3} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front + self.border_distance} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_front + 3 + self.border_distance} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3 + self.border_distance} E{purge_volume * 2} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.TOP: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( @@ -577,19 +589,19 @@ class PurgeLinesAndUnload(Script): x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0 purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm) # Travel to the purge start - purge_str += f"G0 F{self.speed_travel} Y{self.machine_back} Z2; Ortho Move to back\n" + purge_str += f"G0 F{self.speed_travel} Y{self.machine_back - self.border_distance} Z2; Ortho Move to back\n" purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n" purge_str += f"G0 F600 Z0.3 ; Move down\n" # Purge two lines - purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back} E{purge_volume} ; First line\n" - purge_str += f"G0 X{x_stop} Y{self.machine_back - 3} ; Move over\n" - purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3} E{purge_volume * 2} ; Second line\n" + purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back - self.border_distance} E{purge_volume} ; First line\n" + purge_str += f"G0 X{x_stop} Y{self.machine_back - 3 - self.border_distance} ; Move over\n" + purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3 - self.border_distance} E{purge_volume * 2} ; Second line\n" # Retract if enabled purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else "" purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 35} Y{self.machine_back - 3} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": From e5723a108849cb0487ea36d3c0f463352aef5cfe Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 10 Feb 2025 19:44:10 +0100 Subject: [PATCH 19/27] small fix --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 54afdc5747..68c16b1e2f 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -169,11 +169,7 @@ class PurgeLinesAndUnload(Script): "unload_quick_purge": { "label": " Quick purge before unload", -<<<<<<< HEAD - "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick puge' will take place at the last position of the nozzle. If there is no 'Present Print' move in the Ending Gcode then the Quick Purge can be over the print.", -======= "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.", ->>>>>>> 1728db94273aa91e9497c0b5af02464b983e18c7 "type": "bool", "default_value": false, "enabled": "enable_unload" From b421c057a82079132c7c6bb846cc1f4c5f72542d Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 10 Feb 2025 20:15:22 +0100 Subject: [PATCH 20/27] Add overwritten part for start_x and start_y set to None --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 68c16b1e2f..8329da8a3f 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -54,7 +54,9 @@ class PurgeLinesAndUnload(Script): self.machine_right = self.machine_width - 1.0 self.machine_front = 1.0 self.machine_back = self.machine_depth - 1.0 - + self.start_x = None + self.start_y = None + def initialize(self) -> None: super().initialize() # Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup. From 573a92bd798c15d26313282201502fcf0366c387 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 10 Feb 2025 20:55:23 +0100 Subject: [PATCH 21/27] Indentation fix --- .../scripts/PurgeLinesAndUnload.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 8329da8a3f..b06ac8a16a 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -81,7 +81,7 @@ class PurgeLinesAndUnload(Script): "version": 2, "settings": { - "add_purge_lines": + "add_purge_lines": { "label": "Add Purge Lines to StartUp", "description": "The purge lines can be left, right, front or back. If there are purge lines present in the StartUp Gcode remove them or comment them out before using this script. You don't want to double dip.", @@ -116,8 +116,8 @@ class PurgeLinesAndUnload(Script): }, "border_distance": { - "label": " Border Distance", - "description": "This is the distance from the build plate edge to the first purge line. '0' works for most printers but you might want the lines further inboard. The allowable range is -12 to 12. ⚠️ Negative numbers are allowed for printers that have 'Disallowed Areas'. You must use due caution when using a negative value.", + "label": "Border Distance", + "description": "This is the distance from the build plate edge to the first purge line. '0' works for most printers but you might want the lines further inboard. The allowable range is -12 to 12. ⚠️ Negative numbers are allowed for printers that have 'Disallowed Areas'. You must use due caution when using a negative value.", "type": "int", "unit": "mm ", "default_value": 0, @@ -144,7 +144,7 @@ class PurgeLinesAndUnload(Script): }, "adjust_e_loc_to": { - "label": " Starting E location", + "label": "Starting E location", "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", "type": "float", "unit": "mm ", @@ -161,7 +161,7 @@ class PurgeLinesAndUnload(Script): }, "unload_distance": { - "label": " Unload Distance", + "label": "Unload Distance", "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", "type": "int", "default_value": 440, @@ -170,7 +170,7 @@ class PurgeLinesAndUnload(Script): }, "unload_quick_purge": { - "label": " Quick purge before unload", + "label": "Quick purge before unload", "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.", "type": "bool", "default_value": false, From 3afffbd40372007cf037ccd01931d2748b9d4c3d Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 10 Feb 2025 20:56:35 +0100 Subject: [PATCH 22/27] Wipe move consistency for rectangular - Remove the travel move -> type probably - Remove the second F in 3 out of 8 wipe moves --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index b06ac8a16a..a140714eb8 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -562,7 +562,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" + purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR elif purge_location == Location.FRONT: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( @@ -581,7 +581,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" + purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.REAR: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( @@ -601,7 +601,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" + purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": From 8f14e62809718ecf8ba099b22592063939a104cf Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 10 Feb 2025 21:32:03 +0100 Subject: [PATCH 23/27] re-indent the collaterals --- .../PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index a140714eb8..b8a578cdde 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -116,7 +116,7 @@ class PurgeLinesAndUnload(Script): }, "border_distance": { - "label": "Border Distance", + "label": " Border Distance", "description": "This is the distance from the build plate edge to the first purge line. '0' works for most printers but you might want the lines further inboard. The allowable range is -12 to 12. ⚠️ Negative numbers are allowed for printers that have 'Disallowed Areas'. You must use due caution when using a negative value.", "type": "int", "unit": "mm ", @@ -144,7 +144,7 @@ class PurgeLinesAndUnload(Script): }, "adjust_e_loc_to": { - "label": "Starting E location", + "label": " Starting E location", "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", "type": "float", "unit": "mm ", @@ -161,7 +161,7 @@ class PurgeLinesAndUnload(Script): }, "unload_distance": { - "label": "Unload Distance", + "label": " Unload Distance", "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", "type": "int", "default_value": 440, @@ -170,7 +170,7 @@ class PurgeLinesAndUnload(Script): }, "unload_quick_purge": { - "label": "Quick purge before unload", + "label": " Quick purge before unload", "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.", "type": "bool", "default_value": false, From 5fe1193df1a7b32b1c7e7550de5ee889a8e02441 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:43:56 -0500 Subject: [PATCH 24/27] Update PurgeLinesAndUnload.py Added purge blob option. Required changes in some settings to 'self'. Fixed 'quick_purge_speed' to adjust for 2.85 filament. --- .../scripts/PurgeLinesAndUnload.py | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index b8a578cdde..fa07ce1ae9 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -72,7 +72,7 @@ class PurgeLinesAndUnload(Script): self._instance.setProperty("move_to_prime_tower", "value", True if self.global_stack.getProperty("machine_extruder_count", "value") > 1 else False) # Set the default E adjustment self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1))) - + def getSettingDataString(self): return """{ "name": "Purge Lines and Unload Filament", @@ -125,6 +125,23 @@ class PurgeLinesAndUnload(Script): "maximum_value": 12, "enabled": "add_purge_lines" }, + "prime_blob_enable": + { + "label": " Start with Prime Blob️​", + "description": "Enable a stationary purge before starting the purge lines. Available only when purge line location is 'left' or 'front'", + "type": "bool", + "default_value": false, + "enabled": "add_purge_lines and purge_line_location in ['front', 'left']" + }, + "prime_blob_distance": + { + "label": " Blob Distance️​", + "description": "How many mm's of filament should be extruded for the blob.", + "type": "int", + "default_value": 0, + "unit": "mm ", + "enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']" + }, "move_to_start": { "label": "Circle around to layer start ⚠️​", @@ -144,7 +161,7 @@ class PurgeLinesAndUnload(Script): }, "adjust_e_loc_to": { - "label": " Starting E location", + "label": "Starting E location", "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", "type": "float", "unit": "mm ", @@ -161,7 +178,7 @@ class PurgeLinesAndUnload(Script): }, "unload_distance": { - "label": " Unload Distance", + "label": "Unload Distance", "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", "type": "int", "default_value": 440, @@ -170,7 +187,7 @@ class PurgeLinesAndUnload(Script): }, "unload_quick_purge": { - "label": " Quick purge before unload", + "label": "Quick purge before unload", "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.", "type": "bool", "default_value": false, @@ -214,6 +231,11 @@ class PurgeLinesAndUnload(Script): # The start location changes according to which quadrant the nozzle is in at the beginning self.end_purge_location = self._get_real_start_point(data[1]) self.border_distance = self.getSettingValueByKey("border_distance") + self.prime_blob_enable = self.getSettingValueByKey("prime_blob_enable") + if self.prime_blob_enable: + self.prime_blob_distance = self.getSettingValueByKey("prime_blob_distance") + else: + self.prime_blob_distance = 0 # Mapping settings to corresponding methods procedures = { @@ -295,7 +317,7 @@ class PurgeLinesAndUnload(Script): if startup[index + 1].startswith("G0"): prime_move = startup[index + 1] + " ; Move to Prime Tower" adjustment_lines = self._move_to_location("Prime Tower", prime_tower_loc) - startup[index + 1] = adjustment_lines + prime_move + "\n" + startup[index] + startup[index + 1] = adjustment_lines + prime_move + "\n;---------------------[End of Prime Tower moves]\n" + startup[index] startup.pop(index) first_section[1] = "\n".join(startup) move_to_prime_present = True @@ -443,6 +465,7 @@ class PurgeLinesAndUnload(Script): purge_location = self.getSettingValueByKey("purge_line_location") purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n" + purge_str += self._get_blob_code() # Normal cartesian printer with origin at the left front corner if self.bed_shape == "rectangular" and not self.origin_at_center: if purge_location == Location.LEFT: @@ -562,7 +585,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" + purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR elif purge_location == Location.FRONT: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( @@ -581,7 +604,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.REAR: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( @@ -601,7 +624,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n" - purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" + purge_str += f"G0 F{self.print_speed} X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": @@ -788,7 +811,8 @@ class PurgeLinesAndUnload(Script): # Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments. def _unload_filament(self, data: str) -> str: extrude_speed = 3000 - quick_purge_speed = round(float(self.extruder[0].getProperty("machine_nozzle_size", "value")) * 500) + quick_purge_speed = round(float(self.nozzle_size) * 500) + if self.material_diameter > 2: quick_purge_speed *= .38 # Adjustment for 2.85 filament retract_amount = self.extruder[0].getProperty("retraction_amount", "value") quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2 unload_distance = self.getSettingValueByKey("unload_distance") @@ -875,17 +899,33 @@ class PurgeLinesAndUnload(Script): else: self.nozzle_offset_x = 0.0 self.nozzle_offset_y = 0.0 - material_diameter = self.extruder[num].getProperty("material_diameter", "value") + self.material_diameter = self.extruder[num].getProperty("material_diameter", "value") + self.nozzle_size = self.extruder[num].getProperty("machine_nozzle_size", "value") self.init_line_width = self.extruder[num].getProperty("skirt_brim_line_width", "value") self.print_speed = round(self.extruder[num].getProperty("speed_print", "value") * 60 * .75) self.speed_travel = round(self.extruder[num].getProperty("speed_travel", "value") * 60) self.retract_dist = self.extruder[num].getProperty("retraction_amount", "value") self.retraction_enable = self.extruder[num].getProperty("retraction_enable", "value") self.retract_speed = self.extruder[num].getProperty("retraction_retract_speed", "value") * 60 - self.mm3_per_mm = (material_diameter / 2) ** 2 * math.pi + self.mm3_per_mm = (self.material_diameter / 2) ** 2 * math.pi # Don't add purge lines if 'T0' has offsets. t0_x_offset = self.extruder[0].getProperty("machine_nozzle_offset_x", "value") t0_y_offset = self.extruder[0].getProperty("machine_nozzle_offset_y", "value") if t0_x_offset or t0_y_offset: self.t0_has_offsets = True return num + + def _get_blob_code(self) -> str: + if not self.prime_blob_enable or self.prime_blob_distance == 0 or self.getSettingValueByKey("purge_line_location") not in ["front", "left"]: + return "" + # Set extruder speed for 1.75 filament + speed_blob = round(float(self.nozzle_size) * 500) + # Adjust speed if 2.85 filament + if self.material_diameter > 2: speed_blob *= .4 + blob_string = "G0 F1200 Z20 ; Move up\n" + blob_string += f"G1 F{speed_blob} E{self.prime_blob_distance} ; Blob\n" + blob_string += f"G1 F{self.retract_speed} E-{self.retract_dist} ; Retract\n" + blob_string += "G92 E0 ; Reset extruder\n" + blob_string += "M300 P500 S600 ; Beep\n" + blob_string += "G4 S2 ; Wait\n" + return blob_string From 5d0d782b8b6a0450b519ee9400caf33f67ebe06d Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:27:56 -0500 Subject: [PATCH 25/27] Update PurgeLinesAndUnload.py Indents for child settings --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index fa07ce1ae9..f120994c1f 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -161,7 +161,7 @@ class PurgeLinesAndUnload(Script): }, "adjust_e_loc_to": { - "label": "Starting E location", + "label": " Starting E location", "description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.", "type": "float", "unit": "mm ", @@ -178,7 +178,7 @@ class PurgeLinesAndUnload(Script): }, "unload_distance": { - "label": "Unload Distance", + "label": " Unload Distance", "description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.", "type": "int", "default_value": 440, @@ -187,7 +187,7 @@ class PurgeLinesAndUnload(Script): }, "unload_quick_purge": { - "label": "Quick purge before unload", + "label": " Quick purge before unload", "description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.", "type": "bool", "default_value": false, From cebdafc548d88df3b6628b6eb96e164108fd9ff7 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Wed, 12 Feb 2025 12:42:24 +0100 Subject: [PATCH 26/27] Make wipe move consistent --- plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index f120994c1f..5f517a3939 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -585,7 +585,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.speed_travel} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" + purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR elif purge_location == Location.FRONT: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int( @@ -604,7 +604,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.print_speed} X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" + purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n" self.end_purge_location = Position.LEFT_FRONT elif purge_location == Location.REAR: purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs( @@ -624,7 +624,7 @@ class PurgeLinesAndUnload(Script): purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n" # Wipe purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n" - purge_str += f"G0 F{self.print_speed} X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" + purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n" self.end_purge_location = Position.RIGHT_REAR # Elliptic printers with Origin at Center elif self.bed_shape == "elliptic": From 436e3e84b8d148df6226deb75d24e821c2851272 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Fri, 14 Feb 2025 12:43:08 -0500 Subject: [PATCH 27/27] Create PurgeLinesAndUnload_old.py Update PurgeLinesAndUnload.py Added 2 settings so the user can dictate where the prime blob will be. "Blob Location X" and "Blob Location Y". Delete PurgeLinesAndUnload_old.py I'm not sure how I do these things. --- .../scripts/PurgeLinesAndUnload.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py index 5f517a3939..de2b1c0f7a 100644 --- a/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py +++ b/plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py @@ -142,6 +142,24 @@ class PurgeLinesAndUnload(Script): "unit": "mm ", "enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']" }, + "prime_blob_loc_x": + { + "label": " Blob Location X", + "description": "The 'X' position to put the prime blob. 'Origin at Center' printers might require a negative value here. Keep in mind that purge lines always start in the left front, or the right rear. Pay attention or the nozzle can sit down into the prime blob.", + "type": "int", + "default_value": 0, + "unit": "mm ", + "enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']" + }, + "prime_blob_loc_y": + { + "label": " Blob location Y", + "description": "The 'Y' position to put the prime blob. 'Origin at Center' printers might require a negative value here. Keep in mind that purge lines always start in the left front, or the right rear. Pay attention or the nozzle can sit down into the prime blob.", + "type": "int", + "default_value": 0, + "unit": "mm ", + "enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']" + }, "move_to_start": { "label": "Circle around to layer start ⚠️​", @@ -922,7 +940,10 @@ class PurgeLinesAndUnload(Script): speed_blob = round(float(self.nozzle_size) * 500) # Adjust speed if 2.85 filament if self.material_diameter > 2: speed_blob *= .4 + blob_x = self.getSettingValueByKey("prime_blob_loc_x") + blob_y = self.getSettingValueByKey("prime_blob_loc_y") blob_string = "G0 F1200 Z20 ; Move up\n" + blob_string += f"G0 F{self.speed_travel} X{blob_x} Y{blob_y} ; Move to blob location\n" blob_string += f"G1 F{speed_blob} E{self.prime_blob_distance} ; Blob\n" blob_string += f"G1 F{self.retract_speed} E-{self.retract_dist} ; Retract\n" blob_string += "G92 E0 ; Reset extruder\n"