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.
kev/Drawer/SSBase/WellPoleLib/ParalleLineGenerator.h

471 lines
16 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 "clipper2/clipper.h"
using namespace Clipper2Lib;
class PointInPolygonTester
{
private:
// 将 double 坐标转换为 int64_tClipper2 使用整数坐标)
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); //不好用