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++

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