多边形剪裁算法
多边形剪裁算法
用 box 剪裁任意多边形。
算法原理:
原多边形
↓ 用 LEFT 裁剪
中间多边形
↓ 用 RIGHT 裁剪
中间多边形
↓ 用 BOTTOM 裁剪
中间多边形
↓ 用 TOP 裁剪
最终结果
每一步都保证输出多边形在当前剪裁边的内侧。
- 处理 box 的 4 条边,对于每一条边:
- 遍历多边形的所有点,获取当前点 prev 和前一个点 curr 和这条边的关系,根据下面表格做处理,判断有哪些点要加入生成的多边形:
| prev | curr | 行为 |
| ------- | ------- | -------------- |
| inside | inside | 保留curr|
| outside | inside | 加交点 + curr |
| inside | outside | 加交点 |
| outside | outside | 全丢弃 |
- 遍历多边形的所有点,获取当前点 prev 和前一个点 curr 和这条边的关系,根据下面表格做处理,判断有哪些点要加入生成的多边形:
对于下图类型的 polygon 会出现折返线,暂未解决。

实现代码:
enum Edge { LEFT, RIGHT, BOTTOM, TOP };
// 判断这个点和box 的位置关系
bool inside(const zaPoint& p, Edge e, const zaBox& box) {
switch (e) {
case LEFT:
return p.x() > box.l();
case RIGHT:
return p.x() < box.r();
case BOTTOM:
return p.y() > box.b();
case TOP:
return p.y() < box.t();
}
return false;
}
static zaPoint
intersect(const zaPoint& a, const zaPoint& b, Edge e,
const zaBox& box) {
// 使用 __int128 防止中间溢出
__int128 dx = (__int128)b.x() - a.x();
__int128 dy = (__int128)b.y() - a.y();
if (e == LEFT || e == RIGHT) {
zaIntx X = (e == LEFT) ? box.l() : box.r();
__int128 t_num = (__int128)X - a.x();
__int128 y = (__int128)a.y() + t_num * dy / dx;
return {X, (zaIntx)y};
} else {
zaIntx Y = (e == BOTTOM) ? box.b() : box.t();
__int128 t_num = (__int128)Y - a.y();
__int128 x = (__int128)a.x() + t_num * dx / dy;
return zaPoint(x, Y);
}
}
abDensity1::clipPolygonWithBox(const zaPointArray& poly, const zaBox& box) {
auto clipEdge = [&](const zaPointArray& in, Edge e) {
vector<zaPoint> out;
int n = (int)in.getNumPoints();
if (n == 0) return zaPointArray(0);
out.reserve(n + 2);
for (int i = 0; i < n; ++i) {
const zaPoint& curr = in[i];
const zaPoint& prev = in[(i + n - 1) % n];
bool currIn = inside(curr, e, box);
bool prevIn = inside(prev, e, box);
if (currIn) {
if (!prevIn) {
out.push_back(intersect(prev, curr, e, box));
}
out.push_back(curr);
} else if (prevIn) {
out.push_back(intersect(prev, curr, e, box));
}
}
zaPointArray res(out.size());
res.set(out.data(), out.size());
return res;
};
zaPointArray result = poly;
result = clipEdge(result, LEFT);
result = clipEdge(result, RIGHT);
result = clipEdge(result, BOTTOM);
result = clipEdge(result, TOP);
return result;
}
浙公网安备 33010602011771号