请问如何快速判断一个点在三角形内,外,边上?

goodsong 2002-06-11 08:20:27
我想这个问题很特殊,会不会比有判断点在任意多边形内的算法更简单的算法呢?
速度快越好
谢谢关注!
...全文
1615 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
eion 2002-06-12
  • 打赏
  • 举报
回复
CString s;
int i=0,k=0;
const int N=10000;
struct TPoint tr[N][3],p[N];
for(i=0;i<N;i++)
{
for(int j=0;j<3;j++)
{
tr[i][j].x = (rand()%500);
tr[i][j].y = (rand()%500);
}
p[i].x =(rand()%500);
p[i].y =(rand()%500);
}

SYSTEMTIME tm1,tm2;
int totaltime;

GetLocalTime(&tm1);
for(k=0;k<10;k++)for(i=0;i<N;i++) inside1(tr[i],p[i]);
GetLocalTime(&tm2);
totaltime = (tm2.wSecond-tm1.wSecond)*1000+(tm2.wMilliseconds-tm1.wMilliseconds);
s.Format("inside1 == %ld ms",totaltime);
dc.TextOut(0,20,s);

GetLocalTime(&tm1);
for(k=0;k<10;k++)for(i=0;i<N;i++) inside2(tr[i],p[i]);
GetLocalTime(&tm2);
totaltime = (tm2.wSecond-tm1.wSecond)*1000+(tm2.wMilliseconds-tm1.wMilliseconds);
s.Format("inside2 == %ld ms",totaltime);
dc.TextOut(0,40,s);

GetLocalTime(&tm1);
for(k=0;k<10;k++)for(i=0;i<N;i++) inside3(tr[i],p[i]);
GetLocalTime(&tm2);
totaltime = (tm2.wSecond-tm1.wSecond)*1000+(tm2.wMilliseconds-tm1.wMilliseconds);
s.Format("inside3 == %ld ms",totaltime);
dc.TextOut(0,60,s);

GetLocalTime(&tm1);
for(k=0;k<10;k++)for(i=0;i<N;i++) inside4(tr[i],p[i]);
GetLocalTime(&tm2);
totaltime = (tm2.wSecond-tm1.wSecond)*1000+(tm2.wMilliseconds-tm1.wMilliseconds);
s.Format("inside4 == %ld ms",totaltime);
dc.TextOut(0,80,s);
}

算法速度比较结果:
inside1 == 80 ms
inside2 == 141 ms
inside3 == 250 ms
inside4 == 70 ms

所以算法4最好
不过那是在进行了100,000次的差别,所以那一种算法都无所谓了
eion 2002-06-12
  • 打赏
  • 举报
回复
int inside4(const struct TPoint tr[], struct TPoint p)
{
struct TPoint arr[3];
memcpy(arr,tr,sizeof(arr));
for(int i=0;i<3;i++) // 求三个向量
{
arr[i].x = tr[i].x - p.x;
arr[i].y = tr[i].y - p.y;
}
for(i=0;i<3;i++) // 判断是否在边界上
{
int j=(i+1)%3;
if( arr[i].x*arr[j].y-arr[i].y*arr[j].x==0 ) //点在边界上,向量对称
{
if( arr[i].x*arr[j].x>0 || arr[i].y*arr[j].y>0 ) return 0;// 同方向
return 1; // 方向相反
}
}
for(i=0;i<2;i++) // 判断在内还是外,在此只需判断两个向量,下有说明【注1】
{
int front = (i+2)%3, next = 3-i-front;
int cnt = 0;
int t1 = arr[i].y*arr[front].x - arr[front].y*arr[i].x;
int t2 = arr[i].y*arr[next].x - arr[next].y *arr[i].x;
if( (t1>0)+(t2>0)!=1 ) return 0; //向量分布在同一侧,则在外;否则不能确定
}
return 1; // 三个向量都在不同两侧,则在内
// 【注1】:如果三个向量分布在同一侧,则必定有两个向量使得另外的两个向量在该向量的同一侧
// a b c
// 如 \|/ 三条线,b,c在a的一边, a,b在c的一边,所以a,b,c中有两条线都可以判断
// a,b,c三个向量对应的三个顶点在三个向量原点的一个方向,所以该点不在此三角形中
}
one_add_one 2002-06-11
  • 打赏
  • 举报
回复
我写了两个算法,一个是用面积来判断。还有一个是用叉乘来判断。

#include <stdio.h>
#include <math.h>

struct TPoint{
float x;
float y;
};

float area(struct TPoint p1,struct TPoint p2,struct TPoint p3){
return fabs((p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y));
}

float mul(struct TPoint p1,struct TPoint p2,struct TPoint p0){
return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}

int inside(struct TPoint tr[],struct TPoint p){
int i;
for (i=0;i<3;i++)
if (mul(p,tr[i],tr[(i+1)%3])*mul(p,tr[(i+2)%3],tr[(i+1)%3])>0)
return 0;
return 1;
}

int inside2(struct TPoint tr[],struct TPoint p){
if (fabs(area(tr[0],tr[1],tr[2])-
area(p,tr[1],tr[2])-
area(tr[0],p,tr[2])-
area(tr[0],tr[1],p))<1.0e-20)
return 1;
else
return 0;
}

main(){
struct TPoint tr[3]={{-1,1},{1,0},{3,0}},p={1,2};
printf("algorithm 1:");
if (inside(tr,p))
printf("In\n");
else
printf("Out\n");
printf("algorithm 2:");
if (inside2(tr,p))
printf("In\n");
else
printf("Out\n");
}
one_add_one 2002-06-11
  • 打赏
  • 举报
回复
判断点是否在一个多边形内的算法复杂度已经很低了。

如果是三角形,有几种特殊情况不用考虑。编起来还是很方便的。
goodsong 2002-06-11
  • 打赏
  • 举报
回复
up
goodsong 2002-06-11
  • 打赏
  • 举报
回复
谢谢!
结贴。
one_add_one 2002-06-11
  • 打赏
  • 举报
回复
to goodsong(如风)
经过在数据算法版的努力,终于有了一个星星了:)
one_add_one 2002-06-11
  • 打赏
  • 举报
回复
或者用我上面的程序,把
int inside(struct TPoint tr[],struct TPoint p){
int i;
for (i=0;i<3;i++)
if (mul(p,tr[i],tr[(i+1)%3])*mul(p,tr[(i+2)%3],tr[(i+1)%3])>0)
return 0;
return 1;
}

稍微改一下就可以判断点是否在线段上了。
one_add_one 2002-06-11
  • 打赏
  • 举报
回复
判断点是否在线段上有很简单的方法,就是叉乘。下面的程序(by starfish)可以达到这个目的。

struct TPoint{
float x,y;
};

struct TLineSeg{
TPoint a,b;
};

/********************************************************

返回(P1-P0)*(P2-P0)的叉积。
若结果为正,则<P0,P1>在<P0,P2>的顺时针方向;
若为0则<P0,P1><P0,P2>共线;
若为负则<P0,P1>在<P0,P2>的在逆时针方向;
可以根据这个函数确定两条线段在交点处的转向,
比如确定p0p1和p1p2在p1处是左转还是右转,只要求
(p2-p0)*(p1-p0),若<0则左转,>0则右转,=0则共线

*********************************************************/

float multiply(struct TPoint p1,struct TPoint p2,struct TPoint p0)
{
return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}

//判断点p是否在线段l上
int online(struct TLineSeg l,struct TPoint p)
{
return( (multiply(l.b,p,l.a)==0)&&( ((p.x-l.a.x)*(p.x-l.b.x)<0 )||( (p.y-l.a.y)*(p.y-l.b.y)<0 )) );
}
goodsong 2002-06-11
  • 打赏
  • 举报
回复
是啊,怎么办?
yehe999 2002-06-11
  • 打赏
  • 举报
回复
PA,PB,PC所夹角之和是否为360度,如果是360度则P在三角形内


好像有问题:
如果P在线上,它与三个点夹角和也是360啊。
如P在AB连线上则PA与PB的夹角为180,PA与PC + PB 与PC = 180
qtf 2002-06-11
  • 打赏
  • 举报
回复
mark
zhoukun666 2002-06-11
  • 打赏
  • 举报
回复
咳!我又落后了一步了!
goodsong 2002-06-11
  • 打赏
  • 举报
回复
谢谢各位!
>>方法2:分别连接P点和三个顶点A,B,C,然后判断矢量PA,PB,PC所夹角之和是否为360度,如果是360度则P在三角形内

请问怎么判断?

to one_add_one()快给分给分:)你都有一颗星了呀!

one_add_one 2002-06-11
  • 打赏
  • 举报
回复
to eion(那个谁)
你的算法有acos,sqrt,/。这些函数的运算量太大了。

最好只用乘法和加减法。
eion 2002-06-11
  • 打赏
  • 举报
回复
还有一个更简单的就是判断点到三个顶点的夹角之和为2*PI

// 结构体同one_add_one()快给分给分:
// 返回1 表示在那,0表示在外
int inside3(const struct TPoint tr[], struct TPoint p)
{
TPoint p0,p1,p2;
p0.x = tr[0].x - p.x ; p0.y = tr[0].y - p.y;
p1.x = tr[1].x - p.x ; p1.y = tr[1].y - p.y;
p2.x = tr[2].x - p.x ; p2.y = tr[2].y - p.y;

double arg1 = acos((p0.x*p1.x + p0.y*p1.y)/sqrt(p0.x*p0.x + p0.y*p0.y)/sqrt(p1.x*p1.x+p1.y*p1.y));
double arg2 = acos((p0.x*p2.x + p0.y*p2.y)/sqrt(p0.x*p0.x + p0.y*p0.y)/sqrt(p2.x*p2.x+p2.y*p2.y));
double arg3 = acos((p2.x*p1.x + p2.y*p1.y)/sqrt(p2.x*p2.x + p2.y*p2.y)/sqrt(p1.x*p1.x+p1.y*p1.y));

if( fabs(2*3.1415926-arg1-arg2-arg3)<0.0001 ) return 1;
return 0;
}
starfish 2002-06-11
  • 打赏
  • 举报
回复
方法1:过该点作一条射线,然后判断该射线与三条边的相交情况;
方法2:分别连接P点和三个顶点A,B,C,然后判断矢量PA,PB,PC所夹角之和是否为360度,如果是360度则P在三角形内
课程解决的问题: 作为游戏行业或者图形学从业者,你是否面临以下问题: 到底openGL底层如何实现的? 到底矩阵操作变换是怎么做到的? 到底光栅化的算法以及原理是什么? 到底如何才能从3D世界投射到2D屏幕呢? 图形学有这么多的矩阵操作,到底如何推导如何应用呢? 学完这门课程,你应该就可以从底层了解一个初级的openGL图形接口如何实现,图形学最底层的封装到底面临哪些挑战;跟随我们一行一行写完代码,你就会得到一个迷你版本的openGL图形库,你可以深度体会图形从模型变换,观察矩阵变换,投影矩阵变换一直到光栅化纹理操作的全套模拟流程。 课程介绍: 本课程将带领学员不使用任何图形库,实现从0到1的图形学接口封装以及算法讲解,并且带领大家手敲代码,一行一行进行实现。 涵盖了(环境搭建,绘制点,Bresenham算法绘制完美直线,三角形拆分绘制算法,颜色插值算法,图片操作,图片二次插值放缩算法,纹理系统接口搭建及封装,矩阵操作理论以及实践,openGL类似接口封装,3D世界的图形学理论及接口封装等) 最终将带领大家通过C++实现一个3D世界的图形接口,方便所有人入门图形学,进行接下来的openGL接口以及GPU编程的学习   本课程为系列课程的第一步入门,且带领所有人进行实现,更加实用,可以让大家打牢图形学的基础知识及编程技能

33,010

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧