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.
Files changed (187) hide show
  1. meerk40t/balormk/balor_params.py +1 -43
  2. meerk40t/balormk/controller.py +1 -41
  3. meerk40t/balormk/device.py +16 -22
  4. meerk40t/balormk/driver.py +4 -4
  5. meerk40t/balormk/gui/balorconfig.py +2 -2
  6. meerk40t/balormk/gui/balorcontroller.py +13 -5
  7. meerk40t/balormk/gui/baloroperationproperties.py +0 -46
  8. meerk40t/balormk/gui/gui.py +17 -17
  9. meerk40t/camera/gui/camerapanel.py +18 -11
  10. meerk40t/core/cutcode/rastercut.py +3 -1
  11. meerk40t/core/cutplan.py +145 -14
  12. meerk40t/core/elements/clipboard.py +18 -9
  13. meerk40t/core/elements/element_treeops.py +320 -180
  14. meerk40t/core/elements/element_types.py +7 -2
  15. meerk40t/core/elements/elements.py +53 -27
  16. meerk40t/core/elements/geometry.py +8 -0
  17. meerk40t/core/elements/offset_clpr.py +129 -4
  18. meerk40t/core/elements/offset_mk.py +3 -1
  19. meerk40t/core/elements/shapes.py +28 -25
  20. meerk40t/core/laserjob.py +7 -0
  21. meerk40t/core/node/bootstrap.py +4 -0
  22. meerk40t/core/node/effect_hatch.py +85 -96
  23. meerk40t/core/node/effect_wobble.py +309 -0
  24. meerk40t/core/node/elem_image.py +49 -19
  25. meerk40t/core/node/elem_line.py +60 -0
  26. meerk40t/core/node/elem_rect.py +5 -3
  27. meerk40t/core/node/image_processed.py +766 -0
  28. meerk40t/core/node/image_raster.py +113 -0
  29. meerk40t/core/node/node.py +120 -1
  30. meerk40t/core/node/op_cut.py +2 -8
  31. meerk40t/core/node/op_dots.py +0 -8
  32. meerk40t/core/node/op_engrave.py +2 -18
  33. meerk40t/core/node/op_image.py +22 -35
  34. meerk40t/core/node/op_raster.py +0 -9
  35. meerk40t/core/planner.py +32 -2
  36. meerk40t/core/svg_io.py +699 -461
  37. meerk40t/core/treeop.py +191 -0
  38. meerk40t/core/undos.py +15 -1
  39. meerk40t/core/units.py +14 -4
  40. meerk40t/device/dummydevice.py +3 -2
  41. meerk40t/device/gui/defaultactions.py +43 -55
  42. meerk40t/device/gui/formatterpanel.py +58 -49
  43. meerk40t/device/gui/warningpanel.py +12 -12
  44. meerk40t/device/mixins.py +13 -0
  45. meerk40t/dxf/dxf_io.py +9 -5
  46. meerk40t/extra/ezd.py +28 -26
  47. meerk40t/extra/imageactions.py +300 -308
  48. meerk40t/extra/lbrn.py +19 -2
  49. meerk40t/fill/fills.py +6 -6
  50. meerk40t/fill/patternfill.py +1061 -1061
  51. meerk40t/fill/patterns.py +2 -6
  52. meerk40t/grbl/controller.py +168 -52
  53. meerk40t/grbl/device.py +23 -18
  54. meerk40t/grbl/driver.py +39 -0
  55. meerk40t/grbl/emulator.py +79 -19
  56. meerk40t/grbl/gcodejob.py +10 -0
  57. meerk40t/grbl/gui/grblconfiguration.py +2 -2
  58. meerk40t/grbl/gui/grblcontroller.py +24 -8
  59. meerk40t/grbl/gui/grblhardwareconfig.py +153 -0
  60. meerk40t/grbl/gui/gui.py +17 -14
  61. meerk40t/grbl/mock_connection.py +15 -34
  62. meerk40t/grbl/plugin.py +0 -4
  63. meerk40t/grbl/serial_connection.py +2 -1
  64. meerk40t/gui/about.py +8 -5
  65. meerk40t/gui/alignment.py +10 -6
  66. meerk40t/gui/basicops.py +27 -17
  67. meerk40t/gui/bufferview.py +2 -2
  68. meerk40t/gui/choicepropertypanel.py +101 -13
  69. meerk40t/gui/consolepanel.py +12 -9
  70. meerk40t/gui/devicepanel.py +38 -25
  71. meerk40t/gui/executejob.py +6 -4
  72. meerk40t/gui/help_assets/help_assets.py +13 -10
  73. meerk40t/gui/hersheymanager.py +8 -6
  74. meerk40t/gui/icons.py +1951 -3065
  75. meerk40t/gui/imagesplitter.py +14 -7
  76. meerk40t/gui/keymap.py +3 -3
  77. meerk40t/gui/laserpanel.py +151 -84
  78. meerk40t/gui/laserrender.py +61 -70
  79. meerk40t/gui/lasertoolpanel.py +8 -7
  80. meerk40t/gui/materialtest.py +3 -3
  81. meerk40t/gui/mkdebug.py +254 -1
  82. meerk40t/gui/navigationpanels.py +321 -180
  83. meerk40t/gui/notes.py +3 -3
  84. meerk40t/gui/opassignment.py +12 -12
  85. meerk40t/gui/operation_info.py +13 -13
  86. meerk40t/gui/plugin.py +5 -0
  87. meerk40t/gui/position.py +20 -18
  88. meerk40t/gui/preferences.py +21 -6
  89. meerk40t/gui/propertypanels/attributes.py +70 -22
  90. meerk40t/gui/propertypanels/blobproperty.py +2 -2
  91. meerk40t/gui/propertypanels/consoleproperty.py +2 -2
  92. meerk40t/gui/propertypanels/groupproperties.py +3 -3
  93. meerk40t/gui/propertypanels/hatchproperty.py +11 -18
  94. meerk40t/gui/propertypanels/imageproperty.py +4 -3
  95. meerk40t/gui/propertypanels/opbranchproperties.py +1 -1
  96. meerk40t/gui/propertypanels/pathproperty.py +2 -2
  97. meerk40t/gui/propertypanels/pointproperty.py +2 -2
  98. meerk40t/gui/propertypanels/propertywindow.py +4 -4
  99. meerk40t/gui/propertypanels/textproperty.py +3 -3
  100. meerk40t/gui/propertypanels/wobbleproperty.py +204 -0
  101. meerk40t/gui/ribbon.py +367 -259
  102. meerk40t/gui/scene/scene.py +31 -5
  103. meerk40t/gui/scenewidgets/elementswidget.py +12 -4
  104. meerk40t/gui/scenewidgets/gridwidget.py +2 -2
  105. meerk40t/gui/scenewidgets/laserpathwidget.py +7 -2
  106. meerk40t/gui/scenewidgets/machineoriginwidget.py +6 -2
  107. meerk40t/gui/scenewidgets/relocatewidget.py +1 -1
  108. meerk40t/gui/scenewidgets/reticlewidget.py +9 -0
  109. meerk40t/gui/scenewidgets/selectionwidget.py +12 -7
  110. meerk40t/gui/simpleui.py +95 -8
  111. meerk40t/gui/simulation.py +44 -36
  112. meerk40t/gui/spoolerpanel.py +124 -26
  113. meerk40t/gui/statusbarwidgets/defaultoperations.py +18 -6
  114. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  115. meerk40t/gui/statusbarwidgets/opassignwidget.py +12 -12
  116. meerk40t/gui/statusbarwidgets/shapepropwidget.py +45 -18
  117. meerk40t/gui/statusbarwidgets/statusbar.py +11 -4
  118. meerk40t/gui/themes.py +78 -0
  119. meerk40t/gui/toolwidgets/toolcircle.py +2 -1
  120. meerk40t/gui/toolwidgets/toolellipse.py +2 -1
  121. meerk40t/gui/toolwidgets/toolimagecut.py +132 -0
  122. meerk40t/gui/toolwidgets/toolline.py +144 -0
  123. meerk40t/gui/toolwidgets/toolnodeedit.py +72 -145
  124. meerk40t/gui/toolwidgets/toolpoint.py +1 -1
  125. meerk40t/gui/toolwidgets/toolpolygon.py +8 -55
  126. meerk40t/gui/toolwidgets/toolrect.py +2 -1
  127. meerk40t/gui/usbconnect.py +2 -2
  128. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +2 -2
  129. meerk40t/gui/utilitywidgets/harmonograph.py +7 -7
  130. meerk40t/gui/utilitywidgets/scalewidget.py +1 -1
  131. meerk40t/gui/wordlisteditor.py +33 -18
  132. meerk40t/gui/wxmeerk40t.py +166 -66
  133. meerk40t/gui/wxmmain.py +236 -157
  134. meerk40t/gui/wxmribbon.py +49 -25
  135. meerk40t/gui/wxmscene.py +49 -38
  136. meerk40t/gui/wxmtree.py +216 -85
  137. meerk40t/gui/wxutils.py +62 -4
  138. meerk40t/image/imagetools.py +443 -15
  139. meerk40t/internal_plugins.py +2 -10
  140. meerk40t/kernel/kernel.py +12 -4
  141. meerk40t/lihuiyu/controller.py +7 -7
  142. meerk40t/lihuiyu/device.py +3 -1
  143. meerk40t/lihuiyu/driver.py +3 -0
  144. meerk40t/lihuiyu/gui/gui.py +8 -8
  145. meerk40t/lihuiyu/gui/lhyaccelgui.py +2 -2
  146. meerk40t/lihuiyu/gui/lhycontrollergui.py +73 -27
  147. meerk40t/lihuiyu/gui/lhydrivergui.py +2 -2
  148. meerk40t/lihuiyu/gui/tcpcontroller.py +22 -9
  149. meerk40t/main.py +6 -1
  150. meerk40t/moshi/controller.py +5 -5
  151. meerk40t/moshi/device.py +5 -2
  152. meerk40t/moshi/driver.py +4 -0
  153. meerk40t/moshi/gui/gui.py +8 -8
  154. meerk40t/moshi/gui/moshicontrollergui.py +24 -8
  155. meerk40t/moshi/gui/moshidrivergui.py +2 -2
  156. meerk40t/newly/controller.py +2 -0
  157. meerk40t/newly/device.py +9 -2
  158. meerk40t/newly/driver.py +4 -0
  159. meerk40t/newly/gui/gui.py +16 -17
  160. meerk40t/newly/gui/newlyconfig.py +2 -2
  161. meerk40t/newly/gui/newlycontroller.py +13 -5
  162. meerk40t/rotary/gui/gui.py +2 -2
  163. meerk40t/rotary/gui/rotarysettings.py +2 -2
  164. meerk40t/ruida/device.py +3 -0
  165. meerk40t/ruida/driver.py +4 -0
  166. meerk40t/ruida/gui/gui.py +6 -6
  167. meerk40t/ruida/gui/ruidaconfig.py +2 -2
  168. meerk40t/ruida/gui/ruidacontroller.py +13 -5
  169. meerk40t/svgelements.py +9 -9
  170. meerk40t/tools/geomstr.py +849 -153
  171. meerk40t/tools/kerftest.py +8 -4
  172. meerk40t/tools/livinghinges.py +15 -8
  173. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/METADATA +21 -16
  174. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/RECORD +185 -177
  175. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/entry_points.txt +0 -1
  176. test/test_core_elements.py +8 -24
  177. test/test_file_svg.py +88 -0
  178. test/test_fill.py +9 -9
  179. test/test_geomstr.py +258 -8
  180. test/test_kernel.py +4 -0
  181. test/test_tools_rasterplotter.py +29 -0
  182. meerk40t/extra/embroider.py +0 -56
  183. meerk40t/extra/pathoptimize.py +0 -249
  184. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/LICENSE +0 -0
  185. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/WHEEL +0 -0
  186. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/top_level.txt +0 -0
  187. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/zip-safe +0 -0
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  meerk40t = meerk40t.main:run
3
-
@@ -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
- shape = node.shape
74
- self.assertEqual(
75
- shape,
76
- Circle(
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
- shape = node.shape
100
- self.assertEqual(
101
- shape,
102
- Rect(
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
- hatch = list(kernel.elements.ops())[0]
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
- hatch_copy = copy(hatch)
88
- hatch_copy.copy_children_as_real(hatch)
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
- hatch.add_reference(rect0)
113
+ hatch_effect.add_reference(rect0)
113
114
  rect1 = list(kernel.elements.elems())[1]
114
- hatch.add_reference(rect1)
115
+ hatch_effect.add_reference(rect1)
115
116
 
116
- hatch_copy = copy(hatch)
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
- def test_point_in_polygon(self):
1013
- from meerk40t.fill.patternfill import set_diamond1
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")
@@ -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)]