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