You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
7.5 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma once
#include "DrawOperator/CurveEx.h"
#include "clipper2/clipper.h"
#include <memory>
/**
* \brief 提供基于 Clipper2 库的多边形布尔运算和转换的实用工具类。
*
* 此类封装了 Clipper2 的常用操作,例如交集、并集、差集,
* 并提供了在浮点坐标 (PathD) 和整数坐标 (PathD) 之间转换的辅助函数。
* 它还包括与自定义 `CCurveEx` 类型相互转换的方法。
*/
class PolygonUtils
{
public:
/**
* \brief 计算两个浮点路径的交集。
* \param subject 主体路径。
* \param clip 裁剪路径。
* \return 表示交集区域的一个或多个路径 (PathsD)。
*/
static Clipper2Lib::PathsD Intersect(const Clipper2Lib::PathD& subject, const Clipper2Lib::PathD& clip)
{
return Clipper2Lib::Intersect({ subject }, { clip }, Clipper2Lib::FillRule::NonZero);
}
/**
* \brief 查找两个路径之间小于指定 `minGap` 的间隙区域。
*
* \warning 原始逻辑存在问题!
* 此方法通过膨胀路径来检测 "间隙"。
* 原始逻辑计算 `Difference(inflatedOverlap, inflatedUnion)`
* 这将始终返回空集,因为 `inflatedOverlap` 总是 `inflatedUnion` 的子集。
*
* \param subject 主体路径。
* \param clip 裁剪路径。
* \param minGap 视为 "间隙" 的最大距离。
* \return 表示间隙区域的一个或多个路径 (PathsD)。
*/
static Clipper2Lib::PathsD FindGapRegions(const Clipper2Lib::PathD& subject, const Clipper2Lib::PathD& clip, double minGap)
{
double delta = minGap / 2.0;
Clipper2Lib::PathsD subjects = { subject };
Clipper2Lib::PathsD clips = { clip };
Clipper2Lib::PathsD inflatedSubjects = Clipper2Lib::InflatePaths(subjects, delta, Clipper2Lib::JoinType::Miter, Clipper2Lib::EndType::Polygon, 2.0);
Clipper2Lib::PathsD inflatedClips = Clipper2Lib::InflatePaths(clips, delta, Clipper2Lib::JoinType::Miter, Clipper2Lib::EndType::Polygon, 2.0);
// 2. 膨胀后求交
Clipper2Lib::PathsD inflatedOverlap = Clipper2Lib::Intersect(inflatedSubjects, inflatedClips, Clipper2Lib::FillRule::NonZero);
if (inflatedOverlap.empty())
{
return {};
}
// 3. 原始并集
Clipper2Lib::PathsD originalUnion = Clipper2Lib::Union(subjects, clips, Clipper2Lib::FillRule::NonZero);
// 4. 差集
return Clipper2Lib::Difference(inflatedOverlap, originalUnion, Clipper2Lib::FillRule::NonZero);
}
/**
* \brief 将 CCurveEx 对象转换为 Clipper2Lib::PathD 路径。
*
* Clipper2 的路径定义为不含重复闭合点的顶点列表。
* 此函数会检查 `curve->IsClosed()`
* - 如果 curve 是闭合的 (最后一个点 == 第一个点),则转换时会 *省略* 最后一个重复点。
* - 如果 curve 是开放的,则包含所有点。
*
* \param curve 要转换的 CCurveEx 指针。
* \return 转换后的 Clipper2Lib::PathD。
*/
static Clipper2Lib::PathD ConvertCurveToPath(CCurveEx* curve) // 优化: pCurve -> curve
{
Clipper2Lib::PathD clipperPath;
clipperPath.reserve(curve->num);
for (int i = 0; i < curve->num; i++)
{
if (i != curve->num - 1)
{
clipperPath.push_back({ curve->x[i], curve->y[i] });
}
else
{
// Clipper2 路径不需要重复的闭合点。
// 如果 curve 是闭合的 (最后一个点 == 第一个点),我们省略最后一个点。
// 如果 curve 是开放的,我们包含所有点。
if (!curve->IsClosed())
{
clipperPath.push_back({ curve->x[i], curve->y[i] });
}
}
}
return clipperPath;
}
/**
* \brief 将 Clipper2Lib::PathD 路径转换为一个 *闭合* 的 CCurveEx 对象。
*
* \param clipperPath 要转换的 Clipper 路径。
* \return 转换后的 CCurveEx 对象的 `std::unique_ptr`。
* 如果 `clipperPath` 的点数少于 3则返回 `nullptr`。
*/
static std::unique_ptr<CCurveEx> ConvertPathToClosedCurve(const Clipper2Lib::PathD& clipperPath) // 优化: path -> clipperPath
{
if (clipperPath.size() < 3)
{
return nullptr;
}
auto curve = std::make_unique<CCurveEx>();
int closedPointCount = clipperPath.size() + 1;
curve->Create(closedPointCount);
curve->num = closedPointCount;
for (size_t i = 0; i < clipperPath.size(); ++i)
{
curve->x[i] = clipperPath[i].x;
curve->y[i] = clipperPath[i].y;
}
// 复制第一个点到末尾以闭合
curve->x[clipperPath.size()] = clipperPath[0].x;
curve->y[clipperPath.size()] = clipperPath[0].y;
return curve;
}
/**
* \brief (辅助函数) 计算两个整数路径 (PathD) 的交集。
*/
static Clipper2Lib::PathsD Intersect(const Clipper2Lib::PathD& subjects, const Clipper2Lib::PathD& clips, Clipper2Lib::FillRule fillRule)
{
return ClipGeometry({ subjects }, { clips }, Clipper2Lib::ClipType::Intersection, fillRule);
}
/**
* \brief (辅助函数) 计算两组整数路径 (PathsD) 的交集。
*/
static Clipper2Lib::PathsD Intersect(const Clipper2Lib::PathsD& subjects, const Clipper2Lib::PathsD& clips, Clipper2Lib::FillRule fillRule) // 优化: polygons1/2 -> subjects/clips
{
return ClipGeometry(subjects, clips, Clipper2Lib::ClipType::Intersection, fillRule);
}
/**
* \brief (辅助函数) 计算两个整数路径 (PathD) 的并集。
*/
static Clipper2Lib::PathsD Union(const Clipper2Lib::PathD& subjects, const Clipper2Lib::PathD& clips, Clipper2Lib::FillRule fillRule)
{
return ClipGeometry({ subjects }, { clips }, Clipper2Lib::ClipType::Union, fillRule);
}
/**
* \brief (辅助函数) 计算两组整数路径 (PathsD) 的并集。
*/
static Clipper2Lib::PathsD Union(const Clipper2Lib::PathsD& subjects, const Clipper2Lib::PathsD& clips, Clipper2Lib::FillRule fillRule) // 优化: polygons1/2 -> subjects/clips
{
return ClipGeometry(subjects, clips, Clipper2Lib::ClipType::Union, fillRule);
}
/**
* \brief (辅助函数) 计算两个整数路径 (PathD) 的差集 (subjects - clips)。
*/
static Clipper2Lib::PathsD Difference(const Clipper2Lib::PathD& subjects, const Clipper2Lib::PathD& clips, Clipper2Lib::FillRule fillRule)
{
return ClipGeometry({ subjects }, { clips }, Clipper2Lib::ClipType::Difference, fillRule);
}
/**
* \brief (辅助函数) 计算两组整数路径 (PathsD) 的差集 (subjects - clips)。
*/
static Clipper2Lib::PathsD Difference(const Clipper2Lib::PathsD& subjects, const Clipper2Lib::PathsD& clips, Clipper2Lib::FillRule fillRule) // 优化: polygons1/2 -> subjects/clips
{
return ClipGeometry(subjects, clips, Clipper2Lib::ClipType::Difference, fillRule);
}
/**
* \brief (辅助函数) 计算两个整数路径 (PathD) 的异或 (Xor)。
*/
static Clipper2Lib::PathsD Xor(const Clipper2Lib::PathD& subjects, const Clipper2Lib::PathD& clips, Clipper2Lib::FillRule fillRule)
{
return ClipGeometry({ subjects }, { clips }, Clipper2Lib::ClipType::Xor, fillRule);
}
/**
* \brief (辅助函数) 计算两组整数路径 (PathsD) 的异或 (Xor)。
*/
static Clipper2Lib::PathsD Xor(const Clipper2Lib::PathsD& subjects, const Clipper2Lib::PathsD& clips, Clipper2Lib::FillRule fillRule) // 优化: polygons1/2 -> subjects/clips
{
return ClipGeometry(subjects, clips, Clipper2Lib::ClipType::Xor, fillRule);
}
/**
* \brief 膨胀
*
* \param paths
* \param delta
* \return
*/
static Clipper2Lib::PathsD InflatePaths(const Clipper2Lib::PathsD& paths, double delta)
{
return Clipper2Lib::InflatePaths(paths, delta, Clipper2Lib::JoinType::Miter, Clipper2Lib::EndType::Polygon);
}
/**
* \brief (核心函数) 执行通用的 Clipper2 布尔运算。
*
* 这是所有布尔运算 (Intersect, Union, Difference, Xor) 的核心实现。
*
* \param subjects 主体路径集合。
* \param clips 裁剪路径集合。
* \param clipType 布尔运算类型 (Intersection, Union, Difference, Xor)。
* \param fillRule 填充规则。
* \return 运算结果路径集合。
*/
static Clipper2Lib::PathsD ClipGeometry(const Clipper2Lib::PathsD& subjects, const Clipper2Lib::PathsD& clips, Clipper2Lib::ClipType clipType, Clipper2Lib::FillRule fillRule)
{
Clipper2Lib::ClipperD clipper;
clipper.AddSubject(subjects);
clipper.AddClip(clips);
Clipper2Lib::PathsD solution;
clipper.Execute(clipType, fillRule, solution);
return solution;
}
};