meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7040__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 (79) hide show
  1. meerk40t/balormk/clone_loader.py +3 -2
  2. meerk40t/balormk/controller.py +28 -11
  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 +109 -51
  14. meerk40t/core/elements/element_treeops.py +435 -140
  15. meerk40t/core/elements/elements.py +100 -9
  16. meerk40t/core/elements/shapes.py +259 -39
  17. meerk40t/core/elements/tree_commands.py +10 -5
  18. meerk40t/core/node/elem_ellipse.py +18 -8
  19. meerk40t/core/node/elem_image.py +51 -19
  20. meerk40t/core/node/elem_line.py +18 -8
  21. meerk40t/core/node/elem_path.py +18 -8
  22. meerk40t/core/node/elem_point.py +10 -4
  23. meerk40t/core/node/elem_polyline.py +19 -11
  24. meerk40t/core/node/elem_rect.py +18 -8
  25. meerk40t/core/node/elem_text.py +11 -5
  26. meerk40t/core/node/filenode.py +2 -8
  27. meerk40t/core/node/groupnode.py +11 -11
  28. meerk40t/core/node/image_processed.py +11 -5
  29. meerk40t/core/node/image_raster.py +11 -5
  30. meerk40t/core/node/node.py +64 -16
  31. meerk40t/core/node/refnode.py +2 -1
  32. meerk40t/core/svg_io.py +91 -34
  33. meerk40t/device/dummydevice.py +7 -1
  34. meerk40t/extra/vtracer.py +222 -0
  35. meerk40t/grbl/device.py +81 -8
  36. meerk40t/gui/about.py +20 -0
  37. meerk40t/gui/devicepanel.py +20 -16
  38. meerk40t/gui/gui_mixins.py +4 -0
  39. meerk40t/gui/icons.py +330 -253
  40. meerk40t/gui/laserpanel.py +8 -3
  41. meerk40t/gui/laserrender.py +41 -21
  42. meerk40t/gui/magnetoptions.py +158 -65
  43. meerk40t/gui/materialtest.py +229 -39
  44. meerk40t/gui/navigationpanels.py +229 -24
  45. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  46. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  47. meerk40t/gui/ribbon.py +6 -1
  48. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  49. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  50. meerk40t/gui/simulation.py +75 -77
  51. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  52. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  53. meerk40t/gui/tips.py +15 -1
  54. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  55. meerk40t/gui/wxmmain.py +242 -114
  56. meerk40t/gui/wxmscene.py +107 -24
  57. meerk40t/gui/wxmtree.py +4 -2
  58. meerk40t/gui/wxutils.py +60 -15
  59. meerk40t/image/imagetools.py +129 -65
  60. meerk40t/internal_plugins.py +4 -0
  61. meerk40t/kernel/kernel.py +39 -18
  62. meerk40t/kernel/settings.py +28 -9
  63. meerk40t/lihuiyu/device.py +24 -12
  64. meerk40t/main.py +1 -1
  65. meerk40t/moshi/device.py +20 -6
  66. meerk40t/network/console_server.py +22 -6
  67. meerk40t/newly/device.py +10 -3
  68. meerk40t/newly/gui/gui.py +10 -0
  69. meerk40t/ruida/device.py +22 -2
  70. meerk40t/ruida/loader.py +6 -3
  71. meerk40t/tools/geomstr.py +193 -125
  72. meerk40t/tools/rasterplotter.py +179 -93
  73. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
  74. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +79 -78
  75. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
  76. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +0 -0
  77. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
  78. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
  79. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.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,78 @@ 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
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
189
+
190
+ def stitch_geometries(geometry_list: list, tolerance: float = 0.0) -> list:
191
+ """
192
+ Stitches geometries within the given tolerance.
193
+
194
+ Args:
195
+ geometry_list: List of Geomstr objects to stitch.
196
+ tolerance: Maximum distance between endpoints to consider a stitch.
197
+
198
+ Returns:
199
+ List of stitched Geomstr objects, or None if no stitches were made.
200
+ """
201
+
202
+ geometries = [g for g in geometry_list if g is not None]
203
+ if not geometries:
204
+ return None
205
+ if tolerance == 0:
206
+ tolerance = 1e-6
207
+
208
+ stitched_geometries = []
209
+ while geometries:
210
+ g1 = geometries.pop(0)
211
+ stitched = False
212
+ for i, g2 in enumerate(stitched_geometries):
213
+ for reverse1 in (False, True):
214
+ for reverse2 in (False, True):
215
+ g1_start = g1.last_point if reverse1 else g1.first_point
216
+ g1_end = g1.first_point if reverse1 else g1.last_point
217
+ g2_start = g2.last_point if reverse2 else g2.first_point
218
+ g2_end = g2.first_point if reverse2 else g2.last_point
219
+
220
+ if (
221
+ g1_start is None
222
+ or g1_end is None
223
+ or g2_start is None
224
+ or g2_end is None
225
+ ):
226
+ continue
227
+ if abs(g2_end - g1_start) <= tolerance:
228
+ if abs(g2_end - g1_start) > 0:
229
+ g2.line(g2_end, g1_start)
230
+ if reverse1:
231
+ g1.reverse()
232
+ g2.append(g1, end=False)
233
+ stitched_geometries[i] = g2
234
+ stitched = True
235
+ break
236
+ elif abs(g2_start - g1_end) <= tolerance:
237
+ if abs(g2_start - g1_end) > 0:
238
+ g1.line(g1_end, g2_start)
239
+ if reverse1:
240
+ g1.reverse()
241
+ g2.insert(0, g1.segments[: g1.index])
242
+ stitched_geometries[i] = g2
243
+ stitched = True
244
+ break
245
+ if stitched:
249
246
  break
247
+ if stitched:
248
+ break
249
+
250
+ if not stitched:
251
+ stitched_geometries.append(g1)
252
+
253
+ # Close any remaining small gaps between start and end points.
254
+ for g in stitched_geometries:
255
+ if 0 < abs(g.last_point - g.first_point) <= tolerance:
256
+ g.line(g.last_point, g.first_point)
257
+
258
+ return stitched_geometries
259
+
250
260
 
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
270
261
 
271
262
  class Simplifier:
272
263
  # Copyright (c) 2014 Elliot Hallmark
@@ -393,7 +384,7 @@ class Simplifier:
393
384
  areas[min_vert] = right_area
394
385
 
395
386
  if min_vert > 1:
396
- # cant try/except because 0-1=-1 is a valid index
387
+ # can't try/except because 0-1=-1 is a valid index
397
388
  left_area = triangle_area(
398
389
  self.pts[i[min_vert - 2]],
399
390
  self.pts[i[min_vert - 1]],
@@ -442,13 +433,15 @@ class Simplifier:
442
433
  # for some points
443
434
  # sort point indices by threshold
444
435
  idx = list(range(len(self.pts)))
445
- sorted_indices = sorted(zip(idx, self.thresholds), reverse=True, key=lambda x: x[1])
436
+ sorted_indices = sorted(
437
+ zip(idx, self.thresholds), reverse=True, key=lambda x: x[1]
438
+ )
446
439
 
447
440
  # grab first n indices
448
441
  sorted_indices = sorted_indices[:n]
449
442
 
450
443
  # re-sort by index
451
- final_indices = sorted( [x[0] for x in sorted_indices] )
444
+ final_indices = sorted([x[0] for x in sorted_indices])
452
445
 
453
446
  return self.pts[final_indices]
454
447
 
@@ -1279,6 +1272,8 @@ class Scanbeam:
1279
1272
  @param tolerance: wiggle room, in favor of inside
1280
1273
  @return:
1281
1274
  """
1275
+ if tolerance == 0:
1276
+ tolerance == 1e-6
1282
1277
  self.scanline_to(y)
1283
1278
  for i in range(1, len(self._active_edge_list), 2):
1284
1279
  prior = self._active_edge_list[i - 1]
@@ -1712,11 +1707,26 @@ class Geomstr:
1712
1707
  ):
1713
1708
  # This is a deliberate subpath break
1714
1709
  obj.end()
1715
- elif isinstance(seg, (Line, Close)) and seg.start is not None and seg.end is not None:
1710
+ elif (
1711
+ isinstance(seg, (Line, Close))
1712
+ and seg.start is not None
1713
+ and seg.end is not None
1714
+ ):
1716
1715
  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:
1716
+ elif (
1717
+ isinstance(seg, QuadraticBezier)
1718
+ and seg.start is not None
1719
+ and seg.end is not None
1720
+ and seg.control is not None
1721
+ ):
1718
1722
  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:
1723
+ elif (
1724
+ isinstance(seg, CubicBezier)
1725
+ and seg.start is not None
1726
+ and seg.end is not None
1727
+ and seg.control1 is not None
1728
+ and seg.control2 is not None
1729
+ ):
1720
1730
  obj.cubic(
1721
1731
  complex(seg.start),
1722
1732
  complex(seg.control1),
@@ -1867,16 +1877,22 @@ class Geomstr:
1867
1877
  rx = abs(rx)
1868
1878
  ry = abs(ry)
1869
1879
  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),
1880
+ (path.line(complex(x, y), complex(x + width, y), settings=settings),)
1881
+ (
1882
+ path.line(
1883
+ complex(x + width, y),
1884
+ complex(x + width, y + height),
1885
+ settings=settings,
1886
+ ),
1887
+ )
1888
+ (
1889
+ path.line(
1890
+ complex(x + width, y + height),
1891
+ complex(x, y + height),
1892
+ settings=settings,
1893
+ ),
1894
+ )
1895
+ (path.line(complex(x, y + height), complex(x, y), settings=settings),)
1880
1896
  else:
1881
1897
  offset = 1 - (1.0 / math.sqrt(2))
1882
1898
  path.line(complex(x + rx, y), complex(x + width - rx, y), settings=settings)
@@ -2028,7 +2044,7 @@ class Geomstr:
2028
2044
  return geometry
2029
2045
 
2030
2046
  @classmethod
2031
- def wobble(cls, algorithm, outer, radius, interval, speed, unit_factor = 1):
2047
+ def wobble(cls, algorithm, outer, radius, interval, speed, unit_factor=1):
2032
2048
  from meerk40t.fill.fills import Wobble
2033
2049
 
2034
2050
  w = Wobble(algorithm, radius=radius, speed=speed, interval=interval)
@@ -2130,12 +2146,28 @@ class Geomstr:
2130
2146
  @classmethod
2131
2147
  def wobble_dash(cls, outer, dashlength, interval, irrelevant, unit_factor=1):
2132
2148
  from meerk40t.fill.fills import dashed_line as algorithm
2133
- return cls.wobble(algorithm, outer, dashlength, interval * unit_factor, irrelevant, unit_factor=unit_factor)
2149
+
2150
+ return cls.wobble(
2151
+ algorithm,
2152
+ outer,
2153
+ dashlength,
2154
+ interval * unit_factor,
2155
+ irrelevant,
2156
+ unit_factor=unit_factor,
2157
+ )
2134
2158
 
2135
2159
  @classmethod
2136
2160
  def wobble_tab(cls, outer, tablength, interval, tabpositions, unit_factor=1):
2137
2161
  from meerk40t.fill.fills import tabbed_path as algorithm
2138
- return cls.wobble(algorithm, outer, tablength, interval * unit_factor, tabpositions, unit_factor=unit_factor)
2162
+
2163
+ return cls.wobble(
2164
+ algorithm,
2165
+ outer,
2166
+ tablength,
2167
+ interval * unit_factor,
2168
+ tabpositions,
2169
+ unit_factor=unit_factor,
2170
+ )
2139
2171
 
2140
2172
  @classmethod
2141
2173
  def from_float_segments(cls, float_segments):
@@ -2261,7 +2293,7 @@ class Geomstr:
2261
2293
  if segments:
2262
2294
  yield segments
2263
2295
 
2264
- def as_equal_interpolated_points(self, distance=100):
2296
+ def as_equal_interpolated_points(self, distance=100, expand_lines=False):
2265
2297
  """
2266
2298
  Regardless of specified distance this will always give the start and end points of each node within the
2267
2299
  geometry. It will not duplicate the nodes if the start of one is the end of another. If the start and end
@@ -2294,7 +2326,21 @@ class Geomstr:
2294
2326
  at_start = True
2295
2327
  continue
2296
2328
  elif seg_type == TYPE_LINE:
2297
- pass
2329
+ if expand_lines:
2330
+ ts = np.linspace(0, 1, 1000)
2331
+ pts = self._line_position(e, ts)
2332
+ distances = np.abs(pts[:-1] - pts[1:])
2333
+ distances = np.cumsum(distances)
2334
+ max_distance = distances[-1]
2335
+ dist_values = np.linspace(
2336
+ 0,
2337
+ max_distance,
2338
+ int(np.ceil(max_distance / distance)),
2339
+ endpoint=False,
2340
+ )[1:]
2341
+ near_t = np.searchsorted(distances, dist_values, side="right")
2342
+ pts = pts[near_t]
2343
+ yield from pts
2298
2344
  elif seg_type == TYPE_QUAD:
2299
2345
  ts = np.linspace(0, 1, 1000)
2300
2346
  pts = self._quad_position(e, ts)
@@ -2811,7 +2857,7 @@ class Geomstr:
2811
2857
 
2812
2858
  @return:
2813
2859
  """
2814
- sweep = end_t - start_t
2860
+ sweep = end_t - start_t
2815
2861
  if slices is None:
2816
2862
  # A full ellipse can be properly represented with 12 slices - we err on the side of caution here...
2817
2863
  slices = int(1.5 * 12 * sweep / math.tau)
@@ -2967,7 +3013,10 @@ class Geomstr:
2967
3013
  current = self.segments[i]
2968
3014
  start0, control0, info0, control20, end0 = previous
2969
3015
  start1, control1, info1, control21, end1 = current
2970
- if self._segtype(previous) != TYPE_LINE or self._segtype(current) != TYPE_LINE:
3016
+ if (
3017
+ self._segtype(previous) != TYPE_LINE
3018
+ or self._segtype(current) != TYPE_LINE
3019
+ ):
2971
3020
  continue
2972
3021
  towards0 = Geomstr.towards(None, start0, end0, 1 - amount)
2973
3022
  towards1 = Geomstr.towards(None, start1, end1, amount)
@@ -2986,7 +3035,10 @@ class Geomstr:
2986
3035
  current = self.segments[i]
2987
3036
  start0, control0, info0, control20, end0 = previous
2988
3037
  start1, control1, info1, control21, end1 = current
2989
- if self._segtype(previous) != TYPE_LINE or self._segtype(current) != TYPE_LINE:
3038
+ if (
3039
+ self._segtype(previous) != TYPE_LINE
3040
+ or self._segtype(current) != TYPE_LINE
3041
+ ):
2990
3042
  continue
2991
3043
  towards0 = Geomstr.towards(None, start0, end0, 1 - amount)
2992
3044
  towards1 = Geomstr.towards(None, start1, end1, amount)
@@ -3527,7 +3579,7 @@ class Geomstr:
3527
3579
 
3528
3580
  def _cubic_length_via_quad(self, line):
3529
3581
  """
3530
- If we have scipy.integrate availible, use quad from that to solve this.
3582
+ If we have scipy.integrate available, use quad from that to solve this.
3531
3583
 
3532
3584
  @param line:
3533
3585
  @return:
@@ -3580,9 +3632,13 @@ class Geomstr:
3580
3632
  yield start, control, info, control2, mid[0]
3581
3633
  for i in range(1, len(mid)):
3582
3634
  if breaks:
3583
- yield mid[i - 1], mid[i - 1], complex(TYPE_END, info.imag), mid[
3584
- i - 1
3585
- ], mid[i - 1]
3635
+ yield (
3636
+ mid[i - 1],
3637
+ mid[i - 1],
3638
+ complex(TYPE_END, info.imag),
3639
+ mid[i - 1],
3640
+ mid[i - 1],
3641
+ )
3586
3642
  yield mid[i - 1], control, info, control2, mid[i]
3587
3643
  if breaks:
3588
3644
  yield mid[-1], 0, complex(TYPE_END, info.imag), 0, mid[-1]
@@ -4366,7 +4422,7 @@ class Geomstr:
4366
4422
  return wh, x_vals + y_vals * 1j, ta_hit, tb_hit
4367
4423
 
4368
4424
  #######################
4369
- # Geom Tranformations
4425
+ # Geom Transformations
4370
4426
  #######################
4371
4427
 
4372
4428
  def transform(self, mx, e=None):
@@ -4656,10 +4712,13 @@ class Geomstr:
4656
4712
  return process(S, K, a, c)[:-1] + process(S, K, c, b)
4657
4713
 
4658
4714
  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])
4715
+ a, b = np.argmin(S[:, 0]), np.argmax(S[:, 0])
4716
+ max_index = np.argmax(S[:, 0])
4661
4717
  # 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]
4718
+ return (
4719
+ process(S, np.arange(S.shape[0]), a, max_index)[:-1]
4720
+ + process(S, np.arange(S.shape[0]), max_index, a)[:-1]
4721
+ )
4663
4722
 
4664
4723
  if len(pts) == 0:
4665
4724
  return
@@ -4685,7 +4744,6 @@ class Geomstr:
4685
4744
  for p in res_pts:
4686
4745
  yield complex(p[0], p[1])
4687
4746
 
4688
-
4689
4747
  def _convex_hull_original(self, pts):
4690
4748
  """
4691
4749
  Generate points of the convex hull around the given points.
@@ -4756,7 +4814,9 @@ class Geomstr:
4756
4814
  # but that will let multiple unit tests fail
4757
4815
  # val = (q.real - p.real) * (r.imag - p.imag) - (q.imag - p.imag) * (r.real - p.real)
4758
4816
 
4759
- val = (q.imag - p.imag) * (r.real - q.real) - (q.real - p.real) * (r.imag - q.imag)
4817
+ val = (q.imag - p.imag) * (r.real - q.real) - (q.real - p.real) * (
4818
+ r.imag - q.imag
4819
+ )
4760
4820
  if val == 0:
4761
4821
  return "linear"
4762
4822
  elif val > 0:
@@ -5414,7 +5474,9 @@ class Geomstr:
5414
5474
  self.segments[p1] = c
5415
5475
  pt = c[-1]
5416
5476
 
5417
- def two_opt_distance(self, max_passes=None, chunk=0, auto_stop_threshold=None, feedback=None):
5477
+ def two_opt_distance(
5478
+ self, max_passes=None, chunk=0, auto_stop_threshold=None, feedback=None
5479
+ ):
5418
5480
  """
5419
5481
  Perform two-opt optimization to minimize travel distances.
5420
5482
  @param max_passes: Max number of passes to attempt
@@ -5435,7 +5497,11 @@ class Geomstr:
5435
5497
  improved = True
5436
5498
  first_travel = self.travel_distance()
5437
5499
  last_travel = first_travel
5438
- threshold_value = None if auto_stop_threshold is None else auto_stop_threshold / 100.0 * last_travel
5500
+ threshold_value = (
5501
+ None
5502
+ if auto_stop_threshold is None
5503
+ else auto_stop_threshold / 100.0 * last_travel
5504
+ )
5439
5505
  while improved:
5440
5506
  improved = False
5441
5507
 
@@ -5644,7 +5710,9 @@ class Geomstr:
5644
5710
  elif threshold is not None:
5645
5711
  newpoints = simplifier.simplify(threshold=threshold)
5646
5712
  else:
5647
- raise ValueError("You need to provide at least one parameter for simplify_geomstr")
5713
+ raise ValueError(
5714
+ "You need to provide at least one parameter for simplify_geomstr"
5715
+ )
5648
5716
  newgeom.append(Geomstr.lines(newpoints))
5649
5717
  points.clear()
5650
5718