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.
- meerk40t/balormk/clone_loader.py +3 -2
- meerk40t/balormk/controller.py +38 -13
- meerk40t/balormk/cylindermod.py +1 -0
- meerk40t/balormk/device.py +13 -9
- meerk40t/balormk/driver.py +9 -2
- meerk40t/balormk/galvo_commands.py +3 -1
- meerk40t/balormk/gui/gui.py +6 -0
- meerk40t/balormk/livelightjob.py +338 -321
- meerk40t/balormk/mock_connection.py +4 -3
- meerk40t/balormk/usb_connection.py +11 -2
- meerk40t/camera/camera.py +19 -14
- meerk40t/camera/gui/camerapanel.py +6 -0
- meerk40t/core/cutplan.py +101 -78
- meerk40t/core/elements/element_treeops.py +435 -140
- meerk40t/core/elements/elements.py +100 -9
- meerk40t/core/elements/shapes.py +259 -72
- meerk40t/core/elements/tree_commands.py +10 -5
- meerk40t/core/node/blobnode.py +19 -4
- meerk40t/core/node/elem_ellipse.py +18 -8
- meerk40t/core/node/elem_image.py +51 -19
- meerk40t/core/node/elem_line.py +18 -8
- meerk40t/core/node/elem_path.py +18 -8
- meerk40t/core/node/elem_point.py +10 -4
- meerk40t/core/node/elem_polyline.py +19 -11
- meerk40t/core/node/elem_rect.py +18 -8
- meerk40t/core/node/elem_text.py +11 -5
- meerk40t/core/node/filenode.py +2 -8
- meerk40t/core/node/groupnode.py +11 -11
- meerk40t/core/node/image_processed.py +11 -5
- meerk40t/core/node/image_raster.py +11 -5
- meerk40t/core/node/node.py +64 -16
- meerk40t/core/node/refnode.py +2 -1
- meerk40t/core/planner.py +25 -11
- meerk40t/core/svg_io.py +91 -34
- meerk40t/device/dummydevice.py +7 -1
- meerk40t/extra/vtracer.py +222 -0
- meerk40t/grbl/device.py +96 -9
- meerk40t/grbl/driver.py +15 -5
- meerk40t/gui/about.py +20 -0
- meerk40t/gui/devicepanel.py +20 -16
- meerk40t/gui/gui_mixins.py +4 -0
- meerk40t/gui/icons.py +330 -253
- meerk40t/gui/laserpanel.py +27 -3
- meerk40t/gui/laserrender.py +41 -21
- meerk40t/gui/magnetoptions.py +158 -65
- meerk40t/gui/materialtest.py +569 -310
- meerk40t/gui/navigationpanels.py +229 -24
- meerk40t/gui/propertypanels/hatchproperty.py +2 -0
- meerk40t/gui/propertypanels/imageproperty.py +160 -106
- meerk40t/gui/propertypanels/wobbleproperty.py +6 -2
- meerk40t/gui/ribbon.py +6 -1
- meerk40t/gui/scenewidgets/gridwidget.py +29 -32
- meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
- meerk40t/gui/simulation.py +75 -77
- meerk40t/gui/spoolerpanel.py +27 -7
- meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/tips.py +15 -1
- meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
- meerk40t/gui/wxmmain.py +242 -114
- meerk40t/gui/wxmscene.py +107 -24
- meerk40t/gui/wxmtree.py +4 -2
- meerk40t/gui/wxutils.py +286 -15
- meerk40t/image/imagetools.py +129 -65
- meerk40t/internal_plugins.py +4 -0
- meerk40t/kernel/kernel.py +67 -18
- meerk40t/kernel/settings.py +28 -9
- meerk40t/lihuiyu/device.py +24 -12
- meerk40t/main.py +14 -9
- meerk40t/moshi/device.py +20 -6
- meerk40t/network/console_server.py +22 -6
- meerk40t/newly/device.py +10 -3
- meerk40t/newly/gui/gui.py +10 -0
- meerk40t/ruida/device.py +22 -2
- meerk40t/ruida/loader.py +9 -4
- meerk40t/ruida/rdjob.py +48 -8
- meerk40t/tools/geomstr.py +240 -123
- meerk40t/tools/rasterplotter.py +185 -94
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/RECORD +85 -84
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/top_level.txt +0 -0
- {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 = (
|
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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
fp2
|
204
|
-
lp2
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
#
|
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(
|
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(
|
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
|
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
|
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
|
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
|
-
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
3584
|
-
i - 1
|
3585
|
-
|
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
|
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
|
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) * (
|
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(
|
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 =
|
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(
|
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
|
|