优化-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;
}

 

 

优化思路解释

  1. 构建边表(Edge Table)

    • 我们遍历多边形的每条边,跳过水平边,记录每条边的起点(较低的 y 值),终点(较高的 y 值),起点 x 坐标,以及 dx(x 的增量)。
    • 使用 std::map 将边按起点的 y 坐标进行分类。
  2. 扫描线填充(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) {}
};

 

posted @ 2024-08-08 07:29  He_LiangLiang  阅读(15)  评论(0编辑  收藏  举报