meerk40t 0.9.2000__py2.py3-none-any.whl → 0.9.3001__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/balor_params.py +1 -43
- meerk40t/balormk/controller.py +1 -41
- meerk40t/balormk/device.py +16 -22
- meerk40t/balormk/driver.py +4 -4
- meerk40t/balormk/gui/balorconfig.py +2 -2
- meerk40t/balormk/gui/balorcontroller.py +13 -5
- meerk40t/balormk/gui/baloroperationproperties.py +0 -46
- meerk40t/balormk/gui/gui.py +17 -17
- meerk40t/camera/gui/camerapanel.py +18 -11
- meerk40t/core/cutcode/rastercut.py +3 -1
- meerk40t/core/cutplan.py +145 -14
- meerk40t/core/elements/clipboard.py +18 -9
- meerk40t/core/elements/element_treeops.py +320 -180
- meerk40t/core/elements/element_types.py +7 -2
- meerk40t/core/elements/elements.py +53 -27
- meerk40t/core/elements/geometry.py +8 -0
- meerk40t/core/elements/offset_clpr.py +129 -4
- meerk40t/core/elements/offset_mk.py +3 -1
- meerk40t/core/elements/shapes.py +28 -25
- meerk40t/core/laserjob.py +7 -0
- meerk40t/core/node/bootstrap.py +4 -0
- meerk40t/core/node/effect_hatch.py +85 -96
- meerk40t/core/node/effect_wobble.py +309 -0
- meerk40t/core/node/elem_image.py +49 -19
- meerk40t/core/node/elem_line.py +60 -0
- meerk40t/core/node/elem_rect.py +5 -3
- meerk40t/core/node/image_processed.py +766 -0
- meerk40t/core/node/image_raster.py +113 -0
- meerk40t/core/node/node.py +120 -1
- meerk40t/core/node/op_cut.py +2 -8
- meerk40t/core/node/op_dots.py +0 -8
- meerk40t/core/node/op_engrave.py +2 -18
- meerk40t/core/node/op_image.py +22 -35
- meerk40t/core/node/op_raster.py +0 -9
- meerk40t/core/planner.py +32 -2
- meerk40t/core/svg_io.py +699 -461
- meerk40t/core/treeop.py +191 -0
- meerk40t/core/undos.py +15 -1
- meerk40t/core/units.py +14 -4
- meerk40t/device/dummydevice.py +3 -2
- meerk40t/device/gui/defaultactions.py +43 -55
- meerk40t/device/gui/formatterpanel.py +58 -49
- meerk40t/device/gui/warningpanel.py +12 -12
- meerk40t/device/mixins.py +13 -0
- meerk40t/dxf/dxf_io.py +9 -5
- meerk40t/extra/ezd.py +28 -26
- meerk40t/extra/imageactions.py +300 -308
- meerk40t/extra/lbrn.py +19 -2
- meerk40t/fill/fills.py +6 -6
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +2 -6
- meerk40t/grbl/controller.py +168 -52
- meerk40t/grbl/device.py +23 -18
- meerk40t/grbl/driver.py +39 -0
- meerk40t/grbl/emulator.py +79 -19
- meerk40t/grbl/gcodejob.py +10 -0
- meerk40t/grbl/gui/grblconfiguration.py +2 -2
- meerk40t/grbl/gui/grblcontroller.py +24 -8
- meerk40t/grbl/gui/grblhardwareconfig.py +153 -0
- meerk40t/grbl/gui/gui.py +17 -14
- meerk40t/grbl/mock_connection.py +15 -34
- meerk40t/grbl/plugin.py +0 -4
- meerk40t/grbl/serial_connection.py +2 -1
- meerk40t/gui/about.py +8 -5
- meerk40t/gui/alignment.py +10 -6
- meerk40t/gui/basicops.py +27 -17
- meerk40t/gui/bufferview.py +2 -2
- meerk40t/gui/choicepropertypanel.py +101 -13
- meerk40t/gui/consolepanel.py +12 -9
- meerk40t/gui/devicepanel.py +38 -25
- meerk40t/gui/executejob.py +6 -4
- meerk40t/gui/help_assets/help_assets.py +13 -10
- meerk40t/gui/hersheymanager.py +8 -6
- meerk40t/gui/icons.py +1951 -3065
- meerk40t/gui/imagesplitter.py +14 -7
- meerk40t/gui/keymap.py +3 -3
- meerk40t/gui/laserpanel.py +151 -84
- meerk40t/gui/laserrender.py +61 -70
- meerk40t/gui/lasertoolpanel.py +8 -7
- meerk40t/gui/materialtest.py +3 -3
- meerk40t/gui/mkdebug.py +254 -1
- meerk40t/gui/navigationpanels.py +321 -180
- meerk40t/gui/notes.py +3 -3
- meerk40t/gui/opassignment.py +12 -12
- meerk40t/gui/operation_info.py +13 -13
- meerk40t/gui/plugin.py +5 -0
- meerk40t/gui/position.py +20 -18
- meerk40t/gui/preferences.py +21 -6
- meerk40t/gui/propertypanels/attributes.py +70 -22
- meerk40t/gui/propertypanels/blobproperty.py +2 -2
- meerk40t/gui/propertypanels/consoleproperty.py +2 -2
- meerk40t/gui/propertypanels/groupproperties.py +3 -3
- meerk40t/gui/propertypanels/hatchproperty.py +11 -18
- meerk40t/gui/propertypanels/imageproperty.py +4 -3
- meerk40t/gui/propertypanels/opbranchproperties.py +1 -1
- meerk40t/gui/propertypanels/pathproperty.py +2 -2
- meerk40t/gui/propertypanels/pointproperty.py +2 -2
- meerk40t/gui/propertypanels/propertywindow.py +4 -4
- meerk40t/gui/propertypanels/textproperty.py +3 -3
- meerk40t/gui/propertypanels/wobbleproperty.py +204 -0
- meerk40t/gui/ribbon.py +367 -259
- meerk40t/gui/scene/scene.py +31 -5
- meerk40t/gui/scenewidgets/elementswidget.py +12 -4
- meerk40t/gui/scenewidgets/gridwidget.py +2 -2
- meerk40t/gui/scenewidgets/laserpathwidget.py +7 -2
- meerk40t/gui/scenewidgets/machineoriginwidget.py +6 -2
- meerk40t/gui/scenewidgets/relocatewidget.py +1 -1
- meerk40t/gui/scenewidgets/reticlewidget.py +9 -0
- meerk40t/gui/scenewidgets/selectionwidget.py +12 -7
- meerk40t/gui/simpleui.py +95 -8
- meerk40t/gui/simulation.py +44 -36
- meerk40t/gui/spoolerpanel.py +124 -26
- meerk40t/gui/statusbarwidgets/defaultoperations.py +18 -6
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/statusbarwidgets/opassignwidget.py +12 -12
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +45 -18
- meerk40t/gui/statusbarwidgets/statusbar.py +11 -4
- meerk40t/gui/themes.py +78 -0
- meerk40t/gui/toolwidgets/toolcircle.py +2 -1
- meerk40t/gui/toolwidgets/toolellipse.py +2 -1
- meerk40t/gui/toolwidgets/toolimagecut.py +132 -0
- meerk40t/gui/toolwidgets/toolline.py +144 -0
- meerk40t/gui/toolwidgets/toolnodeedit.py +72 -145
- meerk40t/gui/toolwidgets/toolpoint.py +1 -1
- meerk40t/gui/toolwidgets/toolpolygon.py +8 -55
- meerk40t/gui/toolwidgets/toolrect.py +2 -1
- meerk40t/gui/usbconnect.py +2 -2
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +2 -2
- meerk40t/gui/utilitywidgets/harmonograph.py +7 -7
- meerk40t/gui/utilitywidgets/scalewidget.py +1 -1
- meerk40t/gui/wordlisteditor.py +33 -18
- meerk40t/gui/wxmeerk40t.py +166 -66
- meerk40t/gui/wxmmain.py +236 -157
- meerk40t/gui/wxmribbon.py +49 -25
- meerk40t/gui/wxmscene.py +49 -38
- meerk40t/gui/wxmtree.py +216 -85
- meerk40t/gui/wxutils.py +62 -4
- meerk40t/image/imagetools.py +443 -15
- meerk40t/internal_plugins.py +2 -10
- meerk40t/kernel/kernel.py +12 -4
- meerk40t/lihuiyu/controller.py +7 -7
- meerk40t/lihuiyu/device.py +3 -1
- meerk40t/lihuiyu/driver.py +3 -0
- meerk40t/lihuiyu/gui/gui.py +8 -8
- meerk40t/lihuiyu/gui/lhyaccelgui.py +2 -2
- meerk40t/lihuiyu/gui/lhycontrollergui.py +73 -27
- meerk40t/lihuiyu/gui/lhydrivergui.py +2 -2
- meerk40t/lihuiyu/gui/tcpcontroller.py +22 -9
- meerk40t/main.py +6 -1
- meerk40t/moshi/controller.py +5 -5
- meerk40t/moshi/device.py +5 -2
- meerk40t/moshi/driver.py +4 -0
- meerk40t/moshi/gui/gui.py +8 -8
- meerk40t/moshi/gui/moshicontrollergui.py +24 -8
- meerk40t/moshi/gui/moshidrivergui.py +2 -2
- meerk40t/newly/controller.py +2 -0
- meerk40t/newly/device.py +9 -2
- meerk40t/newly/driver.py +4 -0
- meerk40t/newly/gui/gui.py +16 -17
- meerk40t/newly/gui/newlyconfig.py +2 -2
- meerk40t/newly/gui/newlycontroller.py +13 -5
- meerk40t/rotary/gui/gui.py +2 -2
- meerk40t/rotary/gui/rotarysettings.py +2 -2
- meerk40t/ruida/device.py +3 -0
- meerk40t/ruida/driver.py +4 -0
- meerk40t/ruida/gui/gui.py +6 -6
- meerk40t/ruida/gui/ruidaconfig.py +2 -2
- meerk40t/ruida/gui/ruidacontroller.py +13 -5
- meerk40t/svgelements.py +9 -9
- meerk40t/tools/geomstr.py +849 -153
- meerk40t/tools/kerftest.py +8 -4
- meerk40t/tools/livinghinges.py +15 -8
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/METADATA +21 -16
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/RECORD +185 -177
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/entry_points.txt +0 -1
- test/test_core_elements.py +8 -24
- test/test_file_svg.py +88 -0
- test/test_fill.py +9 -9
- test/test_geomstr.py +258 -8
- test/test_kernel.py +4 -0
- test/test_tools_rasterplotter.py +29 -0
- meerk40t/extra/embroider.py +0 -56
- meerk40t/extra/pathoptimize.py +0 -249
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/zip-safe +0 -0
test/test_core_elements.py
CHANGED
@@ -70,17 +70,10 @@ class TestElements(unittest.TestCase):
|
|
70
70
|
kernel_root = kernel.get_context("/")
|
71
71
|
kernel_root("circle 1in 1in 1in\n")
|
72
72
|
for node in kernel_root.elements.elems():
|
73
|
-
|
74
|
-
self.assertEqual(
|
75
|
-
|
76
|
-
|
77
|
-
center=(1000 * UNITS_PER_MIL, 1000 * UNITS_PER_MIL),
|
78
|
-
r=1000 * UNITS_PER_MIL,
|
79
|
-
stroke_width=shape.stroke_width,
|
80
|
-
fill=shape.fill,
|
81
|
-
stroke=shape.stroke,
|
82
|
-
),
|
83
|
-
)
|
73
|
+
self.assertEqual(node.cx, 1000 * UNITS_PER_MIL)
|
74
|
+
self.assertEqual(node.cy, 1000 * UNITS_PER_MIL)
|
75
|
+
self.assertEqual(node.rx, 1000 * UNITS_PER_MIL)
|
76
|
+
self.assertEqual(node.ry, 1000 * UNITS_PER_MIL)
|
84
77
|
self.assertEqual(node.stroke, "blue")
|
85
78
|
finally:
|
86
79
|
kernel.shutdown()
|
@@ -96,19 +89,10 @@ class TestElements(unittest.TestCase):
|
|
96
89
|
kernel_root = kernel.get_context("/")
|
97
90
|
kernel_root("rect 1in 1in 1in 1in stroke red fill blue\n")
|
98
91
|
for node in kernel_root.elements.elems():
|
99
|
-
|
100
|
-
self.assertEqual(
|
101
|
-
|
102
|
-
|
103
|
-
1000 * UNITS_PER_MIL,
|
104
|
-
1000 * UNITS_PER_MIL,
|
105
|
-
1000 * UNITS_PER_MIL,
|
106
|
-
1000 * UNITS_PER_MIL,
|
107
|
-
stroke_width=shape.stroke_width,
|
108
|
-
fill=shape.fill,
|
109
|
-
stroke=shape.stroke,
|
110
|
-
),
|
111
|
-
)
|
92
|
+
self.assertEqual(node.x, 1000 * UNITS_PER_MIL)
|
93
|
+
self.assertEqual(node.y, 1000 * UNITS_PER_MIL)
|
94
|
+
self.assertEqual(node.width, 1000 * UNITS_PER_MIL)
|
95
|
+
self.assertEqual(node.height, 1000 * UNITS_PER_MIL)
|
112
96
|
self.assertEqual(node.stroke, "red")
|
113
97
|
self.assertEqual(node.fill, "blue")
|
114
98
|
finally:
|
test/test_file_svg.py
CHANGED
@@ -86,3 +86,91 @@ class TestFileSVG(unittest.TestCase):
|
|
86
86
|
self.assertIsInstance(node_copy.speed, float)
|
87
87
|
finally:
|
88
88
|
kernel.shutdown()
|
89
|
+
|
90
|
+
def test_load_092_operations(self):
|
91
|
+
"""
|
92
|
+
test to ensure that `meerk40t:operation` types load.
|
93
|
+
"""
|
94
|
+
file1 = "test-ops.svg"
|
95
|
+
self.addCleanup(os.remove, file1)
|
96
|
+
with open(file1, "w") as f:
|
97
|
+
f.write(
|
98
|
+
"""
|
99
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:meerk40t="https://github.com/meerk40t/meerk40t/wiki/Namespace" width="400.0mm" height="430.0mm" viewBox="0 0 1032047 1109450">
|
100
|
+
<meerk40t:operation type="op raster" lock="False" speed="150.0" dpi="500" color="black" frequency="30.0" stopop="False" allowed_attributes="[]" power="1000" dwell_time="50.0" id="meerk40t:3" />
|
101
|
+
<meerk40t:operation type="op engrave" label="Engrave (100%, 20mm/s)" lock="False" id="E1" speed="20" power="1000" allowed_attributes="['stroke']" color="#0000ff00" dangerous="False" output="True" dwell_time="50.0" references="meerk40t:6" />
|
102
|
+
<meerk40t:operation type="op cut" label="Cut (100%, 1mm/s)" lock="False" speed="1.0" color="#ff000000" frequency="30.0" kerf="0" allowed_attributes="['stroke']" power="1000" dwell_time="50.0" id="C1" />
|
103
|
+
<ellipse cx="550118.9389283657" cy="363374.1254904113" rx="96436.11909338088" ry="96436.11909338088" stroke_scale="False" type="elem ellipse" id="meerk40t:6" lock="False" mkparam="('circle', 0, 550118.9389283657, 363374.1254904113, 0, 618309.5726906088, 295183.49172816816)" vector-effect="non-scaling-stroke" fill-rule="evenodd" stroke="#0000ff" stroke-width="1000.0" fill="none" />
|
104
|
+
</svg>"""
|
105
|
+
)
|
106
|
+
|
107
|
+
kernel = bootstrap.bootstrap()
|
108
|
+
try:
|
109
|
+
ob = kernel.elements.op_branch
|
110
|
+
kernel.console("operation* delete\n")
|
111
|
+
kernel.console("element* delete\n")
|
112
|
+
kernel.console(f"load {file1}\n")
|
113
|
+
self.assertEqual(len(list(ob.flat(types="op raster"))), 1)
|
114
|
+
self.assertEqual(len(list(ob.flat(types="op engrave"))), 1)
|
115
|
+
self.assertEqual(len(list(ob.flat(types="op cut"))), 1)
|
116
|
+
finally:
|
117
|
+
kernel.shutdown()
|
118
|
+
|
119
|
+
def test_load_hatch_op(self):
|
120
|
+
"""
|
121
|
+
test to ensure that `meerk40t:operation` op hatch loading.
|
122
|
+
"""
|
123
|
+
file1 = "test-hatch.svg"
|
124
|
+
self.addCleanup(os.remove, file1)
|
125
|
+
with open(file1, "w") as f:
|
126
|
+
f.write(
|
127
|
+
"""
|
128
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:meerk40t="https://github.com/meerk40t/meerk40t/wiki/Namespace" width="400.0mm" height="430.0mm" viewBox="0 0 1032047 1109450">
|
129
|
+
<meerk40t:operation type="op hatch" label="Engrave (100%, 20mm/s)" lock="False" id="E1" speed="20" power="1000" allowed_attributes="['stroke']" color="#0000ff00" dangerous="False" output="True" dwell_time="50.0" references="meerk40t:6" />
|
130
|
+
</svg>"""
|
131
|
+
)
|
132
|
+
|
133
|
+
kernel = bootstrap.bootstrap()
|
134
|
+
try:
|
135
|
+
ob = kernel.elements.op_branch
|
136
|
+
kernel.console("operation* delete\n")
|
137
|
+
kernel.console("element* delete\n")
|
138
|
+
kernel.console(f"load {file1}\n")
|
139
|
+
self.assertEqual(len(list(ob.flat(types="op raster"))), 0)
|
140
|
+
self.assertEqual(len(list(ob.flat(types="op engrave"))), 1)
|
141
|
+
self.assertEqual(len(list(ob.flat(types="op cut"))), 0)
|
142
|
+
self.assertEqual(len(list(ob.flat(types="effect hatch"))), 1)
|
143
|
+
finally:
|
144
|
+
kernel.shutdown()
|
145
|
+
|
146
|
+
def test_load_wobble(self):
|
147
|
+
"""
|
148
|
+
test to ensure that `meerk40t:operation` types load.
|
149
|
+
"""
|
150
|
+
file1 = "test-wobble.svg"
|
151
|
+
self.addCleanup(os.remove, file1)
|
152
|
+
with open(file1, "w") as f:
|
153
|
+
f.write(
|
154
|
+
"""
|
155
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:meerk40t="https://github.com/meerk40t/meerk40t/wiki/Namespace" width="400.0mm" height="430.0mm" viewBox="0 0 1032047 1109450">
|
156
|
+
<g type="op engrave" label="Engrave (100%, 20mm/s)" lock="False" id="E1" speed="20" power="1000" allowed_attributes="['stroke']" color="#0000ff00" dangerous="False" output="True" dwell_time="50.0" references="meerk40t:6">
|
157
|
+
<g type="effect wobble" lock="False" output="True" />
|
158
|
+
</g>
|
159
|
+
<ellipse cx="550118.9389283657" cy="363374.1254904113" rx="96436.11909338088" ry="96436.11909338088" stroke_scale="False" type="elem ellipse" id="meerk40t:6" lock="False" mkparam="('circle', 0, 550118.9389283657, 363374.1254904113, 0, 618309.5726906088, 295183.49172816816)" vector-effect="non-scaling-stroke" fill-rule="evenodd" stroke="#0000ff" stroke-width="1000.0" fill="none" />
|
160
|
+
</svg>"""
|
161
|
+
)
|
162
|
+
|
163
|
+
kernel = bootstrap.bootstrap()
|
164
|
+
try:
|
165
|
+
ob = kernel.elements.op_branch
|
166
|
+
kernel.console("operation* delete\n")
|
167
|
+
kernel.console("element* delete\n")
|
168
|
+
kernel.console(f"load {file1}\n")
|
169
|
+
self.assertEqual(len(list(ob.flat(types="op raster"))), 0)
|
170
|
+
engrave = list(ob.flat(types="op engrave"))
|
171
|
+
self.assertEqual(len(engrave), 1)
|
172
|
+
self.assertEqual(len(list(ob.flat(types="op cut"))), 0)
|
173
|
+
self.assertEqual(len(list(ob.flat(types="effect wobble"))), 1)
|
174
|
+
self.assertEqual(len(list(engrave[0].flat(types="effect wobble"))), 1)
|
175
|
+
finally:
|
176
|
+
kernel.shutdown()
|
test/test_fill.py
CHANGED
@@ -79,13 +79,13 @@ class TestFill(unittest.TestCase):
|
|
79
79
|
kernel.console("rect 0 0 1in 1in\n")
|
80
80
|
kernel.console("operation* delete\n")
|
81
81
|
kernel.console("hatch\n")
|
82
|
-
|
83
|
-
|
82
|
+
hatch_op = list(kernel.elements.ops())[0]
|
84
83
|
rect = list(kernel.elements.elems())[0]
|
85
|
-
hatch.add_reference(rect)
|
86
84
|
|
87
|
-
|
88
|
-
|
85
|
+
# Add rect refences into hatch.
|
86
|
+
hatch_op.children[0].add_reference(rect)
|
87
|
+
|
88
|
+
hatch_copy = hatch_op.copy_with_reified_tree()
|
89
89
|
|
90
90
|
hatch_effect = hatch_copy.children[0]
|
91
91
|
shape = hatch_effect.as_geometry()
|
@@ -108,13 +108,13 @@ class TestFill(unittest.TestCase):
|
|
108
108
|
|
109
109
|
ops = list(kernel.elements.ops())
|
110
110
|
hatch = ops[0]
|
111
|
+
hatch_effect = hatch.children[0]
|
111
112
|
rect0 = list(kernel.elements.elems())[0]
|
112
|
-
|
113
|
+
hatch_effect.add_reference(rect0)
|
113
114
|
rect1 = list(kernel.elements.elems())[1]
|
114
|
-
|
115
|
+
hatch_effect.add_reference(rect1)
|
115
116
|
|
116
|
-
hatch_copy =
|
117
|
-
hatch_copy.copy_children_as_real(hatch)
|
117
|
+
hatch_copy = hatch.copy_with_reified_tree()
|
118
118
|
|
119
119
|
hatch_effect = hatch_copy.children[0]
|
120
120
|
shape0 = hatch_effect.as_geometry()
|
test/test_geomstr.py
CHANGED
@@ -8,9 +8,12 @@ from math import tau
|
|
8
8
|
import numpy as np
|
9
9
|
|
10
10
|
from meerk40t.fill.fills import scanline_fill
|
11
|
+
from meerk40t.fill.patterns import set_diamond1, set_line
|
11
12
|
from meerk40t.svgelements import Arc, CubicBezier, Line, Matrix, QuadraticBezier
|
12
13
|
from meerk40t.tools.geomstr import (
|
13
14
|
TYPE_LINE,
|
15
|
+
TYPE_POINT,
|
16
|
+
BeamTable,
|
14
17
|
Clip,
|
15
18
|
Geomstr,
|
16
19
|
MergeGraph,
|
@@ -49,6 +52,39 @@ def draw(segments, min_x, min_y, max_x, max_y, buffer=0, filename="test.png"):
|
|
49
52
|
im.save(filename)
|
50
53
|
|
51
54
|
|
55
|
+
def draw_geom(segments, min_x, min_y, max_x, max_y, buffer=0, filename="test.png"):
|
56
|
+
from PIL import Image, ImageDraw
|
57
|
+
|
58
|
+
min_x -= buffer
|
59
|
+
min_y -= buffer
|
60
|
+
max_x += buffer
|
61
|
+
max_y += buffer
|
62
|
+
im = Image.new("RGBA", (int(max_x - min_x) + 1, int(max_y - min_y) + 1), "white")
|
63
|
+
|
64
|
+
draw = ImageDraw.Draw(im)
|
65
|
+
for line in segments.segments[: segments.index]:
|
66
|
+
if line[2].real == TYPE_POINT:
|
67
|
+
f = line[0]
|
68
|
+
draw.ellipse(
|
69
|
+
(
|
70
|
+
f.real - 3 - min_x,
|
71
|
+
f.imag - 3 - min_y,
|
72
|
+
f.real + 3 - min_x,
|
73
|
+
f.imag + 3 - min_y,
|
74
|
+
),
|
75
|
+
fill="#FF0000",
|
76
|
+
)
|
77
|
+
elif line[2].real == TYPE_LINE:
|
78
|
+
# Draw raw segments.
|
79
|
+
f = line[0]
|
80
|
+
t = line[-1]
|
81
|
+
draw.line(
|
82
|
+
((f.real - min_x, f.imag - min_y), (t.real - min_x, t.imag - min_y)),
|
83
|
+
fill="#000000",
|
84
|
+
)
|
85
|
+
im.save(filename)
|
86
|
+
|
87
|
+
|
52
88
|
def random_point(i=100):
|
53
89
|
return complex(random.random() * i, random.random() * i)
|
54
90
|
|
@@ -341,6 +377,42 @@ class TestGeomstr(unittest.TestCase):
|
|
341
377
|
# print(p.segments)
|
342
378
|
# draw(p.segments, w, h)
|
343
379
|
|
380
|
+
def test_geomstr_y_intercepts(self):
|
381
|
+
"""
|
382
|
+
Draws, 5 perfectly horizontal lines. Queries the y_intercepts
|
383
|
+
@return:
|
384
|
+
"""
|
385
|
+
g = Geomstr()
|
386
|
+
g.line(complex(0, 20), complex(100, 20))
|
387
|
+
g.line(complex(0, 40), complex(100, 40))
|
388
|
+
g.line(complex(0, 80), complex(100, 80))
|
389
|
+
g.line(complex(0, 60), complex(100, 60))
|
390
|
+
g.line(complex(0, 100), complex(100, 100))
|
391
|
+
q = g.y_intercept([0, 1, 2, 3, 4], 10)
|
392
|
+
self.assertEqual(q[0], 20.0)
|
393
|
+
self.assertEqual(q[1], 40.0)
|
394
|
+
self.assertEqual(q[2], 80.0)
|
395
|
+
self.assertEqual(q[3], 60.0)
|
396
|
+
self.assertEqual(q[4], 100.0)
|
397
|
+
|
398
|
+
def test_geomstr_x_intercepts(self):
|
399
|
+
"""
|
400
|
+
Draws, 5 perfectly vertical lines. Queries the x_intercepts
|
401
|
+
@return:
|
402
|
+
"""
|
403
|
+
g = Geomstr()
|
404
|
+
g.line(complex(20, 0), complex(20, 100))
|
405
|
+
g.line(complex(40, 0), complex(40, 100))
|
406
|
+
g.line(complex(80, 0), complex(80, 100))
|
407
|
+
g.line(complex(60, 0), complex(60, 100))
|
408
|
+
g.line(complex(100, 0), complex(100, 100))
|
409
|
+
q = g.x_intercept([0, 1, 2, 3, 4], 10)
|
410
|
+
self.assertEqual(q[0], 20.0)
|
411
|
+
self.assertEqual(q[1], 40.0)
|
412
|
+
self.assertEqual(q[2], 80.0)
|
413
|
+
self.assertEqual(q[3], 60.0)
|
414
|
+
self.assertEqual(q[4], 100.0)
|
415
|
+
|
344
416
|
def test_geomstr_classmethods(self):
|
345
417
|
"""
|
346
418
|
Test various classmethods for making defined geomstr shapes.
|
@@ -776,8 +848,6 @@ class TestGeomstr(unittest.TestCase):
|
|
776
848
|
print(results)
|
777
849
|
|
778
850
|
def test_pattern_generation(self):
|
779
|
-
from meerk40t.fill.patternfill import set_diamond1
|
780
|
-
|
781
851
|
f = set_diamond1
|
782
852
|
p = Pattern()
|
783
853
|
p.create_from_pattern(f)
|
@@ -794,8 +864,6 @@ class TestGeomstr(unittest.TestCase):
|
|
794
864
|
# self.assertTrue((array == array2).all())
|
795
865
|
|
796
866
|
def test_pattern_generation_counts(self):
|
797
|
-
from meerk40t.fill.patternfill import set_diamond1
|
798
|
-
|
799
867
|
f = set_diamond1
|
800
868
|
p = Pattern()
|
801
869
|
p.create_from_pattern(f)
|
@@ -812,8 +880,6 @@ class TestGeomstr(unittest.TestCase):
|
|
812
880
|
print("finished.")
|
813
881
|
|
814
882
|
def test_pattern_clip(self):
|
815
|
-
from meerk40t.fill.patternfill import set_diamond1
|
816
|
-
|
817
883
|
t = time.time()
|
818
884
|
f = set_diamond1
|
819
885
|
p = Pattern()
|
@@ -1009,9 +1075,24 @@ class TestGeomstr(unittest.TestCase):
|
|
1009
1075
|
except ZeroDivisionError:
|
1010
1076
|
pass
|
1011
1077
|
|
1012
|
-
|
1013
|
-
|
1078
|
+
# q = BeamTable(poly.geomstr)
|
1079
|
+
# q.compute_beam_brute()
|
1080
|
+
# t = time.time()
|
1081
|
+
# r = q.points_in_polygon(points)
|
1082
|
+
# t2 = time.time() - t
|
1083
|
+
# i = 0
|
1084
|
+
# for p1, p2 in zip(r, mask):
|
1085
|
+
# i += 1
|
1086
|
+
# if bool(p1) != bool(p2):
|
1087
|
+
# print(f"{i} {points[i]}")
|
1088
|
+
# self.assertEqual(bool(p1), bool(p2))
|
1089
|
+
|
1090
|
+
def test_render(self):
|
1091
|
+
rect = Geomstr.rect(x=300, y=200, width=500, height=500, rx=50, ry=50)
|
1092
|
+
image = rect.segmented().render()
|
1093
|
+
image.save("render-test.png")
|
1014
1094
|
|
1095
|
+
def test_point_in_polygon(self):
|
1015
1096
|
t1 = 0
|
1016
1097
|
t2 = 0
|
1017
1098
|
f = set_diamond1
|
@@ -1047,6 +1128,96 @@ class TestGeomstr(unittest.TestCase):
|
|
1047
1128
|
except ZeroDivisionError:
|
1048
1129
|
print(f"{t2} vs {t1}")
|
1049
1130
|
|
1131
|
+
def test_intersections_near(self):
|
1132
|
+
for r1 in np.linspace(0, 100, 5):
|
1133
|
+
r2 = r1 + 0.0001
|
1134
|
+
circle1 = Geomstr.circle(cx=0, cy=0, r=r1)
|
1135
|
+
circle2 = Geomstr.circle(cx=0, cy=0, r=r2)
|
1136
|
+
circle1.rotate(r1)
|
1137
|
+
for j in range(circle1.index):
|
1138
|
+
for k in range(circle2.index):
|
1139
|
+
c = list(
|
1140
|
+
circle1.intersections(circle1.segments[j], circle2.segments[k])
|
1141
|
+
)
|
1142
|
+
print(r1, r2)
|
1143
|
+
self.assertFalse(c)
|
1144
|
+
|
1145
|
+
def test_livinghinge_whiskers2(self):
|
1146
|
+
clip = Geomstr.ellipse(
|
1147
|
+
rx=96436.11909338088,
|
1148
|
+
ry=96436.11909338088,
|
1149
|
+
cx=550118.9389283657,
|
1150
|
+
cy=363374.1254904113,
|
1151
|
+
)
|
1152
|
+
subject = Geomstr()
|
1153
|
+
subject.line(5.82716822e05 + 343372.64182036j, 6.48483387e05 + 343372.64182036j)
|
1154
|
+
q = Clip(clip)
|
1155
|
+
subject = q.polycut(subject)
|
1156
|
+
subject = q.inside(subject)
|
1157
|
+
|
1158
|
+
m = Geomstr()
|
1159
|
+
m.append(clip)
|
1160
|
+
m.append(subject)
|
1161
|
+
m.uscale(0.002)
|
1162
|
+
draw(list(m.as_interpolated_points()), *m.bbox(), filename="whiskers.png")
|
1163
|
+
|
1164
|
+
def test_livinghinge_whiskers(self):
|
1165
|
+
"""
|
1166
|
+
Test for Whiskers bug. The given clip and exactly the right settings could allow a line to not clip correctly
|
1167
|
+
|
1168
|
+
We use a previously failing set of settings and make sure that the midpoints and both ends are always on the
|
1169
|
+
same side of the polygon.
|
1170
|
+
@return:
|
1171
|
+
"""
|
1172
|
+
clip = Geomstr.ellipse(
|
1173
|
+
rx=96436.11909338088,
|
1174
|
+
ry=96436.11909338088,
|
1175
|
+
cx=550118.9389283657,
|
1176
|
+
cy=363374.1254904113,
|
1177
|
+
)
|
1178
|
+
p = Pattern()
|
1179
|
+
p.create_from_pattern(set_line, 0, 0, outershape=clip)
|
1180
|
+
p.set_cell_padding(-11377.615848868112, -257.2803475393701)
|
1181
|
+
p.set_cell_dims(65766.56560039372, 5593.051033464568)
|
1182
|
+
p.extend_pattern = True
|
1183
|
+
subject = Geomstr()
|
1184
|
+
q = Clip(clip)
|
1185
|
+
self.path = Geomstr()
|
1186
|
+
for s in list(p.generate(*q.bounds)):
|
1187
|
+
subject.append(s)
|
1188
|
+
|
1189
|
+
subject = q.polycut(subject)
|
1190
|
+
subject = q.inside(subject)
|
1191
|
+
|
1192
|
+
m = Geomstr()
|
1193
|
+
m.append(clip)
|
1194
|
+
m.append(subject)
|
1195
|
+
m.uscale(0.002)
|
1196
|
+
draw(list(m.as_interpolated_points()), *m.bbox())
|
1197
|
+
|
1198
|
+
c = Geomstr()
|
1199
|
+
# Pip currently only works with line segments
|
1200
|
+
for sp in clip.as_subpaths():
|
1201
|
+
for segs in sp.as_interpolated_segments(interpolate=100):
|
1202
|
+
c.polyline(segs)
|
1203
|
+
c.end()
|
1204
|
+
sb = Scanbeam(c)
|
1205
|
+
|
1206
|
+
mid_points = subject.position(slice(subject.index), 0.5)
|
1207
|
+
r = np.where(sb.points_in_polygon(mid_points))
|
1208
|
+
|
1209
|
+
s = np.where(
|
1210
|
+
sb.points_in_polygon(subject.position(slice(subject.index), 0.05))
|
1211
|
+
)[0]
|
1212
|
+
|
1213
|
+
e = np.where(
|
1214
|
+
sb.points_in_polygon(subject.position(slice(subject.index), 0.95))
|
1215
|
+
)[0]
|
1216
|
+
|
1217
|
+
for q in r[0]:
|
1218
|
+
self.assertIn(q, s)
|
1219
|
+
self.assertIn(q, e)
|
1220
|
+
|
1050
1221
|
def test_point_towards_numpy(self):
|
1051
1222
|
p1 = complex(0, 100)
|
1052
1223
|
p2 = complex(50, 22)
|
@@ -1253,6 +1424,85 @@ class TestGeomstr(unittest.TestCase):
|
|
1253
1424
|
filename="box.png",
|
1254
1425
|
)
|
1255
1426
|
|
1427
|
+
def test_geom_max_aabb(self):
|
1428
|
+
g = Geomstr.rect(0, 0, 200, 200)
|
1429
|
+
nx, ny, mx, my = g.aabb()
|
1430
|
+
print(nx)
|
1431
|
+
|
1432
|
+
def test_static_beam_horizontal_bowtie(self):
|
1433
|
+
"""
|
1434
|
+
0: down-right
|
1435
|
+
1: right side
|
1436
|
+
2: down-left
|
1437
|
+
3: left side
|
1438
|
+
30 21
|
1439
|
+
|\ /|
|
1440
|
+
| \ / |
|
1441
|
+
| / \ |
|
1442
|
+
|/ \|
|
1443
|
+
@return:
|
1444
|
+
"""
|
1445
|
+
bowtie = Geomstr.lines(
|
1446
|
+
complex(0, 0),
|
1447
|
+
complex(100, 100),
|
1448
|
+
complex(100, 0),
|
1449
|
+
complex(0, 100),
|
1450
|
+
complex(0, 0),
|
1451
|
+
)
|
1452
|
+
sb = BeamTable(bowtie)
|
1453
|
+
result = sb.actives_at(25)
|
1454
|
+
actives = bowtie.x_intercept(result, 25)
|
1455
|
+
|
1456
|
+
for x, y in zip(result, (0, 2)):
|
1457
|
+
self.assertEqual(x, y)
|
1458
|
+
|
1459
|
+
def test_static_beam_vertical_bowtie(self):
|
1460
|
+
"""
|
1461
|
+
0 3
|
1462
|
+
--------
|
1463
|
+
\ /
|
1464
|
+
\ /
|
1465
|
+
\/
|
1466
|
+
/\
|
1467
|
+
/ \
|
1468
|
+
/ \
|
1469
|
+
------
|
1470
|
+
2 1
|
1471
|
+
@return:
|
1472
|
+
"""
|
1473
|
+
bowtie = Geomstr.lines(
|
1474
|
+
complex(0, 0),
|
1475
|
+
complex(100, 100),
|
1476
|
+
complex(0, 100),
|
1477
|
+
complex(100, 0),
|
1478
|
+
complex(0, 0),
|
1479
|
+
)
|
1480
|
+
sb = BeamTable(bowtie)
|
1481
|
+
result = sb.actives_at(complex(25, 0))
|
1482
|
+
actives = bowtie.x_intercept(result, 25)
|
1483
|
+
|
1484
|
+
for x, y in zip(result, (3, 0, 2, 1)):
|
1485
|
+
self.assertEqual(x, y)
|
1486
|
+
|
1487
|
+
def test_scan_table_random(self):
|
1488
|
+
for c in range(1):
|
1489
|
+
print("\n\n\n\n\n")
|
1490
|
+
g = Geomstr()
|
1491
|
+
for i in range(25):
|
1492
|
+
random_segment(
|
1493
|
+
g, i=1000, arc=False, point=False, quad=False, cubic=False
|
1494
|
+
)
|
1495
|
+
t = time.time()
|
1496
|
+
sb = BeamTable(g)
|
1497
|
+
sb.compute_beam_brute()
|
1498
|
+
intersections = sb.intersections
|
1499
|
+
print(f"Time: {time.time() - t}")
|
1500
|
+
try:
|
1501
|
+
g.append(intersections)
|
1502
|
+
draw_geom(g, *g.bbox(), filename="scantable.png")
|
1503
|
+
except PermissionError:
|
1504
|
+
pass
|
1505
|
+
|
1256
1506
|
# def test_geomstr_hatch(self):
|
1257
1507
|
# gs = Geomstr.svg(
|
1258
1508
|
# "M 207770.064517,235321.124952 C 206605.069353,234992.732685 205977.289179,234250.951228 205980.879932,233207.034699 C 205983.217733,232527.380908 206063.501616,232426.095743 206731.813533,232259.66605 L 207288.352862,232121.071081 L 207207.998708,232804.759538 C 207106.904585,233664.912764 207367.871267,234231.469286 207960.295387,234437.989447 C 208960.760372,234786.753419 209959.046638,234459.536445 210380.398871,233644.731075 C 210672.441667,233079.98258 210772.793626,231736.144349 210569.029382,231118.732625 C 210379.268508,230543.75153 209783.667018,230128.095713 209148.499972,230127.379646 C 208627.98084,230126.79283 208274.720902,230294.472682 207747.763851,230792.258962 C 207377.90966,231141.639128 207320.755956,231155.543097 206798.920578,231023.087178 C 206328.09633,230903.579262 206253.35266,230839.656219 206307.510015,230602.818034 C 206382.366365,230275.460062 207158.299204,225839.458855 207158.299204,225738.863735 C 207158.299204,225701.269015 208426.401454,225670.509699 209976.304204,225670.509699 C 211869.528049,225670.509699 212794.309204,225715.990496 212794.309204,225809.099369 C 212794.309204,225885.323687 212726.683921,226357.175687 212644.030798,226857.659369 L 212493.752392,227767.629699 L 210171.516354,227767.629699 L 207849.280317,227767.629699 L 207771.086662,228324.677199 C 207728.080152,228631.053324 207654.900983,229067.454479 207608.466287,229294.457543 L 207524.039566,229707.190387 L 208182.568319,229381.288158 C 209664.399179,228647.938278 211467.922971,228893.537762 212548.92912,229975.888551 C 214130.813964,231559.741067 213569.470754,234195.253882 211455.779825,235108.237047 C 210589.985852,235482.206254 208723.891068,235589.992389 207770.064517,235321.124952 L 207770.064517,235321.124952Z"
|
test/test_kernel.py
CHANGED
@@ -21,6 +21,7 @@ class TestKernel(unittest.TestCase):
|
|
21
21
|
if command in (
|
22
22
|
"quit",
|
23
23
|
"shutdown",
|
24
|
+
"restart",
|
24
25
|
"interrupt",
|
25
26
|
"+laser",
|
26
27
|
"-laser",
|
@@ -88,6 +89,9 @@ class TestKernel(unittest.TestCase):
|
|
88
89
|
func_dict = dict(func.func_dict)
|
89
90
|
print(f"Executing: {func.name}")
|
90
91
|
func(n, **func_dict)
|
92
|
+
if n.parent is None:
|
93
|
+
# This function removed the element. Put it back in the tree.
|
94
|
+
kernel.elements.elem_branch.add_node(n)
|
91
95
|
finally:
|
92
96
|
kernel.console("elements\n")
|
93
97
|
kernel.shutdown()
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import time
|
2
|
+
import unittest
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
from PIL import Image, ImageDraw
|
6
|
+
|
7
|
+
from meerk40t.tools.rasterplotter import RasterPlotter
|
8
|
+
|
9
|
+
|
10
|
+
class TestRasterPlotter(unittest.TestCase):
|
11
|
+
def test_rasterplotter_largecircle(self):
|
12
|
+
"""
|
13
|
+
Tests the speed of rasterplotter for large circle.
|
14
|
+
|
15
|
+
:return:
|
16
|
+
"""
|
17
|
+
image = Image.new("RGBA", (2560, 2560), "white")
|
18
|
+
draw = ImageDraw.Draw(image)
|
19
|
+
draw.ellipse((0, 0, 2560, 2560), "black")
|
20
|
+
image = image.convert("L")
|
21
|
+
img = np.array(image)
|
22
|
+
# img = image.load()
|
23
|
+
plotter = RasterPlotter(img, 2560, 2560)
|
24
|
+
t = time.time()
|
25
|
+
i = 0
|
26
|
+
for x, y, on in plotter.plot():
|
27
|
+
i += 0
|
28
|
+
print(i)
|
29
|
+
print(f"\nTime taken to finish process {time.time() - t}\n")
|
meerk40t/extra/embroider.py
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
from meerk40t.core.units import Length
|
2
|
-
from meerk40t.svgelements import Angle, Matrix, Path, Polyline
|
3
|
-
from meerk40t.tools.pathtools import EulerianFill
|
4
|
-
|
5
|
-
|
6
|
-
def plugin(kernel, lifecycle):
|
7
|
-
if lifecycle == "register":
|
8
|
-
_ = kernel.translation
|
9
|
-
context = kernel.root
|
10
|
-
|
11
|
-
@context.console_option(
|
12
|
-
"angle", "a", type=Angle.parse, default="0deg", help=_("Angle of the fill")
|
13
|
-
)
|
14
|
-
@context.console_option(
|
15
|
-
"distance",
|
16
|
-
"d",
|
17
|
-
type=Length,
|
18
|
-
default="0.5mm",
|
19
|
-
help=_("Length between rungs"),
|
20
|
-
)
|
21
|
-
@context.console_command("embroider", help=_("embroider <angle> <distance>"))
|
22
|
-
def embroider(command, channel, _, angle=None, distance=None, **kwargs):
|
23
|
-
elements = context.elements
|
24
|
-
channel(_("Embroidery Filling"))
|
25
|
-
efill = EulerianFill(float(distance))
|
26
|
-
for node in elements.elems(emphasized=True):
|
27
|
-
try:
|
28
|
-
path = Path(node.shape)
|
29
|
-
except AttributeError:
|
30
|
-
try:
|
31
|
-
path = Path(node.path)
|
32
|
-
except AttributeError:
|
33
|
-
continue
|
34
|
-
if angle is not None:
|
35
|
-
path *= Matrix.rotate(angle)
|
36
|
-
pts = [abs(path).point(i / 100.0, error=1e-4) for i in range(101)]
|
37
|
-
efill += pts
|
38
|
-
|
39
|
-
points = efill.get_fill()
|
40
|
-
|
41
|
-
for s in split(points):
|
42
|
-
result = Path(Polyline(s, stroke="black"))
|
43
|
-
if angle is not None:
|
44
|
-
result *= Matrix.rotate(-angle)
|
45
|
-
node = elements.elem_branch.add(path=result, type="elem path")
|
46
|
-
elements.classify([node])
|
47
|
-
|
48
|
-
|
49
|
-
def split(points):
|
50
|
-
pos = 0
|
51
|
-
for i, pts in enumerate(points):
|
52
|
-
if pts is None:
|
53
|
-
yield points[pos : i - 1]
|
54
|
-
pos = i + 1
|
55
|
-
if pos != len(points):
|
56
|
-
yield points[pos : len(points)]
|