原理
假设有三角形ABC与任意点P, 则如果点P在三角形ABC内时, 满足点P在向量AB,BC,CA的相同侧,即在三个向量的左侧或者右侧。
对于向量AB,C点永远在AB的左侧或右侧,所以只要P点与C点同侧,P点就在向量AB的左侧或右侧。
对向量AB,AP和向量AB,AC做叉乘运行,可以得到两条垂直向量,若这两条向量同向,则可以证明P,C相对AB同侧
对三条边分别计算即可证明P在ABC内。
该原理对多边形也适用
代码
// ******************************************************************
// /\ /| @file TriangleTest.cs
// \ V/ @brief 三角形测试
// | "") @author Shadowrabbit, yingtu0401@gmail.com
// / |
// / \\ @Modified 2021-05-12 11:13:59
// *(__\_\ @Copyright Copyright (c) 2021, Shadowrabbit
// ******************************************************************
using UnityEngine;
public class TriangleTest : MonoBehaviour
{
public Transform transformA;
public Transform transformB;
public Transform transformC;
public Transform transformP;
private void OnDrawGizmos()
{
if (transformP == null)
{
return;
}
var positionA = transformA.position;
var positionB = transformB.position;
var positionC = transformC.position;
Gizmos.color = IsPointInTriangle(positionA, positionB, positionC,
transformP.position)
? Color.green
: Color.red;
Gizmos.DrawLine(positionA, positionB);
Gizmos.DrawLine(positionB, positionC);
Gizmos.DrawLine(positionC, positionA);
}
/// <summary>
/// 判断同边 点AB构成一个线段 计算以AB顺时针旋转 AP,AC是否在AB同侧
/// </summary>
/// <param name="pointA">点A</param>
/// <param name="pointB">点B</param>
/// <param name="pointC"></param>
/// <param name="pointP"></param>
private bool IsSameSide(Vector3 pointA, Vector3 pointB, Vector3 pointC, Vector3 pointP)
{
//计算向量
var ab = pointB - pointA;
var ac = pointC - pointA;
var ap = pointP - pointA;
//计算ab与ac的叉乘 ab与ap的叉乘
var crossAbAc = Vector3.Cross(ab, ac);
var crossAbAp = Vector3.Cross(ab, ap);
//点乘>0 则为相同方向
return Vector3.Dot(crossAbAc, crossAbAp) > 0;
}
/// <summary>
/// 判断P点是否在三角形ABC中
/// </summary>
/// <param name="pointA"></param>
/// <param name="pointB"></param>
/// <param name="pointC"></param>
/// <param name="pointP"></param>
/// <returns></returns>
private bool IsPointInTriangle(Vector3 pointA, Vector3 pointB, Vector3 pointC, Vector3 pointP)
{
//点P对三边都是同侧则在三角形内
return IsSameSide(pointA, pointB, pointC, pointP) && IsSameSide(pointB, pointC, pointA, pointP) &&
IsSameSide(pointC, pointA, pointB, pointP);
}
}