点与线段的位置关系 - 夹角类型判断

更推荐这个:点到线段的最近距离, 点与线段的位置关系 - 投影方式

 

判断依据

点与线段端点组成的三角形,有一个角是钝角或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;
        }
    }
}

 

参考

判断线段与圆是否相交_判断线段和圆相交-CSDN博客

 

posted @ 2023-11-03 00:08  yanghui01  阅读(55)  评论(0编辑  收藏  举报