如何判断两条2D线段是否相交?附旧实现脚本参考
线段相交判断与多边形穿侧问题解决
我之前见过类似问题,但看不懂别人的解答。现在有四个Vector2点:P1和P2组成线段1,P3和P4组成线段2。我已经实现了计算交点坐标的代码,但不知道怎么判断这两条线段是否相交;更具体地说,我需要判断一条假想线穿过多边形的哪一侧。之前在旧测试脚本里实现过类似功能,但没加注释,现在没法适配当前需求,下面是旧脚本代码:
旧脚本代码1:角度区间判断
if rotation_angle > PI/2 && rotation_angle < 3*PI/2: if rad_overflow(($Position2D.position-position).angle()-PI/2) < rad_overflow(rotation_angle-PI/2) or rad_overflow(($Position2D.position-position).angle()-PI/2) > rad_overflow(rotation_angle+PI/2): actives.x = 1 else: actives.x = 0 if rad_overflow(($Position2D2.position-position).angle()-PI/2) < rad_overflow(rotation_angle-PI/2) or rad_overflow(($Position2D2.position-position).angle()-PI/2) > rad_overflow(rotation_angle+PI/2): actives.y = 1 else: actives.y = 0 else: if rad_overflow(($Position2D.position-position).angle()-PI/2) < rad_overflow(rotation_angle-PI/2) && rad_overflow(($Position2D.position-position).angle()-PI/2) > rad_overflow(rotation_angle+PI/2): actives.x = 1 else: actives.x = 0 if rad_overflow(($Position2D2.position-position).angle()-PI/2) < rad_overflow(rotation_angle-PI/2) && rad_overflow(($Position2D2.position-position).angle()-PI/2) > rad_overflow(rotation_angle+PI/2): actives.y = 1 else: actives.y = 0
代码逻辑解析:
- 核心是通过角度范围判断两个点(
$Position2D和$Position2D2)的位置 rad_overflow是处理角度溢出的工具函数(作用是把角度限制在02π或-ππ的有效范围内)- 当旋转角在π/2到3π/2之间时,判断点的角度是否在
rotation_angle±π/2区间之外,满足则标记actives对应位为1 - 其他情况判断点的角度是否在该区间之内,满足则标记为1
- 本质是用角度区间区分点的位置,为后续判断线段是否穿过区间边界做准备
旧脚本代码2:直线交点计算
var point1 = $Position2D.position var point2 = $Position2D2.position var limit3 = Vector2(0,1).rotated(rotation_angle+PI/2) var limit4 = Vector2(0,1).rotated(rotation_angle-PI/2) var det = (point1.x - point2.x)*(limit3.y - limit4.y) - (point1.y - point2.y)*(limit3.x - limit4.x) var new_position = Vector2( ((point1.x*point2.y - point1.y*point2.x) * (limit3.x-limit4.x) - (point1.x-point2.x) * (limit3.x*limit4.y - limit3.y*limit4.x))/det, ((point1.x*point2.y - point1.y*point2.x) * (limit3.y-limit4.y) - (point1.y-point2.y) * (limit3.x*limit4.y - limit3.y*limit4.x))/det)
代码逻辑解析:
- 计算线段(
point1到point2)与角度区间边界线的交点 limit3和limit4是沿rotation_angle±π/2方向的单位向量,代表角度区间的两条边界直线- 行列式
det用于判断两条直线是否平行(det为0时直线平行,无交点) - 后续公式是直线相交坐标的通用计算方法,最终得到线段与边界线的交点
new_position
旧脚本代码3:相交结果判断
if actives.x != actives.y: print("hit") else: print("miss")
代码逻辑解析:
- 当两个点的标记(
actives.x和actives.y)不同时,说明线段穿过了角度区间的边界,输出"hit";否则输出"miss" - 本质是判断线段是否与角度区间的边界相交,即线段是否穿过了假想的角度分割线
一、通用线段相交判断方法
判断两条线段(P1-P2和P3-P4)是否相交,用跨立实验结合方向叉乘即可实现:
- 先定义叉乘工具函数:
func cross(v1: Vector2, v2: Vector2) -> float: return v1.x * v2.y - v1.y * v2.x
- 完整相交判断逻辑:
func segments_intersect(p1: Vector2, p2: Vector2, p3: Vector2, p4: Vector2) -> bool: var d1 = cross(p2 - p1, p3 - p1) var d2 = cross(p2 - p1, p4 - p1) var d3 = cross(p4 - p3, p1 - p3) var d4 = cross(p4 - p3, p2 - p3) # 跨立实验:两点分别在对方线段两侧 if ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)): return true # 处理点在线段上的特殊情况 if d1 == 0 && point_on_segment(p1, p2, p3): return true if d2 == 0 && point_on_segment(p1, p2, p4): return true if d3 == 0 && point_on_segment(p3, p4, p1): return true if d4 == 0 && point_on_segment(p3, p4, p2): return true return false func point_on_segment(a: Vector2, b: Vector2, p: Vector2) -> bool: # 判断点p是否在a-b线段上(包含端点) return min(a.x, b.x) <= p.x <= max(a.x, b.x) && min(a.y, b.y) <= p.y <= max(a.y, b.y)
二、判断假想线穿过多边形的哪一侧
假设假想线是无限长直线,判断它穿过多边形的方向可以按以下步骤操作:
用叉乘区分顶点位置:
假设假想线由点O和方向向量D定义,对多边形的每个顶点V,计算cross(D, V-O):- 结果为正:顶点在直线的一侧
- 结果为负:顶点在直线的另一侧
- 结果为0:顶点在直线上
统计两侧顶点数或跟踪交点顺序:
- 若正侧顶点数远多于负侧,说明直线从负侧穿入正侧;反之则相反
- 若需要更精确的穿侧方向,可以跟踪假想线与多边形边的交点顺序,结合多边形法线方向判断穿入/穿出方向
如果是类似旧脚本的角度区间穿侧判断,可简化为:对多边形的每条边,用旧脚本的逻辑判断边是否穿过角度区间边界,统计穿过的方向(内到外/外到内)即可确定整体穿侧方向。
内容的提问来源于stack exchange,提问作者weg
相关产品推荐
相关产品推荐

