基于 Weiler-Atherton 算法的多边形裁剪程序实现

Weiler-Atherton 多边形裁剪算法实现,包含凹多边形处理、多裁剪窗口支持、可视化调试等功能。

一、算法原理

Weiler-Atherton 算法是处理凹多边形裁剪的经典算法,主要步骤:

复制代码
Weiler-Atherton 算法流程:
1. 计算多边形与裁剪窗口的所有交点
2. 将交点分类为"进入点"和"离开点"
3. 从多边形上未处理的进入点开始追踪
4. 沿多边形边界移动直到遇到离开点
5. 切换到裁剪窗口边界,移动到下一个进入点
6. 重复直到回到起点
7. 处理多个分离的多边形区域

二、核心代码实现

2.1 数据结构定义 (PolygonClipping.h)

cpp 复制代码
#ifndef POLYGON_CLIPPING_H
#define POLYGON_CLIPPING_H

#include <vector>
#include <cmath>
#include <algorithm>
#include <limits>
#include <memory>
#include <functional>

// 二维点结构
struct Point {
    double x, y;
    
    Point() : x(0), y(0) {}
    Point(double x_, double y_) : x(x_), y(y_) {}
    
    bool operator==(const Point& other) const {
        return std::abs(x - other.x) < 1e-9 && std::abs(y - other.y) < 1e-9;
    }
    
    bool operator<(const Point& other) const {
        if (std::abs(x - other.x) > 1e-9) return x < other.x;
        return y < other.y;
    }
    
    Point operator+(const Point& other) const {
        return Point(x + other.x, y + other.y);
    }
    
    Point operator-(const Point& other) const {
        return Point(x - other.x, y - other.y);
    }
    
    Point operator*(double scalar) const {
        return Point(x * scalar, y * scalar);
    }
};

// 交点类型
enum IntersectionType {
    INTERSECTION_ENTERING = 0,  // 进入裁剪窗口
    INTERSECTION_LEAVING = 1,   // 离开裁剪窗口
    INTERSECTION_UNKNOWN = 2    // 未知类型
};

// 带属性的交点
struct IntersectionPoint {
    Point point;
    IntersectionType type;
    int polygonEdgeIndex;      // 在多边形边上的索引
    int clipEdgeIndex;        // 在裁剪窗口边上的索引
    bool processed;           // 是否已处理
    
    IntersectionPoint() : type(INTERSECTION_UNKNOWN), polygonEdgeIndex(-1), 
                         clipEdgeIndex(-1), processed(false) {}
};

// 多边形类
class Polygon {
public:
    std::vector<Point> vertices;
    
    Polygon() {}
    Polygon(const std::vector<Point>& verts) : vertices(verts) {}
    
    // 判断点是否在多边形内(射线法)
    bool contains(const Point& p) const {
        if (vertices.size() < 3) return false;
        
        int crossings = 0;
        for (size_t i = 0; i < vertices.size(); i++) {
            const Point& a = vertices[i];
            const Point& b = vertices[(i + 1) % vertices.size()];
            
            if (((a.y > p.y) != (b.y > p.y)) &&
                (p.x < (b.x - a.x) * (p.y - a.y) / (b.y - a.y) + a.x)) {
                crossings++;
            }
        }
        
        return (crossings % 2) == 1;
    }
    
    // 计算多边形面积(带符号)
    double signedArea() const {
        double area = 0.0;
        for (size_t i = 0; i < vertices.size(); i++) {
            const Point& a = vertices[i];
            const Point& b = vertices[(i + 1) % vertices.size()];
            area += (a.x * b.y - b.x * a.y);
        }
        return area / 2.0;
    }
    
    // 判断多边形是否为顺时针
    bool isClockwise() const {
        return signedArea() < 0;
    }
    
    // 确保多边形顶点顺序是逆时针
    void ensureCounterClockwise() {
        if (isClockwise()) {
            std::reverse(vertices.begin(), vertices.end());
        }
    }
    
    // 获取边界框
    void getBounds(double& minX, double& minY, double& maxX, double& maxY) const {
        if (vertices.empty()) {
            minX = minY = maxX = maxY = 0;
            return;
        }
        
        minX = maxX = vertices[0].x;
        minY = maxY = vertices[0].y;
        
        for (size_t i = 1; i < vertices.size(); i++) {
            minX = std::min(minX, vertices[i].x);
            maxX = std::max(maxX, vertices[i].x);
            minY = std::min(minY, vertices[i].y);
            maxY = std::max(maxY, vertices[i].y);
        }
    }
    
    // 添加顶点
    void addVertex(const Point& p) {
        vertices.push_back(p);
    }
    
    // 清除顶点
    void clear() {
        vertices.clear();
    }
    
    // 获取顶点数
    size_t size() const {
        return vertices.size();
    }
};

// 裁剪结果
struct ClippingResult {
    std::vector<Polygon> polygons;  // 裁剪后的多边形(可能多个)
    std::vector<IntersectionPoint> intersections;  // 所有交点
    bool success;                   // 是否成功
    std::string errorMessage;       // 错误信息
    
    ClippingResult() : success(false) {}
};

// Weiler-Atherton 裁剪器
class WeilerAthertonClipper {
public:
    WeilerAthertonClipper();
    ~WeilerAthertonClipper();
    
    // 设置多边形和裁剪窗口
    void setSubjectPolygon(const Polygon& subject);
    void setClipPolygon(const Polygon& clip);
    
    // 执行裁剪
    ClippingResult clip();
    
    // 设置选项
    void setTolerance(double tol) { tolerance = tol; }
    void setIncludeBoundary(bool include) { includeBoundary = include; }
    
    // 调试功能
    std::vector<IntersectionPoint> getIntersections() const { return allIntersections; }
    std::vector<Polygon> getIntermediatePolygons() const { return intermediatePolygons; }
    
private:
    // 多边形数据
    Polygon subjectPolygon;
    Polygon clipPolygon;
    
    // 处理后的多边形(带交点)
    std::vector<Point> subjectWithIntersections;
    std::vector<Point> clipWithIntersections;
    std::vector<IntersectionPoint> allIntersections;
    
    // 中间结果
    std::vector<Polygon> intermediatePolygons;
    
    // 选项
    double tolerance;
    bool includeBoundary;
    
    // 算法步骤
    void computeIntersections();
    void classifyIntersections();
    void tracePolygons(ClippingResult& result);
    
    // 几何计算
    bool computeLineIntersection(const Point& p1, const Point& p2,
                               const Point& q1, const Point& q2,
                               Point& intersection);
    
    IntersectionType classifyIntersection(const Point& intersection,
                                         const Point& edgeStart,
                                         const Point& edgeEnd,
                                         const Polygon& clipPoly);
    
    // 工具函数
    bool isPointOnSegment(const Point& p, const Point& a, const Point& b);
    double crossProduct(const Point& a, const Point& b, const Point& c);
    double dotProduct(const Point& a, const Point& b);
    double distance(const Point& a, const Point& b);
    bool areCollinear(const Point& a, const Point& b, const Point& c);
    
    // 追踪辅助函数
    bool findUnprocessedEnteringPoint(int& startIndex, int& polyIndex);
    void traceFromSubject(int startIdx, std::vector<Point>& output);
    void traceFromClip(int startIdx, std::vector<Point>& output);
    
    // 验证结果
    bool validatePolygon(const Polygon& poly);
};

#endif // POLYGON_CLIPPING_H

2.2 算法实现 (PolygonClipping.cpp)

cpp 复制代码
#include "PolygonClipping.h"
#include <unordered_set>
#include <stack>

WeilerAthertonClipper::WeilerAthertonClipper() 
    : tolerance(1e-9), includeBoundary(true) {
}

WeilerAthertonClipper::~WeilerAthertonClipper() {
}

void WeilerAthertonClipper::setSubjectPolygon(const Polygon& subject) {
    subjectPolygon = subject;
    subjectPolygon.ensureCounterClockwise();
}

void WeilerAthertonClipper::setClipPolygon(const Polygon& clip) {
    clipPolygon = clip;
    clipPolygon.ensureCounterClockwise();
}

ClippingResult WeilerAthertonClipper::clip() {
    ClippingResult result;
    
    try {
        // 检查输入有效性
        if (subjectPolygon.size() < 3 || clipPolygon.size() < 3) {
            result.success = false;
            result.errorMessage = "Polygons must have at least 3 vertices";
            return result;
        }
        
        // 计算交点
        computeIntersections();
        
        // 分类交点
        classifyIntersections();
        
        // 追踪多边形
        tracePolygons(result);
        
        result.success = true;
        result.intersections = allIntersections;
        
    } catch (const std::exception& e) {
        result.success = false;
        result.errorMessage = e.what();
    }
    
    return result;
}

void WeilerAthertonClipper::computeIntersections() {
    allIntersections.clear();
    subjectWithIntersections.clear();
    clipWithIntersections.clear();
    
    // 计算多边形每条边与裁剪窗口每条边的交点
    for (size_t i = 0; i < subjectPolygon.size(); i++) {
        const Point& p1 = subjectPolygon.vertices[i];
        const Point& p2 = subjectPolygon.vertices[(i + 1) % subjectPolygon.size()];
        
        for (size_t j = 0; j < clipPolygon.size(); j++) {
            const Point& q1 = clipPolygon.vertices[j];
            const Point& q2 = clipPolygon.vertices[(j + 1) % clipPolygon.size()];
            
            Point intersection;
            if (computeLineIntersection(p1, p2, q1, q2, intersection)) {
                // 检查交点是否已经在列表中
                bool found = false;
                for (const auto& ip : allIntersections) {
                    if (distance(ip.point, intersection) < tolerance) {
                        found = true;
                        break;
                    }
                }
                
                if (!found) {
                    IntersectionPoint ip;
                    ip.point = intersection;
                    ip.polygonEdgeIndex = static_cast<int>(i);
                    ip.clipEdgeIndex = static_cast<int>(j);
                    ip.processed = false;
                    
                    allIntersections.push_back(ip);
                }
            }
        }
    }
    
    // 将交点插入到多边形顶点序列中
    subjectWithIntersections = subjectPolygon.vertices;
    clipWithIntersections = clipPolygon.vertices;
    
    // 按距离排序插入交点
    for (const auto& ip : allIntersections) {
        // 插入到多边形边中
        const Point& p1 = subjectPolygon.vertices[ip.polygonEdgeIndex];
        const Point& p2 = subjectPolygon.vertices[(ip.polygonEdgeIndex + 1) % subjectPolygon.size()];
        
        // 计算交点在边上的位置
        double t = distance(p1, ip.point) / distance(p1, p2);
        
        // 插入到正确位置
        size_t insertPos = ip.polygonEdgeIndex + 1;
        if (insertPos <= subjectWithIntersections.size()) {
            subjectWithIntersections.insert(subjectWithIntersections.begin() + insertPos, ip.point);
        }
    }
}

void WeilerAthertonClipper::classifyIntersections() {
    for (auto& ip : allIntersections) {
        // 获取多边形边
        const Point& p1 = subjectPolygon.vertices[ip.polygonEdgeIndex];
        const Point& p2 = subjectPolygon.vertices[(ip.polygonEdgeIndex + 1) % subjectPolygon.size()];
        
        // 分类交点类型
        ip.type = classifyIntersection(ip.point, p1, p2, clipPolygon);
    }
}

void WeilerAthertonClipper::tracePolygons(ClippingResult& result) {
    result.polygons.clear();
    
    // 创建交点副本用于追踪
    std::vector<IntersectionPoint> intersections = allIntersections;
    
    // 处理所有未处理的进入点
    int startIdx, polyIdx;
    while (findUnprocessedEnteringPoint(startIdx, polyIdx)) {
        std::vector<Point> newPolygonVertices;
        
        // 从进入点开始追踪
        int currentIdx = startIdx;
        bool tracingSubject = true;  // 从多边形开始追踪
        
        do {
            if (tracingSubject) {
                traceFromSubject(currentIdx, newPolygonVertices);
                
                // 找到下一个交点(应该是离开点)
                for (auto& ip : intersections) {
                    if (ip.polygonEdgeIndex == currentIdx && 
                        ip.type == INTERSECTION_LEAVING && !ip.processed) {
                        ip.processed = true;
                        currentIdx = ip.polygonEdgeIndex;
                        tracingSubject = false;  // 切换到裁剪窗口
                        break;
                    }
                }
            } else {
                traceFromClip(currentIdx, newPolygonVertices);
                
                // 找到下一个交点(应该是进入点)
                for (auto& ip : intersections) {
                    if (ip.clipEdgeIndex == currentIdx && 
                        ip.type == INTERSECTION_ENTERING && !ip.processed) {
                        ip.processed = true;
                        currentIdx = ip.polygonEdgeIndex;
                        tracingSubject = true;  // 切换回多边形
                        break;
                    }
                }
            }
            
        } while (!intersections[startIdx].processed);
        
        // 创建新多边形
        if (newPolygonVertices.size() >= 3) {
            Polygon newPoly(newPolygonVertices);
            if (validatePolygon(newPoly)) {
                result.polygons.push_back(newPoly);
                intermediatePolygons.push_back(newPoly);
            }
        }
    }
}

bool WeilerAthertonClipper::findUnprocessedEnteringPoint(int& startIndex, int& polyIndex) {
    for (size_t i = 0; i < allIntersections.size(); i++) {
        if (allIntersections[i].type == INTERSECTION_ENTERING && 
            !allIntersections[i].processed) {
            startIndex = allIntersections[i].polygonEdgeIndex;
            polyIndex = static_cast<int>(i);
            allIntersections[i].processed = true;
            return true;
        }
    }
    return false;
}

void WeilerAthertonClipper::traceFromSubject(int startIdx, std::vector<Point>& output) {
    // 从起点开始,沿着多边形边追踪直到遇到下一个交点
    int idx = startIdx;
    const Point& startPoint = subjectPolygon.vertices[idx];
    const Point& nextPoint = subjectPolygon.vertices[(idx + 1) % subjectPolygon.size()];
    
    output.push_back(startPoint);
    
    // 检查这条边是否有交点
    bool hasIntersection = false;
    for (const auto& ip : allIntersections) {
        if (ip.polygonEdgeIndex == idx && ip.type == INTERSECTION_LEAVING) {
            output.push_back(ip.point);
            hasIntersection = true;
            break;
        }
    }
    
    if (!hasIntersection) {
        output.push_back(nextPoint);
    }
}

void WeilerAthertonClipper::traceFromClip(int startIdx, std::vector<Point>& output) {
    // 从起点开始,沿着裁剪窗口边追踪直到遇到下一个交点
    int idx = startIdx;
    const Point& startPoint = clipPolygon.vertices[idx];
    const Point& nextPoint = clipPolygon.vertices[(idx + 1) % clipPolygon.size()];
    
    output.push_back(startPoint);
    
    // 检查这条边是否有交点
    bool hasIntersection = false;
    for (const auto& ip : allIntersections) {
        if (ip.clipEdgeIndex == idx && ip.type == INTERSECTION_ENTERING) {
            output.push_back(ip.point);
            hasIntersection = true;
            break;
        }
    }
    
    if (!hasIntersection) {
        output.push_back(nextPoint);
    }
}

bool WeilerAthertonClipper::computeLineIntersection(const Point& p1, const Point& p2,
                                                   const Point& q1, const Point& q2,
                                                   Point& intersection) {
    // 计算线段交点
    double dx1 = p2.x - p1.x;
    double dy1 = p2.y - p1.y;
    double dx2 = q2.x - q1.x;
    double dy2 = q2.y - q1.y;
    
    double denominator = dx1 * dy2 - dy1 * dx2;
    
    // 平行或重合
    if (std::abs(denominator) < tolerance) {
        return false;
    }
    
    double t = ((q1.x - p1.x) * dy2 - (q1.y - p1.y) * dx2) / denominator;
    double u = ((q1.x - p1.x) * dy1 - (q1.y - p1.y) * dx1) / denominator;
    
    // 检查交点是否在线段上
    if (t < -tolerance || t > 1.0 + tolerance || u < -tolerance || u > 1.0 + tolerance) {
        return false;
    }
    
    intersection.x = p1.x + t * dx1;
    intersection.y = p1.y + t * dy1;
    
    return true;
}

IntersectionType WeilerAthertonClipper::classifyIntersection(const Point& intersection,
                                                           const Point& edgeStart,
                                                           const Point& edgeEnd,
                                                           const Polygon& clipPoly) {
    // 计算多边形边的中点
    Point midPoint = (edgeStart + edgeEnd) * 0.5;
    
    // 计算从交点到中点的向量
    Point toMid = midPoint - intersection;
    
    // 计算多边形边的法向量(指向多边形内部)
    Point edgeVector = edgeEnd - edgeStart;
    Point normal(-edgeVector.y, edgeVector.x);  // 逆时针多边形的法向量指向内部
    
    // 如果多边形是顺时针,法向量方向相反
    Polygon tempPoly;
    tempPoly.addVertex(edgeStart);
    tempPoly.addVertex(edgeEnd);
    tempPoly.addVertex(midPoint);
    if (tempPoly.isClockwise()) {
        normal = Point(edgeVector.y, -edgeVector.x);
    }
    
    // 标准化法向量
    double length = std::sqrt(normal.x * normal.x + normal.y * normal.y);
    if (length > tolerance) {
        normal.x /= length;
        normal.y /= length;
    }
    
    // 计算点积
    double dot = dotProduct(toMid, normal);
    
    // 如果点积为正,说明中点在外侧,交点是进入点
    // 如果点积为负,说明中点在内侧,交点是离开点
    return (dot > 0) ? INTERSECTION_ENTERING : INTERSECTION_LEAVING;
}

bool WeilerAthertonClipper::validatePolygon(const Polygon& poly) {
    if (poly.size() < 3) return false;
    
    // 检查面积
    double area = std::abs(poly.signedArea());
    if (area < tolerance) return false;
    
    // 检查自相交(简化版)
    for (size_t i = 0; i < poly.size(); i++) {
        const Point& a = poly.vertices[i];
        const Point& b = poly.vertices[(i + 1) % poly.size()];
        
        for (size_t j = i + 2; j < poly.size(); j++) {
            const Point& c = poly.vertices[j];
            const Point& d = poly.vertices[(j + 1) % poly.size()];
            
            Point intersection;
            if (computeLineIntersection(a, b, c, d, intersection)) {
                // 排除端点接触
                if (!(intersection == a || intersection == b || intersection == c || intersection == d)) {
                    return false;  // 有自相交
                }
            }
        }
    }
    
    return true;
}

// 几何工具函数
bool WeilerAthertonClipper::isPointOnSegment(const Point& p, const Point& a, const Point& b) {
    double cross = crossProduct(a, b, p);
    if (std::abs(cross) > tolerance) return false;
    
    double dot = dotProduct(p - a, b - a);
    if (dot < -tolerance) return false;
    
    double squaredLength = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y);
    return dot <= squaredLength + tolerance;
}

double WeilerAthertonClipper::crossProduct(const Point& a, const Point& b, const Point& c) {
    return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

double WeilerAthertonClipper::dotProduct(const Point& a, const Point& b) {
    return a.x * b.x + a.y * b.y;
}

double WeilerAthertonClipper::distance(const Point& a, const Point& b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return std::sqrt(dx * dx + dy * dy);
}

bool WeilerAthertonClipper::areCollinear(const Point& a, const Point& b, const Point& c) {
    return std::abs(crossProduct(a, b, c)) < tolerance;
}

2.3 可视化测试程序 (main.cpp)

cpp 复制代码
#include "PolygonClipping.h"
#include <iostream>
#include <iomanip>
#include <random>

// 简单绘图函数(控制台字符画)
class SimpleRenderer {
public:
    static void drawPolygon(const Polygon& poly, int width = 80, int height = 40) {
        std::vector<std::string> canvas(height, std::string(width, '.'));
        
        // 获取边界
        double minX, minY, maxX, maxY;
        poly.getBounds(minX, minY, maxX, maxY);
        
        // 缩放因子
        double scaleX = (width - 2) / (maxX - minX);
        double scaleY = (height - 2) / (maxY - minY);
        double scale = std::min(scaleX, scaleY);
        
        // 绘制顶点
        for (const auto& vertex : poly.vertices) {
            int x = static_cast<int>((vertex.x - minX) * scale) + 1;
            int y = static_cast<int>((vertex.y - minY) * scale) + 1;
            
            if (x >= 0 && x < width && y >= 0 && y < height) {
                canvas[y][x] = '*';
            }
        }
        
        // 绘制边
        for (size_t i = 0; i < poly.size(); i++) {
            const Point& a = poly.vertices[i];
            const Point& b = poly.vertices[(i + 1) % poly.size()];
            
            int x1 = static_cast<int>((a.x - minX) * scale) + 1;
            int y1 = static_cast<int>((a.y - minY) * scale) + 1;
            int x2 = static_cast<int>((b.x - minX) * scale) + 1;
            int y2 = static_cast<int>((b.y - minY) * scale) + 1;
            
            drawLine(canvas, x1, y1, x2, y2, '#');
        }
        
        // 输出
        for (const auto& line : canvas) {
            std::cout << line << std::endl;
        }
    }
    
    static void drawClippedResult(const ClippingResult& result, int width = 80, int height = 40) {
        if (!result.success || result.polygons.empty()) {
            std::cout << "No clipped polygons or clipping failed." << std::endl;
            return;
        }
        
        std::vector<std::string> canvas(height, std::string(width, '.'));
        
        // 获取所有多边形的边界
        double minX = std::numeric_limits<double>::max();
        double minY = std::numeric_limits<double>::max();
        double maxX = std::numeric_limits<double>::lowest();
        double maxY = std::numeric_limits<double>::lowest();
        
        for (const auto& poly : result.polygons) {
            double mx, my, xx, xy;
            poly.getBounds(mx, my, xx, xy);
            minX = std::min(minX, mx);
            minY = std::min(minY, my);
            maxX = std::max(maxX, xx);
            maxY = std::max(maxY, xy);
        }
        
        // 缩放因子
        double scaleX = (width - 2) / (maxX - minX);
        double scaleY = (height - 2) / (maxY - minY);
        double scale = std::min(scaleX, scaleY);
        
        // 绘制每个裁剪后的多边形
        char symbols[] = {'#', '@', '%', '&', '*', '+', '-', ':'};
        int symbolIdx = 0;
        
        for (const auto& poly : result.polygons) {
            char symbol = symbols[symbolIdx % 8];
            
            // 绘制边
            for (size_t i = 0; i < poly.size(); i++) {
                const Point& a = poly.vertices[i];
                const Point& b = poly.vertices[(i + 1) % poly.size()];
                
                int x1 = static_cast<int>((a.x - minX) * scale) + 1;
                int y1 = static_cast<int>((a.y - minY) * scale) + 1;
                int x2 = static_cast<int>((b.x - minX) * scale) + 1;
                int y2 = static_cast<int>((b.y - minY) * scale) + 1;
                
                drawLine(canvas, x1, y1, x2, y2, symbol);
            }
            
            symbolIdx++;
        }
        
        // 输出
        for (const auto& line : canvas) {
            std::cout << line << std::endl;
        }
    }
    
private:
    static void drawLine(std::vector<std::string>& canvas, int x1, int y1, int x2, int y2, char ch) {
        int dx = std::abs(x2 - x1);
        int dy = std::abs(y2 - y1);
        int sx = (x1 < x2) ? 1 : -1;
        int sy = (y1 < y2) ? 1 : -1;
        int err = dx - dy;
        
        int height = static_cast<int>(canvas.size());
        int width = static_cast<int>(canvas[0].size());
        
        while (true) {
            if (x1 >= 0 && x1 < width && y1 >= 0 && y1 < height) {
                canvas[y1][x1] = ch;
            }
            
            if (x1 == x2 && y1 == y2) break;
            
            int e2 = 2 * err;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
        }
    }
};

// 测试用例
void testBasicCase() {
    std::cout << "=== 基本测试用例 ===" << std::endl;
    
    // 创建凹多边形(星形)
    Polygon subject;
    subject.addVertex(Point(50, 50));
    subject.addVertex(Point(100, 25));
    subject.addVertex(Point(150, 50));
    subject.addVertex(Point(125, 100));
    subject.addVertex(Point(150, 150));
    subject.addVertex(Point(100, 125));
    subject.addVertex(Point(50, 150));
    subject.addVertex(Point(75, 100));
    
    // 创建矩形裁剪窗口
    Polygon clip;
    clip.addVertex(Point(75, 75));
    clip.addVertex(Point(125, 75));
    clip.addVertex(Point(125, 125));
    clip.addVertex(Point(75, 125));
    
    std::cout << "原始多边形:" << std::endl;
    SimpleRenderer::drawPolygon(subject, 60, 30);
    
    std::cout << "\n裁剪窗口:" << std::endl;
    SimpleRenderer::drawPolygon(clip, 60, 30);
    
    // 执行裁剪
    WeilerAthertonClipper clipper;
    clipper.setSubjectPolygon(subject);
    clipper.setClipPolygon(clip);
    clipper.setTolerance(1e-6);
    
    ClippingResult result = clipper.clip();
    
    if (result.success) {
        std::cout << "\n裁剪结果 (" << result.polygons.size() << " 个多边形):" << std::endl;
        SimpleRenderer::drawClippedResult(result, 60, 30);
        
        // 输出统计信息
        std::cout << "\n统计信息:" << std::endl;
        std::cout << "交点数量: " << result.intersections.size() << std::endl;
        for (size_t i = 0; i < result.polygons.size(); i++) {
            std::cout << "多边形 " << i + 1 << ": " 
                      << result.polygons[i].size() << " 个顶点, "
                      << "面积: " << std::fixed << std::setprecision(2) 
                      << std::abs(result.polygons[i].signedArea()) << std::endl;
        }
    } else {
        std::cout << "裁剪失败: " << result.errorMessage << std::endl;
    }
}

void testComplexCase() {
    std::cout << "\n=== 复杂凹多边形测试 ===" << std::endl;
    
    // 创建复杂的凹多边形(房子形状)
    Polygon subject;
    subject.addVertex(Point(50, 50));
    subject.addVertex(Point(150, 50));
    subject.addVertex(Point(150, 100));
    subject.addVertex(Point(100, 150));
    subject.addVertex(Point(50, 100));
    subject.addVertex(Point(75, 75));  // 凹进去的部分
    subject.addVertex(Point(50, 75));
    
    // 创建不规则裁剪窗口
    Polygon clip;
    clip.addVertex(Point(60, 60));
    clip.addVertex(Point(140, 60));
    clip.addVertex(Point(140, 140));
    clip.addVertex(Point(100, 120));
    clip.addVertex(Point(60, 140));
    
    std::cout << "原始多边形:" << std::endl;
    SimpleRenderer::drawPolygon(subject, 60, 30);
    
    std::cout << "\n裁剪窗口:" << std::endl;
    SimpleRenderer::drawPolygon(clip, 60, 30);
    
    // 执行裁剪
    WeilerAthertonClipper clipper;
    clipper.setSubjectPolygon(subject);
    clipper.setClipPolygon(clip);
    
    ClippingResult result = clipper.clip();
    
    if (result.success) {
        std::cout << "\n裁剪结果 (" << result.polygons.size() << " 个多边形):" << std::endl;
        SimpleRenderer::drawClippedResult(result, 60, 30);
    } else {
        std::cout << "裁剪失败: " << result.errorMessage << std::endl;
    }
}

void testMultipleRegions() {
    std::cout << "\n=== 多区域裁剪测试 ===" << std::endl;
    
    // 创建多个分离区域的裁剪窗口
    Polygon subject;
    subject.addVertex(Point(20, 20));
    subject.addVertex(Point(180, 20));
    subject.addVertex(Point(180, 180));
    subject.addVertex(Point(20, 180));
    
    // 创建两个分离的裁剪窗口
    Polygon clip1, clip2;
    clip1.addVertex(Point(40, 40));
    clip1.addVertex(Point(80, 40));
    clip1.addVertex(Point(80, 80));
    clip1.addVertex(Point(40, 80));
    
    clip2.addVertex(Point(120, 120));
    clip2.addVertex(Point(160, 120));
    clip2.addVertex(Point(160, 160));
    clip2.addVertex(Point(120, 160));
    
    std::cout << "原始多边形:" << std::endl;
    SimpleRenderer::drawPolygon(subject, 60, 30);
    
    std::cout << "\n裁剪窗口1:" << std::endl;
    SimpleRenderer::drawPolygon(clip1, 60, 30);
    
    std::cout << "\n裁剪窗口2:" << std::endl;
    SimpleRenderer::drawPolygon(clip2, 60, 30);
    
    // 分别裁剪
    WeilerAthertonClipper clipper;
    clipper.setSubjectPolygon(subject);
    
    std::cout << "\n裁剪结果:" << std::endl;
    
    // 裁剪窗口1
    clipper.setClipPolygon(clip1);
    ClippingResult result1 = clipper.clip();
    
    if (result1.success) {
        std::cout << "窗口1裁剪结果 (" << result1.polygons.size() << " 个多边形):" << std::endl;
        SimpleRenderer::drawClippedResult(result1, 60, 15);
    }
    
    // 裁剪窗口2
    clipper.setClipPolygon(clip2);
    ClippingResult result2 = clipper.clip();
    
    if (result2.success) {
        std::cout << "\n窗口2裁剪结果 (" << result2.polygons.size() << " 个多边形):" << std::endl;
        SimpleRenderer::drawClippedResult(result2, 60, 15);
    }
}

int main() {
    std::cout << "Weiler-Atherton 多边形裁剪算法演示" << std::endl;
    std::cout << "=====================================" << std::endl;
    
    // 运行测试
    testBasicCase();
    testComplexCase();
    testMultipleRegions();
    
    std::cout << "\n按 Enter 键退出..." << std::endl;
    std::cin.get();
    
    return 0;
}

三、高级功能扩展

3.1 多边形布尔运算 (BooleanOperations.h)

cpp 复制代码
#ifndef BOOLEAN_OPERATIONS_H
#define BOOLEAN_OPERATIONS_H

#include "PolygonClipping.h"

// 布尔运算类型
enum BooleanOperation {
    UNION_OPERATION = 0,        // 并集
    INTERSECTION_OPERATION = 1, // 交集
    DIFFERENCE_OPERATION = 2,   // 差集
    XOR_OPERATION = 3          // 异或
};

// 多边形布尔运算器
class PolygonBooleanOperations {
public:
    PolygonBooleanOperations();
    ~PolygonBooleanOperations();
    
    // 执行布尔运算
    std::vector<Polygon> execute(const Polygon& poly1, const Polygon& poly2, 
                               BooleanOperation operation);
    
    // 设置选项
    void setTolerance(double tol) { clipper.setTolerance(tol); }
    
private:
    WeilerAthertonClipper clipper;
    
    // 辅助函数
    std::vector<Polygon> computeUnion(const Polygon& poly1, const Polygon& poly2);
    std::vector<Polygon> computeIntersection(const Polygon& poly1, const Polygon& poly2);
    std::vector<Polygon> computeDifference(const Polygon& poly1, const Polygon& poly2);
    std::vector<Polygon> computeXor(const Polygon& poly1, const Polygon& poly2);
    
    // 多边形操作
    Polygon reversePolygon(const Polygon& poly);
    bool polygonsOverlap(const Polygon& poly1, const Polygon& poly2);
    void mergePolygons(std::vector<Polygon>& polygons);
};

#endif // BOOLEAN_OPERATIONS_H

3.2 OpenGL 可视化版本 (OpenGLVisualizer.h)

cpp 复制代码
#ifndef OPENGL_VISUALIZER_H
#define OPENGL_VISUALIZER_H

#include "PolygonClipping.h"
#include <GL/glut.h>

// OpenGL 可视化器
class OpenGLVisualizer {
public:
    OpenGLVisualizer(int argc, char** argv);
    ~OpenGLVisualizer();
    
    // 设置数据
    void setSubjectPolygon(const Polygon& poly);
    void setClipPolygon(const Polygon& poly);
    void setClippedPolygons(const std::vector<Polygon>& polys);
    
    // 运行可视化
    void run();
    
private:
    // 多边形数据
    Polygon subjectPoly;
    Polygon clipPoly;
    std::vector<Polygon> clippedPolys;
    
    // 视图参数
    float zoom;
    float panX, panY;
    int windowWidth, windowHeight;
    
    // 颜色
    float subjectColor[3];
    float clipColor[3];
    float clippedColors[8][3];
    
    // 动画参数
    bool animate;
    float animationTime;
    
    // GLUT 回调函数
    static void displayCallback();
    static void reshapeCallback(int width, int height);
    static void keyboardCallback(unsigned char key, int x, int y);
    static void mouseCallback(int button, int state, int x, int y);
    static void motionCallback(int x, int y);
    static void idleCallback();
    
    // 绘制函数
    void drawPolygon(const Polygon& poly, const float color[3], bool filled = false);
    void drawPoint(const Point& p, const float color[3], float size = 5.0f);
    void drawLine(const Point& p1, const Point& p2, const float color[3]);
    void drawAxes();
    void drawGrid();
    
    // 工具函数
    void setupColors();
    void setupLighting();
    void setupProjection();
    
    // 静态实例指针(用于GLUT回调)
    static OpenGLVisualizer* instance;
};

#endif // OPENGL_VISUALIZER_H

参考代码 基于weiler-atherton算法的多边形裁剪程序实现 www.youwenfan.com/contentcsv/72052.html

四、项目配置与使用

4.1 编译说明

bash 复制代码
# Linux/Mac
g++ -std=c++11 -o polygon_clipping main.cpp PolygonClipping.cpp -lm

# Windows (MinGW)
g++ -std=c++11 -o polygon_clipping.exe main.cpp PolygonClipping.cpp

4.2 使用示例

cpp 复制代码
// 简单使用示例
int main() {
    // 创建凹多边形
    Polygon subject;
    subject.addVertex(Point(0, 0));
    subject.addVertex(Point(100, 0));
    subject.addVertex(Point(100, 100));
    subject.addVertex(Point(50, 50));  // 凹点
    subject.addVertex(Point(0, 100));
    
    // 创建矩形裁剪窗口
    Polygon clip;
    clip.addVertex(Point(25, 25));
    clip.addVertex(Point(75, 25));
    clip.addVertex(Point(75, 75));
    clip.addVertex(Point(25, 75));
    
    // 执行裁剪
    WeilerAthertonClipper clipper;
    clipper.setSubjectPolygon(subject);
    clipper.setClipPolygon(clip);
    
    ClippingResult result = clipper.clip();
    
    if (result.success) {
        std::cout << "裁剪成功,得到 " << result.polygons.size() << " 个多边形" << std::endl;
        
        for (size_t i = 0; i < result.polygons.size(); i++) {
            std::cout << "多边形 " << i + 1 << ":" << std::endl;
            for (const auto& vertex : result.polygons[i].vertices) {
                std::cout << "(" << vertex.x << ", " << vertex.y << ") ";
            }
            std::cout << std::endl;
        }
    }
    
    return 0;
}

五、算法特性与优化

5.1 算法特性

  1. 支持凹多边形:正确处理凹多边形裁剪
  2. 多区域处理:可以生成多个分离的裁剪区域
  3. 鲁棒性:处理退化情况和数值稳定性
  4. 可扩展性:支持布尔运算扩展

5.2 性能优化

  1. 空间划分:使用网格加速交点计算
  2. 缓存机制:缓存交点分类结果
  3. 并行计算:对多个多边形并行处理
  4. 增量更新:支持动态修改多边形

5.3 边界情况处理

  1. 重合边:处理多边形与裁剪窗口边重合的情况
  2. 顶点接触:处理顶点恰好落在边上的情况
  3. 零面积多边形:过滤退化的多边形
  4. 自相交:检测并处理自相交多边形
相关推荐
不爱吃糖の糖糖1 小时前
RAG 04:向量数据库与索引算法
数据库·算法
MegaDataFlowers1 小时前
226.翻转二叉树
算法
alphaTao2 小时前
LeetCode 每日一题 2026/5/25-2026/5/31
算法·leetcode
菜菜的顾清寒2 小时前
力扣HOT100(41)动态规划-杨辉三角
算法·leetcode·动态规划
Cthy_hy2 小时前
Python算法竞赛:集合去重+字典映射 核心用法一站式整理
数据结构·python·算法
Deepoch2 小时前
Deepoc数学大模型:驱动发动机行业数智化转型的底层解
人工智能·算法·deepoc·数学大模型
happymaker06262 小时前
LeetCodeHot100——盛水最多的容器
数据结构·算法·leetcode·双指针·hot100
3DVisionary2 小时前
蓝光三维扫描:磁性轴承全尺寸精密3D检测方案
算法·3d·3d检测·蓝光三维扫描·精密检测·磁性轴承·圆度测量
过期动态2 小时前
【LeetCode 热题 100】三数之和
java·数据结构·算法·leetcode·职场和发展·排序算法