Skip to content

Commit d090b84

Browse files
committed
refactor(sizing_field): 移动点到线段距离计算到类内并重构
将原geom_utils中的point_to_segment_distance_3d函数迁移到SurfaceSizingField类内作为静态方法,同时在AdaptiveSizingField中添加同名实例方法,移除geom_utils中的冗余工具函数并重构相交检测逻辑
1 parent 8163a36 commit d090b84

3 files changed

Lines changed: 260 additions & 1466 deletions

File tree

sfmesh/geom_utils.py

Lines changed: 9 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -861,9 +861,15 @@ def check_triangle_vs_existing(
861861
if segment_segment_distance_3d(a1, a2, b1, b2) < tolerance:
862862
return True
863863

864-
# 注意:在曲面上,共享一个顶点的三角形的非共享边可能在3D空间
865-
# 穿过对方三角形的"内部"(因为曲面弯曲),这是合法的。
866-
# 因此跳过 edge_intersects_triangle_core 检查。
864+
# 新三角形的非共享边 vs 现有三角形内部
865+
for a1, a2 in edges_new:
866+
if _edge_intersects_triangle_core(a1, a2, q0, q1, q2):
867+
return True
868+
869+
# 现有三角形的非共享边 vs 新三角形内部
870+
for b1, b2 in edges_ex:
871+
if _edge_intersects_triangle_core(b1, b2, p0, p1, p2):
872+
return True
867873

868874
return False
869875

@@ -873,203 +879,6 @@ def check_triangle_vs_existing(
873879
)
874880

875881

876-
# ============================================================================
877-
# 三角形辅助工具
878-
# ============================================================================
879-
880-
def triangle_edges(p0: np.ndarray, p1: np.ndarray, p2: np.ndarray):
881-
"""
882-
返回三角形的三条边(有序对)。
883-
884-
Args:
885-
p0, p1, p2: 三角形顶点坐标
886-
887-
Returns:
888-
[(p0, p1), (p1, p2), (p2, p0)]
889-
"""
890-
return [(p0, p1), (p1, p2), (p2, p0)]
891-
892-
893-
def triangle_min_angle_from_coords(p0: np.ndarray, p1: np.ndarray, p2: np.ndarray) -> float:
894-
"""
895-
计算三角形最小内角(度)。
896-
897-
Args:
898-
p0, p1, p2: 三角形顶点坐标
899-
900-
Returns:
901-
最小内角(度),退化三角形返回 0.0
902-
"""
903-
angles = []
904-
for apex, a, b in [(p0, p1, p2), (p1, p0, p2), (p2, p0, p1)]:
905-
va = a - apex
906-
vb = b - apex
907-
la = np.linalg.norm(va)
908-
lb = np.linalg.norm(vb)
909-
if la < 1e-15 or lb < 1e-15:
910-
return 0.0
911-
cos_a = np.clip(np.dot(va, vb) / (la * lb), -1.0, 1.0)
912-
angles.append(np.degrees(np.arccos(cos_a)))
913-
return min(angles)
914-
915-
916-
def triangle_max_angle_from_coords(p0: np.ndarray, p1: np.ndarray, p2: np.ndarray) -> float:
917-
"""
918-
计算三角形最大内角(度)。
919-
920-
Args:
921-
p0, p1, p2: 三角形顶点坐标
922-
923-
Returns:
924-
最大内角(度),退化三角形返回 0.0
925-
"""
926-
angles = []
927-
for apex, a, b in [(p0, p1, p2), (p1, p0, p2), (p2, p0, p1)]:
928-
va = a - apex
929-
vb = b - apex
930-
la = np.linalg.norm(va)
931-
lb = np.linalg.norm(vb)
932-
if la < 1e-15 or lb < 1e-15:
933-
return 0.0
934-
cos_a = np.clip(np.dot(va, vb) / (la * lb), -1.0, 1.0)
935-
angles.append(np.degrees(np.arccos(cos_a)))
936-
return max(angles)
937-
938-
939-
def point_to_segment_distance_3d(
940-
point: np.ndarray,
941-
seg_start: np.ndarray,
942-
seg_end: np.ndarray,
943-
) -> float:
944-
"""
945-
计算 3D 点到线段的最短距离。
946-
947-
Args:
948-
point: (3,) 查询点
949-
seg_start: (3,) 线段起点
950-
seg_end: (3,) 线段终点
951-
952-
Returns:
953-
最短距离
954-
"""
955-
d = seg_end - seg_start
956-
seg_len_sq = np.dot(d, d)
957-
if seg_len_sq < 1e-30:
958-
return np.linalg.norm(point - seg_start)
959-
t = np.clip(np.dot(point - seg_start, d) / seg_len_sq, 0.0, 1.0)
960-
closest = seg_start + t * d
961-
return np.linalg.norm(point - closest)
962-
963-
964-
def check_min_edge_distance(
965-
new_coords: np.ndarray,
966-
existing_coords: np.ndarray,
967-
min_dist: float,
968-
) -> bool:
969-
"""
970-
检查两个三角形的边-边最小距离是否小于阈值。
971-
972-
Args:
973-
new_coords: 新三角形顶点坐标 (3, 3)
974-
existing_coords: 已有三角形顶点坐标 (3, 3)
975-
min_dist: 最小允许距离
976-
977-
Returns:
978-
True 表示距离过小(应拒绝)
979-
"""
980-
edges_new = triangle_edges(new_coords[0], new_coords[1], new_coords[2])
981-
edges_ex = triangle_edges(existing_coords[0], existing_coords[1], existing_coords[2])
982-
for a1, a2 in edges_new:
983-
for b1, b2 in edges_ex:
984-
if segment_segment_distance_3d(a1, a2, b1, b2) < min_dist:
985-
return True
986-
return False
987-
988-
989-
def uv_out_of_bounds(
990-
uv: Tuple[float, float],
991-
bounds: Tuple[float, float, float, float],
992-
margin: float = 0.1,
993-
) -> bool:
994-
"""
995-
检查 UV 参数坐标是否超出面的参数域。
996-
997-
Args:
998-
uv: (u, v) 参数坐标
999-
bounds: (u_min, u_max, v_min, v_max) 参数域
1000-
margin: 边界余量比例
1001-
1002-
Returns:
1003-
True 表示超出参数域
1004-
"""
1005-
u_min, u_max, v_min, v_max = bounds
1006-
u_range = u_max - u_min
1007-
v_range = v_max - v_min
1008-
u_margin = max(u_range * margin, 1e-6)
1009-
v_margin = max(v_range * margin, 1e-6)
1010-
u, v = uv
1011-
return (u < u_min - u_margin or u > u_max + u_margin or
1012-
v < v_min - v_margin or v > v_max + v_margin)
1013-
1014-
1015-
def get_most_visible_plane(normal: np.ndarray) -> Tuple[int, int]:
1016-
"""
1017-
根据法向量选择最可见的投影平面(返回要保留的两个轴索引)。
1018-
1019-
丢弃法向量绝对值最大的分量,保留其余两个分量。
1020-
1021-
Args:
1022-
normal: (3,) 法向量
1023-
1024-
Returns:
1025-
(axis_i, axis_j) 要保留的两个轴索引
1026-
"""
1027-
abs_n = np.abs(normal)
1028-
drop = np.argmax(abs_n)
1029-
axes = [0, 1, 2]
1030-
axes.pop(drop)
1031-
return tuple(axes)
1032-
1033-
1034-
def segments_cross_strict_2d(
1035-
p1: Tuple[float, float],
1036-
p2: Tuple[float, float],
1037-
q1: Tuple[float, float],
1038-
q2: Tuple[float, float],
1039-
eps: float = 1e-8,
1040-
) -> bool:
1041-
"""
1042-
严格 2D 线段相交检测(排除端点接触和 T 型相交)。
1043-
1044-
使用叉积符号测试,要求交点参数在 (eps, 1-eps) 范围内。
1045-
1046-
Args:
1047-
p1, p2: 第一条线段端点
1048-
q1, q2: 第二条线段端点
1049-
eps: 端点排除容差
1050-
1051-
Returns:
1052-
True 表示两线段严格相交
1053-
"""
1054-
p1 = np.array(p1, dtype=float)
1055-
p2 = np.array(p2, dtype=float)
1056-
q1 = np.array(q1, dtype=float)
1057-
q2 = np.array(q2, dtype=float)
1058-
1059-
d1 = p2 - p1
1060-
d2 = q2 - q1
1061-
d1_cross_d2 = d1[0] * d2[1] - d1[1] * d2[0]
1062-
1063-
if abs(d1_cross_d2) < 1e-12:
1064-
return False # 平行或共线
1065-
1066-
dq = q1 - p1
1067-
t = (dq[0] * d2[1] - dq[1] * d2[0]) / d1_cross_d2
1068-
u = (dq[0] * d1[1] - dq[1] * d1[0]) / d1_cross_d2
1069-
1070-
return eps < t < 1.0 - eps and eps < u < 1.0 - eps
1071-
1072-
1073882
# ============================================================================
1074883
# 网格拓扑分析工具
1075884
# ============================================================================

0 commit comments

Comments
 (0)