点与线段的位置关系 - 夹角类型判断
更推荐这个:点到线段的最近距离, 点与线段的位置关系 - 投影方式
判断依据
点与线段端点组成的三角形,有一个角是钝角或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; } } }
参考