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.
Files changed (60) hide show
  1. flkit/__init__.py +2 -0
  2. flkit/alg/__init__.py +68 -0
  3. flkit/alg/build_idx.py +9 -0
  4. flkit/alg/c_track.py +88 -0
  5. flkit/alg/collierydb.py +1217 -0
  6. flkit/alg/del2.py +421 -0
  7. flkit/alg/delaunay_triangulation.py +476 -0
  8. flkit/alg/finite_plane_distance.py +239 -0
  9. flkit/alg/get_up_low.py +32 -0
  10. flkit/alg/in_tin.py +139 -0
  11. flkit/alg/index.py +9 -0
  12. flkit/alg/inter/BasePredictor.py +69 -0
  13. flkit/alg/inter/Kriging.py +226 -0
  14. flkit/alg/inter/KrigingM.py +262 -0
  15. flkit/alg/inter/LossFuncs.py +164 -0
  16. flkit/alg/inter/__init__.py +178 -0
  17. flkit/alg/inter/idw.py +253 -0
  18. flkit/alg/inter/midw.py +145 -0
  19. flkit/alg/inter/nearest_neighbor.py +445 -0
  20. flkit/alg/inter/rbf.py +345 -0
  21. flkit/alg/inter/trilinear.py +572 -0
  22. flkit/alg/point_in_polygon.py +126 -0
  23. flkit/alg/point_in_volume.py +188 -0
  24. flkit/alg/point_in_volume_rs.pyi +7 -0
  25. flkit/alg/point_to_triangle.py +116 -0
  26. flkit/alg/point_to_triangle_mesh.py +309 -0
  27. flkit/alg/point_to_triangle_rs.pyi +9 -0
  28. flkit/alg/rotated_rect.py +184 -0
  29. flkit/db/__init__.py +17 -0
  30. flkit/db/db_model.py +724 -0
  31. flkit/db/duck_model.py +151 -0
  32. flkit/db/fileobj.py +72 -0
  33. flkit/db/path_utl.py +174 -0
  34. flkit/mtcli/__init__.py +18 -0
  35. flkit/mtcli/core.py +489 -0
  36. flkit/mtcli/parse.py +218 -0
  37. flkit/py.typed +0 -0
  38. flkit/tools/__init__.py +28 -0
  39. flkit/tools/del_key.py +13 -0
  40. flkit/tools/dict_to_list.py +10 -0
  41. flkit/tools/get_region.py +42 -0
  42. flkit/tools/max_key.py +7 -0
  43. flkit/tools/mongo.py +119 -0
  44. flkit/tools/mt_dash.py +121 -0
  45. flkit/tools/prase_type.py +27 -0
  46. flkit/typr/Res.py +21 -0
  47. flkit/typr/__init__.py +107 -0
  48. flkit/typr/c_grid.py +425 -0
  49. flkit/typr/console.py +283 -0
  50. flkit/typr/engine.py +59 -0
  51. flkit/typr/getenv.py +89 -0
  52. flkit/typr/result.py +81 -0
  53. flkit/typr/table.py +34 -0
  54. flkit/typr/utils.py +542 -0
  55. flkit/typr/verify.py +64 -0
  56. flkit/typr/webview.py +219 -0
  57. flkit/typr/wstask.py +344 -0
  58. flkit-0.1.0.dist-info/METADATA +186 -0
  59. flkit-0.1.0.dist-info/RECORD +60 -0
  60. 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