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/Module/GeoSigmaDraw/SegmentSnappingEngine.h

339 lines
9.6 KiB
C

1 month ago
#pragma once
#include "SpatialIndex.h"
#include "DrawOperator/Xy.h"
#include "GeometryUtils.h"
namespace Geometry
{
/**
* \brief <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>Χ
*/
struct SnapConfig
{
double searchRadius = 0.0; // [<5B><>ɸ] <20>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>뾶 (ͨ<><CDA8><EFBFBD><EFBFBD> snapDistance <20><>һ<EFBFBD><EFBFBD><E3A3AC><EFBFBD>ڻ<EFBFBD>ȡ<EFBFBD><C8A1>ѡ<EFBFBD><D1A1>)
double angleTolerance = 0.0; // [<5B>Ƕ<EFBFBD>] ƽ<><C6BD><EFBFBD>ж<EFBFBD><D0B6>ݲ<DDB2><EEA3AC>λ<EFBFBD><CEBB><EFBFBD><EFBFBD> (Degrees)<29><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 5.0 <20><>ʾ <20><>5 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊƽ<CEAA><C6BD>
double snapDistance = 0.0; // [<5B><>ֵ] <20><>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ (<28><>ʵ<EFBFBD><CAB5><EFBFBD>굥λ)<29><>С<EFBFBD>ڴ˾<DAB4><CBBE><EFBFBD><EFBFBD>Żᴥ<C5BB><E1B4A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
};
/**
* \brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ö<EFBFBD><EFBFBD>
*/
enum class SnapType
{
NONE, // δ<><CEB4><EFBFBD><EFBFBD>
POINT, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ij<EFBFBD><C4B3><EFBFBD>ض<EFBFBD><D8B6><EFBFBD> (<28>˵㡢<CBB5>е<EFBFBD><D0B5><EFBFBD>)
PARALLEL_LINE, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ij<EFBFBD><C4B3>ƽ<EFBFBD><C6BD><EFBFBD>߶<EFBFBD><>ֹ<EFBFBD><D6B9><EFBFBD>)
};
/**
* \brief <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>Ϣ
*/
struct SnapResult
{
SnapType snapType = SnapType::NONE; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
double distanceSq = 0.0; // ԭʼ<D4AD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڶ<EFBFBD><DAB6><EFBFBD>ѡʱ<D1A1><CAB1><EFBFBD><EFBFBD><EFBFBD>ȼ<EFBFBD><C8BC>ȶԣ<C8B6>
POSITION sourceElement = nullptr; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>ͼԪ (Source Object)
Point offset = { 0.0, 0.0 }; // [<5B><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>] <20><><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>InputPoint + Offset = SnappedPoint
Segment snappedSegment; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><><D4A4><EFBFBD>ã<EFBFBD><C3A3><EFBFBD>Ӧ<EFBFBD><D3A6> offset)
Point snappedReferencePoint; // <20>Ӿ<EFBFBD><D3BE>ο<EFBFBD><CEBF><EFBFBD> (<28><><EFBFBD><EFBFBD><E7A3BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĸ<EFBFBD><C4B8>˵㣬<CBB5><E3A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD>ߵ<EFBFBD><DFB5>ĸ<EFBFBD>ͶӰ<CDB6><D3B0>)
Segment targetSegment; // [ƽ<><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ר<EFBFBD><D7A8>] <20><>¼Ŀ<C2BC><C4BF><EFBFBD>߶εļ<CEB5><C4BC><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD><CFA2><EFBFBD><EFBFBD><EFBFBD>ں<EFBFBD><DABA><EFBFBD><EFBFBD><EFBFBD>ͶӰ<CDB6><D3B0><EFBFBD><EFBFBD>
};
/**
* \brief <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> (Input Segment)<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><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>
*/
class SnappingEngine
{
public:
/**
* \brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* \param pXy ͼԪ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>
* \param index <EFBFBD>ռ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (QuadTree/R-Tree)
* \param config <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* \param ignoreSet <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>)
*/
SnappingEngine(CXy* pXy, const CSpatialIndex& index, const SnapConfig& config, std::set<void*> ignoreSet = {})
: m_pXy(pXy), m_index(index), m_config(config), m_ingoreSet(ignoreSet)
{
}
/**
* \brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* * <EFBFBD><EFBFBD><EFBFBD>̣<EFBFBD>
* 1. AABB <EFBFBD><EFBFBD>ѯ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD>߶<EFBFBD><EFBFBD><EFBFBD>Χ<EFBFBD>ĺ<EFBFBD>ѡͼԪ<EFBFBD><EFBFBD>
* 2. <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>
* 3. <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>
* 4. <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>
* \param inputSegment <EFBFBD><EFBFBD>ǰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>϶<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD>߶<EFBFBD>
* \return SnapResult <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> SnapType::NONE
*/
SnapResult getSnapResult(const Segment& inputSegment)
{
// Step 1: <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28>߶<EFBFBD> AABB <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
CRect8 area = getLineSegmentAABB(inputSegment, m_config.snapDistance);
// Step 2: <20>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ
std::vector<POSITION> positions = m_index.FindElementsInRect(area);
SnapResult bestPointSnap;
SnapResult bestLineSnap;
// Step 3: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><CFB8><EFBFBD>μ<EFBFBD><CEBC><EFBFBD>
for (POSITION pos : positions)
{
SnapResult pResult = calcPointSnap(inputSegment, pos);
if (isBetter(pResult, bestPointSnap))
{
bestPointSnap = pResult;
}
SnapResult lResultl = calcParallelSnap(inputSegment, pos);
if (isBetter(lResultl, bestLineSnap))
{
bestLineSnap = lResultl;
}
}
// Step 4: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӯ<EFBFBD><D3AE> (Point vs Line)
SnapResult finalResult = decideWinner(bestPointSnap, bestLineSnap);
// Step 5: <20><>ֵ<EFBFBD>ض<EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ľ<EFBFBD><C4BE><EFBFBD><EFBFBD><EFBFBD>Ȼ<EFBFBD><C8BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD> snapDistance<63><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (finalResult.distanceSq <= m_config.snapDistance)
{
return finalResult;
}
else
{
return noneResult();
}
}
private:
CRect8 getLineSegmentAABB(const Segment& segment, double width) const
{
double halfWidth = width / 2.0;
double rawMinX = min(segment.start.x, segment.end.x);
double rawMaxX = max(segment.start.x, segment.end.x);
double rawMinY = min(segment.start.y, segment.end.y);
double rawMaxY = max(segment.start.y, segment.end.y);
return CRect8(rawMinX - halfWidth, rawMaxY + halfWidth, rawMaxX + halfWidth, rawMinY - halfWidth);
}
/**
* \brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (Point Snap)
* * <EFBFBD>߼<EFBFBD><EFBFBD><EFBFBD>
* 1. <EFBFBD><EFBFBD>ȡĿ<EFBFBD><EFBFBD>ͼԪ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>йؼ<EFBFBD><EFBFBD><EFBFBD> (<EFBFBD>˵<EFBFBD>ڵ<EFBFBD>)<EFBFBD><EFBFBD>
* 2. <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Щ<EFBFBD> inputSegment <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֱ<EFBFBD>ߵ<EFBFBD>ͶӰλ<EFBFBD>á<EFBFBD>
* 3. Լ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͶӰ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> inputSegment <EFBFBD>߶<EFBFBD><EFBFBD>ڲ<EFBFBD> (t <EFBFBD><EFBFBD> [0, 1])<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>
* 4. <EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD>С<EFBFBD><EFBFBD><EFBFBD>
*/
SnapResult calcPointSnap(const Segment& inputSegment, POSITION target) const
{
COne* pOne = m_pXy->GetAt(target);
if (!pOne)
{
return noneResult();
}
if (inIngoreSet(pOne->value))
{
return noneResult();
}
// 1. <20>ռ<EFBFBD>Ŀ<EFBFBD><EFBFBD><EABCB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>еĹؼ<C4B9><D8BC><EFBFBD>
std::vector<Point> candidatePoints;
int pOneType = pOne->GetType();
if (pOneType == DOUBLEFOX_POINT)
{
CPointNameEx* pPoint = pOne->GetValueSafe<CPointNameEx>();
candidatePoints.push_back({ pPoint->x0, pPoint->y0 });
}
else if (pOneType == DOUBLEFOX_CURVE)
{
CCurveEx* pCurve = pOne->GetValueSafe<CCurveEx>();
for (int i = 0; i < pCurve->num; i++)
{
candidatePoints.push_back({ pCurve->x[i], pCurve->y[i] });
}
}
SnapResult bestLocal = noneResult();
bestLocal.distanceSq = std::numeric_limits<double>::max();
double snapDistSqLimit = m_config.snapDistance * m_config.snapDistance;
// 2. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>к<EFBFBD>ѡ<EFBFBD><D1A1>
for (const auto& targetPt : candidatePoints)
{
// <20><><EFBFBD><EFBFBD> Ŀ<><C4BF><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD> <20>ϵ<EFBFBD>ͶӰ<CDB6><D3B0><EFBFBD><EFBFBD> t
// <20><>ʽ: P_proj = Start + t * Dir
double t = GeometryUtils::getProjectionFactor(targetPt, inputSegment);
// [<5B><><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC>]: ֻ<>е<EFBFBD> t <20><> [0, 1] ֮<><D6AE>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><CEBB><EFBFBD>߶εġ<CEB5><C4A1><EFBFBD><EFBFBD><EFBFBD><E0B7BD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƣ<EFBFBD><C6A3>߶ε<DFB6><CEB5>ӳ<EFBFBD><D3B3><EFBFBD>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD>ĵ㣬<C4B5><E3A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܲ<EFBFBD>
if (t >= 0.0 && t <= 1.0)
{
// <20><><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><DFB6>ϵ<EFBFBD>ͶӰ<CDB6><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Point dir = inputSegment.direction();
Point projPoint = inputSegment.start + (dir * t);
// <20><><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ͶӰ<CDB6><D3B0> ָ<><D6B8> Ŀ<><C4BF><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><DFB6><EFBFBD>Ҫ<EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)
Point offset = targetPt - projPoint;
double currentDistSq = offset.lengthSq();
// 3. <20><><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><D0B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (currentDistSq <= snapDistSqLimit && currentDistSq < bestLocal.distanceSq)
{
bestLocal.snapType = SnapType::POINT;
bestLocal.sourceElement = target;
bestLocal.distanceSq = currentDistSq;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
bestLocal.offset = offset;
bestLocal.snappedReferencePoint = targetPt; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><>߶<EFBFBD>ƽ<EFBFBD><C6BD> offset)
bestLocal.snappedSegment.start = inputSegment.start + offset;
bestLocal.snappedSegment.end = inputSegment.end + offset;
}
}
}
return bestLocal;
}
/**
* \brief <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (Parallel Line Snap)
* * <EFBFBD>߼<EFBFBD><EFBFBD><EFBFBD>
* 1. <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶Ρ<EFBFBD>
* 2. ƽ<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> angleTolerance <EFBFBD><EFBFBD>Χ<EFBFBD>ڡ<EFBFBD>
* 3. <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>
* 4. <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> inputSegment <EFBFBD><EFBFBD><EFBFBD> targetSegment <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֱ<EFBFBD>ߵĴ<EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD>
*/
SnapResult calcParallelSnap(const Segment& inputSegment, POSITION target) const
{
COne* pOne = m_pXy->GetAt(target);
// ֻ<><D6BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F>߶<EFBFBD><DFB6><EFBFBD><EFBFBD><EFBFBD>
if (!pOne || pOne->GetType() != DOUBLEFOX_CURVE)
{
return noneResult();
}
if (inIngoreSet(pOne->value))
{
return noneResult();
}
CCurveEx* pCurve = pOne->GetValueSafe<CCurveEx>();
SnapResult bestLocal = noneResult();
bestLocal.distanceSq = std::numeric_limits<double>::max();
double snapDistSqLimit = m_config.snapDistance * m_config.snapDistance;
// <20><><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD><DFB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
for (int i = 0; i < pCurve->num - 1; i++)
{
Segment targetSeg;
targetSeg.start = { pCurve->x[i], pCurve->y[i] };
targetSeg.end = { pCurve->x[i + 1], pCurve->y[i + 1] };
// 1. <20>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7>ӽ<EFBFBD>ƽ<EFBFBD><C6BD> (<28>Ƕ<EFBFBD><C7B6>ݲ<EFBFBD>)
if (GeometryUtils::isNearlyParallel(inputSegment, targetSeg, m_config.angleTolerance))
{
// 2. <20>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7><EFBFBD>ͶӰ<CDB6><D3B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD> (<28><>ֹ<EFBFBD><D6B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3><EFBFBD>)
if (GeometryUtils::areSegmentsOverlapping(inputSegment, targetSeg))
{
// 3. <20><><EFBFBD>㴹ֱ<E3B4B9><D6B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƫ<EFBFBD><C6AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> inputSegment <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͶӰ<CDB6><D3B0> targetSeg <20><><EFBFBD>ڵ<EFBFBD>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD>
double t = GeometryUtils::getProjectionFactor(inputSegment.start, targetSeg);
Point targetDir = targetSeg.direction();
Point projStart = targetSeg.start + (targetDir * t);
// ƫ<><C6AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD> = ͶӰ<CDB6><D3B0> - <20><><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD><DFB6><EFBFBD><EFBFBD><EFBFBD>
Point offset = projStart - inputSegment.start;
double currentDistSq = offset.lengthSq();
if (currentDistSq <= snapDistSqLimit && currentDistSq < bestLocal.distanceSq)
{
bestLocal.snapType = SnapType::PARALLEL_LINE;
bestLocal.sourceElement = target;
bestLocal.distanceSq = currentDistSq;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
bestLocal.offset = offset;
bestLocal.snappedReferencePoint = projStart; // <20>Ӿ<EFBFBD><D3BE>ο<EFBFBD><CEBF><EFBFBD>
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
bestLocal.snappedSegment.start = inputSegment.start + offset;
bestLocal.snappedSegment.end = inputSegment.end + offset;
bestLocal.targetSegment = targetSeg;
}
}
}
}
return bestLocal;
}
bool isBetter(const SnapResult& current, const SnapResult& target)
{
if (target.snapType == SnapType::NONE)
{
return true;
}
if (current.snapType != SnapType::NONE && current.snapType == target.snapType)
{
return current.distanceSq < target.distanceSq;
}
return false;
}
SnapResult decideWinner(const SnapResult& current, const SnapResult& target) const
{
if (current.snapType != SnapType::NONE && target.snapType != SnapType::NONE)
{
return current.distanceSq < target.distanceSq ? current : target;
}
return current.snapType == SnapType::NONE ? target : current;
}
SnapResult noneResult() const
{
SnapResult result;
result.sourceElement = nullptr;
result.snapType = SnapType::NONE;
return result;
}
double calcDistanceSq(double dx, double dy) const
{
return dx * dx + dy * dy;
}
bool inIngoreSet(void* pValue) const
{
return m_ingoreSet.find(pValue) != m_ingoreSet.end();
}
const CSpatialIndex& m_index;
SnapConfig m_config;
CXy* m_pXy = nullptr;
std::set<void*> m_ingoreSet; // <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>洢 COne Value ָ<><D6B8>
};
}