|
|
#pragma once
|
|
|
|
|
|
#include "clipper2/clipper.h"
|
|
|
|
|
|
using namespace Clipper2Lib;
|
|
|
|
|
|
class PointInPolygonTester
|
|
|
{
|
|
|
private:
|
|
|
// 将 double 坐标转换为 int64_t(Clipper2 使用整数坐标)
|
|
|
Point64 toPoint64(double x, double y, double scale = 100.0) {
|
|
|
return Point64(
|
|
|
static_cast<int64_t>(x * scale),
|
|
|
static_cast<int64_t>(y * scale)
|
|
|
);
|
|
|
}
|
|
|
|
|
|
public:
|
|
|
BOOL isPointInCurvePath(double x, double y, Clipper2Lib::Path64& curvePath, int scale = 100);
|
|
|
// 方法1:判断单个点
|
|
|
void getPolygonPath(CCurve& curve, Clipper2Lib::Path64& curvePath, int scale = 100);
|
|
|
|
|
|
bool isPointInPolygon(double x, double y, const std::vector<std::pair<double, double>>& polygon);
|
|
|
|
|
|
// 方法2:判断点是否在多边形内或边界上
|
|
|
PointInPolygonResult detailedPointTest(double x, double y,
|
|
|
const std::vector<std::pair<double, double>>& polygon);
|
|
|
|
|
|
// 方法3:批量判断点
|
|
|
std::vector<bool> batchTestPoints(const std::vector<std::pair<double, double>>& points,
|
|
|
const std::vector<std::pair<double, double>>& polygon);
|
|
|
|
|
|
// 方法4:带孔的多边形判断
|
|
|
bool isPointInPolygonWithHoles(double x, double y,
|
|
|
const std::vector<std::pair<double, double>>& outerPolygon,
|
|
|
const std::vector<std::vector<std::pair<double, double>>>& holes);
|
|
|
|
|
|
// 方法5:使用 PolyTree 进行复杂判断
|
|
|
bool isPointInComplexPolygon(double x, double y, const Paths64& polygons);
|
|
|
//enum class PointInPolygonResult {
|
|
|
// Inside,
|
|
|
// Outside,
|
|
|
// OnBoundary
|
|
|
//};
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*一些网上ai生成的算法测试,不太好用*/
|
|
|
|
|
|
typedef struct _tPoint2D {
|
|
|
double x0, y0;
|
|
|
|
|
|
_tPoint2D() : x0(0), y0(0) {}
|
|
|
_tPoint2D(double x_, double y_) : x0(x_), y0(y_) {}
|
|
|
|
|
|
_tPoint2D operator+(const _tPoint2D& other) const {
|
|
|
return _tPoint2D(x0 + other.x0, y0 + other.y0);
|
|
|
}
|
|
|
|
|
|
_tPoint2D operator-(const _tPoint2D& other) const {
|
|
|
return _tPoint2D(x0 - other.x0, y0 - other.y0);
|
|
|
}
|
|
|
|
|
|
_tPoint2D operator*(double scalar) const {
|
|
|
return _tPoint2D(x0 * scalar, y0 * scalar);
|
|
|
}
|
|
|
|
|
|
double length() const {
|
|
|
return std::sqrt(x0 * x0 + y0 * y0);
|
|
|
}
|
|
|
|
|
|
_tPoint2D normalize() const {
|
|
|
double len = length();
|
|
|
if (len == 0) return _tPoint2D(0, 0);
|
|
|
return _tPoint2D(x0 / len, y0 / len);
|
|
|
}
|
|
|
|
|
|
// 为了方便输出
|
|
|
void print() const {
|
|
|
std::cout << "(" << x0 << ", " << y0 << ")";
|
|
|
}
|
|
|
} tPoint2D;
|
|
|
|
|
|
class ParallelPolylineGenerator //这个不好用
|
|
|
{
|
|
|
public:
|
|
|
// 连接类型枚举
|
|
|
enum class JoinType {
|
|
|
MITER, // 斜接连接
|
|
|
ROUND, // 圆角连接
|
|
|
BEVEL // 斜角连接(平头)
|
|
|
};
|
|
|
|
|
|
// 端点类型枚举
|
|
|
enum class EndType {
|
|
|
BUTT, // 平头端点
|
|
|
SQUARE, // 方形端点
|
|
|
ROUND // 圆角端点
|
|
|
};
|
|
|
|
|
|
private:
|
|
|
// 计算点到直线的垂直向量
|
|
|
tPoint2D perpendicularVector(const tPoint2D& p1, const tPoint2D& p2, double distance) {
|
|
|
tPoint2D dir = p2 - p1;
|
|
|
double len = dir.length();
|
|
|
if (len == 0) return tPoint2D(0, 0);
|
|
|
|
|
|
// 垂直向量(旋转90度)
|
|
|
tPoint2D perp = tPoint2D(-dir.y0, dir.x0);
|
|
|
perp = perp.normalize();
|
|
|
return perp * distance;
|
|
|
}
|
|
|
|
|
|
// 计算两条直线的交点
|
|
|
bool lineIntersection(const tPoint2D& p1, const tPoint2D& p2,
|
|
|
const tPoint2D& p3, const tPoint2D& p4,
|
|
|
tPoint2D& result) {
|
|
|
double denom = (p4.y0 - p3.y0) * (p2.x0 - p1.x0) - (p4.x0 - p3.x0) * (p2.y0 - p1.y0);
|
|
|
if (std::abs(denom) < 1e-10) return false;
|
|
|
|
|
|
double ua = ((p4.x0 - p3.x0) * (p1.y0 - p3.y0) - (p4.y0 - p3.y0) * (p1.x0 - p3.x0)) / denom;
|
|
|
|
|
|
result.x0 = p1.x0 + ua * (p2.x0 - p1.x0);
|
|
|
result.y0 = p1.y0 + ua * (p2.y0 - p1.y0);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 计算向量夹角(0-180度)
|
|
|
double angleBetweenVectors(const tPoint2D& v1, const tPoint2D& v2) {
|
|
|
double dot = v1.x0 * v2.x0 + v1.y0 * v2.y0;
|
|
|
double len1 = v1.length();
|
|
|
double len2 = v2.length();
|
|
|
|
|
|
if (len1 == 0 || len2 == 0) return 0;
|
|
|
|
|
|
double cosAngle = dot / (len1 * len2);
|
|
|
cosAngle = max(-1.0, min(1.0, cosAngle));
|
|
|
return std::acos(cosAngle) * 180.0 / PI;
|
|
|
}
|
|
|
|
|
|
// 计算折线在顶点处的夹角
|
|
|
double calculateAngle(const tPoint2D& prev, const tPoint2D& curr, const tPoint2D& next) {
|
|
|
tPoint2D v1 = prev - curr;
|
|
|
tPoint2D v2 = next - curr;
|
|
|
return angleBetweenVectors(v1, v2);
|
|
|
}
|
|
|
|
|
|
// 生成圆弧点
|
|
|
std::vector<tPoint2D> generateArcPoints(const tPoint2D& center,
|
|
|
double radius,
|
|
|
double startAngle,
|
|
|
double endAngle,
|
|
|
int segments = 8) {
|
|
|
std::vector<tPoint2D> points;
|
|
|
|
|
|
// 确保角度递增
|
|
|
if (endAngle < startAngle) {
|
|
|
endAngle += 2 * PI;
|
|
|
}
|
|
|
|
|
|
double angleStep = (endAngle - startAngle) / segments;
|
|
|
|
|
|
for (int i = 1; i < segments; i++) {
|
|
|
double angle = startAngle + angleStep * i;
|
|
|
double x = center.x0 + std::cos(angle) * radius;
|
|
|
double y = center.y0 + std::sin(angle) * radius;
|
|
|
points.push_back(tPoint2D(x, y));
|
|
|
}
|
|
|
|
|
|
return points;
|
|
|
}
|
|
|
|
|
|
// 检查线段是否相交
|
|
|
bool segmentsIntersect(const tPoint2D& p1, const tPoint2D& p2,
|
|
|
const tPoint2D& p3, const tPoint2D& p4) {
|
|
|
auto orientation = [](const tPoint2D& p, const tPoint2D& q, const tPoint2D& r) -> double {
|
|
|
return (q.y0 - p.y0) * (r.x0 - q.x0) - (q.x0 - p.x0) * (r.y0 - q.y0);
|
|
|
};
|
|
|
|
|
|
auto onSegment = [](const tPoint2D& p, const tPoint2D& q, const tPoint2D& r) -> bool {
|
|
|
return q.x0 <= max(p.x0, r.x0) && q.x0 >= min(p.x0, r.x0) &&
|
|
|
q.y0 <= max(p.y0, r.y0) && q.y0 >= min(p.y0, r.y0);
|
|
|
};
|
|
|
|
|
|
double o1 = orientation(p1, p2, p3);
|
|
|
double o2 = orientation(p1, p2, p4);
|
|
|
double o3 = orientation(p3, p4, p1);
|
|
|
double o4 = orientation(p3, p4, p2);
|
|
|
|
|
|
// 一般情况
|
|
|
if (o1 * o2 < 0 && o3 * o4 < 0) return true;
|
|
|
|
|
|
// 特殊情况:共线
|
|
|
if (o1 == 0 && onSegment(p1, p3, p2)) return true;
|
|
|
if (o2 == 0 && onSegment(p1, p4, p2)) return true;
|
|
|
if (o3 == 0 && onSegment(p3, p1, p4)) return true;
|
|
|
if (o4 == 0 && onSegment(p3, p2, p4)) return true;
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 检查路径是否自相交
|
|
|
bool checkSelfIntersection(const std::vector<tPoint2D>& path) {
|
|
|
if (path.size() < 4) return false;
|
|
|
|
|
|
for (size_t i = 0; i < path.size() - 3; i++) {
|
|
|
for (size_t j = i + 2; j < path.size() - 1; j++) {
|
|
|
if (segmentsIntersect(path[i], path[i + 1], path[j], path[j + 1])) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 修复自相交
|
|
|
std::vector<tPoint2D> fixSelfIntersection(const std::vector<tPoint2D>& original,
|
|
|
const std::vector<tPoint2D>& parallel,
|
|
|
double distance,
|
|
|
JoinType joinType,
|
|
|
double miterLimit) {
|
|
|
std::vector<tPoint2D> fixedPath;
|
|
|
|
|
|
if (parallel.size() < 2) return parallel;
|
|
|
|
|
|
// 保留第一个点
|
|
|
fixedPath.push_back(parallel[0]);
|
|
|
|
|
|
// 重新计算连接点,对于锐角使用斜角连接
|
|
|
for (size_t i = 1; i < original.size() - 1; i++) {
|
|
|
tPoint2D prev = original[i - 1];
|
|
|
tPoint2D curr = original[i];
|
|
|
tPoint2D next = original[i + 1];
|
|
|
|
|
|
double angle = calculateAngle(prev, curr, next);
|
|
|
|
|
|
// 计算偏移点
|
|
|
tPoint2D offsetPrev = curr + perpendicularVector(curr, prev, -distance);
|
|
|
tPoint2D offsetNext = curr + perpendicularVector(curr, next, distance);
|
|
|
|
|
|
if (angle < 90.0) {
|
|
|
// 锐角:强制使用斜角连接
|
|
|
fixedPath.push_back(offsetPrev);
|
|
|
fixedPath.push_back(offsetNext);
|
|
|
}
|
|
|
else {
|
|
|
// 钝角或直角:根据连接类型处理
|
|
|
tPoint2D miterPoint;
|
|
|
if (lineIntersection(offsetPrev, offsetPrev + (prev - curr),
|
|
|
offsetNext, offsetNext + (next - curr),
|
|
|
miterPoint)) {
|
|
|
// 检查斜接长度
|
|
|
double miterLength = (miterPoint - curr).length();
|
|
|
double miterRatio = miterLength / std::abs(distance);
|
|
|
|
|
|
if (joinType == JoinType::MITER && miterRatio <= miterLimit) {
|
|
|
fixedPath.push_back(miterPoint);
|
|
|
}
|
|
|
else {
|
|
|
fixedPath.push_back(offsetPrev);
|
|
|
fixedPath.push_back(offsetNext);
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
fixedPath.push_back(offsetPrev);
|
|
|
fixedPath.push_back(offsetNext);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 添加最后一个点
|
|
|
fixedPath.push_back(parallel.back());
|
|
|
|
|
|
return fixedPath;
|
|
|
}
|
|
|
|
|
|
public:
|
|
|
// 生成平行折线的主函数
|
|
|
std::vector<tPoint2D> createParallelPolyline(const std::vector<tPoint2D>& original,
|
|
|
double distance,
|
|
|
JoinType joinType = JoinType::MITER,
|
|
|
EndType endType = EndType::BUTT,
|
|
|
double miterLimit = 2.0,
|
|
|
int roundSegments = 8) {
|
|
|
if (original.size() < 2) return {};
|
|
|
|
|
|
std::vector<tPoint2D> parallel;
|
|
|
size_t n = original.size();
|
|
|
|
|
|
// 1. 生成所有偏移点
|
|
|
std::vector<tPoint2D> offsetPoints(n);
|
|
|
|
|
|
// 第一个点
|
|
|
if (n > 1) {
|
|
|
tPoint2D dir = original[1] - original[0];
|
|
|
dir = dir.normalize();
|
|
|
tPoint2D perp = tPoint2D(-dir.y0, dir.x0);
|
|
|
offsetPoints[0] = original[0] + perp * distance;
|
|
|
}
|
|
|
|
|
|
// 中间点
|
|
|
for (size_t i = 1; i < n - 1; i++) {
|
|
|
tPoint2D prevDir = (original[i - 1] - original[i]).normalize();
|
|
|
tPoint2D nextDir = (original[i + 1] - original[i]).normalize();
|
|
|
|
|
|
tPoint2D perpPrev = tPoint2D(-prevDir.y0, prevDir.x0);
|
|
|
tPoint2D perpNext = tPoint2D(-nextDir.y0, nextDir.x0);
|
|
|
|
|
|
// 平均垂直向量
|
|
|
tPoint2D perpAvg = (perpPrev + perpNext).normalize();
|
|
|
offsetPoints[i] = original[i] + perpAvg * distance;
|
|
|
}
|
|
|
|
|
|
// 最后一个点
|
|
|
if (n > 1) {
|
|
|
tPoint2D dir = original[n - 1] - original[n - 2];
|
|
|
dir = dir.normalize();
|
|
|
tPoint2D perp = tPoint2D(-dir.y0, dir.x0);
|
|
|
offsetPoints[n - 1] = original[n - 1] + perp * distance;
|
|
|
}
|
|
|
|
|
|
// 2. 处理端点
|
|
|
if (endType == EndType::BUTT) {
|
|
|
parallel.push_back(offsetPoints[0]);
|
|
|
}
|
|
|
else if (endType == EndType::SQUARE) {
|
|
|
// 方形端点:延长半个距离
|
|
|
if (n > 1) {
|
|
|
tPoint2D dir = original[0] - original[1];
|
|
|
dir = dir.normalize() * std::abs(distance) * 0.5;
|
|
|
parallel.push_back(offsetPoints[0] + dir);
|
|
|
}
|
|
|
}
|
|
|
else if (endType == EndType::ROUND) {
|
|
|
// 圆角端点:添加圆弧
|
|
|
parallel.push_back(offsetPoints[0]);
|
|
|
if (n > 1) {
|
|
|
tPoint2D dir = (original[1] - original[0]).normalize();
|
|
|
double startAngle = std::atan2(dir.y0, dir.x0) + PI/2;
|
|
|
double endAngle = startAngle + PI/2;
|
|
|
auto arcPoints = generateArcPoints(original[0], std::abs(distance),
|
|
|
startAngle, endAngle, roundSegments);
|
|
|
parallel.insert(parallel.end(), arcPoints.begin(), arcPoints.end());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 3. 处理连接点
|
|
|
for (size_t i = 1; i < n - 1; i++) {
|
|
|
double angle = calculateAngle(original[i - 1], original[i], original[i + 1]);
|
|
|
|
|
|
// 计算两个偏移方向
|
|
|
tPoint2D perpPrev = perpendicularVector(original[i], original[i - 1], distance);
|
|
|
tPoint2D perpNext = perpendicularVector(original[i], original[i + 1], distance);
|
|
|
|
|
|
tPoint2D offsetPrev = original[i] + perpPrev;
|
|
|
tPoint2D offsetNext = original[i] + perpNext;
|
|
|
|
|
|
if (joinType == JoinType::BEVEL || angle < 45.0) {
|
|
|
// 斜角连接或锐角:直接连接
|
|
|
parallel.push_back(offsetPrev);
|
|
|
parallel.push_back(offsetNext);
|
|
|
}
|
|
|
else if (joinType == JoinType::MITER) {
|
|
|
// 计算斜接点
|
|
|
tPoint2D miterPoint;
|
|
|
if (lineIntersection(offsetPrev, offsetPrev + (original[i - 1] - original[i]),
|
|
|
offsetNext, offsetNext + (original[i + 1] - original[i]),
|
|
|
miterPoint)) {
|
|
|
// 检查斜接限制
|
|
|
double miterLength = (miterPoint - original[i]).length();
|
|
|
double miterRatio = miterLength / std::abs(distance);
|
|
|
|
|
|
if (miterRatio <= miterLimit) {
|
|
|
parallel.push_back(miterPoint);
|
|
|
}
|
|
|
else {
|
|
|
// 超过限制,使用斜角连接
|
|
|
parallel.push_back(offsetPrev);
|
|
|
parallel.push_back(offsetNext);
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
parallel.push_back(offsetPrev);
|
|
|
parallel.push_back(offsetNext);
|
|
|
}
|
|
|
}
|
|
|
else if (joinType == JoinType::ROUND) {
|
|
|
// 圆角连接
|
|
|
parallel.push_back(offsetPrev);
|
|
|
|
|
|
if (angle < 180.0 && std::abs(distance) > 0) {
|
|
|
// 计算圆弧
|
|
|
tPoint2D center = original[i];
|
|
|
double radius = std::abs(distance);
|
|
|
|
|
|
tPoint2D dir1 = (original[i - 1] - original[i]).normalize();
|
|
|
tPoint2D dir2 = (original[i + 1] - original[i]).normalize();
|
|
|
|
|
|
double startAngle = std::atan2(dir1.y0, dir1.x0) + PI/2;
|
|
|
double endAngle = std::atan2(dir2.y0, dir2.x0) + PI/2;
|
|
|
|
|
|
// 根据角度调整分段数
|
|
|
int segments = max(3, static_cast<int>(angle / 30.0));
|
|
|
segments = min(segments, roundSegments);
|
|
|
|
|
|
auto arcPoints = generateArcPoints(center, radius,
|
|
|
startAngle, endAngle, segments);
|
|
|
parallel.insert(parallel.end(), arcPoints.begin(), arcPoints.end());
|
|
|
}
|
|
|
|
|
|
parallel.push_back(offsetNext);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 4. 处理最后一个点之前的连接
|
|
|
if (n > 1) {
|
|
|
parallel.push_back(offsetPoints[n - 2]);
|
|
|
}
|
|
|
|
|
|
// 5. 处理终点
|
|
|
if (endType == EndType::BUTT) {
|
|
|
parallel.push_back(offsetPoints[n - 1]);
|
|
|
}
|
|
|
else if (endType == EndType::SQUARE) {
|
|
|
parallel.push_back(offsetPoints[n - 1]);
|
|
|
if (n > 1) {
|
|
|
tPoint2D dir = original[n - 1] - original[n - 2];
|
|
|
dir = dir.normalize() * std::abs(distance) * 0.5;
|
|
|
parallel.push_back(offsetPoints[n - 1] + dir);
|
|
|
}
|
|
|
}
|
|
|
else if (endType == EndType::ROUND) {
|
|
|
parallel.push_back(offsetPoints[n - 1]);
|
|
|
}
|
|
|
|
|
|
// 6. 检查并修复自相交
|
|
|
if (checkSelfIntersection(parallel)) {
|
|
|
std::cout << "Warning: Self-intersection detected, applying fix..." << std::endl;
|
|
|
parallel = fixSelfIntersection(original, parallel, distance, joinType, miterLimit);
|
|
|
}
|
|
|
|
|
|
return parallel;
|
|
|
}
|
|
|
|
|
|
// 打印点集
|
|
|
//void printPoints(const std::vector<tPoint2D>& points, const std::string& name) {
|
|
|
// std::cout << "\n" << name << " (" << points.size() << " points):" << std::endl;
|
|
|
// for (size_t i = 0; i < points.size(); i++) {
|
|
|
// std::cout << " " << i << ": (" << points[i].x0 << ", " << points[i].y0 << ")" << std::endl;
|
|
|
// }
|
|
|
//}
|
|
|
|
|
|
// 生成平行折线(简化接口)
|
|
|
std::vector<tPoint2D> createParallel(const std::vector<tPoint2D>& original,
|
|
|
double distance,
|
|
|
bool miterJoin = true,
|
|
|
bool roundEnds = false) {
|
|
|
JoinType joinType = miterJoin ? JoinType::MITER : JoinType::BEVEL;
|
|
|
EndType endType = roundEnds ? EndType::ROUND : EndType::BUTT;
|
|
|
|
|
|
return createParallelPolyline(original, distance, joinType, endType, 2.0, 8);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
extern AFX_EXT_API void makeCurveParallelSimple(CCurve& inCurve, double offset, CCurveEx& outCurve);
|
|
|
extern AFX_EXT_API void makeCurveParallel(CCurve& inCurve, double offset, CCurveEx& outCurve);
|
|
|
extern AFX_EXT_API void offsetCurveWithClipper3(CCurve& inCurve, double offset, CCurveEx& outCurve);//不好用
|
|
|
extern AFX_EXT_API void offsetCurveWithClipper2(CCurve& inCurve, double offset, CCurveEx& outCurve); //不好用
|