meerk40t 0.9.7051__py2.py3-none-any.whl → 0.9.7910__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 (69) hide show
  1. meerk40t/balormk/controller.py +3 -3
  2. meerk40t/balormk/device.py +7 -0
  3. meerk40t/balormk/driver.py +23 -14
  4. meerk40t/balormk/galvo_commands.py +18 -3
  5. meerk40t/balormk/gui/balorconfig.py +6 -0
  6. meerk40t/balormk/livelightjob.py +36 -14
  7. meerk40t/camera/camera.py +1 -0
  8. meerk40t/camera/gui/camerapanel.py +154 -58
  9. meerk40t/camera/plugin.py +46 -5
  10. meerk40t/core/elements/branches.py +90 -20
  11. meerk40t/core/elements/elements.py +59 -37
  12. meerk40t/core/elements/trace.py +10 -6
  13. meerk40t/core/node/node.py +2 -0
  14. meerk40t/core/plotplanner.py +7 -4
  15. meerk40t/device/gui/defaultactions.py +78 -14
  16. meerk40t/dxf/dxf_io.py +42 -0
  17. meerk40t/grbl/controller.py +245 -35
  18. meerk40t/grbl/device.py +102 -26
  19. meerk40t/grbl/driver.py +8 -2
  20. meerk40t/grbl/gui/grblconfiguration.py +6 -0
  21. meerk40t/grbl/gui/grblcontroller.py +1 -1
  22. meerk40t/gui/about.py +7 -0
  23. meerk40t/gui/choicepropertypanel.py +20 -30
  24. meerk40t/gui/devicepanel.py +27 -16
  25. meerk40t/gui/help_assets/help_assets.py +126 -2
  26. meerk40t/gui/icons.py +15 -0
  27. meerk40t/gui/laserpanel.py +102 -54
  28. meerk40t/gui/materialtest.py +10 -0
  29. meerk40t/gui/mkdebug.py +268 -9
  30. meerk40t/gui/navigationpanels.py +74 -8
  31. meerk40t/gui/propertypanels/operationpropertymain.py +185 -91
  32. meerk40t/gui/scenewidgets/elementswidget.py +7 -1
  33. meerk40t/gui/scenewidgets/selectionwidget.py +24 -9
  34. meerk40t/gui/simulation.py +1 -1
  35. meerk40t/gui/statusbarwidgets/shapepropwidget.py +50 -40
  36. meerk40t/gui/statusbarwidgets/statusbar.py +2 -2
  37. meerk40t/gui/toolwidgets/toolmeasure.py +1 -1
  38. meerk40t/gui/toolwidgets/toolnodeedit.py +4 -1
  39. meerk40t/gui/toolwidgets/tooltabedit.py +9 -7
  40. meerk40t/gui/wxmeerk40t.py +45 -15
  41. meerk40t/gui/wxmmain.py +23 -9
  42. meerk40t/gui/wxmribbon.py +36 -0
  43. meerk40t/gui/wxutils.py +66 -42
  44. meerk40t/kernel/inhibitor.py +120 -0
  45. meerk40t/kernel/kernel.py +38 -0
  46. meerk40t/lihuiyu/controller.py +33 -3
  47. meerk40t/lihuiyu/device.py +99 -4
  48. meerk40t/lihuiyu/driver.py +65 -5
  49. meerk40t/lihuiyu/gui/lhycontrollergui.py +69 -24
  50. meerk40t/lihuiyu/gui/lhydrivergui.py +6 -0
  51. meerk40t/lihuiyu/laserspeed.py +17 -10
  52. meerk40t/lihuiyu/parser.py +23 -0
  53. meerk40t/main.py +2 -2
  54. meerk40t/moshi/gui/moshidrivergui.py +7 -0
  55. meerk40t/newly/controller.py +3 -2
  56. meerk40t/newly/device.py +23 -2
  57. meerk40t/newly/driver.py +8 -3
  58. meerk40t/newly/gui/newlyconfig.py +7 -0
  59. meerk40t/ruida/gui/ruidaconfig.py +7 -0
  60. meerk40t/tools/geomstr.py +142 -49
  61. meerk40t/tools/rasterplotter.py +0 -5
  62. meerk40t/tools/ttfparser.py +921 -168
  63. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/METADATA +1 -1
  64. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/RECORD +69 -68
  65. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/LICENSE +0 -0
  66. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/WHEEL +0 -0
  67. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/entry_points.txt +0 -0
  68. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/top_level.txt +0 -0
  69. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/zip-safe +0 -0
meerk40t/tools/geomstr.py CHANGED
@@ -199,13 +199,17 @@ def stitcheable_nodes(data, tolerance) -> list:
199
199
  if tolerance == 0:
200
200
  tolerance = 1e-6
201
201
  for idx1, (nodeidx1, g1) in enumerate(geoms):
202
+ fp1 = g1.first_point
203
+ lp1 = g1.last_point
204
+ if fp1 is None or lp1 is None:
205
+ continue
202
206
  for idx2 in range(idx1 + 1, len(geoms)):
203
207
  nodeidx2 = geoms[idx2][0]
204
208
  g2 = geoms[idx2][1]
205
- fp1 = g1.first_point
206
209
  fp2 = g2.first_point
207
- lp1 = g1.last_point
208
210
  lp2 = g2.last_point
211
+ if fp2 is None or lp2 is None:
212
+ continue
209
213
  if (
210
214
  abs(lp1 - lp2) <= tolerance
211
215
  or abs(lp1 - fp2) <= tolerance
@@ -251,11 +255,16 @@ def stitch_geometries(geometry_list: list, tolerance: float = 0.0) -> list:
251
255
  while geometries:
252
256
  candidate = geometries.pop(0)
253
257
  stitched = False
258
+ cand_fp = candidate.first_point
259
+ cand_lp = candidate.last_point
260
+ if cand_fp is None or cand_lp is None:
261
+ stitched_geometries.append(candidate)
262
+ continue
254
263
  for i, target in enumerate(stitched_geometries):
255
- cand_fp = candidate.first_point
256
- cand_lp = candidate.last_point
257
264
  targ_fp = target.first_point
258
265
  targ_lp = target.last_point
266
+ if targ_fp is None or targ_lp is None:
267
+ continue
259
268
  if abs(targ_lp - cand_fp) <= tolerance:
260
269
  # Just append g1 to g2
261
270
  if abs(targ_lp - cand_fp) > 0:
@@ -496,7 +505,7 @@ class Simplifier:
496
505
 
497
506
  def by_ratio(self, r):
498
507
  if r <= 0 or r > 1:
499
- raise ValueError("Ratio must be 0<r<=1. Got {}".format(r))
508
+ raise ValueError(f"Ratio must be 0<r<=1. Got {r}")
500
509
 
501
510
  return self.by_number(r * len(self.thresholds))
502
511
 
@@ -531,7 +540,7 @@ class Clip:
531
540
  x3 < y3,
532
541
  )
533
542
  ).all(axis=2)
534
- splits = [list() for _ in range(len(subject))]
543
+ splits = [[] for _ in range(len(subject))]
535
544
  for s0, s1 in sorted(np.argwhere(checks), key=lambda e: e[0], reverse=True):
536
545
  splits[s0].extend(
537
546
  [t for t, _ in subject.intersections(int(s0), clip.segments[s1])]
@@ -546,7 +555,7 @@ class Clip:
546
555
  @param clip:
547
556
  @return:
548
557
  """
549
- splits = [list() for _ in range(len(subject))]
558
+ splits = [[] for _ in range(len(subject))]
550
559
  for s0 in range(len(subject)):
551
560
  for s1 in range(len(clip)):
552
561
  for t0, t1 in subject.intersections(int(s0), clip.segments[s1]):
@@ -774,12 +783,12 @@ class Pattern:
774
783
  # Scale once, translate often
775
784
  m = Matrix.scale(cw, ch)
776
785
  geom = self.geomstr.as_transformed(m)
777
- for col in range(start_value_x, cols + end_value_x, 1):
786
+ for col in range(start_value_x, cols + end_value_x):
778
787
  x_offset = col * (cw + 2 * px)
779
788
  x = top_left_x + x_offset
780
789
 
781
790
  top_left_y = y0
782
- for row in range(start_value_y, rows + end_value_y, 1):
791
+ for row in range(start_value_y, rows + end_value_y):
783
792
  y_offset = row * (ch + 2 * py)
784
793
  if col % 2:
785
794
  y_offset += (ch + 2 * py) / 2
@@ -942,9 +951,9 @@ class BeamTable:
942
951
  event = events[i]
943
952
  pt, adds, removes, sorts = event
944
953
  try:
945
- next, _, _, _ = events[i + 1]
954
+ next_idx, _, _, _ = events[i + 1]
946
955
  except IndexError:
947
- next = complex(float("inf"), float("inf"))
956
+ next_idx = complex(float("inf"), float("inf"))
948
957
 
949
958
  for index in removes:
950
959
  try:
@@ -978,7 +987,7 @@ class BeamTable:
978
987
  # was removed
979
988
  pass
980
989
  i += 1
981
- if pt == next:
990
+ if pt == next_idx:
982
991
  continue
983
992
  if len(actives) > largest_actives:
984
993
  largest_actives = len(actives)
@@ -1041,11 +1050,11 @@ class BeamTable:
1041
1050
  pt, index, swap = event
1042
1051
 
1043
1052
  try:
1044
- next, _, _ = events[i + 1]
1045
- scanline = (pt + next) / 2
1053
+ next_idx, _, _ = events[i + 1]
1054
+ scanline = (pt + next_idx) / 2
1046
1055
  except IndexError:
1047
- next = complex(float("inf"), float("inf"))
1048
- scanline = next
1056
+ next_idx = complex(float("inf"), float("inf"))
1057
+ scanline = next_idx
1049
1058
 
1050
1059
  if swap is not None:
1051
1060
  s1 = actives.index(swap[0])
@@ -1058,7 +1067,7 @@ class BeamTable:
1058
1067
  remove_index = actives.index(~index)
1059
1068
  del actives[remove_index]
1060
1069
 
1061
- if pt != next:
1070
+ if pt != next_idx:
1062
1071
  if len(actives) > largest_actives:
1063
1072
  largest_actives = len(actives)
1064
1073
  # actives.sort(key=y_ints)
@@ -1125,9 +1134,9 @@ class BeamTable:
1125
1134
  starts = np.ravel(np.real(from_vals) + y_start * 1j)
1126
1135
  ends = np.ravel(np.real(to_vals) + y_end * 1j)
1127
1136
 
1128
- filter = np.dstack((starts != ends, ~np.isnan(starts))).all(axis=2)[0]
1129
- starts = starts[filter]
1130
- ends = ends[filter]
1137
+ filtered = np.dstack((starts != ends, ~np.isnan(starts))).all(axis=2)[0]
1138
+ starts = starts[filtered]
1139
+ ends = ends[filtered]
1131
1140
  count = starts.shape[0]
1132
1141
  segments = np.dstack(
1133
1142
  (starts, [0] * count, [TYPE_LINE] * count, [0] * count, ends)
@@ -1147,7 +1156,7 @@ class BeamTable:
1147
1156
  def difference(self, *args):
1148
1157
  return self.cag("difference", *args)
1149
1158
 
1150
- def cag(self, cag_op, *args):
1159
+ def cag_org(self, cag_op, *args):
1151
1160
  if self.geometry.index == 0:
1152
1161
  return Geomstr()
1153
1162
  if self._nb_scan is None:
@@ -1198,6 +1207,79 @@ class BeamTable:
1198
1207
  g.append_lines(segments)
1199
1208
  return g
1200
1209
 
1210
+ def cag(self, cag_op, *args):
1211
+ """
1212
+ Vectorized CAG function that processes all arguments at once to reduce
1213
+ Python loop overhead.
1214
+ """
1215
+ if self.geometry.index == 0:
1216
+ return Geomstr()
1217
+ if self._nb_scan is None:
1218
+ self.compute_beam()
1219
+
1220
+ g = Geomstr()
1221
+ actives = self._nb_scan[:-1]
1222
+ lines = self.geometry.segments[actives, 2]
1223
+
1224
+ # Create a 3D mask for all arguments at once. Shape: (args, events, actives)
1225
+ # This avoids the Python loop over `args`.
1226
+ m = (np.imag(lines)[None, :, :] == np.array(args)[:, None, None]) & (
1227
+ actives != -1
1228
+ )
1229
+
1230
+ # Perform cumsum on the 3D array and combine results.
1231
+ # This is the most computationally expensive part.
1232
+ qq = (np.cumsum(m, axis=2) & 1).astype(bool)
1233
+
1234
+ # Combine the results from all arguments based on the CAG operation.
1235
+ if cag_op == "union":
1236
+ cc = np.any(qq, axis=0)
1237
+ elif cag_op == "intersection":
1238
+ cc = np.all(qq, axis=0)
1239
+ elif cag_op == "xor":
1240
+ # XOR is a reduction, so we sum the boolean values and check for oddness.
1241
+ cc = (np.sum(qq, axis=0) & 1).astype(bool)
1242
+ elif cag_op == "difference":
1243
+ # A \ B is equivalent to A & ~B. For multiple args: A \ B \ C -> A & ~B & ~C
1244
+ cc = qq[0]
1245
+ if len(args) > 1:
1246
+ cc &= np.all(~qq[1:], axis=0)
1247
+ else: # "eq"
1248
+ # Check if all rows in qq are identical for each event/active pair.
1249
+ cc = np.all(qq == qq[0], axis=0)
1250
+
1251
+ # manual pad (faster than np.pad), then diff
1252
+ hshape = (cc.shape[0], cc.shape[1] + 1)
1253
+ yy = np.zeros(hshape, dtype=np.int8)
1254
+ yy[:, 1:] = cc.view(np.int8)
1255
+ hh = np.diff(yy, axis=1)
1256
+
1257
+ # prepare event arrays only once
1258
+ ev0, ev1 = self._nb_events[:-1], self._nb_events[1:]
1259
+ r0, i0 = np.real(ev0), np.imag(ev0)
1260
+ r1, i1 = np.real(ev1), np.imag(ev1)
1261
+
1262
+ # compute all intercepts
1263
+ y0 = self.geometry.y_intercept(actives, r0, i0)
1264
+ y1 = self.geometry.y_intercept(actives, r1, i1)
1265
+
1266
+ # broadcast real parts to match y-shapes (views, no copy)
1267
+ starts = r0[:, None] + y0 * 1j
1268
+ ends = r1[:, None] + y1 * 1j
1269
+
1270
+ # one-shot filter
1271
+ valid = (starts != ends) & ~np.isnan(starts) & (hh != 0)
1272
+ if np.any(valid):
1273
+ segs = np.empty((np.count_nonzero(valid), 5), dtype=complex)
1274
+ segs[:, 0] = starts[valid]
1275
+ segs[:, 1] = 0
1276
+ segs[:, 2] = TYPE_LINE
1277
+ segs[:, 3] = 0
1278
+ segs[:, 4] = ends[valid]
1279
+ g.append_lines(segs)
1280
+
1281
+ return g
1282
+
1201
1283
 
1202
1284
  class Scanbeam:
1203
1285
  """
@@ -1575,7 +1657,7 @@ class Geomstr:
1575
1657
  """
1576
1658
 
1577
1659
  def __init__(self, segments=None):
1578
- self._settings = dict()
1660
+ self._settings = {}
1579
1661
  if segments is not None:
1580
1662
  if isinstance(segments, Geomstr):
1581
1663
  self._settings.update(segments._settings)
@@ -2261,13 +2343,13 @@ class Geomstr:
2261
2343
  @param end_pos:
2262
2344
  @param start_pos:
2263
2345
  """
2264
- segments = list()
2346
+ segments = []
2265
2347
  for point in self.as_contiguous_points(start_pos=start_pos, end_pos=end_pos):
2266
2348
  if isinstance(point, tuple):
2267
2349
  point, settings = point
2268
2350
  if segments:
2269
2351
  yield segments, settings
2270
- segments = list()
2352
+ segments = []
2271
2353
  else:
2272
2354
  segments.append(point)
2273
2355
 
@@ -2331,12 +2413,12 @@ class Geomstr:
2331
2413
  @param distance:
2332
2414
  @return:
2333
2415
  """
2334
- segments = list()
2416
+ segments = []
2335
2417
  for point in self.as_equal_interpolated_points(distance=distance):
2336
2418
  if point is None:
2337
2419
  if segments:
2338
2420
  yield segments
2339
- segments = list()
2421
+ segments = []
2340
2422
  else:
2341
2423
  segments.append(point)
2342
2424
  if segments:
@@ -2445,12 +2527,12 @@ class Geomstr:
2445
2527
  @param interpolate:
2446
2528
  @return:
2447
2529
  """
2448
- segments = list()
2530
+ segments = []
2449
2531
  for point in self.as_interpolated_points(interpolate=interpolate):
2450
2532
  if point is None:
2451
2533
  if segments:
2452
2534
  yield segments
2453
- segments = list()
2535
+ segments = []
2454
2536
  else:
2455
2537
  segments.append(point)
2456
2538
  if segments:
@@ -2871,7 +2953,7 @@ class Geomstr:
2871
2953
 
2872
2954
  p_start = point_at_t(current_t)
2873
2955
 
2874
- for i in range(0, slices):
2956
+ for i in range(slices):
2875
2957
  next_t = current_t + t_slice
2876
2958
  mid_t = (next_t + current_t) / 2
2877
2959
 
@@ -2932,7 +3014,7 @@ class Geomstr:
2932
3014
 
2933
3015
  p_start = point_at_t(current_t)
2934
3016
 
2935
- for i in range(0, slices):
3017
+ for i in range(slices):
2936
3018
  next_t = current_t + t_slice
2937
3019
  if i == slices - 1:
2938
3020
  next_t = end_t
@@ -3035,7 +3117,7 @@ class Geomstr:
3035
3117
  c = Clip(other)
3036
3118
  polycut = c.polycut(self, breaks=True)
3037
3119
 
3038
- geoms = list()
3120
+ geoms = []
3039
3121
  g = Geomstr()
3040
3122
  geoms.append(g)
3041
3123
  for e in polycut.segments[: self.index]:
@@ -4009,10 +4091,9 @@ class Geomstr:
4009
4091
  return
4010
4092
  if segtype2 in NON_GEOMETRY_TYPES:
4011
4093
  return
4012
- if segtype1 == TYPE_LINE:
4013
- if segtype2 == TYPE_LINE:
4014
- yield from self._line_line_intersections(line1, line2)
4015
- return
4094
+ if segtype1 == TYPE_LINE and segtype2 == TYPE_LINE:
4095
+ yield from self._line_line_intersections(line1, line2)
4096
+ return
4016
4097
  # if oinfo.real == TYPE_QUAD:
4017
4098
  # yield from self._line_quad_intersections(line1, line2)
4018
4099
  # return
@@ -5227,22 +5308,34 @@ class Geomstr:
5227
5308
  def as_contiguous(self):
5228
5309
  """
5229
5310
  Generate individual subpaths of contiguous segments
5311
+ Attention: this will not yield meta segments, like TYPE_NOP and others
5230
5312
 
5231
5313
  @return:
5232
5314
  """
5233
- last = 0
5315
+ new_segments = []
5234
5316
  for idx, seg in enumerate(self.segments[: self.index]):
5235
5317
  segtype = self._segtype(seg)
5318
+ if segtype in META_TYPES:
5319
+ # Skip meta segments
5320
+ continue
5236
5321
  if segtype == TYPE_END:
5237
- yield Geomstr(self.segments[last:idx])
5238
- last = idx + 1
5239
- elif idx > 0:
5240
- # are the start and endpositions different?
5241
- if self.segments[idx, 0] != self.segments[idx - 1, -1]:
5242
- yield Geomstr(self.segments[last:idx])
5243
- last = idx
5244
- if last != self.index:
5245
- yield Geomstr(self.segments[last : self.index])
5322
+ if len(new_segments) > 0:
5323
+ yield Geomstr(new_segments)
5324
+ new_segments.clear()
5325
+ else:
5326
+ if len(new_segments) == 0:
5327
+ new_segments.append(seg)
5328
+ elif seg[0] != new_segments[-1][-1]:
5329
+ # If the start of the current segment is not the end of the last segment
5330
+ yield Geomstr(new_segments)
5331
+ new_segments.clear()
5332
+ new_segments.append(seg)
5333
+ else:
5334
+ # If the start of the current segment is the end of the last segment
5335
+ new_segments.append(seg)
5336
+ if len(new_segments) > 0:
5337
+ # If there are still segments left, yield them
5338
+ yield Geomstr(new_segments)
5246
5339
 
5247
5340
  def ensure_proper_subpaths(self):
5248
5341
  """
@@ -5489,7 +5582,7 @@ class Geomstr:
5489
5582
  """
5490
5583
  infos = self.segments[: self.index, 2]
5491
5584
  q = np.where(np.real(infos).astype(int) & 0b1001)[0]
5492
- for mid in range(0, len(q)):
5585
+ for mid in range(len(q)):
5493
5586
  idxs = q[mid:]
5494
5587
  p1 = idxs[0]
5495
5588
  pen_downs = self.segments[idxs, 0]
@@ -5676,8 +5769,8 @@ class Geomstr:
5676
5769
  if lines is None:
5677
5770
  lines = self.segments[: self.index]
5678
5771
  if function_dict is None:
5679
- function_dict = dict()
5680
- default_dict = dict()
5772
+ function_dict = {}
5773
+ default_dict = {}
5681
5774
  defining_function = 0
5682
5775
  function_start = 0
5683
5776
  for index, line in enumerate(lines):
@@ -5735,7 +5828,7 @@ class Geomstr:
5735
5828
  final = Geomstr()
5736
5829
  for subgeom in geom.as_subpaths():
5737
5830
  newgeom = Geomstr()
5738
- points = list()
5831
+ points = []
5739
5832
  closed = subgeom.is_closed()
5740
5833
 
5741
5834
  def processpts():
@@ -467,11 +467,6 @@ class RasterPlotter:
467
467
  return int(round(self.offset_x)), int(round(self.offset_y))
468
468
  else:
469
469
  return self.offset_x, self.offset_y
470
- if self.use_integers:
471
- if self.use_integers:
472
- return int(round(self.offset_x)), int(round(self.offset_y))
473
- else:
474
- return self.offset_x, self.offset_y
475
470
  if self.use_integers:
476
471
  return (
477
472
  int(round(self.offset_x + self.final_x * self.step_x)),