@@ -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