点的定位
点的定位属于几何查找,是计算几何中的一个重要的问题。其包括点在三角形内外,多边形内外判断,平面剖分中的位置等。
关于出现在这篇文章中但没有给出的自定义函数,如:CroMul(V a,V b)等,上篇博客有提到,向量叉乘。
判断点是否在线上:
我们可以用刚刚我们提到的叉积重要性质,即:两向量叉积为0,说明两向量在一条直线上。因此:
设点Q在线段(P1,P2)上。则:
1.(Q-P1)(P2-P1)=0;(保证Q在线段(P1,P2)上)
2.Q在以P1,P2为对角顶点的矩形内。(保证Q不在线段(P1,P2)的延长线上)。
(画出来大概是这样)
Code:
int OnLine(Point P1,Point P2,Point Q){
if((Q.x-P1.x)*(P2.y-P1.y)==(P2.x-P1.x)*(Q.y-P1.y)){
if(min(P1.x,P2.x)<=Q.x&&Q.x<=max(P1.x,P2.x)
&&min(P1.y,P2.y)<=Q.y&&Q.y<=max(P1.x,P2.y)){
return 1;
}
}return 0;
}
判断点在三角形的内外:
点在平面内与三角形三个顶点中任意两点构成三个三角形。可以通过计算这三个三角形的面积和与原三角形面积来比较判断点是否在三角形内。
设△ABC,判断Q是否在三角形内(包含在某条边上)。则:
S△PAB+S△PAC+S△PBC=S△ABC,即可判定点Q在△ABC内。
S△ABC=(1/2)*|CroMul(a,b)|。其中a,b为三角形三点所确定的两个向量。
Code:
int InTriangle(Triangle Tri,Point P){
V AB,AC,PA,PB,PC;
AB.start=Tri.A;AB.end=Tri.B;
AC.start=Tri.A;AC.end=Tri.C;
PA.start=P;PA.end=Tri.A;
PB.start=P;PB.end=Tri.B;
PC.start=P;PC.end=Tri.C;
double Sabc=fabs(CroMul(AB,AC));
double Spab=fabs(CroMul(PA,PB));
double Spac=fabs(CroMul(PA,PC));
double Spbc=fabs(CroMul(PB,PC));
if(Spab+Spac+Spbc==Sabc) return 1;
return 0;
}
判断点在多边形的内外:
点在几何图形内外的判断中,多边形的判断是通用情况,分三种方法:1.扫描法,2.叉积判别法,3.角度和判断法。
1.扫描法
设点Q和多边形(P1,P2,P3,P4,P5,P6),判断点Q是否在多边形内。
首先以Q为端点,向任意方向作射线,由于多边形是有界的,所以射线一定会延伸到多边形外,若射线与多边形没有交点,则点在多边形外,若有1个交点,则在多边形内,若有两个交点,则在多边形外。
推论:当射线与多边形的交点数目是奇数时,Q在多边形内;若交点个数为偶数时,Q在多边形外。
我们用一个长的平行于x轴的线段来代替射线。
发现有一些情况要分类讨论,可以根据分类情况做出一些限制,例如:
1.多边形的水平边不做考虑。
2.对于与多边形顶点相交,如果该顶点是其所属边上纵坐标比较大的顶点,则计数,否则忽略。
3.对于Q在多边形边上的情形,直接可判断Q属于多边形。
Code:
int InPolygonScan(Point Q){//扫描法
int counter=0;double xinters;Point P1,P2;
P1=Poly[0];
for(int i=1;i<=n;++i){//遍历所有的点
P2=Poly[i%n];
if(OnLine(P1,P2,Q)) return 1;//在线上
if(Q.y>min(P1.y,P2.y)&&Q.y<=max(P1.y,P2.y)){//在线段内
if(Q.x<=max(P1.x,P2.x)){
if(P1.y!=P2.y){
xinters=(Q.y-P1.y)*(P2.x-P1.x)/(P2.y-P1.y)+P1.x;
if(P1.x==P2.x||Q.x<=xinters) counter++;//符合要求
}
}
}P1=P2;
}
if(counter%2==0) return 0;
return 1;
}
int main(){
int t=0;Point Q;
while(cin>>n){
if(n==0) break;
if(t++>0) cout<<endl;
cout<<"Problem "<<t<<":"<<endl;
cin>>m;
for(int i=0;i<n;++i){
cin>>Poly[i].x>>Poly[i].y;
}
for(int i=0;i<m;++i){
cin>>Q.x>>Q.y;
if(InPolygonScan(Q)) cout<<"Within"<<endl;
else cout<<"Outside"<<endl;
}
}
}
2.叉乘判别法(只适用于凸多边形)
一个凸多边形,每条边都将整个平面划分成为左右两边。设这个多边形的边数为n,选一条边作为1号边,然后按照顺时针或者逆时针的顺序给每条边进行编号。
连接第i条边的第一个端点P1和要测试的点Q连接成一个向量(P1,Q),连接P1和P2得向量(P1,P2)。做叉积。
除第一条边外,都与前一次运算得到的叉积做乘积。如果为正,继续判断,直到遍历所有的边。若全部满足,则证明点在多边形内,否则点在多变形外。
Code:
int InPolygonCroPro(Point Q){
//现在已经从选择一个点,逆时针||顺时针方向排好序了
int i;double pre,now;
for(int i=0;i<n;++i){//逆时针||顺时针遍历所有的点
Point P1=Poly[i],P2=Poly[i+1];
V v1=V(P1,Q),v2=V(P1,P2);
now=CroMul(v1,v2);
if(i>0){
if(pre*now<0) return 0;
}pre=now;
}return 1;
}
3.角度和的判断法(适用于任意多边形,注意精度损失)
对于平面三角形来说,连接多边形内点与多边形所有顶点所形成的所有角的角度和在要求精度范围内应该=360°,如果大于||小于360°,则证明点不在多边形中。
Code:
int InPolygonAngle(Point Q){//角度判别法
double Angle=0;
realPointList::interator iter1.begin();
for(realPointList::interator iter2=(iter1+1);iter2<Points.end();++iter1,++iter2){
double x1=(*iter1).x-Q.x,y1=(*iter1).y-Q.y;
double x2=(*iter2).x-Q.x,y2=(*iter2).y-Q.y;
Angle+=Angle2D(x1,x2,y1,y2);
}
if(fabs(Angle-span::PI2)<=0.01) return 1;
return 0;
}
判断点是否在矩形||圆中。
矩形:只用判断Q点的横坐标和纵坐标是否夹在矩形的左右边和上下边之间即可。
圆:只用判断圆心O到Q的距离与半径之间的关系即可。