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
meerk40t/core/cutplan.py CHANGED
@@ -17,7 +17,7 @@ CutPlan handles the various complicated algorithms to optimising the sequence of
17
17
  from copy import copy
18
18
  from math import isinf
19
19
  from os import times
20
- from time import time
20
+ from time import perf_counter, time
21
21
  from typing import Optional
22
22
 
23
23
  import numpy as np
@@ -175,7 +175,16 @@ class CutPlan:
175
175
  # axis = rotary.axis
176
176
 
177
177
  original_ops = copy(self.plan)
178
+ if self.context.opt_raster_optimisation and self.context.do_optimization:
179
+ try:
180
+ margin = float(Length(self.context.opt_raster_opt_margin, "0"))
181
+ except (AttributeError, ValueError):
182
+ margin = 0
183
+ self.optimize_rasters(original_ops, "op raster", margin)
184
+ # We could do this as well, but images are burnt separately anyway...
185
+ # self.optimize_rasters(original_ops, "op image", margin)
178
186
  self.plan.clear()
187
+
179
188
  idx = 0
180
189
  self.context.elements.mywordlist.push()
181
190
 
@@ -185,7 +194,10 @@ class CutPlan:
185
194
  self.context.elements.mywordlist.move_all_indices(1)
186
195
 
187
196
  for original_op in original_ops:
188
- op = copy(original_op)
197
+ try:
198
+ op = original_op.copy_with_reified_tree()
199
+ except AttributeError:
200
+ op = original_op
189
201
  if not hasattr(op, "type") or op.type is None:
190
202
  self.plan.append(op)
191
203
  continue
@@ -193,8 +205,6 @@ class CutPlan:
193
205
  continue
194
206
  self.plan.append(op)
195
207
  if op.type.startswith("op"):
196
- for child in original_op.children:
197
- op.add_node(copy(child))
198
208
  if hasattr(op, "preprocess"):
199
209
  op.preprocess(self.context, placement, self)
200
210
  for node in op.flat():
@@ -531,8 +541,8 @@ class CutPlan:
531
541
  float(Length(stol))
532
542
  * 2
533
543
  / (
534
- self.context.device.native_scale_x
535
- + self.context.device.native_scale_y
544
+ self.context.device.view.native_scale_x
545
+ + self.context.device.view.native_scale_y
536
546
  )
537
547
  )
538
548
  except ValueError:
@@ -637,6 +647,122 @@ class CutPlan:
637
647
  self.plan.clear()
638
648
  self.commands.clear()
639
649
 
650
+ def optimize_rasters(self, operation_list, op_type, margin):
651
+ def generate_clusters(operation):
652
+ def overlapping(bounds1, bounds2, margin):
653
+ # The rectangles don't overlap if
654
+ # one rectangle's minimum in some dimension
655
+ # is greater than the other's maximum in
656
+ # that dimension.
657
+ flagx = (bounds1[0] > bounds2[2] + margin) or (
658
+ bounds2[0] > bounds1[2] + margin
659
+ )
660
+ flagy = (bounds1[1] > bounds2[3] + margin) or (
661
+ bounds2[1] > bounds1[3] + margin
662
+ )
663
+ return bool(not (flagx or flagy))
664
+
665
+ clusters = list()
666
+ cluster_bounds = list()
667
+ for node in operation.children:
668
+ try:
669
+ if node.type == "reference":
670
+ node = node.node
671
+ bb = node.paint_bounds
672
+ except AttributeError:
673
+ # Either no element node or does not have bounds
674
+ continue
675
+ clusters.append([node])
676
+ cluster_bounds.append(
677
+ (
678
+ bb[0],
679
+ bb[1],
680
+ bb[2],
681
+ bb[3],
682
+ )
683
+ )
684
+
685
+ def detail_overlap(index1, index2):
686
+ # But is there a real overlap, or just one with the union bounds?
687
+ for outer_node in clusters[index1]:
688
+ try:
689
+ bb_outer = outer_node.paint_bounds
690
+ except AttributeError:
691
+ continue
692
+ for inner_node in clusters[index2]:
693
+ try:
694
+ bb_inner = inner_node.paint_bounds
695
+ except AttributeError:
696
+ continue
697
+ if overlapping(bb_outer, bb_inner, margin):
698
+ return True
699
+ # We did not find anything...
700
+ return False
701
+
702
+ needs_repeat = True
703
+ while needs_repeat:
704
+ needs_repeat = False
705
+ for outer_idx in range(len(clusters) - 1, -1, -1):
706
+ # Loop downwards as we are manipulating the arrays
707
+ bb = cluster_bounds[outer_idx]
708
+ for inner_idx in range(outer_idx - 1, -1, -1):
709
+ cc = cluster_bounds[inner_idx]
710
+ if not overlapping(bb, cc, margin):
711
+ continue
712
+ # Overlap!
713
+ # print (f"Reuse cluster {inner_idx} for {outer_idx}")
714
+ real_overlap = detail_overlap(outer_idx, inner_idx)
715
+ if real_overlap:
716
+ needs_repeat = True
717
+ # We need to extend the inner cluster by the outer
718
+ clusters[inner_idx].extend(clusters[outer_idx])
719
+ cluster_bounds[inner_idx] = (
720
+ min(bb[0], cc[0]),
721
+ min(bb[1], cc[1]),
722
+ max(bb[2], cc[2]),
723
+ max(bb[3], cc[3]),
724
+ )
725
+ clusters.pop(outer_idx)
726
+ cluster_bounds.pop(outer_idx)
727
+ # We are done with the inner loop, as we effectively
728
+ # destroyed the cluster element we compared
729
+ break
730
+
731
+ return clusters
732
+
733
+ stime = perf_counter()
734
+ scount = 0
735
+ ecount = 0
736
+ for idx in range(len(operation_list) - 1, -1, -1):
737
+ op = operation_list[idx]
738
+ if (
739
+ not hasattr(op, "type")
740
+ or not hasattr(op, "children")
741
+ or op.type != op_type
742
+ ):
743
+ # That's not what we are looking for
744
+ continue
745
+ scount += 1
746
+ clusters = generate_clusters(op)
747
+ ecount += len(clusters)
748
+ if len(clusters) > 0:
749
+ # Create cluster copies of the raster op
750
+ for entry in clusters:
751
+ newop = copy(op)
752
+ newop._references.clear()
753
+ for node in entry:
754
+ newop.add_reference(node)
755
+ newop.set_dirty_bounds()
756
+ operation_list.insert(idx + 1, newop)
757
+
758
+ # And remove the original one...
759
+ operation_list.pop(idx)
760
+ etime = perf_counter()
761
+ if self.channel:
762
+ self.channel(
763
+ f"Optimise {op_type} finished after {etime-stime:.2f} seconds, inflated {scount} operations to {ecount}"
764
+ )
765
+
640
766
 
641
767
  def is_inside(inner, outer, tolerance=0):
642
768
  """
@@ -805,6 +931,10 @@ def inner_first_ident(context: CutGroup, channel=None, tolerance=0):
805
931
  groups = [cut for cut in context if isinstance(cut, (CutGroup, RasterCut))]
806
932
  closed_groups = [g for g in groups if isinstance(g, CutGroup) and g.closed]
807
933
  context.contains = closed_groups
934
+ if channel:
935
+ channel(
936
+ f"Compare {len(groups)} groups against {len(closed_groups)} closed groups"
937
+ )
808
938
 
809
939
  constrained = False
810
940
  for outer in closed_groups:
@@ -842,7 +972,7 @@ def inner_first_ident(context: CutGroup, channel=None, tolerance=0):
842
972
  if channel:
843
973
  end_times = times()
844
974
  channel(
845
- f"Inner paths identified in {time() - start_time:.3f} elapsed seconds "
975
+ f"Inner paths identified in {time() - start_time:.3f} elapsed seconds: {constrained} "
846
976
  f"using {end_times[0] - start_times[0]:.3f} seconds CPU"
847
977
  )
848
978
  return context
@@ -1175,11 +1305,12 @@ def inner_selection_cutcode(
1175
1305
  if channel:
1176
1306
  end_times = times()
1177
1307
  end_length = ordered.length_travel(True)
1178
- channel(
1179
- f"Length at end: {end_length:.0f} steps "
1180
- f"({(end_length - start_length) / start_length:+.0%}), "
1181
- f"optimized in {time() - start_time:.3f} "
1182
- f"elapsed seconds using {end_times[0] - start_times[0]:.3f} "
1183
- f"seconds CPU in {iterations} iterations"
1184
- )
1308
+ msg = f"Length at end: {end_length:.0f} steps "
1309
+ if start_length != 0:
1310
+ msg += f"({(end_length - start_length) / start_length:+.0%}), "
1311
+ msg += f"optimized in {time() - start_time:.3f} "
1312
+ msg += f"elapsed seconds using {end_times[0] - start_times[0]:.3f} "
1313
+ msg += f"seconds CPU in {iterations} iterations"
1314
+
1315
+ channel(msg)
1185
1316
  return ordered
@@ -59,14 +59,13 @@ def init_commands(kernel):
59
59
  copy_node = copy(e)
60
60
  # Need to add stroke and fill, as copy will take the
61
61
  # default values for these attributes
62
- for optional in ("fill", "stroke"):
62
+ options = ["fill", "stroke", "wxfont"]
63
+ for prop in dir(e):
64
+ if prop.startswith("mk"):
65
+ options.append(prop)
66
+ for optional in options:
63
67
  if hasattr(e, optional):
64
68
  setattr(copy_node, optional, getattr(e, optional))
65
- hadoptional = False
66
- for optional in ("wxfont", "mktext", "mkfont", "mkfontsize"):
67
- if hasattr(e, optional):
68
- setattr(copy_node, optional, getattr(e, optional))
69
- hadoptional = True
70
69
  self._clipboard[destination].append(copy_node)
71
70
  # Let the world know we have filled the clipboard
72
71
  self.signal("icons")
@@ -93,14 +92,20 @@ def init_commands(kernel):
93
92
  continue
94
93
  # Need to add stroke and fill, as copy will take the
95
94
  # default values for these attributes
96
- for optional in ("fill", "stroke"):
95
+ options = ["fill", "stroke", "wxfont"]
96
+ for optional in options:
97
97
  if hasattr(e, optional):
98
98
  setattr(copy_node, optional, getattr(e, optional))
99
99
  hadoptional = False
100
- for optional in ("wxfont", "mktext", "mkfont", "mkfontsize"):
100
+ options = []
101
+ for prop in dir(e):
102
+ if prop.startswith("mk"):
103
+ options.append(prop)
104
+ for optional in options:
101
105
  if hasattr(e, optional):
102
106
  setattr(copy_node, optional, getattr(e, optional))
103
107
  hadoptional = True
108
+
104
109
  if hadoptional:
105
110
  for property_op in self.kernel.lookup_all("path_updater/.*"):
106
111
  property_op(self.kernel.root, copy_node)
@@ -158,7 +163,11 @@ def init_commands(kernel):
158
163
  self._clipboard[destination] = []
159
164
  for e in data:
160
165
  copy_node = copy(e)
161
- for optional in ("wxfont", "mktext", "mkfont", "mkfontsize"):
166
+ options = ["fill", "stroke", "wxfont"]
167
+ for prop in dir(e):
168
+ if prop.startswith("mk"):
169
+ options.append(prop)
170
+ for optional in options:
162
171
  if hasattr(e, optional):
163
172
  setattr(copy_node, optional, getattr(e, optional))
164
173
  self._clipboard[destination].append(copy_node)