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