相交 - 点是否在三角形内 - 重心法

1) 点在三角形的边上时

AP=AE+AF (向量加法)

设AE=v*AB, AF=u*AC; 则AP=v*AB+u*AC(二元一次方程,u, v为我们引入的变量)

根据向量三点共线定理可知:u+v=1

 

2) 点在三角形内时

AE不变, 让AF变短一些, 当用u*AC表示AF时, u的值肯定也比1)中小了,所以此时u+v<1

 

所以点是否在三角形内,就转换为判断u+v<=1

这边利用二元一次方程来处理这个问题:AP=v*AB+u*AC, 其中u, v为未知数

将方程的两边分别点乘向量AB和AC

AP•AB = v*AB•AB + u*AC•AB, 设a=AC•AB, b=AB•AB, e=AP•AB, 则方程可表示成 a*u+b*v=e

AP•AC = v*AB•AC + u*AC•AC, 设c=AC•AC, d=AB•AC, f=AP•AC, 则方程可表示成 c*u+d*v=f

 

利用矩阵求二元一次方程组的方式,即可求出u, v的值

线性代数 - 矩阵求直线方程组(二元一次方程)

跟这边是类似的,把x换成u, v换成y就可以了

 

//重心法判断点是否在三角形内
public static bool IsPointInTriangle2(Vector2 P, Vector2 A, Vector2 B, Vector2 C)
{
    var AB = B - A;
    var AC = C - A;
    var AP = P - A;

    float a = Vector2.Dot(AC, AB);
    float b = Vector2.Dot(AB, AB);
    float e = Vector2.Dot(AP, AB);

    float c = Vector2.Dot(AC, AC);
    float d = a; //Vector2.Dot(AB, AC);
    float f = Vector2.Dot(AP, AC);

    float invDetDown = 1 / (a * d - b * c);
    float u = (d * e - b * f) * invDetDown;
    if (u < 0 || u > 1)
        return false;

    float v = (-c * e + a * f) * invDetDown;
    if (v < 0 || v > 1)
        return false;

    var result = (u + v) <= 1;
    return result;
}

 

效果

可以看到日志打印的耗时,重心法比同侧法快一点 

 

测试代码

using System;
using UnityEditor;
using UnityEngine;

public class PointTriTest : CollideTestBase
{

    //三角形3个顶点
    public Transform m_A;
    public Transform m_B;
    public Transform m_C;

    public Transform m_P;

    void Update()
    {
        m_IsIntersect = false;
        if (m_P && m_A && m_B && m_C)
        {
            var t1 = DateTime.Now;
            switch (m_ApiType)
            {
            case 1:
                for (int i = 0; i < m_InvokeCount; ++i)
                    m_IsIntersect = Shape2DHelper.IsPointInTriangle(m_P.position, m_A.position, m_B.position, m_C.position);
                break;

            case 2:
                for (int i = 0; i < m_InvokeCount; ++i)
                    m_IsIntersect = Shape2DHelper.IsPointInTriangle2(m_P.position, m_A.position, m_B.position, m_C.position);
                break;

            }

            CheckTimeCost(t1, 2);
        }
    }

    private void OnDrawGizmos()
    {
        if (m_P && m_A && m_B && m_C)
        {
            if (m_IsIntersect)
            {
                Gizmos.color = Color.red;
                Gizmos.DrawLine(m_A.position, m_B.position);
                Gizmos.DrawLine(m_B.position, m_C.position);
                Gizmos.DrawLine(m_C.position, m_A.position);
                Gizmos.color = Color.white;
            }
            else
            {
                Gizmos.DrawLine(m_A.position, m_B.position);
                Gizmos.DrawLine(m_B.position, m_C.position);
                Gizmos.DrawLine(m_C.position, m_A.position);
            }
        }
    }

}

 

参考

判断点是否在三角形内 - 翰墨小生 - 博客园 (cnblogs.com)

几种方法判断平面点在三角形内_判断点在三角形内-CSDN博客

 

posted @ 2023-11-22 20:47  yanghui01  阅读(78)  评论(0编辑  收藏  举报