#pragma once #include "DrawOperator/One.h" #include "DrawOperator/Xy.h" #include "DrawOperator/RTree.h" #include "Util.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, CCurveEx* pIgnoreCurve = nullptr) const { std::vector positions = FindElementsInRadius(searchCenter, searchRadius); if (positions.empty()) { return std::nullopt; } std::optional nearestPoint = FindNearestPoint(positions, searchCenter); std::optional pointOnCurve = GetSnapPointOnCurve(positions, searchCenter, searchRadius, pIgnoreCurve); if (nearestPoint && pointOnCurve) { if (DistanceSq(searchCenter, *nearestPoint) < DistanceSq(searchCenter, *pointOnCurve)) { return nearestPoint; } return pointOnCurve; } return nearestPoint ? nearestPoint : pointOnCurve; } /** * 查找范围内元素 * * \param rect 范围 * \return 返回范围内元素列表 */ std::vector FindElementsInRect(const NBase::CRect8& rect) const { double minX = min(rect.left, rect.right); double maxX = max(rect.left, rect.right); double minY = min(rect.top, rect.bottom); double maxY = max(rect.top, rect.bottom); double minValue[2]{ minX, minY }; double maxValue[2]{ maxX, maxY }; 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 { double r = searchRadius; NBase::CRect8 rect( searchCenter.x0 - r, searchCenter.y0 - r, searchCenter.x0 + r, searchCenter.y0 + r ); 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) || IsVisibleCurveElement(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") } std::optional CSpatialIndex::GetSnapPointOnCurve(const std::vector& positions, const NBase::CPoint2D& searchCenter, double searchRadius, CCurveEx* pIgnoreCurve) const { double minDistanceSq = searchRadius * searchRadius; std::optional result; for (POSITION pos : positions) { COne* pOne = m_xy.GetAt(pos); if (pOne == nullptr || pOne->GetType() != DOUBLEFOX_CURVE) { continue; } CCurveEx* pCurve = pOne->GetValueSafe(); if (pCurve->num < 2) { continue; } if (pCurve == pIgnoreCurve) { continue; } for (int i = 0; i < pCurve->num - 1; i++) { double x1 = pCurve->x[i]; double y1 = pCurve->y[i]; double x2 = pCurve->x[i + 1]; double y2 = pCurve->y[i + 1]; NBase::CPoint2D point = GetClosestPointOnSegment(searchCenter, x1, y1, x2, y2); double distSq = DistanceSq(searchCenter, point); if (distSq < minDistanceSq) { minDistanceSq = distSq; result = point; } } } return result; } bool IsVisiblePointElement(COne* pOne) const { return pOne != nullptr && pOne->GetType() == DOUBLEFOX_POINT && pOne->IsView(); } bool IsVisibleCurveElement(COne* pOne) const { return pOne != nullptr && pOne->GetType() == DOUBLEFOX_CURVE && pOne->IsView(); } RTree m_tree; CXy& m_xy; };