点与线段的位置关系 - 夹角类型判断
更推荐这个:点到线段的最近距离, 点与线段的位置关系 - 投影方式
判断依据
点与线段端点组成的三角形,有一个角是钝角或180度时,点在线段外侧
sin(锐角)>0, sin(钝角)>0,无法区分,所以叉乘不行。
cos(锐角)>0, cos(钝角)<0,可以区分,所以用点乘。
1) 在外侧时
a) ap和ab的夹角为钝角,或bp和ba的为钝角
b) ap和ab的夹角为180度,或bp和ba的夹角为180度
2) p和线段端点重叠,pa或pb的长度为0
3) 在内侧时
a) ap与ab的夹角为0度,或bp和ba的夹角为0度
b) ap和ab的夹角为直角,或bp和ba的夹角为直角
c) ap和ab的夹角为锐角, 或bp和ba的夹角为锐角
//点是否在线段外侧 public static bool IsPointOutsideOfSegment(Vector2 p, Vector2 a, Vector2 b, out bool isNearA) { isNearA = false; var ap = p - a; var ab = b - a; if (Vector2.Dot(ap, ab) < 0) //夹角为钝角或180度时, cos值<0 { isNearA = true; return true; } var ba = -ab; var bp = p - b; if (Vector2.Dot(ba, bp) < 0) //夹角为钝角或180度时, cos值<0 return true; return false; }
//点与线段的位置关系: -1_外侧, 0_与端点重合, 1_内侧; isA: 是否点A那边的外侧或是否和点A重合 public static int GetPointSideOfSegment(Vector2 p, Vector2 a, Vector2 b, out bool isA) { isA = false; var ap = p - a; var ab = b - a; if (Vector2.Dot(ap, ab) < 0) //夹角为钝角或180度时, cos值<0 { isA = true; return -1; } var ba = -ab; var bp = p - b; if (Vector2.Dot(ba, bp) < 0) //夹角为钝角或180度时, cos值<0 return -1; //点和线段端点重合时 float apSqrLen = ap.sqrMagnitude; if (apSqrLen < float.Epsilon) { isA = true; return 0; } float bpSqrLen = bp.sqrMagnitude; if (bpSqrLen < float.Epsilon) return 0; return 1; }
效果
测试代码
using System; using UnityEngine; public class PointSideOfSegmentTest : CollideTestBase { //线段的端点A, B public Transform m_A; public Transform m_B; public Transform m_P; private bool m_Result; private bool m_IsA; public int m_Side; void Update() { m_IsIntersect = false; if (m_A && m_B && m_P) { var t1 = DateTime.Now; switch (m_ApiType) { case 1: for (int i = 0; i < m_InvokeCount; ++i) m_Result = Shape2DHelper.IsPointOutsideOfSegment(m_P.position, m_A.position, m_B.position, out m_IsA); break; case 2: for (int i = 0; i < m_InvokeCount; ++i) m_Side = Shape2DHelper.GetPointSideOfSegment(m_P.position, m_A.position, m_B.position, out m_IsA); break; } CheckTimeCost(t1, 2); } } private void OnDrawGizmos() { if (m_A && m_B && m_P) { var a = (Vector2)m_A.position; var b = (Vector2)m_B.position; if (m_Result || -1 == m_Side) { Gizmos.color = m_IsA ? Color.red : Color.blue; Gizmos.DrawLine(a, b); } else { Gizmos.DrawLine(a, b); } Gizmos.color = Color.green; var ab = b - a; var abCenter = a + ab * 0.5f; var abLeftNormal = new Vector2(-ab.y, ab.x); var abRightNormal = new Vector2(ab.y, -ab.x); Gizmos.DrawLine(a, a + abLeftNormal * 0.8f); Gizmos.DrawLine(a, a + abRightNormal * 0.8f); Gizmos.DrawLine(b, b + abLeftNormal * 0.8f); Gizmos.DrawLine(b, b + abRightNormal * 0.8f); Gizmos.color = Color.white; } } }
参考
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话