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.

207 lines
4.5 KiB
C

1 month ago
#pragma once
#include <cmath>
#include <algorithm>
#include <vector>
namespace Geometry
{
struct Point
{
double x = 0.0;
double y = 0.0;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD><D8A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Point operator+(const Point& other) const
{
return { x + other.x, y + other.y };
}
Point operator-(const Point& other) const
{
return { x - other.x, y - other.y };
}
Point operator*(double scalar) const
{
return { x * scalar, y * scalar };
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
double dot(const Point& other) const
{
return x * other.x + y * other.y;
}
// <20><><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3>ƽ<EFBFBD><C6BD> (<28><><EFBFBD><EFBFBD><E2BFAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ż<EFBFBD>)
double lengthSq() const
{
return x * x + y * y;
}
// <20><><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3>
double length() const
{
return std::sqrt(lengthSq());
}
};
struct Segment
{
Point start;
Point end;
// <20><>ȡ<EFBFBD>߶εķ<CEB5><C4B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (End - Start)
Point direction() const
{
return end - start;
}
};
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E1B9B9>
struct PointSnapResult
{
Point closestPoint; // <20>߶<EFBFBD><DFB6><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD>
double distance; // ʵ<>ʾ<EFBFBD><CABE><EFBFBD>
};
/**
*
* GeometryUtils <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*/
class GeometryUtils
{
public:
/**
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<EFBFBD><EFBFBD><EFBFBD>߶ζ<EFBFBD><EFBFBD><EFBFBD>)<EFBFBD><EFBFBD>ͶӰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> t
* P_proj = Start + t * (End - Start)
*/
static double getProjectionFactor(const Point& point, const Segment& lineSeg)
{
Point ab = lineSeg.direction();
double lenSq = ab.lengthSq();
if (lenSq < std::numeric_limits<double>::epsilon())
{
return 0.0;
}
Point ap = point - lineSeg.start;
return ap.dot(ab) / lenSq;
}
/**
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶ε<EFBFBD><EFBFBD><EFBFBD><EFBFBD>̾<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* \param point <EFBFBD><EFBFBD>ѯ<EFBFBD><EFBFBD>
* \param segment Ŀ<EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
* \return <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>;<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ľ<EFBFBD><EFBFBD>
*/
static PointSnapResult pointToSegmentDistance(const Point& point, const Segment& segment)
{
Point ab = segment.direction();
Point ap = point - segment.start;
double abLenSq = ab.lengthSq();
// <20>߽<EFBFBD><DFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶γ<DFB6><CEB3><EFBFBD>Ϊ0<CEAA><30><EFBFBD>˻<EFBFBD>Ϊһ<CEAA><D2BB><EFBFBD>
if (abLenSq < std::numeric_limits<double>::epsilon())
{
double dist = ap.length();
return { segment.start, dist };
}
// <20><><EFBFBD><EFBFBD>ͶӰϵ<D3B0><CFB5> t
// t = (AP . AB) / |AB|^2
double t = ap.dot(ab) / abLenSq;
// <20><><EFBFBD><EFBFBD> t <20><> [0, 1] ֮<><EFBFBD>о<EFBFBD> Clamping<6E><67>
// <20><><EFBFBD><EFBFBD> t < 0<><30>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> start <20><><EFBFBD><EFBFBD><E0A3BB><EFBFBD><EFBFBD> t > 1<><31><EFBFBD><EFBFBD> end <20><><EFBFBD><EFBFBD>
t = max(0.0, min(1.0, t));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: Closest = Start + t * AB
Point closest = segment.start + (ab * t);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
double dist = (point - closest).length();
return { closest, dist };
}
/**
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>޳<EFBFBD>ֱ<EFBFBD>ߵĴ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD>
*/
static double pointToLineDistanceSq(const Point& point, const Segment& lineSeg)
{
Point ab = lineSeg.direction();
Point ap = point - lineSeg.start;
double abLenSq = ab.lengthSq();
if (abLenSq < std::numeric_limits<double>::epsilon())
{
return ap.lengthSq();
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ (2D<32><44><EFBFBD><EFBFBD> = x1*y2 - x2*y1) <20><>ʾƽ<CABE><C6BD><EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD> = <20><> * <20><> => <20><> = <20><><EFBFBD><EFBFBD> / <20><>
double crossProduct = ab.x * ap.y - ab.y * ap.x;
return (crossProduct * crossProduct) / abLenSq;
}
/**
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/ƽ<EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD>ͶӰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD>
* <EFBFBD><EFBFBD><EFBFBD>ڷ<EFBFBD>ֹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȼƽ<EFBFBD>е<EFBFBD>ʵ<EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
*/
static bool areSegmentsOverlapping(const Segment& seg1, const Segment& seg2)
{
// <20><> Seg2 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD>ͶӰ<CDB6><D3B0> Seg1 <20><>ֱ<EFBFBD><D6B1><EFBFBD>ϣ<EFBFBD><CFA3><EFBFBD> t ֵ<><D6B5>Χ
double t1 = getProjectionFactor(seg2.start, seg1);
double t2 = getProjectionFactor(seg2.end, seg1);
double minT = min(t1, t2);
double maxT = max(t1, t2);
// Seg1 <20><> t <20><>Χ<EFBFBD><CEA7> [0, 1]
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> [minT, maxT] <20><> [0, 1] <20>Ƿ<EFBFBD><C7B7>н<EFBFBD><D0BD><EFBFBD>
return max(0.0, minT) <= min(1.0, maxT);
}
/**
* <EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><EFBFBD>Ƿ񡰽ӽ<EFBFBD>ƽ<EFBFBD>С<EFBFBD>
* \param seg1 <EFBFBD>߶<EFBFBD>1
* \param seg2 <EFBFBD>߶<EFBFBD>2
* \param toleranceDegrees <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD><EFBFBD><EFBFBD>Ƕȣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* \return true <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӽ<EFBFBD>ƽ<EFBFBD><EFBFBD>
*/
static bool isNearlyParallel(const Segment& seg1, const Segment& seg2, double toleranceDegrees)
{
Point v1 = seg1.direction();
Point v2 = seg2.direction();
double len1 = v1.length();
double len2 = v2.length();
// <20><>ֹ<EFBFBD><D6B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (len1 < std::numeric_limits<double>::epsilon() || len2 < std::numeric_limits<double>::epsilon())
{
return false;
}
// <20><><EFBFBD>㵥λ<E3B5A5><CEBB><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>
// cos(theta) = (v1 . v2) / (|v1| * |v2|)
double cosTheta = v1.dot(v2) / (len1 * len2);
// ƽ<><C6BD><EFBFBD><EFBFBD>ζ<EFBFBD>ŽǶȽӽ<C8BD> 0<><30> (cos=1) <20><> 180<38><30> (cos=-1)
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD> abs(cosTheta) <20>Ƿ<EFBFBD><C7B7>ӽ<EFBFBD> 1
// <20><><EFBFBD>ݲ<EFBFBD><DDB2>Ƕ<EFBFBD>ת<EFBFBD><D7AA>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD>
double toleranceRad = toleranceDegrees * (M_PI / 180.0);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݲ<EFBFBD><DDB2><EFBFBD> 5<>ȣ<EFBFBD><C8A3><EFBFBD>ô cos(5<><35>) ԼΪ 0.996
// ֻҪ abs(cosTheta) > cos(tolerance)<29><>˵<EFBFBD><CBB5><EFBFBD>н<EFBFBD>С<EFBFBD><D0A1> tolerance
double threshold = std::cos(toleranceRad);
return std::abs(cosTheta) >= threshold;
}
};
}