1 #!/usr/bin/env python |
1 #!/usr/bin/env python3 |
2 # This file is copied from GCoder. |
|
3 # |
2 # |
4 # GCoder is free software: you can redistribute it and/or modify |
3 # This file is part of the Printrun suite. |
|
4 # |
|
5 # Printrun is free software: you can redistribute it and/or modify |
5 # it under the terms of the GNU General Public License as published by |
6 # it under the terms of the GNU General Public License as published by |
6 # the Free Software Foundation, either version 3 of the License, or |
7 # the Free Software Foundation, either version 3 of the License, or |
7 # (at your option) any later version. |
8 # (at your option) any later version. |
8 # |
9 # |
9 # GCoder is distributed in the hope that it will be useful, |
10 # Printrun is distributed in the hope that it will be useful, |
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 # GNU General Public License for more details. |
13 # GNU General Public License for more details. |
13 # |
14 # |
14 # You should have received a copy of the GNU General Public License |
15 # You should have received a copy of the GNU General Public License |
20 import datetime |
21 import datetime |
21 import logging |
22 import logging |
22 from array import array |
23 from array import array |
23 |
24 |
24 gcode_parsed_args = ["x", "y", "e", "f", "z", "i", "j"] |
25 gcode_parsed_args = ["x", "y", "e", "f", "z", "i", "j"] |
25 gcode_parsed_nonargs = ["g", "t", "m", "n"] |
26 gcode_parsed_nonargs = 'gtmnd' |
26 to_parse = "".join(gcode_parsed_args + gcode_parsed_nonargs) |
27 to_parse = "".join(gcode_parsed_args) + gcode_parsed_nonargs |
27 gcode_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n|([%s])([-+]?[0-9]*\.?[0-9]*)" % to_parse) |
28 gcode_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n|([%s])\s*([-+]?[0-9]*\.?[0-9]*)" % to_parse) |
28 gcode_strip_comment_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n") |
29 gcode_strip_comment_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n") |
29 m114_exp = re.compile("\([^\(\)]*\)|[/\*].*\n|([XYZ]):?([-+]?[0-9]*\.?[0-9]*)") |
30 m114_exp = re.compile("\([^\(\)]*\)|[/\*].*\n|([XYZ]):?([-+]?[0-9]*\.?[0-9]*)") |
30 specific_exp = "(?:\([^\(\)]*\))|(?:;.*)|(?:[/\*].*\n)|(%s[-+]?[0-9]*\.?[0-9]*)" |
31 specific_exp = "(?:\([^\(\)]*\))|(?:;.*)|(?:[/\*].*\n)|(%s[-+]?[0-9]*\.?[0-9]*)" |
31 move_gcodes = ["G0", "G1", "G2", "G3"] |
32 move_gcodes = ["G0", "G1", "G2", "G3"] |
32 |
33 |
33 class PyLine(object): |
34 class PyLine: |
34 |
35 |
35 __slots__ = ('x', 'y', 'z', 'e', 'f', 'i', 'j', |
36 __slots__ = ('x', 'y', 'z', 'e', 'f', 'i', 'j', |
36 'raw', 'command', 'is_move', |
37 'raw', 'command', 'is_move', |
37 'relative', 'relative_e', |
38 'relative', 'relative_e', |
38 'current_x', 'current_y', 'current_z', 'extruding', |
39 'current_x', 'current_y', 'current_z', 'extruding', |
43 self.raw = l |
44 self.raw = l |
44 |
45 |
45 def __getattr__(self, name): |
46 def __getattr__(self, name): |
46 return None |
47 return None |
47 |
48 |
48 class PyLightLine(object): |
49 class PyLightLine: |
49 |
50 |
50 __slots__ = ('raw', 'command') |
51 __slots__ = ('raw', 'command') |
51 |
52 |
52 def __init__(self, l): |
53 def __init__(self, l): |
53 self.raw = l |
54 self.raw = l |
54 |
55 |
55 def __getattr__(self, name): |
56 def __getattr__(self, name): |
56 return None |
57 return None |
57 |
58 |
58 try: |
59 try: |
59 import gcoder_line |
60 from . import gcoder_line |
60 Line = gcoder_line.GLine |
61 Line = gcoder_line.GLine |
61 LightLine = gcoder_line.GLightLine |
62 LightLine = gcoder_line.GLightLine |
62 except Exception, e: |
63 except Exception as e: |
63 logging.warning("Memory-efficient GCoder implementation unavailable: %s" % e) |
64 logging.warning("Memory-efficient GCoder implementation unavailable: %s" % e) |
64 Line = PyLine |
65 Line = PyLine |
65 LightLine = PyLightLine |
66 LightLine = PyLightLine |
66 |
67 |
67 def find_specific_code(line, code): |
68 def find_specific_code(line, code): |
215 def _get_layers_count(self): |
218 def _get_layers_count(self): |
216 return len(self.all_zs) |
219 return len(self.all_zs) |
217 layers_count = property(_get_layers_count) |
220 layers_count = property(_get_layers_count) |
218 |
221 |
219 def __init__(self, data = None, home_pos = None, |
222 def __init__(self, data = None, home_pos = None, |
220 layer_callback = None, deferred = False): |
223 layer_callback = None, deferred = False, |
|
224 cutting_as_extrusion = False): |
|
225 self.cutting_as_extrusion = cutting_as_extrusion |
221 if not deferred: |
226 if not deferred: |
222 self.prepare(data, home_pos, layer_callback) |
227 self.prepare(data, home_pos, layer_callback) |
223 |
228 |
224 def prepare(self, data = None, home_pos = None, layer_callback = None): |
229 def prepare(self, data = None, home_pos = None, layer_callback = None): |
225 self.home_pos = home_pos |
230 self.home_pos = home_pos |
312 self._preprocess([gline]) |
319 self._preprocess([gline]) |
313 if store: |
320 if store: |
314 self.lines.append(gline) |
321 self.lines.append(gline) |
315 self.append_layer.append(gline) |
322 self.append_layer.append(gline) |
316 self.layer_idxs.append(self.append_layer_id) |
323 self.layer_idxs.append(self.append_layer_id) |
317 self.line_idxs.append(len(self.append_layer)) |
324 self.line_idxs.append(len(self.append_layer)-1) |
318 return gline |
325 return gline |
319 |
326 |
320 def _preprocess(self, lines = None, build_layers = False, |
327 def _preprocess(self, lines = None, build_layers = False, |
321 layer_callback = None): |
328 layer_callback = None): |
322 """Checks for imperial/relativeness settings and tool changes""" |
329 """Checks for imperial/relativeness settings and tool changes""" |
336 # Extrusion computation |
343 # Extrusion computation |
337 current_e = self.current_e |
344 current_e = self.current_e |
338 offset_e = self.offset_e |
345 offset_e = self.offset_e |
339 total_e = self.total_e |
346 total_e = self.total_e |
340 max_e = self.max_e |
347 max_e = self.max_e |
|
348 cutting = self.cutting |
341 |
349 |
342 current_e_multi = self.current_e_multi[current_tool] |
350 current_e_multi = self.current_e_multi[current_tool] |
343 offset_e_multi = self.offset_e_multi[current_tool] |
351 offset_e_multi = self.offset_e_multi[current_tool] |
344 total_e_multi = self.total_e_multi[current_tool] |
352 total_e_multi = self.total_e_multi[current_tool] |
345 max_e_multi = self.max_e_multi[current_tool] |
353 max_e_multi = self.max_e_multi[current_tool] |
365 # Duration estimation |
373 # Duration estimation |
366 # TODO: |
374 # TODO: |
367 # get device caps from firmware: max speed, acceleration/axis |
375 # get device caps from firmware: max speed, acceleration/axis |
368 # (including extruder) |
376 # (including extruder) |
369 # calculate the maximum move duration accounting for above ;) |
377 # calculate the maximum move duration accounting for above ;) |
370 lastx = lasty = lastz = laste = lastf = 0.0 |
378 lastx = lasty = lastz = None |
|
379 laste = lastf = 0 |
371 lastdx = 0 |
380 lastdx = 0 |
372 lastdy = 0 |
381 lastdy = 0 |
373 x = y = e = f = 0.0 |
382 x = y = e = f = 0.0 |
374 currenttravel = 0.0 |
383 currenttravel = 0.0 |
375 moveduration = 0.0 |
384 moveduration = 0.0 |
381 all_layers = self.all_layers = [] |
390 all_layers = self.all_layers = [] |
382 all_zs = self.all_zs = set() |
391 all_zs = self.all_zs = set() |
383 layer_idxs = self.layer_idxs = [] |
392 layer_idxs = self.layer_idxs = [] |
384 line_idxs = self.line_idxs = [] |
393 line_idxs = self.line_idxs = [] |
385 |
394 |
386 layer_id = 0 |
|
387 layer_line = 0 |
|
388 |
395 |
389 last_layer_z = None |
396 last_layer_z = None |
390 prev_z = None |
397 prev_z = None |
391 prev_base_z = (None, None) |
|
392 cur_z = None |
398 cur_z = None |
393 cur_lines = [] |
399 cur_lines = [] |
|
400 |
|
401 def append_lines(lines, isEnd): |
|
402 if not build_layers: |
|
403 return |
|
404 nonlocal layerbeginduration, last_layer_z |
|
405 if cur_layer_has_extrusion and prev_z != last_layer_z \ |
|
406 or not all_layers or isEnd: |
|
407 layer = Layer([], prev_z) |
|
408 last_layer_z = prev_z |
|
409 finished_layer = len(all_layers)-1 if all_layers else None |
|
410 all_layers.append(layer) |
|
411 else: |
|
412 layer = all_layers[-1] |
|
413 finished_layer = None |
|
414 layer_id = len(all_layers)-1 |
|
415 layer_line = len(layer) |
|
416 for i, ln in enumerate(lines): |
|
417 layer.append(ln) |
|
418 layer_idxs.append(layer_id) |
|
419 line_idxs.append(layer_line+i) |
|
420 layer.duration += totalduration - layerbeginduration |
|
421 layerbeginduration = totalduration |
|
422 if layer_callback: |
|
423 # we finish a layer when inserting the next |
|
424 if finished_layer is not None: |
|
425 layer_callback(self, finished_layer) |
|
426 # notify about end layer, there will not be next |
|
427 if isEnd: |
|
428 layer_callback(self, layer_id) |
394 |
429 |
395 if self.line_class != Line: |
430 if self.line_class != Line: |
396 get_line = lambda l: Line(l.raw) |
431 get_line = lambda l: Line(l.raw) |
397 else: |
432 else: |
398 get_line = lambda l: l |
433 get_line = lambda l: l |
420 elif line.command == "M82": |
455 elif line.command == "M82": |
421 relative_e = False |
456 relative_e = False |
422 elif line.command == "M83": |
457 elif line.command == "M83": |
423 relative_e = True |
458 relative_e = True |
424 elif line.command[0] == "T": |
459 elif line.command[0] == "T": |
425 current_tool = int(line.command[1:]) |
460 try: |
426 while(current_tool+1>len(self.current_e_multi)): |
461 current_tool = int(line.command[1:]) |
|
462 except: |
|
463 pass #handle T? by treating it as no tool change |
|
464 while current_tool+1 > len(self.current_e_multi): |
427 self.current_e_multi+=[0] |
465 self.current_e_multi+=[0] |
428 self.offset_e_multi+=[0] |
466 self.offset_e_multi+=[0] |
429 self.total_e_multi+=[0] |
467 self.total_e_multi+=[0] |
430 self.max_e_multi+=[0] |
468 self.max_e_multi+=[0] |
|
469 elif line.command == "M3" or line.command == "M4": |
|
470 cutting = True |
|
471 elif line.command == "M5": |
|
472 cutting = False |
|
473 |
431 current_e_multi = self.current_e_multi[current_tool] |
474 current_e_multi = self.current_e_multi[current_tool] |
432 offset_e_multi = self.offset_e_multi[current_tool] |
475 offset_e_multi = self.offset_e_multi[current_tool] |
433 total_e_multi = self.total_e_multi[current_tool] |
476 total_e_multi = self.total_e_multi[current_tool] |
434 max_e_multi = self.max_e_multi[current_tool] |
477 max_e_multi = self.max_e_multi[current_tool] |
435 |
478 |
502 max_e_multi=max(max_e_multi, total_e_multi) |
545 max_e_multi=max(max_e_multi, total_e_multi) |
503 cur_layer_has_extrusion |= line.extruding |
546 cur_layer_has_extrusion |= line.extruding |
504 elif line.command == "G92": |
547 elif line.command == "G92": |
505 offset_e = current_e - line.e |
548 offset_e = current_e - line.e |
506 offset_e_multi = current_e_multi - line.e |
549 offset_e_multi = current_e_multi - line.e |
|
550 if cutting and self.cutting_as_extrusion: |
|
551 line.extruding = True |
507 |
552 |
508 self.current_e_multi[current_tool]=current_e_multi |
553 self.current_e_multi[current_tool]=current_e_multi |
509 self.offset_e_multi[current_tool]=offset_e_multi |
554 self.offset_e_multi[current_tool]=offset_e_multi |
510 self.max_e_multi[current_tool]=max_e_multi |
555 self.max_e_multi[current_tool]=max_e_multi |
511 self.total_e_multi[current_tool]=total_e_multi |
556 self.total_e_multi[current_tool]=total_e_multi |
514 if build_layers: |
559 if build_layers: |
515 # Update bounding box |
560 # Update bounding box |
516 if line.is_move: |
561 if line.is_move: |
517 if line.extruding: |
562 if line.extruding: |
518 if line.current_x is not None: |
563 if line.current_x is not None: |
519 xmin_e = min(xmin_e, line.current_x) |
564 # G0 X10 ; G1 X20 E5 results in 10..20 even as G0 is not extruding |
520 xmax_e = max(xmax_e, line.current_x) |
565 xmin_e = min(xmin_e, line.current_x, xmin_e if lastx is None else lastx) |
|
566 xmax_e = max(xmax_e, line.current_x, xmax_e if lastx is None else lastx) |
521 if line.current_y is not None: |
567 if line.current_y is not None: |
522 ymin_e = min(ymin_e, line.current_y) |
568 ymin_e = min(ymin_e, line.current_y, ymin_e if lasty is None else lasty) |
523 ymax_e = max(ymax_e, line.current_y) |
569 ymax_e = max(ymax_e, line.current_y, ymax_e if lasty is None else lasty) |
524 if max_e <= 0: |
570 if max_e <= 0: |
525 if line.current_x is not None: |
571 if line.current_x is not None: |
526 xmin = min(xmin, line.current_x) |
572 xmin = min(xmin, line.current_x) |
527 xmax = max(xmax, line.current_x) |
573 xmax = max(xmax, line.current_x) |
528 if line.current_y is not None: |
574 if line.current_y is not None: |
529 ymin = min(ymin, line.current_y) |
575 ymin = min(ymin, line.current_y) |
530 ymax = max(ymax, line.current_y) |
576 ymax = max(ymax, line.current_y) |
531 |
577 |
532 # Compute duration |
578 # Compute duration |
533 if line.command == "G0" or line.command == "G1": |
579 if line.command == "G0" or line.command == "G1": |
534 x = line.x if line.x is not None else lastx |
580 x = line.x if line.x is not None else (lastx or 0) |
535 y = line.y if line.y is not None else lasty |
581 y = line.y if line.y is not None else (lasty or 0) |
536 z = line.z if line.z is not None else lastz |
582 z = line.z if line.z is not None else (lastz or 0) |
537 e = line.e if line.e is not None else laste |
583 e = line.e if line.e is not None else laste |
538 # mm/s vs mm/m => divide by 60 |
584 # mm/s vs mm/m => divide by 60 |
539 f = line.f / 60.0 if line.f is not None else lastf |
585 f = line.f / 60.0 if line.f is not None else lastf |
540 |
586 |
541 # given last feedrate and current feedrate calculate the |
587 # given last feedrate and current feedrate calculate the |
551 # speed is constant but printer has to fully decellerate |
597 # speed is constant but printer has to fully decellerate |
552 # and reaccelerate |
598 # and reaccelerate |
553 # The following code tries to fix it by forcing a full |
599 # The following code tries to fix it by forcing a full |
554 # reacceleration if this move is in the opposite direction |
600 # reacceleration if this move is in the opposite direction |
555 # of the previous one |
601 # of the previous one |
556 dx = x - lastx |
602 dx = x - (lastx or 0) |
557 dy = y - lasty |
603 dy = y - (lasty or 0) |
558 if dx * lastdx + dy * lastdy <= 0: |
604 if dx * lastdx + dy * lastdy <= 0: |
559 lastf = 0 |
605 lastf = 0 |
560 |
606 |
561 currenttravel = math.hypot(dx, dy) |
607 currenttravel = math.hypot(dx, dy) |
562 if currenttravel == 0: |
608 if currenttravel == 0: |
563 if line.z is not None: |
609 if line.z is not None: |
564 currenttravel = abs(line.z) if line.relative else abs(line.z - lastz) |
610 currenttravel = abs(line.z) if line.relative else abs(line.z - (lastz or 0)) |
565 elif line.e is not None: |
611 elif line.e is not None: |
566 currenttravel = abs(line.e) if line.relative_e else abs(line.e - laste) |
612 currenttravel = abs(line.e) if line.relative_e else abs(line.e - laste) |
567 # Feedrate hasn't changed, no acceleration/decceleration planned |
613 # Feedrate hasn't changed, no acceleration/decceleration planned |
568 if f == lastf: |
614 if f == lastf: |
569 moveduration = currenttravel / f if f != 0 else 0. |
615 moveduration = currenttravel / f if f != 0 else 0. |
604 if line.relative and cur_z is not None: |
650 if line.relative and cur_z is not None: |
605 cur_z += line.z |
651 cur_z += line.z |
606 else: |
652 else: |
607 cur_z = line.z |
653 cur_z = line.z |
608 |
654 |
609 # FIXME: the logic behind this code seems to work, but it might be |
655 if cur_z != prev_z and cur_layer_has_extrusion: |
610 # broken |
656 append_lines(cur_lines, False) |
611 if cur_z != prev_z: |
657 all_zs.add(prev_z) |
612 if prev_z is not None and last_layer_z is not None: |
658 cur_lines = [] |
613 offset = self.est_layer_height if self.est_layer_height else 0.01 |
659 cur_layer_has_extrusion = False |
614 if abs(prev_z - last_layer_z) < offset: |
|
615 if self.est_layer_height is None: |
|
616 zs = sorted([l.z for l in all_layers if l.z is not None]) |
|
617 heights = [round(zs[i + 1] - zs[i], 3) for i in range(len(zs) - 1)] |
|
618 heights = [height for height in heights if height] |
|
619 if len(heights) >= 2: self.est_layer_height = heights[1] |
|
620 elif heights: self.est_layer_height = heights[0] |
|
621 else: self.est_layer_height = 0.1 |
|
622 base_z = round(prev_z - (prev_z % self.est_layer_height), 2) |
|
623 else: |
|
624 base_z = round(prev_z, 2) |
|
625 else: |
|
626 base_z = prev_z |
|
627 |
|
628 if base_z != prev_base_z: |
|
629 new_layer = Layer(cur_lines, base_z) |
|
630 new_layer.duration = totalduration - layerbeginduration |
|
631 layerbeginduration = totalduration |
|
632 all_layers.append(new_layer) |
|
633 if cur_layer_has_extrusion and prev_z not in all_zs: |
|
634 all_zs.add(prev_z) |
|
635 cur_lines = [] |
|
636 cur_layer_has_extrusion = False |
|
637 layer_id += 1 |
|
638 layer_line = 0 |
|
639 last_layer_z = base_z |
|
640 if layer_callback is not None: |
|
641 layer_callback(self, len(all_layers) - 1) |
|
642 |
|
643 prev_base_z = base_z |
|
644 |
660 |
645 if build_layers: |
661 if build_layers: |
646 cur_lines.append(true_line) |
662 cur_lines.append(true_line) |
647 layer_idxs.append(layer_id) |
|
648 line_idxs.append(layer_line) |
|
649 layer_line += 1 |
|
650 prev_z = cur_z |
663 prev_z = cur_z |
651 # ## Loop done |
664 # ## Loop done |
652 |
665 |
653 # Store current status |
666 # Store current status |
654 self.imperial = imperial |
667 self.imperial = imperial |
667 self.total_e = total_e |
680 self.total_e = total_e |
668 self.current_e_multi[current_tool]=current_e_multi |
681 self.current_e_multi[current_tool]=current_e_multi |
669 self.offset_e_multi[current_tool]=offset_e_multi |
682 self.offset_e_multi[current_tool]=offset_e_multi |
670 self.max_e_multi[current_tool]=max_e_multi |
683 self.max_e_multi[current_tool]=max_e_multi |
671 self.total_e_multi[current_tool]=total_e_multi |
684 self.total_e_multi[current_tool]=total_e_multi |
|
685 self.cutting = cutting |
672 |
686 |
673 |
687 |
674 # Finalize layers |
688 # Finalize layers |
675 if build_layers: |
689 if build_layers: |
676 if cur_lines: |
690 if cur_lines: |
677 new_layer = Layer(cur_lines, prev_z) |
691 append_lines(cur_lines, True) |
678 new_layer.duration = totalduration - layerbeginduration |
692 all_zs.add(prev_z) |
679 layerbeginduration = totalduration |
|
680 all_layers.append(new_layer) |
|
681 if cur_layer_has_extrusion and prev_z not in all_zs: |
|
682 all_zs.add(prev_z) |
|
683 |
693 |
684 self.append_layer_id = len(all_layers) |
694 self.append_layer_id = len(all_layers) |
685 self.append_layer = Layer([]) |
695 self.append_layer = Layer([]) |
686 self.append_layer.duration = 0 |
696 self.append_layer.duration = 0 |
687 all_layers.append(self.append_layer) |
697 all_layers.append(self.append_layer) |
688 self.layer_idxs = array('I', layer_idxs) |
698 self.layer_idxs = array('I', layer_idxs) |
689 self.line_idxs = array('I', line_idxs) |
699 self.line_idxs = array('I', line_idxs) |
690 |
700 |
691 # Compute bounding box |
701 # Compute bounding box |
692 all_zs = self.all_zs.union(set([zmin])).difference(set([None])) |
702 all_zs = self.all_zs.union({zmin}).difference({None}) |
693 zmin = min(all_zs) |
703 zmin = min(all_zs) |
694 zmax = max(all_zs) |
704 zmax = max(all_zs) |
695 |
705 |
696 self.filament_length = self.max_e |
706 self.filament_length = self.max_e |
697 while len(self.filament_length_multi)<len(self.max_e_multi): |
707 while len(self.filament_length_multi)<len(self.max_e_multi): |
729 class LightGCode(GCode): |
739 class LightGCode(GCode): |
730 line_class = LightLine |
740 line_class = LightLine |
731 |
741 |
732 def main(): |
742 def main(): |
733 if len(sys.argv) < 2: |
743 if len(sys.argv) < 2: |
734 print "usage: %s filename.gcode" % sys.argv[0] |
744 print("usage: %s filename.gcode" % sys.argv[0]) |
735 return |
745 return |
736 |
746 |
737 print "Line object size:", sys.getsizeof(Line("G0 X0")) |
747 print("Line object size:", sys.getsizeof(Line("G0 X0"))) |
738 print "Light line object size:", sys.getsizeof(LightLine("G0 X0")) |
748 print("Light line object size:", sys.getsizeof(LightLine("G0 X0"))) |
739 gcode = GCode(open(sys.argv[1], "rU")) |
749 gcode = GCode(open(sys.argv[1], "rU")) |
740 |
750 |
741 print "Dimensions:" |
751 print("Dimensions:") |
742 xdims = (gcode.xmin, gcode.xmax, gcode.width) |
752 xdims = (gcode.xmin, gcode.xmax, gcode.width) |
743 print "\tX: %0.02f - %0.02f (%0.02f)" % xdims |
753 print("\tX: %0.02f - %0.02f (%0.02f)" % xdims) |
744 ydims = (gcode.ymin, gcode.ymax, gcode.depth) |
754 ydims = (gcode.ymin, gcode.ymax, gcode.depth) |
745 print "\tY: %0.02f - %0.02f (%0.02f)" % ydims |
755 print("\tY: %0.02f - %0.02f (%0.02f)" % ydims) |
746 zdims = (gcode.zmin, gcode.zmax, gcode.height) |
756 zdims = (gcode.zmin, gcode.zmax, gcode.height) |
747 print "\tZ: %0.02f - %0.02f (%0.02f)" % zdims |
757 print("\tZ: %0.02f - %0.02f (%0.02f)" % zdims) |
748 print "Filament used: %0.02fmm" % gcode.filament_length |
758 print("Filament used: %0.02fmm" % gcode.filament_length) |
749 for i in enumerate(gcode.filament_length_multi): |
759 for i in enumerate(gcode.filament_length_multi): |
750 print "E%d %0.02fmm" % (i[0],i[1]) |
760 print("E%d %0.02fmm" % (i[0],i[1])) |
751 print "Number of layers: %d" % gcode.layers_count |
761 print("Number of layers: %d" % gcode.layers_count) |
752 print "Estimated duration: %s" % gcode.estimate_duration()[1] |
762 print("Estimated duration: %s" % gcode.estimate_duration()[1]) |
753 |
763 |
754 if __name__ == '__main__': |
764 if __name__ == '__main__': |
755 main() |
765 main() |