有 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;
}