forked from gargansa/MELT
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CoreLibrary.py
177 lines (161 loc) · 6.38 KB
/
CoreLibrary.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
################################################################################
#
# MELT Core Library
#
# Methods to facilitate processing gcode.
#
# Usage:
# Creating a mix:
# mix = Miso.Mix([0.5,0.5,0.5,0.5])
#
# Configuring a tool:
# tool = Miso.Tool([mix])
# Miso.setToolConfig(0, tool) # sets this mix for tool 0
#
# Configuring a gradient:
# gradientStart = Miso.Mix([0.1,0.1,0.1,0.1], 0.25)
# gradientStop = Miso.Mix([0.9,0,0,0], 0.75)
# tool = Miso.Tool([gradientStart, gradientStop])
# Miso.setToolConfig(2, tool) # sets this gradient for tool 2
#
# Converting gcode:
# maxZHeight = maxHeightOfPrint
# newcode = Miso.fromGcode(gcode, maxZHeight)
#
################################################################################
import re
class Miso:
# Hash of ToolConfigurations
# Allows extruder mixes to be assigned to different tools
_toolConfigs = {}
@staticmethod
def setToolConfig(toolId, toolConfig):
Miso._toolConfigs[toolId] = toolConfig
@staticmethod
def getToolConfig(toolId):
if toolId in Miso._toolConfigs:
return Miso._toolConfigs[toolId]
return Miso.Tool() #default
# Forward-reading modification of gcode here
# tracks tool changes, z changes, and relative / absolute changes
# When an extrusion command is found and any of this info has changed
# then a new mix command is written
@staticmethod
def fromGcode(gcode, zmax):
tool = 0
toolHistory = tool
zpos = 0
zposHistory = zpos
relative = False
relativeHistory = relative
newcode = ''
for line in gcode:
tool = Miso.Gcode.updateTool(line, tool)
zpos = Miso.Gcode.updateZ(line, zpos, relative)
relative = Miso.Gcode.updateRelative(line, relative)
isDirty = tool != toolHistory or zpos != zposHistory or relative != relativeHistory
if isDirty and Miso.Gcode.isExtrude(line):
toolHistory = tool
zposHistory = zpos
relativeHistory = relative
newcode += Miso.Gcode.formatMix(tool, zpos, zmax) + "\n"
newcode += line + '\n'
return newcode
# Miso.Tool
# Mix and gradient information for a specific tool
# Example:
# toolConfig = Miso.Tool([mix1, mix2, ...])
class Tool:
def __init__(self, stops=None):
stops = stops or [Miso.Mix()]
self.stops = {}
for stop in stops:
self.stops[stop.zstop] = stop.mix
# Miso.Mix
# Mix information for a single stop (layer)
# Z is expressed in percentage (0 to 1)
# extruders is an array of percentages (0 to 1)
class Mix:
def __init__(self, mix=[1], zstop=0):
self.mix = mix
self.zstop = zstop
# Miso.Gcode
# Methods that help read and generate gcode
class Gcode:
_movecodes = re.compile('^\\s*(G0|G1).+Z(?P<distance>\\d+)\\b')
_extrudecodes = re.compile('^\\s*(G0|G1|G2|G3).+(E|F)\\d+\\b')
_toolcodes = re.compile('^\\s*T(?P<toolid>\\d+)\\b')
_absolutecodes = re.compile('^\\s*G91\\b')
_relativecodes = re.compile('^\\s*G90\\b')
@staticmethod
def updateRelative(line, current):
if Miso.Gcode._relativecodes.match(line):
return True
if Miso.Gcode._absolutecodes.match(line):
return False
return current
@staticmethod
def updateTool(line, current):
match = Miso.Gcode._toolcodes.search(line)
if match:
return int(match.group('toolid'))
return current
@staticmethod
def updateZ(line, current, relative):
match = Miso.Gcode._movecodes.search(line)
if match and relative:
change = float(match.group('distance'))
return current + change
if match:
return float(match.group('distance'))
return current
@staticmethod
def isExtrude(line):
return Miso.Gcode._extrudecodes.match(line)
@staticmethod
def formatMix(tool, zpos, zmax):
index = zpos / zmax
mix = Miso.Gcode._calcMix(index, tool)
for i in range(len(mix)):
mix[i] = Miso.Gcode._formatNumber(mix[i])
return 'M567 P' + str(tool) + ' E' + ':'.join(mix)
@staticmethod
def _calcMix(index, tool):
segment = Miso.Gcode._calcSegment(index, tool)
srange = segment.keys()
if len(srange) == 0:
return [1]
if len(srange) == 1:
return segment[srange[0]]
index = (index - min(srange[0], srange[1])) / (max(srange[0], srange[1])-min(srange[0], srange[1]))
mix = []
start = segment[min(srange[0], srange[1])]
end = segment[max(srange[0], srange[1])]
for extruder in range(max(len(start), len(end))):
svalue = len(start) < extruder and start[extruder] or 0
evalue = len(end) < extruder and end[extruder] or 0
mix.append((evalue - svalue) * index + svalue)
return mix
@staticmethod
def _calcSegment(index, tool): # NOTE: this will allow mixes that total more than 1
stops = Miso.getToolConfig(tool).stops
start = None
end = None
for stop in stops.keys(): # TODO: If stop is 0 there will be a bug
start = stop <= index and (start != None and max(start, stop) or stop) or start
end = stop >= index and (end != None and max(end, stop) or stop) or end
segment = {}
if start:
segment[start] = stops[start]
if end:
segment[end] = stops[end]
return segment
@staticmethod
def _formatNumber(value):
value = str(value).strip()
if re.match('^\\.', value):
value = '0' + value
filter = re.search('\\d+(\\.\\d{1,2})?', value)
if not filter:
return '0'
return filter.string[filter.start():filter.end()]