meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7050__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/balormk/clone_loader.py +3 -2
- meerk40t/balormk/controller.py +38 -13
- meerk40t/balormk/cylindermod.py +1 -0
- meerk40t/balormk/device.py +13 -9
- meerk40t/balormk/driver.py +9 -2
- meerk40t/balormk/galvo_commands.py +3 -1
- meerk40t/balormk/gui/gui.py +6 -0
- meerk40t/balormk/livelightjob.py +338 -321
- meerk40t/balormk/mock_connection.py +4 -3
- meerk40t/balormk/usb_connection.py +11 -2
- meerk40t/camera/camera.py +19 -14
- meerk40t/camera/gui/camerapanel.py +6 -0
- meerk40t/core/cutplan.py +101 -78
- meerk40t/core/elements/element_treeops.py +435 -140
- meerk40t/core/elements/elements.py +100 -9
- meerk40t/core/elements/shapes.py +259 -72
- meerk40t/core/elements/tree_commands.py +10 -5
- meerk40t/core/node/blobnode.py +19 -4
- meerk40t/core/node/elem_ellipse.py +18 -8
- meerk40t/core/node/elem_image.py +51 -19
- meerk40t/core/node/elem_line.py +18 -8
- meerk40t/core/node/elem_path.py +18 -8
- meerk40t/core/node/elem_point.py +10 -4
- meerk40t/core/node/elem_polyline.py +19 -11
- meerk40t/core/node/elem_rect.py +18 -8
- meerk40t/core/node/elem_text.py +11 -5
- meerk40t/core/node/filenode.py +2 -8
- meerk40t/core/node/groupnode.py +11 -11
- meerk40t/core/node/image_processed.py +11 -5
- meerk40t/core/node/image_raster.py +11 -5
- meerk40t/core/node/node.py +64 -16
- meerk40t/core/node/refnode.py +2 -1
- meerk40t/core/planner.py +25 -11
- meerk40t/core/svg_io.py +91 -34
- meerk40t/device/dummydevice.py +7 -1
- meerk40t/extra/vtracer.py +222 -0
- meerk40t/grbl/device.py +96 -9
- meerk40t/grbl/driver.py +15 -5
- meerk40t/gui/about.py +20 -0
- meerk40t/gui/devicepanel.py +20 -16
- meerk40t/gui/gui_mixins.py +4 -0
- meerk40t/gui/icons.py +330 -253
- meerk40t/gui/laserpanel.py +27 -3
- meerk40t/gui/laserrender.py +41 -21
- meerk40t/gui/magnetoptions.py +158 -65
- meerk40t/gui/materialtest.py +569 -310
- meerk40t/gui/navigationpanels.py +229 -24
- meerk40t/gui/propertypanels/hatchproperty.py +2 -0
- meerk40t/gui/propertypanels/imageproperty.py +160 -106
- meerk40t/gui/propertypanels/wobbleproperty.py +6 -2
- meerk40t/gui/ribbon.py +6 -1
- meerk40t/gui/scenewidgets/gridwidget.py +29 -32
- meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
- meerk40t/gui/simulation.py +75 -77
- meerk40t/gui/spoolerpanel.py +27 -7
- meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/tips.py +15 -1
- meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
- meerk40t/gui/wxmmain.py +242 -114
- meerk40t/gui/wxmscene.py +107 -24
- meerk40t/gui/wxmtree.py +4 -2
- meerk40t/gui/wxutils.py +286 -15
- meerk40t/image/imagetools.py +129 -65
- meerk40t/internal_plugins.py +4 -0
- meerk40t/kernel/kernel.py +67 -18
- meerk40t/kernel/settings.py +28 -9
- meerk40t/lihuiyu/device.py +24 -12
- meerk40t/main.py +14 -9
- meerk40t/moshi/device.py +20 -6
- meerk40t/network/console_server.py +22 -6
- meerk40t/newly/device.py +10 -3
- meerk40t/newly/gui/gui.py +10 -0
- meerk40t/ruida/device.py +22 -2
- meerk40t/ruida/loader.py +9 -4
- meerk40t/ruida/rdjob.py +48 -8
- meerk40t/tools/geomstr.py +240 -123
- meerk40t/tools/rasterplotter.py +185 -94
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/RECORD +85 -84
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/zip-safe +0 -0
meerk40t/tools/rasterplotter.py
CHANGED
@@ -18,19 +18,21 @@ The rasters can either be BIDIRECTIONAL or UNIDIRECTIONAL meaning they raster on
|
|
18
18
|
or only on forward swing.
|
19
19
|
"""
|
20
20
|
|
21
|
-
import
|
22
|
-
from math import sqrt
|
21
|
+
from math import isinf, sqrt
|
23
22
|
from time import perf_counter, sleep
|
23
|
+
|
24
|
+
import numpy as np
|
25
|
+
|
24
26
|
from meerk40t.constants import (
|
25
|
-
RASTER_T2B,
|
26
27
|
RASTER_B2T,
|
27
|
-
|
28
|
-
RASTER_L2R,
|
29
|
-
RASTER_HATCH,
|
28
|
+
RASTER_CROSSOVER,
|
30
29
|
RASTER_GREEDY_H,
|
31
30
|
RASTER_GREEDY_V,
|
32
|
-
|
31
|
+
RASTER_HATCH,
|
32
|
+
RASTER_L2R,
|
33
|
+
RASTER_R2L,
|
33
34
|
RASTER_SPIRAL,
|
35
|
+
RASTER_T2B,
|
34
36
|
)
|
35
37
|
|
36
38
|
|
@@ -90,7 +92,7 @@ class RasterPlotter:
|
|
90
92
|
|
91
93
|
if special is None:
|
92
94
|
special = {}
|
93
|
-
self.debug_level = 0
|
95
|
+
self.debug_level = 0 # 0 Nothing, 1 file creation, 2 file + summary, 3 file + summary + details
|
94
96
|
self.data = data
|
95
97
|
self.width = width
|
96
98
|
self.height = height
|
@@ -98,16 +100,18 @@ class RasterPlotter:
|
|
98
100
|
self._cache = None
|
99
101
|
parameters = {
|
100
102
|
# Provide an override for the minimumx / minimumy / horizontal / bidirectional
|
101
|
-
RASTER_T2B: (None, True, True, None),
|
102
|
-
RASTER_B2T: (None, False, True, None),
|
103
|
-
RASTER_R2L: (False, None, False, None),
|
104
|
-
RASTER_L2R: (True, None, False, None),
|
105
|
-
RASTER_HATCH: (None, None, None, None),
|
106
|
-
RASTER_GREEDY_H: (None, None, None, True),
|
107
|
-
RASTER_GREEDY_V: (None, None, None, True),
|
108
|
-
RASTER_CROSSOVER: (None, None, None, True),
|
103
|
+
RASTER_T2B: (None, True, True, None), # top to bottom
|
104
|
+
RASTER_B2T: (None, False, True, None), # bottom to top
|
105
|
+
RASTER_R2L: (False, None, False, None), # right to left
|
106
|
+
RASTER_L2R: (True, None, False, None), # left to right
|
107
|
+
RASTER_HATCH: (None, None, None, None), # crossraster (one of the two)
|
108
|
+
RASTER_GREEDY_H: (None, None, None, True), # greedy neighbour horizontal
|
109
|
+
RASTER_GREEDY_V: (None, None, None, True), # greedy neighbour
|
110
|
+
RASTER_CROSSOVER: (None, None, None, True), # true crossover
|
109
111
|
}
|
110
|
-
def_x, def_y, def_hor, def_bidir = parameters.get(
|
112
|
+
def_x, def_y, def_hor, def_bidir = parameters.get(
|
113
|
+
direction, (None, None, None, None)
|
114
|
+
)
|
111
115
|
self.start_minimum_x = start_minimum_x if def_x is None else def_x
|
112
116
|
self.start_minimum_y = start_minimum_y if def_y is None else def_y
|
113
117
|
self.horizontal = horizontal if def_hor is None else def_hor
|
@@ -120,7 +124,7 @@ class RasterPlotter:
|
|
120
124
|
# and calculate the overlap in pixels to the left / to the right
|
121
125
|
self.overlap = int(laserspot / sqrt(2) / 2)
|
122
126
|
# self.overlap = 1
|
123
|
-
self.special = dict(special)
|
127
|
+
self.special = dict(special) # Copy it so it wont be changed
|
124
128
|
if horizontal:
|
125
129
|
self.overscan = round(overscan / float(step_x))
|
126
130
|
else:
|
@@ -150,11 +154,13 @@ class RasterPlotter:
|
|
150
154
|
if 0 <= self.direction < len(methods):
|
151
155
|
s_meth = f"Rasterplotter ({self.width}x{self.height}): {methods[self.direction]} ({self.direction})"
|
152
156
|
else:
|
153
|
-
s_meth =
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
157
|
+
s_meth = (
|
158
|
+
f"Rasterplotter ({self.width}x{self.height}): Unknown {self.direction}"
|
159
|
+
)
|
160
|
+
s_direc = "Bidirectional" if self.bidirectional else "Unidirectional"
|
161
|
+
s_axis = "horizontal" if self.horizontal else "vertical"
|
162
|
+
s_ystart = "top" if self.start_minimum_y else "bottom"
|
163
|
+
s_xstart = "left" if self.start_minimum_x else "right"
|
158
164
|
return f"{s_meth}, {s_direc} {s_axis} plot starting at {s_ystart}-{s_xstart}"
|
159
165
|
|
160
166
|
def reset(self):
|
@@ -434,7 +440,7 @@ class RasterPlotter:
|
|
434
440
|
Returns the initial position for this within the scene. Taking into account start corner, and step size.
|
435
441
|
@return: initial position within scene. The first plot location.
|
436
442
|
"""
|
437
|
-
if self.initial_x is None: # image is blank.
|
443
|
+
if self.initial_x is None or isinf(self.initial_x): # image is blank.
|
438
444
|
if self.use_integers:
|
439
445
|
return int(round(self.offset_x)), int(round(self.offset_y))
|
440
446
|
else:
|
@@ -456,7 +462,12 @@ class RasterPlotter:
|
|
456
462
|
of the width and height.
|
457
463
|
@return:
|
458
464
|
"""
|
459
|
-
if self.final_x is None: # image is blank.
|
465
|
+
if self.final_x is None or isinf(self.final_x): # image is blank.
|
466
|
+
if self.use_integers:
|
467
|
+
return int(round(self.offset_x)), int(round(self.offset_y))
|
468
|
+
else:
|
469
|
+
return self.offset_x, self.offset_y
|
470
|
+
if self.use_integers:
|
460
471
|
if self.use_integers:
|
461
472
|
return int(round(self.offset_x)), int(round(self.offset_y))
|
462
473
|
else:
|
@@ -518,15 +529,20 @@ class RasterPlotter:
|
|
518
529
|
s_meth = f"Method: {m} ({self.direction})"
|
519
530
|
except IndexError:
|
520
531
|
s_meth = f"Method: Unknown {self.direction}"
|
521
|
-
print
|
532
|
+
print(s_meth)
|
522
533
|
data = list(self._plot_pixels())
|
523
534
|
from platform import system
|
535
|
+
|
524
536
|
defaultdir = "c:\\temp\\" if system() == "Windows" else ""
|
525
537
|
has_duplicates = 0
|
526
538
|
tstamp = int(perf_counter() * 100)
|
527
539
|
with open(f"{defaultdir}plot_{tstamp}.txt", mode="w") as f:
|
528
|
-
f.write(
|
529
|
-
|
540
|
+
f.write(
|
541
|
+
f"0.9.7\n{s_meth}\n{'Bidirectional' if self.bidirectional else 'Unidirectional'} {'horizontal' if self.horizontal else 'vertical'} plot starting at {'top' if self.start_minimum_y else 'bottom'}-{'left' if self.start_minimum_x else 'right'}\n"
|
542
|
+
)
|
543
|
+
f.write(
|
544
|
+
f"Overscan: {self.overscan:.2f}, Stepx={step_x:.2f}, Stepy={step_y:.2f}\n"
|
545
|
+
)
|
530
546
|
f.write(f"Image dimensions: {self.width}x{self.height}\n")
|
531
547
|
f.write(f"Startpoint: {self.initial_x}, {self.initial_y}\n")
|
532
548
|
f.write(f"Overlapping pixels to any side: {self.overlap}\n")
|
@@ -534,7 +550,9 @@ class RasterPlotter:
|
|
534
550
|
f.write(f"Special instructions:\n")
|
535
551
|
for key, value in self.special.items():
|
536
552
|
f.write(f" {key} = {value}\n")
|
537
|
-
f.write(
|
553
|
+
f.write(
|
554
|
+
"----------------------------------------------------------------------\n"
|
555
|
+
)
|
538
556
|
test_dict = {}
|
539
557
|
lastx = self.initial_x
|
540
558
|
lasty = self.initial_y
|
@@ -543,32 +561,48 @@ class RasterPlotter:
|
|
543
561
|
if lastx is not None:
|
544
562
|
dx = x - lastx
|
545
563
|
dy = y - lasty
|
546
|
-
if dx != 0 and dy != 0:
|
547
|
-
f.write
|
548
|
-
|
564
|
+
if dx != 0 and dy != 0: # and abs(dx) != abs(dy):
|
565
|
+
f.write(
|
566
|
+
f"You f**ed up! No zigzag movement from line {lineno - 1} to {lineno}: {lastx}, {lasty} -> {x}, {y}\n"
|
567
|
+
)
|
568
|
+
print(
|
569
|
+
f"You f**ed up! No zigzag movement from line {lineno - 1} to {lineno}: {lastx}, {lasty} -> {x}, {y}"
|
570
|
+
)
|
549
571
|
failed = True
|
550
572
|
lastx = x
|
551
573
|
lasty = y
|
552
574
|
if not failed:
|
553
575
|
f.write("Good news, no zig-zag movements identified!\n")
|
554
|
-
f.write(
|
576
|
+
f.write(
|
577
|
+
"----------------------------------------------------------------------\n"
|
578
|
+
)
|
555
579
|
for lineno, (x, y, on) in enumerate(data, start=1):
|
556
580
|
if x is None or y is None:
|
557
581
|
continue
|
558
582
|
key = f"{x} - {y}"
|
559
583
|
if key in test_dict:
|
560
|
-
f.write
|
584
|
+
f.write(
|
585
|
+
f"Duplicate coordinates in list at ({x}, {y})! 1st: #{test_dict[key][0]}, on={test_dict[key][1]}, 2nd: #{lineno}, on={on}\n"
|
586
|
+
)
|
561
587
|
has_duplicates += 1
|
562
588
|
else:
|
563
589
|
test_dict[key] = (lineno, on)
|
564
590
|
if has_duplicates:
|
565
|
-
f.write(
|
591
|
+
f.write(
|
592
|
+
"----------------------------------------------------------------------\n"
|
593
|
+
)
|
566
594
|
for lineno, (x, y, on) in enumerate(data, start=1):
|
567
595
|
f.write(f"{lineno}: {x}, {y}, {on}\n")
|
568
596
|
if has_duplicates:
|
569
|
-
print(
|
570
|
-
|
571
|
-
|
597
|
+
print(
|
598
|
+
f"Attention: the generated plot has {has_duplicates} duplicate coordinate values!"
|
599
|
+
)
|
600
|
+
print(
|
601
|
+
f"{'Bidirectional' if self.bidirectional else 'Unidirectional'} {'horizontal' if self.horizontal else 'vertical'} plot starting at {'top' if self.start_minimum_y else 'bottom'}-{'left' if self.start_minimum_x else 'right'}"
|
602
|
+
)
|
603
|
+
print(
|
604
|
+
f"Overscan: {self.overscan:.2f}, Stepx={step_x:.2f}, Stepy={step_y:.2f}"
|
605
|
+
)
|
572
606
|
print(f"Image dimensions: {self.width}x{self.height}")
|
573
607
|
else:
|
574
608
|
data = list(self._plot_pixels())
|
@@ -585,8 +619,22 @@ class RasterPlotter:
|
|
585
619
|
else:
|
586
620
|
nx = int(round(offset_x + step_x * x))
|
587
621
|
ny = int(round(offset_y + y * step_y))
|
588
|
-
self._distance_burn +=
|
589
|
-
|
622
|
+
self._distance_burn += (
|
623
|
+
0
|
624
|
+
if on == 0
|
625
|
+
else sqrt(
|
626
|
+
(nx - last_x) * (nx - last_x)
|
627
|
+
+ (ny - last_y) * (ny - last_y)
|
628
|
+
)
|
629
|
+
)
|
630
|
+
self._distance_travel += (
|
631
|
+
0
|
632
|
+
if on != 0
|
633
|
+
else sqrt(
|
634
|
+
(nx - last_x) * (nx - last_x)
|
635
|
+
+ (ny - last_y) * (ny - last_y)
|
636
|
+
)
|
637
|
+
)
|
590
638
|
yield nx, ny, on
|
591
639
|
last_x = nx
|
592
640
|
last_y = ny
|
@@ -598,8 +646,22 @@ class RasterPlotter:
|
|
598
646
|
else:
|
599
647
|
nx = round(offset_x + step_x * x)
|
600
648
|
ny = round(offset_y + y * step_y)
|
601
|
-
self._distance_burn +=
|
602
|
-
|
649
|
+
self._distance_burn += (
|
650
|
+
0
|
651
|
+
if on == 0
|
652
|
+
else sqrt(
|
653
|
+
(nx - last_x) * (nx - last_x)
|
654
|
+
+ (ny - last_y) * (ny - last_y)
|
655
|
+
)
|
656
|
+
)
|
657
|
+
self._distance_travel += (
|
658
|
+
0
|
659
|
+
if on != 0
|
660
|
+
else sqrt(
|
661
|
+
(nx - last_x) * (nx - last_x)
|
662
|
+
+ (ny - last_y) * (ny - last_y)
|
663
|
+
)
|
664
|
+
)
|
603
665
|
yield offset_x + step_x * x, offset_y + y * step_y, on
|
604
666
|
last_x = nx
|
605
667
|
last_y = ny
|
@@ -632,12 +694,12 @@ class RasterPlotter:
|
|
632
694
|
return
|
633
695
|
BLANK = 255
|
634
696
|
for y in range(self.height):
|
635
|
-
msg:str = f"{y:3d}: "
|
697
|
+
msg: str = f"{y:3d}: "
|
636
698
|
for x in range(self.width):
|
637
699
|
msg += "." if self.data[x, y] == BLANK else "X"
|
638
|
-
print
|
700
|
+
print(msg)
|
639
701
|
|
640
|
-
def _get_pixel_chains(self, xy:int, is_x
|
702
|
+
def _get_pixel_chains(self, xy: int, is_x: bool) -> list:
|
641
703
|
last_pixel = None
|
642
704
|
segments = []
|
643
705
|
upper = self.width if is_x else self.height
|
@@ -648,11 +710,11 @@ class RasterPlotter:
|
|
648
710
|
if on == last_pixel:
|
649
711
|
segments[-1][1] = idx
|
650
712
|
else:
|
651
|
-
segments.append
|
713
|
+
segments.append([idx, idx, on])
|
652
714
|
last_pixel = on
|
653
715
|
return segments
|
654
716
|
|
655
|
-
def _consume_pixel_chains(self, segments:list, xy:int, is_x
|
717
|
+
def _consume_pixel_chains(self, segments: list, xy: int, is_x: bool):
|
656
718
|
BLANK = 255
|
657
719
|
# for x in range(5):
|
658
720
|
# msg1 = f"{x}: "
|
@@ -727,7 +789,11 @@ class RasterPlotter:
|
|
727
789
|
# We consider as well the overscan value
|
728
790
|
overscan_top = 0 if dy >= 0 else self.overscan
|
729
791
|
overscan_bottom = 0 if dy <= 0 else self.overscan
|
730
|
-
if not first and (
|
792
|
+
if not first and (
|
793
|
+
segments[0][0] - overscan_top
|
794
|
+
<= last_y
|
795
|
+
<= segments[-1][1] + overscan_bottom
|
796
|
+
):
|
731
797
|
# inside the chain!
|
732
798
|
# So lets move a bit to the side
|
733
799
|
if dy > 0:
|
@@ -814,7 +880,11 @@ class RasterPlotter:
|
|
814
880
|
# We consider as well the overscan value
|
815
881
|
overscan_left = 0 if dx >= 0 else self.overscan
|
816
882
|
overscan_right = 0 if dx <= 0 else self.overscan
|
817
|
-
if not first and (
|
883
|
+
if not first and (
|
884
|
+
segments[0][0] - overscan_left
|
885
|
+
<= last_x
|
886
|
+
<= segments[-1][1] + overscan_right
|
887
|
+
):
|
818
888
|
# inside the chain!
|
819
889
|
# So lets move a bit to the side
|
820
890
|
if dx > 0:
|
@@ -1021,12 +1091,14 @@ class RasterPlotter:
|
|
1021
1091
|
"""
|
1022
1092
|
|
1023
1093
|
def walk_segments(segments, horizontal=True, xy_penalty=1, width=1, height=1):
|
1024
|
-
n:int = len(segments)
|
1094
|
+
n: int = len(segments)
|
1025
1095
|
visited = np.zeros(n, dtype=bool)
|
1026
1096
|
path = []
|
1027
1097
|
window_size = 10
|
1028
1098
|
current_point = np.array(segments[0][0], dtype=float)
|
1029
|
-
segment_points = np.array(
|
1099
|
+
segment_points = np.array(
|
1100
|
+
[point for segment in segments for point in segment], dtype=float
|
1101
|
+
)
|
1030
1102
|
mask = ~visited.repeat(2)
|
1031
1103
|
while len(path) < n:
|
1032
1104
|
# Find the range of segments within the x- and y-window
|
@@ -1035,17 +1107,19 @@ class RasterPlotter:
|
|
1035
1107
|
y_min = current_point[1] - window_size
|
1036
1108
|
y_max = current_point[1] + window_size
|
1037
1109
|
unvisited_indices = np.where(
|
1038
|
-
(segment_points[:, 0] >= x_min)
|
1039
|
-
(segment_points[:, 0] <= x_max)
|
1040
|
-
(segment_points[:, 1] >= y_min)
|
1041
|
-
(segment_points[:, 1] <= y_max)
|
1042
|
-
mask
|
1110
|
+
(segment_points[:, 0] >= x_min)
|
1111
|
+
& (segment_points[:, 0] <= x_max)
|
1112
|
+
& (segment_points[:, 1] >= y_min)
|
1113
|
+
& (segment_points[:, 1] <= y_max)
|
1114
|
+
& mask
|
1043
1115
|
)[0]
|
1044
1116
|
if len(unvisited_indices) == 0:
|
1045
1117
|
# If no segments are within the window, expand the window
|
1046
1118
|
window_size *= 2
|
1047
1119
|
# print (f"Did not find points: now window: {window_size}")
|
1048
|
-
if
|
1120
|
+
if (
|
1121
|
+
window_size <= 2 * height or window_size <= 2 * width
|
1122
|
+
): # Safety belt
|
1049
1123
|
continue
|
1050
1124
|
|
1051
1125
|
unvisited_points = segment_points[unvisited_indices]
|
@@ -1053,11 +1127,11 @@ class RasterPlotter:
|
|
1053
1127
|
# distances = distance_matrix(unvisited_points, current_point, y_penalty)
|
1054
1128
|
diff = unvisited_points - current_point
|
1055
1129
|
if horizontal:
|
1056
|
-
diff[:, 1] *= xy_penalty
|
1130
|
+
diff[:, 1] *= xy_penalty # Apply penalty to y-distances
|
1057
1131
|
else:
|
1058
|
-
diff[:, 0] *= xy_penalty
|
1132
|
+
diff[:, 0] *= xy_penalty # Apply penalty to x-distances
|
1059
1133
|
|
1060
|
-
distances = np.sum(diff
|
1134
|
+
distances = np.sum(diff**2, axis=1) # Return squared distances
|
1061
1135
|
|
1062
1136
|
min_distance_idx = np.argmin(distances)
|
1063
1137
|
next_segment = unvisited_indices[min_distance_idx] // 2
|
@@ -1068,12 +1142,16 @@ class RasterPlotter:
|
|
1068
1142
|
mask[2 * next_segment] = False
|
1069
1143
|
mask[2 * next_segment + 1] = False
|
1070
1144
|
if min_distance_idx % 2 == 0:
|
1071
|
-
path.append((next_segment,
|
1072
|
-
current_point = segment_points[
|
1145
|
+
path.append((next_segment, "end"))
|
1146
|
+
current_point = segment_points[
|
1147
|
+
next_segment * 2 + 1
|
1148
|
+
] # Move to the other endpoint
|
1073
1149
|
else:
|
1074
|
-
path.append((next_segment,
|
1075
|
-
current_point = segment_points[
|
1076
|
-
|
1150
|
+
path.append((next_segment, "start"))
|
1151
|
+
current_point = segment_points[
|
1152
|
+
next_segment * 2
|
1153
|
+
] # Move to the other endpoint
|
1154
|
+
window_size = 10 # Reset window size
|
1077
1155
|
|
1078
1156
|
return path
|
1079
1157
|
|
@@ -1093,14 +1171,16 @@ class RasterPlotter:
|
|
1093
1171
|
line_parts = []
|
1094
1172
|
on_parts = []
|
1095
1173
|
if self.debug_level > 2:
|
1096
|
-
print
|
1174
|
+
print(
|
1175
|
+
f"{'horizontal' if horizontal else 'Vertical'} for {self.width}x{self.height} image. {'y' if horizontal else 'x'} from {lower} to {upper}"
|
1176
|
+
)
|
1097
1177
|
if horizontal:
|
1098
1178
|
while lower <= y <= upper:
|
1099
1179
|
segments = self._get_pixel_chains(y, True)
|
1100
1180
|
self._consume_pixel_chains(segments, y, True)
|
1101
1181
|
for seg in segments:
|
1102
1182
|
# Append (xstart, y), (xend, y), on
|
1103
|
-
line_parts.append(
|
1183
|
+
line_parts.append(((seg[0], y), (seg[1], y)))
|
1104
1184
|
on_parts.append(seg[2])
|
1105
1185
|
y += dy
|
1106
1186
|
else:
|
@@ -1109,14 +1189,20 @@ class RasterPlotter:
|
|
1109
1189
|
self._consume_pixel_chains(segments, x, False)
|
1110
1190
|
for seg in segments:
|
1111
1191
|
# Append (xstart, y), (xend, y), on
|
1112
|
-
line_parts.append(
|
1192
|
+
line_parts.append(((x, seg[0]), (x, seg[1])))
|
1113
1193
|
on_parts.append(seg[2])
|
1114
1194
|
x += dx
|
1115
1195
|
if self.debug_level > 2:
|
1116
|
-
print
|
1196
|
+
print(f"Created {len(line_parts)} segments")
|
1117
1197
|
t1 = perf_counter()
|
1118
1198
|
penalty = 3 if self.special.get("gantry", False) else 1
|
1119
|
-
path = walk_segments(
|
1199
|
+
path = walk_segments(
|
1200
|
+
line_parts,
|
1201
|
+
horizontal=horizontal,
|
1202
|
+
xy_penalty=penalty,
|
1203
|
+
width=self.width,
|
1204
|
+
height=self.height,
|
1205
|
+
)
|
1120
1206
|
# print("Order of segments:", path)
|
1121
1207
|
t2 = perf_counter()
|
1122
1208
|
if horizontal:
|
@@ -1171,14 +1257,16 @@ class RasterPlotter:
|
|
1171
1257
|
last_y = ey
|
1172
1258
|
t3 = perf_counter()
|
1173
1259
|
if self.debug_level > 1:
|
1174
|
-
print
|
1175
|
-
|
1260
|
+
print(
|
1261
|
+
f"Overall time for {'horizontal' if horizontal else 'vertical'} consumption: {t3-t0:.2f}s - created: {len(line_parts)} segments"
|
1262
|
+
)
|
1263
|
+
print(
|
1264
|
+
f"Computation: {t2-t0:.2f}s - Chain creation:{t1 - t0:.2f}s, Walk: {t2 - t1:.2f}s"
|
1265
|
+
)
|
1176
1266
|
self.final_x = last_x
|
1177
1267
|
self.final_y = last_y
|
1178
1268
|
|
1179
1269
|
def _plot_spiral(self):
|
1180
|
-
|
1181
|
-
|
1182
1270
|
rows = self.height
|
1183
1271
|
cols = self.width
|
1184
1272
|
center_row, center_col = rows // 2, cols // 2
|
@@ -1223,7 +1311,7 @@ class RasterPlotter:
|
|
1223
1311
|
if on == last_pixel and len(segments):
|
1224
1312
|
segments[-1][1] = (col, row)
|
1225
1313
|
else:
|
1226
|
-
segments.append
|
1314
|
+
segments.append([(col, row), (col, row), on])
|
1227
1315
|
|
1228
1316
|
last_pixel = on
|
1229
1317
|
count += 1
|
@@ -1255,7 +1343,7 @@ class RasterPlotter:
|
|
1255
1343
|
sy = min(start_y, end_y)
|
1256
1344
|
ey = max(start_y, end_y)
|
1257
1345
|
|
1258
|
-
if direction_index in (0, 2):
|
1346
|
+
if direction_index in (0, 2): # horizontal
|
1259
1347
|
for y_idx in range(-self.overlap, self.overlap + 1):
|
1260
1348
|
ny = sy + y_idx
|
1261
1349
|
for nx in range(sx, ex + 1):
|
@@ -1268,11 +1356,9 @@ class RasterPlotter:
|
|
1268
1356
|
if 0 <= nx < self.width and 0 <= ny < self.height:
|
1269
1357
|
self.data[nx, ny] = BLANK
|
1270
1358
|
|
1271
|
-
|
1272
1359
|
direction_index = (direction_index + 1) % 4
|
1273
1360
|
steps += 1
|
1274
1361
|
|
1275
|
-
|
1276
1362
|
def _plot_crossover(self):
|
1277
1363
|
"""
|
1278
1364
|
This algorithm scans through the image looking for the row or the column with the most pixels.
|
@@ -1289,8 +1375,8 @@ class RasterPlotter:
|
|
1289
1375
|
Yields:
|
1290
1376
|
list of tuples with (x, y, on)
|
1291
1377
|
"""
|
1292
|
-
ROW=0
|
1293
|
-
COL=1
|
1378
|
+
ROW = 0
|
1379
|
+
COL = 1
|
1294
1380
|
|
1295
1381
|
def process_image(image):
|
1296
1382
|
# We will modify the image to keep track of deleted rows and columns
|
@@ -1373,28 +1459,30 @@ class RasterPlotter:
|
|
1373
1459
|
# msg = f"{msg}{'X' if on else '.'}"
|
1374
1460
|
if on:
|
1375
1461
|
if not covered_col[idx]:
|
1376
|
-
covered_col[idx] = None
|
1462
|
+
covered_col[idx] = None # needs recalc
|
1377
1463
|
if on == last_pixel:
|
1378
1464
|
segments[-1][1] = idx
|
1379
1465
|
else:
|
1380
|
-
segments.append
|
1466
|
+
segments.append([idx, idx, on])
|
1381
1467
|
last_pixel = on
|
1382
|
-
results.append(
|
1468
|
+
results.append(
|
1469
|
+
(COL, rowidx, segments)
|
1470
|
+
) # Intentionally so, as the numpy array has x and y exchanged
|
1383
1471
|
# print (f"Col #{rowidx}: {msg} -> {segments}")
|
1384
1472
|
|
1385
1473
|
# Clear the column
|
1386
|
-
image[rowidx
|
1474
|
+
image[rowidx, :] = 0
|
1387
1475
|
covered_row[rowidx] = True
|
1388
1476
|
stored_row[rowidx] = 0
|
1389
1477
|
for rc in range(self.overlap):
|
1390
1478
|
r = rowidx - rc
|
1391
1479
|
if 0 <= r < rows:
|
1392
|
-
image[r
|
1480
|
+
image[r, :] = 0
|
1393
1481
|
covered_row[r] = True
|
1394
1482
|
stored_row[r] = 0
|
1395
1483
|
r = rowidx + rc
|
1396
1484
|
if 0 <= r < rows:
|
1397
|
-
image[r
|
1485
|
+
image[r, :] = 0
|
1398
1486
|
covered_row[r] = True
|
1399
1487
|
stored_row[r] = 0
|
1400
1488
|
recalc_col = True
|
@@ -1407,11 +1495,11 @@ class RasterPlotter:
|
|
1407
1495
|
# msg = f"{msg}{'X' if on else '.'}"
|
1408
1496
|
if on:
|
1409
1497
|
if not covered_row[idx]:
|
1410
|
-
covered_row[idx] = None
|
1498
|
+
covered_row[idx] = None # needs recalc
|
1411
1499
|
if on == last_pixel:
|
1412
1500
|
segments[-1][1] = idx
|
1413
1501
|
else:
|
1414
|
-
segments.append
|
1502
|
+
segments.append([idx, idx, on])
|
1415
1503
|
last_pixel = on
|
1416
1504
|
results.append((ROW, colidx, segments))
|
1417
1505
|
# print (f"Row #{colidx}: {msg} -> {segments}")
|
@@ -1437,7 +1525,7 @@ class RasterPlotter:
|
|
1437
1525
|
for ridx in range(rows):
|
1438
1526
|
on = image[ridx, cidx]
|
1439
1527
|
msg = f"{msg}{'X' if on else '.'}"
|
1440
|
-
print
|
1528
|
+
print(f"{cidx:3d}: {msg}")
|
1441
1529
|
|
1442
1530
|
return results
|
1443
1531
|
|
@@ -1448,7 +1536,7 @@ class RasterPlotter:
|
|
1448
1536
|
for x in range(self.width):
|
1449
1537
|
for y in range(self.height):
|
1450
1538
|
px = self.px(x, y)
|
1451
|
-
if px==self.skip_pixel:
|
1539
|
+
if px == self.skip_pixel:
|
1452
1540
|
px = 0
|
1453
1541
|
image[x, y] = px
|
1454
1542
|
t1 = perf_counter()
|
@@ -1528,7 +1616,7 @@ class RasterPlotter:
|
|
1528
1616
|
if self.bidirectional:
|
1529
1617
|
if mode == ROW:
|
1530
1618
|
dx = -dx
|
1531
|
-
else:
|
1619
|
+
else: # column
|
1532
1620
|
dy = -dy
|
1533
1621
|
|
1534
1622
|
# We need to set the final values so that the rastercut is able to carry on
|
@@ -1536,8 +1624,11 @@ class RasterPlotter:
|
|
1536
1624
|
self.final_y = last_y
|
1537
1625
|
t3 = perf_counter()
|
1538
1626
|
if self.debug_level > 1:
|
1539
|
-
print
|
1540
|
-
print
|
1627
|
+
print(f"Overall time for crossover consumption: {t3-t0:.2f}s")
|
1628
|
+
print(
|
1629
|
+
f"Computation: {t2 - t0:.2f}s - Array creation:{t1 - t0:.2f}s, Algorithm: {t2 - t1:.2f}s"
|
1630
|
+
)
|
1631
|
+
|
1541
1632
|
"""
|
1542
1633
|
# Testpattern generation
|
1543
1634
|
def testpattern_generator(self):
|
@@ -1657,4 +1748,4 @@ class RasterPlotter:
|
|
1657
1748
|
yield from methods[method]()
|
1658
1749
|
except IndexError:
|
1659
1750
|
print (f"Unknown testgenerator for {self.direction}")
|
1660
|
-
"""
|
1751
|
+
"""
|