判断点是否在多边形内(2D空间)
2012-10-24 19:42
260 查看
1. 叉乘判别法(只适用于凸多边形)
想象一个凸多边形,其每一个边都将整个2D屏幕划分成为左右两边,连接每一边的第一个端点和要测试的点得到一个矢量v,将两个2维矢量扩展成3维的,然后将该边与v叉乘,判断结果3维矢量中Z分量的符号是否发生变化,进而推导出点是否处于凸多边形内外。这里要注意的是,多边形顶点究竟是左手序还是右手序,这对具体判断方式有影响。
2. 面积判别法(只适用于凸多边形)
第四点分别与三角形的两个点组成的面积分别设为S1,S2,S3,只要S1+S2+S3>原来的三角形面积就不在三角形范围中.可以使用海伦公式 。推广一下是否可以得到面向凸多边形的算法?(不确定)
3. 角度和判别法(适用于任意多边形)
该点与没对相邻顶点的夹角之和为360,则在内,否则在外
4. 水平/垂直交叉点数判别法(适用于任意多边形)
注意到如果从P作水平向左的射线的话,如果P在多边形内部,那么这条射线与多边形的交点必为奇数,如果P在多边形外部,则交点个数必为偶数(0也在内)。所以,我们可以顺序考虑多边形的每条边,求出交点的总个数。还有一些特殊情况要考虑。假如考虑边(P1,P2),
1)如果射线正好穿过P1或者P2,那么这个交点会被算作2次,处理办法是如果P的从坐标与P1,P2中较小的纵坐标相同,则直接忽略这种情况(需要考虑上下尖点问题)
2)如果射线水平,则射线要么与其无交点,要么有无数个,这种情况也直接忽略。
3)如果射线竖直,而P0的横坐标小于P1,P2的横坐标,则必然相交。
4)再判断相交之前,先判断P是否在边(P1,P2)的上面,如果在,则直接得出结论:P再多边形内部。
此处我实现了后两种方式:
想象一个凸多边形,其每一个边都将整个2D屏幕划分成为左右两边,连接每一边的第一个端点和要测试的点得到一个矢量v,将两个2维矢量扩展成3维的,然后将该边与v叉乘,判断结果3维矢量中Z分量的符号是否发生变化,进而推导出点是否处于凸多边形内外。这里要注意的是,多边形顶点究竟是左手序还是右手序,这对具体判断方式有影响。
2. 面积判别法(只适用于凸多边形)
第四点分别与三角形的两个点组成的面积分别设为S1,S2,S3,只要S1+S2+S3>原来的三角形面积就不在三角形范围中.可以使用海伦公式 。推广一下是否可以得到面向凸多边形的算法?(不确定)
3. 角度和判别法(适用于任意多边形)
该点与没对相邻顶点的夹角之和为360,则在内,否则在外
4. 水平/垂直交叉点数判别法(适用于任意多边形)
注意到如果从P作水平向左的射线的话,如果P在多边形内部,那么这条射线与多边形的交点必为奇数,如果P在多边形外部,则交点个数必为偶数(0也在内)。所以,我们可以顺序考虑多边形的每条边,求出交点的总个数。还有一些特殊情况要考虑。假如考虑边(P1,P2),
1)如果射线正好穿过P1或者P2,那么这个交点会被算作2次,处理办法是如果P的从坐标与P1,P2中较小的纵坐标相同,则直接忽略这种情况(需要考虑上下尖点问题)
2)如果射线水平,则射线要么与其无交点,要么有无数个,这种情况也直接忽略。
3)如果射线竖直,而P0的横坐标小于P1,P2的横坐标,则必然相交。
4)再判断相交之前,先判断P是否在边(P1,P2)的上面,如果在,则直接得出结论:P再多边形内部。
此处我实现了后两种方式:
#include <iostream> #include <vector> #include <cmath> #include <algorithm> #define MIN_NUMBER (10e-4) struct MYPOINT { float x; float y; }; float getAngle_AOB(MYPOINT &pA,MYPOINT &pO,MYPOINT &pB); bool isOnLineAB(MYPOINT &po,MYPOINT &pa,MYPOINT &pb); bool isInPolygon_crossPoint(const std::vector<MYPOINT> & poly, MYPOINT p); bool isInPolygon_angle(const std::vector<MYPOINT> & poly, MYPOINT p); int main(int argc, const char * argv[]) { std::vector<MYPOINT> polygon; MYPOINT p1; p1.x = 0; p1.y = 0; MYPOINT p2; p2.x = 1; p2.y = 5; MYPOINT p3; p3.x = 5; p3.y = 5; MYPOINT p4; p4.x = 5; p4.y = 1; // 顺次加入顶点 polygon.push_back(p1); polygon.push_back(p2); polygon.push_back(p3); polygon.push_back(p4); MYPOINT curPoint; curPoint.x = 5; curPoint.y = 5; if ( isInPolygon_crossPoint(polygon, curPoint) ) { std::cout << "the point is in polygon.\n"; } else { std::cout << "the point is out of polygon.\n"; } if ( isInPolygon_angle(polygon, curPoint) ) { std::cout << "the point is in polygon.\n"; } else { std::cout << "the point is out of polygon.\n"; } return 0; } /** 水平/垂直交叉点数判别法(适用于任意多边形) @功能:判断点是否在多边形内(多边形顶点需要顺/逆时针顺序排列) @方法:求解通过该点的水平线与多边形各边的交点 @结论:单边交点为奇数,成立!(右侧交点) */ bool isInPolygon_crossPoint(const std::vector<MYPOINT> & poly, MYPOINT p) { int nCross = 0; bool isOnLine = false; int nCount = (int)poly.size(); for (int i = 0; i < nCount; i++) { MYPOINT p1 = poly[i]; MYPOINT p2 = poly[(i + 1) % nCount]; // 首先判断是否在线段上 if (isOnLineAB(p, p1, p2)) { isOnLine = true; break; } // 求解 y=p.y 与 p1p2 的交点 if ( p1.y == p2.y ) // p1p2 与 y=p0.y平行 continue; if ( p.y < fminf(p1.y, p2.y) ) // 交点在p1p2延长线上,或端点上 continue; if ( p.y > fmaxf(p1.y, p2.y) ) // 交点在p1p2延长线上,或端点上 continue; // 求交点的 X 坐标 float x = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; // p点在线段p1p2上,即:在多边形边界上 if ( fabsf(p.x-x) < MIN_NUMBER ) { isOnLine = true; break; } // 交点在p1/p2上,忽略一次 if (fabsf(p1.y-p.y) < MIN_NUMBER) { // 考虑点p 在 上尖点、下尖点 的正右侧 情况 float maxY = poly[0].y; float minY = poly[0].y; for (int i=1; i<nCount; ++i) { if (maxY<poly[i].y) { maxY = poly[i].y; } if (minY>poly[i].y) { minY = poly[i].y; } } if ( (minY==p1.y || maxY==p1.y) && p.x<p1.x) { isOnLine = false; break; } continue; } // 只统计单边交点 : 统计右侧交点 if ( x > p.x ) { nCross++; } } // end loop // 点在边上;单边交点为偶数,则点在多边形外 if ( isOnLine || (nCross % 2 == 1) ) { return true; } return false; } /** 角度和判别法(适用于任意多边形) 计算这个点与多边形的点的连线的夹角之和,角度和为360度在内部,否则在外部。 */ bool isInPolygon_angle(const std::vector<MYPOINT> & poly, MYPOINT p) { float sumAngle = 0; bool isOnLine =false; int nCount = (int)poly.size(); for (int i=0; i<nCount; ++i) { MYPOINT p1 = poly[i]; MYPOINT p2 = poly[(i + 1) % nCount]; // 首先判断是否在线段上 if (isOnLineAB(p, p1, p2)) { isOnLine = true; break; } sumAngle += getAngle_AOB(p1, p, p2); } //如果角度和等于360度则在内部,否则在外部 / 在多边形上 if( isOnLine || (fabsf(sumAngle - M_PI * 2) < MIN_NUMBER) ) { return true; } return false; } /**求 AOB 的角度*/ float getAngle_AOB(MYPOINT &pA,MYPOINT &pO,MYPOINT &pB) { float oa = sqrtf( powf((pA.y - pO.y),2) + powf((pA.x - pO.x),2) ); float ob = sqrtf( powf((pB.y - pO.y),2) + powf((pB.x - pO.x),2) ); float ab = sqrtf( powf((pA.y - pB.y),2) + powf((pA.x - pB.x),2) ); float cos_AOB = (oa*oa + ob*ob - ab*ab) / (2 * oa * ob); return acosf(cos_AOB);; } /** @检测点po是否在线段AB上 判断二维坐标系中是否三个点在一条直线上: { area(ABC) = 1/2 * ( AC X BC ) = 1/2 *((ax-cx)*(by-cy)-(bx-cx)*(ay-cy)) 判断 (ax-cx)*(by-cy) == (bx-cx)*(ay-cy) 即可。 AC X BC 为两矢量的叉积 } */ bool isOnLineAB(MYPOINT &po,MYPOINT &pa,MYPOINT &pb) { if ( po.x>=fminf(pa.x, pb.x) && po.x<=fmaxf(pa.x, pb.x) && po.y>=fminf(pa.y, pb.y) && po.y<=fmaxf(pa.y, pb.y) ) { float area1 = (pa.x-po.x)*(pb.y-po.y); float area2 = (pb.x-po.x)*(pa.y-po.y); if ( fabsf(area1-area2)< MIN_NUMBER ) { return true; } return false; } else return false; }
相关文章推荐
- 2D空间中判断一点是否在三角形内
- 2D空间中求一点是否在多边形内
- c# 判断点是否在区域内 点在区域内 在多边形内 判断
- 判断点是否在多边形内
- hdu 2108 Shape of HDU(判断多边形是否为凸多边形)
- 判断点是否在多边形中
- 判断点是否在多边形内部
- 射线法判断一个点是否在多边形内
- POJ 3335 判断一个多边形是否存在核
- 判断点是否位于多边形内部
- cocos2d-x 学习笔记-判断点击点是否在不规则多边形中
- 要判断点是否落在多边形的关系
- PostGis 如何判断两个多边形是否有重叠的区域
- 用Oracle的函数,判断点是否在多边形内
- hdu 2108 Shape of HDU【判断多边形是否是凸多边形模板】
- FZU1120 A Pilot in Danger! 判断点是否在多边形中
- 判断一个点是否在多边形内部 [2] 射线法实现
- 90. 1.不开辟用于交换数据的临时空间 2.字符串的逆序 3判断单链表中是否存在环
- [ACM] hdu Cupid's Arrow (判断点是否在多边形内)
- 判断空间上点是否在直线上