//解题思想用射线法
//该题思想是向由点P向x正方向发射一个射线,穿过多边形线段上的个数为奇数则在多边形内,偶数则在多边形外
//具体方法是:点的Y值大于等于多边形上某个线段的最小值且小于该线段上的最大值,在该线段上取一个y值为点P.y的点P1。如果P.x<P1.x //,则计数器加1,若计数器为奇数则在多边形内,若为偶数则在多边形外
private bool PointInFences(point pnt1, point[] fencePnts)
{
int j=0, cnt = 0;
for (int i = 0; i < fencePnts.Length; i++)
{
j = (i == fencePnts.Length - 1) ? 0 : j + 1;
if ((fencePnts[i].y!=fencePnts[j].y)&&(((pnt1.y>= fencePnts[i].y) && (pnt1.y < fencePnts[j].y)) ||((pnt1.y >= fencePnts[j].y) && (pnt1.y <fencePnts[i].y))) && (pnt1.x < (fencePnts[j].x -fencePnts[i].x) * (pnt1.y - fencePnts[i].y) / (fencePnts[j].y -fencePnts[i].y) + fencePnts[i].x)) cnt++;
}
return (cnt%2>0)?true:false;
}
我的一哥们写的比较精炼的一个小程序。原程序没有考虑到点P与多边形上的某个平行x轴的线段的两个端点三点共线问题。我加了一个先决判断条件就是到线段不平行于x轴。
该方法构思巧妙:
1、向X轴正方向发射射线,先判断是否与多边形的线段相交,若相交点的X值大于P的x值,则计数器加1.
2、通过大于等于线段两个端点的最小Y值,小于线段两个端点的最大Y值,判断出射线与线段是否相交。——避免了P在线段的延长线上的情况对计算结果的困扰。
-----------------------------------------------------------------------------------------------------------------------------------------
12. 判断点是否在多边形中:
判断点P是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点P为端点,向左方作射线L,由于多边形是有界的,所以射线L的左端一定在多边形外,考虑沿着L从无穷远处开始自左向右移动,遇到和多边形的第一个交点的时候,进入到了多边形的内部,遇到第二个交点的时候,离开了多边形,……所以很容易看出当L和多边形的交点数目C是奇数的时候,P在多边形内,是偶数的话P在多边形外。
但是有些特殊情况要加以考虑。如图下图(a)(b)(c)(d)所示。在图(a)中,L和多边形的顶点相交,这时候交点只能计算一个;在图(b)中,L和多边形顶点的交点不应被计算;在图(c)和(d)中,L和多边形的一条边重合,这条边应该被忽略不计。如果L和多边形的一条边重合,这条边应该被忽略不计。
为了统一起见,我们在计算射线L和多边形的交点的时候,1。对于多边形的水平边不作考虑;2。对于多边形的顶点和L相交的情况,如果该顶点是其所属的边上纵坐标较大的顶点,则计数,否则忽略;3。对于P在多边形边上的情形,直接可判断P属于多边行。由此得出算法的伪代码如下:
count ← 0;
以P为端点,作从右向左的射线L;
for 多边形的每条边s
do if P在边s上
then return true;
if s不是水平的
then if s的一个端点在L上
if 该端点是s两端点中纵坐标较大的端点
then count ← count+1
else if s和L相交
then count ← count+1;
if count mod 2 = 1
then return true;
else return false;
其中做射线L的方法是:设P'的纵坐标和P相同,横坐标为正无穷大(很大的一个正数),则P和P'就确定了射线L。
判断点是否在多边形中的这个算法的时间复杂度为O(n)。
另外还有一种算法是用带符号的三角形面积之和与多边形面积进行比较,这种算法由于使用浮点数运算所以会带来一定误差,不推荐大家使用。
------------------------------------------------------------------------------------------------------------------------------------------ |
摘 要 本文提出一种新方法以检测一个点是否在多边形内。该方法将矢量和射线法结合,彻底解决了射线法所具有的奇异情况。实验结果证明,该方法具有简单、易实现、快速等优点。
1.引言
判断点在多边形内外是计算机图形学的最基本算法,在计算机图形处理、模式识别、CAD 及科学计算可视化中有着广泛的应用。判断点在多边形内外的算法有主要有定向射线法、角度法。角度法要使用复杂的三角运算,计算量大;在工程上应用最多的是定向射线法,这种方法简单、可靠,但其难以处理对边界点及边界与射线共线等特殊情况的处理。
2.基本概念
假设OA、OB是非零矢量,将OA绕点O旋转到与OB方向相同的位置时,所形成的角称为有向角<AOB。规定逆时针旋转为正方向,顺时针旋转为负方向。
设线段s的两个顶点为a和b,则s与直线l所形成的关系可划分为三大类:(a)s有且仅有一个顶点(a或者b)在l上,称这种关系为半跨越;(b)s的两个顶点a和b分别在l的两侧,称这种关系为跨越;(c)s的两个顶点a和b在l的同一侧或线上,称这种关系为未跨越。
过点Q作x正方向的水平射线l。若线段s跨越(或半跨越)射线l且与点Q所成的有向角为正,称为正向跨越(或正向半跨越),否则称为负向跨越(负向半跨越)。如图1
![](https://i-blog.csdnimg.cn/blog_migrate/b429f02f86dffead948e9cea299b5f91.bmp)
3.多边形内外点判定算法
在本文中,假定多边形P的顶点P0、P1、…,Pn(=P0)按逆时针方向存储,同样可将多边形看作有n条有向线段Si,即 (i=1,…,n-1),头尾相连而成。
定理1.过平面上任意一点Q作x正方向的水平射线l,若∑f(Si,l)=0(其中i>=0,i<n),则点Q在多边形P外。
证明:设射线l与多边形P的Si交点为kj,且kj不属于多边形P的顶点,则将交点分别插入到Pi、Pi+1之间形成两条有向线段Pikj、kjPi+1。将所有此类交点都添加进到顶点序列中,形成一个新的多边形P’,顶点序列为P0、…、Pi、kj、Pi+1、…Pm、kk、Pm+1、 Pn(=P0),如图2
(a)形成的顶点序列为P0、P1、k0、P2、P3、P0。因此多边形P’的有向线段与射线l的跨越情况全部转换为半跨越,如图2(b、c、d、e)四类情况。若将点Q与多边形P’的各个顶点相连,组成一系列有向角。在多边形P’中任选一个在射线l上的点ki作为起始点,则每两个交点之间的所有有向线段所组成的有向角的代数和的关系如图2(b、c、d、e)所示。显然可以得出时,有向角的角度代数和为0,所以点Q在多边形P’外,因P’与P同构,因此,点Q在多边形P外。定理2.过平面上任意一点Q作x正方向的水平射线l,若∑f(Si,l)=0(其中i>=0,i<n) 的值为2或者-2,则点Q在多边形P内。
证明过程与定理1证明类似,可以得出 值为2或者-2时,有向角的角度代数和为360或者-360度,从而可以得出点Q在多边形P内。
算法的具体描述:
(1)获得一个点Pi相对与点Q的位置,如果Pi的y坐标值大于Q的y坐标值,status=1,若小于,则status=-1,否则statue=0;用lastStatus记录上一点的相对位置值。cnt表示f(si,l)的代数和。
2)temp = status – lastStatus;
if(temp > 0)
{
if(点Q在有向线段Pi-1Pi的右侧)
cnt = cnt + temp;
else if (点Q在有向线段Pi-1Pi上)
return(“点Q在有向线段Pi-1Pi上”);
}
else if(temp < 0)
{
if(点Q在有向线段Pi-1Pi的左侧)
cnt = cnt + temp;
else if (点Q在有向线段Pi-1Pi上)
return(“点Q在有向线段Pi-1Pi上”);
}
(3)循环(2),直到多边形所有的顶点遍历完毕,
if(cnt == 0)
return(“点在多边形外”);
else return(“点在多边形内”);
4.算法分析和小结
射线法的复杂度为O(n),本文算法的复杂度也为O(n)。射线法中对每一条边都要进行两次以上的乘法运算,而本算法只对跨越或者半跨越射线的边最多进行一次叉积运算;射线法需要对奇异情况进行特殊处理,而本算法彻底解决了奇异情况的发生。在本文中,最坏的情况是所需的计算量为(8次加减运算,2次乘法运算)*n。
-----------------------------------------------------------------------
vc知识库
inline int
isLeft( CPoint *P0, CPoint *P1, CPoint *P2 )
{
return ( (P1->x - P0->x) * (P2->y - P0->y)
- (P2->x - P0->x) * (P1->y - P0->y) );
}
bool CPointInPolygonDlg::PointPolygonAlgorithm(CPoint *pt)
{
int wn = 0; // the winding number counter
std::vector<CPoint *>::iterator it;
// loop through all edges of the polygon
for (it=vPolygon.begin(); it<vPolygon.end()-1; it++)// edge from V[i] to V[i+1]
{
if ((*(it))->y <= pt->y) { // start y <= pt->y
if ((*(it+1))->y > pt->y) // an upward crossing
if (isLeft( *it, *(it+1), pt) > 0) // P left of edge
++wn; // have a valid up intersect
}
else { // start y > P.y (no test needed)
if ((*(it+1))->y <= pt->y) // a downward crossing
if (isLeft( *it, *(it+1), pt) < 0) // P right of edge
--wn; // have a valid down intersect
}
}
if (wn==0)
return false;
return true;
}