C++ 计算几何 判断一个点是否在多边形内

多边形由多个线段确定,在多边形内部可能有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;
}
posted @ 2024-02-26 16:01  小松鼠树懒  阅读(446)  评论(0编辑  收藏  举报