多边形由多个线段确定,在多边形内部可能有0或多个挖孔。
判断一个点是否在多边形内部,但不位于挖孔内部。
思路:
从点出发作向右延伸的射线,判断射线经过的线段个数。
交点为奇数则在内部,偶数则在外部。
需要用到直线方程的两点式
#include <iostream>
#include <vector>
#include <list>
using namespace std;
template<class T>
struct Point
{
T x;
T y;
};
template<class T>
class Polygon
{
private:
list<Point<T>> points; // 外环点集合
vector<list<Point<T>>> inners; // 内环点集合
public:
void AddPoint(const Point<T>& point)
{
points.push_back(point);
}
void AddInnerPolygon(const list<Point<T>>& innerPolygon)
{
inners.push_back(innerPolygon);
}
// 列出所有边
void ListEdges()
{
cout << "多边形的边:" << endl;
auto nextPoint = points.begin();
for (const auto& point : points)
{
nextPoint++;
if (nextPoint == points.end())
nextPoint = points.begin();
cout << "(" << point.x << ", " << point.y << ") - (" << nextPoint->x << ", " << nextPoint->y << ")" << endl;
}
}
bool IsInPolygon(const Point<T>& targetPoint)
{
bool isInOuterPolygon = IsInPolygonHelper(points, targetPoint);
if (!isInOuterPolygon)
return false;
for (const auto& inner : inners)
{
if (IsInPolygonHelper(inner, targetPoint))
return false;
}
return true;
}
private:
bool IsInPolygonHelper(const list<Point<T>>& polygon, const Point<T>& targetPoint)
{
int intersectCount = 0;
auto toPoint = polygon.begin();
for (const auto& fromPoint : polygon)
{
toPoint++;
if (toPoint == polygon.end())
toPoint = polygon.begin();
// 检查射线与多边形的边是否相交
// 仅当检测点的垂直坐标位于线段之前才有可能相交
if ((fromPoint.y <= targetPoint.y) && (toPoint->y >= targetPoint.y) ||
(fromPoint.y >= targetPoint.y) && (toPoint->y <= targetPoint.y) )
{
// 根据直线方程的两点式,代入射线(向右延伸)的方程,得到交点的X坐标
double interSecX = (toPoint->x - fromPoint.x) / (toPoint->y - fromPoint.y)
* (targetPoint.y - fromPoint.y) + fromPoint.x;
if (targetPoint.x < interSecX)
intersectCount++;
}
}
return (intersectCount % 2) == 1;
}
};
int main()
{
// 创建一个多边形
Polygon<double> polygon;
// 添加外环的点
polygon.AddPoint({ 1.0, 1.0 });
polygon.AddPoint({ 5.0, 1.0 });
polygon.AddPoint({ 5.0, 4.0 });
polygon.AddPoint({ 3.0, 5.0 });
polygon.AddPoint({ 1.0, 4.0 });
// 添加一个内环
list<Point<double>> innerPolygon;
innerPolygon.push_back({ 2.0, 2.0 });
innerPolygon.push_back({ 4.0, 2.0 });
innerPolygon.push_back({ 4.0, 3.0 });
polygon.AddInnerPolygon(innerPolygon);
// 列出多边形的边
polygon.ListEdges();
// 测试一个点是否在多边形内部
Point<double> testPoint = { 0, 1 };
bool isInPolygon = polygon.IsInPolygon(testPoint);
if (isInPolygon)
cout << "点在多边形内部。" << endl;
else
cout << "点在多边形外部。" << endl;
return 0;
}