|
|
|
|
|
#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>
|