题目
解答
#include<stdio.h>
#include<stdlib.h>
#define EXP 1e-8 //精度
typedef struct Point{
double x;
double y;
struct Point *next;
}Point, *Polygon; //多边形就是点的集合
/*
创建多边形【将构成多边形的一系列点存储在一个循环队列中】
返回1:创建成功
返回0:创建不成功,点没有闭合
*/
int CreatePolygon(char *filename, Polygon *pHead){ //由于多边形头指针pHead会发生改变,因此用二级指针
FILE *fp;
int n;
int i;
Point *p,*q;
double a,b;
double suba, subb;
fp = fopen(filename, "r");
if(!fp)
exit(0);
/*n=构成多边形的点数+1【由于原点要遇到两次,因此+1】【构成多边形点数要>=3】*/
fscanf(fp,"%d", &n);
p = NULL;
for(i=0; i<n; i++){
/*读取第i个点的x坐标【a】和y坐标【b】*/
fscanf(fp,"%lf%lf", &a, &b);
/*分配一个点的空间并赋予其坐标属性*/
q = (Point*)malloc(sizeof(Point));
if(!q)
exit(0);
q->x = a;
q->y = b;
/*将各个点连接成循环队列【队列先进先出】*/
if(p==NULL){ //第一个点
q->next = q; //将第一个点q的next指向它自己
p = q; //令p指向q【p始终指向队尾】
*pHead = q; //令队头结点指针指向q
}
else{ //第2—>n个点
/*第1个结点在队头*/
suba = (*pHead)->x - a; //将【队头结点的x坐标-a】赋给suba
subb = (*pHead)->y - b; //将【队头结点的y坐标-b】赋给subb
/*若第一个结点和最后一个结点重合,即x、y坐标均相等,说明多边形闭合了*/
/*因此按理来讲suba和subb都应为0*/
/*但我们用EXP控制精度【达到了这个精度,就可以认为这两个点是重合的】*/
if(suba>=-EXP && suba<=EXP && subb>=-EXP && subb<=EXP){
free(q); //释放这个重合的结点【第二次遇到的这个】
return 1; //构造成功,返回即可
}
p->next = q; //将新结点q挂在p后面【p始终指向队尾】
q->next = *pHead; //新结点q的next指向队头,构成循环队列
p = q; //p指向新结点q,使其始终指向队尾
}
}
fclose(fp);
return 0;
}
/*射线法判断点A是否在多边形内部【假设从点A向右作射线】*/
int PointInPolygon(Point A, Polygon head){
Point *p, *p1, *p2;
int cnt; //从A点向右作的射线与多边形的交点个数
double ymax;
double ymin;
double x;
cnt=0;
p = head; //令p指针指向头结点
do{
p1 = p; //令p1指针指向前一个结点
p2 = p->next; //令p2指针指向后一个结点
//将y坐标值大的赋给ymax,小的赋给ymin
if(p1->y > p2->y){
ymax = p1->y;
ymin = p2->y;
}
else{
ymax = p2->y;
ymin = p1->y;
}
/*如果从点A向右作的射线【或其反向延长线】不与直线p1p2相交【只有一个交点】,则令p指向下一个点,进行下一个线段的判断*/
/*我们用EXP控制精度【达到了这个精度,就可以认为这两个点的y坐标是相等的】*/
if( p1->y - p2->y >= -EXP && p1->y - p2->y <= EXP ){ //p1与p2所指点的y坐标相等
p = p->next; //指针p指向下一个点
continue;
}
else if( A.y < ymin ){ //点A与直线p1p2的交点在p1p2延长线上【下面的延长线】
p = p->next; //指针p指向下一个点
continue;
}
else if( A.y >= ymax ){ //点A与直线p1p2的交点在p1p2延长线上【上面的延长线】【注意是大于等于】
p = p->next; //指针p指向下一个点
continue;
}
/*求直线p1p2与直线y=A.y的交点的x坐标【根据两点式方程求交点坐标】*/
x = (double)(p2->x - p1->x) / (double)(p2->y - p1->y) * (double)(A.y - p1->y) + p1->x;
if(x > A.x) //如果交点在点A的右侧【因为我们是向右作射线的,因此只计算与右边相交的个数】
cnt++; //从A点向右作的射线与多边形的交点个数++
p = p->next; //指针p指向下一个点,继续判断下一个线段
}while(p!=head); //当p不指向头结点时【指向头结点表明队列遍历完毕】
/*当射线与多边形的交点个数为偶数时,返回0【说明A点在多边形外】*/
/*当射线与多边形的交点个数为奇数时,返回1【说明A点在多边形内】*/
return cnt%2==1;
}
int main(){
Point A;
Polygon P;
CreatePolygon("2013-05_Polygon.txt",&P);
printf("输入点A的坐标:\n>>> ");
scanf("%lf%lf", &A.x, &A.y);
if( PointInPolygon(A, P))
printf("A点在多边形内部\n");
else
printf("A点在多边形外部\n");
return 0;
}