二维平面上判断点是否在三角形内

最近在项目中碰到的这个问题,在此记录一下。已知三角形的三个顶点坐标,判断某个点是否在三角形中(在三角形的边上,我们也视作在三角形中),本文给出了三种方法。

 

算法1

利用面积法,如上图所示,如果点P在三角形ABC的内部,则三个小三角形PAB, PBC, PAC的面积之和 = ABC的面积,反之则不相等。

已知三角形的三个顶点坐标求其面积,可以根据向量的叉乘,参考here

该算法详见后面的函数:IsPointInTriangle1

 

算法2

首先看一下这个问题,如何判断某两个点在某条直线的同一侧(代码中函数:IsPointAtSameSideOfLine)?

根据向量的叉乘以及右手螺旋定则,AB^AM (^表示叉乘,这里向量省略了字母上面的箭头符号)的方向为向外指出屏幕,AB^AN也是向外指出屏幕,但AB^AO的方向是向内指向屏幕,因此M,N在直线AB的同侧,M ,O在直线AB的两侧。实际计算时,只需要考虑叉积的数值正负

假设以上各点坐标为A(0,0), B(4,0), M(1,2), N(3,4), O(3,-4), 则:

AB^AM = (4,0)^(1,2) = 4*2 - 0*1 = 8

AB^AN = (4,0)^(3,4) = 4*4 – 0*3 = 16

AB^AO = (4,0)^(3,-4) = 4*-4 – 0*3 = –16

由上面的数值可知,可以根据数值的正负判断叉乘后向量的方向。即,如果叉积AB^AM 和 AB^AN的结果同号,那么M,N两点就在直线的同侧,否则不在同一侧。特殊地,如果点M在直线AB上,则AB^AM的值为0。(如果是在三维坐标系中,求出的叉积是一个向量,可以根据两个向量的点积结果正负来判断两个向量的是否指向同一侧)                                                         本文地址

 

以上的问题解决了,就很容易的用来判断某个点是否在三角形内,如果P在三角形ABC内部,则满足以下三个条件:P,A在BC的同侧、P,B在AC的同侧、PC在AB的同侧。某一个不满足则表示P不在三角形内部。

该算法详见后面的函数:IsPointInTriangle2

 

算法3

该方法也用到了向量。对于三角形ABC和一点P,可以有如下的向量表示:

如果p点在三角形内部,则满足:u >= 0,   v >= 0,   u+v <= 1。

 

已知A,B,C,P四个点的坐标,可以求出u,v,把上面的式子分别点乘向量AC和向量AB

解方程得到:

解出u,v后只需要看他们是否满足“u >= 0, v >= 0, u+v <= 1”,如满足,则,p 在三角形内。

(u = 0时,p在AB上, v = 0时,p在AC上,两者均为0时,p和A重合)

该算法详见后面的函数:IsPointInTriangle3


经过测试,算法3最快,算法2次之,算法2最慢。直观的从计算量上来看,也是算法3的计算量最少。

以下是代码,定义了两个类:二维向量类 和 三角形类

 #include<string>
 #include<sstream>
 #include<fstream>
 #include<cstdio>
 #include<cstring>
 #include<iostream>
 #include<vector>
 #include<stack>
 #include<queue>
 #include<list>
 #include<algorithm>
 #include<ctime>
 #include<unordered_map>
 #include<map>
 #include<typeinfo>
 #include<cmath>
 #include<ctime>
 #include<climits>
 using namespace std;

//类定义:二维向量
class Vector2d
{
public:
    double x_;
    double y_;

public:
    Vector2d(double x, double y):x_(x), y_(y){}
    Vector2d():x_(0), y_(0){}

    //二维向量叉乘, 叉乘的结果其实是向量,方向垂直于两个向量组成的平面,这里我们只需要其大小和方向
    double CrossProduct(const Vector2d vec)
    {
        return x_*vec.y_ - y_*vec.x_;
    }

    double DotProduct(const Vector2d vec)
    {
        return x_ * vec.x_ + y_ * vec.y_;
    }

    //二维向量减法
    Vector2d Minus(const Vector2d vec) const
    {
        return Vector2d(x_ - vec.x_, y_ - vec.y_);
    }

    //判断点M,N是否在直线AB的同一侧
    static bool IsPointAtSameSideOfLine(const Vector2d &pointM, const Vector2d &pointN,
                                        const Vector2d &pointA, const Vector2d &pointB)
    {
        Vector2d AB = pointB.Minus(pointA);
        Vector2d AM = pointM.Minus(pointA);
        Vector2d AN = pointN.Minus(pointA);

        //等于0时表示某个点在直线上
        return AB.CrossProduct(AM) * AB.CrossProduct(AN) >= 0;
    }
};

//三角形类
class Triangle
{
private:
    Vector2d pointA_, pointB_, pointC_;

public:
    Triangle(Vector2d point1, Vector2d point2, Vector2d point3)
        :pointA_(point1), pointB_(point2), pointC_(point3)
    {
        //todo 判断三点是否共线
    }

    //计算三角形面积
    double ComputeTriangleArea()
    {
        //依据两个向量的叉乘来计算,可参考http://blog.csdn.net/zxj1988/article/details/6260576
        Vector2d AB = pointB_.Minus(pointA_);
        Vector2d BC = pointC_.Minus(pointB_);
        return fabs(AB.CrossProduct(BC) / 2.0);
    }

    bool IsPointInTriangle1(const Vector2d pointP)
    {
        double area_ABC = ComputeTriangleArea();
        double area_PAB = Triangle(pointP, pointA_, pointB_).ComputeTriangleArea();
        double area_PAC = Triangle(pointP, pointA_, pointC_).ComputeTriangleArea();
        double area_PBC = Triangle(pointP, pointB_, pointC_).ComputeTriangleArea();

        if(fabs(area_PAB + area_PBC + area_PAC - area_ABC) < 0.000001)
            return true;
        else return false;
    }

    bool IsPointInTriangle2(const Vector2d pointP)
    {
        return Vector2d::IsPointAtSameSideOfLine(pointP, pointA_, pointB_, pointC_) &&
            Vector2d::IsPointAtSameSideOfLine(pointP, pointB_, pointA_, pointC_) &&
            Vector2d::IsPointAtSameSideOfLine(pointP, pointC_, pointA_, pointB_);
    }

    bool IsPointInTriangle3(const Vector2d pointP)
    {
        Vector2d AB = pointB_.Minus(pointA_);
        Vector2d AC = pointC_.Minus(pointA_);
        Vector2d AP = pointP.Minus(pointA_);
        double dot_ac_ac = AC.DotProduct(AC);
        double dot_ac_ab = AC.DotProduct(AB);
        double dot_ac_ap = AC.DotProduct(AP);
        double dot_ab_ab = AB.DotProduct(AB);
        double dot_ab_ap = AB.DotProduct(AP);

        double tmp = 1.0 / (dot_ac_ac * dot_ab_ab - dot_ac_ab * dot_ac_ab);

        double u = (dot_ab_ab * dot_ac_ap - dot_ac_ab * dot_ab_ap) * tmp;
        if(u < 0 || u > 1)
            return false;
        double v = (dot_ac_ac * dot_ab_ap - dot_ac_ab * dot_ac_ap) * tmp;
        if(v < 0 || v > 1)
            return false;

        return u + v <= 1;
    }
};

int main()
{
    Triangle tri(Vector2d(0,0), Vector2d(6,6), Vector2d(12,0));
    srand(time(0));
    for(int i = 0; i < 20; ++i)
    {
         Vector2d point((rand()*1.0 / RAND_MAX) * 12, (rand()*1.0 / RAND_MAX) * 6);
         cout<<point.x_<<" "<<point.y_<<":     ";
         cout<<tri.IsPointInTriangle1(point)<<" ";
         cout<<tri.IsPointInTriangle2(point)<<" ";
         cout<<tri.IsPointInTriangle3(point)<<endl;
    }
}

 

参考资料:

http://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html

http://blog.csdn.net/fox64194167/article/details/8147460

 

【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/4024413.html

时间: 2024-10-13 07:41:11

二维平面上判断点是否在三角形内的相关文章

二维平面上判断点在三角形内的最优算法

园子里有很多关于点是否在三角形内的文章,提供了各种方法.这让人很纠结,到底该用哪种算法?这里提供一套我认为最优的算法.如果你有不同的意见,亦或有更好的算法,欢迎来讨论. 算法使用的是同向法,其原理是:假设点P位于三角形ABC内,会有这样一个规律:三角形的每一个边,其对角点与P在边的同一侧:或者说三角形的每一个顶点与P在其对角边的同一侧. (1)代码 // 2D vector class Vec2 { public: Vec2() { x = 0.0f; y = 0.0f; } Vec2(floa

9.7数学与概率(三)——在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分

/** * 功能:在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分. * 假定正方形的上下两条边与x轴平行. */ /** * 考虑: * 线的准确含义,可能性有: * 1)由斜率和y轴截距确定: * 2)由这条边上的任意两点确定: * 3)线段,以正方形的边作为起点和终点. * * 假设:这条线的端点应该落在正方形的边上. * 思路:要将两个正方形对半分,这条线必须连接两个正方形的中心点. */ public class Square { //正方形的四条边 int lef

9.7数学与概率(四)——在二维平面上,有一些点,请找出经过点数最多的那条线

/** * 功能:在二维平面上,有一些点,请找出经过点数最多的那条线. /** * 思路:在任意两点之间画一条无线长的直线,用散列表追踪那条直线出现的次数最多.时间复杂度O(N*N) * 注意: * 1)用斜率和y轴截距来确定是否是同一条直线. * 2)浮点数不一定能用二进制数准确表示,因此检查两个浮点数的差值是否在某个极小值(epsilon)内. * 3)对于散列表而言,斜率相等,未必散列值相同.因此,将斜率减去一个极小值,并以得到的结果flooredSlope作为散列键. * 4)取得所有可

数字之魅:寻找二维平面上的最近的点对

在二维平面上的n个点中,如何快速的找出最近的一对点,就是最近点对问题. 初看这个题,可能感觉有点儿复杂. 方案一:蛮力法.数组中总共包含N个数,所以我们可以把平面内所有的点按X轴排序,然后依次算出后一个坐标与前面所有左边的距离,然后用Min和position来记录最近的距离和两个坐标.该方案和在一维空间求两个最近点的距离有点儿类似,其时间复杂度为:O(N*N). 方案二:在一维空间里,我们知道如果数组有序,我们可以很快找出最近的两个点.我们可以用O(N*logN)的时间复杂度来对数据进行排序[快

编写一个表示二维平面上的点的类MyPoint,满足以下条件: 1、定义private的成员变量x和y,表示点的x和y坐标,类型为double

编写一个表示二维平面上的点的类MyPoint,满足以下条件:1.定义private的成员变量x和y,表示点的x和y坐标,类型为double2.定义两个MyPoint的构造方法,一个构造方法不带参数,而且x和y的初始值为0,另一个构造方法有两个参数,参数名为x和y,类型为double,用这两个参数分别作为初始x和y坐标3.定义一个getD方法,有一个类型为MyPoint的对象参数,功能为返回当前对象和参数对象这两个坐标点的距离,返回值为double类型4.编写测试的main方法,调用getD计算两

判断点是否在三角形内【转】

概述 给定三角形ABC和一点P(x,y,z),判断点P是否在ABC内.这是游戏设计中一个常见的问题.需要注意的是,这里假定点和三角形位于同一个平面内. 本文介绍三种不同的方法,由浅入深 一 内角和法 连接点P和三角形的三个顶点得到三条线段PA,PB和PC,求出这三条线段与三角形各边的夹角,如果所有夹角之和为180度,那么点P在三角形内,否则不在,此法直观,但效率低下. 二 同向法 假设点P位于三角形内,会有这样一个规律,当我们沿着ABCA的方向在三条边上行走时,你会发现点P始终位于边AB,BC和

判断点是否在三角形内

http://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html#!comments 本文只是翻译和整理,原文在此http://www.blackpawn.com/texts/pointinpoly/default.html 概述 给定三角形ABC和一点P(x,y,z),判断点P是否在ABC内.这是游戏设计中一个常见的问题.需要注意的是,这里假定点和三角形位于同一个平面内. 本文介绍三种不同的方法,由浅入深 一 内角和法 连接点P和三

华为oj 之 判断点是否在三角形内

/* * 任意顶点为A.B.C的三角形,如果点P在三角形内,那么 * 1)点P和点C在AB边的同一侧 2)点P和点A在BC边的同一侧 3)点P和点B在AC边的同一侧 * 反之亦然(充要条件) * * 关键:如何判断某两个点在某条直线的同一侧 * 参考图示发现: *   对于点P: AB*BC的方向 和 AB*BP的方向相同 *    对于点P1: AB*BC的方向 和 AB*BP1的方向不同 * 故:对于边AB,以及点C.P,如果AB*BC的方向 和 AB*BP的方向相同, *    那么点P和

2D空间中判断一点是否在三角形内

本来打算做三角形填充多边形,但需要用到耳切法正在看.所以先研究了这个 要注意如果是XY坐标轴的2D空间,要取差乘分量z而不是y. 实现原理是,将三角形ABC三个边(AB,BC,CA)分别与比较点判断差乘,如果这3个差乘结果表示的方向一致,说明就在三角形内. 效果: 代码(Unity3D): using UnityEngine; using System.Collections; using System.Collections.Generic; public class TriangleColl