meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7050__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 (85) hide show
  1. meerk40t/balormk/clone_loader.py +3 -2
  2. meerk40t/balormk/controller.py +38 -13
  3. meerk40t/balormk/cylindermod.py +1 -0
  4. meerk40t/balormk/device.py +13 -9
  5. meerk40t/balormk/driver.py +9 -2
  6. meerk40t/balormk/galvo_commands.py +3 -1
  7. meerk40t/balormk/gui/gui.py +6 -0
  8. meerk40t/balormk/livelightjob.py +338 -321
  9. meerk40t/balormk/mock_connection.py +4 -3
  10. meerk40t/balormk/usb_connection.py +11 -2
  11. meerk40t/camera/camera.py +19 -14
  12. meerk40t/camera/gui/camerapanel.py +6 -0
  13. meerk40t/core/cutplan.py +101 -78
  14. meerk40t/core/elements/element_treeops.py +435 -140
  15. meerk40t/core/elements/elements.py +100 -9
  16. meerk40t/core/elements/shapes.py +259 -72
  17. meerk40t/core/elements/tree_commands.py +10 -5
  18. meerk40t/core/node/blobnode.py +19 -4
  19. meerk40t/core/node/elem_ellipse.py +18 -8
  20. meerk40t/core/node/elem_image.py +51 -19
  21. meerk40t/core/node/elem_line.py +18 -8
  22. meerk40t/core/node/elem_path.py +18 -8
  23. meerk40t/core/node/elem_point.py +10 -4
  24. meerk40t/core/node/elem_polyline.py +19 -11
  25. meerk40t/core/node/elem_rect.py +18 -8
  26. meerk40t/core/node/elem_text.py +11 -5
  27. meerk40t/core/node/filenode.py +2 -8
  28. meerk40t/core/node/groupnode.py +11 -11
  29. meerk40t/core/node/image_processed.py +11 -5
  30. meerk40t/core/node/image_raster.py +11 -5
  31. meerk40t/core/node/node.py +64 -16
  32. meerk40t/core/node/refnode.py +2 -1
  33. meerk40t/core/planner.py +25 -11
  34. meerk40t/core/svg_io.py +91 -34
  35. meerk40t/device/dummydevice.py +7 -1
  36. meerk40t/extra/vtracer.py +222 -0
  37. meerk40t/grbl/device.py +96 -9
  38. meerk40t/grbl/driver.py +15 -5
  39. meerk40t/gui/about.py +20 -0
  40. meerk40t/gui/devicepanel.py +20 -16
  41. meerk40t/gui/gui_mixins.py +4 -0
  42. meerk40t/gui/icons.py +330 -253
  43. meerk40t/gui/laserpanel.py +27 -3
  44. meerk40t/gui/laserrender.py +41 -21
  45. meerk40t/gui/magnetoptions.py +158 -65
  46. meerk40t/gui/materialtest.py +569 -310
  47. meerk40t/gui/navigationpanels.py +229 -24
  48. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  49. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  50. meerk40t/gui/propertypanels/wobbleproperty.py +6 -2
  51. meerk40t/gui/ribbon.py +6 -1
  52. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  53. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  54. meerk40t/gui/simulation.py +75 -77
  55. meerk40t/gui/spoolerpanel.py +27 -7
  56. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  57. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  58. meerk40t/gui/tips.py +15 -1
  59. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  60. meerk40t/gui/wxmmain.py +242 -114
  61. meerk40t/gui/wxmscene.py +107 -24
  62. meerk40t/gui/wxmtree.py +4 -2
  63. meerk40t/gui/wxutils.py +286 -15
  64. meerk40t/image/imagetools.py +129 -65
  65. meerk40t/internal_plugins.py +4 -0
  66. meerk40t/kernel/kernel.py +67 -18
  67. meerk40t/kernel/settings.py +28 -9
  68. meerk40t/lihuiyu/device.py +24 -12
  69. meerk40t/main.py +14 -9
  70. meerk40t/moshi/device.py +20 -6
  71. meerk40t/network/console_server.py +22 -6
  72. meerk40t/newly/device.py +10 -3
  73. meerk40t/newly/gui/gui.py +10 -0
  74. meerk40t/ruida/device.py +22 -2
  75. meerk40t/ruida/loader.py +9 -4
  76. meerk40t/ruida/rdjob.py +48 -8
  77. meerk40t/tools/geomstr.py +240 -123
  78. meerk40t/tools/rasterplotter.py +185 -94
  79. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/METADATA +1 -1
  80. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/RECORD +85 -84
  81. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/LICENSE +0 -0
  82. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/WHEEL +0 -0
  83. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/entry_points.txt +0 -0
  84. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/top_level.txt +0 -0
  85. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/zip-safe +0 -0
meerk40t/tools/geomstr.py CHANGED
@@ -107,7 +107,15 @@ TYPE_CALL = 0xB0 | 0b1111 # The two higher level bytes are call label index.
107
107
 
108
108
  # A summary of all points that have a special meaning, ie intended for other uses than pure geometry
109
109
  META_TYPES = (TYPE_NOP, TYPE_FUNCTION, TYPE_VERTEX, TYPE_UNTIL, TYPE_CALL)
110
- NON_GEOMETRY_TYPES = (TYPE_NOP, TYPE_FUNCTION, TYPE_VERTEX, TYPE_UNTIL, TYPE_CALL, TYPE_END)
110
+ NON_GEOMETRY_TYPES = (
111
+ TYPE_NOP,
112
+ TYPE_FUNCTION,
113
+ TYPE_VERTEX,
114
+ TYPE_UNTIL,
115
+ TYPE_CALL,
116
+ TYPE_END,
117
+ )
118
+
111
119
 
112
120
  class Polygon:
113
121
  def __init__(self, *args):
@@ -118,7 +126,6 @@ class Polygon:
118
126
  return self.geomstr.bbox(mx)
119
127
 
120
128
 
121
-
122
129
  def triangle_area(p1, p2, p3):
123
130
  """
124
131
  calculates the area of a triangle given its vertices
@@ -179,94 +186,127 @@ def remove(s, i):
179
186
  """
180
187
  s[i:-1] = s[i + 1 :]
181
188
 
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
189
+
190
+ def stitcheable_nodes(data, tolerance) -> list:
191
+ out = []
192
+ geoms = []
193
+ # Store all geometries together with an indicator, to which node they belong
194
+ for idx, node in enumerate(data):
195
+ if not hasattr(node, "as_geometry"):
196
+ continue
197
+ for g1 in node.as_geometry().as_contiguous():
198
+ geoms.append((idx, g1))
199
+ if tolerance == 0:
200
+ tolerance = 1e-6
201
+ for idx1, (nodeidx1, g1) in enumerate(geoms):
202
+ for idx2 in range(idx1 + 1, len(geoms)):
203
+ nodeidx2 = geoms[idx2][0]
204
+ g2 = geoms[idx2][1]
197
205
  fp1 = g1.first_point
206
+ fp2 = g2.first_point
198
207
  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
208
+ lp2 = g2.last_point
209
+ if (
210
+ abs(lp1 - lp2) <= tolerance
211
+ or abs(lp1 - fp2) <= tolerance
212
+ or abs(fp1 - fp2) <= tolerance
213
+ or abs(fp1 - lp2) <= tolerance
214
+ ):
215
+ if nodeidx1 not in out:
216
+ out.append(nodeidx1)
217
+ if nodeidx2 not in out:
218
+ out.append(nodeidx2)
219
+ # print (f"Stitchable nodes: {len(out)}")
220
+ return [data[idx] for idx in out]
221
+
222
+
223
+ def stitch_geometries(geometry_list: list, tolerance: float = 0.0) -> list:
224
+ """
225
+ Stitches geometries within the given tolerance.
226
+
227
+ Args:
228
+ geometry_list: List of Geomstr objects to stitch.
229
+ tolerance: Maximum distance between endpoints to consider a stitch.
230
+
231
+ Returns:
232
+ List of stitched Geomstr objects, or None if no stitches were made.
233
+ """
234
+ # def coord(point):
235
+ # return f"{point.real:.2f},{point.imag:.2f}"
236
+
237
+ geometries = [g for g in geometry_list if g is not None]
238
+ if not geometries:
239
+ return None
240
+ if tolerance == 0:
241
+ tolerance = 1e-6
242
+ # geometries.sort(key=lambda g: g.first_point)
243
+ anystitches = 1
244
+ pass_count = 0
245
+ # for idx, g in enumerate(geometries):
246
+ # print (f"{idx}: {g.first_point} -> {g.last_point} ({g.index} segments)")
247
+
248
+ while anystitches > 0:
249
+ stitched_geometries = []
250
+ anystitches = 0
251
+ while geometries:
252
+ candidate = geometries.pop(0)
253
+ stitched = False
254
+ for i, target in enumerate(stitched_geometries):
255
+ cand_fp = candidate.first_point
256
+ cand_lp = candidate.last_point
257
+ targ_fp = target.first_point
258
+ targ_lp = target.last_point
259
+ if abs(targ_lp - cand_fp) <= tolerance:
260
+ # Just append g1 to g2
261
+ if abs(targ_lp - cand_fp) > 0:
262
+ target.line(targ_lp, cand_fp)
263
+ target.append(candidate)
264
+ stitched_geometries[i] = target
265
+ stitched = True
266
+ break
267
+ if abs(targ_fp - cand_lp) <= tolerance:
268
+ # Insert g1 at the start of g2
269
+ if abs(targ_fp - cand_lp) > 0:
270
+ candidate.line(cand_lp, targ_fp)
271
+ candidate.append(target)
272
+ stitched_geometries[i] = candidate
273
+ stitched = True
274
+ break
275
+ if abs(targ_fp - cand_fp) <= tolerance:
276
+ # Insert the reverse of g1 at the start
277
+ candidate.reverse()
278
+ if abs(targ_fp - cand_fp) > 0:
279
+ candidate.line(cand_fp, targ_fp)
280
+ candidate.append(target)
281
+ stitched_geometries[i] = candidate
282
+ stitched = True
283
+ break
284
+ if abs(targ_lp - cand_lp) <= tolerance:
285
+ if abs(targ_lp - cand_lp) > 0:
286
+ target.line(targ_lp, cand_lp)
287
+ candidate.reverse()
288
+ target.append(candidate)
289
+ stitched_geometries[i] = target
290
+ stitched = True
249
291
  break
250
292
 
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
293
+ if stitched:
294
+ anystitches += 1
295
+ else:
296
+ stitched_geometries.append(candidate)
297
+
298
+ # print (f"Stitch pass {pass_count}: {len(stitched_geometries)} geometries, {anystitches} stitches made, org= {len(geometry_list)}, tolerance={tolerance:.2f}")
299
+ if anystitches > 0:
300
+ # Stitches were made, so lets try again
301
+ geometries = list(stitched_geometries)
302
+ pass_count += 1
303
+ # Close any remaining small gaps between start and end points.
304
+ for g in stitched_geometries:
305
+ if 0 < abs(g.last_point - g.first_point) <= tolerance:
306
+ g.line(g.last_point, g.first_point)
307
+
308
+ return stitched_geometries
309
+
270
310
 
271
311
  class Simplifier:
272
312
  # Copyright (c) 2014 Elliot Hallmark
@@ -393,7 +433,7 @@ class Simplifier:
393
433
  areas[min_vert] = right_area
394
434
 
395
435
  if min_vert > 1:
396
- # cant try/except because 0-1=-1 is a valid index
436
+ # can't try/except because 0-1=-1 is a valid index
397
437
  left_area = triangle_area(
398
438
  self.pts[i[min_vert - 2]],
399
439
  self.pts[i[min_vert - 1]],
@@ -442,13 +482,15 @@ class Simplifier:
442
482
  # for some points
443
483
  # sort point indices by threshold
444
484
  idx = list(range(len(self.pts)))
445
- sorted_indices = sorted(zip(idx, self.thresholds), reverse=True, key=lambda x: x[1])
485
+ sorted_indices = sorted(
486
+ zip(idx, self.thresholds), reverse=True, key=lambda x: x[1]
487
+ )
446
488
 
447
489
  # grab first n indices
448
490
  sorted_indices = sorted_indices[:n]
449
491
 
450
492
  # re-sort by index
451
- final_indices = sorted( [x[0] for x in sorted_indices] )
493
+ final_indices = sorted([x[0] for x in sorted_indices])
452
494
 
453
495
  return self.pts[final_indices]
454
496
 
@@ -1279,6 +1321,8 @@ class Scanbeam:
1279
1321
  @param tolerance: wiggle room, in favor of inside
1280
1322
  @return:
1281
1323
  """
1324
+ if tolerance == 0:
1325
+ tolerance == 1e-6
1282
1326
  self.scanline_to(y)
1283
1327
  for i in range(1, len(self._active_edge_list), 2):
1284
1328
  prior = self._active_edge_list[i - 1]
@@ -1712,11 +1756,26 @@ class Geomstr:
1712
1756
  ):
1713
1757
  # This is a deliberate subpath break
1714
1758
  obj.end()
1715
- elif isinstance(seg, (Line, Close)) and seg.start is not None and seg.end is not None:
1759
+ elif (
1760
+ isinstance(seg, (Line, Close))
1761
+ and seg.start is not None
1762
+ and seg.end is not None
1763
+ ):
1716
1764
  obj.line(complex(seg.start), complex(seg.end))
1717
- elif isinstance(seg, QuadraticBezier) and seg.start is not None and seg.end is not None and seg.control is not None:
1765
+ elif (
1766
+ isinstance(seg, QuadraticBezier)
1767
+ and seg.start is not None
1768
+ and seg.end is not None
1769
+ and seg.control is not None
1770
+ ):
1718
1771
  obj.quad(complex(seg.start), complex(seg.control), complex(seg.end))
1719
- elif isinstance(seg, CubicBezier) and seg.start is not None and seg.end is not None and seg.control1 is not None and seg.control2 is not None:
1772
+ elif (
1773
+ isinstance(seg, CubicBezier)
1774
+ and seg.start is not None
1775
+ and seg.end is not None
1776
+ and seg.control1 is not None
1777
+ and seg.control2 is not None
1778
+ ):
1720
1779
  obj.cubic(
1721
1780
  complex(seg.start),
1722
1781
  complex(seg.control1),
@@ -1867,16 +1926,22 @@ class Geomstr:
1867
1926
  rx = abs(rx)
1868
1927
  ry = abs(ry)
1869
1928
  if rx == ry == 0:
1870
- path.line(complex(x, y), complex(x + width, y), settings=settings),
1871
- path.line(
1872
- complex(x + width, y), complex(x + width, y + height), settings=settings
1873
- ),
1874
- path.line(
1875
- complex(x + width, y + height),
1876
- complex(x, y + height),
1877
- settings=settings,
1878
- ),
1879
- path.line(complex(x, y + height), complex(x, y), settings=settings),
1929
+ (path.line(complex(x, y), complex(x + width, y), settings=settings),)
1930
+ (
1931
+ path.line(
1932
+ complex(x + width, y),
1933
+ complex(x + width, y + height),
1934
+ settings=settings,
1935
+ ),
1936
+ )
1937
+ (
1938
+ path.line(
1939
+ complex(x + width, y + height),
1940
+ complex(x, y + height),
1941
+ settings=settings,
1942
+ ),
1943
+ )
1944
+ (path.line(complex(x, y + height), complex(x, y), settings=settings),)
1880
1945
  else:
1881
1946
  offset = 1 - (1.0 / math.sqrt(2))
1882
1947
  path.line(complex(x + rx, y), complex(x + width - rx, y), settings=settings)
@@ -2028,7 +2093,7 @@ class Geomstr:
2028
2093
  return geometry
2029
2094
 
2030
2095
  @classmethod
2031
- def wobble(cls, algorithm, outer, radius, interval, speed, unit_factor = 1):
2096
+ def wobble(cls, algorithm, outer, radius, interval, speed, unit_factor=1):
2032
2097
  from meerk40t.fill.fills import Wobble
2033
2098
 
2034
2099
  w = Wobble(algorithm, radius=radius, speed=speed, interval=interval)
@@ -2130,12 +2195,28 @@ class Geomstr:
2130
2195
  @classmethod
2131
2196
  def wobble_dash(cls, outer, dashlength, interval, irrelevant, unit_factor=1):
2132
2197
  from meerk40t.fill.fills import dashed_line as algorithm
2133
- return cls.wobble(algorithm, outer, dashlength, interval * unit_factor, irrelevant, unit_factor=unit_factor)
2198
+
2199
+ return cls.wobble(
2200
+ algorithm,
2201
+ outer,
2202
+ dashlength,
2203
+ interval * unit_factor,
2204
+ irrelevant,
2205
+ unit_factor=unit_factor,
2206
+ )
2134
2207
 
2135
2208
  @classmethod
2136
2209
  def wobble_tab(cls, outer, tablength, interval, tabpositions, unit_factor=1):
2137
2210
  from meerk40t.fill.fills import tabbed_path as algorithm
2138
- return cls.wobble(algorithm, outer, tablength, interval * unit_factor, tabpositions, unit_factor=unit_factor)
2211
+
2212
+ return cls.wobble(
2213
+ algorithm,
2214
+ outer,
2215
+ tablength,
2216
+ interval * unit_factor,
2217
+ tabpositions,
2218
+ unit_factor=unit_factor,
2219
+ )
2139
2220
 
2140
2221
  @classmethod
2141
2222
  def from_float_segments(cls, float_segments):
@@ -2261,7 +2342,7 @@ class Geomstr:
2261
2342
  if segments:
2262
2343
  yield segments
2263
2344
 
2264
- def as_equal_interpolated_points(self, distance=100):
2345
+ def as_equal_interpolated_points(self, distance=100, expand_lines=False):
2265
2346
  """
2266
2347
  Regardless of specified distance this will always give the start and end points of each node within the
2267
2348
  geometry. It will not duplicate the nodes if the start of one is the end of another. If the start and end
@@ -2294,7 +2375,21 @@ class Geomstr:
2294
2375
  at_start = True
2295
2376
  continue
2296
2377
  elif seg_type == TYPE_LINE:
2297
- pass
2378
+ if expand_lines:
2379
+ ts = np.linspace(0, 1, 1000)
2380
+ pts = self._line_position(e, ts)
2381
+ distances = np.abs(pts[:-1] - pts[1:])
2382
+ distances = np.cumsum(distances)
2383
+ max_distance = distances[-1]
2384
+ dist_values = np.linspace(
2385
+ 0,
2386
+ max_distance,
2387
+ int(np.ceil(max_distance / distance)),
2388
+ endpoint=False,
2389
+ )[1:]
2390
+ near_t = np.searchsorted(distances, dist_values, side="right")
2391
+ pts = pts[near_t]
2392
+ yield from pts
2298
2393
  elif seg_type == TYPE_QUAD:
2299
2394
  ts = np.linspace(0, 1, 1000)
2300
2395
  pts = self._quad_position(e, ts)
@@ -2811,7 +2906,7 @@ class Geomstr:
2811
2906
 
2812
2907
  @return:
2813
2908
  """
2814
- sweep = end_t - start_t
2909
+ sweep = end_t - start_t
2815
2910
  if slices is None:
2816
2911
  # A full ellipse can be properly represented with 12 slices - we err on the side of caution here...
2817
2912
  slices = int(1.5 * 12 * sweep / math.tau)
@@ -2967,7 +3062,10 @@ class Geomstr:
2967
3062
  current = self.segments[i]
2968
3063
  start0, control0, info0, control20, end0 = previous
2969
3064
  start1, control1, info1, control21, end1 = current
2970
- if self._segtype(previous) != TYPE_LINE or self._segtype(current) != TYPE_LINE:
3065
+ if (
3066
+ self._segtype(previous) != TYPE_LINE
3067
+ or self._segtype(current) != TYPE_LINE
3068
+ ):
2971
3069
  continue
2972
3070
  towards0 = Geomstr.towards(None, start0, end0, 1 - amount)
2973
3071
  towards1 = Geomstr.towards(None, start1, end1, amount)
@@ -2986,7 +3084,10 @@ class Geomstr:
2986
3084
  current = self.segments[i]
2987
3085
  start0, control0, info0, control20, end0 = previous
2988
3086
  start1, control1, info1, control21, end1 = current
2989
- if self._segtype(previous) != TYPE_LINE or self._segtype(current) != TYPE_LINE:
3087
+ if (
3088
+ self._segtype(previous) != TYPE_LINE
3089
+ or self._segtype(current) != TYPE_LINE
3090
+ ):
2990
3091
  continue
2991
3092
  towards0 = Geomstr.towards(None, start0, end0, 1 - amount)
2992
3093
  towards1 = Geomstr.towards(None, start1, end1, amount)
@@ -3527,7 +3628,7 @@ class Geomstr:
3527
3628
 
3528
3629
  def _cubic_length_via_quad(self, line):
3529
3630
  """
3530
- If we have scipy.integrate availible, use quad from that to solve this.
3631
+ If we have scipy.integrate available, use quad from that to solve this.
3531
3632
 
3532
3633
  @param line:
3533
3634
  @return:
@@ -3580,9 +3681,13 @@ class Geomstr:
3580
3681
  yield start, control, info, control2, mid[0]
3581
3682
  for i in range(1, len(mid)):
3582
3683
  if breaks:
3583
- yield mid[i - 1], mid[i - 1], complex(TYPE_END, info.imag), mid[
3584
- i - 1
3585
- ], mid[i - 1]
3684
+ yield (
3685
+ mid[i - 1],
3686
+ mid[i - 1],
3687
+ complex(TYPE_END, info.imag),
3688
+ mid[i - 1],
3689
+ mid[i - 1],
3690
+ )
3586
3691
  yield mid[i - 1], control, info, control2, mid[i]
3587
3692
  if breaks:
3588
3693
  yield mid[-1], 0, complex(TYPE_END, info.imag), 0, mid[-1]
@@ -4366,7 +4471,7 @@ class Geomstr:
4366
4471
  return wh, x_vals + y_vals * 1j, ta_hit, tb_hit
4367
4472
 
4368
4473
  #######################
4369
- # Geom Tranformations
4474
+ # Geom Transformations
4370
4475
  #######################
4371
4476
 
4372
4477
  def transform(self, mx, e=None):
@@ -4656,10 +4761,13 @@ class Geomstr:
4656
4761
  return process(S, K, a, c)[:-1] + process(S, K, c, b)
4657
4762
 
4658
4763
  def quickhull_2d(S: np.ndarray) -> np.ndarray:
4659
- a, b = np.argmin(S[:,0]), np.argmax(S[:,0])
4660
- max_index = np.argmax(S[:,0])
4764
+ a, b = np.argmin(S[:, 0]), np.argmax(S[:, 0])
4765
+ max_index = np.argmax(S[:, 0])
4661
4766
  # max_element = S[max_index]
4662
- return process(S, np.arange(S.shape[0]), a, max_index)[:-1] + process(S, np.arange(S.shape[0]), max_index, a)[:-1]
4767
+ return (
4768
+ process(S, np.arange(S.shape[0]), a, max_index)[:-1]
4769
+ + process(S, np.arange(S.shape[0]), max_index, a)[:-1]
4770
+ )
4663
4771
 
4664
4772
  if len(pts) == 0:
4665
4773
  return
@@ -4685,7 +4793,6 @@ class Geomstr:
4685
4793
  for p in res_pts:
4686
4794
  yield complex(p[0], p[1])
4687
4795
 
4688
-
4689
4796
  def _convex_hull_original(self, pts):
4690
4797
  """
4691
4798
  Generate points of the convex hull around the given points.
@@ -4756,7 +4863,9 @@ class Geomstr:
4756
4863
  # but that will let multiple unit tests fail
4757
4864
  # val = (q.real - p.real) * (r.imag - p.imag) - (q.imag - p.imag) * (r.real - p.real)
4758
4865
 
4759
- val = (q.imag - p.imag) * (r.real - q.real) - (q.real - p.real) * (r.imag - q.imag)
4866
+ val = (q.imag - p.imag) * (r.real - q.real) - (q.real - p.real) * (
4867
+ r.imag - q.imag
4868
+ )
4760
4869
  if val == 0:
4761
4870
  return "linear"
4762
4871
  elif val > 0:
@@ -5414,7 +5523,9 @@ class Geomstr:
5414
5523
  self.segments[p1] = c
5415
5524
  pt = c[-1]
5416
5525
 
5417
- def two_opt_distance(self, max_passes=None, chunk=0, auto_stop_threshold=None, feedback=None):
5526
+ def two_opt_distance(
5527
+ self, max_passes=None, chunk=0, auto_stop_threshold=None, feedback=None
5528
+ ):
5418
5529
  """
5419
5530
  Perform two-opt optimization to minimize travel distances.
5420
5531
  @param max_passes: Max number of passes to attempt
@@ -5435,7 +5546,11 @@ class Geomstr:
5435
5546
  improved = True
5436
5547
  first_travel = self.travel_distance()
5437
5548
  last_travel = first_travel
5438
- threshold_value = None if auto_stop_threshold is None else auto_stop_threshold / 100.0 * last_travel
5549
+ threshold_value = (
5550
+ None
5551
+ if auto_stop_threshold is None
5552
+ else auto_stop_threshold / 100.0 * last_travel
5553
+ )
5439
5554
  while improved:
5440
5555
  improved = False
5441
5556
 
@@ -5644,7 +5759,9 @@ class Geomstr:
5644
5759
  elif threshold is not None:
5645
5760
  newpoints = simplifier.simplify(threshold=threshold)
5646
5761
  else:
5647
- raise ValueError("You need to provide at least one parameter for simplify_geomstr")
5762
+ raise ValueError(
5763
+ "You need to provide at least one parameter for simplify_geomstr"
5764
+ )
5648
5765
  newgeom.append(Geomstr.lines(newpoints))
5649
5766
  points.clear()
5650
5767