多边形剪裁算法

多边形剪裁算法

用 box 剪裁任意多边形。

算法原理:

原多边形
  ↓ 用 LEFT 裁剪
中间多边形
  ↓ 用 RIGHT 裁剪
中间多边形
  ↓ 用 BOTTOM 裁剪
中间多边形
  ↓ 用 TOP 裁剪
最终结果

每一步都保证输出多边形在当前剪裁边的内侧。
  • 处理 box 的 4 条边,对于每一条边:
    • 遍历多边形的所有点,获取当前点 prev 和前一个点 curr 和这条边的关系,根据下面表格做处理,判断有哪些点要加入生成的多边形:
      | prev | curr | 行为 |
      | ------- | ------- | -------------- |
      | inside | inside | 保留 curr |
      | outside | inside | 加交点 + curr |
      | inside | outside | 加交点 |
      | outside | outside | 全丢弃 |

对于下图类型的 polygon 会出现折返线,暂未解决。
img

实现代码:

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;
}
posted @ 2026-01-23 21:14  卑以自牧lq  阅读(10)  评论(0)    收藏  举报