printrun-src/printrun/gcodeplater.py

changeset 15
0bbb006204fc
child 46
cce0af6351f0
equal deleted inserted replaced
14:51bf56ba3c10 15:0bbb006204fc
1 #!/usr/bin/env python
2
3 # This file is part of the Printrun suite.
4 #
5 # Printrun is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Printrun is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Printrun. If not, see <http://www.gnu.org/licenses/>.
17
18 # Set up Internationalization using gettext
19 # searching for installed locales on /usr/share; uses relative folder if not found (windows)
20 from .utils import install_locale, get_home_pos
21 install_locale('pronterface')
22
23 import wx
24 import sys
25 import os
26 import time
27 import types
28 import re
29 import math
30 import logging
31
32 from printrun import gcoder
33 from printrun.objectplater import make_plater, PlaterPanel
34 from printrun.gl.libtatlin import actors
35 import printrun.gui.viz # NOQA
36 from printrun import gcview
37
38 def extrusion_only(gline):
39 return gline.e is not None \
40 and (gline.x, gline.y, gline.z) == (None, None, None)
41
42 # Custom method for gcoder.GCode to analyze & output gcode in a single call
43 def gcoder_write(self, f, line, store = False):
44 f.write(line)
45 self.append(line, store = store)
46
47 rewrite_exp = re.compile("(%s)" % "|".join(["X([-+]?[0-9]*\.?[0-9]*)",
48 "Y([-+]?[0-9]*\.?[0-9]*)"]))
49
50 def rewrite_gline(centeroffset, gline, cosr, sinr):
51 if gline.is_move and (gline.x is not None or gline.y is not None):
52 if gline.relative:
53 xc = yc = 0
54 cox = coy = 0
55 if gline.x is not None:
56 xc = gline.x
57 if gline.y is not None:
58 yc = gline.y
59 else:
60 xc = gline.current_x + centeroffset[0]
61 yc = gline.current_y + centeroffset[1]
62 cox = centeroffset[0]
63 coy = centeroffset[1]
64 new_x = "X%.04f" % (xc * cosr - yc * sinr - cox)
65 new_y = "Y%.04f" % (xc * sinr + yc * cosr - coy)
66 new = {"X": new_x, "Y": new_y}
67 new_line = rewrite_exp.sub(lambda ax: new[ax.group()[0]], gline.raw)
68 new_line = new_line.split(";")[0]
69 if gline.x is None: new_line += " " + new_x
70 if gline.y is None: new_line += " " + new_y
71 return new_line
72 else:
73 return gline.raw
74
75 class GcodePlaterPanel(PlaterPanel):
76
77 load_wildcard = _("GCODE files (*.gcode;*.GCODE;*.g)") + "|*.gcode;*.gco;*.g"
78 save_wildcard = _("GCODE files (*.gcode;*.GCODE;*.g)") + "|*.gcode;*.gco;*.g"
79
80 def prepare_ui(self, filenames = [], callback = None,
81 parent = None, build_dimensions = None,
82 circular_platform = False, antialias_samples = 0):
83 super(GcodePlaterPanel, self).prepare_ui(filenames, callback, parent, build_dimensions)
84 viewer = gcview.GcodeViewPanel(self, build_dimensions = self.build_dimensions,
85 antialias_samples = antialias_samples)
86 self.set_viewer(viewer)
87 self.platform = actors.Platform(self.build_dimensions,
88 circular = circular_platform)
89 self.platform_object = gcview.GCObject(self.platform)
90
91 def get_objects(self):
92 return [self.platform_object] + self.models.values()
93 objects = property(get_objects)
94
95 def load_file(self, filename):
96 gcode = gcoder.GCode(open(filename, "rU"),
97 get_home_pos(self.build_dimensions))
98 model = actors.GcodeModel()
99 if gcode.filament_length > 0:
100 model.display_travels = False
101 generator = model.load_data(gcode)
102 generator_output = generator.next()
103 while generator_output is not None:
104 generator_output = generator.next()
105 obj = gcview.GCObject(model)
106 obj.offsets = [self.build_dimensions[3], self.build_dimensions[4], 0]
107 obj.gcode = gcode
108 obj.dims = [gcode.xmin, gcode.xmax,
109 gcode.ymin, gcode.ymax,
110 gcode.zmin, gcode.zmax]
111 obj.centeroffset = [-(obj.dims[1] + obj.dims[0]) / 2,
112 -(obj.dims[3] + obj.dims[2]) / 2,
113 0]
114 self.add_model(filename, obj)
115 wx.CallAfter(self.Refresh)
116
117 def done(self, event, cb):
118 if not os.path.exists("tempgcode"):
119 os.mkdir("tempgcode")
120 name = "tempgcode/" + str(int(time.time()) % 10000) + ".gcode"
121 self.export_to(name)
122 if cb is not None:
123 cb(name)
124 if self.destroy_on_done:
125 self.Destroy()
126
127 # What's hard in there ?
128 # 1) [x] finding the order in which the objects are printed
129 # 2) [x] handling layers correctly
130 # 3) [x] handling E correctly
131 # 4) [x] handling position shifts: should we either reset absolute 0 using
132 # G92 or should we rewrite all positions ? => we use G92s
133 # 5) [ ] handling the start & end gcode properly ?
134 # 6) [x] handling of current tool
135 # 7) [x] handling of Z moves for sequential printing (don't lower Z before
136 # reaching the next object print area)
137 # 8) [x] handling of absolute/relative status
138 # Initial implementation should just print the objects sequentially,
139 # but the end goal is to have a clean per-layer merge
140 def export_to(self, name):
141 return self.export_combined(name)
142 return self.export_sequential(name)
143
144 def export_combined(self, name):
145 models = self.models.values()
146 last_real_position = None
147 # Sort models by Z max to print smaller objects first
148 models.sort(key = lambda x: x.dims[-1])
149 alllayers = []
150 for (model_i, model) in enumerate(models):
151 def add_offset(layer):
152 return layer.z + model.offsets[2] if layer.z is not None else layer.z
153 alllayers += [(add_offset(layer), model_i, layer_i)
154 for (layer_i, layer) in enumerate(model.gcode.all_layers) if layer]
155 alllayers.sort()
156 laste = [0] * len(models)
157 lasttool = [0] * len(models)
158 lastrelative = [False] * len(models)
159 with open(name, "w") as f:
160 analyzer = gcoder.GCode(None, get_home_pos(self.build_dimensions))
161 analyzer.write = types.MethodType(lambda self, line: gcoder_write(self, f, line), analyzer)
162 for (layer_z, model_i, layer_i) in alllayers:
163 model = models[model_i]
164 layer = model.gcode.all_layers[layer_i]
165 r = math.radians(model.rot)
166 o = model.offsets
167 co = model.centeroffset
168 offset_pos = last_real_position if last_real_position is not None else (0, 0, 0)
169 analyzer.write("; %f %f %f\n" % offset_pos)
170 trans = (- (o[0] + co[0]),
171 - (o[1] + co[1]),
172 - (o[2] + co[2]))
173 trans_wpos = (offset_pos[0] + trans[0],
174 offset_pos[1] + trans[1],
175 offset_pos[2] + trans[2])
176 analyzer.write("; GCodePlater: Model %d Layer %d at Z = %s\n" % (model_i, layer_i, layer_z))
177 if lastrelative[model_i]:
178 analyzer.write("G91\n")
179 else:
180 analyzer.write("G90\n")
181 if analyzer.current_tool != lasttool[model_i]:
182 analyzer.write("T%d\n" % lasttool[model_i])
183 analyzer.write("G92 X%.5f Y%.5f Z%.5f\n" % trans_wpos)
184 analyzer.write("G92 E%.5f\n" % laste[model_i])
185 for l in layer:
186 if l.command != "G28" and (l.command != "G92" or extrusion_only(l)):
187 if r == 0:
188 analyzer.write(l.raw + "\n")
189 else:
190 analyzer.write(rewrite_gline(co, l, math.cos(r), math.sin(r)) + "\n")
191 # Find the current real position & E
192 last_real_position = analyzer.current_pos
193 laste[model_i] = analyzer.current_e
194 lastrelative[model_i] = analyzer.relative
195 lasttool[model_i] = analyzer.current_tool
196 logging.info(_("Exported merged G-Codes to %s") % name)
197
198 def export_sequential(self, name):
199 models = self.models.values()
200 last_real_position = None
201 # Sort models by Z max to print smaller objects first
202 models.sort(key = lambda x: x.dims[-1])
203 with open(name, "w") as f:
204 for model_i, model in enumerate(models):
205 r = math.radians(model.rot)
206 o = model.offsets
207 co = model.centeroffset
208 offset_pos = last_real_position if last_real_position is not None else (0, 0, 0)
209 trans = (- (o[0] + co[0]),
210 - (o[1] + co[1]),
211 - (o[2] + co[2]))
212 trans_wpos = (offset_pos[0] + trans[0],
213 offset_pos[1] + trans[1],
214 offset_pos[2] + trans[2])
215 f.write("; GCodePlater: Model %d\n" % model_i)
216 f.write("G90\n")
217 f.write("G92 X%.5f Y%.5f Z%.5f E0\n" % trans_wpos)
218 f.write("G1 X%.5f Y%.5f" % (-co[0], -co[1]))
219 for l in model.gcode:
220 if l.command != "G28" and (l.command != "G92" or extrusion_only(l)):
221 if r == 0:
222 f.write(l.raw + "\n")
223 else:
224 f.write(rewrite_gline(co, l, math.cos(r), math.sin(r)) + "\n")
225 # Find the current real position
226 for i in xrange(len(model.gcode) - 1, -1, -1):
227 gline = model.gcode.lines[i]
228 if gline.is_move:
229 last_real_position = (- trans[0] + gline.current_x,
230 - trans[1] + gline.current_y,
231 - trans[2] + gline.current_z)
232 break
233 logging.info(_("Exported merged G-Codes to %s") % name)
234
235 GcodePlater = make_plater(GcodePlaterPanel)
236
237 if __name__ == '__main__':
238 app = wx.App(False)
239 main = GcodePlater(filenames = sys.argv[1:])
240 for fn in main.filenames:
241 main.load_file(fn)
242 main.filenames = None
243 main.autoplate()
244 main.export_to("gcodeplate___test.gcode")
245 raise SystemExit
246 main.Show()
247 app.MainLoop()

mercurial