76 using BoxType = std::remove_cvref_t<decltype(std::declval<const S&>().bbox())>;
77 using NumberType = std::remove_cvref_t<decltype(std::declval<const BoxType&>().min().x())>;
85 using NodeId = std::uint32_t;
86 static constexpr NodeId invalidNode = std::numeric_limits<NodeId>::max();
88 enum class Color :
unsigned char { red, black };
102 std::uint32_t count = 0;
103 NodeId left = invalidNode;
104 NodeId right = invalidNode;
108 struct MutationNode {
109 NodeId parent = invalidNode;
110 Color color = Color::black;
113 std::vector<ShapeType> elements_;
114 std::vector<QueryNode> nodes_;
115 std::vector<MutationNode> mutationNodes_;
116 NodeId root_ = invalidNode;
119 [[nodiscard]]
static bool less(
const T& a,
const T& b) {
123 template <
class A,
class B>
124 [[nodiscard]]
static bool equivalent(
const A& a,
const B& b) {
125 return !(a < b) && !(b < a);
128 template <
class A,
class B>
129 [[nodiscard]]
static const auto& minimum(
const A& a,
const B& b) {
130 return b < a ? b : a;
133 template <
class A,
class B>
134 [[nodiscard]]
static const auto& maximum(
const A& a,
const B& b) {
135 return a < b ? b : a;
138 [[nodiscard]]
const QueryNode& node(NodeId
id)
const {
139 return nodes_[
static_cast<std::size_t
>(id)];
142 [[nodiscard]] QueryNode& node(NodeId
id) {
143 return nodes_[
static_cast<std::size_t
>(id)];
146 [[nodiscard]]
const MutationNode& mutationNode(NodeId
id)
const {
147 return mutationNodes_[
static_cast<std::size_t
>(id)];
150 [[nodiscard]] MutationNode& mutationNode(NodeId
id) {
151 return mutationNodes_[
static_cast<std::size_t
>(id)];
157 [[nodiscard]]
bool live(NodeId
id)
const {
158 return static_cast<std::size_t
>(id) < elements_.size();
161 [[nodiscard]]
const ShapeType& shapeOf(NodeId
id)
const {
162 return elements_[
static_cast<std::size_t
>(id)];
167 [[nodiscard]] std::size_t tombstones()
const {
168 return nodes_.size() - elements_.size();
171 [[nodiscard]]
static Color colorOf(
const IntervalTree& tree, NodeId
id) {
172 return id == invalidNode ? Color::black : tree.mutationNode(
id).color;
175 template <
class Shape>
176 [[nodiscard]]
static auto project(
const Shape& shape) {
177 const auto box = shape.bbox();
179 return std::pair{box.min().x(), box.max().x()};
181 return std::pair{box.min().y(), box.max().y()};
187 if (less(lowA, lowB)) {
190 if (less(lowB, lowA)) {
193 return less(highA, highB);
197 NodeId
id, NodeId otherId)
const {
198 const QueryNode& other = node(otherId);
199 if (intervalLess(low, high, other.low, other.high)) {
202 if (intervalLess(other.low, other.high, low, high)) {
208 void update(NodeId
id) {
209 if (
id == invalidNode) {
215 QueryNode& n = node(
id);
216 n.minLow = n.maxLow = n.low;
217 n.minHigh = n.maxHigh = n.high;
218 n.count = live(
id) ? 1 : 0;
219 for (
const NodeId child : {n.left, n.right}) {
220 if (child == invalidNode) {
223 const QueryNode& c = node(child);
224 n.minLow = minimum(n.minLow, c.minLow);
225 n.maxLow = maximum(n.maxLow, c.maxLow);
226 n.minHigh = minimum(n.minHigh, c.minHigh);
227 n.maxHigh = maximum(n.maxHigh, c.maxHigh);
232 void updateUpward(NodeId
id) {
233 while (
id != invalidNode) {
235 id = mutationNode(
id).parent;
245 void relocateNode(NodeId from, NodeId to) {
246 nodes_[
static_cast<std::size_t
>(to)] = std::move(nodes_[
static_cast<std::size_t
>(from)]);
247 mutationNodes_[
static_cast<std::size_t
>(to)] =
248 mutationNodes_[
static_cast<std::size_t
>(from)];
250 const QueryNode& moved = node(to);
251 for (
const NodeId child : {moved.left, moved.right}) {
252 if (child != invalidNode) {
253 mutationNode(child).parent = to;
256 const NodeId parent = mutationNode(to).parent;
257 if (parent == invalidNode) {
259 }
else if (node(parent).left == from) {
260 node(parent).left = to;
262 node(parent).right = to;
268 NodeId relocateNodeToEnd(NodeId from) {
269 nodes_.push_back(QueryNode{});
271 mutationNodes_.push_back(MutationNode{});
276 const NodeId to =
static_cast<NodeId
>(nodes_.size() - 1);
277 relocateNode(from, to);
286 if (nodes_.size() + 1 >=
static_cast<std::size_t
>(invalidNode)) {
287 throw std::length_error(
"IntervalTree exceeds its 32-bit node capacity");
291 fresh.low = fresh.minLow = fresh.maxLow = low;
292 fresh.high = fresh.minHigh = fresh.maxHigh = high;
294 if (
static_cast<std::size_t
>(slot) == nodes_.size()) {
295 nodes_.push_back(std::move(fresh));
297 mutationNodes_.push_back(MutationNode{invalidNode, Color::red});
303 relocateNodeToEnd(slot);
304 node(slot) = std::move(fresh);
305 mutationNode(slot) = MutationNode{invalidNode, Color::red};
310 void rotateLeft(NodeId
x) {
311 const NodeId
y = node(
x).right;
312 node(
x).right = node(
y).left;
313 if (node(
y).left != invalidNode) {
314 mutationNode(node(
y).left).parent =
x;
316 mutationNode(
y).parent = mutationNode(
x).parent;
317 if (mutationNode(
x).parent == invalidNode) {
319 }
else if (
x == node(mutationNode(
x).parent).left) {
320 node(mutationNode(
x).parent).left =
y;
322 node(mutationNode(
x).parent).right =
y;
325 mutationNode(
x).parent =
y;
330 void rotateRight(NodeId
x) {
331 const NodeId
y = node(
x).left;
332 node(
x).left = node(
y).right;
333 if (node(
y).right != invalidNode) {
334 mutationNode(node(
y).right).parent =
x;
336 mutationNode(
y).parent = mutationNode(
x).parent;
337 if (mutationNode(
x).parent == invalidNode) {
339 }
else if (
x == node(mutationNode(
x).parent).right) {
340 node(mutationNode(
x).parent).right =
y;
342 node(mutationNode(
x).parent).left =
y;
345 mutationNode(
x).parent =
y;
350 void insertFixup(NodeId z) {
351 while (z != root_ && colorOf(*
this, mutationNode(z).parent) == Color::red) {
352 const NodeId parent = mutationNode(z).parent;
353 const NodeId grandparent = mutationNode(parent).parent;
354 if (parent == node(grandparent).left) {
355 NodeId uncle = node(grandparent).right;
356 if (colorOf(*
this, uncle) == Color::red) {
357 mutationNode(parent).color = Color::black;
358 mutationNode(uncle).color = Color::black;
359 mutationNode(grandparent).color = Color::red;
362 if (z == node(parent).right) {
366 mutationNode(mutationNode(z).parent).color = Color::black;
367 mutationNode(mutationNode(mutationNode(z).parent).parent).color = Color::red;
368 rotateRight(mutationNode(mutationNode(z).parent).parent);
371 NodeId uncle = node(grandparent).left;
372 if (colorOf(*
this, uncle) == Color::red) {
373 mutationNode(parent).color = Color::black;
374 mutationNode(uncle).color = Color::black;
375 mutationNode(grandparent).color = Color::red;
378 if (z == node(parent).left) {
382 mutationNode(mutationNode(z).parent).color = Color::black;
383 mutationNode(mutationNode(mutationNode(z).parent).parent).color = Color::red;
384 rotateLeft(mutationNode(mutationNode(z).parent).parent);
388 mutationNode(root_).color = Color::black;
392 const NodeId z = allocateNode(low, high, slot);
394 NodeId parent = invalidNode;
395 NodeId current = root_;
396 while (current != invalidNode) {
398 if (keyLess(low, high, z, current)) {
399 current = node(current).left;
401 current = node(current).right;
404 mutationNode(z).parent = parent;
405 if (parent == invalidNode) {
407 }
else if (keyLess(low, high, z, parent)) {
408 node(parent).left = z;
410 node(parent).right = z;
419 void rebuildFromElements() {
421 mutationNodes_.clear();
422 nodes_.reserve(elements_.size());
423 mutationNodes_.reserve(elements_.size());
425 for (std::size_t i = 0; i < elements_.size(); ++i) {
426 const auto [low, high] = project(elements_[i]);
427 insertExisting(low, high,
static_cast<NodeId
>(i));
431 [[nodiscard]] NodeId minimumNode(NodeId
id)
const {
432 while (node(
id).left != invalidNode) {
438 [[nodiscard]] NodeId successor(NodeId
id)
const {
439 if (node(
id).right != invalidNode) {
440 return minimumNode(node(
id).right);
442 NodeId parent = mutationNode(
id).parent;
443 while (parent != invalidNode &&
id == node(parent).right) {
445 parent = mutationNode(parent).parent;
450 [[nodiscard]] NodeId lowerBoundInterval(
const NumberType& low,
453 NodeId result = invalidNode;
454 while (
id != invalidNode) {
455 const QueryNode& n = node(
id);
456 if (intervalLess(n.low, n.high, low, high)) {
468 for (NodeId
id = lowerBoundInterval(low, high);
id != invalidNode;
id = successor(
id)) {
469 const QueryNode& n = node(
id);
470 if (!equivalent(n.low, low) || !equivalent(n.high, high)) {
473 if (live(
id) && shapeOf(
id) == shape) {
480 template <
class Low,
class High>
481 [[nodiscard]]
static bool intersects(
const QueryNode& n,
const Low& low,
const High& high) {
482 return !(n.high < low) && !(high < n.low);
485 template <
class Low,
class High>
486 [[nodiscard]]
static bool mayIntersect(
const QueryNode& n,
const Low& low,
const High& high) {
487 return !(n.maxHigh < low) && !(high < n.minLow);
490 template <
class Low,
class High>
491 [[nodiscard]]
static bool allIntersect(
const QueryNode& n,
const Low& low,
const High& high) {
492 return !(high < n.maxLow) && !(n.minHigh < low);
495 template <
class Low,
class High>
496 [[nodiscard]]
static bool containedIn(
const QueryNode& n,
const Low& low,
const High& high) {
497 return !(n.low < low) && !(high < n.low) && !(high < n.high);
500 template <
class Low,
class High>
501 [[nodiscard]]
static bool mayContain(
const QueryNode& n,
const Low& low,
const High& high) {
502 return !(n.maxLow < low) && !(high < n.minLow) && !(high < n.minHigh);
505 template <
class Low,
class High>
506 [[nodiscard]]
static bool allContainedIn(
const QueryNode& n,
const Low& low,
const High& high) {
507 return !(n.minLow < low) && !(high < n.maxLow) && !(high < n.maxHigh);
511 [[nodiscard]]
bool visitAll(NodeId
id, Fn& fn)
const {
512 if (
id == invalidNode) {
515 const QueryNode& n = node(
id);
519 if (live(
id) && detail::invokeIntervalTreeVisitor(fn, shapeOf(
id))) {
522 return visitAll(n.left, fn) || visitAll(n.right, fn);
525 template <
class Low,
class High,
class Fn>
526 [[nodiscard]]
bool visitIntersecting(NodeId
id,
const Low& low,
const High& high,
528 if (
id == invalidNode) {
531 const QueryNode& n = node(
id);
532 if (n.count == 0 || !mayIntersect(n, low, high)) {
535 if (allIntersect(n, low, high)) {
536 return visitAll(
id, fn);
538 if (live(
id) && intersects(n, low, high) &&
539 detail::invokeIntervalTreeVisitor(fn, shapeOf(
id))) {
542 return visitIntersecting(n.left, low, high, fn) ||
543 visitIntersecting(n.right, low, high, fn);
546 template <
class Low,
class High,
class Fn>
547 [[nodiscard]]
bool visitContainedIn(NodeId
id,
const Low& low,
const High& high,
549 if (
id == invalidNode) {
552 const QueryNode& n = node(
id);
553 if (n.count == 0 || !mayContain(n, low, high)) {
556 if (allContainedIn(n, low, high)) {
557 return visitAll(
id, fn);
559 if (live(
id) && containedIn(n, low, high) &&
560 detail::invokeIntervalTreeVisitor(fn, shapeOf(
id))) {
563 return visitContainedIn(n.left, low, high, fn) ||
564 visitContainedIn(n.right, low, high, fn);
571 template <
class Low,
class High,
class Q,
class Fn>
572 [[nodiscard]]
bool visitShapeIntersecting(NodeId
id,
const Low& low,
const High& high,
573 const Q& q, Fn& fn)
const {
574 if (
id == invalidNode) {
577 const QueryNode& n = node(
id);
578 if (n.count == 0 || !mayIntersect(n, low, high)) {
583 if (shape.intersects(q) && detail::invokeIntervalTreeVisitor(fn, shape)) {
587 return visitShapeIntersecting(n.left, low, high, q, fn) ||
588 visitShapeIntersecting(n.right, low, high, q, fn);
591 template <
class Low,
class High,
class Q,
class Fn>
592 [[nodiscard]]
bool visitShapeContainedIn(NodeId
id,
const Low& low,
const High& high,
593 const Q& q, Fn& fn)
const {
594 if (
id == invalidNode) {
597 const QueryNode& n = node(
id);
598 if (n.count == 0 || !mayContain(n, low, high)) {
603 if (q.contains(shape) && detail::invokeIntervalTreeVisitor(fn, shape)) {
607 return visitShapeContainedIn(n.left, low, high, q, fn) ||
608 visitShapeContainedIn(n.right, low, high, q, fn);
611 template <
class Low,
class High>
612 [[nodiscard]] std::size_t countIntersecting(NodeId
id,
const Low& low,
613 const High& high)
const {
614 if (
id == invalidNode) {
617 const QueryNode& n = node(
id);
618 if (n.count == 0 || !mayIntersect(n, low, high)) {
621 if (allIntersect(n, low, high)) {
624 return (live(
id) && intersects(n, low, high) ? 1 : 0) +
625 countIntersecting(n.left, low, high) + countIntersecting(n.right, low, high);
628 template <
class Low,
class High>
629 [[nodiscard]] std::size_t countContainedIn(NodeId
id,
const Low& low,
630 const High& high)
const {
631 if (
id == invalidNode) {
634 const QueryNode& n = node(
id);
635 if (n.count == 0 || !mayContain(n, low, high)) {
638 if (allContainedIn(n, low, high)) {
641 return (live(
id) && containedIn(n, low, high) ? 1 : 0) +
642 countContainedIn(n.left, low, high) + countContainedIn(n.right, low, high);
649 template <
class Container>
651 if constexpr (
requires {
shapes.size(); }) {
652 const std::size_t count =
static_cast<std::size_t
>(
shapes.size());
653 if (count >
static_cast<std::size_t
>(invalidNode)) {
654 throw std::length_error(
"IntervalTree exceeds its 32-bit node capacity");
656 elements_.reserve(count);
657 nodes_.reserve(count);
658 mutationNodes_.reserve(count);
660 for (
const auto& shape :
shapes) {
666 [[nodiscard]] std::size_t
size()
const {
667 return elements_.size();
672 return elements_.empty();
676 [[nodiscard]]
const std::vector<ShapeType>&
shapes()
const {
691 const auto [low, high] = project(shape);
692 elements_.push_back(shape);
694 insertExisting(low, high,
static_cast<NodeId
>(elements_.size() - 1));
696 elements_.pop_back();
722 if (root_ == invalidNode) {
725 const auto [low, high] = project(shape);
726 const NodeId
id = findEqualNode(shape, low, high);
727 if (
id == invalidNode) {
735 const NodeId last =
static_cast<NodeId
>(elements_.size() - 1);
737 const NodeId temporary = relocateNodeToEnd(
id);
738 relocateNode(last,
id);
739 relocateNode(temporary, last);
741 mutationNodes_.pop_back();
742 elements_[
static_cast<std::size_t
>(id)] =
743 std::move(elements_[
static_cast<std::size_t
>(last)]);
745 elements_.pop_back();
751 if (tombstones() > elements_.size()) {
752 rebuildFromElements();
759 if (root_ == invalidNode) {
762 const auto [low, high] = project(shape);
763 return findEqualNode(shape, low, high) != invalidNode;
769 if (root_ == invalidNode) {
772 const auto [low, high] = project(q);
773 return countIntersecting(root_, low, high);
779 std::vector<ShapeType> out;
780 if (root_ != invalidNode) {
781 const auto [low, high] = project(q);
782 auto append = [&out](
const ShapeType& shape) { out.push_back(shape); };
783 (void)visitIntersecting(root_, low, high, append);
789 template <
class Q,
class Fn>
791 if (root_ == invalidNode) {
794 const auto [low, high] = project(q);
795 return visitIntersecting(root_, low, high, fn);
807 if (root_ == invalidNode) {
810 const auto [low, high] = project(q);
811 return countContainedIn(root_, low, high);
817 std::vector<ShapeType> out;
818 if (root_ != invalidNode) {
819 const auto [low, high] = project(q);
820 auto append = [&out](
const ShapeType& shape) { out.push_back(shape); };
821 (void)visitContainedIn(root_, low, high, append);
827 template <
class Q,
class Fn>
829 if (root_ == invalidNode) {
832 const auto [low, high] = project(q);
833 return visitContainedIn(root_, low, high, fn);
850 std::size_t count = 0;
851 (void)visitIntersecting(q, [&](
const ShapeType&) { ++count; });
858 std::vector<ShapeType> out;
859 (void)visitIntersecting(q, [&](
const ShapeType& shape) { out.push_back(shape); });
869 template <
class Q,
class Fn>
871 if (root_ == invalidNode) {
874 const auto [low, high] = project(q);
875 return visitShapeIntersecting(root_, low, high, q, fn);
881 return !visitIntersecting(q, [](
const ShapeType&) {
return true; });
892 std::size_t count = 0;
893 (void)visitContainedIn(q, [&](
const ShapeType&) { ++count; });
900 std::vector<ShapeType> out;
901 (void)visitContainedIn(q, [&](
const ShapeType& shape) { out.push_back(shape); });
906 template <
class Q,
class Fn>
908 if (root_ == invalidNode) {
911 const auto [low, high] = project(q);
912 return visitShapeContainedIn(root_, low, high, q, fn);
918 return !visitContainedIn(q, [](
const ShapeType&) {
return true; });