参考:【精选】判断地图上一个点是否在多边形内部--射线法_地图上算点是否在多边形内-CSDN博客
解决方案:以点为起点做水平向右的射线,判断射线与边的交点数,若交点数为奇数则点在多边形内,若交点数为偶数则点在多边形外。
特殊情况:
- 1.点与多边形的顶点重合视为点在多边形内
- 2.点与多边形的顶边重合视为点在多边形内
- 3.射线与任意一边的一个顶点重合,则判断边的另外一个顶点是否在交点的上方,若在上方则交点有效,否则舍弃
- 4.射线与边平行视为没有交点
实验结果:
关键代码:
#ifndef EPS
#define EPS 5E-5
#endif // ! ESP
template<typename T>
bool OnSegment(T P1, T P2, T Q)
{
double x1 = P1.x() - Q.x();
double y1 = P1.y() - Q.y();
double x2 = P2.x() - Q.x();
double y2 = P2.y() - Q.y();
return (x1 * y2- y1 * x2 == 0) && (x1 * x2 + y1 * y2 <= 0);
}
template<typename T>
bool judge_point_polygon(T* data, std::vector<std::pair<int,int>>& edge, T P){
int number = 0;
T P1,P2;
for(int i = 0; i < edge.size(); i++)
{
int id1 = edge[i].first;
int id2 = edge[i].second;
P1 = data[id1];
P2 = data[id2];
//点在线上
if(OnSegment(P1,P2,P))
return true;
//水平的边
if(P1.y() == P2.y())
continue;
//不相交
if ((P1.y() - P.y() > EPS) && (P2.y() - P.y() > EPS))
continue;
if ((P1.y() - P.y() < -EPS) && (P2.y() - P.y() < -EPS))
continue;
//不相交
double x =(P.y()-P1.y())*(P1.x()-P2.x())/(P1.y()-P2.y())+P1.x();
if(x <= P.x())
continue;
if(fabs(x - P1.x())<EPS && abs(P.y() - P1.y())<EPS){
if(P2.y() > P.y())
++number;
}
else if(fabs(x - P2.x())<EPS && abs(P.y() - P2.y())<EPS){
if(P1.y() > P.y())
++number;
}
else
++number;
}
return number%2 == 1;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::black);
for(auto& pair : edges){
QPoint p1 = pts[pair.first];
QPoint p2 = pts[pair.second];
painter.drawLine(p1, p2);
}
for(QPoint& pt : pt_test){
bool in = judge_point_polygon<QPoint>(pts.data(), edges, pt);
if(in){
painter.setBrush(Qt::blue);
painter.drawEllipse(pt, 5,5);
painter.drawText(pt+QPoint(5,0), "in");
}
else{
painter.setBrush(Qt::red);
painter.drawEllipse(pt, 5,5);
painter.drawText(pt+QPoint(5,0), "out");
}
}
}