优化-v2
为了优化该算法,我们可以利用扫描线算法的改进版,减少冗余计算并提升效率。扫描线算法的基本思想是通过水平线段逐行扫描多边形的每一行,并确定每行中哪些点在多边形内。
我们将使用一种称为“边表”(Edge Table,简称 ET)的数据结构来记录每条边的起点和终点,以及扫描线的当前交点。这样可以避免重复计算每个点是否在多边形内部。
以下是优化后的 C++ 实现:
#include <iostream> #include <vector> #include <algorithm> #include <map> #include <cmath> struct Point { int x, y; Point(int _x, int _y) : x(_x), y(_y) {} }; struct Edge { int ymax; double x; double dx; Edge(int _ymax, double _x, double _dx) : ymax(_ymax), x(_x), dx(_dx) {} }; void addEdgeToTable(std::map<int, std::vector<Edge>>& edgeTable, int y, int ymax, double x, double dx) { edgeTable[y].emplace_back(ymax, x, dx); } void buildEdgeTable(const std::vector<Point>& polygon, std::map<int, std::vector<Edge>>& edgeTable) { size_t n = polygon.size(); for (size_t i = 0; i < n; ++i) { const Point& p1 = polygon[i]; const Point& p2 = polygon[(i + 1) % n]; if (p1.y == p2.y) continue; // 忽略水平线 int ymin = std::min(p1.y, p2.y); int ymax = std::max(p1.y, p2.y); double x = (p1.y < p2.y) ? p1.x : p2.x; double dx = static_cast<double>(p2.x - p1.x) / (p2.y - p1.y); addEdgeToTable(edgeTable, ymin, ymax, x, dx); } } void scanlineFill(const std::map<int, std::vector<Edge>>& edgeTable, std::vector<Point>& inPolygonVec) { std::vector<Edge> activeEdgeTable; for (auto it = edgeTable.begin(); it != edgeTable.end(); ++it) { int y = it->first; const std::vector<Edge>& edges = it->second; for (const auto& edge : edges) { activeEdgeTable.push_back(edge); } activeEdgeTable.erase( std::remove_if(activeEdgeTable.begin(), activeEdgeTable.end(), [y](const Edge& e) { return e.ymax <= y; }), activeEdgeTable.end() ); std::sort(activeEdgeTable.begin(), activeEdgeTable.end(), [](const Edge& e1, const Edge& e2) { return e1.x < e2.x; }); for (size_t i = 0; i < activeEdgeTable.size(); i += 2) { int xStart = static_cast<int>(std::ceil(activeEdgeTable[i].x)); int xEnd = static_cast<int>(std::floor(activeEdgeTable[i + 1].x)); for (int x = xStart; x <= xEnd; ++x) { inPolygonVec.emplace_back(x, y); } } for (auto& edge : activeEdgeTable) { edge.x += edge.dx; } } } void findPointsInsidePolygon(const std::vector<Point>& polygon, std::vector<Point>& inPolygonVec) { std::map<int, std::vector<Edge>> edgeTable; buildEdgeTable(polygon, edgeTable); scanlineFill(edgeTable, inPolygonVec); } int main() { std::vector<Point> vecPoint = {{100, 100}, {200, 100}, {200, 200}, {100, 200}}; std::vector<Point> inPolygonVec; findPointsInsidePolygon(vecPoint, inPolygonVec); for (const auto& p : inPolygonVec) { std::cout << "(" << p.x << ", " << p.y << ")\n"; } return 0; }
优化思路解释
-
构建边表(Edge Table):
- 我们遍历多边形的每条边,跳过水平边,记录每条边的起点(较低的 y 值),终点(较高的 y 值),起点 x 坐标,以及 dx(x 的增量)。
- 使用
std::map
将边按起点的 y 坐标进行分类。
-
扫描线填充(Scanline Fill):
- 我们从最低的 y 坐标开始,逐行扫描到最高的 y 坐标。
- 在每一行中,更新当前的活动边表(Active Edge Table,简称 AET),添加新的边并移除已经处理完的边。
- 根据 AET 中的边,确定当前行中哪些 x 范围在多边形内部,并将这些点加入结果向量中。
- 最后,更新每条边的 x 坐标,为下一行扫描做准备。
这样的方法减少了冗余计算,并显著提高了效率
#include <iostream> #include "calcv1.h" #include "calcv2.h" #include <unordered_map> #include <string> using namespace std; void testV1() { std::vector<Point> vecPoint = { {100, 100}, {200, 100}, {200, 200}, {100, 200},{300,100},{300,120},{10,300} }; std::vector<Point> inPolygonVec; findPointsInsidePolygonV1(vecPoint, inPolygonVec); int iPointCount = 0; int iRepeatCount = 0; std::unordered_map<std::string, int> pointMap; for (const auto& p : inPolygonVec) { //std::cout << "(" << p.x << ", " << p.y << ")\n"; std::string key = std::to_string(p.x) + "_" + std::to_string(p.y); if (pointMap.find(key) == pointMap.end()) { pointMap[key] = 1; ++iPointCount; } else { ++iRepeatCount; } } std::cout << "inPolygonVec size:" << inPolygonVec.size() << ",iPointCount:" << iPointCount << ",iRepeatCount:" << iRepeatCount << std::endl; } void testV2() { std::vector<Point> vecPoint = { {100, 100}, {200, 100}, {200, 200}, {100, 200} }; std::vector<Point> inPolygonVec; findPointsInsidePolygonV2(vecPoint, inPolygonVec); for (const auto& p : inPolygonVec) { std::cout << "(" << p.x << ", " << p.y << ")\n"; } } int main(){ testV1(); std::cout << "hello c++" << std::endl; return 0; }
#pragma once struct Point { int x, y; Point(int _x, int _y) : x(_x), y(_y) {} Point() : x(0), y(0) {} }; struct Edge { int ymax; double x; double dx; Edge(int _ymax, double _x, double _dx) : ymax(_ymax), x(_x), dx(_dx) {} Edge() : ymax(0), x(0.0f), dx(0.0f) {} };