meerk40t 0.9.7020__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 (39) hide show
  1. meerk40t/core/cutcode/cutcode.py +1 -1
  2. meerk40t/core/cutplan.py +70 -2
  3. meerk40t/core/elements/element_treeops.py +9 -7
  4. meerk40t/core/elements/grid.py +8 -1
  5. meerk40t/core/elements/offset_mk.py +2 -1
  6. meerk40t/core/elements/shapes.py +378 -259
  7. meerk40t/core/node/node.py +6 -3
  8. meerk40t/core/planner.py +23 -0
  9. meerk40t/core/undos.py +1 -1
  10. meerk40t/core/wordlist.py +1 -0
  11. meerk40t/dxf/dxf_io.py +6 -0
  12. meerk40t/extra/mk_potrace.py +1959 -0
  13. meerk40t/extra/param_functions.py +1 -1
  14. meerk40t/extra/potrace.py +14 -10
  15. meerk40t/grbl/interpreter.py +1 -1
  16. meerk40t/gui/about.py +3 -5
  17. meerk40t/gui/basicops.py +3 -3
  18. meerk40t/gui/choicepropertypanel.py +1 -4
  19. meerk40t/gui/gui_mixins.py +4 -1
  20. meerk40t/gui/spoolerpanel.py +6 -9
  21. meerk40t/gui/themes.py +7 -1
  22. meerk40t/gui/wxmeerk40t.py +26 -0
  23. meerk40t/gui/wxmscene.py +93 -0
  24. meerk40t/image/imagetools.py +1 -1
  25. meerk40t/kernel/kernel.py +10 -4
  26. meerk40t/kernel/settings.py +2 -0
  27. meerk40t/lihuiyu/device.py +9 -3
  28. meerk40t/main.py +22 -5
  29. meerk40t/ruida/gui/gui.py +6 -6
  30. meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
  31. meerk40t/ruida/rdjob.py +3 -3
  32. meerk40t/tools/geomstr.py +88 -0
  33. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/METADATA +1 -1
  34. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/RECORD +39 -38
  35. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/WHEEL +1 -1
  36. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/LICENSE +0 -0
  37. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/entry_points.txt +0 -0
  38. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/top_level.txt +0 -0
  39. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7030.dist-info}/zip-safe +0 -0
@@ -435,7 +435,7 @@ def plugin(kernel, lifecycle):
435
435
  @self.console_argument("inversions", nargs="*", type=int)
436
436
  @context.console_command(
437
437
  "ffractal",
438
- help=_("ffractal iterations"),
438
+ help=_("fractal iterations"),
439
439
  output_type="geometry",
440
440
  hidden=True,
441
441
  )
meerk40t/extra/potrace.py CHANGED
@@ -48,21 +48,25 @@ potracer: https://github.com/tatarize/potrace
48
48
 
49
49
  def plugin(kernel, lifecycle=None):
50
50
  if lifecycle == "invalidate":
51
+ invalid = False
52
+ return invalid
53
+
54
+ if lifecycle == "register":
55
+ _ = kernel.translation
56
+ import numpy
57
+ valid = False
51
58
  try:
52
- import numpy
53
59
  import potrace
54
- if not hasattr(potrace, "Bitmap"):
60
+ if hasattr(potrace, "Bitmap"):
61
+ valid = True
62
+ else:
55
63
  # This is a strange variant, that we do not support!
56
64
  print ("The version of potrace that is installed on your machine is incompatible. Please report this to the developers.")
57
- return True
58
65
  except ImportError:
59
- # print("Potrace plugin could not load because potracer/pypotrace is not installed.")
60
- return True
61
-
62
- if lifecycle == "register":
63
- _ = kernel.translation
64
- import numpy
65
- import potrace
66
+ valid = False
67
+
68
+ if not valid:
69
+ from . import mk_potrace as potrace
66
70
 
67
71
  def make_vector(
68
72
  image,
@@ -12,7 +12,7 @@ from meerk40t.kernel import Module
12
12
  class GRBLInterpreter(Module):
13
13
  def __init__(self, service, path):
14
14
  Module.__init__(self, service, path)
15
- self.emulator = GRBLEmulator(self, service.space.display.matrix())
15
+ self.emulator = GRBLEmulator(self, service.space.display.matrix)
16
16
  self._attached_device = None
17
17
 
18
18
  def __repr__(self):
meerk40t/gui/about.py CHANGED
@@ -1770,6 +1770,8 @@ class ComponentPanel(ScrolledPanel):
1770
1770
 
1771
1771
  def get_potrace():
1772
1772
  entry = ["potracer", "", "", "https://pypi.org/project/potracer/"]
1773
+ status = _("Present (slow)")
1774
+ info = "0.05 (internal)"
1773
1775
  try:
1774
1776
  import potrace
1775
1777
 
@@ -1780,14 +1782,10 @@ class ComponentPanel(ScrolledPanel):
1780
1782
  entry[0] = "pypotrace"
1781
1783
  entry[3] = "https://pypi.org/project/pypotrace/"
1782
1784
  info = potrace.potracelib_version()
1783
- else:
1784
- status = _("Present (slow)")
1785
- info = "??"
1786
1785
  if not hasattr(potrace, "Bitmap"):
1787
1786
  status = _("Faulty, please report")
1788
1787
  except ImportError:
1789
- info = "??"
1790
- status = _("Missing")
1788
+ pass
1791
1789
  entry[1] = info
1792
1790
  entry[2] = status
1793
1791
  self.content.append(entry)
meerk40t/gui/basicops.py CHANGED
@@ -459,21 +459,21 @@ class BasicOpPanel(wx.Panel):
459
459
  self.context.themes.set_window_colors(header)
460
460
  header.SetMinSize(dip_size(self, 20, -1))
461
461
  header.SetMaxSize(dip_size(self, 20, -1))
462
- header.SetToolTip(_("Active"))
462
+ header.SetToolTip(_("A: Active = toggle whether the elements assigned to this operation will be burned or not"))
463
463
  info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
464
464
 
465
465
  header = wxStaticText(self.op_panel, wx.ID_ANY, label="S")
466
466
  self.context.themes.set_window_colors(header)
467
467
  header.SetMinSize(dip_size(self, 20, -1))
468
468
  header.SetMaxSize(dip_size(self, 20, -1))
469
- header.SetToolTip(_("Show"))
469
+ header.SetToolTip(_("S: Show = if inactive then you can suppress the drawing of the assigned elements"))
470
470
  info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
471
471
 
472
472
  header = wxStaticText(self.op_panel, wx.ID_ANY, label="C")
473
473
  self.context.themes.set_window_colors(header)
474
474
  header.SetMinSize(dip_size(self, 20, -1))
475
475
  header.SetMaxSize(dip_size(self, 20, -1))
476
- header.SetToolTip(_("Coolant"))
476
+ header.SetToolTip(_("C: Coolant = determines whether coolant remains / will be turned on / turned off at start of this operation"))
477
477
  info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
478
478
 
479
479
  unit = " [%]" if self.use_percent else ""
@@ -1117,10 +1117,7 @@ class ChoicePropertyPanel(ScrolledPanel):
1117
1117
  control.SetValue(bool((data >> b) & 1))
1118
1118
  if mask:
1119
1119
  control.Enable(bool((mask_bits >> b) & 1))
1120
- control.Bind(
1121
- wx.EVT_CHECKBOX,
1122
- on_checkbox_check(attr, control, obj, b, additional_signal),
1123
- )
1120
+ control.Bind(wx.EVT_CHECKBOX, on_checkbox_bitcheck(attr, control, obj, b, additional_signal), )
1124
1121
 
1125
1122
  # mask bit
1126
1123
  if mask:
@@ -584,7 +584,10 @@ class Warnings:
584
584
  image_node = node.node if hasattr(node, "node") else node
585
585
  if getattr(image_node, "hidden", False):
586
586
  continue
587
- opdpi = op.dpi if useop else image_node.dpi
587
+ if hasattr(image_node, "dpi"):
588
+ opdpi = op.dpi if useop else image_node.dpi
589
+ else:
590
+ opdpi = op.dpi
588
591
  step_x, step_y = self.context.device.view.dpi_to_steps(opdpi)
589
592
  step_x *= self.context.device.view.native_scale_x
590
593
  step_y *= self.context.device.view.native_scale_y
@@ -673,15 +673,6 @@ class SpoolerPanel(wx.Panel):
673
673
 
674
674
  return routine
675
675
 
676
- def pane_show(self, *args):
677
- self.shown = True
678
- self.context.schedule(self.timerjob)
679
- self.refresh_spooler_list()
680
-
681
- def pane_hide(self, *args):
682
- self.context.unschedule(self.timerjob)
683
- self.shown = False
684
-
685
676
  @staticmethod
686
677
  def _name_str(named_obj):
687
678
  try:
@@ -1171,10 +1162,16 @@ class SpoolerPanel(wx.Panel):
1171
1162
  self.on_device_update(None)
1172
1163
 
1173
1164
  def pane_show(self):
1165
+ self.shown = True
1174
1166
  self.list_job_history.load_column_widths()
1175
1167
  self.list_job_spool.load_column_widths()
1168
+ self.context.schedule(self.timerjob)
1169
+ self.refresh_spooler_list()
1176
1170
 
1177
1171
  def pane_hide(self):
1172
+ self.context.unschedule(self.timerjob)
1173
+ self.shown = False
1174
+
1178
1175
  self.list_job_history.save_column_widths()
1179
1176
  self.list_job_spool.save_column_widths()
1180
1177
 
meerk40t/gui/themes.py CHANGED
@@ -31,6 +31,9 @@ def color_distance(c1:wx.Colour, c2:wx.Colour) -> bool:
31
31
  # print (f"Distance from {c1.GetAsString()} to {c2.GetAsString()} = {sqrt(sq_dist)}")
32
32
  return sqrt(sq_dist)
33
33
 
34
+ def inverted_color(c1:wx.Colour) -> wx.Colour:
35
+ return wx.Colour(255-c1.red, 255-c1.green, 255-c1.blue, c1.alpha)
36
+
34
37
  def is_a_bright_color(c1):
35
38
  return color_distance(c1, wx.BLACK) > color_distance(c1, wx.WHITE)
36
39
 
@@ -132,6 +135,10 @@ class Themes(Service):
132
135
  tp["inactive_fg"] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
133
136
  # for key, col in tp.items():
134
137
  # print (f'tp["{key}"] = wx.Colour({col.red}, {col.green}, {col.blue}, {col.alpha})')
138
+ for p1, p2 in (("label_bg", "label_fg"), ("button_bg", "button_fg"), ("text_bg", "text_fg"), ("list_bg", "list_fg")):
139
+ inv_col = inverted_color(tp[p2])
140
+ if color_distance(tp[p2], tp[p1]) < color_distance(inv_col, tp[p1]):
141
+ tp[p2] = inv_col
135
142
  if system() != "Darwin":
136
143
  # Alas, Darwin does not properly support overloading of colors...
137
144
  if not self.dark and is_a_dark_color(tp["win_bg"]):
@@ -166,7 +173,6 @@ class Themes(Service):
166
173
  tp["highlight"] = wx.Colour(0, 0, 255)
167
174
  tp["inactive_bg"] = wx.Colour(46, 46, 46)
168
175
  tp["inactive_fg"] = base_fg
169
-
170
176
  tp["pause_bg"] = (
171
177
  wx.Colour(87, 87, 0) if self._dark else wx.Colour(200, 200, 0)
172
178
  )
@@ -24,6 +24,7 @@ from meerk40t.gui.wxmscene import SceneWindow
24
24
  from meerk40t.gui.wxutils import TextCtrl, wxButton, wxStaticText
25
25
  from meerk40t.kernel import CommandSyntaxError, Module, get_safe_path
26
26
  from meerk40t.kernel.kernel import Job
27
+ from meerk40t.core.units import Length
27
28
 
28
29
  from ..main import APPLICATION_NAME, APPLICATION_VERSION
29
30
  from ..tools.kerftest import KerfTool
@@ -835,6 +836,31 @@ class wxMeerK40t(wx.App, Module):
835
836
  dialog.cancel_it()
836
837
  dialog.Destroy()
837
838
 
839
+ @kernel.console_argument("info", type=str, help=_("Unit to translate"))
840
+ @kernel.console_command("unit", help=_("Translate units"))
841
+ def show_unit_info(command, channel, _, info=None, **kwargs):
842
+ if info is None:
843
+ channel(_("You need to provide a value to translate"))
844
+ device = kernel.root.device
845
+ try:
846
+ valuex = Length(info, relative_length=device.view.width, digits=4)
847
+ except ValueError:
848
+ channel(f"Invalid value: '{info}'")
849
+ return
850
+ channel(f"{info} translates to:")
851
+ channel(f"tat : {float(valuex):.4f}")
852
+ channel(f"mil : {valuex.mil}")
853
+ channel(f"um : {valuex.um}")
854
+ channel(f"nm : {valuex.nm}")
855
+ channel(f"mm : {valuex.mm}")
856
+ channel(f"cm : {valuex.cm}")
857
+ channel(f"Pixels : {valuex.pixels}")
858
+ channel(f"Point : {valuex.pt}")
859
+ channel(f"spx (screen) : {valuex.spx}")
860
+ channel(f"inch : {valuex.inches}")
861
+ channel(f"Device units : {float(valuex) / device.view.native_scale_x:.4f}")
862
+
863
+
838
864
  def module_open(self, *args, **kwargs):
839
865
  context = self.context
840
866
  kernel = context.kernel
meerk40t/gui/wxmscene.py CHANGED
@@ -868,6 +868,99 @@ class MeerK40tScenePanel(wx.Panel):
868
868
  else:
869
869
  channel(_("Target needs to be one of primary, secondary, circular"))
870
870
 
871
+ # Establishes magnet commands
872
+ @context.console_argument(
873
+ "action", type=str, help=_("Action: clear or set / delete with coordinate")
874
+ )
875
+ @context.console_argument(
876
+ "axis", type=str, help=_("Axis (X or Y)")
877
+ )
878
+ @context.console_argument("pos", type=str, help=_("Position for magnetline"))
879
+ @context.console_command(
880
+ "magnet",
881
+ help=_("magnet <action> <axis> <position>"),
882
+ input_type="scene",
883
+ )
884
+ def magnet_set(
885
+ command,
886
+ channel,
887
+ _,
888
+ action=None,
889
+ axis=None,
890
+ pos=None,
891
+ **kwarg,
892
+ ):
893
+ def info(opt_msg):
894
+ channel(
895
+ _("You need to provide the intended action:") + "\n" +
896
+ _("clear x - clear y : will clear all magnets on the given axis") + "\n" +
897
+ _("set x <pos> - set y <pos>: will set a magnet line on the given axis") + "\n" +
898
+ _("delete x <pos> - delete y <pos>: will delete the magnet line on the given axis")
899
+ )
900
+ if opt_msg:
901
+ channel(opt_msg)
902
+
903
+ if action is None or axis is None or axis.upper() not in ("X", "Y"):
904
+ info("")
905
+ return
906
+ action = action.lower()
907
+ axis = axis.upper()
908
+ value = None
909
+ if pos:
910
+ try:
911
+ rel_len = self.context.device.view.width if axis == "X" else self.context.device.view.height
912
+ value = float(Length(pos, relative_length=rel_len))
913
+ except ValueError:
914
+ info (f"Invalid length: {pos}")
915
+ return
916
+
917
+ if action != "clear" and value is None:
918
+ info(_("You need to provide a position"))
919
+ return
920
+
921
+ if action == "clear":
922
+ if axis == "X":
923
+ count = len(self.magnet_x)
924
+ self.magnet_x.clear()
925
+ else:
926
+ count = len(self.magnet_y)
927
+ self.magnet_y.clear()
928
+ self.save_magnets()
929
+ self.context.signal("refresh_scene", "Scene")
930
+ channel (_("Deleted {count} magnet lines on axis {axis}").format(axis=axis, count=count))
931
+ elif action == "set":
932
+ done = False
933
+ if axis == "X":
934
+ if not value in self.magnet_x:
935
+ done = True
936
+ self.magnet_x.append(value)
937
+ else:
938
+ if not value in self.magnet_y:
939
+ done = True
940
+ self.magnet_y.append(value)
941
+ self.save_magnets()
942
+ self.context.signal("refresh_scene", "Scene")
943
+ if done:
944
+ channel(_("Magnetline appended at {pos} on axis {axis}").format(pos=pos, axis=axis))
945
+ else:
946
+ channel(_("Magnetline was already present"))
947
+ elif action.startswith("del"):
948
+ done = False
949
+ if axis == "X":
950
+ if value in self.magnet_x:
951
+ done = True
952
+ self.magnet_x.remove(value)
953
+ else:
954
+ if value in self.magnet_y:
955
+ done = True
956
+ self.magnet_y.remove(value)
957
+ self.save_magnets()
958
+ self.context.signal("refresh_scene", "Scene")
959
+ if done:
960
+ channel(_("Magnetline removed at {pos} on axis {axis}").format(pos=pos, axis=axis))
961
+ else:
962
+ channel(_("Magnetline was not existing"))
963
+
871
964
  def toggle_ref_obj(self):
872
965
  for e in self.scene.context.elements.flat(types=elem_nodes, emphasized=True):
873
966
  if self.reference_object == e:
@@ -1495,7 +1495,7 @@ def plugin(kernel, lifecycle=None):
1495
1495
  )
1496
1496
  @context.console_command(
1497
1497
  "linecut",
1498
- help=_("Cuts and image with a line"),
1498
+ help=_("Cuts an image with a line"),
1499
1499
  input_type="image",
1500
1500
  output_type="image",
1501
1501
  hidden=True,
meerk40t/kernel/kernel.py CHANGED
@@ -254,15 +254,21 @@ class Kernel(Settings):
254
254
  kwargs_repr = [f"{k}={v}" for k, v in kwargs.items()]
255
255
  signature = ", ".join(args_repr + kwargs_repr)
256
256
  start = f"Calling {str(obj)}.{func.__name__}({signature})"
257
- debug_file.write(start + "\n")
257
+ try:
258
+ debug_file.write(start + "\n")
259
+ except (ValueError, OSError):
260
+ pass
258
261
  print(start)
259
262
  t = time.time()
260
263
  value = func(*args, **kwargs)
261
264
  t = time.time() - t
262
265
  finish = f" {func.__name__} returned {value} after {t * 1000}ms"
263
266
  print(finish)
264
- debug_file.write(finish + "\n")
265
- debug_file.flush()
267
+ try:
268
+ debug_file.write(finish + "\n")
269
+ debug_file.flush()
270
+ except (ValueError, OSError):
271
+ pass
266
272
  return value
267
273
 
268
274
  return wrapper_debug
@@ -1236,7 +1242,7 @@ class Kernel(Settings):
1236
1242
 
1237
1243
  line = await loop.run_in_executor(None, sys.stdin.readline)
1238
1244
  line = line.strip()
1239
- if line in ("quit", "shutdown", "restart"):
1245
+ if line in ("quit", "shutdown", "exit", "restart"):
1240
1246
  self._quit = True
1241
1247
  if line == "restart":
1242
1248
  self._restart = True
@@ -54,6 +54,8 @@ class Settings:
54
54
  FileNotFoundError,
55
55
  ):
56
56
  return
57
+ except UnicodeDecodeError as e:
58
+ print ("The config file contained unsupported characters, please share the file with the dev team")
57
59
  except (configparser.DuplicateOptionError, configparser.DuplicateSectionError) as e:
58
60
  print (f"We had a duplication error in the config, try to recover from {e}")
59
61
  for section in parser.sections():
@@ -6,14 +6,16 @@ the given device type.
6
6
  """
7
7
 
8
8
  from hashlib import md5
9
+ import platform
10
+
9
11
  import meerk40t.constants as mkconst
10
12
  from meerk40t.core.laserjob import LaserJob
11
13
  from meerk40t.core.spoolers import Spooler
12
14
  from meerk40t.core.view import View
13
15
  from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
14
16
 
15
- from ..core.units import UNITS_PER_MIL, Length
16
- from ..device.mixins import Status
17
+ from meerk40t.core.units import UNITS_PER_MIL, Length
18
+ from meerk40t.device.mixins import Status
17
19
  from .controller import LihuiyuController
18
20
  from .driver import LihuiyuDriver
19
21
  from .tcp_connection import TCPOutput
@@ -514,7 +516,10 @@ class LihuiyuDevice(Service, Status):
514
516
  self.setting(str, "serial", None)
515
517
  self.setting(bool, "serial_enable", False)
516
518
 
517
- self.setting(int, "port", 1022)
519
+ # Linux prevents ports below 1024 to be used in an non-root context
520
+ def_port = 1022 if platform.system() == "Windows" else "1025"
521
+
522
+ self.setting(int, "port", def_port)
518
523
  self.setting(str, "address", "localhost")
519
524
 
520
525
  self.driver = LihuiyuDriver(self)
@@ -978,6 +983,7 @@ class LihuiyuDevice(Service, Status):
978
983
  server = self.open_as("module/TCPServer", server_name, port=port)
979
984
  if quit:
980
985
  self.close(server_name)
986
+ channel(_("TCP Server for lihuiyu has been closed"))
981
987
  return
982
988
  channel(_("TCP Server for lihuiyu on port: {port}").format(port=port))
983
989
  if verbose:
meerk40t/main.py CHANGED
@@ -11,7 +11,7 @@ import os.path
11
11
  import sys
12
12
 
13
13
  APPLICATION_NAME = "MeerK40t"
14
- APPLICATION_VERSION = "0.9.7020"
14
+ APPLICATION_VERSION = "0.9.7030"
15
15
 
16
16
  if not getattr(sys, "frozen", False):
17
17
  # If .git directory does not exist we are running from a package like pypi
@@ -151,6 +151,9 @@ parser.add_argument(
151
151
  action="store_true",
152
152
  help="start window maximized",
153
153
  )
154
+ parser.add_argument(
155
+ "-d", "--daemon", action="store_true", help="keep MeerK40t in background"
156
+ )
154
157
 
155
158
 
156
159
  def run():
@@ -220,12 +223,26 @@ def _exe(restarted, args):
220
223
  kernel.add_plugin(internal_plugins)
221
224
  kernel.add_plugin(external_plugins)
222
225
  auto = hasattr(kernel.args, "auto") and kernel.args.auto
226
+ command = hasattr(kernel.args, "execute") and kernel.args.execute
223
227
  console = hasattr(kernel.args, "console") and kernel.args.console
228
+ daemon = hasattr(kernel.args, "daemon") and kernel.args.daemon
229
+ server_mode = False
230
+ if command:
231
+ for c in command:
232
+ server_mode = server_mode or any(substring in c for substring in ("lhyserver", "grblserver", "ruidacontrol", "grblcontrol", "webserver"))
233
+ nogui = (
234
+ (hasattr(kernel.args, "gui_suppress") and kernel.args.gui_suppress) or
235
+ (hasattr(kernel.args, "no_gui") and kernel.args.no_gui)
236
+ )
224
237
  for idx, attrib in enumerate(("mktablength", "mktabpositions")):
225
238
  kernel.register(f"registered_mk_svg_parameters/tabs{idx}", attrib)
226
239
 
227
- if auto and not console:
228
- kernel(partial=True)
229
- else:
230
- kernel()
240
+ require_partial_mode = False
241
+ if (
242
+ (not console or nogui) and
243
+ (auto or daemon or server_mode)
244
+ ):
245
+ require_partial_mode = True
246
+ # print (f"Auto: {auto}, Command: {command}, Console: {console}, daemon: {daemon}, nogui:{nogui}, Server: {server_mode} -> {require_partial_mode}")
247
+ kernel(partial=require_partial_mode)
231
248
  return hasattr(kernel, "restart") and kernel.restart
meerk40t/ruida/gui/gui.py CHANGED
@@ -48,7 +48,7 @@ def plugin(service, lifecycle):
48
48
 
49
49
  from meerk40t.ruida.gui.ruidaconfig import RuidaConfiguration
50
50
  from meerk40t.ruida.gui.ruidacontroller import RuidaController
51
- from meerk40t.ruida.gui.ruidaoperationproperties import RuidaOperationPanel
51
+ # from meerk40t.ruida.gui.ruidaoperationproperties import RuidaOperationPanel
52
52
 
53
53
  service.register("window/Controller", RuidaController)
54
54
  service.register("window/Configuration", RuidaConfiguration)
@@ -56,11 +56,11 @@ def plugin(service, lifecycle):
56
56
  service.register("winpath/Controller", service)
57
57
  service.register("winpath/Configuration", service)
58
58
 
59
- service.register("property/RasterOpNode/Ruida", RuidaOperationPanel)
60
- service.register("property/CutOpNode/Ruida", RuidaOperationPanel)
61
- service.register("property/EngraveOpNode/Ruida", RuidaOperationPanel)
62
- service.register("property/ImageOpNode/Ruida", RuidaOperationPanel)
63
- service.register("property/DotsOpNode/Ruida", RuidaOperationPanel)
59
+ # service.register("property/RasterOpNode/Ruida", RuidaOperationPanel)
60
+ # service.register("property/CutOpNode/Ruida", RuidaOperationPanel)
61
+ # service.register("property/EngraveOpNode/Ruida", RuidaOperationPanel)
62
+ # service.register("property/ImageOpNode/Ruida", RuidaOperationPanel)
63
+ # service.register("property/DotsOpNode/Ruida", RuidaOperationPanel)
64
64
 
65
65
  service.add_service_delegate(RuidaGui(service))
66
66
 
@@ -17,16 +17,7 @@ class RuidaOperationPanel(ScrolledPanel):
17
17
  self.parent = args[0]
18
18
  self.operation = node
19
19
 
20
- choices = [
21
- {
22
- "attr": "air_assist",
23
- "object": node,
24
- "default": True,
25
- "type": bool,
26
- "label": _("Air Assist"),
27
- "tip": _("Trigger the per element air assist"),
28
- },
29
- ]
20
+ choices = []
30
21
 
31
22
  self.panel = ChoicePropertyPanel(
32
23
  self, wx.ID_ANY, context=self.context, choices=choices, scrolling=False
meerk40t/ruida/rdjob.py CHANGED
@@ -1376,13 +1376,13 @@ class RDJob:
1376
1376
  part = current_settings.get("part", 0)
1377
1377
  speed = current_settings.get("speed", 0)
1378
1378
  power = current_settings.get("power", 0) / 10.0
1379
- air = current_settings.get("air_assist", True)
1379
+ air = current_settings.get("coolant", 0)
1380
1380
  self.layer_end()
1381
1381
  self.layer_number_part(part)
1382
1382
  self.laser_device_0()
1383
- if air:
1383
+ if air == 1:
1384
1384
  self.air_assist_on()
1385
- else:
1385
+ elif air==2:
1386
1386
  self.air_assist_off()
1387
1387
  self.speed_laser_1(speed)
1388
1388
  self.laser_on_delay(0)
meerk40t/tools/geomstr.py CHANGED
@@ -179,6 +179,94 @@ def remove(s, i):
179
179
  """
180
180
  s[i:-1] = s[i + 1 :]
181
181
 
182
+ def stitch_geometries(geometry_list:list, tolerance:float=0.0) -> list:
183
+ action_list = list(geometry_list)
184
+ result_list = []
185
+ start_points = []
186
+ end_points = []
187
+ anychanges = True
188
+ were_stitches = False
189
+ iteration_loop = 0
190
+ while anychanges:
191
+ iteration_loop += 1
192
+ # print (f"Loop {iteration_loop}")
193
+ anychanges = False
194
+ for g1 in action_list:
195
+ if g1 is None:
196
+ continue
197
+ fp1 = g1.first_point
198
+ lp1 = g1.last_point
199
+ if fp1 is None or lp1 is None:
200
+ continue
201
+ was_stitched = False
202
+ for idx, g2 in enumerate(result_list):
203
+ fp2 = start_points[idx]
204
+ lp2 = end_points[idx]
205
+ # end - start: append
206
+ # end - end : append reverse
207
+ # start - start: insert reverse
208
+ # start - end: insert
209
+ dist_e_s = abs(lp2 - fp1)
210
+ dist_e_e = abs(lp2 - lp1)
211
+ dist_s_s = abs(fp2 - fp1)
212
+ dist_s_e = abs(fp2 - lp1)
213
+ # print (f"test #{idx} ({fp1.real:.2f},{fp1.imag:.2f})->({lp1.real:.2f},{lp1.imag:.2f}) versus ({fp2.real:.2f},{fp2.imag:.2f})->({lp2.real:.2f},{lp2.imag:.2f})")
214
+ # print (f"Gaps: e -> s: {dist_e_s:.3f}, e -> e: {dist_e_e:.3f}, s -> s: {dist_s_s:.3f},s -> e: {dist_s_e:.3f},")
215
+ if dist_e_s <= tolerance:
216
+ # append
217
+ if dist_e_s > 0:
218
+ g2.line(lp2, fp1)
219
+ g2.append(g1, end=False)
220
+ was_stitched = True
221
+ elif dist_e_e <= tolerance:
222
+ # append reverse
223
+ g1.reverse()
224
+ if dist_e_e > 0:
225
+ g2.line(lp2, lp1)
226
+ g2.append(g1, end=False)
227
+ was_stitched = True
228
+ elif dist_s_s <= tolerance:
229
+ # insert reverse
230
+ g1.reverse()
231
+ if dist_s_s > 0:
232
+ g1.line(fp1, fp2)
233
+ g2.insert(0, g1.segments[:g1.index])
234
+ was_stitched = True
235
+ elif dist_s_e <= tolerance:
236
+ # insert
237
+ if dist_s_e > 0:
238
+ g1.line(lp1, fp2)
239
+ g2.insert(0, g1.segments[:g1.index])
240
+ was_stitched = True
241
+ if was_stitched:
242
+ # print ("stitched")
243
+ # g2.debug_me()
244
+ anychanges = True
245
+ start_points[idx] = g2.first_point
246
+ end_points[idx] = g2.last_point
247
+ result_list[idx] = g2
248
+ were_stitches = True
249
+ break
250
+
251
+ if not was_stitched:
252
+ # print ("Unchanged")
253
+ result_list.append(g1)
254
+ start_points.append(fp1)
255
+ end_points.append(lp1)
256
+ if anychanges:
257
+ action_list = list(result_list)
258
+ result_list.clear()
259
+ start_points.clear()
260
+ end_points.clear()
261
+ if were_stitches:
262
+ for g1 in result_list:
263
+ fp1 = g1.first_point
264
+ lp1 = g1.last_point
265
+ dist_e_s = abs(lp1 - fp1)
266
+ if 0 < dist_e_s <= tolerance:
267
+ g1.line(lp1, fp1)
268
+ return result_list
269
+ return None
182
270
 
183
271
  class Simplifier:
184
272
  # Copyright (c) 2014 Elliot Hallmark
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerk40t
3
- Version: 0.9.7020
3
+ Version: 0.9.7030
4
4
  Summary: MeerK40t LaserCutter Software
5
5
  Home-page: https://github.com/meerk40t/meerk40t
6
6
  Author: Tatarize