meerk40t 0.9.7010__py2.py3-none-any.whl → 0.9.7020__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 (55) hide show
  1. meerk40t/balormk/galvo_commands.py +1 -2
  2. meerk40t/core/elements/branches.py +18 -4
  3. meerk40t/core/elements/element_treeops.py +34 -0
  4. meerk40t/core/elements/elements.py +49 -63
  5. meerk40t/core/elements/offset_clpr.py +4 -3
  6. meerk40t/core/elements/shapes.py +1 -1
  7. meerk40t/core/elements/testcases.py +105 -0
  8. meerk40t/core/node/op_cut.py +9 -8
  9. meerk40t/core/node/op_dots.py +8 -8
  10. meerk40t/core/node/op_engrave.py +7 -7
  11. meerk40t/core/node/op_raster.py +8 -8
  12. meerk40t/extra/encode_detect.py +8 -2
  13. meerk40t/extra/hershey.py +2 -3
  14. meerk40t/extra/inkscape.py +3 -5
  15. meerk40t/extra/outerworld.py +2 -3
  16. meerk40t/extra/param_functions.py +1 -1
  17. meerk40t/grbl/device.py +4 -1
  18. meerk40t/grbl/gui/grblcontroller.py +2 -2
  19. meerk40t/gui/busy.py +75 -13
  20. meerk40t/gui/choicepropertypanel.py +364 -375
  21. meerk40t/gui/consolepanel.py +3 -3
  22. meerk40t/gui/hersheymanager.py +13 -3
  23. meerk40t/gui/laserpanel.py +12 -7
  24. meerk40t/gui/materialmanager.py +33 -6
  25. meerk40t/gui/plugin.py +9 -3
  26. meerk40t/gui/propertypanels/operationpropertymain.py +1 -1
  27. meerk40t/gui/ribbon.py +4 -1
  28. meerk40t/gui/scene/widget.py +1 -1
  29. meerk40t/gui/scenewidgets/rectselectwidget.py +19 -16
  30. meerk40t/gui/scenewidgets/selectionwidget.py +26 -20
  31. meerk40t/gui/simpleui.py +13 -8
  32. meerk40t/gui/simulation.py +22 -2
  33. meerk40t/gui/spoolerpanel.py +2 -2
  34. meerk40t/gui/tips.py +2 -3
  35. meerk40t/gui/toolwidgets/toolmeasure.py +4 -1
  36. meerk40t/gui/wxmeerk40t.py +6 -3
  37. meerk40t/gui/wxmmain.py +72 -6
  38. meerk40t/gui/wxmscene.py +2 -6
  39. meerk40t/gui/wxmtree.py +17 -11
  40. meerk40t/gui/wxutils.py +1 -1
  41. meerk40t/image/imagetools.py +20 -5
  42. meerk40t/kernel/kernel.py +21 -2
  43. meerk40t/main.py +1 -1
  44. meerk40t/network/console_server.py +52 -14
  45. meerk40t/network/web_server.py +15 -1
  46. meerk40t/ruida/device.py +5 -1
  47. meerk40t/tools/polybool.py +2 -1
  48. meerk40t/tools/shxparser.py +92 -34
  49. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +1 -1
  50. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/RECORD +55 -54
  51. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +0 -0
  52. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +0 -0
  53. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
  54. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -0
  55. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +0 -0
@@ -1153,7 +1153,6 @@ def plugin(service, lifecycle):
1153
1153
  import platform
1154
1154
 
1155
1155
  from meerk40t.balormk.clone_loader import load_sys
1156
- from meerk40t.kernel import get_safe_path
1157
1156
 
1158
1157
  kernel = service.kernel
1159
1158
 
@@ -1173,7 +1172,7 @@ def plugin(service, lifecycle):
1173
1172
  return
1174
1173
 
1175
1174
  # Check for file in the meerk40t directory (safe_path)
1176
- directory = get_safe_path(kernel.name, create=True)
1175
+ directory = kernel.os_information["WORKDIR"]
1177
1176
  p = os.path.join(directory, service.clone_sys)
1178
1177
  if os.path.exists(p):
1179
1178
  load_sys(p, channel=channel)
@@ -187,7 +187,7 @@ def init_commands(kernel):
187
187
  return
188
188
  try:
189
189
  channel(_("loading..."))
190
- result = self.load(new_file)
190
+ result = self.load(new_file, svg_ppi=self.svg_ppi)
191
191
  if result:
192
192
  channel(_("Done."))
193
193
  except AttributeError:
@@ -1410,6 +1410,20 @@ def init_commands(kernel):
1410
1410
  self.remove_operations(data)
1411
1411
  self.signal("update_group_labels")
1412
1412
 
1413
+ @self.console_command(
1414
+ "clear_all", help=_("Clear all content"), input_type=("elements", "ops")
1415
+ )
1416
+ def e_clear(command, channel, _, data=None, data_type=None, **kwargs):
1417
+ channel(_("Deleting…"))
1418
+ fast = True
1419
+ with self.undoscope("Deleting"):
1420
+ if data_type == "elements":
1421
+ self.clear_elements(fast=fast)
1422
+ self.emphasized()
1423
+ else:
1424
+ self.clear_operations(fast=fast)
1425
+ self.signal("rebuild_tree", "all")
1426
+
1413
1427
  # ==========
1414
1428
  # ELEMENT BASE
1415
1429
  # ==========
@@ -1490,7 +1504,7 @@ def init_commands(kernel):
1490
1504
  else:
1491
1505
  target = self.reg_branch
1492
1506
  scope = "Elements -> Regmarks"
1493
-
1507
+
1494
1508
  with self.undoscope(scope):
1495
1509
  if data is None:
1496
1510
  data = list()
@@ -1826,8 +1840,8 @@ def init_commands(kernel):
1826
1840
  subpath.ensure_proper_subpaths()
1827
1841
  idx += 1
1828
1842
  subnode = group_node.add(
1829
- geometry=subpath,
1830
- type="elem path",
1843
+ geometry=subpath,
1844
+ type="elem path",
1831
1845
  label=f"{node_label}-{idx}",
1832
1846
  stroke=node_attributes.get("stroke", None),
1833
1847
  fill=node_attributes.get("fill", None),
@@ -3570,6 +3570,40 @@ def init_tree(kernel):
3570
3570
  with self.undoscope("Outline"):
3571
3571
  self(f"outline {offset}mm\n")
3572
3572
  self.signal("refresh_tree")
3573
+
3574
+ @tree_conditional(
3575
+ lambda node: not is_regmark(node)
3576
+ and hasattr(node, "as_geometry")
3577
+ )
3578
+ @tree_submenu(_("Offset shapes..."))
3579
+ @tree_iterate("offset", 1, 5)
3580
+ @tree_operation(
3581
+ _("...to outside with {offset}mm distance"),
3582
+ node_type=elem_nodes,
3583
+ help=_("Create an outer offset around the selected elements"),
3584
+ grouping="50_ELEM_MODIFY_ZMISC"
3585
+ )
3586
+ def make_positive_offsets(node, offset=1, **kwargs):
3587
+ with self.undoscope("Offset"):
3588
+ self(f"offset {offset}mm\n")
3589
+ self.signal("refresh_tree")
3590
+
3591
+ @tree_conditional(
3592
+ lambda node: not is_regmark(node)
3593
+ and hasattr(node, "as_geometry")
3594
+ )
3595
+ @tree_submenu(_("Offset shapes..."))
3596
+ @tree_iterate("offset", 1, 5)
3597
+ @tree_operation(
3598
+ _("...to inside with {offset}mm distance"),
3599
+ node_type=elem_nodes,
3600
+ help=_("Create an inner offset around the selected elements"),
3601
+ grouping="50_ELEM_MODIFY_ZMISC"
3602
+ )
3603
+ def make_negative_offsets(node, offset=1, **kwargs):
3604
+ with self.undoscope("Offset"):
3605
+ self(f"offset -{offset}mm\n")
3606
+ self.signal("refresh_tree")
3573
3607
 
3574
3608
  def mergeable(node):
3575
3609
  elems = list(self.elems(emphasized=True))
@@ -75,6 +75,7 @@ def plugin(kernel, lifecycle=None):
75
75
  tree_commands,
76
76
  undo_redo,
77
77
  wordlist,
78
+ testcases,
78
79
  )
79
80
 
80
81
  return [
@@ -96,6 +97,7 @@ def plugin(kernel, lifecycle=None):
96
97
  placements.plugin,
97
98
  offset_mk.plugin,
98
99
  offset_clpr.plugin,
100
+ testcases.plugin,
99
101
  ]
100
102
  elif lifecycle == "preregister":
101
103
  kernel.register(
@@ -2699,6 +2701,13 @@ class Elemental(Service):
2699
2701
  if elements is None:
2700
2702
  return
2701
2703
  new_operations_added = False
2704
+ debug_set = {}
2705
+
2706
+ def update_debug_set(debug_set, opnode):
2707
+ if opnode.type not in debug_set:
2708
+ debug_set[opnode.type] = 0
2709
+ debug_set[opnode.type] = debug_set[opnode.type] + 1
2710
+
2702
2711
 
2703
2712
  if len(list(self.ops())) == 0 and not self.operation_default_empty:
2704
2713
  has_cut = False
@@ -2732,6 +2741,13 @@ class Elemental(Service):
2732
2741
  add_op_function = self.add_classify_op
2733
2742
  for node in elements:
2734
2743
  node_desc = f"[{node.type}]{'' if node.id is None else node.id + '-'}{'<none>' if node.label is None else node.display_label()}"
2744
+ if hasattr(node, "stroke") or hasattr(node, "fill"):
2745
+ info = ""
2746
+ if hasattr(node, "stroke") and node.stroke is not None:
2747
+ info += f"S:{node.stroke},"
2748
+ if hasattr(node, "fill") and node.fill is not None:
2749
+ info += f"F:{node.fill},"
2750
+ node_desc += f"({info})"
2735
2751
  # Following lines added to handle 0.7 special ops added to ops list
2736
2752
  if hasattr(node, "operation"):
2737
2753
  add_op_function(node)
@@ -2758,7 +2774,7 @@ class Elemental(Service):
2758
2774
  if not do_fill and op.type in ("op raster", "op image"):
2759
2775
  continue
2760
2776
  is_black = False
2761
- whisperer = True
2777
+ perform_classification = True
2762
2778
  if (
2763
2779
  hasattr(node, "stroke")
2764
2780
  and node.stroke is not None
@@ -2780,18 +2796,18 @@ class Elemental(Service):
2780
2796
  and is_black
2781
2797
  and isinstance(op, RasterOpNode)
2782
2798
  ):
2783
- whisperer = False
2799
+ perform_classification = False
2784
2800
  elif (
2785
2801
  self.classify_black_as_raster
2786
2802
  and is_black
2787
2803
  and isinstance(op, EngraveOpNode)
2788
2804
  ):
2789
- whisperer = False
2805
+ perform_classification = False
2790
2806
  if debug:
2791
2807
  debug(
2792
- f"For {op.type}.{op.id}: black={is_black}, perform={whisperer}, flag={self.classify_black_as_raster}"
2808
+ f"For {op.type}.{op.id}: black={is_black}, perform={perform_classification}, flag={self.classify_black_as_raster}"
2793
2809
  )
2794
- if hasattr(op, "classify") and whisperer:
2810
+ if hasattr(op, "classify") and perform_classification:
2795
2811
  classified, should_break, feedback = op.classify(
2796
2812
  node,
2797
2813
  fuzzy=tempfuzzy,
@@ -2801,6 +2817,7 @@ class Elemental(Service):
2801
2817
  else:
2802
2818
  continue
2803
2819
  if classified:
2820
+ update_debug_set(debug_set, op)
2804
2821
  if feedback is not None and "stroke" in feedback:
2805
2822
  classif_info[0] = True
2806
2823
  if feedback is not None and "fill" in feedback:
@@ -2869,78 +2886,41 @@ class Elemental(Service):
2869
2886
  # let's iterate through the default ops and add them
2870
2887
  if debug:
2871
2888
  debug("Pass 2 (wasn't classified), looking for default ops")
2889
+ default_candidates = []
2872
2890
  for op in operations:
2873
- if classif_info[0] and op.type in (
2874
- "op engrave",
2875
- "op cut",
2876
- "op dots",
2877
- ):
2878
- continue
2879
- if classif_info[1] and op.type in ("op raster", "op image"):
2880
- continue
2881
- is_black = False
2882
- whisperer = True
2883
2891
  if (
2884
- hasattr(node, "stroke")
2885
- and node.stroke is not None
2886
- and node.stroke.argb is not None
2887
- and node.type != "elem text"
2888
- ):
2889
- if fuzzy:
2890
- is_black = (
2891
- Color.distance("black", abs(node.stroke))
2892
- <= fuzzydistance
2893
- or Color.distance("white", abs(node.stroke))
2894
- <= fuzzydistance
2895
- )
2896
- else:
2897
- is_black = Color("black") == abs(node.stroke) or Color(
2898
- "white"
2899
- ) == abs(node.stroke)
2900
- if (
2901
- not self.classify_black_as_raster
2902
- and is_black
2903
- and isinstance(op, RasterOpNode)
2892
+ hasattr(op, "classify") and
2893
+ getattr(op, "default", False) and
2894
+ hasattr(op, "valid_node_for_reference") and
2895
+ op.valid_node_for_reference(node)
2904
2896
  ):
2905
- # print ("Default Skip Raster")
2906
- whisperer = False
2907
- elif (
2908
- self.classify_black_as_raster
2909
- and is_black
2910
- and isinstance(op, EngraveOpNode)
2911
- ):
2912
- whisperer = False
2913
- if debug:
2914
- debug(
2915
- f"For {op.type}.{op.id}: black={is_black}, perform={whisperer}, flag={self.classify_black_as_raster}"
2916
- )
2917
- if hasattr(op, "classify") and whisperer:
2918
- classified, should_break, feedback = op.classify(
2919
- node,
2920
- fuzzy=fuzzy,
2921
- fuzzydistance=fuzzydistance,
2922
- usedefault=True,
2923
- )
2924
- else:
2925
- continue
2897
+ default_candidates.append(op)
2898
+ if len(default_candidates) > 1 and debug:
2899
+ debug(f"For node {node_desc} there were {len(default_candidates)} default operations available, nb the very first will be taken!")
2900
+ for op in default_candidates:
2901
+ classified, should_break, feedback = op.classify(
2902
+ node,
2903
+ fuzzy=fuzzy,
2904
+ fuzzydistance=fuzzydistance,
2905
+ usedefault=True,
2906
+ )
2926
2907
  if classified:
2927
- if feedback is not None and "stroke" in feedback:
2928
- classif_info[0] = True
2929
- if feedback is not None and "fill" in feedback:
2930
- classif_info[1] = True
2908
+ update_debug_set(debug_set, op)
2909
+ # Default ops fulfill stuff by definition
2910
+ classif_info[0] = True
2911
+ classif_info[1] = True
2931
2912
  was_classified = True
2932
2913
  if debug:
2933
2914
  debug(
2934
- f"Was classified to default operation: {type(op).__name__}, break={should_break}"
2915
+ f"Was classified to default operation: {type(op).__name__}"
2935
2916
  )
2936
- if should_break:
2937
2917
  break
2938
2918
  # Let's make sure we only consider relevant, i.e. existing attributes...
2939
2919
  if hasattr(node, "stroke"):
2940
2920
  if node.stroke is None or node.stroke.argb is None:
2941
2921
  classif_info[0] = True
2942
2922
  if node.type == "elem text":
2943
- # even if it has, we are not going to something with it
2923
+ # even if it has, we are not going to do something with it
2944
2924
  classif_info[0] = True
2945
2925
  else:
2946
2926
  classif_info[0] = True
@@ -3218,8 +3198,14 @@ class Elemental(Service):
3218
3198
 
3219
3199
  if not existing:
3220
3200
  op.add_reference(node)
3201
+ update_debug_set(debug_set, op)
3202
+
3221
3203
 
3222
3204
  self.remove_unused_default_copies()
3205
+ if debug:
3206
+ debug("Summary:")
3207
+ for key, count in debug_set.items():
3208
+ debug(f"{count} items assigned to {key}")
3223
3209
  if new_operations_added:
3224
3210
  self.signal("tree_changed")
3225
3211
 
@@ -876,9 +876,6 @@ def init_commands(kernel):
876
876
  if method is None:
877
877
  method = "union"
878
878
  method = method.lower()
879
- if filltype is None:
880
- filltype = "evenodd"
881
- filltype = filltype.lower()
882
879
  if keep is None:
883
880
  keep = False
884
881
 
@@ -891,6 +888,10 @@ def init_commands(kernel):
891
888
  else:
892
889
  long_method = "Union"
893
890
 
891
+ if filltype is None:
892
+ filltype = "evenodd" if method != "union" else "nonzero"
893
+ filltype = filltype.lower()
894
+
894
895
  if filltype.startswith("no") or filltype.startswith("z"):
895
896
  long_filltype = "NonZero"
896
897
  elif filltype.startswith("p") or filltype.startswith("+"):
@@ -1729,7 +1729,7 @@ def init_commands(kernel):
1729
1729
  # self.signal("rebuild_tree")
1730
1730
  self.signal("refresh_tree", apply)
1731
1731
  else:
1732
- self.signal("element_property_update", apply)
1732
+ self.signal("element_property_reload", apply)
1733
1733
  self.signal("refresh_scene", "Scene")
1734
1734
  return "elements", data
1735
1735
 
@@ -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
@@ -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):
@@ -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