flkit 0.1.0__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.
- flkit/__init__.py +2 -0
- flkit/alg/__init__.py +68 -0
- flkit/alg/build_idx.py +9 -0
- flkit/alg/c_track.py +88 -0
- flkit/alg/collierydb.py +1217 -0
- flkit/alg/del2.py +421 -0
- flkit/alg/delaunay_triangulation.py +476 -0
- flkit/alg/finite_plane_distance.py +239 -0
- flkit/alg/get_up_low.py +32 -0
- flkit/alg/in_tin.py +139 -0
- flkit/alg/index.py +9 -0
- flkit/alg/inter/BasePredictor.py +69 -0
- flkit/alg/inter/Kriging.py +226 -0
- flkit/alg/inter/KrigingM.py +262 -0
- flkit/alg/inter/LossFuncs.py +164 -0
- flkit/alg/inter/__init__.py +178 -0
- flkit/alg/inter/idw.py +253 -0
- flkit/alg/inter/midw.py +145 -0
- flkit/alg/inter/nearest_neighbor.py +445 -0
- flkit/alg/inter/rbf.py +345 -0
- flkit/alg/inter/trilinear.py +572 -0
- flkit/alg/point_in_polygon.py +126 -0
- flkit/alg/point_in_volume.py +188 -0
- flkit/alg/point_in_volume_rs.pyi +7 -0
- flkit/alg/point_to_triangle.py +116 -0
- flkit/alg/point_to_triangle_mesh.py +309 -0
- flkit/alg/point_to_triangle_rs.pyi +9 -0
- flkit/alg/rotated_rect.py +184 -0
- flkit/db/__init__.py +17 -0
- flkit/db/db_model.py +724 -0
- flkit/db/duck_model.py +151 -0
- flkit/db/fileobj.py +72 -0
- flkit/db/path_utl.py +174 -0
- flkit/mtcli/__init__.py +18 -0
- flkit/mtcli/core.py +489 -0
- flkit/mtcli/parse.py +218 -0
- flkit/py.typed +0 -0
- flkit/tools/__init__.py +28 -0
- flkit/tools/del_key.py +13 -0
- flkit/tools/dict_to_list.py +10 -0
- flkit/tools/get_region.py +42 -0
- flkit/tools/max_key.py +7 -0
- flkit/tools/mongo.py +119 -0
- flkit/tools/mt_dash.py +121 -0
- flkit/tools/prase_type.py +27 -0
- flkit/typr/Res.py +21 -0
- flkit/typr/__init__.py +107 -0
- flkit/typr/c_grid.py +425 -0
- flkit/typr/console.py +283 -0
- flkit/typr/engine.py +59 -0
- flkit/typr/getenv.py +89 -0
- flkit/typr/result.py +81 -0
- flkit/typr/table.py +34 -0
- flkit/typr/utils.py +542 -0
- flkit/typr/verify.py +64 -0
- flkit/typr/webview.py +219 -0
- flkit/typr/wstask.py +344 -0
- flkit-0.1.0.dist-info/METADATA +186 -0
- flkit-0.1.0.dist-info/RECORD +60 -0
- flkit-0.1.0.dist-info/WHEEL +4 -0
flkit/alg/del2.py
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
import numpy as np
|
|
3
|
+
import triangle as tr
|
|
4
|
+
from scipy.spatial import ConvexHull
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def convex_hull_boundary(points):
|
|
8
|
+
"""
|
|
9
|
+
使用凸包算法计算点云边界点
|
|
10
|
+
适用于寻找最外层的凸边界
|
|
11
|
+
"""
|
|
12
|
+
points = np.asarray(points)
|
|
13
|
+
hull = ConvexHull(points[:, :2])
|
|
14
|
+
boundary_points = points[hull.vertices]
|
|
15
|
+
return boundary_points.tolist()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def interpolate_delaunary_z(xy, triangle):
|
|
19
|
+
"""
|
|
20
|
+
计算 Delaunary 三角形内一点xy的 z 坐标。
|
|
21
|
+
|
|
22
|
+
参数:
|
|
23
|
+
triangle: 长度为3的列表或元组,表示三角形三个顶点的 (x, y, z)
|
|
24
|
+
xy: 长度为2的列表或元组,表示待求点的 (x, y)
|
|
25
|
+
返回:
|
|
26
|
+
z: 插值得到的 z 坐标
|
|
27
|
+
"""
|
|
28
|
+
x1, y1, z1 = triangle[0]
|
|
29
|
+
x2, y2, z2 = triangle[1]
|
|
30
|
+
x3, y3, z3 = triangle[2]
|
|
31
|
+
x, y = xy[:2]
|
|
32
|
+
|
|
33
|
+
# 计算两个边向量
|
|
34
|
+
ux, uy, uz = x2 - x1, y2 - y1, z2 - z1
|
|
35
|
+
vx, vy, vz = x3 - x1, y3 - y1, z3 - z1
|
|
36
|
+
|
|
37
|
+
# 计算法向量(叉积)
|
|
38
|
+
nx = uy * vz - uz * vy
|
|
39
|
+
ny = uz * vx - ux * vz
|
|
40
|
+
nz = ux * vy - uy * vx
|
|
41
|
+
|
|
42
|
+
# 检查三角形是否退化
|
|
43
|
+
if nx == 0 and ny == 0 and nz == 0:
|
|
44
|
+
raise ValueError("三点共线,无法构成三角形")
|
|
45
|
+
|
|
46
|
+
# 平面方程: nx*(X-x1) + ny*(Y-y1) + nz*(Z-z1) = 0
|
|
47
|
+
# 解出 Z
|
|
48
|
+
if nz == 0:
|
|
49
|
+
raise ValueError("三角形垂直于 xy 平面,无法由 xy 唯一确定 z")
|
|
50
|
+
z = z1 - (nx * (x - x1) + ny * (y - y1)) / nz
|
|
51
|
+
return z
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def bound_triangulation(points: List[List[float]]):
|
|
55
|
+
"""
|
|
56
|
+
构建边界三角网:采用扇形三角剖分(0, i, i+1)。
|
|
57
|
+
假设边界为凸多边形;若为非凸,多边形需另行剖分。
|
|
58
|
+
"""
|
|
59
|
+
n = len(points)
|
|
60
|
+
if n < 3:
|
|
61
|
+
return []
|
|
62
|
+
triangles = []
|
|
63
|
+
for i in range(1, n - 1):
|
|
64
|
+
triangles.append([0, i, i + 1])
|
|
65
|
+
return triangles
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def point_in_polygon(point, polygon, epsilon=1e-12):
|
|
69
|
+
"""
|
|
70
|
+
简洁的射线法(奇偶规则),返回:
|
|
71
|
+
1 -> 内部
|
|
72
|
+
0 -> 边缘
|
|
73
|
+
-1 -> 外部
|
|
74
|
+
|
|
75
|
+
:param point: 点坐标
|
|
76
|
+
:param polygon: 多边形顶点坐标列表
|
|
77
|
+
:param epsilon: 浮点数精度
|
|
78
|
+
:return: 1/0/-1, (边索引1, 边索引2)
|
|
79
|
+
"""
|
|
80
|
+
x, y = point[:2]
|
|
81
|
+
n = len(polygon)
|
|
82
|
+
|
|
83
|
+
# 先判断是否在边界上
|
|
84
|
+
for i in range(n):
|
|
85
|
+
x1, y1 = polygon[i][:2]
|
|
86
|
+
x2, y2 = polygon[(i + 1) % n][:2]
|
|
87
|
+
|
|
88
|
+
cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1)
|
|
89
|
+
if (
|
|
90
|
+
abs(cross) < epsilon
|
|
91
|
+
and min(x1, x2) - epsilon <= x <= max(x1, x2) + epsilon
|
|
92
|
+
and min(y1, y2) - epsilon <= y <= max(y1, y2) + epsilon
|
|
93
|
+
):
|
|
94
|
+
# 返回该点所在的边索引
|
|
95
|
+
return 0, (i, (i + 1) % n)
|
|
96
|
+
|
|
97
|
+
# 射线法判断内外
|
|
98
|
+
inside = False
|
|
99
|
+
for i in range(n):
|
|
100
|
+
x1, y1 = polygon[i][:2]
|
|
101
|
+
x2, y2 = polygon[(i + 1) % n][:2]
|
|
102
|
+
if (y1 > y) != (y2 > y):
|
|
103
|
+
x_intersect = (x2 - x1) * (y - y1) / (y2 - y1) + x1
|
|
104
|
+
if x_intersect > x:
|
|
105
|
+
inside = not inside
|
|
106
|
+
|
|
107
|
+
return (1, (-1, -1)) if inside else (-1, (-1, -1))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class DelaunayTriangulation:
|
|
111
|
+
"""
|
|
112
|
+
Delaunay三角剖分类
|
|
113
|
+
|
|
114
|
+
>>> dt = DelaunayTriangulation([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]])
|
|
115
|
+
>>> dt.add_point([0.5, 0.5])
|
|
116
|
+
True
|
|
117
|
+
>>> dt.triangles
|
|
118
|
+
[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [1, 2, 5], [2, 3, 5], [3, 4, 5]]
|
|
119
|
+
>>> dt.add_point([0.25, 0.25])
|
|
120
|
+
True
|
|
121
|
+
>>> dt.triangles
|
|
122
|
+
[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [1, 2, 5], [2, 3, 5], [3, 4, 5]]
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __init__(self, bound: List[List[float]]):
|
|
126
|
+
"""
|
|
127
|
+
初始化Delaunay三角网
|
|
128
|
+
|
|
129
|
+
:param bound: 边界多边形顶点坐标列表
|
|
130
|
+
:param points: 内部点坐标列表
|
|
131
|
+
|
|
132
|
+
>>> dt = DelaunayTriangulation([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]])
|
|
133
|
+
>>> dt.add_point([0.5, 0.5])
|
|
134
|
+
True
|
|
135
|
+
"""
|
|
136
|
+
self.bound = bound
|
|
137
|
+
self.points = list(bound)
|
|
138
|
+
self.triangles = bound_triangulation(bound)
|
|
139
|
+
|
|
140
|
+
def add_point(self, point: List[float]):
|
|
141
|
+
"""
|
|
142
|
+
添加内部点
|
|
143
|
+
|
|
144
|
+
:param point: 内部点坐标
|
|
145
|
+
"""
|
|
146
|
+
# 先判断点是否在边界上
|
|
147
|
+
sign, _ = point_in_polygon(point, self.bound)
|
|
148
|
+
if sign == -1:
|
|
149
|
+
return False
|
|
150
|
+
# 检查点是否已存在
|
|
151
|
+
if point in self.points:
|
|
152
|
+
return False
|
|
153
|
+
self.points.append(point)
|
|
154
|
+
n_p_idx = len(self.points) - 1
|
|
155
|
+
# 查找包含点的三角形
|
|
156
|
+
# 如果点在边界上则将三角形拆分为2个三角形
|
|
157
|
+
for _, triangle in enumerate(self.triangles):
|
|
158
|
+
sign, edge = point_in_polygon(point, [self.points[idx] for idx in triangle])
|
|
159
|
+
if sign == 1:
|
|
160
|
+
# 点在内部,删除包含点的三角形,将其拆分为3个三角形
|
|
161
|
+
self.triangles.remove(triangle)
|
|
162
|
+
self.triangles.append([triangle[0], triangle[1], n_p_idx])
|
|
163
|
+
self.triangles.append([triangle[1], triangle[2], n_p_idx])
|
|
164
|
+
self.triangles.append([triangle[2], triangle[0], n_p_idx])
|
|
165
|
+
return True
|
|
166
|
+
elif sign == 0:
|
|
167
|
+
# 点在边界上,删除包含点的三角形,将其拆分为2个三角形
|
|
168
|
+
# 找到点所在的线的位置
|
|
169
|
+
self.triangles.remove(triangle)
|
|
170
|
+
if edge == (0, 1):
|
|
171
|
+
# 在0-1之间,则拆分为1-2-n_p_idx;2-0-n_p_idx;
|
|
172
|
+
self.triangles.append([triangle[1], triangle[2], n_p_idx])
|
|
173
|
+
self.triangles.append([triangle[2], triangle[0], n_p_idx])
|
|
174
|
+
elif edge == (1, 2):
|
|
175
|
+
# 在1-2之间,则拆分为0-1-n_p_idx;2-0-n_p_idx;
|
|
176
|
+
self.triangles.append([triangle[0], triangle[1], n_p_idx])
|
|
177
|
+
self.triangles.append([triangle[2], triangle[0], n_p_idx])
|
|
178
|
+
elif edge == (2, 0):
|
|
179
|
+
# 在2-0之间,则拆分为0-1-n_p_idx;1-2-n_p_idx;
|
|
180
|
+
self.triangles.append([triangle[0], triangle[1], n_p_idx])
|
|
181
|
+
self.triangles.append([triangle[1], triangle[2], n_p_idx])
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
def add_points(self, points: List[List[float]]):
|
|
185
|
+
"""
|
|
186
|
+
添加内部点
|
|
187
|
+
|
|
188
|
+
:param points: 内部点坐标列表
|
|
189
|
+
"""
|
|
190
|
+
for point in points:
|
|
191
|
+
self.add_point(point)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class OptimizedDelaunayTriangulation(DelaunayTriangulation):
|
|
195
|
+
"""
|
|
196
|
+
优化版 Delaunay 三角剖分类(继承自 DelaunayTriangulation)
|
|
197
|
+
|
|
198
|
+
在添加新点后,进行局部的边翻转优化,尽量减少钝角三角形。
|
|
199
|
+
优化策略基于“最大角度下降”原则:若翻转共享边后,两个相邻三角形的最大角度减小,则执行翻转。
|
|
200
|
+
|
|
201
|
+
用法示例:
|
|
202
|
+
>>> dt = OptimizedDelaunayTriangulation([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]])
|
|
203
|
+
>>> dt.add_point([0.5, 0.5])
|
|
204
|
+
True
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
def add_point(self, point: List[float]):
|
|
208
|
+
"""
|
|
209
|
+
添加内部点,并在添加后对新点邻域进行局部优化,减少钝角三角形。
|
|
210
|
+
"""
|
|
211
|
+
inserted = super().add_point(point)
|
|
212
|
+
if not inserted:
|
|
213
|
+
return False
|
|
214
|
+
n_p_idx = len(self.points) - 1
|
|
215
|
+
self._optimize_around_point(n_p_idx)
|
|
216
|
+
return True
|
|
217
|
+
|
|
218
|
+
# ---------- 以下为局部优化所需的辅助函数 ----------
|
|
219
|
+
def _xy(self, idx):
|
|
220
|
+
p = self.points[idx]
|
|
221
|
+
return float(p[0]), float(p[1])
|
|
222
|
+
|
|
223
|
+
def _angle_at(self, a_idx, b_idx, c_idx, eps=1e-12):
|
|
224
|
+
import math
|
|
225
|
+
|
|
226
|
+
ax, ay = self._xy(a_idx)
|
|
227
|
+
bx, by = self._xy(b_idx)
|
|
228
|
+
cx, cy = self._xy(c_idx)
|
|
229
|
+
v1x, v1y = ax - bx, ay - by
|
|
230
|
+
v2x, v2y = cx - bx, cy - by
|
|
231
|
+
n1 = (v1x * v1x + v1y * v1y) ** 0.5
|
|
232
|
+
n2 = (v2x * v2x + v2y * v2y) ** 0.5
|
|
233
|
+
if n1 < eps or n2 < eps:
|
|
234
|
+
return 0.0
|
|
235
|
+
cos_val = (v1x * v2x + v1y * v2y) / (n1 * n2)
|
|
236
|
+
if cos_val < -1.0:
|
|
237
|
+
cos_val = -1.0
|
|
238
|
+
elif cos_val > 1.0:
|
|
239
|
+
cos_val = 1.0
|
|
240
|
+
return math.degrees(math.acos(cos_val))
|
|
241
|
+
|
|
242
|
+
def _max_angle(self, tri):
|
|
243
|
+
a, b, c = tri
|
|
244
|
+
return max(
|
|
245
|
+
self._angle_at(b, a, c),
|
|
246
|
+
self._angle_at(a, b, c),
|
|
247
|
+
self._angle_at(a, c, b),
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def _find_adjacent_triangle_edge(self, i, j, exclude_triangle):
|
|
251
|
+
# 查找共享边(i, j)的另一侧三角形
|
|
252
|
+
for t in self.triangles:
|
|
253
|
+
if t is exclude_triangle:
|
|
254
|
+
continue
|
|
255
|
+
if i in t and j in t:
|
|
256
|
+
return t
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
def _degenerate(self, tri, eps=1e-12):
|
|
260
|
+
ax, ay = self._xy(tri[0])
|
|
261
|
+
bx, by = self._xy(tri[1])
|
|
262
|
+
cx, cy = self._xy(tri[2])
|
|
263
|
+
area2 = abs((bx - ax) * (cy - ay) - (by - ay) * (cx - ax))
|
|
264
|
+
return area2 < eps
|
|
265
|
+
|
|
266
|
+
def _should_flip_by_angle(self, p, a, b, c, margin=1e-9):
|
|
267
|
+
# 翻转判据:翻转后两个三角形的“最大角”更小
|
|
268
|
+
old1 = [p, a, b]
|
|
269
|
+
old2 = [p, a, c]
|
|
270
|
+
new1 = [p, b, c]
|
|
271
|
+
new2 = [a, b, c]
|
|
272
|
+
# 避免翻转后出现退化三角形
|
|
273
|
+
if self._degenerate(new1) or self._degenerate(new2):
|
|
274
|
+
return False
|
|
275
|
+
old_max = max(self._max_angle(old1), self._max_angle(old2))
|
|
276
|
+
new_max = max(self._max_angle(new1), self._max_angle(new2))
|
|
277
|
+
return new_max + margin < old_max
|
|
278
|
+
|
|
279
|
+
def _optimize_around_point(self, p_idx, max_iterations=100):
|
|
280
|
+
"""
|
|
281
|
+
以新插入点为中心,对其相邻边执行局部边翻转,
|
|
282
|
+
若翻转能降低相邻两三角形的最大角度,则执行翻转。
|
|
283
|
+
"""
|
|
284
|
+
iterations = 0
|
|
285
|
+
changed = True
|
|
286
|
+
while changed and iterations < max_iterations:
|
|
287
|
+
iterations += 1
|
|
288
|
+
changed = False
|
|
289
|
+
triangles_with_p = [t for t in self.triangles if p_idx in t]
|
|
290
|
+
for t1 in triangles_with_p:
|
|
291
|
+
# t1 = [p_idx, a, b]
|
|
292
|
+
ab = [v for v in t1 if v != p_idx]
|
|
293
|
+
if len(ab) != 2:
|
|
294
|
+
continue
|
|
295
|
+
for a in ab:
|
|
296
|
+
b = ab[0] if a == ab[1] else ab[1]
|
|
297
|
+
neighbor = self._find_adjacent_triangle_edge(
|
|
298
|
+
p_idx, a, exclude_triangle=t1
|
|
299
|
+
)
|
|
300
|
+
if neighbor is None:
|
|
301
|
+
continue
|
|
302
|
+
# neighbor = [p_idx, a, c]
|
|
303
|
+
c_candidates = [v for v in neighbor if v != p_idx and v != a]
|
|
304
|
+
if len(c_candidates) != 1:
|
|
305
|
+
continue
|
|
306
|
+
c = c_candidates[0]
|
|
307
|
+
if self._should_flip_by_angle(p_idx, a, b, c):
|
|
308
|
+
# 执行边翻转:移除旧三角形,加入新三角形
|
|
309
|
+
try:
|
|
310
|
+
self.triangles.remove(t1)
|
|
311
|
+
except ValueError:
|
|
312
|
+
pass
|
|
313
|
+
try:
|
|
314
|
+
self.triangles.remove(neighbor)
|
|
315
|
+
except ValueError:
|
|
316
|
+
pass
|
|
317
|
+
self.triangles.append([p_idx, b, c])
|
|
318
|
+
self.triangles.append([a, b, c])
|
|
319
|
+
changed = True
|
|
320
|
+
break
|
|
321
|
+
if changed:
|
|
322
|
+
break
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class TriangleLibTriangulation:
|
|
326
|
+
"""
|
|
327
|
+
基于Triangle库的Delaunay三角剖分。
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
def __init__(self, bound):
|
|
331
|
+
"""
|
|
332
|
+
初始化三角网。
|
|
333
|
+
|
|
334
|
+
参数:
|
|
335
|
+
boundary: 边界点列表,格式为[(x1,y1), (x2,y2), ...]
|
|
336
|
+
"""
|
|
337
|
+
self.bound = np.asarray(bound).tolist()
|
|
338
|
+
|
|
339
|
+
def add_points(self, points):
|
|
340
|
+
"""
|
|
341
|
+
添加内部点云数据。
|
|
342
|
+
|
|
343
|
+
参数:
|
|
344
|
+
points: 内部点云数据列表 [[x1,y1], [x2,y2], ...]
|
|
345
|
+
"""
|
|
346
|
+
boundary_arr = np.array(self.bound)
|
|
347
|
+
points_arr = np.array(points)
|
|
348
|
+
|
|
349
|
+
boundary_xy = set((float(b[0]), float(b[1])) for b in boundary_arr)
|
|
350
|
+
filtered_points = [
|
|
351
|
+
p.tolist() if isinstance(p, np.ndarray) else list(p)
|
|
352
|
+
for p in points_arr
|
|
353
|
+
if (float(p[0]), float(p[1])) not in boundary_xy
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
boundary_2d = boundary_arr[:, :2]
|
|
357
|
+
cloud_2d = (
|
|
358
|
+
np.array(filtered_points, dtype=float)[:, :2]
|
|
359
|
+
if len(filtered_points) > 0
|
|
360
|
+
else np.empty((0, 2), dtype=float)
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
result = self.point_cloud_triangulation(boundary_2d, cloud_2d)
|
|
364
|
+
|
|
365
|
+
vertices = result.get("vertices")
|
|
366
|
+
triangles = result.get("triangles", [])
|
|
367
|
+
if vertices is None:
|
|
368
|
+
vertices = np.vstack([boundary_2d, cloud_2d])
|
|
369
|
+
self.points = vertices.tolist()
|
|
370
|
+
self.triangles = (
|
|
371
|
+
triangles.tolist() if isinstance(triangles, np.ndarray) else triangles
|
|
372
|
+
)
|
|
373
|
+
return result
|
|
374
|
+
|
|
375
|
+
def point_cloud_triangulation(
|
|
376
|
+
self, boundary_points, cloud_points, quality_params="pq20a0.001"
|
|
377
|
+
):
|
|
378
|
+
"""
|
|
379
|
+
对点云数据生成带边界约束的三角网
|
|
380
|
+
|
|
381
|
+
参数:
|
|
382
|
+
boundary_points: 边界点列表 [[x1,y1], [x2,y2], ...]
|
|
383
|
+
cloud_points: 内部点云数据列表
|
|
384
|
+
quality_params: 三角化质量参数
|
|
385
|
+
"""
|
|
386
|
+
# 合并所有点
|
|
387
|
+
boundary_points = np.array(boundary_points, dtype=float)
|
|
388
|
+
cloud_points = np.array(cloud_points, dtype=float)
|
|
389
|
+
if cloud_points.size == 0:
|
|
390
|
+
all_points = boundary_points
|
|
391
|
+
else:
|
|
392
|
+
all_points = np.vstack([boundary_points, cloud_points])
|
|
393
|
+
n_boundary = len(boundary_points)
|
|
394
|
+
|
|
395
|
+
# 创建边界段(连接边界点)
|
|
396
|
+
segments = []
|
|
397
|
+
for i in range(n_boundary):
|
|
398
|
+
segments.append([i, (i + 1) % n_boundary]) # 确保边界闭合
|
|
399
|
+
|
|
400
|
+
# 准备输入数据
|
|
401
|
+
# Normalize coordinates to improve numerical stability
|
|
402
|
+
bbox_min = np.min(all_points, axis=0)
|
|
403
|
+
bbox_max = np.max(all_points, axis=0)
|
|
404
|
+
center = (bbox_min + bbox_max) / 2.0
|
|
405
|
+
scale = max(bbox_max[0] - bbox_min[0], bbox_max[1] - bbox_min[1])
|
|
406
|
+
if scale == 0:
|
|
407
|
+
scale = 1.0
|
|
408
|
+
norm_points = (all_points - center) / scale
|
|
409
|
+
mesh_data = {"vertices": norm_points, "segments": segments}
|
|
410
|
+
# 执行三角化(带约束和质量控制)
|
|
411
|
+
# 组合开关:p(约束) + 质量参数 + D(Delaunay)
|
|
412
|
+
switches = "pD"
|
|
413
|
+
result = tr.triangulate(mesh_data, switches)
|
|
414
|
+
|
|
415
|
+
# De-normalize vertices back to original coordinate scale
|
|
416
|
+
verts = result.get("vertices")
|
|
417
|
+
if verts is not None:
|
|
418
|
+
verts = np.asarray(verts, dtype=float)
|
|
419
|
+
result["vertices"] = verts * scale + center
|
|
420
|
+
|
|
421
|
+
return result
|