meerk40t 0.9.7020__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 (98) 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/cutcode/cutcode.py +1 -1
  14. meerk40t/core/cutplan.py +169 -43
  15. meerk40t/core/elements/element_treeops.py +444 -147
  16. meerk40t/core/elements/elements.py +100 -9
  17. meerk40t/core/elements/grid.py +8 -1
  18. meerk40t/core/elements/offset_mk.py +2 -1
  19. meerk40t/core/elements/shapes.py +618 -279
  20. meerk40t/core/elements/tree_commands.py +10 -5
  21. meerk40t/core/node/elem_ellipse.py +18 -8
  22. meerk40t/core/node/elem_image.py +51 -19
  23. meerk40t/core/node/elem_line.py +18 -8
  24. meerk40t/core/node/elem_path.py +18 -8
  25. meerk40t/core/node/elem_point.py +10 -4
  26. meerk40t/core/node/elem_polyline.py +19 -11
  27. meerk40t/core/node/elem_rect.py +18 -8
  28. meerk40t/core/node/elem_text.py +11 -5
  29. meerk40t/core/node/filenode.py +2 -8
  30. meerk40t/core/node/groupnode.py +11 -11
  31. meerk40t/core/node/image_processed.py +11 -5
  32. meerk40t/core/node/image_raster.py +11 -5
  33. meerk40t/core/node/node.py +70 -19
  34. meerk40t/core/node/refnode.py +2 -1
  35. meerk40t/core/planner.py +23 -0
  36. meerk40t/core/svg_io.py +91 -34
  37. meerk40t/core/undos.py +1 -1
  38. meerk40t/core/wordlist.py +1 -0
  39. meerk40t/device/dummydevice.py +7 -1
  40. meerk40t/dxf/dxf_io.py +6 -0
  41. meerk40t/extra/mk_potrace.py +1959 -0
  42. meerk40t/extra/param_functions.py +1 -1
  43. meerk40t/extra/potrace.py +14 -10
  44. meerk40t/extra/vtracer.py +222 -0
  45. meerk40t/grbl/device.py +81 -8
  46. meerk40t/grbl/interpreter.py +1 -1
  47. meerk40t/gui/about.py +21 -3
  48. meerk40t/gui/basicops.py +3 -3
  49. meerk40t/gui/choicepropertypanel.py +1 -4
  50. meerk40t/gui/devicepanel.py +20 -16
  51. meerk40t/gui/gui_mixins.py +8 -1
  52. meerk40t/gui/icons.py +330 -253
  53. meerk40t/gui/laserpanel.py +8 -3
  54. meerk40t/gui/laserrender.py +41 -21
  55. meerk40t/gui/magnetoptions.py +158 -65
  56. meerk40t/gui/materialtest.py +229 -39
  57. meerk40t/gui/navigationpanels.py +229 -24
  58. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  59. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  60. meerk40t/gui/ribbon.py +6 -1
  61. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  62. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  63. meerk40t/gui/simulation.py +75 -77
  64. meerk40t/gui/spoolerpanel.py +6 -9
  65. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  66. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  67. meerk40t/gui/themes.py +7 -1
  68. meerk40t/gui/tips.py +15 -1
  69. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  70. meerk40t/gui/wxmeerk40t.py +26 -0
  71. meerk40t/gui/wxmmain.py +242 -114
  72. meerk40t/gui/wxmscene.py +180 -4
  73. meerk40t/gui/wxmtree.py +4 -2
  74. meerk40t/gui/wxutils.py +60 -15
  75. meerk40t/image/imagetools.py +130 -66
  76. meerk40t/internal_plugins.py +4 -0
  77. meerk40t/kernel/kernel.py +49 -22
  78. meerk40t/kernel/settings.py +29 -8
  79. meerk40t/lihuiyu/device.py +30 -12
  80. meerk40t/main.py +22 -5
  81. meerk40t/moshi/device.py +20 -6
  82. meerk40t/network/console_server.py +22 -6
  83. meerk40t/newly/device.py +10 -3
  84. meerk40t/newly/gui/gui.py +10 -0
  85. meerk40t/ruida/device.py +22 -2
  86. meerk40t/ruida/gui/gui.py +6 -6
  87. meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
  88. meerk40t/ruida/loader.py +6 -3
  89. meerk40t/ruida/rdjob.py +3 -3
  90. meerk40t/tools/geomstr.py +195 -39
  91. meerk40t/tools/rasterplotter.py +179 -93
  92. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
  93. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +98 -96
  94. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +1 -1
  95. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
  96. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
  97. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
  98. {meerk40t-0.9.7020.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
@@ -180,6 +187,78 @@ def remove(s, i):
180
187
  s[i:-1] = s[i + 1 :]
181
188
 
182
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:
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
+
260
+
261
+
183
262
  class Simplifier:
184
263
  # Copyright (c) 2014 Elliot Hallmark
185
264
  # The MIT License (MIT)
@@ -305,7 +384,7 @@ class Simplifier:
305
384
  areas[min_vert] = right_area
306
385
 
307
386
  if min_vert > 1:
308
- # cant try/except because 0-1=-1 is a valid index
387
+ # can't try/except because 0-1=-1 is a valid index
309
388
  left_area = triangle_area(
310
389
  self.pts[i[min_vert - 2]],
311
390
  self.pts[i[min_vert - 1]],
@@ -354,13 +433,15 @@ class Simplifier:
354
433
  # for some points
355
434
  # sort point indices by threshold
356
435
  idx = list(range(len(self.pts)))
357
- 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
+ )
358
439
 
359
440
  # grab first n indices
360
441
  sorted_indices = sorted_indices[:n]
361
442
 
362
443
  # re-sort by index
363
- final_indices = sorted( [x[0] for x in sorted_indices] )
444
+ final_indices = sorted([x[0] for x in sorted_indices])
364
445
 
365
446
  return self.pts[final_indices]
366
447
 
@@ -1191,6 +1272,8 @@ class Scanbeam:
1191
1272
  @param tolerance: wiggle room, in favor of inside
1192
1273
  @return:
1193
1274
  """
1275
+ if tolerance == 0:
1276
+ tolerance == 1e-6
1194
1277
  self.scanline_to(y)
1195
1278
  for i in range(1, len(self._active_edge_list), 2):
1196
1279
  prior = self._active_edge_list[i - 1]
@@ -1624,11 +1707,26 @@ class Geomstr:
1624
1707
  ):
1625
1708
  # This is a deliberate subpath break
1626
1709
  obj.end()
1627
- 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
+ ):
1628
1715
  obj.line(complex(seg.start), complex(seg.end))
1629
- 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
+ ):
1630
1722
  obj.quad(complex(seg.start), complex(seg.control), complex(seg.end))
1631
- 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
+ ):
1632
1730
  obj.cubic(
1633
1731
  complex(seg.start),
1634
1732
  complex(seg.control1),
@@ -1779,16 +1877,22 @@ class Geomstr:
1779
1877
  rx = abs(rx)
1780
1878
  ry = abs(ry)
1781
1879
  if rx == ry == 0:
1782
- path.line(complex(x, y), complex(x + width, y), settings=settings),
1783
- path.line(
1784
- complex(x + width, y), complex(x + width, y + height), settings=settings
1785
- ),
1786
- path.line(
1787
- complex(x + width, y + height),
1788
- complex(x, y + height),
1789
- settings=settings,
1790
- ),
1791
- 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),)
1792
1896
  else:
1793
1897
  offset = 1 - (1.0 / math.sqrt(2))
1794
1898
  path.line(complex(x + rx, y), complex(x + width - rx, y), settings=settings)
@@ -1940,7 +2044,7 @@ class Geomstr:
1940
2044
  return geometry
1941
2045
 
1942
2046
  @classmethod
1943
- def wobble(cls, algorithm, outer, radius, interval, speed, unit_factor = 1):
2047
+ def wobble(cls, algorithm, outer, radius, interval, speed, unit_factor=1):
1944
2048
  from meerk40t.fill.fills import Wobble
1945
2049
 
1946
2050
  w = Wobble(algorithm, radius=radius, speed=speed, interval=interval)
@@ -2042,12 +2146,28 @@ class Geomstr:
2042
2146
  @classmethod
2043
2147
  def wobble_dash(cls, outer, dashlength, interval, irrelevant, unit_factor=1):
2044
2148
  from meerk40t.fill.fills import dashed_line as algorithm
2045
- 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
+ )
2046
2158
 
2047
2159
  @classmethod
2048
2160
  def wobble_tab(cls, outer, tablength, interval, tabpositions, unit_factor=1):
2049
2161
  from meerk40t.fill.fills import tabbed_path as algorithm
2050
- 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
+ )
2051
2171
 
2052
2172
  @classmethod
2053
2173
  def from_float_segments(cls, float_segments):
@@ -2173,7 +2293,7 @@ class Geomstr:
2173
2293
  if segments:
2174
2294
  yield segments
2175
2295
 
2176
- def as_equal_interpolated_points(self, distance=100):
2296
+ def as_equal_interpolated_points(self, distance=100, expand_lines=False):
2177
2297
  """
2178
2298
  Regardless of specified distance this will always give the start and end points of each node within the
2179
2299
  geometry. It will not duplicate the nodes if the start of one is the end of another. If the start and end
@@ -2206,7 +2326,21 @@ class Geomstr:
2206
2326
  at_start = True
2207
2327
  continue
2208
2328
  elif seg_type == TYPE_LINE:
2209
- 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
2210
2344
  elif seg_type == TYPE_QUAD:
2211
2345
  ts = np.linspace(0, 1, 1000)
2212
2346
  pts = self._quad_position(e, ts)
@@ -2723,7 +2857,7 @@ class Geomstr:
2723
2857
 
2724
2858
  @return:
2725
2859
  """
2726
- sweep = end_t - start_t
2860
+ sweep = end_t - start_t
2727
2861
  if slices is None:
2728
2862
  # A full ellipse can be properly represented with 12 slices - we err on the side of caution here...
2729
2863
  slices = int(1.5 * 12 * sweep / math.tau)
@@ -2879,7 +3013,10 @@ class Geomstr:
2879
3013
  current = self.segments[i]
2880
3014
  start0, control0, info0, control20, end0 = previous
2881
3015
  start1, control1, info1, control21, end1 = current
2882
- 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
+ ):
2883
3020
  continue
2884
3021
  towards0 = Geomstr.towards(None, start0, end0, 1 - amount)
2885
3022
  towards1 = Geomstr.towards(None, start1, end1, amount)
@@ -2898,7 +3035,10 @@ class Geomstr:
2898
3035
  current = self.segments[i]
2899
3036
  start0, control0, info0, control20, end0 = previous
2900
3037
  start1, control1, info1, control21, end1 = current
2901
- 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
+ ):
2902
3042
  continue
2903
3043
  towards0 = Geomstr.towards(None, start0, end0, 1 - amount)
2904
3044
  towards1 = Geomstr.towards(None, start1, end1, amount)
@@ -3439,7 +3579,7 @@ class Geomstr:
3439
3579
 
3440
3580
  def _cubic_length_via_quad(self, line):
3441
3581
  """
3442
- 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.
3443
3583
 
3444
3584
  @param line:
3445
3585
  @return:
@@ -3492,9 +3632,13 @@ class Geomstr:
3492
3632
  yield start, control, info, control2, mid[0]
3493
3633
  for i in range(1, len(mid)):
3494
3634
  if breaks:
3495
- yield mid[i - 1], mid[i - 1], complex(TYPE_END, info.imag), mid[
3496
- i - 1
3497
- ], 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
+ )
3498
3642
  yield mid[i - 1], control, info, control2, mid[i]
3499
3643
  if breaks:
3500
3644
  yield mid[-1], 0, complex(TYPE_END, info.imag), 0, mid[-1]
@@ -4278,7 +4422,7 @@ class Geomstr:
4278
4422
  return wh, x_vals + y_vals * 1j, ta_hit, tb_hit
4279
4423
 
4280
4424
  #######################
4281
- # Geom Tranformations
4425
+ # Geom Transformations
4282
4426
  #######################
4283
4427
 
4284
4428
  def transform(self, mx, e=None):
@@ -4568,10 +4712,13 @@ class Geomstr:
4568
4712
  return process(S, K, a, c)[:-1] + process(S, K, c, b)
4569
4713
 
4570
4714
  def quickhull_2d(S: np.ndarray) -> np.ndarray:
4571
- a, b = np.argmin(S[:,0]), np.argmax(S[:,0])
4572
- max_index = np.argmax(S[:,0])
4715
+ a, b = np.argmin(S[:, 0]), np.argmax(S[:, 0])
4716
+ max_index = np.argmax(S[:, 0])
4573
4717
  # max_element = S[max_index]
4574
- 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
+ )
4575
4722
 
4576
4723
  if len(pts) == 0:
4577
4724
  return
@@ -4597,7 +4744,6 @@ class Geomstr:
4597
4744
  for p in res_pts:
4598
4745
  yield complex(p[0], p[1])
4599
4746
 
4600
-
4601
4747
  def _convex_hull_original(self, pts):
4602
4748
  """
4603
4749
  Generate points of the convex hull around the given points.
@@ -4668,7 +4814,9 @@ class Geomstr:
4668
4814
  # but that will let multiple unit tests fail
4669
4815
  # val = (q.real - p.real) * (r.imag - p.imag) - (q.imag - p.imag) * (r.real - p.real)
4670
4816
 
4671
- 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
+ )
4672
4820
  if val == 0:
4673
4821
  return "linear"
4674
4822
  elif val > 0:
@@ -5326,7 +5474,9 @@ class Geomstr:
5326
5474
  self.segments[p1] = c
5327
5475
  pt = c[-1]
5328
5476
 
5329
- 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
+ ):
5330
5480
  """
5331
5481
  Perform two-opt optimization to minimize travel distances.
5332
5482
  @param max_passes: Max number of passes to attempt
@@ -5347,7 +5497,11 @@ class Geomstr:
5347
5497
  improved = True
5348
5498
  first_travel = self.travel_distance()
5349
5499
  last_travel = first_travel
5350
- 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
+ )
5351
5505
  while improved:
5352
5506
  improved = False
5353
5507
 
@@ -5556,7 +5710,9 @@ class Geomstr:
5556
5710
  elif threshold is not None:
5557
5711
  newpoints = simplifier.simplify(threshold=threshold)
5558
5712
  else:
5559
- 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
+ )
5560
5716
  newgeom.append(Geomstr.lines(newpoints))
5561
5717
  points.clear()
5562
5718