#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 GetSnapPoint(const NBase::CPoint2D& searchCenter, double searchRadius) const { std::vector positions = FindElementsInRadius(searchCenter, searchRadius); return positions.empty() ? std::nullopt : FindNearestPoint(positions, searchCenter); } /** * 查找范围内元素 * * \param rect 范围 * \return 返回范围内元素列表 */ std::vector FindElementsInRect(const NBase::CRect8& rect) const { double minValue[2]{ rect.left, rect.top }; double maxValue[2]{ rect.right, rect.bottom }; std::vector 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 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(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 FindNearestPoint(const std::vector& positions, const NBase::CPoint2D& searchCenter) const { #pragma push_macro("max") #undef max double minDistanceSquared = std::numeric_limits::max(); NBase::CPoint2D nearestPoint; // 标记最近的点 // 从范围内查找最近的点 for (POSITION pos : positions) { COne* pOne = m_xy.GetAt(pos); if (!IsVisiblePointElement(pOne)) { continue; } CPointNameEx* pPoint = pOne->GetValueSafe(); 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::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 m_tree; CXy& m_xy; };