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.
160 lines
3.7 KiB
C++
160 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include "DrawOperator/One.h"
|
|
#include "DrawOperator/Xy.h"
|
|
#include "DrawOperator/RTree.h"
|
|
|
|
class CSpatialIndex
|
|
{
|
|
public:
|
|
CSpatialIndex(CXy& xy)
|
|
: m_xy(xy)
|
|
{
|
|
BuildRTree(xy);
|
|
}
|
|
|
|
/**
|
|
* 获取吸附点
|
|
*
|
|
* \param searchCenter 当前点
|
|
* \param searchRadius 搜索半径(其实是矩形区域)
|
|
* \return 范围内最近元素的点
|
|
*/
|
|
std::optional<NBase::CPoint2D> GetSnapPoint(const NBase::CPoint2D& searchCenter, double searchRadius) const
|
|
{
|
|
std::vector<POSITION> positions = FindElementsInRadius(searchCenter, searchRadius);
|
|
return positions.empty() ? std::nullopt : FindNearestPoint(positions, searchCenter);
|
|
}
|
|
|
|
/**
|
|
* 查找范围内元素
|
|
*
|
|
* \param rect 范围
|
|
* \return 返回范围内元素列表
|
|
*/
|
|
std::vector<POSITION> FindElementsInRect(const NBase::CRect8& rect) const
|
|
{
|
|
double minValue[2]{ rect.left, rect.top };
|
|
double maxValue[2]{ rect.right, rect.bottom };
|
|
|
|
std::vector<POSITION> positions;
|
|
|
|
auto callback = [&positions](POSITION pos)
|
|
{
|
|
positions.push_back(pos);
|
|
return true;
|
|
};
|
|
|
|
m_tree.Search(minValue, maxValue, callback);
|
|
|
|
return positions;
|
|
|
|
}
|
|
|
|
/**
|
|
* 查找范围内的图元
|
|
*
|
|
* \param searchCenter 当前点
|
|
* \param searchRadius 搜索半径(其实是矩形区域)
|
|
* \return
|
|
*/
|
|
std::vector<POSITION> FindElementsInRadius(const NBase::CPoint2D& searchCenter, double searchRadius) const
|
|
{
|
|
NBase::CRect8 rect(
|
|
searchCenter.x0 - searchRadius / 2,
|
|
searchCenter.y0 - searchRadius / 2,
|
|
searchCenter.x0 + searchRadius / 2,
|
|
searchCenter.y0 + searchRadius / 2
|
|
);
|
|
return FindElementsInRect(rect);
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* 构建 RTree
|
|
*
|
|
*/
|
|
void BuildRTree(CXy& xy)
|
|
{
|
|
CPtrList& values = *(xy.GetValueList());
|
|
|
|
for (POSITION pos = values.GetHeadPosition(); pos != nullptr; values.GetNext(pos))
|
|
{
|
|
COne* pOne = reinterpret_cast<COne*>(values.GetAt(pos));
|
|
if (IsVisiblePointElement(pOne))
|
|
{
|
|
NBase::CRect8 range(1e100, -1e100, -1e100, 1e100);
|
|
pOne->GetRange(range);
|
|
|
|
double minX = min(range.left, range.right);
|
|
double minY = min(range.top, range.bottom);
|
|
double maxX = max(range.left, range.right);
|
|
double maxY = max(range.top, range.bottom);
|
|
|
|
double minValue[2]{ minX, minY };
|
|
double maxValue[2]{ maxX, maxY };
|
|
|
|
m_tree.Insert(minValue, maxValue, pos);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 查找元素中离搜索点最近图元的坐标
|
|
*
|
|
* \param positions 图元 POSITION
|
|
* \param searchCenter 搜索点,真实坐标
|
|
* \return 如果有,返回真实坐标,否则返回 std::nullopt;
|
|
*/
|
|
std::optional<NBase::CPoint2D> FindNearestPoint(const std::vector<POSITION>& positions, const NBase::CPoint2D& searchCenter) const
|
|
{
|
|
#pragma push_macro("max")
|
|
#undef max
|
|
|
|
double minDistanceSquared = std::numeric_limits<double>::max();
|
|
NBase::CPoint2D nearestPoint; // 标记最近的点
|
|
|
|
// 从范围内查找最近的点
|
|
for (POSITION pos : positions)
|
|
{
|
|
COne* pOne = m_xy.GetAt(pos);
|
|
if (!IsVisiblePointElement(pOne))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CPointNameEx* pPoint = pOne->GetValueSafe<CPointNameEx>();
|
|
if (pPoint == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
double dx = pPoint->x0 - searchCenter.x0;
|
|
double dy = pPoint->y0 - searchCenter.y0;
|
|
// 我们其实只想知道距离远近而不是真实距离,所以不里不开根了,降低性能开销
|
|
double distanceSquared = dx * dx + dy * dy;
|
|
|
|
if (distanceSquared < minDistanceSquared)
|
|
{
|
|
minDistanceSquared = distanceSquared;
|
|
nearestPoint = NBase::CPoint2D(pPoint->x0, pPoint->y0);
|
|
}
|
|
}
|
|
return (minDistanceSquared != std::numeric_limits<double>::max())
|
|
? std::optional(nearestPoint)
|
|
: std::nullopt;
|
|
|
|
#pragma pop_macro("max")
|
|
}
|
|
|
|
bool IsVisiblePointElement(COne* pOne) const
|
|
{
|
|
return pOne != nullptr
|
|
&& pOne->GetType() == DOUBLEFOX_POINT
|
|
&& pOne->IsView();
|
|
}
|
|
|
|
RTree<POSITION, double, 2> m_tree;
|
|
CXy& m_xy;
|
|
}; |