Skip to content

Commit 5fab2ca

Browse files
committed
Speed up searchImpl and neighborsImpl
1 parent 12617bd commit 5fab2ca

1 file changed

Lines changed: 38 additions & 20 deletions

File tree

flatbush.h

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,19 +1756,31 @@ std::vector<size_t> Flatbush<ArrayType>::searchImpl(const Box<ArrayType>& iBound
17561756
// find the end index of the node
17571757
const size_t wEnd = std::min(wNodeIndex + wNodeSize, upperBound(wNodeIndex));
17581758

1759-
// search through child nodes
1760-
for (size_t wPosition = wNodeIndex; wPosition < wEnd; ++wPosition) {
1761-
// check if node bbox intersects with query bbox
1762-
if (!detail::boxesIntersect(iBounds, mBoxes[wPosition])) {
1763-
continue;
1759+
// Split node-vs-leaf: the check is invariant across all children of a node
1760+
if (wNodeIndex >= wNumItems) {
1761+
// Internal node: just collect intersecting child node indices
1762+
for (size_t wPosition = wNodeIndex; wPosition < wEnd; ++wPosition) {
1763+
if (detail::boxesIntersect(iBounds, mBoxes[wPosition])) {
1764+
wQueue.push_back(getIndex<IsWideIndex>(wPosition));
1765+
}
17641766
}
1765-
1766-
const size_t wIndex = getIndex<IsWideIndex>(wPosition);
1767-
1768-
if (wNodeIndex >= wNumItems) {
1769-
wQueue.push_back(wIndex); // node; add it to the search queue
1770-
} else if (!iFilterFn || iFilterFn(wIndex, mBoxes[wPosition])) {
1771-
wResults.push_back(wIndex); // leaf item
1767+
} else if (iFilterFn) {
1768+
// Leaf node with filter
1769+
for (size_t wPosition = wNodeIndex; wPosition < wEnd; ++wPosition) {
1770+
if (!detail::boxesIntersect(iBounds, mBoxes[wPosition])) {
1771+
continue;
1772+
}
1773+
const auto wIndex = getIndex<IsWideIndex>(wPosition);
1774+
if (iFilterFn(wIndex, mBoxes[wPosition])) {
1775+
wResults.push_back(wIndex);
1776+
}
1777+
}
1778+
} else {
1779+
// Leaf node without filter
1780+
for (size_t wPosition = wNodeIndex; wPosition < wEnd; ++wPosition) {
1781+
if (detail::boxesIntersect(iBounds, mBoxes[wPosition])) {
1782+
wResults.push_back(getIndex<IsWideIndex>(wPosition));
1783+
}
17721784
}
17731785
}
17741786

@@ -1812,6 +1824,7 @@ std::vector<size_t> Flatbush<ArrayType>::neighborsImpl(const Point<ArrayType>& i
18121824
const auto wNumItems = numItems();
18131825
const auto wNodeSize = nodeSize();
18141826
auto wNodeIndex = mBoxes.size() - 1UL;
1827+
auto wMaxDistance = iMaxDistSquared;
18151828
std::vector<IndexDistance> wQueueStorage;
18161829
wQueueStorage.reserve(wNodeSize << 2U);
18171830
std::priority_queue<IndexDistance> wQueue(std::less<IndexDistance>(), std::move(wQueueStorage));
@@ -1822,18 +1835,18 @@ std::vector<size_t> Flatbush<ArrayType>::neighborsImpl(const Point<ArrayType>& i
18221835
// find the end index of the node
18231836
const auto wEnd = std::min(wNodeIndex + wNodeSize, upperBound(wNodeIndex));
18241837

1825-
// search through child nodes
18261838
for (auto wPosition = wNodeIndex; wPosition < wEnd; ++wPosition) {
1827-
const size_t wIndex = getIndex<IsWideIndex>(wPosition);
18281839
const auto wDistSquared = detail::computeDistanceSquared(iPoint, mBoxes[wPosition]);
18291840

1830-
if (wDistSquared > iMaxDistSquared) {
1841+
if (wDistSquared > wMaxDistance) {
18311842
continue;
1832-
} else if (wNodeIndex >= wNumItems) {
1833-
wQueue.emplace(wIndex << 1U, wDistSquared);
1834-
} else if (!iFilterFn || iFilterFn(wIndex, mBoxes[wPosition])) {
1835-
// put an odd index if it's an item rather than a node, to recognize later
1836-
wQueue.emplace((wIndex << 1U) + 1U, wDistSquared); // leaf node
1843+
}
1844+
1845+
const auto wIndex = getIndex<IsWideIndex>(wPosition);
1846+
const auto wIsInternalNode = wNodeIndex >= wNumItems;
1847+
1848+
if (wIsInternalNode || !iFilterFn || iFilterFn(wIndex, mBoxes[wPosition])) {
1849+
wQueue.emplace((wIndex << 1U) + !wIsInternalNode, wDistSquared);
18371850
}
18381851
}
18391852

@@ -1848,6 +1861,11 @@ std::vector<size_t> Flatbush<ArrayType>::neighborsImpl(const Point<ArrayType>& i
18481861

18491862
if (wQueue.empty()) {
18501863
break;
1864+
} else if (wResults.size() + 1U >= iMaxResults) {
1865+
// Tighten search radius: if we have enough pending results and the queue
1866+
// top is a node, its distance is the minimum possible for any future leaf.
1867+
// Any child farther than the farthest queued leaf can't contribute.
1868+
wMaxDistance = std::min(wMaxDistance, static_cast<double>(wQueue.top().mDistance));
18511869
}
18521870

18531871
wNodeIndex = wQueue.top().mId >> 3U; // 1 to undo indexing + 2 for binary compatibility with JS

0 commit comments

Comments
 (0)