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

1 month ago
#pragma once
#include "clipper2/clipper.h"
using namespace Clipper2Lib;
class PointInPolygonTester
{
private:
// <20><> double <20><><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>Ϊ int64_t<5F><74>Clipper2 ʹ<><CAB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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);
// <20><><EFBFBD><EFBFBD>1<EFBFBD><31><EFBFBD>жϵ<D0B6><CFB5><EFBFBD><EFBFBD><EFBFBD>
void getPolygonPath(CCurve& curve, Clipper2Lib::Path64& curvePath, int scale = 100);
bool isPointInPolygon(double x, double y, const std::vector<std::pair<double, double>>& polygon);
// <20><><EFBFBD><EFBFBD>2<EFBFBD><32><EFBFBD>жϵ<D0B6><CFB5>Ƿ<EFBFBD><C7B7>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڻ<EFBFBD><DABB>߽<EFBFBD><DFBD><EFBFBD>
PointInPolygonResult detailedPointTest(double x, double y,
const std::vector<std::pair<double, double>>& polygon);
// <20><><EFBFBD><EFBFBD>3<EFBFBD><33><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>жϵ<D0B6>
std::vector<bool> batchTestPoints(const std::vector<std::pair<double, double>>& points,
const std::vector<std::pair<double, double>>& polygon);
// <20><><EFBFBD><EFBFBD>4<EFBFBD><34><EFBFBD><EFBFBD><EFBFBD>׵Ķ<D7B5><C4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD>
bool isPointInPolygonWithHoles(double x, double y,
const std::vector<std::pair<double, double>>& outerPolygon,
const std::vector<std::vector<std::pair<double, double>>>& holes);
// <20><><EFBFBD><EFBFBD>5<EFBFBD><35>ʹ<EFBFBD><CAB9> PolyTree <20><><EFBFBD>и<EFBFBD><D0B8><EFBFBD><EFBFBD>ж<EFBFBD>
bool isPointInComplexPolygon(double x, double y, const Paths64& polygons);
//enum class PointInPolygonResult {
// Inside,
// Outside,
// OnBoundary
//};
};
/*һЩ<D2BB><D0A9><EFBFBD><EFBFBD>ai<61><69><EFBFBD>ɵ<EFBFBD><C9B5><EFBFBD><E3B7A8><EFBFBD><EFBFBD>,<2C><>̫<EFBFBD><CCAB><EFBFBD><EFBFBD>*/
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);
}
// Ϊ<>˷<EFBFBD><CBB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
void print() const {
std::cout << "(" << x0 << ", " << y0 << ")";
}
} tPoint2D;
class ParallelPolylineGenerator //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
{
public:
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ö<EFBFBD><C3B6>
enum class JoinType {
MITER, // б<><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ROUND, // Բ<><D4B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
BEVEL // б<><D0B1><EFBFBD><EFBFBD><EFBFBD>ӣ<EFBFBD>ƽͷ<C6BD><CDB7>
};
// <20>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD>ö<EFBFBD><C3B6>
enum class EndType {
BUTT, // ƽͷ<C6BD>˵<EFBFBD>
SQUARE, // <20><><EFBFBD>ζ˵<CEB6>
ROUND // Բ<>Ƕ˵<C7B6>
};
private:
// <20><><EFBFBD><EFBFBD><EFBFBD>㵽ֱ<E3B5BD>ߵĴ<DFB5>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD>
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);
// <20><>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת90<39>ȣ<EFBFBD>
tPoint2D perp = tPoint2D(-dir.y0, dir.x0);
perp = perp.normalize();
return perp * distance;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֱ<EFBFBD>ߵĽ<DFB5><C4BD><EFBFBD>
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;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>нǣ<D0BD>0-180<38>ȣ<EFBFBD>
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;
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڶ<EFBFBD><DAB6><EFBFBD>ļн<C4BC>
double calculateAngle(const tPoint2D& prev, const tPoint2D& curr, const tPoint2D& next) {
tPoint2D v1 = prev - curr;
tPoint2D v2 = next - curr;
return angleBetweenVectors(v1, v2);
}
// <20><><EFBFBD><EFBFBD>Բ<EFBFBD><D4B2><EFBFBD><EFBFBD>
std::vector<tPoint2D> generateArcPoints(const tPoint2D& center,
double radius,
double startAngle,
double endAngle,
int segments = 8) {
std::vector<tPoint2D> points;
// ȷ<><C8B7><EFBFBD>Ƕȵ<C7B6><C8B5><EFBFBD>
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;
}
// <20><><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><DFB6>Ƿ<EFBFBD><C7B7>
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);
// һ<><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (o1 * o2 < 0 && o3 * o4 < 0) return true;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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;
}
// <20><><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD>
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;
}
// <20>޸<EFBFBD><DEB8><EFBFBD><EFBFBD>
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;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
fixedPath.push_back(parallel[0]);
// <20><><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ㣬<D3B5><E3A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>б<EFBFBD><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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);
// <20><><EFBFBD><EFBFBD>ƫ<EFBFBD>Ƶ<EFBFBD>
tPoint2D offsetPrev = curr + perpendicularVector(curr, prev, -distance);
tPoint2D offsetNext = curr + perpendicularVector(curr, next, distance);
if (angle < 90.0) {
// <20><><EFBFBD>ǣ<EFBFBD>ǿ<EFBFBD><C7BF>ʹ<EFBFBD><CAB9>б<EFBFBD><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
fixedPath.push_back(offsetPrev);
fixedPath.push_back(offsetNext);
}
else {
// <20>۽ǻ<DBBD>ֱ<EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CDB4><EFBFBD>
tPoint2D miterPoint;
if (lineIntersection(offsetPrev, offsetPrev + (prev - curr),
offsetNext, offsetNext + (next - curr),
miterPoint)) {
// <20><><EFBFBD><EFBFBD>б<EFBFBD>ӳ<EFBFBD><D3B3><EFBFBD>
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);
}
}
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
fixedPath.push_back(parallel.back());
return fixedPath;
}
public:
// <20><><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD>Ƶ<EFBFBD>
std::vector<tPoint2D> offsetPoints(n);
// <20><>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
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;
}
// <20>м<EFBFBD><D0BC><EFBFBD>
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);
// ƽ<><C6BD><EFBFBD><EFBFBD>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD>
tPoint2D perpAvg = (perpPrev + perpNext).normalize();
offsetPoints[i] = original[i] + perpAvg * distance;
}
// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
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. <20><><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD>
if (endType == EndType::BUTT) {
parallel.push_back(offsetPoints[0]);
}
else if (endType == EndType::SQUARE) {
// <20><><EFBFBD>ζ˵㣺<CBB5>ӳ<EFBFBD><D3B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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) {
// Բ<>Ƕ˵㣺<CBB5><E3A3BA><EFBFBD><EFBFBD>Բ<EFBFBD><D4B2>
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. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD>
for (size_t i = 1; i < n - 1; i++) {
double angle = calculateAngle(original[i - 1], original[i], original[i + 1]);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD>Ʒ<EFBFBD><C6B7><EFBFBD>
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) {
// б<><D0B1><EFBFBD><EFBFBD><EFBFBD>ӻ<EFBFBD><D3BB><EFBFBD><EFBFBD>ǣ<EFBFBD>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
parallel.push_back(offsetPrev);
parallel.push_back(offsetNext);
}
else if (joinType == JoinType::MITER) {
// <20><><EFBFBD><EFBFBD>б<EFBFBD>ӵ<EFBFBD>
tPoint2D miterPoint;
if (lineIntersection(offsetPrev, offsetPrev + (original[i - 1] - original[i]),
offsetNext, offsetNext + (original[i + 1] - original[i]),
miterPoint)) {
// <20><><EFBFBD><EFBFBD>б<EFBFBD><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
double miterLength = (miterPoint - original[i]).length();
double miterRatio = miterLength / std::abs(distance);
if (miterRatio <= miterLimit) {
parallel.push_back(miterPoint);
}
else {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƣ<EFBFBD>ʹ<EFBFBD><CAB9>б<EFBFBD><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
parallel.push_back(offsetPrev);
parallel.push_back(offsetNext);
}
}
else {
parallel.push_back(offsetPrev);
parallel.push_back(offsetNext);
}
}
else if (joinType == JoinType::ROUND) {
// Բ<><D4B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
parallel.push_back(offsetPrev);
if (angle < 180.0 && std::abs(distance) > 0) {
// <20><><EFBFBD><EFBFBD>Բ<EFBFBD><D4B2>
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;
// <20><><EFBFBD>ݽǶȵ<C7B6><C8B5><EFBFBD><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD>
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. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>֮ǰ<D6AE><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (n > 1) {
parallel.push_back(offsetPoints[n - 2]);
}
// 5. <20><><EFBFBD><EFBFBD><EFBFBD>յ<EFBFBD>
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. <20><><EFBFBD><EFBFBD>޸<EFBFBD><DEB8><EFBFBD><EFBFBD>
if (checkSelfIntersection(parallel)) {
std::cout << "Warning: Self-intersection detected, applying fix..." << std::endl;
parallel = fixSelfIntersection(original, parallel, distance, joinType, miterLimit);
}
return parallel;
}
// <20><>ӡ<EFBFBD>
//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;
// }
//}
// <20><><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD>ߣ<EFBFBD><DFA3>򻯽ӿڣ<D3BF>
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);//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
extern AFX_EXT_API void offsetCurveWithClipper2(CCurve& inCurve, double offset, CCurveEx& outCurve); //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>