From da0fe19c962c5e9b22c1b53949ca1d05b3addae5 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:55:36 -0400 Subject: [PATCH 01/98] Update DisplayInfoOnLCD.py Bug fix for "remaining_time". Affected M118 and M73_time Update DisplayInfoOnLCD.py Add options for Time-to-pause to include Filament Change. Update DisplayInfoOnLCD.py Make M117 optional. Combine M73 R and M73 P lines into a single line. Add A and P parameters for M118 lines. Update DisplayInfoOnLCD A TouchUp. Update DisplayInfoOnLCD.py TouchUp Update DisplayInfoOnLCD.py TuneUp --- .../scripts/DisplayInfoOnLCD.py | 265 +++++++++++++----- .../scripts/DisplayInfoOnLCD.zip | Bin 0 -> 7911 bytes 2 files changed, 195 insertions(+), 70 deletions(-) create mode 100644 plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.zip diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 63c1c8c788..870a64834d 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -7,7 +7,8 @@ # Cura Post-Process plugin # Combines 'Display Filename and Layer on the LCD' with 'Display Progress' # Combined and with additions by: GregValiant (Greg Foresi) -# Date: September 8, 2023 +# Date: September 8, 2023 +# Date: March 31, 2024 - Bug fix for problem with adding M118 lines if 'Remaining Time' was not checked. # NOTE: This combined post processor will make 'Display Filename and Layer on the LCD' and 'Display Progress' obsolete # Description: Display Filename and Layer options: # Status messages sent to the printer... @@ -34,9 +35,19 @@ import time import datetime import math from UM.Message import Message +import re class DisplayInfoOnLCD(Script): + def initialize(self) -> None: + super().initialize() + try: + if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "all_at_once": + enable_countdown = True + self._instance.setProperty("enable_countdown", "value", enable_countdown) + except: + pass + def getSettingDataString(self): return """{ "name": "Display Info on LCD", @@ -77,7 +88,7 @@ class DisplayInfoOnLCD(Script): "label": "Initial layer number:", "description": "Choose which number you prefer for the initial layer, 0 or 1", "type": "int", - "default_value": 0, + "default_value": 1, "minimum_value": 0, "maximum_value": 1, "enabled": "display_option == 'filename_layer'" @@ -114,17 +125,40 @@ class DisplayInfoOnLCD(Script): "default_value": true, "enabled": "display_option == 'display_progress'" }, + "add_m117_line": + { + "label": "Add M117 Line", + "description": "M117 sends a message to the LCD screen. Some screen firmware will not accept or display messages.", + "type": "bool", + "default_value": true + }, "add_m118_line": { "label": "Add M118 Line", "description": "Adds M118 in addition to the M117. It will bounce the message back through the USB port to a computer print server (if a printer server like Octoprint or Pronterface is in use).", "type": "bool", - "default_value": false + "default_value": true + }, + "add_m118_a1": + { + "label": " Add A1 to M118 Line", + "description": "Adds A1 parameter. A1 adds a double foreslash '//' to the response. Octoprint may require this.", + "type": "bool", + "default_value": false, + "enabled": "add_m118_line" + }, + "add_m118_p0": + { + "label": " Add P0 to M118 Line", + "description": "Adds P0 parameter. P0 has the printer send the response out through all it's ports. Octoprint may require this.", + "type": "bool", + "default_value": false, + "enabled": "add_m118_line" }, "add_m73_line": { "label": "Add M73 Line(s)", - "description": "Adds M73 in addition to the M117. For some firmware this will set the printers time and or percentage.", + "description": "Adds M73 in addition to the M117. For some firmware this will set the printers time and or percentage. M75 is added to the beginning of the file and M77 is added to the end of the file. M73 will be added if one or both of the following options is chosen.", "type": "bool", "default_value": false, "enabled": "display_option == 'display_progress'" @@ -132,7 +166,7 @@ class DisplayInfoOnLCD(Script): "add_m73_percent": { "label": " Add M73 Percentage", - "description": "Adds M73 with the P parameter. For some firmware this will set the printers 'percentage' of layers completed and it will count upward.", + "description": "Adds M73 with the P parameter to the start of each layer. For some firmware this will set the printers 'percentage' of layers completed and it will count upward.", "type": "bool", "default_value": false, "enabled": "add_m73_line and display_option == 'display_progress'" @@ -140,10 +174,10 @@ class DisplayInfoOnLCD(Script): "add_m73_time": { "label": " Add M73 Time", - "description": "Adds M73 with the R parameter. For some firmware this will set the printers 'print time' and it will count downward.", + "description": "Adds M73 with the R parameter to the start of each layer. For some firmware this will set the printers 'print time' and it will count downward.", "type": "bool", "default_value": false, - "enabled": "add_m73_line and display_option == 'display_progress'" + "enabled": "add_m73_line and display_option == 'display_progress' and display_remaining_time" }, "speed_factor": { @@ -154,13 +188,29 @@ class DisplayInfoOnLCD(Script): "default_value": 100, "enabled": "enable_end_message or display_option == 'display_progress'" }, + "enable_countdown": + { + "label": "Enable Countdown to Pauses", + "description": "If print sequence is 'one_at_a_time' this is false. This setting is always hidden.", + "type": "bool", + "value": false, + "enabled": false + }, "countdown_to_pause": { "label": "Countdown to Pauses", - "description": "Instead of the remaining print time the LCD will show the estimated time to pause (TP).", + "description": "This must run AFTER any script that adds a pause. Instead of the remaining print time the LCD will show the estimated time to the next layer that has a pause (TP).", "type": "bool", "default_value": false, - "enabled": "display_option == 'display_progress'" + "enabled": "display_option == 'display_progress' and enable_countdown and display_remaining_time" + }, + "pause_cmd": + { + "label": " What pause command(s) are used?", + "description": "This might be M0, or M25 or M600 if Filament Change is used. If you have mixed commands then delimit them with a comma ',' (Ex: M0,M600). Spaces are not allowed.", + "type": "str", + "default_value": "M0", + "enabled": "countdown_to_pause and enable_countdown and display_remaining_time" }, "enable_end_message": { @@ -173,40 +223,57 @@ class DisplayInfoOnLCD(Script): "print_start_time": { "label": "Print Start Time (Ex 16:45)", - "description": "Use 'Military' time. 16:45 would be 4:45PM. 09:30 would be 9:30AM. If you leave this blank it will be assumed that the print will start Now. If you enter a guesstimate of your printer start time and that time is before 'Now' the guesstimate will consider that the print will start tomorrow at the entered time. ", + "description": "Use 'Military' time. 16:45 would be 4:45PM. 09:30 would be 9:30AM. If you leave this blank it will be assumed that the print will start Now. If you enter a guesstimate of your printer start time and that time is before 'Now' then the guesstimate will consider that the print will start tomorrow at the entered time. ", "type": "str", "default_value": "", "unit": "hrs ", "enabled": "enable_end_message" } - } }""" def execute(self, data): display_option = self.getSettingValueByKey("display_option") + add_m117_line = self.getSettingValueByKey("add_m117_line") add_m118_line = self.getSettingValueByKey("add_m118_line") + add_m118_a1 = self.getSettingValueByKey("add_m118_a1") + add_m118_p0 = self.getSettingValueByKey("add_m118_p0") + m118_text = "M118 " + m118_str = "M118 " + if add_m118_line: + if add_m118_a1 and not add_m118_p0: + m118_str = "M118 A1 " + if add_m118_p0 and not add_m118_a1: + m118_str = "M118 P0 " + if add_m118_p0 and add_m118_a1: + m118_str = "M118 A1 P0 " add_m73_line = self.getSettingValueByKey("add_m73_line") add_m73_time = self.getSettingValueByKey("add_m73_time") add_m73_percent = self.getSettingValueByKey("add_m73_percent") + m73_str = "" # This is Display Filename and Layer on LCD--------------------------------------------------------- if display_option == "filename_layer": max_layer = 0 lcd_text = "M117 " + octo_text = "M118 " if self.getSettingValueByKey("file_name") != "": file_name = self.getSettingValueByKey("file_name") else: file_name = Application.getInstance().getPrintInformation().jobName if self.getSettingValueByKey("addPrefixPrinting"): lcd_text += "Printing " + octo_text += "Printing " if not self.getSettingValueByKey("scroll"): lcd_text += "Layer " + octo_text += "Layer " else: lcd_text += file_name + " - Layer " + octo_text += file_name + " - Layer " i = self.getSettingValueByKey("startNum") for layer in data: display_text = lcd_text + str(i) + m118_text = octo_text + str(i) layer_index = data.index(layer) lines = layer.split("\n") for line in lines: @@ -217,18 +284,29 @@ class DisplayInfoOnLCD(Script): max_layer = str(int(max_layer) - 1) if line.startswith(";LAYER:"): if self.getSettingValueByKey("maxlayer"): - display_text = display_text + " of " + max_layer + display_text += " of " + max_layer + m118_text += " of " + max_layer if not self.getSettingValueByKey("scroll"): - display_text = display_text + " " + file_name + display_text += " " + file_name + m118_text += " " + file_name else: if not self.getSettingValueByKey("scroll"): - display_text = display_text + " " + file_name + "!" + display_text += " " + file_name + "!" + m118_text += " " + file_name + "!" else: - display_text = display_text + "!" + display_text += "!" + m118_text += "!" line_index = lines.index(line) - lines.insert(line_index + 1, display_text) + if add_m117_line: + lines.insert(line_index + 1, display_text) if add_m118_line: - lines.insert(line_index + 2, str(display_text.replace("M117", "M118", 1))) + if add_m118_a1 and not add_m118_p0: + m118_str = m118_text.replace("M118 ","M118 A1 ") + if add_m118_p0 and not add_m118_a1: + m118_str = m118_text.replace("M118 ","M118 P0 ") + if add_m118_p0 and add_m118_a1: + m118_str = m118_text.replace("M118 ","M118 A1 P0 ") + lines.insert(line_index + 2, m118_str) i += 1 final_lines = "\n".join(lines) data[layer_index] = final_lines @@ -239,7 +317,13 @@ class DisplayInfoOnLCD(Script): # Display Progress (from 'Show Progress' and 'Display Progress on LCD')--------------------------------------- elif display_option == "display_progress": - # get settings + print_sequence = Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") + ## Add the Initial Layer Height just below Layer Height in data[0] + init_layer_hgt_line = ";Initial Layer Height: " + str(Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")) + nozzle_size_line = ";Nozzle Size T0: " + str(Application.getInstance().getGlobalContainerStack().extruderList[0].getProperty("machine_nozzle_size", "value")) + match = re.search(";Layer height: (\d\.\d*)", data[0])[0] + data[0] = re.sub(match, match + "\n" + init_layer_hgt_line + "\n" + nozzle_size_line, data[0]) + ## Get settings display_total_layers = self.getSettingValueByKey("display_total_layers") display_remaining_time = self.getSettingValueByKey("display_remaining_time") speed_factor = self.getSettingValueByKey("speed_factor") / 100 @@ -249,12 +333,16 @@ class DisplayInfoOnLCD(Script): m73_time = True if add_m73_line and add_m73_percent: m73_percent = True - # initialize global variables + if add_m73_line: + data[1] = "M75\n" + data[1] + data[len(data)-1] += "M77\n" + ## Initialize some variables first_layer_index = 0 - time_total = 0 + time_total = int(data[0].split(";TIME:")[1].split("\n")[0]) number_of_layers = 0 time_elapsed = 0 - # if at least one of the settings is disabled, there is enough room on the display to display "layer" + + ## If at least one of the settings is disabled, there is enough room on the display to display "layer" first_section = data[0] lines = first_section.split("\n") for line in lines: @@ -268,28 +356,40 @@ class DisplayInfoOnLCD(Script): orig_hhh = cura_time/3600 orig_hr = round(orig_hhh // 1) orig_mmm = math.floor((orig_hhh % 1) * 60) - orig_sec = round((((orig_hhh % 1) * 60) % 1) * 60) - if add_m118_line: lines.insert(tindex + 3,"M118 Adjusted Print Time " + str(hr) + "hr " + str(mmm) + "min") - lines.insert(tindex + 3,"M117 ET " + str(hr) + "hr " + str(mmm) + "min") - # add M73 line at beginning + if add_m118_line: lines.insert(tindex + 5, m118_str + "Adjusted Print Time " + str(hr) + "hr " + str(mmm) + "min") + if add_m117_line: lines.insert(tindex + 5,"M117 ET " + str(hr) + "hr " + str(mmm) + "min") + ## Add M73 line at beginning mins = int(60 * hr + mmm) - if m73_time: - lines.insert(tindex + 3, "M73 R{}".format(mins)) - if m73_percent: - lines.insert(tindex + 3, "M73 P0") - # If Countdonw to pause is enabled then count the pauses + if add_m73_line and (add_m73_time or add_m73_percent): + if m73_time: + m73_str += " R{}".format(mins) + if m73_percent: + m73_str += " P0" + lines.insert(tindex + 4, "M73" + m73_str) + # If Countdown to pause is enabled then count the pauses pause_str = "" if bool(self.getSettingValueByKey("countdown_to_pause")): pause_count = 0 - for num in range(2,len(data) - 1, 1): - if "PauseAtHeight.py" in data[num]: - pause_count += 1 - pause_str = f" with {pause_count} pause(s)" - # This line goes in to convert seconds to hours and minutes - lines.insert(tindex + 3, f";Cura Time Estimate: {cura_time}sec = {orig_hr}hr {orig_mmm}min {orig_sec}sec {pause_str}") + pause_setting = self.getSettingValueByKey("pause_cmd").upper() + pause_cmd = [] + if "," in pause_setting: + pause_cmd = pause_setting.split(",") + else: + pause_cmd.append(pause_setting) + for q in range(0, len(pause_cmd)): + pause_cmd[q] = "\n" + pause_cmd[q] + for num in range(2,len(data) - 2, 1): + for q in range(0,len(pause_cmd)): + if pause_cmd[q] in data[num]: + pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) + pause_str = f" with {pause_count} pause(s)" + ## This line goes in to convert seconds to hours and minutes + lines.insert(tindex + 5, f";Cura Time Estimate: {orig_hr}hr {orig_mmm}min {pause_str}") data[0] = "\n".join(lines) - data[len(data)-1] += "M117 Orig Cura Est " + str(orig_hr) + "hr " + str(orig_mmm) + "min\n" - if add_m118_line: data[len(data)-1] += "M118 Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n" + if add_m117_line: + data[len(data)-1] += "M117 Orig Cura Est " + str(orig_hr) + "hr " + str(orig_mmm) + "min\n" + if add_m118_line: + data[len(data)-1] += m118_str + " Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n" if not display_total_layers or not display_remaining_time: base_display_text = "layer " else: @@ -308,6 +408,11 @@ class DisplayInfoOnLCD(Script): for line in data_section.split("\n"): if line.startswith(";LAYER_COUNT:"): number_of_layers = int(line.split(":")[1]) + if print_sequence == "one_at_a_time": + number_of_layers = 1 + for lay in range(2,len(data)-1,1): + if ";LAYER:" in data[lay]: + number_of_layers += 1 elif line.startswith(";TIME:"): time_total = int(line.split(":")[1]) # for all layers... @@ -352,15 +457,26 @@ class DisplayInfoOnLCD(Script): # insert the text AFTER the first line of the layer (in case other scripts use ";LAYER:") for l_index, line in enumerate(lines): if line.startswith(";LAYER:"): - lines[l_index] += "\nM117 " + display_text - # add M73 line - mins = int(60 * h + m) - if m73_time: - lines[l_index] += "\nM73 R{}".format(mins) - if m73_percent: - lines[l_index] += "\nM73 P" + str(round(int(current_layer) / int(number_of_layers) * 100)) + if add_m117_line: + lines[l_index] += "\nM117 " + display_text if add_m118_line: - lines[l_index] += "\nM118 " + display_text + a1_str = "" + p0_str = "" + if add_m118_a1: + a1_str = "A1 " + if add_m118_p0: + p0_str = "P0 " + lines[l_index] += "\nM118 " + a1_str + p0_str + display_text + # add M73 line + if display_remaining_time: + mins = int(60 * h + m) + if add_m73_line and (add_m73_time or add_m73_percent): + m73_str = "" + if m73_time and display_remaining_time: + m73_str += " R{}".format(mins) + if m73_percent: + m73_str += " P" + str(round(int(current_layer) / int(number_of_layers) * 100)) + lines[l_index] += "\nM73" + m73_str break # overwrite the layer with the modified layer data[layer_index] = "\n".join(lines) @@ -381,34 +497,29 @@ class DisplayInfoOnLCD(Script): if line.startswith(";TIME_ELAPSED:"): this_time = (float(line.split(":")[1]))*speed_factor time_list.append(str(this_time)) - if "PauseAtHeight.py" in layer: - for qnum in range(num - 1, pause_index, -1): - time_list[qnum] = str(float(this_time) - float(time_list[qnum])) + "P" - pause_index = num-1 + for p_cmd in pause_cmd: + if p_cmd in layer: + for qnum in range(num - 1, pause_index, -1): + time_list[qnum] = str(float(this_time) - float(time_list[qnum])) + "P" + pause_index = num-1 + break # Make the adjustments to the M117 (and M118) lines that are prior to a pause for num in range (2, len(data) - 1,1): layer = data[num] lines = layer.split("\n") for line in lines: - if line.startswith("M117") and "|" in line and "P" in time_list[num]: - M117_line = line.split("|")[0] + "| TP " - alt_time = time_list[num][:-1] - hhh = int(float(alt_time) / 3600) - if hhh > 0: - hhr = str(hhh) + "h" - else: - hhr = "" - mmm = ((float(alt_time) / 3600) - (int(float(alt_time) / 3600))) * 60 - sss = int((mmm - int(mmm)) * 60) - mmm = str(round(mmm)) + "m" - time_to_go = str(hhr) + str(mmm) - if hhr == "": time_to_go = time_to_go + str(sss) + "s" - M117_line = M117_line + time_to_go - layer = layer.replace(line, M117_line) - if line.startswith("M118") and "|" in line and "P" in time_list[num]: - M118_line = line.split("|")[0] + "| TP " + time_to_go - layer = layer.replace(line, M118_line) + try: + if line.startswith("M117") and "|" in line and "P" in time_list[num]: + time_to_go = self.get_time_to_go(time_list[num]) + M117_line = line.split("|")[0] + "| TP " + time_to_go + layer = layer.replace(line, M117_line) + if line.startswith("M118") and "|" in line and "P" in time_list[num]: + time_to_go = self.get_time_to_go(time_list[num]) + M118_line = line.split("|")[0] + "| TP " + time_to_go + layer = layer.replace(line, M118_line) + except: + continue data[num] = layer setting_data = "" if bool(self.getSettingValueByKey("enable_end_message")): @@ -476,8 +587,22 @@ class DisplayInfoOnLCD(Script): if print_start_time != "": print_start_str = "Print Start Time................." + str(print_start_time) + "hrs" else: - print_start_str = "Print Start Time.................Now." + print_start_str = "Print Start Time.................Now" estimate_str = "Cura Time Estimate.........." + str(print_time) adjusted_str = "Adjusted Time Estimate..." + str(time_change) finish_str = week_day + " " + str(mo_str) + " " + str(new_time.strftime("%d")) + ", " + str(new_time.strftime("%Y")) + " at " + str(show_hr) + str(new_time.strftime("%M")) + str(show_ampm) - return finish_str, estimate_str, adjusted_str, print_start_str \ No newline at end of file + return finish_str, estimate_str, adjusted_str, print_start_str + + def get_time_to_go(self, time_str: str): + alt_time = time_str[:-1] + hhh = int(float(alt_time) / 3600) + if hhh > 0: + hhr = str(hhh) + "h" + else: + hhr = "" + mmm = ((float(alt_time) / 3600) - (int(float(alt_time) / 3600))) * 60 + sss = int((mmm - int(mmm)) * 60) + mmm = str(round(mmm)) + "m" + time_to_go = str(hhr) + str(mmm) + if hhr == "": time_to_go = time_to_go + str(sss) + "s" + return time_to_go \ No newline at end of file diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.zip b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.zip new file mode 100644 index 0000000000000000000000000000000000000000..fd9e5d90ae894ed7d61fce36a2fb771d58f0c026 GIT binary patch literal 7911 zcmV%RhXHMv{m(GgEEDBk= z;OynoXY6Xljsp1NFqgBZPLg{5BEkws*>#qrZ0BIWc1Gia&p&&>P9xWwd)%F{@1taq zIqWQ2@~xNr#!IlptLQxst^oXaFK?r;IE$m}m?uP`@su>h+Rb9dne@KfSrWibp#ON)C>9V2=x2RU$NaW_52m~ ze7=Zo@@{XSBgb`?DRGiT`%>b5Xm)RFJgZEZ^!qXFl9Y_V9z6H z?Dc6BPRb{YopYc$i3Lg0&aUwd_%~ZfrtLgtTkPBHn$5jiHjiSqjH4@`2YJ8YH9H-T z513EG^X9Dkh6j!p;)e?_;9X*J7^T1z9N)OeOaJL~{y2VLEG zzRv>Z9dAuEd|Ra%i>{K$=P9Q#@?;i!OQaU`-vpQHBok~T%6aOfS;7K<>7bw`90;67 z)crE{LJ-g4aG2M}vc=A4ape0*jo$gwH?Lp5oW6Yh>c#Z=$;;zcN2kZn-Ez=8A@~5O$B@e;;0ig3~)dy3VbWwqqt7kw<}O2b0_mtXebDq zC7dv)VPEr*#}3Xym~SMW3#|o&-Ko`NJB@j5% zDq~MU6|h%XFkw#@Q3O5SEIg2{FeA~!@hxLMhd)uY5F3!2)N_31BM$~_#Nhq74EEHy z6-=72V9zE3RnDvU~@ARU9xHu`?9rp)ay0$Q6jZ$S3%nk0Ki>a}{lShe*v?|S8(4`8J)Jo)Q}fjg`vZNu zzilX8s5L{Dy3tLj6jI1pA>S zt3@C=RTiv(zGs5rQa7?IXZ8*;ae&4FtlM(70o~st=@)U7T`#D`kLTY4@?i*i%%;8s zUi-T_S}-EgQZ_vw`YRF!P(?K3<)lDN$RYq@Agr^V6tX^v7`}SbBiV?R;8%12O?Fs` zp2O#4u_acvQ#sr%&}%$YT71A#ShNJI4AIA~W{yA0e2|hvw9r#aP?Coz%qOV?0Plf> zGvX~(txPyETBJ!_P|T9tq<(Q=YKqV-Ng{){IKj)SAaLRH&&V7b4$o<|WL~h00G4=? zAAdX@9xazX&Jduos$#s#>-}#!dIFlXWU zQ%_QKyn-gl@1JY@Y@-fnI)u_z>eaZoB=TGbDrV~2&SlWo}xY$tT2GYJk8>e zbvm67MK~Rl-Ohw{lsqH1rNZqDN_F4yl~Gm8MWtTAQ*cmH2P%K~SpEpgECIH{x1I8* zM2sUT^Q43`)s+j>RH!Q(7cH%*CCp*&<2cZHlVFsJ9uCWc(aqf42sY_uv4YWd2NedE!^8_fiN$Kg6KGb~ zg=W+g(Ije!&v<*hrxxaT=OD26FFZ`NSmvaV=fVOi7)-+8P?kYGKho8b1eb$hTw6&l zW+;gE8dOWk(o!O7@>d~W*98HXOWsb&a>nLWZg=Y2m*K17j3q!i$3&v`0*TO)7}>LU9#EegjrT zs?K3~=XI4OUMBGc6>%Zy78&1d%?@Zb6`J=Bthhik6(+F6XVRAZhC_?9ZI;C`TIOmD z+DFK3|6J(|rMr=0-()KS0uku)Sotk;Q{t6|NFrDZ7O1B<(0D!Hk~D73woYx$KZR#x z7=l}VCzhR-#JuU+q?>{2jf|5Bnmu6st29c8lIiTc)XQtO51 zVtugbA5LWjDaDYLXj?I(v8*Mul2rmE1$84b%QYG)E!_F85k^b8%JM`*R++dC8j$z9bXh!15;YhzC6u*eh*+I#UrgCg5_BW@IgK8K@2Je&%J3MmJPLUMJEuw$TQPooLR7A}%)H;iTWrl`rA=ujU z<|-fvPx7Vjz2odPIIMD!j?p*73gE?ZbYTF$EaCnC0uDLjJMjmfkVi*jB+lJ91#Op3 z3=0_U>@j?#g}`BMl*!mBPkbj?uB>%ryv%h;!H72)L zdP#pHo?yFrJ@y=w3cAesiePNf^3>#AB9?A+wlTv%Zvf&h)BY3Mv~V$*Lw0b*uf33# ze!2gKcBb6P*{{?_u`ucp%w1y-5vziVjBsx*2CbVeboB@~6saG%k3?A};J=1{lZgFQVMM%)`=!Ua2)Ex$GAcC4qz?f z-BBQ7TcO-xN6$z_jzwY!!&sPtTFo(08}J4Csoi)=X}}~7l^tLpGpBq566W;+JNF}} zJ_MSDUW&Dv*j5UXj%uTyhD6I9U|49U%2Uamy-%XED*MlOk>6vH!SU40lo81_ZN(Ub zN4ZvFFP^pw!E(S2cxNgUPAc;(NFOM+t3VQQ{Ki=&Y~g|BabG)PL)}BwT9RDTdYVR4 z$|JfH(eFWRB3yt|E6zf8^!(!Z4NWpp3Fz#k${!>AaCV1D${lU*m10CPKqR9Vd9EN? ziCQ0+Q6x&DqBTf#w{UcCtaowtsomq2Y;0u)l1?knn@_Ne(BP-Dz`c_=(Bn@?N2id6)ZM;E`palAT2j)*W<3#vpWw{q!*cwsdAP^_)Up=s55e7RE+< zgr}q0x`uF6C+?pm3RS&-Cz&{I$gFEC3opQ2$YYe0T8G?Ckh>0>!;Dz4#P??02(Ve^ zzAJJ)l@dTX%~xWP5-Qf?^_<;k;T_f#t*=+U6TZ`S zJ^JxUk_D)}OFSb+i!Ai$CI_YRB(F0N8ELacbCIo~14=r3Qrd@(e8T=Y#{O6rfOdtx zz&=fA(%}db&sD336|FQ1qBsUglobdwxeNimYvpVs4YJlWT!8yj-)E!EXVV(RSH|PV zPN!pLUh-Q$%TkecIlyV?m>GZN^-km}=E;Kp#_@f-`hl-{qG?Jq%~u5`r_fwqK4~wqK4a?Ue(%9)OB2S|HGDXa#|EkOhJa%(MfdB2@+)d<`=I z+UN50K&Lt@SF<)z_}G40Pj)plRtB(l$UfU_uop>DgwvR ztZ-1S0QfYJGEdg(voDZnmUThbY37Onst5zKqt<|JOo^st2NQBJ%@1n8+h3zAJjb-j zNS#WmM&E*zCikO5x!$3-U{Pf`-J5CxN@)Hypp{VS;d)$zl&(d@TCIiwCj6WkkH9G3 zV(q$X)yiuY8=ZPqk{ZnX5_7@}X#uc|KqrO>`OgOgYiZo``qdVs{$zDyD7EzogQ*v~ z{1#du8bf;M(Fa=tI)HO11C@ZDm-afpgyqp;p%J3ag#l7C%LuW65aH(`Q7S>#qu2TR z<CRQ|M==+VwE0MOx=QD+SSy5t06!qm*cnfjhiQ5&ScbvqXu-bn1~Q#!nCK~ z;jzQ9g_>CEDb-A)X5@8dT@wf_1#!gLh$dtQq)Q{eX53mh@6h;Os-|43BSB2`bYBmk zG?bRDYZR4P{>ovZR{NqMwOy5_!M1_XV%S(J*sg<%rHETo3!FJx(~&+>st*3HnQ&Mz zoy0Vc*Nj9#PI#R5j6NT+@t`ooR$U&n>C&tYHZ~f46qL!$;TF`Vn!>cyG#$nq&;>h1 zT3!S3@6njq^u1Y3l)!ASpyZ4{ElOvjyP))^p#v0Zfl4-lEd`B?P+S@@0G( z%P-~kJ2mfj-}Z+XmFt!8mM8v@FS(Of-bZ9TIu_b3Trg5|C4@Q>)?er6$&#bPeY?X~ zj=z^3D;bbe`$lVApnIJPEq46i0qxaeXh|L15N7*#oI>ZccX`EqAX?$Qw8DYAC3y!R z!akWUu2U66=zMJhKB2V{O}cxJR`y6>rz2yurQwFr@4v%bOT6ECj@v7G${9-(8i&w6o@k-QNNb2=|aeo>9;y&zm z28tQ|3Pa=v2`Ibj5!`_UujiSKZ~9}IYI%1RVZuneI@?-8L@nk<+J3VyXlqfHU;7qC zJGiB7jS^&2VXpC6EvmJ|%RCzPyt>I+jC5HlsI}Nk3o%1yw#6dvv3j1n37$lx24s%r zTBtS$9i?XcmSR);PlRmAcXi7w4|^2u-NMC!+U-NTirAg#^Y$VS=)DtrxT=<9aPGy4 z5_4tSmRN;E6A}mC(AAWpt~}qbFHTO6h5Kvz^VP{9POnX)c`oa=uDI_kF~5VpR>w8R zz`xI(Bo!%$V&_`TFpOe?G*j4pfL~%R4wzC>YaGFXC(kuXnK#Z-GNCj(HVP%;x(ylQ zsOS`BD-|l&M>pC;Hmtt7Bz29knQrKv%(B=~a{?JF`ZMDhH4Vj5DqH4I9u`!Dz3F0s zDm(|Uy}MV|4Z137nK;Tqw}-8^w`=1XK@jMwRQF4$3h3|IX!IlU;H% z1gJV*QC^^#sw5I8jI;c-$(!HAL*ElSG?5~cZ%f(gv~U|O8CX`gQ5Bh)Lnv03qH0Bz zlGbSX#`VYZ~JZ`Q$u$^i4SIdnzHZFg^;c#NiG3t#546Pasu90b2bj{1($aE5Rlliu}Z!kpi-ggGtqyjg7X8>w> zP4^Y_H^UwaaI`Q819)n zqr94`A*m&`;sB_!K_Ivmw*$iHW}7mxWg@o5q~H@iq!1e7^d;Sc)XE&F;4XJCtllwB zUgKNQU$qSG$^mAVc0q-e-*OMw3ZT@~ms@ca3KpwX39NQObqx6HV{!KC#Vm4pM}`|< zO|JO(R$f_K=M5jQbBdbiJ457cBKi3qmDMktJu-(VM3^5lLC8f^5SW;dS|Mu0Y?=^1 zl)WhtG&V8)iHk&24hmx>?vS~`>|%845|Px#Ww-jM8hBok6_mbtD{@g8nBB`N8n~pY zolm8G6--|D}H=?f`W=0 zDPhw)Xw8{1a*4Fcs|HacLq(o!)e0%tXkclXACIcft;ipEEEl*|>kGNJuiw{o;LKd{hT&1W!`%qL~bt_U<^!jBm9GAzrc_}9FTE6PGrV)Yo+H zp6HRrj_2d}e&=ySGoRyqoK9RL8LHG{(rF3|H4i{q?KSP!#0>5Ox~u{0unKFQCy%A* z4F!E*K%aLLIWO| zXDrZ?B=-a^bFd9D5-hdW$@KW;(b@U&v!a#qfMrXUC}&b!c^a>d|L>x`ZC)+~kV3W4 zxm}yp(_opZQZmd@`6i@xP^BPu#mPy-sqpfBCMhw%l9_`yrr-{fJYY#D1AEK*8T9?v5Rf>3~hwkFr=RxBF?9k4j#HKAWs zmZO#xO$1tHX5}bgYFbmx_8qrMn#+fT>0WnZ%>l7S4&jjk*_K&ikx`p2jZEFh>`|q% zM}vC{&Hm#j-J!u!JoM63)7Z~xIPcklv3K3>!Lreo50>J#k1~zLRwWX?=-aSu;zq@C zmf^cK$l0Z^uxC^cprU&565*TJlPj)JuKKc*K>kiwo%2-jc&s5?v%8adX?sc>a>mZU z5(f+5;*6AZm*UA6@&JCf*!3seiK3N_+)Dcfkp8ZASip3%@TYAxaB9@1y=AvcM8=gP zJ}y%3g}gIHun2)z9Edf0E47x2w2|^XjGAg*>!TxB+9X-1vZ1wOD<*Xt?30Ju%OfRC3m6`%mv1!b1yff)PB`j!5024HnuKE3h9Q$WS#ztBu8K7EOdpgpE@i zsUYZ^(*nAYuZq?CSLaN|75HF=u5fSrXv!qBYuCV^G8SZBc9Lb zIp=-LZB!XQnO;XM-_)PxZ+k_EEqsepDMJZe?^KRCIRXh{SP}@b= z!65H)y;WhO>A^pNrU!pLO%K-5)cRXJKY7DzKkL)s(@`Gpa)Ybuehqi^yfnFF74*n>bZq_K~z&M zC?SY()G4gNN}62xqkcp6M{fn<5kz?CWHkVS4v~vO_VXhnUKZu`ojT z;hy{(YS}vzduR%X{K-}G=E=O7`VG}-L0cEGsLO+Y@GbfYOr*?e*8o(a?iuf^_wYw7 z7mG;{Ow1qtgUUIxELc$bdR2{U!q^bs!q6IleaK49@qI-L2w)gN+8Y(Cmiesul|3LN z%5^un{eo9mO}B%(#j9+nzvYoN9d4RaQU3ir_T*pZ4j44}_uEV4gBp&hX>LH%ytT0)06TS2i@0YX9z@DYB(&u( z%M&L2Dpr5fFQVfgM*zwfZ|KAKS;)n|K0P00*C3UIzWRZ6e89`*<)5#j_xklSJ`;b} z4-u8=Rx$mBTyfJ4r=+%>$BS4C-FQbn^QQL(ei%3T~@EfQ-7Rp_VIdMkHM zpJf#PRdllkRkJp_vvg6NvOv3KGpwub5~-j7aiTZQM5R(n zN=|e=PAwVOphTlssz>%kRAX4003jj3s3+6 literal 0 HcmV?d00001 From 9fe36482f7978e87288d0994711412dc1be0473a Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 7 Apr 2024 09:03:46 -0400 Subject: [PATCH 02/98] Delete DisplayInfoOnLCD.zip Should not have been included. --- .../scripts/DisplayInfoOnLCD.zip | Bin 7911 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.zip diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.zip b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.zip deleted file mode 100644 index fd9e5d90ae894ed7d61fce36a2fb771d58f0c026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7911 zcmV%RhXHMv{m(GgEEDBk= z;OynoXY6Xljsp1NFqgBZPLg{5BEkws*>#qrZ0BIWc1Gia&p&&>P9xWwd)%F{@1taq zIqWQ2@~xNr#!IlptLQxst^oXaFK?r;IE$m}m?uP`@su>h+Rb9dne@KfSrWibp#ON)C>9V2=x2RU$NaW_52m~ ze7=Zo@@{XSBgb`?DRGiT`%>b5Xm)RFJgZEZ^!qXFl9Y_V9z6H z?Dc6BPRb{YopYc$i3Lg0&aUwd_%~ZfrtLgtTkPBHn$5jiHjiSqjH4@`2YJ8YH9H-T z513EG^X9Dkh6j!p;)e?_;9X*J7^T1z9N)OeOaJL~{y2VLEG zzRv>Z9dAuEd|Ra%i>{K$=P9Q#@?;i!OQaU`-vpQHBok~T%6aOfS;7K<>7bw`90;67 z)crE{LJ-g4aG2M}vc=A4ape0*jo$gwH?Lp5oW6Yh>c#Z=$;;zcN2kZn-Ez=8A@~5O$B@e;;0ig3~)dy3VbWwqqt7kw<}O2b0_mtXebDq zC7dv)VPEr*#}3Xym~SMW3#|o&-Ko`NJB@j5% zDq~MU6|h%XFkw#@Q3O5SEIg2{FeA~!@hxLMhd)uY5F3!2)N_31BM$~_#Nhq74EEHy z6-=72V9zE3RnDvU~@ARU9xHu`?9rp)ay0$Q6jZ$S3%nk0Ki>a}{lShe*v?|S8(4`8J)Jo)Q}fjg`vZNu zzilX8s5L{Dy3tLj6jI1pA>S zt3@C=RTiv(zGs5rQa7?IXZ8*;ae&4FtlM(70o~st=@)U7T`#D`kLTY4@?i*i%%;8s zUi-T_S}-EgQZ_vw`YRF!P(?K3<)lDN$RYq@Agr^V6tX^v7`}SbBiV?R;8%12O?Fs` zp2O#4u_acvQ#sr%&}%$YT71A#ShNJI4AIA~W{yA0e2|hvw9r#aP?Coz%qOV?0Plf> zGvX~(txPyETBJ!_P|T9tq<(Q=YKqV-Ng{){IKj)SAaLRH&&V7b4$o<|WL~h00G4=? zAAdX@9xazX&Jduos$#s#>-}#!dIFlXWU zQ%_QKyn-gl@1JY@Y@-fnI)u_z>eaZoB=TGbDrV~2&SlWo}xY$tT2GYJk8>e zbvm67MK~Rl-Ohw{lsqH1rNZqDN_F4yl~Gm8MWtTAQ*cmH2P%K~SpEpgECIH{x1I8* zM2sUT^Q43`)s+j>RH!Q(7cH%*CCp*&<2cZHlVFsJ9uCWc(aqf42sY_uv4YWd2NedE!^8_fiN$Kg6KGb~ zg=W+g(Ije!&v<*hrxxaT=OD26FFZ`NSmvaV=fVOi7)-+8P?kYGKho8b1eb$hTw6&l zW+;gE8dOWk(o!O7@>d~W*98HXOWsb&a>nLWZg=Y2m*K17j3q!i$3&v`0*TO)7}>LU9#EegjrT zs?K3~=XI4OUMBGc6>%Zy78&1d%?@Zb6`J=Bthhik6(+F6XVRAZhC_?9ZI;C`TIOmD z+DFK3|6J(|rMr=0-()KS0uku)Sotk;Q{t6|NFrDZ7O1B<(0D!Hk~D73woYx$KZR#x z7=l}VCzhR-#JuU+q?>{2jf|5Bnmu6st29c8lIiTc)XQtO51 zVtugbA5LWjDaDYLXj?I(v8*Mul2rmE1$84b%QYG)E!_F85k^b8%JM`*R++dC8j$z9bXh!15;YhzC6u*eh*+I#UrgCg5_BW@IgK8K@2Je&%J3MmJPLUMJEuw$TQPooLR7A}%)H;iTWrl`rA=ujU z<|-fvPx7Vjz2odPIIMD!j?p*73gE?ZbYTF$EaCnC0uDLjJMjmfkVi*jB+lJ91#Op3 z3=0_U>@j?#g}`BMl*!mBPkbj?uB>%ryv%h;!H72)L zdP#pHo?yFrJ@y=w3cAesiePNf^3>#AB9?A+wlTv%Zvf&h)BY3Mv~V$*Lw0b*uf33# ze!2gKcBb6P*{{?_u`ucp%w1y-5vziVjBsx*2CbVeboB@~6saG%k3?A};J=1{lZgFQVMM%)`=!Ua2)Ex$GAcC4qz?f z-BBQ7TcO-xN6$z_jzwY!!&sPtTFo(08}J4Csoi)=X}}~7l^tLpGpBq566W;+JNF}} zJ_MSDUW&Dv*j5UXj%uTyhD6I9U|49U%2Uamy-%XED*MlOk>6vH!SU40lo81_ZN(Ub zN4ZvFFP^pw!E(S2cxNgUPAc;(NFOM+t3VQQ{Ki=&Y~g|BabG)PL)}BwT9RDTdYVR4 z$|JfH(eFWRB3yt|E6zf8^!(!Z4NWpp3Fz#k${!>AaCV1D${lU*m10CPKqR9Vd9EN? ziCQ0+Q6x&DqBTf#w{UcCtaowtsomq2Y;0u)l1?knn@_Ne(BP-Dz`c_=(Bn@?N2id6)ZM;E`palAT2j)*W<3#vpWw{q!*cwsdAP^_)Up=s55e7RE+< zgr}q0x`uF6C+?pm3RS&-Cz&{I$gFEC3opQ2$YYe0T8G?Ckh>0>!;Dz4#P??02(Ve^ zzAJJ)l@dTX%~xWP5-Qf?^_<;k;T_f#t*=+U6TZ`S zJ^JxUk_D)}OFSb+i!Ai$CI_YRB(F0N8ELacbCIo~14=r3Qrd@(e8T=Y#{O6rfOdtx zz&=fA(%}db&sD336|FQ1qBsUglobdwxeNimYvpVs4YJlWT!8yj-)E!EXVV(RSH|PV zPN!pLUh-Q$%TkecIlyV?m>GZN^-km}=E;Kp#_@f-`hl-{qG?Jq%~u5`r_fwqK4~wqK4a?Ue(%9)OB2S|HGDXa#|EkOhJa%(MfdB2@+)d<`=I z+UN50K&Lt@SF<)z_}G40Pj)plRtB(l$UfU_uop>DgwvR ztZ-1S0QfYJGEdg(voDZnmUThbY37Onst5zKqt<|JOo^st2NQBJ%@1n8+h3zAJjb-j zNS#WmM&E*zCikO5x!$3-U{Pf`-J5CxN@)Hypp{VS;d)$zl&(d@TCIiwCj6WkkH9G3 zV(q$X)yiuY8=ZPqk{ZnX5_7@}X#uc|KqrO>`OgOgYiZo``qdVs{$zDyD7EzogQ*v~ z{1#du8bf;M(Fa=tI)HO11C@ZDm-afpgyqp;p%J3ag#l7C%LuW65aH(`Q7S>#qu2TR z<CRQ|M==+VwE0MOx=QD+SSy5t06!qm*cnfjhiQ5&ScbvqXu-bn1~Q#!nCK~ z;jzQ9g_>CEDb-A)X5@8dT@wf_1#!gLh$dtQq)Q{eX53mh@6h;Os-|43BSB2`bYBmk zG?bRDYZR4P{>ovZR{NqMwOy5_!M1_XV%S(J*sg<%rHETo3!FJx(~&+>st*3HnQ&Mz zoy0Vc*Nj9#PI#R5j6NT+@t`ooR$U&n>C&tYHZ~f46qL!$;TF`Vn!>cyG#$nq&;>h1 zT3!S3@6njq^u1Y3l)!ASpyZ4{ElOvjyP))^p#v0Zfl4-lEd`B?P+S@@0G( z%P-~kJ2mfj-}Z+XmFt!8mM8v@FS(Of-bZ9TIu_b3Trg5|C4@Q>)?er6$&#bPeY?X~ zj=z^3D;bbe`$lVApnIJPEq46i0qxaeXh|L15N7*#oI>ZccX`EqAX?$Qw8DYAC3y!R z!akWUu2U66=zMJhKB2V{O}cxJR`y6>rz2yurQwFr@4v%bOT6ECj@v7G${9-(8i&w6o@k-QNNb2=|aeo>9;y&zm z28tQ|3Pa=v2`Ibj5!`_UujiSKZ~9}IYI%1RVZuneI@?-8L@nk<+J3VyXlqfHU;7qC zJGiB7jS^&2VXpC6EvmJ|%RCzPyt>I+jC5HlsI}Nk3o%1yw#6dvv3j1n37$lx24s%r zTBtS$9i?XcmSR);PlRmAcXi7w4|^2u-NMC!+U-NTirAg#^Y$VS=)DtrxT=<9aPGy4 z5_4tSmRN;E6A}mC(AAWpt~}qbFHTO6h5Kvz^VP{9POnX)c`oa=uDI_kF~5VpR>w8R zz`xI(Bo!%$V&_`TFpOe?G*j4pfL~%R4wzC>YaGFXC(kuXnK#Z-GNCj(HVP%;x(ylQ zsOS`BD-|l&M>pC;Hmtt7Bz29knQrKv%(B=~a{?JF`ZMDhH4Vj5DqH4I9u`!Dz3F0s zDm(|Uy}MV|4Z137nK;Tqw}-8^w`=1XK@jMwRQF4$3h3|IX!IlU;H% z1gJV*QC^^#sw5I8jI;c-$(!HAL*ElSG?5~cZ%f(gv~U|O8CX`gQ5Bh)Lnv03qH0Bz zlGbSX#`VYZ~JZ`Q$u$^i4SIdnzHZFg^;c#NiG3t#546Pasu90b2bj{1($aE5Rlliu}Z!kpi-ggGtqyjg7X8>w> zP4^Y_H^UwaaI`Q819)n zqr94`A*m&`;sB_!K_Ivmw*$iHW}7mxWg@o5q~H@iq!1e7^d;Sc)XE&F;4XJCtllwB zUgKNQU$qSG$^mAVc0q-e-*OMw3ZT@~ms@ca3KpwX39NQObqx6HV{!KC#Vm4pM}`|< zO|JO(R$f_K=M5jQbBdbiJ457cBKi3qmDMktJu-(VM3^5lLC8f^5SW;dS|Mu0Y?=^1 zl)WhtG&V8)iHk&24hmx>?vS~`>|%845|Px#Ww-jM8hBok6_mbtD{@g8nBB`N8n~pY zolm8G6--|D}H=?f`W=0 zDPhw)Xw8{1a*4Fcs|HacLq(o!)e0%tXkclXACIcft;ipEEEl*|>kGNJuiw{o;LKd{hT&1W!`%qL~bt_U<^!jBm9GAzrc_}9FTE6PGrV)Yo+H zp6HRrj_2d}e&=ySGoRyqoK9RL8LHG{(rF3|H4i{q?KSP!#0>5Ox~u{0unKFQCy%A* z4F!E*K%aLLIWO| zXDrZ?B=-a^bFd9D5-hdW$@KW;(b@U&v!a#qfMrXUC}&b!c^a>d|L>x`ZC)+~kV3W4 zxm}yp(_opZQZmd@`6i@xP^BPu#mPy-sqpfBCMhw%l9_`yrr-{fJYY#D1AEK*8T9?v5Rf>3~hwkFr=RxBF?9k4j#HKAWs zmZO#xO$1tHX5}bgYFbmx_8qrMn#+fT>0WnZ%>l7S4&jjk*_K&ikx`p2jZEFh>`|q% zM}vC{&Hm#j-J!u!JoM63)7Z~xIPcklv3K3>!Lreo50>J#k1~zLRwWX?=-aSu;zq@C zmf^cK$l0Z^uxC^cprU&565*TJlPj)JuKKc*K>kiwo%2-jc&s5?v%8adX?sc>a>mZU z5(f+5;*6AZm*UA6@&JCf*!3seiK3N_+)Dcfkp8ZASip3%@TYAxaB9@1y=AvcM8=gP zJ}y%3g}gIHun2)z9Edf0E47x2w2|^XjGAg*>!TxB+9X-1vZ1wOD<*Xt?30Ju%OfRC3m6`%mv1!b1yff)PB`j!5024HnuKE3h9Q$WS#ztBu8K7EOdpgpE@i zsUYZ^(*nAYuZq?CSLaN|75HF=u5fSrXv!qBYuCV^G8SZBc9Lb zIp=-LZB!XQnO;XM-_)PxZ+k_EEqsepDMJZe?^KRCIRXh{SP}@b= z!65H)y;WhO>A^pNrU!pLO%K-5)cRXJKY7DzKkL)s(@`Gpa)Ybuehqi^yfnFF74*n>bZq_K~z&M zC?SY()G4gNN}62xqkcp6M{fn<5kz?CWHkVS4v~vO_VXhnUKZu`ojT z;hy{(YS}vzduR%X{K-}G=E=O7`VG}-L0cEGsLO+Y@GbfYOr*?e*8o(a?iuf^_wYw7 z7mG;{Ow1qtgUUIxELc$bdR2{U!q^bs!q6IleaK49@qI-L2w)gN+8Y(Cmiesul|3LN z%5^un{eo9mO}B%(#j9+nzvYoN9d4RaQU3ir_T*pZ4j44}_uEV4gBp&hX>LH%ytT0)06TS2i@0YX9z@DYB(&u( z%M&L2Dpr5fFQVfgM*zwfZ|KAKS;)n|K0P00*C3UIzWRZ6e89`*<)5#j_xklSJ`;b} z4-u8=Rx$mBTyfJ4r=+%>$BS4C-FQbn^QQL(ei%3T~@EfQ-7Rp_VIdMkHM zpJf#PRdllkRkJp_vvg6NvOv3KGpwub5~-j7aiTZQM5R(n zN=|e=PAwVOphTlssz>%kRAX4003jj3s3+6 From eec792db0ac697345cf45828e34591785268d9a2 Mon Sep 17 00:00:00 2001 From: Asterchades Date: Thu, 28 Nov 2024 16:08:37 +1000 Subject: [PATCH 03/98] Update fdmprinter.def.json Set global Top and Bottom Layer values to match previous Ultimaker defaults. --- resources/definitions/fdmprinter.def.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index c930a624d0..a4adfd1d83 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1595,7 +1595,7 @@ "maximum_value": "999999", "type": "int", "minimum_value_warning": "2", - "value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))", + "value": "math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))" "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true } @@ -1625,7 +1625,7 @@ "default_value": 6, "maximum_value": "999999", "type": "int", - "value": "999999 if infill_sparse_density == 100 and not magic_spiralize else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))", + "value": "math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))" "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true }, @@ -9193,4 +9193,4 @@ } } } -} \ No newline at end of file +} From e0cda11b811c4a704289661852549a9f847db636 Mon Sep 17 00:00:00 2001 From: Asterchades Date: Thu, 28 Nov 2024 16:09:20 +1000 Subject: [PATCH 04/98] Update ultimaker.def.json Removed Top and Bottom Layers values. With global defaults updated it is no longer required to re-set them. --- resources/definitions/ultimaker.def.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json index 65ceaf6f0f..b1a1cf87b1 100644 --- a/resources/definitions/ultimaker.def.json +++ b/resources/definitions/ultimaker.def.json @@ -16,7 +16,6 @@ { "acceleration_layer_0": { "value": "acceleration_topbottom" }, "acceleration_travel_enabled": { "value": false }, - "bottom_layers": { "value": "math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))" }, "bridge_enable_more_layers": { "value": false }, "bridge_fan_speed": { "value": "cool_fan_speed_max" }, "bridge_fan_speed_2": { "value": "cool_fan_speed_min" }, @@ -131,7 +130,6 @@ "support_wall_count": { "value": "1 if support_structure == 'tree' else 0" }, "support_xy_distance_overhang": { "value": "0.2" }, "support_z_distance": { "value": "0" }, - "top_layers": { "value": "math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))" }, "wall_0_material_flow_layer_0": { "value": "1.10 * material_flow_layer_0" }, "wall_thickness": { "value": "wall_line_width_0 + wall_line_width_x" }, "wall_x_material_flow_layer_0": { "value": "0.95 * material_flow_layer_0" }, @@ -141,4 +139,4 @@ "z_seam_relative": { "value": "True" }, "zig_zaggify_support": { "value": true } } -} \ No newline at end of file +} From cabd4261c2bd5ecc63971175d50c6656be570a03 Mon Sep 17 00:00:00 2001 From: Asterchades Date: Thu, 28 Nov 2024 16:10:58 +1000 Subject: [PATCH 05/98] Update fdmprinter.def.json Restore missing commas. Going to need those. --- resources/definitions/fdmprinter.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index a4adfd1d83..64e3c7d963 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1595,7 +1595,7 @@ "maximum_value": "999999", "type": "int", "minimum_value_warning": "2", - "value": "math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))" + "value": "math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))", "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true } @@ -1625,7 +1625,7 @@ "default_value": 6, "maximum_value": "999999", "type": "int", - "value": "math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))" + "value": "math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))", "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true }, From e7a1bf89ec94397c5952377de4e47272991a96e0 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:35:02 -0500 Subject: [PATCH 06/98] Update DisplayInfoOnLCD.py Revised the M118 lines and insertion point in the gcode. Revised M117 same. Added nozzle size and filament type to data[0] --- .../scripts/DisplayInfoOnLCD.py | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 870a64834d..ed226c08f3 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -47,7 +47,7 @@ class DisplayInfoOnLCD(Script): self._instance.setProperty("enable_countdown", "value", enable_countdown) except: pass - + def getSettingDataString(self): return """{ "name": "Display Info on LCD", @@ -238,15 +238,6 @@ class DisplayInfoOnLCD(Script): add_m118_line = self.getSettingValueByKey("add_m118_line") add_m118_a1 = self.getSettingValueByKey("add_m118_a1") add_m118_p0 = self.getSettingValueByKey("add_m118_p0") - m118_text = "M118 " - m118_str = "M118 " - if add_m118_line: - if add_m118_a1 and not add_m118_p0: - m118_str = "M118 A1 " - if add_m118_p0 and not add_m118_a1: - m118_str = "M118 P0 " - if add_m118_p0 and add_m118_a1: - m118_str = "M118 A1 P0 " add_m73_line = self.getSettingValueByKey("add_m73_line") add_m73_time = self.getSettingValueByKey("add_m73_time") add_m73_percent = self.getSettingValueByKey("add_m73_percent") @@ -318,12 +309,22 @@ class DisplayInfoOnLCD(Script): # Display Progress (from 'Show Progress' and 'Display Progress on LCD')--------------------------------------- elif display_option == "display_progress": print_sequence = Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") - ## Add the Initial Layer Height just below Layer Height in data[0] + # Add the Initial Layer Height just below Layer Height in data[0] + extruder_count = Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value") init_layer_hgt_line = ";Initial Layer Height: " + str(Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")) - nozzle_size_line = ";Nozzle Size T0: " + str(Application.getInstance().getGlobalContainerStack().extruderList[0].getProperty("machine_nozzle_size", "value")) - match = re.search(";Layer height: (\d\.\d*)", data[0])[0] - data[0] = re.sub(match, match + "\n" + init_layer_hgt_line + "\n" + nozzle_size_line, data[0]) - ## Get settings + nozzle_size_line = ";Nozzle Size T0: " + str(Application.getInstance().getGlobalContainerStack().extruderList[0].getProperty("machine_nozzle_size", "value")) + filament_type = "\n;Filament type for T0: " + str(Application.getInstance().getGlobalContainerStack().extruderList[0].getProperty("material_type", "value")) + if extruder_count > 1: + nozzle_size_line += "\n;Nozzle Size T1: " + str(Application.getInstance().getGlobalContainerStack().extruderList[1].getProperty("machine_nozzle_size", "value")) + filament_type += "\n;Filament type for T1: " + str(Application.getInstance().getGlobalContainerStack().extruderList[1].getProperty("material_type", "value")) + lines = data[0].split("\n") + for index, line in enumerate(lines): + if line.startswith(";Layer height:"): + lines[index] += "\n" + init_layer_hgt_line + "\n" + nozzle_size_line + if line.startswith(";Filament used"): + lines[index] += filament_type + data[0] = "\n".join(lines) + # Get settings display_total_layers = self.getSettingValueByKey("display_total_layers") display_remaining_time = self.getSettingValueByKey("display_remaining_time") speed_factor = self.getSettingValueByKey("speed_factor") / 100 @@ -336,13 +337,13 @@ class DisplayInfoOnLCD(Script): if add_m73_line: data[1] = "M75\n" + data[1] data[len(data)-1] += "M77\n" - ## Initialize some variables + # Initialize some variables first_layer_index = 0 time_total = int(data[0].split(";TIME:")[1].split("\n")[0]) number_of_layers = 0 time_elapsed = 0 - ## If at least one of the settings is disabled, there is enough room on the display to display "layer" + # If at least one of the settings is disabled, there is enough room on the display to display "layer" first_section = data[0] lines = first_section.split("\n") for line in lines: @@ -356,10 +357,10 @@ class DisplayInfoOnLCD(Script): orig_hhh = cura_time/3600 orig_hr = round(orig_hhh // 1) orig_mmm = math.floor((orig_hhh % 1) * 60) - if add_m118_line: lines.insert(tindex + 5, m118_str + "Adjusted Print Time " + str(hr) + "hr " + str(mmm) + "min") - if add_m117_line: lines.insert(tindex + 5,"M117 ET " + str(hr) + "hr " + str(mmm) + "min") - ## Add M73 line at beginning - mins = int(60 * hr + mmm) + if add_m118_line: lines.insert(tindex + 6,"M118 Adjusted Print Time " + str(hr) + "hr " + str(mmm) + "min") + if add_m117_line: lines.insert(tindex + 6,"M117 ET " + str(hr) + "hr " + str(mmm) + "min") + # Add M73 line at beginning + mins = int(60 * hr + mmm) if add_m73_line and (add_m73_time or add_m73_percent): if m73_time: m73_str += " R{}".format(mins) @@ -383,13 +384,13 @@ class DisplayInfoOnLCD(Script): if pause_cmd[q] in data[num]: pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) pause_str = f" with {pause_count} pause(s)" - ## This line goes in to convert seconds to hours and minutes - lines.insert(tindex + 5, f";Cura Time Estimate: {orig_hr}hr {orig_mmm}min {pause_str}") + # This line goes in to convert seconds to hours and minutes + lines.insert(tindex + 1, f";Cura Time Estimate: {orig_hr}hr {orig_mmm}min {pause_str}") data[0] = "\n".join(lines) if add_m117_line: data[len(data)-1] += "M117 Orig Cura Est " + str(orig_hr) + "hr " + str(orig_mmm) + "min\n" if add_m118_line: - data[len(data)-1] += m118_str + " Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n" + data[len(data)-1] += "M118 Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n" if not display_total_layers or not display_remaining_time: base_display_text = "layer " else: @@ -463,7 +464,7 @@ class DisplayInfoOnLCD(Script): a1_str = "" p0_str = "" if add_m118_a1: - a1_str = "A1 " + a1_str = "A1 " if add_m118_p0: p0_str = "P0 " lines[l_index] += "\nM118 " + a1_str + p0_str + display_text @@ -592,8 +593,8 @@ class DisplayInfoOnLCD(Script): adjusted_str = "Adjusted Time Estimate..." + str(time_change) finish_str = week_day + " " + str(mo_str) + " " + str(new_time.strftime("%d")) + ", " + str(new_time.strftime("%Y")) + " at " + str(show_hr) + str(new_time.strftime("%M")) + str(show_ampm) return finish_str, estimate_str, adjusted_str, print_start_str - - def get_time_to_go(self, time_str: str): + + def get_time_to_go(self, time_str: str): alt_time = time_str[:-1] hhh = int(float(alt_time) / 3600) if hhh > 0: From da0b99cb8273b5e50a6bd3c994af73800f7d3f72 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Wed, 8 Jan 2025 07:46:42 -0500 Subject: [PATCH 07/98] Update DisplayInfoOnLCD.py Added "global_stack" variable. --- .../scripts/DisplayInfoOnLCD.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index ed226c08f3..c4f7ecf55c 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -308,15 +308,16 @@ class DisplayInfoOnLCD(Script): # Display Progress (from 'Show Progress' and 'Display Progress on LCD')--------------------------------------- elif display_option == "display_progress": - print_sequence = Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") + global_stack = Application.getInstance().getGlobalContainerStack() + print_sequence = global_stack.getProperty("print_sequence", "value") # Add the Initial Layer Height just below Layer Height in data[0] - extruder_count = Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value") - init_layer_hgt_line = ";Initial Layer Height: " + str(Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")) - nozzle_size_line = ";Nozzle Size T0: " + str(Application.getInstance().getGlobalContainerStack().extruderList[0].getProperty("machine_nozzle_size", "value")) - filament_type = "\n;Filament type for T0: " + str(Application.getInstance().getGlobalContainerStack().extruderList[0].getProperty("material_type", "value")) + extruder_count = global_stack.getProperty("machine_extruder_count", "value") + init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) + nozzle_size_line = ";Nozzle Size T0: " + str(global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")) + filament_type = "\n;Filament type for T0: " + str(global_stack.extruderList[0].getProperty("material_type", "value")) if extruder_count > 1: - nozzle_size_line += "\n;Nozzle Size T1: " + str(Application.getInstance().getGlobalContainerStack().extruderList[1].getProperty("machine_nozzle_size", "value")) - filament_type += "\n;Filament type for T1: " + str(Application.getInstance().getGlobalContainerStack().extruderList[1].getProperty("material_type", "value")) + nozzle_size_line += "\n;Nozzle Size T1: " + str(global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")) + filament_type += "\n;Filament type for T1: " + str(global_stack.extruderList[1].getProperty("material_type", "value")) lines = data[0].split("\n") for index, line in enumerate(lines): if line.startswith(";Layer height:"): From 984c19decc764ca51a3ea68a78df3808517bbc92 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:01:19 -0500 Subject: [PATCH 08/98] Update DisplayInfoOnLCD.py Bug fix. --- .../scripts/DisplayInfoOnLCD.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index c4f7ecf55c..fc3b22a7a6 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -238,12 +238,14 @@ class DisplayInfoOnLCD(Script): add_m118_line = self.getSettingValueByKey("add_m118_line") add_m118_a1 = self.getSettingValueByKey("add_m118_a1") add_m118_p0 = self.getSettingValueByKey("add_m118_p0") + m118_str = "M118 " + m118_text = "M118 " add_m73_line = self.getSettingValueByKey("add_m73_line") add_m73_time = self.getSettingValueByKey("add_m73_time") add_m73_percent = self.getSettingValueByKey("add_m73_percent") m73_str = "" - # This is Display Filename and Layer on LCD--------------------------------------------------------- + # This is from the original Display Filename and Layer on LCD if display_option == "filename_layer": max_layer = 0 lcd_text = "M117 " @@ -256,7 +258,7 @@ class DisplayInfoOnLCD(Script): lcd_text += "Printing " octo_text += "Printing " if not self.getSettingValueByKey("scroll"): - lcd_text += "Layer " + lcd_text += "Lay " octo_text += "Layer " else: lcd_text += file_name + " - Layer " @@ -275,15 +277,15 @@ class DisplayInfoOnLCD(Script): max_layer = str(int(max_layer) - 1) if line.startswith(";LAYER:"): if self.getSettingValueByKey("maxlayer"): - display_text += " of " + max_layer - m118_text += " of " + max_layer + display_text += "/" + max_layer + m118_text += "/" + max_layer if not self.getSettingValueByKey("scroll"): - display_text += " " + file_name - m118_text += " " + file_name + display_text += "|" + file_name + m118_text += " | " + file_name else: if not self.getSettingValueByKey("scroll"): - display_text += " " + file_name + "!" - m118_text += " " + file_name + "!" + display_text += "|" + file_name + "!" + m118_text += " | " + file_name + "!" else: display_text += "!" m118_text += "!" @@ -291,6 +293,8 @@ class DisplayInfoOnLCD(Script): if add_m117_line: lines.insert(line_index + 1, display_text) if add_m118_line: + if not (add_m118_p0 and add_m118_a1): + m118_str = m118_text if add_m118_a1 and not add_m118_p0: m118_str = m118_text.replace("M118 ","M118 A1 ") if add_m118_p0 and not add_m118_a1: @@ -306,7 +310,7 @@ class DisplayInfoOnLCD(Script): Message(title = "Display Info on LCD - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() return data - # Display Progress (from 'Show Progress' and 'Display Progress on LCD')--------------------------------------- + # This is from 'Show Progress on LCD' elif display_option == "display_progress": global_stack = Application.getInstance().getGlobalContainerStack() print_sequence = global_stack.getProperty("print_sequence", "value") From 043afdb6ed7fbaa672c383ca2337c19564254177 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:01:44 -0500 Subject: [PATCH 09/98] Update DisplayInfoOnLCD.py Update. Remove vestigial line of code at line 530. --- .../scripts/DisplayInfoOnLCD.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index fc3b22a7a6..38649ed6ce 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -377,18 +377,21 @@ class DisplayInfoOnLCD(Script): if bool(self.getSettingValueByKey("countdown_to_pause")): pause_count = 0 pause_setting = self.getSettingValueByKey("pause_cmd").upper() - pause_cmd = [] - if "," in pause_setting: - pause_cmd = pause_setting.split(",") + if pause_setting != "": + pause_cmd = [] + if "," in pause_setting: + pause_cmd = pause_setting.split(",") + else: + pause_cmd.append(pause_setting) + for q in range(0, len(pause_cmd)): + pause_cmd[q] = "\n" + pause_cmd[q] + for num in range(2,len(data) - 2, 1): + for q in range(0,len(pause_cmd)): + if pause_cmd[q] in data[num]: + pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) + pause_str = f"with {pause_count} pause(s)" else: - pause_cmd.append(pause_setting) - for q in range(0, len(pause_cmd)): - pause_cmd[q] = "\n" + pause_cmd[q] - for num in range(2,len(data) - 2, 1): - for q in range(0,len(pause_cmd)): - if pause_cmd[q] in data[num]: - pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) - pause_str = f" with {pause_count} pause(s)" + pause_str = "" # This line goes in to convert seconds to hours and minutes lines.insert(tindex + 1, f";Cura Time Estimate: {orig_hr}hr {orig_mmm}min {pause_str}") data[0] = "\n".join(lines) @@ -527,7 +530,6 @@ class DisplayInfoOnLCD(Script): except: continue data[num] = layer - setting_data = "" if bool(self.getSettingValueByKey("enable_end_message")): message_str = self.message_to_user(speed_factor) Message(title = "[Display Info on LCD] - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() From a8849f7081ca8c84a85469f225ed3c0cf464fb79 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Fri, 10 Jan 2025 17:26:43 -0500 Subject: [PATCH 10/98] Update DisplayInfoOnLCD.py Added a condition so the pause_cmd setting box is hidden unless the 'display_option' == "display_progress". --- plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 38649ed6ce..399578fef5 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -210,7 +210,7 @@ class DisplayInfoOnLCD(Script): "description": "This might be M0, or M25 or M600 if Filament Change is used. If you have mixed commands then delimit them with a comma ',' (Ex: M0,M600). Spaces are not allowed.", "type": "str", "default_value": "M0", - "enabled": "countdown_to_pause and enable_countdown and display_remaining_time" + "enabled": "display_option == 'display_progress' and countdown_to_pause and enable_countdown and display_remaining_time" }, "enable_end_message": { From 02847714302bd026de3ddd7a50e8c47b1bb90ec5 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 11 Jan 2025 10:12:42 -0500 Subject: [PATCH 11/98] Update DisplayInfoOnLCD.py Split functions from Execute. Added function "Add Settings" so it also works with the "filename_layer" option. --- .../scripts/DisplayInfoOnLCD.py | 688 +++++++++--------- 1 file changed, 358 insertions(+), 330 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 399578fef5..2177f91f3b 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -1,32 +1,35 @@ -# Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 -# Modified: Joshua Pope-Lewis on November 16, 2018 -# Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019 -# Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here. -#--------------------------------------------------------------- -# DisplayNameOrProgressOnLCD.py -# Cura Post-Process plugin -# Combines 'Display Filename and Layer on the LCD' with 'Display Progress' -# Combined and with additions by: GregValiant (Greg Foresi) -# Date: September 8, 2023 -# Date: March 31, 2024 - Bug fix for problem with adding M118 lines if 'Remaining Time' was not checked. -# NOTE: This combined post processor will make 'Display Filename and Layer on the LCD' and 'Display Progress' obsolete -# Description: Display Filename and Layer options: -# Status messages sent to the printer... -# - Scrolling (SCROLL_LONG_FILENAMES) if enabled in Marlin and you aren't printing a small item select this option. -# - Name: By default it will use the name generated by Cura (EG: TT_Test_Cube) - You may enter a custom name here -# - Start Num: Choose which number you prefer for the initial layer, 0 or 1 -# - Max Layer: Enabling this will show how many layers are in the entire print (EG: Layer 1 of 265!) -# - Add prefix 'Printing': Enabling this will add the prefix 'Printing' -# - Example Line on LCD: Printing Layer 0 of 395 3DBenchy -# Display Progress options: -# - Display Total Layer Count -# - Disply Time Remaining for the print -# - Time Fudge Factor % - Divide the Actual Print Time by the Cura Estimate. Enter as a percentage and the displayed time will be adjusted. This allows you to bring the displayed time closer to reality (Ex: Entering 87.5 would indicate an adjustment to 87.5% of the Cura estimate). -# - Example line on LCD: 1/479 | ET 2h13m -# - Time to Pauses changes the M117/M118 lines to countdown to the next pause as 1/479 | TP 2h36m -# - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. -# - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) -# - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It takes into account the Time Fudge Factor. The user may enter a print start time. This is also available for Display Filename. +""" +Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 + Modified: Joshua Pope-Lewis on November 16, 2018 + Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019 + Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here. +--------------------------------------------------------------- + DisplayNameOrProgressOnLCD.py + Cura Post-Process plugin + Combines 'Display Filename and Layer on the LCD' with 'Display Progress' + Combined and with additions by: GregValiant (Greg Foresi) + Date: September 8, 2023 + Date: March 31, 2024 - Bug fix for problem with adding M118 lines if 'Remaining Time' was not checked. + NOTE: This combined post processor will make 'Display Filename and Layer on the LCD' and 'Display Progress' obsolete + Description: Display Filename and Layer options: + Status messages sent to the printer... + - Scrolling (SCROLL_LONG_FILENAMES) if enabled in Marlin and you aren't printing a small item select this option. + - Name: By default it will use the name generated by Cura (EG: TT_Test_Cube) - You may enter a custom name here + - Start Num: Choose which number you prefer for the initial layer, 0 or 1 + - Max Layer: Enabling this will show how many layers are in the entire print (EG: Layer 1 of 265!) + - Add prefix 'Printing': Enabling this will add the prefix 'Printing' + - Example Line on LCD: Printing Layer 0 of 395 3DBenchy + Display Progress options: + - Display Total Layer Count + - Disply Time Remaining for the print + - Time Fudge Factor % - Divide the Actual Print Time by the Cura Estimate. Enter as a percentage and the displayed time will be adjusted. + This allows you to bring the displayed time closer to reality (Ex: Entering 87.5 would indicate an adjustment to 87.5% of the Cura estimate). + - Example line on LCD: 1/479 | ET 2h13m + - Time to Pauses changes the M117/M118 lines to countdown to the next pause as 1/479 | TP 2h36m + - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. + - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) + - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It uses the Time Fudge Factor. The user may enter a print start time. +""" from ..Script import Script from UM.Application import Application @@ -47,7 +50,7 @@ class DisplayInfoOnLCD(Script): self._instance.setProperty("enable_countdown", "value", enable_countdown) except: pass - + def getSettingDataString(self): return """{ "name": "Display Info on LCD", @@ -234,308 +237,301 @@ class DisplayInfoOnLCD(Script): def execute(self, data): display_option = self.getSettingValueByKey("display_option") - add_m117_line = self.getSettingValueByKey("add_m117_line") - add_m118_line = self.getSettingValueByKey("add_m118_line") - add_m118_a1 = self.getSettingValueByKey("add_m118_a1") - add_m118_p0 = self.getSettingValueByKey("add_m118_p0") - m118_str = "M118 " - m118_text = "M118 " - add_m73_line = self.getSettingValueByKey("add_m73_line") - add_m73_time = self.getSettingValueByKey("add_m73_time") - add_m73_percent = self.getSettingValueByKey("add_m73_percent") - m73_str = "" - - # This is from the original Display Filename and Layer on LCD + self.add_m117_line = self.getSettingValueByKey("add_m117_line") + self.add_m118_line = self.getSettingValueByKey("add_m118_line") + self.add_m118_a1 = self.getSettingValueByKey("add_m118_a1") + self.add_m118_p0 = self.getSettingValueByKey("add_m118_p0") + self.m118_str = "M118 " + self.m118_text = "M118 " + self.add_m73_line = self.getSettingValueByKey("add_m73_line") + self.add_m73_time = self.getSettingValueByKey("add_m73_time") + self.add_m73_percent = self.getSettingValueByKey("add_m73_percent") + self.m73_str = "" if display_option == "filename_layer": - max_layer = 0 - lcd_text = "M117 " - octo_text = "M118 " - if self.getSettingValueByKey("file_name") != "": - file_name = self.getSettingValueByKey("file_name") - else: - file_name = Application.getInstance().getPrintInformation().jobName - if self.getSettingValueByKey("addPrefixPrinting"): - lcd_text += "Printing " - octo_text += "Printing " - if not self.getSettingValueByKey("scroll"): - lcd_text += "Lay " - octo_text += "Layer " - else: - lcd_text += file_name + " - Layer " - octo_text += file_name + " - Layer " - i = self.getSettingValueByKey("startNum") - for layer in data: - display_text = lcd_text + str(i) - m118_text = octo_text + str(i) - layer_index = data.index(layer) - lines = layer.split("\n") - for line in lines: - if line.startswith(";LAYER_COUNT:"): - max_layer = line - max_layer = max_layer.split(":")[1] - if self.getSettingValueByKey("startNum") == 0: - max_layer = str(int(max_layer) - 1) - if line.startswith(";LAYER:"): - if self.getSettingValueByKey("maxlayer"): - display_text += "/" + max_layer - m118_text += "/" + max_layer - if not self.getSettingValueByKey("scroll"): - display_text += "|" + file_name - m118_text += " | " + file_name - else: - if not self.getSettingValueByKey("scroll"): - display_text += "|" + file_name + "!" - m118_text += " | " + file_name + "!" - else: - display_text += "!" - m118_text += "!" - line_index = lines.index(line) - if add_m117_line: - lines.insert(line_index + 1, display_text) - if add_m118_line: - if not (add_m118_p0 and add_m118_a1): - m118_str = m118_text - if add_m118_a1 and not add_m118_p0: - m118_str = m118_text.replace("M118 ","M118 A1 ") - if add_m118_p0 and not add_m118_a1: - m118_str = m118_text.replace("M118 ","M118 P0 ") - if add_m118_p0 and add_m118_a1: - m118_str = m118_text.replace("M118 ","M118 A1 P0 ") - lines.insert(line_index + 2, m118_str) - i += 1 - final_lines = "\n".join(lines) - data[layer_index] = final_lines - if bool(self.getSettingValueByKey("enable_end_message")): - message_str = self.message_to_user(self.getSettingValueByKey("speed_factor") / 100) - Message(title = "Display Info on LCD - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() - return data - - # This is from 'Show Progress on LCD' - elif display_option == "display_progress": - global_stack = Application.getInstance().getGlobalContainerStack() - print_sequence = global_stack.getProperty("print_sequence", "value") - # Add the Initial Layer Height just below Layer Height in data[0] - extruder_count = global_stack.getProperty("machine_extruder_count", "value") - init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) - nozzle_size_line = ";Nozzle Size T0: " + str(global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")) - filament_type = "\n;Filament type for T0: " + str(global_stack.extruderList[0].getProperty("material_type", "value")) - if extruder_count > 1: - nozzle_size_line += "\n;Nozzle Size T1: " + str(global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")) - filament_type += "\n;Filament type for T1: " + str(global_stack.extruderList[1].getProperty("material_type", "value")) - lines = data[0].split("\n") - for index, line in enumerate(lines): - if line.startswith(";Layer height:"): - lines[index] += "\n" + init_layer_hgt_line + "\n" + nozzle_size_line - if line.startswith(";Filament used"): - lines[index] += filament_type - data[0] = "\n".join(lines) - # Get settings - display_total_layers = self.getSettingValueByKey("display_total_layers") - display_remaining_time = self.getSettingValueByKey("display_remaining_time") - speed_factor = self.getSettingValueByKey("speed_factor") / 100 - m73_time = False - m73_percent = False - if add_m73_line and add_m73_time: - m73_time = True - if add_m73_line and add_m73_percent: - m73_percent = True - if add_m73_line: - data[1] = "M75\n" + data[1] - data[len(data)-1] += "M77\n" - # Initialize some variables - first_layer_index = 0 - time_total = int(data[0].split(";TIME:")[1].split("\n")[0]) - number_of_layers = 0 - time_elapsed = 0 - - # If at least one of the settings is disabled, there is enough room on the display to display "layer" - first_section = data[0] - lines = first_section.split("\n") + data = self._display_filename_layer(data) + else: + data = self._display_progress(data) + return data + # This is from the original 'Display Filename and Layer on LCD' + def _display_filename_layer(self, data: str) -> str: + data[0] = self._add_stats(data) + max_layer = 0 + lcd_text = "M117 " + octo_text = "M118 " + if self.getSettingValueByKey("file_name") != "": + file_name = self.getSettingValueByKey("file_name") + else: + file_name = Application.getInstance().getPrintInformation().jobName + if self.getSettingValueByKey("addPrefixPrinting"): + lcd_text += "Printing " + octo_text += "Printing " + if not self.getSettingValueByKey("scroll"): + lcd_text += "Lay " + octo_text += "Layer " + else: + lcd_text += file_name + " - Layer " + octo_text += file_name + " - Layer " + i = self.getSettingValueByKey("startNum") + for layer in data: + display_text = lcd_text + str(i) + self.m118_text = octo_text + str(i) + layer_index = data.index(layer) + lines = layer.split("\n") for line in lines: - if line.startswith(";TIME:"): - tindex = lines.index(line) - cura_time = int(line.split(":")[1]) - print_time = cura_time * speed_factor - hhh = print_time/3600 - hr = round(hhh // 1) - mmm = round((hhh % 1) * 60) - orig_hhh = cura_time/3600 - orig_hr = round(orig_hhh // 1) - orig_mmm = math.floor((orig_hhh % 1) * 60) - if add_m118_line: lines.insert(tindex + 6,"M118 Adjusted Print Time " + str(hr) + "hr " + str(mmm) + "min") - if add_m117_line: lines.insert(tindex + 6,"M117 ET " + str(hr) + "hr " + str(mmm) + "min") - # Add M73 line at beginning - mins = int(60 * hr + mmm) - if add_m73_line and (add_m73_time or add_m73_percent): - if m73_time: - m73_str += " R{}".format(mins) - if m73_percent: - m73_str += " P0" - lines.insert(tindex + 4, "M73" + m73_str) - # If Countdown to pause is enabled then count the pauses - pause_str = "" - if bool(self.getSettingValueByKey("countdown_to_pause")): - pause_count = 0 - pause_setting = self.getSettingValueByKey("pause_cmd").upper() - if pause_setting != "": - pause_cmd = [] - if "," in pause_setting: - pause_cmd = pause_setting.split(",") - else: - pause_cmd.append(pause_setting) - for q in range(0, len(pause_cmd)): - pause_cmd[q] = "\n" + pause_cmd[q] - for num in range(2,len(data) - 2, 1): - for q in range(0,len(pause_cmd)): - if pause_cmd[q] in data[num]: - pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) - pause_str = f"with {pause_count} pause(s)" - else: - pause_str = "" - # This line goes in to convert seconds to hours and minutes - lines.insert(tindex + 1, f";Cura Time Estimate: {orig_hr}hr {orig_mmm}min {pause_str}") - data[0] = "\n".join(lines) - if add_m117_line: - data[len(data)-1] += "M117 Orig Cura Est " + str(orig_hr) + "hr " + str(orig_mmm) + "min\n" - if add_m118_line: - data[len(data)-1] += "M118 Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n" - if not display_total_layers or not display_remaining_time: - base_display_text = "layer " - else: - base_display_text = "" - layer = data[len(data)-1] - data[len(data)-1] = layer.replace(";End of Gcode" + "\n", "") - data[len(data)-1] += ";End of Gcode" + "\n" - # Search for the number of layers and the total time from the start code - for index in range(len(data)): - data_section = data[index] - # We have everything we need, save the index of the first layer and exit the loop - if ";LAYER:" in data_section: - first_layer_index = index - break - else: - for line in data_section.split("\n"): - if line.startswith(";LAYER_COUNT:"): - number_of_layers = int(line.split(":")[1]) - if print_sequence == "one_at_a_time": - number_of_layers = 1 - for lay in range(2,len(data)-1,1): - if ";LAYER:" in data[lay]: - number_of_layers += 1 - elif line.startswith(";TIME:"): - time_total = int(line.split(":")[1]) - # for all layers... - current_layer = 0 - for layer_counter in range(len(data)-2): - current_layer += 1 - layer_index = first_layer_index + layer_counter - display_text = base_display_text - display_text += str(current_layer) - # create a list where each element is a single line of code within the layer - lines = data[layer_index].split("\n") - if not ";LAYER:" in data[layer_index]: - current_layer -= 1 - continue - # add the total number of layers if this option is checked - if display_total_layers: - display_text += "/" + str(number_of_layers) - # if display_remaining_time is checked, it is calculated in this loop - if display_remaining_time: - time_remaining_display = " | ET " # initialize the time display - m = (time_total - time_elapsed) // 60 # estimated time in minutes - m *= speed_factor # correct for printing time - m = int(m) - h, m = divmod(m, 60) # convert to hours and minutes - # add the time remaining to the display_text - if h > 0: # if it's more than 1 hour left, display format = xhxxm - time_remaining_display += str(h) + "h" - if m < 10: # add trailing zero if necessary - time_remaining_display += "0" - time_remaining_display += str(m) + "m" + if line.startswith(";LAYER_COUNT:"): + max_layer = line + max_layer = max_layer.split(":")[1] + if self.getSettingValueByKey("startNum") == 0: + max_layer = str(int(max_layer) - 1) + if line.startswith(";LAYER:"): + if self.getSettingValueByKey("maxlayer"): + display_text += "/" + max_layer + self.m118_text += "/" + max_layer + if not self.getSettingValueByKey("scroll"): + display_text += "|" + file_name + self.m118_text += " | " + file_name else: - time_remaining_display += str(m) + "m" - display_text += time_remaining_display - # find time_elapsed at the end of the layer (used to calculate the remaining time of the next layer) - if not current_layer == number_of_layers: - for line_index in range(len(lines) - 1, -1, -1): - line = lines[line_index] - if line.startswith(";TIME_ELAPSED:"): - # update time_elapsed for the NEXT layer and exit the loop - time_elapsed = int(float(line.split(":")[1])) - break - # insert the text AFTER the first line of the layer (in case other scripts use ";LAYER:") - for l_index, line in enumerate(lines): - if line.startswith(";LAYER:"): - if add_m117_line: - lines[l_index] += "\nM117 " + display_text - if add_m118_line: - a1_str = "" - p0_str = "" - if add_m118_a1: - a1_str = "A1 " - if add_m118_p0: - p0_str = "P0 " - lines[l_index] += "\nM118 " + a1_str + p0_str + display_text - # add M73 line - if display_remaining_time: - mins = int(60 * h + m) - if add_m73_line and (add_m73_time or add_m73_percent): - m73_str = "" - if m73_time and display_remaining_time: - m73_str += " R{}".format(mins) - if m73_percent: - m73_str += " P" + str(round(int(current_layer) / int(number_of_layers) * 100)) - lines[l_index] += "\nM73" + m73_str - break - # overwrite the layer with the modified layer - data[layer_index] = "\n".join(lines) - - # If enabled then change the ET to TP for 'Time To Pause' - if bool(self.getSettingValueByKey("countdown_to_pause")): - time_list = [] - time_list.append("0") - time_list.append("0") - this_time = 0 - pause_index = 1 - - # Get the layer times - for num in range(2,len(data) - 1): - layer = data[num] - lines = layer.split("\n") - for line in lines: - if line.startswith(";TIME_ELAPSED:"): - this_time = (float(line.split(":")[1]))*speed_factor - time_list.append(str(this_time)) - for p_cmd in pause_cmd: - if p_cmd in layer: - for qnum in range(num - 1, pause_index, -1): - time_list[qnum] = str(float(this_time) - float(time_list[qnum])) + "P" - pause_index = num-1 - break - - # Make the adjustments to the M117 (and M118) lines that are prior to a pause - for num in range (2, len(data) - 1,1): - layer = data[num] - lines = layer.split("\n") - for line in lines: - try: - if line.startswith("M117") and "|" in line and "P" in time_list[num]: - time_to_go = self.get_time_to_go(time_list[num]) - M117_line = line.split("|")[0] + "| TP " + time_to_go - layer = layer.replace(line, M117_line) - if line.startswith("M118") and "|" in line and "P" in time_list[num]: - time_to_go = self.get_time_to_go(time_list[num]) - M118_line = line.split("|")[0] + "| TP " + time_to_go - layer = layer.replace(line, M118_line) - except: - continue - data[num] = layer - if bool(self.getSettingValueByKey("enable_end_message")): - message_str = self.message_to_user(speed_factor) - Message(title = "[Display Info on LCD] - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() + if not self.getSettingValueByKey("scroll"): + display_text += "|" + file_name + "!" + self.m118_text += " | " + file_name + "!" + else: + display_text += "!" + self.m118_text += "!" + line_index = lines.index(line) + if self.add_m117_line: + lines.insert(line_index + 1, display_text) + if self.add_m118_line: + if not (self.add_m118_p0 and self.add_m118_a1): + self.m118_str = self.m118_text + if self.add_m118_a1 and not self.add_m118_p0: + self.m118_str = self.m118_text.replace("M118 ","M118 A1 ") + if self.add_m118_p0 and not self.add_m118_a1: + self.m118_str = self.m118_text.replace("M118 ","M118 P0 ") + if self.add_m118_p0 and self.add_m118_a1: + self.m118_str = self.m118_text.replace("M118 ","M118 A1 P0 ") + lines.insert(line_index + 2, self.m118_str) + i += 1 + final_lines = "\n".join(lines) + data[layer_index] = final_lines + if bool(self.getSettingValueByKey("enable_end_message")): + message_str = self._message_to_user(self.getSettingValueByKey("speed_factor") / 100) + Message(title = "Display Info on LCD - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() return data - def message_to_user(self, speed_factor: float): + # This is from 'Show Progress on LCD' + def _display_progress(self, data: str) -> str: + # Add some print settings to the start of the gcode + data[0] = self._add_stats(data) + # Get settings + print_sequence = Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") + display_total_layers = self.getSettingValueByKey("display_total_layers") + display_remaining_time = self.getSettingValueByKey("display_remaining_time") + speed_factor = self.getSettingValueByKey("speed_factor") / 100 + m73_time = False + m73_percent = False + if self.add_m73_line and self.add_m73_time: + m73_time = True + if self.add_m73_line and self.add_m73_percent: + m73_percent = True + if self.add_m73_line: + data[1] = "M75\n" + data[1] + data[len(data)-1] += "M77\n" + # Initialize some variables + first_layer_index = 0 + time_total = int(data[0].split(";TIME:")[1].split("\n")[0]) + number_of_layers = 0 + time_elapsed = 0 + + # If at least one of the settings is disabled, there is enough room on the display to display "layer" + first_section = data[0] + lines = first_section.split("\n") + for line in lines: + if line.startswith(";TIME:"): + tindex = lines.index(line) + cura_time = int(line.split(":")[1]) + print_time = cura_time * speed_factor + hhh = print_time/3600 + hr = round(hhh // 1) + mmm = round((hhh % 1) * 60) + orig_hhh = cura_time/3600 + orig_hr = round(orig_hhh // 1) + orig_mmm = math.floor((orig_hhh % 1) * 60) + if self.add_m118_line: + lines.insert(len(lines) - 2, f"M118 Adjusted Print Time: {hr} hr {mmm} min") + if self.add_m117_line: + lines.insert(len(lines) - 2, f"M117 ET {hr} hr {mmm} min") + # Add M73 line at beginning + mins = int(60 * hr + mmm) + if self.add_m73_line and (self.add_m73_time or self.add_m73_percent): + if m73_time: + self.m73_str += " R{}".format(mins) + if m73_percent: + self.m73_str += " P0" + lines.insert(tindex + 4, "M73" + self.m73_str) + # If Countdown to pause is enabled then count the pauses + pause_str = "" + if bool(self.getSettingValueByKey("countdown_to_pause")): + pause_count = 0 + pause_setting = self.getSettingValueByKey("pause_cmd").upper() + if pause_setting != "": + pause_cmd = [] + if "," in pause_setting: + pause_cmd = pause_setting.split(",") + else: + pause_cmd.append(pause_setting) + for q in range(0, len(pause_cmd)): + pause_cmd[q] = "\n" + pause_cmd[q] + for num in range(2,len(data) - 2, 1): + for q in range(0,len(pause_cmd)): + if pause_cmd[q] in data[num]: + pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) + pause_str = f"with {pause_count} pause(s)" + else: + pause_str = "" + # This line goes in to convert seconds to hours and minutes + lines.insert(tindex + 1, f";Cura Time Estimate: {orig_hr}hr {orig_mmm}min {pause_str}") + data[0] = "\n".join(lines) + if self.add_m117_line: + data[len(data)-1] += "M117 Orig Cura Est " + str(orig_hr) + "hr " + str(orig_mmm) + "min\n" + if self.add_m118_line: + data[len(data)-1] += "M118 Est w/FudgeFactor " + str(speed_factor * 100) + "% was " + str(hr) + "hr " + str(mmm) + "min\n" + if not display_total_layers or not display_remaining_time: + base_display_text = "layer " + else: + base_display_text = "" + layer = data[len(data)-1] + data[len(data)-1] = layer.replace(";End of Gcode" + "\n", "") + data[len(data)-1] += ";End of Gcode" + "\n" + # Search for the number of layers and the total time from the start code + for index in range(len(data)): + data_section = data[index] + # We have everything we need, save the index of the first layer and exit the loop + if ";LAYER:" in data_section: + first_layer_index = index + break + else: + for line in data_section.split("\n"): + if line.startswith(";LAYER_COUNT:"): + number_of_layers = int(line.split(":")[1]) + if print_sequence == "one_at_a_time": + number_of_layers = 1 + for lay in range(2,len(data)-1,1): + if ";LAYER:" in data[lay]: + number_of_layers += 1 + elif line.startswith(";TIME:"): + time_total = int(line.split(":")[1]) + # for all layers... + current_layer = 0 + for layer_counter in range(len(data)-2): + current_layer += 1 + layer_index = first_layer_index + layer_counter + display_text = base_display_text + display_text += str(current_layer) + # create a list where each element is a single line of code within the layer + lines = data[layer_index].split("\n") + if not ";LAYER:" in data[layer_index]: + current_layer -= 1 + continue + # add the total number of layers if this option is checked + if display_total_layers: + display_text += "/" + str(number_of_layers) + # if display_remaining_time is checked, it is calculated in this loop + if display_remaining_time: + time_remaining_display = " | ET " # initialize the time display + m = (time_total - time_elapsed) // 60 # estimated time in minutes + m *= speed_factor # correct for printing time + m = int(m) + h, m = divmod(m, 60) # convert to hours and minutes + # add the time remaining to the display_text + if h > 0: # if it's more than 1 hour left, display format = xhxxm + time_remaining_display += str(h) + "h" + if m < 10: # add trailing zero if necessary + time_remaining_display += "0" + time_remaining_display += str(m) + "m" + else: + time_remaining_display += str(m) + "m" + display_text += time_remaining_display + # find time_elapsed at the end of the layer (used to calculate the remaining time of the next layer) + if not current_layer == number_of_layers: + for line_index in range(len(lines) - 1, -1, -1): + line = lines[line_index] + if line.startswith(";TIME_ELAPSED:"): + # update time_elapsed for the NEXT layer and exit the loop + time_elapsed = int(float(line.split(":")[1])) + break + # insert the text AFTER the first line of the layer (in case other scripts use ";LAYER:") + for l_index, line in enumerate(lines): + if line.startswith(";LAYER:"): + if self.add_m117_line: + lines[l_index] += "\nM117 " + display_text + if self.add_m118_line: + a1_str = "" + p0_str = "" + if self.add_m118_a1: + a1_str = "A1 " + if self.add_m118_p0: + p0_str = "P0 " + lines[l_index] += "\nM118 " + a1_str + p0_str + display_text + # add M73 line + if display_remaining_time: + mins = int(60 * h + m) + if self.add_m73_line and (self.add_m73_time or self.add_m73_percent): + self.m73_str = "" + if m73_time and display_remaining_time: + self.m73_str += " R{}".format(mins) + if m73_percent: + self.m73_str += " P" + str(round(int(current_layer) / int(number_of_layers) * 100)) + lines[l_index] += "\nM73" + self.m73_str + break + # overwrite the layer with the modified layer + data[layer_index] = "\n".join(lines) + + # If enabled then change the ET to TP for 'Time To Pause' + if bool(self.getSettingValueByKey("countdown_to_pause")): + time_list = [] + time_list.append("0") + time_list.append("0") + this_time = 0 + pause_index = 1 + + # Get the layer times + for num in range(2,len(data) - 1): + layer = data[num] + lines = layer.split("\n") + for line in lines: + if line.startswith(";TIME_ELAPSED:"): + this_time = (float(line.split(":")[1]))*speed_factor + time_list.append(str(this_time)) + for p_cmd in pause_cmd: + if p_cmd in layer: + for qnum in range(num - 1, pause_index, -1): + time_list[qnum] = str(float(this_time) - float(time_list[qnum])) + "P" + pause_index = num-1 + break + + # Make the adjustments to the M117 (and M118) lines that are prior to a pause + for num in range (2, len(data) - 1,1): + layer = data[num] + lines = layer.split("\n") + for line in lines: + try: + if line.startswith("M117") and "|" in line and "P" in time_list[num]: + time_to_go = self._get_time_to_go(time_list[num]) + M117_line = line.split("|")[0] + "| TP " + time_to_go + layer = layer.replace(line, M117_line) + if line.startswith("M118") and "|" in line and "P" in time_list[num]: + time_to_go = self._get_time_to_go(time_list[num]) + M118_line = line.split("|")[0] + "| TP " + time_to_go + layer = layer.replace(line, M118_line) + except: + continue + data[num] = layer + if bool(self.getSettingValueByKey("enable_end_message")): + message_str = self._message_to_user(speed_factor) + Message(title = "[Display Info on LCD] - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() + return data + + def _message_to_user(self, speed_factor: float): # Message the user of the projected finish time of the print print_time = Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601) print_start_time = self.getSettingValueByKey("print_start_time") @@ -600,8 +596,8 @@ class DisplayInfoOnLCD(Script): adjusted_str = "Adjusted Time Estimate..." + str(time_change) finish_str = week_day + " " + str(mo_str) + " " + str(new_time.strftime("%d")) + ", " + str(new_time.strftime("%Y")) + " at " + str(show_hr) + str(new_time.strftime("%M")) + str(show_ampm) return finish_str, estimate_str, adjusted_str, print_start_str - - def get_time_to_go(self, time_str: str): + + def _get_time_to_go(self, time_str: str): alt_time = time_str[:-1] hhh = int(float(alt_time) / 3600) if hhh > 0: @@ -613,4 +609,36 @@ class DisplayInfoOnLCD(Script): mmm = str(round(mmm)) + "m" time_to_go = str(hhr) + str(mmm) if hhr == "": time_to_go = time_to_go + str(sss) + "s" - return time_to_go \ No newline at end of file + return time_to_go + + def _add_stats(self, data: str) -> str: + global_stack = Application.getInstance().getGlobalContainerStack() + # Create a list of the models in the file + model_list = [] + for mdex, layer in enumerate(data): + layer = data[mdex].split("\n") + for line in layer: + if line.startswith(";MESH:") and "NONMESH" not in line: + model_name = line.split(":")[1] + if not model_name in model_list: + model_list.append(model_name) + # Add the Initial Layer Height just below Layer Height in data[0]model_list = [] + extruder_count = global_stack.getProperty("machine_extruder_count", "value") + init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) + nozzle_size_line = ";Nozzle Size (T0): " + str(global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")) + filament_type = "\n;Filament Type (T0): " + str(global_stack.extruderList[0].getProperty("material_type", "value")) + print_temperature_line = ";Print Temperature (T0): " + str(global_stack.extruderList[0].getProperty("material_print_temperature", "value")) + if extruder_count > 1: + nozzle_size_line += "\n;Nozzle Size (T1): " + str(global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")) + filament_type += "\n;Filament type (T1): " + str(global_stack.extruderList[1].getProperty("material_type", "value")) + print_temperature_line += "\n;Print Temperature (T1): " + str(global_stack.extruderList[1].getProperty("material_print_temperature", "value")) + lines = data[0].split("\n") + for index, line in enumerate(lines): + if line.startswith(";Layer height:"): + lines[index] += "\n" + init_layer_hgt_line + "\n" + nozzle_size_line + "\n" + print_temperature_line + if line.startswith(";Filament used"): + lines[index] += filament_type + if "MINX" in line or "MIN.X" in line: + # Add the model list + lines[index - 1] += f"\n;Model List: {str(model_list)}" + return "\n".join(lines) \ No newline at end of file From 017a05fff7067a4f8740c482dc2dd6da1cc0ddc1 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:02:59 -0500 Subject: [PATCH 12/98] Update DisplayInfoOnLCD.py Changed the fialment type 'get' from: 'getProperty' to 'material.getMetaDataEntry' --- .../PostProcessingPlugin/scripts/DisplayInfoOnLCD.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 2177f91f3b..b19f222f2a 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -202,7 +202,7 @@ class DisplayInfoOnLCD(Script): "countdown_to_pause": { "label": "Countdown to Pauses", - "description": "This must run AFTER any script that adds a pause. Instead of the remaining print time the LCD will show the estimated time to the next layer that has a pause (TP).", + "description": "This must run AFTER any script that adds a pause. Instead of the remaining print time the LCD will show the estimated time to the next layer that has a pause (TP). Countdown to Pause is not available when in One-at-a-Time' mode.", "type": "bool", "default_value": false, "enabled": "display_option == 'display_progress' and enable_countdown and display_remaining_time" @@ -252,7 +252,8 @@ class DisplayInfoOnLCD(Script): else: data = self._display_progress(data) return data - # This is from the original 'Display Filename and Layer on LCD' + + # This is from the original 'Display Filename and Layer on LCD' def _display_filename_layer(self, data: str) -> str: data[0] = self._add_stats(data) max_layer = 0 @@ -385,7 +386,7 @@ class DisplayInfoOnLCD(Script): for q in range(0,len(pause_cmd)): if pause_cmd[q] in data[num]: pause_count += data[num].count(pause_cmd[q], 0, len(data[num])) - pause_str = f"with {pause_count} pause(s)" + pause_str = f"with {pause_count} pause" + ("s" if pause_count > 1 else "") else: pause_str = "" # This line goes in to convert seconds to hours and minutes @@ -626,11 +627,11 @@ class DisplayInfoOnLCD(Script): extruder_count = global_stack.getProperty("machine_extruder_count", "value") init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) nozzle_size_line = ";Nozzle Size (T0): " + str(global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")) - filament_type = "\n;Filament Type (T0): " + str(global_stack.extruderList[0].getProperty("material_type", "value")) + filament_type = "\n;Filament Type (T0): " + str(global_stack.extruderList[0].material.getMetaDataEntry("material", "")) print_temperature_line = ";Print Temperature (T0): " + str(global_stack.extruderList[0].getProperty("material_print_temperature", "value")) if extruder_count > 1: nozzle_size_line += "\n;Nozzle Size (T1): " + str(global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")) - filament_type += "\n;Filament type (T1): " + str(global_stack.extruderList[1].getProperty("material_type", "value")) + filament_type += "\n;Filament type (T1): " + str(global_stack.extruderList[1].material.getMetaDataEntry("material", "")) print_temperature_line += "\n;Print Temperature (T1): " + str(global_stack.extruderList[1].getProperty("material_print_temperature", "value")) lines = data[0].split("\n") for index, line in enumerate(lines): From cf52772fd19043d20fe2110ef4a539172ff32238 Mon Sep 17 00:00:00 2001 From: Jeremy Salwen Date: Fri, 21 Mar 2025 08:42:43 -0400 Subject: [PATCH 13/98] Enable more retraction settings to be set per-model These retraction settings were already supported on a per-object basis on the backend by https://github.com/Ultimaker/CuraEngine/pull/1769, but were not enabled by the corresponding frontend PR at the time. This helps get incrementally closer to exposing per-model settings https://github.com/Ultimaker/Cura/issues/3193 --- resources/definitions/fdmprinter.def.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b42b5c417a..ce43289636 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -4553,7 +4553,7 @@ "minimum_value_warning": "-0.0001", "maximum_value_warning": "10.0", "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"", - "settable_per_mesh": false, + "settable_per_mesh": true, "settable_per_extruder": true }, "retraction_speed": @@ -4643,7 +4643,7 @@ "maximum_value": 999999999, "type": "int", "enabled": "retraction_enable", - "settable_per_mesh": false, + "settable_per_mesh": true, "settable_per_extruder": true }, "retraction_extrusion_window": @@ -4657,7 +4657,7 @@ "maximum_value_warning": "retraction_amount * 2", "value": "retraction_amount", "enabled": "retraction_enable", - "settable_per_mesh": false, + "settable_per_mesh": true, "settable_per_extruder": true }, "retraction_combing": From 416b159a7ea3c82516aaf88cf6cd145fb1d2e921 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 22 Mar 2025 11:18:30 -0400 Subject: [PATCH 14/98] Update DisplayInfoOnLCD.py Added the "time to pause" to the user message. Update DisplayInfoOnLCD.py update --- .../scripts/DisplayInfoOnLCD.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index b19f222f2a..49222882bc 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -488,8 +488,8 @@ class DisplayInfoOnLCD(Script): data[layer_index] = "\n".join(lines) # If enabled then change the ET to TP for 'Time To Pause' + time_list = [] if bool(self.getSettingValueByKey("countdown_to_pause")): - time_list = [] time_list.append("0") time_list.append("0") this_time = 0 @@ -528,11 +528,11 @@ class DisplayInfoOnLCD(Script): continue data[num] = layer if bool(self.getSettingValueByKey("enable_end_message")): - message_str = self._message_to_user(speed_factor) + message_str = self._message_to_user(data, speed_factor, pause_cmd) Message(title = "[Display Info on LCD] - Estimated Finish Time", text = message_str[0] + "\n\n" + message_str[1] + "\n" + message_str[2] + "\n" + message_str[3]).show() return data - def _message_to_user(self, speed_factor: float): + def _message_to_user(self, data: str, speed_factor: float, pause_cmd: str) -> str: # Message the user of the projected finish time of the print print_time = Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601) print_start_time = self.getSettingValueByKey("print_start_time") @@ -596,6 +596,15 @@ class DisplayInfoOnLCD(Script): estimate_str = "Cura Time Estimate.........." + str(print_time) adjusted_str = "Adjusted Time Estimate..." + str(time_change) finish_str = week_day + " " + str(mo_str) + " " + str(new_time.strftime("%d")) + ", " + str(new_time.strftime("%Y")) + " at " + str(show_hr) + str(new_time.strftime("%M")) + str(show_ampm) + + # If there are pauses and if countdown is enabled, then add the time-to-pause to the message. + if bool(self.getSettingValueByKey("countdown_to_pause")): + num = 1 + for layer in data: + for p_cmd in pause_cmd: + if p_cmd in layer or "Do the actual pause" in layer: + adjusted_str += "\n" + self._get_time_to_go(layer.split("TIME_ELAPSED:")[1].split("\n")[0]) + " ET from start to pause #" + str(num) + num += 1 return finish_str, estimate_str, adjusted_str, print_start_str def _get_time_to_go(self, time_str: str): @@ -623,7 +632,7 @@ class DisplayInfoOnLCD(Script): model_name = line.split(":")[1] if not model_name in model_list: model_list.append(model_name) - # Add the Initial Layer Height just below Layer Height in data[0]model_list = [] + # Add some settings to data[0] extruder_count = global_stack.getProperty("machine_extruder_count", "value") init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) nozzle_size_line = ";Nozzle Size (T0): " + str(global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")) From ee5c37bd808d4e15dea6306b5deaf0038f9885df Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 23 Mar 2025 07:09:04 -0400 Subject: [PATCH 15/98] Update DisplayInfoOnLCD.py Fixed the m118_str/m118_text confusion. --- .../scripts/DisplayInfoOnLCD.py | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 49222882bc..6315e28af1 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -241,7 +241,6 @@ class DisplayInfoOnLCD(Script): self.add_m118_line = self.getSettingValueByKey("add_m118_line") self.add_m118_a1 = self.getSettingValueByKey("add_m118_a1") self.add_m118_p0 = self.getSettingValueByKey("add_m118_p0") - self.m118_str = "M118 " self.m118_text = "M118 " self.add_m73_line = self.getSettingValueByKey("add_m73_line") self.add_m73_time = self.getSettingValueByKey("add_m73_time") @@ -302,15 +301,11 @@ class DisplayInfoOnLCD(Script): if self.add_m117_line: lines.insert(line_index + 1, display_text) if self.add_m118_line: - if not (self.add_m118_p0 and self.add_m118_a1): - self.m118_str = self.m118_text - if self.add_m118_a1 and not self.add_m118_p0: - self.m118_str = self.m118_text.replace("M118 ","M118 A1 ") - if self.add_m118_p0 and not self.add_m118_a1: - self.m118_str = self.m118_text.replace("M118 ","M118 P0 ") - if self.add_m118_p0 and self.add_m118_a1: - self.m118_str = self.m118_text.replace("M118 ","M118 A1 P0 ") - lines.insert(line_index + 2, self.m118_str) + if self.add_m118_a1: + self.m118_text = self.m118_text.replace("M118 ","M118 A1 ") + if self.add_m118_p0: + self.m118_text = self.m118_text.replace("M118 ","M118 P0 ") + lines.insert(line_index + 2, self.m118_text) i += 1 final_lines = "\n".join(lines) data[layer_index] = final_lines @@ -466,13 +461,12 @@ class DisplayInfoOnLCD(Script): if self.add_m117_line: lines[l_index] += "\nM117 " + display_text if self.add_m118_line: - a1_str = "" - p0_str = "" + m118_str = "\nM118 " if self.add_m118_a1: - a1_str = "A1 " + m118_str += "A1 " if self.add_m118_p0: - p0_str = "P0 " - lines[l_index] += "\nM118 " + a1_str + p0_str + display_text + m118_str += "P0 " + lines[l_index] += m118_str + display_text # add M73 line if display_remaining_time: mins = int(60 * h + m) From f0d198507a6acd7d1852eb06cecc8813eab96f43 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 23 Mar 2025 07:30:23 -0400 Subject: [PATCH 16/98] Update DisplayInfoOnLCD.py Requested changes --- .../scripts/DisplayInfoOnLCD.py | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 6315e28af1..85b9744856 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -38,7 +38,6 @@ import time import datetime import math from UM.Message import Message -import re class DisplayInfoOnLCD(Script): @@ -48,7 +47,11 @@ class DisplayInfoOnLCD(Script): if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "all_at_once": enable_countdown = True self._instance.setProperty("enable_countdown", "value", enable_countdown) - except: + except AttributeError: + # Handle cases where the global container stack or its properties are not accessible + pass + except KeyError: + # Handle cases where the "print_sequence" property is missing pass def getSettingDataString(self): @@ -461,12 +464,12 @@ class DisplayInfoOnLCD(Script): if self.add_m117_line: lines[l_index] += "\nM117 " + display_text if self.add_m118_line: - m118_str = "\nM118 " + m118_text = "\nM118 " if self.add_m118_a1: - m118_str += "A1 " + m118_text += "A1 " if self.add_m118_p0: - m118_str += "P0 " - lines[l_index] += m118_str + display_text + m118_text += "P0 " + lines[l_index] += m118_text + display_text # add M73 line if display_remaining_time: mins = int(60 * h + m) @@ -589,7 +592,7 @@ class DisplayInfoOnLCD(Script): print_start_str = "Print Start Time.................Now" estimate_str = "Cura Time Estimate.........." + str(print_time) adjusted_str = "Adjusted Time Estimate..." + str(time_change) - finish_str = week_day + " " + str(mo_str) + " " + str(new_time.strftime("%d")) + ", " + str(new_time.strftime("%Y")) + " at " + str(show_hr) + str(new_time.strftime("%M")) + str(show_ampm) + finish_str = f"{week_day} {mo_str} {new_time.strftime('%d')}, {new_time.strftime('%Y')} at {show_hr}{new_time.strftime('%M')}{show_ampm}" # If there are pauses and if countdown is enabled, then add the time-to-pause to the message. if bool(self.getSettingValueByKey("countdown_to_pause")): @@ -602,17 +605,20 @@ class DisplayInfoOnLCD(Script): return finish_str, estimate_str, adjusted_str, print_start_str def _get_time_to_go(self, time_str: str): + """ + Converts a time string in seconds to a human-readable format (e.g., "2h30m"). + :param time_str: The time string in seconds. + :return: A formatted string representing the time. + """ alt_time = time_str[:-1] - hhh = int(float(alt_time) / 3600) - if hhh > 0: - hhr = str(hhh) + "h" - else: - hhr = "" - mmm = ((float(alt_time) / 3600) - (int(float(alt_time) / 3600))) * 60 - sss = int((mmm - int(mmm)) * 60) - mmm = str(round(mmm)) + "m" - time_to_go = str(hhr) + str(mmm) - if hhr == "": time_to_go = time_to_go + str(sss) + "s" + total_seconds = float(alt_time) + hours = int(total_seconds // 3600) + minutes = int((total_seconds % 3600) // 60) + seconds = int(total_seconds % 60) + time_to_go = f"{hours}h" if hours > 0 else "" + time_to_go += f"{minutes}m" + if hours == 0: + time_to_go += f"{seconds}s" return time_to_go def _add_stats(self, data: str) -> str: From f6469032c66e7797d585b01ae594e577b57b754c Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 6 Apr 2025 11:06:47 -0400 Subject: [PATCH 17/98] Create ZHopOnTravel.py New script to customize Z-hops in a gcode file. --- .../scripts/ZHopOnTravel.py | 544 ++++++++++++++++++ 1 file changed, 544 insertions(+) create mode 100644 plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py new file mode 100644 index 0000000000..82a60239be --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -0,0 +1,544 @@ +""" + By GregValiant (Greg Foresi) July of 2024 + Insert Z-hops for travel moves regardless of retraction. The 'Layer Range' (or comma delimited 'Layer List'), 'Minimum Travel Distance' and the 'Hop-Height' are user defined. + This script is compatible with Z-hops enabled in Cura. If Z-hops are enabled: There will occasionally be a hop on top of a hop, but the 'resume Z height' will be correct. + It is not necessary to have "retractions" enabled. If retractions are disabled in Cura you may elect to have this script add retractions. The Cura retraction distance and speeds are used. + + Compatibility: + Multi-Extruder printers: NOTE - The retraction settings for a multi-extruder printer are always taken from Extruder 1 (T0). + There is support for: + Absolute and Relative Extrusion + Firmware Retraction + Extra Prime Amount > 0 + Adaptive Layers + G2/G3 arc moves are supported but are treated as straight line moves. + + Incompatibility: + "One at a Time" mode is not supported + + Please Note: + This is a slow running post processor as it must check the cumulative distances of all travel moves (G0 moves) in the range of layers. +""" + +from UM.Application import Application +from ..Script import Script +import re +from UM.Message import Message +import math +from UM.Logger import Logger + +class ZHopOnTravel(Script): + + def getSettingDataString(self): + return """{ + "name": "Z-Hop on Travel", + "key": "ZHopOnTravel", + "metadata": {}, + "version": 2, + "settings": { + "zhop_travel_enabled": { + "label": "Enable script", + "description": "Enables the script so it will run. 'One-at-a-Time' is not supported. This script is slow running because it must check the length of all travel moves in your layer range. ", + "type": "bool", + "default_value": true, + "enabled": true + }, + "list_or_range": + { + "label": "Layer List or Range", + "description": "Using a list allows you to call out one or more individual layers delimited by commas (Ex: 23,29,35). A layer range has a start layer and an end layer.", + "type": "enum", + "options": { + "range_of_layers": "Range of Layers", + "list_of_layers": "Layer List" }, + "default_value": "range_of_layers", + "enabled": "zhop_travel_enabled" + }, + "layers_of_interest": + { + "label": "List of Layers", + "description": "Use the Cura preview layer numbers. Enter 'individual layer' numbers delimited by commas. Spaces are not allowed.", + "type": "str", + "default_value": "10,28,31,54", + "enabled": "zhop_travel_enabled and list_or_range == 'list_of_layers'" + }, + "start_layer": { + "label": "Start Layer", + "description": "Layer number to start the changes at. Use the Cura preview layer numbers. The changes will start at the start of the layer.", + "unit": "Lay# ", + "type": "int", + "default_value": 1, + "minimum_value": "1", + "enabled": "zhop_travel_enabled and list_or_range == 'range_of_layers'" + }, + "end_layer": { + "label": "End Layer", + "description": "Enter '-1' to indicate the top layer, or enter a specific Layer number from the Cura preview. The changes will end at the end of this layer.", + "unit": "Lay# ", + "type": "int", + "default_value": -1, + "minimum_value": "-1", + "enabled": "zhop_travel_enabled and list_or_range == 'range_of_layers'" + }, + "hop_height": { + "label": "Z-Hop Height", + "description": "I refuse to provide a description for this.", + "unit": "mm ", + "type": "float", + "default_value": 0.5, + "minimum_value": "0", + "maximum_value_warning": 5, + "enabled": "zhop_travel_enabled" + }, + "min_travel_dist": { + "label": "Minimum Travel Distance", + "description": "Travel distances longer than this will cause a Z-Hop to occur.", + "unit": "mm ", + "type": "int", + "default_value": 10, + "minimum_value": "1", + "maximum_value": "200", + "enabled": "zhop_travel_enabled" + }, + "add_retract": { + "label": "Add retraction when necessary", + "description": "Depending on your travel settings there may not be a retraction prior to a travel move. When enabled, if there is no retraction prior to an added z-hop, this setting will add a retraction before the Z-hop, and a prime after returning to the working layer height. If retractions are disabled in Cura this setting is still available and will add retractions based on the Cura settings for Retraction Amount and Retract and Prime Speeds. All retraction settings are from the settings for 'Extruder 1' regardless of the number of extruders.", + "type": "bool", + "default_value": false, + "enabled": "zhop_travel_enabled" + }, + "infill_only": { + "label": "Add Z-hops to Infill Only", + "description": "Only add Z-hops to 'Infill' within the layer range.", + "type": "bool", + "default_value": false, + "enabled": "zhop_travel_enabled" + } + } + }""" + + def execute(self, data): + """ + The script will parse the gcode and check the cumulative length of travel moves. When they exceed the "min_travel_dist" then hop-ups are added before the travel and at the end of the travel. The user may select to add retractions/primes if there were none. + params: + layer_list: The list of 'layers-of-interest' for both 'Layer Range' and a 'Layer List'. + index_list: A list of the indexes within the data[] for the layers-of-interest. + self._cur_z: The variable used to track the working Z-height through the gcode + self._add_retract: User setting of whether to insure a retraction at inserted Z-hops + self._is_retracted: Whether a retraction has occurred prior to the added Z-hop + min_travel_dist: The user setting for the minimum distance of travel for Z-hops to be inserted + start_index: The index (in data[]) of the first layer-of-interest. The Z-hops start at the beginning of this layer. + end_index: The index (in data[]) of the last layer-of-interest. The Z-hops end at the end of this layer. + hop_up_lines: The string to insert for 'Hop up' + hop_down_lines: The string to insert for 'Hop down' + hop_start: The index within a layer where a 'Hop up' is inserted + hop_end: The index within a layer where a 'Hop down' is inserted + extra_prime_dist: Is calculated from the Cura extra_prime_volume and if > 0 is used to reset the E location prior to unretracting. + """ + + # Exit if the script is not enabled + if not self.getSettingValueByKey("zhop_travel_enabled"): + data[0] += "; [Z-Hop on Travel] Not enabled\n" + Logger.log("i", "[Z-Hop on Travel] Not enabled") + return data + + # Exit if the gcode has already been post-processed + if ";POSTPROCESSED" in data[0]: + return data + + # Define the global_stack to access the Cura settings + global_stack = Application.getInstance().getGlobalContainerStack() + + # Exit if the Print Sequence is One-at-a-Time + if global_stack.getProperty("print_sequence", "value") == "one_at_a_time": + Message(title = "[ZHop On Travel]", text = "Is not compatible with 'One at a Time' print sequence.").show() + data[0] += "; [ZHop On Travel] did not run because One at a Time is enabled" + return data + + # Define some variables + extruder = global_stack.extruderList + speed_zhop = extruder[0].getProperty("speed_z_hop", "value") * 60 + speed_travel = extruder[0].getProperty("speed_travel", "value") * 60 + retraction_enabled = extruder[0].getProperty("retraction_enable", "value") + retraction_amount = extruder[0].getProperty("retraction_amount", "value") + retract_speed = int(extruder[0].getProperty("retraction_retract_speed", "value")) * 60 + prime_speed = int(extruder[0].getProperty("retraction_prime_speed", "value")) * 60 + firmware_retract = global_stack.getProperty("machine_firmware_retract", "value") + relative_extrusion = global_stack.getProperty("relative_extrusion", "value") + self._cur_z = float(global_stack.getProperty("layer_height_0", "value")) + filament_dia = extruder[0].getProperty("material_diameter", "value") + extra_prime_vol = extruder[0].getProperty("retraction_extra_prime_amount", "value") + extra_prime_dist = extra_prime_vol / (math.pi * (filament_dia / 2)**2) + self._add_retract = self.getSettingValueByKey("add_retract") + min_travel_dist = self.getSettingValueByKey("min_travel_dist") + hop_height = round(self.getSettingValueByKey("hop_height"),2) + list_or_range = self.getSettingValueByKey("list_or_range") + infill_only = self.getSettingValueByKey("infill_only") + layer_list = [] + index_list = [] + + # Get either the 'range_of_layers' or the 'list_of_layers' and convert them to 'layer_list' and then 'index_list' + if list_or_range == "list_of_layers": + layer_string = self.getSettingValueByKey("layers_of_interest") + layer_list = layer_string.split(",") + layer_list.sort() + for layer in layer_list: + for num in range(2, len(data) - 1): + if ";LAYER:" + str(int(layer) - 1) + "\n" in data[num]: + index_list.append(num) + start_index = index_list[0] + end_index = index_list[len(index_list) - 1] + + elif list_or_range == "range_of_layers": + start_layer = self.getSettingValueByKey("start_layer") + end_layer = self.getSettingValueByKey("end_layer") + + # Get the indexes for the start and end layers + start_index = 2 + for num in range(1, len(data) - 1): + if ";LAYER:" + str(start_layer - 1) + "\n" in data[num]: + start_index = num + break + if end_layer == -1: + if retraction_enabled: + end_index = len(data) - 3 + else: + end_index = len(data) - 2 + elif end_layer != -1: + for num in range(1, len(data) - 1): + if ";LAYER:" + str(end_layer) + "\n" in data[num]: + end_layer = data[num].splitlines()[0].split(":")[1] + end_index = num + break + for num in range(start_index, end_index): + index_list.append(num) + + # Track the Z up to the starting layer + for num in range(1, start_index): + lines = data[num].splitlines() + for line in lines: + if " Z" in line and self.getValue(line, "Z"): + self._cur_z = self.getValue(line, "Z") + + # Use 'start_here' to avoid a zhop on the first move of the initial layer because a double-retraction may occur. + if start_index == 2: + start_here = False + else: + start_here = True + + # Initialize variables + self._is_retracted = False + hop_up_lines = "" + hop_down_lines = "" + hop_start = 0 + hop_end = 0 + self._cur_x = 0.0 + self._cur_y = 0.0 + self._prev_x = 0.0 + self._prev_y = 0.0 + self._cur_e = 0.0 + self._prev_e = 0.0 + cmd_list = ["G0 ", "G1 ", "G2 ", "G3 "] + self.reset_type = 0 + + # Keep track of the axes locations if the start layer > layer:0 + if start_index > 2: + self._track_all_axes(data, cmd_list, start_index, relative_extrusion) + + # Make the insertions + in_the_infill = False + for num in index_list: + lines = data[num].splitlines() + for index, line in enumerate(lines): + if num == 2: + if line.startswith(";TYPE"): + start_here = True + if line.startswith(";") and in_the_infill == True: + in_the_infill = False + if line.startswith(";TYPE:FILL"): + in_the_infill = True + if line.startswith("G92") and " E" in line: + self._cur_e = self.getValue(line, "E") + self._prev_e = self._cur_e + continue + # Get the XYZ values from movement commands + if line[0:3] in cmd_list: + if " X" in line and self.getValue(line, "X"): + self._prev_x = self._cur_x + self._cur_x = self.getValue(line, "X") + if " Y" in line and self.getValue(line, "Y"): + self._prev_y = self._cur_y + self._cur_y = self.getValue(line, "Y") + if " Z" in line and self.getValue(line, "Z"): + self._cur_z = self.getValue(line, "Z") + + # Check whether retractions have occured + if line[0:3] in ["G1 ", "G2 ", "G3 "] and "X" in line and "Y" in line and "E" in line: + self._is_retracted = False + self._cur_e = self.getValue(line, "E") + elif (line.startswith("G1") and "F" in line and "E" in line and not "X" in line or not "Y" in line) or "G10" in line: + if self.getValue(line, "E"): + self._cur_e = self.getValue(line, "E") + if not relative_extrusion: + if self._cur_e < self._prev_e or "G10" in line: + self._is_retracted = True + elif relative_extrusion: + if self._cur_e < 0 or "G10" in line: + self._is_retracted = True + if line.startswith(";TYPE"): + start_here = True + if not start_here: + continue + + # All travels are checked for their cumulative length + if line.startswith("G0 ") and hop_start == 0: + hop_indexes = self._total_travel_length(index, lines) + hop_start = int(hop_indexes[0]) + hop_end = int(hop_indexes[1]) + if infill_only and not in_the_infill: + hop_start = 0 + hop_end = 0 + if hop_start > 0: + # For any lines that are XYZ moves right before layer change + if " Z" in line: + lines[index] = lines[index].replace("Z" + str(self._cur_z), "Z" + str(self._cur_z + hop_height)) + # If there is no 'F' in the next line then add one at the Travel Speed so the z-hop speed doesn't carry over + if not " F" in lines[index] and lines[index].startswith("G0"): + lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") + if "X" in lines[index - 1] and "Y" in lines[index - 1] and "E" in lines[index - 1]: + self._is_retracted = False + hop_up_lines = self.get_hop_up_lines(retraction_amount, speed_zhop, retract_speed, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height) + lines[index] = hop_up_lines + lines[index] + + # Make the 'Zhop down' insertion at the correct index location (or as soon as practicable after it) + if hop_end > 0 and index >= hop_end: + # If there is no 'F' in the next line then add one to reinstate the Travel Speed (so the z-hop speed doesn't carry over through the travel moves) + if not " F" in lines[index] and lines[index].startswith("G0"): + lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") + hop_down_lines = self.get_hop_down_lines(retraction_amount, speed_zhop, retract_speed, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height, lines[index]) + lines[index] = hop_down_lines + lines[index] + self._is_retracted = False + hop_end = 0 + hop_start = 0 + hop_down = "" + if line.startswith(";"): + continue + self._prev_e = self._cur_e + data[num] = "\n".join(lines) + "\n" + + # Message to the user informing them of the number of Z-hops and retractions added + hop_cnt = 0 + retract_cnt = 0 + try: + for index_nr in index_list: + hop_cnt += data[index_nr].count("; Hop Up") + retract_cnt += data[index_nr].count("; Retract") + msg_txt = str(hop_cnt) + " Z-Hops were added to the file\n" + if self._add_retract: + msg_txt += str(retract_cnt) + " Retracts and unretracts were added to the file" + Message(title = "[Z-hop On Travel]", text = msg_txt).show() + except: + pass + return data + + def _total_travel_length(self, l_index: int, lines: str) -> float: + """ + This function gets the cummulative total travel distance of each individual travel move. + :parameters: + g_num: is the line index as passed from the calling function and when returned indicates the end of travel + travel_total: is the cummulative travel distance + """ + g_num = l_index + travel_total = 0.0 + # Total the lengths of each move and compare them to the Minimum Distance for a Z-hop to occur + while lines[g_num].startswith("G0 "): + travel_total += self._get_distance() + self._prev_x = self._cur_x + if self.getValue(lines[g_num], "X"): + self._cur_x = self.getValue(lines[g_num], "X") + self._prev_y = self._cur_y + if self.getValue(lines[g_num], "Y"): + self._cur_y = self.getValue(lines[g_num], "Y") + g_num += 1 + if g_num == len(lines): + break + if travel_total > self.getSettingValueByKey("min_travel_dist"): + return l_index, g_num + else: + return 0, 0 + + def _get_distance(self) -> float: + """ + This function gets the distance from the previous location to the current location. + """ + try: + dist = math.sqrt((self._prev_x - self._cur_x)**2 + (self._prev_y - self._cur_y)**2) + except ValueError: + return 0 + return dist + + def get_hop_up_lines(self, retraction_amount: float, speed_zhop: str, retract_speed: str, prime_speed: str, extra_prime_dist: float, firmware_retract: bool, relative_extrusion: bool, hop_height: str) -> str: + """ + Determine if the hop will require a retraction + :parameters: + reset_type: An indicator to handle differences when Firmware Retraction, and Relative Extrusion, and Extra Prime are enabled + up_lines: The inserted line(s) for the Z-hop Up + front_text and back_text: Are the line splits to account for existing gcode lines that have comments in them + """ + hop_retraction = not self._is_retracted + if not self._add_retract: + hop_retraction = False + reset_type = 0 # no other options + if hop_retraction: + reset_type += 1 + if firmware_retract and hop_retraction: + reset_type += 2 + if relative_extrusion and hop_retraction: + reset_type += 4 + if extra_prime_dist > 0 and hop_retraction: + reset_type += 8 + up_lines = f"G0 F{speed_zhop} Z{round(self._cur_z + hop_height,2)} ; Hop Up" + if reset_type in [1, 9] and hop_retraction: # add retract only when necessary + up_lines = f"G1 F{retract_speed} E{round(self._cur_e - retraction_amount, 5)} ; Retract\n" + up_lines + self._cur_e = round(self._cur_e - retraction_amount, 5) + if reset_type in [3, 7] and hop_retraction: # add retract and firmware retract + up_lines = "G10 ; Retract\n" + up_lines + if reset_type in [5, 13] and hop_retraction: # add retract and relative extrusion + up_lines = f"G1 F{retract_speed} E-{retraction_amount} ; Retract\n" + up_lines + self._cur_e = 0 + if reset_type in [11, 15] and hop_retraction: # add retract, firmware retraction, extra prime + up_lines = "G10 ; Retract\n" + up_lines + + # Format the added lines for readability + if "\n" in up_lines: # for lines that include a retraction + lines = up_lines.split("\n") + for index, line in enumerate(lines): + front_txt = lines[index].split(";")[0] + back_txt = lines[index].split(";")[1] + lines[index] = front_txt + str(" " * (40 - len(front_txt))) +";" + back_txt + up_lines = "\n".join(lines) + "\n" + else: # for lines without a retraction + front_txt = up_lines.split(";")[0] + back_txt = up_lines.split(";")[1] + up_lines = front_txt + str(" " * (40 - len(front_txt))) +";" + back_txt + "\n" + return up_lines + + # The Zhop down may require different kinds of primes depending on the Cura settings. + def get_hop_down_lines(self, retraction_amount: float, speed_zhop: str, retract_speed: str, prime_speed: str, extra_prime_dist: str, firmware_retract: bool, relative_extrusion: bool, hop_height: str, next_line: str) -> str: + + """ + Determine if the hop will require a prime + :parameters: + reset_type: An indicator to handle differences when Firmware Retraction, and Relative Extrusion, and Extra Prime are enabled + dn_lines: The inserted line(s) for the Z-hop Down + front_text and back_text: Are the line splits to account for existing gcode lines that have comments in them + """ + hop_retraction = not self._is_retracted + if not self._add_retract: + hop_retraction = False + # Base the prime on the combination of Cura settings + reset_type = 0 + if hop_retraction: + reset_type += 1 + if firmware_retract and hop_retraction: + reset_type += 2 + if relative_extrusion and hop_retraction: + reset_type += 4 + if extra_prime_dist > 0.0 and hop_retraction: + reset_type += 8 + dn_lines = f"G0 F{speed_zhop} Z{self._cur_z} ; Hop Down" + # Format the line and return if the retraction option is unchecked + if "G11" in next_line or re.search("G1 F(\d+\.\d+|\d+) E(-?\d+\.\d+|-?\d+)", next_line) and reset_type == 0: + front_txt = dn_lines.split(";")[0] + back_txt = dn_lines.split(";")[1] + dn_lines = front_txt + str(" " * (40 - len(front_txt))) +";" + back_txt + "\n" + self._is_retracted = False + return dn_lines + # If the retraction option is checked then determine the required unretract code for the particular combination of Cura settings. + # Add retract 1 + if reset_type == 1 and hop_retraction: + dn_lines += f"\nG1 F{prime_speed} E{round(self._prev_e + retraction_amount, 5)} ; Unretract" + # Add retract 1 + firmware retract 2 + if reset_type == 3 and hop_retraction: + dn_lines += "\nG11 ; UnRetract" + # Add retract 1 + relative extrusion 4 + if reset_type == 5 and hop_retraction: + dn_lines += f"\nG1 F{prime_speed} E{retraction_amount} ; UnRetract" + # Add retract 1 + firmware retraction 2 + relative extrusion 4 + if reset_type == 7 and hop_retraction: + dn_lines += f"\nG11 ; Unretract" + # Add retract 1 + extra prime 8 + if reset_type == 9 and hop_retraction: + dn_lines += f"\nG92 E{round(self._prev_e - extra_prime_dist,5)} ; Extra prime adjustment" + dn_lines += f"\nG1 F{prime_speed} E{round(self._prev_e + retraction_amount, 5)} ; UnRetract" + self._cur_e = round(self._prev_e + retraction_amount, 5) + # Add retract 1 + firmware retraction 2 + extra prime 8 + if reset_type == 11 and hop_retraction: + dn_lines += "\nG11 ; UnRetract" + dn_lines += "\nM83 ; Relative extrusion" + dn_lines += f"\nG1 F{prime_speed} E{round(extra_prime_dist, 5)} ; Extra prime" + if not relative_extrusion: + dn_lines += "\nM82 ; Absolute extrusion" + # Add retract 1 + relative extrusion 4 + extra prime 8 + if reset_type == 13 and hop_retraction: + dn_lines += f"\nG1 F{prime_speed} E{round(retraction_amount + extra_prime_dist, 5)} ; Unretract with extra prime" + # Add retract 1 + firmware retraction 2 + relative extrusion 4 + extra prime 8 + if reset_type == 15 and hop_retraction: + dn_lines += "\nG11 ; UnRetract" + dn_lines += f"\nG1 F{prime_speed} E{round(extra_prime_dist, 5)} ; Extra prime" + + # Format the added lines for readability + if "\n" in dn_lines: # for lines with primes + lines = dn_lines.split("\n") + for index, line in enumerate(lines): + front_txt = lines[index].split(";")[0] + back_txt = lines[index].split(";")[1] + lines[index] = front_txt + str(" " * (40 - len(front_txt))) +";" + back_txt + dn_lines = "\n".join(lines) + "\n" + else: # for lines with no prime + front_txt = dn_lines.split(";")[0] + back_txt = dn_lines.split(";")[1] + dn_lines = front_txt + str(" " * (40 - len(front_txt))) +";" + back_txt + "\n" + self._is_retracted = False + return dn_lines + + def _track_all_axes(self, data: str, cmd_list: int, start_index: int, relative_extrusion: bool) -> str: + """ + This function tracks the XYZE locations prior to the beginning of the first 'layer-of-interest' + + """ + for num in range(2, start_index - 1): + lines = data[num].split("\n") + for line in lines: + # Get the XYZ values from movement commands + if line[0:3] in cmd_list: + if " X" in line and self.getValue(line, "X"): + self._prev_x = self._cur_x + self._cur_x = self.getValue(line, "X") + if " Y" in line and self.getValue(line, "Y"): + self._prev_y = self._cur_y + self._cur_y = self.getValue(line, "Y") + if " Z" in line and self.getValue(line, "Z"): + self._cur_z = self.getValue(line, "Z") + + # Check whether retractions have occured and track the E location + if not relative_extrusion: + if line.startswith("G1 ") and " X" in line and " Y" in line and " E" in line: + self._is_retracted = False + self._cur_e = self.getValue(line, "E") + elif line.startswith("G1 ") and " F" in line and " E" in line and not " X" in line and not " Y" in line: + if self.getValue(line, "E"): + self._cur_e = self.getValue(line, "E") + elif line.startswith("G10"): + self._is_retracted = True + elif line.startswith("G11"): + self._is_retracted = False + elif relative_extrusion: + if self._cur_e < 0 or "G10" in line: + self._is_retracted = True + self._cur_e = 0 + if line.startswith("G11"): + self._is_retracted = False + self._cur_e = 0 + self._prev_e = self._cur_e + return From e1246beb67966b035492bd217a93af532a4e0509 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 12 Apr 2025 14:11:09 -0400 Subject: [PATCH 18/98] Update DisplayInfoOnLCD.py Minor changes to the statistics added to the beginning of the gcode. --- .../scripts/DisplayInfoOnLCD.py | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 85b9744856..abff57c804 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -319,7 +319,7 @@ class DisplayInfoOnLCD(Script): # This is from 'Show Progress on LCD' def _display_progress(self, data: str) -> str: - # Add some print settings to the start of the gcode + # Add some common print settings to the start of the gcode data[0] = self._add_stats(data) # Get settings print_sequence = Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") @@ -344,6 +344,7 @@ class DisplayInfoOnLCD(Script): # If at least one of the settings is disabled, there is enough room on the display to display "layer" first_section = data[0] lines = first_section.split("\n") + pause_cmd = [] for line in lines: if line.startswith(";TIME:"): tindex = lines.index(line) @@ -530,7 +531,9 @@ class DisplayInfoOnLCD(Script): return data def _message_to_user(self, data: str, speed_factor: float, pause_cmd: str) -> str: - # Message the user of the projected finish time of the print + """ + Message the user of the projected finish time of the print and when any pauses might occur + """ print_time = Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601) print_start_time = self.getSettingValueByKey("print_start_time") # If the user entered a print start time make sure it is in the correct format or ignore it. @@ -623,7 +626,10 @@ class DisplayInfoOnLCD(Script): def _add_stats(self, data: str) -> str: global_stack = Application.getInstance().getGlobalContainerStack() - # Create a list of the models in the file + """ + Make a list of the models in the file. + Add some of the filament stats to the first section of the gcode. + """ model_list = [] for mdex, layer in enumerate(data): layer = data[mdex].split("\n") @@ -632,22 +638,34 @@ class DisplayInfoOnLCD(Script): model_name = line.split(":")[1] if not model_name in model_list: model_list.append(model_name) - # Add some settings to data[0] + # Filament stats extruder_count = global_stack.getProperty("machine_extruder_count", "value") init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) - nozzle_size_line = ";Nozzle Size (T0): " + str(global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")) - filament_type = "\n;Filament Type (T0): " + str(global_stack.extruderList[0].material.getMetaDataEntry("material", "")) - print_temperature_line = ";Print Temperature (T0): " + str(global_stack.extruderList[0].getProperty("material_print_temperature", "value")) + filament_line_t0 = ";Extruder 1 (T0)\n" + filament_amount = Application.getInstance().getPrintInformation().materialLengths + filament_line_t0 += f"; Filament used: {filament_amount[0]}m\n" + filament_line_t0 += f"; Filament Type: {global_stack.extruderList[0].material.getMetaDataEntry("material", "")}\n" + filament_line_t0 += f"; Filament Dia.: {global_stack.extruderList[0].getProperty("material_diameter", "value")}mm\n" + filament_line_t0 += f"; Nozzle Size : {global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")}mm\n" + filament_line_t0 += f"; Print Temp. : {global_stack.extruderList[0].getProperty("material_print_temperature", "value")}°" + + # if there is more than one extruder then get the stats for the second one. + filament_line_t1 = "" if extruder_count > 1: - nozzle_size_line += "\n;Nozzle Size (T1): " + str(global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")) - filament_type += "\n;Filament type (T1): " + str(global_stack.extruderList[1].material.getMetaDataEntry("material", "")) - print_temperature_line += "\n;Print Temperature (T1): " + str(global_stack.extruderList[1].getProperty("material_print_temperature", "value")) + filament_line_t1 = "\n;Extruder 2 (T1)\n" + filament_line_t1 += f"; Filament used: {filament_amount[1]}m\n" + filament_line_t1 += f"; Filament Type: {global_stack.extruderList[1].material.getMetaDataEntry("material", "")}\n" + filament_line_t1 += f"; Filament Dia.: {global_stack.extruderList[1].getProperty("material_diameter", "value")}mm\n" + filament_line_t1 += f"; Nozzle Size : {global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")}mm\n" + filament_line_t1 += f"; Print Temp. : {global_stack.extruderList[1].getProperty("material_print_temperature", "value")}°" + + # Add the stats to the gcode file lines = data[0].split("\n") for index, line in enumerate(lines): if line.startswith(";Layer height:"): - lines[index] += "\n" + init_layer_hgt_line + "\n" + nozzle_size_line + "\n" + print_temperature_line + lines[index] += "\n" + init_layer_hgt_line if line.startswith(";Filament used"): - lines[index] += filament_type + lines[index] = filament_line_t0 + filament_line_t1 if "MINX" in line or "MIN.X" in line: # Add the model list lines[index - 1] += f"\n;Model List: {str(model_list)}" From 1b518b8c926096d1835f50e64147d17b1cf6198b Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 12 Apr 2025 14:13:53 -0400 Subject: [PATCH 19/98] Update DisplayInfoOnLCD.py Changes to the print statistics entered into the gcode. --- .../scripts/DisplayInfoOnLCD.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index abff57c804..e68b4c92c8 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -1,5 +1,6 @@ """ Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 +<<<<<<< Updated upstream Modified: Joshua Pope-Lewis on November 16, 2018 Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019 Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here. @@ -29,6 +30,35 @@ Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It uses the Time Fudge Factor. The user may enter a print start time. +======= +Modified: Joshua Pope-Lewis on November 16, 2018 +Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019 +Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here. +--------------------------------------------------------------- +DisplayNameOrProgressOnLCD.py +Cura Post-Process plugin +Combines 'Display Filename and Layer on the LCD' with 'Display Progress' +Combined and with additions by: GregValiant (Greg Foresi) +Date: September 8, 2023 +NOTE: This combined post processor will make 'Display Filename and Layer on the LCD' and 'Display Progress' obsolete +Description: Display Filename and Layer options: + Status messages sent to the printer... + - Scrolling (SCROLL_LONG_FILENAMES) if enabled in Marlin and you aren't printing a small item select this option. + - Name: By default it will use the name generated by Cura (EG: TT_Test_Cube) - You may enter a custom name here + - Start Num: Choose which number you prefer for the initial layer, 0 or 1 + - Max Layer: Enabling this will show how many layers are in the entire print (EG: Layer 1 of 265!) + - Add prefix 'Printing': Enabling this will add the prefix 'Printing' + - Example Line on LCD: Printing Layer 0 of 395 3DBenchy + Display Progress options: + - Display Total Layer Count + - Disply Time Remaining for the print + - Time Fudge Factor % - Divide the Actual Print Time by the Cura Estimate. Enter as a percentage and the displayed time will be adjusted. This allows you to bring the displayed time closer to reality (Ex: Entering 87.5 would indicate an adjustment to 87.5% of the Cura estimate). + - Example line on LCD: 1/479 | ET 2h13m + - Time to Pauses changes the M117/M118 lines to countdown to the next pause as 1/479 | TP 2h36m + - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. + - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) + - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It takes into account the Time Fudge Factor. The user may enter a print start time. This is also available for Display Filename. +>>>>>>> Stashed changes """ from ..Script import Script @@ -240,6 +270,7 @@ class DisplayInfoOnLCD(Script): def execute(self, data): display_option = self.getSettingValueByKey("display_option") +<<<<<<< Updated upstream self.add_m117_line = self.getSettingValueByKey("add_m117_line") self.add_m118_line = self.getSettingValueByKey("add_m118_line") self.add_m118_a1 = self.getSettingValueByKey("add_m118_a1") @@ -249,12 +280,21 @@ class DisplayInfoOnLCD(Script): self.add_m73_time = self.getSettingValueByKey("add_m73_time") self.add_m73_percent = self.getSettingValueByKey("add_m73_percent") self.m73_str = "" +======= + add_m118_line = self.getSettingValueByKey("add_m118_line") + add_m73_line = self.getSettingValueByKey("add_m73_line") + add_m73_time = self.getSettingValueByKey("add_m73_time") + add_m73_percent = self.getSettingValueByKey("add_m73_percent") + + # This is Display Filename and Layer on LCD +>>>>>>> Stashed changes if display_option == "filename_layer": data = self._display_filename_layer(data) else: data = self._display_progress(data) return data +<<<<<<< Updated upstream # This is from the original 'Display Filename and Layer on LCD' def _display_filename_layer(self, data: str) -> str: data[0] = self._add_stats(data) @@ -280,6 +320,28 @@ class DisplayInfoOnLCD(Script): self.m118_text = octo_text + str(i) layer_index = data.index(layer) lines = layer.split("\n") +======= + # Display Progress (from 'Show Progress' and 'Display Progress on LCD') + elif display_option == "display_progress": + # get settings + display_total_layers = self.getSettingValueByKey("display_total_layers") + display_remaining_time = self.getSettingValueByKey("display_remaining_time") + speed_factor = self.getSettingValueByKey("speed_factor") / 100 + m73_time = False + m73_percent = False + if add_m73_line and add_m73_time: + m73_time = True + if add_m73_line and add_m73_percent: + m73_percent = True + # initialize global variables + first_layer_index = 0 + time_total = 0 + number_of_layers = 0 + time_elapsed = 0 + # if at least one of the settings is disabled, there is enough room on the display to display "layer" + first_section = data[0] + lines = first_section.split("\n") +>>>>>>> Stashed changes for line in lines: if line.startswith(";LAYER_COUNT:"): max_layer = line From bf8d31aa515e1be3a19525b689dec84fee7f7be9 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:04:34 -0400 Subject: [PATCH 20/98] Update DisplayInfoOnLCD.py Add the 'Quality Name' to the gcode with the other settings. Update DisplayInfoOnLCD.py Touchups. Update DisplayInfoOnLCD.py Minor change --- .../scripts/DisplayInfoOnLCD.py | 72 ++----------------- 1 file changed, 6 insertions(+), 66 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index e68b4c92c8..e016bbd3d6 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -1,6 +1,5 @@ """ Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 -<<<<<<< Updated upstream Modified: Joshua Pope-Lewis on November 16, 2018 Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019 Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here. @@ -30,35 +29,6 @@ Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It uses the Time Fudge Factor. The user may enter a print start time. -======= -Modified: Joshua Pope-Lewis on November 16, 2018 -Display Progress on LCD by Mathias Lyngklip Kjeldgaard, Alexander Gee, Kimmo Toivanen, Inigo Martinez on July 31, 2019 -Show Progress was adapted from Display Progress by Louis Wooters on January 6, 2020. His changes are included here. ---------------------------------------------------------------- -DisplayNameOrProgressOnLCD.py -Cura Post-Process plugin -Combines 'Display Filename and Layer on the LCD' with 'Display Progress' -Combined and with additions by: GregValiant (Greg Foresi) -Date: September 8, 2023 -NOTE: This combined post processor will make 'Display Filename and Layer on the LCD' and 'Display Progress' obsolete -Description: Display Filename and Layer options: - Status messages sent to the printer... - - Scrolling (SCROLL_LONG_FILENAMES) if enabled in Marlin and you aren't printing a small item select this option. - - Name: By default it will use the name generated by Cura (EG: TT_Test_Cube) - You may enter a custom name here - - Start Num: Choose which number you prefer for the initial layer, 0 or 1 - - Max Layer: Enabling this will show how many layers are in the entire print (EG: Layer 1 of 265!) - - Add prefix 'Printing': Enabling this will add the prefix 'Printing' - - Example Line on LCD: Printing Layer 0 of 395 3DBenchy - Display Progress options: - - Display Total Layer Count - - Disply Time Remaining for the print - - Time Fudge Factor % - Divide the Actual Print Time by the Cura Estimate. Enter as a percentage and the displayed time will be adjusted. This allows you to bring the displayed time closer to reality (Ex: Entering 87.5 would indicate an adjustment to 87.5% of the Cura estimate). - - Example line on LCD: 1/479 | ET 2h13m - - Time to Pauses changes the M117/M118 lines to countdown to the next pause as 1/479 | TP 2h36m - - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. - - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) - - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It takes into account the Time Fudge Factor. The user may enter a print start time. This is also available for Display Filename. ->>>>>>> Stashed changes """ from ..Script import Script @@ -270,7 +240,6 @@ class DisplayInfoOnLCD(Script): def execute(self, data): display_option = self.getSettingValueByKey("display_option") -<<<<<<< Updated upstream self.add_m117_line = self.getSettingValueByKey("add_m117_line") self.add_m118_line = self.getSettingValueByKey("add_m118_line") self.add_m118_a1 = self.getSettingValueByKey("add_m118_a1") @@ -280,21 +249,12 @@ class DisplayInfoOnLCD(Script): self.add_m73_time = self.getSettingValueByKey("add_m73_time") self.add_m73_percent = self.getSettingValueByKey("add_m73_percent") self.m73_str = "" -======= - add_m118_line = self.getSettingValueByKey("add_m118_line") - add_m73_line = self.getSettingValueByKey("add_m73_line") - add_m73_time = self.getSettingValueByKey("add_m73_time") - add_m73_percent = self.getSettingValueByKey("add_m73_percent") - - # This is Display Filename and Layer on LCD ->>>>>>> Stashed changes if display_option == "filename_layer": data = self._display_filename_layer(data) else: data = self._display_progress(data) return data -<<<<<<< Updated upstream # This is from the original 'Display Filename and Layer on LCD' def _display_filename_layer(self, data: str) -> str: data[0] = self._add_stats(data) @@ -320,28 +280,6 @@ class DisplayInfoOnLCD(Script): self.m118_text = octo_text + str(i) layer_index = data.index(layer) lines = layer.split("\n") -======= - # Display Progress (from 'Show Progress' and 'Display Progress on LCD') - elif display_option == "display_progress": - # get settings - display_total_layers = self.getSettingValueByKey("display_total_layers") - display_remaining_time = self.getSettingValueByKey("display_remaining_time") - speed_factor = self.getSettingValueByKey("speed_factor") / 100 - m73_time = False - m73_percent = False - if add_m73_line and add_m73_time: - m73_time = True - if add_m73_line and add_m73_percent: - m73_percent = True - # initialize global variables - first_layer_index = 0 - time_total = 0 - number_of_layers = 0 - time_elapsed = 0 - # if at least one of the settings is disabled, there is enough room on the display to display "layer" - first_section = data[0] - lines = first_section.split("\n") ->>>>>>> Stashed changes for line in lines: if line.startswith(";LAYER_COUNT:"): max_layer = line @@ -710,7 +648,7 @@ class DisplayInfoOnLCD(Script): filament_line_t0 += f"; Filament Dia.: {global_stack.extruderList[0].getProperty("material_diameter", "value")}mm\n" filament_line_t0 += f"; Nozzle Size : {global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")}mm\n" filament_line_t0 += f"; Print Temp. : {global_stack.extruderList[0].getProperty("material_print_temperature", "value")}°" - + # if there is more than one extruder then get the stats for the second one. filament_line_t1 = "" if extruder_count > 1: @@ -720,15 +658,17 @@ class DisplayInfoOnLCD(Script): filament_line_t1 += f"; Filament Dia.: {global_stack.extruderList[1].getProperty("material_diameter", "value")}mm\n" filament_line_t1 += f"; Nozzle Size : {global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")}mm\n" filament_line_t1 += f"; Print Temp. : {global_stack.extruderList[1].getProperty("material_print_temperature", "value")}°" - + # Add the stats to the gcode file lines = data[0].split("\n") for index, line in enumerate(lines): if line.startswith(";Layer height:"): - lines[index] += "\n" + init_layer_hgt_line + lines[index] += f"\n{init_layer_hgt_line}" + lines[index] += f"\n;Base Quality Name : '{global_stack.quality.getMetaDataEntry("name", "")}'" + lines[index] += f"\n;Custom Quality Name: '{global_stack.qualityChanges.getMetaDataEntry("name")}'" if line.startswith(";Filament used"): lines[index] = filament_line_t0 + filament_line_t1 if "MINX" in line or "MIN.X" in line: - # Add the model list + # Add the Object List lines[index - 1] += f"\n;Model List: {str(model_list)}" return "\n".join(lines) \ No newline at end of file From 65ecca94f26563482df40dbb0c0992bd20ca5e09 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Thu, 24 Apr 2025 08:48:20 -0400 Subject: [PATCH 21/98] Update ZHopOnTravel.py Update per RBurema requested changes. --- .../scripts/ZHopOnTravel.py | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index 82a60239be..588e642d4d 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -28,7 +28,9 @@ import math from UM.Logger import Logger class ZHopOnTravel(Script): - + def __init__(self): + super().__init__() + def getSettingDataString(self): return """{ "name": "Z-Hop on Travel", @@ -170,7 +172,6 @@ class ZHopOnTravel(Script): extra_prime_vol = extruder[0].getProperty("retraction_extra_prime_amount", "value") extra_prime_dist = extra_prime_vol / (math.pi * (filament_dia / 2)**2) self._add_retract = self.getSettingValueByKey("add_retract") - min_travel_dist = self.getSettingValueByKey("min_travel_dist") hop_height = round(self.getSettingValueByKey("hop_height"),2) list_or_range = self.getSettingValueByKey("list_or_range") infill_only = self.getSettingValueByKey("infill_only") @@ -187,7 +188,6 @@ class ZHopOnTravel(Script): if ";LAYER:" + str(int(layer) - 1) + "\n" in data[num]: index_list.append(num) start_index = index_list[0] - end_index = index_list[len(index_list) - 1] elif list_or_range == "range_of_layers": start_layer = self.getSettingValueByKey("start_layer") @@ -307,7 +307,7 @@ class ZHopOnTravel(Script): lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") if "X" in lines[index - 1] and "Y" in lines[index - 1] and "E" in lines[index - 1]: self._is_retracted = False - hop_up_lines = self.get_hop_up_lines(retraction_amount, speed_zhop, retract_speed, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height) + hop_up_lines = self.get_hop_up_lines(retraction_amount, speed_zhop, retract_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height) lines[index] = hop_up_lines + lines[index] # Make the 'Zhop down' insertion at the correct index location (or as soon as practicable after it) @@ -320,7 +320,7 @@ class ZHopOnTravel(Script): self._is_retracted = False hop_end = 0 hop_start = 0 - hop_down = "" + hop_down_lines = "" if line.startswith(";"): continue self._prev_e = self._cur_e @@ -341,7 +341,7 @@ class ZHopOnTravel(Script): pass return data - def _total_travel_length(self, l_index: int, lines: str) -> float: + def _total_travel_length(self, l_index: int, lines: str) -> int: """ This function gets the cummulative total travel distance of each individual travel move. :parameters: @@ -363,9 +363,9 @@ class ZHopOnTravel(Script): if g_num == len(lines): break if travel_total > self.getSettingValueByKey("min_travel_dist"): - return l_index, g_num + return [l_index, g_num] else: - return 0, 0 + return [0, 0] def _get_distance(self) -> float: """ @@ -377,7 +377,7 @@ class ZHopOnTravel(Script): return 0 return dist - def get_hop_up_lines(self, retraction_amount: float, speed_zhop: str, retract_speed: str, prime_speed: str, extra_prime_dist: float, firmware_retract: bool, relative_extrusion: bool, hop_height: str) -> str: + def get_hop_up_lines(self, retraction_amount: float, speed_zhop: str, retract_speed: str, extra_prime_dist: float, firmware_retract: bool, relative_extrusion: bool, hop_height: str) -> str: """ Determine if the hop will require a retraction :parameters: @@ -401,13 +401,11 @@ class ZHopOnTravel(Script): if reset_type in [1, 9] and hop_retraction: # add retract only when necessary up_lines = f"G1 F{retract_speed} E{round(self._cur_e - retraction_amount, 5)} ; Retract\n" + up_lines self._cur_e = round(self._cur_e - retraction_amount, 5) - if reset_type in [3, 7] and hop_retraction: # add retract and firmware retract + if reset_type in [3, 7, 11, 15] and hop_retraction: # add retract and firmware retract up_lines = "G10 ; Retract\n" + up_lines if reset_type in [5, 13] and hop_retraction: # add retract and relative extrusion up_lines = f"G1 F{retract_speed} E-{retraction_amount} ; Retract\n" + up_lines self._cur_e = 0 - if reset_type in [11, 15] and hop_retraction: # add retract, firmware retraction, extra prime - up_lines = "G10 ; Retract\n" + up_lines # Format the added lines for readability if "\n" in up_lines: # for lines that include a retraction @@ -458,15 +456,12 @@ class ZHopOnTravel(Script): # Add retract 1 if reset_type == 1 and hop_retraction: dn_lines += f"\nG1 F{prime_speed} E{round(self._prev_e + retraction_amount, 5)} ; Unretract" - # Add retract 1 + firmware retract 2 - if reset_type == 3 and hop_retraction: + # Add retract 1 + firmware retract 2 and Add retract 1 + firmware retraction 2 + relative extrusion 4 + if reset_type in [3, 7] and hop_retraction: dn_lines += "\nG11 ; UnRetract" # Add retract 1 + relative extrusion 4 if reset_type == 5 and hop_retraction: dn_lines += f"\nG1 F{prime_speed} E{retraction_amount} ; UnRetract" - # Add retract 1 + firmware retraction 2 + relative extrusion 4 - if reset_type == 7 and hop_retraction: - dn_lines += f"\nG11 ; Unretract" # Add retract 1 + extra prime 8 if reset_type == 9 and hop_retraction: dn_lines += f"\nG92 E{round(self._prev_e - extra_prime_dist,5)} ; Extra prime adjustment" @@ -502,7 +497,7 @@ class ZHopOnTravel(Script): self._is_retracted = False return dn_lines - def _track_all_axes(self, data: str, cmd_list: int, start_index: int, relative_extrusion: bool) -> str: + def _track_all_axes(self, data: str, cmd_list: str, start_index: int, relative_extrusion: bool) -> str: """ This function tracks the XYZE locations prior to the beginning of the first 'layer-of-interest' @@ -541,4 +536,4 @@ class ZHopOnTravel(Script): self._is_retracted = False self._cur_e = 0 self._prev_e = self._cur_e - return + return None From 1eaed0fb3c3e6ac799e863b7571d47a28f5b72a3 Mon Sep 17 00:00:00 2001 From: RedBlackAka <140876408+RedBlackAka@users.noreply.github.com> Date: Sun, 4 May 2025 01:29:54 +0200 Subject: [PATCH 22/98] Remove NSIS uninstall shortcut removal Remove NSIS uninstall shortcut removal that I accidentally left behind. This is no longer needed. --- packaging/NSIS/Ultimaker-Cura.nsi.jinja | 2 -- 1 file changed, 2 deletions(-) diff --git a/packaging/NSIS/Ultimaker-Cura.nsi.jinja b/packaging/NSIS/Ultimaker-Cura.nsi.jinja index 9f61e6950c..f3bb2572be 100644 --- a/packaging/NSIS/Ultimaker-Cura.nsi.jinja +++ b/packaging/NSIS/Ultimaker-Cura.nsi.jinja @@ -158,12 +158,10 @@ RmDir /r /REBOOTOK "$INSTDIR" !ifdef REG_START_MENU Delete "$SMPROGRAMS\${APP_NAME}.lnk" -Delete "$SMPROGRAMS\Uninstall ${APP_NAME}.lnk" !endif !ifndef REG_START_MENU Delete "$SMPROGRAMS\${APP_NAME}.lnk" -Delete "$SMPROGRAMS\Uninstall ${APP_NAME}.lnk" !endif !insertmacro APP_UNASSOCIATE "stl" "Cura.model" From 264a63103f81d3b43de3f32b37113a67d61477e4 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Mon, 12 May 2025 23:33:50 -0400 Subject: [PATCH 23/98] Update script Changed the inserted "G0 F Z" lines to "G1 F Z" so they match the existing Cura syntax. --- plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index 588e642d4d..f6191f6cc9 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -397,7 +397,7 @@ class ZHopOnTravel(Script): reset_type += 4 if extra_prime_dist > 0 and hop_retraction: reset_type += 8 - up_lines = f"G0 F{speed_zhop} Z{round(self._cur_z + hop_height,2)} ; Hop Up" + up_lines = f"G1 F{speed_zhop} Z{round(self._cur_z + hop_height,2)} ; Hop Up" if reset_type in [1, 9] and hop_retraction: # add retract only when necessary up_lines = f"G1 F{retract_speed} E{round(self._cur_e - retraction_amount, 5)} ; Retract\n" + up_lines self._cur_e = round(self._cur_e - retraction_amount, 5) @@ -444,7 +444,7 @@ class ZHopOnTravel(Script): reset_type += 4 if extra_prime_dist > 0.0 and hop_retraction: reset_type += 8 - dn_lines = f"G0 F{speed_zhop} Z{self._cur_z} ; Hop Down" + dn_lines = f"G1 F{speed_zhop} Z{self._cur_z} ; Hop Down" # Format the line and return if the retraction option is unchecked if "G11" in next_line or re.search("G1 F(\d+\.\d+|\d+) E(-?\d+\.\d+|-?\d+)", next_line) and reset_type == 0: front_txt = dn_lines.split(";")[0] From bdb771682dae54626cc217463e609c002a9a96ca Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Wed, 21 May 2025 08:41:58 -0400 Subject: [PATCH 24/98] Update ZHopOnTravel.py Bug fix in line 505 that resulted in the first hop being the wrong Z. --- plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index f6191f6cc9..5d58c2d35e 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -502,7 +502,7 @@ class ZHopOnTravel(Script): This function tracks the XYZE locations prior to the beginning of the first 'layer-of-interest' """ - for num in range(2, start_index - 1): + for num in range(2, start_index): lines = data[num].split("\n") for line in lines: # Get the XYZ values from movement commands From 61523062178e53c99651a8f6787302dc9c7cf468 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Thu, 22 May 2025 12:12:42 -0400 Subject: [PATCH 25/98] Update DisplayInfoOnLCD.py Made some changes to the print stats that are added to the gcode. Update DisplayInfoOnLCD.py --- plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index e016bbd3d6..0e3b52c81f 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -647,7 +647,8 @@ class DisplayInfoOnLCD(Script): filament_line_t0 += f"; Filament Type: {global_stack.extruderList[0].material.getMetaDataEntry("material", "")}\n" filament_line_t0 += f"; Filament Dia.: {global_stack.extruderList[0].getProperty("material_diameter", "value")}mm\n" filament_line_t0 += f"; Nozzle Size : {global_stack.extruderList[0].getProperty("machine_nozzle_size", "value")}mm\n" - filament_line_t0 += f"; Print Temp. : {global_stack.extruderList[0].getProperty("material_print_temperature", "value")}°" + filament_line_t0 += f"; Print Temp. : {global_stack.extruderList[0].getProperty("material_print_temperature", "value")}°\n" + filament_line_t0 += f"; Bed Temp. : {global_stack.extruderList[0].getProperty("material_bed_temperature", "value")}°" # if there is more than one extruder then get the stats for the second one. filament_line_t1 = "" @@ -668,6 +669,11 @@ class DisplayInfoOnLCD(Script): lines[index] += f"\n;Custom Quality Name: '{global_stack.qualityChanges.getMetaDataEntry("name")}'" if line.startswith(";Filament used"): lines[index] = filament_line_t0 + filament_line_t1 + # The target machine "machine_name" is actually the printer model. This adds the user defined printer name to the "TARGET_MACHINE" line. + if line.startswith(";TARGET_MACHINE"): + machine_model = str(global_stack.getProperty("machine_name", "value")) + machine_name = str(global_stack.getName()) + lines[index] += f" / {machine_name}" if "MINX" in line or "MIN.X" in line: # Add the Object List lines[index - 1] += f"\n;Model List: {str(model_list)}" From 3f16cc917af43905db5114f3437e6c1a7a7806e2 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Thu, 5 Jun 2025 07:19:07 -0400 Subject: [PATCH 26/98] Update ZHopOnTravel.py Bug fix for End_Layer when the entered layer > total layer count. Update ZHopOnTravel.py I'm not sure why git desktop did this. Update ZHopOnTravel.py I'm not sure why git desktop did this. Bug fix Bug fix for End_Layer when the entered layer > total layer count. Bug fix for 'index_list' when using a layer list. It now tracks the Z through the file rather than just the layers of interest. --- .../scripts/ZHopOnTravel.py | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index 5d58c2d35e..dd3d0296d7 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -192,7 +192,7 @@ class ZHopOnTravel(Script): elif list_or_range == "range_of_layers": start_layer = self.getSettingValueByKey("start_layer") end_layer = self.getSettingValueByKey("end_layer") - + end_index = None # Get the indexes for the start and end layers start_index = 2 for num in range(1, len(data) - 1): @@ -210,6 +210,8 @@ class ZHopOnTravel(Script): end_layer = data[num].splitlines()[0].split(":")[1] end_index = num break + if end_index == None: + end_index = len(data)-1 for num in range(start_index, end_index): index_list.append(num) @@ -247,6 +249,96 @@ class ZHopOnTravel(Script): # Make the insertions in_the_infill = False +<<<<<<< Updated upstream +<<<<<<< Updated upstream + for num in range(start_index, len(data)-1): + if num not in index_list: + lines = data[num].splitlines() + for line in lines: + if " Z" in line and self.getValue(line, "Z"): + self._cur_z = self.getValue(line, "Z") + continue + elif num in index_list: + lines = data[num].splitlines() + for index, line in enumerate(lines): + if num == 2: + if line.startswith(";TYPE"): + start_here = True + if line.startswith(";") and in_the_infill == True: + in_the_infill = False + if line.startswith(";TYPE:FILL"): + in_the_infill = True + if line.startswith("G92") and " E" in line: + self._cur_e = self.getValue(line, "E") + self._prev_e = self._cur_e + continue + # Get the XYZ values from movement commands + if line[0:3] in cmd_list: + if " X" in line and self.getValue(line, "X"): + self._prev_x = self._cur_x + self._cur_x = self.getValue(line, "X") + if " Y" in line and self.getValue(line, "Y"): + self._prev_y = self._cur_y + self._cur_y = self.getValue(line, "Y") + if " Z" in line and self.getValue(line, "Z"): + self._cur_z = self.getValue(line, "Z") + + # Check whether retractions have occured + if line[0:3] in ["G1 ", "G2 ", "G3 "] and "X" in line and "Y" in line and "E" in line: + self._is_retracted = False + self._cur_e = self.getValue(line, "E") + elif (line.startswith("G1") and "F" in line and "E" in line and not "X" in line or not "Y" in line) or "G10" in line: + if self.getValue(line, "E"): + self._cur_e = self.getValue(line, "E") + if not relative_extrusion: + if self._cur_e < self._prev_e or "G10" in line: + self._is_retracted = True + elif relative_extrusion: + if self._cur_e < 0 or "G10" in line: + self._is_retracted = True + if line.startswith(";TYPE"): + start_here = True + if not start_here: + continue + + # All travels are checked for their cumulative length + if line.startswith("G0 ") and hop_start == 0: + hop_indexes = self._total_travel_length(index, lines) + hop_start = int(hop_indexes[0]) + hop_end = int(hop_indexes[1]) + if infill_only and not in_the_infill: + hop_start = 0 + hop_end = 0 + if hop_start > 0: + # For any lines that are XYZ moves right before layer change + if " Z" in line: + lines[index] = lines[index].replace("Z" + str(self._cur_z), "Z" + str(self._cur_z + hop_height)) + # If there is no 'F' in the next line then add one at the Travel Speed so the z-hop speed doesn't carry over + if not " F" in lines[index] and lines[index].startswith("G0"): + lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") + if "X" in lines[index - 1] and "Y" in lines[index - 1] and "E" in lines[index - 1]: + self._is_retracted = False + hop_up_lines = self.get_hop_up_lines(retraction_amount, speed_zhop, retract_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height) + lines[index] = hop_up_lines + lines[index] + + # Make the 'Zhop down' insertion at the correct index location (or as soon as practicable after it) + if hop_end > 0 and index >= hop_end: + # If there is no 'F' in the next line then add one to reinstate the Travel Speed (so the z-hop speed doesn't carry over through the travel moves) + if not " F" in lines[index] and lines[index].startswith("G0"): + lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") + hop_down_lines = self.get_hop_down_lines(retraction_amount, speed_zhop, retract_speed, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height, lines[index]) + lines[index] = hop_down_lines + lines[index] + self._is_retracted = False + hop_end = 0 + hop_start = 0 + hop_down_lines = "" + if line.startswith(";"): + continue + self._prev_e = self._cur_e + data[num] = "\n".join(lines) + "\n" +======= +======= +>>>>>>> Stashed changes for num in index_list: lines = data[num].splitlines() for index, line in enumerate(lines): @@ -325,6 +417,10 @@ class ZHopOnTravel(Script): continue self._prev_e = self._cur_e data[num] = "\n".join(lines) + "\n" +<<<<<<< Updated upstream +>>>>>>> Stashed changes +======= +>>>>>>> Stashed changes # Message to the user informing them of the number of Z-hops and retractions added hop_cnt = 0 From 81dc20ce1842e6ef20f5d483bbac36b75a17c842 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 7 Jun 2025 07:59:44 -0400 Subject: [PATCH 27/98] Update ZHopOnTravel.py Removed the Stashed Changes beginning and ends. Update ZHopOnTravel.py Add comments. --- .../scripts/ZHopOnTravel.py | 92 +------------------ 1 file changed, 5 insertions(+), 87 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index dd3d0296d7..7ac57a308e 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -249,15 +249,18 @@ class ZHopOnTravel(Script): # Make the insertions in_the_infill = False -<<<<<<< Updated upstream -<<<<<<< Updated upstream for num in range(start_index, len(data)-1): + # Leave if the num > highest index number to speed up the script. + if num > index_list[len(index_list)-1]: + break + # If the num is not an "index of interest" then just track the Z through the layer if num not in index_list: lines = data[num].splitlines() for line in lines: if " Z" in line and self.getValue(line, "Z"): self._cur_z = self.getValue(line, "Z") continue + # If the num is in the index_list then make changes elif num in index_list: lines = data[num].splitlines() for index, line in enumerate(lines): @@ -336,91 +339,6 @@ class ZHopOnTravel(Script): continue self._prev_e = self._cur_e data[num] = "\n".join(lines) + "\n" -======= -======= ->>>>>>> Stashed changes - for num in index_list: - lines = data[num].splitlines() - for index, line in enumerate(lines): - if num == 2: - if line.startswith(";TYPE"): - start_here = True - if line.startswith(";") and in_the_infill == True: - in_the_infill = False - if line.startswith(";TYPE:FILL"): - in_the_infill = True - if line.startswith("G92") and " E" in line: - self._cur_e = self.getValue(line, "E") - self._prev_e = self._cur_e - continue - # Get the XYZ values from movement commands - if line[0:3] in cmd_list: - if " X" in line and self.getValue(line, "X"): - self._prev_x = self._cur_x - self._cur_x = self.getValue(line, "X") - if " Y" in line and self.getValue(line, "Y"): - self._prev_y = self._cur_y - self._cur_y = self.getValue(line, "Y") - if " Z" in line and self.getValue(line, "Z"): - self._cur_z = self.getValue(line, "Z") - - # Check whether retractions have occured - if line[0:3] in ["G1 ", "G2 ", "G3 "] and "X" in line and "Y" in line and "E" in line: - self._is_retracted = False - self._cur_e = self.getValue(line, "E") - elif (line.startswith("G1") and "F" in line and "E" in line and not "X" in line or not "Y" in line) or "G10" in line: - if self.getValue(line, "E"): - self._cur_e = self.getValue(line, "E") - if not relative_extrusion: - if self._cur_e < self._prev_e or "G10" in line: - self._is_retracted = True - elif relative_extrusion: - if self._cur_e < 0 or "G10" in line: - self._is_retracted = True - if line.startswith(";TYPE"): - start_here = True - if not start_here: - continue - - # All travels are checked for their cumulative length - if line.startswith("G0 ") and hop_start == 0: - hop_indexes = self._total_travel_length(index, lines) - hop_start = int(hop_indexes[0]) - hop_end = int(hop_indexes[1]) - if infill_only and not in_the_infill: - hop_start = 0 - hop_end = 0 - if hop_start > 0: - # For any lines that are XYZ moves right before layer change - if " Z" in line: - lines[index] = lines[index].replace("Z" + str(self._cur_z), "Z" + str(self._cur_z + hop_height)) - # If there is no 'F' in the next line then add one at the Travel Speed so the z-hop speed doesn't carry over - if not " F" in lines[index] and lines[index].startswith("G0"): - lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") - if "X" in lines[index - 1] and "Y" in lines[index - 1] and "E" in lines[index - 1]: - self._is_retracted = False - hop_up_lines = self.get_hop_up_lines(retraction_amount, speed_zhop, retract_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height) - lines[index] = hop_up_lines + lines[index] - - # Make the 'Zhop down' insertion at the correct index location (or as soon as practicable after it) - if hop_end > 0 and index >= hop_end: - # If there is no 'F' in the next line then add one to reinstate the Travel Speed (so the z-hop speed doesn't carry over through the travel moves) - if not " F" in lines[index] and lines[index].startswith("G0"): - lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") - hop_down_lines = self.get_hop_down_lines(retraction_amount, speed_zhop, retract_speed, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height, lines[index]) - lines[index] = hop_down_lines + lines[index] - self._is_retracted = False - hop_end = 0 - hop_start = 0 - hop_down_lines = "" - if line.startswith(";"): - continue - self._prev_e = self._cur_e - data[num] = "\n".join(lines) + "\n" -<<<<<<< Updated upstream ->>>>>>> Stashed changes -======= ->>>>>>> Stashed changes # Message to the user informing them of the number of Z-hops and retractions added hop_cnt = 0 From e63243bca81e88655d09e3c20277f42315114216 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 7 Jun 2025 09:09:59 -0400 Subject: [PATCH 28/98] Update DisplayInfoOnLCD.py It turns out that some firmware doesn't like colons within M118 commands. --- plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 0e3b52c81f..89c76769c9 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -357,7 +357,7 @@ class DisplayInfoOnLCD(Script): orig_hr = round(orig_hhh // 1) orig_mmm = math.floor((orig_hhh % 1) * 60) if self.add_m118_line: - lines.insert(len(lines) - 2, f"M118 Adjusted Print Time: {hr} hr {mmm} min") + lines.insert(len(lines) - 2, f"M118 Adjusted Print Time is {hr} hr {mmm} min") if self.add_m117_line: lines.insert(len(lines) - 2, f"M117 ET {hr} hr {mmm} min") # Add M73 line at beginning From 187237519d758600461a94ad7709300d90c08af9 Mon Sep 17 00:00:00 2001 From: Frederic Meeuwissen <13856291+Frederic98@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:21:03 +0200 Subject: [PATCH 29/98] [PP-574] Add BVOH profiles --- .../um_f4_bb0.4_bvoh_0.15mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.4_bvoh_0.1mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.4_bvoh_0.2mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.4_bvoh_0.3mm.inst.cfg | 44 +++++++++++++++++++ .../um_f4_bb0.8_bvoh_0.2mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.8_bvoh_0.3mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.8_bvoh_0.4mm.inst.cfg | 43 ++++++++++++++++++ .../um_s3_bb0.4_bvoh_0.15mm.inst.cfg | 35 +++++++++++++++ .../um_s3_bb0.4_bvoh_0.1mm.inst.cfg | 36 +++++++++++++++ .../um_s3_bb0.4_bvoh_0.2mm.inst.cfg | 35 +++++++++++++++ .../um_s3_bb0.4_bvoh_0.3mm.inst.cfg | 37 ++++++++++++++++ .../um_s3_bb0.8_bvoh_0.2mm.inst.cfg | 34 ++++++++++++++ .../um_s3_bb0.8_bvoh_0.3mm.inst.cfg | 36 +++++++++++++++ .../um_s3_bb0.8_bvoh_0.4mm.inst.cfg | 35 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.15mm.inst.cfg | 35 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.1mm.inst.cfg | 36 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.2mm.inst.cfg | 35 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.3mm.inst.cfg | 37 ++++++++++++++++ .../um_s5_bb0.8_bvoh_0.2mm.inst.cfg | 34 ++++++++++++++ .../um_s5_bb0.8_bvoh_0.3mm.inst.cfg | 36 +++++++++++++++ .../um_s5_bb0.8_bvoh_0.4mm.inst.cfg | 35 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.15mm.inst.cfg | 35 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.1mm.inst.cfg | 36 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.2mm.inst.cfg | 35 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.3mm.inst.cfg | 37 ++++++++++++++++ .../um_s8_bb0.8_bvoh_0.2mm.inst.cfg | 34 ++++++++++++++ .../um_s8_bb0.8_bvoh_0.3mm.inst.cfg | 36 +++++++++++++++ .../um_s8_bb0.8_bvoh_0.4mm.inst.cfg | 35 +++++++++++++++ 28 files changed, 1046 insertions(+) create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..c6548f0173 --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..6f1b986c4e --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..ff49315c1e --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..209b16b697 --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,44 @@ +[general] +definition = ultimaker_factor4 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..d10b0c939e --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..dd2e2da565 --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..fa230a1f5f --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..93c1d4947d --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s3 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..f06f8deb6f --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s3 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..61059651ef --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s3 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..8f06dd0562 --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,37 @@ +[general] +definition = ultimaker_s3 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..a9d67b0552 --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,34 @@ +[general] +definition = ultimaker_s3 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..016887c23c --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s3 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..4d4bffbab9 --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s3 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..e5dc8e7295 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s5 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..ed7638a4e0 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s5 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..1d02c886bc --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s5 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..b759ae0bb3 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,37 @@ +[general] +definition = ultimaker_s5 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..58875826ad --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,34 @@ +[general] +definition = ultimaker_s5 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..74b33f5366 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s5 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..cd501dcf0e --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s5 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..e10db6b743 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s8 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..9c5d0e9bd8 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s8 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..8f4c52d7b5 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s8 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..e37500de1a --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,37 @@ +[general] +definition = ultimaker_s8 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..9a8e2a51c4 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,34 @@ +[general] +definition = ultimaker_s8 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..f325937cb9 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s8 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..e26028fdc1 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s8 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + From 3ad2c1b5788466412e17f23840deb9197ba9e19e Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:39:03 -0400 Subject: [PATCH 30/98] Update DisplayInfoOnLCD.py Add "Electricity Cost" to the statistics added to the gcode. Update DisplayInfoOnLCD.py Formatting changes for layer heights --- .../scripts/DisplayInfoOnLCD.py | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index 89c76769c9..ddf292fb85 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -29,6 +29,7 @@ Display Filename and Layer on the LCD by Amanda de Castilho on August 28, 2018 - 'Add M118 Line' is available with either option. M118 will bounce the message back to a remote print server through the USB connection. - 'Add M73 Line' is used by 'Display Progress' only. There are options to incluse M73 P(percent) and M73 R(time remaining) - Enable 'Finish-Time' Message - when enabled, takes the Print Time and calculates when the print will end. It uses the Time Fudge Factor. The user may enter a print start time. +Date: June 30, 2025 Cost of electricity added to the other print statistics in '_add_stats'. """ from ..Script import Script @@ -37,6 +38,7 @@ from UM.Qt.Duration import DurationFormat import time import datetime import math +from UM.Preferences import Preferences from UM.Message import Message class DisplayInfoOnLCD(Script): @@ -234,7 +236,26 @@ class DisplayInfoOnLCD(Script): "default_value": "", "unit": "hrs ", "enabled": "enable_end_message" + }, + "electricity_cost": + { + "label": "Electricity Cost per kWh", + "description": "Cost of electricity per kilowatt-hour. This should be on your electric utility bill.", + "type": "float", + "default_value": 0.151, + "minimum_value": 0, + "unit": "€/kWh " + }, + "printer_power_usage": + { + "label": "Printer Power Usage", + "description": "Average power usage of the 3D printer in Watts. The actual wattage has many variables. 50% of the power supply rating would be a ballpark figure.", + "type": "float", + "default_value": 175, + "minimum_value": 0, + "unit": "Watts " } + } }""" @@ -249,6 +270,11 @@ class DisplayInfoOnLCD(Script): self.add_m73_time = self.getSettingValueByKey("add_m73_time") self.add_m73_percent = self.getSettingValueByKey("add_m73_percent") self.m73_str = "" + para_1 = data[0].split("\n") + for line in para_1: + if line.startswith(";TIME:"): + self.time_total = int(line.split(":")[1]) + break if display_option == "filename_layer": data = self._display_filename_layer(data) else: @@ -337,7 +363,6 @@ class DisplayInfoOnLCD(Script): data[len(data)-1] += "M77\n" # Initialize some variables first_layer_index = 0 - time_total = int(data[0].split(";TIME:")[1].split("\n")[0]) number_of_layers = 0 time_elapsed = 0 @@ -418,8 +443,6 @@ class DisplayInfoOnLCD(Script): for lay in range(2,len(data)-1,1): if ";LAYER:" in data[lay]: number_of_layers += 1 - elif line.startswith(";TIME:"): - time_total = int(line.split(":")[1]) # for all layers... current_layer = 0 for layer_counter in range(len(data)-2): @@ -438,7 +461,7 @@ class DisplayInfoOnLCD(Script): # if display_remaining_time is checked, it is calculated in this loop if display_remaining_time: time_remaining_display = " | ET " # initialize the time display - m = (time_total - time_elapsed) // 60 # estimated time in minutes + m = (self.time_total - time_elapsed) // 60 # estimated time in minutes m *= speed_factor # correct for printing time m = int(m) h, m = divmod(m, 60) # convert to hours and minutes @@ -640,7 +663,8 @@ class DisplayInfoOnLCD(Script): model_list.append(model_name) # Filament stats extruder_count = global_stack.getProperty("machine_extruder_count", "value") - init_layer_hgt_line = ";Initial Layer Height: " + str(global_stack.getProperty("layer_height_0", "value")) + layheight_0 = global_stack.getProperty("layer_height_0", "value") + init_layer_hgt_line = ";Initial Layer Height: " + f"{layheight_0:.2f}".format(layheight_0) filament_line_t0 = ";Extruder 1 (T0)\n" filament_amount = Application.getInstance().getPrintInformation().materialLengths filament_line_t0 += f"; Filament used: {filament_amount[0]}m\n" @@ -659,16 +683,23 @@ class DisplayInfoOnLCD(Script): filament_line_t1 += f"; Filament Dia.: {global_stack.extruderList[1].getProperty("material_diameter", "value")}mm\n" filament_line_t1 += f"; Nozzle Size : {global_stack.extruderList[1].getProperty("machine_nozzle_size", "value")}mm\n" filament_line_t1 += f"; Print Temp. : {global_stack.extruderList[1].getProperty("material_print_temperature", "value")}°" - + + # Calculate the cost of electricity for the print + electricity_cost = self.getSettingValueByKey("electricity_cost") + printer_power_usage = self.getSettingValueByKey("printer_power_usage") + currency_unit = Application.getInstance().getPreferences().getValue("cura/currency") + total_cost_electricity = (printer_power_usage / 1000) * (self.time_total / 3600) * electricity_cost + # Add the stats to the gcode file lines = data[0].split("\n") for index, line in enumerate(lines): if line.startswith(";Layer height:"): + lines[index] = ";Layer height: " + f"{float(line.split(":")[1]):.2f}".format(float(line.split(":")[1])) lines[index] += f"\n{init_layer_hgt_line}" lines[index] += f"\n;Base Quality Name : '{global_stack.quality.getMetaDataEntry("name", "")}'" lines[index] += f"\n;Custom Quality Name: '{global_stack.qualityChanges.getMetaDataEntry("name")}'" if line.startswith(";Filament used"): - lines[index] = filament_line_t0 + filament_line_t1 + lines[index] = filament_line_t0 + filament_line_t1 + f"\n;Electric Cost: {currency_unit}{total_cost_electricity:.2f}".format(total_cost_electricity) # The target machine "machine_name" is actually the printer model. This adds the user defined printer name to the "TARGET_MACHINE" line. if line.startswith(";TARGET_MACHINE"): machine_model = str(global_stack.getProperty("machine_name", "value")) From cef22c5ca743a3d82ea22c8f95719e72cb27f22c Mon Sep 17 00:00:00 2001 From: THeijmans Date: Wed, 16 Jul 2025 13:33:02 +0200 Subject: [PATCH 31/98] PP-658 Fix BVT temperature limits based on thermal model F4 --- resources/definitions/ultimaker_factor4.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/ultimaker_factor4.def.json b/resources/definitions/ultimaker_factor4.def.json index 710ee29a40..07ff2b7cf8 100644 --- a/resources/definitions/ultimaker_factor4.def.json +++ b/resources/definitions/ultimaker_factor4.def.json @@ -74,8 +74,8 @@ { "maximum_value": "max(35, min((material_bed_temperature + 20) / 2, 70))", "maximum_value_warning": "max(30, min((material_bed_temperature + 10) / 2, 70))", - "minimum_value": "max((material_bed_temperature - 30) / 2, 30)", - "minimum_value_warning": "max((material_bed_temperature - 20) / 2, 30)" + "minimum_value": "max((material_bed_temperature - 40) / 1.5, 30)", + "minimum_value_warning": "max((material_bed_temperature - 35) / 1.5, 30)" }, "cool_min_layer_time": { "value": 3 }, "cool_min_layer_time_fan_speed_max": { "value": "cool_min_layer_time + 12" }, From 890543b7de78a5a6f879c14d159b77482b287d13 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 25 Jul 2025 11:02:37 +0200 Subject: [PATCH 32/98] Change Marketplace dialog creation and destruction CURA-11810 This will hopefully fix some display issues we have, especially on Mac platforms --- plugins/Marketplace/Marketplace.py | 29 +- .../Marketplace/resources/qml/Marketplace.qml | 440 +++++++++--------- 2 files changed, 229 insertions(+), 240 deletions(-) diff --git a/plugins/Marketplace/Marketplace.py b/plugins/Marketplace/Marketplace.py index 86910f8f4a..fc287b5877 100644 --- a/plugins/Marketplace/Marketplace.py +++ b/plugins/Marketplace/Marketplace.py @@ -21,7 +21,6 @@ class Marketplace(Extension, QObject): def __init__(self, parent: Optional[QObject] = None) -> None: QObject.__init__(self, parent) Extension.__init__(self) - self._window: Optional["QObject"] = None # If the window has been loaded yet, it'll be cached in here. self._package_manager = CuraApplication.getInstance().getPackageManager() self._material_package_list: Optional[RemotePackageList] = None @@ -79,20 +78,17 @@ class Marketplace(Extension, QObject): If the window hadn't been loaded yet into Qt, it will be created lazily. """ - if self._window is None: - plugin_registry = PluginRegistry.getInstance() - plugin_registry.pluginsEnabledOrDisabledChanged.connect(self.checkIfRestartNeeded) - plugin_path = plugin_registry.getPluginPath(self.getPluginId()) - if plugin_path is None: - plugin_path = os.path.dirname(__file__) - path = os.path.join(plugin_path, "resources", "qml", "Marketplace.qml") - self._window = CuraApplication.getInstance().createQmlComponent(path, {"manager": self}) - if self._window is None: # Still None? Failed to load the QML then. - return - if not self._window.isVisible(): - self.setTabShown(0) - self._window.show() - self._window.requestActivate() # Bring window into focus, if it was already open in the background. + + plugin_registry = PluginRegistry.getInstance() + plugin_registry.pluginsEnabledOrDisabledChanged.connect(self.checkIfRestartNeeded) + plugin_path = plugin_registry.getPluginPath(self.getPluginId()) + if plugin_path is None: + plugin_path = os.path.dirname(__file__) + path = os.path.join(plugin_path, "resources", "qml", "Marketplace.qml") + window = CuraApplication.getInstance().createQmlSubWindow(path, {"manager": self}) + + if window is not None: # Still None? Failed to load the QML then. + window.show() @pyqtSlot() def setVisibleTabToMaterials(self) -> None: @@ -103,9 +99,6 @@ class Marketplace(Extension, QObject): self.setTabShown(1) def checkIfRestartNeeded(self) -> None: - if self._window is None: - return - if self._package_manager.hasPackagesToRemoveOrInstall or \ PluginRegistry.getInstance().getCurrentSessionActivationChangedPlugins(): self._restart_needed = True diff --git a/plugins/Marketplace/resources/qml/Marketplace.qml b/plugins/Marketplace/resources/qml/Marketplace.qml index 8028b89e02..c858297ac9 100644 --- a/plugins/Marketplace/resources/qml/Marketplace.qml +++ b/plugins/Marketplace/resources/qml/Marketplace.qml @@ -9,7 +9,7 @@ import QtQuick.Window 2.2 import UM 1.5 as UM import Cura 1.6 as Cura -Window +UM.Dialog { id: marketplaceDialog property variant catalog: UM.I18nCatalog { name: "cura" } @@ -25,293 +25,289 @@ Window width: minimumWidth height: minimumHeight - onVisibleChanged: - { - while(contextStack.depth > 1) - { - contextStack.pop(); //Do NOT use the StackView.Immediate transition here, since it causes the window to stay empty. Seemingly a Qt bug: https://bugreports.qt.io/browse/QTBUG-60670? - } - } - - Connections - { - target: Cura.API.account - function onLoginStateChanged() - { - close(); - } - } - title: "Marketplace" //Seen by Ultimaker as a brand name, so this doesn't get translated. - modality: Qt.NonModal // Background color Rectangle { anchors.fill: parent color: UM.Theme.getColor("main_background") - } - //The Marketplace can have a page in front of everything with package details. The stack view controls its visibility. - StackView - { - id: contextStack - anchors.fill: parent - initialItem: packageBrowse - - ColumnLayout + //The Marketplace can have a page in front of everything with package details. The stack view controls its visibility. + StackView { - id: packageBrowse + id: contextStack + anchors.fill: parent - spacing: UM.Theme.getSize("narrow_margin").height + initialItem: packageBrowse - // Page title. - Item + ColumnLayout { - Layout.preferredWidth: parent.width - Layout.preferredHeight: childrenRect.height + UM.Theme.getSize("default_margin").height + id: packageBrowse - UM.Label + spacing: UM.Theme.getSize("narrow_margin").height + + // Page title. + Item { - id: pageTitle - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - bottom: parent.bottom - } + Layout.preferredWidth: parent.width + Layout.preferredHeight: childrenRect.height + UM.Theme.getSize("default_margin").height - font: UM.Theme.getFont("large") - text: content.item ? content.item.pageTitle: catalog.i18nc("@title", "Loading...") + UM.Label + { + id: pageTitle + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + bottom: parent.bottom + } + + font: UM.Theme.getFont("large") + text: content.item ? content.item.pageTitle : catalog.i18nc("@title", "Loading...") + } } - } - OnboardBanner - { - id: onBoardBanner - visible: content.item && content.item.bannerVisible - text: content.item && content.item.bannerText - icon: content.item && content.item.bannerIcon - onRemove: content.item && content.item.onRemoveBanner - readMoreUrl: content.item && content.item.bannerReadMoreUrl - - Layout.fillWidth: true - Layout.leftMargin: UM.Theme.getSize("default_margin").width - Layout.rightMargin: UM.Theme.getSize("default_margin").width - } - - // Search & Top-Level Tabs - Item - { - id: searchHeader - implicitHeight: childrenRect.height - implicitWidth: parent.width - 2 * UM.Theme.getSize("default_margin").width - Layout.alignment: Qt.AlignHCenter - RowLayout + OnboardBanner { - width: parent.width - height: UM.Theme.getSize("button_icon").height + UM.Theme.getSize("default_margin").height - spacing: UM.Theme.getSize("thin_margin").width + id: onBoardBanner + visible: content.item && content.item.bannerVisible + text: content.item && content.item.bannerText + icon: content.item && content.item.bannerIcon + onRemove: content.item && content.item.onRemoveBanner + readMoreUrl: content.item && content.item.bannerReadMoreUrl - Cura.SearchBar + Layout.fillWidth: true + Layout.leftMargin: UM.Theme.getSize("default_margin").width + Layout.rightMargin: UM.Theme.getSize("default_margin").width + } + + // Search & Top-Level Tabs + Item + { + id: searchHeader + implicitHeight: childrenRect.height + implicitWidth: parent.width - 2 * UM.Theme.getSize("default_margin").width + Layout.alignment: Qt.AlignHCenter + RowLayout { - id: searchBar - implicitHeight: UM.Theme.getSize("button_icon").height - Layout.fillWidth: true - onTextEdited: searchStringChanged(text) - } + width: parent.width + height: UM.Theme.getSize("button_icon").height + UM.Theme.getSize("default_margin").height + spacing: UM.Theme.getSize("thin_margin").width - // Page selection. - TabBar - { - id: pageSelectionTabBar - Layout.alignment: Qt.AlignRight - height: UM.Theme.getSize("button_icon").height - spacing: 0 - background: Rectangle { color: "transparent" } - currentIndex: manager.tabShown - - onCurrentIndexChanged: + Cura.SearchBar { - manager.tabShown = currentIndex - searchBar.text = ""; - searchBar.visible = currentItem.hasSearch; - content.source = currentItem.sourcePage; + id: searchBar + implicitHeight: UM.Theme.getSize("button_icon").height + Layout.fillWidth: true + onTextEdited: searchStringChanged(text) } - PackageTypeTab + // Page selection. + TabBar { - id: pluginTabText - width: implicitWidth - text: catalog.i18nc("@button", "Plugins") - property string sourcePage: "Plugins.qml" - property bool hasSearch: true - } - PackageTypeTab - { - id: materialsTabText - width: implicitWidth - text: catalog.i18nc("@button", "Materials") - property string sourcePage: "Materials.qml" - property bool hasSearch: true - } - ManagePackagesButton - { - property string sourcePage: "ManagedPackages.qml" - property bool hasSearch: false + id: pageSelectionTabBar + Layout.alignment: Qt.AlignRight + height: UM.Theme.getSize("button_icon").height + spacing: 0 + background: Rectangle { + color: "transparent" + } + currentIndex: manager.tabShown - Cura.NotificationIcon + onCurrentIndexChanged: { - anchors - { - horizontalCenter: parent.right - verticalCenter: parent.top - } - visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0 + manager.tabShown = currentIndex + searchBar.text = ""; + searchBar.visible = currentItem.hasSearch; + content.source = currentItem.sourcePage; + } - labelText: + PackageTypeTab + { + id: pluginTabText + width: implicitWidth + text: catalog.i18nc("@button", "Plugins") + property string sourcePage: "Plugins.qml" + property bool hasSearch: true + } + PackageTypeTab + { + id: materialsTabText + width: implicitWidth + text: catalog.i18nc("@button", "Materials") + property string sourcePage: "Materials.qml" + property bool hasSearch: true + } + ManagePackagesButton + { + property string sourcePage: "ManagedPackages.qml" + property bool hasSearch: false + + Cura.NotificationIcon { - const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length - return itemCount > 9 ? "9+" : itemCount + anchors + { + horizontalCenter: parent.right + verticalCenter: parent.top + } + visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0 + + labelText: + { + const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length + return itemCount > 9 ? "9+" : itemCount + } } } } } } - } - FontMetrics - { - id: fontMetrics - font: UM.Theme.getFont("default") - } + FontMetrics + { + id: fontMetrics + font: UM.Theme.getFont("default") + } - Cura.TertiaryButton - { - text: catalog.i18nc("@info", "Search in the browser") - iconSource: UM.Theme.getIcon("LinkExternal") - visible: pageSelectionTabBar.currentItem.hasSearch && searchHeader.visible - isIconOnRightSide: true - height: fontMetrics.height - textFont: fontMetrics.font - textColor: UM.Theme.getColor("text") + Cura.TertiaryButton + { + text: catalog.i18nc("@info", "Search in the browser") + iconSource: UM.Theme.getIcon("LinkExternal") + visible: pageSelectionTabBar.currentItem.hasSearch && searchHeader.visible + isIconOnRightSide: true + height: fontMetrics.height + textFont: fontMetrics.font + textColor: UM.Theme.getColor("text") - onClicked: content.item && Qt.openUrlExternally(content.item.searchInBrowserUrl) - } - - // Page contents. - Rectangle - { - Layout.preferredWidth: parent.width - Layout.fillHeight: true - color: UM.Theme.getColor("detail_background") + onClicked: content.item && Qt.openUrlExternally(content.item.searchInBrowserUrl) + } // Page contents. - Loader + Rectangle { - id: content - anchors.fill: parent - anchors.margins: UM.Theme.getSize("default_margin").width - source: "Plugins.qml" + Layout.preferredWidth: parent.width + Layout.fillHeight: true + color: UM.Theme.getColor("detail_background") - Connections + // Page contents. + Loader { - target: content - function onLoaded() + id: content + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + source: "Plugins.qml" + + Connections { - pageTitle.text = content.item.pageTitle - searchStringChanged.connect(handleSearchStringChanged) - } - function handleSearchStringChanged(new_search) - { - content.item.model.searchString = new_search + target: content + + function onLoaded() + { + pageTitle.text = content.item.pageTitle + searchStringChanged.connect(handleSearchStringChanged) + } + + function handleSearchStringChanged(new_search) + { + content.item.model.searchString = new_search + } } } } } } - } - Rectangle - { - height: quitButton.height + 2 * UM.Theme.getSize("default_margin").width - color: UM.Theme.getColor("primary") - visible: manager.showRestartNotification - anchors - { - left: parent.left - right: parent.right - bottom: parent.bottom - } - - RowLayout + Rectangle { + height: quitButton.height + 2 * UM.Theme.getSize("default_margin").width + color: UM.Theme.getColor("primary") + visible: manager.showRestartNotification anchors { left: parent.left right: parent.right - verticalCenter: parent.verticalCenter - margins: UM.Theme.getSize("default_margin").width + bottom: parent.bottom } - spacing: UM.Theme.getSize("default_margin").width - UM.ColorImage - { - id: bannerIcon - source: UM.Theme.getIcon("Plugin") - color: UM.Theme.getColor("primary_button_text") - implicitWidth: UM.Theme.getSize("banner_icon_size").width - implicitHeight: UM.Theme.getSize("banner_icon_size").height - } - Text + RowLayout { - color: UM.Theme.getColor("primary_button_text") - text: catalog.i18nc("@button", "In order to use the package you will need to restart Cura") - font: UM.Theme.getFont("default") - renderType: Text.NativeRendering - Layout.fillWidth: true - } - Cura.SecondaryButton - { - id: quitButton - text: catalog.i18nc("@info:button, %1 is the application name", "Quit %1").arg(CuraApplication.applicationDisplayName) - onClicked: + anchors { - marketplaceDialog.hide(); - CuraApplication.checkAndExitApplication(); + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + margins: UM.Theme.getSize("default_margin").width + } + spacing: UM.Theme.getSize("default_margin").width + UM.ColorImage + { + id: bannerIcon + source: UM.Theme.getIcon("Plugin") + + color: UM.Theme.getColor("primary_button_text") + implicitWidth: UM.Theme.getSize("banner_icon_size").width + implicitHeight: UM.Theme.getSize("banner_icon_size").height + } + Text + { + color: UM.Theme.getColor("primary_button_text") + text: catalog.i18nc("@button", "In order to use the package you will need to restart Cura") + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + Layout.fillWidth: true + } + Cura.SecondaryButton + { + id: quitButton + text: catalog.i18nc("@info:button, %1 is the application name", "Quit %1").arg(CuraApplication.applicationDisplayName) + onClicked: + { + marketplaceDialog.hide(); + CuraApplication.checkAndExitApplication(); + } } } } - } - Rectangle - { - color: UM.Theme.getColor("main_background") - anchors.fill: parent - visible: !Cura.API.account.isLoggedIn && CuraApplication.isEnterprise - - UM.Label + Rectangle { - id: signInLabel - anchors.centerIn: parent - width: Math.round(UM.Theme.getSize("modal_window_minimum").width / 2.5) - text: catalog.i18nc("@description","Please sign in to get verified plugins and materials for UltiMaker Cura Enterprise") - horizontalAlignment: Text.AlignHCenter + color: UM.Theme.getColor("main_background") + anchors.fill: parent + visible: !Cura.API.account.isLoggedIn && CuraApplication.isEnterprise + + UM.Label + { + id: signInLabel + anchors.centerIn: parent + width: Math.round(UM.Theme.getSize("modal_window_minimum").width / 2.5) + text: catalog.i18nc("@description", "Please sign in to get verified plugins and materials for UltiMaker Cura Enterprise") + horizontalAlignment: Text.AlignHCenter + } + + Cura.PrimaryButton + { + id: loginButton + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: signInLabel.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height * 2 + text: catalog.i18nc("@button", "Sign in") + fixedWidthMode: true + onClicked: Cura.API.account.login() + } } - Cura.PrimaryButton + Connections { - id: loginButton - width: UM.Theme.getSize("account_button").width - height: UM.Theme.getSize("account_button").height - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: signInLabel.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height * 2 - text: catalog.i18nc("@button", "Sign in") - fixedWidthMode: true - onClicked: Cura.API.account.login() + target: Cura.API.account + function onLoginStateChanged() + { + reject(); + } } } } From 376d18c7ee7d7abdf151b75ee79213d51c0d679f Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 25 Jul 2025 13:21:06 +0200 Subject: [PATCH 33/98] Change Preferences dialog creation and destruction CURA-11810 --- resources/qml/Cura.qml | 55 +++---- .../qml/Preferences/PreferencesDialog.qml | 136 ++++++++++++++++++ resources/qml/qmldir | 4 + 3 files changed, 161 insertions(+), 34 deletions(-) create mode 100644 resources/qml/Preferences/PreferencesDialog.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 77efc45fe8..bc5cc6a044 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -456,45 +456,32 @@ UM.MainWindow } } - UM.PreferencesDialog + Component { - id: preferences - - Component.onCompleted: + id: preferencesDialogComponent + Cura.PreferencesDialog { - //; Remove & re-add the general page as we want to use our own instead of uranium standard. - removePage(0); - insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml")); - - removePage(1); - insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml")); - - insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml")); - - insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/Materials/MaterialsPage.qml")); - - insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml")); - currentPage = 0; + selfDestroy: true } + } - onVisibleChanged: - { - // When the dialog closes, switch to the General page. - // This prevents us from having a heavy page like Setting Visibility active in the background. - setPage(0); - } + function showPreferencesDialog() + { + var dialog = preferencesDialogComponent.createObject(base) + dialog.show() + return dialog } Connections { target: Cura.Actions.preferences - function onTriggered() { preferences.visible = true } + function onTriggered() { showPreferencesDialog() } } Connections { target: CuraApplication - function onShowPreferencesWindow() { preferences.visible = true } + function onShowPreferencesWindow() { showPreferencesDialog() } } Connections @@ -511,8 +498,8 @@ UM.MainWindow target: Cura.Actions.configureMachines function onTriggered() { - preferences.visible = true; - preferences.setPage(2); + var dialog = showPreferencesDialog() + dialog.currentPage = 2; } } @@ -521,8 +508,8 @@ UM.MainWindow target: Cura.Actions.manageProfiles function onTriggered() { - preferences.visible = true; - preferences.setPage(4); + var dialog = showPreferencesDialog() + dialog.currentPage = 4; } } @@ -531,8 +518,8 @@ UM.MainWindow target: Cura.Actions.manageMaterials function onTriggered() { - preferences.visible = true; - preferences.setPage(3) + var dialog = showPreferencesDialog() + dialog.currentPage = 3; } } @@ -541,11 +528,11 @@ UM.MainWindow target: Cura.Actions.configureSettingVisibility function onTriggered(source) { - preferences.visible = true; - preferences.setPage(1); + var dialog = showPreferencesDialog() + dialog.currentPage = 1; if(source && source.key) { - preferences.getCurrentItem().scrollToSection(source.key); + dialog.currentItem.scrollToSection(source.key); } } } diff --git a/resources/qml/Preferences/PreferencesDialog.qml b/resources/qml/Preferences/PreferencesDialog.qml new file mode 100644 index 0000000000..0d46cd6b42 --- /dev/null +++ b/resources/qml/Preferences/PreferencesDialog.qml @@ -0,0 +1,136 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import ".." + +import UM 1.6 as UM + +UM.Dialog +{ + id: base + + title: catalog.i18nc("@title:window", "Preferences") + minimumWidth: UM.Theme.getSize("modal_window_minimum").width + minimumHeight: UM.Theme.getSize("modal_window_minimum").height + width: minimumWidth + height: minimumHeight + + property alias currentPage: pagesList.currentIndex + property alias currentItem: pagesList.currentItem + + Rectangle + { + anchors.fill: parent + color: UM.Theme.getColor("background_2") + } + + Item + { + id: test + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + + ListView + { + id: pagesList + width: UM.Theme.getSize("preferences_page_list_item").width + anchors.top: parent.top + anchors.bottom: parent.bottom + + ScrollBar.vertical: UM.ScrollBar {} + clip: true + model: [ + { + name: catalog.i18nc("@title:tab","General"), + item: Qt.resolvedUrl("GeneralPage.qml") + }, + { + name: catalog.i18nc("@title:tab","Settings"), + item: Qt.resolvedUrl("SettingVisibilityPage.qml") + }, + { + name: catalog.i18nc("@title:tab","Printers"), + item: Qt.resolvedUrl("MachinesPage.qml") + }, + { + name: catalog.i18nc("@title:tab","Materials"), + item: Qt.resolvedUrl("Materials/MaterialsPage.qml") + }, + { + name: catalog.i18nc("@title:tab","Profiles"), + item: Qt.resolvedUrl("ProfilesPage.qml") + } + ] + + delegate: Rectangle + { + width: parent ? parent.width : 0 + height: pageLabel.height + + color: ListView.isCurrentItem ? UM.Theme.getColor("background_3") : UM.Theme.getColor("main_background") + + UM.Label + { + id: pageLabel + anchors.centerIn: parent + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + width: parent.width + height: UM.Theme.getSize("preferences_page_list_item").height + color: UM.Theme.getColor("text_default") + text: modelData.name + } + MouseArea + { + anchors.fill: parent + onClicked: pagesList.currentIndex = index + } + } + + onCurrentIndexChanged: stackView.replace(model[currentIndex].item) + } + + StackView + { + id: stackView + anchors + { + left: pagesList.right + leftMargin: UM.Theme.getSize("narrow_margin").width + top: parent.top + bottom: parent.bottom + right: parent.right + } + + initialItem: Item { property bool resetEnabled: false } + + replaceEnter: Transition + { + NumberAnimation + { + properties: "opacity" + from: 0 + to: 1 + duration: 100 + } + } + replaceExit: Transition + { + NumberAnimation + { + properties: "opacity" + from: 1 + to: 0 + duration: 100 + } + } + } + + UM.I18nCatalog { id: catalog; name: "uranium"; } + } +} diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 8fce82c858..98140608c6 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -52,3 +52,7 @@ NumericTextFieldWithUnit 1.0 NumericTextFieldWithUnit.qml PrintHeadMinMaxTextField 1.0 PrintHeadMinMaxTextField.qml SimpleCheckBox 1.0 SimpleCheckBox.qml RenameDialog 1.0 RenameDialog.qml + +# Cura/Preferences + +PreferencesDialog 1.0 PreferencesDialog.qml From d00a9b8715fb9eaa8f0c4b6b605de2f684bb69d4 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 25 Jul 2025 14:15:55 +0200 Subject: [PATCH 34/98] Fix double margin in Preferences dialog CURA-11810 --- .../qml/Preferences/PreferencesDialog.qml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/resources/qml/Preferences/PreferencesDialog.qml b/resources/qml/Preferences/PreferencesDialog.qml index 0d46cd6b42..f175370205 100644 --- a/resources/qml/Preferences/PreferencesDialog.qml +++ b/resources/qml/Preferences/PreferencesDialog.qml @@ -19,21 +19,15 @@ UM.Dialog minimumHeight: UM.Theme.getSize("modal_window_minimum").height width: minimumWidth height: minimumHeight + backgroundColor: UM.Theme.getColor("background_2") property alias currentPage: pagesList.currentIndex property alias currentItem: pagesList.currentItem - Rectangle - { - anchors.fill: parent - color: UM.Theme.getColor("background_2") - } - Item { id: test anchors.fill: parent - anchors.margins: UM.Theme.getSize("default_margin").width ListView { @@ -46,23 +40,23 @@ UM.Dialog clip: true model: [ { - name: catalog.i18nc("@title:tab","General"), + name: catalog.i18nc("@title:tab", "General"), item: Qt.resolvedUrl("GeneralPage.qml") }, { - name: catalog.i18nc("@title:tab","Settings"), + name: catalog.i18nc("@title:tab", "Settings"), item: Qt.resolvedUrl("SettingVisibilityPage.qml") }, { - name: catalog.i18nc("@title:tab","Printers"), + name: catalog.i18nc("@title:tab", "Printers"), item: Qt.resolvedUrl("MachinesPage.qml") }, { - name: catalog.i18nc("@title:tab","Materials"), + name: catalog.i18nc("@title:tab", "Materials"), item: Qt.resolvedUrl("Materials/MaterialsPage.qml") }, { - name: catalog.i18nc("@title:tab","Profiles"), + name: catalog.i18nc("@title:tab", "Profiles"), item: Qt.resolvedUrl("ProfilesPage.qml") } ] From 37c364e8a5692bc3b7fd950a586a8918589132da Mon Sep 17 00:00:00 2001 From: THeijmans Date: Wed, 30 Jul 2025 14:11:57 +0200 Subject: [PATCH 35/98] PP-650-High-speed-profile-improvements-S6S8 --- resources/definitions/ultimaker_s8.def.json | 61 +++++++++++-------- ...um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg | 22 +++++++ ...m_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg | 22 +++++++ ...um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg | 22 +++++++ ...aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg | 22 +++++++ .../um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg | 5 +- .../um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg | 4 +- .../um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg | 4 ++ .../um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg | 4 ++ .../um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg | 4 ++ ...m_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg | 4 ++ ...um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg | 4 ++ ...um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg | 4 ++ 13 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg create mode 100644 resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg create mode 100644 resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg create mode 100644 resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg diff --git a/resources/definitions/ultimaker_s8.def.json b/resources/definitions/ultimaker_s8.def.json index b7de27722d..cbd6e255f8 100644 --- a/resources/definitions/ultimaker_s8.def.json +++ b/resources/definitions/ultimaker_s8.def.json @@ -98,7 +98,7 @@ { "maximum_value": "machine_max_acceleration_x", "maximum_value_warning": "machine_max_acceleration_x*0.8", - "value": "acceleration_wall_0" + "value": "acceleration_topbottom / 2" }, "acceleration_skirt_brim": { @@ -199,15 +199,19 @@ }, "adhesion_type": { "value": "'brim' if support_enable and support_structure=='tree' else 'skirt'" }, "bottom_thickness": { "value": "3*layer_height if top_layers==4 and not support_enable else top_bottom_thickness" }, - "bridge_skin_material_flow": { "value": 200 }, + "bridge_enable_more_layers": { "value": true }, + "bridge_skin_density": { "value": 70 }, + "bridge_skin_material_flow": { "value": 150 }, + "bridge_skin_material_flow_2": { "value": 70 }, "bridge_skin_speed": { "unit": "mm/s", - "value": "bridge_wall_speed" + "value": 35 }, + "bridge_skin_speed_2": { "value": "speed_print*2/3" }, "bridge_sparse_infill_max_density": { "value": 50 }, - "bridge_wall_material_flow": { "value": "bridge_skin_material_flow" }, - "bridge_wall_min_length": { "value": 10 }, + "bridge_wall_material_flow": { "value": 200 }, + "bridge_wall_min_length": { "value": 2 }, "bridge_wall_speed": { "unit": "mm/s", @@ -221,13 +225,13 @@ ] }, "cool_during_extruder_switch": { "value": "'all_fans'" }, - "cool_min_layer_time": { "value": 5 }, - "cool_min_layer_time_overhang": { "value": 9 }, - "cool_min_layer_time_overhang_min_segment_length": { "value": 2 }, + "cool_min_layer_time": { "value": 6 }, + "cool_min_layer_time_overhang": { "value": 11 }, + "cool_min_layer_time_overhang_min_segment_length": { "value": 1.5 }, "cool_min_speed": { "value": 6 }, "cool_min_temperature": { - "minimum_value_warning": "material_print_temperature-15", + "minimum_value_warning": "material_print_temperature-20", "value": "material_print_temperature-15" }, "default_material_print_temperature": { "maximum_value_warning": 320 }, @@ -235,9 +239,9 @@ "flooring_layer_count": { "value": 1 }, "gradual_flow_enabled": { "value": false }, "hole_xy_offset": { "value": 0.075 }, - "infill_material_flow": { "value": "material_flow" }, + "infill_material_flow": { "value": "material_flow if infill_sparse_density < 95 else 95" }, "infill_overlap": { "value": 10 }, - "infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'grid'" }, + "infill_pattern": { "value": "'zigzag' if infill_sparse_density > 50 else 'grid'" }, "infill_sparse_density": { "value": 15 }, "infill_wall_line_count": { "value": "1 if infill_sparse_density > 80 else 0" }, "initial_bottom_layers": { "value": 2 }, @@ -281,7 +285,7 @@ { "maximum_value_warning": "machine_max_jerk_xy / 2", "unit": "m/s\u00b3", - "value": "jerk_wall_0" + "value": "jerk_print" }, "jerk_skirt_brim": { @@ -438,10 +442,11 @@ "retraction_hop": { "value": 1 }, "retraction_hop_after_extruder_switch_height": { "value": 2 }, "retraction_hop_enabled": { "value": true }, - "retraction_min_travel": { "value": "5 if support_enable and support_structure=='tree' else line_width * 2.5" }, + "retraction_min_travel": { "value": "2.5 if support_enable and support_structure=='tree' else line_width * 2.5" }, "retraction_prime_speed": { "value": 15 }, "skin_edge_support_thickness": { "value": 0 }, - "skin_material_flow": { "value": 95 }, + "skin_material_flow": { "value": 93 }, + "skin_outline_count": { "value": 0 }, "skin_overlap": { "value": 0 }, "skin_preshrink": { "value": 0 }, "skirt_brim_minimal_length": { "value": 1000 }, @@ -571,38 +576,46 @@ "value": "speed_wall" }, "support_angle": { "value": 60 }, - "support_bottom_distance": { "maximum_value_warning": "3*layer_height" }, + "support_bottom_distance": + { + "maximum_value_warning": "3*layer_height", + "value": "support_z_distance" + }, "support_bottom_offset": { "value": 0 }, "support_brim_width": { "value": 10 }, "support_interface_enable": { "value": true }, "support_interface_offset": { "value": "support_offset" }, "support_line_width": { "value": "1.25*line_width" }, - "support_offset": { "value": "1.2 if support_structure == 'tree' else 0.8" }, + "support_offset": { "value": 0.8 }, "support_pattern": { "value": "'gyroid' if support_structure == 'tree' else 'lines'" }, "support_roof_height": { "minimum_value_warning": 0 }, "support_structure": { "value": "'normal'" }, "support_top_distance": { "maximum_value_warning": "3*layer_height" }, "support_tree_angle": { "value": 50 }, "support_tree_angle_slow": { "value": 35 }, - "support_tree_bp_diameter": { "value": 15 }, - "support_tree_branch_diameter": { "value": 8 }, - "support_tree_tip_diameter": { "value": 1.0 }, - "support_tree_top_rate": { "value": 20 }, - "support_xy_distance_overhang": { "value": "machine_nozzle_size" }, - "support_z_distance": { "value": "0.4*material_shrinkage_percentage_z/100.0" }, - "top_bottom_thickness": { "value": "round(4*layer_height, 2)" }, + "support_tree_bp_diameter": { "value": 20 }, + "support_tree_branch_diameter": { "value": 5 }, + "support_tree_branch_diameter_angle": { "value": 5 }, + "support_tree_max_diameter": { "value": 15 }, + "support_tree_tip_diameter": { "value": 2.0 }, + "support_tree_top_rate": { "value": 10 }, + "support_xy_distance": { "value": 1.2 }, + "support_xy_distance_overhang": { "value": "1.5*machine_nozzle_size" }, + "support_z_distance": { "value": "2*layer_height" }, + "top_bottom_thickness": { "value": "wall_thickness" }, "travel_avoid_other_parts": { "value": true }, "travel_avoid_supports": { "value": true }, "wall_0_acceleration": { "value": 1000 }, "wall_0_deceleration": { "value": 1000 }, "wall_0_end_speed_ratio": { "value": 100 }, + "wall_0_inset": { "value": 0.05 }, "wall_0_speed_split_distance": { "value": 0.2 }, "wall_0_start_speed_ratio": { "value": 100 }, "wall_0_wipe_dist": { "value": 0 }, "wall_material_flow": { "value": 95 }, "wall_overhang_angle": { "value": 45 }, "wall_x_material_flow": { "value": 100 }, - "xy_offset": { "value": 0.05 }, + "xy_offset": { "value": 0.075 }, "z_seam_corner": { "value": "'z_seam_corner_weighted'" }, "z_seam_position": { "value": "'backright'" }, "z_seam_type": { "value": "'sharpest_corner'" } diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg new file mode 100644 index 0000000000..9889424797 --- /dev/null +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg @@ -0,0 +1,22 @@ +[general] +definition = ultimaker_s8 +name = Quick +version = 4 + +[metadata] +intent_category = quick +material = generic_abs +quality_type = draft +setting_version = 25 +type = intent +variant = AA+ 0.4 + +[values] +cool_min_layer_time = 5 +cool_min_layer_time_overhang = 9 +cool_min_speed = 6 +cool_min_temperature = =material_print_temperature - 15 +speed_wall_x = =speed_print +speed_wall_x_roofing = =speed_wall +wall_line_width_x = =wall_line_width + diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg new file mode 100644 index 0000000000..ae38c02395 --- /dev/null +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg @@ -0,0 +1,22 @@ +[general] +definition = ultimaker_s8 +name = Quick +version = 4 + +[metadata] +intent_category = quick +material = generic_petg +quality_type = draft +setting_version = 25 +type = intent +variant = AA+ 0.4 + +[values] +cool_min_layer_time = 5 +cool_min_layer_time_overhang = 9 +cool_min_speed = 6 +cool_min_temperature = =material_print_temperature - 15 +speed_wall_x = =speed_print +speed_wall_x_roofing = =speed_wall +wall_line_width_x = =wall_line_width + diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg new file mode 100644 index 0000000000..4509317076 --- /dev/null +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg @@ -0,0 +1,22 @@ +[general] +definition = ultimaker_s8 +name = Quick +version = 4 + +[metadata] +intent_category = quick +material = generic_pla +quality_type = draft +setting_version = 25 +type = intent +variant = AA+ 0.4 + +[values] +cool_min_layer_time = 5 +cool_min_layer_time_overhang = 9 +cool_min_speed = 6 +cool_min_temperature = =material_print_temperature - 15 +speed_wall_x = =speed_print +speed_wall_x_roofing = =speed_wall +wall_line_width_x = =wall_line_width + diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg new file mode 100644 index 0000000000..4f75b631df --- /dev/null +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg @@ -0,0 +1,22 @@ +[general] +definition = ultimaker_s8 +name = Quick +version = 4 + +[metadata] +intent_category = quick +material = generic_tough_pla +quality_type = draft +setting_version = 25 +type = intent +variant = AA+ 0.4 + +[values] +cool_min_layer_time = 5 +cool_min_layer_time_overhang = 9 +cool_min_speed = 6 +cool_min_temperature = =material_print_temperature - 15 +speed_wall_x = =speed_print +speed_wall_x_roofing = =speed_wall +wall_line_width_x = =wall_line_width + diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg index 37767673aa..cc5e850220 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg @@ -14,7 +14,10 @@ weight = -2 [values] cool_min_layer_time = 4 cool_min_layer_time_fan_speed_max = 9 -cool_min_temperature = =material_print_temperature - 10 +cool_min_temperature = =material_print_temperature - 20 retraction_prime_speed = 15 +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg index a22e4fbeec..9abcd5ddd2 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg @@ -15,5 +15,7 @@ weight = -2 cool_min_layer_time = 4 material_print_temperature = =default_material_print_temperature + 5 retraction_prime_speed = 15 -support_structure = tree +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg index 6c445180f8..9feab61e0e 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg @@ -12,8 +12,12 @@ variant = AA+ 0.4 weight = -1 [values] +cool_min_temperature = =material_print_temperature - 20 material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg index d3d99eec9e..8431bb9c43 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg @@ -12,9 +12,13 @@ variant = AA+ 0.4 weight = 0 [values] +cool_min_temperature = =material_print_temperature - 20 material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree top_bottom_thickness = =round(6*layer_height,3) +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg index 2e015f8a88..7a5d19dc2c 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg @@ -12,8 +12,12 @@ variant = AA+ 0.4 weight = -2 [values] +cool_min_temperature = =material_print_temperature - 20 material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg index e3477c1e7d..f17d3fde40 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg @@ -12,7 +12,11 @@ variant = AA+ 0.4 weight = -1 [values] +cool_min_temperature = =material_print_temperature - 20 retraction_prime_speed = =retraction_speed retraction_speed = 25 +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg index 6ccc0da6dd..672eae3e4a 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg @@ -12,7 +12,11 @@ variant = AA+ 0.4 weight = 0 [values] +cool_min_temperature = =material_print_temperature - 20 retraction_prime_speed = =retraction_speed +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree top_bottom_thickness = =round(6*layer_height,3) +wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg index bb629e0758..716765aac5 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg @@ -12,6 +12,10 @@ variant = AA+ 0.4 weight = -2 [values] +cool_min_temperature = =material_print_temperature - 20 retraction_prime_speed = =retraction_speed +speed_wall_x = =speed_wall +speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree +wall_line_width_x = =wall_line_width * 1.25 From 5040e7f230b031ece60ccbee3cf6224a0567cf74 Mon Sep 17 00:00:00 2001 From: Timur Seitosmanov Date: Wed, 3 Jul 2024 12:10:43 +0200 Subject: [PATCH 36/98] Trigger machine error checking during startup. Otherwise slicing will keep failing until selected printer is changed. --- cura/Machines/MachineErrorChecker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index 5edee0778f..87d50e46d4 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -61,6 +61,7 @@ class MachineErrorChecker(QObject): self._machine_manager.globalContainerChanged.connect(self.startErrorCheck) self._onMachineChanged() + self.startErrorCheck() def _setCheckTimer(self) -> None: """A QTimer to regulate error check frequency From da0509cda35f66df6f03bb1046ff6170a7ab5c4a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 30 Jul 2025 15:58:26 +0200 Subject: [PATCH 37/98] Fix painting through an invisible object CURA-12660 --- cura/PickingPass.py | 6 ++++-- plugins/PaintTool/PaintTool.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cura/PickingPass.py b/cura/PickingPass.py index 4d6ef671df..dd91659afe 100644 --- a/cura/PickingPass.py +++ b/cura/PickingPass.py @@ -7,6 +7,7 @@ from UM.Qt.QtApplication import QtApplication from UM.Logger import Logger from UM.Math.Vector import Vector from UM.Resources import Resources +from UM.Scene.Selection import Selection from UM.View.RenderPass import RenderPass from UM.View.GL.OpenGL import OpenGL @@ -27,13 +28,14 @@ class PickingPass(RenderPass): .. note:: that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels """ - def __init__(self, width: int, height: int) -> None: + def __init__(self, width: int, height: int, only_selected_objects: bool = False) -> None: super().__init__("picking", width, height) self._renderer = QtApplication.getInstance().getRenderer() self._shader = None #type: Optional[ShaderProgram] self._scene = QtApplication.getInstance().getController().getScene() + self._only_selected_objects = only_selected_objects def render(self) -> None: if not self._shader: @@ -53,7 +55,7 @@ class PickingPass(RenderPass): # Fill up the batch with objects that can be sliced. ` for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. - if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible(): + if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible() and (not self._only_selected_objects or Selection.isSelected(node)): batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) self.bind() diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index 524011af9d..fcc5c9a3a0 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -300,7 +300,9 @@ class PaintTool(Tool): return False if not self._picking_pass: - self._picking_pass = PickingPass(camera.getViewportWidth(), camera.getViewportHeight()) + self._picking_pass = PickingPass(camera.getViewportWidth(), + camera.getViewportHeight(), + only_selected_objects = True) self._picking_pass.render() self._selection_pass.renderFacesMode() From 73f5b817b438df0b03b943b3993d2dea656cdffe Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 30 Jul 2025 15:59:30 +0200 Subject: [PATCH 38/98] Display build plate in paint mode CURA-12660 --- plugins/PaintTool/PaintView.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index 22eb8c55f6..18dc067c5a 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -8,10 +8,13 @@ from typing import Optional, List, Tuple, Dict from PyQt6.QtGui import QImage, QColor, QPainter from cura.CuraApplication import CuraApplication +from cura.BuildVolume import BuildVolume from UM.PluginRegistry import PluginRegistry from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.Texture import Texture from UM.View.View import View +from UM.View.SelectionPass import SelectionPass +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Selection import Selection from UM.View.GL.OpenGL import OpenGL from UM.i18n import i18nCatalog @@ -163,6 +166,11 @@ class PaintView(View): def beginRendering(self) -> None: renderer = self.getRenderer() self._checkSetup() + + for node in DepthFirstIterator(self._scene.getRoot()): + if isinstance(node, BuildVolume): + node.render(renderer) + paint_batch = renderer.createRenderBatch(shader=self._paint_shader) renderer.addRenderBatch(paint_batch) From 6bf9a8a0aeba3b7157ae4bdd695e9ffa50396bca Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 30 Jul 2025 16:00:13 +0200 Subject: [PATCH 39/98] Ignore invisible object for selection in paint mode CURA-12660 --- plugins/PaintTool/PaintView.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index 18dc067c5a..c37afd178d 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -3,7 +3,7 @@ import os from PyQt6.QtCore import QRect -from typing import Optional, List, Tuple, Dict +from typing import Optional, List, Tuple, Dict, cast from PyQt6.QtGui import QImage, QColor, QPainter @@ -47,7 +47,9 @@ class PaintView(View): self._force_opaque_mask = QImage(2, 2, QImage.Format.Format_Mono) self._force_opaque_mask.fill(1) - CuraApplication.getInstance().engineCreatedSignal.connect(self._makePaintModes) + application = CuraApplication.getInstance() + application.engineCreatedSignal.connect(self._makePaintModes) + self._scene = application.getController().getScene() def _makePaintModes(self): theme = CuraApplication.getInstance().getTheme() @@ -164,6 +166,9 @@ class PaintView(View): return start_index, end_index def beginRendering(self) -> None: + if self._current_paint_type == "": + return + renderer = self.getRenderer() self._checkSetup() @@ -174,12 +179,20 @@ class PaintView(View): paint_batch = renderer.createRenderBatch(shader=self._paint_shader) renderer.addRenderBatch(paint_batch) - node = Selection.getSelectedObject(0) - if node is None: - return + display_objects = Selection.getAllSelectedObjects().copy() + if display_objects: + selection_pass = cast(SelectionPass, renderer.getRenderPass("selection")) + if selection_pass is not None: + selection_pass.setIgnoreUnselectedObjectsDuringNextRender() + else: + for node in DepthFirstIterator(self._scene.getRoot()): + if node.callDecoration("isSliceable"): + display_objects.append(node) - if self._current_paint_type == "": - return + for node in display_objects: + paint_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) + self._current_paint_texture = node.callDecoration("getPaintTexture") + self._paint_shader.setTexture(0, self._current_paint_texture) self._paint_shader.setUniformValue("u_bitsRangesStart", self._current_bits_ranges[0]) self._paint_shader.setUniformValue("u_bitsRangesEnd", self._current_bits_ranges[1]) @@ -187,8 +200,3 @@ class PaintView(View): colors = [paint_type_obj.display_color for paint_type_obj in self._paint_modes[self._current_paint_type].values()] colors_values = [[int(color_part * 255) for color_part in [color.r, color.g, color.b]] for color in colors] self._paint_shader.setUniformValueArray("u_renderColors", colors_values) - - self._current_paint_texture = node.callDecoration("getPaintTexture") - self._paint_shader.setTexture(0, self._current_paint_texture) - - paint_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) From 6896c0ed4bfb23475fb52d50d76900ec6a85906e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 30 Jul 2025 16:53:41 +0200 Subject: [PATCH 40/98] Display classic view when there is no selection CURA-12660 --- plugins/PaintTool/PaintView.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index c37afd178d..60656086ff 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -9,10 +9,10 @@ from PyQt6.QtGui import QImage, QColor, QPainter from cura.CuraApplication import CuraApplication from cura.BuildVolume import BuildVolume +from plugins.SolidView.SolidView import SolidView from UM.PluginRegistry import PluginRegistry from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.Texture import Texture -from UM.View.View import View from UM.View.SelectionPass import SelectionPass from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Selection import Selection @@ -23,7 +23,7 @@ from UM.Math.Color import Color catalog = i18nCatalog("cura") -class PaintView(View): +class PaintView(SolidView): """View for model-painting.""" UNDO_STACK_SIZE = 1024 @@ -62,6 +62,8 @@ class PaintView(View): } def _checkSetup(self): + super()._checkSetup() + if not self._paint_shader: shader_filename = os.path.join(PluginRegistry.getInstance().getPluginPath("PaintTool"), "paint.shader") self._paint_shader = OpenGL.getInstance().createShaderProgram(shader_filename) @@ -169,8 +171,14 @@ class PaintView(View): if self._current_paint_type == "": return - renderer = self.getRenderer() + display_objects = Selection.getAllSelectedObjects().copy() + if not display_objects: + # Display the classic view until an object is selected + super().beginRendering() + return + self._checkSetup() + renderer = self.getRenderer() for node in DepthFirstIterator(self._scene.getRoot()): if isinstance(node, BuildVolume): @@ -179,15 +187,9 @@ class PaintView(View): paint_batch = renderer.createRenderBatch(shader=self._paint_shader) renderer.addRenderBatch(paint_batch) - display_objects = Selection.getAllSelectedObjects().copy() - if display_objects: - selection_pass = cast(SelectionPass, renderer.getRenderPass("selection")) - if selection_pass is not None: - selection_pass.setIgnoreUnselectedObjectsDuringNextRender() - else: - for node in DepthFirstIterator(self._scene.getRoot()): - if node.callDecoration("isSliceable"): - display_objects.append(node) + selection_pass = cast(SelectionPass, renderer.getRenderPass("selection")) + if selection_pass is not None: + selection_pass.setIgnoreUnselectedObjectsDuringNextRender() for node in display_objects: paint_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) From 91e986697d229f85fd0375424d43e57e32ce5685 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 30 Jul 2025 16:54:05 +0200 Subject: [PATCH 41/98] Fix painting after changing the selected object CURA-12660 --- plugins/PaintTool/PaintTool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index fcc5c9a3a0..2099a59691 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -293,6 +293,7 @@ class PaintTool(Tool): self._node_cache.transformationChanged.disconnect(self._nodeTransformChanged) self._node_cache = node self._node_cache.transformationChanged.connect(self._nodeTransformChanged) + self._cache_dirty = True if self._cache_dirty: self._cache_dirty = False self._mesh_transformed_cache = self._node_cache.getMeshDataTransformed() From 6292f5b133f6423bd758bf1d56c738f8517d050d Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 30 Jul 2025 16:57:29 +0200 Subject: [PATCH 42/98] Hide paint-on-support option until it is implemented CURA-12660 --- plugins/PaintTool/PaintTool.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PaintTool/PaintTool.qml b/plugins/PaintTool/PaintTool.qml index 4cbe9d4ade..e3f244dd4c 100644 --- a/plugins/PaintTool/PaintTool.qml +++ b/plugins/PaintTool/PaintTool.qml @@ -57,6 +57,7 @@ Item icon: "Support" tooltipText: catalog.i18nc("@tooltip", "Refine support placement by defining preferred/avoidance areas") mode: "support" + visible: false } } From ef7bde87fa4469174fd01c5cfca8cca59c545129 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 31 Jul 2025 11:24:36 +0200 Subject: [PATCH 43/98] Allow painting only when 1 object is selected CURA-12660 --- plugins/PaintTool/PaintView.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index 60656086ff..61a6d0079c 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -172,8 +172,8 @@ class PaintView(SolidView): return display_objects = Selection.getAllSelectedObjects().copy() - if not display_objects: - # Display the classic view until an object is selected + if len(display_objects) != 1: + # Display the classic view until a single object is selected super().beginRendering() return From 3cb7eb3c873ed195ae998b0dd8ed15cea884a162 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 31 Jul 2025 11:47:41 +0200 Subject: [PATCH 44/98] Avoid too dark or too light areas while painting CURA-12660 This avoid having parts of the model where you cannot see the painted areas anymore --- plugins/PaintTool/paint.shader | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PaintTool/paint.shader b/plugins/PaintTool/paint.shader index bd769f5cb2..f2af66ffe6 100644 --- a/plugins/PaintTool/paint.shader +++ b/plugins/PaintTool/paint.shader @@ -55,7 +55,7 @@ fragment = color_index = (color_index << (32 - 1 - u_bitsRangesEnd)) >> 32 - 1 - (u_bitsRangesEnd - u_bitsRangesStart); vec4 diffuse_color = vec4(u_renderColors[color_index] / 255.0, 1.0); - highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0); + highp float n_dot_l = mix(0.3, 0.7, dot(normal, light_dir)); final_color += (n_dot_l * diffuse_color); final_color.a = u_opacity; @@ -122,7 +122,7 @@ fragment41core = color_index = (color_index << (32 - 1 - u_bitsRangesEnd)) >> 32 - 1 - (u_bitsRangesEnd - u_bitsRangesStart); vec4 diffuse_color = vec4(u_renderColors[color_index] / 255.0, 1.0); - highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0); + highp float n_dot_l = mix(0.3, 0.7, dot(normal, light_dir)); final_color += (n_dot_l * diffuse_color); final_color.a = u_opacity; From ab58dec5d141c12a0cde128fb702126ec39914cd Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 1 Aug 2025 13:10:03 +0200 Subject: [PATCH 45/98] Fix unability to paint with visible message box CURA-12660 When a message box is displayed, some offscreen rendering passes (face selection) render an unpredictable result and we are unable to start painting. This went through a refactoring of the rendering passes. Since doing the offscreen rendering outside the Qt rendering loop caused some troubles, we now use the rendering passes only inside the Qt rendering loop, so that they work properly. Tools also have the ability to indicate which extra passes they require, so that we don't run all the passes when they are not required. Since this issue also concerns the support blockers placement and rotation by face selection, they have been updated so that they now also always work. The face selection mechanism using the Selection class was partially working and used only by the rotation, so now it has been deprecated in favor of the new mechanism. --- cura/CuraApplication.py | 4 +++ cura/CuraRenderer.py | 46 ++++++++++++++++++++++++++ cura/PickingPass.py | 2 +- plugins/PaintTool/PaintTool.py | 40 +++++++++++++++------- plugins/PaintTool/PaintView.py | 5 --- plugins/SupportEraser/SupportEraser.py | 17 +++++++--- 6 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 cura/CuraRenderer.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 8af98c2d0e..660f312468 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -60,6 +60,7 @@ from cura import ApplicationMetadata from cura.API import CuraAPI from cura.API.Account import Account from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob +from cura.CuraRenderer import CuraRenderer from cura.Machines.MachineErrorChecker import MachineErrorChecker from cura.Machines.Models.BuildPlateModel import BuildPlateModel from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel @@ -362,6 +363,9 @@ class CuraApplication(QtApplication): self._machine_action_manager = MachineActionManager(self) self._machine_action_manager.initialize() + def makeRenderer(self) -> CuraRenderer: + return CuraRenderer(self) + def __sendCommandToSingleInstance(self): self._single_instance = SingleInstance(self, self._files_to_open, self._urls_to_open) diff --git a/cura/CuraRenderer.py b/cura/CuraRenderer.py new file mode 100644 index 0000000000..77030b3fe8 --- /dev/null +++ b/cura/CuraRenderer.py @@ -0,0 +1,46 @@ +# Copyright (c) 2025 UltiMaker +# Uranium is released under the terms of the LGPLv3 or higher. + + +from typing import TYPE_CHECKING + +from cura.PickingPass import PickingPass +from UM.Qt.QtRenderer import QtRenderer +from UM.View.RenderPass import RenderPass +from UM.View.SelectionPass import SelectionPass + +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + + +class CuraRenderer(QtRenderer): + """An overridden Renderer implementation that adds some behaviors specific to Cura.""" + + def __init__(self, application: "CuraApplication") -> None: + super().__init__() + + self._controller = application.getController() + self._controller.activeToolChanged.connect(self._onActiveToolChanged) + self._extra_rendering_passes: list[RenderPass] = [] + + def _onActiveToolChanged(self) -> None: + tool_extra_rendering_passes = [] + + active_tool = self._controller.getActiveTool() + if active_tool is not None: + tool_extra_rendering_passes = active_tool.getRequiredExtraRenderingPasses() + + for extra_rendering_pass in self._extra_rendering_passes: + extra_rendering_pass.setEnabled(extra_rendering_pass.getName() in tool_extra_rendering_passes) + + def _makeRenderPasses(self) -> list[RenderPass]: + self._extra_rendering_passes = [ + SelectionPass(self._viewport_width, self._viewport_height, SelectionPass.SelectionMode.FACES), + PickingPass(self._viewport_width, self._viewport_height, only_selected_objects=True), + PickingPass(self._viewport_width, self._viewport_height, only_selected_objects=False) + ] + + for extra_rendering_pass in self._extra_rendering_passes: + extra_rendering_pass.setEnabled(False) + + return super()._makeRenderPasses() + self._extra_rendering_passes diff --git a/cura/PickingPass.py b/cura/PickingPass.py index dd91659afe..e585e72269 100644 --- a/cura/PickingPass.py +++ b/cura/PickingPass.py @@ -29,7 +29,7 @@ class PickingPass(RenderPass): """ def __init__(self, width: int, height: int, only_selected_objects: bool = False) -> None: - super().__init__("picking", width, height) + super().__init__("picking" if not only_selected_objects else "picking_selected", width, height) self._renderer = QtApplication.getInstance().getRenderer() diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index 2099a59691..e57c6d5a11 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -17,7 +17,9 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.Selection import Selection from UM.Tool import Tool +from cura.CuraApplication import CuraApplication from cura.PickingPass import PickingPass +from UM.View.SelectionPass import SelectionPass from .PaintView import PaintView @@ -34,6 +36,7 @@ class PaintTool(Tool): super().__init__() self._picking_pass: Optional[PickingPass] = None + self._faces_selection_pass: Optional[SelectionPass] = None self._shortcut_key: Qt.Key = Qt.Key.Key_P @@ -52,6 +55,8 @@ class PaintTool(Tool): self._last_mouse_coords: Optional[Tuple[int, int]] = None self._last_face_id: Optional[int] = None + Selection.selectionChanged.connect(self._updateIgnoreUnselectedObjects) + def _createBrushPen(self) -> QPen: pen = QPen() pen.setWidth(self._brush_size) @@ -179,7 +184,7 @@ class PaintTool(Tool): self._cache_dirty = True def _getTexCoordsFromClick(self, node: SceneNode, x: float, y: float) -> Tuple[int, Optional[numpy.ndarray]]: - face_id = self._selection_pass.getFaceIdAtPosition(x, y) + face_id = self._faces_selection_pass.getFaceIdAtPosition(x, y) if face_id < 0 or face_id >= node.getMeshData().getFaceCount(): return face_id, None @@ -248,11 +253,14 @@ class PaintTool(Tool): if event.type == Event.ToolActivateEvent: controller.setActiveStage("PrepareStage") controller.setActiveView("PaintTool") # Because that's the plugin-name, and the view is registered to it. + self._updateIgnoreUnselectedObjects() return True if event.type == Event.ToolDeactivateEvent: controller.setActiveStage("PrepareStage") controller.setActiveView("SolidView") + CuraApplication.getInstance().getRenderer().getRenderPass("selection").setIgnoreUnselectedObjects(False) + CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces").setIgnoreUnselectedObjects(False) return True if event.type == Event.MouseReleaseEvent and self._controller.getToolsEnabled(): @@ -281,8 +289,15 @@ class PaintTool(Tool): if paintview is None: return False - if not self._selection_pass: - return False + if not self._faces_selection_pass: + self._faces_selection_pass = CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces") + if not self._faces_selection_pass: + return False + + if not self._picking_pass: + self._picking_pass = CuraApplication.getInstance().getRenderer().getRenderPass("picking_selected") + if not self._picking_pass: + return False camera = self._controller.getScene().getActiveCamera() if not camera: @@ -300,14 +315,6 @@ class PaintTool(Tool): if not self._mesh_transformed_cache: return False - if not self._picking_pass: - self._picking_pass = PickingPass(camera.getViewportWidth(), - camera.getViewportHeight(), - only_selected_objects = True) - self._picking_pass.render() - - self._selection_pass.renderFacesMode() - face_id, texcoords = self._getTexCoordsFromClick(node, mouse_evt.x, mouse_evt.y) if texcoords is None: return False @@ -347,4 +354,13 @@ class PaintTool(Tool): if node is None: node = Selection.getSelectedObject(0) if node is not None: - Application.getInstance().getController().getScene().sceneChanged.emit(node) \ No newline at end of file + Application.getInstance().getController().getScene().sceneChanged.emit(node) + + def getRequiredExtraRenderingPasses(self) -> list[str]: + return ["selection_faces", "picking_selected"] + + def _updateIgnoreUnselectedObjects(self): + if self._controller.getActiveTool() is self: + ignore_unselected_objects = len(Selection.getAllSelectedObjects()) == 1 + CuraApplication.getInstance().getRenderer().getRenderPass("selection").setIgnoreUnselectedObjects(ignore_unselected_objects) + CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces").setIgnoreUnselectedObjects(ignore_unselected_objects) \ No newline at end of file diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index 61a6d0079c..a3d4b36315 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -13,7 +13,6 @@ from plugins.SolidView.SolidView import SolidView from UM.PluginRegistry import PluginRegistry from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.Texture import Texture -from UM.View.SelectionPass import SelectionPass from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Selection import Selection from UM.View.GL.OpenGL import OpenGL @@ -187,10 +186,6 @@ class PaintView(SolidView): paint_batch = renderer.createRenderBatch(shader=self._paint_shader) renderer.addRenderBatch(paint_batch) - selection_pass = cast(SelectionPass, renderer.getRenderPass("selection")) - if selection_pass is not None: - selection_pass.setIgnoreUnselectedObjectsDuringNextRender() - for node in display_objects: paint_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) self._current_paint_texture = node.callDecoration("getPaintTexture") diff --git a/plugins/SupportEraser/SupportEraser.py b/plugins/SupportEraser/SupportEraser.py index 0a714396aa..afdad6a4d0 100644 --- a/plugins/SupportEraser/SupportEraser.py +++ b/plugins/SupportEraser/SupportEraser.py @@ -1,6 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + from PyQt6.QtCore import Qt, QTimer from PyQt6.QtWidgets import QApplication @@ -35,6 +37,7 @@ class SupportEraser(Tool): self._controller = self.getController() self._selection_pass = None + self._picking_pass: Optional[PickingPass] = None CuraApplication.getInstance().globalContainerStackChanged.connect(self._updateEnabled) # Note: if the selection is cleared with this tool active, there is no way to switch to @@ -84,12 +87,13 @@ class SupportEraser(Tool): # Only "normal" meshes can have anti_overhang_meshes added to them return - # Create a pass for picking a world-space location from the mouse location - active_camera = self._controller.getScene().getActiveCamera() - picking_pass = PickingPass(active_camera.getViewportWidth(), active_camera.getViewportHeight()) - picking_pass.render() + # Get the pass for picking a world-space location from the mouse location + if self._picking_pass is None: + self._picking_pass = Application.getInstance().getRenderer().getRenderPass("picking_selected") + if not self._picking_pass: + return - picked_position = picking_pass.getPickedPosition(event.x, event.y) + picked_position = self._picking_pass.getPickedPosition(event.x, event.y) # Add the anti_overhang_mesh cube at the picked location self._createEraserMesh(picked_node, picked_position) @@ -189,3 +193,6 @@ class SupportEraser(Tool): mesh.calculateNormals() return mesh + + def getRequiredExtraRenderingPasses(self) -> list[str]: + return ["picking_selected"] \ No newline at end of file From ea488f02029563c587abdc45bcb30d56377a1ef4 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 1 Aug 2025 14:23:02 +0200 Subject: [PATCH 46/98] Fix wrongly displayed error message CURA-12660 --- cura/XRayPass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/XRayPass.py b/cura/XRayPass.py index 965294ba89..20fe38741e 100644 --- a/cura/XRayPass.py +++ b/cura/XRayPass.py @@ -16,7 +16,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator class XRayPass(RenderPass): def __init__(self, width, height): - super().__init__("xray", width, height) + super().__init__("xray", width, height, -100) self._shader = None self._gl = OpenGL.getInstance().getBindingsObject() From 78daa94ebff6a76b5fa91e59483c6541a9c80a18 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 1 Aug 2025 17:08:53 +0200 Subject: [PATCH 47/98] Save and restore painting tool settings CURA-12660 --- plugins/PaintTool/BrushColorButton.qml | 22 ++++++++++--- plugins/PaintTool/BrushShapeButton.qml | 22 ++++++++++--- plugins/PaintTool/PaintModeButton.qml | 23 +++++++++++--- plugins/PaintTool/PaintTool.py | 43 ++++++++++++++++++++------ plugins/PaintTool/PaintTool.qml | 20 ++++-------- plugins/PaintTool/PaintView.py | 22 +++++++++---- plugins/SolidView/SolidView.py | 5 +-- 7 files changed, 113 insertions(+), 44 deletions(-) diff --git a/plugins/PaintTool/BrushColorButton.qml b/plugins/PaintTool/BrushColorButton.qml index 71556f2681..ae4ab6243f 100644 --- a/plugins/PaintTool/BrushColorButton.qml +++ b/plugins/PaintTool/BrushColorButton.qml @@ -13,13 +13,27 @@ UM.ToolbarButton property string color - checked: base.selectedColor === buttonBrushColor.color - onClicked: setColor() function setColor() { - base.selectedColor = buttonBrushColor.color - UM.Controller.triggerActionWithData("setBrushColor", buttonBrushColor.color) + UM.Controller.setProperty("BrushColor", buttonBrushColor.color); + } + + function isChecked() + { + return UM.Controller.properties.getValue("BrushColor") === buttonBrushColor.color; + } + + Component.onCompleted: + { + buttonBrushColor.checked = isChecked(); + } + + Binding + { + target: buttonBrushColor + property: "checked" + value: isChecked() } } diff --git a/plugins/PaintTool/BrushShapeButton.qml b/plugins/PaintTool/BrushShapeButton.qml index 5c290e4a13..ef4256792a 100644 --- a/plugins/PaintTool/BrushShapeButton.qml +++ b/plugins/PaintTool/BrushShapeButton.qml @@ -13,13 +13,27 @@ UM.ToolbarButton property int shape - checked: base.selectedShape === buttonBrushShape.shape - onClicked: setShape() function setShape() { - base.selectedShape = buttonBrushShape.shape - UM.Controller.triggerActionWithData("setBrushShape", buttonBrushShape.shape) + UM.Controller.setProperty("BrushShape", buttonBrushShape.shape) + } + + function isChecked() + { + return UM.Controller.properties.getValue("BrushShape") === buttonBrushShape.shape; + } + + Component.onCompleted: + { + buttonBrushShape.checked = isChecked(); + } + + Binding + { + target: buttonBrushShape + property: "checked" + value: isChecked() } } diff --git a/plugins/PaintTool/PaintModeButton.qml b/plugins/PaintTool/PaintModeButton.qml index 473996e04b..eb294f7ad6 100644 --- a/plugins/PaintTool/PaintModeButton.qml +++ b/plugins/PaintTool/PaintModeButton.qml @@ -6,19 +6,34 @@ import QtQuick import UM 1.7 as UM import Cura 1.0 as Cura + Cura.ModeSelectorButton { id: modeSelectorButton property string mode - selected: base.selectedMode === modeSelectorButton.mode - onClicked: setMode() function setMode() { - base.selectedMode = modeSelectorButton.mode - UM.Controller.triggerActionWithData("setPaintType", modeSelectorButton.mode) + UM.Controller.setProperty("PaintType", modeSelectorButton.mode); + } + + function isSelected() + { + return UM.Controller.properties.getValue("PaintType") === modeSelectorButton.mode; + } + + Component.onCompleted: + { + modeSelectorButton.selected = isSelected(); + } + + Binding + { + target: modeSelectorButton + property: "selected" + value: isSelected() } } diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index e57c6d5a11..a1812a5766 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -45,8 +45,8 @@ class PaintTool(Tool): self._cache_dirty: bool = True self._brush_size: int = 10 - self._brush_color: str = "" - self._brush_shape: PaintTool.Brush.Shape = PaintTool.Brush.Shape.SQUARE + self._brush_color: str = "preferred" + self._brush_shape: PaintTool.Brush.Shape = PaintTool.Brush.Shape.CIRCLE self._brush_pen: QPen = self._createBrushPen() self._mouse_held: bool = False @@ -55,6 +55,8 @@ class PaintTool(Tool): self._last_mouse_coords: Optional[Tuple[int, int]] = None self._last_face_id: Optional[int] = None + self.setExposedProperties("PaintType", "BrushSize", "BrushColor", "BrushShape") + Selection.selectionChanged.connect(self._updateIgnoreUnselectedObjects) def _createBrushPen(self) -> QPen: @@ -91,28 +93,51 @@ class PaintTool(Tool): return stroke_image, (start_x, start_y) + def getPaintType(self) -> str: + paint_view = self._get_paint_view() + if paint_view is None: + return "" + + return paint_view.getPaintType() + def setPaintType(self, paint_type: str) -> None: paint_view = self._get_paint_view() if paint_view is None: return - paint_view.setPaintType(paint_type) + if paint_type != self.getPaintType(): + paint_view.setPaintType(paint_type) - self._brush_pen = self._createBrushPen() - self._updateScene() + self._brush_pen = self._createBrushPen() + self._updateScene() + self.propertyChanged.emit() + + def getBrushSize(self) -> int: + return self._brush_size def setBrushSize(self, brush_size: float) -> None: - if brush_size != self._brush_size: - self._brush_size = int(brush_size) + brush_size_int = int(brush_size) + if brush_size_int != self._brush_size: + self._brush_size = brush_size_int self._brush_pen = self._createBrushPen() + self.propertyChanged.emit() + + def getBrushColor(self) -> str: + return self._brush_color def setBrushColor(self, brush_color: str) -> None: - self._brush_color = brush_color + if brush_color != self._brush_color: + self._brush_color = brush_color + self.propertyChanged.emit() + + def getBrushShape(self) -> int: + return self._brush_shape def setBrushShape(self, brush_shape: int) -> None: if brush_shape != self._brush_shape: self._brush_shape = brush_shape self._brush_pen = self._createBrushPen() + self.propertyChanged.emit() def undoStackAction(self, redo_instead: bool) -> bool: paint_view = self._get_paint_view() @@ -251,13 +276,11 @@ class PaintTool(Tool): # Make sure the displayed values are updated if the bounding box of the selected mesh(es) changes if event.type == Event.ToolActivateEvent: - controller.setActiveStage("PrepareStage") controller.setActiveView("PaintTool") # Because that's the plugin-name, and the view is registered to it. self._updateIgnoreUnselectedObjects() return True if event.type == Event.ToolDeactivateEvent: - controller.setActiveStage("PrepareStage") controller.setActiveView("SolidView") CuraApplication.getInstance().getRenderer().getRenderPass("selection").setIgnoreUnselectedObjects(False) CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces").setIgnoreUnselectedObjects(False) diff --git a/plugins/PaintTool/PaintTool.qml b/plugins/PaintTool/PaintTool.qml index e3f244dd4c..94642b1f66 100644 --- a/plugins/PaintTool/PaintTool.qml +++ b/plugins/PaintTool/PaintTool.qml @@ -15,10 +15,6 @@ Item height: childrenRect.height UM.I18nCatalog { id: catalog; name: "cura"} - property string selectedMode: "" - property string selectedColor: "" - property int selectedShape: 0 - Action { id: undoAction @@ -167,15 +163,19 @@ Item from: 1 to: 40 - value: 10 onPressedChanged: function(pressed) { if(! pressed) { - UM.Controller.triggerActionWithData("setBrushSize", shapeSizeSlider.value) + UM.Controller.setProperty("BrushSize", shapeSizeSlider.value); } } + + Component.onCompleted: + { + shapeSizeSlider.value = UM.Controller.properties.getValue("BrushSize"); + } } //Line between the sections. @@ -227,12 +227,4 @@ Item } } } - - Component.onCompleted: - { - // Force first types for consistency, otherwise UI may become different from controller - rowPaintMode.children[0].setMode() - rowBrushColor.children[1].setColor() - rowBrushShape.children[1].setShape() - } } diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index a3d4b36315..c723f4321d 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -60,6 +60,8 @@ class PaintView(SolidView): "support": usual_types, } + self._current_paint_type = "seam" + def _checkSetup(self): super()._checkSetup() @@ -82,6 +84,8 @@ class PaintView(SolidView): if self._current_paint_texture is None or self._current_paint_texture.getImage() is None: return + self._prepareDataMapping() + actual_image = self._current_paint_texture.getImage() bit_range_start, bit_range_end = self._current_bits_ranges @@ -141,20 +145,26 @@ class PaintView(SolidView): return self._current_paint_texture.getWidth(), self._current_paint_texture.getHeight() return 0, 0 + def getPaintType(self) -> str: + return self._current_paint_type + def setPaintType(self, paint_type: str) -> None: + self._current_paint_type = paint_type + self._prepareDataMapping() + + def _prepareDataMapping(self): node = Selection.getAllSelectedObjects()[0] if node is None: return paint_data_mapping = node.callDecoration("getTextureDataMapping") - if paint_type not in paint_data_mapping: - new_mapping = self._add_mapping(paint_data_mapping, len(self._paint_modes[paint_type])) - paint_data_mapping[paint_type] = new_mapping + if self._current_paint_type not in paint_data_mapping: + new_mapping = self._add_mapping(paint_data_mapping, len(self._paint_modes[self._current_paint_type])) + paint_data_mapping[self._current_paint_type] = new_mapping node.callDecoration("setTextureDataMapping", paint_data_mapping) - self._current_paint_type = paint_type - self._current_bits_ranges = paint_data_mapping[paint_type] + self._current_bits_ranges = paint_data_mapping[self._current_paint_type] @staticmethod def _add_mapping(actual_mapping: Dict[str, tuple[int, int]], nb_storable_values: int) -> tuple[int, int]: @@ -167,7 +177,7 @@ class PaintView(SolidView): return start_index, end_index def beginRendering(self) -> None: - if self._current_paint_type == "": + if self._current_paint_type not in self._paint_modes: return display_objects = Selection.getAllSelectedObjects().copy() diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index bffc3aa526..e25273cb13 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -289,8 +289,9 @@ class SolidView(View): def endRendering(self): # check whether the xray overlay is showing badness - if time.time() > self._next_xray_checking_time\ - and Application.getInstance().getPreferences().getValue(self._show_xray_warning_preference): + if (time.time() > self._next_xray_checking_time + and Application.getInstance().getPreferences().getValue(self._show_xray_warning_preference) + and self._xray_pass is not None): self._next_xray_checking_time = time.time() + self._xray_checking_update_time xray_img = self._xray_pass.getOutput() From f6486bdffcf06984ea613fce22c3fac31be3bc79 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:28:38 -0400 Subject: [PATCH 48/98] Update DisplayInfoOnLCD.py If the printer was a UM then the statistics and model list weren't being added. This is the fix. --- plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index ddf292fb85..a7578e3565 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -40,6 +40,7 @@ import datetime import math from UM.Preferences import Preferences from UM.Message import Message +from UM.Logger import Logger class DisplayInfoOnLCD(Script): @@ -272,7 +273,7 @@ class DisplayInfoOnLCD(Script): self.m73_str = "" para_1 = data[0].split("\n") for line in para_1: - if line.startswith(";TIME:"): + if line.startswith(";TIME:") or line.startswith(";PRINT.TIME:"): self.time_total = int(line.split(":")[1]) break if display_option == "filename_layer": @@ -693,8 +694,8 @@ class DisplayInfoOnLCD(Script): # Add the stats to the gcode file lines = data[0].split("\n") for index, line in enumerate(lines): - if line.startswith(";Layer height:"): - lines[index] = ";Layer height: " + f"{float(line.split(":")[1]):.2f}".format(float(line.split(":")[1])) + if line.startswith(";Layer height:") or line.startswith(";TARGET_MACHINE.NAME:"): + lines[index] = ";Layer height: " + f"{global_stack.getProperty("layer_height", "value")}" lines[index] += f"\n{init_layer_hgt_line}" lines[index] += f"\n;Base Quality Name : '{global_stack.quality.getMetaDataEntry("name", "")}'" lines[index] += f"\n;Custom Quality Name: '{global_stack.qualityChanges.getMetaDataEntry("name")}'" From 8c3c36b241819a662f4fc97346f62990cd3ed047 Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:04:29 -0400 Subject: [PATCH 49/98] Update DisplayInfoOnLCD.py Changes per Reviewer request. --- plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py index a7578e3565..edfa9d8632 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayInfoOnLCD.py @@ -38,9 +38,7 @@ from UM.Qt.Duration import DurationFormat import time import datetime import math -from UM.Preferences import Preferences from UM.Message import Message -from UM.Logger import Logger class DisplayInfoOnLCD(Script): @@ -286,6 +284,7 @@ class DisplayInfoOnLCD(Script): def _display_filename_layer(self, data: str) -> str: data[0] = self._add_stats(data) max_layer = 0 + format_option = self.getSettingValueByKey("format_option") lcd_text = "M117 " octo_text = "M118 " if self.getSettingValueByKey("file_name") != "": @@ -295,7 +294,7 @@ class DisplayInfoOnLCD(Script): if self.getSettingValueByKey("addPrefixPrinting"): lcd_text += "Printing " octo_text += "Printing " - if not self.getSettingValueByKey("scroll"): + if not format_option: lcd_text += "Lay " octo_text += "Layer " else: @@ -317,11 +316,11 @@ class DisplayInfoOnLCD(Script): if self.getSettingValueByKey("maxlayer"): display_text += "/" + max_layer self.m118_text += "/" + max_layer - if not self.getSettingValueByKey("scroll"): + if not format_option: display_text += "|" + file_name self.m118_text += " | " + file_name else: - if not self.getSettingValueByKey("scroll"): + if not format_option: display_text += "|" + file_name + "!" self.m118_text += " | " + file_name + "!" else: From 8353d55ce2b2d5e090ebf6a5922af5dffd2875dc Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:32:00 -0400 Subject: [PATCH 50/98] Create AnnealingOrDrying.py A new script. The user can hold the build plate and chamber temepratures to "anneal" a print. Optionally the heated be can be used to attempt to dry filament rolls. --- .../scripts/AnnealingOrDrying.py | 513 ++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py diff --git a/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py b/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py new file mode 100644 index 0000000000..6d6775c6ea --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py @@ -0,0 +1,513 @@ +""" +Copyright (c) 2025 GregValiant (Greg Foresi) + + When Annealing: + The user may elect to hold the build plate at a temperature for a period of time. When the hold expires, the 'Timed Cooldown' will begin. + If there is no 'Hold Time' then the 'Annealing' cooldown will begin when the print ends. In 'Annealing' cooldown the bed temperature drops in 3° increments across the time span. + G4 commands are used for the cooldown steps. + If there is a 'Heated Chamber' then the chamber will start to cool when the bed temperature reaches the chamber temperature. + + When drying filament: + The bed must be empty because the printer will auto-home before raising the Z to 'machine_height minus 20mm' and then park the head in the XY. + The bed will heat up to the set point. + G4 commands are used to keep the machine from turning the bed off until the Drying Time has expired. + If you happen to have an enclosure with a fan, the fan can be set up to run during the drying or annealing. + + NOTE: This script uses the G4 Dwell command as a timer. It cannot be canceled from the LCD. If you wish to ;excape' from G4 you might have to cancel the print from the LCD or cycle the printer on and off to reset. +""" + +from UM.Application import Application +from ..Script import Script +from UM.Message import Message + +class AnnealingOrDrying(Script): + + def initialize(self) -> None: + super().initialize() + # Get the Bed Temperature from Cura + bed_temp = str(Application.getInstance().getGlobalContainerStack().getProperty("material_bed_temperature", "value")) + self._instance.setProperty("startout_temp", "value", bed_temp) + # Get the Build Volume temperature if there is one + heated_build_volume = bool(Application.getInstance().getGlobalContainerStack().getProperty("machine_heated_build_volume", "value")) + curaApp = Application.getInstance().getGlobalContainerStack() + chamber_fan_nr = curaApp.getProperty("build_volume_fan_nr", "value") + extruder_count = curaApp.getProperty("machine_extruder_count", "value") + if heated_build_volume: + chamber_temp = curaApp.getProperty("build_volume_temperature", "value") + self._instance.setProperty("has_build_volume_heater", "value", heated_build_volume) + self._instance.setProperty("build_volume_temp", "value", chamber_temp) + try: + if chamber_fan_nr > 0: + self._instance.setProperty("enable_chamber_fan_setting", "value", True) + except: + pass + + def getSettingDataString(self): + return """{ + "name": "Annealing CoolDown or Filament Drying", + "key": "AnnealingOrDrying", + "metadata": {}, + "version": 2, + "settings": + { + "enable_annealing": + { + "label": "Enable the Script", + "description": "If it isn't enabled it doesn't run.", + "type": "bool", + "default_value": true, + "enabled": true + }, + "cycle_type": + { + "label": "Anneal Print or Dry Filament", + "description": "Whether to Anneal the Print (by keeping the bed hot for a period of time), or to use the bed as a Filament Dryer. If drying; you will still need to slice a model, but it will not print. The gcode will consist only of a short script to heat the bed, wait for a while, then turn the bed off. The 'Z' will move to the max height and XY park position so the filament can be covered. The 'Hold Time', 'Bed Start Temp' and (if applicable) the 'Chamber Temp' come from these settings rather than from the Cura settings. When annealing; the Timed Cooldown will commence when the print ends.", + "type": "enum", + "options": + { + + "anneal_cycle": "Anneal Print", + "dry_cycle": "Dry Filament"}, + "default_value": "anneal_cycle", + "enabled": true, + "enabled": "enable_annealing" + }, + "bed_and_chamber": + { + "label": "Hold the Temp for the:", + "description": "Select the 'Bed' for just the bed, or 'Bed and Chamber' if you want to include your 'Heated Build Volume'.", + "type": "enum", + "options": + { + "bed_only": "Bed", + "bed_chamber": "Bed and Chamber"}, + "default_value": "bed_only", + "enabled": "enable_annealing" + }, + "wait_time": + { + "label": "Hold Time at Temp(s)", + "description": "Hold the bed temp at the 'Bed Start Out Temperature' for this amount of time (in decimal hours). When this time expires then the Annealing cool down will start. This is also the 'Drying Time' used when 'Drying Filament'.", + "type": "float", + "default_value": 0.0, + "unit": "Decimal Hrs ", + "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + }, + "dry_time": + { + "label": "Drying Time", + "description": "Hold the bed temp at the 'Bed Start Out Temperature' for this amount of time (in decimal hours). When this time expires the bed will shut off.", + "type": "float", + "default_value": 4.0, + "unit": "Decimal Hrs ", + "enabled": "enable_annealing and cycle_type == 'dry_cycle'" + }, + "pause_cmd": + { + "label": "Pause Cmd for Auto-Home", + "description": "Not required when you are paying attention and the bed is empty; ELSE; Enter the pause command to use prior to the Auto-Home command. The pause insures that the user IS paying attention and clears the build plate for Auto-Home. If you leave the box empty then there won't be a pause.", + "type": "str", + "default_value": "", + "enabled": "enable_annealing and cycle_type == 'dry_cycle'" + }, + "startout_temp": + { + "label": "Bed Start Out Temperature:", + "description": "Enter the temperature to start at. This is typically the bed temperature during the print but can be changed here. This is also the temperature used when drying filament.", + "type": "int", + "value": 30, + "unit": "Degrees ", + "minimum_value": 30, + "maximum_value": 110, + "maximum_value_warning": 100, + "enabled": "enable_annealing" + }, + "lowest_temp": + { + "label": "Shut-Off Temp:", + "description": "Enter the lowest temperature to control the cool down. This is the shut-off temperature for the build plate and (when applicable) the Heated Chamber. The minimum value is 30", + "type": "int", + "default_value": 30, + "unit": "Degrees ", + "minimum_value": 30, + "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + }, + "build_volume_temp": + { + "label": "Build Volume Temperature:", + "description": "Enter the temperature for the Build Volume (Heated Chamber). This is typically the temperature during the print but can be changed here.", + "type": "int", + "value": 24, + "unit": "Degrees ", + "minimum_value": 0, + "maximum_value": 90, + "maximum_value_warning": 75, + "enabled": "enable_annealing and has_build_volume_heater and bed_and_chamber == 'bed_chamber'" + }, + "enable_chamber_fan_setting": + { + "label": "Hidden Setting", + "description": "Enables chamber fan and speed.", + "type": "bool", + "default_value": false, + "enabled": false + }, + "chamber_fan_speed": + { + "label": "Chamber Fan Speed", + "description": "Set to % fan speed. Set to 0 to turn it off.", + "type": "int", + "default_value": 0, + "minimum_value": 0, + "maximum_value": 100, + "enabled": "enable_annealing and enable_chamber_fan_setting" + }, + "time_span": + { + "label": "Cool Down Time Span:", + "description": "The total amount of time (in decimal hours) to control the cool down. The build plate temperature will be dropped in 3° increments across this time span. 'Cool Down Time' starts at the end of the 'Hold Time' if you entered one.", + "type": "float", + "default_value": 1.0, + "unit": "Decimal Hrs ", + "minimum_value_warning": 0.25, + "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + }, + "park_head": + { + "label": "Park at MaxX and MaxY", + "description": "When unchecked, the park position is X0 Y0. Enable this setting to move the nozzle to the Max X and Max Y to allow access to the print.", + "type": "bool", + "default_value": false, + "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + }, + "park_max_z": + { + "label": "Move to MaxZ", + "description": "Enable this setting to move the nozzle to 'Machine_Height - 20' to allow the print to be covered.", + "type": "bool", + "default_value": false, + "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + }, + "beep_when_done": + { + "label": "Beep when done", + "description": "Add an annoying noise when the Cool Down completes.", + "type": "bool", + "default_value": true, + "enabled": "enable_annealing" + }, + "beep_duration": + { + "label": "Beep Duration", + "description": "The length of the buzzer sound. Units are in milliseconds so 1000ms = 1 second.", + "type": "int", + "unit": "milliseconds ", + "default_value": 1000, + "enabled": "beep_when_done and enable_annealing" + }, + "add_messages": + { + "label": "Include M117 and M118 messages", + "description": "Add messages to the LCD and any print server.", + "type": "bool", + "default_value": false, + "enabled": "enable_annealing" + }, + "has_build_volume_heater": + { + "label": "Hidden setting", + "description": "Hidden. This setting enables the build volume settings.", + "type": "bool", + "default_value": false, + "enabled": false + } + } + }""" + + def execute(self, data): + # Exit if there is no heated bed. + if not bool(Application.getInstance().getGlobalContainerStack().getProperty("machine_heated_bed", "value")): + Message(title = "[Anneal or Dry Filament]", text = "The script did not run because Heated Bed is disabled in Machine Settings.").show() + return data + # Enter a message in the gcode if the script is not enabled. + if not bool(self.getSettingValueByKey("enable_annealing")): + data[0] += "; [Anneal or Dry Filament] was not enabled\n" + return data + lowest_temp = int(self.getSettingValueByKey("lowest_temp")) + + # If the shutoff temp is under 30° then exit as a safety precaution so the bed doesn't stay on. + if lowest_temp < 30: + data[0] += "; Anneal or Dry Filament did not run. Shutoff Temp < 30\n" + Message(title = "[Anneal or Dry Filament]", text = "The script did not run because the Shutoff Temp is less than 30°.").show() + return data + self.global_stack = Application.getInstance().getGlobalContainerStack() + extruder = self.global_stack.extruderList + bed_temperature = int(self.getSettingValueByKey("startout_temp")) + heated_chamber = bool(Application.getInstance().getGlobalContainerStack().getProperty("machine_heated_build_volume", "value")) + anneal_type = self.getSettingValueByKey("bed_and_chamber") + + # Get the heated chamber temperature or set to 0 if no chamber + if heated_chamber: + chamber_temp = str(self.getSettingValueByKey("build_volume_temp")) + else: + anneal_type = "bed_only" + chamber_temp = "0" + + # For compatibility with earlier Cura versions + if self.global_stack.getProperty("build_volume_fan_nr", "value") is not None: + has_bv_fan = bool(self.global_stack.getProperty("build_volume_fan_nr", "value")) + bv_fan_nr = int(self.global_stack.getProperty("build_volume_fan_nr", "value")) + if bv_fan_nr > 0: + speed_bv_fan = int(self.getSettingValueByKey("chamber_fan_speed")) + else: + speed_bv_fan = 0 + + if bool(extruder[0].getProperty("machine_scale_fan_speed_zero_to_one", "value")) and has_bv_fan: + speed_bv_fan = round(speed_bv_fan * .01) + else: + speed_bv_fan = round(speed_bv_fan * 2.55) + + if has_bv_fan and speed_bv_fan > 0: + self.bv_fan_on_str = f"M106 S{speed_bv_fan} P{bv_fan_nr} ; Build Chamber Fan On\n" + self.bv_fan_off_str = f"M106 S0 P{bv_fan_nr} ; Build Chamber Fan Off\n" + else: + self.bv_fan_on_str = "" + self.bv_fan_off_str = "" + else: + has_bv_fan = False + bv_fan_nr = 0 + speed_bv_fan = 0 + self.bv_fan_on_str = "" + self.bv_fan_off_str = "" + + # Park Head + max_y = str(self.global_stack.getProperty("machine_depth", "value")) + max_x = str(self.global_stack.getProperty("machine_width", "value")) + # Max_z is limited to 'machine_height - 20' just so the print head doesn't smack into anything. + max_z = str(int(self.global_stack.getProperty("machine_height", "value")) - 20) + speed_travel = str(round(extruder[0].getProperty("speed_travel", "value")*60)) + park_xy = bool(self.getSettingValueByKey("park_head")) + park_z = bool(self.getSettingValueByKey("park_max_z")) + cycle_type = self.getSettingValueByKey("cycle_type") + add_messages = bool(self.getSettingValueByKey("add_messages")) + + if cycle_type == "anneal_cycle": + data = self._anneal_print(data, park_xy, park_z, bed_temperature, lowest_temp, heated_chamber, chamber_temp, max_y, max_x, max_z, speed_travel, add_messages, anneal_type) + elif cycle_type == "dry_cycle": + data = self._dry_filament_only(data, anneal_type, heated_chamber, chamber_temp, bed_temperature, max_z, max_y, speed_travel) + return data + + def _anneal_print(self, anneal_data: str, park_xy: bool, park_z: bool, bed_temperature:int, lowest_temp: int, heated_chamber: bool, chamber_temp: str, max_x: str, max_y: str, max_z: str, speed_travel: str, add_messages: bool, anneal_type: str): + """ + The procedure disables the M140 (and M141) lines at the end of the print, and adds additional bed (and chamber) temperature commands to the end of the G-Code file. The bed is allowed to cool down over a period of time. + """ + # Put the head parking string together + time_minutes = 1 + time_span = int(float(self.getSettingValueByKey("time_span")) * 3600) + park_string = "" + if park_xy and not park_z: + park_string = f"G0 F{speed_travel} X{max_x} Y{max_y} ; Park XY\nM84 X Y E ; Disable steppers except Z\n" + elif park_xy and park_z: + park_string = f"G0 F{speed_travel} X{max_x} Y{max_y} ; Park XY\nG0 Z{max_z} ; Raise Z to 'ZMax - 20'\nM84 X Y E ; Disable steppers except Z\n" + elif not park_xy and park_z: + park_string = f"G0 F{speed_travel} Z{max_z} ; Raise Z to 'ZMax - 20'\nM84 X Y E ; Disable steppers except Z\n" + elif not park_xy and not park_z: + park_string = f"G91 ; Relative movement\nG0 F{speed_travel} Z5 ; Raise Z\nG90 ; Absolute movement\nG0 X0 Y0 ; Park\nM84 X Y E ; Disable steppers except Z\n" + + # Calculate the temperature differential + hysteresis = bed_temperature - lowest_temp + + # if the bed temp is below the shutoff temp then exit + if hysteresis <= 0: + anneal_data[0] += "; Anneal or Dry Filament did not run. Bed Temp < Shutoff Temp\n" + Message(title = "Anneal or Dry Filament", text = "Did not run because the Bed Temp < Shutoff Temp.").show() + return anneal_data + + # Drop the bed temperature in 3° increments. + num_steps = int(hysteresis / 3) + step_index = 2 + deg_per_step = int(hysteresis / num_steps) + time_per_step = int(time_span / num_steps) + step_down = bed_temperature + wait_time = int(float(self.getSettingValueByKey("wait_time")) * 3600) + + # Put the first lines of the anneal string together + anneal_string = ";\n;TYPE:CUSTOM ---------------- Anneal Print\n" + if add_messages: + anneal_string += "M117 Cool Down for " + str(round((wait_time + time_span)/3600,2)) + "hr\n" + anneal_string += "M118 Cool Down for " + str(round((wait_time + time_span)/3600,2)) + "hr\n" + anneal_string += self.bv_fan_on_str + if wait_time > 0: + # Move the head before the M190 + anneal_string += park_string + if anneal_type == "bed_only": + anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\n" + if anneal_type == "bed_chamber": + anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\nM141 S{chamber_temp} ; Set the chamber temp\n" + anneal_string += f"G4 S{wait_time} ; Hold for {round(wait_time / 3600,2)} hrs\n" + else: + # Move the head after the M140 + anneal_string += f"M140 S{step_down} ; Set bed temp\n" + anneal_string += park_string + anneal_string += f"G4 S{time_per_step} ; wait time in seconds\n" + step_down -= deg_per_step + + time_remaining = round(time_span/3600,2) + # Step the bed/chamber temps down and add each step to the anneal string. The chamber remains at it's temperature until the bed gets down to that temperature. + for num in range(bed_temperature, lowest_temp, -3): + anneal_string += f"M140 S{step_down} ; Step down bed\n" + if anneal_type == "bed_chamber" and int(step_down) < int(chamber_temp): + anneal_string += f"M141 S{step_down} ; Step down chamber\n" + anneal_string += f"G4 S{time_per_step} ; Wait\n" + #time_remaining = round((time_span-(step_index*time_per_step))/3600,2) + if time_remaining >= 1.00: + if add_messages: + anneal_string += f"M117 CoolDown - {round(time_remaining,1)}hr\n" + anneal_string += f"M118 CoolDown - {round(time_remaining,1)}hr\n" + elif time_minutes > 0: + time_minutes = round(time_remaining * 60,1) + if add_messages: + anneal_string += f"M117 CoolDown - {time_minutes}min\n" + anneal_string += f"M118 CoolDown - {time_minutes}min\n" + time_remaining = round((time_span-(step_index*time_per_step))/3600,2) + step_down -= deg_per_step + step_index += 1 + if step_down <= lowest_temp: + break + + # Beep line + if bool(self.getSettingValueByKey("beep_when_done")): + beep_string = "M300 S440 P" + str(self.getSettingValueByKey("beep_duration")) + " ; Beep\n" + else: + beep_string = "" + + # Close out the anneal string + anneal_string += "M140 S0 ; Shut off the bed heater" + "\n" + if anneal_type == "bed_chamber": + anneal_string += "M141 S0 ; Shut off the chamber heater\n" + anneal_string += self.bv_fan_off_str + anneal_string += beep_string + if add_messages: + anneal_string += "M117 CoolDown Complete\n" + anneal_string += "M118 CoolDown Complete\n" + anneal_string += ";TYPE:CUSTOM ---------------- End of Anneal\n;" + + # Format + anneal_lines = anneal_string.split("\n") + for index, line in enumerate(anneal_lines): + if not line.startswith(";") and ";" in line: + front_txt = anneal_lines[index].split(";")[0] + back_txt = anneal_lines[index].split(";")[1] + anneal_lines[index] = front_txt + str(" " * (30 - len(front_txt))) +";" + back_txt + anneal_string = "\n".join(anneal_lines) + "\n" + + layer = anneal_data[len(anneal_data)-1] + lines = layer.split("\n") + + # Comment out the M140 S0 line in the ending gcode. + for num in range(len(lines)-1,-1,-1): + if lines[num].startswith("M140 S0"): + lines[num] = ";M140 S0 ; Shutoff Overide - Anneal or Dry Filament" + anneal_data[len(anneal_data)-1] = "\n".join(lines) + + # If there is a Heated Chamber and it's included then comment out the M141 S0 line + if anneal_type == "bed_chamber" and heated_chamber: + for num in range(0,len(lines)-1,1): + if lines[num].startswith("M141 S0"): + lines[num] = ";M141 S0 ; Shutoff Overide - Anneal or Dry Filament" + anneal_data[len(anneal_data)-1] = "\n".join(lines) + + # If park head is enabled then dont let the steppers disable until the head is parked + disable_string = "" + for num in range(0,len(lines)-1,1): + if lines[num][:3] in ("M84", "M18"): + disable_string = lines[num] + "\n" + stepper_timeout = int(wait_time + time_span) + if stepper_timeout > 14400: stepper_timeout = 14400 + lines[num] = ";" + lines[num] + " ; Overide - Anneal or Dry Filament" + lines.insert(num, "M84 S" + str(stepper_timeout) + " ; Increase stepper timeout - Anneal or Dry Filament") + anneal_data[len(anneal_data)-1] = "\n".join(lines) + break + + # The Anneal string is the new end of the gcode so move the 'End of Gcode' comment line in case there are other scripts running + anneal_data[len(anneal_data)-1] = anneal_data[len(anneal_data)-1].replace(";End of Gcode", anneal_string + disable_string + ";End of Gcode") + return anneal_data + + def _dry_filament_only(self, drydata: str, anneal_type: str, heated_chamber: bool, chamber_temp: int, bed_temperature: int, max_z:str, max_y:str, speed_travel: str) -> str: + """ + This procedure turns the bed on, homes the printer, parks the head. After the time period the bed is turned off. There is no actual print in the generated gcode, just a couple of moves to get the nozzle out of the way, and the bed heat (and possibly chamber heat) control. It allows a user to use the bed to warm up and hopefully dry a filament roll. + """ + for num in range(2, len(drydata)): + drydata[num] = "" + drydata[0] = drydata[0].split("\n")[0] + "\n" + add_messages = bool(self.getSettingValueByKey("add_messages")) + pause_cmd = self.getSettingValueByKey("pause_cmd").upper() + if pause_cmd != "": + pause_cmd = "M300 ; Beep\n" + pause_cmd + dry_time = self.getSettingValueByKey("dry_time") * 3600 + lines = drydata[1].split("\n") + drying_string = lines[0] + "\n" + ";............TYPE:CUSTOM: Dry Filament\n" + if add_messages: + drying_string += f"M117 Cool Down for {round(dry_time/3600,2)} hr ; Message\n" + drying_string += f"M118 Cool Down for {round(dry_time/3600,2)} hr ; Message\n" + # M113 sends messages to a print server as a 'Keep Alive' and can generate a lot of traffic over the USB + drying_string += "M113 S0 ; No echo\n" + drying_string += f"M84 S{round(dry_time)} ; Set stepper timeout\n" + drying_string += f"M140 S{bed_temperature} ; Heat bed\n" + drying_string += self.bv_fan_on_str + if heated_chamber and anneal_type == "bed_chamber": + drying_string += f"M141 S{chamber_temp} ; Chamber temp\n" + if pause_cmd == "M0": + pause_cmd = "M0 Clear bed and click...; Pause" + if pause_cmd != "": + drying_string += pause_cmd + " ; Pause\n" + drying_string += "G28 ; Auto-Home\n" + drying_string += f"G0 F{speed_travel} Z{max_z} ; Raise Z to 'ZMax - 20'\n" + drying_string += f"G0 F{speed_travel} X0 Y{max_y} ; Park print head\n" + if dry_time <= 3600: + if add_messages: + drying_string += f"M117 {dry_time/3600} hr remaining ; Message\n" + drying_string += f"M118 {dry_time/3600} hr remaining ; Message\n" + drying_string += f"G4 S{dry_time} ; Dry time\n" + elif dry_time > 3600: + temp_time = dry_time + while temp_time > 3600: + if add_messages: + drying_string += f"M117 {temp_time/3600} hr remaining ; Message\n" + drying_string += f"M118 {temp_time/3600} hr remaining ; Message\n" + drying_string += f"G4 S3600 ; Dry time split\n" + if temp_time > 3600: + temp_time -= 3600 + if temp_time > 0: + if add_messages: + drying_string += f"M117 {temp_time/3600} hr remaining ; Message\n" + drying_string += f"M118 {temp_time/3600} hr remaining ; Message\n" + drying_string += f"G4 S{temp_time} ; Dry time\n" + if heated_chamber and anneal_type == "bed_chamber": + drying_string += f"M141 S0 ; Shut off chamber\n" + drying_string += "M140 S0 ; Shut off bed\n" + drying_string += self.bv_fan_off_str + if self.getSettingValueByKey("beep_when_done"): + beep_duration = self.getSettingValueByKey("beep_duration") + drying_string += f"M300 P{beep_duration} ; Beep\n" + if add_messages: + drying_string += "M117 End of drying cycle ; Message\n" + drying_string += "M118 End of drying cycle ; Message\n" + drying_string += "M84 X Y E ; Disable steppers except Z\n" + drying_string += ";End of Gcode" + + # Format + lines = drying_string.split("\n") + for index, line in enumerate(lines): + if not line.startswith(";") and ";" in line: + front_txt = lines[index].split(";")[0] + back_txt = lines[index].split(";")[1] + lines[index] = front_txt + str(" " * (30 - len(front_txt))) +";" + back_txt + drydata[1] = "\n".join(lines) + "\n" + dry_txt = "; Drying time ...................... " + str(self.getSettingValueByKey("dry_time")) + " hrs\n" + dry_txt += "; Drying temperature ........ " + str(bed_temperature) + "°\n" + if heated_chamber and anneal_type == "bed_chamber": + dry_txt += "; Chamber temperature ... " + str(chamber_temp) + "°\n" + Message(title = "[Dry Filament]", text = dry_txt).show() + drydata[0] = "; <<< This is a filament drying file only. There is no actual print. >>>\n;\n" + dry_txt + ";\n" + return drydata \ No newline at end of file From 44d6c0a9694d6cb5ddbf78da25735e37a0e8c7e2 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 4 Aug 2025 11:28:31 +0200 Subject: [PATCH 51/98] Call SolidView dynamically instead of by inheritance CURA-12660 The previous method actually doesn't work when Cura is packaged because the plugins paths change. This method is much safer, and uses the actual SolidView instance. --- plugins/PaintTool/PaintView.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index c723f4321d..e4a8b3c493 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -9,8 +9,8 @@ from PyQt6.QtGui import QImage, QColor, QPainter from cura.CuraApplication import CuraApplication from cura.BuildVolume import BuildVolume -from plugins.SolidView.SolidView import SolidView from UM.PluginRegistry import PluginRegistry +from UM.View.View import View from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.Texture import Texture from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -22,7 +22,7 @@ from UM.Math.Color import Color catalog = i18nCatalog("cura") -class PaintView(SolidView): +class PaintView(View): """View for model-painting.""" UNDO_STACK_SIZE = 1024 @@ -50,6 +50,8 @@ class PaintView(SolidView): application.engineCreatedSignal.connect(self._makePaintModes) self._scene = application.getController().getScene() + self._solid_view = None + def _makePaintModes(self): theme = CuraApplication.getInstance().getTheme() usual_types = {"none": self.PaintType(Color(*theme.getColor("paint_normal_area").getRgb()), 0), @@ -63,8 +65,6 @@ class PaintView(SolidView): self._current_paint_type = "seam" def _checkSetup(self): - super()._checkSetup() - if not self._paint_shader: shader_filename = os.path.join(PluginRegistry.getInstance().getPluginPath("PaintTool"), "paint.shader") self._paint_shader = OpenGL.getInstance().createShaderProgram(shader_filename) @@ -180,10 +180,16 @@ class PaintView(SolidView): if self._current_paint_type not in self._paint_modes: return + if self._solid_view is None: + plugin_registry = PluginRegistry.getInstance() + solid_view = plugin_registry.getPluginObject("SolidView") + if isinstance(solid_view, View): + self._solid_view = solid_view + display_objects = Selection.getAllSelectedObjects().copy() - if len(display_objects) != 1: + if len(display_objects) != 1 and self._solid_view is not None: # Display the classic view until a single object is selected - super().beginRendering() + self._solid_view.beginRendering() return self._checkSetup() From 47ad02bcf1baf4278e15a8879408641f23055fba Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:05:32 -0400 Subject: [PATCH 52/98] Update AnnealingOrDrying.py Changed some verbiage. Update AnnealingOrDrying.py Changes per the review, bug fixes, cleanup my code. Add more beeps. Update AnnealingOrDrying.py Fixed a typo --- .../scripts/AnnealingOrDrying.py | 170 +++++++++--------- 1 file changed, 88 insertions(+), 82 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py b/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py index 6d6775c6ea..496fc1cb42 100644 --- a/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py +++ b/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py @@ -3,7 +3,7 @@ Copyright (c) 2025 GregValiant (Greg Foresi) When Annealing: The user may elect to hold the build plate at a temperature for a period of time. When the hold expires, the 'Timed Cooldown' will begin. - If there is no 'Hold Time' then the 'Annealing' cooldown will begin when the print ends. In 'Annealing' cooldown the bed temperature drops in 3° increments across the time span. + If there is no 'Hold Time' then the 'Annealing' cooldown will begin when the print ends. In 'Annealing' the bed temperature drops in 3° increments across the time span. G4 commands are used for the cooldown steps. If there is a 'Heated Chamber' then the chamber will start to cool when the bed temperature reaches the chamber temperature. @@ -12,8 +12,8 @@ Copyright (c) 2025 GregValiant (Greg Foresi) The bed will heat up to the set point. G4 commands are used to keep the machine from turning the bed off until the Drying Time has expired. If you happen to have an enclosure with a fan, the fan can be set up to run during the drying or annealing. - - NOTE: This script uses the G4 Dwell command as a timer. It cannot be canceled from the LCD. If you wish to ;excape' from G4 you might have to cancel the print from the LCD or cycle the printer on and off to reset. + + NOTE: This script uses the G4 Dwell command as a timer. It cannot be canceled from the LCD. If you wish to 'escape' from G4 you might have to cancel the print from the LCD or cycle the printer on and off to reset. """ from UM.Application import Application @@ -25,15 +25,15 @@ class AnnealingOrDrying(Script): def initialize(self) -> None: super().initialize() # Get the Bed Temperature from Cura - bed_temp = str(Application.getInstance().getGlobalContainerStack().getProperty("material_bed_temperature", "value")) - self._instance.setProperty("startout_temp", "value", bed_temp) + self.global_stack = Application.getInstance().getGlobalContainerStack() + bed_temp_during_print = str(self.global_stack.getProperty("material_bed_temperature", "value")) + self._instance.setProperty("startout_temp", "value", bed_temp_during_print) # Get the Build Volume temperature if there is one - heated_build_volume = bool(Application.getInstance().getGlobalContainerStack().getProperty("machine_heated_build_volume", "value")) - curaApp = Application.getInstance().getGlobalContainerStack() - chamber_fan_nr = curaApp.getProperty("build_volume_fan_nr", "value") - extruder_count = curaApp.getProperty("machine_extruder_count", "value") + heated_build_volume = bool(self.global_stack.getProperty("machine_heated_build_volume", "value")) + chamber_fan_nr = self.global_stack.getProperty("build_volume_fan_nr", "value") + extruder_count = self.global_stack.getProperty("machine_extruder_count", "value") if heated_build_volume: - chamber_temp = curaApp.getProperty("build_volume_temperature", "value") + chamber_temp = self.global_stack.getProperty("build_volume_temperature", "value") self._instance.setProperty("has_build_volume_heater", "value", heated_build_volume) self._instance.setProperty("build_volume_temp", "value", chamber_temp) try: @@ -50,7 +50,7 @@ class AnnealingOrDrying(Script): "version": 2, "settings": { - "enable_annealing": + "enable_script": { "label": "Enable the Script", "description": "If it isn't enabled it doesn't run.", @@ -65,14 +65,14 @@ class AnnealingOrDrying(Script): "type": "enum", "options": { - "anneal_cycle": "Anneal Print", - "dry_cycle": "Dry Filament"}, + "dry_cycle": "Dry Filament" + }, "default_value": "anneal_cycle", "enabled": true, - "enabled": "enable_annealing" + "enabled": "enable_script" }, - "bed_and_chamber": + "heating_zone_selection": { "label": "Hold the Temp for the:", "description": "Select the 'Bed' for just the bed, or 'Bed and Chamber' if you want to include your 'Heated Build Volume'.", @@ -82,7 +82,7 @@ class AnnealingOrDrying(Script): "bed_only": "Bed", "bed_chamber": "Bed and Chamber"}, "default_value": "bed_only", - "enabled": "enable_annealing" + "enabled": "enable_script" }, "wait_time": { @@ -91,7 +91,7 @@ class AnnealingOrDrying(Script): "type": "float", "default_value": 0.0, "unit": "Decimal Hrs ", - "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + "enabled": "enable_script and cycle_type == 'anneal_cycle'" }, "dry_time": { @@ -100,7 +100,7 @@ class AnnealingOrDrying(Script): "type": "float", "default_value": 4.0, "unit": "Decimal Hrs ", - "enabled": "enable_annealing and cycle_type == 'dry_cycle'" + "enabled": "enable_script and cycle_type == 'dry_cycle'" }, "pause_cmd": { @@ -108,7 +108,7 @@ class AnnealingOrDrying(Script): "description": "Not required when you are paying attention and the bed is empty; ELSE; Enter the pause command to use prior to the Auto-Home command. The pause insures that the user IS paying attention and clears the build plate for Auto-Home. If you leave the box empty then there won't be a pause.", "type": "str", "default_value": "", - "enabled": "enable_annealing and cycle_type == 'dry_cycle'" + "enabled": "enable_script and cycle_type == 'dry_cycle'" }, "startout_temp": { @@ -120,7 +120,7 @@ class AnnealingOrDrying(Script): "minimum_value": 30, "maximum_value": 110, "maximum_value_warning": 100, - "enabled": "enable_annealing" + "enabled": "enable_script" }, "lowest_temp": { @@ -130,7 +130,7 @@ class AnnealingOrDrying(Script): "default_value": 30, "unit": "Degrees ", "minimum_value": 30, - "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + "enabled": "enable_script and cycle_type == 'anneal_cycle'" }, "build_volume_temp": { @@ -142,7 +142,7 @@ class AnnealingOrDrying(Script): "minimum_value": 0, "maximum_value": 90, "maximum_value_warning": 75, - "enabled": "enable_annealing and has_build_volume_heater and bed_and_chamber == 'bed_chamber'" + "enabled": "enable_script and has_build_volume_heater and heating_zone_selection == 'bed_chamber'" }, "enable_chamber_fan_setting": { @@ -160,7 +160,8 @@ class AnnealingOrDrying(Script): "default_value": 0, "minimum_value": 0, "maximum_value": 100, - "enabled": "enable_annealing and enable_chamber_fan_setting" + "unit": "% ", + "enabled": "enable_script and enable_chamber_fan_setting" }, "time_span": { @@ -170,7 +171,7 @@ class AnnealingOrDrying(Script): "default_value": 1.0, "unit": "Decimal Hrs ", "minimum_value_warning": 0.25, - "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + "enabled": "enable_script and cycle_type == 'anneal_cycle'" }, "park_head": { @@ -178,7 +179,7 @@ class AnnealingOrDrying(Script): "description": "When unchecked, the park position is X0 Y0. Enable this setting to move the nozzle to the Max X and Max Y to allow access to the print.", "type": "bool", "default_value": false, - "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + "enabled": "enable_script and cycle_type == 'anneal_cycle'" }, "park_max_z": { @@ -186,7 +187,7 @@ class AnnealingOrDrying(Script): "description": "Enable this setting to move the nozzle to 'Machine_Height - 20' to allow the print to be covered.", "type": "bool", "default_value": false, - "enabled": "enable_annealing and cycle_type == 'anneal_cycle'" + "enabled": "enable_script and cycle_type == 'anneal_cycle'" }, "beep_when_done": { @@ -194,7 +195,7 @@ class AnnealingOrDrying(Script): "description": "Add an annoying noise when the Cool Down completes.", "type": "bool", "default_value": true, - "enabled": "enable_annealing" + "enabled": "enable_script" }, "beep_duration": { @@ -203,7 +204,7 @@ class AnnealingOrDrying(Script): "type": "int", "unit": "milliseconds ", "default_value": 1000, - "enabled": "beep_when_done and enable_annealing" + "enabled": "beep_when_done and enable_script" }, "add_messages": { @@ -211,7 +212,7 @@ class AnnealingOrDrying(Script): "description": "Add messages to the LCD and any print server.", "type": "bool", "default_value": false, - "enabled": "enable_annealing" + "enabled": "enable_script" }, "has_build_volume_heater": { @@ -226,11 +227,11 @@ class AnnealingOrDrying(Script): def execute(self, data): # Exit if there is no heated bed. - if not bool(Application.getInstance().getGlobalContainerStack().getProperty("machine_heated_bed", "value")): + if not bool(self.global_stack.getProperty("machine_heated_bed", "value")): Message(title = "[Anneal or Dry Filament]", text = "The script did not run because Heated Bed is disabled in Machine Settings.").show() return data # Enter a message in the gcode if the script is not enabled. - if not bool(self.getSettingValueByKey("enable_annealing")): + if not bool(self.getSettingValueByKey("enable_script")): data[0] += "; [Anneal or Dry Filament] was not enabled\n" return data lowest_temp = int(self.getSettingValueByKey("lowest_temp")) @@ -240,11 +241,10 @@ class AnnealingOrDrying(Script): data[0] += "; Anneal or Dry Filament did not run. Shutoff Temp < 30\n" Message(title = "[Anneal or Dry Filament]", text = "The script did not run because the Shutoff Temp is less than 30°.").show() return data - self.global_stack = Application.getInstance().getGlobalContainerStack() - extruder = self.global_stack.extruderList + extruders = self.global_stack.extruderList bed_temperature = int(self.getSettingValueByKey("startout_temp")) - heated_chamber = bool(Application.getInstance().getGlobalContainerStack().getProperty("machine_heated_build_volume", "value")) - anneal_type = self.getSettingValueByKey("bed_and_chamber") + heated_chamber = bool(self.global_stack.getProperty("machine_heated_build_volume", "value")) + anneal_type = self.getSettingValueByKey("heating_zone_selection") # Get the heated chamber temperature or set to 0 if no chamber if heated_chamber: @@ -253,6 +253,13 @@ class AnnealingOrDrying(Script): anneal_type = "bed_only" chamber_temp = "0" + # Beep line + if bool(self.getSettingValueByKey("beep_when_done")): + beep_duration = self.getSettingValueByKey("beep_duration") + self.beep_string = f"M300 S440 P{beep_duration} ; Beep\n" + else: + self.beep_string = "" + # For compatibility with earlier Cura versions if self.global_stack.getProperty("build_volume_fan_nr", "value") is not None: has_bv_fan = bool(self.global_stack.getProperty("build_volume_fan_nr", "value")) @@ -262,7 +269,7 @@ class AnnealingOrDrying(Script): else: speed_bv_fan = 0 - if bool(extruder[0].getProperty("machine_scale_fan_speed_zero_to_one", "value")) and has_bv_fan: + if bool(extruders[0].getProperty("machine_scale_fan_speed_zero_to_one", "value")) and has_bv_fan: speed_bv_fan = round(speed_bv_fan * .01) else: speed_bv_fan = round(speed_bv_fan * 2.55) @@ -283,9 +290,10 @@ class AnnealingOrDrying(Script): # Park Head max_y = str(self.global_stack.getProperty("machine_depth", "value")) max_x = str(self.global_stack.getProperty("machine_width", "value")) + # Max_z is limited to 'machine_height - 20' just so the print head doesn't smack into anything. max_z = str(int(self.global_stack.getProperty("machine_height", "value")) - 20) - speed_travel = str(round(extruder[0].getProperty("speed_travel", "value")*60)) + speed_travel = str(round(extruders[0].getProperty("speed_travel", "value")*60)) park_xy = bool(self.getSettingValueByKey("park_head")) park_z = bool(self.getSettingValueByKey("park_max_z")) cycle_type = self.getSettingValueByKey("cycle_type") @@ -295,6 +303,7 @@ class AnnealingOrDrying(Script): data = self._anneal_print(data, park_xy, park_z, bed_temperature, lowest_temp, heated_chamber, chamber_temp, max_y, max_x, max_z, speed_travel, add_messages, anneal_type) elif cycle_type == "dry_cycle": data = self._dry_filament_only(data, anneal_type, heated_chamber, chamber_temp, bed_temperature, max_z, max_y, speed_travel) + return data def _anneal_print(self, anneal_data: str, park_xy: bool, park_z: bool, bed_temperature:int, lowest_temp: int, heated_chamber: bool, chamber_temp: str, max_x: str, max_y: str, max_z: str, speed_travel: str, add_messages: bool, anneal_type: str): @@ -302,17 +311,17 @@ class AnnealingOrDrying(Script): The procedure disables the M140 (and M141) lines at the end of the print, and adds additional bed (and chamber) temperature commands to the end of the G-Code file. The bed is allowed to cool down over a period of time. """ # Put the head parking string together + bed_temp_during_print = int(self.global_stack.getProperty("material_bed_temperature", "value")) time_minutes = 1 time_span = int(float(self.getSettingValueByKey("time_span")) * 3600) park_string = "" - if park_xy and not park_z: - park_string = f"G0 F{speed_travel} X{max_x} Y{max_y} ; Park XY\nM84 X Y E ; Disable steppers except Z\n" - elif park_xy and park_z: - park_string = f"G0 F{speed_travel} X{max_x} Y{max_y} ; Park XY\nG0 Z{max_z} ; Raise Z to 'ZMax - 20'\nM84 X Y E ; Disable steppers except Z\n" - elif not park_xy and park_z: - park_string = f"G0 F{speed_travel} Z{max_z} ; Raise Z to 'ZMax - 20'\nM84 X Y E ; Disable steppers except Z\n" - elif not park_xy and not park_z: - park_string = f"G91 ; Relative movement\nG0 F{speed_travel} Z5 ; Raise Z\nG90 ; Absolute movement\nG0 X0 Y0 ; Park\nM84 X Y E ; Disable steppers except Z\n" + if park_xy: + park_string += f"G0 F{speed_travel} X{max_x} Y{max_y} ; Park XY\n" + if park_z: + park_string += f"G0 Z{max_z} ; Raise Z to 'ZMax - 20'\n" + if not park_xy and not park_z: + park_string += f"G91 ; Relative movement\nG0 F{speed_travel} Z5 ; Raise Z\nG90 ; Absolute movement\nG0 X0 Y0 ; Park\n" + park_string += "M84 X Y E ; Disable steppers except Z\n" # Calculate the temperature differential hysteresis = bed_temperature - lowest_temp @@ -333,33 +342,35 @@ class AnnealingOrDrying(Script): # Put the first lines of the anneal string together anneal_string = ";\n;TYPE:CUSTOM ---------------- Anneal Print\n" + if bed_temperature == bed_temp_during_print: + anneal_string += self.beep_string if add_messages: anneal_string += "M117 Cool Down for " + str(round((wait_time + time_span)/3600,2)) + "hr\n" anneal_string += "M118 Cool Down for " + str(round((wait_time + time_span)/3600,2)) + "hr\n" anneal_string += self.bv_fan_on_str if wait_time > 0: - # Move the head before the M190 + # Add the parking string BEFORE the M190 anneal_string += park_string if anneal_type == "bed_only": - anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\n" + anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\n{self.beep_string}" if anneal_type == "bed_chamber": - anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\nM141 S{chamber_temp} ; Set the chamber temp\n" + anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\nM141 S{chamber_temp} ; Set the chamber temp\n{self.beep_string}" anneal_string += f"G4 S{wait_time} ; Hold for {round(wait_time / 3600,2)} hrs\n" else: - # Move the head after the M140 + # Add the parking string AFTER the M140 anneal_string += f"M140 S{step_down} ; Set bed temp\n" anneal_string += park_string anneal_string += f"G4 S{time_per_step} ; wait time in seconds\n" - step_down -= deg_per_step + step_down -= deg_per_step time_remaining = round(time_span/3600,2) + # Step the bed/chamber temps down and add each step to the anneal string. The chamber remains at it's temperature until the bed gets down to that temperature. for num in range(bed_temperature, lowest_temp, -3): anneal_string += f"M140 S{step_down} ; Step down bed\n" if anneal_type == "bed_chamber" and int(step_down) < int(chamber_temp): anneal_string += f"M141 S{step_down} ; Step down chamber\n" anneal_string += f"G4 S{time_per_step} ; Wait\n" - #time_remaining = round((time_span-(step_index*time_per_step))/3600,2) if time_remaining >= 1.00: if add_messages: anneal_string += f"M117 CoolDown - {round(time_remaining,1)}hr\n" @@ -375,24 +386,18 @@ class AnnealingOrDrying(Script): if step_down <= lowest_temp: break - # Beep line - if bool(self.getSettingValueByKey("beep_when_done")): - beep_string = "M300 S440 P" + str(self.getSettingValueByKey("beep_duration")) + " ; Beep\n" - else: - beep_string = "" - # Close out the anneal string anneal_string += "M140 S0 ; Shut off the bed heater" + "\n" if anneal_type == "bed_chamber": anneal_string += "M141 S0 ; Shut off the chamber heater\n" anneal_string += self.bv_fan_off_str - anneal_string += beep_string + anneal_string += self.beep_string if add_messages: anneal_string += "M117 CoolDown Complete\n" anneal_string += "M118 CoolDown Complete\n" anneal_string += ";TYPE:CUSTOM ---------------- End of Anneal\n;" - # Format + # Format the inserted lines. anneal_lines = anneal_string.split("\n") for index, line in enumerate(anneal_lines): if not line.startswith(";") and ";" in line: @@ -401,36 +406,36 @@ class AnnealingOrDrying(Script): anneal_lines[index] = front_txt + str(" " * (30 - len(front_txt))) +";" + back_txt anneal_string = "\n".join(anneal_lines) + "\n" - layer = anneal_data[len(anneal_data)-1] - lines = layer.split("\n") + end_gcode = anneal_data[-1] + end_lines = end_gcode.split("\n") - # Comment out the M140 S0 line in the ending gcode. - for num in range(len(lines)-1,-1,-1): - if lines[num].startswith("M140 S0"): - lines[num] = ";M140 S0 ; Shutoff Overide - Anneal or Dry Filament" - anneal_data[len(anneal_data)-1] = "\n".join(lines) + # Comment out the existing M140 S0 lines in the ending gcode. + for num in range(len(end_lines)-1,-1,-1): + if end_lines[num].startswith("M140 S0"): + end_lines[num] = ";M140 S0 ; Shutoff Overide - Anneal or Dry Filament" + anneal_data[-1] = "\n".join(end_lines) # If there is a Heated Chamber and it's included then comment out the M141 S0 line if anneal_type == "bed_chamber" and heated_chamber: - for num in range(0,len(lines)-1,1): - if lines[num].startswith("M141 S0"): - lines[num] = ";M141 S0 ; Shutoff Overide - Anneal or Dry Filament" - anneal_data[len(anneal_data)-1] = "\n".join(lines) + for num in range(0,len(end_lines)-1): + if end_lines[num].startswith("M141 S0"): + end_lines[num] = ";M141 S0 ; Shutoff Overide - Anneal or Dry Filament" + anneal_data[-1] = "\n".join(end_lines) # If park head is enabled then dont let the steppers disable until the head is parked disable_string = "" - for num in range(0,len(lines)-1,1): - if lines[num][:3] in ("M84", "M18"): - disable_string = lines[num] + "\n" + for num in range(0,len(end_lines)-1): + if end_lines[num][:3] in ("M84", "M18"): + disable_string = end_lines[num] + "\n" stepper_timeout = int(wait_time + time_span) if stepper_timeout > 14400: stepper_timeout = 14400 - lines[num] = ";" + lines[num] + " ; Overide - Anneal or Dry Filament" - lines.insert(num, "M84 S" + str(stepper_timeout) + " ; Increase stepper timeout - Anneal or Dry Filament") - anneal_data[len(anneal_data)-1] = "\n".join(lines) + end_lines[num] = ";" + end_lines[num] + " ; Overide - Anneal or Dry Filament" + end_lines.insert(num, "M84 S" + str(stepper_timeout) + " ; Increase stepper timeout - Anneal or Dry Filament") + anneal_data[-1] = "\n".join(end_lines) break # The Anneal string is the new end of the gcode so move the 'End of Gcode' comment line in case there are other scripts running - anneal_data[len(anneal_data)-1] = anneal_data[len(anneal_data)-1].replace(";End of Gcode", anneal_string + disable_string + ";End of Gcode") + anneal_data[-1] = anneal_data[-1].replace(";End of Gcode", anneal_string + disable_string + ";End of Gcode") return anneal_data def _dry_filament_only(self, drydata: str, anneal_type: str, heated_chamber: bool, chamber_temp: int, bed_temperature: int, max_z:str, max_y:str, speed_travel: str) -> str: @@ -441,15 +446,16 @@ class AnnealingOrDrying(Script): drydata[num] = "" drydata[0] = drydata[0].split("\n")[0] + "\n" add_messages = bool(self.getSettingValueByKey("add_messages")) - pause_cmd = self.getSettingValueByKey("pause_cmd").upper() + pause_cmd = self.getSettingValueByKey("pause_cmd") if pause_cmd != "": - pause_cmd = "M300 ; Beep\n" + pause_cmd + pause_cmd = self.beep_string + pause_cmd dry_time = self.getSettingValueByKey("dry_time") * 3600 lines = drydata[1].split("\n") - drying_string = lines[0] + "\n" + ";............TYPE:CUSTOM: Dry Filament\n" + drying_string = lines[0] + f"\n;............TYPE:CUSTOM: Dry Filament\n{self.beep_string}" if add_messages: drying_string += f"M117 Cool Down for {round(dry_time/3600,2)} hr ; Message\n" drying_string += f"M118 Cool Down for {round(dry_time/3600,2)} hr ; Message\n" + # M113 sends messages to a print server as a 'Keep Alive' and can generate a lot of traffic over the USB drying_string += "M113 S0 ; No echo\n" drying_string += f"M84 S{round(dry_time)} ; Set stepper timeout\n" @@ -489,14 +495,14 @@ class AnnealingOrDrying(Script): drying_string += self.bv_fan_off_str if self.getSettingValueByKey("beep_when_done"): beep_duration = self.getSettingValueByKey("beep_duration") - drying_string += f"M300 P{beep_duration} ; Beep\n" + drying_string += self.beep_string if add_messages: drying_string += "M117 End of drying cycle ; Message\n" drying_string += "M118 End of drying cycle ; Message\n" drying_string += "M84 X Y E ; Disable steppers except Z\n" drying_string += ";End of Gcode" - # Format + # Format the lines lines = drying_string.split("\n") for index, line in enumerate(lines): if not line.startswith(";") and ";" in line: From 968576472121c88735e0ebc5252336719738fac2 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 4 Aug 2025 15:19:04 +0200 Subject: [PATCH 53/98] Implement undo-redo by full stroke CURA-12661 --- plugins/PaintTool/PaintTool.py | 4 +- plugins/PaintTool/PaintUndoCommand.py | 104 ++++++++++++++++++++++++++ plugins/PaintTool/PaintView.py | 95 ++++++++--------------- 3 files changed, 136 insertions(+), 67 deletions(-) create mode 100644 plugins/PaintTool/PaintUndoCommand.py diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index fa6436f10d..4f6516ffd7 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -130,7 +130,7 @@ class PaintTool(Tool): width, height = paintview.getUvTexDimensions() clear_image = QImage(width, height, QImage.Format.Format_RGB32) clear_image.fill(Qt.GlobalColor.white) - paintview.addStroke(clear_image, 0, 0, "none") + paintview.addStroke(clear_image, 0, 0, "none", False) self._updateScene() @@ -333,7 +333,7 @@ class PaintTool(Tool): end_coords[0] * w, end_coords[1] * h ) - paintview.addStroke(sub_image, start_x, start_y, self._brush_color) + paintview.addStroke(sub_image, start_x, start_y, self._brush_color, is_moved) self._last_text_coords = texcoords self._last_mouse_coords = (mouse_evt.x, mouse_evt.y) diff --git a/plugins/PaintTool/PaintUndoCommand.py b/plugins/PaintTool/PaintUndoCommand.py new file mode 100644 index 0000000000..c100780c7f --- /dev/null +++ b/plugins/PaintTool/PaintUndoCommand.py @@ -0,0 +1,104 @@ +# Copyright (c) 2025 UltiMaker +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import cast, Optional + +from PyQt6.QtCore import QRect, QPoint +from PyQt6.QtGui import QUndoCommand, QImage, QPainter + +from UM.View.GL.Texture import Texture + + +class PaintUndoCommand(QUndoCommand): + """Provides the command that does the actual painting on objects with undo/redo mechanisms""" + + def __init__(self, + texture: Texture, + stroke_mask: QImage, + x: int, + y: int, + set_value: int, + bit_range: tuple[int, int], + mergeable: bool) -> None: + super().__init__() + + self._original_texture_image: Optional[QImage] = texture.getImage().copy() if not mergeable else None + self._texture: Texture = texture + self._stroke_mask: QImage = stroke_mask + self._x: int = x + self._y: int = y + self._set_value: int = set_value + self._bit_range: tuple[int, int] = bit_range + self._mergeable: bool = mergeable + + def id(self) -> int: + # Since the undo stack will contain only commands of this type, we can use a fixed ID + return 0 + + def redo(self) -> None: + actual_image = self._texture.getImage() + + bit_range_start, bit_range_end = self._bit_range + full_int32 = 0xffffffff + clear_mask = full_int32 ^ (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> ( + 32 - 1 - bit_range_end)) + image_rect = QRect(0, 0, self._stroke_mask.width(), self._stroke_mask.height()) + + clear_bits_image = self._stroke_mask.copy() + clear_bits_image.invertPixels() + painter = QPainter(clear_bits_image) + painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Lighten) + painter.fillRect(image_rect, clear_mask) + painter.end() + + set_value_image = self._stroke_mask.copy() + painter = QPainter(set_value_image) + painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Multiply) + painter.fillRect(image_rect, self._set_value) + painter.end() + + stroked_image = actual_image.copy(self._x, self._y, self._stroke_mask.width(), self._stroke_mask.height()) + painter = QPainter(stroked_image) + painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceAndDestination) + painter.drawImage(0, 0, clear_bits_image) + painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceOrDestination) + painter.drawImage(0, 0, set_value_image) + painter.end() + + self._texture.setSubImage(stroked_image, self._x, self._y) + + def undo(self) -> None: + if self._original_texture_image is not None: + self._texture.setSubImage(self._original_texture_image.copy(self._x, + self._y, + self._stroke_mask.width(), + self._stroke_mask.height()), + self._x, + self._y) + + def mergeWith(self, command: QUndoCommand) -> bool: + if not isinstance(command, PaintUndoCommand): + return False + paint_undo_command = cast(PaintUndoCommand, command) + + if not paint_undo_command._mergeable: + return False + + self_rect = QRect(QPoint(self._x, self._y), self._stroke_mask.size()) + command_rect = QRect(QPoint(paint_undo_command._x, paint_undo_command._y), paint_undo_command._stroke_mask.size()) + bounding_rect = self_rect.united(command_rect) + + merged_mask = QImage(bounding_rect.width(), bounding_rect.height(), self._stroke_mask.format()) + merged_mask.fill(0) + + painter = QPainter(merged_mask) + painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Lighten) + painter.drawImage(self._x - bounding_rect.x(), self._y - bounding_rect.y(), self._stroke_mask) + painter.drawImage(paint_undo_command._x - bounding_rect.x(), paint_undo_command._y - bounding_rect.y(), paint_undo_command._stroke_mask) + painter.end() + + self._x = bounding_rect.x() + self._y = bounding_rect.y() + self._stroke_mask = merged_mask + + return True diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index 749fa463e4..da00e16c2a 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -5,7 +5,7 @@ import os from PyQt6.QtCore import QRect from typing import Optional, List, Tuple, Dict -from PyQt6.QtGui import QImage, QColor, QPainter +from PyQt6.QtGui import QImage, QColor, QPainter, QUndoStack from cura.CuraApplication import CuraApplication from UM.PluginRegistry import PluginRegistry @@ -17,14 +17,14 @@ from UM.View.GL.OpenGL import OpenGL from UM.i18n import i18nCatalog from UM.Math.Color import Color +from .PaintUndoCommand import PaintUndoCommand + catalog = i18nCatalog("cura") class PaintView(View): """View for model-painting.""" - UNDO_STACK_SIZE = 1024 - class PaintType: def __init__(self, display_color: Color, value: int): self.display_color: Color = display_color @@ -38,8 +38,8 @@ class PaintView(View): self._current_paint_type = "" self._paint_modes: Dict[str, Dict[str, "PaintView.PaintType"]] = {} - self._stroke_undo_stack: List[Tuple[QImage, int, int]] = [] - self._stroke_redo_stack: List[Tuple[QImage, int, int]] = [] + self._paint_undo_stack: QUndoStack = QUndoStack() + self._paint_undo_stack.setUndoLimit(32) # Set a quite low amount since every command copies the full texture self._force_opaque_mask = QImage(2, 2, QImage.Format.Format_Mono) self._force_opaque_mask.fill(1) @@ -61,74 +61,39 @@ class PaintView(View): shader_filename = os.path.join(PluginRegistry.getInstance().getPluginPath("PaintTool"), "paint.shader") self._paint_shader = OpenGL.getInstance().createShaderProgram(shader_filename) - def _forceOpaqueDeepCopy(self, image: QImage): - res = QImage(image.width(), image.height(), QImage.Format.Format_RGBA8888) - res.fill(QColor(255, 255, 255, 255)) - painter = QPainter(res) - painter.setRenderHint(QPainter.RenderHint.Antialiasing, False) - painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceOver) - painter.drawImage(0, 0, image) - painter.end() - res.setAlphaChannel(self._force_opaque_mask.scaled(image.width(), image.height())) - return res - - def addStroke(self, stroke_mask: QImage, start_x: int, start_y: int, brush_color: str) -> None: + def addStroke(self, stroke_mask: QImage, start_x: int, start_y: int, brush_color: str, merge_with_previous: bool) -> None: if self._current_paint_texture is None or self._current_paint_texture.getImage() is None: return - actual_image = self._current_paint_texture.getImage() + current_image = self._current_paint_texture.getImage() + texture_rect = QRect(0, 0, current_image.width(), current_image.height()) + stroke_rect = QRect(start_x, start_y, stroke_mask.width(), stroke_mask.height()) + intersect_rect = texture_rect.intersected(stroke_rect) + if intersect_rect != stroke_rect: + # Stroke doesn't fully fit into the image, we have to crop it + stroke_mask = stroke_mask.copy(intersect_rect.x() - start_x, + intersect_rect.y() - start_y, + intersect_rect.width(), + intersect_rect.height()) + start_x = intersect_rect.x() + start_y = intersect_rect.y() bit_range_start, bit_range_end = self._current_bits_ranges - set_value = self._paint_modes[self._current_paint_type][brush_color].value << self._current_bits_ranges[0] - full_int32 = 0xffffffff - clear_mask = full_int32 ^ (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> (32 - 1 - bit_range_end)) - image_rect = QRect(0, 0, stroke_mask.width(), stroke_mask.height()) + set_value = self._paint_modes[self._current_paint_type][brush_color].value << bit_range_start - clear_bits_image = stroke_mask.copy() - clear_bits_image.invertPixels() - painter = QPainter(clear_bits_image) - painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Lighten) - painter.fillRect(image_rect, clear_mask) - painter.end() + self._paint_undo_stack.push(PaintUndoCommand(self._current_paint_texture, + stroke_mask, + start_x, + start_y, + set_value, + (bit_range_start, bit_range_end), + merge_with_previous)) - set_value_image = stroke_mask.copy() - painter = QPainter(set_value_image) - painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Multiply) - painter.fillRect(image_rect, set_value) - painter.end() + def undoStroke(self) -> None: + self._paint_undo_stack.undo() - stroked_image = actual_image.copy(start_x, start_y, stroke_mask.width(), stroke_mask.height()) - painter = QPainter(stroked_image) - painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceAndDestination) - painter.drawImage(0, 0, clear_bits_image) - painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceOrDestination) - painter.drawImage(0, 0, set_value_image) - painter.end() - - self._stroke_redo_stack.clear() - if len(self._stroke_undo_stack) >= PaintView.UNDO_STACK_SIZE: - self._stroke_undo_stack.pop(0) - undo_image = self._forceOpaqueDeepCopy(self._current_paint_texture.setSubImage(stroked_image, start_x, start_y)) - if undo_image is not None: - self._stroke_undo_stack.append((undo_image, start_x, start_y)) - - def _applyUndoStacksAction(self, from_stack: List[Tuple[QImage, int, int]], to_stack: List[Tuple[QImage, int, int]]) -> bool: - if len(from_stack) <= 0 or self._current_paint_texture is None: - return False - from_image, x, y = from_stack.pop() - to_image = self._forceOpaqueDeepCopy(self._current_paint_texture.setSubImage(from_image, x, y)) - if to_image is None: - return False - if len(to_stack) >= PaintView.UNDO_STACK_SIZE: - to_stack.pop(0) - to_stack.append((to_image, x, y)) - return True - - def undoStroke(self) -> bool: - return self._applyUndoStacksAction(self._stroke_undo_stack, self._stroke_redo_stack) - - def redoStroke(self) -> bool: - return self._applyUndoStacksAction(self._stroke_redo_stack, self._stroke_undo_stack) + def redoStroke(self) -> None: + self._paint_undo_stack.redo() def getUvTexDimensions(self): if self._current_paint_texture is not None: From 586c2939a60dd2750c44bdad383e5d66e3a82d0a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 4 Aug 2025 15:46:51 +0200 Subject: [PATCH 54/98] Enable undo/redo buttons when appropriate CURA-12661 --- plugins/PaintTool/PaintTool.py | 68 ++++++++++++++------------------- plugins/PaintTool/PaintTool.qml | 20 +++++++++- plugins/PaintTool/PaintView.py | 20 +++++++--- plugins/PaintTool/__init__.py | 7 +++- 4 files changed, 65 insertions(+), 50 deletions(-) diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index b9f7a5e95b..293c069786 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -33,9 +33,13 @@ class PaintTool(Tool): SQUARE = 0 CIRCLE = 1 - def __init__(self) -> None: + def __init__(self, view: PaintView) -> None: super().__init__() + self._view: PaintView = view + self._view.canUndoChanged.connect(self._onCanUndoChanged) + self._view.canRedoChanged.connect(self._onCanRedoChanged) + self._picking_pass: Optional[PickingPass] = None self._faces_selection_pass: Optional[SelectionPass] = None @@ -56,7 +60,7 @@ class PaintTool(Tool): self._last_mouse_coords: Optional[Tuple[int, int]] = None self._last_face_id: Optional[int] = None - self.setExposedProperties("PaintType", "BrushSize", "BrushColor", "BrushShape") + self.setExposedProperties("PaintType", "BrushSize", "BrushColor", "BrushShape", "CanUndo", "CanRedo") Selection.selectionChanged.connect(self._updateIgnoreUnselectedObjects) @@ -95,19 +99,11 @@ class PaintTool(Tool): return stroke_image, (start_x, start_y) def getPaintType(self) -> str: - paint_view = self._get_paint_view() - if paint_view is None: - return "" - - return paint_view.getPaintType() + return self._view.getPaintType() def setPaintType(self, paint_type: str) -> None: - paint_view = self._get_paint_view() - if paint_view is None: - return - if paint_type != self.getPaintType(): - paint_view.setPaintType(paint_type) + self._view.setPaintType(paint_type) self._brush_pen = self._createBrushPen() self._updateScene() @@ -140,38 +136,34 @@ class PaintTool(Tool): self._brush_pen = self._createBrushPen() self.propertyChanged.emit() - def undoStackAction(self, redo_instead: bool) -> bool: - paint_view = self._get_paint_view() - if paint_view is None: - return False + def getCanUndo(self) -> bool: + return self._view.canUndo() - if redo_instead: - paint_view.redoStroke() - else: - paint_view.undoStroke() + def _onCanUndoChanged(self): + self.propertyChanged.emit() + def getCanRedo(self) -> bool: + return self._view.canRedo() + + def _onCanRedoChanged(self): + self.propertyChanged.emit() + + def undoStackAction(self) -> None: + self._view.undoStroke() + self._updateScene() + + def redoStackAction(self) -> None: + self._view.redoStroke() self._updateScene() - return True def clear(self) -> None: - paintview = self._get_paint_view() - if paintview is None: - return - - width, height = paintview.getUvTexDimensions() + width, height = self._view.getUvTexDimensions() clear_image = QImage(width, height, QImage.Format.Format_RGB32) clear_image.fill(Qt.GlobalColor.white) - paintview.addStroke(clear_image, 0, 0, "none", False) + self._view.addStroke(clear_image, 0, 0, "none", False) self._updateScene() - @staticmethod - def _get_paint_view() -> Optional[PaintView]: - paint_view = Application.getInstance().getController().getActiveView() - if paint_view is None or paint_view.getPluginId() != "PaintTool": - return None - return cast(PaintView, paint_view) - @staticmethod def _get_intersect_ratio_via_pt(a: numpy.ndarray, pt: numpy.ndarray, b: numpy.ndarray, c: numpy.ndarray) -> float: # compute the intersection of (param) A - pt with (param) B - (param) C @@ -330,10 +322,6 @@ class PaintTool(Tool): else: self._mouse_held = True - paintview = self._get_paint_view() - if paintview is None: - return False - if not self._faces_selection_pass: self._faces_selection_pass = CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces") if not self._faces_selection_pass: @@ -379,7 +367,7 @@ class PaintTool(Tool): (self._last_mouse_coords, (self._last_face_id, self._last_text_coords)), ((mouse_evt.x, mouse_evt.y), (face_id, texcoords))) - w, h = paintview.getUvTexDimensions() + w, h = self._view.getUvTexDimensions() for start_coords, end_coords in substrokes: sub_image, (start_x, start_y) = self._createStrokeImage( start_coords[0] * w, @@ -387,7 +375,7 @@ class PaintTool(Tool): end_coords[0] * w, end_coords[1] * h ) - paintview.addStroke(sub_image, start_x, start_y, self._brush_color, is_moved) + self._view.addStroke(sub_image, start_x, start_y, self._brush_color, is_moved) self._last_text_coords = texcoords self._last_mouse_coords = (mouse_evt.x, mouse_evt.y) diff --git a/plugins/PaintTool/PaintTool.qml b/plugins/PaintTool/PaintTool.qml index 2bb3106dd5..c448835bc5 100644 --- a/plugins/PaintTool/PaintTool.qml +++ b/plugins/PaintTool/PaintTool.qml @@ -19,14 +19,14 @@ Item { id: undoAction shortcut: "Ctrl+L" - onTriggered: UM.Controller.triggerActionWithData("undoStackAction", false) + onTriggered: UM.Controller.triggerAction("undoStackAction") } Action { id: redoAction shortcut: "Ctrl+Shift+L" - onTriggered: UM.Controller.triggerActionWithData("undoStackAction", true) + onTriggered: UM.Controller.triggerAction("redoStackAction") } Column @@ -192,6 +192,7 @@ Item { id: undoButton + enabled: undoAction.enabled text: catalog.i18nc("@action:button", "Undo Stroke") toolItem: UM.ColorImage { @@ -206,6 +207,7 @@ Item { id: redoButton + enabled: redoAction.enabled text: catalog.i18nc("@action:button", "Redo Stroke") toolItem: UM.ColorImage { @@ -227,4 +229,18 @@ Item } } } + + Binding + { + target: undoAction + property: "enabled" + value: UM.Controller.properties.getValue("CanUndo") + } + + Binding + { + target: redoAction + property: "enabled" + value: UM.Controller.properties.getValue("CanRedo") + } } diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index ed06da17d4..b5cd44772d 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -2,10 +2,10 @@ # Cura is released under the terms of the LGPLv3 or higher. import os -from PyQt6.QtCore import QRect -from typing import Optional, List, Tuple, Dict, cast +from PyQt6.QtCore import QRect, pyqtSignal +from typing import Optional, Dict -from PyQt6.QtGui import QImage, QColor, QPainter, QUndoStack +from PyQt6.QtGui import QImage, QUndoStack from cura.CuraApplication import CuraApplication from cura.BuildVolume import BuildVolume @@ -42,9 +42,8 @@ class PaintView(View): self._paint_undo_stack: QUndoStack = QUndoStack() self._paint_undo_stack.setUndoLimit(32) # Set a quite low amount since every command copies the full texture - - self._force_opaque_mask = QImage(2, 2, QImage.Format.Format_Mono) - self._force_opaque_mask.fill(1) + self._paint_undo_stack.canUndoChanged.connect(self.canUndoChanged) + self._paint_undo_stack.canRedoChanged.connect(self.canRedoChanged) application = CuraApplication.getInstance() application.engineCreatedSignal.connect(self._makePaintModes) @@ -52,6 +51,15 @@ class PaintView(View): self._solid_view = None + canUndoChanged = pyqtSignal(bool) + canRedoChanged = pyqtSignal(bool) + + def canUndo(self): + return self._paint_undo_stack.canUndo() + + def canRedo(self): + return self._paint_undo_stack.canRedo() + def _makePaintModes(self): theme = CuraApplication.getInstance().getTheme() usual_types = {"none": self.PaintType(Color(*theme.getColor("paint_normal_area").getRgb()), 0), diff --git a/plugins/PaintTool/__init__.py b/plugins/PaintTool/__init__.py index e92c169ee6..637e0b00f2 100644 --- a/plugins/PaintTool/__init__.py +++ b/plugins/PaintTool/__init__.py @@ -27,7 +27,10 @@ def getMetaData(): def register(app): qmlRegisterUncreatableType(PaintTool.PaintTool.Brush, "Cura", 1, 0, "This is an enumeration class", "PaintToolBrush") + + view = PaintView.PaintView() + return { - "tool": PaintTool.PaintTool(), - "view": PaintView.PaintView() + "tool": PaintTool.PaintTool(view), + "view": view } From 8f9a17d49e7ee40a28849ccdfc41671c198dbe27 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 4 Aug 2025 16:03:09 +0200 Subject: [PATCH 55/98] Simplify QML code CURA-12661 --- plugins/PaintTool/BrushColorButton.qml | 25 ++----------------------- plugins/PaintTool/BrushShapeButton.qml | 25 ++----------------------- plugins/PaintTool/PaintModeButton.qml | 25 ++----------------------- plugins/PaintTool/PaintTool.qml | 24 ++++-------------------- 4 files changed, 10 insertions(+), 89 deletions(-) diff --git a/plugins/PaintTool/BrushColorButton.qml b/plugins/PaintTool/BrushColorButton.qml index ae4ab6243f..b62ab09e92 100644 --- a/plugins/PaintTool/BrushColorButton.qml +++ b/plugins/PaintTool/BrushColorButton.qml @@ -13,27 +13,6 @@ UM.ToolbarButton property string color - onClicked: setColor() - - function setColor() - { - UM.Controller.setProperty("BrushColor", buttonBrushColor.color); - } - - function isChecked() - { - return UM.Controller.properties.getValue("BrushColor") === buttonBrushColor.color; - } - - Component.onCompleted: - { - buttonBrushColor.checked = isChecked(); - } - - Binding - { - target: buttonBrushColor - property: "checked" - value: isChecked() - } + checked: UM.Controller.properties.getValue("BrushColor") === buttonBrushColor.color + onClicked: UM.Controller.setProperty("BrushColor", buttonBrushColor.color) } diff --git a/plugins/PaintTool/BrushShapeButton.qml b/plugins/PaintTool/BrushShapeButton.qml index ef4256792a..e05cd206f3 100644 --- a/plugins/PaintTool/BrushShapeButton.qml +++ b/plugins/PaintTool/BrushShapeButton.qml @@ -13,27 +13,6 @@ UM.ToolbarButton property int shape - onClicked: setShape() - - function setShape() - { - UM.Controller.setProperty("BrushShape", buttonBrushShape.shape) - } - - function isChecked() - { - return UM.Controller.properties.getValue("BrushShape") === buttonBrushShape.shape; - } - - Component.onCompleted: - { - buttonBrushShape.checked = isChecked(); - } - - Binding - { - target: buttonBrushShape - property: "checked" - value: isChecked() - } + checked: UM.Controller.properties.getValue("BrushShape") === buttonBrushShape.shape + onClicked: UM.Controller.setProperty("BrushShape", buttonBrushShape.shape) } diff --git a/plugins/PaintTool/PaintModeButton.qml b/plugins/PaintTool/PaintModeButton.qml index eb294f7ad6..833a009551 100644 --- a/plugins/PaintTool/PaintModeButton.qml +++ b/plugins/PaintTool/PaintModeButton.qml @@ -13,27 +13,6 @@ Cura.ModeSelectorButton property string mode - onClicked: setMode() - - function setMode() - { - UM.Controller.setProperty("PaintType", modeSelectorButton.mode); - } - - function isSelected() - { - return UM.Controller.properties.getValue("PaintType") === modeSelectorButton.mode; - } - - Component.onCompleted: - { - modeSelectorButton.selected = isSelected(); - } - - Binding - { - target: modeSelectorButton - property: "selected" - value: isSelected() - } + selected: UM.Controller.properties.getValue("PaintType") === modeSelectorButton.mode + onClicked: UM.Controller.setProperty("PaintType", modeSelectorButton.mode) } diff --git a/plugins/PaintTool/PaintTool.qml b/plugins/PaintTool/PaintTool.qml index c448835bc5..01d866967a 100644 --- a/plugins/PaintTool/PaintTool.qml +++ b/plugins/PaintTool/PaintTool.qml @@ -19,6 +19,7 @@ Item { id: undoAction shortcut: "Ctrl+L" + enabled: UM.Controller.properties.getValue("CanUndo") onTriggered: UM.Controller.triggerAction("undoStackAction") } @@ -26,6 +27,7 @@ Item { id: redoAction shortcut: "Ctrl+Shift+L" + enabled: UM.Controller.properties.getValue("CanRedo") onTriggered: UM.Controller.triggerAction("redoStackAction") } @@ -53,7 +55,7 @@ Item icon: "Support" tooltipText: catalog.i18nc("@tooltip", "Refine support placement by defining preferred/avoidance areas") mode: "support" - visible: false + // visible: false } } @@ -163,6 +165,7 @@ Item from: 10 to: 1000 + value: UM.Controller.properties.getValue("BrushSize") onPressedChanged: function(pressed) { @@ -171,11 +174,6 @@ Item UM.Controller.setProperty("BrushSize", shapeSizeSlider.value); } } - - Component.onCompleted: - { - shapeSizeSlider.value = UM.Controller.properties.getValue("BrushSize"); - } } //Line between the sections. @@ -229,18 +227,4 @@ Item } } } - - Binding - { - target: undoAction - property: "enabled" - value: UM.Controller.properties.getValue("CanUndo") - } - - Binding - { - target: redoAction - property: "enabled" - value: UM.Controller.properties.getValue("CanRedo") - } } From b5e2ce6168240406169fbb1b764ff5dce030fc81 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 4 Aug 2025 16:07:49 +0200 Subject: [PATCH 56/98] Restore disabled support painting CURA-12661 --- plugins/PaintTool/PaintTool.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PaintTool/PaintTool.qml b/plugins/PaintTool/PaintTool.qml index 01d866967a..0d1129eb60 100644 --- a/plugins/PaintTool/PaintTool.qml +++ b/plugins/PaintTool/PaintTool.qml @@ -55,7 +55,7 @@ Item icon: "Support" tooltipText: catalog.i18nc("@tooltip", "Refine support placement by defining preferred/avoidance areas") mode: "support" - // visible: false + visible: false } } From 4006272fee2f6b90af4ac78cbf7e071aa39f7746 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Tue, 5 Aug 2025 00:45:48 +0200 Subject: [PATCH 57/98] Some housekeeping - anneal_type -> heating_zone: lines up the variable name with intent. - Sort the variable inputs of the functions (annealing / drying) - Add description for parameters in both functions (annealing / drying) --- .../scripts/AnnealingOrDrying.py | 94 ++++++++++++++----- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py b/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py index 496fc1cb42..1841a8b62b 100644 --- a/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py +++ b/plugins/PostProcessingPlugin/scripts/AnnealingOrDrying.py @@ -67,7 +67,7 @@ class AnnealingOrDrying(Script): { "anneal_cycle": "Anneal Print", "dry_cycle": "Dry Filament" - }, + }, "default_value": "anneal_cycle", "enabled": true, "enabled": "enable_script" @@ -80,7 +80,8 @@ class AnnealingOrDrying(Script): "options": { "bed_only": "Bed", - "bed_chamber": "Bed and Chamber"}, + "bed_chamber": "Bed and Chamber" + }, "default_value": "bed_only", "enabled": "enable_script" }, @@ -244,13 +245,13 @@ class AnnealingOrDrying(Script): extruders = self.global_stack.extruderList bed_temperature = int(self.getSettingValueByKey("startout_temp")) heated_chamber = bool(self.global_stack.getProperty("machine_heated_build_volume", "value")) - anneal_type = self.getSettingValueByKey("heating_zone_selection") + heating_zone = self.getSettingValueByKey("heating_zone_selection") # Get the heated chamber temperature or set to 0 if no chamber if heated_chamber: chamber_temp = str(self.getSettingValueByKey("build_volume_temp")) else: - anneal_type = "bed_only" + heating_zone = "bed_only" chamber_temp = "0" # Beep line @@ -270,7 +271,7 @@ class AnnealingOrDrying(Script): speed_bv_fan = 0 if bool(extruders[0].getProperty("machine_scale_fan_speed_zero_to_one", "value")) and has_bv_fan: - speed_bv_fan = round(speed_bv_fan * .01) + speed_bv_fan = round(speed_bv_fan * 0.01) else: speed_bv_fan = round(speed_bv_fan * 2.55) @@ -300,15 +301,45 @@ class AnnealingOrDrying(Script): add_messages = bool(self.getSettingValueByKey("add_messages")) if cycle_type == "anneal_cycle": - data = self._anneal_print(data, park_xy, park_z, bed_temperature, lowest_temp, heated_chamber, chamber_temp, max_y, max_x, max_z, speed_travel, add_messages, anneal_type) + data = self._anneal_print(add_messages, data, bed_temperature, chamber_temp, heated_chamber, heating_zone, lowest_temp, max_x, max_y, max_z, park_xy, park_z, speed_travel) elif cycle_type == "dry_cycle": - data = self._dry_filament_only(data, anneal_type, heated_chamber, chamber_temp, bed_temperature, max_z, max_y, speed_travel) + data = self._dry_filament_only(data, bed_temperature, chamber_temp, heated_chamber, heating_zone, max_y, max_z, speed_travel) return data - def _anneal_print(self, anneal_data: str, park_xy: bool, park_z: bool, bed_temperature:int, lowest_temp: int, heated_chamber: bool, chamber_temp: str, max_x: str, max_y: str, max_z: str, speed_travel: str, add_messages: bool, anneal_type: str): + def _anneal_print( + self, + add_messages: bool, + anneal_data: str, + bed_temperature: int, + chamber_temp: str, + heated_chamber: bool, + heating_zone: str, + lowest_temp: int, + max_x: str, + max_y: str, + max_z: str, + park_xy: bool, + park_z: bool, + speed_travel: str) -> str: """ - The procedure disables the M140 (and M141) lines at the end of the print, and adds additional bed (and chamber) temperature commands to the end of the G-Code file. The bed is allowed to cool down over a period of time. + The procedure disables the M140 (and M141) lines at the end of the print, and adds additional bed (and chamber) temperature commands to the end of the G-Code file. + The bed is allowed to cool down over a period of time. + + :param add_messages: Whether to include M117 and M118 messages for LCD and print server + :param anneal_data: The G-code data to be modified with annealing commands + :param bed_temperature: Starting bed temperature in degrees Celsius + :param chamber_temp: Chamber/build volume temperature in degrees Celsius as string + :param heated_chamber: Whether the printer has a heated build volume/chamber + :param heating_zone: Zone selection - "bed_only" or "bed_chamber" + :param lowest_temp: Final shutdown temperature in degrees Celsius + :param max_x: Maximum X axis position for parking as string + :param max_y: Maximum Y axis position for parking as string + :param max_z: Maximum Z axis position (machine height - 20mm) as string + :param park_xy: Whether to park the print head at max X and Y positions + :param park_z: Whether to raise Z to maximum safe height + :param speed_travel: Travel speed for positioning moves in mm/min as string + :return: Modified G-code data with annealing cooldown sequence """ # Put the head parking string together bed_temp_during_print = int(self.global_stack.getProperty("material_bed_temperature", "value")) @@ -326,7 +357,7 @@ class AnnealingOrDrying(Script): # Calculate the temperature differential hysteresis = bed_temperature - lowest_temp - # if the bed temp is below the shutoff temp then exit + # Exit if the bed temp is below the shutoff temp if hysteresis <= 0: anneal_data[0] += "; Anneal or Dry Filament did not run. Bed Temp < Shutoff Temp\n" Message(title = "Anneal or Dry Filament", text = "Did not run because the Bed Temp < Shutoff Temp.").show() @@ -351,9 +382,9 @@ class AnnealingOrDrying(Script): if wait_time > 0: # Add the parking string BEFORE the M190 anneal_string += park_string - if anneal_type == "bed_only": + if heating_zone == "bed_only": anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\n{self.beep_string}" - if anneal_type == "bed_chamber": + if heating_zone == "bed_chamber": anneal_string += f"M190 S{bed_temperature} ; Set the bed temp\nM141 S{chamber_temp} ; Set the chamber temp\n{self.beep_string}" anneal_string += f"G4 S{wait_time} ; Hold for {round(wait_time / 3600,2)} hrs\n" else: @@ -368,7 +399,7 @@ class AnnealingOrDrying(Script): # Step the bed/chamber temps down and add each step to the anneal string. The chamber remains at it's temperature until the bed gets down to that temperature. for num in range(bed_temperature, lowest_temp, -3): anneal_string += f"M140 S{step_down} ; Step down bed\n" - if anneal_type == "bed_chamber" and int(step_down) < int(chamber_temp): + if heating_zone == "bed_chamber" and int(step_down) < int(chamber_temp): anneal_string += f"M141 S{step_down} ; Step down chamber\n" anneal_string += f"G4 S{time_per_step} ; Wait\n" if time_remaining >= 1.00: @@ -388,7 +419,7 @@ class AnnealingOrDrying(Script): # Close out the anneal string anneal_string += "M140 S0 ; Shut off the bed heater" + "\n" - if anneal_type == "bed_chamber": + if heating_zone == "bed_chamber": anneal_string += "M141 S0 ; Shut off the chamber heater\n" anneal_string += self.bv_fan_off_str anneal_string += self.beep_string @@ -416,7 +447,7 @@ class AnnealingOrDrying(Script): anneal_data[-1] = "\n".join(end_lines) # If there is a Heated Chamber and it's included then comment out the M141 S0 line - if anneal_type == "bed_chamber" and heated_chamber: + if heating_zone == "bed_chamber" and heated_chamber: for num in range(0,len(end_lines)-1): if end_lines[num].startswith("M141 S0"): end_lines[num] = ";M141 S0 ; Shutoff Overide - Anneal or Dry Filament" @@ -438,9 +469,30 @@ class AnnealingOrDrying(Script): anneal_data[-1] = anneal_data[-1].replace(";End of Gcode", anneal_string + disable_string + ";End of Gcode") return anneal_data - def _dry_filament_only(self, drydata: str, anneal_type: str, heated_chamber: bool, chamber_temp: int, bed_temperature: int, max_z:str, max_y:str, speed_travel: str) -> str: + def _dry_filament_only( + self, + bed_temperature: int, + chamber_temp: int, + drydata: str, + heated_chamber: bool, + heating_zone: str, + max_y: str, + max_z: str, + speed_travel: str) -> str: """ - This procedure turns the bed on, homes the printer, parks the head. After the time period the bed is turned off. There is no actual print in the generated gcode, just a couple of moves to get the nozzle out of the way, and the bed heat (and possibly chamber heat) control. It allows a user to use the bed to warm up and hopefully dry a filament roll. + This procedure turns the bed on, homes the printer, parks the head. After the time period the bed is turned off. + There is no actual print in the generated gcode, just a couple of moves to get the nozzle out of the way, and the bed heat (and possibly chamber heat) control. + It allows a user to use the bed to warm up and hopefully dry a filament roll. + + :param bed_temperature: Bed temperature for drying in degrees Celsius + :param chamber_temp: Chamber/build volume temperature for drying in degrees Celsius + :param drydata: The G-code data to be replaced with filament drying commands + :param heated_chamber: Whether the printer has a heated build volume/chamber + :param heating_zone: Zone selection - "bed_only" or "bed_chamber" + :param max_y: Maximum Y axis position for parking as string + :param max_z: Maximum Z axis position (machine height - 20mm) as string + :param speed_travel: Travel speed for positioning moves in mm/min as string + :return: Modified G-code data containing only filament drying sequence """ for num in range(2, len(drydata)): drydata[num] = "" @@ -461,7 +513,7 @@ class AnnealingOrDrying(Script): drying_string += f"M84 S{round(dry_time)} ; Set stepper timeout\n" drying_string += f"M140 S{bed_temperature} ; Heat bed\n" drying_string += self.bv_fan_on_str - if heated_chamber and anneal_type == "bed_chamber": + if heated_chamber and heating_zone == "bed_chamber": drying_string += f"M141 S{chamber_temp} ; Chamber temp\n" if pause_cmd == "M0": pause_cmd = "M0 Clear bed and click...; Pause" @@ -489,7 +541,7 @@ class AnnealingOrDrying(Script): drying_string += f"M117 {temp_time/3600} hr remaining ; Message\n" drying_string += f"M118 {temp_time/3600} hr remaining ; Message\n" drying_string += f"G4 S{temp_time} ; Dry time\n" - if heated_chamber and anneal_type == "bed_chamber": + if heated_chamber and heating_zone == "bed_chamber": drying_string += f"M141 S0 ; Shut off chamber\n" drying_string += "M140 S0 ; Shut off bed\n" drying_string += self.bv_fan_off_str @@ -512,8 +564,8 @@ class AnnealingOrDrying(Script): drydata[1] = "\n".join(lines) + "\n" dry_txt = "; Drying time ...................... " + str(self.getSettingValueByKey("dry_time")) + " hrs\n" dry_txt += "; Drying temperature ........ " + str(bed_temperature) + "°\n" - if heated_chamber and anneal_type == "bed_chamber": + if heated_chamber and heating_zone == "bed_chamber": dry_txt += "; Chamber temperature ... " + str(chamber_temp) + "°\n" Message(title = "[Dry Filament]", text = dry_txt).show() drydata[0] = "; <<< This is a filament drying file only. There is no actual print. >>>\n;\n" + dry_txt + ";\n" - return drydata \ No newline at end of file + return drydata From 9d97eb7d594208bde9891b515d59aa6673cf6362 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 5 Aug 2025 14:07:44 +0200 Subject: [PATCH 58/98] Fix sometimes wrong painting color display CURA-12660 --- plugins/PaintTool/paint.shader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PaintTool/paint.shader b/plugins/PaintTool/paint.shader index f2af66ffe6..c1b90b376b 100644 --- a/plugins/PaintTool/paint.shader +++ b/plugins/PaintTool/paint.shader @@ -132,7 +132,7 @@ fragment41core = [defaults] u_ambientColor = [0.3, 0.3, 0.3, 1.0] -u_opacity = 0.5 +u_opacity = 1.0 u_texture = 0 [bindings] From cf24ed91e9650dcde09f87d4d53524b878889b7d Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Tue, 5 Aug 2025 15:55:23 +0200 Subject: [PATCH 59/98] Improve fix for opacity issues CURA-12660 Previous fix caused issues when moving to preview --- plugins/PaintTool/paint.shader | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/PaintTool/paint.shader b/plugins/PaintTool/paint.shader index c1b90b376b..1982724910 100644 --- a/plugins/PaintTool/paint.shader +++ b/plugins/PaintTool/paint.shader @@ -29,7 +29,6 @@ fragment = uniform mediump vec4 u_ambientColor; uniform highp vec3 u_lightPosition; uniform highp vec3 u_viewPosition; - uniform mediump float u_opacity; uniform sampler2D u_texture; uniform mediump int u_bitsRangesStart; uniform mediump int u_bitsRangesEnd; @@ -58,7 +57,7 @@ fragment = highp float n_dot_l = mix(0.3, 0.7, dot(normal, light_dir)); final_color += (n_dot_l * diffuse_color); - final_color.a = u_opacity; + final_color.a = 1.0; frag_color = final_color; } @@ -95,7 +94,6 @@ fragment41core = uniform mediump vec4 u_ambientColor; uniform highp vec3 u_lightPosition; uniform highp vec3 u_viewPosition; - uniform mediump float u_opacity; uniform sampler2D u_texture; uniform mediump int u_bitsRangesStart; uniform mediump int u_bitsRangesEnd; @@ -125,14 +123,13 @@ fragment41core = highp float n_dot_l = mix(0.3, 0.7, dot(normal, light_dir)); final_color += (n_dot_l * diffuse_color); - final_color.a = u_opacity; + final_color.a = 1.0; frag_color = final_color; } [defaults] u_ambientColor = [0.3, 0.3, 0.3, 1.0] -u_opacity = 1.0 u_texture = 0 [bindings] From e69a4369423aa57cd00d81a4b459fb6f4888c30b Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Wed, 6 Aug 2025 16:15:05 +0200 Subject: [PATCH 60/98] Fix sometimes wrongly displayed view CURA-12660 This required a refactoring of the management of the active view. The previous behavior was that anyone could set the active view, depending on certain conditions. But now we also have a view that is set by a tool, so sometimes the actually set view would be incorrect. Now each Stage requests an active view, and each tool CAN also request an active view. Then the Controller decides which view should actually be active depending on the active stage and tool. --- cura/CuraApplication.py | 5 +---- cura/Stages/CuraStage.py | 6 ++++-- plugins/PaintTool/PaintTool.py | 22 ++++++++++------------ plugins/PaintTool/PaintView.py | 22 ++++------------------ plugins/PreviewStage/PreviewStage.py | 19 ------------------- plugins/SimulationView/SimulationView.py | 16 +++++++++++++--- resources/qml/ViewsSelector.qml | 4 ++-- 7 files changed, 34 insertions(+), 60 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5ce358080c..57d4773cb3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1038,7 +1038,6 @@ class CuraApplication(QtApplication): # Initialize UI state controller.setActiveStage("PrepareStage") - controller.setActiveView("SolidView") controller.setCameraTool("CameraTool") controller.setSelectionTool("SelectionTool") @@ -2089,9 +2088,7 @@ class CuraApplication(QtApplication): is_non_sliceable = "." + file_extension in self._non_sliceable_extensions if is_non_sliceable: - # Need to switch first to the preview stage and then to layer view - self.callLater(lambda: (self.getController().setActiveStage("PreviewStage"), - self.getController().setActiveView("SimulationView"))) + self.callLater(lambda: (self.getController().setActiveStage("PreviewStage"))) block_slicing_decorator = BlockSlicingDecorator() node.addDecorator(block_slicing_decorator) diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 869ed309dc..8c207db8ad 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,6 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + from PyQt6.QtCore import pyqtProperty, QUrl from UM.Stage import Stage @@ -13,8 +15,8 @@ from UM.Stage import Stage # * The MainComponent is the component that will be drawn starting from the bottom of the stageBar and fills the rest # of the screen. class CuraStage(Stage): - def __init__(self, parent = None) -> None: - super().__init__(parent) + def __init__(self, parent = None, active_view: Optional[str] = "SolidView") -> None: + super().__init__(parent, active_view = active_view) @pyqtProperty(str, constant = True) def stageId(self) -> str: diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index eaeb2dc69b..7a1b25f331 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -58,7 +58,8 @@ class PaintTool(Tool): self.setExposedProperties("PaintType", "BrushSize", "BrushColor", "BrushShape") - Selection.selectionChanged.connect(self._updateIgnoreUnselectedObjects) + Selection.selectionChanged.connect(self._updateActiveView) + self._controller.activeViewChanged.connect(self._updateIgnoreUnselectedObjects) def _createBrushPen(self) -> QPen: pen = QPen() @@ -298,14 +299,9 @@ class PaintTool(Tool): # Make sure the displayed values are updated if the bounding box of the selected mesh(es) changes if event.type == Event.ToolActivateEvent: - controller.setActiveView("PaintTool") # Because that's the plugin-name, and the view is registered to it. - self._updateIgnoreUnselectedObjects() return True if event.type == Event.ToolDeactivateEvent: - controller.setActiveView("SolidView") - CuraApplication.getInstance().getRenderer().getRenderPass("selection").setIgnoreUnselectedObjects(False) - CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces").setIgnoreUnselectedObjects(False) return True if event.type == Event.MouseReleaseEvent and self._controller.getToolsEnabled(): @@ -397,6 +393,9 @@ class PaintTool(Tool): return False + def getRequiredExtraRenderingPasses(self) -> list[str]: + return ["selection_faces", "picking_selected"] + @staticmethod def _updateScene(node: SceneNode = None): if node is None: @@ -404,11 +403,10 @@ class PaintTool(Tool): if node is not None: Application.getInstance().getController().getScene().sceneChanged.emit(node) - def getRequiredExtraRenderingPasses(self) -> list[str]: - return ["selection_faces", "picking_selected"] + def _updateActiveView(self): + self.setActiveView("PaintTool" if len(Selection.getAllSelectedObjects()) == 1 else None) def _updateIgnoreUnselectedObjects(self): - if self._controller.getActiveTool() is self: - ignore_unselected_objects = len(Selection.getAllSelectedObjects()) == 1 - CuraApplication.getInstance().getRenderer().getRenderPass("selection").setIgnoreUnselectedObjects(ignore_unselected_objects) - CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces").setIgnoreUnselectedObjects(ignore_unselected_objects) \ No newline at end of file + ignore_unselected_objects = self._controller.getActiveView().name == "PaintTool" + CuraApplication.getInstance().getRenderer().getRenderPass("selection").setIgnoreUnselectedObjects(ignore_unselected_objects) + CuraApplication.getInstance().getRenderer().getRenderPass("selection_faces").setIgnoreUnselectedObjects(ignore_unselected_objects) \ No newline at end of file diff --git a/plugins/PaintTool/PaintView.py b/plugins/PaintTool/PaintView.py index 73e8a511a6..22629e340c 100644 --- a/plugins/PaintTool/PaintView.py +++ b/plugins/PaintTool/PaintView.py @@ -9,8 +9,8 @@ from PyQt6.QtGui import QImage, QColor, QPainter from cura.CuraApplication import CuraApplication from cura.BuildVolume import BuildVolume +from cura.CuraView import CuraView from UM.PluginRegistry import PluginRegistry -from UM.View.View import View from UM.View.GL.ShaderProgram import ShaderProgram from UM.View.GL.Texture import Texture from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -22,7 +22,7 @@ from UM.Math.Color import Color catalog = i18nCatalog("cura") -class PaintView(View): +class PaintView(CuraView): """View for model-painting.""" UNDO_STACK_SIZE = 1024 @@ -33,7 +33,7 @@ class PaintView(View): self.value: int = value def __init__(self) -> None: - super().__init__() + super().__init__(use_empty_menu_placeholder = True) self._paint_shader: Optional[ShaderProgram] = None self._current_paint_texture: Optional[Texture] = None self._current_bits_ranges: tuple[int, int] = (0, 0) @@ -50,8 +50,6 @@ class PaintView(View): application.engineCreatedSignal.connect(self._makePaintModes) self._scene = application.getController().getScene() - self._solid_view = None - def _makePaintModes(self): theme = CuraApplication.getInstance().getTheme() usual_types = {"none": self.PaintType(Color(*theme.getColor("paint_normal_area").getRgb()), 0), @@ -179,18 +177,6 @@ class PaintView(View): if self._current_paint_type not in self._paint_modes: return - if self._solid_view is None: - plugin_registry = PluginRegistry.getInstance() - solid_view = plugin_registry.getPluginObject("SolidView") - if isinstance(solid_view, View): - self._solid_view = solid_view - - display_objects = Selection.getAllSelectedObjects().copy() - if len(display_objects) != 1 and self._solid_view is not None: - # Display the classic view until a single object is selected - self._solid_view.beginRendering() - return - self._checkSetup() renderer = self.getRenderer() @@ -201,7 +187,7 @@ class PaintView(View): paint_batch = renderer.createRenderBatch(shader=self._paint_shader) renderer.addRenderBatch(paint_batch) - for node in display_objects: + for node in Selection.getAllSelectedObjects(): paint_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData(), normal_transformation=node.getCachedNormalMatrix()) self._current_paint_texture = node.callDecoration("getPaintTexture") self._paint_shader.setTexture(0, self._current_paint_texture) diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py index 88f432ef9b..3f1a4423b2 100644 --- a/plugins/PreviewStage/PreviewStage.py +++ b/plugins/PreviewStage/PreviewStage.py @@ -24,25 +24,6 @@ class PreviewStage(CuraStage): super().__init__(parent) self._application = application self._application.engineCreatedSignal.connect(self._engineCreated) - self._previously_active_view = None # type: Optional[View] - - def onStageSelected(self) -> None: - """When selecting the stage, remember which was the previous view so that - - we can revert to that view when we go out of the stage later. - """ - self._previously_active_view = self._application.getController().getActiveView() - - def onStageDeselected(self) -> None: - """Called when going to a different stage (away from the Preview Stage). - - When going to a different stage, the view should be reverted to what it - was before. Normally, that just reverts it to solid view. - """ - - if self._previously_active_view is not None: - self._application.getController().setActiveView(self._previously_active_view.getPluginId()) - self._previously_active_view = None def _engineCreated(self) -> None: """Delayed load of the QML files. diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 083fc73bf1..5d339e7f74 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -172,13 +172,20 @@ class SimulationView(CuraView): self._updateSliceWarningVisibility() self.activityChanged.emit() - def getSimulationPass(self) -> SimulationPass: + def getSimulationPass(self) -> Optional[SimulationPass]: if not self._layer_pass: + renderer = self.getRenderer() + if renderer is None: + return None + # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. self._layer_pass = SimulationPass(1, 1) self._compatibility_mode = self._evaluateCompatibilityMode() self._layer_pass.setSimulationView(self) + self._layer_pass.setEnabled(False) + renderer.addRenderPass(self._layer_pass) + return self._layer_pass def getCurrentLayer(self) -> int: @@ -734,11 +741,14 @@ class SimulationView(CuraView): # Make sure the SimulationPass is created layer_pass = self.getSimulationPass() + if layer_pass is None: + return False + renderer = self.getRenderer() if renderer is None: return False - renderer.addRenderPass(layer_pass) + layer_pass.setEnabled(True) # Make sure the NozzleNode is add to the root nozzle = self.getNozzleNode() @@ -778,7 +788,7 @@ class SimulationView(CuraView): return False if self._layer_pass is not None: - renderer.removeRenderPass(self._layer_pass) + self._layer_pass.setEnabled(False) if self._composite_pass: self._composite_pass.setLayerBindings(cast(List[str], self._old_layer_bindings)) self._composite_pass.setCompositeShader(cast(ShaderProgram, self._old_composite_shader)) diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index e76e5dbb67..b0e31ac532 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -38,7 +38,7 @@ Cura.ExpandablePopup { if (activeView == null) { - UM.Controller.setActiveView(viewModel.getItem(0).id) + UM.Controller.activeStage.setActiveView(viewModel.getItem(0).id) } } @@ -110,7 +110,7 @@ Cura.ExpandablePopup onClicked: { toggleContent() - UM.Controller.setActiveView(id) + UM.Controller.activeStage.setActiveView(id) } } } From db514f0be77dd585092ca77a1da977837b912b83 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 7 Aug 2025 11:03:51 +0200 Subject: [PATCH 61/98] Properly prepare the model for painting CURA-12660 The UV-unwrapping is now done in a background job, and the UI displays a waiting state. This fixes the issue where the user would start painting but the model was not ready yet, and the first stroke would be missing. --- plugins/PaintTool/PaintTool.py | 70 ++++++++++++++++--------- plugins/PaintTool/PaintTool.qml | 71 ++++++++++++++++++++++++++ plugins/PaintTool/PrepareTextureJob.py | 33 ++++++++++++ plugins/PaintTool/__init__.py | 1 + 4 files changed, 152 insertions(+), 23 deletions(-) create mode 100644 plugins/PaintTool/PrepareTextureJob.py diff --git a/plugins/PaintTool/PaintTool.py b/plugins/PaintTool/PaintTool.py index 7a1b25f331..82361e80ec 100644 --- a/plugins/PaintTool/PaintTool.py +++ b/plugins/PaintTool/PaintTool.py @@ -12,6 +12,7 @@ from numpy import ndarray from UM.Application import Application from UM.Event import Event, MouseEvent, KeyEvent +from UM.Job import Job from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode from UM.Scene.Selection import Selection @@ -22,6 +23,7 @@ from cura.CuraApplication import CuraApplication from cura.PickingPass import PickingPass from UM.View.SelectionPass import SelectionPass from .PaintView import PaintView +from .PrepareTextureJob import PrepareTextureJob class PaintTool(Tool): @@ -33,6 +35,13 @@ class PaintTool(Tool): SQUARE = 0 CIRCLE = 1 + class Paint(QObject): + @pyqtEnum + class State(IntEnum): + MULTIPLE_SELECTION = 0 # Multiple objects are selected, wait until there is only one + PREPARING_MODEL = 1 # Model is being prepared (UV-unwrapping, texture generation) + READY = 2 # Ready to paint ! + def __init__(self) -> None: super().__init__() @@ -56,10 +65,13 @@ class PaintTool(Tool): self._last_mouse_coords: Optional[Tuple[int, int]] = None self._last_face_id: Optional[int] = None - self.setExposedProperties("PaintType", "BrushSize", "BrushColor", "BrushShape") + self._state: PaintTool.Paint.State = PaintTool.Paint.State.MULTIPLE_SELECTION + self._prepare_texture_job: Optional[PrepareTextureJob] = None + + self.setExposedProperties("PaintType", "BrushSize", "BrushColor", "BrushShape", "State") - Selection.selectionChanged.connect(self._updateActiveView) self._controller.activeViewChanged.connect(self._updateIgnoreUnselectedObjects) + self._controller.activeToolChanged.connect(self._updateState) def _createBrushPen(self) -> QPen: pen = QPen() @@ -141,6 +153,9 @@ class PaintTool(Tool): self._brush_pen = self._createBrushPen() self.propertyChanged.emit() + def getState(self) -> int: + return self._state + def undoStackAction(self, redo_instead: bool) -> bool: paint_view = self._get_paint_view() if paint_view is None: @@ -266,23 +281,6 @@ class PaintTool(Tool): self._iteratateSplitSubstroke(node, substrokes, mid_struct, info_b) self._iteratateSplitSubstroke(node, substrokes, info_a, mid_struct) - def _setupNodeForPainting(self, node: SceneNode) -> bool: - mesh = node.getMeshData() - if mesh.hasUVCoordinates(): - return True - - texture_width, texture_height = mesh.calculateUnwrappedUVCoordinates() - if texture_width <= 0 or texture_height <= 0: - return False - - node.callDecoration("prepareTexture", texture_width, texture_height) - - if hasattr(mesh, OpenGL.VertexBufferProperty): - # Force clear OpenGL buffer so that new UV coordinates will be sent - delattr(mesh, OpenGL.VertexBufferProperty) - - return True - def event(self, event: Event) -> bool: """Handle mouse and keyboard events. @@ -304,6 +302,9 @@ class PaintTool(Tool): if event.type == Event.ToolDeactivateEvent: return True + if self._state != PaintTool.Paint.State.READY: + return False + if event.type == Event.MouseReleaseEvent and self._controller.getToolsEnabled(): if MouseEvent.LeftButton not in cast(MouseEvent, event).buttons: return False @@ -356,9 +357,6 @@ class PaintTool(Tool): if not self._mesh_transformed_cache: return False - if not self._setupNodeForPainting(node): - return False - face_id, texcoords = self._getTexCoordsFromClick(node, mouse_evt.x, mouse_evt.y) if texcoords is None: return False @@ -403,8 +401,34 @@ class PaintTool(Tool): if node is not None: Application.getInstance().getController().getScene().sceneChanged.emit(node) - def _updateActiveView(self): + def _onSelectionChanged(self): + super()._onSelectionChanged() + self.setActiveView("PaintTool" if len(Selection.getAllSelectedObjects()) == 1 else None) + self._updateState() + + def _updateState(self): + if len(Selection.getAllSelectedObjects()) == 1 and self._controller.getActiveTool() == self: + selected_object = Selection.getSelectedObject(0) + if selected_object.callDecoration("getPaintTexture") is not None: + new_state = PaintTool.Paint.State.READY + else: + new_state = PaintTool.Paint.State.PREPARING_MODEL + self._prepare_texture_job = PrepareTextureJob(selected_object) + self._prepare_texture_job.finished.connect(self._onPrepareTextureFinished) + self._prepare_texture_job.start() + else: + new_state = PaintTool.Paint.State.MULTIPLE_SELECTION + + if new_state != self._state: + self._state = new_state + self.propertyChanged.emit() + + def _onPrepareTextureFinished(self, job: Job): + if job == self._prepare_texture_job: + self._prepare_texture_job = None + self._state = PaintTool.Paint.State.READY + self.propertyChanged.emit() def _updateIgnoreUnselectedObjects(self): ignore_unselected_objects = self._controller.getActiveView().name == "PaintTool" diff --git a/plugins/PaintTool/PaintTool.qml b/plugins/PaintTool/PaintTool.qml index 2bb3106dd5..7b04f9e58d 100644 --- a/plugins/PaintTool/PaintTool.qml +++ b/plugins/PaintTool/PaintTool.qml @@ -227,4 +227,75 @@ Item } } } + + Rectangle + { + id: waitPrepareItem + anchors.fill: parent + color: UM.Theme.getColor("main_background") + visible: UM.Controller.properties.getValue("State") === Cura.PaintToolState.PREPARING_MODEL + + ColumnLayout + { + anchors.fill: parent + + UM.Label + { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.verticalStretchFactor: 2 + + text: catalog.i18nc("@label", "Preparing model for painting...") + verticalAlignment: Text.AlignBottom + horizontalAlignment: Text.AlignHCenter + } + + Item + { + Layout.preferredWidth: loadingIndicator.width + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: true + Layout.verticalStretchFactor: 1 + + UM.ColorImage + { + id: loadingIndicator + + anchors.top: parent.top + anchors.left: parent.left + width: UM.Theme.getSize("card_icon").width + height: UM.Theme.getSize("card_icon").height + source: UM.Theme.getIcon("ArrowDoubleCircleRight") + color: UM.Theme.getColor("text_default") + + RotationAnimator + { + target: loadingIndicator + from: 0 + to: 360 + duration: 2000 + loops: Animation.Infinite + running: true + alwaysRunToEnd: true + } + } + } + } + } + + Rectangle + { + id: selectSingleMessageItem + anchors.fill: parent + color: UM.Theme.getColor("main_background") + visible: UM.Controller.properties.getValue("State") === Cura.PaintToolState.MULTIPLE_SELECTION + + UM.Label + { + anchors.fill: parent + text: catalog.i18nc("@label", "Select a single model to start painting") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } } diff --git a/plugins/PaintTool/PrepareTextureJob.py b/plugins/PaintTool/PrepareTextureJob.py new file mode 100644 index 0000000000..6c5e61c009 --- /dev/null +++ b/plugins/PaintTool/PrepareTextureJob.py @@ -0,0 +1,33 @@ +# Copyright (c) 2025 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Job import Job +from UM.Scene.SceneNode import SceneNode +from UM.View.GL.OpenGL import OpenGL + + +class PrepareTextureJob(Job): + """ + Background job to prepare a model for painting, i.e. do the UV-unwrapping and create the appropriate texture image, + which can last a few seconds + """ + + def __init__(self, node: SceneNode): + super().__init__() + self._node: SceneNode = node + + def run(self) -> None: + # If the model has already-provided UV coordinates, we can only assume that the associated texture + # should be a square + texture_width = texture_height = 4096 + + mesh = self._node.getMeshData() + if not mesh.hasUVCoordinates(): + texture_width, texture_height = mesh.calculateUnwrappedUVCoordinates() + + self._node.callDecoration("prepareTexture", texture_width, texture_height) + + if hasattr(mesh, OpenGL.VertexBufferProperty): + # Force clear OpenGL buffer so that new UV coordinates will be sent + delattr(mesh, OpenGL.VertexBufferProperty) + diff --git a/plugins/PaintTool/__init__.py b/plugins/PaintTool/__init__.py index e92c169ee6..93b47c7266 100644 --- a/plugins/PaintTool/__init__.py +++ b/plugins/PaintTool/__init__.py @@ -27,6 +27,7 @@ def getMetaData(): def register(app): qmlRegisterUncreatableType(PaintTool.PaintTool.Brush, "Cura", 1, 0, "This is an enumeration class", "PaintToolBrush") + qmlRegisterUncreatableType(PaintTool.PaintTool.Paint, "Cura", 1, 0, "This is an enumeration class", "PaintToolState") return { "tool": PaintTool.PaintTool(), "view": PaintView.PaintView() From f348a31a23cfe9834d49bc5b3b1cd4edcd231379 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 11 Aug 2025 07:48:36 +0200 Subject: [PATCH 62/98] Update um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg To address: - Under-extrusion issue after bridging - Overhang quality & Warping --- .../um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg b/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg index 7fe5437a80..e3d9ecd894 100644 --- a/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg @@ -12,4 +12,8 @@ variant = 0.4mm weight = -2 [values] - +bridge_wall_speed = 40 +cool_min_layer_time_overhang = 12 +cool_min_speed = 40 +wall_overhang_angle = 30 +wall_overhang_speed_factors = [60] \ No newline at end of file From 07c7e78b3fb37fa509600e4548ca0b42accb171b Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 11 Aug 2025 07:52:56 +0200 Subject: [PATCH 63/98] Add build chamber fan commands to start gcode - Ensure the Fans are active - Set the servo to bring air from outside rather than internal circulation --- resources/definitions/ultimaker_sketch_sprint.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/ultimaker_sketch_sprint.def.json b/resources/definitions/ultimaker_sketch_sprint.def.json index 529abc0940..a9883d268f 100644 --- a/resources/definitions/ultimaker_sketch_sprint.def.json +++ b/resources/definitions/ultimaker_sketch_sprint.def.json @@ -158,7 +158,7 @@ "machine_max_feedrate_z": { "default_value": 40 }, "machine_min_cool_heat_time_window": { "value": "15" }, "machine_name": { "default_value": "MakerBot Sketch Sprint" }, - "machine_start_gcode": { "default_value": "G28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651 S255\nG1 Z0.25 F6000\nG1 E-1.5 F800\nG1 E2 F800\nG1 X111 Y111 Z0.25 F4800\nG1 X111 Y-111 E25 F1200" }, + "machine_start_gcode": { "default_value": "G28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651 S255\nSET_SERVO SERVO=my_servo ANGLE=180\nSET_FAN_SPEED FAN=external_fan SPEED=1\nSET_FAN_SPEED FAN=internal_fan SPEED=1\nG1 Z0.25 F6000\nG1 E-1.5 F800\nG1 E2 F800\nG1 X111 Y111 Z0.25 F4800\nG1 X111 Y-111 E25 F1200" }, "machine_width": { "default_value": 221.5 }, "material_bed_temp_wait": { "value": "False" }, "material_bed_temperature": From 6f85d9f1f49941f8b9e80de42d676eeab622d6e0 Mon Sep 17 00:00:00 2001 From: HellAholic <28710690+HellAholic@users.noreply.github.com> Date: Mon, 11 Aug 2025 05:54:25 +0000 Subject: [PATCH 64/98] Apply printer-linter format --- .../um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg b/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg index e3d9ecd894..a7e18af32d 100644 --- a/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.2mm.inst.cfg @@ -16,4 +16,5 @@ bridge_wall_speed = 40 cool_min_layer_time_overhang = 12 cool_min_speed = 40 wall_overhang_angle = 30 -wall_overhang_speed_factors = [60] \ No newline at end of file +wall_overhang_speed_factors = [60] + From 32f06fe2df41be7a5b455c4a037edadd1a8c2f64 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 11 Aug 2025 11:06:48 +0200 Subject: [PATCH 65/98] Remove the link to campaign - Incorrect redirect --- resources/qml/WelcomePages/AddUltimakerPrinter.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/WelcomePages/AddUltimakerPrinter.qml b/resources/qml/WelcomePages/AddUltimakerPrinter.qml index 0c9b7d4f7f..7b1a9ced20 100644 --- a/resources/qml/WelcomePages/AddUltimakerPrinter.qml +++ b/resources/qml/WelcomePages/AddUltimakerPrinter.qml @@ -129,7 +129,6 @@ Control text: catalog.i18nc("@button", "Sign in to Digital Factory") onClicked: function() { - Qt.openUrlExternally("https://digitalfactory.ultimaker.com/app/printers?add_printer=true&utm_source=cura&utm_medium=software&utm_campaign=onboarding-add-printer") text = catalog.i18nc("@button", "Waiting for new printers") busy = true; enabled = false; From b1e4080a204e7bb061f8c0afe9a3a6922a254269 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 11 Aug 2025 12:02:27 +0200 Subject: [PATCH 66/98] Update the sign in button when login is in progress --- resources/qml/WelcomePages/CloudContent.qml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/qml/WelcomePages/CloudContent.qml b/resources/qml/WelcomePages/CloudContent.qml index 54e95462e0..106582d1a1 100644 --- a/resources/qml/WelcomePages/CloudContent.qml +++ b/resources/qml/WelcomePages/CloudContent.qml @@ -210,7 +210,13 @@ Item anchors.bottom: parent.bottom text: catalog.i18nc("@button", "Sign in") - onClicked: Cura.API.account.login() + onClicked: function() + { + text = catalog.i18nc("@button", "Waiting for new printers") + busy = true; + enabled = false; + Cura.API.account.isLoggedIn? Cura.API.account.sync():Cura.API.account.login(); + } // Content Item is used in order to align the text inside the button. Without it, when resizing the // button, the text will be aligned on the left contentItem: Text { From 3da366f31633a93fc42c44d845aa2fcb4ee0ef06 Mon Sep 17 00:00:00 2001 From: HellAholic <28710690+HellAholic@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:58:51 +0000 Subject: [PATCH 67/98] Apply printer-linter format --- resources/definitions/fdmprinter.def.json | 2 +- resources/definitions/ultimaker.def.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d4813429ef..68852d805f 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -9603,4 +9603,4 @@ } } } -} +} \ No newline at end of file diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json index 2995562e59..c5f441479b 100644 --- a/resources/definitions/ultimaker.def.json +++ b/resources/definitions/ultimaker.def.json @@ -227,4 +227,4 @@ "z_seam_relative": { "value": "True" }, "zig_zaggify_support": { "value": true } } -} +} \ No newline at end of file From 845713b03a0dbcca06391d3cebbe185d5657d1e7 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 11 Aug 2025 13:38:37 +0200 Subject: [PATCH 68/98] Update zyyx_plus.def.json --- resources/definitions/zyyx_plus.def.json | 97 ++++++++++++++++-------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/resources/definitions/zyyx_plus.def.json b/resources/definitions/zyyx_plus.def.json index 4df6e50a85..89ba8e08b0 100644 --- a/resources/definitions/zyyx_plus.def.json +++ b/resources/definitions/zyyx_plus.def.json @@ -2,8 +2,7 @@ "version": 2, "name": "ZYYX+", "inherits": "fdmprinter", - "metadata": - { + "metadata": { "visible": true, "author": "Theodor Hansson", "manufacturer": "ZYYX Labs AB", @@ -75,10 +74,6 @@ "Extrudr_GreenTECPro_Silver_175", "Extrudr_GreenTECPro_White_175", "verbatim_bvoh_175", - "Vertex_Delta_ABS", - "Vertex_Delta_PET", - "Vertex_Delta_PLA", - "Vertex_Delta_TPU", "chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", @@ -183,37 +178,75 @@ "zyyx_pronylon" ], "has_machine_quality": true, - "machine_extruder_trains": { "0": "zyyx_plus_extruder_0" }, + "machine_extruder_trains": { + "0": "zyyx_plus_extruder_0" + }, "machine_x3g_variant": "z", "preferred_material": "generic_pla", - "preferred_quality_type": "normal", - "quality_definition": "zyyx_plus", - "setting_version": 3 + "preferred_quality_type": "normal" }, - "overrides": - { - "gantry_height": { "value": "10" }, - "infill_overlap": { "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" }, - "machine_center_is_zero": { "default_value": true }, - "machine_depth": { "default_value": 210 }, - "machine_end_gcode": { "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" }, - "machine_gcode_flavor": { "default_value": "Makerbot" }, - "machine_head_with_fans_polygon": - { + "overrides": { + "gantry_height": { + "value": "10" + }, + "infill_overlap": { + "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" + }, + "machine_center_is_zero": { + "default_value": true + }, + "machine_depth": { + "default_value": 210 + }, + "machine_end_gcode": { + "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" + }, + "machine_gcode_flavor": { + "default_value": "Makerbot" + }, + "machine_head_with_fans_polygon": { "default_value": [ - [-37, 50], - [25, 50], - [25, -40], - [-37, -40] + [ + -37, + 50 + ], + [ + 25, + 50 + ], + [ + 25, + -40 + ], + [ + -37, + -40 + ] ] }, - "machine_height": { "default_value": 195 }, - "machine_name": { "default_value": "ZYYX+" }, - "machine_start_gcode": { "default_value": "; ZYYX+ start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nM126 S0; Turn off fan\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y80 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X132.5 Y120\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-44.5 Y120 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X132.5 Y-110 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\nG1 Z1 F1000\n\n; Lay prime strip\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 X80 Y-110 Z0.2 F1000\nG1 X20 E9 F1000\nG1 X-20 E12.5 F1000\nG92 E0 ; Set E to 0\n\n; End of start gcode" }, - "machine_steps_per_mm_e": { "default_value": 96.27520187033366 }, - "machine_steps_per_mm_x": { "default_value": 88.888889 }, - "machine_steps_per_mm_y": { "default_value": 88.888889 }, - "machine_steps_per_mm_z": { "default_value": 400 }, - "machine_width": { "default_value": 265 } + "machine_height": { + "default_value": 195 + }, + "machine_name": { + "default_value": "ZYYX+" + }, + "machine_start_gcode": { + "default_value": "; ZYYX+ start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nM126 S0; Turn off fan\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y80 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X132.5 Y120\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-44.5 Y120 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X132.5 Y-110 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\nG1 Z1 F1000\n\n; Lay prime strip\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 X80 Y-110 Z0.2 F1000\nG1 X20 E9 F1000\nG1 X-20 E12.5 F1000\nG92 E0 ; Set E to 0\n\n; End of start gcode" + }, + "machine_steps_per_mm_e": { + "default_value": 96.27520187033366 + }, + "machine_steps_per_mm_x": { + "default_value": 88.888889 + }, + "machine_steps_per_mm_y": { + "default_value": 88.888889 + }, + "machine_steps_per_mm_z": { + "default_value": 400 + }, + "machine_width": { + "default_value": 265 + } } } \ No newline at end of file From d0365db17664dbfd2f2cdcae97973550e8e8a06f Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 11 Aug 2025 13:38:40 +0200 Subject: [PATCH 69/98] Update zyyx_pro.def.json --- resources/definitions/zyyx_pro.def.json | 186 +++++++++++++++++------- 1 file changed, 135 insertions(+), 51 deletions(-) diff --git a/resources/definitions/zyyx_pro.def.json b/resources/definitions/zyyx_pro.def.json index 3fdfbe27d7..c96d87d0b9 100644 --- a/resources/definitions/zyyx_pro.def.json +++ b/resources/definitions/zyyx_pro.def.json @@ -2,8 +2,7 @@ "version": 2, "name": "ZYYX Pro", "inherits": "fdmprinter", - "metadata": - { + "metadata": { "visible": true, "author": "Theodor Hansson", "manufacturer": "ZYYX Labs AB", @@ -73,10 +72,6 @@ "Extrudr_GreenTECPro_Silver_175", "Extrudr_GreenTECPro_White_175", "verbatim_bvoh_175", - "Vertex_Delta_ABS", - "Vertex_Delta_PET", - "Vertex_Delta_PLA", - "Vertex_Delta_TPU", "chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", @@ -181,69 +176,158 @@ "has_materials": true, "has_variants": true, "machine": "zyyx_pro", - "machine_extruder_trains": { "0": "zyyx_pro_extruder" }, + "machine_extruder_trains": { + "0": "zyyx_pro_extruder" + }, "machine_x3g_variant": "z", "preferred_material": "generic_pla", "preferred_variant_name": "Carbon0.6", - "quality_definition": "zyyx_pro", - "setting_version": 3, "variants_name": "SwiftTool" }, - "overrides": - { - "gantry_height": { "value": "10" }, - "infill_overlap": { "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" }, - "machine_center_is_zero": { "default_value": true }, - "machine_depth": { "default_value": 228 }, - "machine_disallowed_areas": - { + "overrides": { + "gantry_height": { + "value": "10" + }, + "infill_overlap": { + "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" + }, + "machine_center_is_zero": { + "default_value": true + }, + "machine_depth": { + "default_value": 228 + }, + "machine_disallowed_areas": { "default_value": [ [ - [-58, 117.5], - [-58, 108], - [-50, 108], - [-50, 117.5] + [ + -58, + 117.5 + ], + [ + -58, + 108 + ], + [ + -50, + 108 + ], + [ + -50, + 117.5 + ] ], [ - [119, 117.5], - [119, 108], - [140, 108], - [140, 117.5] + [ + 119, + 117.5 + ], + [ + 119, + 108 + ], + [ + 140, + 108 + ], + [ + 140, + 117.5 + ] ], [ - [-58, -117.5], - [-58, -108], - [-50, -108], - [-50, -117.5] + [ + -58, + -117.5 + ], + [ + -58, + -108 + ], + [ + -50, + -108 + ], + [ + -50, + -117.5 + ] ], [ - [119, -117.5], - [119, -108], - [140, -108], - [140, -117.5] + [ + 119, + -117.5 + ], + [ + 119, + -108 + ], + [ + 140, + -108 + ], + [ + 140, + -117.5 + ] ] ] }, - "machine_end_gcode": { "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" }, - "machine_gcode_flavor": { "default_value": "Makerbot" }, - "machine_head_with_fans_polygon": - { + "machine_end_gcode": { + "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" + }, + "machine_gcode_flavor": { + "default_value": "Makerbot" + }, + "machine_head_with_fans_polygon": { "default_value": [ - [-37, 50], - [25, 50], - [25, -40], - [-37, -40] + [ + -37, + 50 + ], + [ + 25, + 50 + ], + [ + 25, + -40 + ], + [ + -37, + -40 + ] ] }, - "machine_heated_bed": { "default_value": true }, - "machine_height": { "default_value": 195 }, - "machine_name": { "default_value": "ZYYX Pro" }, - "machine_start_gcode": { "default_value": "; ZYYX Pro start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM420 P20; set back fan speed 10 off 11-20 10-100%\nM140 S10 T0; set 100% heater power\nM140 S99 T0; set chamber heater negative hysteresis 19 degrees\nM140 S102 T0; set chamber heater positive hysteresis 2 degrees\nM140 S{material_bed_temperature_layer_0} T0; set chamber temperature\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y90 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X135 Y114\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-47 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X135 Y-114 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\n\n; Extrude material over hole\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 Z0.10 E500 F50\nG1 X115 Y-95 F1000\nG92 E0 ; Set E to 0\n; End of start gcode" }, - "machine_steps_per_mm_e": { "default_value": 96.27520187033366 }, - "machine_steps_per_mm_x": { "default_value": 88.888889 }, - "machine_steps_per_mm_y": { "default_value": 88.888889 }, - "machine_steps_per_mm_z": { "default_value": 400 }, - "machine_width": { "default_value": 265 }, - "material_diameter": { "default_value": 1.75 } + "machine_heated_bed": { + "default_value": true + }, + "machine_height": { + "default_value": 195 + }, + "machine_name": { + "default_value": "ZYYX Pro" + }, + "machine_start_gcode": { + "default_value": "; ZYYX Pro start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM420 P20; set back fan speed 10 off 11-20 10-100%\nM140 S10 T0; set 100% heater power\nM140 S99 T0; set chamber heater negative hysteresis 19 degrees\nM140 S102 T0; set chamber heater positive hysteresis 2 degrees\nM140 S{material_bed_temperature_layer_0} T0; set chamber temperature\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y90 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X135 Y114\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-47 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X135 Y-114 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\n\n; Extrude material over hole\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 Z0.10 E500 F50\nG1 X115 Y-95 F1000\nG92 E0 ; Set E to 0\n; End of start gcode" + }, + "machine_steps_per_mm_e": { + "default_value": 96.27520187033366 + }, + "machine_steps_per_mm_x": { + "default_value": 88.888889 + }, + "machine_steps_per_mm_y": { + "default_value": 88.888889 + }, + "machine_steps_per_mm_z": { + "default_value": 400 + }, + "machine_width": { + "default_value": 265 + }, + "material_diameter": { + "default_value": 1.75 + } } } \ No newline at end of file From b2e7877bc6318fa88c2ee9c9cabe39e312563ebb Mon Sep 17 00:00:00 2001 From: HellAholic <28710690+HellAholic@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:40:17 +0000 Subject: [PATCH 70/98] Apply printer-linter format --- resources/definitions/zyyx_plus.def.json | 89 ++++------- resources/definitions/zyyx_pro.def.json | 180 ++++++----------------- 2 files changed, 70 insertions(+), 199 deletions(-) diff --git a/resources/definitions/zyyx_plus.def.json b/resources/definitions/zyyx_plus.def.json index 89ba8e08b0..73b08ec6ef 100644 --- a/resources/definitions/zyyx_plus.def.json +++ b/resources/definitions/zyyx_plus.def.json @@ -2,7 +2,8 @@ "version": 2, "name": "ZYYX+", "inherits": "fdmprinter", - "metadata": { + "metadata": + { "visible": true, "author": "Theodor Hansson", "manufacturer": "ZYYX Labs AB", @@ -178,75 +179,35 @@ "zyyx_pronylon" ], "has_machine_quality": true, - "machine_extruder_trains": { - "0": "zyyx_plus_extruder_0" - }, + "machine_extruder_trains": { "0": "zyyx_plus_extruder_0" }, "machine_x3g_variant": "z", "preferred_material": "generic_pla", "preferred_quality_type": "normal" }, - "overrides": { - "gantry_height": { - "value": "10" - }, - "infill_overlap": { - "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" - }, - "machine_center_is_zero": { - "default_value": true - }, - "machine_depth": { - "default_value": 210 - }, - "machine_end_gcode": { - "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" - }, - "machine_gcode_flavor": { - "default_value": "Makerbot" - }, - "machine_head_with_fans_polygon": { + "overrides": + { + "gantry_height": { "value": "10" }, + "infill_overlap": { "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" }, + "machine_center_is_zero": { "default_value": true }, + "machine_depth": { "default_value": 210 }, + "machine_end_gcode": { "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" }, + "machine_gcode_flavor": { "default_value": "Makerbot" }, + "machine_head_with_fans_polygon": + { "default_value": [ - [ - -37, - 50 - ], - [ - 25, - 50 - ], - [ - 25, - -40 - ], - [ - -37, - -40 - ] + [-37, 50], + [25, 50], + [25, -40], + [-37, -40] ] }, - "machine_height": { - "default_value": 195 - }, - "machine_name": { - "default_value": "ZYYX+" - }, - "machine_start_gcode": { - "default_value": "; ZYYX+ start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nM126 S0; Turn off fan\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y80 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X132.5 Y120\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-44.5 Y120 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X132.5 Y-110 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\nG1 Z1 F1000\n\n; Lay prime strip\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 X80 Y-110 Z0.2 F1000\nG1 X20 E9 F1000\nG1 X-20 E12.5 F1000\nG92 E0 ; Set E to 0\n\n; End of start gcode" - }, - "machine_steps_per_mm_e": { - "default_value": 96.27520187033366 - }, - "machine_steps_per_mm_x": { - "default_value": 88.888889 - }, - "machine_steps_per_mm_y": { - "default_value": 88.888889 - }, - "machine_steps_per_mm_z": { - "default_value": 400 - }, - "machine_width": { - "default_value": 265 - } + "machine_height": { "default_value": 195 }, + "machine_name": { "default_value": "ZYYX+" }, + "machine_start_gcode": { "default_value": "; ZYYX+ start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nM126 S0; Turn off fan\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y80 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X132.5 Y120\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-44.5 Y120 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X132.5 Y-110 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\nG1 Z1 F1000\n\n; Lay prime strip\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 X80 Y-110 Z0.2 F1000\nG1 X20 E9 F1000\nG1 X-20 E12.5 F1000\nG92 E0 ; Set E to 0\n\n; End of start gcode" }, + "machine_steps_per_mm_e": { "default_value": 96.27520187033366 }, + "machine_steps_per_mm_x": { "default_value": 88.888889 }, + "machine_steps_per_mm_y": { "default_value": 88.888889 }, + "machine_steps_per_mm_z": { "default_value": 400 }, + "machine_width": { "default_value": 265 } } } \ No newline at end of file diff --git a/resources/definitions/zyyx_pro.def.json b/resources/definitions/zyyx_pro.def.json index c96d87d0b9..385cf9b0ff 100644 --- a/resources/definitions/zyyx_pro.def.json +++ b/resources/definitions/zyyx_pro.def.json @@ -2,7 +2,8 @@ "version": 2, "name": "ZYYX Pro", "inherits": "fdmprinter", - "metadata": { + "metadata": + { "visible": true, "author": "Theodor Hansson", "manufacturer": "ZYYX Labs AB", @@ -176,158 +177,67 @@ "has_materials": true, "has_variants": true, "machine": "zyyx_pro", - "machine_extruder_trains": { - "0": "zyyx_pro_extruder" - }, + "machine_extruder_trains": { "0": "zyyx_pro_extruder" }, "machine_x3g_variant": "z", "preferred_material": "generic_pla", "preferred_variant_name": "Carbon0.6", "variants_name": "SwiftTool" }, - "overrides": { - "gantry_height": { - "value": "10" - }, - "infill_overlap": { - "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" - }, - "machine_center_is_zero": { - "default_value": true - }, - "machine_depth": { - "default_value": 228 - }, - "machine_disallowed_areas": { + "overrides": + { + "gantry_height": { "value": "10" }, + "infill_overlap": { "value": "12 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0" }, + "machine_center_is_zero": { "default_value": true }, + "machine_depth": { "default_value": 228 }, + "machine_disallowed_areas": + { "default_value": [ [ - [ - -58, - 117.5 - ], - [ - -58, - 108 - ], - [ - -50, - 108 - ], - [ - -50, - 117.5 - ] + [-58, 117.5], + [-58, 108], + [-50, 108], + [-50, 117.5] ], [ - [ - 119, - 117.5 - ], - [ - 119, - 108 - ], - [ - 140, - 108 - ], - [ - 140, - 117.5 - ] + [119, 117.5], + [119, 108], + [140, 108], + [140, 117.5] ], [ - [ - -58, - -117.5 - ], - [ - -58, - -108 - ], - [ - -50, - -108 - ], - [ - -50, - -117.5 - ] + [-58, -117.5], + [-58, -108], + [-50, -108], + [-50, -117.5] ], [ - [ - 119, - -117.5 - ], - [ - 119, - -108 - ], - [ - 140, - -108 - ], - [ - 140, - -117.5 - ] + [119, -117.5], + [119, -108], + [140, -108], + [140, -117.5] ] ] }, - "machine_end_gcode": { - "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" - }, - "machine_gcode_flavor": { - "default_value": "Makerbot" - }, - "machine_head_with_fans_polygon": { + "machine_end_gcode": { "default_value": "; ZYYX 3D Printer end gcode\nM73 P100 ; end build progress\nG0 Z195 F1000 ; send Z axis to bottom of machine\nM104 S0 T0 ; cool down extruder\nM127 ; stop blower fan\nG162 X Y F3000 ; home XY maximum\nM18 ; disable stepper\nM70 P5 (ZYYX Print Finished!)\nM72 P1 ; play Ta-Da song\n" }, + "machine_gcode_flavor": { "default_value": "Makerbot" }, + "machine_head_with_fans_polygon": + { "default_value": [ - [ - -37, - 50 - ], - [ - 25, - 50 - ], - [ - 25, - -40 - ], - [ - -37, - -40 - ] + [-37, 50], + [25, 50], + [25, -40], + [-37, -40] ] }, - "machine_heated_bed": { - "default_value": true - }, - "machine_height": { - "default_value": 195 - }, - "machine_name": { - "default_value": "ZYYX Pro" - }, - "machine_start_gcode": { - "default_value": "; ZYYX Pro start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM420 P20; set back fan speed 10 off 11-20 10-100%\nM140 S10 T0; set 100% heater power\nM140 S99 T0; set chamber heater negative hysteresis 19 degrees\nM140 S102 T0; set chamber heater positive hysteresis 2 degrees\nM140 S{material_bed_temperature_layer_0} T0; set chamber temperature\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y90 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X135 Y114\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-47 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X135 Y-114 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\n\n; Extrude material over hole\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 Z0.10 E500 F50\nG1 X115 Y-95 F1000\nG92 E0 ; Set E to 0\n; End of start gcode" - }, - "machine_steps_per_mm_e": { - "default_value": 96.27520187033366 - }, - "machine_steps_per_mm_x": { - "default_value": 88.888889 - }, - "machine_steps_per_mm_y": { - "default_value": 88.888889 - }, - "machine_steps_per_mm_z": { - "default_value": 400 - }, - "machine_width": { - "default_value": 265 - }, - "material_diameter": { - "default_value": 1.75 - } + "machine_heated_bed": { "default_value": true }, + "machine_height": { "default_value": 195 }, + "machine_name": { "default_value": "ZYYX Pro" }, + "machine_start_gcode": { "default_value": "; ZYYX Pro start gcode\n; Author Theodor Hansson 2024\nM73 P0; enable build progress\nM420 P20; set back fan speed 10 off 11-20 10-100%\nM140 S10 T0; set 100% heater power\nM140 S99 T0; set chamber heater negative hysteresis 19 degrees\nM140 S102 T0; set chamber heater positive hysteresis 2 degrees\nM140 S{material_bed_temperature_layer_0} T0; set chamber temperature\nM104 S{material_print_temperature_layer_0} T0; set nozzle temperature\nG21; set units to mm\nG90; set positioning to absolute\nG130 X80 Y90 A127 B127 ; Set Stepper Vref to default value\n\n; Home xy-axis\nG162 X Y F2500; home XY axes maximum\nG92 X0 Y0\nG1 X-5 Y-5 F2500\nG162 X Y F200; home XY axes maximum slowly\nG92 X135 Y114\n\n; Home z-axis\nG161 Z F1100; home Z axis minimum\nG92 Z0\nG1 Z2 F1100\nG161 Z F100; home Z axis minimum slowly\nG92 Z0\nM132 Z; Recall home offsets for Z\n\n; Calibrate point 1 (we're already at point 1)\nM131 A; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 2\nG1 X-47 F7000; move to 2nd probing point\nG161 Z F100\nM131 B; store surface calibration point 1\nG1 Z2 F1100; back up from buildplate\n\n; Calibrate point 3\nG1 X135 Y-114 F7000; move to 2nd probing point\nG161 Z F100\nM131 AB; store surface calibration point 3\nG1 Z2 F1100; back up from buildplate\nM132 AB; Activate auto-leveling\n\n; Extrude material over hole\nM133 T0; stabilize extruder temperature\nM126 S{cool_fan_speed_0}; Activate fan\nG4 P1000; Wait a little bit longer\nG1 Z0.10 E500 F50\nG1 X115 Y-95 F1000\nG92 E0 ; Set E to 0\n; End of start gcode" }, + "machine_steps_per_mm_e": { "default_value": 96.27520187033366 }, + "machine_steps_per_mm_x": { "default_value": 88.888889 }, + "machine_steps_per_mm_y": { "default_value": 88.888889 }, + "machine_steps_per_mm_z": { "default_value": 400 }, + "machine_width": { "default_value": 265 }, + "material_diameter": { "default_value": 1.75 } } } \ No newline at end of file From d372c68bd7f03b069379780cb25445aaf84f3146 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Tue, 12 Aug 2025 11:58:41 +0000 Subject: [PATCH 71/98] Set conan package version 5.10.3 --- conandata.yml | 14 +++++++------- resources/conandata.yml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/conandata.yml b/conandata.yml index 9d709096f4..da5149f15c 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1,15 +1,15 @@ -version: "5.10.2" +version: "5.10.3" requirements: - - "cura_resources/5.10.2" - - "uranium/5.10.2" - - "curaengine/5.10.2" - - "cura_binary_data/5.10.2" - - "fdm_materials/5.10.2" + - "cura_resources/5.10.3" + - "uranium/5.10.3" + - "curaengine/5.10.3" + - "cura_binary_data/5.10.3" + - "fdm_materials/5.10.3" - "dulcificum/5.10.0" - "pysavitar/5.10.0" - "pynest2d/5.10.0" requirements_internal: - - "fdm_materials/5.10.2" + - "fdm_materials/5.10.3" - "cura_private_data/5.10.0-alpha.0@internal/testing" requirements_enterprise: - "native_cad_plugin/2.0.0" diff --git a/resources/conandata.yml b/resources/conandata.yml index c4418d4f57..71d63a5684 100644 --- a/resources/conandata.yml +++ b/resources/conandata.yml @@ -1 +1 @@ -version: "5.10.2" +version: "5.10.3" From 0c408cb43cc2a9626597f033af9110c598d14f03 Mon Sep 17 00:00:00 2001 From: Frederic Meeuwissen <13856291+Frederic98@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:21:03 +0200 Subject: [PATCH 72/98] [PP-574] Add BVOH profiles --- .../um_f4_bb0.4_bvoh_0.15mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.4_bvoh_0.1mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.4_bvoh_0.2mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.4_bvoh_0.3mm.inst.cfg | 44 +++++++++++++++++++ .../um_f4_bb0.8_bvoh_0.2mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.8_bvoh_0.3mm.inst.cfg | 43 ++++++++++++++++++ .../um_f4_bb0.8_bvoh_0.4mm.inst.cfg | 43 ++++++++++++++++++ .../um_s3_bb0.4_bvoh_0.15mm.inst.cfg | 35 +++++++++++++++ .../um_s3_bb0.4_bvoh_0.1mm.inst.cfg | 36 +++++++++++++++ .../um_s3_bb0.4_bvoh_0.2mm.inst.cfg | 35 +++++++++++++++ .../um_s3_bb0.4_bvoh_0.3mm.inst.cfg | 37 ++++++++++++++++ .../um_s3_bb0.8_bvoh_0.2mm.inst.cfg | 34 ++++++++++++++ .../um_s3_bb0.8_bvoh_0.3mm.inst.cfg | 36 +++++++++++++++ .../um_s3_bb0.8_bvoh_0.4mm.inst.cfg | 35 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.15mm.inst.cfg | 35 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.1mm.inst.cfg | 36 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.2mm.inst.cfg | 35 +++++++++++++++ .../um_s5_bb0.4_bvoh_0.3mm.inst.cfg | 37 ++++++++++++++++ .../um_s5_bb0.8_bvoh_0.2mm.inst.cfg | 34 ++++++++++++++ .../um_s5_bb0.8_bvoh_0.3mm.inst.cfg | 36 +++++++++++++++ .../um_s5_bb0.8_bvoh_0.4mm.inst.cfg | 35 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.15mm.inst.cfg | 35 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.1mm.inst.cfg | 36 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.2mm.inst.cfg | 35 +++++++++++++++ .../um_s8_bb0.4_bvoh_0.3mm.inst.cfg | 37 ++++++++++++++++ .../um_s8_bb0.8_bvoh_0.2mm.inst.cfg | 34 ++++++++++++++ .../um_s8_bb0.8_bvoh_0.3mm.inst.cfg | 36 +++++++++++++++ .../um_s8_bb0.8_bvoh_0.4mm.inst.cfg | 35 +++++++++++++++ 28 files changed, 1046 insertions(+) create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg create mode 100644 resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..c6548f0173 --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..6f1b986c4e --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..ff49315c1e --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..209b16b697 --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,44 @@ +[general] +definition = ultimaker_factor4 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..d10b0c939e --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..dd2e2da565 --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..fa230a1f5f --- /dev/null +++ b/resources/quality/ultimaker_factor4/um_f4_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,43 @@ +[general] +definition = ultimaker_factor4 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_print = 1000.0 +acceleration_support_bottom = 100 +acceleration_support_interface = 1000 +brim_replaces_support = False +build_volume_temperature = =40 if extruders_enabled_count > 1 else 35 +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +gradual_flow_discretisation_step_size = 0.1 +gradual_flow_enabled = True +gradual_support_infill_steps = 0 +initial_layer_line_width_factor = 125 +jerk_print = 10 +material_flow_layer_0 = 90 +max_flow_acceleration = 1 +minimum_support_area = 4 +prime_tower_flow = 90 +prime_tower_min_volume = 15 +retraction_min_travel = 5.0 +retraction_prime_speed = 10.0 +skin_material_flow = =material_flow * 0.93 +speed_print = 30 +support_angle = 45 +support_infill_rate = 20 +support_infill_sparse_thickness = =min(layer_height * 2, machine_nozzle_size * 3 / 4) if layer_height <= 0.15 / 0.4 * machine_nozzle_size else layer_height +support_interface_offset = 1 +support_offset = 3 +support_xy_distance = 2 +support_z_distance = 0 +switch_extruder_prime_speed = 10.0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..93c1d4947d --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s3 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..f06f8deb6f --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s3 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..61059651ef --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s3 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..8f06dd0562 --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,37 @@ +[general] +definition = ultimaker_s3 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..a9d67b0552 --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,34 @@ +[general] +definition = ultimaker_s3 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..016887c23c --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s3 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..4d4bffbab9 --- /dev/null +++ b/resources/quality/ultimaker_s3/um_s3_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s3 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..e5dc8e7295 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s5 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..ed7638a4e0 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s5 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..1d02c886bc --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s5 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..b759ae0bb3 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,37 @@ +[general] +definition = ultimaker_s5 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..58875826ad --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,34 @@ +[general] +definition = ultimaker_s5 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..74b33f5366 --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s5 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..cd501dcf0e --- /dev/null +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s5 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 20 +jerk_support = 20 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg new file mode 100644 index 0000000000..e10db6b743 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.15mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s8 +name = Normal +version = 4 + +[metadata] +material = generic_bvoh +quality_type = fast +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -1 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg new file mode 100644 index 0000000000..9c5d0e9bd8 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.1mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s8 +name = Fine +version = 4 + +[metadata] +material = generic_bvoh +quality_type = normal +setting_version = 25 +type = quality +variant = BB 0.4 +weight = 0 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = =2 * layer_height +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..8f4c52d7b5 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.2mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s8 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..e37500de1a --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.4_bvoh_0.3mm.inst.cfg @@ -0,0 +1,37 @@ +[general] +definition = ultimaker_s8 +name = Extra Fast - Experimental +version = 4 + +[metadata] +is_experimental = True +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.4 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature - 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg new file mode 100644 index 0000000000..9a8e2a51c4 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.2mm.inst.cfg @@ -0,0 +1,34 @@ +[general] +definition = ultimaker_s8 +name = Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = draft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -2 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg new file mode 100644 index 0000000000..f325937cb9 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.3mm.inst.cfg @@ -0,0 +1,36 @@ +[general] +definition = ultimaker_s8 +name = Extra Fast +version = 4 + +[metadata] +material = generic_bvoh +quality_type = verydraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -3 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_infill_sparse_thickness = 0.3 +support_interface_enable = True +support_z_distance = 0 + diff --git a/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg new file mode 100644 index 0000000000..e26028fdc1 --- /dev/null +++ b/resources/quality/ultimaker_s8/um_s8_bb0.8_bvoh_0.4mm.inst.cfg @@ -0,0 +1,35 @@ +[general] +definition = ultimaker_s8 +name = Sprint +version = 4 + +[metadata] +material = generic_bvoh +quality_type = superdraft +setting_version = 25 +type = quality +variant = BB 0.8 +weight = -4 + +[values] +acceleration_prime_tower = 1500 +acceleration_support = 1500 +brim_replaces_support = False +build_volume_temperature = =70 if extruders_enabled_count > 1 else 35 +cool_fan_enabled = =not (support_enable and (extruder_nr == support_infill_extruder_nr)) +default_material_bed_temperature = =0 if extruders_enabled_count > 1 else 60 +initial_layer_line_width_factor = 150 +jerk_prime_tower = 4000 +jerk_support = 4000 +material_print_temperature = =default_material_print_temperature + 5 +minimum_support_area = 4 +retraction_amount = 6.5 +retraction_count_max = 5 +skirt_brim_minimal_length = =min(2000, 175 / (layer_height * line_width)) +speed_prime_tower = 50 +speed_support = 50 +speed_support_interface = 50 +support_bottom_density = 70 +support_interface_enable = True +support_z_distance = 0 + From f5c91b942cee9dd49f5d4706a92759c8d09ceeee Mon Sep 17 00:00:00 2001 From: HellAholic Date: Fri, 15 Aug 2025 11:03:04 +0200 Subject: [PATCH 73/98] Update um_sketch_sprint_0.4mm_um-pla-175_0.27mm.inst.cfg --- .../um_sketch_sprint_0.4mm_um-pla-175_0.27mm.inst.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.27mm.inst.cfg b/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.27mm.inst.cfg index e88b8d8308..577faaca43 100644 --- a/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.27mm.inst.cfg +++ b/resources/quality/ultimaker_sketch_sprint/um_sketch_sprint_0.4mm_um-pla-175_0.27mm.inst.cfg @@ -13,4 +13,9 @@ variant = 0.4mm weight = -3 [values] +bridge_wall_speed = 40 +cool_min_layer_time_overhang = 12 +cool_min_speed = 40 +wall_overhang_angle = 30 +wall_overhang_speed_factors = [60] From da90d270151418300bde1ebbef6fd98f565ccd19 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Mon, 18 Aug 2025 16:08:32 +0200 Subject: [PATCH 74/98] Set default_value and minimum_value for Inside Travel avoid distance Minimum value is set to half the nozzle width as any lower could potentially cause collision with the printed outer wall Default value is set to 0.6 --- resources/definitions/fdmprinter.def.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 68852d805f..fdb4bc5ebb 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -4712,10 +4712,9 @@ "description": "The distance between the nozzle and already printed outer walls when travelling inside a model.", "unit": "mm", "type": "float", - "default_value": 0, + "default_value": 0.6, "value": "machine_nozzle_size * 1.5", - "minimum_value": "0", - "minimum_value_warning": "machine_nozzle_size * 0.5", + "minimum_value": "machine_nozzle_size * 0.5", "maximum_value_warning": "machine_nozzle_size * 10", "enabled": "resolveOrValue('retraction_combing') != 'off'", "settable_per_mesh": false, From a55cca73f405d1d2b5029379425068532668b320 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Tue, 26 Aug 2025 13:05:56 +0200 Subject: [PATCH 75/98] Apply Review clear_mask -> clear_texture_bit_mask --- plugins/PaintTool/PaintUndoCommand.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PaintTool/PaintUndoCommand.py b/plugins/PaintTool/PaintUndoCommand.py index c100780c7f..50bfb787b7 100644 --- a/plugins/PaintTool/PaintUndoCommand.py +++ b/plugins/PaintTool/PaintUndoCommand.py @@ -40,7 +40,7 @@ class PaintUndoCommand(QUndoCommand): bit_range_start, bit_range_end = self._bit_range full_int32 = 0xffffffff - clear_mask = full_int32 ^ (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> ( + clear_texture_bit_mask = full_int32 ^ (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> ( 32 - 1 - bit_range_end)) image_rect = QRect(0, 0, self._stroke_mask.width(), self._stroke_mask.height()) @@ -48,7 +48,7 @@ class PaintUndoCommand(QUndoCommand): clear_bits_image.invertPixels() painter = QPainter(clear_bits_image) painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Lighten) - painter.fillRect(image_rect, clear_mask) + painter.fillRect(image_rect, clear_texture_bit_mask) painter.end() set_value_image = self._stroke_mask.copy() From cc6083f4fa9567d7d6491f172762b5b57fe91df0 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Fri, 29 Aug 2025 19:23:07 +0200 Subject: [PATCH 76/98] Add theme colors --- resources/themes/cura-light/theme.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 436aaceb3c..d561f7e6dc 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -463,6 +463,8 @@ "layerview_support_infill": [0, 230, 230, 127], "layerview_move_combing": [0, 0, 255, 255], "layerview_move_retraction": [128, 127, 255, 255], + "layerview_move_while_retracting": [127, 255, 255, 255], + "layerview_move_while_unretracting": [255, 127, 255, 255], "layerview_support_interface": [63, 127, 255, 127], "layerview_prime_tower": [0, 255, 255, 255], "layerview_nozzle": [224, 192, 16, 64], From 851544a3c23846149d60d505f2b15a5b5807ace1 Mon Sep 17 00:00:00 2001 From: Paul Kuiper <46715907+pkuiper-ultimaker@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:40:48 +0200 Subject: [PATCH 77/98] Disable the retract during travel settings for the S6 and S8 for now (they first need firmware update to use this). PP-673 --- resources/definitions/ultimaker_s8.def.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/definitions/ultimaker_s8.def.json b/resources/definitions/ultimaker_s8.def.json index cbd6e255f8..b0d11ec463 100644 --- a/resources/definitions/ultimaker_s8.def.json +++ b/resources/definitions/ultimaker_s8.def.json @@ -384,6 +384,7 @@ "unit": "m/s\u00b3", "value": "jerk_wall_0" }, + "keep_retracting_during_travel": { "enabled": false }, "machine_gcode_flavor": { "default_value": "Cheetah" }, "machine_max_acceleration_x": { "default_value": 50000 }, "machine_max_acceleration_y": { "default_value": 50000 }, @@ -433,12 +434,14 @@ "meshfix_maximum_resolution": { "value": 0.4 }, "min_infill_area": { "default_value": 10 }, "optimize_wall_printing_order": { "value": false }, + "prime_during_travel_ratio": { "enabled": false }, "prime_tower_brim_enable": { "value": true }, "prime_tower_min_volume": { "value": 10 }, "prime_tower_mode": { "resolve": "'normal'" }, "retraction_amount": { "value": 6.5 }, "retraction_combing_avoid_distance": { "value": 1.2 }, "retraction_combing_max_distance": { "value": 50 }, + "retraction_during_travel_ratio": { "enabled": false }, "retraction_hop": { "value": 1 }, "retraction_hop_after_extruder_switch_height": { "value": 2 }, "retraction_hop_enabled": { "value": true }, From 969cbf09e3c7ce62b1df80925f673074f36efa1e Mon Sep 17 00:00:00 2001 From: THeijmans Date: Thu, 21 Aug 2025 15:22:59 +0200 Subject: [PATCH 78/98] PP-650 R3 --- resources/definitions/ultimaker_s8.def.json | 11 ++++--- ...aa_plus_0.4_abs_0.2mm_engineering.inst.cfg | 3 ++ ...um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg | 5 +++ ...us_0.4_cpe-plus_0.2mm_engineering.inst.cfg | 3 ++ ...aa_plus_0.4_cpe_0.2mm_engineering.inst.cfg | 3 ++ ..._aa_plus_0.4_pc_0.2mm_engineering.inst.cfg | 3 ++ ...a_plus_0.4_petg_0.2mm_engineering.inst.cfg | 3 ++ ...m_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg | 5 +++ ...aa_plus_0.4_pla_0.2mm_engineering.inst.cfg | 3 ++ ...um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg | 5 +++ ...s_0.4_tough-pla_0.2mm_engineering.inst.cfg | 3 ++ ...aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg | 5 +++ ...us_0.4_cpe-plus_0.2mm_engineering.inst.cfg | 3 ++ ..._nylon-cf-slide_0.2mm_engineering.inst.cfg | 3 ++ ..._cc_plus_0.4_pc_0.2mm_engineering.inst.cfg | 3 ++ ..._plus_0.4_petcf_0.2mm_engineering.inst.cfg | 3 ++ ..._nylon-cf-slide_0.2mm_engineering.inst.cfg | 3 ++ ..._plus_0.6_petcf_0.2mm_engineering.inst.cfg | 3 ++ .../um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg | 3 ++ .../um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg | 3 ++ .../um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg | 3 ++ .../um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg | 3 ++ .../um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg | 3 ++ ...m_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg | 3 ++ ...um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg | 3 ++ ...um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg | 3 ++ ..._cc_plus_0.4_nylon-cf-slide_0.2mm.inst.cfg | 31 ++++++++++++++++++- .../um_s8_cc_plus_0.4_petcf_0.2mm.inst.cfg | 31 +++++++++++++++++++ 28 files changed, 151 insertions(+), 5 deletions(-) diff --git a/resources/definitions/ultimaker_s8.def.json b/resources/definitions/ultimaker_s8.def.json index cbd6e255f8..48f89a5502 100644 --- a/resources/definitions/ultimaker_s8.def.json +++ b/resources/definitions/ultimaker_s8.def.json @@ -237,7 +237,9 @@ "default_material_print_temperature": { "maximum_value_warning": 320 }, "extra_infill_lines_to_support_skins": { "value": "'walls_and_lines'" }, "flooring_layer_count": { "value": 1 }, - "gradual_flow_enabled": { "value": false }, + "flooring_material_flow": { "value": "skin_material_flow * 110/93" }, + "gradual_flow_discretisation_step_size": { "value": 1 }, + "gradual_flow_enabled": { "value": true }, "hole_xy_offset": { "value": 0.075 }, "infill_material_flow": { "value": "material_flow if infill_sparse_density < 95 else 95" }, "infill_overlap": { "value": 10 }, @@ -428,7 +430,7 @@ }, "material_print_temperature": { "maximum_value_warning": 320 }, "material_print_temperature_layer_0": { "maximum_value_warning": 320 }, - "max_flow_acceleration": { "value": 8.0 }, + "max_flow_acceleration": { "value": 1.5 }, "max_skin_angle_for_expansion": { "value": 45 }, "meshfix_maximum_resolution": { "value": 0.4 }, "min_infill_area": { "default_value": 10 }, @@ -444,6 +446,7 @@ "retraction_hop_enabled": { "value": true }, "retraction_min_travel": { "value": "2.5 if support_enable and support_structure=='tree' else line_width * 2.5" }, "retraction_prime_speed": { "value": 15 }, + "seam_overhang_angle": { "value": 35 }, "skin_edge_support_thickness": { "value": 0 }, "skin_material_flow": { "value": 93 }, "skin_outline_count": { "value": 0 }, @@ -543,12 +546,12 @@ "speed_wall": { "maximum_value_warning": 300, - "value": "speed_print*2/3" + "value": "speed_print*1/2" }, "speed_wall_0": { "maximum_value_warning": 300, - "value": "speed_wall" + "value": "speed_wall*60/75" }, "speed_wall_0_flooring": { diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_engineering.inst.cfg index 1d1cae70e6..7bbf373b2e 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg index 9889424797..fe4b0497e1 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm_quick.inst.cfg @@ -16,7 +16,12 @@ cool_min_layer_time = 5 cool_min_layer_time_overhang = 9 cool_min_speed = 6 cool_min_temperature = =material_print_temperature - 15 +gradual_flow_enable = False +hole_xy_offset = 0.075 +inset_direction = outside-in +speed_wall = =speed_print speed_wall_x = =speed_print speed_wall_x_roofing = =speed_wall wall_line_width_x = =wall_line_width +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg index 21e814e112..d01f907d33 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe_0.2mm_engineering.inst.cfg index f52cdf8124..7a96aa3f79 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_cpe_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pc_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pc_0.2mm_engineering.inst.cfg index 8332ecacbc..1b666e4d83 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pc_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pc_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_engineering.inst.cfg index 4c4490876f..3dd4ef5f01 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg index ae38c02395..0fe5944438 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm_quick.inst.cfg @@ -16,7 +16,12 @@ cool_min_layer_time = 5 cool_min_layer_time_overhang = 9 cool_min_speed = 6 cool_min_temperature = =material_print_temperature - 15 +gradual_flow_enable = False +hole_xy_offset = 0.075 +inset_direction = outside-in +speed_wall = =speed_print speed_wall_x = =speed_print speed_wall_x_roofing = =speed_wall wall_line_width_x = =wall_line_width +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_engineering.inst.cfg index 484b9536a6..d2bbcf226e 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg index 4509317076..a4730213de 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm_quick.inst.cfg @@ -16,7 +16,12 @@ cool_min_layer_time = 5 cool_min_layer_time_overhang = 9 cool_min_speed = 6 cool_min_temperature = =material_print_temperature - 15 +gradual_flow_enable = False +hole_xy_offset = 0.075 +inset_direction = outside-in +speed_wall = =speed_print speed_wall_x = =speed_print speed_wall_x_roofing = =speed_wall wall_line_width_x = =wall_line_width +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_engineering.inst.cfg index c5b81e4a4c..ed68baf7b1 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = AA+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg index 4f75b631df..908ecae7ed 100644 --- a/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm_quick.inst.cfg @@ -16,7 +16,12 @@ cool_min_layer_time = 5 cool_min_layer_time_overhang = 9 cool_min_speed = 6 cool_min_temperature = =material_print_temperature - 15 +gradual_flow_enable = False +hole_xy_offset = 0.075 +inset_direction = outside-in +speed_wall = =speed_print speed_wall_x = =speed_print speed_wall_x_roofing = =speed_wall wall_line_width_x = =wall_line_width +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg index 832912a022..0252cd6eb5 100644 --- a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_cpe-plus_0.2mm_engineering.inst.cfg @@ -13,7 +13,10 @@ type = intent variant = CC+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm_engineering.inst.cfg index a0e65969e9..2ce40b3df1 100644 --- a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm_engineering.inst.cfg @@ -13,7 +13,10 @@ type = intent variant = CC+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_pc_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_pc_0.2mm_engineering.inst.cfg index f40d2509b6..cb3c7bbac2 100644 --- a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_pc_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_pc_0.2mm_engineering.inst.cfg @@ -13,7 +13,10 @@ type = intent variant = CC+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm_engineering.inst.cfg index 824018a1d5..706c77e91a 100644 --- a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm_engineering.inst.cfg @@ -13,7 +13,10 @@ type = intent variant = CC+ 0.4 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_nylon-cf-slide_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_nylon-cf-slide_0.2mm_engineering.inst.cfg index 8cbb513108..57dfaa8f45 100644 --- a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_nylon-cf-slide_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_nylon-cf-slide_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = CC+ 0.6 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_petcf_0.2mm_engineering.inst.cfg b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_petcf_0.2mm_engineering.inst.cfg index 5384f380ac..9f2ddc6be5 100644 --- a/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_petcf_0.2mm_engineering.inst.cfg +++ b/resources/intent/ultimaker_s8/um_s8_cc_plus_0.6_petcf_0.2mm_engineering.inst.cfg @@ -12,7 +12,10 @@ type = intent variant = CC+ 0.6 [values] +hole_xy_offset = 0.075 infill_sparse_density = 20 +inset_direction = outside-in top_bottom_thickness = =wall_thickness wall_thickness = =line_width * 4 +xy_offset = 0.075 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg index cc5e850220..23863493e9 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg @@ -15,9 +15,12 @@ weight = -2 cool_min_layer_time = 4 cool_min_layer_time_fan_speed_max = 9 cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out retraction_prime_speed = 15 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg index 9abcd5ddd2..c739be3112 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg @@ -13,9 +13,12 @@ weight = -2 [values] cool_min_layer_time = 4 +hole_xy_offset = 0.1 +inset_direction = inside-out material_print_temperature = =default_material_print_temperature + 5 retraction_prime_speed = 15 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg index 9feab61e0e..e206c6ca7c 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg @@ -13,6 +13,8 @@ weight = -1 [values] cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed @@ -20,4 +22,5 @@ speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg index 8431bb9c43..19575814fd 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg @@ -13,6 +13,8 @@ weight = 0 [values] cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed @@ -21,4 +23,5 @@ speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree top_bottom_thickness = =round(6*layer_height,3) wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg index 7a5d19dc2c..72132baab2 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg @@ -13,6 +13,8 @@ weight = -2 [values] cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed @@ -20,4 +22,5 @@ speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg index f17d3fde40..c7daafbcf6 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg @@ -13,10 +13,13 @@ weight = -1 [values] cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out retraction_prime_speed = =retraction_speed retraction_speed = 25 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg index 672eae3e4a..ffcdbf79e3 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg @@ -13,10 +13,13 @@ weight = 0 [values] cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out retraction_prime_speed = =retraction_speed speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree top_bottom_thickness = =round(6*layer_height,3) wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg index 716765aac5..9dc8f752d8 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg @@ -13,9 +13,12 @@ weight = -2 [values] cool_min_temperature = =material_print_temperature - 20 +hole_xy_offset = 0.1 +inset_direction = inside-out retraction_prime_speed = =retraction_speed speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree wall_line_width_x = =wall_line_width * 1.25 +xy_offset = 0.025 diff --git a/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm.inst.cfg index 5f3180ef4e..04249a0af3 100644 --- a/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_nylon-cf-slide_0.2mm.inst.cfg @@ -13,12 +13,41 @@ variant = CC+ 0.4 weight = -2 [values] +acceleration_roofing = =acceleration_topbottom/2 +bridge_enable_more_layers = True +bridge_skin_density = 70 bridge_skin_material_flow = 100 +bridge_skin_material_flow_2 = 70 bridge_skin_speed = 30 +bridge_skin_speed_2 = =speed_print*2/3 bridge_wall_material_flow = 100 +bridge_wall_min_length = 2 bridge_wall_speed = 30 +cool_min_layer_time = 6 cool_min_layer_time_fan_speed_max = 11 -retraction_prime_speed = 15 +cool_min_layer_time_overhang = 11 +cool_min_temperature = =material_print_temperature-10 +flooring_monotonic = False +infill_material_flow = =material_flow if infill_sparse_density < 95 else 95 +infill_pattern = ='zigzag' if infill_sparse_density > 50 else 'grid' +jerk_roofing = =jerk_print +material_flow = 95 +retraction_hop_enabled = False +retraction_prime_speed = 25 +roofing_material_flow = =skin_material_flow +roofing_monotonic = False +skin_material_flow = =0.95*material_flow +skin_outline_count = 0 +support_bottom_distance = =support_z_distance support_structure = tree +support_tree_tip_diameter = 2.0 +support_tree_top_rate = 10 +support_xy_distance = 1.2 +support_xy_distance_overhang = =1.5*machine_nozzle_size +support_z_distance = =min(2*layer_height, 0.4) +top_bottom_thickness = =wall_thickness +wall_0_inset = =0.05 wall_overhang_speed_factors = [100,90,80,70,60,50] +wall_x_material_flow = =material_flow +xy_offset = 0.075 diff --git a/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm.inst.cfg index 3467ed5ded..664748835f 100644 --- a/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_cc_plus_0.4_petcf_0.2mm.inst.cfg @@ -13,12 +13,43 @@ variant = CC+ 0.4 weight = -2 [values] +acceleration_roofing = =acceleration_topbottom/2 adhesion_type = skirt +bridge_enable_more_layers = True +bridge_skin_density = 70 bridge_skin_material_flow = 100 +bridge_skin_material_flow_2 = 70 bridge_skin_speed = 30 +bridge_skin_speed_2 = =speed_print*2/3 bridge_wall_material_flow = 100 +bridge_wall_min_length = 2 bridge_wall_speed = 30 +cool_min_layer_time = 6 +cool_min_layer_time_overhang = 11 +cool_min_temperature = =material_print_temperature-10 +flooring_monotonic = False +infill_material_flow = =material_flow if infill_sparse_density < 95 else 95 +infill_pattern = ='zigzag' if infill_sparse_density > 50 else 'grid' +jerk_roofing = =jerk_print +material_pressure_advance_factor = 0.25 +retraction_hop_enabled = False +retraction_prime_speed = 15 +roofing_material_flow = =skin_material_flow +roofing_monotonic = False +skin_material_flow = =0.95*material_flow +skin_outline_count = 0 +skirt_height = 5 +support_bottom_distance = =support_z_distance support_structure = tree +support_tree_tip_diameter = 2.0 +support_tree_top_rate = 10 +support_xy_distance = 1.2 +support_xy_distance_overhang = =1.5*machine_nozzle_size +support_z_distance = =min(2*layer_height, 0.4) switch_extruder_retraction_amount = 16 +top_bottom_thickness = =wall_thickness +wall_0_inset = =0.05 wall_overhang_speed_factors = [100,90,80,70,60,50] +wall_x_material_flow = =material_flow +xy_offset = 0.075 From 28f2e4fb499b47fbe28f2899400a296f23e0cd7f Mon Sep 17 00:00:00 2001 From: THeijmans Date: Fri, 29 Aug 2025 14:36:59 +0200 Subject: [PATCH 79/98] PP-650 R5 --- resources/definitions/ultimaker_s8.def.json | 5 ++++- .../ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg | 1 + .../ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg | 1 + 9 files changed, 12 insertions(+), 1 deletion(-) diff --git a/resources/definitions/ultimaker_s8.def.json b/resources/definitions/ultimaker_s8.def.json index 48f89a5502..165a7719b2 100644 --- a/resources/definitions/ultimaker_s8.def.json +++ b/resources/definitions/ultimaker_s8.def.json @@ -238,6 +238,7 @@ "extra_infill_lines_to_support_skins": { "value": "'walls_and_lines'" }, "flooring_layer_count": { "value": 1 }, "flooring_material_flow": { "value": "skin_material_flow * 110/93" }, + "flooring_monotonic": { "value": false }, "gradual_flow_discretisation_step_size": { "value": 1 }, "gradual_flow_enabled": { "value": true }, "hole_xy_offset": { "value": 0.075 }, @@ -446,11 +447,13 @@ "retraction_hop_enabled": { "value": true }, "retraction_min_travel": { "value": "2.5 if support_enable and support_structure=='tree' else line_width * 2.5" }, "retraction_prime_speed": { "value": 15 }, + "roofing_monotonic": { "value": false }, + "roofing_pattern": { "value": "'zigzag'" }, "seam_overhang_angle": { "value": 35 }, "skin_edge_support_thickness": { "value": 0 }, "skin_material_flow": { "value": 93 }, "skin_outline_count": { "value": 0 }, - "skin_overlap": { "value": 0 }, + "skin_overlap": { "value": 20 }, "skin_preshrink": { "value": 0 }, "skirt_brim_minimal_length": { "value": 1000 }, "skirt_brim_speed": diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg index 23863493e9..014a2012c8 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_abs_0.2mm.inst.cfg @@ -18,6 +18,7 @@ cool_min_temperature = =material_print_temperature - 20 hole_xy_offset = 0.1 inset_direction = inside-out retraction_prime_speed = 15 +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg index c739be3112..9e9320d45d 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_petg_0.2mm.inst.cfg @@ -17,6 +17,7 @@ hole_xy_offset = 0.1 inset_direction = inside-out material_print_temperature = =default_material_print_temperature + 5 retraction_prime_speed = 15 +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 wall_line_width_x = =wall_line_width * 1.25 diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg index e206c6ca7c..a63f0371b4 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.15mm.inst.cfg @@ -18,6 +18,7 @@ inset_direction = inside-out material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg index 19575814fd..b9ed1a649a 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.1mm.inst.cfg @@ -18,6 +18,7 @@ inset_direction = inside-out material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg index 72132baab2..8ab383a08d 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_pla_0.2mm.inst.cfg @@ -18,6 +18,7 @@ inset_direction = inside-out material_final_print_temperature = =material_print_temperature - 15 material_initial_print_temperature = =material_print_temperature - 15 retraction_prime_speed = =retraction_speed +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg index c7daafbcf6..2f48c95b7b 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.15mm.inst.cfg @@ -17,6 +17,7 @@ hole_xy_offset = 0.1 inset_direction = inside-out retraction_prime_speed = =retraction_speed retraction_speed = 25 +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg index ffcdbf79e3..d32f2466ea 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.1mm.inst.cfg @@ -16,6 +16,7 @@ cool_min_temperature = =material_print_temperature - 20 hole_xy_offset = 0.1 inset_direction = inside-out retraction_prime_speed = =retraction_speed +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree diff --git a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg index 9dc8f752d8..a7eebb69be 100644 --- a/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg +++ b/resources/quality/ultimaker_s8/um_s8_aa_plus_0.4_tough-pla_0.2mm.inst.cfg @@ -16,6 +16,7 @@ cool_min_temperature = =material_print_temperature - 20 hole_xy_offset = 0.1 inset_direction = inside-out retraction_prime_speed = =retraction_speed +speed_roofing = =speed_topbottom * 1/3 speed_wall_x = =speed_wall speed_wall_x_roofing = =speed_wall * 0.8 support_structure = tree From 066ac3eb20052a16afd528de6c987278ea0d9c62 Mon Sep 17 00:00:00 2001 From: THeijmans Date: Mon, 8 Sep 2025 13:22:01 +0200 Subject: [PATCH 80/98] PP-664 reduce z-resonances in F4 print process --- resources/definitions/ultimaker_factor4.def.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/definitions/ultimaker_factor4.def.json b/resources/definitions/ultimaker_factor4.def.json index 710ee29a40..483300d878 100644 --- a/resources/definitions/ultimaker_factor4.def.json +++ b/resources/definitions/ultimaker_factor4.def.json @@ -86,6 +86,7 @@ "gantry_height": { "value": 35 }, "gradual_support_infill_steps": { "value": "3 if support_interface_enable and support_structure != 'tree' else 0" }, "group_outer_walls": { "value": "False" }, + "infill_angles": { "value": "[-40, 50] if infill_pattern in ('grid', 'lines', 'zigzag') else [ ]" }, "infill_before_walls": { "value": "False if infill_sparse_density > 50 else True" }, "infill_enable_travel_optimization": { "value": "True" }, "infill_material_flow": @@ -94,7 +95,7 @@ "value": "(1 + (skin_material_flow-infill_sparse_density) / 100 if infill_sparse_density > skin_material_flow else 1) * material_flow" }, "infill_overlap": { "value": "0" }, - "infill_pattern": { "value": "'zigzag' if infill_sparse_density > 50 else 'triangles'" }, + "infill_pattern": { "value": "'zigzag' if infill_sparse_density > 50 else 'gyroid' if 15 < speed_infill / ( infill_line_width * 300 / infill_sparse_density ) < 25 else 'triangles'" }, "infill_sparse_density": { "maximum_value": "100" }, "infill_wipe_dist": { "value": "0" }, "inset_direction": { "value": "'inside_out'" }, @@ -199,6 +200,7 @@ "value": "skin_material_flow" }, "roofing_monotonic": { "value": "True" }, + "skin_angles": { "value": "[-40, 50]" }, "skin_material_flow": { "maximum_value": "100", From 34eac462bd5655e2b8075966afab6cd46fd55973 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 23 Jul 2025 16:58:09 +0200 Subject: [PATCH 81/98] More authentication, since printer-API call-responses can include user-info. The new regulations make a decent amount of sense -- but just because we agree with them doesn't mean we'd implemented this yet. Anyway, information wich can be used to personally identify people should be kept behind (virtual) locks and bars. The new firmware will only allow certain operations _after_ a request has been made to the .../auth/request endpoint, and someone in the physical vicinity (of the printer) has pressed ALLOW on a popup (with the application and name of the requester shown, on the printers' UI). After that, _as long as you put the relevant Authorization Digest in your HTTP headers_ (and use at least SHA-256), you may proceed to make other requests without the printer-server flipping out with a FORBIDDEN error. The current commit _should_ also still work with printers that still have old (well, current I guess...) firmware -- but I didn't test that yet. CURA-12624 --- .../src/Network/ClusterApiClient.py | 90 +++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index fd8118306b..a1f7a47da6 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -1,6 +1,8 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2025 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. +import hashlib import json +import secrets from json import JSONDecodeError from typing import Callable, List, Optional, Dict, Union, Any, Type, cast, TypeVar, Tuple @@ -9,6 +11,8 @@ from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkRepl from UM.Logger import Logger +from cura.CuraApplication import CuraApplication + from ..Models.BaseModel import BaseModel from ..Models.Http.ClusterPrintJobStatus import ClusterPrintJobStatus from ..Models.Http.ClusterPrinterStatus import ClusterPrinterStatus @@ -27,6 +31,14 @@ class ClusterApiClient: PRINTER_API_PREFIX = "/api/v1" CLUSTER_API_PREFIX = "/cluster-api/v1" + AUTH_REALM = "Jedi-API" + AUTH_QOP = "auth" + AUTH_NC = "00000001" + AUTH_NONCE_LEN = 16 + AUTH_CNONCE_LEN = 8 + + AUTH_MAX_TRIES = 5 + # In order to avoid garbage collection we keep the callbacks in this list. _anti_gc_callbacks = [] # type: List[Callable[[], None]] @@ -40,6 +52,8 @@ class ClusterApiClient: self._manager = QNetworkAccessManager() self._address = address self._on_error = on_error + self._auth_info = None + self._auth_tries = 0 def getSystem(self, on_finished: Callable) -> None: """Get printer system information. @@ -81,13 +95,13 @@ class ClusterApiClient: """Move a print job to the top of the queue.""" url = "{}/print_jobs/{}/action/move".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.post(self._createEmptyRequest(url), json.dumps({"to_position": 0, "list": "queued"}).encode()) + self._manager.post(self._createEmptyRequest(url, method="POST"), json.dumps({"to_position": 0, "list": "queued"}).encode()) def forcePrintJob(self, print_job_uuid: str) -> None: """Override print job configuration and force it to be printed.""" url = "{}/print_jobs/{}".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.put(self._createEmptyRequest(url), json.dumps({"force": True}).encode()) + self._manager.put(self._createEmptyRequest(url, method="PUT"), json.dumps({"force": True}).encode()) def deletePrintJob(self, print_job_uuid: str) -> None: """Delete a print job from the queue.""" @@ -101,7 +115,7 @@ class ClusterApiClient: url = "{}/print_jobs/{}/action".format(self.CLUSTER_API_PREFIX, print_job_uuid) # We rewrite 'resume' to 'print' here because we are using the old print job action endpoints. action = "print" if state == "resume" else state - self._manager.put(self._createEmptyRequest(url), json.dumps({"action": action}).encode()) + self._manager.put(self._createEmptyRequest(url, method="PUT"), json.dumps({"action": action}).encode()) def getPrintJobPreviewImage(self, print_job_uuid: str, on_finished: Callable) -> None: """Get the preview image data of a print job.""" @@ -110,16 +124,23 @@ class ClusterApiClient: reply = self._manager.get(self._createEmptyRequest(url)) self._addCallback(reply, on_finished) - def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json", method: str = "GET", skip_auth: bool = False) -> QNetworkRequest: """We override _createEmptyRequest in order to add the user credentials. :param url: The URL to request :param content_type: The type of the body contents. + :param method: The HTTP method to use, such as GET, POST, PUT, etc. + :param skip_auth: Skips the authentication step if set; prevents a loop on request of authentication token. """ url = QUrl("http://" + self._address + path) request = QNetworkRequest(url) if content_type: request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type) + if self._auth_info: + digest_str = self._makeAuthDigestHeaderPart(path, method=method) + request.setRawHeader(b"Authorization", f"Digest {digest_str}".encode("utf-8")) + elif not skip_auth: + self._setupAuth() return request @staticmethod @@ -158,6 +179,65 @@ class ClusterApiClient: except (JSONDecodeError, TypeError, ValueError): Logger.log("e", "Could not parse response from network: %s", str(response)) + def _makeAuthDigestHeaderPart(self, url_part: str, method: str = "GET") -> str: + """ Make the data-part for a Digest Authentication HTTP-header. + + :param url_part: The part of the URL beyond the host name. + :param method: The HTTP method to use, such as GET, POST, PUT, etc. + :return: A string with the data, can be used as in `f"Digest {return_value}".encode()`. + """ + + def sha256_utf8(x: str) -> str: + return hashlib.sha256(x.encode("utf-8")).hexdigest() + + nonce = secrets.token_hex(ClusterApiClient.AUTH_NONCE_LEN) + cnonce = secrets.token_hex(ClusterApiClient.AUTH_CNONCE_LEN) + + ha1 = sha256_utf8(f"{self._auth_info["id"]}:{ClusterApiClient.AUTH_REALM}:{self._auth_info["key"]}") + ha2 = sha256_utf8(f"{method}:{url_part}") + resp_digest = sha256_utf8(f"{ha1}:{nonce}:{ClusterApiClient.AUTH_NC}:{cnonce}:{ClusterApiClient.AUTH_QOP}:{ha2}") + return ", ".join([ + f'username="{self._auth_info["id"]}"', + f'realm="{ClusterApiClient.AUTH_REALM}"', + f'nonce="{nonce}"', + f'uri="{url_part}"', + f'nc={ClusterApiClient.AUTH_NC}', + f'cnonce="{cnonce}"', + f'qop={ClusterApiClient.AUTH_QOP}', + f'response="{resp_digest}"', + f'algorithm="SHA-256"' + ]) + + def _setupAuth(self) -> None: + """ Handles the setup process for authentication by making a temporary digest-token request to the printer API. + """ + + if self._auth_tries >= ClusterApiClient.AUTH_MAX_TRIES: + Logger.warning("Maximum authorization temporary digest-token request tries exceeded. Is printer-firmware up to date?") + return + + username = CuraApplication.getInstance().getCuraAPI().account.userName + if (not username) or username == "": + return + + def on_finished(resp) -> None: + self._auth_tries += 1 + try: + self._auth_info = json.loads(resp.data().decode()) + except Exception as ex: + Logger.warning(f"Couldn't get temporary digest token: {str(ex)}") + return + self._auth_tries = 0 + + url = "{}/auth/request".format(self.PRINTER_API_PREFIX) + request_body = json.dumps({ + "application": CuraApplication.getInstance().getApplicationDisplayName(), + "user": username, + }).encode("utf-8") + reply = self._manager.post(self._createEmptyRequest(url, method="POST", skip_auth=True), request_body) + + self._addCallback(reply, on_finished) + def _addCallback(self, reply: QNetworkReply, on_finished: Union[Callable[[ClusterApiClientModel], Any], Callable[[List[ClusterApiClientModel]], Any]], model: Type[ClusterApiClientModel] = None, ) -> None: From 115d2d5b775901a966aa66ff0ae1393acb4dab48 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 23 Jul 2025 18:19:17 +0200 Subject: [PATCH 82/98] Fix 2 calls w.r.t. new authorization workflow _outside_ of the API. New rules means we have to put printjobs and such behind a little authentication, as these contain personally identifiable info. These two effected calls where found _outside_ of the API class where I thought to be able to fix it 100%. See also the TODO's in the neighbourhood -- but I'm not sure I can just do what those say (move the relevant methods to the API), as those methods to be moved are _inside_ the larger Cura SDK (and they're public) and the place where I'm meant to move them to (the ClusterAPIClient) is _not_ (as they're in a plugin). part of CURA-12624 --- .../NetworkedPrinterOutputDevice.py | 6 +++-- .../src/Network/ClusterApiClient.py | 22 +++++++++---------- .../src/Network/LocalClusterOutputDevice.py | 3 ++- .../src/Network/SendMaterialJob.py | 3 ++- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 1d0be1389e..0eb55d81c5 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -288,9 +288,11 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Optional[Callable[[int, int], None]] = None) -> QNetworkReply: + on_progress: Optional[Callable[[int, int], None]] = None, + request: Optional[QNetworkRequest] = None) -> QNetworkReply: self._validateManager() - request = self._createEmptyRequest(target, content_type=None) + if request is None: + request = self._createEmptyRequest(target, content_type=None) multi_post_part = QHttpMultiPart(QHttpMultiPart.ContentType.FormDataType) for part in parts: multi_post_part.append(part) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index a1f7a47da6..ed92b4aafe 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -61,7 +61,7 @@ class ClusterApiClient: :param on_finished: The callback in case the response is successful. """ url = "{}/system".format(self.PRINTER_API_PREFIX) - reply = self._manager.get(self._createEmptyRequest(url)) + reply = self._manager.get(self.createEmptyRequest(url)) self._addCallback(reply, on_finished, PrinterSystemStatus) def getMaterials(self, on_finished: Callable[[List[ClusterMaterial]], Any]) -> None: @@ -70,7 +70,7 @@ class ClusterApiClient: :param on_finished: The callback in case the response is successful. """ url = "{}/materials".format(self.CLUSTER_API_PREFIX) - reply = self._manager.get(self._createEmptyRequest(url)) + reply = self._manager.get(self.createEmptyRequest(url)) self._addCallback(reply, on_finished, ClusterMaterial) def getPrinters(self, on_finished: Callable[[List[ClusterPrinterStatus]], Any]) -> None: @@ -79,7 +79,7 @@ class ClusterApiClient: :param on_finished: The callback in case the response is successful. """ url = "{}/printers".format(self.CLUSTER_API_PREFIX) - reply = self._manager.get(self._createEmptyRequest(url)) + reply = self._manager.get(self.createEmptyRequest(url)) self._addCallback(reply, on_finished, ClusterPrinterStatus) def getPrintJobs(self, on_finished: Callable[[List[ClusterPrintJobStatus]], Any]) -> None: @@ -88,26 +88,26 @@ class ClusterApiClient: :param on_finished: The callback in case the response is successful. """ url = "{}/print_jobs".format(self.CLUSTER_API_PREFIX) - reply = self._manager.get(self._createEmptyRequest(url)) + reply = self._manager.get(self.createEmptyRequest(url)) self._addCallback(reply, on_finished, ClusterPrintJobStatus) def movePrintJobToTop(self, print_job_uuid: str) -> None: """Move a print job to the top of the queue.""" url = "{}/print_jobs/{}/action/move".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.post(self._createEmptyRequest(url, method="POST"), json.dumps({"to_position": 0, "list": "queued"}).encode()) + self._manager.post(self.createEmptyRequest(url, method="POST"), json.dumps({"to_position": 0, "list": "queued"}).encode()) def forcePrintJob(self, print_job_uuid: str) -> None: """Override print job configuration and force it to be printed.""" url = "{}/print_jobs/{}".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.put(self._createEmptyRequest(url, method="PUT"), json.dumps({"force": True}).encode()) + self._manager.put(self.createEmptyRequest(url, method="PUT"), json.dumps({"force": True}).encode()) def deletePrintJob(self, print_job_uuid: str) -> None: """Delete a print job from the queue.""" url = "{}/print_jobs/{}".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.deleteResource(self._createEmptyRequest(url)) + self._manager.deleteResource(self.createEmptyRequest(url)) def setPrintJobState(self, print_job_uuid: str, state: str) -> None: """Set the state of a print job.""" @@ -115,16 +115,16 @@ class ClusterApiClient: url = "{}/print_jobs/{}/action".format(self.CLUSTER_API_PREFIX, print_job_uuid) # We rewrite 'resume' to 'print' here because we are using the old print job action endpoints. action = "print" if state == "resume" else state - self._manager.put(self._createEmptyRequest(url, method="PUT"), json.dumps({"action": action}).encode()) + self._manager.put(self.createEmptyRequest(url, method="PUT"), json.dumps({"action": action}).encode()) def getPrintJobPreviewImage(self, print_job_uuid: str, on_finished: Callable) -> None: """Get the preview image data of a print job.""" url = "{}/print_jobs/{}/preview_image".format(self.CLUSTER_API_PREFIX, print_job_uuid) - reply = self._manager.get(self._createEmptyRequest(url)) + reply = self._manager.get(self.createEmptyRequest(url)) self._addCallback(reply, on_finished) - def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json", method: str = "GET", skip_auth: bool = False) -> QNetworkRequest: + def createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json", method: str = "GET", skip_auth: bool = False) -> QNetworkRequest: """We override _createEmptyRequest in order to add the user credentials. :param url: The URL to request @@ -234,7 +234,7 @@ class ClusterApiClient: "application": CuraApplication.getInstance().getApplicationDisplayName(), "user": username, }).encode("utf-8") - reply = self._manager.post(self._createEmptyRequest(url, method="POST", skip_auth=True), request_body) + reply = self._manager.post(self.createEmptyRequest(url, method="POST", skip_auth=True), request_body) self._addCallback(reply, on_finished) diff --git a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py index 2a57bd0321..f9e0b95b59 100644 --- a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py @@ -204,7 +204,8 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice): parts.append(self._createFormPart("name=require_printer_name", bytes(unique_name, "utf-8"), "text/plain")) # FIXME: move form posting to API client self.postFormWithParts("/cluster-api/v1/print_jobs/", parts, on_finished=self._onPrintUploadCompleted, - on_progress=self._onPrintJobUploadProgress) + on_progress=self._onPrintJobUploadProgress, + request=self._cluster_api.createEmptyRequest("/cluster-api/v1/print_jobs/", content_type=None, method="POST")) self._active_exported_job = None def _onPrintJobUploadProgress(self, bytes_sent: int, bytes_total: int) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py index 9f5484ba15..2f3fb9ff19 100644 --- a/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py @@ -147,7 +147,8 @@ class SendMaterialJob(Job): # FIXME: move form posting to API client self.device.postFormWithParts(target = "/cluster-api/v1/materials/", parts = parts, - on_finished = self._sendingFinished) + on_finished = self._sendingFinished, + request=self._cluster_api.createEmptyRequest("/cluster-api/v1/materials/", content_type=None, method="POST")) def _sendingFinished(self, reply: QNetworkReply) -> None: """Check a reply from an upload to the printer and log an error when the call failed""" From 7f35a5074b3dc3f417cc7a5f5cd311bbd120b8da Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 23 Jul 2025 20:25:17 +0200 Subject: [PATCH 83/98] Make authentication info a little less brittle. Otherwise if the server (on the printer) gives back something that can be parsed into JSON, but _isn't_ the authorization digest info, the thing breaks. part of CURA-12624 --- .../src/Network/ClusterApiClient.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index ed92b4aafe..c7562db96b 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -52,7 +52,8 @@ class ClusterApiClient: self._manager = QNetworkAccessManager() self._address = address self._on_error = on_error - self._auth_info = None + self._auth_id = None + self._auth_key = None self._auth_tries = 0 def getSystem(self, on_finished: Callable) -> None: @@ -136,7 +137,7 @@ class ClusterApiClient: request = QNetworkRequest(url) if content_type: request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type) - if self._auth_info: + if self._auth_id and self._auth_key: digest_str = self._makeAuthDigestHeaderPart(path, method=method) request.setRawHeader(b"Authorization", f"Digest {digest_str}".encode("utf-8")) elif not skip_auth: @@ -193,11 +194,11 @@ class ClusterApiClient: nonce = secrets.token_hex(ClusterApiClient.AUTH_NONCE_LEN) cnonce = secrets.token_hex(ClusterApiClient.AUTH_CNONCE_LEN) - ha1 = sha256_utf8(f"{self._auth_info["id"]}:{ClusterApiClient.AUTH_REALM}:{self._auth_info["key"]}") + ha1 = sha256_utf8(f"{self._auth_id}:{ClusterApiClient.AUTH_REALM}:{self._auth_key}") ha2 = sha256_utf8(f"{method}:{url_part}") resp_digest = sha256_utf8(f"{ha1}:{nonce}:{ClusterApiClient.AUTH_NC}:{cnonce}:{ClusterApiClient.AUTH_QOP}:{ha2}") return ", ".join([ - f'username="{self._auth_info["id"]}"', + f'username="{self._auth_id}"', f'realm="{ClusterApiClient.AUTH_REALM}"', f'nonce="{nonce}"', f'uri="{url_part}"', @@ -223,7 +224,9 @@ class ClusterApiClient: def on_finished(resp) -> None: self._auth_tries += 1 try: - self._auth_info = json.loads(resp.data().decode()) + auth_info = json.loads(resp.data().decode()) + self._auth_id = auth_info["id"] + self._auth_key = auth_info["key"] except Exception as ex: Logger.warning(f"Couldn't get temporary digest token: {str(ex)}") return From 75fc0782da38f591796a2885a29c4ecaedbd9a0d Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 29 Jul 2025 11:00:15 +0200 Subject: [PATCH 84/98] Code review: Replace string with enum. First use of 3.11's StrEnum in the code base I think -- anyway, Python autoboxes these (maybe even the old str,enum things as well, but irrelevant now), so there's nothing in the way of making this an enum and have type-_checking_ instead of type-_o_'s. done as part of CURA-12624 --- .../src/Network/ClusterApiClient.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index c7562db96b..5cd8457188 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -3,6 +3,7 @@ import hashlib import json import secrets +from enum import StrEnum from json import JSONDecodeError from typing import Callable, List, Optional, Dict, Union, Any, Type, cast, TypeVar, Tuple @@ -24,6 +25,18 @@ ClusterApiClientModel = TypeVar("ClusterApiClientModel", bound=BaseModel) """The generic type variable used to document the methods below.""" +class HttpRequestMethod(StrEnum): + GET = "GET", + HEAD = "HEAD", + POST = "POST", + PUT = "PUT", + DELETE = "DELETE", + CONNECT = "CONNECT", + OPTIONS = "OPTIONS", + TRACE = "TRACE", + PATCH = "PATCH", + + class ClusterApiClient: """The ClusterApiClient is responsible for all network calls to local network clusters.""" @@ -96,13 +109,13 @@ class ClusterApiClient: """Move a print job to the top of the queue.""" url = "{}/print_jobs/{}/action/move".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.post(self.createEmptyRequest(url, method="POST"), json.dumps({"to_position": 0, "list": "queued"}).encode()) + self._manager.post(self.createEmptyRequest(url, method=HttpRequestMethod.POST), json.dumps({"to_position": 0, "list": "queued"}).encode()) def forcePrintJob(self, print_job_uuid: str) -> None: """Override print job configuration and force it to be printed.""" url = "{}/print_jobs/{}".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.put(self.createEmptyRequest(url, method="PUT"), json.dumps({"force": True}).encode()) + self._manager.put(self.createEmptyRequest(url, method=HttpRequestMethod.PUT), json.dumps({"force": True}).encode()) def deletePrintJob(self, print_job_uuid: str) -> None: """Delete a print job from the queue.""" @@ -116,7 +129,7 @@ class ClusterApiClient: url = "{}/print_jobs/{}/action".format(self.CLUSTER_API_PREFIX, print_job_uuid) # We rewrite 'resume' to 'print' here because we are using the old print job action endpoints. action = "print" if state == "resume" else state - self._manager.put(self.createEmptyRequest(url, method="PUT"), json.dumps({"action": action}).encode()) + self._manager.put(self.createEmptyRequest(url, method=HttpRequestMethod.PUT), json.dumps({"action": action}).encode()) def getPrintJobPreviewImage(self, print_job_uuid: str, on_finished: Callable) -> None: """Get the preview image data of a print job.""" @@ -125,10 +138,10 @@ class ClusterApiClient: reply = self._manager.get(self.createEmptyRequest(url)) self._addCallback(reply, on_finished) - def createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json", method: str = "GET", skip_auth: bool = False) -> QNetworkRequest: + def createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json", method: HttpRequestMethod = HttpRequestMethod.GET, skip_auth: bool = False) -> QNetworkRequest: """We override _createEmptyRequest in order to add the user credentials. - :param url: The URL to request + :param path: Part added to the base-endpoint forming the total request URL (the path from the endpoint to the requested resource). :param content_type: The type of the body contents. :param method: The HTTP method to use, such as GET, POST, PUT, etc. :param skip_auth: Skips the authentication step if set; prevents a loop on request of authentication token. @@ -180,7 +193,7 @@ class ClusterApiClient: except (JSONDecodeError, TypeError, ValueError): Logger.log("e", "Could not parse response from network: %s", str(response)) - def _makeAuthDigestHeaderPart(self, url_part: str, method: str = "GET") -> str: + def _makeAuthDigestHeaderPart(self, url_part: str, method: HttpRequestMethod = HttpRequestMethod.GET) -> str: """ Make the data-part for a Digest Authentication HTTP-header. :param url_part: The part of the URL beyond the host name. @@ -237,7 +250,7 @@ class ClusterApiClient: "application": CuraApplication.getInstance().getApplicationDisplayName(), "user": username, }).encode("utf-8") - reply = self._manager.post(self.createEmptyRequest(url, method="POST", skip_auth=True), request_body) + reply = self._manager.post(self.createEmptyRequest(url, method=HttpRequestMethod.POST, skip_auth=True), request_body) self._addCallback(reply, on_finished) From 6b1f29cdb1f25aa12303fa88c40864c56e5df3bc Mon Sep 17 00:00:00 2001 From: Frederic Meeuwissen <13856291+Frederic98@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:12:07 +0200 Subject: [PATCH 85/98] Fix crash on AttributeError --- .../src/Network/LocalClusterOutputDevice.py | 20 +++++++++---------- .../src/Network/SendMaterialJob.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py index f9e0b95b59..f51ff5a4e8 100644 --- a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py @@ -94,15 +94,15 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice): @pyqtSlot(str, name="sendJobToTop") def sendJobToTop(self, print_job_uuid: str) -> None: - self._getApiClient().movePrintJobToTop(print_job_uuid) + self.getApiClient().movePrintJobToTop(print_job_uuid) @pyqtSlot(str, name="deleteJobFromQueue") def deleteJobFromQueue(self, print_job_uuid: str) -> None: - self._getApiClient().deletePrintJob(print_job_uuid) + self.getApiClient().deletePrintJob(print_job_uuid) @pyqtSlot(str, name="forceSendJob") def forceSendJob(self, print_job_uuid: str) -> None: - self._getApiClient().forcePrintJob(print_job_uuid) + self.getApiClient().forcePrintJob(print_job_uuid) def setJobState(self, print_job_uuid: str, action: str) -> None: """Set the remote print job state. @@ -111,20 +111,20 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice): :param action: The action to undertake ('pause', 'resume', 'abort'). """ - self._getApiClient().setPrintJobState(print_job_uuid, action) + self.getApiClient().setPrintJobState(print_job_uuid, action) def _update(self) -> None: super()._update() if time() - self._time_of_last_request < self.CHECK_CLUSTER_INTERVAL: return # avoid calling the cluster too often - self._getApiClient().getPrinters(self._updatePrinters) - self._getApiClient().getPrintJobs(self._updatePrintJobs) + self.getApiClient().getPrinters(self._updatePrinters) + self.getApiClient().getPrintJobs(self._updatePrintJobs) self._updatePrintJobPreviewImages() def getMaterials(self, on_finished: Callable[[List[ClusterMaterial]], Any]) -> None: """Get a list of materials that are installed on the cluster host.""" - self._getApiClient().getMaterials(on_finished = on_finished) + self.getApiClient().getMaterials(on_finished = on_finished) def sendMaterialProfiles(self) -> None: """Sync the material profiles in Cura with the printer. @@ -205,7 +205,7 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice): # FIXME: move form posting to API client self.postFormWithParts("/cluster-api/v1/print_jobs/", parts, on_finished=self._onPrintUploadCompleted, on_progress=self._onPrintJobUploadProgress, - request=self._cluster_api.createEmptyRequest("/cluster-api/v1/print_jobs/", content_type=None, method="POST")) + request=self.getApiClient().createEmptyRequest("/cluster-api/v1/print_jobs/", content_type=None, method="POST")) self._active_exported_job = None def _onPrintJobUploadProgress(self, bytes_sent: int, bytes_total: int) -> None: @@ -237,9 +237,9 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice): for print_job in self._print_jobs: if print_job.getPreviewImage() is None: - self._getApiClient().getPrintJobPreviewImage(print_job.key, print_job.updatePreviewImageData) + self.getApiClient().getPrintJobPreviewImage(print_job.key, print_job.updatePreviewImageData) - def _getApiClient(self) -> ClusterApiClient: + def getApiClient(self) -> ClusterApiClient: """Get the API client instance.""" if not self._cluster_api: diff --git a/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py index 2f3fb9ff19..3b9f127c77 100644 --- a/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py @@ -148,7 +148,7 @@ class SendMaterialJob(Job): # FIXME: move form posting to API client self.device.postFormWithParts(target = "/cluster-api/v1/materials/", parts = parts, on_finished = self._sendingFinished, - request=self._cluster_api.createEmptyRequest("/cluster-api/v1/materials/", content_type=None, method="POST")) + request=self.device.getApiClient().createEmptyRequest("/cluster-api/v1/materials/", content_type=None, method="POST")) def _sendingFinished(self, reply: QNetworkReply) -> None: """Check a reply from an upload to the printer and log an error when the call failed""" From 7fc87cb4c126bf77d3104a379f53fc6755544d6e Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 3 Sep 2025 14:28:12 +0200 Subject: [PATCH 86/98] Fill in correct nonce and nonce-count for cluster-auth. part of CURA-12624 --- .../src/Network/ClusterApiClient.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index 5cd8457188..1a0a915a03 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import hashlib import json +import re import secrets from enum import StrEnum from json import JSONDecodeError @@ -46,7 +47,6 @@ class ClusterApiClient: AUTH_REALM = "Jedi-API" AUTH_QOP = "auth" - AUTH_NC = "00000001" AUTH_NONCE_LEN = 16 AUTH_CNONCE_LEN = 8 @@ -69,6 +69,9 @@ class ClusterApiClient: self._auth_key = None self._auth_tries = 0 + self._nonce_count = 1 + self._nonce = None + def getSystem(self, on_finished: Callable) -> None: """Get printer system information. @@ -153,6 +156,7 @@ class ClusterApiClient: if self._auth_id and self._auth_key: digest_str = self._makeAuthDigestHeaderPart(path, method=method) request.setRawHeader(b"Authorization", f"Digest {digest_str}".encode("utf-8")) + self._nonce_count += 1 elif not skip_auth: self._setupAuth() return request @@ -204,18 +208,19 @@ class ClusterApiClient: def sha256_utf8(x: str) -> str: return hashlib.sha256(x.encode("utf-8")).hexdigest() - nonce = secrets.token_hex(ClusterApiClient.AUTH_NONCE_LEN) + nonce = secrets.token_hex(ClusterApiClient.AUTH_NONCE_LEN) if self._nonce is None else self._nonce cnonce = secrets.token_hex(ClusterApiClient.AUTH_CNONCE_LEN) + auth_nc = f"{self._nonce_count:08x}" ha1 = sha256_utf8(f"{self._auth_id}:{ClusterApiClient.AUTH_REALM}:{self._auth_key}") ha2 = sha256_utf8(f"{method}:{url_part}") - resp_digest = sha256_utf8(f"{ha1}:{nonce}:{ClusterApiClient.AUTH_NC}:{cnonce}:{ClusterApiClient.AUTH_QOP}:{ha2}") + resp_digest = sha256_utf8(f"{ha1}:{nonce}:{auth_nc}:{cnonce}:{ClusterApiClient.AUTH_QOP}:{ha2}") return ", ".join([ f'username="{self._auth_id}"', f'realm="{ClusterApiClient.AUTH_REALM}"', f'nonce="{nonce}"', f'uri="{url_part}"', - f'nc={ClusterApiClient.AUTH_NC}', + f'nc={auth_nc}', f'cnonce="{cnonce}"', f'qop={ClusterApiClient.AUTH_QOP}', f'response="{resp_digest}"', @@ -275,6 +280,11 @@ class ClusterApiClient: return if reply.error() != QNetworkReply.NetworkError.NoError: + if reply.error() == QNetworkReply.NetworkError.AuthenticationRequiredError: + nonce_match = re.search(r'nonce="([^"]+)', str(reply.rawHeader(b"WWW-Authenticate"))) + if nonce_match: + self._nonce = nonce_match.group(1) + self._nonce_count = 1 self._on_error(reply.errorString()) return From 70a8f9b0a3f816f6ce0de34fe22fff5c086a608c Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 3 Sep 2025 14:37:18 +0200 Subject: [PATCH 87/98] Use machine-node-ID as username. Otherwise there's _still_ personal information in there. part of CURA-12624 --- plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index 1a0a915a03..bbe7932d66 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import hashlib import json +import platform import re import secrets from enum import StrEnum @@ -235,10 +236,6 @@ class ClusterApiClient: Logger.warning("Maximum authorization temporary digest-token request tries exceeded. Is printer-firmware up to date?") return - username = CuraApplication.getInstance().getCuraAPI().account.userName - if (not username) or username == "": - return - def on_finished(resp) -> None: self._auth_tries += 1 try: @@ -253,7 +250,7 @@ class ClusterApiClient: url = "{}/auth/request".format(self.PRINTER_API_PREFIX) request_body = json.dumps({ "application": CuraApplication.getInstance().getApplicationDisplayName(), - "user": username, + "user": f"user@{platform.node()}", }).encode("utf-8") reply = self._manager.post(self.createEmptyRequest(url, method=HttpRequestMethod.POST, skip_auth=True), request_body) From a8ac8e93324cda60a36191657474a7a730a2e540 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 9 Sep 2025 09:27:23 +0200 Subject: [PATCH 88/98] Forgot to update this (needed for authentication). part of CURA-12624 --- plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index bbe7932d66..25617ce824 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -125,7 +125,7 @@ class ClusterApiClient: """Delete a print job from the queue.""" url = "{}/print_jobs/{}".format(self.CLUSTER_API_PREFIX, print_job_uuid) - self._manager.deleteResource(self.createEmptyRequest(url)) + self._manager.deleteResource(self.createEmptyRequest(url, method=HttpRequestMethod.DELETE)) def setPrintJobState(self, print_job_uuid: str, state: str) -> None: """Set the state of a print job.""" From f41c3d197ce57b46b91927e2b0842c04aedbe887 Mon Sep 17 00:00:00 2001 From: takanuva15 <6986426+takanuva15@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:00:58 -0400 Subject: [PATCH 89/98] Add profile for Anycubic Kobra S1 --- .../definitions/anycubic_kobra_s1.def.json | 38 + .../anycubic_kobra_s1_extruder_0.def.json | 16 + .../anycubic_kobra_s1_buildplate_texture.svg | 35 + .../meshes/anycubic_kobra_s1_buildplate.obj | 917 ++++++++++++++++++ .../anycubic_kobra_s1_high_quality.inst.cfg | 33 + .../anycubic_kobra_s1_standard.inst.cfg | 33 + 6 files changed, 1072 insertions(+) create mode 100644 resources/definitions/anycubic_kobra_s1.def.json create mode 100644 resources/extruders/anycubic_kobra_s1_extruder_0.def.json create mode 100644 resources/images/anycubic_kobra_s1_buildplate_texture.svg create mode 100644 resources/meshes/anycubic_kobra_s1_buildplate.obj create mode 100644 resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg create mode 100644 resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg diff --git a/resources/definitions/anycubic_kobra_s1.def.json b/resources/definitions/anycubic_kobra_s1.def.json new file mode 100644 index 0000000000..8cedd8c591 --- /dev/null +++ b/resources/definitions/anycubic_kobra_s1.def.json @@ -0,0 +1,38 @@ +{ + "version": 2, + "name": "Anycubic Kobra S1", + "inherits": "fdmprinter", + "metadata": + { + "visible": true, + "author": "takanuva15", + "manufacturer": "Anycubic", + "file_formats": "text/x-gcode", + "has_machine_quality": true, + "has_materials": true, + "platform": "anycubic_kobra_s1_buildplate.obj", + "platform_texture": "anycubic_kobra_s1_buildplate_texture.svg", + "has_textured_buildplate": true, + "machine_extruder_trains": { "0": "anycubic_kobra_s1_extruder_0" }, + "has_variant_buildplates": false, + "has_variants": false, + "preferred_variant_name": "0.4mm", + "preferred_quality_type": "normal" + }, + "overrides": + { + "machine_depth": { "default_value": 250 }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_heated_bed": { "default_value": true }, + "machine_height": { "default_value": 250 }, + "machine_name": + { + "default_value": "Anycubic Kobra S1", + "description": "Anycubic Kobra S1" + }, + "machine_buildplate_type": { "default_value": "PEI Spring Steel" }, + "machine_start_gcode": { "default_value": "M106 S0\nM106 P2 S0\n;TYPE:Custom\nG9111 bedTemp={material_bed_temperature} extruderTemp={material_print_temperature}\nM117\nM106 P3 S153\nG90\nG21\nM83 ; use relative distances for extrusion\n; filament start gcode\nM900 K0.035 ; Override pressure advance value\nM106 S0\nM106 P2 S0\n\nM420 S1 ;load stored mesh to avoid auto-leveling" }, + "machine_end_gcode": { "default_value": "; move printhead away from object\nG1 Z22.000 ; for object exclusion\nG1 E-.76675 F2400\n; fan off\nM106 S0\nM106 P2 S0\n;TYPE:Custom\n; filament end gcode\nG92 E0\nG1 E-2 F3000\nG1 Z24 F900 ; Move print head further up \nG1 F12000; present print\nG1 X44; throw_position_x\nG1 Y270; throw_position_y\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM106 P1 S0 ; turn off fan\nM106 P2 S0\nM106 P3 S0\nM84; disable motors \n; disable stepper motors\nM106 P3 S204\n\n; CONFIG FOR SCREEN PRINT PREVIEW\n; total filament used [g] = {filament_weight}\n\n; CONFIG_BLOCK_START = begin\n; filament_type = {material_type}\n; nozzle_temperature = {material_print_temperature}\n; bed_temperature = {material_bed_temperature}\n; CONFIG_BLOCK_END = end" }, + "machine_width": { "default_value": 250 } + } +} diff --git a/resources/extruders/anycubic_kobra_s1_extruder_0.def.json b/resources/extruders/anycubic_kobra_s1_extruder_0.def.json new file mode 100644 index 0000000000..c850757b6e --- /dev/null +++ b/resources/extruders/anycubic_kobra_s1_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": + { + "machine": "anycubic_kobra_s1", + "position": "0" + }, + "overrides": + { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} \ No newline at end of file diff --git a/resources/images/anycubic_kobra_s1_buildplate_texture.svg b/resources/images/anycubic_kobra_s1_buildplate_texture.svg new file mode 100644 index 0000000000..d5a4c3d2aa --- /dev/null +++ b/resources/images/anycubic_kobra_s1_buildplate_texture.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/meshes/anycubic_kobra_s1_buildplate.obj b/resources/meshes/anycubic_kobra_s1_buildplate.obj new file mode 100644 index 0000000000..c206e9d45d --- /dev/null +++ b/resources/meshes/anycubic_kobra_s1_buildplate.obj @@ -0,0 +1,917 @@ +# Blender 4.5.2 LTS +# www.blender.org +mtllib anycubic_kobra_s1_buildplate.mtl +o anycubic_kobra_s1_buildplate +v -129.054123 -130.091019 -0.700027 +v -131.999741 -125.129326 -0.700027 +v 24.434870 -134.329071 -0.699997 +v 17.018261 -141.131271 -0.700027 +v 17.993422 -138.654526 -0.700017 +v 17.573563 -138.625687 -0.700039 +v 7.467300 -130.848297 -0.700027 +v 10.650862 -131.688293 -0.700024 +v 10.835647 -131.177765 -0.700052 +v 10.634915 -131.406662 -0.700027 +v 28.897467 -129.170013 -0.700027 +v 129.731293 -141.134842 -0.700027 +v 28.763437 -139.157059 -0.700023 +v 19.657635 1.777050 -0.700027 +v 130.689468 -140.855942 -0.700027 +v -126.003876 134.864807 -0.700027 +v 94.529488 119.736588 -0.700027 +v -127.664345 134.508484 -0.700027 +v -53.626228 131.701767 -0.700027 +v -58.244865 131.375092 -0.700011 +v -56.412327 131.738388 -0.700027 +v -58.738430 134.509171 -0.700027 +v -51.261589 134.509155 -0.700027 +v 59.504433 134.866333 -0.700027 +v 58.738407 134.509140 -0.700027 +v 56.373764 131.701767 -0.700027 +v 129.054123 133.823685 -0.700027 +v 6.849222 -131.822647 -0.700027 +v 5.973866 -131.306747 -0.700027 +v -125.937408 -131.133133 -0.700027 +v 16.145327 -140.860641 -0.700027 +v 15.498124 -140.467697 -0.700027 +v 19.726137 -139.083862 -0.699903 +v 19.846451 -139.155289 -0.699967 +v 19.694454 -138.804977 -0.700012 +v 19.672100 -138.985672 -0.699999 +v 17.791988 -138.688354 -0.699921 +v 24.677004 -131.159836 -0.700026 +v 5.082874 -131.134628 -0.700027 +v -131.642151 -126.798012 -0.700027 +v -130.345932 -128.984650 -0.700027 +v -131.129166 -127.883667 -0.700027 +v -127.521126 -130.824478 -0.700027 +v 24.189461 -134.309204 -0.699963 +v 28.900984 -139.058914 -0.699964 +v 28.937689 -138.922531 -0.699970 +v 131.429153 -140.236237 -0.700027 +v 131.829224 -139.561752 -0.700027 +v 131.999878 -138.837708 -0.700027 +v 24.932178 -131.709625 -0.699983 +v 24.986902 -131.483047 -0.700003 +v 24.872414 -131.261368 -0.700028 +v -131.972137 129.010681 -0.700027 +v -131.690811 130.387451 -0.700027 +v -129.850983 133.212265 -0.700027 +v -130.957367 131.920441 -0.700027 +v -59.504433 134.866333 -0.700027 +v -55.206390 131.169067 -0.700027 +v -54.353245 131.252808 -0.700027 +v -55.876461 131.360275 -0.700027 +v -61.206200 131.340363 -0.699992 +v -61.103699 131.284119 -0.700031 +v -58.337986 131.290283 -0.700022 +v -61.257591 131.533386 -0.700020 +v -60.041721 132.993088 -0.699486 +v -59.706219 133.092285 -0.699463 +v -59.445782 133.003464 -0.699889 +v -128.750000 133.995499 -0.700027 +v -58.214905 131.529205 -0.699996 +v -59.081882 134.772491 -0.700027 +v -50.918118 134.772491 -0.700027 +v 54.123543 131.360275 -0.700027 +v 54.793610 131.169067 -0.700027 +v 55.646759 131.252808 -0.700027 +v -50.495571 134.866333 -0.700027 +v 58.372162 131.283875 -0.700029 +v 61.090813 131.281311 -0.700040 +v 61.215893 131.350204 -0.699977 +v 131.996445 128.883270 -0.700027 +v 131.642151 130.530670 -0.700027 +v 53.603378 131.725235 -0.700027 +v 50.495571 134.866333 -0.700027 +v 59.437256 133.000992 -0.699896 +v 50.918118 134.772491 -0.700027 +v 51.261566 134.509171 -0.700027 +v 59.081882 134.772491 -0.700027 +v 59.768948 133.092285 -0.699463 +v 61.261791 131.523209 -0.699989 +v 60.038094 132.999237 -0.699867 +v 130.345932 132.717316 -0.700027 +v 131.129166 131.616333 -0.700027 +v 125.947678 134.864441 -0.700027 +v 127.521126 134.557144 -0.700027 +v 15.477744 -140.445251 -0.000027 +v 6.921199 -131.894821 -0.000027 +v 6.172548 -131.389053 -0.000027 +v 5.182955 -131.133942 -0.000027 +v -126.021477 -131.129410 -0.000027 +v -127.664345 -130.775818 -0.000027 +v -128.750000 -130.262833 -0.000027 +v -129.850983 -129.479599 -0.000027 +v -130.957352 -128.187790 -0.000027 +v -131.690811 -126.654793 -0.000027 +v -131.999237 -125.072853 -0.000027 +v -131.999634 128.862701 -0.000027 +v -131.642151 130.530670 -0.000027 +v -131.129166 131.616333 -0.000027 +v -130.345932 132.717316 -0.000027 +v -129.054123 133.823685 -0.000027 +v -127.521126 134.557144 -0.000027 +v -125.943855 134.864960 -0.000027 +v -59.504433 134.866333 -0.000027 +v -59.081867 134.772491 -0.000027 +v -58.738400 134.509140 -0.000027 +v -56.382183 131.713150 -0.000027 +v -55.646759 131.252808 -0.000027 +v -54.793610 131.169067 -0.000027 +v -54.123543 131.360275 -0.000027 +v -53.588142 131.744583 -0.000027 +v -51.261597 134.509155 -0.000027 +v -50.918118 134.772491 -0.000027 +v -50.495571 134.866333 -0.000027 +v 50.495571 134.866333 -0.000027 +v 50.918118 134.772491 -0.000027 +v 53.590721 131.735840 -0.000027 +v 51.261597 134.509155 -0.000027 +v 54.353245 131.252808 -0.000027 +v 55.206390 131.169067 -0.000027 +v 55.876461 131.360275 -0.000027 +v 56.411842 131.744568 -0.000027 +v 58.738411 134.509155 -0.000027 +v 59.081882 134.772491 -0.000027 +v 126.004631 134.864685 -0.000027 +v 59.504433 134.866333 -0.000027 +v 127.664345 134.508484 -0.000027 +v 128.750000 133.995499 -0.000027 +v 129.850983 133.212265 -0.000027 +v 130.957367 131.920441 -0.000027 +v 131.690811 130.387451 -0.000027 +v 131.998947 128.807678 -0.000027 +v 131.999741 -138.838501 -0.000027 +v 131.720840 -139.824554 -0.000027 +v 131.101089 -140.564301 -0.000027 +v 130.426453 -140.964417 -0.000027 +v 129.667175 -141.134506 -0.000027 +v 17.248808 -141.134827 -0.000027 +v 16.351576 -140.962082 -0.000027 +v 19.707363 -138.788864 -0.000041 +v 24.162891 -134.333496 -0.000053 +v 19.663969 -138.943939 -0.000087 +v 19.797115 -139.148376 -0.000032 +v 28.767149 -139.155899 -0.000027 +v 28.914742 -139.033264 -0.000058 +v 28.935387 -138.848969 -0.000056 +v 28.901199 -138.794693 -0.699900 +v 24.398291 -134.300629 -0.000029 +v 17.543394 -138.596664 -0.000027 +v 10.667012 -131.718216 -0.000029 +v 17.720654 -138.684357 -0.000258 +v 18.011297 -138.637558 -0.000064 +v 24.922869 -131.725021 -0.000033 +v 24.982822 -131.458267 -0.000442 +v 24.862761 -131.252243 -0.000026 +v 24.679277 -131.160583 -0.000028 +v 10.902476 -131.165527 -0.000039 +v 10.718793 -131.272064 -0.000636 +v 10.623919 -131.451797 -0.000026 +v 61.245735 131.562759 -0.000011 +v 61.253937 131.430710 0.000029 +v 61.149254 131.294632 -0.000034 +v 60.027687 133.012787 -0.000037 +v 59.431732 132.991699 -0.000385 +v 59.678104 133.087860 -0.000445 +v 58.228245 131.561798 -0.000019 +v 58.211628 131.530136 -0.699993 +v 58.355827 131.286514 -0.000025 +v 58.224735 131.394821 -0.000031 +v 58.258984 131.349060 -0.699994 +v -58.215385 131.472092 -0.000096 +v -58.242355 131.581573 -0.000025 +v -58.251167 131.362717 -0.000042 +v -58.354370 131.287048 -0.000028 +v -59.444996 133.012695 -0.000070 +v -60.042431 132.997391 -0.000213 +v -59.797066 133.087875 -0.000445 +v -61.231110 131.585205 -0.000019 +v -61.179737 131.318527 -0.000114 +v -61.088017 131.281326 -0.000014 +v -61.257828 131.433472 0.000060 +v -27.913065 -9.021112 -0.000027 +v -96.694077 101.654877 -0.000027 +v 7.098911 -130.230896 -0.000027 +v 25.810665 -131.897369 -0.000027 +v 31.053526 -138.925018 -0.000027 +vn -0.0000 -0.0000 -1.0000 +vn -0.0000 -0.0001 -1.0000 +vn -0.0003 0.0004 -1.0000 +vn 0.0006 -0.0007 -1.0000 +vn 0.0001 -0.0001 -1.0000 +vn 0.0005 -0.0001 -1.0000 +vn -0.0005 0.0002 -1.0000 +vn -0.0000 0.0006 -1.0000 +vn -0.0020 -0.0000 -1.0000 +vn -0.0001 0.0001 -1.0000 +vn -0.0000 0.0008 -1.0000 +vn 0.0001 -0.0000 -1.0000 +vn 0.0012 -0.0006 -1.0000 +vn 0.0002 -0.0003 -1.0000 +vn -0.0017 -0.0001 -1.0000 +vn -0.0000 0.0001 -1.0000 +vn -0.0001 -0.0001 -1.0000 +vn 0.0002 0.0008 -1.0000 +vn 0.0002 0.0006 -1.0000 +vn 0.0012 0.0005 -1.0000 +vn -0.0017 -0.0006 -1.0000 +vn 0.0001 -0.0003 -1.0000 +vn -0.7069 -0.7074 -0.0003 +vn -0.7069 -0.7073 0.0021 +vn -0.5073 -0.8607 0.0428 +vn -0.5596 -0.8283 -0.0279 +vn -0.1896 -0.9815 0.0281 +vn -0.2494 -0.9674 -0.0430 +vn -0.0000 -1.0000 0.0053 +vn -0.0000 -1.0000 0.0009 +vn -0.2103 -0.9773 0.0249 +vn -0.1913 -0.9814 -0.0177 +vn -0.4269 -0.9036 -0.0363 +vn -0.4314 -0.9018 -0.0256 +vn -0.5789 -0.8137 0.0518 +vn -0.6486 -0.7573 -0.0768 +vn -0.8138 -0.5789 -0.0518 +vn -0.9018 -0.4314 0.0256 +vn -0.9036 -0.4269 0.0363 +vn -0.9814 -0.1913 0.0161 +vn -0.9775 -0.2095 -0.0251 +vn -0.7573 -0.6486 0.0768 +vn -0.9992 0.0001 -0.0392 +vn -1.0000 -0.0000 0.0007 +vn -0.9775 0.2095 0.0251 +vn -0.9797 0.2002 0.0038 +vn -0.9036 0.4269 -0.0363 +vn -0.9018 0.4314 -0.0256 +vn -0.8137 0.5789 0.0518 +vn -0.7573 0.6486 -0.0769 +vn -0.5789 0.8137 -0.0518 +vn -0.4314 0.9018 0.0256 +vn -0.4269 0.9036 0.0363 +vn -0.1915 0.9814 0.0162 +vn -0.2098 0.9774 -0.0250 +vn -0.6486 0.7573 0.0768 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 1.0000 -0.0002 +vn 0.2168 0.9762 -0.0000 +vn 0.6085 0.7936 0.0001 +vn 0.6084 0.7936 -0.0000 +vn 0.7659 0.6430 -0.0000 +vn 0.7646 0.6444 -0.0097 +vn 0.5765 0.8171 0.0046 +vn 0.5301 0.8468 -0.0439 +vn 0.2739 0.9600 0.0575 +vn 0.0975 0.9936 -0.0575 +vn -0.0975 0.9936 0.0575 +vn -0.2740 0.9600 -0.0575 +vn -0.5250 0.8501 0.0418 +vn -0.5830 0.8123 -0.0180 +vn -0.7648 0.6442 0.0022 +vn -0.7651 0.6439 -0.0000 +vn -0.2168 0.9762 -0.0000 +vn -0.6085 0.7936 -0.0000 +vn -0.6084 0.7936 -0.0000 +vn 0.6085 0.7936 -0.0000 +vn 0.7653 0.6437 0.0041 +vn 0.7658 0.6431 -0.0000 +vn 0.5743 0.8186 -0.0020 +vn 0.5346 0.8439 -0.0458 +vn 0.2740 0.9600 0.0575 +vn -0.2739 0.9600 -0.0575 +vn -0.6084 0.7936 -0.0001 +vn -0.0000 1.0000 -0.0004 +vn 0.2098 0.9774 0.0250 +vn 0.1917 0.9813 -0.0159 +vn 0.4269 0.9036 -0.0363 +vn 0.4314 0.9018 -0.0256 +vn 0.5789 0.8137 0.0518 +vn 0.6486 0.7573 -0.0768 +vn 0.8137 0.5789 -0.0518 +vn 0.9018 0.4314 0.0256 +vn 0.9036 0.4269 0.0363 +vn 0.9814 0.1914 0.0172 +vn 0.9773 0.2102 -0.0249 +vn 0.7573 0.6486 0.0768 +vn 1.0000 -0.0000 0.0002 +vn 1.0000 -0.0000 -0.0036 +vn 0.9612 -0.2719 0.0468 +vn 0.9733 -0.2294 -0.0001 +vn 0.8586 -0.5093 -0.0583 +vn 0.7652 -0.6411 0.0582 +vn 0.6411 -0.7652 -0.0582 +vn 0.5092 -0.8587 0.0583 +vn 0.2186 -0.9756 0.0204 +vn 0.2792 -0.9592 -0.0437 +vn -0.0000 -1.0000 -0.0050 +vn -0.0000 -1.0000 0.0004 +vn -0.1887 -0.9804 0.0572 +vn -0.2957 -0.9539 -0.0511 +vn -0.5090 -0.8607 0.0128 +vn -0.5188 -0.8544 0.0290 +vn 0.7072 -0.7070 0.0023 +vn 0.7071 -0.7071 0.0032 +vn 0.5103 0.8596 0.0275 +vn 0.8759 0.4821 -0.0186 +vn 0.9923 -0.1228 0.0189 +vn 0.9629 -0.2695 -0.0116 +vn 0.8374 0.5454 -0.0347 +vn 0.0002 1.0000 -0.0099 +vn 0.0008 1.0000 -0.0016 +vn -0.5808 0.8140 0.0018 +vn -0.9937 0.1113 -0.0150 +vn -0.9656 0.2599 0.0095 +vn -0.9613 -0.2744 0.0257 +vn -0.6390 0.7690 -0.0157 +vn -0.7070 -0.7071 -0.0082 +vn -0.7078 -0.7061 -0.0201 +vn -0.0806 -0.9961 0.0363 +vn 0.1382 -0.9900 -0.0291 +vn 0.7072 0.7070 0.0012 +vn 0.7078 0.7063 0.0139 +vn 0.2757 0.9610 0.0226 +vn 0.4433 0.8962 -0.0181 +vn -0.1656 0.9859 -0.0225 +vn -0.1589 0.9871 -0.0199 +vn -0.7072 0.7071 0.0009 +vn -0.7074 0.7068 0.0062 +vn -0.9720 0.2347 -0.0140 +vn -0.9756 0.2193 -0.0082 +vn -0.8885 -0.4588 0.0111 +vn -0.8640 -0.5035 -0.0053 +vn -0.4611 -0.8874 0.0052 +vn -0.4469 -0.8946 0.0005 +vn 0.0004 -0.9998 0.0175 +vn 0.0013 -1.0000 -0.0011 +vn 0.5015 -0.8645 -0.0328 +vn 0.7513 -0.6589 0.0367 +vn 0.9982 0.0565 0.0193 +vn 0.8842 -0.4668 -0.0162 +vn 0.9870 0.1597 -0.0160 +vn -0.9979 -0.0621 -0.0194 +vn -0.9663 0.2563 0.0230 +vn -0.7923 0.6096 -0.0270 +vn -0.4824 0.8757 0.0236 +vn -0.7657 -0.6432 0.0011 +vn -0.7697 -0.6381 0.0184 +vn 0.3636 -0.9315 -0.0095 +vn -0.2098 -0.9772 -0.0334 +vn 0.2652 -0.9638 0.0284 +vn -0.3267 -0.9450 0.0134 +vn 0.7682 -0.6401 0.0107 +vn 0.7651 -0.6439 -0.0025 +vn 0.6368 0.7708 -0.0192 +vn 0.4990 0.8665 0.0084 +vn 0.9995 -0.0210 -0.0228 +vn 0.9670 0.2529 0.0308 +vn 0.0009 1.0000 -0.0037 +vn -0.0029 0.9998 -0.0188 +vn -0.9815 0.1908 0.0149 +vn -0.5913 0.8064 -0.0101 +vn -0.6733 0.7393 0.0070 +vn -0.9708 -0.2391 -0.0202 +vn -0.9504 0.3109 -0.0031 +vn -0.7654 -0.6432 0.0181 +vn -0.7676 -0.6409 0.0093 +vn 0.3460 -0.9382 0.0061 +vn -0.2087 -0.9774 -0.0333 +vn 0.2834 -0.9585 0.0308 +vn -0.3227 -0.9464 0.0128 +vn 0.7650 -0.6440 0.0047 +vn 0.7682 -0.6399 0.0183 +vn 0.3758 0.9267 -0.0047 +vn 0.8271 0.5619 -0.0138 +vn 0.4810 0.8767 0.0092 +vn 0.9846 -0.1733 -0.0244 +vn 0.9657 0.2571 0.0370 +vn -0.0021 1.0000 0.0046 +vn -0.0022 1.0000 0.0041 +vn -0.0000 -0.0000 1.0000 +vn -0.0001 0.0002 1.0000 +vn 0.0002 -0.0004 1.0000 +vn -0.0008 0.0006 1.0000 +vn 0.0010 -0.0005 1.0000 +vn 0.0010 0.0029 1.0000 +vn -0.0010 -0.0039 1.0000 +vn -0.0005 0.0006 1.0000 +vn 0.0008 -0.0010 1.0000 +vn -0.0007 0.0009 1.0000 +vn -0.0001 0.0003 1.0000 +vn -0.0001 0.0005 1.0000 +vn -0.0000 -0.0006 1.0000 +vn -0.0001 -0.0000 1.0000 +vn 0.0008 -0.0005 1.0000 +vn 0.0012 -0.0008 1.0000 +vn -0.0010 0.0002 1.0000 +vn -0.0000 0.0003 1.0000 +vn -0.0000 0.0004 1.0000 +vn -0.0001 0.0001 1.0000 +vn 0.0003 0.0015 1.0000 +vn -0.0023 -0.0033 1.0000 +vn -0.0000 -0.0001 1.0000 +vn -0.0000 0.0001 1.0000 +vn -0.0000 -0.0005 1.0000 +vn 0.0004 -0.0001 1.0000 +vn -0.0014 -0.0012 1.0000 +vn 0.0003 -0.0001 1.0000 +vt 0.000000 0.000000 +vt 0.000107 0.058203 +vt 0.016454 0.037622 +vt 0.394244 0.478671 +vt 0.016996 0.998790 +vt 0.001448 0.984261 +vt 0.000121 0.978240 +vt 0.133726 0.879669 +vt 0.286443 0.988604 +vt 0.276187 0.999660 +vt 0.279358 0.988093 +vt 0.540230 0.035080 +vt 0.526857 0.039508 +vt 0.540433 0.034155 +vt 0.574484 0.007985 +vt 0.561949 0.000747 +vt 0.565302 0.000027 +vt 0.597730 0.033470 +vt 0.999878 0.008326 +vt 0.720558 0.987447 +vt 0.713609 0.988715 +vt 0.991098 0.000046 +vt 0.998752 0.004815 +vt 0.012310 0.039392 +vt 0.008205 0.042301 +vt 0.519592 0.036165 +vt 0.022652 0.036358 +vt 0.004032 0.046963 +vt 0.001265 0.052495 +vt 0.558636 0.002548 +vt 0.526167 0.033461 +vt 0.566417 0.009197 +vt 0.574994 0.007280 +vt 0.574631 0.008489 +vt 0.567037 0.008919 +vt 0.568114 0.009061 +vt 0.523292 0.035196 +vt 0.540622 0.035760 +vt 0.541267 0.036099 +vt 0.593443 0.036109 +vt 0.022945 0.999896 +vt 0.003298 0.988224 +vt 0.006339 0.992151 +vt 0.011213 0.996143 +vt 0.268058 0.988106 +vt 0.267978 0.987584 +vt 0.268216 0.987186 +vt 0.274587 1.000000 +vt 0.278953 0.987065 +vt 0.268591 0.987023 +vt 0.279330 0.987333 +vt 0.279457 0.987685 +vt 0.289243 0.987038 +vt 0.292425 0.986655 +vt 0.294945 0.987343 +vt 0.296968 0.988715 +vt 0.274751 0.993207 +vt 0.272600 0.993165 +vt 0.277488 0.998706 +vt 0.273704 0.993558 +vt 0.308709 1.000000 +vt 0.305808 0.998706 +vt 0.307109 0.999660 +vt 0.608914 0.007209 +vt 0.617588 0.008008 +vt 0.609438 0.007577 +vt 0.609480 0.008255 +vt 0.592353 0.024675 +vt 0.591541 0.024638 +vt 0.594342 0.034112 +vt 0.594123 0.035786 +vt 0.594533 0.035019 +vt 0.996497 0.002108 +vt 0.993956 0.000667 +vt 0.694129 0.998706 +vt 0.709065 0.986656 +vt 0.705884 0.987038 +vt 0.711585 0.987343 +vt 0.702989 0.988695 +vt 0.720555 0.988028 +vt 0.720989 0.987063 +vt 0.999829 0.978042 +vt 0.731580 0.987122 +vt 0.731963 0.987555 +vt 0.731920 0.988003 +vt 0.691227 1.000000 +vt 0.692828 0.999660 +vt 0.722448 0.998706 +vt 0.724957 0.993040 +vt 0.727273 0.993212 +vt 0.723749 0.999660 +vt 0.725349 1.000000 +vt 0.726232 0.993558 +vt 0.977220 0.999882 +vt 0.983483 0.998614 +vt 0.998672 0.983742 +vt 0.995905 0.989274 +vt 0.991731 0.993936 +vt 0.987626 0.996845 +s 0 +usemtl Material.002 +f 2/1/1 1/1/1 40/1/1 +f 14/1/1 9/1/1 7/1/1 +f 10/1/1 8/1/1 7/1/1 +f 14/1/1 20/1/1 21/1/1 +f 25/1/1 175/1/1 26/1/1 +f 26/1/1 76/1/1 17/1/1 +f 26/1/1 17/1/1 14/1/1 +f 29/1/1 7/1/1 28/1/1 +f 6/1/1 28/1/1 7/1/1 +f 7/1/2 9/1/2 10/1/2 +f 4/1/1 31/1/1 32/1/1 +f 4/1/3 33/1/3 34/1/3 +f 36/1/4 33/1/4 4/1/4 +f 4/1/5 35/1/5 36/1/5 +f 4/1/6 6/1/6 37/1/6 +f 6/1/1 4/1/1 32/1/1 +f 32/1/1 28/1/1 6/1/1 +f 14/1/1 38/1/1 9/1/1 +f 6/1/1 7/1/1 8/1/1 +f 5/1/7 4/1/7 37/1/7 +f 5/1/1 35/1/1 4/1/1 +f 39/1/1 7/1/1 29/1/1 +f 40/1/1 41/1/1 42/1/1 +f 1/1/1 30/1/1 43/1/1 +f 40/1/1 1/1/1 41/1/1 +f 2/1/1 14/1/1 1/1/1 +f 30/1/1 14/1/1 39/1/1 +f 30/1/1 1/1/1 14/1/1 +f 14/1/1 7/1/1 39/1/1 +f 14/1/1 11/1/1 38/1/1 +f 34/1/1 13/1/1 4/1/1 +f 12/1/1 4/1/1 13/1/1 +f 11/1/1 155/1/1 3/1/1 +f 44/1/1 35/1/1 5/1/1 +f 13/1/8 45/1/8 12/1/8 +f 12/1/1 46/1/1 11/1/1 +f 12/1/1 45/1/1 46/1/1 +f 11/1/9 46/1/9 155/1/9 +f 12/1/1 14/1/1 15/1/1 +f 47/1/1 15/1/1 48/1/1 +f 49/1/1 48/1/1 15/1/1 +f 14/1/1 49/1/1 15/1/1 +f 14/1/1 12/1/1 11/1/1 +f 50/1/1 11/1/1 5/1/1 +f 11/1/10 3/1/10 44/1/10 +f 5/1/2 11/1/2 44/1/2 +f 11/1/5 50/1/5 51/1/5 +f 51/1/2 52/1/2 11/1/2 +f 52/1/1 38/1/1 11/1/1 +f 16/1/1 14/1/1 2/1/1 +f 54/1/1 16/1/1 53/1/1 +f 16/1/1 54/1/1 56/1/1 +f 2/1/1 53/1/1 16/1/1 +f 14/1/1 16/1/1 62/1/1 +f 61/1/11 62/1/11 16/1/11 +f 62/1/1 63/1/1 14/1/1 +f 14/1/1 58/1/1 59/1/1 +f 58/1/1 14/1/1 60/1/1 +f 14/1/12 63/1/12 20/1/12 +f 56/1/1 55/1/1 16/1/1 +f 55/1/1 18/1/1 16/1/1 +f 64/1/13 57/1/13 65/1/13 +f 65/1/14 57/1/14 66/1/14 +f 57/1/15 67/1/15 66/1/15 +f 64/1/1 16/1/1 57/1/1 +f 55/1/1 68/1/1 18/1/1 +f 69/1/16 21/1/16 20/1/16 +f 57/1/17 21/1/17 67/1/17 +f 21/1/16 69/1/16 67/1/16 +f 14/1/1 21/1/1 60/1/1 +f 59/1/1 19/1/1 14/1/1 +f 14/1/1 19/1/1 23/1/1 +f 21/1/1 57/1/1 22/1/1 +f 70/1/1 22/1/1 57/1/1 +f 71/1/1 75/1/1 14/1/1 +f 71/1/1 14/1/1 23/1/1 +f 16/1/2 64/1/2 61/1/2 +f 14/1/1 75/1/1 82/1/1 +f 14/1/1 74/1/1 26/1/1 +f 17/1/1 24/1/1 92/1/1 +f 14/1/1 73/1/1 74/1/1 +f 73/1/1 14/1/1 72/1/1 +f 76/1/18 26/1/18 178/1/18 +f 76/1/1 77/1/1 17/1/1 +f 77/1/19 78/1/19 17/1/19 +f 14/1/1 17/1/1 49/1/1 +f 17/1/1 79/1/1 49/1/1 +f 79/1/1 17/1/1 27/1/1 +f 79/1/1 27/1/1 80/1/1 +f 14/1/1 81/1/1 72/1/1 +f 175/1/1 178/1/1 26/1/1 +f 175/1/12 25/1/12 83/1/12 +f 84/1/1 85/1/1 14/1/1 +f 84/1/1 14/1/1 82/1/1 +f 25/1/1 86/1/1 24/1/1 +f 14/1/1 85/1/1 81/1/1 +f 83/1/20 25/1/20 87/1/20 +f 88/1/2 17/1/2 78/1/2 +f 89/1/21 87/1/21 24/1/21 +f 88/1/16 89/1/16 17/1/16 +f 89/1/2 24/1/2 17/1/2 +f 80/1/1 27/1/1 91/1/1 +f 90/1/1 91/1/1 27/1/1 +f 27/1/1 92/1/1 93/1/1 +f 17/1/1 92/1/1 27/1/1 +f 24/1/22 87/1/22 25/1/22 +f 95/1/23 28/1/23 94/1/23 +f 28/1/24 32/1/24 94/1/24 +f 29/1/25 28/1/25 96/1/25 +f 28/1/26 95/1/26 96/1/26 +f 39/1/27 29/1/27 97/1/27 +f 29/1/28 96/1/28 97/1/28 +f 30/1/29 97/1/29 98/1/29 +f 39/1/30 97/1/30 30/1/30 +f 98/1/31 99/1/31 43/1/31 +f 98/1/32 43/1/32 30/1/32 +f 99/1/33 100/1/33 1/1/33 +f 99/1/34 1/1/34 43/1/34 +f 100/1/35 101/1/35 1/1/35 +f 41/1/36 1/1/36 101/1/36 +f 102/1/37 42/1/37 41/1/37 +f 102/1/38 103/1/38 40/1/38 +f 102/1/39 40/1/39 42/1/39 +f 103/1/40 104/1/40 2/1/40 +f 103/1/41 2/1/41 40/1/41 +f 41/1/42 101/1/42 102/1/42 +f 2/1/43 105/1/43 53/1/43 +f 2/1/44 104/1/44 105/1/44 +f 105/1/45 106/1/45 54/1/45 +f 105/1/46 54/1/46 53/1/46 +f 106/1/47 107/1/47 56/1/47 +f 106/1/48 56/1/48 54/1/48 +f 107/1/49 108/1/49 56/1/49 +f 55/1/50 56/1/50 108/1/50 +f 109/1/51 68/1/51 55/1/51 +f 109/1/52 110/1/52 18/1/52 +f 109/1/53 18/1/53 68/1/53 +f 110/1/54 111/1/54 16/1/54 +f 110/1/55 16/1/55 18/1/55 +f 55/1/56 108/1/56 109/1/56 +f 112/1/57 57/1/57 16/1/57 +f 112/1/58 16/1/58 111/1/58 +f 113/1/59 70/1/59 57/1/59 +f 113/1/59 57/1/59 112/1/59 +f 22/1/60 113/1/60 114/1/60 +f 113/1/61 22/1/61 70/1/61 +f 114/1/62 21/1/62 22/1/62 +f 114/1/63 115/1/63 21/1/63 +f 60/1/64 21/1/64 115/1/64 +f 60/1/65 115/1/65 116/1/65 +f 58/1/66 60/1/66 116/1/66 +f 58/1/67 116/1/67 117/1/67 +f 59/1/68 58/1/68 117/1/68 +f 59/1/69 117/1/69 118/1/69 +f 19/1/70 59/1/70 118/1/70 +f 19/1/71 118/1/71 119/1/71 +f 23/1/72 19/1/72 119/1/72 +f 23/1/73 119/1/73 120/1/73 +f 71/1/74 121/1/74 122/1/74 +f 71/1/74 122/1/74 75/1/74 +f 120/1/75 71/1/75 23/1/75 +f 71/1/76 120/1/76 121/1/76 +f 82/1/57 122/1/57 123/1/57 +f 82/1/57 75/1/57 122/1/57 +f 82/1/59 123/1/59 124/1/59 +f 84/1/59 82/1/59 124/1/59 +f 124/1/61 126/1/61 84/1/61 +f 126/1/77 85/1/77 84/1/77 +f 81/1/78 85/1/78 125/1/78 +f 126/1/79 125/1/79 85/1/79 +f 72/1/80 81/1/80 125/1/80 +f 72/1/81 125/1/81 127/1/81 +f 73/1/67 127/1/67 128/1/67 +f 74/1/68 73/1/68 128/1/68 +f 73/1/82 72/1/82 127/1/82 +f 26/1/70 74/1/70 129/1/70 +f 26/1/71 129/1/71 130/1/71 +f 74/1/83 128/1/83 129/1/83 +f 25/1/72 26/1/72 130/1/72 +f 25/1/73 130/1/73 131/1/73 +f 86/1/74 132/1/74 24/1/74 +f 24/1/74 132/1/74 134/1/74 +f 25/1/84 131/1/84 132/1/84 +f 86/1/75 25/1/75 132/1/75 +f 92/1/85 24/1/85 133/1/85 +f 133/1/57 24/1/57 134/1/57 +f 133/1/86 135/1/86 93/1/86 +f 133/1/87 93/1/87 92/1/87 +f 135/1/88 136/1/88 27/1/88 +f 135/1/89 27/1/89 93/1/89 +f 136/1/90 137/1/90 27/1/90 +f 90/1/91 27/1/91 137/1/91 +f 138/1/92 91/1/92 90/1/92 +f 138/1/93 139/1/93 80/1/93 +f 138/1/94 80/1/94 91/1/94 +f 139/1/95 140/1/95 79/1/95 +f 139/1/96 79/1/96 80/1/96 +f 90/1/97 137/1/97 138/1/97 +f 49/1/98 140/1/98 141/1/98 +f 79/1/99 140/1/99 49/1/99 +f 141/1/100 142/1/100 48/1/100 +f 141/1/101 48/1/101 49/1/101 +f 142/1/102 47/1/102 48/1/102 +f 47/1/103 142/1/103 143/1/103 +f 143/1/104 15/1/104 47/1/104 +f 143/1/105 144/1/105 15/1/105 +f 144/1/106 145/1/106 12/1/106 +f 144/1/107 12/1/107 15/1/107 +f 12/1/108 146/1/108 4/1/108 +f 145/1/109 146/1/109 12/1/109 +f 146/1/110 147/1/110 4/1/110 +f 31/1/111 4/1/111 147/1/111 +f 147/1/112 94/1/112 32/1/112 +f 147/1/113 32/1/113 31/1/113 +f 35/1/114 44/1/114 149/1/114 +f 35/1/115 149/1/115 148/1/115 +f 34/1/116 33/1/116 151/1/116 +f 33/1/117 36/1/117 150/1/117 +f 36/1/118 35/1/118 150/1/118 +f 35/1/119 148/1/119 150/1/119 +f 150/1/120 151/1/120 33/1/120 +f 34/1/121 151/1/121 13/1/121 +f 152/1/122 13/1/122 151/1/122 +f 45/1/123 13/1/123 152/1/123 +f 154/1/124 46/1/124 153/1/124 +f 153/1/125 46/1/125 45/1/125 +f 155/1/126 46/1/126 154/1/126 +f 152/1/127 153/1/127 45/1/127 +f 155/1/128 156/1/128 3/1/128 +f 154/1/129 156/1/129 155/1/129 +f 3/1/130 156/1/130 44/1/130 +f 44/1/131 156/1/131 149/1/131 +f 157/1/132 6/1/132 158/1/132 +f 6/1/133 8/1/133 158/1/133 +f 159/1/134 37/1/134 6/1/134 +f 159/1/135 6/1/135 157/1/135 +f 5/1/136 37/1/136 159/1/136 +f 5/1/137 159/1/137 160/1/137 +f 5/1/138 160/1/138 161/1/138 +f 5/1/139 161/1/139 50/1/139 +f 50/1/140 162/1/140 51/1/140 +f 162/1/141 50/1/141 161/1/141 +f 52/1/142 51/1/142 162/1/142 +f 52/1/143 162/1/143 163/1/143 +f 38/1/144 52/1/144 163/1/144 +f 38/1/145 163/1/145 164/1/145 +f 9/1/146 164/1/146 165/1/146 +f 38/1/147 164/1/147 9/1/147 +f 166/1/148 9/1/148 165/1/148 +f 166/1/149 10/1/149 9/1/149 +f 8/1/150 10/1/150 167/1/150 +f 10/1/151 166/1/151 167/1/151 +f 8/1/152 167/1/152 158/1/152 +f 88/1/153 169/1/153 168/1/153 +f 78/1/154 169/1/154 88/1/154 +f 170/1/155 169/1/155 78/1/155 +f 77/1/156 170/1/156 78/1/156 +f 171/1/157 89/1/157 168/1/157 +f 168/1/158 89/1/158 88/1/158 +f 83/1/159 173/1/159 172/1/159 +f 171/1/160 173/1/160 87/1/160 +f 173/1/161 83/1/161 87/1/161 +f 171/1/162 87/1/162 89/1/162 +f 175/1/163 83/1/163 174/1/163 +f 83/1/164 172/1/164 174/1/164 +f 177/1/165 176/1/165 178/1/165 +f 178/1/166 176/1/166 76/1/166 +f 175/1/167 174/1/167 177/1/167 +f 178/1/168 175/1/168 177/1/168 +f 77/1/169 76/1/169 176/1/169 +f 176/1/170 170/1/170 77/1/170 +f 20/1/171 179/1/171 69/1/171 +f 63/1/172 182/1/172 181/1/172 +f 181/1/173 20/1/173 63/1/173 +f 69/1/174 179/1/174 180/1/174 +f 179/1/175 20/1/175 181/1/175 +f 183/1/176 69/1/176 180/1/176 +f 183/1/177 67/1/177 69/1/177 +f 184/1/178 65/1/178 185/1/178 +f 183/1/179 185/1/179 66/1/179 +f 185/1/180 65/1/180 66/1/180 +f 183/1/181 66/1/181 67/1/181 +f 186/1/182 65/1/182 184/1/182 +f 186/1/183 64/1/183 65/1/183 +f 187/1/184 188/1/184 62/1/184 +f 189/1/185 187/1/185 61/1/185 +f 61/1/186 187/1/186 62/1/186 +f 186/1/187 189/1/187 64/1/187 +f 64/1/188 189/1/188 61/1/188 +f 188/1/189 182/1/189 63/1/189 +f 63/1/190 62/1/190 188/1/190 +f 104/2/191 99/3/191 190/4/191 +f 110/5/191 106/6/191 105/7/191 +f 110/5/191 105/7/191 191/8/191 +f 115/9/191 113/10/191 180/11/191 +f 190/4/191 115/9/191 191/8/191 +f 167/12/191 192/13/191 158/14/191 +f 150/15/191 147/16/191 146/17/191 +f 190/4/191 193/18/191 141/19/191 +f 190/4/191 177/20/191 130/21/191 +f 141/19/191 145/22/191 142/23/191 +f 100/24/191 99/3/191 101/25/191 +f 97/26/191 190/4/191 98/27/191 +f 101/25/191 99/3/191 102/28/191 +f 103/29/191 102/28/191 99/3/191 +f 99/3/191 104/2/191 103/29/191 +f 98/27/191 190/4/191 99/3/191 +f 190/4/191 105/7/191 104/2/191 +f 94/30/191 192/13/191 95/31/191 +f 158/14/191 147/16/191 157/32/191 +f 158/14/191 192/13/191 94/30/191 +f 147/16/191 158/14/191 94/30/191 +f 150/15/192 146/17/192 151/33/192 +f 150/15/193 148/34/193 147/16/193 +f 159/35/194 147/16/194 160/36/194 +f 159/35/195 157/32/195 147/16/195 +f 192/13/191 96/37/191 95/31/191 +f 96/37/191 192/13/191 97/26/191 +f 190/4/191 97/26/191 192/13/191 +f 166/38/196 192/13/196 167/12/196 +f 166/38/197 165/39/197 192/13/197 +f 192/13/191 165/39/191 190/4/191 +f 165/39/191 164/40/191 190/4/191 +f 105/7/191 190/4/191 191/8/191 +f 110/5/191 191/8/191 111/41/191 +f 107/42/191 106/6/191 108/43/191 +f 109/44/191 108/43/191 110/5/191 +f 106/6/191 110/5/191 108/43/191 +f 186/45/198 191/8/198 189/46/198 +f 191/8/199 187/47/199 189/46/199 +f 191/8/191 112/48/191 111/41/191 +f 191/8/191 113/10/191 112/48/191 +f 182/49/191 188/50/191 191/8/191 +f 182/49/191 191/8/191 115/9/191 +f 191/8/200 188/50/200 187/47/200 +f 182/49/201 115/9/201 181/51/201 +f 181/51/202 115/9/202 179/52/202 +f 115/9/203 180/11/203 179/52/203 +f 190/4/191 116/53/191 115/9/191 +f 117/54/191 116/53/191 190/4/191 +f 118/55/191 190/4/191 119/56/191 +f 190/4/191 118/55/191 117/54/191 +f 183/57/204 180/11/204 113/10/204 +f 184/58/205 113/10/205 186/45/205 +f 114/59/191 113/10/191 115/9/191 +f 113/10/191 191/8/191 186/45/191 +f 185/60/206 113/10/206 184/58/206 +f 183/57/207 113/10/207 185/60/207 +f 119/56/191 122/61/191 120/62/191 +f 119/56/191 190/4/191 122/61/191 +f 121/63/191 120/62/191 122/61/191 +f 146/17/191 152/64/191 151/33/191 +f 194/65/208 153/66/208 152/64/208 +f 146/17/191 194/65/191 152/64/191 +f 194/65/209 141/19/209 154/67/209 +f 141/19/191 156/68/191 154/67/191 +f 153/66/191 194/65/191 154/67/191 +f 149/69/191 160/36/191 148/34/191 +f 147/16/191 148/34/191 160/36/191 +f 193/18/191 160/36/191 149/69/191 +f 161/70/191 160/36/191 193/18/191 +f 149/69/210 156/68/210 193/18/210 +f 193/18/191 156/68/191 141/19/191 +f 190/4/191 164/40/191 163/71/191 +f 193/18/191 190/4/191 163/71/191 +f 161/70/211 193/18/211 162/72/211 +f 162/72/212 193/18/212 163/71/212 +f 194/65/191 146/17/191 141/19/191 +f 145/22/191 141/19/191 146/17/191 +f 143/73/191 145/22/191 144/74/191 +f 143/73/191 142/23/191 145/22/191 +f 190/4/191 126/75/191 122/61/191 +f 128/76/191 127/77/191 190/4/191 +f 190/4/191 129/78/191 128/76/191 +f 125/79/191 190/4/191 127/77/191 +f 129/78/191 190/4/191 130/21/191 +f 130/21/213 177/20/213 174/80/213 +f 177/20/191 190/4/191 176/81/191 +f 140/82/191 176/81/191 190/4/191 +f 140/82/214 170/83/214 176/81/214 +f 140/82/215 169/84/215 170/83/215 +f 168/85/208 169/84/208 140/82/208 +f 190/4/191 125/79/191 126/75/191 +f 122/61/191 126/75/191 123/86/191 +f 123/86/191 126/75/191 124/87/191 +f 131/88/191 130/21/191 174/80/191 +f 131/88/216 174/80/216 172/89/216 +f 171/90/191 168/85/191 140/82/191 +f 132/91/191 131/88/191 134/92/191 +f 131/88/191 171/90/191 134/92/191 +f 131/88/217 173/93/217 171/90/217 +f 134/92/191 171/90/191 140/82/191 +f 172/89/218 173/93/218 131/88/218 +f 190/4/191 141/19/191 140/82/191 +f 140/82/191 133/94/191 134/92/191 +f 140/82/191 135/95/191 133/94/191 +f 139/96/191 135/95/191 140/82/191 +f 138/97/191 135/95/191 139/96/191 +f 138/97/191 137/98/191 135/95/191 +f 136/99/191 135/95/191 137/98/191 diff --git a/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg new file mode 100644 index 0000000000..1462ee5e55 --- /dev/null +++ b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg @@ -0,0 +1,33 @@ +[general] +definition = anycubic_kobra_s1 +name = High Quality +version = 4 + +[metadata] +global_quality = True +quality_type = high +setting_version = 25 +type = quality +weight = 1 + +[values] +adhesion_type = brim +brim_width = 5 +line_width = 0.42 +initial_layer_line_width_factor = 125 +layer_height = 0.2 +layer_height_0 = 0.2 +material_bed_temperature = 55 +top_thickness = =layer_height*5 +bottom_thickness = =layer_height*3 +infill_sparse_density = 15 +material_print_temperature = 215.0 +skirt_height = 1 +speed_layer_0 = 40 +speed_travel_layer_0 = 60 +speed_print = 180.0 +speed_travel = 300.0 +speed_wall = 180.0 +speed_wall_0 = 60.0 +speed_wall_x = 180.0 +wall_thickness = =line_width*4 diff --git a/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg new file mode 100644 index 0000000000..3b3280331e --- /dev/null +++ b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg @@ -0,0 +1,33 @@ +[general] +definition = anycubic_kobra_s1 +name = Standard +version = 4 + +[metadata] +global_quality = True +quality_type = standard +setting_version = 25 +type = quality +weight = 0 + +[values] +adhesion_type = brim +brim_width = 5 +line_width = 0.42 +initial_layer_line_width_factor = 125 +layer_height = 0.2 +layer_height_0 = 0.2 +material_bed_temperature = 55 +top_thickness = =layer_height*5 +bottom_thickness = =layer_height*3 +infill_sparse_density = 15 +material_print_temperature = 215.0 +skirt_height = 1 +speed_layer_0 = 50 +speed_travel_layer_0 = 80 +speed_print = 300.0 +speed_travel = 300.0 +speed_wall = 300.0 +speed_wall_0 = 200.0 +speed_wall_x = 300.0 +wall_thickness = =line_width*2 From 9acb0ffa6b51b70302af1299cf731e5b701199eb Mon Sep 17 00:00:00 2001 From: HellAholic Date: Fri, 12 Sep 2025 09:02:23 +0200 Subject: [PATCH 90/98] replace svg with png application consistency --- .../definitions/anycubic_kobra_s1.def.json | 2 +- .../anycubic_kobra_s1_buildplate_texture.png | Bin 0 -> 15318 bytes .../anycubic_kobra_s1_buildplate_texture.svg | 35 ------------------ 3 files changed, 1 insertion(+), 36 deletions(-) create mode 100644 resources/images/anycubic_kobra_s1_buildplate_texture.png delete mode 100644 resources/images/anycubic_kobra_s1_buildplate_texture.svg diff --git a/resources/definitions/anycubic_kobra_s1.def.json b/resources/definitions/anycubic_kobra_s1.def.json index 8cedd8c591..6104d1d54c 100644 --- a/resources/definitions/anycubic_kobra_s1.def.json +++ b/resources/definitions/anycubic_kobra_s1.def.json @@ -11,7 +11,7 @@ "has_machine_quality": true, "has_materials": true, "platform": "anycubic_kobra_s1_buildplate.obj", - "platform_texture": "anycubic_kobra_s1_buildplate_texture.svg", + "platform_texture": "anycubic_kobra_s1_buildplate_texture.png", "has_textured_buildplate": true, "machine_extruder_trains": { "0": "anycubic_kobra_s1_extruder_0" }, "has_variant_buildplates": false, diff --git a/resources/images/anycubic_kobra_s1_buildplate_texture.png b/resources/images/anycubic_kobra_s1_buildplate_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..1f80dd346de2c8f6b9a3bac8c4d29825e36cb966 GIT binary patch literal 15318 zcmeHtc~q0xwr7-5EGLYVA~I=EDOARw$gC1TL7+q<3IZw;W{`PKEKv!{P-+CisBi%V z0g)jqVG`o8z>>t3t>>i*UemfvvLXAi&q+k2n* zUu-P4Y}~yO0)cEfeF|X*fvn{~AQDm=)`L4&&Daj$*P0+Zi<4`5-yg<+|NP*7!ukXR zQl7kN>FPT0|2?j!?5rV>$O90_&D#*jBDi&P3<3$!gh0Msfk5=rAP|`wnRRCk!GrbJ ztSk`VH@F0*MDc1r-Z*tB2m(=87XMolo^KKgZb}B9wlJJLcom&fR^t0)i8sJTZ6sfIW6a`DaI(PFEz=e`U+(*$p&{+F)nbrv9VD%0el$xg7g`) z|GM!}SP}LUtxXC7nSU|89jM7e5&}UOKpEo3W5J;}V_Zxfq*8I;K+kid9Hz1u` zzrQNJvHI~FjJ zQ?mbMG3T}4cmKEB|Icf#rg-k8$(2G{1PY1NJ9>1^RKw&yb@i7enQZB;Qc_A8c$d1e z7G3KKpBpzOgUo^BIwJp6Z=;O|b@Y8ruHhWG^Q4#HfT4_86fqdrH(|{98 zReDe<@h&;_MRT>OaU3JkFP3l(<-h=jRN z;_{H4<@oxA``qEH8kx=>=_3dKc=YS9zqaEeJP`<#x`M211pSp;Tea|*va+(u^%12o zWl^6^VNg{$1k#ulXlzr5YEP$EJCxXVTns@XE`QKze~d)>`lT1Bz?BrC8t*7Aw!AK# z_9#h7NtW+n2t`UvJr@*#L1IbFCD`_?i*PNI+v8ru5D9cu=QD#jg`M3<_ zLE_NRkV_eRaedyO%D=*GXAEn#rKlAr9UH{TTtLp&W5chbxJXOcWUQR>;H&4)pEo$y z1`Z!F2cHo4^s(!VZ*=CGVU(b@1F!wg<>P=>ewEq;w0p0Ylr-`E2^g6e zQJYflnQs5s4jrfwy8PsdB;=+-n?Qf+d0}=P@^g$T9 zGS<{jcGs?B^q(P+E1^LvC%RTvR#;R@vKit!3em*oefjN>@=1_=8bi;E0iXtx!4KKMnSS(R%6cIzyaZCWpU%hS$v_`{{^=7-yg)yRdB&kpMpqaV3Bn=nxlEaWV%4-T@ z-ap(=ZKY^%zIu}7pccD9uB?dy`|ano3k<}jH}N4bc=1@>>mw(i0Z7$hZ+wKSYQ70< zYce(;-zx)6eQa0SHz$u5!1KV0eoEAq(-!9D#?sIhv!)CS3kwrFdI-KYX7*dRkig(z zu-FzvQ&5VA3p)NR0Eo3_!guf9ol_nhv}@fVAtBK~AT8g$dpFw&*==vBDvG*t<@2TQ zzQp$inziMquUxkf949 zAxd?)nM+mKA|)m2^bux8etS+tB4-D{|(1MabmM6C8SO=X&BKO1LFk=JYmz5OQ}4JO#XOvLuTJ}vj8kxD_a7l4bu%d`DSJEAUoC$rI!cJMBO@&%UfQ}u zS&SY_>PEAyyWxw%!qLV8&lE%x0yUs%Y>W8Y{|!VlSBm1Ln&sdnbd zgUytsxv{aaQFa~q_YJ^bWe`zLbg6Fu_}G-_xvilLJ4*o%7&S)+P}@)Bh}x=az}*YE zdp{||=CIggDPWObwZvXvF8uJ}gETzIbDmyosaocfK5_&K&ByrzweN(!&Bskqe9W4j zi$QVIE)HHW!@YLSGNKpIy}Oj9t6|JsfV}?dF63xUSjSjtRNePAf!!Oq)SRuGH%FVm zu6cQRoeSd+$_kx6BrfeQ%nY?Gq9cnLDB9pyd`$^cb;Zu7&eu?8tucOTXqC!7&3)hBc+^1ne_*9ipiO3SC}a zjs^KJheAOz!$tOD=;wG-XMfBnsD0M$Wo_0(zQ0o~(h_m`Vz{BB*0Gmhj`r!NkTAMZPavcZI2+~2gRK{=(2{u?}qc@Cm{)0pm6)Ebt3e;1H z!LdujOc#QVHQzlSXK&F&uls_Y{8dtM;j;KmgR0RlnNjT*$!;TeW4G#$;hAAwdAPuy z&!0bAD8tOn&B+%m1raDzakoAUrmndgc$$~7dh+-gjWGSJ72l%`9?jKTXl)=c8jiws z!(7qv<>K%#2i{>%YvY6pr)X^=QD>n=Z)3gCOs-b1ND8i2%3Atoz zOAT(!G1Aeggu(q)^E0a+ooJsch@c$)(UY-E%0Ev z=2oh7_mRi0uC6lOAu&WKu!=f%)wlZm2qqc$hK5)sSn^z|hRe#2s{xe3C$`j2CHni! z&&))BQM_dYyB2_mj=p~VtJ^AH(gn#bV$4)&F%CL)8;iAT9l?Iu22C|@QiFCnT1K2g zDQ4g?pNziFU>EAY$9@#b1|#myuo@LN?&PjoU@%DF4G;ZuV~Vy3b4XP}7MQet>Z(wv-ukjyOm1dOJbDlI5w1E(cX`4 zRV|zmQy3VmLp}d86zYF;6-@OSY*#U}xM=Y>kd@M}0?Wk(#vuB4L1Ul+H&E)|N?Dos zEMMwb%UYi+SFSvt0{UI*HyU|@4YxscTn;;qkk@P@NBNG*N6Bpgso`8Dh!=xC5Zwk5 z>Fh-CM^>+!S*iSG2ndi$hZ0lmAQnabK32}gWO>FVVtfsNd<)^X?&6$oud4NMzAUuU zB*;;9_?um;N=r*i9LNjIU1EgHi)l|kQdUy*O&Z_Z*NM)@@%qDu5-|%a^9`b{fAGxI zLs_AHo>3qUJIAl^(7%4PuWM`gIsfucP)8U{oBwXVRZ9I^@oH=GVgTpVNF$3PRfl6z zokDwE4YE3}=zc8Qy=zza07yu0Emc!P>1uGWhl`CK$ykdp%z!>9wYXd4j_PFDxS!J< zl(9f`g~XWBba?3l4W(=yF*&aBU{whDt#zdUSBi@Ix||5%&1m~jOttbgU9pgN?$0sm zOj-)}b`QXS(6mwtxY-`K z=rBCT!Sd2WJdF@pZv-z(z)~OEbtvxI^~py}P#Qapb##KgkVqa;hPdJp96Tc%;>5bL z%IWnQqQsQMw5TYZEE$C>K|5R4=HjMl*qV@LZZaSSO4_T=J7<^~f8>YTepA}$NzXa)G6#m~}9VvT64Qccm;F|Mcr znEH?41@78lXIZNQOWFmB=Mf)aS4bAVwqD2`*)Oinuuw%V}1CP7N{X2RNZm_ zw5txYp#i{)VoENsL4QjfrYHk-Ig}sm#R$ULp^qFpwh-0~Wa8hLPXqpIcnB^!3d?ycr%ze5Af8qVI9`J4dUjKmWdLA>51>v{+`FUw3S%9*7pWMx9nOI#by70 za!H-hqpQ4NFE)Z|p^!x%0)&nX_}uqz9NPuDxpVXLN)PauAeUtSDhJ?D|I$QAii$3TVwTJD^734mC}9BDb!`B!$XYSWdjt(=qlCPD`_@&Q=ljpn zXPv5n`y|Q&$ENr_RZqsJmDh(e67jXyBWrQmejtUtoa{ui6OLoJhZ#BZY?brKj70D< zx6AoSi(o2YcnD;NKIjw%mYapAQYoXh{_=8iLMd^QT$eQGT}GPAkW`AEw1$=+BP>3` zE30Z^|18=k(ZUW+JIw;{l83JWnxii#_b zu;G9~&~#jO$;&TY3R}JpKPN8#4qsLT?(k>lZFPMow+<6*pH6$hh2mL`*#F;Cd_i{A zNMA9%Izq@kYR$(}meAq~U9uUZtktWr|6Q%|7eKy&R`!D(*|nyE5*_2E1MJnE=|Xe1 zjPOAsl?LL6bj@#r?&(eSuw*e<)PCB9p8R%k@l0#>U3-`a^R-@aw|{ zvhh*o2%McCmC_)upb!Iic%FC@VO~^$%No4J8I4ps^?^`vN{q(~xV}lfMzED&(5?`g zHx;is$oY1`kHz1f!qKF_6pnvJEeRLssp|GhC=38}N#G>*Gtl(Heu|2GiVJL`Y%w)8 zl}g681QVQe03Ut^Bv(nbOUi)GQjdOboN46G*lG|QO3I0b7m=j!$Xd_zst~6z7>vx% z;w?F;y44fMZ~VDzjc?@Dz`*I7zPDD)m^nikM}6x&BA4%OkxQ3tnUw{-2a0Q@`v~TY z2B0=egz;gJ5e(JfISl6L8#vW4sBNaYt`uIE+Ksb2DhJ-~_w3*)!a+)F5DJAB@Sw9O zvM-=P4G2JWrfOs{`Q0EP#9|nT3wi+6yP@tK7iV8$j~#?XD#I*Q zO-L(?Y!f4X8YOyqCQx_DDzNTqr&Q0yR-uTH3xR#HoG>MBAoSlBDsx=-Uh>I-F|{9o`y1m0zm8wg$iKWD>N4R+ zF2GF_@^Nxvn$ek$6I~UjaB(XOKrEBk#sg{nSR`=F1iwa6G%&{w02opLS4nbQ4c+hj zsjTSRS?2MEh@s}hx6F`?x?jo1V7aAihNY@|uvElX(1Q7^jrfTqH)aArn1etxfomqt zTE>>AbL7Zu1s7e?VmP98U)(tXT-i!BgJ@ESdNBY<7Hfw14U}PDm?0pk#EGr&#!Q&V z{5`gS4tl<_P)F^GkS3_bL`CH6H;^@sd}J<3%=-W+0_3Q`Q=O}>SPz~oyEiQrV6jC- zexP5)x{p;sAg+(7h&y3BHTyup)8Jy+n}#m|(D+d{qrfB~9e#2}!`#9`)Gy|g!oa7B z9^v5&zR)QEfxh?%W>;6&9dVbh8oFx2NjcvEmS-7TU-??Yh2p9(kN_3{(`*lMN{;-4 z2Ls(d(&krU!~IZPT@o4hF&^7yrJ5tJq~zjApRHN-6$^2<3uNC4XeTjm*;3U-Nlwo3 zU_T(z!jUDeR$+GJH@|!wzc7}BdsU7LJRK&m2jzzHH5h2)ie8qL@zk1uIG_h2>V@&mx-q#9_DT zCr&<2jhPd6rcAt~-*_p*f6-~SsA%iAQubxEVAyZXkKnBx9B9BfQ1~hyBV7}hP|yI2 zd8b(tC`>E%!CBqRr6DmF|6@VWr(UgIPJzI*Ez`*NxrZI&@fzxjid;aiur5F(yXyW4 zb3F7sk#!F%r(ID9bYTWRYVAT_`HgD;sxlXGgZ!pg$#Yesi?_vHy6nl@Du(5_k1VgM zqwSw&ez$7{RjCDH2_SUzcz!B+ARoLAt|fDZ+q?zHwP;UNHquhi4f;wvF+fb*@&HYq z08jz1$;Yku5~>&+SM4AqqF+-_Pw+-uU~+iTqXwAkfZb^3WsMH~13-uWvm&pb>sU7< zr4+kpv>;F2K8>UeygHDwqs@h~?wJAMDD+{NgR?VeFesTv0Y`$;UOQU~Y)~s6i;i~g ztrQEhCb*?47*bR)mocb)s}qp{o%R(Z(hPAqq}MIgC?bjwEDs7*RXAXe4G!dJ!TVK6 z%;0O-AIwh(mGKXd0=>IW5kV_9pwE#5yEdK>&Kf|=m^B^BDCoWR7ci~v|ND8++Is$LTKNBmqJX{pGZXvU z?Duce&%bo`&lA@F#t=G}X$7qGFMmTk#{O%`|IiHoegZ6h<6j5ef5_)8;Ua`U z0<6x*S9c?R`0p3~_Z$C@7yIvwmO(cBKN#izslUHT_U{MM$19@%r2WfrfxlV#ztrOY z!K)f_5? zf#SfNmuAz;M9bKkFK;%9rbMNV^HcNkn58Mv?z4)a0}-PN>L3iS$T(K}TzZV>Wv8WG zjF~8J$v%%1l!mZ-6lgkGJ4!AHjl>=tS}sGLyaNscpp9V%tlrN9qH{s1zb0TPB)^GD zFL5NC7uSdCfpfj?JUqU|De(P59hi@Xxi_zI%rjDodAB!CG@s#54zNbeR_l_dGWAGS zdE(M8$}`z$F4CQ*Q=Dx0YJQUP%^FT{O9`8wRU0Vr9Hdz&F@qk8-&+^DA{aF>tdI{M zj8{Qd5p+%Dp)p~JbNyk-Mn&+We)Wsz@DUWCrHYGUN!At>&3yoL%0Mie4m>FRvW5&Qd>^%Tz*z+goOS#&>wu6v4APnnCzPHZmzS7jx^J2VoRO$Zq3 z&hf2^yx`Vs4Af_PX#ao=bSSH%3kn@>El?c=9ou7ooNe_dM6m)GzJ!>ca~DCiAY2tQ zmKn<6B-j-42gFjwLx*pdyjFD0KeKF&9QL+~*SJ2i7b?I~rcL1dVOk5S`8bi(vXURX zvh`ME62}UloaU3#ii--c4_LoiSHJ@AXZf%RR>>QZb!Bu>h zKX`tO1h?IVg%YWd zKCf#b`+;5~3EB-Bw0zyYePvjz(h=*5-%S8EbN#m(&3rg#HU7eAvQc85nn0_rV9<7| zwW_4ptr+JzM6@5FFIl0u04${ zhu)LI!g*zt%o|IGddfsi?%G2&9FC(Ue}vL+0<%v_PGyaA5~=22ldZ-x?U5xIl5k~; zyh(lGW8)^5@Cg|(vT=@?eh(^O0<~=zP|OeAG|);73>=){cL*0~%B@Pwx7<|R!#J}h zq>R%{vb|Z;m;5FI7p>P`RSuG;rmAj-;m!`0m!m0KE%K@O`&Rfx8&tL?V-3ES;Wv-|+$3$gWRCp(>S%Tp zm)Fl29hnAYf#T%1x{kM74JI~$)*hyxP8i#Pd_3CzilLB?JG3HQX!J8&sz21NP`V{p ze@H|i5W?E0T9iaB;&|fT31J&g)lmw<_#^F@3c7QYsHMldn>W&8O^awJ8Mz~oA~l$8 zUucZ#@ICL++QlE^Hk$abQH35OfE2L+R#CNdB@5s=pt!Hk$#W zufXxOGZPhbjowbakK7J$M(R8#lvfN!32~r@cnbmF_+Emh;VZwlvP)f`*fSW6?)e*FVmxr!2v5`DF!*}^DJ zDAgV|_}Y2P0w)FE>6c1CG!?tQKszA}_XeesQRtr1-$_%W3oOXln` zvA33s>;|QYi>FslB`neW-)y>ISzC%*h{tf2js{p8K%{=g3+6u6dVliwP4AGa8*Q}< z6cyc*!EM)Q<*M{r#)lzr;0#y}Me(uL1}r+x~HT4~DD-2nt$ zy*6RS0q(P+XnN_&&^}lTd}#7g^}wzNRw%s@a9#?FWfDx-+E6GVOq9qoT5f2-1^OZq z+)!XV+_$(}w3zk~Z^BqqR?;8xafD5SD&9ytNW^W;v1q!EkOz~70-@nnEH>LWTQp+V z`jFD^%@|FUW!-yMn7Z91@n>*ajfr818rlpv5W^4AF)GPQ1_0D`5tX6$vC3BQ9o_aW z%lGUCV`v6vY*H}sKeJdXN?V6>GEmbG?`vd7V-Wo>^CW@Ja~w)~Ho3Dd7=r?mV5O?m zAIj(%-DQL?8P&0#IHISAwnIcv6I>+;KJh_)oav$>FD2UenH4j{N_QxXJLl+6nq}j| z)<@Atq-_lYOp!<->Zk?I&Rl~WzVr#Czm>(`cgqRm3ZDDDr{tlQ2w}P#l3OWxu-qWP z4g6~O^M*A-i}$l2U!ENr%Hy^A6r>+0BXX)4*-B?us9Wtqy}(hkHb56bZ>^9(I}DJf zAa?;vkO+3M5K9@6K_XknJY;>+!-Yqqd^6M1!s!bOW0#;)JmS$lS8GItmx0L7dHY#) zl2A)f05AsKKv_70xP)GsDx=!Ce}h5;7&A}t0cXN;=x0Pv9W42E0I6F{l>&L&}cSdo|kHii5mwU)Q-92XKH`a8u-v6cF_`Y|(AHCl|`pv6*n+88R zacDK%KA4}w5bUelh+Q$`TKJUPY1jw1@*TH|t9t6lJ9_(nfF9kp@z?7If~fwYwD>c3 z@!Pz$4ll=K$4QQm0TR27M7ehujk4tAdNpdgP z@2QK^-CsCmj3PDZSjA3-AKox|zcA%>Y{4b{>T{fg6FsE-wPyXWvMo8Z8OWxgUKq?>m$A-{`UBA(>*uK zpwHb9HPoRgvds^NTFSYTg6MUWXs5Zrk85F7w@!6m#*lPhgs|!~H)p4(H{3c^0?|~j zWe(82E-ZS(`qS#lYZxI+X*6a<1E-y>v$;s>hmEW?9$sAiJf@0GCKm3#e01gA17!>6 zX~EKtsqkpiln^lf>8VLM<-7iwzKP2+!EWhzv$mX}BDJf&{?;W2{@vG`CggcsV^3Y( z`6b9_mmGA**ypFFi&m8WPqt&2X9dmV1M*vW>%t?vzP&B*H|*_DF7O@a%Lk*^J{B< zR!dmvle;@Sr?hb3%o283~ds1Ylv6}ij?~~R{Mxs!VJ9z6eY0ND* z)AN5~Wu9tcOHOIeEiSLy;NH_cg*^NXbL zs4N$IdtFtaE=_mQoqMuO2bGi_I^CO5tu!6}A#?L`bCQQnU1Ue59yK3b_27g0?QJJd z-QY>XoNewJoR>F_4zIHNCMR*(-+RxtUm#@2Grg#}eWCjpJXB(1qH|eTXi|jS5?Yi9 znZ|n9R8RV+k6fc2K60jFMw>pIXF26ku&&Eu?XQgrNkfO(T<$?sj})x8V?oEr$SBcr zs(l$Tn?+gBsz5&}DcQeq#sRAj zcoT((6A|V~`lckiv@K6wzI=IOBi!vSS2)e*tAGXX28+5woBB#B{+AQC{gLO-f32#nUXgTHX)oVR?!t-qR zPu!)It1g%vKYsF_qb_Dt)wo!~;XjKm*P1W&Y52gOc<5CsQ&yQ3AnYmX{ zDltz_HKBEOS0#U%t0SRJb_R!pShGnd_T)yxst)Wr91v|R%1Rnqf>)4rt@H*AZhUSu zMx{R{sz;)K7&{2zNyfTj&LEzWhcAXD`<5}h8SJ_3vU81IX?F`(lJeg$*BtlfjmH~J z)GeGyf-F23I@WM}ZUmvwlB^{9@rh&FZ0py`rn}@XQl{};9UfgPQe||z9rf{^vFhKfpSX zZd|QudG%r=O5woAcv`GMY1i)gxVKjeOFPJqPcdl*8awaL9wpYTOvv9Ss{eE_#;5m{ z8}s#*8<*zdyw=>D+wZSf|H$LFIQ7S?uDjXp20dLg{HS!4e>T4*7WMW?A-fG8pD8TOh%%i|~R%~e)ag>3)Qc4_SxH9b9j>-Mu+(3mT`CX?j#rq{xJ zCir{HemRbK3rUdZdiDE#86~#@7u1CNKkO=tYHGfga{aD*x=<7IERxCT8!&0?C-u%Q zw@-0kcI-#2lkST5Z62=~T4dNXYb6zxlqj@QGzxDG2j?fa8)nygF2Cr}K~d5I5NW^d zpD{}9<6^XeNbEX=_tpIi5h-<|?Z>xN`r?c6PN@}R%iEtn zI$(ONuRF-MJU0j!L~Q=N6}EBhaaY$NI7D>Nw^o-}7p@Iu9*ubA&j;r<;x4A_@bu$e z$~m(Ak#XAQ-xnNML2Lzlk%&OOK{f<@?ha%-MCGGju>LvzrdK@s7-lu6CGV`z9T6-G zZ%B2OQhwMrny&FZ%kl1<9GPYvsL44I=5N3~@5ucD?r#!2_Q4;g4TB&h&U0o5Gm?fT z#yEHKQhWL@UcPL&zk*0SzpW@oFNnJjhNKa*>g($xdf#ALPoqkUUc8u*{$jb0SdzJU zayKe8ZHmkuEEEWI_FC;~>y2tZo=@TS7vKF75d-EP`zh}~v{NSPe6-jtzfE)&#^L8>cI?;z$&~v1w!~2` zG{TTvr6}dg5BJEp8V>t%V<9WyW%LElcG3B>XQ6WNg6nIJOFnAYxn_2D*1>z3Gvg+s zQQZgOLdFgW1g4=~Jv~WhmS4dqpFHa_gq+&9eSU9Ap)$(&^^0PBFUQE`*W;fD@7>#o zYOWs8ck7|%pA;>~j|#TlLpsX6l1Cv=NTiqud;0sUfUMlc<|ya~FPKBjD49~v-m9spS*f)hE`RaS>Sp#M z$_&DM*Eh?3+YN$3a*j>b8Fd#J^*-KHCi^ZDadB`qvMnRSRk!(?;R)~gk3%iVB7|x> zet6`Kn~l%@)*hYp76t|egf+odi@{}d6J5o^Ezc+4Sx#+e3E&=S|M+0b7RRNeA;iXw z8_80?6%sr*Lksdb{JNr|u?>)%P6B6%Z4@=g-l_g#Zr6J9mPuzuwM<_%>1AD=eglZb zpwdap+o$h-=|!G92mc~%{<~XMRh4U7YwJKli)$*J&!;Te+1f50YK~NBqGa;*>yqp*XWbeN7X>`%iJFSDfyt89-|OmVTDjTwRQcu-&~&` zy*Vh$PK3R=idy)%9zUKny6x!`Lg99lao49$n%_n;M(b^6ref1#=BO0RZ^lo~1UAZg zSskKFdKSB>9fB(<=elUQ9&41=j*7cFVqU^D*!=d`55H8ek&S{>Y7ga3kg6c3Le0ie zXNcPx*U1ihSQH(4ci8xP^C5)%mFA}Nk8;is_4SjYBgQ^l<=`bann99KXNfU=$Q@7G zyh=vQhn&H#Mq@RINCmEXvv6+i!dr?z^y#K2-rCj6yCG%>`y7rpEkq#q<)z@f&h9PH z=XK=a4;bG|J$CI5j6V3_R5VS~uq0OSL7~F=4Mj=S%`m7m2VNp@yg87EE1!+7Kk&%u zmyaljI@GUmJa2JD1KwbbP=+jNf%CJOkZ_xn&XcyfCN*CA#SWo@FIr%Yx4cEDH>E>C}}HQZjVb@6o;z7)Qhx+#`rwVrvpi*diLR;~Af zOZzdGviDt8lcURr?Qj#1$A%Wu9xd8eyKVX?5#=Sc&+lPVbN=D-uvVQq;6F!ed!!`_ z@+?#DD92xe9qc%pv=&A=<;zZCrOxm7q={+W*9}|<#{IXoW4EcfwZB7d4K}UWj0!Hj z-N(R=t*;Hd^Id`S37+X9&5N%=QM0dd(<)!Nvuzcgu>3YIj@X6-1< - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 57073e2282643955d7d1ea7a920aa3ef948d7787 Mon Sep 17 00:00:00 2001 From: HellAholic <28710690+HellAholic@users.noreply.github.com> Date: Fri, 12 Sep 2025 07:34:24 +0000 Subject: [PATCH 91/98] Apply printer-linter format --- .../definitions/anycubic_kobra_s1.def.json | 20 +++++++++---------- .../anycubic_kobra_s1_high_quality.inst.cfg | 11 +++++----- .../anycubic_kobra_s1_standard.inst.cfg | 11 +++++----- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/resources/definitions/anycubic_kobra_s1.def.json b/resources/definitions/anycubic_kobra_s1.def.json index 6104d1d54c..09fc5c64e2 100644 --- a/resources/definitions/anycubic_kobra_s1.def.json +++ b/resources/definitions/anycubic_kobra_s1.def.json @@ -8,31 +8,31 @@ "author": "takanuva15", "manufacturer": "Anycubic", "file_formats": "text/x-gcode", + "platform": "anycubic_kobra_s1_buildplate.obj", "has_machine_quality": true, "has_materials": true, - "platform": "anycubic_kobra_s1_buildplate.obj", - "platform_texture": "anycubic_kobra_s1_buildplate_texture.png", "has_textured_buildplate": true, - "machine_extruder_trains": { "0": "anycubic_kobra_s1_extruder_0" }, "has_variant_buildplates": false, "has_variants": false, - "preferred_variant_name": "0.4mm", - "preferred_quality_type": "normal" + "machine_extruder_trains": { "0": "anycubic_kobra_s1_extruder_0" }, + "platform_texture": "anycubic_kobra_s1_buildplate_texture.png", + "preferred_quality_type": "normal", + "preferred_variant_name": "0.4mm" }, "overrides": { + "machine_buildplate_type": { "default_value": "PEI Spring Steel" }, "machine_depth": { "default_value": 250 }, + "machine_end_gcode": { "default_value": "; move printhead away from object\nG1 Z22.000 ; for object exclusion\nG1 E-.76675 F2400\n; fan off\nM106 S0\nM106 P2 S0\n;TYPE:Custom\n; filament end gcode\nG92 E0\nG1 E-2 F3000\nG1 Z24 F900 ; Move print head further up \nG1 F12000; present print\nG1 X44; throw_position_x\nG1 Y270; throw_position_y\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM106 P1 S0 ; turn off fan\nM106 P2 S0\nM106 P3 S0\nM84; disable motors \n; disable stepper motors\nM106 P3 S204\n\n; CONFIG FOR SCREEN PRINT PREVIEW\n; total filament used [g] = {filament_weight}\n\n; CONFIG_BLOCK_START = begin\n; filament_type = {material_type}\n; nozzle_temperature = {material_print_temperature}\n; bed_temperature = {material_bed_temperature}\n; CONFIG_BLOCK_END = end" }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, "machine_heated_bed": { "default_value": true }, "machine_height": { "default_value": 250 }, "machine_name": { - "default_value": "Anycubic Kobra S1", - "description": "Anycubic Kobra S1" + "default_value": "Anycubic Kobra S1", + "description": "Anycubic Kobra S1" }, - "machine_buildplate_type": { "default_value": "PEI Spring Steel" }, "machine_start_gcode": { "default_value": "M106 S0\nM106 P2 S0\n;TYPE:Custom\nG9111 bedTemp={material_bed_temperature} extruderTemp={material_print_temperature}\nM117\nM106 P3 S153\nG90\nG21\nM83 ; use relative distances for extrusion\n; filament start gcode\nM900 K0.035 ; Override pressure advance value\nM106 S0\nM106 P2 S0\n\nM420 S1 ;load stored mesh to avoid auto-leveling" }, - "machine_end_gcode": { "default_value": "; move printhead away from object\nG1 Z22.000 ; for object exclusion\nG1 E-.76675 F2400\n; fan off\nM106 S0\nM106 P2 S0\n;TYPE:Custom\n; filament end gcode\nG92 E0\nG1 E-2 F3000\nG1 Z24 F900 ; Move print head further up \nG1 F12000; present print\nG1 X44; throw_position_x\nG1 Y270; throw_position_y\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM106 P1 S0 ; turn off fan\nM106 P2 S0\nM106 P3 S0\nM84; disable motors \n; disable stepper motors\nM106 P3 S204\n\n; CONFIG FOR SCREEN PRINT PREVIEW\n; total filament used [g] = {filament_weight}\n\n; CONFIG_BLOCK_START = begin\n; filament_type = {material_type}\n; nozzle_temperature = {material_print_temperature}\n; bed_temperature = {material_bed_temperature}\n; CONFIG_BLOCK_END = end" }, "machine_width": { "default_value": 250 } } -} +} \ No newline at end of file diff --git a/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg index 1462ee5e55..fe48dcc12c 100644 --- a/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg +++ b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_high_quality.inst.cfg @@ -12,22 +12,23 @@ weight = 1 [values] adhesion_type = brim +bottom_thickness = =layer_height*3 brim_width = 5 -line_width = 0.42 +infill_sparse_density = 15 initial_layer_line_width_factor = 125 layer_height = 0.2 layer_height_0 = 0.2 +line_width = 0.42 material_bed_temperature = 55 -top_thickness = =layer_height*5 -bottom_thickness = =layer_height*3 -infill_sparse_density = 15 material_print_temperature = 215.0 skirt_height = 1 speed_layer_0 = 40 -speed_travel_layer_0 = 60 speed_print = 180.0 speed_travel = 300.0 +speed_travel_layer_0 = 60 speed_wall = 180.0 speed_wall_0 = 60.0 speed_wall_x = 180.0 +top_thickness = =layer_height*5 wall_thickness = =line_width*4 + diff --git a/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg index 3b3280331e..fd6a2962a3 100644 --- a/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg +++ b/resources/quality/anycubic_kobra_s1/anycubic_kobra_s1_standard.inst.cfg @@ -12,22 +12,23 @@ weight = 0 [values] adhesion_type = brim +bottom_thickness = =layer_height*3 brim_width = 5 -line_width = 0.42 +infill_sparse_density = 15 initial_layer_line_width_factor = 125 layer_height = 0.2 layer_height_0 = 0.2 +line_width = 0.42 material_bed_temperature = 55 -top_thickness = =layer_height*5 -bottom_thickness = =layer_height*3 -infill_sparse_density = 15 material_print_temperature = 215.0 skirt_height = 1 speed_layer_0 = 50 -speed_travel_layer_0 = 80 speed_print = 300.0 speed_travel = 300.0 +speed_travel_layer_0 = 80 speed_wall = 300.0 speed_wall_0 = 200.0 speed_wall_x = 300.0 +top_thickness = =layer_height*5 wall_thickness = =line_width*2 + From 75a15a5831d531b8760207887c044064042f7e92 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 12 Sep 2025 10:28:20 +0200 Subject: [PATCH 92/98] Fix linter not checking for parent's parent's definitions --- .../src/printerlinter/linters/defintion.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/printer-linter/src/printerlinter/linters/defintion.py b/printer-linter/src/printerlinter/linters/defintion.py index a95fede55a..3e325a7522 100644 --- a/printer-linter/src/printerlinter/linters/defintion.py +++ b/printer-linter/src/printerlinter/linters/defintion.py @@ -145,17 +145,18 @@ class Definition(Linter): if "overrides" not in self._definitions[inherits_from]: return self._isDefinedInParent(key, value_dict, self._definitions[inherits_from]["inherits"]) - parent = self._definitions[inherits_from]["overrides"] + parent = self._definitions[inherits_from] + parent_overrides = self._definitions[inherits_from]["overrides"] if key not in self._definitions[self.base_def]["overrides"]: is_number = False else: is_number = self._definitions[self.base_def]["overrides"][key]["type"] in ("float", "int") for child_key, child_value in value_dict.items(): - if key in parent: + if key in parent_overrides: if child_key in ("default_value", "value"): - check_values = [cv for cv in [parent[key].get("default_value", None), parent[key].get("value", None)] if cv is not None] + check_values = [cv for cv in [parent_overrides[key].get("default_value", None), parent_overrides[key].get("value", None)] if cv is not None] else: - check_values = [parent[key].get(child_key, None)] + check_values = [parent_overrides[key].get(child_key, None)] for check_value in check_values: if is_number and child_key in ("default_value", "value"): try: @@ -170,10 +171,10 @@ class Definition(Linter): v = child_value cv = check_value if v == cv: - return True, child_key, child_value, parent, inherits_from + return True, child_key, child_value, parent_overrides, inherits_from - if "inherits" in parent: - return self._isDefinedInParent(key, value_dict, parent["inherits"]) + if "inherits" in parent: + return self._isDefinedInParent(key, value_dict, parent["inherits"]) return False, None, None, None, None def _loadExperimentalSettings(self): From eb697f25712d6b1641803c46281b85fe299d489a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 12 Sep 2025 10:37:11 +0200 Subject: [PATCH 93/98] Remove useless comment publishing step --- .github/workflows/printer-linter-pr-diagnose.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/printer-linter-pr-diagnose.yml b/.github/workflows/printer-linter-pr-diagnose.yml index 666383c8f9..ad0a7ea587 100644 --- a/.github/workflows/printer-linter-pr-diagnose.yml +++ b/.github/workflows/printer-linter-pr-diagnose.yml @@ -45,10 +45,3 @@ jobs: with: name: printer-linter-result path: printer-linter-result/ - - - name: Run clang-tidy-pr-comments action - uses: platisd/clang-tidy-pr-comments@v1.8.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - clang_tidy_fixes: result.yml - request_changes: true From 13cd6e0ca18549d1de2294e26be3d9d992123941 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 12 Sep 2025 11:02:52 +0200 Subject: [PATCH 94/98] Fix comment action --- .github/workflows/printer-linter-pr-post.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/printer-linter-pr-post.yml b/.github/workflows/printer-linter-pr-post.yml index 7b3dd152cb..d7f43d06dc 100644 --- a/.github/workflows/printer-linter-pr-post.yml +++ b/.github/workflows/printer-linter-pr-post.yml @@ -103,9 +103,10 @@ jobs: body-path: 'printer-linter-result/comment.md' - name: Run clang-tidy-pr-comments action - uses: platisd/clang-tidy-pr-comments@v1 + uses: platisd/clang-tidy-pr-comments@v1.8.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} clang_tidy_fixes: printer-linter-result/fixes.yml pull_request_id: ${{ env.PR_ID }} request_changes: true + auto_resolve_conversations: true From 778ffb930c5d0fed69051515c606458a4e737724 Mon Sep 17 00:00:00 2001 From: HellAholic Date: Sat, 13 Sep 2025 17:15:54 +0200 Subject: [PATCH 95/98] Adjust based on review comments --- .../scripts/ZHopOnTravel.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index 7ac57a308e..71924419fe 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -70,7 +70,7 @@ class ZHopOnTravel(Script): "unit": "Lay# ", "type": "int", "default_value": 1, - "minimum_value": "1", + "minimum_value": 1, "enabled": "zhop_travel_enabled and list_or_range == 'range_of_layers'" }, "end_layer": { @@ -79,16 +79,16 @@ class ZHopOnTravel(Script): "unit": "Lay# ", "type": "int", "default_value": -1, - "minimum_value": "-1", + "minimum_value": -1, "enabled": "zhop_travel_enabled and list_or_range == 'range_of_layers'" }, "hop_height": { "label": "Z-Hop Height", - "description": "I refuse to provide a description for this.", + "description": "The relative 'Height' that the nozzle will 'Hop' in the 'Z'.", "unit": "mm ", "type": "float", "default_value": 0.5, - "minimum_value": "0", + "minimum_value": 0, "maximum_value_warning": 5, "enabled": "zhop_travel_enabled" }, @@ -98,8 +98,8 @@ class ZHopOnTravel(Script): "unit": "mm ", "type": "int", "default_value": 10, - "minimum_value": "1", - "maximum_value": "200", + "minimum_value": 1, + "maximum_value": 200, "enabled": "zhop_travel_enabled" }, "add_retract": { @@ -111,7 +111,7 @@ class ZHopOnTravel(Script): }, "infill_only": { "label": "Add Z-hops to Infill Only", - "description": "Only add Z-hops to 'Infill' within the layer range.", + "description": "Only add Z-hops to 'Infill' within the layer range. (NOTE: For technical reasons it is not possible to add Z-hops to travel moves that start somewhere and just 'cross infill'.)", "type": "bool", "default_value": false, "enabled": "zhop_travel_enabled" @@ -210,7 +210,7 @@ class ZHopOnTravel(Script): end_layer = data[num].splitlines()[0].split(":")[1] end_index = num break - if end_index == None: + if end_index is None: end_index = len(data)-1 for num in range(start_index, end_index): index_list.append(num) @@ -290,7 +290,7 @@ class ZHopOnTravel(Script): if line[0:3] in ["G1 ", "G2 ", "G3 "] and "X" in line and "Y" in line and "E" in line: self._is_retracted = False self._cur_e = self.getValue(line, "E") - elif (line.startswith("G1") and "F" in line and "E" in line and not "X" in line or not "Y" in line) or "G10" in line: + elif (line.startswith("G1") and "F" in line and "E" in line and (not "X" in line or not "Y" in line)) or "G10" in line: if self.getValue(line, "E"): self._cur_e = self.getValue(line, "E") if not relative_extrusion: @@ -329,7 +329,7 @@ class ZHopOnTravel(Script): # If there is no 'F' in the next line then add one to reinstate the Travel Speed (so the z-hop speed doesn't carry over through the travel moves) if not " F" in lines[index] and lines[index].startswith("G0"): lines[index] = lines[index].replace("G0", f"G0 F{speed_travel}") - hop_down_lines = self.get_hop_down_lines(retraction_amount, speed_zhop, retract_speed, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, hop_height, lines[index]) + hop_down_lines = self.get_hop_down_lines(retraction_amount, speed_zhop, prime_speed, extra_prime_dist, firmware_retract, relative_extrusion, lines[index]) lines[index] = hop_down_lines + lines[index] self._is_retracted = False hop_end = 0 @@ -357,10 +357,10 @@ class ZHopOnTravel(Script): def _total_travel_length(self, l_index: int, lines: str) -> int: """ - This function gets the cummulative total travel distance of each individual travel move. + This function gets the cumulative total travel distance of each individual travel move. :parameters: g_num: is the line index as passed from the calling function and when returned indicates the end of travel - travel_total: is the cummulative travel distance + travel_total: is the cumulative travel distance """ g_num = l_index travel_total = 0.0 @@ -402,7 +402,13 @@ class ZHopOnTravel(Script): hop_retraction = not self._is_retracted if not self._add_retract: hop_retraction = False - reset_type = 0 # no other options + # 'reset_type' is a bitmask representing the combination of retraction and related options: + # Bit 0 (1): Retraction is required + # Bit 1 (2): Firmware retraction is enabled + # Bit 2 (4): Relative extrusion is enabled + # Bit 3 (8): Extra prime amount is greater than 0 + # The value of 'reset_type' determines which G-code lines are inserted for the Z-hop. + reset_type = 0 if hop_retraction: reset_type += 1 if firmware_retract and hop_retraction: @@ -436,8 +442,7 @@ class ZHopOnTravel(Script): return up_lines # The Zhop down may require different kinds of primes depending on the Cura settings. - def get_hop_down_lines(self, retraction_amount: float, speed_zhop: str, retract_speed: str, prime_speed: str, extra_prime_dist: str, firmware_retract: bool, relative_extrusion: bool, hop_height: str, next_line: str) -> str: - + def get_hop_down_lines(self, retraction_amount: float, speed_zhop: str, prime_speed: str, extra_prime_dist: str, firmware_retract: bool, relative_extrusion: bool, next_line: str) -> str: """ Determine if the hop will require a prime :parameters: From 7420f2486d1c7572b0579698fa4df4934c6c67ab Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sat, 13 Sep 2025 15:10:39 -0400 Subject: [PATCH 96/98] Create TweakAtZ.py This is a re-write of Change At Z. The name change was necessary to avoid conflicting names in earlier project files. --- .../PostProcessingPlugin/scripts/TweakAtZ.py | 981 ++++++++++++++++++ 1 file changed, 981 insertions(+) create mode 100644 plugins/PostProcessingPlugin/scripts/TweakAtZ.py diff --git a/plugins/PostProcessingPlugin/scripts/TweakAtZ.py b/plugins/PostProcessingPlugin/scripts/TweakAtZ.py new file mode 100644 index 0000000000..8bf75e917f --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/TweakAtZ.py @@ -0,0 +1,981 @@ +""" + TweakAtZ 2.0 is a re-write of ChangeAtZ by GregValiant (Greg Foresi) March 2025 + The script name change was required to avoid conflicts with project files that might use ChangeAtZ variables. + Differences from the previous ChangeAtZ version (5.3.0): + ~"By Height" will work with Z-hops enabled, Adaptive Layers, Scarf Z-seam, and Rafts. The changes will commence at the first layer where the height is reached or exceeded. The changes will end at the start of the layer where the End Height is reached or exceeded. + ~The user can opt to change just the print speed or both print and travel speeds. The 'F' parameters are re-calculated line-by-line using the percentage that the user inputs. Speeds can now be changed 'per extruder'. M220 is no longer used to change speeds as it affected all speeds. + ~Changing the print speed no longer affects retraction or unretract speeds. + ~The Z-hop speed is never affected. + ~The 'Output to LCD' setting is obsolete to avoid flooding the screen with messages that were quickly over-written. + ~Allows the user to select a Range of Layers (rather than just 'Single Layer' or 'To the End'.) + ~Added support for control of a single fan. This might be a Build Volume Fan, Auxilliary Fan, or a Layer Cooling Fan. It would depend on the fan circuit number that the user inputs. + ~Added support for Relative Extrusion + ~Added support for Firmware Retraction + ~Added support for 'G2' and 'G3' moves. + ~The script supports a maximum of 2 extruders. + ~'One-at-a-Time' is not supported and a kick-out is added + + Previous contributions by: + Original Authors and contributors to the ChangeAtZ post-processing script and the earlier TweakAtZ: + Written by Steven Morlock, + Modified by Ricardo Gomez, to add Bed Temperature and make it work with Cura_13.06.04+ + Modified by Stefan Heule, since V3.0 + Modified by Jaime van Kessel (Ultimaker), to make it work for 15.10 / 2.x + Modified by Ghostkeeper (Ultimaker), to debug. + Modified by Wes Hanney, Retract Length + Speed, Clean up + Modified by Alex Jaxon, Added option to modify Build Volume Temperature + Re-write by GregValiant, to work with new variables in Cura 5.x and with the changes noted above +""" + +from UM.Application import Application +from ..Script import Script +import re +from UM.Message import Message +from UM.Logger import Logger + +class TweakAtZ(Script): + version = "2.0.0" + + def initialize(self) -> None: + """ + Prepare the script settings for the machine hardware configuration on Cura opening + """ + super().initialize() + self.global_stack = Application.getInstance().getGlobalContainerStack() + self.extruder_count = int(self.global_stack.getProperty("machine_extruder_count", "value")) + # If the printer is multi-extruder then enable the settings for T1 + if self.extruder_count == 1: + self._instance.setProperty("multi_extruder", "value", False) + else: + self._instance.setProperty("multi_extruder", "value", True) + + # Enable the build volume temperature change when a heated build volume is present + machine_heated_build_volume = bool(self.global_stack.getProperty("machine_heated_build_volume", "value")) + if machine_heated_build_volume: + self._instance.setProperty("heated_build_volume", "value", True) + else: + self._instance.setProperty("heated_build_volume", "value", False) + + # If it doesn't have a heated bed it is unlikely to have a Chamber or Auxiliary fan. + has_heated_bed = bool(self.global_stack.getProperty("machine_heated_bed", "value")) + if has_heated_bed: + self._instance.setProperty("has_bv_fan", "value", True) + + def getSettingDataString(self): + return """{ + "name": "Tweak At Z (2.0)", + "key": "TweakAtZ", + "metadata": {}, + "version": 2, + "settings": { + "taz_enabled": { + "label": "Enable Tweak at Z", + "description": "Enables the script so it will run. You may have more than one instance of 'Tweak At Z' in the list of post processors.", + "type": "bool", + "default_value": true, + "enabled": true + }, + "by_layer_or_height": { + "label": "'By Layer' or 'By Height'", + "description": "Which criteria to use to start and end the changes.", + "type": "enum", + "options": + { + "by_layer": "By Layer", + "by_height": "By Height" + }, + "default_value": "by_layer", + "enabled": "taz_enabled" + }, + "a_start_layer": { + "label": "Start Layer", + "description": "Layer number to start the changes at. Use the Cura preview layer numbers. The changes will start at the beginning of the layer.", + "unit": "", + "type": "int", + "default_value": 1, + "minimum_value": "-7", + "minimum_value_warning": "1", + "unit": "Layer #", + "enabled": "taz_enabled and by_layer_or_height == 'by_layer'" + }, + "a_end_layer": { + "label": "End Layer", + "description": "Use '-1' to indicate the end of the last layer. The changes will end at the end of the indicated layer. Use the Cura preview layer number. If the 'Start Layer' is equal to the 'End Layer' then the changes only affect that single layer.", + "type": "int", + "default_value": -1, + "unit": "Layer #", + "enabled": "taz_enabled and by_layer_or_height == 'by_layer'" + }, + "a_height_start": { + "label": "Height Start of Changes", + "description": "Enter the 'Z-Height' to Start the changes at. The changes START at the beginning of the first layer where this height is reached (or exceeded). If the model is on a raft then this height will be from the top of the air gap (first height of the actual model print).", + "type": "float", + "default_value": 0, + "unit": "mm", + "enabled": "taz_enabled and by_layer_or_height == 'by_height'" + }, + "a_height_end": { + "label": "Height End of Changes", + "description": "Enter the 'Z-Height' to End the changes at. The changes continue until this height is reached or exceeded. If the model is on a raft then this height will be from the top of the air gap (first height of the actual model print).", + "type": "float", + "default_value": 0, + "unit": "mm", + "enabled": "taz_enabled and by_layer_or_height == 'by_height'" + } , + "b_change_speed": { + "label": "Change Speeds", + "description": "Check to enable a speed change for the Print Speeds.", + "type": "bool", + "default_value": false, + "enabled": "taz_enabled" + }, + "change_speed_per_extruder": { + "label": " Which extruder(s)", + "description": "For multi-extruder printers the changes can be for either or both extruders.", + "type": "enum", + "options": { + "ext_0": "Extruder 1 (T0)", + "ext_1": "Extruder 2 (T1)", + "ext_both": "Both extruders"}, + "default_value": "ext_both", + "enabled": "taz_enabled and b_change_speed and multi_extruder" + }, + "b_change_printspeed": { + "label": " Include Travel Speeds", + "description": "Check this box to change the Travel Speeds as well as the Print Speeds.", + "type": "bool", + "default_value": false, + "enabled": "b_change_speed and taz_enabled" + }, + "b_speed": { + "label": " Speed %", + "description": "Speed factor as a percentage. The chosen speeds will be altered by this much.", + "unit": "% ", + "type": "int", + "default_value": 100, + "minimum_value": "10", + "minimum_value_warning": "50", + "maximum_value_warning": "200", + "enabled": "b_change_speed and taz_enabled" + }, + "c_change_flowrate": { + "label": "Change Flow Rate", + "description": "Select to change the flow rate of all extrusions in the layer range. This command uses M221 to set the flow percentage in the printer.", + "type": "bool", + "default_value": false, + "enabled": "taz_enabled" + }, + "c_flowrate_t0": { + "label": " Flow Rate % (T0)", + "description": "Enter the new Flow Rate Percentage. For a multi-extruder printer this will apply to Extruder 1 (T0).", + "unit": "% ", + "type": "int", + "default_value": 100, + "minimum_value": "25", + "minimum_value_warning": "50", + "maximum_value_warning": "150", + "maximum_value": "200", + "enabled": "c_change_flowrate and taz_enabled" + }, + "multi_extruder": { + "label": "Hidden setting to enable 2nd extruder settings for multi-extruder printers.", + "description": "Enable T1 options.", + "type": "bool", + "value": false, + "default_value": false, + "enabled": false + }, + "c_flowrate_t1": { + "label": " Flow Rate % T1", + "description": "New Flow rate percentage for Extruder 2 (T1).", + "unit": "% ", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "multi_extruder and c_change_flowrate and taz_enabled" + }, + "d_change_bed_temp": { + "label": "Change Bed Temp", + "description": "Select if Bed Temperature is to be changed. The bed temperature will revert at the End Layer.", + "type": "bool", + "default_value": false, + "enabled": "taz_enabled" + }, + "d_bedTemp": { + "label": " Bed Temp", + "description": "New Bed Temperature", + "unit": "°C ", + "type": "int", + "default_value": 60, + "minimum_value": "0", + "minimum_value_warning": "30", + "maximum_value_warning": "120", + "enabled": "d_change_bed_temp and taz_enabled" + }, + "heated_build_volume": { + "label": "Hidden setting", + "description": "This enables the build volume settings", + "type": "bool", + "default_value": false, + "enabled": false + }, + "e_change_build_volume_temperature": { + "label": "Change Build Volume Temperature", + "description": "Select if Build Volume Temperature is to be changed", + "type": "bool", + "default_value": false, + "enabled": "heated_build_volume and taz_enabled" + }, + "e_build_volume_temperature": { + "label": " Build Volume Temperature", + "description": "New Build Volume Temperature. This will revert at the end of the End Layer.", + "unit": "°C ", + "type": "int", + "default_value": 20, + "minimum_value": "0", + "minimum_value_warning": "15", + "maximum_value_warning": "80", + "enabled": "heated_build_volume and e_change_build_volume_temperature and taz_enabled" + }, + "f_change_extruder_temperature": { + "label": "Change Print Temp", + "description": "Select if the Printing Temperature is to be changed", + "type": "bool", + "default_value": false, + "enabled": "taz_enabled" + }, + "f_extruder_temperature_t0": { + "label": " Extruder 1 Temp (T0)", + "description": "New temperature for Extruder 1 (T0).", + "unit": "°C ", + "type": "int", + "default_value": 190, + "minimum_value": "0", + "minimum_value_warning": "160", + "maximum_value_warning": "250", + "enabled": "f_change_extruder_temperature and taz_enabled" + }, + "f_extruder_temperature_t1": { + "label": " Extruder 2 Temp (T1)", + "description": "New temperature for Extruder 2 (T1).", + "unit": "°C ", + "type": "int", + "default_value": 190, + "minimum_value": "0", + "minimum_value_warning": "160", + "maximum_value_warning": "250", + "enabled": "multi_extruder and f_change_extruder_temperature and taz_enabled" + }, + "g_change_retract": { + "label": "Change Retraction Settings", + "description": "Indicates you would like to modify retraction properties. If 'Firmware Retraction' is enabled then M207 and M208 lines are added. Your firmware must understand those commands.", + "type": "bool", + "default_value": false, + "enabled": "taz_enabled and not multi_extruder" + }, + "g_change_retract_speed": { + "label": " Change Retract/Prime Speed", + "description": "Changes the retraction and prime speed.", + "type": "bool", + "default_value": false, + "enabled": "g_change_retract and taz_enabled and not multi_extruder" + }, + "g_retract_speed": { + "label": " Retract/Prime Speed", + "description": "New Retract Feed Rate (mm/s). If 'Firmware Retraction' is enabled then M207 and M208 are used to change the retract and prime speeds and the distance. NOTE: the same speed will be used for both retract and prime.", + "unit": "mm/s ", + "type": "float", + "default_value": 40, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "enabled": "g_change_retract and g_change_retract_speed and taz_enabled and not multi_extruder" + }, + "g_change_retract_amount": { + "label": " Change Retraction Amount", + "description": "Changes the retraction length during print", + "type": "bool", + "default_value": false, + "enabled": "g_change_retract and taz_enabled and not multi_extruder" + }, + "g_retract_amount": { + "label": " Retract Amount", + "description": "New Retraction Distance (mm). If firmware retraction is used then M207 and M208 are used to change the retract and prime amount.", + "unit": "mm ", + "type": "float", + "default_value": 6.5, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "20", + "enabled": "g_change_retract and g_change_retract_amount and taz_enabled and not multi_extruder" + }, + "enable_bv_fan_change": { + "label": "Chamber/Aux Fan Change", + "description": "Can alter the setting of a secondary fan when the printer is equipped with one.", + "type": "bool", + "default_value": false, + "enabled": "has_bv_fan and taz_enabled" + }, + "e1_build_volume_fan_speed": { + "label": " Chamber/Aux Fan Speed", + "description": "New Build Volume or Auxiliary Fan Speed. This will reset to zero at the end of the 'End Layer'.", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": 0, + "maximum_value": 100, + "enabled": "has_bv_fan and enable_bv_fan_change and taz_enabled" + }, + "bv_fan_nr": { + "label": " Chamber/Aux Fan Number", + "description": "The circuit number of the Auxilliary or Chamber fan. M106 will be used and the 'P' parameter (the fan number) will be the number entered here.", + "type": "int", + "unit": "#", + "default_value": 3, + "minimum_value": 0, + "enabled": "has_bv_fan and enable_bv_fan_change and taz_enabled" + }, + "has_bv_fan": { + "label": "Hidden setting", + "description": "Enables the Build Volume/Auxiliary fan speed control when 'machine_heated_bed' is true.", + "type": "bool", + "default_value": false, + "enabled": false + } + } + }""" + + def execute(self, data): + # Exit if the script isn't enabled + if not bool(self.getSettingValueByKey("taz_enabled")): + data[0] += "; [Tweak at Z] is not enabled\n" + Logger.log("i", "[Tweak at Z] is not enabled") + return data + + # Message the user and exit if the print sequence is 'One at a Time' + if self.global_stack.getProperty("print_sequence", "value") == "one_at_a_time": + Message(title = "[Tweak at Z]", text = "One-at-a-Time mode is not supported. The script will exit without making any changes.").show() + data[0] += "; [Tweak at Z] Did not run (One at a Time mode is not supported)\n" + Logger.log("i", "TweakAtZ does not support 'One at a Time' mode") + return data + + # Exit if the gcode has been previously post-processed. + if ";POSTPROCESSED" in data[0]: + return data + + # Pull some settings from Cura + self.extruder_list = self.global_stack.extruderList + self.firmware_retraction = bool(self.global_stack.getProperty("machine_firmware_retract", "value")) + self.relative_extrusion = bool(self.global_stack.getProperty("relative_extrusion", "value")) + self.initial_layer_height = self.global_stack.getProperty("layer_height_0", "value") + self.heated_build_volume = bool(self.global_stack.getProperty("machine_heated_build_volume", "value")) + self.heated_bed = bool(self.global_stack.getProperty("machine_heated_bed", "value")) + self.retract_enabled = bool(self.extruder_list[0].getProperty("retraction_enable", "value")) + self.orig_bed_temp = self.global_stack.getProperty("material_bed_temperature", "value") + self.orig_bv_temp = self.global_stack.getProperty("build_volume_temperature", "value") + self.z_hop_enabled = bool(self.extruder_list[0].getProperty("retraction_hop_enabled", "value")) + self.raft_enabled = True if str(self.global_stack.getProperty("adhesion_type", "value")) == "raft" else False + # The Start and end layer numbers are used when 'By Layer' is selected + self.start_layer = self.getSettingValueByKey("a_start_layer") - 1 + end_layer = int(self.getSettingValueByKey("a_end_layer")) + nbr_raft_layers = 0 + if self.raft_enabled: + for layer in data: + if ";LAYER:-" in layer: + nbr_raft_layers += 1 + if ";LAYER:0\n" in layer: + break + + # Adjust the start layer to account for any raft layers + self.start_layer -= nbr_raft_layers + + # Find the indexes of the Start and End layers if 'By Layer' + self.start_index = 0 + + # When retraction is enabled it adds a single line item to the data list + self.end_index = len(data) - 1 - int(self.retract_enabled) + if self.getSettingValueByKey("by_layer_or_height") == "by_layer": + for index, layer in enumerate(data): + if ";LAYER:" + str(self.start_layer) + "\n" in layer: + self.start_index = index + break + + # If the changes continue to the top layer + if end_layer == -1: + if self.retract_enabled: + self.end_index = len(data) - 2 + else: + self.end_index = len(data) - 1 + + # If the changes end below the top layer + else: + + # Adjust the end layer from base1 numbering to base0 numbering + end_layer -= 1 + + # Adjust the End Layer if it is not the top layer and if bed adhesion is 'raft' + end_layer -= nbr_raft_layers + for index, layer in enumerate(data): + if ";LAYER:" + str(end_layer) + "\n" in layer: + self.end_index = index + break + + # The Start and End heights are used to find the Start and End indexes when changes are 'By Height' + elif self.getSettingValueByKey("by_layer_or_height") == "by_height": + start_height = float(self.getSettingValueByKey("a_height_start")) + end_height = float(self.getSettingValueByKey("a_height_end")) + # Get the By Height start and end indexes + self.start_index = self._is_legal_z(data, start_height) + self.end_index = self._is_legal_z(data, end_height) - 1 + + # Exit if the Start Layer wasn't found + if self.start_index == 0: + Message(title = "[Tweak at Z]", text = "The 'Start Layer' is beyond the top of the print. The script did not run.").show() + Logger.log("w", "[Tweak at Z] The 'Start Layer' is beyond the top of the print. The script did not run.") + return data + + # Adjust the End Index if the End Index < Start Index (required for the script to make changes) + if self.end_index < self.start_index: + self.start_index = self.end_index + Message(title = "[Tweak at Z]", text = "Check the Gcode. Your 'Start Layer/Height' input is higher than the End Layer/Height input. The Start Layer has been adjusted to equal the End Layer.").show() + + # Map settings to corresponding methods + procedures = { + "b_change_speed": self._change_speed, + "c_change_flowrate": self._change_flow, + "d_change_bed_temp": self._change_bed_temp, + "e_change_build_volume_temperature": self._change_bv_temp, + "f_change_extruder_temperature": self._change_hotend_temp, + "g_change_retract": self._change_retract, + "has_bv_fan": self._change_bv_fan_speed + } + + # Run the selected procedures + for setting, method in procedures.items(): + if self.getSettingValueByKey(setting): + method(data) + data = self._format_lines(data) + return data + + def _change_speed(self, data:str)->str: + """ + The actual speed will be a percentage of the Cura calculated 'F' values in the gcode. The percentage can be different for each extruder. Travel speeds can also be affected dependent on the user input. + :params: + speed_x: The speed percentage to use + print_speed_only: Only change speeds with extrusions (but not retract or primes) + target_extruder: For multi-extruder printers this is the active extruder + off_extruder: For multi-extruders this is the inactive extruder. + """ + # Since a single extruder changes all relevant speed settings then for a multi-extruder 'both extruders' is the same + if self.extruder_count == 1 or self.getSettingValueByKey("change_speed_per_extruder") == "ext_both": + speed_x = self.getSettingValueByKey("b_speed")/100 + print_speed_only = not bool(self.getSettingValueByKey("b_change_printspeed")) + for index, layer in enumerate(data): + if index >= self.start_index and index <= self.end_index: + lines = layer.splitlines() + for l_index, line in enumerate(lines): + if " F" in line and " X" in line and " Y" in line and not " Z" in line: + f_value = self.getValue(line, "F") + if line.startswith(("G1", "G2", "G3")): + lines[l_index] = line.replace("F" + str(f_value), "F" + str(round(f_value * speed_x))) + lines[l_index] += f" ; TweakAtZ: {round(speed_x * 100)}% Print Speed" + continue + if not print_speed_only and line.startswith("G0"): + lines[l_index] = line.replace("F" + str(f_value), "F" + str(round(f_value * speed_x))) + lines[l_index] += f" ; TweakAtZ: {round(speed_x * 100)}% Travel Speed" + data[index] = "\n".join(lines) + "\n" + elif self.extruder_count > 1: + speed_x = self.getSettingValueByKey("b_speed")/100 + print_speed_only = not bool(self.getSettingValueByKey("b_change_printspeed")) + target_extruder = self.getSettingValueByKey("change_speed_per_extruder") + + # These variables are used as the 'turn changes on' and 'turn changes off' at tool changes. + if target_extruder == "ext_0": + target_extruder = "T0" + off_extruder = "T1" + elif target_extruder == "ext_1": + target_extruder = "T1" + off_extruder = "T0" + + # After all of that it goes to work. + for index, layer in enumerate(data): + if index < self.start_index: + lineT = layer.splitlines() + for tline in lineT: + if "T0" in tline: + active_tool = "T0" + if "T1" in tline: + active_tool = "T1" + if index >= self.start_index and index <= self.end_index: + lines = layer.splitlines() + for l_index, line in enumerate(lines): + if active_tool == target_extruder: + if " F" in line and " X" in line and " Y" in line and not " Z" in line: + f_value = self.getValue(line, "F") + if line.startswith(("G1", "G2", "G3")): + lines[l_index] = line.replace("F" + str(f_value), "F" + str(round(f_value * speed_x))) + lines[l_index] += f" ; TweakAtZ: {round(speed_x * 100)}% Print Speed" + continue + if not print_speed_only and line.startswith("G0"): + lines[l_index] = line.replace("F" + str(f_value), "F" + str(round(f_value * speed_x))) + lines[l_index] += f" ; TweakAtZ: {round(speed_x * 100)}% Travel Speed" + if line.startswith(off_extruder): + active_tool = off_extruder + if line.startswith(target_extruder): + active_tool = target_extruder + + data[index] = "\n".join(lines) + "\n" + return data + + def _change_flow(self, data:str)->str: + """ + M221 is used to change the flow rate. + :params: + new_flow_ext_0: The flowrate percentage from these script settings (for the primary extruder) + new_flowrate_0: The string to use for the new flowrate for T0 + reset_flowrate_0: Resets the flowrate to 100% (for either extruder) + new_flow_ext_1: The flowrate percentage from these script settings (for the secondary extruder) + new_flowrate_1: The string to use for the new flowrate for T1 + """ + new_flow_ext_0 = self.getSettingValueByKey("c_flowrate_t0") + new_flowrate_0 = f"\nM221 S{new_flow_ext_0} ; TweakAtZ: Alter Flow Rate" + reset_flowrate_0 = "\nM221 S100 ; TweakAtZ: Reset Flow Rate" + if self.extruder_count > 1: + new_flow_ext_1 = self.getSettingValueByKey("c_flowrate_t1") + new_flowrate_1 = f"\nM221 S{new_flow_ext_1} ; TweakAtZ: Alter Flow Rate" + else: + new_flowrate_1 = "" + + # For single extruder + if self.extruder_count == 1: + lines = data[self.start_index].splitlines() + lines[0] += new_flowrate_0 + data[self.start_index] = "\n".join(lines) + "\n" + lines = data[self.end_index].splitlines() + lines[len(lines) - 2] += reset_flowrate_0 + data[self.end_index] = "\n".join(lines) + "\n" + + # For dual-extruders + elif self.extruder_count > 1: + for index, layer in enumerate(data): + if index < self.start_index: + continue + else: + lines = layer.splitlines() + for l_index, line in enumerate(lines): + if line.startswith("T0"): + lines[l_index] += new_flowrate_0 + " T0" + if line.startswith("T1"): + lines[l_index] += new_flowrate_1 + " T1" + data[index] = "\n".join(lines) + "\n" + if index == self.end_index: + lines = data[index].splitlines() + lines[len(lines) - 2] += "\nM221 S100 ; TweakAtZ: Reset Flow Rate" + data[index] = "\n".join(lines) + "\n" + break + if index > self.end_index: + break + return data + + def _change_bed_temp(self, data:str)->str: + """ + Change the Bed Temperature at height or layer + :params: + new_bed_temp: The new temperature from the settings for this script + """ + if not self.heated_bed: + return data + new_bed_temp = self.getSettingValueByKey("d_bedTemp") + if self.start_index == 2: + if "M140 S" in data[2]: + data[2] = re.sub("M140 S", ";M140 S", data[2]) + if "M140 S" in data[3]: + data[3] = re.sub("M140 S", ";M140 S", data[3]) + lines = data[self.start_index].splitlines() + lines[0] += "\nM140 S" + str(new_bed_temp) + " ; TweakAtZ: Change Bed Temperature" + data[self.start_index] = "\n".join(lines) + "\n" + lines = data[self.end_index].splitlines() + lines[len(lines) - 2] += "\nM140 S" + str(self.orig_bed_temp) + " ; TweakAtZ: Reset Bed Temperature" + data[self.end_index] = "\n".join(lines) + "\n" + return data + + def _change_bv_temp(self, data:str)->str: + """ + Change the Build Volume temperature at height or layer + :param: + new_bv_temp: The new temperature from the settings for this script + """ + if not self.heated_build_volume: + return data + new_bv_temp = self.getSettingValueByKey("e_build_volume_temperature") + lines = data[self.start_index].splitlines() + lines[0] += "\nM141 S" + str(new_bv_temp) + " ; TweakAtZ: Change Build Volume Temperature" + data[self.start_index] = "\n".join(lines) + "\n" + lines = data[self.end_index].splitlines() + lines[len(lines) - 2] += "\nM141 S" + str(self.orig_bv_temp) + " ; TweakAtZ: Reset Build Volume Temperature" + data[self.end_index] = "\n".join(lines) + "\n" + return data + + def _change_hotend_temp(self, data:str)->str: + """ + Changes to the hot end temperature(s). + :params: + extruders_share_heater: Lets the script know how to handle the differences + active_tool: Tracks the active tool through the gcode + new_hotend_temp_0: The new temperature for the primary extruder T0 + orig_hot_end_temp_0: The print temperature for the primary extruder T0 as set in Cura + orig_standby_temp_0: The standby temperature for the primary extruder T0 from Cura. This marks a temperature line to ignore. + new_hotend_temp_1: The new temperature for the secondary extruder T1 + orig_hot_end_temp_1: The print temperature for the secondary extruder T1 as set in Cura + orig_standby_temp_1: The standby temperature for the secondary extruder T1 from Cura. This marks a temperature line to ignore. + """ + extruders_share_heater = bool(self.global_stack.getProperty("machine_extruders_share_heater", "value")) + self.active_tool = "T0" + self.new_hotend_temp_0 = self.getSettingValueByKey("f_extruder_temperature_t0") + self.orig_hot_end_temp_0 = int(self.extruder_list[0].getProperty("material_print_temperature", "value")) + self.orig_standby_temp_0 = int(self.extruder_list[0].getProperty("material_standby_temperature", "value")) + + # Start with single extruder machines + if self.extruder_count == 1: + if self.start_index == 2: + if "M104 S" in data[2]: + data[2] = re.sub("M104 S", ";M104 S", data[2]) + if "M104 S" in data[3]: + data[3] = re.sub("M104 S", ";M104 S", data[3]) + + # Add the temperature change at the beginning of the start layer + lines = data[self.start_index].splitlines() + for index, line in enumerate(lines): + lines[0] += "\n" + "M104 S" + str(self.new_hotend_temp_0) + " ; TweakAtZ: Change Nozzle Temperature" + data[self.start_index] = "\n".join(lines) + "\n" + break + + # Revert the temperature to the Cura setting at the end of the end layer + lines = data[self.end_index].splitlines() + for index, line in enumerate(lines): + lines[len(lines) - 2] += "\n" + "M104 S" + str(self.orig_hot_end_temp_0) + " ; TweakAtZ: Reset Nozzle Temperature" + data[self.end_index] = "\n".join(lines) + "\n" + break + + # Multi-extruder machines + elif self.extruder_count > 1: + self.new_hotend_temp_1 = self.getSettingValueByKey("f_extruder_temperature_t1") + self.orig_hot_end_temp_1 = int(self.extruder_list[1].getProperty("material_print_temperature", "value")) + self.orig_standby_temp_1 = int(self.extruder_list[1].getProperty("material_standby_temperature", "value")) + + # Track the tool number up to the start of the start layer + self.getTool("T0") + for index, layer in enumerate(data): + lines = layer.split("\n") + for line in lines: + if line.startswith("T"): + self.getTool(line) + if index == self.start_index - 1: + break + + # Add the active extruder initial temperature change at the start of the starting layer + data[self.start_index] = data[self.start_index].replace("\n", f"\nM104 S{self.active_print_temp} ; TweakAtZ: Start Temperature Change\n",1) + + # At the start layer commence making the changes + for index, layer in enumerate(data): + if index < self.start_index: + continue + if index > self.end_index: + break + lines = layer.splitlines() + for l_index, line in enumerate(lines): + + # Continue to track the tool number + if line.startswith("T"): + self.getTool(line) + if line.startswith("M109"): + lines[l_index] = f"M109 S{self.active_print_temp} ; TweakAtZ: Alter temperature" + elif line.startswith("M104"): + if self.getValue(line, "S") == self.inactive_standby_temp: + continue + elif self.getValue(line, "S") == self.inactive_tool_orig_temp: + lines[l_index] = re.sub("S(\d+|\d.+)", f"S{self.inactive_print_temp} ; TweakAtZ: Alter temperature", line) + elif self.getValue(line, "S") == self.active_tool_orig_temp: + lines[l_index] = re.sub("S(\d+|\d.+)", f"S{self.active_print_temp} ; TweakAtZ: Alter temperature", line) + data[index] = "\n".join(lines) + "\n" + + # Revert the active extruder temperature at the end of the changes + lines = data[self.end_index].split("\n") + lines[len(lines) - 3] += f"\nM104 {self.active_tool} S{self.active_tool_orig_temp} ; TweakAtZ: Original Temperature active tool" + data[self.end_index] = "\n".join(lines) + return data + + def getTool(self, line): + if line.startswith("T1"): + self.active_tool = "T1" + self.active_tool_orig_temp = self.orig_hot_end_temp_1 + self.active_print_temp = self.new_hotend_temp_1 + self.inactive_tool = "T0" + self.inactive_tool_orig_temp = self.orig_hot_end_temp_0 + self.inactive_print_temp = self.new_hotend_temp_0 + self.inactive_standby_temp = self.orig_standby_temp_0 + else: + self.active_tool = "T0" + self.active_tool_orig_temp = self.orig_hot_end_temp_0 + self.active_print_temp = self.new_hotend_temp_0 + self.inactive_tool = "T1" + self.inactive_tool_orig_temp = self.orig_hot_end_temp_1 + self.inactive_print_temp = self.new_hotend_temp_1 + self.inactive_standby_temp = self.orig_standby_temp_1 + return + + def _change_retract(self, data:str)->str: + """ + This is for single extruder printers only (tool change retractions get in the way for multi-extruders). + Depending on the selected options, this will change the Retraction Speeds and Prime Speeds, and the Retraction Distance. NOTE: The retraction and prime speeds will be the same. + :params: + speed_retract_0: The set retraction and prime speed from Cura. + retract_amt_0: The set retraction distance from Cura + change_retract_amt: Boolean to trip changing the retraction distance + change_retract_speed: Boolean to trip changing the speeds + new_retract_amt: The new retraction amount to use from this script settings. + new_retract_speed: The new retract and prime speed from this script settings. + firmware_start_str: The string to insert for changes to firmware retraction + firmware_reset: The last insertion for firmware retraction will set the numbers back to the settings in Cura. + is_retracted: Tracks the end of the filament location + cur_e: The current location of the extruder + prev_e: The location of where the extruder was before the current e + """ + if not self.retract_enabled: + return + speed_retract_0 = int(self.extruder_list[0].getProperty("retraction_speed", "value") * 60) + retract_amt_0 = self.extruder_list[0].getProperty("retraction_amount", "value") + change_retract_amt = self.getSettingValueByKey("g_change_retract_amount") + change_retract_speed = self.getSettingValueByKey("g_change_retract_speed") + new_retract_speed = int(self.getSettingValueByKey("g_retract_speed") * 60) + new_retract_amt = self.getSettingValueByKey("g_retract_amount") + + # Use M207 and M208 to adjust firmware retraction when required + if self.firmware_retraction: + lines = data[self.start_index].splitlines() + firmware_start_str = "\nM207" + firmware_reset = "" + if change_retract_speed: + firmware_start_str += f" F{new_retract_speed} ; TweakAtZ: Alter Firmware Retract speed" + if change_retract_amt: + firmware_start_str += f" S{new_retract_amt} ; TweakAtZ: Alter Firmware Retract amt" + if change_retract_speed: + firmware_start_str += f"\nM208 F{new_retract_speed} ; TweakAtZ: Alter Firmware Prime speed" + lines[0] += firmware_start_str + data[self.start_index] = "\n".join(lines) + "\n" + lines = data[self.end_index].splitlines() + firmware_reset = f"M207 F{speed_retract_0} S{retract_amt_0} ; TweakAtZ: Reset Firmware Retract" + if change_retract_speed: + firmware_reset += f"\nM208 S{speed_retract_0} ; TweakAtZ: Reset Firmware Prime" + if len(lines) < 2: + lines.append(firmware_reset) + else: + lines[len(lines) - 2] += "\n" + firmware_reset + data[self.end_index] = "\n".join(lines) + "\n" + return data + + if not self.firmware_retraction: + prev_e = 0 + cur_e = 0 + is_retracted = False + for num in range(1, self.start_index - 1): + lines = data[num].splitlines() + for line in lines: + if " E" in line: + cur_e = self.getValue(line, "E") + prev_e = cur_e + for num in range(self.start_index, self.end_index): + lines = data[num].splitlines() + for index, line in enumerate(lines): + if line == "G92 E0": + cur_e = 0 + prev_e = 0 + continue + if " E" in line and self.getValue(line, "E") is not None: + cur_e = self.getValue(line, "E") + if cur_e >= prev_e and " X" in line and " Y" in line: + prev_e = cur_e + is_retracted = False + continue + if " F" in line and " E" in line and not " X" in line and not " Z" in line: + cur_speed = self.getValue(line, "F") + if cur_e < prev_e: + is_retracted = True + new_e = prev_e - new_retract_amt + if not self.relative_extrusion: + if change_retract_amt: + lines[index] = lines[index].replace("E" + str(cur_e), "E" + str(new_e)) + prev_e = new_e + if change_retract_speed: + lines[index] = lines[index].replace("F" + str(cur_speed), "F" + str(new_retract_speed)) + elif self.relative_extrusion: + if change_retract_amt: + lines[index] = lines[index].replace("E" + str(cur_e), "E-" + str(new_retract_amt)) + prev_e = 0 + if change_retract_speed: + lines[index] = lines[index].replace("F" + str(cur_speed), "F" + str(new_retract_speed)) + lines[index] += " ; TweakAtZ: Alter retract" + else: + + # Prime line + if change_retract_speed: + lines[index] = lines[index].replace("F" + str(cur_speed), "F" + str(new_retract_speed)) + prev_e = cur_e + if self.relative_extrusion: + if change_retract_amt: + lines[index] = lines[index].replace("E" + str(cur_e), "E" + str(new_retract_amt)) + prev_e = 0 + lines[index] += " ; TweakAtZ: Alter retract" + is_retracted = False + data[num] = "\n".join(lines) + "\n" + + # If the changes end before the last layer and the filament is retracted, then adjust the first prime of the next layer so it doesn't blob. + if is_retracted and self.getSettingValueByKey("a_end_layer") != -1: + layer = data[self.end_index] + lines = layer.splitlines() + for index, line in enumerate(lines): + if " X" in line and " Y" in line and " E" in line: + break + if " F" in line and " E" in line and not " X" in line and not " Z" in line: + cur_e = self.getValue(line, "E") + if not self.relative_extrusion: + new_e = prev_e + new_retract_amt + if change_retract_amt: + lines[index] = lines[index].replace("E" + str(cur_e), "E" + str(new_e)) + " ; TweakAtZ: Alter retract" + break + elif self.relative_extrusion: + if change_retract_amt: + lines[index] = lines[index].replace("E" + str(cur_e), "E" + str(new_retract_amt)) + " ; TweakAtZ: Alter retract" + break + data[self.end_index] = "\n".join(lines) + "\n" + return data + + def _format_lines(self, temp_data: str) -> str: + """ + This adds '-' as padding so the setting descriptions are more readable in the gcode + """ + for l_index, layer in enumerate(temp_data): + lines = layer.split("\n") + for index, line in enumerate(lines): + if "; TweakAtZ:" in line: + lines[index] = lines[index].split(";")[0] + ";" + ("-" * (40 - len(lines[index].split(";")[0]))) + lines[index].split(";")[1] + temp_data[l_index] = "\n".join(lines) + return temp_data + + def _change_bv_fan_speed(self, temp_data: str) -> str: + """ + This can be used to control any fan. Typically this would be an Auxilliary or Build Volume fan + :params: + bv_fan_nr: The 'P' number of the fan + bv_fan_speed: The new speed for the fan + orig_bv_fan_speed: The reset speed. This is currently always "0" as the fan speed may not exist in Cura, or the fan might be 'on-off' and not PWM controlled. + """ + if not self.getSettingValueByKey("enable_bv_fan_change"): + return temp_data + bv_fan_nr = self.getSettingValueByKey("bv_fan_nr") + bv_fan_speed = self.getSettingValueByKey("e1_build_volume_fan_speed") + orig_bv_fan_speed = 0 + if bool(self.extruder_list[0].getProperty("machine_scale_fan_speed_zero_to_one", "value")): + bv_fan_speed = round(bv_fan_speed * 0.01, 2) + orig_bv_fan_speed = round(orig_bv_fan_speed * 0.01, 2) + else: + bv_fan_speed = round(bv_fan_speed * 2.55) + orig_bv_fan_speed = round(orig_bv_fan_speed * 2.55) + + # Add the changes to the gcode + for index, layer in enumerate(temp_data): + if index == self.start_index: + lines = layer.split("\n") + lines.insert(1, f"M106 S{bv_fan_speed} P{bv_fan_nr} ; TweakAtZ: Change Build Volume Fan Speed") + temp_data[index] = "\n".join(lines) + if index == self.end_index: + lines = layer.split("\n") + lines.insert(len(lines) - 2, f"M106 S{orig_bv_fan_speed} P{bv_fan_nr} ; TweakAtZ: Reset Build Volume Fan Speed") + temp_data[index] = "\n".join(lines) + return temp_data + + # Get the starting index or ending index of the change range when 'By Height' + def _is_legal_z(self, data: str, the_height: float) -> int: + """ + When in 'By Height' mode, this will return the index of the layer where the working Z is >= the Starting Z height, or the index of the layer where the working Z >= the Ending Z height + :params: + max_z: The maximum Z height within the Gcode. This is used to determine the upper limit of the data list that should be returned. + the_height: The user input height. This will be adjusted if rafts are enabled and/or Z-hops are enabled + cur_z: Is the current Z height as tracked through the gcode + the_index: The number to return. + """ + # The height passed down cannot exceed the height of the model or the search for the Z fails + lines = data[0].split("\n") + for line in lines: + if "MAXZ" in line or "MAX.Z" in line: + max_z = float(line.split(":")[1]) + break + if the_height > max_z: + the_height = max_z + + starting_z = 0 + the_index = 0 + + # The start height varies depending whether or not rafts are enabled and whether Z-hops are enabled. + if str(self.global_stack.getProperty("adhesion_type", "value")) == "raft": + + # If z-hops are enabled then start looking for the working Z after layer:0 + if self.z_hop_enabled: + for layer in data: + if ";LAYER:0" in layer: + lines = layer.splitlines() + for index, line in enumerate(lines): + try: + if " Z" in line and " E" in lines[index + 1]: + starting_z = round(float(self.getValue(line, "Z")),2) + the_height += starting_z + break + + # If the layer ends without an extruder move following the Z line, then just jump out + except IndexError: + starting_z = round(float(self.getValue(line, "Z")),2) + the_height += starting_z + break + + # If Z-hops are disabled, then look for the starting Z from the start of the raft up to Layer:0 + else: + for layer in data: + lines = layer.splitlines() + for index, line in enumerate(lines): + + # This try/except catches comments in the startup gcode + try: + if " Z" in line and " E" in lines[index - 1]: + starting_z = float(self.getValue(line, "Z")) + except TypeError: + + # Just pass beause there will be further Z values + pass + if ";LAYER:0" in line: + the_height += starting_z + break + + # Initialize 'cur_z' + cur_z = self.initial_layer_height + for index, layer in enumerate(data): + + # Skip over the opening paragraph and StartUp Gcode + if index < 2: + continue + lines = layer.splitlines() + for z_index, line in enumerate(lines): + if line[0:3] in ["G0 ", "G1 ", "G2 ", "G3 "] and index <= self.end_index: + if " Z" in line: + cur_z = float(self.getValue(line, "Z")) + if cur_z >= the_height and lines[z_index - 1].startswith(";TYPE:"): + the_index = index + break + if the_index > 0: + break + + # Catch-all to insure an entry of the 'model_height'. This allows the changes to continue to the end of the top layer + if the_height >= max_z: + the_index = len(data) - 2 + return the_index \ No newline at end of file From 061944c8a7f346f8ccff0242443722f966a494bb Mon Sep 17 00:00:00 2001 From: HellAholic Date: Sun, 14 Sep 2025 10:14:43 +0200 Subject: [PATCH 97/98] add a limiter to the z-height hop --- plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py index 71924419fe..69cf3d1938 100644 --- a/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py +++ b/plugins/PostProcessingPlugin/scripts/ZHopOnTravel.py @@ -90,6 +90,7 @@ class ZHopOnTravel(Script): "default_value": 0.5, "minimum_value": 0, "maximum_value_warning": 5, + "maximum_value": 10, "enabled": "zhop_travel_enabled" }, "min_travel_dist": { @@ -417,7 +418,12 @@ class ZHopOnTravel(Script): reset_type += 4 if extra_prime_dist > 0 and hop_retraction: reset_type += 8 - up_lines = f"G1 F{speed_zhop} Z{round(self._cur_z + hop_height,2)} ; Hop Up" + + machine_height = Application.getInstance().getGlobalContainerStack().getProperty("machine_height", "value") + if self._cur_z + hop_height < machine_height: + up_lines = f"G1 F{speed_zhop} Z{round(self._cur_z + hop_height,2)} ; Hop Up" + else: + up_lines = f"G1 F{speed_zhop} Z{round(machine_height, 2)} ; Hop Up" if reset_type in [1, 9] and hop_retraction: # add retract only when necessary up_lines = f"G1 F{retract_speed} E{round(self._cur_e - retraction_amount, 5)} ; Retract\n" + up_lines self._cur_e = round(self._cur_e - retraction_amount, 5) From 7ca3a2a9bfe1cc262efcf78abb18e05558e6573d Mon Sep 17 00:00:00 2001 From: GregValiant <64202104+GregValiant@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:10:28 -0400 Subject: [PATCH 98/98] Update TweakAtZ.py Made changes per review requests. Update TweakAtZ.py One more change. --- .../PostProcessingPlugin/scripts/TweakAtZ.py | 134 ++++++++++-------- 1 file changed, 71 insertions(+), 63 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/TweakAtZ.py b/plugins/PostProcessingPlugin/scripts/TweakAtZ.py index 8bf75e917f..adb610c949 100644 --- a/plugins/PostProcessingPlugin/scripts/TweakAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/TweakAtZ.py @@ -90,11 +90,10 @@ class TweakAtZ(Script): "a_start_layer": { "label": "Start Layer", "description": "Layer number to start the changes at. Use the Cura preview layer numbers. The changes will start at the beginning of the layer.", - "unit": "", "type": "int", "default_value": 1, - "minimum_value": "-7", - "minimum_value_warning": "1", + "minimum_value": -7, + "minimum_value_warning": 1, "unit": "Layer #", "enabled": "taz_enabled and by_layer_or_height == 'by_layer'" }, @@ -121,7 +120,7 @@ class TweakAtZ(Script): "default_value": 0, "unit": "mm", "enabled": "taz_enabled and by_layer_or_height == 'by_height'" - } , + }, "b_change_speed": { "label": "Change Speeds", "description": "Check to enable a speed change for the Print Speeds.", @@ -153,9 +152,9 @@ class TweakAtZ(Script): "unit": "% ", "type": "int", "default_value": 100, - "minimum_value": "10", - "minimum_value_warning": "50", - "maximum_value_warning": "200", + "minimum_value": 10, + "minimum_value_warning": 50, + "maximum_value_warning": 200, "enabled": "b_change_speed and taz_enabled" }, "c_change_flowrate": { @@ -171,10 +170,10 @@ class TweakAtZ(Script): "unit": "% ", "type": "int", "default_value": 100, - "minimum_value": "25", - "minimum_value_warning": "50", - "maximum_value_warning": "150", - "maximum_value": "200", + "minimum_value": 25, + "minimum_value_warning": 50, + "maximum_value_warning": 150, + "maximum_value": 200, "enabled": "c_change_flowrate and taz_enabled" }, "multi_extruder": { @@ -191,9 +190,9 @@ class TweakAtZ(Script): "unit": "% ", "type": "int", "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", + "minimum_value": 1, + "minimum_value_warning": 10, + "maximum_value_warning": 200, "enabled": "multi_extruder and c_change_flowrate and taz_enabled" }, "d_change_bed_temp": { @@ -209,9 +208,9 @@ class TweakAtZ(Script): "unit": "°C ", "type": "int", "default_value": 60, - "minimum_value": "0", - "minimum_value_warning": "30", - "maximum_value_warning": "120", + "minimum_value": 0, + "minimum_value_warning": 30, + "maximum_value_warning": 120, "enabled": "d_change_bed_temp and taz_enabled" }, "heated_build_volume": { @@ -234,9 +233,9 @@ class TweakAtZ(Script): "unit": "°C ", "type": "int", "default_value": 20, - "minimum_value": "0", - "minimum_value_warning": "15", - "maximum_value_warning": "80", + "minimum_value": 0, + "minimum_value_warning": 15, + "maximum_value_warning": 80, "enabled": "heated_build_volume and e_change_build_volume_temperature and taz_enabled" }, "f_change_extruder_temperature": { @@ -252,9 +251,9 @@ class TweakAtZ(Script): "unit": "°C ", "type": "int", "default_value": 190, - "minimum_value": "0", - "minimum_value_warning": "160", - "maximum_value_warning": "250", + "minimum_value": 0, + "minimum_value_warning": 160, + "maximum_value_warning": 250, "enabled": "f_change_extruder_temperature and taz_enabled" }, "f_extruder_temperature_t1": { @@ -263,9 +262,9 @@ class TweakAtZ(Script): "unit": "°C ", "type": "int", "default_value": 190, - "minimum_value": "0", - "minimum_value_warning": "160", - "maximum_value_warning": "250", + "minimum_value": 0, + "minimum_value_warning": 160, + "maximum_value_warning": 250, "enabled": "multi_extruder and f_change_extruder_temperature and taz_enabled" }, "g_change_retract": { @@ -288,9 +287,9 @@ class TweakAtZ(Script): "unit": "mm/s ", "type": "float", "default_value": 40, - "minimum_value": "0", - "minimum_value_warning": "0", - "maximum_value_warning": "100", + "minimum_value": 1, + "minimum_value_warning": 0, + "maximum_value_warning": 100, "enabled": "g_change_retract and g_change_retract_speed and taz_enabled and not multi_extruder" }, "g_change_retract_amount": { @@ -306,9 +305,8 @@ class TweakAtZ(Script): "unit": "mm ", "type": "float", "default_value": 6.5, - "minimum_value": "0", - "minimum_value_warning": "0", - "maximum_value_warning": "20", + "minimum_value": 0, + "maximum_value_warning": 20, "enabled": "g_change_retract and g_change_retract_amount and taz_enabled and not multi_extruder" }, "enable_bv_fan_change": { @@ -387,13 +385,13 @@ class TweakAtZ(Script): nbr_raft_layers += 1 if ";LAYER:0\n" in layer: break - + # Adjust the start layer to account for any raft layers self.start_layer -= nbr_raft_layers - + # Find the indexes of the Start and End layers if 'By Layer' self.start_index = 0 - + # When retraction is enabled it adds a single line item to the data list self.end_index = len(data) - 1 - int(self.retract_enabled) if self.getSettingValueByKey("by_layer_or_height") == "by_layer": @@ -401,20 +399,20 @@ class TweakAtZ(Script): if ";LAYER:" + str(self.start_layer) + "\n" in layer: self.start_index = index break - + # If the changes continue to the top layer if end_layer == -1: if self.retract_enabled: self.end_index = len(data) - 2 else: self.end_index = len(data) - 1 - + # If the changes end below the top layer else: - + # Adjust the end layer from base1 numbering to base0 numbering end_layer -= 1 - + # Adjust the End Layer if it is not the top layer and if bed adhesion is 'raft' end_layer -= nbr_raft_layers for index, layer in enumerate(data): @@ -476,7 +474,7 @@ class TweakAtZ(Script): if index >= self.start_index and index <= self.end_index: lines = layer.splitlines() for l_index, line in enumerate(lines): - if " F" in line and " X" in line and " Y" in line and not " Z" in line: + if self._f_x_y_not_z(line): f_value = self.getValue(line, "F") if line.startswith(("G1", "G2", "G3")): lines[l_index] = line.replace("F" + str(f_value), "F" + str(round(f_value * speed_x))) @@ -512,7 +510,7 @@ class TweakAtZ(Script): lines = layer.splitlines() for l_index, line in enumerate(lines): if active_tool == target_extruder: - if " F" in line and " X" in line and " Y" in line and not " Z" in line: + if self._f_x_y_not_z(line): f_value = self.getValue(line, "F") if line.startswith(("G1", "G2", "G3")): lines[l_index] = line.replace("F" + str(f_value), "F" + str(round(f_value * speed_x))) @@ -547,7 +545,7 @@ class TweakAtZ(Script): new_flowrate_1 = f"\nM221 S{new_flow_ext_1} ; TweakAtZ: Alter Flow Rate" else: new_flowrate_1 = "" - + # For single extruder if self.extruder_count == 1: lines = data[self.start_index].splitlines() @@ -556,7 +554,7 @@ class TweakAtZ(Script): lines = data[self.end_index].splitlines() lines[len(lines) - 2] += reset_flowrate_0 data[self.end_index] = "\n".join(lines) + "\n" - + # For dual-extruders elif self.extruder_count > 1: for index, layer in enumerate(data): @@ -644,14 +642,14 @@ class TweakAtZ(Script): data[2] = re.sub("M104 S", ";M104 S", data[2]) if "M104 S" in data[3]: data[3] = re.sub("M104 S", ";M104 S", data[3]) - + # Add the temperature change at the beginning of the start layer lines = data[self.start_index].splitlines() for index, line in enumerate(lines): lines[0] += "\n" + "M104 S" + str(self.new_hotend_temp_0) + " ; TweakAtZ: Change Nozzle Temperature" data[self.start_index] = "\n".join(lines) + "\n" break - + # Revert the temperature to the Cura setting at the end of the end layer lines = data[self.end_index].splitlines() for index, line in enumerate(lines): @@ -664,7 +662,7 @@ class TweakAtZ(Script): self.new_hotend_temp_1 = self.getSettingValueByKey("f_extruder_temperature_t1") self.orig_hot_end_temp_1 = int(self.extruder_list[1].getProperty("material_print_temperature", "value")) self.orig_standby_temp_1 = int(self.extruder_list[1].getProperty("material_standby_temperature", "value")) - + # Track the tool number up to the start of the start layer self.getTool("T0") for index, layer in enumerate(data): @@ -674,10 +672,10 @@ class TweakAtZ(Script): self.getTool(line) if index == self.start_index - 1: break - + # Add the active extruder initial temperature change at the start of the starting layer data[self.start_index] = data[self.start_index].replace("\n", f"\nM104 S{self.active_print_temp} ; TweakAtZ: Start Temperature Change\n",1) - + # At the start layer commence making the changes for index, layer in enumerate(data): if index < self.start_index: @@ -686,7 +684,7 @@ class TweakAtZ(Script): break lines = layer.splitlines() for l_index, line in enumerate(lines): - + # Continue to track the tool number if line.startswith("T"): self.getTool(line) @@ -700,7 +698,7 @@ class TweakAtZ(Script): elif self.getValue(line, "S") == self.active_tool_orig_temp: lines[l_index] = re.sub("S(\d+|\d.+)", f"S{self.active_print_temp} ; TweakAtZ: Alter temperature", line) data[index] = "\n".join(lines) + "\n" - + # Revert the active extruder temperature at the end of the changes lines = data[self.end_index].split("\n") lines[len(lines) - 3] += f"\nM104 {self.active_tool} S{self.active_tool_orig_temp} ; TweakAtZ: Original Temperature active tool" @@ -745,23 +743,30 @@ class TweakAtZ(Script): """ if not self.retract_enabled: return + + # Exit if neither child setting is checked. + if not (change_retract_amt or change_retract_speed): + return + speed_retract_0 = int(self.extruder_list[0].getProperty("retraction_speed", "value") * 60) retract_amt_0 = self.extruder_list[0].getProperty("retraction_amount", "value") change_retract_amt = self.getSettingValueByKey("g_change_retract_amount") change_retract_speed = self.getSettingValueByKey("g_change_retract_speed") new_retract_speed = int(self.getSettingValueByKey("g_retract_speed") * 60) new_retract_amt = self.getSettingValueByKey("g_retract_amount") - + # Use M207 and M208 to adjust firmware retraction when required if self.firmware_retraction: lines = data[self.start_index].splitlines() firmware_start_str = "\nM207" firmware_reset = "" if change_retract_speed: - firmware_start_str += f" F{new_retract_speed} ; TweakAtZ: Alter Firmware Retract speed" + firmware_start_str += f" F{new_retract_speed}" if change_retract_amt: - firmware_start_str += f" S{new_retract_amt} ; TweakAtZ: Alter Firmware Retract amt" - if change_retract_speed: + firmware_start_str += f" S{new_retract_amt}" + if change_retract_speed or change_retract_amt: + firmware_start_str += " ; TweakAtZ: Alter Firmware Retract speed/amt" + if change_retract_speed: firmware_start_str += f"\nM208 F{new_retract_speed} ; TweakAtZ: Alter Firmware Prime speed" lines[0] += firmware_start_str data[self.start_index] = "\n".join(lines) + "\n" @@ -818,7 +823,7 @@ class TweakAtZ(Script): lines[index] = lines[index].replace("F" + str(cur_speed), "F" + str(new_retract_speed)) lines[index] += " ; TweakAtZ: Alter retract" else: - + # Prime line if change_retract_speed: lines[index] = lines[index].replace("F" + str(cur_speed), "F" + str(new_retract_speed)) @@ -920,7 +925,7 @@ class TweakAtZ(Script): # The start height varies depending whether or not rafts are enabled and whether Z-hops are enabled. if str(self.global_stack.getProperty("adhesion_type", "value")) == "raft": - + # If z-hops are enabled then start looking for the working Z after layer:0 if self.z_hop_enabled: for layer in data: @@ -932,7 +937,7 @@ class TweakAtZ(Script): starting_z = round(float(self.getValue(line, "Z")),2) the_height += starting_z break - + # If the layer ends without an extruder move following the Z line, then just jump out except IndexError: starting_z = round(float(self.getValue(line, "Z")),2) @@ -944,29 +949,29 @@ class TweakAtZ(Script): for layer in data: lines = layer.splitlines() for index, line in enumerate(lines): - + # This try/except catches comments in the startup gcode try: if " Z" in line and " E" in lines[index - 1]: starting_z = float(self.getValue(line, "Z")) except TypeError: - + # Just pass beause there will be further Z values pass if ";LAYER:0" in line: the_height += starting_z break - + # Initialize 'cur_z' cur_z = self.initial_layer_height for index, layer in enumerate(data): - + # Skip over the opening paragraph and StartUp Gcode if index < 2: continue lines = layer.splitlines() for z_index, line in enumerate(lines): - if line[0:3] in ["G0 ", "G1 ", "G2 ", "G3 "] and index <= self.end_index: + if len(line) >= 3 and line[0:3] in ['G0 ', 'G1 ', 'G2 ', 'G3 '] and index <= self.end_index: if " Z" in line: cur_z = float(self.getValue(line, "Z")) if cur_z >= the_height and lines[z_index - 1].startswith(";TYPE:"): @@ -978,4 +983,7 @@ class TweakAtZ(Script): # Catch-all to insure an entry of the 'model_height'. This allows the changes to continue to the end of the top layer if the_height >= max_z: the_index = len(data) - 2 - return the_index \ No newline at end of file + return the_index + + def _f_x_y_not_z(self, line): + return " F" in line and " X" in line and " Y" in line and not " Z" in line