给定2D平面上的一个闭合多边形, 和一个不在多边形边界上的点p0(x0, y0), 想要知道点是在外面还是里面
方法是射线法, 就是做一条以p0为起点的射线, 射向任意一个方向, 如果射线和多边形的交点数目为奇数, 说明p0在里面, 否则在外面.
所以,我们可以枚举多边形的每一条边界,看看射线是不是穿过,把射线穿过的边界数统计出来。
原理理解不难, 关键是有些细节需要注意。如下图的c, d, e, f
为了方便,不妨规定射线总是平行于x轴,而且从p0射向负无穷的方向。
假定现在我们画出了那条从p0射出的射线,称作originL
如果originL穿过了多边形的顶点(也就是多边形的某两条边界的公共端点)或者穿过多边形上某些平行于x轴的边界重合的话,会导致我们最后计算出来的交点数错误,为了消除这种特殊情况,我们采取一个特别的策略。
先引入两个定理。
定理1:当p0不在多边形的边界上的时候,总是存在一个足够小的正数e1,使得“p0上移距离e以后,p0与多边形的位置关系不变”满足
定理2: 当originL穿过了多边形的顶点(也就是多边形的某两条边界的交点)或者和多边形上那些平行于x轴的边界重合,总是存在一个足够小的正数e2, 使得“originL上移距离e2以后,originL不穿过多边形的任意顶点并且不穿过多边形的任意一条与x轴平行的边界”满足
这两个定理的正确性不难想象出来。
我们先找出一个恰当的e,这个e是根据定理1和定理2里得到的e1和e2的较小值,也就是,e = min{e1,e2},这个e的具体数值算法里用不到,但是这个数的存在性是正确的。我们将p0上移e,然后画出一条新的射线newL,这样,我们就把问题转化为求newL与多边形的交点数目,而且,newL是不会出现“穿过了多边形的顶点或者穿过了多边形上那些平行于x轴的边界”的特殊情况的!
但是, 由于直接计算一个恰当的e非常困难, 所以要用间接的方法判断newL与某条边界的关系。
具体的流程是:
枚举多边形的每条边界E:
=> 如果E是与x轴平行的,那么newL和E不相交
=> 否则,如果E不是在p0的左侧, 那么newL和E不相交。
=> 否则, 如果originL穿过E的下端点 或者 穿过E的下端点和上端点的中间部分(不包括上端点),那么newL和E有一个交点。
=> 否则, newL和E没有交点
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
struct Seg {
int x1, y1, x2, y2;
};
Seg newSeg(int x1, int y1, int x2, int y2) {
Seg s;
s.x1 = x1;
s.y1 = y1;
s.x2 = x2;
s.y2 = y2;
return s;
}
//=======================
vector< Seg > xSegs; //与x轴平行的那些Segment
vector< Seg > ySegs;
int N;
int tX, tY;
//=============================
bool belongs() {
Seg cur;
int i;
for (i = 0; i < xSegs.size(); ++i) {
cur = xSegs[i];
if (tY == cur.y1 && cur.x1 <= tX && tX <= cur.x2) return true;
}
for (i = 0; i < ySegs.size(); ++i) {
cur = ySegs[i];
if (tX == cur.x1 && cur.y1 <= tY && tY <= cur.y2) return true;
}
return false;
}
bool inside() {
int i;
Seg cur;
int crossNum = 0;
for (i = 0; i < ySegs.size(); ++i) {
cur = ySegs[i];
if (cur.x1 < tX && cur.y1 <= tY && tY < cur.y2) ++crossNum; // <= 注意看这里 tY < cur.y2
}
if (crossNum % 2 == 1) return true;
else return false;
}
void input() {
scanf("%d", &N);
int i;
int x1, x2, y1, y2;
xSegs.clear();
ySegs.clear();
for (i = 0; i < N; ++i) {
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
if (x1 == x2) {
if (y1 > y2) swap(y1, y2);
ySegs.push_back(newSeg(x1, y1, x2, y2));
}
else {
if (x1 > x2) swap(x1, x2);
xSegs.push_back(newSeg(x1, y1, x2, y2));
}
}
scanf("%d %d", &tX, &tY);
}
int main() {
input();
if (belongs()) {
printf("BORDER\n");
} else if (inside()) {
printf("INSIDE\n");
} else {
printf("OUTSIDE\n");
}
return 0;
}