有 hole 的polygon 转成一笔画 polygon

有 hole 的polygon 转成一笔画 polygon

// 判断点是否在 ring 包含的区域内(射线法)
bool
PointInPolygon(const CPoint64& p, const CPath64& r) {
  bool inside = false;
  for (int i = 0, j = r.size() - 1; i < r.size(); j = i++) {
    const CPoint64 &a = r[j], &b = r[i];
    bool inter = ((b.y > p.y) != (a.y > p.y)) &&
                 (p.x < (a.x - b.x) * (p.y - b.y) / (a.y - b.y + 1e-15) + b.x);
    if (inter) inside = !inside;
  }
  return inside;
}

// ---------------------------- 找洞的可见点 ----------------------------
// 为 hole 的最左点找到 outer 中可见点(简单实现:选 outer 中 x 最小

// 返回 outer 中的可见顶点索引,如果交点不是顶点则插入新点并返回其 index
int
FindVisibleVertex(CPath64& outer, const CPoint64& H) {
  const double Hy = (double)H.y;

  double bestX = -1e300;  // 射线上最右的交点(但必须 < H.x)
  int bestIndex = -1;     // 如果交点是顶点,记录其 index
  bool isVertexMatch = false;
  CPoint64 bestPt;

  int N = (int)outer.size();

  // 遍历每条边 outer[i] -> outer[j]
  for (int i = 0; i < N; ++i) {
    int j = (i + 1) % N;
    const CPoint64& A = outer[i];
    const CPoint64& B = outer[j];

    double Ay = (double)A.y;
    double By = (double)B.y;

    // 水平线 Hy 必须在边的 y 范围内(不含上端点避免重复)
    if (!((Ay > Hy && By <= Hy) || (By > Hy && Ay <= Hy))) continue;

    // 求交点 x(A->B 与水平线 y=Hy 的交点)
    double t = (Hy - Ay) / (By - Ay);
    double ix = (double)A.x + t * ((double)B.x - (double)A.x);

    // 必须是左侧射线:ix < H.x
    if (ix >= (double)H.x) continue;

    // 更新最近交点(x 最大)
    if (ix > bestX) {
      bestX = ix;
      bestPt = {(int64_t)std::llround(ix), H.y};

      // 判断交点是否是 outer 的已有顶点(A 或 B)
      if ((ix == A.x && Hy == A.y)) {
        isVertexMatch = true;
        bestIndex = i;
      } else if ((ix == B.x && Hy == B.y)) {
        isVertexMatch = true;
        bestIndex = j;
      } else {
        isVertexMatch = false;  // 新点
        bestIndex = i;          // 新点应插在 i 和 j 之间
      }
    }
  }

  // 根本没有交点(这理论上不应该出现)
  if (bestX < -1e290) {
    return -1;
  }

  // 若交点就是顶点,则直接返回 index
  if (isVertexMatch) return bestIndex;

  // 插入新点:插入在 outer[bestIndex] 和 outer[bestIndex+1] 之间
  int insertPos = bestIndex + 1;
  outer.insert(outer.begin() + insertPos, bestPt);
  return insertPos;
}


// ---------------------------- 将一个 outer + 多个 hole 合成 simple polygon
// ----------------------------
CPath64
MergeOnePolygon(const CPath64& outer, const std::vector<CPath64>& holes) {
  CPath64 result = outer;

  for (const CPath64& hole : holes) {
    // 找 hole 最左点
    int hk = 0;
    for (int i = 1; i < hole.size(); ++i)
      if (hole[i].x < hole[hk].x) hk = i;

    CPoint64 H = hole[hk];

    // 找 outer 中的可见点
    int oi = FindVisibleVertex(result, H);

    // 构建新路径
    CPath64 newR;

    // outer[0 .. oi]
    for (int i = 0; i <= oi; i++) newR.push_back(result[i]);

    // 加 bridge: outer[oi] -> hole
    // 加 hole(hk..end) + hole(0..hk-1)
    for (int i = hk; i < hole.size(); i++) newR.push_back(hole[i]);
    for (int i = 0; i < hk + 1; i++) newR.push_back(hole[i]);

    // 回到 outer[oi]
    newR.push_back(result[oi]);

    // outer[oi+1 .. end]
    for (int i = oi + 1; i < result.size(); i++) newR.push_back(result[i]);

    result = std::move(newR);
  }

  return result;
}

// ---------------------------- 主流程:输入多个 ring → 输出 simple polygons
// ----------------------------
CPaths64
ConvertToSimplePolygons(const std::vector<CPath64>& rings) {
  // 1. 分离 outer / holes
  struct Node {
    CPath64 ring;
    bool isOuter;
  };

  std::vector<Node> nodes;
  for (auto r : rings) {
    nodes.push_back({r, Clipper2Lib::IsPositive(r)});
  }

  // 2. 将 hole 分配给最近的 outer
  std::vector<CPaths64> polys;  // 每个 polygon: [0]=outer, [1..]=holes

  // 先将 outer 建立 polygon group
  for (auto& n : nodes) {
    if (n.isOuter) polys.push_back(CPaths64{n.ring});
  }

  // 给每个 hole 找所属 outer
  for (auto& n : nodes) {
    if (n.isOuter) continue;

    // hole:取其任意一点
    CPoint64 p = n.ring[0];

    int best = -1;
    for (int i = 0; i < polys.size(); i++) {
      if (PointInPolygon(p, polys[i][0])) {
        best = i;
        break;
      }
    }

    // 若没找到,hole 无效(不应出现),忽略
    if (best >= 0) polys[best].push_back(n.ring);
  }

  // 3. 对每个 polygon 合成 simple polygon
  CPaths64 out;

  for (auto& poly : polys) {
    CPath64 outer = poly[0];

    // 保证 outer 为 CCW
    if (!Clipper2Lib::IsPositive(outer)) std::reverse(outer.begin(), outer.end());

    std::vector<CPath64> holes;

    // 收集 hole(方向必须 CW)
    for (int i = 1; i < poly.size(); i++) {
      CPath64 h = poly[i];
      if (Clipper2Lib::IsPositive(h)) std::reverse(h.begin(), h.end());  // hole 必须 CW
      holes.push_back(h);
    }

    // 合成 simple polygon
    out.push_back(MergeOnePolygon(outer, holes));
  }

  return out;
}
posted @ 2025-11-17 18:31  卑以自牧lq  阅读(7)  评论(0)    收藏  举报