├── .gitignore
├── AlterZhops.py
├── BridgeTemperatureAdjustment.py
├── ChangeFanValue.py
├── CheckFirstSpeed.py
├── CommentGCode.py
├── CreateJPEGThumbnail.py
├── CreateThumbnail.py
├── Cura_GCode.CSV
├── CustomTimeLapse.py
├── DiagonalZHop.py
├── DisplayPrintInfosOnLCD.py
├── FanIroning.py
├── FastFirstInfill.py
├── FlowTower.py
├── GCodeDocumentation.py
├── GradientInfill.py
├── GregVInsertAtLayerChange.py
├── InfillLast.py
├── InhibFan.py
├── InsertAtLayerChange.py
├── InsertAtLayerNumber.py
├── KlipperPrintArea.py
├── LICENSE
├── LevelingMeshOptimizer.py
├── MultiBrim.py
├── README.md
├── RepRapPrintInfos.py
├── RetractContinue.py
├── RetractTower.py
├── SlowZ.py
├── SpeedTower.py
├── SpoonOrder.py
├── TempFanTower.py
├── TemperatureTower.py
├── UPPostProcess.py
├── ZMoveG0.py
├── ZMoveIroning.py
├── ZOffsetBrim.py
├── images
├── AlterZhops.png
├── ChangeFanValue.jpg
├── CheckFirstSpeed.JPG
├── CheckFirstSpeedorigine.png
├── CheckFirstSpeedresult.png
├── ErrorZmoveG0.jpg
├── FanIroning.jpg
├── FanWithBridgeValue.jpg
├── FanWithoutBridgeValue.jpg
├── GcodeDocumentation.jpg
├── InfillLast.png
├── InhibFan.jpg
├── InsertAtLayerNumber.jpg
├── NotDetectedFanIroning.jpg
├── PrintInfos.jpg
├── SpoonOrder.png
├── ZmoveG0.jpg
├── ZmoveIroning.jpg
├── benchy.jpg
├── commentGcode.jpg
├── fastfirstinfill.jpg
├── gcode_temptower.jpg
├── gradient.jpg
├── gradient2.jpg
├── gradient3.jpg
├── multibrim.jpg
├── multilayerbrim.svg
├── origine.png
├── plugins.jpg
├── result.png
├── retract-tower.jpg
├── slowz.jpg
├── slowzsettings.jpg
├── speedtower.jpg
└── tempfan.jpg
└── startHeatingAtPercentage.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | live_script.py
3 |
--------------------------------------------------------------------------------
/ChangeFanValue.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: January 13, 2020
6 | #
7 | # Description: postprocessing-script to manage Fan Value
8 | #
9 | #
10 | #------------------------------------------------------------------------------------------------------------------------------------
11 | #
12 | # Version 1.1 9/01/2020
13 | #
14 | #------------------------------------------------------------------------------------------------------------------------------------
15 |
16 | import string
17 | from ..Script import Script
18 | from UM.Application import Application # To get the current printer's settings.
19 | from cura.CuraVersion import CuraVersion # type: ignore
20 | from UM.Message import Message
21 | from UM.Logger import Logger
22 | from UM.i18n import i18nCatalog # Translation
23 | catalog = i18nCatalog("cura")
24 |
25 | __version__ = '1.1'
26 |
27 | class ChangeFanValue(Script):
28 | def __init__(self):
29 | super().__init__()
30 |
31 | def getSettingDataString(self):
32 | return """{
33 | "name": "ChangeFanValue",
34 | "key": "ChangeFanValue",
35 | "metadata": {},
36 | "version": 2,
37 | "settings":
38 | {
39 | "usefanvalue":
40 | {
41 | "label": "Set Fan Value on Minimum Time",
42 | "description": "Change the Fan Value on minimum Time situation",
43 | "type": "bool",
44 | "default_value": false
45 | },
46 | "fanchange":
47 | {
48 | "label": "Fan values in %",
49 | "description": "The fan speed change of each block for minimum time Layer situation",
50 | "type": "int",
51 | "unit": "%",
52 | "default_value": 100,
53 | "minimum_value": 1,
54 | "maximum_value": 100,
55 | "minimum_value_warning": 50,
56 | "maximum_value_warning": 100
57 | }
58 | }
59 | }"""
60 |
61 | # Get the value
62 | def GetDataExtruder(self,id_ex,key,dec=0):
63 |
64 | # Deprecation Warning
65 | # extrud = list(Application.getInstance().getGlobalContainerStack().extruders.values())
66 | extruder_stack = Application.getInstance().getExtruderManager().getActiveExtruderStacks()
67 | GetVal = extruder_stack[id_ex].getProperty(key, "value")
68 | #GetLabel = Application.getInstance().getGlobalContainerStack().getProperty(key, "label")
69 | #GetType = Application.getInstance().getGlobalContainerStack().getProperty(key, "type")
70 | #GetUnit = Application.getInstance().getGlobalContainerStack().getProperty(key, "unit")
71 |
72 | return GetVal
73 |
74 | def execute(self, data):
75 |
76 | usefan = False
77 | fanvalues = 0
78 | usefan = bool(self.getSettingValueByKey("usefanvalue"))
79 | fanvalues = int(self.getSettingValueByKey("fanchange"))
80 |
81 | # machine_extruder_count
82 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
83 | extruder_count = extruder_count-1
84 | extruder_id=extruder_count
85 |
86 | # cool_min_layer_time
87 | self._cool_min_layer_time = float(self.GetDataExtruder(extruder_id,"cool_min_layer_time"))
88 | Logger.log('d', "cool_min_layer_time --> " + str(self._cool_min_layer_time) )
89 |
90 | currentfan = 0
91 | Current_Fan_Value = 0
92 | Current_Layer = 0
93 | setfan = int((int(fanvalues)/100)*255) # 100% = 255 pour ventilateur
94 | #Logger.log('d', "setfan --> " + str(setfan) )
95 |
96 | save_time=0
97 | Just_Modi=0
98 | idl=0
99 |
100 | for layer in data:
101 | layer_index = data.index(layer)
102 |
103 | lines = layer.split("\n")
104 | for line in lines:
105 | if line.startswith(";LAYER:0"):
106 | idl=0
107 |
108 | if line.startswith(";LAYER:"):
109 | Current_Layer = int(line.split(":")[1])
110 |
111 | if line.startswith("; MODI_FAN"):
112 | Just_Modi=1
113 |
114 | if line.startswith("M106 S") and usefan :
115 | if Just_Modi==1 :
116 | Just_Modi=0
117 | else :
118 | line_index = lines.index(line)
119 | Current_Fan_Value = int(line.split("S")[1])
120 | #Logger.log('d', "Current_Fan_Value --> " + str(Current_Fan_Value) )
121 | if idl==1:
122 | lines[line_index] = "; " + line
123 |
124 | # M107: Eteindre les ventilateurs
125 | if line.startswith("M107") and (usefan):
126 | line_index = lines.index(line)
127 | Current_Fan_Value=0
128 | if idl==1:
129 | lines[line_index] = "; " + line
130 |
131 | if line.startswith(";TIME_ELAPSED:"):
132 | line_index = lines.index(line)
133 | total_time = float(line.split(":")[1])
134 | Layer_time=total_time-save_time
135 |
136 | if Layer_time<=self._cool_min_layer_time :
137 | if idl==0:
138 | #Logger.log('d', "Time MODI --> " + str(Layer_time))
139 | #Logger.log('d', "MODI LAYER--> " + str(Current_Layer))
140 | lines.insert(line_index + 1, "; MODI_FAN")
141 | lines.insert(line_index + 2, "M106 S"+str(setfan))
142 | idl=1
143 | Just_Modi=1
144 | else:
145 |
146 | if idl==1:
147 | #Logger.log('d', "Reset Time --> " + str(Layer_time) )
148 | #Logger.log('d', "Reset FAN VALUE --> " + str(Current_Fan_Value))
149 | Cline=lines[line_index]
150 | if Current_Fan_Value == 0:
151 | lines.insert(line_index + 1, "M107")
152 | else:
153 | lines.insert(line_index + 1, "M106 S"+str(Current_Fan_Value))
154 | idl=0
155 |
156 | save_time=total_time
157 |
158 |
159 | result = "\n".join(lines)
160 | data[layer_index] = result
161 |
162 | return data
163 |
--------------------------------------------------------------------------------
/CheckFirstSpeed.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: January 04, 2024
6 | #
7 | # Description: postprocessing script to modifiy the first layer infill and Check the first Wall Speed Bug Cura 5.6
8 | #
9 | #------------------------------------------------------------------------------------------------------------------------------------
10 | #
11 | # Version 1.0 04/01/2024
12 | #
13 | #------------------------------------------------------------------------------------------------------------------------------------
14 |
15 | import string
16 | import re # To perform the search
17 | from ..Script import Script
18 | from UM.Application import Application # To get the current printer's settings.
19 | from cura.CuraVersion import CuraVersion # type: ignore
20 | from UM.Logger import Logger
21 |
22 | from enum import Enum
23 |
24 |
25 |
26 | __version__ = '1.0'
27 |
28 | class Section(Enum):
29 | """Enum for section type."""
30 |
31 | NOTHING = 0
32 | SKIRT = 1
33 | INNER_WALL = 2
34 | OUTER_WALL = 3
35 | INFILL = 4
36 | SKIN = 5
37 | SKIN2 = 6
38 |
39 | def is_begin_layer_line(line: str) -> bool:
40 | """Check if current line is the start of a layer section.
41 |
42 | Args:
43 | line (str): Gcode line
44 |
45 | Returns:
46 | bool: True if the line is the start of a layer section
47 | """
48 | return line.startswith(";LAYER:")
49 |
50 | def is_begin_type_line(line: str) -> bool:
51 | """Check if current line is the start of a new type section.
52 |
53 | Args:
54 | line (str): Gcode line
55 |
56 | Returns:
57 | bool: True if the line is the start of a new type section
58 | """
59 | return line.startswith(";TYPE:")
60 |
61 | def is_retract_line(line: str) -> bool:
62 | """Check if current line is a retract segment.
63 |
64 | Args:
65 | line (str): Gcode line
66 |
67 | Returns:
68 | bool: True if the line is a retract segment
69 | """
70 | return "G1" in line and "F" in line and "E" in line and not "X" in line and not "Y" in line and not "Z" in line
71 |
72 | def is_extrusion_line(line: str) -> bool:
73 | """Check if current line is a standard printing segment.
74 |
75 | Args:
76 | line (str): Gcode line
77 |
78 | Returns:
79 | bool: True if the line is a standard printing segment
80 | """
81 | return "G1" in line and "X" in line and "Y" in line and "E" in line
82 |
83 | def is_not_extrusion_line(line: str) -> bool:
84 | """Check if current line is a rapid movement segment.
85 |
86 | Args:
87 | line (str): Gcode line
88 |
89 | Returns:
90 | bool: True if the line is a standard printing segment
91 | """
92 | return "G0" in line and "X" in line and "Y" in line and not "E" in line
93 |
94 | def is_begin_skin_segment_line(line: str) -> bool:
95 | """Check if current line is the start of an skin.
96 |
97 | Args:
98 | line (str): Gcode line
99 |
100 | Returns:
101 | bool: True if the line is the start of an skin section
102 | """
103 | return line.startswith(";TYPE:SKIN")
104 |
105 | def is_begin_inner_wall_segment_line(line: str) -> bool:
106 | """Check if current line is the start of an inner wall.
107 |
108 | Args:
109 | line (str): Gcode line
110 |
111 | Returns:
112 | bool: True if the line is the start of an inner wall section
113 | """
114 | return line.startswith(";TYPE:WALL-INNER")
115 |
116 | def is_begin_outer_wall_segment_line(line: str) -> bool:
117 | """Check if current line is the start of an outer wall.
118 |
119 | Args:
120 | line (str): Gcode line
121 |
122 | Returns:
123 | bool: True if the line is the start of an outer wall section
124 | """
125 | return line.startswith(";TYPE:WALL-OUTER")
126 |
127 | class CheckFirstSpeed(Script):
128 | def __init__(self):
129 | super().__init__()
130 |
131 | def getSettingDataString(self):
132 | return """{
133 | "name": "Check First Speed",
134 | "key": "CheckFirstSpeed",
135 | "metadata": {},
136 | "version": 2,
137 | "settings":
138 | {
139 | "modifyinfillspeed":
140 | {
141 | "label": "Modify Infill Speed",
142 | "description": "Option to modify First layer infill speed value.",
143 | "type": "bool",
144 | "default_value": true
145 | },
146 | "infillspeed":
147 | {
148 | "label": "First layer infill speed",
149 | "description": "First layer infill speed value.",
150 | "type": "float",
151 | "unit": "mm/s",
152 | "default_value": 30,
153 | "minimum_value": 1,
154 | "maximum_value": 100,
155 | "maximum_value_warning": 50,
156 | "enabled": "modifyinfillspeed"
157 | },
158 | "replacewallspeed":
159 | {
160 | "label": "Replace Wall Speed",
161 | "description": "Option to replace wall speed on first layer (Cura 5.6 bug fix).",
162 | "type": "bool",
163 | "default_value": true
164 | }
165 | }
166 | }"""
167 |
168 | # Get the value
169 | def GetDataExtruder(self,id_ex,key,dec=0):
170 |
171 | extruder_stack = Application.getInstance().getExtruderManager().getActiveExtruderStacks()
172 | GetVal = extruder_stack[id_ex].getProperty(key, "value")
173 |
174 | return GetVal
175 |
176 | def execute(self, data):
177 |
178 | InfillSpeed = float(self.getSettingValueByKey("infillspeed")) * 60
179 | checkFirstWallSpeed = bool(self.getSettingValueByKey("replacewallspeed"))
180 | modifyFirstInfillSpeed = bool(self.getSettingValueByKey("modifyinfillspeed"))
181 |
182 | # machine_extruder_count
183 | extruder_count=int(Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value"))
184 | extruder_count = extruder_count-1
185 | extruder_stack = Application.getInstance().getExtruderManager().getActiveExtruderStacks()
186 | extruder_nr=len(extruder_stack)
187 | extruder_nr = int(Application.getInstance().getExtruderManager().getActiveExtruderStacks()[0].getProperty("extruder_nr", "value"))
188 | extruder_id=int(Application.getInstance().getGlobalContainerStack().getProperty("wall_extruder_nr", "value"))
189 |
190 |
191 | if extruder_id == -1 :
192 | extruder_id=extruder_nr
193 |
194 | if extruder_id>extruder_count :
195 | extruder_id=extruder_count
196 |
197 | # speed_print_layer_0
198 | self._speed_print_layer_0 = float(self.GetDataExtruder(extruder_id,"speed_print_layer_0"))
199 | Logger.log('d', "extruder_count --> " + str(extruder_count))
200 | Logger.log('d', "extruder_nr --> " + str(extruder_nr))
201 | Logger.log('d', "extruder_id --> " + str(extruder_id))
202 | Logger.log('d', "speed_print_layer_0 --> " + str(self._speed_print_layer_0) )
203 |
204 | idl=0
205 |
206 | for layer in data:
207 | layer_index = data.index(layer)
208 |
209 | lines = layer.split("\n")
210 | for line in lines:
211 |
212 | if is_begin_layer_line(line):
213 | # Logger.log('d', 'layer_index : {:d}'.format(layer_index))
214 | # Logger.log('d', 'layer_lines : {}'.format(line))
215 | if line.startswith(";LAYER:0"):
216 | idl=1
217 | else :
218 | idl=0
219 |
220 | if is_begin_type_line(line) and idl > 0:
221 | if is_begin_skin_segment_line(line) and modifyFirstInfillSpeed :
222 | idl=4
223 | ReplaceSpeedInstruction="F" + str(InfillSpeed)
224 | # Logger.log('d', 'Skin line : {}'.format(ReplaceSpeedInstruction))
225 | elif is_begin_inner_wall_segment_line(line) and checkFirstWallSpeed :
226 | idl=3
227 | ReplaceSpeedInstruction="F" + str(self._speed_print_layer_0*60)
228 | # Logger.log('d', 'Inner Wall line : {}'.format(ReplaceSpeedInstruction))
229 | elif is_begin_outer_wall_segment_line(line) and checkFirstWallSpeed :
230 | idl=2
231 | ReplaceSpeedInstruction="F" + str(self._speed_print_layer_0*60)
232 | # Logger.log('d', 'Outer Wall line : {}'.format(ReplaceSpeedInstruction))
233 | else :
234 | idl=1
235 |
236 | if idl >= 2 and is_extrusion_line(line):
237 | searchF = re.search(r"F(\d*\.?\d*)", line)
238 | if searchF:
239 | line_index = lines.index(line)
240 | save_F=float(searchF.group(1))
241 | instructionF="F"+str(searchF.group(1))
242 | # Logger.log('d', 'save_F : {:f}'.format(save_F))
243 | # Logger.log('d', 'line : {}'.format(line))
244 | # Logger.log('d', 'line replace : {}'.format(line.replace(instructionF,ReplaceSpeedInstruction)))
245 | lines[line_index]=line.replace(instructionF,ReplaceSpeedInstruction)
246 |
247 | result = "\n".join(lines)
248 | data[layer_index] = result
249 |
250 | return data
251 |
--------------------------------------------------------------------------------
/CommentGCode.py:
--------------------------------------------------------------------------------
1 |
2 | # Copyright (c) 2019 Lisa Erlingheuser
3 | # This Cura PostProcessing-Script is released under the terms of the AGPLv3 or higher.
4 |
5 | # This Cura Postprocessing Script adds comments to the G-Code.
6 | # The user can select or deselect comments for M-Commands and G-Commands separately.
7 |
8 | # G0 and G1 commands are only commented if a retract is included.
9 |
10 | # Command, description and parameters are read from a CSV file. If a command is not contained, the required data is determined once via the website http://marlinfw.org/docs/gcode/
11 | # and added to the CSV file.
12 |
13 | import re #To perform the search and replace.
14 | import string
15 | import os
16 | import urllib.request
17 |
18 | from UM.Logger import Logger
19 | from UM.Message import Message
20 | from UM.i18n import i18nCatalog
21 | catalog = i18nCatalog("cura")
22 |
23 | from ..Script import Script
24 |
25 |
26 | class CommentGCode(Script):
27 |
28 | def getSettingDataString(self):
29 | return """{
30 | "name": "Comment G Code",
31 | "key": "CommentGCode",
32 | "metadata": {},
33 | "version": 2,
34 | "settings":
35 | {
36 | "is_MCmd":
37 | {
38 | "label": "M-Commands",
39 | "description": "When enabled, M-Commands will be commented.",
40 | "type": "bool",
41 | "default_value": true
42 | },
43 | "is_GCmd":
44 | {
45 | "label": "G-Commands",
46 | "description": "When enabled, G-Commands will be commented, except G0 and G1.",
47 | "type": "bool",
48 | "default_value": true
49 | }
50 | }
51 | }"""
52 |
53 | def getVar(self):
54 | global bM, bG
55 | if hasattr(self, 'getSettingValueByKey'):
56 | bM = self.getSettingValueByKey("is_MCmd")
57 | bG = self.getSettingValueByKey("is_GCmd")
58 | else:
59 | bM = True
60 | bG = True
61 | return
62 |
63 | def getCmdParamTab(self, cmdparam):
64 | arret = []
65 | #Logger.log('d', "CommentGCode --> " + 'getCmdParamTab cmdparam: ' + str(cmdparam))
66 | for cp in cmdparam:
67 | if cp == '':
68 | break
69 | arp = cp.split("-")
70 | var1 = arp[0].strip()
71 | var1 = var1.strip('[')
72 | arp[0] = var1.split('<')[0]
73 | arp[1] = arp[1].strip()
74 | arret.append(arp)
75 | return arret
76 |
77 | def getCmdDescP(self, param, cmdparam):
78 | arcmd = self.getCmdParamTab(cmdparam)
79 | ret = ''
80 | for lp in param:
81 | for cp in arcmd:
82 | if lp[0] == cp[0]:
83 | if ret != '':
84 | ret = ret + ", "
85 | ret = ret + str(cp[1]) + '=' + str(lp[1:])
86 | return ret
87 |
88 | def _restmit(self, data, text):
89 | ret = data[data.index(text):]
90 | return ret
91 |
92 | def _restbisohne(self, data, text):
93 | try:
94 | ret = data[0:data.index(text)]
95 | except:
96 | ret = ""
97 | return ret
98 |
99 | def _restbismit(self, data, text):
100 | ret = data[0:data.index(text)+len(text)+1]
101 | return ret
102 |
103 | def _restohne(self, data, text):
104 | try:
105 | ret = data[data.index(text)+ len(text):]
106 | except:
107 | ret = ""
108 | return ret
109 |
110 |
111 | def getCmdDescFromHTML(self, htmlData, cmd):
112 | desc = None
113 | param = None
114 | erg = htmlData
115 | erg = self._restmit(erg, '
')
116 | desc = self._restbisohne(erg, "
")
117 | desc = self._restohne(desc, "-")
118 | desc = desc.strip()
119 | retlist = [cmd, desc]
120 | erg = self._restmit(erg, "Parameters
")
121 | erg = self._restmit(erg, "")
124 | for line in table:
125 | line = line.replace("<", "<")
126 | line = line.replace(">", ">")
127 | p = self._restohne(line, "")
128 | if p != "":
129 | p = self._restbisohne(p, "
")
130 | p = p.strip()
131 | d = self._restohne(line, "")
132 | d = self._restbisohne(d, "
")
133 | if '.' in d:
134 | d = self._restbisohne(d, ".")
135 | d = d.strip()
136 | retlist.append(p + " - " + d)
137 | ret = ";".join(retlist)
138 | #Logger.log('d', "CommentGCode --> " + 'getCmdDescFromHTML ret: ' + ret)
139 | return ret
140 |
141 | def getCmdDescUrl(self, cmd):
142 | result = None
143 | cmdB = cmd[0]
144 | cmdI = cmd[1:]
145 | scmdI = "%03i" % int(cmdI)
146 | url = "http://marlinfw.org/docs/gcode/" + cmdB + scmdI + ".html"
147 | #Logger.log('d', "CommentGCode --> " + 'getCmdDescUrl URL: ' + url)
148 | try:
149 | request = urllib.request.Request(url)
150 | response = urllib.request.urlopen(request)
151 | result = response.read().decode("utf-8")
152 | #Logger.log('d', "CommentGCode --> " + 'getCmdDescUrl result: ' + str(result))
153 | except URLError:
154 | Logger.log('w', "CommentGCode --> " + 'getCmdDescUrl Error')
155 | return
156 | return result
157 |
158 |
159 | def getCmdDesc(self, cmd, cmdplist):
160 | global cmdtab, _msg
161 | cmddesc = ''
162 | cmdp = ''
163 | line = ""
164 | bgef = False
165 | if cmdtab == None:
166 | cmddir = os.path.join( os.path.dirname(__file__), 'Cura_GCode.CSV')
167 | cmdges = open(cmddir).read()
168 | tablines = cmdges.split("\n")
169 | if cmd + ";" not in cmdges:
170 | cmdhtml = self.getCmdDescUrl(cmd)
171 | if cmdhtml != None:
172 | newline = self.getCmdDescFromHTML(cmdhtml, cmd)
173 | #Logger.log('d', "CommentGCode --> " + 'getCmdDesc newline: ' + str(newline))
174 | cmdges += newline
175 | tablines = cmdges.split("\n")
176 | try:
177 | fobj_out = open(cmddir,'w')
178 | fobj_out.write(cmdges)
179 | fobj_out.close()
180 | except:
181 | pass
182 | for line in tablines:
183 | cols = line.split(";")
184 | if cols[0] == cmd:
185 | bgef = True
186 | if len(cols) > 1 :
187 | cmddesc = cols[1]
188 | if len(cols) > 2:
189 | cmddp = cols[2:]
190 | if cmdplist != []:
191 | cmdp = self.getCmdDescP(cmdplist, cmddp)
192 | if cmdp != '':
193 | cmddesc = cmddesc + ', ' + cmdp
194 | if bgef == False:
195 | _msg += 'Command ' + str(cmd) + ' missing in G-Code file' + '\n'
196 | return cmddesc
197 |
198 | def addCmd(self, line, cmd, cmdplist):
199 | desc = self.getCmdDesc(cmd, cmdplist)
200 | if desc != '':
201 | line = line + '; --> ' + desc
202 | return line
203 |
204 | def execute(self, data):
205 | global bM, bG, bShort, _msg
206 | global cmdtab
207 | _msg = ''
208 | cmdtab = None
209 | self.getVar()
210 | lastE = 0
211 | lineNo = 0
212 |
213 | for layer_number, layer in enumerate(data):
214 | lines = layer.split("\n")
215 | i1 = 0
216 | for line in lines:
217 | lineNo = lineNo +1
218 | if line != '':
219 | if not ';' in line:
220 | arline = line.split(' ')
221 | cmd = arline[0]
222 | if cmd[0] == 'G':
223 | if cmd != 'G1' and cmd != 'G0':
224 | if bG == True:
225 | line = self.addCmd(line, cmd, arline[1:])
226 | else:
227 | actE = Script.getValue(self, line = line, key = 'E')
228 | if actE != None:
229 | actE = float(actE)
230 | if actE < lastE:
231 | retract = actE - lastE
232 | line = line + '; --> Retract ' + str(round(retract, 2)) + ' mm'
233 | if i1 > 0:
234 | print(' line ' + str(lineNo-1) + ' : ' + lines[i1-1])
235 | print(' line ' + str(lineNo) + ' : ' + line)
236 | else:
237 | print(' line ' + str(lineNo) + ' : ' + line)
238 | lastE = actE
239 | if cmd == 'G92':
240 | lastE = Script.getValue(self, line = line, key = 'E')
241 | if cmd == 'G91':
242 | lastE = 0
243 | elif cmd[0] == 'M':
244 | if bM == True:
245 | line = self.addCmd(line, cmd, arline[1:])
246 | elif cmd[0] == 'T':
247 | line = line + '; --> Activation Extruder ' + cmd[1]
248 | lines[i1] = line
249 | i1 = i1 + 1
250 | sep = '\n'
251 | data[layer_number] = sep.join(lines)
252 | if _msg != None and _msg != '':
253 | Message("Info Comment G-Code:" + "\n" + _msg, title = catalog.i18nc("@info:title", "Post Processing")).show()
254 | return data
255 |
--------------------------------------------------------------------------------
/CreateJPEGThumbnail.py:
--------------------------------------------------------------------------------
1 | #--------------------------------------
2 | # Cura JPEG Thumbnail creator
3 | # Professional firmware for Ender3v2
4 | # Miguel A. Risco-Castillo
5 | # 2021-07-01
6 | # Contains code from:
7 | # https://github.com/Ultimaker/Cura/blob/master/plugins/PostProcessingPlugin/scripts/CreateThumbnail.py
8 | #--------------------------------------
9 |
10 | import base64
11 |
12 | from UM.Logger import Logger
13 | from cura.Snapshot import Snapshot
14 | from PyQt5.QtCore import QByteArray, QIODevice, QBuffer
15 |
16 | from ..Script import Script
17 |
18 |
19 | class CreateJPEGThumbnail(Script):
20 | def __init__(self):
21 | super().__init__()
22 |
23 | def _createSnapshot(self, width, height):
24 | Logger.log("d", "Creating thumbnail image...")
25 | try:
26 | return Snapshot.snapshot(width, height)
27 | except Exception:
28 | Logger.logException("w", "Failed to create snapshot image")
29 |
30 | def _encodeSnapshot(self, snapshot):
31 | Logger.log("d", "Encoding thumbnail image...")
32 | try:
33 | thumbnail_buffer = QBuffer()
34 | thumbnail_buffer.open(QBuffer.ReadWrite)
35 | thumbnail_image = snapshot
36 | thumbnail_image.save(thumbnail_buffer, "JPG")
37 | base64_bytes = base64.b64encode(thumbnail_buffer.data())
38 | base64_message = base64_bytes.decode('ascii')
39 | thumbnail_buffer.close()
40 | return base64_message
41 | except Exception:
42 | Logger.logException("w", "Failed to encode snapshot image")
43 |
44 | def _convertSnapshotToGcode(self, encoded_snapshot, width, height, chunk_size=78):
45 | gcode = []
46 |
47 | encoded_snapshot_length = len(encoded_snapshot)
48 | gcode.append(";")
49 | gcode.append("; thumbnail begin {}x{} {}".format(
50 | width, height, encoded_snapshot_length))
51 |
52 | chunks = ["; {}".format(encoded_snapshot[i:i+chunk_size])
53 | for i in range(0, len(encoded_snapshot), chunk_size)]
54 | gcode.extend(chunks)
55 |
56 | gcode.append("; thumbnail end")
57 | gcode.append(";")
58 | gcode.append("")
59 |
60 | return gcode
61 |
62 | def getSettingDataString(self):
63 | return """{
64 | "name": "Create JPEG Thumbnail",
65 | "key": "CreateJPEGThumbnail",
66 | "metadata": {},
67 | "version": 2,
68 | "settings":
69 | {
70 | "width":
71 | {
72 | "label": "Width",
73 | "description": "Width of the generated thumbnail",
74 | "unit": "px",
75 | "type": "int",
76 | "default_value": 230,
77 | "minimum_value": "0",
78 | "minimum_value_warning": "12",
79 | "maximum_value_warning": "800"
80 | },
81 | "height":
82 | {
83 | "label": "Height",
84 | "description": "Height of the generated thumbnail",
85 | "unit": "px",
86 | "type": "int",
87 | "default_value": 180,
88 | "minimum_value": "0",
89 | "minimum_value_warning": "12",
90 | "maximum_value_warning": "600"
91 | }
92 | }
93 | }"""
94 |
95 | def execute(self, data):
96 | width = self.getSettingValueByKey("width")
97 | height = self.getSettingValueByKey("height")
98 |
99 | snapshot = self._createSnapshot(width, height)
100 | if snapshot:
101 | encoded_snapshot = self._encodeSnapshot(snapshot)
102 | snapshot_gcode = self._convertSnapshotToGcode(
103 | encoded_snapshot, width, height)
104 |
105 | for layer in data:
106 | layer_index = data.index(layer)
107 | lines = data[layer_index].split("\n")
108 | for line in lines:
109 | if line.startswith(";Generated with Cura"):
110 | line_index = lines.index(line)
111 | insert_index = line_index + 1
112 | lines[insert_index:insert_index] = snapshot_gcode
113 | break
114 |
115 | final_lines = "\n".join(lines)
116 | data[layer_index] = final_lines
117 |
118 | return data
--------------------------------------------------------------------------------
/CreateThumbnail.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------
2 | # Cura JPEG Thumbnail creator
3 | # Professional firmware for Ender3v2
4 | # Miguel A. Risco-Castillo
5 | # version: 1.4
6 | # date: 2022-05-18
7 | #
8 | # Contains code from:
9 | # https://github.com/Ultimaker/Cura/blob/master/plugins/PostProcessingPlugin/scripts/CreateThumbnail.py
10 | #------------------------------------------------------------------------------
11 |
12 | import base64
13 |
14 | from UM.Logger import Logger
15 | from cura.Snapshot import Snapshot
16 | from cura.CuraVersion import CuraVersion
17 |
18 | from ..Script import Script
19 |
20 |
21 | class CreateJPEGThumbnail(Script):
22 | def __init__(self):
23 | super().__init__()
24 |
25 | def _createSnapshot(self, width, height):
26 | Logger.log("d", "Creating thumbnail image...")
27 | try:
28 | return Snapshot.snapshot(width, height)
29 | except Exception:
30 | Logger.logException("w", "Failed to create snapshot image")
31 |
32 | def _encodeSnapshot(self, snapshot):
33 |
34 | Major=0
35 | Minor=0
36 | try:
37 | Major = int(CuraVersion.split(".")[0])
38 | Minor = int(CuraVersion.split(".")[1])
39 | except:
40 | pass
41 |
42 | if Major < 5 :
43 | from PyQt5.QtCore import QByteArray, QIODevice, QBuffer
44 | else :
45 | from PyQt6.QtCore import QByteArray, QIODevice, QBuffer
46 |
47 | Logger.log("d", "Encoding thumbnail image...")
48 | try:
49 | thumbnail_buffer = QBuffer()
50 | if Major < 5 :
51 | thumbnail_buffer.open(QBuffer.ReadWrite)
52 | else:
53 | thumbnail_buffer.open(QBuffer.OpenModeFlag.ReadWrite)
54 | thumbnail_image = snapshot
55 | thumbnail_image.save(thumbnail_buffer, "JPG")
56 | base64_bytes = base64.b64encode(thumbnail_buffer.data())
57 | base64_message = base64_bytes.decode('ascii')
58 | thumbnail_buffer.close()
59 | return base64_message
60 | except Exception:
61 | Logger.logException("w", "Failed to encode snapshot image")
62 |
63 | def _convertSnapshotToGcode(self, encoded_snapshot, width, height, chunk_size=78):
64 | gcode = []
65 |
66 | encoded_snapshot_length = len(encoded_snapshot)
67 | gcode.append(";")
68 | gcode.append("; thumbnail begin {}x{} {}".format(
69 | width, height, encoded_snapshot_length))
70 |
71 | chunks = ["; {}".format(encoded_snapshot[i:i+chunk_size])
72 | for i in range(0, len(encoded_snapshot), chunk_size)]
73 | gcode.extend(chunks)
74 |
75 | gcode.append("; thumbnail end")
76 | gcode.append(";")
77 | gcode.append("")
78 |
79 | return gcode
80 |
81 | def getSettingDataString(self):
82 | return """{
83 | "name": "Create JPEG Thumbnail",
84 | "key": "CreateJPEGThumbnail",
85 | "metadata": {},
86 | "version": 2,
87 | "settings":
88 | {
89 | "width":
90 | {
91 | "label": "Width",
92 | "description": "Width of the generated thumbnail",
93 | "unit": "px",
94 | "type": "int",
95 | "default_value": 230,
96 | "minimum_value": "0",
97 | "minimum_value_warning": "12",
98 | "maximum_value_warning": "800"
99 | },
100 | "height":
101 | {
102 | "label": "Height",
103 | "description": "Height of the generated thumbnail",
104 | "unit": "px",
105 | "type": "int",
106 | "default_value": 180,
107 | "minimum_value": "0",
108 | "minimum_value_warning": "12",
109 | "maximum_value_warning": "600"
110 | }
111 | }
112 | }"""
113 |
114 | def execute(self, data):
115 | width = self.getSettingValueByKey("width")
116 | height = self.getSettingValueByKey("height")
117 |
118 | snapshot = self._createSnapshot(width, height)
119 | if snapshot:
120 | encoded_snapshot = self._encodeSnapshot(snapshot)
121 | snapshot_gcode = self._convertSnapshotToGcode(
122 | encoded_snapshot, width, height)
123 |
124 | for layer in data:
125 | layer_index = data.index(layer)
126 | lines = data[layer_index].split("\n")
127 | for line in lines:
128 | if line.startswith(";Generated with Cura"):
129 | line_index = lines.index(line)
130 | insert_index = line_index + 1
131 | lines[insert_index:insert_index] = snapshot_gcode
132 | break
133 |
134 | final_lines = "\n".join(lines)
135 | data[layer_index] = final_lines
136 |
137 | return data
--------------------------------------------------------------------------------
/Cura_GCode.CSV:
--------------------------------------------------------------------------------
1 | Command;Title;Parameter;;;;;;;
2 | M140;Set Bed Temperature;[S] - Target temperature;;;;;;;
3 | M190;Wait for Bed Temperature;[R] - Target temperature;[S] - Target temperature;;;;;;
4 | M109;Wait for Hotend Temperature;[B] - With AUTOTEMP, the max auto-temperature;[F] - Autotemp flag. Omit to disable autotemp;[R] - Target temperature (wait for cooling or heating);[S] - Target temperature;[T] - Hotend index;;;
5 | M107;Fan Off ;[P] - Fan index;;;;;;;
6 | M106;Set Fan Speed;[P] - Fan index;[S] - Speed;[T] - Secondary speed;;;;;
7 | M84;Disable steppers;;;;;;;;
8 | M92;Set Axis Steps-per-unit;[E] - E steps per unit;[T] - Target extruder;[X] - X steps per unit;[Y] - Y steps per unit;[Z] - Z steps per unit;;;
9 | G29;Bed Leveling;;;;;;;;
10 | M18;Disable steppers;;;;;;;;
11 | M204;Set Starting Acceleration;[P] - Printing acceleration;[R] - Retract acceleration;[T] - Travel acceleration;;;;;
12 | M205;Set Advanced Settings;[B<µs>] - Minimum segment time (µs);[E] - E max jerk (units/s);"[J] - Junction deviation (requires JUNCTION_DEVIATION
)";[S] - Minimum feedrate for print moves (units/s);[T] - Minimum feedrate for travel moves (units/s);[X] - X max jerk (units/s);[Y] - Y max jerk (units/s);[Z] - Z max jerk (units/s)
13 | M211;Software Endstops;[S] - Software endstops state;;;;;;;
14 | M203;Set Max Feedrate;[E] - E axis max feedrate;"[T] - Target extruder (Requires DISTINCT_E_FACTORS
)";[X] - X axis max feedrate;[Y] - Y axis max feedrate;[Z] - Z axis max feedrate;;;
15 | G90;Absolute Positioning;;;;;;;;
16 | M82;Absolute Extrusion Mode;;;;;;;;
17 | M163;Set Mix Factor;[P] - Mix factor;[S] - Component index;;;;;;
18 | M164;Save Mix;S - Tool index (active virtual tool if omitted);;;;;;;
19 | M105;Report Temperatures;[T] - Hotend index;;;;;;;
20 | G92;Set Position;[E] - New extruder position;[X] - New X axis position;[Y] - New Y axis position;[Z] - New Z axis position;;;;
21 | M104;Set Hotend Temperature;[B] - AUTOTEMP
: The max auto-temperature;[F] - AUTOTEMP
: Autotemp flag;[S] - Target temperature;[T] - Hotend index
--------------------------------------------------------------------------------
/CustomTimeLapse.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script Custom Time Lapse
4 | # Author : ????
5 | # Modification : Arpadvezer1 : https://github.com/Arpadvezer1
6 | # Date : January 31, 2023
7 | #
8 | # Description : CustomTimelapse
9 | #
10 | #
11 | #------------------------------------------------------------------------------------------------------------------------------------
12 |
13 | from ..Script import Script
14 | from UM.Logger import Logger
15 |
16 | __version__ = '1.0'
17 |
18 | class CustomTimeLapse(Script):
19 | def __init__(self):
20 | super().__init__()
21 |
22 | def getSettingDataString(self):
23 | return """{
24 | "name": "Custom timelapse",
25 | "key": "CustomTimeLapse",
26 | "metadata": {},
27 | "version": 2,
28 | "settings":
29 | {
30 | "activate_plugin":
31 | {
32 | "label": "Enable plugin",
33 | "description": "Select if you want the plugin to be active (allows you to desactivate without losing your configuration)",
34 | "type": "bool",
35 | "default_value": true
36 | },
37 | "first_gcode":
38 | {
39 | "label": "GCODE for the first position(display position).",
40 | "description": "GCODE to add before or after layer change.",
41 | "type": "str",
42 | "default_value": "G0 Y235"
43 | },
44 | "second_gcode":
45 | {
46 | "label": "GCODE for the second position(trigger position).",
47 | "description": "GCODE to add before or after layer change.",
48 | "type": "str",
49 | "default_value": "G0 X235"
50 | },
51 | "enable_custom_return_speed":
52 | {
53 | "label": "Specify a return speed",
54 | "description": "Set the value below",
55 | "type": "bool",
56 | "default_value": false
57 | },
58 | "return_speed":
59 | {
60 | "label": "return speed in mm/minutes",
61 | "description": "return speed in mm/minute as for the F gcode parameter.",
62 | "type": "int",
63 | "unit": "mm/m",
64 | "enabled": "enable_custom_return_speed"
65 | },
66 | "pause_length":
67 | {
68 | "label": "Pause length",
69 | "description": "How long to wait (in ms) after camera was triggered.",
70 | "type": "int",
71 | "default_value": 700,
72 | "minimum_value": 0,
73 | "unit": "ms"
74 | },
75 | "enable_retraction":
76 | {
77 | "label": "Enable retraction",
78 | "description": "Retract the filament before moving the head",
79 | "type": "bool",
80 | "default_value": true
81 | },
82 | "retraction_distance":
83 | {
84 | "label": "Retraction distance",
85 | "description": "How much to retract the filament.",
86 | "unit": "mm",
87 | "type": "float",
88 | "default_value": 5,
89 | "enabled": "enable_retraction"
90 | },
91 | "display_photo_number":
92 | {
93 | "label": "Display current photo number",
94 | "description": "Display the current photo number on the panel during the shots",
95 | "type": "bool",
96 | "default_value": false
97 | },
98 | "send_photo_command":
99 | {
100 | "label": "Send camera command",
101 | "description": "Send a customisable G-code command for compatible printers",
102 | "type": "bool",
103 | "default_value": false
104 | },
105 | "trigger_command":
106 | {
107 | "label": "Trigger camera command",
108 | "description": "Gcode command used to trigger camera.",
109 | "type": "str",
110 | "default_value": "M240",
111 | "enabled": "send_photo_command"
112 | }
113 | }
114 | }"""
115 | # Note : This function and some other bits of code comes from PauseAtHeight.py
116 | ## Get the X and Y values for a layer (will be used to get X and Y of the
117 | # layer after the pause).
118 | def getNextXY(self, layer):
119 | lines = layer.split("\n")
120 | for line in lines:
121 | if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None:
122 | x = self.getValue(line, "X")
123 | y = self.getValue(line, "Y")
124 | return x, y
125 | return 0, 0
126 |
127 | def execute(self, data):
128 | activate_plugin = self.getSettingValueByKey("activate_plugin")
129 | first_gcode = self.getSettingValueByKey("first_gcode")
130 | second_gcode = self.getSettingValueByKey("second_gcode")
131 | pause_length = self.getSettingValueByKey("pause_length")
132 | enable_custom_return_speed = self.getSettingValueByKey("enable_custom_return_speed")
133 | return_speed = self.getSettingValueByKey("return_speed")
134 | enable_retraction = self.getSettingValueByKey("enable_retraction")
135 | retraction_distance = self.getSettingValueByKey("retraction_distance")
136 | display_photo_number = self.getSettingValueByKey("display_photo_number")
137 | send_photo_command = self.getSettingValueByKey("send_photo_command")
138 | trigger_command = self.getSettingValueByKey("trigger_command")
139 |
140 | for layerIndex, layer in enumerate(data):
141 | # Check that a layer is being printed
142 | lines = layer.split("\n")
143 | for line in lines:
144 | if ";LAYER:" in line:
145 | index = data.index(layer)
146 |
147 | next_layer = data[layerIndex + 1]
148 | x, y = self.getNextXY(next_layer)
149 |
150 | gcode_to_append = ""
151 |
152 | if activate_plugin:
153 | gcode_to_append += ";CustomTimelapse Begin\n"
154 |
155 | if display_photo_number:
156 | gcode_to_append += "M117 Taking photo " + str(layerIndex) + "...\n"
157 |
158 | gcode_to_append += "; STEP 1 : retraction\n"
159 | gcode_to_append += self.putValue(M = 83) + " ; switch to relative E values for any needed retraction\n"
160 | if enable_retraction:
161 | gcode_to_append += self.putValue(G = 1, F = 1800, E = -retraction_distance) + ";Retraction\n"
162 | gcode_to_append += self.putValue(M = 82) + ";Switch back to absolute E values\n"
163 |
164 | gcode_to_append += "; STEP 2 : Move the head up a bit\n"
165 | gcode_to_append += self.putValue(G = 91) + ";Switch to relative positioning\n"
166 | gcode_to_append += self.putValue(G = 0, Z = 1) + ";Move Z axis up a bit\n"
167 | gcode_to_append += self.putValue(G = 90) + ";Switch back to absolute positioning\n"
168 |
169 | gcode_to_append += "; STEP 3 : Move the head to \"display\" position and wait\n"
170 | gcode_to_append += first_gcode + ";GCODE for the first position(display position)\n"
171 | gcode_to_append += second_gcode + ";GCODE for the second position(trigger position)\n"
172 | gcode_to_append += self.putValue(M = 400) + ";Wait for moves to finish\n"
173 | gcode_to_append += self.putValue(G = 4, P = pause_length) + ";Wait for camera\n"
174 |
175 | gcode_to_append += "; STEP 4 : send photo trigger command if set\n"
176 | if send_photo_command:
177 | gcode_to_append += trigger_command + " ;Snap Photo\n"
178 |
179 | # TODO skip steps 5 and 6 for the last layer
180 | gcode_to_append += "; STEP 5 : Move the head back in its original place\n"
181 | if enable_custom_return_speed:
182 | gcode_to_append += self.putValue(G = 0, X = x, Y = y, F = return_speed) + "\n"
183 | else:
184 | gcode_to_append += self.putValue(G = 0, X = x, Y = y) + "\n"
185 |
186 | gcode_to_append += "; STEP 6 : Move the head height back down\n"
187 | gcode_to_append += self.putValue(G = 91) + ";Switch to relative positioning\n"
188 | gcode_to_append += self.putValue(G = 0, Z = -1) + ";Restore Z axis position\n"
189 | gcode_to_append += self.putValue(G = 90) + ";Switch back to absolute positioning\n"
190 |
191 | gcode_to_append += ";CustomTimelapse End\n"
192 |
193 |
194 | layer += gcode_to_append
195 |
196 | data[index] = layer
197 | break
198 | return data
--------------------------------------------------------------------------------
/DiagonalZHop.py:
--------------------------------------------------------------------------------
1 | # DiagonalZHop
2 | """
3 | DiagonalZHop for 3D prints.
4 |
5 | Diagonal Z Hop
6 |
7 | Author: 5axes
8 | Version: 0.1
9 |
10 | Note : https://github.com/Ultimaker/Cura/issues/15583
11 | """
12 |
13 | import re #To perform the search
14 |
15 | from ..Script import Script
16 |
17 | from UM.Application import Application
18 | from UM.Logger import Logger
19 | from UM.Message import Message
20 | from UM.i18n import i18nCatalog
21 |
22 | catalog = i18nCatalog("cura")
23 |
24 | __version__ = '0.1'
25 |
26 | class DiagonalZHop(Script):
27 | def getSettingDataString(self):
28 | return """{
29 | "name": "Diagonal Z Hop",
30 | "key": "DiagonalZHop",
31 | "metadata": {},
32 | "version": 2,
33 | "settings":
34 | {
35 | "extruder_nb":
36 | {
37 | "label": "Extruder Id",
38 | "description": "Define extruder Id in case of multi extruders",
39 | "unit": "",
40 | "type": "int",
41 | "default_value": 1
42 | }
43 | }
44 | }"""
45 |
46 | ## -----------------------------------------------------------------------------
47 | #
48 | # Main Prog
49 | #
50 | ## -----------------------------------------------------------------------------
51 | def execute(self, data):
52 |
53 | extruder_id = self.getSettingValueByKey("extruder_nb")
54 | extruder_id = extruder_id -1
55 |
56 | # machine_extruder_count
57 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
58 | extruder_count = extruder_count-1
59 | if extruder_id>extruder_count :
60 | extruder_id=extruder_count
61 |
62 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
63 |
64 | # Get the Cura retraction_hop and speed_z_hop as Zhop parameter
65 | retraction_hop = float(extrud[extruder_id].getProperty("retraction_hop", "value"))
66 | speed_z_hop = int(extrud[extruder_id].getProperty("speed_z_hop", "value"))
67 | speed_z_hop = speed_z_hop * 60
68 |
69 | # Check if Z hop is desactivated
70 | retraction_hop_enabled= extrud[extruder_id].getProperty("retraction_hop_enabled", "value")
71 | if retraction_hop_enabled == False:
72 | #
73 | Logger.log('d', 'Mode Z Hop must be activated !')
74 | Message(catalog.i18nc("@message", "Mode Z Hop must be activated !"), title = catalog.i18nc("@info:title", "Post Processing")).show()
75 | return data
76 |
77 | In_Zhop = False
78 | for layer_index, layer in enumerate(data):
79 | lines = layer.split("\n")
80 |
81 | for line_index, currentLine in enumerate(lines):
82 |
83 | # Zhop G1
84 | if currentLine.startswith("G1") and "Z" in currentLine and not "X" in currentLine and not "Y" in currentLine and not In_Zhop :
85 | In_Zhop = True
86 | lines[line_index] = ";" + currentLine + " ; Modified by DiagonalZhop"
87 | else :
88 | if currentLine.startswith("G1") and "Z" in currentLine and not "X" in currentLine and not "Y" in currentLine :
89 | In_Zhop = False
90 |
91 | #
92 | # end of analyse
93 | #
94 |
95 | final_lines = "\n".join(lines)
96 | data[layer_index] = final_lines
97 | return data
98 |
--------------------------------------------------------------------------------
/DisplayPrintInfosOnLCD.py:
--------------------------------------------------------------------------------
1 | #
2 | # Cura PostProcessingPlugin
3 | # Author: Amanda de Castilho for the Layer part
4 | # Author: Mathias Lyngklip Kjeldgaard for the remaining time part
5 | # Author: 5axes
6 | # Date: Janvier 02 2020
7 | # Modified: Janvier 05 2020 Option Display LayerId
8 | #
9 | # Description: This script shows custom messages about your print on the Printer Panel...
10 | # Please look at the option
11 | # - LayerId: Uses the Layer ID encoded in the original file
12 | #
13 | from ..Script import Script
14 | from UM.Application import Application
15 |
16 | class DisplayPrintInfosOnLCD(Script):
17 | def __init__(self):
18 | super().__init__()
19 |
20 | def getSettingDataString(self):
21 | return """{
22 | "name": "Display Print Infos On LCD",
23 | "key": "DisplayPrintInfosOnLCD",
24 | "metadata": {},
25 | "version": 2,
26 | "settings":
27 | {
28 | "LayerId":
29 | {
30 | "label": "Use Layer Id G-Code",
31 | "description": "Uses the Layer Id encoded in the G-Code file. Must be used in Print Sequence : One at a time",
32 | "type": "bool",
33 | "default_value": false
34 | }
35 | }
36 | }"""
37 |
38 | def execute(self, data):
39 | max_layer = 0
40 | total_time = 0
41 | part = 0
42 | total_time_string = ""
43 | current_time_string = ""
44 | lcd_text = "M117 ("
45 | Id = 1
46 | for layer in data:
47 | display_text = lcd_text + str(Id) + "/"
48 | layer_index = data.index(layer)
49 | lines = layer.split("\n")
50 | for line in lines:
51 | if line.startswith(";LAYER_COUNT:"):
52 | max_layer = line.split(":")[1] # Recuperation Nb Layer Maxi
53 | elif line.startswith(";LAYER:"):
54 | line_index = lines.index(line)
55 | if part > 1:
56 | display_text = display_text + max_layer + ") " + current_time_string + " P" + str(part)
57 | else:
58 | display_text = display_text + max_layer + ") " + current_time_string
59 |
60 | lines.insert(line_index + 1, display_text) # Insert du code M117 apres les layers
61 | if self.getSettingValueByKey("LayerId"):
62 | Id = int(line.split(":")[1]) # Utilise le Layer dans G-Code ;LAYER:1
63 | if Id == 0:
64 | part += 1 # Incrémente le numero de pièce
65 | Id += 1
66 | else:
67 | Id += 1 # Incrémente le numero de Layer (sans utiliser celui du Gcode)
68 | if line.startswith(";TIME:"):
69 | line_index = lines.index(line)
70 | total_time = int(line.split(":")[1])
71 | m, s = divmod(total_time, 60) # Decomposition en
72 | h, m = divmod(m, 60) # heures, minutes et secondes
73 | total_time_string = "{:d}h{:d}m{:d}s".format(int(h), int(m), int(s))
74 | current_time_string = total_time_string
75 | display_text = lcd_text + total_time_string + ")"
76 | lines.insert(line_index + 1, display_text)
77 | elif line.startswith(";TIME_ELAPSED:"):
78 | line_index = lines.index(line)
79 | current_time = float(line.split(":")[1])
80 | time_left = total_time - current_time # Calcul du temps restant
81 | m1, s1 = divmod(time_left, 60) # Decomposition en
82 | h1, m1 = divmod(m1, 60) # heures, minutes et secondes
83 | current_time_string = "{:d}h{:d}m{:d}s".format(int(h1), int(m1), int(s1))
84 |
85 | final_lines = "\n".join(lines)
86 | data[layer_index] = final_lines
87 |
88 | return data
89 |
--------------------------------------------------------------------------------
/FanIroning.py:
--------------------------------------------------------------------------------
1 | # FanIroning
2 | """
3 | FanIroning for 3D prints.
4 |
5 | Set Fan value for ironing
6 |
7 | Author : 5axes
8 | Version : 1.0
9 | Date : 3/01/2023
10 |
11 | """
12 |
13 | from ..Script import Script
14 | from UM.Logger import Logger
15 | from UM.Application import Application
16 | import re #To perform the search
17 | from cura.Settings.ExtruderManager import ExtruderManager
18 | from enum import Enum
19 | from UM.Message import Message
20 | from UM.i18n import i18nCatalog
21 | catalog = i18nCatalog("cura")
22 |
23 | __version__ = '1.0'
24 |
25 | class Section(Enum):
26 | """Enum for section type."""
27 |
28 | NOTHING = 0
29 | SKIRT = 1
30 | INNER_WALL = 2
31 | OUTER_WALL = 3
32 | INFILL = 4
33 | SKIN = 5
34 | SKIN2 = 6
35 | PRIME_TOWER = 7
36 | BRIDGE = 8
37 |
38 |
39 |
40 | def is_begin_layer_line(line: str) -> bool:
41 | """Check if current line is the start of a layer section.
42 |
43 | Args:
44 | line (str): Gcode line
45 |
46 | Returns:
47 | bool: True if the line is the start of a layer section
48 | """
49 | return line.startswith(";LAYER:")
50 |
51 |
52 | def is_extrusion_line(line: str) -> bool:
53 | """Check if current line is a standard printing segment.
54 |
55 | Args:
56 | line (str): Gcode line
57 |
58 | Returns:
59 | bool: True if the line is a standard printing segment
60 | """
61 | return "G1" in line and "X" in line and "Y" in line and "E" in line
62 |
63 | def is_not_extrusion_line(line: str) -> bool:
64 | """Check if current line is a rapid movement segment.
65 |
66 | Args:
67 | line (str): Gcode line
68 |
69 | Returns:
70 | bool: True if the line is a standard printing segment
71 | """
72 | return "G0" in line and "X" in line and "Y" in line and not "E" in line
73 |
74 | def is_begin_skin_segment_line(line: str) -> bool:
75 | """Check if current line is the start of an skin.
76 |
77 | Args:
78 | line (str): Gcode line
79 |
80 | Returns:
81 | bool: True if the line is the start of an skin section
82 | """
83 | return line.startswith(";TYPE:SKIN")
84 |
85 |
86 | class FanIroning(Script):
87 | def getSettingDataString(self):
88 | return """{
89 | "name": "Fan Ironing",
90 | "key": "FanIroning",
91 | "metadata": {},
92 | "version": 2,
93 | "settings":
94 | {
95 | "extruder_nb":
96 | {
97 | "label": "Extruder Id",
98 | "description": "Define extruder Id in case of multi extruders",
99 | "unit": "",
100 | "type": "int",
101 | "default_value": 1
102 | },
103 | "fan_value":
104 | {
105 | "label": "Fan Value",
106 | "description": "Fan value during ironing operation.",
107 | "type": "int",
108 | "default_value": 0,
109 | "minimum_value": 0,
110 | "maximum_value": 100
111 | }
112 | }
113 | }"""
114 |
115 |
116 | ## -----------------------------------------------------------------------------
117 | #
118 | # Main Prog
119 | #
120 | ## -----------------------------------------------------------------------------
121 |
122 | def execute(self, data):
123 |
124 | extruder_id = self.getSettingValueByKey("extruder_nb")
125 | extruder_id = extruder_id -1
126 |
127 | ironing_fan_value = int((float(self.getSettingValueByKey("fan_value"))/100)*255)
128 |
129 | # machine_extruder_count
130 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
131 | extruder_count = extruder_count-1
132 | if extruder_id>extruder_count :
133 | extruder_id=extruder_count
134 |
135 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
136 |
137 | ironingenabled = extrud[extruder_id].getProperty("ironing_enabled", "value")
138 | if ironingenabled == False:
139 | #
140 | Logger.log('d', 'Gcode must be generate with ironing mode')
141 | Message('Gcode must be generate with ironing mode', title = catalog.i18nc("@info:title", "Post Processing")).show()
142 | return None
143 |
144 | """Parse Gcode and modify infill portions with an extrusion width gradient."""
145 | currentSection = Section.NOTHING
146 | set_ironing_fan_value = False
147 | current_Fan = 0
148 | Fan_On = False
149 |
150 | for layer_index, layer in enumerate(data):
151 | lines = layer.split("\n")
152 | for line_index, currentLine in enumerate(lines):
153 | # M107 - Set Fan Speed
154 | if currentLine.startswith("M106") and not set_ironing_fan_value :
155 | searchM106 = re.search(r"S(\d*\.?\d*)", currentLine)
156 | if searchM106:
157 | current_Fan=int(searchM106.group(1))
158 | Logger.log('d', 'current_Fan :' + str(current_Fan))
159 | Fan_On = True
160 | # M107 - Fan Off
161 | if currentLine.startswith("M107") :
162 | Fan_On = False
163 |
164 | if is_begin_skin_segment_line(currentLine) and not (currentSection == Section.SKIN):
165 | currentSection = Section.SKIN
166 | continue
167 |
168 | # SKIN After SKIN = Ironing operation
169 | if currentSection == Section.SKIN:
170 | if is_begin_skin_segment_line(currentLine):
171 | currentSection = Section.SKIN2
172 | set_ironing_fan_value = True
173 | outPutLine = "\nM106 S{:d}".format(ironing_fan_value)
174 | # Logger.log('d', 'outPutLine :' + str(outPutLine))
175 | outPutLine = currentLine + outPutLine
176 | lines[line_index] = outPutLine
177 | elif currentLine.startswith(";TYPE:"):
178 | currentSection = Section.NOTHING
179 | if set_ironing_fan_value :
180 | set_ironing_fan_value = False
181 | if Fan_On == True :
182 | outPutLine = "\nM106 S{:d}".format(current_Fan)
183 | else:
184 | outPutLine = "\nM107"
185 | # Logger.log('d', 'Reset A outPutLine :' + str(outPutLine))
186 | outPutLine = currentLine + outPutLine
187 | lines[line_index] = outPutLine
188 |
189 | #
190 | # comment like ;MESH:NONMESH
191 | #
192 | if currentLine.startswith(";MESH:"):
193 | currentSection = Section.NOTHING
194 | if set_ironing_fan_value :
195 | set_ironing_fan_value = False
196 | # Logger.log('d', 'Reset B outPutLine :' + str(outPutLine))
197 | if Fan_On == True :
198 | outPutLine = "\nM106 S{:d}".format(current_Fan)
199 | else:
200 | outPutLine = "\nM107"
201 | outPutLine = currentLine + outPutLine
202 | lines[line_index] = outPutLine
203 |
204 | #
205 | # end of analyse
206 | #
207 |
208 | final_lines = "\n".join(lines)
209 | data[layer_index] = final_lines
210 | return data
211 |
--------------------------------------------------------------------------------
/FastFirstInfill.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: November 06, 2021
6 | #
7 | # Description: postprocessing script to modifiy the first layer infill
8 | #
9 | #------------------------------------------------------------------------------------------------------------------------------------
10 | #
11 | # Version 1.0 06/11/2021
12 | # Version 1.1 07/11/2021 Modification for Print Sequence
13 | #
14 | #------------------------------------------------------------------------------------------------------------------------------------
15 |
16 | from ..Script import Script
17 | from UM.Logger import Logger
18 | from UM.Application import Application
19 | import re # To perform the search
20 | from enum import Enum
21 |
22 | __version__ = '1.1'
23 |
24 | class Section(Enum):
25 | """Enum for section type."""
26 |
27 | NOTHING = 0
28 | SKIRT = 1
29 | INNER_WALL = 2
30 | OUTER_WALL = 3
31 | INFILL = 4
32 | SKIN = 5
33 | SKIN2 = 6
34 |
35 | def is_begin_layer_line(line: str) -> bool:
36 | """Check if current line is the start of a layer section.
37 |
38 | Args:
39 | line (str): Gcode line
40 |
41 | Returns:
42 | bool: True if the line is the start of a layer section
43 | """
44 | return line.startswith(";LAYER:")
45 |
46 | def is_begin_type_line(line: str) -> bool:
47 | """Check if current line is the start of a new type section.
48 |
49 | Args:
50 | line (str): Gcode line
51 |
52 | Returns:
53 | bool: True if the line is the start of a new type section
54 | """
55 | return line.startswith(";TYPE:")
56 |
57 | def is_retract_line(line: str) -> bool:
58 | """Check if current line is a retract segment.
59 |
60 | Args:
61 | line (str): Gcode line
62 |
63 | Returns:
64 | bool: True if the line is a retract segment
65 | """
66 | return "G1" in line and "F" in line and "E" in line and not "X" in line and not "Y" in line and not "Z" in line
67 |
68 | def is_extrusion_line(line: str) -> bool:
69 | """Check if current line is a standard printing segment.
70 |
71 | Args:
72 | line (str): Gcode line
73 |
74 | Returns:
75 | bool: True if the line is a standard printing segment
76 | """
77 | return "G1" in line and "X" in line and "Y" in line and "E" in line
78 |
79 | def is_not_extrusion_line(line: str) -> bool:
80 | """Check if current line is a rapid movement segment.
81 |
82 | Args:
83 | line (str): Gcode line
84 |
85 | Returns:
86 | bool: True if the line is a standard printing segment
87 | """
88 | return "G0" in line and "X" in line and "Y" in line and not "E" in line
89 |
90 | def is_begin_skin_segment_line(line: str) -> bool:
91 | """Check if current line is the start of an skin.
92 |
93 | Args:
94 | line (str): Gcode line
95 |
96 | Returns:
97 | bool: True if the line is the start of an skin section
98 | """
99 | return line.startswith(";TYPE:SKIN")
100 |
101 | class FastFirstInfill(Script):
102 | def __init__(self):
103 | super().__init__()
104 |
105 | def getSettingDataString(self):
106 | return """{
107 | "name": "FastFirstInfill",
108 | "key": "FastFirstInfill",
109 | "metadata": {},
110 | "version": 2,
111 | "settings":
112 | {
113 | "infillspeed":
114 | {
115 | "label": "First layer infill speed",
116 | "description": "First layer infill speed value.",
117 | "type": "float",
118 | "unit": "mm/s",
119 | "default_value": 30,
120 | "minimum_value": 1,
121 | "maximum_value": 100,
122 | "maximum_value_warning": 50
123 | }
124 | }
125 | }"""
126 |
127 | def execute(self, data):
128 |
129 | InfillSpeed = float(self.getSettingValueByKey("infillspeed")) * 60
130 | InfillSpeedInstruction = "F" + str(InfillSpeed)
131 | Logger.log('d', 'InfillSpeedInstruction : {}'.format(InfillSpeedInstruction))
132 |
133 | idl=0
134 |
135 | for layer in data:
136 | layer_index = data.index(layer)
137 |
138 | lines = layer.split("\n")
139 | for line in lines:
140 |
141 | if is_begin_layer_line(line):
142 | # Logger.log('d', 'layer_index : {:d}'.format(layer_index))
143 | # Logger.log('d', 'layer_lines : {}'.format(line))
144 | if line.startswith(";LAYER:0"):
145 | idl=1
146 | else :
147 | idl=0
148 |
149 | if is_begin_type_line(line) and idl > 0:
150 | if is_begin_skin_segment_line(line):
151 | idl=2
152 | Logger.log('d', 'layer_lines : {}'.format(line))
153 | else :
154 | idl=1
155 |
156 | if idl >= 2 and is_extrusion_line(line):
157 | searchF = re.search(r"F(\d*\.?\d*)", line)
158 | if searchF:
159 | line_index = lines.index(line)
160 | save_F=float(searchF.group(1))
161 | instructionF="F"+str(searchF.group(1))
162 | # Logger.log('d', 'save_F : {:f}'.format(save_F))
163 | # Logger.log('d', 'line : {}'.format(line))
164 | # Logger.log('d', 'line replace : {}'.format(line.replace(instructionF,InfillSpeedInstruction)))
165 | lines[line_index]=line.replace(instructionF,InfillSpeedInstruction)
166 |
167 | result = "\n".join(lines)
168 | data[layer_index] = result
169 |
170 | return data
171 |
--------------------------------------------------------------------------------
/FlowTower.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: August 29, 2021
6 | #
7 | # Description: postprocessing script to easily define a Flow Tower
8 | #
9 | #------------------------------------------------------------------------------------------------------------------------------------
10 | #
11 | # Version 1.0 29/08/2021
12 | #
13 | #------------------------------------------------------------------------------------------------------------------------------------
14 |
15 | from ..Script import Script
16 | from UM.Application import Application
17 | from UM.Logger import Logger
18 | import re #To perform the search
19 |
20 | __version__ = '1.0'
21 |
22 | class FlowTower(Script):
23 | def __init__(self):
24 | super().__init__()
25 |
26 | def getSettingDataString(self):
27 | return """{
28 | "name": "FlowTower",
29 | "key": "FlowTower",
30 | "metadata": {},
31 | "version": 2,
32 | "settings":
33 | {
34 | "startValue":
35 | {
36 | "label": "Starting value",
37 | "description": "the starting value of the Tower.",
38 | "type": "float",
39 | "default_value": 110.0
40 | },
41 | "valueChange":
42 | {
43 | "label": "Value Increment",
44 | "description": "the value change of each block, can be positive or negative. I you want 110 and then 108, you need to set this to -2.",
45 | "type": "float",
46 | "default_value": -2.0
47 | },
48 | "changelayer":
49 | {
50 | "label": "Change Layer",
51 | "description": "how many layers needs to be printed before the value should be changed.",
52 | "type": "float",
53 | "default_value": 40,
54 | "minimum_value": 1,
55 | "maximum_value": 1000,
56 | "maximum_value_warning": 100
57 | },
58 | "changelayeroffset":
59 | {
60 | "label": "Change Layer Offset",
61 | "description": "if the Tower has a base, put the layer high off it here",
62 | "type": "float",
63 | "default_value": 0,
64 | "minimum_value": 0,
65 | "maximum_value": 1000,
66 | "maximum_value_warning": 100
67 | },
68 | "lcdfeedback":
69 | {
70 | "label": "Display details on LCD",
71 | "description": "This setting will insert M117 gcode instructions, to display current modification in the G-Code is being used.",
72 | "type": "bool",
73 | "default_value": true
74 | }
75 | }
76 | }"""
77 |
78 | def execute(self, data):
79 |
80 | UseLcd = self.getSettingValueByKey("lcdfeedback")
81 | StartValue = float(self.getSettingValueByKey("startValue"))
82 | ValueChange = float(self.getSettingValueByKey("valueChange"))
83 | ChangeLayer = self.getSettingValueByKey("changelayer")
84 | ChangeLayerOffset = self.getSettingValueByKey("changelayeroffset")
85 | ChangeLayerOffset += 2 # Modification to take into account the numbering offset in Gcode
86 | # layer_index = 0 for initial Block 1= Start Gcode normaly first layer = 0
87 |
88 | CurrentValue = StartValue
89 | Command=""
90 |
91 | idl=0
92 |
93 | for layer in data:
94 | layer_index = data.index(layer)
95 |
96 | lines = layer.split("\n")
97 | for line in lines:
98 |
99 | if line.startswith(";LAYER:"):
100 | line_index = lines.index(line)
101 | # Logger.log('d', 'Instruction : {}'.format(Instruction))
102 |
103 | if (layer_index==ChangeLayerOffset):
104 | Command = "M221 S{:d}".format(int(CurrentValue))
105 | lcd_gcode = "M117 Flow S{:d}".format(int(CurrentValue))
106 |
107 | lines.insert(line_index + 1, ";TYPE:CUSTOM LAYER")
108 | lines.insert(line_index + 2, Command)
109 | if UseLcd == True :
110 | lines.insert(line_index + 3, lcd_gcode)
111 |
112 | if ((layer_index-ChangeLayerOffset) % ChangeLayer == 0) and ((layer_index-ChangeLayerOffset)>0):
113 | CurrentValue += ValueChange
114 | Command = "M221 S{:d}".format(int(CurrentValue))
115 | lcd_gcode = "M117 Flow S{:d}".format(int(CurrentValue))
116 |
117 | lines.insert(line_index + 1, ";TYPE:CUSTOM VALUE")
118 | lines.insert(line_index + 2, Command)
119 | if UseLcd == True :
120 | lines.insert(line_index + 3, lcd_gcode)
121 |
122 | result = "\n".join(lines)
123 | data[layer_index] = result
124 |
125 | return data
126 |
--------------------------------------------------------------------------------
/GregVInsertAtLayerChange.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Ultimaker B.V.
2 | # Cura is released under the terms of the LGPLv3 or higher.
3 | # Created by Wayne Porter.
4 | # altered January of 2023 by GregValiant
5 |
6 | from ..Script import Script
7 |
8 | class GregVInsertAtLayerChange(Script):
9 | def __init__(self):
10 | super().__init__()
11 |
12 | def getSettingDataString(self):
13 | return """{
14 | "name": "Greg's Insert at layer change",
15 | "key": "GregVInsertAtLayerChange",
16 | "metadata": {},
17 | "version": 2,
18 | "settings":
19 | {
20 | "insert_location":
21 | {
22 | "label": "When to insert",
23 | "description": "Whether to insert code at the beginning or end of a layer.",
24 | "type": "enum",
25 | "options": {"before": "Beginning", "after": "End"},
26 | "default_value": "before"
27 | },
28 | "insert_frequency":
29 | {
30 | "label": "How often to insert",
31 | "description": "Every so many layers starting with the Start Layer.",
32 | "type": "enum",
33 | "options": {"once_only": "One insertion only", "every_layer": "Every Layer", "every_second": "Every 2nd", "every_third": "Every 3rd", "every_fifth": "Every 5th", "every_tenth": "Every 10th", "every_XXV": "Every 25th", "every_L": "Every 50th", "every_C": "Every 100th"},
34 | "default_value": "every_layer"
35 | },
36 | "start_layer":
37 | {
38 | "label": "Starting Layer",
39 | "description": "Layer to start the insertion at.",
40 | "type": "int",
41 | "default_value": 0,
42 | "minimum_value": 0,
43 | "enabled": "insert_frequency != 'once_only'"
44 | },
45 | "end_layer_enabled":
46 | {
47 | "label": "Enable End Layer",
48 | "description": "Check to use an ending layer for the insertion.",
49 | "type": "bool",
50 | "default_value": false,
51 | "enabled": "insert_frequency != 'once_only'"
52 | },
53 | "end_layer":
54 | {
55 | "label": "Ending Layer",
56 | "description": "Layer to end the insertion at. Enter 'End' for entire file (or disable this setting).",
57 | "type": "str",
58 | "default_value": "End",
59 | "enabled": "end_layer_enabled and insert_frequency != 'once_only'"
60 | },
61 | "single_end_layer":
62 | {
63 | "label": "Layer # for Single Insertion",
64 | "description": "Layer for a single insertion of the Gcode.",
65 | "type": "str",
66 | "default_value": "",
67 | "enabled": "insert_frequency == 'once_only'"
68 | },
69 | "gcode_to_add":
70 | {
71 | "label": "G-code to insert.",
72 | "description": "G-code to add before or after layer change. Use a comma to delimit multi-line commands. EX: G28 X Y,M220 S100,M117 HELL0. Note that all commands will be converted to upper-case.",
73 | "type": "str",
74 | "default_value": ""
75 | }
76 | }
77 | }"""
78 |
79 | def execute(self, data):
80 | MyCode = self.getSettingValueByKey("gcode_to_add").upper()
81 | the_start_layer = self.getSettingValueByKey("start_layer")
82 | the_end_layer = self.getSettingValueByKey("end_layer")
83 | the_end_is_enabled = self.getSettingValueByKey("end_layer_enabled")
84 | when_to_insert = self.getSettingValueByKey("insert_frequency")
85 | start_here = False
86 | RealNum = 0
87 |
88 | if the_end_layer == "End" or not the_end_is_enabled:
89 | the_end_layer = "9999999999"
90 |
91 | if "," in MyCode:
92 | gc = MyCode.split(",")
93 | gcode_to_add = ""
94 | for g_code in gc:
95 | gcode_to_add += g_code + "\n"
96 |
97 | else:
98 | gcode_to_add = MyCode + "\n"
99 |
100 | if when_to_insert == "every_layer":
101 | freq = 1
102 |
103 | if when_to_insert == "every_second":
104 | freq = 2
105 |
106 | if when_to_insert == "every_third":
107 | freq = 3
108 |
109 | if when_to_insert == "every_fifth":
110 | freq = 5
111 |
112 | if when_to_insert == "every_tenth":
113 | freq = 10
114 |
115 | if when_to_insert == "every_XXV":
116 | freq = 25
117 |
118 | if when_to_insert == "every_L":
119 | freq = 50
120 |
121 | if when_to_insert == "every_C":
122 | freq = 100
123 |
124 | if when_to_insert == "once_only":
125 | the_search_layer = self.getSettingValueByKey("single_end_layer")
126 |
127 | index = 0
128 | if when_to_insert == "once_only":
129 | for layer in data:
130 | lines = layer.split("\n")
131 | for line in lines:
132 | if ";LAYER:" in line:
133 | layer_number = int(line.split(":")[1])
134 | if layer_number == int(the_search_layer):
135 | index = data.index(layer)
136 | if self.getSettingValueByKey("insert_location") == "before":
137 | layer = gcode_to_add + layer
138 | else:
139 | layer = layer + gcode_to_add
140 |
141 | data[index] = layer
142 | break
143 |
144 | if when_to_insert != "once_only":
145 | for layer in data:
146 | lines = layer.split("\n")
147 | for line in lines:
148 | if ";LAYER:" in line:
149 | layer_number = int(line.split(":")[1])
150 | if layer_number >= int(the_start_layer) and layer_number <= int(the_end_layer):
151 | index = data.index(layer)
152 | RealNum = layer_number - int(the_start_layer)
153 | if int(RealNum / freq) - (RealNum / freq) == 0:
154 | if self.getSettingValueByKey("insert_location") == "before":
155 | layer = gcode_to_add + layer
156 | else:
157 | layer = layer + gcode_to_add
158 |
159 | data[index] = layer
160 | break
161 |
162 | return data
163 |
--------------------------------------------------------------------------------
/InfillLast.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: March 13, 2023
6 | #
7 | # Description: InfillLast
8 | #
9 | #------------------------------------------------------------------------------------------------------------------------------------
10 | #
11 | # Version 1.0 13/03/2023 first prototype right now must be use with the relative extrusion activated
12 | # Zhop Management must be include in this script ! Not manage in this release !
13 | #
14 | #------------------------------------------------------------------------------------------------------------------------------------
15 |
16 | from ..Script import Script
17 | from UM.Logger import Logger
18 | from UM.Application import Application
19 | from UM.Message import Message
20 | import re #To perform the search
21 | from enum import Enum
22 |
23 | from UM.i18n import i18nCatalog # Translation
24 | catalog = i18nCatalog("cura")
25 |
26 | __version__ = '1.0'
27 |
28 | class Section(Enum):
29 | """Enum for section type."""
30 |
31 | NOTHING = 0
32 | SKIRT = 1
33 | INNER_WALL = 2
34 | OUTER_WALL = 3
35 | INFILL = 4
36 | SKIN = 5
37 | SKIN2 = 6
38 |
39 | def is_begin_layer_line(line: str) -> bool:
40 | """Check if current line is the start of a layer section.
41 |
42 | Args:
43 | line (str): Gcode line
44 |
45 | Returns:
46 | bool: True if the line is the start of a layer section
47 | """
48 | return line.startswith(";LAYER:")
49 |
50 | def is_begin_skirt_line(line: str) -> bool:
51 | """Check if current line is the start of a SKIRT section.
52 |
53 | Args:
54 | line (str): Gcode line
55 |
56 | Returns:
57 | bool: True if the line is the start of a SKIRT section
58 | """
59 | return line.startswith(";TYPE:SKIRT")
60 |
61 | def is_begin_type_line(line: str) -> bool:
62 | """Check if current line is the start of a type section.
63 |
64 | Args:
65 | line (str): Gcode line
66 |
67 | Returns:
68 | bool: True if the line is the start of a type section
69 | """
70 | return line.startswith(";TYPE")
71 |
72 | def is_begin_mesh_line(line: str) -> bool:
73 | """Check if current line is the start of a new MESH.
74 |
75 | Args:
76 | line (str): Gcode line
77 |
78 | Returns:
79 | bool: True if the line is the start of a new MESH
80 | """
81 | return line.startswith(";MESH:")
82 |
83 | def is_e_line(line: str) -> bool:
84 | """Check if current line is a an Extruder line
85 |
86 | Args:
87 | line (str): Gcode line
88 |
89 | Returns:
90 | bool: True if the line is an Extruder line segment
91 | """
92 | return "G1" in line and "E" in line
93 |
94 | def is_relative_extrusion_line(line: str) -> bool:
95 | """Check if current line is a relative extrusion line
96 |
97 | Args:
98 | line (str): Gcode line
99 |
100 | Returns:
101 | bool: True if the line is a relative extrusion line
102 | """
103 | return "M83" in line
104 |
105 | def is_absolute_extrusion_line(line: str) -> bool:
106 | """Check if current line is an absolute extrusion line
107 |
108 | Args:
109 | line (str): Gcode line
110 |
111 | Returns:
112 | bool: True if the line is an absolute extrusion line
113 | """
114 | return "M82" in line
115 |
116 | def is_relative_instruction_line(line: str) -> bool:
117 | """Check if current line contain a M83 / G91 instruction
118 |
119 | Args:
120 | line (str): Gcode line
121 |
122 | Returns:
123 | bool: True contain a M83 / G91 instruction
124 | """
125 | return "G91" in line or "M83" in line
126 |
127 | def is_not_relative_instruction_line(line: str) -> bool:
128 | """Check if current line contain a M82 / G90 instruction
129 |
130 | Args:
131 | line (str): Gcode line
132 |
133 | Returns:
134 | bool: True contain a M82 / G90 instruction
135 | """
136 | return "G90" in line or "M82" in line
137 |
138 | def is_reset_extruder_line(line: str) -> bool:
139 | """Check if current line contain a G92 E0
140 |
141 | Args:
142 | line (str): Gcode line
143 |
144 | Returns:
145 | bool: True contain a G92 E0 instruction
146 | """
147 | return "G92" in line and "E0" in line
148 |
149 | def is_begin_skin_segment_line(line: str) -> bool:
150 | """Check if current line is the start of an skin.
151 |
152 | Args:
153 | line (str): Gcode line
154 |
155 | Returns:
156 | bool: True if the line is the start of an skin section
157 | """
158 | return line.startswith(";TYPE:SKIN")
159 |
160 | class InfillLast(Script):
161 | def __init__(self):
162 | super().__init__()
163 |
164 | def getSettingDataString(self):
165 | return """{
166 | "name": "InfillLast",
167 | "key": "InfillLast",
168 | "metadata": {},
169 | "version": 2,
170 | "settings":
171 | {
172 | "extruder_nb":
173 | {
174 | "label": "Extruder Id",
175 | "description": "Define extruder Id in case of multi extruders",
176 | "unit": "",
177 | "type": "int",
178 | "default_value": 1
179 | }
180 | }
181 | }"""
182 |
183 | def execute(self, data):
184 |
185 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
186 | relative_extrusion = bool(extrud[0].getProperty("relative_extrusion", "value"))
187 |
188 |
189 |
190 | if not relative_extrusion:
191 | Message("Must be in mode Relative extrusion", title = catalog.i18nc("@info:title", "Post Processing Skin Last")).show()
192 | return data
193 |
194 | extruder_id = self.getSettingValueByKey("extruder_nb")
195 | extruder_id = extruder_id -1
196 |
197 | # machine_extruder_count
198 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
199 | extruder_count = extruder_count-1
200 | if extruder_id>extruder_count :
201 | extruder_id=extruder_count
202 |
203 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
204 |
205 | # Check if Z hop is desactivated
206 | retraction_hop_enabled= extrud[extruder_id].getProperty("retraction_hop_enabled", "value")
207 |
208 | # Get the Cura retraction_hop and speed_z_hop as Zhop parameter
209 | retraction_hop = float(extrud[extruder_id].getProperty("retraction_hop", "value"))
210 | speed_z_hop = int(extrud[extruder_id].getProperty("speed_z_hop", "value"))
211 | speed_z_hop = speed_z_hop * 60
212 | speed_travel = extrud[0].getProperty("speed_travel", "value")
213 | speed_travel = speed_travel * 60
214 |
215 | idl=0
216 |
217 | current_z = 0
218 | Zr = "Z0"
219 | Zc = "Z0"
220 |
221 | currentlayer=0
222 | CurrentE=0
223 | ResetE=0
224 | RelativeExtruder = False
225 | SaveE = -1
226 | In_G0 = False
227 |
228 | for layer in data:
229 | layer_index = data.index(layer)
230 | # Logger.log('d', "Layer_index founded : {}".format(layer_index))
231 | lines_skin = []
232 | lines_not_skin = []
233 |
234 | lines = layer.split("\n")
235 |
236 | for line_index, line in enumerate(lines):
237 |
238 | if line.startswith("G0") and "Z" in line :
239 | searchZ = re.search(r"Z(\d*\.?\d*)", line)
240 | if searchZ:
241 | current_z=float(searchZ.group(1))
242 | Zc = "Z"+searchZ.group(1)
243 |
244 | # Check if the line start with the Comment Char
245 | if is_relative_instruction_line(line) and line[0] != ";" :
246 | relative_extrusion = True
247 | # Logger.log('d', "Relative_extrusion founded : {}".format(line))
248 |
249 | # Check if the line start with the Comment Char
250 | if is_not_relative_instruction_line(line) and line[0] != ";" :
251 | relative_extrusion = False
252 |
253 | if is_reset_extruder_line(line) and line[0] != ";" :
254 | # Logger.log('d', "Reset_extruder :" + str(CurrentE))
255 | CurrentE = 0
256 | SaveE = 0
257 |
258 | if is_relative_extrusion_line(line):
259 | RelativeExtruder = True
260 |
261 | if is_absolute_extrusion_line(line):
262 | RelativeExtruder = False
263 |
264 | if line.startswith(";LAYER_COUNT:"):
265 | # Logger.log("w", "found LAYER_COUNT %s", line[13:])
266 | layercount=int(line[13:])
267 |
268 | # ;LAYER:X
269 | if is_begin_layer_line(line):
270 | Logger.log('d', "layer_lines : {}".format(line))
271 | currentlayer=int(line[7:])
272 | if currentlayer == 0 :
273 | # Logger.log('d', "CurrentLayer : {}".format(currentlayer))
274 | idl=1
275 | else:
276 | idl=0
277 |
278 | if is_begin_type_line(line) and idl > 0:
279 | if is_begin_skin_segment_line(line):
280 | idl=2
281 | Logger.log('d', 'layer_lines : {}'.format(line))
282 | if retraction_hop_enabled :
283 | Logger.log('d', 'Output_Z : {}'.format(Output_Z))
284 | Output_Z=current_z+retraction_hop
285 | outPutLine = "G1 F{} Z{:.3f}\n".format(speed_z_hop,Output_Z)
286 | lines_skin.append(outPutLine)
287 | In_G0 = True
288 | # Must integrate Zhop Management
289 | else :
290 | idl=1
291 |
292 | #---------------------------------------
293 | # Add the Skin line to the Skin path
294 | #---------------------------------------
295 | if idl == 2 :
296 | if line.startswith("G1") and In_G0 :
297 | Output_Z=current_z+retraction_hop
298 | Logger.log('d', 'Current_z : {}'.format(current_z))
299 | outPutLine = "G0 F{} Z{:.3f}\n".format(speed_z_hop,Output_Z)
300 | Zr = "Z{:.3f}".format(Output_Z)
301 | outPutLine=line.replace(Zc, Zr)
302 | lines_skin.append(outPutLine.replace("G1", "G0"))
303 | Output_Z=current_z
304 | outPutLine = "G1 F{} Z{:.3f}\n".format(speed_z_hop,Output_Z)
305 | lines_skin.append(outPutLine)
306 | In_G0 = False
307 |
308 | lines_skin.append(line)
309 |
310 | if idl == 1 :
311 | lines_not_skin.append(line)
312 |
313 | # Logger.log('d', "idl : {}".format(idl))
314 | if idl>0 :
315 | result = ";BEGIN_OF_MODIFICATION"
316 | for line in lines_not_skin:
317 | result += "\n"
318 | result += line
319 |
320 | for line in lines_skin:
321 | result += "\n"
322 | result += line
323 |
324 | result += ";END_OF_MODIFICATION\n"
325 | else:
326 | result = "\n".join(lines)
327 |
328 | data[layer_index] = result
329 |
330 | return data
331 |
--------------------------------------------------------------------------------
/InhibFan.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: July 13, 2022
6 | #
7 | # Description: postprocessing-script to supress the Fan on First layers
8 | #
9 | #------------------------------------------------------------------------------------------------------------------------------------
10 | #
11 | # Version 1.1 13/07/2022
12 | #
13 | #------------------------------------------------------------------------------------------------------------------------------------
14 |
15 | from ..Script import Script
16 | from UM.Application import Application # To get the current printer's settings.
17 | from cura.CuraVersion import CuraVersion # type: ignore
18 | from UM.Message import Message
19 | from UM.Logger import Logger
20 | from UM.i18n import i18nCatalog # Translation
21 | catalog = i18nCatalog("cura")
22 |
23 | __version__ = '1.1'
24 |
25 | class InhibFan(Script):
26 | def __init__(self):
27 | super().__init__()
28 |
29 | def getSettingDataString(self):
30 | return """{
31 | "name": "InhibFan",
32 | "key": "InhibFan",
33 | "metadata": {},
34 | "version": 2,
35 | "settings":
36 | {
37 | "inhiblayer":
38 | {
39 | "label": "Nb of Layers for Fan inhibition",
40 | "description": "Number of Layer where we want to turn off the print cooling fan",
41 | "type": "int",
42 | "default_value": 4,
43 | "minimum_value": 1,
44 | "maximum_value": 100,
45 | "minimum_value_warning": 1,
46 | "maximum_value_warning": 100
47 | },
48 | "extruder_nb":
49 | {
50 | "label": "Extruder Id",
51 | "description": "Define extruder Id in case of multi extruders",
52 | "unit": "",
53 | "type": "int",
54 | "default_value": 1
55 | }
56 | }
57 | }"""
58 |
59 | def execute(self, data):
60 |
61 | inhiblayer = 0
62 | inhiblayer = int(self.getSettingValueByKey("inhiblayer"))
63 |
64 | extruder_id = self.getSettingValueByKey("extruder_nb")
65 | extruder_id = extruder_id -1
66 |
67 | # GEt extrud
68 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
69 | # machine_extruder_count
70 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
71 | extruder_count = extruder_count-1
72 | if extruder_id>extruder_count :
73 | extruder_id=extruder_count
74 |
75 | cool_fan_speed = extrud[extruder_id].getProperty("cool_fan_speed", "value")
76 | Logger.log('d', 'cool_fan_speed : {}'.format(cool_fan_speed))
77 | setfan = int((int(cool_fan_speed)/100)*255)
78 |
79 | current_Layer = 0
80 | idl=0
81 |
82 | for layer in data:
83 | layer_index = data.index(layer)
84 |
85 | lines = layer.split("\n")
86 | for line in lines:
87 |
88 | if line.startswith(";LAYER:") and inhiblayer > 0 :
89 | current_Layer = int(line.split(":")[1])
90 | current_Layer += 1
91 | # If the layer number is smaller than the Nb of Layers for Fan inhibition
92 | if current_Layer <= inhiblayer :
93 | idl=1
94 | # Special analyse for the following layer
95 | elif current_Layer == (inhiblayer + 1) :
96 | line_index = lines.index(line)
97 | next_line=lines[line_index+1]
98 | Logger.log('d', 'next_line : {}'.format(next_line))
99 | # If we have a Fan Instruction leave It
100 | if next_line.startswith("M106 S") :
101 | Logger.log('d', 'Keep the S Value layer {}'.format(current_Layer))
102 | # If we have a Fan turned off leave it like that
103 | elif next_line.startswith("M107") :
104 | Logger.log('d', 'Keep the Fan OFF layer {}'.format(current_Layer))
105 | # If we have nothing then we need to set the fan value to the regular speed
106 | else :
107 | lines.insert(line_index + 1, "M106 S"+str(setfan))
108 | idl=0
109 |
110 | else :
111 | idl=0
112 |
113 | if line.startswith("M106 S") and idl == 1 :
114 | line_index = lines.index(line)
115 | lines[line_index] = "M107"
116 |
117 | result = "\n".join(lines)
118 | data[layer_index] = result
119 |
120 | return data
121 |
--------------------------------------------------------------------------------
/InsertAtLayerChange.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Ultimaker B.V.
2 | # Cura is released under the terms of the LGPLv3 or higher.
3 | # Created by Wayne Porter
4 | # Modification 5@xes 12/2020 Option increment_layer
5 |
6 | from ..Script import Script
7 |
8 | class InsertAtLayerChange(Script):
9 | def __init__(self):
10 | super().__init__()
11 |
12 | def getSettingDataString(self):
13 | return """{
14 | "name": "Insert at layer change",
15 | "key": "InsertAtLayerChange",
16 | "metadata": {},
17 | "version": 2,
18 | "settings":
19 | {
20 | "insert_location":
21 | {
22 | "label": "When to insert",
23 | "description": "Whether to insert code before or after layer change.",
24 | "type": "enum",
25 | "options": {"before": "Before", "after": "After"},
26 | "default_value": "before"
27 | },
28 | "increment_layer":
29 | {
30 | "label": "Layer increment",
31 | "description": "Number of layer increment to add this code",
32 | "type": "int",
33 | "value": "1",
34 | "minimum_value": "1"
35 | },
36 | "gcode_to_add":
37 | {
38 | "label": "G-code to insert.",
39 | "description": "G-code to add before or after layer change.",
40 | "type": "str",
41 | "default_value": ""
42 | }
43 | }
44 | }"""
45 |
46 | def execute(self, data):
47 | increment = self.getSettingValueByKey("increment_layer")
48 | gcode_to_add = ";Code inserted by script : \n"
49 | tablines = self.getSettingValueByKey("gcode_to_add").split("
")
50 | for line in tablines:
51 | gcode_to_add = gcode_to_add + line + "\n"
52 | cur_inc=1
53 |
54 | for layer in data:
55 | # Check that a layer is being printed
56 | lines = layer.split("\n")
57 | for line in lines:
58 | if ";LAYER:" in line:
59 | index = data.index(layer)
60 | if increment == cur_inc :
61 | if self.getSettingValueByKey("insert_location") == "before":
62 | layer = gcode_to_add + layer
63 | else:
64 | layer = layer + gcode_to_add
65 | data[index] = layer
66 | cur_inc=1
67 | else :
68 | cur_inc+=1
69 |
70 | break
71 | return data
72 |
--------------------------------------------------------------------------------
/InsertAtLayerNumber.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023 5@xes
2 | # Insert At Layer Number Add comment 08/02/2023
3 |
4 | from ..Script import Script
5 |
6 | class InsertAtLayerNumber(Script):
7 | def __init__(self):
8 | super().__init__()
9 |
10 | def getSettingDataString(self):
11 | return """{
12 | "name": "Insert at layer number",
13 | "key": "InsertAtLayerNumber",
14 | "metadata": {},
15 | "version": 2,
16 | "settings":
17 | {
18 | "insert_location":
19 | {
20 | "label": "When to insert",
21 | "description": "Whether to insert code before or after layer change.",
22 | "type": "enum",
23 | "options": {"before": "Before", "after": "After"},
24 | "default_value": "before"
25 | },
26 | "position_layer":
27 | {
28 | "label": "Layer position",
29 | "description": "Layer Position where to add this code",
30 | "type": "int",
31 | "value": 1,
32 | "minimum_value": 1
33 | },
34 | "gcode_to_add":
35 | {
36 | "label": "G-code to insert.",
37 | "description": "G-code to add before or after layer change.",
38 | "type": "str",
39 | "default_value": ""
40 | }
41 | }
42 | }"""
43 |
44 | def execute(self, data):
45 | position = self.getSettingValueByKey("position_layer")
46 | gcode_to_add = ";Code inserted by script : \n"
47 | tablines = self.getSettingValueByKey("gcode_to_add").split("
")
48 | for line in tablines:
49 | gcode_to_add = gcode_to_add + line + "\n"
50 |
51 | for layer in data:
52 | # Check that a layer is being printed
53 | lines = layer.split("\n")
54 | for line in lines:
55 | if ";LAYER:" in line:
56 | layer_number = int(line.split(":")[1])+1
57 | index = data.index(layer)
58 | if position == layer_number :
59 | if self.getSettingValueByKey("insert_location") == "before":
60 | layer = gcode_to_add + layer
61 | else:
62 | layer = layer + gcode_to_add
63 | data[index] = layer
64 |
65 | break
66 | return data
67 |
--------------------------------------------------------------------------------
/KlipperPrintArea.py:
--------------------------------------------------------------------------------
1 | ''' Based on script by frankbags@https://gist.github.com/frankbags/c85d37d9faff7bce67b6d18ec4e716ff '''
2 | import re # To perform the search and replace.
3 | from ..Script import Script
4 |
5 |
6 | class KlipperPrintArea(Script):
7 | def __init__(self):
8 | super().__init__()
9 |
10 | def getSettingDataString(self):
11 | return """{
12 | "name": "Klipper print area mesh",
13 | "key": "KlipperPrintArea",
14 | "metadata": {},
15 | "version": 2,
16 | "settings":{}
17 | }"""
18 |
19 | def execute(self, data):
20 |
21 | minMaxXY = {'MINX': 0, 'MINY': 0, 'MAXX': 0, 'MAXY': 0}
22 | startGcodeLineData = ''
23 |
24 | for layerNumber, layerData in enumerate(data):
25 |
26 | # search for print area boundary
27 | for k, v in minMaxXY.items():
28 | result = re.search(str(k)+":(\d*\.?\d*)", layerData)
29 | if result is not None:
30 | minMaxXY[k] = result.group(1)
31 | # search for set print area macro
32 | areaStartGcode = re.search(
33 | ".*%(MINX|MAXX|MINY|MAXY)%.*", layerData)
34 | # replace print area template
35 | if areaStartGcode is not None:
36 | if not startGcodeLineData:
37 | startGcodeLineData = layerData
38 | for k, v in minMaxXY.items():
39 | pattern3 = re.compile('%' + k + '%')
40 | startGcodeLineData = re.sub(
41 | pattern3, v, startGcodeLineData)
42 | data[layerNumber] = startGcodeLineData
43 |
44 | return data
45 |
46 |
47 | # start g-code format
48 | # START_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0} AREA_START=%MINX%,%MINY% AREA_END=%MAXX%,%MAXY%
--------------------------------------------------------------------------------
/LevelingMeshOptimizer.py:
--------------------------------------------------------------------------------
1 | #
2 | # Author : CCS86
3 | # Source initial : https://forum.duet3d.com/topic/14994/f-r-auto-define-m557-mesh-bounds-from-gcode/5?_=1637506151764
4 | # We have a single parameter (mesh spacing), and it parses the first layer gcode for min/max X and Y coordinates, and then replaces the M557 line in your start gcode.
5 | # You must have a static mesh leveling command in your start gcode, like: M557 X0:200 Y0:200 S20
6 | # This command wil be replace by the new M557 command based on the dimmension in the initial G-Code
7 | #
8 | # M557 : https://reprap.org/wiki/G-code#M557:_Set_Z_probe_point_or_define_probing_grid
9 | #
10 |
11 | import re
12 |
13 | from ..Script import Script
14 |
15 | class LevelingMeshOptimizer(Script):
16 | def getSettingDataString(self):
17 | return """{
18 | "name": "Leveling Mesh Optimizer",
19 | "key": "LevelingMeshOptimizer",
20 | "metadata": {},
21 | "version": 2,
22 | "settings": {
23 | "spacing": {
24 | "label": "Spacing",
25 | "description": "How far apart to space the probe points within the mesh",
26 | "unit": "mm",
27 | "type": "float",
28 | "default_value": 10
29 | }
30 | }
31 | }"""
32 |
33 |
34 | ## Calculates and fills in the bounds of the first layer.
35 | # \param data A list of lines of GCODE representing the entire print.
36 | # \return A similar list, with the bounds of the mesh filled in.
37 | def execute(self, data: [str]) -> [str]:
38 | _DATA_START_GCODE = 1
39 | _DATA_LAYER_0 = 2
40 |
41 | # Calculate bounds of first layer
42 | bounds = self.findBounds(data[_DATA_LAYER_0])
43 |
44 | # Fill in bounds in start GCODE
45 | data[_DATA_START_GCODE] = self.fillBounds(data[_DATA_START_GCODE], bounds)
46 |
47 | return data
48 |
49 |
50 | ## Finds the minimum and maximum X and Y coordinates in a GCODE layer.
51 | # \param data A block of GCODE representing the layer.
52 | # \return A dict such that [X|Y][min|max] resolves to a float
53 | def findBounds(self, data: str) -> {str: {str: float}}:
54 | bounds = {
55 | "X": {"min": float("inf"), "max": float("-inf")},
56 | "Y": {"min": float("inf"), "max": float("-inf")},
57 | }
58 |
59 | for line in data.split("\n"):
60 | # Get coordinates on this line
61 | for match in re.findall(r"([XY])([\d.]+)\s", line):
62 | # Get axis letter
63 | axis = match[0]
64 |
65 | # Skip axes we don't care about
66 | if axis not in bounds:
67 | continue
68 |
69 | # Parse parameter value
70 | value = float(match[1])
71 |
72 | # Update bounds
73 | bounds[axis]["min"] = round(min(bounds[axis]["min"], value),0)
74 | bounds[axis]["max"] = round(max(bounds[axis]["max"], value),0)
75 |
76 | return bounds
77 |
78 |
79 | ## Replaces the M557 command in the start GCODE so that the bounds are filled in.
80 | # \param data The entire start GCODE block.
81 | # \return The same GCODE but with the bounds of the mesh filled in.
82 | def fillBounds(self, data: str, bounds: {str: {str: float}}) -> str:
83 | # Fill in the level command template
84 | new_cmd = "M557 X%.1f:%.1f Y%.1f:%.1f S%.1f ; Leveling mesh defined by LevelingMeshOptimizer" % (
85 | bounds["X"]["min"]-1, bounds["X"]["max"],
86 | bounds["Y"]["min"]-1, bounds["Y"]["max"],
87 | self.getSettingValueByKey("spacing"),
88 | )
89 |
90 | # Replace M557 command in GCODE
91 | return re.sub(r"^M557 .*$", new_cmd, data, flags=re.MULTILINE)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cura-Postprocessing-Scripts
2 | Compilation of personal Ultimaker Cura postprocessing scripts
3 |
4 |
5 | Installation
6 | --
7 |
8 | The files must be stored in the user script directory of the respective Cura version: **\AppData\Roaming\cura\ (current version of cura)\scripts**
9 |
10 | After the next start of Cura, the script can be added via Extension / Post-Processing / Modify G-Code Add a script.
11 |
12 | 
13 |
14 |
15 | [DisplayPrintInfosOnLCD.py](DisplayPrintInfosOnLCD.py)
16 | -----
17 |
18 | Description: This plugin shows custom messages about your print on the Printer Panel...
19 | Please look at the option
20 | - LayerId: Use the Layer ID coded in the Gcode and not an increment starting from 0
21 |
22 | 
23 |
24 | [GCodeDocumentation.py](GCodeDocumentation.py)
25 | -----
26 | Description: Add slicing parameter in the GCode Header
27 |
28 | 
29 |
30 | [SpeedTower.py](SpeedTower.py)
31 | -----
32 | Description: postprocessing-script to easily define a Speed Tower.
33 |
34 | 
35 |
36 | This script offers the possibilities to modify :
37 | - Speed ( By using M220 instruction )
38 | - Acceleration
39 | - Jerk
40 | - Junction Deviation
41 | - Marlin Linear Advance
42 | - RepRap Pressure Advance
43 |
44 | [TempFanTower.py](TempFanTower.py)
45 | -----
46 |
47 | Description: postprocessing-script to easily use a temptower and not use 10 changeAtZ-scripts
48 |
49 | The default values are for this temptower PLA model [https://www.thingiverse.com/thing:2493504](https://www.thingiverse.com/thing:2493504)
50 | - Temp Tower PLA de 210 à 170
51 | - Possibility to define also a Fan Tower, Fan percentage speed indicate with a semi-colon as separator
52 |
53 | 
54 |
55 |
56 | [RetractTower.py](RetractTower.py)
57 | -----
58 |
59 | Description: postprocessing-script to easily create a Retract Tower
60 |
61 | Two options :
62 |
63 | - Speed : Speed variation
64 |
65 | - Retract : Distance retract variation
66 |
67 | 
68 |
69 |
70 | [FanIroning.py](FanIroning.py)
71 | -----
72 |
73 | Description: FlowIroning for 3D prints. Change the Fan value on ironing operation.
74 |
75 | 
76 |
77 | Note: Some ironing areas are not detected by the script. It is mainly the small areas located in contour areas.
78 |
79 | 
80 |
81 | [RepRapPrintInfos.py](RepRapPrintInfos.py)
82 | -----
83 |
84 | Description: add header info and part thumbnail for RepRap machine 3DWOX
85 |
86 | 
87 |
88 | **Not fully tested**
89 |
90 | [GradientInfill.py](GradientInfill.py) (original idea from [CNCKitchen](https://github.com/CNCKitchen/GradientInfill) )
91 | -----
92 |
93 | **Version: 1.5**
94 |
95 | GradientInfill.py Postprocessing Script for Cura Plugin. Save the file in the _C:\Program Files\Ultimaker Cura **X.X**\plugins\PostProcessingPlugin\scripts_ directory.
96 |
97 | Extrusion mode in Cura must be set in relative mode. If it's not the case an error message will be raised in Cura.
98 |
99 | 
100 |
101 | No Gcode will be generated by Cura in this case. Same behavior if Cura settings are not suitable for Gradient Infill modification :
102 |
103 | - Infill pattern types ZigZag, Concentric, Cross, and Cross3D are not allowed
104 | - In Cura, the option "Connect Infill Lines" for the other patterns mustn't be used.
105 |
106 | The wall must be done before the Infill element. So in Cura, the Option infill_before_walls must be set to Off
107 |
108 | **Postprocessing Options**
109 |
110 | 
111 |
112 | - Gradient Distance : Distance of the gradient (max to min) in mm
113 | - Gradient Discretization : Only applicable for linear infills; number of segments within the gradient(segmentLength=gradientThickness / gradientDiscretization) use sensible values to not overload
114 | - Max flow : Maximum extrusion flow
115 | - Min flow : Minimum extrusion flow
116 | - Short distance flow : Extrusion flow for short distance < 2x Gradient distance
117 | - Gradual speed : Activate also Gradual Speed linked to the gradual flow
118 | - Max over speed : Maximum over speed factor
119 | - Min over speed : Minimum over speed factor
120 | - Extruder Id : Define extruder Id in case of multi extruders
121 | - Test with outer wall : "Test the gradient with the outer wall segments
122 |
123 |
124 | A new Flow Value for a short distance (Linear move < 2 x Gradient distance) was added to the standard GradientInfill script.
125 |
126 | Add a gradual speed variation for a machine without a direct drive extruder.
127 |
128 | 
129 |
130 | Sample part with a Gradient distance set to 8 mm :
131 | 
132 |
133 | [CommentGCode.py](CommentGCode.py)
134 | ----
135 |
136 | This Cura Postprocessing Script adds comments to the G-Code. The user can select or deselect comments for M-Commands and G-Commands separately.
137 |
138 | G0 and G1 commands are only commented if a retract is included.
139 |
140 | Command, description, and parameters are read from a CSV file. If a command is not contained, the required data is determined once via the website http://marlinfw.org/docs/gcode/ and added to the CSV file.
141 |
142 | 
143 |
144 |
145 | [InsertAtLayerChange.py](InsertAtLayerChange.py)
146 | ----
147 |
148 | Modification of the official script with Layer Increment.
149 |
150 | [InsertAtLayerNumber.py](InsertAtLayerNumber.py)
151 | ----
152 |
153 | Insert a Gcode at Layer Number. Can be inserted before or after the Layer Number.
154 |
155 | 
156 |
157 |
158 | [FastFirstInfill.py](FastFirstInfill.py)
159 | ----
160 |
161 | Script cura to set a higher filling speed for the first layer.
162 |
163 | 
164 |
165 |
166 | Original first layer speed before modification
167 |
168 |
169 | 
170 |
171 | First layer speed after modification with the script.
172 |
173 |
174 | 
175 |
176 | [CheckFirstSpeed.py](CheckFirstSpeed.py)
177 | ----
178 |
179 | Script to modify the first layer infill and Check the first Wall Speed Bug Cura 5.6
180 |
181 | 
182 |
183 | Script used to fix a bug in Cura versions 5.5 and 5.6 [bug on github](https://github.com/Ultimaker/Cura/issues/17594)
184 |
185 | Original first layer speed before modification
186 |
187 |
188 | 
189 |
190 | First layer speed after modification with the script and a first infill speed of 30 mm/s.
191 |
192 |
193 | 
194 |
195 |
196 | [SlowZ.py](SlowZ.py)
197 | ----
198 |
199 | Script cura to reduce the speed according to the print height.
200 |
201 | 
202 |
203 | - Slow Z percentage : Positive value to slow the print as the z value rises up to this percentage.
204 |
205 | - Slow Z height : Positive value to define the start height of the speed reduction.
206 |
207 | - Display details on LCD : This setting will insert M117 gcode instructions, to display the current modification in the G-Code being used.
208 |
209 | 
210 |
211 |
212 |
213 | [MultiBrim.py](MultiBrim.py)
214 | ----
215 |
216 | Script cura to duplicate the initial Brim/skirt to obtain a thick Brim.
217 |
218 |
219 | **Note :** Updated for Cura 5.0 & 5.1
220 |
221 |
222 | 
223 |
224 | - Brim addition : Number of brims to add to the existing one.
225 |
226 | - Brim speed : Speed for the subsequent brims.
227 |
228 | Multi-layer brim height request on Cura Github [#6929](https://github.com/Ultimaker/Cura/issues/6929)
229 |
230 | Different tests made with the current release (V1.6)
231 |
232 | 
233 |
234 |
235 | [ChangeFanValue.py](ChangeFanValue.py)
236 | ----
237 |
238 | Script Cura to change the FAN value when we are in the case of Minimum Layer Time. When we are in a minimum Layer Time situation the Script fixes the FAN values to the **Fan value in %**
239 |
240 | 
241 |
242 | Note : In GCode 100% = 255 for the Fan Value *M106S255*.
243 |
244 |
245 | [InhibFan.py](InhibFan.py)
246 | ----
247 |
248 | Script Cura to turn off the print cooling fan on the first X Layers.
249 |
250 | 
251 |
252 | Note : In GCode turn off the print cooling fan. [M107](https://marlinfw.org/docs/gcode/M107.html).
253 |
254 | [ZMoveG0.py](ZMoveG0.py)
255 | -----
256 |
257 | Description: Z hop for every G0 displacement even if Retraction is not defined ( Retraction must be deactivated). Use the speed_z_hop and the retraction_hop as retraction parameters.
258 |
259 | 
260 |
261 | Errors messages if Retraction is activated.
262 |
263 | 
264 |
265 |
266 | [ZMoveIroning.py](ZMoveIroning.py)
267 | -----
268 |
269 | Description: ZMoveIroning for 3D prints. Z hop for ironing
270 |
271 | 
272 |
273 |
274 | [SpoonOrder.py](SpoonOrder.py)
275 | -----
276 |
277 | Description: SpoonOrder is a script used in the [spoon Anti-warping Plugin](https://github.com/5axes/SpoonAntiWarping), Print every spoon tabs first.
278 |
279 | 
280 |
281 |
282 | [InfillLast.py](InfillLast.py)
283 | -----
284 |
285 | Description: InfillLast print for the first layer every Infill path at the end of the First Layer. ( Work in progress )
286 |
287 | 
288 |
289 |
290 |
291 | [AlterZhops.py](AlterZhops.py)
292 | -----
293 |
294 | Author : Greg Foresi [(GregValiant)](https://github.com/GregValiant)
295 |
296 | Description: This script changes the Z-Hop height from the beginning of the 'Start Layer' to the end of the 'End Layer'. If the new hop height is 0.0 it negates the z-hop movement. The Z-hop command lines are altered, not removed.
297 |
298 | - This script supports different hop heights for up to 4 extruders.
299 | - Z-Hops at tool change are not affected when 'Alter Z-Hops' runs BEFORE other post-processors that make code insertions just before Tool Change lines
300 | - Adaptive Layers is not compatible and there is an exit if it is enabled in Cura
301 | - Z-Hops must be enabled for at least one extruder in Cura or the plugin exits.
302 |
303 | 
304 |
305 |
306 |
307 | [DiagonalZHop.py](DiagonalZHop.py)
308 | -----
309 |
310 | Modifiy existing Zhop to get Diagonal ZHop for 3D prints.
311 |
--------------------------------------------------------------------------------
/RepRapPrintInfos.py:
--------------------------------------------------------------------------------
1 | # Cura PostProcessingPlugin
2 | # Author: 5axes
3 | # Date: Mars 06 2020
4 | # Modification : Mars 07 2020
5 | # Ajout de l'intégration du fichier RepRapPrintInfos.txt pour image impression
6 | # https://3dprinter.sindoh.com/fr/support/downloads/3dwox1
7 | # Modification : 25/05/2020 add thumbnail_gcode
8 | # Description: Ajout des infos pour machine RepRap machine 3DWOX
9 |
10 | from ..Script import Script
11 |
12 |
13 | from cura.CuraApplication import CuraApplication
14 | from cura.Snapshot import Snapshot
15 | from PyQt5.QtCore import Qt, QByteArray, QBuffer, QIODevice
16 | from PyQt5.QtGui import QImage
17 | from typing import List
18 |
19 | from UM.Logger import Logger
20 | from UM.Message import Message
21 |
22 | import string
23 | import os
24 | import textwrap
25 |
26 | class RepRapPrintInfos(Script):
27 |
28 | GCODE_LINE_PREFIX = "; "
29 | GCODE_LINE_WIDTH = 80
30 |
31 | def __init__(self):
32 | super().__init__()
33 |
34 | def _image_to_byte_array(self, image) -> QByteArray:
35 | byte_array = QByteArray()
36 | buffer = QBuffer(byte_array)
37 | buffer.open(QIODevice.WriteOnly)
38 | image.save(buffer, 'png')
39 | buffer.close()
40 | return byte_array
41 |
42 | def _image_to_base64(self, image) -> QByteArray:
43 | ba = self._image_to_byte_array(image)
44 | ba64 = ba.toBase64()
45 | return ba64
46 |
47 | def _txt_to_gcode(self, txt) -> str:
48 | wrapper = textwrap.TextWrapper(width=self.GCODE_LINE_WIDTH)
49 | Display_txt = wrapper.fill(txt)
50 | lines = Display_txt.split("\n")
51 | _Final_Txt=""
52 | _Counter = Display_txt.count('\n')+1
53 | for currentLine in lines:
54 | line_index = lines.index(currentLine)+1
55 | _Final_Txt += ";IMAGE[%d/%d] %s\n" % (line_index, _Counter, currentLine)
56 |
57 | return _Final_Txt
58 |
59 | def _create_snapshot(self, width, height):
60 | # must be called from the main thread because of OpenGL
61 | Logger.log("d", "Creating thumbnail image...")
62 | try:
63 | snapshot = Snapshot.snapshot(width = width, height = height)
64 | return snapshot
65 | except Exception:
66 | Logger.logException("w", "Failed to create snapshot image")
67 | return None
68 |
69 | def _create_thumbnail_gcode(self, width, height) -> str:
70 | min_size = min(width,height)
71 | tmp_snapshot = self._create_snapshot(min_size, min_size)
72 | # Scale it to the correct size
73 | if (width != height):
74 | snapshot = tmp_snapshot.copy(int((min_size-width)/2), int((min_size-height)/2), width, height)
75 | else:
76 | snapshot = tmp_snapshot
77 |
78 | ba64 = self._image_to_base64(snapshot)
79 | b64str = str(ba64, 'utf-8')
80 | b64gcode = self._txt_to_gcode(b64str)
81 | gcode = "\n" + self.GCODE_LINE_PREFIX + "\n" + \
82 | self.GCODE_LINE_PREFIX + "thumbnail begin " + str(width) + "x" + str(height) + " " + str(len(b64str)) + "\n" + \
83 | b64gcode + "\n" + \
84 | self.GCODE_LINE_PREFIX + "thumbnail end\n" + self.GCODE_LINE_PREFIX + "\n"
85 |
86 | return gcode
87 |
88 | def getSettingDataString(self):
89 | return """{
90 | "name": "Add 3DWOX Print Infos",
91 | "key": "RepRapPrintInfos",
92 | "metadata": {},
93 | "version": 2,
94 | "settings":
95 | {
96 | "LayerId":
97 | {
98 | "label": "Utilisation Layer Id G-Code",
99 | "description": "Utilise le Layer Id codé dans le fichier G-Code. A utiliser pour impression pièce à pièce",
100 | "type": "bool",
101 | "default_value": false
102 | },
103 | "thumbnail_width":
104 | {
105 | "label": "Thumbnail Width",
106 | "description": "Width of the thumbnail",
107 | "unit": "pixels",
108 | "type": "int",
109 | "default_value": 47,
110 | "minimum_value": "16",
111 | "minimum_value_warning": "16"
112 | },
113 | "thumbnail_height":
114 | {
115 | "label": "Thumbnail Height",
116 | "description": "Height of the thumbnail",
117 | "unit": "pixels",
118 | "type": "int",
119 | "default_value": 47,
120 | "minimum_value": "16",
121 | "minimum_value_warning": "16"
122 | }
123 | }
124 | }"""
125 |
126 | def execute(self, data):
127 | max_layer = 0
128 | total_time = 0
129 | part = 0
130 | total_time_string = ""
131 | total_time_s=0
132 | current_time_s=0
133 | current_time_string = ""
134 | MCode = "M532"
135 | # Init variable pour éviter erreur si code non présent ou ordre différent
136 | min_x=0
137 | min_y=0
138 | min_z=0
139 | max_x=0
140 | max_y=0
141 | max_z=0
142 | percent=0
143 |
144 | thumbnail_width = self.getSettingValueByKey("thumbnail_width")
145 | thumbnail_height = self.getSettingValueByKey("thumbnail_height")
146 |
147 |
148 |
149 | Id = 1
150 | for layer in data:
151 | display_text = MCode + " L" + str(Id)
152 | layer_index = data.index(layer)
153 | lines = layer.split("\n")
154 | for line in lines:
155 | line_index = lines.index(line)
156 |
157 | if line.startswith(";FLAVOR:"):
158 | Logger.log("d", "Adding thumbnail image, resolution=" + str(thumbnail_width) + "x" + str(thumbnail_height))
159 | thumbnail_gcode = self._create_thumbnail_gcode(thumbnail_width, thumbnail_height)
160 | tablines = thumbnail_gcode.split("\n")
161 | Ind=1
162 | for Cline in tablines:
163 | lines.insert(line_index + Ind, Cline)
164 | Ind += 1
165 |
166 |
167 | if line.startswith(";MINX:"):
168 | min_x = float(line.split(":")[1]) # Recuperation MINX
169 | if line.startswith(";MINY:"):
170 | min_y = float(line.split(":")[1]) # Recuperation MINY
171 | if line.startswith(";MINZ:"):
172 | min_z = float(line.split(":")[1]) # Recuperation MINZ
173 | if line.startswith(";MAXX:"):
174 | max_x = float(line.split(":")[1]) # Recuperation MAXX
175 | if line.startswith(";MAXY:"):
176 | max_y = float(line.split(":")[1]) # Recuperation MAXY
177 | if line.startswith(";MAXZ:"):
178 | max_z = float(line.split(":")[1]) # Recuperation MAXZ
179 |
180 | dimension_string = "{:05.1f}:{:05.1f}:{:05.1f}".format((max_x-min_x), (max_y-min_y), max_z)
181 | display_text = ";DIMENSION: [" + dimension_string + "]"
182 | lines.insert(line_index + 1, display_text)
183 |
184 | if line.startswith(";Filament used:"):
185 | Filament = line.split(":")[1] # Recuperation Filament used:
186 | Filament = Filament.split("m")[0]
187 |
188 | Filament_Used=float(Filament)
189 | Filament_MM=Filament_Used*1000
190 |
191 | display_text = ";ESTIMATION_FILAMENT: [" + str(int(Filament_MM)) + "]"
192 | lines.insert(line_index + 1, display_text)
193 |
194 | if line.startswith(";LAYER_COUNT:"):
195 | max_layer = line.split(":")[1] # Recuperation Nb Layer Maxi
196 | display_text = ";TOTAL_LAYER: ["+ str(max_layer) + "]"
197 | lines.insert(line_index + 1, display_text)
198 |
199 | # ECRITURE M532
200 | if line.startswith(";LAYER:"):
201 | # Logger.log('d', 'X Pourcentage : {}'.format(percent))
202 | percent_string = " X{:d}".format(int(percent))
203 | display_text = MCode + percent_string + " L" + str(Id)
204 |
205 | lines.insert(line_index + 1, display_text) # Insert du code M532 apres les layers
206 |
207 | if self.getSettingValueByKey("LayerId"):
208 | Id = int(line.split(":")[1]) # Utilise le Layer dans G-Code ;LAYER:1
209 | if Id == 0:
210 | part += 1 # Incrémente le numero de pièce
211 | Id += 1
212 | else:
213 | Id += 1 # Incrémente le numero de Layer (sans utiliser celui du Gcode)
214 |
215 | if line.startswith(";TIME:"):
216 | total_time = int(line.split(":")[1])
217 | m, s = divmod(total_time, 60) # Decomposition en
218 | h, m = divmod(m, 60) # heures, minutes et secondes
219 | total_time_string = "{:04d}:{:02d}:{:02d}".format(int(h), int(m), int(s))
220 | total_time_s= (h*3600)+(m*60)+s
221 | current_time_string = total_time_string
222 | display_text = ";ESTIMATION_TIME: [" + total_time_string + "]"
223 | lines.insert(line_index + 1, display_text)
224 |
225 |
226 | elif line.startswith(";TIME_ELAPSED:"):
227 | current_time = float(line.split(":")[1])
228 | m1, s1 = divmod(current_time, 60) # Decomposition en
229 | h1, m1 = divmod(m1, 60) # heures, minutes et secondes
230 | current_time_s= (h1*3600)+(m1*60)+s1
231 | if total_time_s>0 :
232 | percent=(current_time_s/total_time_s)*100
233 | if percent>100 :
234 | percent=100
235 | current_time_string = "{:04d}:{:04d}:{:02d}".format(int(h1), int(m1), int(s1))
236 | # Logger.log('d', 'Pourcentage : {}'.format(percent))
237 |
238 | final_lines = "\n".join(lines)
239 | data[layer_index] = final_lines
240 |
241 | return data
242 |
--------------------------------------------------------------------------------
/RetractContinue.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2023 UltiMaker B.V.
2 | # The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
3 | # Altered 06-01-2023 by GregValiant (Greg Foresi)
4 | # Added regex to check for Zhop lines because they were interfering with the script during combing.
5 | # Changed the significant digits to: F=0, X=3, Y=3, Z=2
6 |
7 | from ..Script import Script
8 | import re
9 | from UM.Application import Application # To get current absolute/relative setting.
10 | from UM.Math.Vector import Vector
11 |
12 | from typing import List, Tuple
13 |
14 |
15 | class RetractContinue(Script):
16 | """Continues retracting during all travel moves."""
17 |
18 | def getSettingDataString(self) -> str:
19 | return """{
20 | "name": "Retract Continue",
21 | "key": "RetractContinue",
22 | "metadata": {},
23 | "version": 2,
24 | "settings":
25 | {
26 | "extra_retraction_speed":
27 | {
28 | "label": "Extra Retraction Ratio",
29 | "description": "How much does it retract during the travel move, by ratio of the travel length.",
30 | "unit": "mmRet/mmDist",
31 | "type": "float",
32 | "default_value": 0.05
33 | }
34 | }
35 | }"""
36 |
37 | def _getTravelMove(self, travel_move: str, default_pos: Vector) -> Tuple[Vector, float]:
38 | travel = Vector(
39 | self.getValue(travel_move, "X", default_pos.x),
40 | self.getValue(travel_move, "Y", default_pos.y),
41 | self.getValue(travel_move, "Z", default_pos.z)
42 | )
43 | f = self.getValue(travel_move, "F", -1.0)
44 | return travel, f
45 |
46 | def _travelMoveString(self, travel: Vector, f: float, e: float) -> str:
47 | # Note that only G1 moves are written, since extrusion is included.
48 | if f <= 0.0:
49 | return f"G1 X{travel.x:.3f} Y{travel.y:.3f} Z{travel.z:.2f} E{e:.5f}"
50 | else:
51 | return f"G1 F{f:.0f} X{travel.x:.3f} Y{travel.y:.3f} Z{travel.z:.2f} E{e:.5f}"
52 |
53 | def execute(self, data: List[str]) -> List[str]:
54 | current_e = 0.0
55 | to_compensate = 0 # Used when extrusion mode is relative.
56 | is_active = False # Whether retract-continue is in effect.
57 |
58 | current_pos = Vector(0.0, 0.0, 0.0)
59 | last_pos = Vector(0.0, 0.0, 0.0)
60 |
61 | extra_retraction_speed = self.getSettingValueByKey("extra_retraction_speed")
62 | relative_extrusion = Application.getInstance().getGlobalContainerStack().getProperty(
63 | "relative_extrusion", "value"
64 | )
65 |
66 | for layer_number, layer in enumerate(data):
67 | lines = layer.split("\n")
68 | for line_number, line in enumerate(lines):
69 |
70 | # Focus on move-type lines.
71 | code_g = self.getValue(line, "G")
72 | if code_g not in [0, 1]:
73 | continue
74 |
75 | # Track X,Y,Z location.
76 | last_pos = last_pos.set(current_pos.x, current_pos.y, current_pos.z)
77 | current_pos = current_pos.set(
78 | self.getValue(line, "X", current_pos.x),
79 | self.getValue(line, "Y", current_pos.y),
80 | self.getValue(line, "Z", current_pos.z)
81 | )
82 |
83 | # Track extrusion 'axis' position.
84 | last_e = current_e
85 | e_value = self.getValue(line, "E")
86 | if e_value:
87 | current_e = (current_e if relative_extrusion else 0) + e_value
88 |
89 | # Handle lines: Detect retractions and compensate relative if G1, potential retract-continue if G0.
90 | # and ignore Zhop lines
91 | if code_g == 1 and re.search("G1 F(\d*) Z(\d.*)", line) == None:
92 | if last_e > (current_e + 0.0001): # Account for floating point inaccuracies.
93 |
94 | # There is a retraction, each following G0 command needs to continue the retraction.
95 | is_active = True
96 | continue
97 |
98 | elif relative_extrusion and is_active:
99 |
100 | # If 'relative', the first G1 command after the total retraction will have to compensate more.
101 | travel, f = self._getTravelMove(lines[line_number], current_pos)
102 | lines[line_number] = self._travelMoveString(travel, f, to_compensate + e_value)
103 | to_compensate = 0.0
104 |
105 | # There is no retraction (see continue in the retract-clause) and everything else has been handled.
106 | is_active = False
107 | # If the line is G0 or a Zhop then maake changes----------------------------------------
108 | elif code_g == 0 or re.search("G1 F(\d*) Z(\d.*)", line) != None:
109 | if not is_active:
110 | continue
111 | #Ignore G1 Z hop lines and act on the G0 lines----------------------------
112 | if re.search("G1 F(\d*) Z(\d.*)", line) == None:
113 | # The retract-continue is active, so each G0 until the next extrusion needs to continue retraction.
114 | travel, f = self._getTravelMove(lines[line_number], current_pos)
115 | travel_length = (current_pos - last_pos).length()
116 | extra_retract = travel_length * extra_retraction_speed
117 | new_e = (0 if relative_extrusion else current_e) - extra_retract
118 | to_compensate += extra_retract
119 | current_e -= extra_retract
120 | lines[line_number] = self._travelMoveString(travel, f, new_e)
121 |
122 | new_layer = "\n".join(lines)
123 | data[layer_number] = new_layer
124 | return data
125 |
--------------------------------------------------------------------------------
/SlowZ.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: November 07, 2021
6 | #
7 | # Description: postprocessing script to slowdown the speed according to the Z height
8 | # https://marlinfw.org/docs/gcode/M220.html
9 | #
10 | #------------------------------------------------------------------------------------------------------------------------------------
11 | #
12 | # Version 1.0 07/11/2021 Add slow down using M220 S(SlowDown%)
13 | #
14 | #------------------------------------------------------------------------------------------------------------------------------------
15 |
16 | from ..Script import Script
17 | from UM.Logger import Logger
18 | from UM.Application import Application
19 | import re #To perform the search
20 | from enum import Enum
21 |
22 | __version__ = '1.0'
23 |
24 | class Section(Enum):
25 | """Enum for section type."""
26 |
27 | NOTHING = 0
28 | SKIRT = 1
29 | INNER_WALL = 2
30 | OUTER_WALL = 3
31 | INFILL = 4
32 | SKIN = 5
33 | SKIN2 = 6
34 |
35 | def is_begin_layer_line(line: str) -> bool:
36 | """Check if current line is the start of a layer section.
37 |
38 | Args:
39 | line (str): Gcode line
40 |
41 | Returns:
42 | bool: True if the line is the start of a layer section
43 | """
44 | return line.startswith(";LAYER:")
45 |
46 | def is_z_line(line: str) -> bool:
47 | """Check if current line is a Z line
48 |
49 | Args:
50 | line (str): Gcode line
51 |
52 | Returns:
53 | bool: True if the line is a Z line segment
54 | """
55 | return "G0" in line and "Z" in line and not "E" in line
56 |
57 | class SlowZ(Script):
58 | def __init__(self):
59 | super().__init__()
60 |
61 | def getSettingDataString(self):
62 | return """{
63 | "name": "SlowZ",
64 | "key": "SlowZ",
65 | "metadata": {},
66 | "version": 2,
67 | "settings":
68 | {
69 | "slowz_percentage":
70 | {
71 | "label": "Slow Z percentage",
72 | "description": "Positive value to slow the print as the z value rises up to this percentage.",
73 | "type": "float",
74 | "unit": "%",
75 | "default_value": 0,
76 | "minimum_value": "0",
77 | "maximum_value_warning": "50",
78 | "maximum_value": "90"
79 | },
80 | "slowz_height":
81 | {
82 | "label": "Slow Z height",
83 | "description": "Positive value to define the start height of the speed reduction.",
84 | "type": "float",
85 | "unit": "mm",
86 | "default_value": 0,
87 | "minimum_value": "0"
88 | },
89 | "lcdfeedback":
90 | {
91 | "label": "Display details on LCD",
92 | "description": "This setting will insert M117 gcode instructions, to display current modification in the G-Code is being used.",
93 | "type": "bool",
94 | "default_value": true
95 | }
96 | }
97 | }"""
98 |
99 | def execute(self, data):
100 |
101 | SlowZPercentage = float(self.getSettingValueByKey("slowz_percentage"))
102 | SlowZHeight = float(self.getSettingValueByKey("slowz_height"))
103 | UseLcd = self.getSettingValueByKey("lcdfeedback")
104 | # Logger.log('d', 'SlowZPercentage : {:f}'.format(SlowZPercentage))
105 | # Logger.log('d', 'SlowZHeight : {:f}'.format(SlowZHeight))
106 |
107 | idl=0
108 | currentz=0
109 |
110 | for layer in data:
111 | layer_index = data.index(layer)
112 |
113 | lines = layer.split("\n")
114 | for line in lines:
115 |
116 | if line.startswith(";LAYER_COUNT:"):
117 | # Logger.log("w", "found LAYER_COUNT %s", line[13:])
118 | layercount=int(line[13:])
119 |
120 | if is_begin_layer_line(line):
121 | line_index = lines.index(line)
122 | # Logger.log('d', 'layer_lines : {}'.format(line))
123 | currentlayer=int(line[7:])
124 | # Logger.log('d', 'currentlayer : {:d}'.format(currentlayer))
125 | if line.startswith(";LAYER:0"):
126 | currentz=0
127 | idl=1
128 |
129 | if idl == 1 and currentz >= SlowZHeight:
130 | idl=2
131 | startlayer=currentlayer
132 | # Logger.log("w", "Z Height %f", currentz)
133 |
134 | #Logger.log("w", "LAYER %s", line[7:])
135 | if idl >= 2 :
136 | speed_value = 100 - int(float(SlowZPercentage)*((currentlayer-startlayer)/(layercount-startlayer)))
137 | lines.insert(2,"M220 S" + str(speed_value))
138 | if UseLcd == True :
139 | lcd_gcode = "M117 Speed {:d}%".format(int(speed_value))
140 | lines.insert(3,lcd_gcode)
141 |
142 |
143 | if idl == 1 and is_z_line(line):
144 | searchZ = re.search(r"Z(\d*\.?\d*)", line)
145 | if searchZ:
146 | currentz=float(searchZ.group(1))
147 | # Logger.log('d', 'Current Z : {:f}'.format(currentz))
148 |
149 | result = "\n".join(lines)
150 | data[layer_index] = result
151 |
152 | return data
153 |
--------------------------------------------------------------------------------
/SpeedTower.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: February 29, 2020
6 | #
7 | # Description: postprocessing script to easily define a Speed Tower
8 | # Option for Speed
9 | # Acceleration
10 | # Jerk
11 | # Junction Deviation
12 | # Marlin Linear Advance
13 | # RepRap Pressure Advance
14 | #
15 | #------------------------------------------------------------------------------------------------------------------------------------
16 | #
17 | # Version 1.0 29/02/2020
18 | # Version 1.1 29/01/2021
19 | # Version 1.2 05/04/2021 by dotdash32(https://github.com/dotdash32) for Marlin Linear Advance & RepRap Pressure Advance
20 | # Version 1.3 18/04/2021 : ChangeLayerOffset += 2
21 | # Version 1.4 18/05/2021 : float
22 | # Version 1.5 14/02/2022 : Set Speed using M220 S
23 | # Version 1.6 15/02/2022 : Change Int for changelayeroffset & changelayer
24 | # Version 1.7 04/08/2022 : Restore and Save the Speed Factor in case of Speed option by using M220 B and M220 R
25 | # https://marlinfw.org/docs/gcode/M220.html
26 | #
27 | #------------------------------------------------------------------------------------------------------------------------------------
28 |
29 | from ..Script import Script
30 | from UM.Application import Application
31 | from UM.Logger import Logger
32 | import re #To perform the search
33 |
34 | __version__ = '1.7'
35 |
36 | class SpeedTower(Script):
37 | def __init__(self):
38 | super().__init__()
39 |
40 | def getSettingDataString(self):
41 | return """{
42 | "name": "SpeedTower",
43 | "key": "SpeedTower",
44 | "metadata": {},
45 | "version": 2,
46 | "settings":
47 | {
48 | "command": {
49 | "label": "Command",
50 | "description": "GCode Commande",
51 | "type": "enum",
52 | "options": {
53 | "speed": "Speed",
54 | "acceleration": "Acceleration",
55 | "jerk": "Jerk",
56 | "junction": "Junction Deviation",
57 | "marlinadv": "Marlin Linear",
58 | "rrfpresure": "RepRap Pressure"
59 | },
60 | "default_value": "acceleration"
61 | },
62 | "startValue":
63 | {
64 | "label": "Starting value",
65 | "description": "The starting value of the Tower.",
66 | "type": "float",
67 | "default_value": 8.0
68 | },
69 | "valueChange":
70 | {
71 | "label": "Value Increment",
72 | "description": "The value change of each block, can be positive or negative. I you want 50 and then 40, you need to set this to -10.",
73 | "type": "float",
74 | "default_value": 4.0
75 | },
76 | "changelayer":
77 | {
78 | "label": "Change Layer",
79 | "description": "How many layers needs to be printed before the value should be changed.",
80 | "type": "int",
81 | "default_value": 30,
82 | "minimum_value": 1,
83 | "maximum_value": 1000,
84 | "maximum_value_warning": 100
85 | },
86 | "changelayeroffset":
87 | {
88 | "label": "Change Layer Offset",
89 | "description": "If the print has a base, indicate the number of layers from which to start the changes.",
90 | "type": "int",
91 | "default_value": 4,
92 | "minimum_value": 0,
93 | "maximum_value": 1000,
94 | "maximum_value_warning": 100
95 | },
96 | "lcdfeedback":
97 | {
98 | "label": "Display details on LCD",
99 | "description": "This setting will insert M117 gcode instructions, to display current modification in the G-Code is being used.",
100 | "type": "bool",
101 | "default_value": true
102 | }
103 | }
104 | }"""
105 |
106 | def execute(self, data):
107 |
108 | UseLcd = self.getSettingValueByKey("lcdfeedback")
109 | Instruction = self.getSettingValueByKey("command")
110 | StartValue = float(self.getSettingValueByKey("startValue"))
111 | ValueChange = float(self.getSettingValueByKey("valueChange"))
112 | ChangeLayer = int(self.getSettingValueByKey("changelayer"))
113 | ChangeLayerOffset = int(self.getSettingValueByKey("changelayeroffset"))
114 | ChangeLayerOffset += 2 # Modification to take into account the numbering offset in Gcode
115 | # layer_index = 0 for initial Block 1= Start Gcode normaly first layer = 0
116 |
117 | CurrentValue = StartValue
118 | Command=""
119 | max_layer=9999
120 | idl=0
121 |
122 | for layer in data:
123 | layer_index = data.index(layer)
124 |
125 | lines = layer.split("\n")
126 | for line in lines:
127 | if line.startswith(";LAYER_COUNT:"):
128 | max_layer = int(line.split(":")[1]) # Recuperation Nb Layer Maxi
129 | # Logger.log('d', 'Max_layer : {}'.format(max_layer))
130 |
131 | if line.startswith(";LAYER:"):
132 | line_index = lines.index(line)
133 | # Logger.log('d', 'Instruction : {}'.format(Instruction))
134 | # Logger.log('d', 'layer_index : {}'.format(layer_index))
135 | # Logger.log('d', 'ChangeLayerOffset : {}'.format(ChangeLayerOffset))
136 |
137 | if (layer_index==ChangeLayerOffset):
138 | if (Instruction=='speed'):
139 | Command = "M220 B\nM220 S{:d}".format(int(CurrentValue))
140 | lcd_gcode = "M117 Speed S{:d}".format(int(CurrentValue))
141 | if (Instruction=='acceleration'):
142 | Command = "M204 S{:d}".format(int(CurrentValue))
143 | lcd_gcode = "M117 Acceleration S{:d}".format(int(CurrentValue))
144 | if (Instruction=='jerk'):
145 | Command = "M205 X{:d} Y{:d}".format(int(CurrentValue), int(CurrentValue))
146 | lcd_gcode = "M117 Jerk X{:d} Y{:d}".format(int(CurrentValue), int(CurrentValue))
147 | if (Instruction=='junction'):
148 | Command = "M205 J{:.3f}".format(float(CurrentValue))
149 | lcd_gcode = "M117 Junction J{:.3f}".format(float(CurrentValue))
150 | if (Instruction=='marlinadv'):
151 | Command = "M900 K{:.3f}".format(float(CurrentValue))
152 | lcd_gcode = "M117 Linear Advance K{:.3f}".format(float(CurrentValue))
153 | if (Instruction=='rrfpresure'):
154 | Command = "M572 D0 S{:.3f}".format(float(CurrentValue))
155 | lcd_gcode = "M117 Pressure Advance S{:.3f}".format(float(CurrentValue))
156 |
157 | lines.insert(line_index + 1, ";TYPE:CUSTOM LAYER")
158 | lines.insert(line_index + 2, Command)
159 | if UseLcd == True :
160 | lines.insert(line_index + 3, lcd_gcode)
161 |
162 | if ((layer_index-ChangeLayerOffset) % ChangeLayer == 0) and ((layer_index-ChangeLayerOffset)>0):
163 | CurrentValue += ValueChange
164 | if (Instruction=='speed'):
165 | Command = "M220 S{:d}".format(int(CurrentValue))
166 | lcd_gcode = "M117 Speed S{:d}".format(int(CurrentValue))
167 | if (Instruction=='acceleration'):
168 | Command = "M204 S{:d}".format(int(CurrentValue))
169 | lcd_gcode = "M117 Acceleration S{:d}".format(int(CurrentValue))
170 | if (Instruction=='jerk'):
171 | Command = "M205 X{:d} Y{:d}".format(int(CurrentValue), int(CurrentValue))
172 | lcd_gcode = "M117 Jerk X{:d} Y{:d}".format(int(CurrentValue), int(CurrentValue))
173 | if (Instruction=='junction'):
174 | Command = "M205 J{:.3f}".format(float(CurrentValue))
175 | lcd_gcode = "M117 Junction J{:.3f}".format(float(CurrentValue))
176 | if (Instruction=='marlinadv'):
177 | Command = "M900 K{:.3f}".format(float(CurrentValue))
178 | lcd_gcode = "M117 Linear Advance K{:.3f}".format(float(CurrentValue))
179 | if (Instruction=='rrfpresure'):
180 | Command = "M572 D0 S{:.3f}".format(float(CurrentValue))
181 | lcd_gcode = "M117 Pressure Advance S{:.3f}".format(float(CurrentValue))
182 |
183 | lines.insert(line_index + 1, ";TYPE:CUSTOM VALUE")
184 | lines.insert(line_index + 2, Command)
185 | if UseLcd == True :
186 | lines.insert(line_index + 3, lcd_gcode)
187 |
188 | # Logger.log('d', 'layer_index : {}'.format(layer_index))
189 | if (Instruction=='speed') and (layer_index==max_layer+1) :
190 | line_index = len(lines)
191 | # Logger.log('d', 'line_index : {}'.format(line_index))
192 | lines.insert(line_index, "M220 R\n")
193 |
194 | result = "\n".join(lines)
195 | data[layer_index] = result
196 |
197 | return data
198 |
--------------------------------------------------------------------------------
/SpoonOrder.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: March 13, 2023
6 | #
7 | # Description: SpoonOrder
8 | #
9 | #------------------------------------------------------------------------------------------------------------------------------------
10 | #
11 | # Version 1.0 13/03/2023 first prototype right now must be use with the relative extrusion activated
12 | #
13 | #------------------------------------------------------------------------------------------------------------------------------------
14 |
15 | from ..Script import Script
16 | from UM.Logger import Logger
17 | from UM.Application import Application
18 | from UM.Message import Message
19 | import re #To perform the search
20 | from UM.i18n import i18nCatalog # Translation
21 | catalog = i18nCatalog("cura")
22 |
23 | __version__ = '1.0'
24 |
25 | def is_begin_layer_line(line: str) -> bool:
26 | """Check if current line is the start of a layer section.
27 |
28 | Args:
29 | line (str): Gcode line
30 |
31 | Returns:
32 | bool: True if the line is the start of a layer section
33 | """
34 | return line.startswith(";LAYER:")
35 |
36 | def is_begin_skirt_line(line: str) -> bool:
37 | """Check if current line is the start of a SKIRT section.
38 |
39 | Args:
40 | line (str): Gcode line
41 |
42 | Returns:
43 | bool: True if the line is the start of a SKIRT section
44 | """
45 | return line.startswith(";TYPE:SKIRT")
46 |
47 | def is_begin_type_line(line: str) -> bool:
48 | """Check if current line is the start of a type section.
49 |
50 | Args:
51 | line (str): Gcode line
52 |
53 | Returns:
54 | bool: True if the line is the start of a type section
55 | """
56 | return line.startswith(";TYPE")
57 |
58 | def is_begin_mesh_line(line: str) -> bool:
59 | """Check if current line is the start of a new MESH.
60 |
61 | Args:
62 | line (str): Gcode line
63 |
64 | Returns:
65 | bool: True if the line is the start of a new MESH
66 | """
67 | return line.startswith(";MESH:")
68 |
69 | def is_e_line(line: str) -> bool:
70 | """Check if current line is a an Extruder line
71 |
72 | Args:
73 | line (str): Gcode line
74 |
75 | Returns:
76 | bool: True if the line is an Extruder line segment
77 | """
78 | return "G1" in line and "E" in line
79 |
80 | def is_relative_extrusion_line(line: str) -> bool:
81 | """Check if current line is a relative extrusion line
82 |
83 | Args:
84 | line (str): Gcode line
85 |
86 | Returns:
87 | bool: True if the line is a relative extrusion line
88 | """
89 | return "M83" in line
90 |
91 | def is_absolute_extrusion_line(line: str) -> bool:
92 | """Check if current line is an absolute extrusion line
93 |
94 | Args:
95 | line (str): Gcode line
96 |
97 | Returns:
98 | bool: True if the line is an absolute extrusion line
99 | """
100 | return "M82" in line
101 |
102 | def is_relative_instruction_line(line: str) -> bool:
103 | """Check if current line contain a M83 / G91 instruction
104 |
105 | Args:
106 | line (str): Gcode line
107 |
108 | Returns:
109 | bool: True contain a M83 / G91 instruction
110 | """
111 | return "G91" in line or "M83" in line
112 |
113 | def is_not_relative_instruction_line(line: str) -> bool:
114 | """Check if current line contain a M82 / G90 instruction
115 |
116 | Args:
117 | line (str): Gcode line
118 |
119 | Returns:
120 | bool: True contain a M82 / G90 instruction
121 | """
122 | return "G90" in line or "M82" in line
123 |
124 | def is_reset_extruder_line(line: str) -> bool:
125 | """Check if current line contain a G92 E0
126 |
127 | Args:
128 | line (str): Gcode line
129 |
130 | Returns:
131 | bool: True contain a G92 E0 instruction
132 | """
133 | return "G92" in line and "E0" in line
134 |
135 |
136 | class SpoonOrder(Script):
137 | def __init__(self):
138 | super().__init__()
139 |
140 | def getSettingDataString(self):
141 | return """{
142 | "name": "SpoonOrder",
143 | "key": "SpoonOrder",
144 | "metadata": {},
145 | "version": 2,
146 | "settings":
147 | {
148 | "layer":
149 | {
150 | "label": "Layer Analyse",
151 | "description": "Number of layer to analyse",
152 | "type": "int",
153 | "default_value": 1,
154 | "minimum_value": 0
155 | },
156 | "marker":
157 | {
158 | "label": "Spoon Identificator",
159 | "description": "Spoon Tab identificator",
160 | "type": "str",
161 | "default_value": "SpoonTab"
162 | }
163 | }
164 | }"""
165 |
166 | def execute(self, data):
167 |
168 | LayerAnalyse = int(self.getSettingValueByKey("layer"))
169 | LayerAnalyse -= 1
170 | Logger.log('d', "LayerAnalyse : {}".format(LayerAnalyse))
171 | Marker = str(self.getSettingValueByKey("marker"))
172 |
173 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
174 | relative_extrusion = bool(extrud[0].getProperty("relative_extrusion", "value"))
175 |
176 | if not relative_extrusion:
177 | Message("Must be in mode Relative extrusion", title = catalog.i18nc("@info:title", "Post Processing Spoon Order")).show()
178 | return data
179 |
180 | idl=0
181 |
182 | currentlayer=0
183 | CurrentE=0
184 | ResetE=0
185 | RelativeExtruder = False
186 | SaveE = -1
187 |
188 | for layer in data:
189 | layer_index = data.index(layer)
190 | # Logger.log('d', "Layer_index founded : {}".format(layer_index))
191 | lines_spoon = []
192 | lines_not_spoon = []
193 |
194 | lines = layer.split("\n")
195 | for line in lines:
196 | # line_index = lines.index(line)
197 | # Check if the line start with the Comment Char
198 | if is_relative_instruction_line(line) and line[0] != ";" :
199 | relative_extrusion = True
200 | # Logger.log('d', "Relative_extrusion founded : {}".format(line))
201 |
202 | # Check if the line start with the Comment Char
203 | if is_not_relative_instruction_line(line) and line[0] != ";" :
204 | relative_extrusion = False
205 |
206 | if is_reset_extruder_line(line) and line[0] != ";" :
207 | # Logger.log('d', "Reset_extruder :" + str(CurrentE))
208 | CurrentE = 0
209 | SaveE = 0
210 |
211 | if is_relative_extrusion_line(line):
212 | RelativeExtruder = True
213 |
214 | if is_absolute_extrusion_line(line):
215 | RelativeExtruder = False
216 |
217 | if line.startswith(";LAYER_COUNT:"):
218 | # Logger.log("w", "found LAYER_COUNT %s", line[13:])
219 | layercount=int(line[13:])
220 |
221 | if idl >= 1 and line.startswith(";TIME_ELAPSED:"):
222 | idl = 1
223 |
224 | # ;LAYER:X
225 | if is_begin_layer_line(line):
226 | Logger.log('d', "layer_lines : {}".format(line))
227 | currentlayer=int(line[7:])
228 | if currentlayer <= LayerAnalyse :
229 | # Logger.log('d', "CurrentLayer : {} / {}".format(currentlayer,LayerAnalyse))
230 | idl=2
231 | else:
232 | idl=0
233 |
234 | if idl >= 1 and is_begin_mesh_line(line) :
235 | if Marker in line :
236 | # Logger.log('d', "Marker mesh : {}".format(line))
237 | idl = 2
238 | else:
239 | # Logger.log('d', "Not Marker mesh : {}".format(line))
240 | idl = 1
241 |
242 | #---------------------------------------
243 | # Add the Spoon line to the spoon path
244 | #---------------------------------------
245 | if idl == 2 :
246 | lines_spoon.append(line)
247 |
248 | if idl == 1 :
249 | lines_not_spoon.append(line)
250 |
251 | # Logger.log('d', "idl : {}".format(idl))
252 | if idl>0 :
253 | result = ";BEGIN_OF_MODIFICATION"
254 | for line in lines_spoon:
255 | result += "\n"
256 | result += line
257 | for line in lines_not_spoon:
258 | result += "\n"
259 | result += line
260 | result += ";END_OF_MODIFICATION\n"
261 | else:
262 | result = "\n".join(lines)
263 |
264 | data[layer_index] = result
265 |
266 | return data
267 |
--------------------------------------------------------------------------------
/TempFanTower.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: 5axes
5 | # Date: January 13, 2020
6 | #
7 | # Description: postprocessing-script to easily define a TempTower and not use 10 changeAtZ-scripts
8 | #
9 | #
10 | #------------------------------------------------------------------------------------------------------------------------------------
11 | #
12 | # Version 1.1 9/01/2020
13 | # Version 1.2 11/01/2020 Fan modification after Bridge
14 | # Version 1.3 18/04/2021 : ChangeLayerOffset += 2
15 | # Version 1.4 18/05/2021 : ChangeLayerOffset
16 | #
17 | # Version 1.5 15/02/2022 Change Int for Layeroffset & changelayer
18 | # Version 1.6 05/08/2022 Option maintainbridgevalue
19 | #------------------------------------------------------------------------------------------------------------------------------------
20 |
21 | from ..Script import Script
22 | from UM.Application import Application
23 | from UM.Logger import Logger
24 |
25 | __version__ = '1.6'
26 |
27 | class TempFanTower(Script):
28 | def __init__(self):
29 | super().__init__()
30 |
31 | def getSettingDataString(self):
32 | return """{
33 | "name": "TempFanTower",
34 | "key": "TempFanTower",
35 | "metadata": {},
36 | "version": 2,
37 | "settings":
38 | {
39 | "startTemperature":
40 | {
41 | "label": "Starting Temperature",
42 | "description": "The starting temperature of the TempTower",
43 | "type": "int",
44 | "default_value": 220,
45 | "minimum_value": 100,
46 | "maximum_value": 300,
47 | "minimum_value_warning": 170,
48 | "maximum_value_warning": 280
49 | },
50 | "temperaturechange":
51 | {
52 | "label": "Temperature Increment",
53 | "description": "The temperature change of each block, can be positive or negative. If you want 220 and then 210, you need to set this to -10",
54 | "type": "int",
55 | "default_value": -5,
56 | "minimum_value": -100,
57 | "maximum_value": 100,
58 | "minimum_value_warning": -20,
59 | "maximum_value_warning": 20
60 | },
61 | "changelayer":
62 | {
63 | "label": "Change Layer",
64 | "description": "How many layers needs to be printed before the temperature should be changed",
65 | "type": "int",
66 | "default_value": 52,
67 | "minimum_value": 1,
68 | "maximum_value": 1000,
69 | "minimum_value_warning": 5,
70 | "maximum_value_warning": 100
71 | },
72 | "changelayeroffset":
73 | {
74 | "label": "Change Layer Offset",
75 | "description": "If the print has a base, indicate the number of layers from which to start the changes.",
76 | "type": "int",
77 | "default_value": 5,
78 | "minimum_value": 0,
79 | "maximum_value": 1000,
80 | "maximum_value_warning": 100
81 | },
82 | "usefanvalue":
83 | {
84 | "label": "Activate Fan Tower",
85 | "description": "Activate also a fan variation to create a Fan Tower",
86 | "type": "bool",
87 | "default_value": false
88 | },
89 | "fanchange":
90 | {
91 | "label": "Fan values in %",
92 | "description": "The fan speed change of each block, list value separated by a comma ';' ",
93 | "type": "str",
94 | "default_value": "100;40;0",
95 | "enabled": "usefanvalue"
96 | },
97 | "maintainbridgevalue":
98 | {
99 | "label": "Keep Bridge Fan Value",
100 | "description": "Don't Change the Bridge Fan value",
101 | "type": "bool",
102 | "default_value": false,
103 | "enabled": "usefanvalue"
104 | }
105 | }
106 | }"""
107 |
108 | def execute(self, data):
109 |
110 | startTemperature = self.getSettingValueByKey("startTemperature")
111 | temperaturechange = self.getSettingValueByKey("temperaturechange")
112 | changelayer = int(self.getSettingValueByKey("changelayer"))
113 | ChangeLayerOffset = int(self.getSettingValueByKey("changelayeroffset"))
114 | ChangeLayerOffset += 2 # Modification to take into account the numbering offset in Gcode
115 | # layer_index = 0 for initial Block 1= Start Gcode normaly first layer = 0
116 |
117 | fanvalues_str = self.getSettingValueByKey("fanchange")
118 | fanvalues = fanvalues_str.split(";")
119 | nbval = len(fanvalues) - 1
120 | usefan = False
121 |
122 | if (nbval>0):
123 | usefan = bool(self.getSettingValueByKey("usefanvalue"))
124 | bridgevalue = bool(self.getSettingValueByKey("maintainbridgevalue"))
125 | else:
126 | usefan = False
127 | bridgevalue = False
128 |
129 |
130 | currentTemperature = startTemperature
131 | currentfan = int((int(fanvalues[0])/100)*255) # 100% = 255 pour ventilateur
132 |
133 | idl=0
134 | afterbridge = False
135 |
136 | for layer in data:
137 | layer_index = data.index(layer)
138 |
139 | lines = layer.split("\n")
140 | for line in lines:
141 | if line.startswith("M106 S") and ((layer_index-ChangeLayerOffset)>0) and (usefan) :
142 | if afterbridge or not bridgevalue :
143 | line_index = lines.index(line)
144 | currentfan = int((int(fanvalues[idl])/100)*255) # 100% = 255 pour ventilateur
145 | lines[line_index] = "M106 S"+str(int(currentfan))+ " ; FAN MODI"
146 | afterbridge -= 1
147 | else :
148 | line_index = lines.index(line)
149 | afterbridge += 1
150 |
151 | if line.startswith("M107") and ((layer_index-ChangeLayerOffset)>0) and (usefan):
152 | afterbridge = 1
153 | line_index = lines.index(line)
154 |
155 | if line.startswith(";BRIDGE") :
156 | afterbridge = 0
157 | line_index = lines.index(line)
158 |
159 | if line.startswith(";LAYER:"):
160 | line_index = lines.index(line)
161 |
162 | if (layer_index==ChangeLayerOffset):
163 | lines.insert(line_index + 1, ";TYPE:CUSTOM LAYER")
164 | lines.insert(line_index + 2, "M104 S"+str(currentTemperature))
165 | idl=0
166 | if (usefan):
167 | currentfan = int((int(fanvalues[idl])/100)*255) # 100% = 255 pour ventilateur
168 | lines.insert(line_index + 3, "M106 S"+str(currentfan))
169 |
170 | if ((layer_index-ChangeLayerOffset) % changelayer == 0) and ((layer_index-ChangeLayerOffset)>0):
171 | if (usefan) and (idl < nbval):
172 | idl += 1
173 | currentfan = int((int(fanvalues[idl])/100)*255) # 100% = 255 pour ventilateur
174 | lines.insert(line_index + 1, ";TYPE:CUSTOM FAN")
175 | lines.insert(line_index + 2, "M106 S"+str(int(currentfan)))
176 | else:
177 | currentTemperature += temperaturechange
178 | lines.insert(line_index + 1, ";TYPE:CUSTOM TEMP")
179 | lines.insert(line_index + 2, "M104 S"+str(currentTemperature))
180 | if (usefan):
181 | # On repart à la valeur de départ
182 | idl = 0
183 | currentfan = int((int(fanvalues[idl])/100)*255) # 100% = 255 pour ventilateur
184 | lines.insert(line_index + 3, "M106 S"+str(int(currentfan)))
185 |
186 |
187 | result = "\n".join(lines)
188 | data[layer_index] = result
189 |
190 | return data
191 |
--------------------------------------------------------------------------------
/TemperatureTower.py:
--------------------------------------------------------------------------------
1 | # Created by Jakub Wietrzyk https://github.com/jaaaco
2 | # Based on https://github.com/asmaurya/Temperature-Tower-Plugin-for-Cura-4.-
3 |
4 | from typing import List
5 |
6 | from ..Script import Script
7 | from UM.Application import Application
8 |
9 | # Performs a search-and-replace on all g-code.
10 | class TemperatureTower(Script):
11 | def getSettingDataString(self):
12 | return """{
13 | "name": "Temperature Tower",
14 | "key": "TemperatureTower",
15 | "metadata": {},
16 | "version": 2,
17 | "settings":
18 | {
19 | "change_at":
20 | {
21 | "label": "Change Temp at",
22 | "description": "Whether to decrease temp at layer or height interval",
23 | "type": "enum",
24 | "options": {"height": "Height", "layer": "Layer No."},
25 | "default_value": "layer"
26 | },
27 | "height_inc":
28 | {
29 | "label": "Height Interval",
30 | "description": "At the increase of how many mm height does the temp decreases",
31 | "unit": "mm",
32 | "type": "float",
33 | "default_value": 5.0,
34 | "minimum_value": 0,
35 | "enabled": "change_at == 'height'"
36 | },
37 | "layer_inc":
38 | {
39 | "label": "Layer Interval",
40 | "description": "At the increase of how many layers does the temp decreases",
41 | "type": "int",
42 | "value": 1,
43 | "minimum_value": 0,
44 | "minimum_value_warning": "1",
45 | "enabled": "change_at == 'layer'"
46 | },
47 | "layer_start":
48 | {
49 | "label": "Start Layer",
50 | "description": "From which layer the temp decrease has to be started",
51 | "type": "int",
52 | "value": 1,
53 | "minimum_value": 0,
54 | "minimum_value_warning": "1",
55 | "enabled": "change_at == 'layer'"
56 | },
57 | "height_start":
58 | {
59 | "label": "Start Height (mm)",
60 | "description": "From which height the temp decrease has to be started",
61 | "type": "float",
62 | "value": 0.2,
63 | "unit": "mm",
64 | "minimum_value": 0.2,
65 | "minimum_value_warning": "1",
66 | "enabled": "change_at == 'height'"
67 | },
68 | "temperature_start":
69 | {
70 | "label": "Start Temperature",
71 | "description": "Initial temperature",
72 | "unit": "°C",
73 | "type": "int",
74 | "default_value": 200
75 | },
76 | "temperature_dec":
77 | {
78 | "label": "Temperature Decrement Step",
79 | "description": "Decrease temperature by this much with each increment",
80 | "unit": "°C",
81 | "type": "int",
82 | "default_value": 5
83 | }
84 | }
85 | }"""
86 |
87 | def execute(self, data: List[str]):
88 | new_temperature = self.getSettingValueByKey("temperature_start") + self.getSettingValueByKey("temperature_dec")
89 | comment = " ; Added by Temperature Tower Post Processing Plugin \n"
90 | layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height", "value")
91 | if self.getSettingValueByKey("change_at") == "layer":
92 | for layer in range(self.getSettingValueByKey("layer_start"), len(data)-2, self.getSettingValueByKey("layer_inc")):
93 | new_temperature -= self.getSettingValueByKey("temperature_dec")
94 | if new_temperature < 0:
95 | new_temperature = 0
96 | data[layer] = "\nM104 S" + str(new_temperature) + comment + data[layer]
97 | else:
98 | for layer in range(int(self.getSettingValueByKey("height_start") / layer_height), len(data)-2, int(self.getSettingValueByKey("height_inc") / layer_height)):
99 | new_temperature -= self.getSettingValueByKey("temperature_dec")
100 | if new_temperature < 0:
101 | new_temperature = 0
102 | data[layer] = "\nM104 S" + str(new_temperature) + comment + data[layer]
103 | return data
--------------------------------------------------------------------------------
/UPPostProcess.py:
--------------------------------------------------------------------------------
1 | from ..Script import Script
2 | import re
3 |
4 | # from cura.Settings.ExtruderManager import ExtruderManager
5 | # Issue History
6 | # Vesrion 1.0 - Inital Release
7 | # Version 2.0 - Fixed error with X and Y axis directions
8 | #.
9 |
10 |
11 | class UPPostProcess(Script):
12 |
13 | def __init__(self):
14 | super().__init__()
15 | self.Ypattern = re.compile("Y\d+")
16 | self.Zpattern = re.compile("Z\d+")
17 | self.Xpattern = re.compile("X\d+")
18 |
19 |
20 |
21 |
22 | def getSettingDataString(self):
23 | return """{
24 | "name":"Up PostProcess",
25 | "key": "UpPostProcess",
26 | "metadata": {},
27 | "version": 2,
28 | "settings": {
29 | "UP_profile":
30 | {
31 | "label": "Profile",
32 | "description": "Convert CURA to UP gcode",
33 | "type": "enum",
34 | "options": {
35 | "UP":"UP"
36 | },
37 | "default_value": "UP"
38 | },
39 | "UP_Zvalue":
40 | {
41 | "label": "Z offset in Up Studio",
42 | "description": " Z height when bed at nozzle",
43 | "type": "float",
44 | "default_value": 205
45 | }
46 | }
47 | }"""
48 |
49 | def execute(self, data: list):
50 |
51 | version = "0.1 C Jackson"
52 |
53 | self.UPProfile = self.getSettingValueByKey("UP_profile")
54 | self.UPZmax = self.getSettingValueByKey("UP_Zvalue")
55 | self.selectedTool = ""
56 | self.valveClosed = True
57 |
58 | index = 0
59 | for active_layer in data:
60 |
61 | self.output = ""
62 | #if index == 0:
63 | self.output += "; Selected profile: " + self.UPProfile + ", ZMax set at " + str(self.UPZmax)
64 | self.output += "; version " + version + "\n"
65 |
66 | lines = active_layer.split("\n")
67 | #self.output += "; Number of lines to be processed " + str(lines) +"\n"
68 | for line in lines:
69 | commentIndex = line.find(";")
70 | if commentIndex >= 0:
71 | comment = line[commentIndex + 1:]
72 | line = line[0:commentIndex]
73 | else:
74 | comment = ""
75 |
76 | if self.UPProfile == "UP":
77 | self.UPGcodeParse(line, comment)
78 | else:
79 | self.output += ";" + "\n"
80 |
81 | data[index] = self.output
82 | index += 1
83 | return data
84 | #
85 | # End of Main Loop
86 | #
87 | def UPGcodeParse(self, line, comment):
88 |
89 | hasY = re.search(self.Ypattern, line)
90 | hasZ = re.search(self.Zpattern, line)
91 | hasX = re.search(self.Xpattern, line)
92 | # There is 'Sxxx' in the line and second tool is selected and line doesn't contain both
93 | if hasX:
94 | line = line.replace("X","A") # Replace "Sxxx" with "Txxx "
95 | if hasY:
96 | line = line.replace("Y","B") # Replace "Sxxx" with "Txxx "
97 | line = line.replace("A","Y-") # Replace "Sxxx" with "Txxx "
98 | line = line.replace("B","X") # Replace "Sxxx" with "Txxx "
99 | if hasZ:
100 | #CBJ Pseudo Code needs to find the Zvlaue on the line i.e. Z0.3
101 | #CBJ then subtract this value from the UPZvalue provide by the user e.g. 205
102 | #CBJ replace theZvalue in the line with UPZvalue - Zvalue e.g. 204.7
103 | LayerZ = line.rpartition('Z')[-1]
104 | LayerZ = str(LayerZ)
105 | A = float(self.UPZmax)
106 | B = float(LayerZ)
107 | UPLayerZ = A-B
108 | UPLayerZ = str(UPLayerZ)
109 | line = line.partition('Z')[0] + "Z-" +UPLayerZ
110 |
111 | # Write the modifed line to file.
112 | if comment != "":
113 | self.output += line + ";" + comment + "\n"
114 | else:
115 | self.output += line + "\n"
116 |
117 |
--------------------------------------------------------------------------------
/ZMoveG0.py:
--------------------------------------------------------------------------------
1 | # ZMoveG0
2 | """
3 | ZMoveG0 for 3D prints.
4 |
5 | Z hop for every G0
6 |
7 | Author: 5axes
8 | Version: 1.0
9 | Version: 1.1 Remove some useless part of the Code.
10 |
11 | """
12 |
13 | import re #To perform the search
14 |
15 | from ..Script import Script
16 |
17 | from UM.Application import Application
18 | from UM.Logger import Logger
19 | from UM.Message import Message
20 | from UM.i18n import i18nCatalog
21 |
22 | catalog = i18nCatalog("cura")
23 |
24 | __version__ = '1.1'
25 |
26 | class ZMoveG0(Script):
27 | def getSettingDataString(self):
28 | return """{
29 | "name": "Z Move G0",
30 | "key": "ZMoveG0",
31 | "metadata": {},
32 | "version": 2,
33 | "settings":
34 | {
35 | "extruder_nb":
36 | {
37 | "label": "Extruder Id",
38 | "description": "Define extruder Id in case of multi extruders",
39 | "unit": "",
40 | "type": "int",
41 | "default_value": 1
42 | }
43 | }
44 | }"""
45 |
46 | ## -----------------------------------------------------------------------------
47 | #
48 | # Main Prog
49 | #
50 | ## -----------------------------------------------------------------------------
51 | def execute(self, data):
52 |
53 | current_z = 0
54 | Zr = "Z0"
55 | Zc = "Z0"
56 |
57 | extruder_id = self.getSettingValueByKey("extruder_nb")
58 | extruder_id = extruder_id -1
59 |
60 | # machine_extruder_count
61 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
62 | extruder_count = extruder_count-1
63 | if extruder_id>extruder_count :
64 | extruder_id=extruder_count
65 |
66 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
67 |
68 | # Get the Cura retraction_hop and speed_z_hop as Zhop parameter
69 | retraction_hop = float(extrud[extruder_id].getProperty("retraction_hop", "value"))
70 | speed_z_hop = int(extrud[extruder_id].getProperty("speed_z_hop", "value"))
71 | speed_z_hop = speed_z_hop * 60
72 |
73 | # Check if Z hop is desactivated
74 | retraction_hop_enabled= extrud[extruder_id].getProperty("retraction_hop_enabled", "value")
75 | if retraction_hop_enabled == True:
76 | #
77 | Logger.log('d', 'Mode Z Hop must not be activated')
78 | Message(catalog.i18nc("@message", "Mode Z Hop must not be activated"), title = catalog.i18nc("@info:title", "Post Processing")).show()
79 | return data
80 |
81 | In_G0 = False
82 | for layer_index, layer in enumerate(data):
83 | lines = layer.split("\n")
84 |
85 | for line_index, currentLine in enumerate(lines):
86 |
87 | if currentLine.startswith("G0") and "Z" in currentLine :
88 | searchZ = re.search(r"Z(\d*\.?\d*)", currentLine)
89 | if searchZ:
90 | current_z=float(searchZ.group(1))
91 | Zc = "Z"+searchZ.group(1)
92 |
93 | if currentLine.startswith("G0") and not In_G0 :
94 | Output_Z=current_z+retraction_hop
95 | outPutLine1 = "G1 F{} Z{:.3f}\n".format(speed_z_hop,Output_Z)
96 | # Logger.log('d', "Zc Zr : {} {}".format(Zc,Zr))
97 | Zr = "Z{:.3f}".format(Output_Z)
98 | currentLine=currentLine.replace(Zc, Zr)
99 | outPutLine = outPutLine1 + currentLine
100 | lines[line_index] = outPutLine
101 | In_G0 = True
102 |
103 | if currentLine.startswith("G1") and In_G0 :
104 | outPutLine2 = "G1 F{} Z{:.3f}\n".format(speed_z_hop,current_z)
105 | outPutLine = outPutLine2 + currentLine
106 | lines[line_index] = outPutLine
107 | In_G0 = False
108 | #
109 | # end of analyse
110 | #
111 |
112 | final_lines = "\n".join(lines)
113 | data[layer_index] = final_lines
114 | return data
115 |
--------------------------------------------------------------------------------
/ZMoveIroning.py:
--------------------------------------------------------------------------------
1 | # ZMoveIroning
2 | """
3 | ZMoveIroning for 3D prints.
4 |
5 | Z hop for ironing
6 |
7 | Author: 5axes
8 | Version: 1.0
9 | Version: 1.1 Remove some useless part of the Code.
10 |
11 | """
12 |
13 | import re #To perform the search
14 | from enum import Enum
15 | from ..Script import Script
16 |
17 | from UM.Application import Application
18 | from UM.Logger import Logger
19 | from UM.Message import Message
20 | from UM.i18n import i18nCatalog
21 |
22 | catalog = i18nCatalog("cura")
23 |
24 | __version__ = '1.1'
25 |
26 | class Section(Enum):
27 | """Enum for section type."""
28 |
29 | NOTHING = 0
30 | SKIRT = 1
31 | INNER_WALL = 2
32 | OUTER_WALL = 3
33 | INFILL = 4
34 | SKIN = 5
35 | SKIN2 = 6
36 |
37 |
38 | def is_extrusion_line(line: str) -> bool:
39 | """Check if current line is a standard printing segment.
40 |
41 | Args:
42 | line (str): Gcode line
43 |
44 | Returns:
45 | bool: True if the line is a standard printing segment
46 | """
47 | return "G1" in line and "X" in line and "Y" in line and "E" in line
48 |
49 | def is_not_extrusion_line(line: str) -> bool:
50 | """Check if current line is a rapid movement segment.
51 |
52 | Args:
53 | line (str): Gcode line
54 |
55 | Returns:
56 | bool: True if the line is a standard printing segment
57 | """
58 | return "G0" in line and "X" in line and "Y" in line and not "E" in line
59 |
60 | def is_begin_skin_segment_line(line: str) -> bool:
61 | """Check if current line is the start of an skin.
62 |
63 | Args:
64 | line (str): Gcode line
65 |
66 | Returns:
67 | bool: True if the line is the start of an skin section
68 | """
69 | return line.startswith(";TYPE:SKIN")
70 |
71 |
72 | class ZMoveIroning(Script):
73 | def getSettingDataString(self):
74 | return """{
75 | "name": "Z Move Ironing",
76 | "key": "ZMoveIroning",
77 | "metadata": {},
78 | "version": 2,
79 | "settings":
80 | {
81 | "extruder_nb":
82 | {
83 | "label": "Extruder Id",
84 | "description": "Define extruder Id in case of multi extruders",
85 | "unit": "",
86 | "type": "int",
87 | "default_value": 1
88 | }
89 | }
90 | }"""
91 |
92 | ## -----------------------------------------------------------------------------
93 | #
94 | # Main Prog
95 | #
96 | ## -----------------------------------------------------------------------------
97 |
98 | def execute(self, data):
99 |
100 | extruder_id = self.getSettingValueByKey("extruder_nb")
101 | extruder_id = extruder_id -1
102 |
103 | # machine_extruder_count
104 | extruder_count=Application.getInstance().getGlobalContainerStack().getProperty("machine_extruder_count", "value")
105 | extruder_count = extruder_count-1
106 | if extruder_id>extruder_count :
107 | extruder_id=extruder_count
108 |
109 | extrud = Application.getInstance().getGlobalContainerStack().extruderList
110 |
111 | retraction_hop = float(extrud[extruder_id].getProperty("retraction_hop", "value"))
112 | speed_z_hop = int(extrud[extruder_id].getProperty("speed_z_hop", "value"))
113 | speed_z_hop = speed_z_hop * 60
114 |
115 | ironingenabled = extrud[extruder_id].getProperty("ironing_enabled", "value")
116 | if ironingenabled == False:
117 | #
118 | Logger.log('d', 'Gcode must be generate with ironing mode')
119 | Message(catalog.i18nc("@message","Gcode must be generate with ironing mode"), title = catalog.i18nc("@info:title", "Post Processing")).show()
120 | return None
121 |
122 | retraction_hop_enabled= extrud[extruder_id].getProperty("retraction_hop_enabled", "value")
123 | if retraction_hop_enabled == True:
124 | #
125 | Logger.log('d', 'Mode Z Hop must not be activated')
126 | Message(catalog.i18nc("@message","Mode Z Hop must not be activated"), title = catalog.i18nc("@info:title", "Post Processing")).show()
127 | return None
128 |
129 | """Parse Gcode and modify infill portions with an extrusion width gradient."""
130 | currentSection = Section.NOTHING
131 | in_z_hop = False
132 |
133 |
134 | for layer_index, layer in enumerate(data):
135 | lines = layer.split("\n")
136 | for line_index, currentLine in enumerate(lines):
137 |
138 | if currentLine.startswith("G0") and "Z" in currentLine :
139 | searchZ = re.search(r"Z(\d*\.?\d*)", currentLine)
140 | if searchZ:
141 | current_z=float(searchZ.group(1))
142 |
143 | if is_begin_skin_segment_line(currentLine) and not (currentSection == Section.SKIN):
144 | currentSection = Section.SKIN
145 | continue
146 |
147 | # SKIN After SKIN = Ironing operation
148 | if currentSection == Section.SKIN:
149 | if is_begin_skin_segment_line(currentLine):
150 | currentSection = Section.SKIN2
151 | continue
152 | elif ";" in currentLine:
153 | currentSection = Section.NOTHING
154 |
155 | if currentSection == Section.SKIN2:
156 | if is_not_extrusion_line(currentLine):
157 |
158 | if not in_z_hop:
159 | in_z_hop = True
160 | Output_Z=current_z+retraction_hop
161 | outPutLine = "G1 F{} Z{:.3f}\n".format(speed_z_hop,Output_Z)
162 | outPutLine = outPutLine + currentLine
163 | lines[line_index] = outPutLine
164 | else:
165 | if in_z_hop:
166 | in_z_hop = False
167 | outPutLine = currentLine + "\nG1 F{} Z{:.3f}".format(speed_z_hop,current_z)
168 | lines[line_index] = outPutLine
169 |
170 | #
171 | # comment like ;MESH:NONMESH
172 | #
173 | if currentLine.startswith(";") :
174 | currentSection = Section.NOTHING
175 | #
176 | # end of analyse
177 | #
178 |
179 | final_lines = "\n".join(lines)
180 | data[layer_index] = final_lines
181 | return data
182 |
--------------------------------------------------------------------------------
/ZOffsetBrim.py:
--------------------------------------------------------------------------------
1 | # ZOffsetBrim
2 | """
3 | ZOffsetBrim for 3D prints.
4 |
5 | Z Offset just on Brim / Skirt
6 |
7 | Author: 5axes
8 | Version: 1.0
9 |
10 | """
11 |
12 | from ..Script import Script
13 | from UM.Logger import Logger
14 | import re #To perform the search
15 | from enum import Enum
16 | from UM.Message import Message
17 | from UM.i18n import i18nCatalog
18 | catalog = i18nCatalog("cura")
19 |
20 | __version__ = '1.0'
21 |
22 | class Section(Enum):
23 | """Enum for section type."""
24 |
25 | NOTHING = 0
26 | SKIRT = 1
27 | INNER_WALL = 2
28 | OUTER_WALL = 3
29 | INFILL = 4
30 | SKIN = 5
31 | SKIN2 = 6
32 |
33 |
34 |
35 | def is_begin_layer_line(line: str) -> bool:
36 | """Check if current line is the start of a layer section.
37 |
38 | Args:
39 | line (str): Gcode line
40 |
41 | Returns:
42 | bool: True if the line is the start of a layer section
43 | """
44 | return line.startswith(";LAYER:")
45 |
46 |
47 | def is_extrusion_line(line: str) -> bool:
48 | """Check if current line is a standard printing segment.
49 |
50 | Args:
51 | line (str): Gcode line
52 |
53 | Returns:
54 | bool: True if the line is a standard printing segment
55 | """
56 | return "G1" in line and "X" in line and "Y" in line and "E" in line
57 |
58 | def is_not_extrusion_line(line: str) -> bool:
59 | """Check if current line is a rapid movement with a Z component segment.
60 |
61 | Args:
62 | line (str): Gcode line
63 |
64 | Returns:
65 | bool: True if the line is a standard printing segment
66 | """
67 | return "G0" in line and "Z" in line and not "E" in line
68 |
69 | def is_retract_line(line: str) -> bool:
70 | """Check if current line is a speed movement with a Z component segment.
71 |
72 | Args:
73 | line (str): Gcode line
74 |
75 | Returns:
76 | bool: True if the line is a standard printing segment
77 | """
78 | return "G1" in line and "Z" in line and not "E" in line
79 |
80 | def is_begin_skirt_segment_line(line: str) -> bool:
81 | """Check if current line is the start of an skirt.
82 |
83 | Args:
84 | line (str): Gcode line
85 |
86 | Returns:
87 | bool: True if the line is the start of an skirt section
88 | """
89 | return line.startswith(";TYPE:SKIRT")
90 |
91 | def is_begin_segment_line(line: str) -> bool:
92 | """Check if current line is the start of a new Type section.
93 |
94 | Args:
95 | line (str): Gcode line
96 |
97 | Returns:
98 | bool: True if the line is the start of a new Type section
99 | """
100 | return line.startswith(";TYPE:")
101 |
102 |
103 | class ZOffsetBrim(Script):
104 | def getSettingDataString(self):
105 | return """{
106 | "name": "Z Offset Brim",
107 | "key": "ZOffsetBrim",
108 | "metadata": {},
109 | "version": 2,
110 | "settings":
111 | {
112 | "offset":
113 | {
114 | "label": "Offset Value",
115 | "description": "Define the Offset Value to the brim",
116 | "unit": "mm",
117 | "type": "float",
118 | "default_value": -0.03
119 | }
120 | }
121 | }"""
122 |
123 |
124 | ## -----------------------------------------------------------------------------
125 | #
126 | # Main Prog
127 | #
128 | ## -----------------------------------------------------------------------------
129 |
130 | def execute(self, data):
131 |
132 | v_offset = self.getSettingValueByKey("offset")
133 |
134 | currentSection = Section.NOTHING
135 | in_Z_offset = False
136 | current_z = 0
137 | current_Layer = 0
138 |
139 | for layer in data:
140 | layer_index = data.index(layer)
141 | lines = layer.split("\n")
142 | line_index = -1
143 |
144 | for currentLine in lines:
145 | #line_index = lines.index(currentLine)
146 | line_index += 1
147 |
148 | if is_begin_layer_line(currentLine) :
149 | current_Layer = int(currentLine.split(":")[1])
150 | current_Layer += 1
151 | continue
152 |
153 | if current_Layer == 1 :
154 | if is_not_extrusion_line(currentLine) or is_retract_line(currentLine) :
155 | # Logger.log('d', 'currentLine with Z : {}'.format(currentLine))
156 | # Logger.log('d', 'line_index : {}'.format(line_index))
157 | searchZ = re.search(r"Z(\d*\.?\d*)", currentLine)
158 | if searchZ :
159 | if not in_Z_offset :
160 | current_z=float(searchZ.group(1))
161 | else :
162 | save_Z=float(searchZ.group(1))
163 | Output_Z=save_Z+v_offset
164 | instructionZ="Z"+str(searchZ.group(1))
165 | outPutZ = "Z{:.3f}".format(Output_Z)
166 | # Logger.log('d', 'save_Z : {:.3f}'.format(save_Z))
167 | # Logger.log('d', 'line : {}'.format(currentLine))
168 | # Logger.log('d', 'line replace : {}'.format(currentLine.replace(instructionZ,outPutZ)))
169 | lines[line_index]=currentLine.replace(instructionZ,outPutZ)
170 |
171 | if is_begin_segment_line(currentLine) and currentSection == Section.SKIRT:
172 | if in_Z_offset:
173 | cLine = lines[line_index+1]
174 | # Logger.log('d', 'cLine : {}'.format(cLine))
175 | searchZ = re.search(r"Z(\d*\.?\d*)", cLine)
176 | if not searchZ :
177 | lines.insert(line_index + 1, "G0 Z{:.3f}".format(current_z))
178 | in_Z_offset = False
179 | currentSection = Section.NOTHING
180 | continue
181 |
182 | if is_begin_skirt_segment_line(currentLine) and currentSection != Section.SKIRT :
183 | currentSection = Section.SKIRT
184 | if not in_Z_offset:
185 | # cas avec Z Hop
186 | cLine = lines[line_index+1]
187 | searchZ = re.search(r"Z(\d*\.?\d*)", cLine)
188 | if searchZ :
189 | current_z=float(searchZ.group(1))
190 | else :
191 | Output_Z=current_z+v_offset
192 | lines.insert(line_index + 1, "G0 Z{:.3f}".format(Output_Z))
193 | in_Z_offset = True
194 | #
195 | # end of analyse
196 | #
197 |
198 | final_lines = "\n".join(lines)
199 | data[layer_index] = final_lines
200 | return data
201 |
--------------------------------------------------------------------------------
/images/AlterZhops.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/AlterZhops.png
--------------------------------------------------------------------------------
/images/ChangeFanValue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/ChangeFanValue.jpg
--------------------------------------------------------------------------------
/images/CheckFirstSpeed.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/CheckFirstSpeed.JPG
--------------------------------------------------------------------------------
/images/CheckFirstSpeedorigine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/CheckFirstSpeedorigine.png
--------------------------------------------------------------------------------
/images/CheckFirstSpeedresult.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/CheckFirstSpeedresult.png
--------------------------------------------------------------------------------
/images/ErrorZmoveG0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/ErrorZmoveG0.jpg
--------------------------------------------------------------------------------
/images/FanIroning.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/FanIroning.jpg
--------------------------------------------------------------------------------
/images/FanWithBridgeValue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/FanWithBridgeValue.jpg
--------------------------------------------------------------------------------
/images/FanWithoutBridgeValue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/FanWithoutBridgeValue.jpg
--------------------------------------------------------------------------------
/images/GcodeDocumentation.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/GcodeDocumentation.jpg
--------------------------------------------------------------------------------
/images/InfillLast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/InfillLast.png
--------------------------------------------------------------------------------
/images/InhibFan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/InhibFan.jpg
--------------------------------------------------------------------------------
/images/InsertAtLayerNumber.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/InsertAtLayerNumber.jpg
--------------------------------------------------------------------------------
/images/NotDetectedFanIroning.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/NotDetectedFanIroning.jpg
--------------------------------------------------------------------------------
/images/PrintInfos.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/PrintInfos.jpg
--------------------------------------------------------------------------------
/images/SpoonOrder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/SpoonOrder.png
--------------------------------------------------------------------------------
/images/ZmoveG0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/ZmoveG0.jpg
--------------------------------------------------------------------------------
/images/ZmoveIroning.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/ZmoveIroning.jpg
--------------------------------------------------------------------------------
/images/benchy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/benchy.jpg
--------------------------------------------------------------------------------
/images/commentGcode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/commentGcode.jpg
--------------------------------------------------------------------------------
/images/fastfirstinfill.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/fastfirstinfill.jpg
--------------------------------------------------------------------------------
/images/gcode_temptower.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/gcode_temptower.jpg
--------------------------------------------------------------------------------
/images/gradient.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/gradient.jpg
--------------------------------------------------------------------------------
/images/gradient2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/gradient2.jpg
--------------------------------------------------------------------------------
/images/gradient3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/gradient3.jpg
--------------------------------------------------------------------------------
/images/multibrim.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/multibrim.jpg
--------------------------------------------------------------------------------
/images/origine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/origine.png
--------------------------------------------------------------------------------
/images/plugins.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/plugins.jpg
--------------------------------------------------------------------------------
/images/result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/result.png
--------------------------------------------------------------------------------
/images/retract-tower.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/retract-tower.jpg
--------------------------------------------------------------------------------
/images/slowz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/slowz.jpg
--------------------------------------------------------------------------------
/images/slowzsettings.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/slowzsettings.jpg
--------------------------------------------------------------------------------
/images/speedtower.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/speedtower.jpg
--------------------------------------------------------------------------------
/images/tempfan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5axes/Cura-Postprocessing-Scripts/7298def59db28e7779affe80d6440c0d6cdb4d90/images/tempfan.jpg
--------------------------------------------------------------------------------
/startHeatingAtPercentage.py:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------------------------------------------------------------
2 | #
3 | # Cura PostProcessing Script
4 | # Author: Ricardo Ortega
5 | # Date: March 01, 2024
6 | #
7 | # Description: Modify M190 line
8 | #
9 | #
10 | #------------------------------------------------------------------------------------------------------------------------------------
11 | #
12 | # Version 1.0 01/03/2024 Change the M190 temperature to a percentage to start heating the nozzle
13 | #
14 | #------------------------------------------------------------------------------------------------------------------------------------
15 |
16 | from ..Script import Script
17 | from UM.Logger import Logger
18 | from UM.Application import Application
19 |
20 | from enum import Enum
21 |
22 | __version__ = '1.0'
23 |
24 | class Section(Enum):
25 | """Enum for section type."""
26 |
27 | NOTHING = 0
28 | SKIRT = 1
29 | INNER_WALL = 2
30 | OUTER_WALL = 3
31 | INFILL = 4
32 | SKIN = 5
33 | SKIN2 = 6
34 |
35 |
36 | class startHeatingAtPercentage(Script):
37 | def __init__(self):
38 | super().__init__()
39 |
40 | def getSettingDataString(self):
41 | return """{
42 | "name": "startHeatingAtPercentage",
43 | "key": "startHeatingAtPercentage",
44 | "metadata": {},
45 | "version": 2,
46 | "settings":
47 | {
48 | "bedTempPercentage":
49 | {
50 | "label": "Bed temperature percentage to start heating the nozzle",
51 | "description": "What is the percentage of bed heating to start heating the nozzle.",
52 | "type": "float",
53 | "unit": "%",
54 | "default_value": 100,
55 | "minimum_value": "50",
56 | "maximum_value": "100"
57 | }
58 | }
59 | }"""
60 |
61 | def execute(self, data):
62 |
63 | bedTempPercentage = float(self.getSettingValueByKey("bedTempPercentage"))
64 | OnlyFirst=True
65 | for layer in data:
66 | layer_index = data.index(layer)
67 |
68 | lines = layer.split("\n")
69 | resLines=[]
70 | for line in lines:
71 |
72 | if ("M190" in line) and OnlyFirst:
73 | temp=line.split("S")
74 | if(len(temp)==2):
75 | percTemp=int((float(temp[1])*bedTempPercentage)/100)
76 | resLines.append(f"M190 S{percTemp}")
77 | OnlyFirst=False
78 | else:
79 | resLines.append(line)
80 |
81 |
82 |
83 |
84 |
85 | result = "\n".join(resLines)
86 | data[layer_index] = result
87 |
88 | return data
--------------------------------------------------------------------------------