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 算法特性
- 支持凹多边形:正确处理凹多边形裁剪
- 多区域处理:可以生成多个分离的裁剪区域
- 鲁棒性:处理退化情况和数值稳定性
- 可扩展性:支持布尔运算扩展
5.2 性能优化
- 空间划分:使用网格加速交点计算
- 缓存机制:缓存交点分类结果
- 并行计算:对多个多边形并行处理
- 增量更新:支持动态修改多边形
5.3 边界情况处理
- 重合边:处理多边形与裁剪窗口边重合的情况
- 顶点接触:处理顶点恰好落在边上的情况
- 零面积多边形:过滤退化的多边形
- 自相交:检测并处理自相交多边形