meerk40t 0.9.7010__py2.py3-none-any.whl → 0.9.7030__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 (77) hide show
  1. meerk40t/balormk/galvo_commands.py +1 -2
  2. meerk40t/core/cutcode/cutcode.py +1 -1
  3. meerk40t/core/cutplan.py +70 -2
  4. meerk40t/core/elements/branches.py +18 -4
  5. meerk40t/core/elements/element_treeops.py +43 -7
  6. meerk40t/core/elements/elements.py +49 -63
  7. meerk40t/core/elements/grid.py +8 -1
  8. meerk40t/core/elements/offset_clpr.py +4 -3
  9. meerk40t/core/elements/offset_mk.py +2 -1
  10. meerk40t/core/elements/shapes.py +379 -260
  11. meerk40t/core/elements/testcases.py +105 -0
  12. meerk40t/core/node/node.py +6 -3
  13. meerk40t/core/node/op_cut.py +9 -8
  14. meerk40t/core/node/op_dots.py +8 -8
  15. meerk40t/core/node/op_engrave.py +7 -7
  16. meerk40t/core/node/op_raster.py +8 -8
  17. meerk40t/core/planner.py +23 -0
  18. meerk40t/core/undos.py +1 -1
  19. meerk40t/core/wordlist.py +1 -0
  20. meerk40t/dxf/dxf_io.py +6 -0
  21. meerk40t/extra/encode_detect.py +8 -2
  22. meerk40t/extra/hershey.py +2 -3
  23. meerk40t/extra/inkscape.py +3 -5
  24. meerk40t/extra/mk_potrace.py +1959 -0
  25. meerk40t/extra/outerworld.py +2 -3
  26. meerk40t/extra/param_functions.py +2 -2
  27. meerk40t/extra/potrace.py +14 -10
  28. meerk40t/grbl/device.py +4 -1
  29. meerk40t/grbl/gui/grblcontroller.py +2 -2
  30. meerk40t/grbl/interpreter.py +1 -1
  31. meerk40t/gui/about.py +3 -5
  32. meerk40t/gui/basicops.py +3 -3
  33. meerk40t/gui/busy.py +75 -13
  34. meerk40t/gui/choicepropertypanel.py +365 -379
  35. meerk40t/gui/consolepanel.py +3 -3
  36. meerk40t/gui/gui_mixins.py +4 -1
  37. meerk40t/gui/hersheymanager.py +13 -3
  38. meerk40t/gui/laserpanel.py +12 -7
  39. meerk40t/gui/materialmanager.py +33 -6
  40. meerk40t/gui/plugin.py +9 -3
  41. meerk40t/gui/propertypanels/operationpropertymain.py +1 -1
  42. meerk40t/gui/ribbon.py +4 -1
  43. meerk40t/gui/scene/widget.py +1 -1
  44. meerk40t/gui/scenewidgets/rectselectwidget.py +19 -16
  45. meerk40t/gui/scenewidgets/selectionwidget.py +26 -20
  46. meerk40t/gui/simpleui.py +13 -8
  47. meerk40t/gui/simulation.py +22 -2
  48. meerk40t/gui/spoolerpanel.py +8 -11
  49. meerk40t/gui/themes.py +7 -1
  50. meerk40t/gui/tips.py +2 -3
  51. meerk40t/gui/toolwidgets/toolmeasure.py +4 -1
  52. meerk40t/gui/wxmeerk40t.py +32 -3
  53. meerk40t/gui/wxmmain.py +72 -6
  54. meerk40t/gui/wxmscene.py +95 -6
  55. meerk40t/gui/wxmtree.py +17 -11
  56. meerk40t/gui/wxutils.py +1 -1
  57. meerk40t/image/imagetools.py +21 -6
  58. meerk40t/kernel/kernel.py +31 -6
  59. meerk40t/kernel/settings.py +2 -0
  60. meerk40t/lihuiyu/device.py +9 -3
  61. meerk40t/main.py +22 -5
  62. meerk40t/network/console_server.py +52 -14
  63. meerk40t/network/web_server.py +15 -1
  64. meerk40t/ruida/device.py +5 -1
  65. meerk40t/ruida/gui/gui.py +6 -6
  66. meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
  67. meerk40t/ruida/rdjob.py +3 -3
  68. meerk40t/tools/geomstr.py +88 -0
  69. meerk40t/tools/polybool.py +2 -1
  70. meerk40t/tools/shxparser.py +92 -34
  71. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/METADATA +1 -1
  72. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/RECORD +77 -75
  73. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/WHEEL +1 -1
  74. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/LICENSE +0 -0
  75. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/entry_points.txt +0 -0
  76. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/top_level.txt +0 -0
  77. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/zip-safe +0 -0
@@ -0,0 +1,105 @@
1
+ from math import sqrt
2
+
3
+ from meerk40t.core.node.node import Fillrule, Linecap, Linejoin, Node
4
+ from meerk40t.core.units import (
5
+ UNITS_PER_MM,
6
+ UNITS_PER_PIXEL,
7
+ UNITS_PER_POINT,
8
+ Angle,
9
+ Length,
10
+ )
11
+ from meerk40t.kernel import CommandSyntaxError
12
+ from meerk40t.svgelements import (
13
+ SVG_RULE_EVENODD,
14
+ SVG_RULE_NONZERO,
15
+ Color,
16
+ Matrix,
17
+ Path,
18
+ Polygon,
19
+ Polyline,
20
+ )
21
+ from meerk40t.tools.geomstr import Geomstr
22
+
23
+
24
+ def plugin(kernel, lifecycle=None):
25
+ _ = kernel.translation
26
+ if lifecycle == "postboot":
27
+ init_commands(kernel)
28
+
29
+
30
+ def init_commands(kernel):
31
+ self = kernel.elements
32
+
33
+ _ = kernel.translation
34
+
35
+ classify_new = self.post_classify
36
+
37
+ def polybool_crash_test(channel, _):
38
+ # rect_info = "M 30961.4173228,10320.4724409 L 474741.732283,10320.4724409 L 474741.732283,392177.952756 L 30961.4173228,392177.952756 L 30961.4173228,10320.4724409"
39
+ # geom = Geomstr().svg(rect_info)
40
+ # Testcase: polybool crash AttributeError: 'NoneType' object has no attribute 'next' for element difference
41
+ data = []
42
+ x_pos = 30961.4173228
43
+ y_pos = 10320.4724409
44
+ width = 474741.732283 - x_pos
45
+ height = 392177.952756 - y_pos
46
+ node1 = self.elem_branch.add(
47
+ label = "Shape 1",
48
+ x=x_pos,
49
+ y=y_pos,
50
+ width=width,
51
+ height=height,
52
+ stroke=self.default_stroke,
53
+ stroke_width=self.default_strokewidth,
54
+ fill=self.default_fill,
55
+ type="elem rect",
56
+ )
57
+ data.append(node1)
58
+ geom = Geomstr()
59
+ definition = (
60
+ ((30961.417322839014,10320.472440944883),
61
+ (72243.30708661533,10320.472440944883)),
62
+ ((72243.30708661533,10320.472440944883),
63
+ (72243.30708661678,20640.944881889765)),
64
+ ((72243.30708661533,10320.472440944883),
65
+ (113525.19685039311,10320.472440944883)),
66
+ ((113525.19685039311,10320.472440944883),
67
+ (113525.19685039311,20640.944881889765)),
68
+ ((72243.30708661678,20640.944881889765),
69
+ (113525.19685039311,20640.944881889765)),
70
+ ((113525.19685039311,10320.472440944883),
71
+ (154807.08661416644,10320.472440944883)),
72
+ ((154807.08661416644,10320.472440944883),
73
+ (154807.08661417232,20640.944881889765)),
74
+ ((154807.08661417232,20640.944881889765),
75
+ (196088.97637794417,20640.944881889765)),
76
+ ((154807.08661416644,10320.472440944883),
77
+ (196088.97637794568,10320.472440944883)),
78
+ ((196088.97637794417,20640.944881889765),
79
+ (196088.97637794568,10320.472440944883)),
80
+ )
81
+ for s, e in definition:
82
+ geom.line(complex(s[0], s[1]), complex(e[0], e[1]))
83
+ node2 = self.elem_branch.add(
84
+ label = "Shape 2",
85
+ geometry=geom,
86
+ stroke=self.default_stroke,
87
+ stroke_width=self.default_strokewidth,
88
+ fill=self.default_fill,
89
+ type="elem path",
90
+ )
91
+
92
+ data.append(node2)
93
+ return "elements", data
94
+
95
+ @self.console_command(
96
+ "test",
97
+ output_type="elements",
98
+ )
99
+ def element_test(command, channel, _, data=None, post=None, **kwargs):
100
+ # rect_info = "M 30961.4173228,10320.4724409 L 474741.732283,10320.4724409 L 474741.732283,392177.952756 L 30961.4173228,392177.952756 L 30961.4173228,10320.4724409"
101
+ # geom = Geomstr().svg(rect_info)
102
+ # Testcase: polybool crash AttributeError: 'NoneType' object has no attribute 'next' for element difference
103
+ info, data = polybool_crash_test(channel, _)
104
+ post.append(classify_new(data))
105
+ return info, data
@@ -499,9 +499,12 @@ class Node:
499
499
  node_copy._parent = copied_parent
500
500
  copied_parent._children.append(node_copy)
501
501
  if node.type == "reference":
502
- original_referenced, copied_referenced = links[id(node.node)]
503
- node_copy.node = copied_referenced
504
- copied_referenced._references.append(node_copy)
502
+ try:
503
+ original_referenced, copied_referenced = links[id(node.node)]
504
+ node_copy.node = copied_referenced
505
+ copied_referenced._references.append(node_copy)
506
+ except KeyError:
507
+ pass
505
508
 
506
509
  def _validate_tree(self):
507
510
  for c in self._children:
@@ -246,7 +246,15 @@ class CutOpNode(Node, Parameters):
246
246
  return False, False, None
247
247
  feedback = []
248
248
  if node.type in self._allowed_elements:
249
- if not self.default:
249
+ if self.default and usedefault:
250
+ # Have classified but more classification might be needed
251
+ if self.valid_node_for_reference(node):
252
+ self.add_reference(node)
253
+ feedback.append("stroke")
254
+ feedback.append("fill")
255
+ return True, self.stopop, feedback
256
+ else:
257
+ # Even if the default attribute set that would be a valid thing
250
258
  if self.has_attributes():
251
259
  result = False
252
260
  for attribute in self.allowed_attributes:
@@ -271,13 +279,6 @@ class CutOpNode(Node, Parameters):
271
279
  feedback.append("stroke")
272
280
  feedback.append("fill")
273
281
  return True, self.stopop, feedback
274
- elif self.default and usedefault:
275
- # Have classified but more classification might be needed
276
- if self.valid_node_for_reference(node):
277
- self.add_reference(node)
278
- feedback.append("stroke")
279
- feedback.append("fill")
280
- return True, self.stopop, feedback
281
282
  return False, False, None
282
283
 
283
284
  def add_reference(self, node=None, pos=None, **kwargs):
@@ -174,7 +174,14 @@ class DotsOpNode(Node, Parameters):
174
174
  return False, False, None
175
175
  feedback = []
176
176
  if node.type in self._allowed_elements:
177
- if not self.default:
177
+ if self.default and usedefault:
178
+ # Have classified but more classification might be needed
179
+ if self.valid_node_for_reference(node):
180
+ self.add_reference(node)
181
+ feedback.append("stroke")
182
+ feedback.append("fill")
183
+ return True, self.stopop, feedback
184
+ else:
178
185
  if self.has_attributes():
179
186
  result = False
180
187
  for attribute in self.allowed_attributes:
@@ -199,13 +206,6 @@ class DotsOpNode(Node, Parameters):
199
206
  feedback.append("stroke")
200
207
  feedback.append("fill")
201
208
  return True, self.stopop, feedback
202
- elif self.default and usedefault:
203
- # Have classified but more classification might be needed
204
- if self.valid_node_for_reference(node):
205
- self.add_reference(node)
206
- feedback.append("stroke")
207
- feedback.append("fill")
208
- return True, self.stopop, feedback
209
209
  return False, False, None
210
210
 
211
211
  def load(self, settings, section):
@@ -211,7 +211,13 @@ class EngraveOpNode(Node, Parameters):
211
211
  return False, False, None
212
212
  feedback = []
213
213
  if node.type in self._allowed_elements:
214
- if not self.default:
214
+ if self.default and usedefault:
215
+ # Have classified but more classification might be needed
216
+ if self.valid_node_for_reference(node):
217
+ feedback.append("stroke")
218
+ feedback.append("fill")
219
+ return True, self.stopop, feedback
220
+ else:
215
221
  if self.has_attributes():
216
222
  result = False
217
223
  for attribute in self.allowed_attributes:
@@ -236,12 +242,6 @@ class EngraveOpNode(Node, Parameters):
236
242
  feedback.append("stroke")
237
243
  feedback.append("fill")
238
244
  return True, self.stopop, feedback
239
- elif self.default and usedefault:
240
- # Have classified but more classification might be needed
241
- if self.valid_node_for_reference(node):
242
- feedback.append("stroke")
243
- feedback.append("fill")
244
- return True, self.stopop, feedback
245
245
  return False, False, None
246
246
 
247
247
  def add_reference(self, node=None, pos=None, **kwargs):
@@ -268,7 +268,14 @@ class RasterOpNode(Node, Parameters):
268
268
 
269
269
  feedback = []
270
270
  if node.type in self._allowed_elements:
271
- if not self.default:
271
+ if self.default and usedefault:
272
+ # Have classified but more classification might be needed
273
+ if self.valid_node_for_reference(node):
274
+ self.add_reference(node)
275
+ feedback.append("stroke")
276
+ feedback.append("fill")
277
+ return True, self.stopop, feedback
278
+ else:
272
279
  if self.has_attributes():
273
280
  result = False
274
281
  for attribute in self.allowed_attributes:
@@ -311,13 +318,6 @@ class RasterOpNode(Node, Parameters):
311
318
  self.add_reference(node)
312
319
  # Have classified but more classification might be needed
313
320
  return True, self.stopop, feedback
314
- elif self.default and usedefault:
315
- # Have classified but more classification might be needed
316
- if self.valid_node_for_reference(node):
317
- self.add_reference(node)
318
- feedback.append("stroke")
319
- feedback.append("fill")
320
- return True, self.stopop, feedback
321
321
  return False, False, None
322
322
 
323
323
  def load(self, settings, section):
meerk40t/core/planner.py CHANGED
@@ -181,6 +181,29 @@ def plugin(kernel, lifecycle=None):
181
181
  "section": "_20_Reducing Movements",
182
182
  "conditional": (context, "opt_reduce_travel"),
183
183
  },
184
+ {
185
+ "attr": "opt_stitching",
186
+ "object": context,
187
+ "default": False,
188
+ "type": bool,
189
+ "label": _("Combine path segments"),
190
+ "tip":
191
+ _("Stitch segments together that are very close (ideally having joint start/end points).") + "\n" +
192
+ _("Only inside a single cut/engrave operation."),
193
+ "page": "Optimisations",
194
+ "section": "_05_Stitching",
195
+ },
196
+ {
197
+ "attr": "opt_stitch_tolerance",
198
+ "object": context,
199
+ "default": "0",
200
+ "type": Length,
201
+ "label": _("Tolerance"),
202
+ "tip": _("Tolerance to decide whether two path segments should be joined."),
203
+ "page": "Optimisations",
204
+ "section": "_05_Stitching",
205
+ "conditional": (context, "opt_stitching"),
206
+ },
184
207
  {
185
208
  "attr": "opt_inner_first",
186
209
  "object": context,
meerk40t/core/undos.py CHANGED
@@ -81,7 +81,7 @@ class Undo:
81
81
  elif self._undo_index < len(self._undo_stack) and self._undo_stack[self._undo_index].hold:
82
82
  # Just add another one on top of it
83
83
  self._undo_index += 1
84
- elif self._undo_stack[self._undo_index].message == self.LAST_STATE:
84
+ elif self._undo_index < len(self._undo_stack) and self._undo_stack[self._undo_index].message == self.LAST_STATE:
85
85
  # Will be overwritten
86
86
  pass
87
87
  elif self._undo_index < len(self._undo_stack) - 1 and self._undo_stack[self._undo_index + 1].message != self.LAST_STATE:
meerk40t/core/wordlist.py CHANGED
@@ -394,6 +394,7 @@ class Wordlist:
394
394
 
395
395
  def load_csv_file(self, filename, force_header=None):
396
396
  self.empty_csv()
397
+ ct = 0
397
398
  headers = []
398
399
  decoder = EncodingDetectFile()
399
400
  result = decoder.load(filename)
meerk40t/dxf/dxf_io.py CHANGED
@@ -251,6 +251,8 @@ class DXFProcessor:
251
251
  elif dxftype == "ELLIPSE":
252
252
  center = (entity.dxf.center) # Center point of the ellipse (3D, but we'll use x,y)
253
253
  major_axis = entity.dxf.major_axis # Vector representing the major axis
254
+ minor_axis = entity.minor_axis # Vector representing the minor axis
255
+ # They should have the same sign, if they are different then they are mirrored?!
254
256
  ratio = entity.dxf.ratio # Ratio of minor to major axis
255
257
  start_angle, end_angle = get_angles(entity)
256
258
 
@@ -262,6 +264,10 @@ class DXFProcessor:
262
264
  major_axis[0] ** 2 + major_axis[1] ** 2
263
265
  ) # Length of the major axis (in XY plane)
264
266
  b = a * ratio # Length of the minor axis
267
+ # Different signs? Inverse
268
+ if major_axis[0] * minor_axis[1] < 0:
269
+ b *= -1
270
+
265
271
  # geom = Geomstr.ellipse(
266
272
  # start_t=start_angle,
267
273
  # end_t=end_angle,
@@ -165,13 +165,19 @@ class EncodingDetectFile:
165
165
 
166
166
  def load(self, file_path):
167
167
  # open file
168
- fh = open(file_path, "rb")
168
+ try:
169
+ fh = open(file_path, "rb")
170
+ except Exception:
171
+ return False
169
172
 
170
173
  # detect a byte order mark (BOM)
171
174
  file_encoding, bom_marker, file_data = self._detect_bom(fh)
172
175
  if file_encoding:
173
176
  # file has a BOM - decode everything past it
174
- decode = fh.read().decode(file_encoding)
177
+ try:
178
+ decode = fh.read().decode(file_encoding)
179
+ except UnicodeDecodeError:
180
+ return False
175
181
  # print(f"decoded: {decode}")
176
182
  fh.close()
177
183
 
meerk40t/extra/hershey.py CHANGED
@@ -7,7 +7,6 @@ from os.path import basename, exists, join, realpath, splitext
7
7
  from meerk40t.core.node.elem_path import PathNode
8
8
  from meerk40t.core.node.node import Fillrule, Linejoin
9
9
  from meerk40t.core.units import UNITS_PER_INCH, Length
10
- from meerk40t.kernel import get_safe_path
11
10
  from meerk40t.tools.geomstr import BeamTable, Geomstr
12
11
  from meerk40t.tools.jhfparser import JhfFont
13
12
  from meerk40t.tools.shxparser import ShxFont, ShxFontParseError
@@ -116,7 +115,7 @@ class Meerk40tFonts:
116
115
 
117
116
  @property
118
117
  def font_directory(self):
119
- safe_dir = realpath(get_safe_path(self.context.kernel.name))
118
+ safe_dir = self.context.kernel.os_information["WORKDIR"]
120
119
  self.context.setting(str, "font_directory", safe_dir)
121
120
  fontdir = self.context.font_directory
122
121
  if not exists(fontdir):
@@ -129,7 +128,7 @@ class Meerk40tFonts:
129
128
  def font_directory(self, value):
130
129
  if not exists(value):
131
130
  # We cant allow a non-valid directory
132
- value = realpath(get_safe_path(self.context.kernel.name))
131
+ value = self.context.kernel.os_information["WORKDIR"]
133
132
  self.context.setting(str, "font_directory", value)
134
133
  self.context.font_directory = value
135
134
  self._available_fonts = None
@@ -5,8 +5,6 @@ from subprocess import PIPE, TimeoutExpired, run
5
5
  from time import time
6
6
 
7
7
  from meerk40t.core.exceptions import BadFileError
8
- from meerk40t.kernel.kernel import get_safe_path
9
-
10
8
 
11
9
  def get_inkscape(context, manual_candidate=None):
12
10
  root_context = context
@@ -143,7 +141,7 @@ class MultiLoader:
143
141
  break
144
142
 
145
143
  context_root = kernel.root
146
- safe_dir = os.path.realpath(get_safe_path(kernel.name))
144
+ safe_dir = kernel.os_information["WORKDIR"]
147
145
  logfile = os.path.join(safe_dir, "inkscape.log")
148
146
 
149
147
  inkscape = get_inkscape(context_root)
@@ -228,7 +226,7 @@ def plugin(kernel, lifecycle):
228
226
  inkscape_path, filename = data
229
227
  channel(_("inkscape load - loading the previous conversion..."))
230
228
  try:
231
- kernel.elements.load(filename)
229
+ kernel.elements.load(filename, svg_ppi=kernel.elements.svg_ppi)
232
230
  except BadFileError as e:
233
231
  channel(_("File is Malformed."))
234
232
  channel(str(e))
@@ -423,7 +421,7 @@ def plugin(kernel, lifecycle):
423
421
  ],
424
422
  "pattern": [False, ("<pattern",), METHOD_CONVERT_TO_PNG],
425
423
  }
426
- safe_dir = os.path.realpath(get_safe_path(kernel.name))
424
+ safe_dir = kernel.os_information["WORKDIR"]
427
425
  svg_temp_file = os.path.join(safe_dir, "temp.svg")
428
426
  png_temp_file = os.path.join(safe_dir, "temp.png")
429
427
  needs_conversion = 0