31 STEPS_PIXEL = MM_PIXEL * 80 # mine is 80 steps/mm on XY |
31 STEPS_PIXEL = MM_PIXEL * 80 # mine is 80 steps/mm on XY |
32 |
32 |
33 # FOR HPGL: |
33 # FOR HPGL: |
34 SCALE_FACTOR = 1.0 / 40.0 # 40 plotter units |
34 SCALE_FACTOR = 1.0 / 40.0 # 40 plotter units |
35 |
35 |
|
36 # GENERAL HEADER AND FOOTER GCODE |
|
37 GCODE_HEAD = """ |
|
38 ; GCode generated by laser.py pronterface library (marlin code flavour) |
|
39 ; 2015/2016 by NeoSoft - Malte Bayer |
|
40 |
|
41 G21 ; Metric |
|
42 ; We assume Z is in focus height and laser head is focus at bottom left of image! |
|
43 G92 X0 Y0 E0; set zero position - new origin |
|
44 G90 ; absolute positioning |
|
45 M82 ; Set extruder (laser) to absolute positioning |
|
46 M201 X1000 Y1000 E1000 ; Set acceleration |
|
47 M203 X1000 Y1000 Z4 E1000 ; Set max feedrate |
|
48 M209 S0 ; disable firmware retraction, we dont want to burn holes... |
|
49 M302 ; Allow cold extrudes - doesnt matter because we hack the extruder physically off with the M571 E mod |
|
50 M571 S1 E1 ; Activate Laser output on extrusion, but block real motor movement! |
|
51 G0 X0 Y0 F%d ; Set moving speed TRAVEL_SPEED |
|
52 G1 X0 Y0 F%d ; Set linear engraving speed ENGRAVE_SPEED |
|
53 |
|
54 """ % (TRAVEL_SPEED, ENGRAVE_SPEED) |
|
55 |
|
56 GCODE_FOOT = """M400 ; Wait for all moves to finish |
|
57 M571 S0 E0 |
|
58 M42 P28 S0 ; Force laser off! |
|
59 M501 ; undo all settings made |
|
60 """ |
36 |
61 |
37 from PIL import Image |
62 from PIL import Image |
38 import sys |
63 import sys |
39 |
64 |
40 # Imports for SVG |
65 # Imports for SVG |
45 |
70 |
46 |
71 |
47 class Lasercutter: |
72 class Lasercutter: |
48 """ |
73 """ |
49 Lasercutter methods |
74 Lasercutter methods |
50 parameters: log = logger function (accepts a string) |
75 parameters: log = logger function (fuction has to accept a string) |
51 """ |
76 """ |
52 def __init__(self, pronterwindow = None): |
77 def __init__(self, pronterwindow = None): |
53 if pronterwindow: |
78 if pronterwindow: |
54 self.pronterwindow = pronterwindow |
79 self.pronterwindow = pronterwindow |
55 self.log = pronterwindow.log |
80 self.log = pronterwindow.log |
|
81 self.pronterwindow.clear_log(None) |
56 else: |
82 else: |
57 self.pronterwindow = None |
83 self.pronterwindow = None |
58 self.log = lambda : None |
84 self.log = lambda : None |
59 self.log("\nLasercutter library initialized resolution: %f mm per pixel" % MM_PIXEL) |
85 self.log("Lasercutter library initialized\n%d DPI (%f mm/pixel)" % (DPI, MM_PIXEL)) |
60 if STEPS_PIXEL <= 5: |
86 if STEPS_PIXEL <= 5: |
61 self.log("WARNING: STEPS PER PIXEL NEEDS TO BE > 5 (otherwise marlin joins lines): %f" % STEPS_PIXEL) |
87 self.log("WARNING: STEPS PER PIXEL NEEDS TO BE > 5 (otherwise marlin joins lines): %f" % STEPS_PIXEL) |
|
88 self.log("Travel/Engrave speed: %d mm/sec, %d mm/sec" % ( |
|
89 TRAVEL_SPEED / 60, ENGRAVE_SPEED / 60) ) |
|
90 self.log("") |
62 |
91 |
63 |
92 |
64 def pixel2bit(self, pixel, threshold=128): |
93 def pixel2bit(self, pixel, threshold=128): |
65 """Convert the pixel value to a bit.""" |
94 """Convert the pixel value to a bit.""" |
66 # some really weird stuff here ;-P |
95 # some really weird stuff here ;-P |
101 self.log("size: %d x %d pixels" % im.size) |
130 self.log("size: %d x %d pixels" % im.size) |
102 |
131 |
103 pix = im.load() |
132 pix = im.load() |
104 |
133 |
105 fo = open(filename + ".g", "w") |
134 fo = open(filename + ".g", "w") |
106 fo.write(""" |
135 fo.write("; Filename: %s\n%s" % (filename, GCODE_HEAD)) |
107 ; Filename: %s |
|
108 ; GCode generated by bitplotter one-night-quick-hack script (marlin code flavour) |
|
109 ; 2015/2016 by NeoSoft - Malte Bayer |
|
110 |
|
111 G21 ; Metric |
|
112 ; We assume Z is in focus height and laser head is focus at bottom left of image! |
|
113 G92 X0 Y0 E0; set zero position - new origin |
|
114 G90 ; absolute positioning |
|
115 M82 ; Set extruder (laser) to absolute positioning |
|
116 M201 X1000 Y1000 E500 ; Set acceleration |
|
117 M203 X1000 Y1000 Z4 E10 ; Set max feedrate |
|
118 M209 S0 ; disable firmware retraction, we dont want to burn holes... |
|
119 M302 ; Allow cold extrudes - doesnt matter because we hack the extruder physically off with the M571 E mod |
|
120 M571 S1 E1 ; Activate Laser output on extrusion, but block real motor movement! |
|
121 G0 X0 Y0 F%d ; Set moving speed TRAVEL_SPEED |
|
122 G1 X0 Y0 F%d ; Set linear engraving speed ENGRAVE_SPEED |
|
123 |
|
124 """ % (filename, TRAVEL_SPEED, ENGRAVE_SPEED) ) |
|
125 self.log("Travel/Engrave speed: %d mm/sec, %d mm/sec" % ( |
|
126 TRAVEL_SPEED / 60, ENGRAVE_SPEED / 60) ) |
|
127 |
136 |
128 fo.write(";Start engraving the raster image: %dx%d points @ %d DPI = %.0fx%.0f mm\n\n" % ( |
137 fo.write(";Start engraving the raster image: %dx%d points @ %d DPI = %.0fx%.0f mm\n\n" % ( |
129 im.size[0], im.size[1], DPI, im.size[0]*MM_PIXEL, im.size[1]*MM_PIXEL) ) |
138 im.size[0], im.size[1], DPI, im.size[0]*MM_PIXEL, im.size[1]*MM_PIXEL) ) |
130 |
139 |
131 INVERT_Y = MM_PIXEL * (im.size[1] -1) * (-1) |
140 INVERT_Y = MM_PIXEL * (im.size[1] -1) * (-1) |
182 fo.write("G1 X%.4f Y%.4f E%.4f\n" % (XMM, YMM, E * E_FACTOR)) |
191 fo.write("G1 X%.4f Y%.4f E%.4f\n" % (XMM, YMM, E * E_FACTOR)) |
183 last_bit = bit |
192 last_bit = bit |
184 if CHANGE_DIRECTION: |
193 if CHANGE_DIRECTION: |
185 DIR = DIR * (-1) # change y direction on every X |
194 DIR = DIR * (-1) # change y direction on every X |
186 |
195 |
187 fo.write("""M400 ; Wait for all moves to finish |
196 fo.write(GCODE_FOOT) |
188 M571 S0 E0 |
|
189 M42 P28 S0 ; Force laser off! |
|
190 M501 ; undo all settings made |
|
191 """) |
|
192 |
|
193 fo.close() |
197 fo.close() |
194 |
198 |
195 if self.pronterwindow: |
199 if self.pronterwindow: |
196 self.log("") |
200 self.log("") |
197 self.pronterwindow.load_gcode_async(filename + '.g') |
201 self.pronterwindow.load_gcode_async(filename + '.g') |
203 self.log("Converting HPGL plot for lasercut:") |
207 self.log("Converting HPGL plot for lasercut:") |
204 self.log("File: %s" % filename) |
208 self.log("File: %s" % filename) |
205 |
209 |
206 fi = open(filename, "r") |
210 fi = open(filename, "r") |
207 fo = open(filename + ".g", "w") |
211 fo = open(filename + ".g", "w") |
208 |
212 fo.write("; Filename: %s\n%s" % (filename, GCODE_HEAD)) |
209 |
|
210 fo.write(""" |
|
211 ; Filename: %s |
|
212 ; GCode generated by hpglplotter (marlin code flavour) |
|
213 |
|
214 G21 ; Metric |
|
215 ; We assume Z is in focus height and laser head is focus at bottom left of image! |
|
216 G92 X0 Y0 E0; set zero position - new origin |
|
217 G90 ; absolute positioning |
|
218 M82 ; Set extruder (laser) to absolute positioning |
|
219 M201 X1000 Y1000 E500 ; Set acceleration |
|
220 M203 X1000 Y1000 Z4 E10 ; Set max feedrate |
|
221 M209 S0 ; disable firmware retraction, we dont want to burn holes... |
|
222 M302 ; Allow cold extrudes - doesnt matter because we hack the extruder physically off with the M571 E mod |
|
223 M571 S1 E1 ; Activate Laser output on extrusion, but block real motor movement! |
|
224 G0 X0 Y0 F%d ; Set moving speed TRAVEL_SPEED |
|
225 G1 X0 Y0 F%d ; Set linear engraving speed ENGRAVE_SPEED |
|
226 |
|
227 """ % (filename, TRAVEL_SPEED, ENGRAVE_SPEED) ) |
|
228 |
213 |
229 G = "0" |
214 G = "0" |
230 LASER_STATE = 0 |
215 LASER_STATE = 0 |
231 last_coord = [0.0,0.0] |
216 last_coord = [0.0,0.0] |
232 last_cmd = "" |
217 last_cmd = "" |
283 |
262 |
284 |
263 |
285 def svg2gcode(self, filename, bed_max_x = 50, bed_max_y = 50, smoothness = 0.2): |
264 def svg2gcode(self, filename, bed_max_x = 50, bed_max_y = 50, smoothness = 0.2): |
286 self.log("Generating paths from SVG...") |
265 self.log("Generating paths from SVG...") |
287 |
266 |
288 preamble = """ |
|
289 ; Filename: %s |
|
290 ; GCode generated by bitplotter one-night-quick-hack script (marlin code flavour) |
|
291 ; 2015/2016 by NeoSoft - Malte Bayer |
|
292 |
|
293 G21 ; Metric |
|
294 ; We assume Z is in focus height and laser head is focus at bottom left of image! |
|
295 G92 X0 Y0 E0; set zero position - new origin |
|
296 G90 ; absolute positioning |
|
297 M82 ; Set extruder (laser) to absolute positioning |
|
298 M201 X1000 Y1000 E500 ; Set acceleration |
|
299 M203 X1000 Y1000 Z4 E10 ; Set max feedrate |
|
300 M209 S0 ; disable firmware retraction, we dont want to burn holes... |
|
301 M302 ; Allow cold extrudes - doesnt matter because we hack the extruder physically off with the M571 E mod |
|
302 M571 S1 E1 ; Activate Laser output on extrusion, but block real motor movement! |
|
303 G0 X0 Y0 F%d ; Set moving speed TRAVEL_SPEED |
|
304 G1 X0 Y0 F%d ; Set linear engraving speed ENGRAVE_SPEED |
|
305 |
|
306 """ % (filename, TRAVEL_SPEED, ENGRAVE_SPEED) |
|
307 self.log("Travel/Engrave speed: %d mm/sec, %d mm/sec" % ( |
|
308 TRAVEL_SPEED / 60, ENGRAVE_SPEED / 60) ) |
|
309 |
|
310 postamble = """M400 ; Wait for all moves to finish |
|
311 M571 S0 E0 |
|
312 M42 P28 S0 ; Force laser off! |
|
313 M501 ; undo all settings made |
|
314 """ |
|
315 shape_preamble = "G92 E0\n" |
267 shape_preamble = "G92 E0\n" |
316 shape_postamble = "" |
268 shape_postamble = "" |
317 |
|
318 |
269 |
319 """ |
270 """ |
320 Used to control the smoothness/sharpness of the curves. |
271 Used to control the smoothness/sharpness of the curves. |
321 Smaller the value greater the sharpness. Make sure the |
272 Smaller the value greater the sharpness. Make sure the |
322 value is greater than 0.1 |
273 value is greater than 0.1 |
346 scale_y = bed_max_y / max(width, height) |
297 scale_y = bed_max_y / max(width, height) |
347 |
298 |
348 self.log("Scaling factor: %.2f, %.2f" % (scale_x,scale_y)) |
299 self.log("Scaling factor: %.2f, %.2f" % (scale_x,scale_y)) |
349 |
300 |
350 fo = open(filename + ".g", "w") |
301 fo = open(filename + ".g", "w") |
351 fo.write(preamble) |
302 fo.write("; Filename: %s\n%s" % (filename, GCODE_HEAD)) |
352 |
303 |
353 for elem in root.iter(): |
304 for elem in root.iter(): |
354 try: |
305 try: |
355 _, tag_suffix = elem.tag.split('}') |
306 _, tag_suffix = elem.tag.split('}') |
356 except ValueError: |
307 except ValueError: |
391 fo.write("G1 X%0.2f Y%0.2f E%.4f F%.4f\n" % (xs, ys, E * E_FACTOR, ENGRAVE_SPEED)) |
342 fo.write("G1 X%0.2f Y%0.2f E%.4f F%.4f\n" % (xs, ys, E * E_FACTOR, ENGRAVE_SPEED)) |
392 else: |
343 else: |
393 self.log("Position outside print dimension: %d, %d" % (xs, ys)) |
344 self.log("Position outside print dimension: %d, %d" % (xs, ys)) |
394 fo.write(shape_postamble) |
345 fo.write(shape_postamble) |
395 |
346 |
396 fo.write(postamble) |
347 fo.write(GCODE_FOOT) |
397 fo.close() |
348 fo.close() |
398 |
349 |
399 if self.pronterwindow: |
350 if self.pronterwindow: |
400 self.log("") |
351 self.log("") |
401 self.pronterwindow.load_gcode_async(filename + '.g') |
352 self.pronterwindow.load_gcode_async(filename + '.g') |
402 |
353 |