Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_r_tree.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 https://github.com/lrleon/Aleph-w
6
7 This file is part of Aleph-w library
8
9 Copyright (c) 2002-2026 Leandro Rabindranath Leon
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28*/
29
74#ifndef TPL_R_TREE_H
75#define TPL_R_TREE_H
76
77#include <algorithm>
78#include <array>
79#include <concepts>
80#include <cstddef>
81#include <limits>
82#include <memory>
83#include <type_traits>
84#include <utility>
85
86#include <ah-errors.H>
87#include <point.H>
88#include <tpl_array.H>
89
90namespace Aleph {
91
98enum class RTreeVariant
99{
100 Guttman,
101 RStar
102};
103
116template <typename Payload, size_t MaxEntries = 16, size_t MinEntries = MaxEntries / 2, RTreeVariant Variant = RTreeVariant::Guttman>
117class RTree
118{
119 static_assert(MaxEntries >= 2, "RTree requires MaxEntries >= 2");
120 static_assert(MinEntries >= 1, "RTree requires MinEntries >= 1");
121 static_assert(2 * MinEntries <= MaxEntries + 1, "RTree requires 2 * MinEntries <= MaxEntries + 1");
122 static_assert(std::default_initializable<Payload>,
123 "RTree requires default-initializable Payload");
124 static_assert(std::movable<Payload>, "RTree requires movable Payload");
125
126public:
128 struct Entry
129 {
132 };
133
148
154 {
156 size_t root = std::numeric_limits<size_t>::max();
157 };
158
159private:
160 struct Node;
161
163 struct Child
164 {
166 std::unique_ptr<Node> child;
167 };
168
176
177 std::unique_ptr<Node> root_;
178 size_t size_ = 0;
179 size_t height_ = 0;
180
181 // R*-only transient state (empty/false while no insertion is in progress).
184
191
192 // ---- geometry helpers -------------------------------------------------
193
194 [[nodiscard]] static Rectangle union_bbox(const Rectangle &a, const Rectangle &b)
195 {
196 const Geom_Number xmin = a.get_xmin() < b.get_xmin() ? a.get_xmin() : b.get_xmin();
197 const Geom_Number ymin = a.get_ymin() < b.get_ymin() ? a.get_ymin() : b.get_ymin();
198 const Geom_Number xmax = a.get_xmax() > b.get_xmax() ? a.get_xmax() : b.get_xmax();
199 const Geom_Number ymax = a.get_ymax() > b.get_ymax() ? a.get_ymax() : b.get_ymax();
200 return {xmin, ymin, xmax, ymax};
201 }
202
205 {
206 return union_bbox(base, added).area() - base.area();
207 }
208
210 [[nodiscard]] static Geom_Number overlap_area(const Rectangle &a, const Rectangle &b)
211 {
212 const Geom_Number xlo = a.get_xmin() > b.get_xmin() ? a.get_xmin() : b.get_xmin();
213 const Geom_Number xhi = a.get_xmax() < b.get_xmax() ? a.get_xmax() : b.get_xmax();
214 if (xhi <= xlo)
215 return Geom_Number(0);
216 const Geom_Number ylo = a.get_ymin() > b.get_ymin() ? a.get_ymin() : b.get_ymin();
217 const Geom_Number yhi = a.get_ymax() < b.get_ymax() ? a.get_ymax() : b.get_ymax();
218 if (yhi <= ylo)
219 return Geom_Number(0);
220 return (xhi - xlo) * (yhi - ylo);
221 }
222
223 [[nodiscard]] static size_t entry_count(const Node &node)
224 {
225 return node.leaf ? node.data.size() : node.children.size();
226 }
227
229 [[nodiscard]] static Rectangle compute_mbr(const Node &node)
230 {
231 if (node.leaf)
232 {
233 Rectangle acc = node.data(0).bbox;
234 for (size_t i = 1; i < node.data.size(); ++i)
235 acc = union_bbox(acc, node.data(i).bbox);
236 return acc;
237 }
238 Rectangle acc = node.children(0).bbox;
239 for (size_t i = 1; i < node.children.size(); ++i)
240 acc = union_bbox(acc, node.children(i).bbox);
241 return acc;
242 }
243
246 [[nodiscard]] static size_t choose_subtree(const Node &node, const Rectangle &bbox)
247 {
248 if constexpr (Variant == RTreeVariant::RStar)
249 if (node.children(0).child->leaf)
250 return rstar_choose_overlap(node, bbox);
251
252 size_t best = 0;
253 Geom_Number best_enlarge = enlargement(node.children(0).bbox, bbox);
254 Geom_Number best_area = node.children(0).bbox.area();
255 for (size_t i = 1; i < node.children.size(); ++i)
256 {
257 const Geom_Number e = enlargement(node.children(i).bbox, bbox);
258 const Geom_Number a = node.children(i).bbox.area();
259 if (e < best_enlarge or (e == best_enlarge and a < best_area))
260 {
261 best = i;
262 best_enlarge = e;
263 best_area = a;
264 }
265 }
266 return best;
267 }
268
272 [[nodiscard]] static size_t rstar_choose_overlap(const Node &node, const Rectangle &bbox)
273 {
274 const size_t k = node.children.size();
275 size_t best = 0;
276 bool first = true;
280 for (size_t i = 0; i < k; ++i)
281 {
282 const Rectangle grown = union_bbox(node.children(i).bbox, bbox);
283 Geom_Number ov = 0;
284 for (size_t j = 0; j < k; ++j)
285 if (j != i)
286 ov += overlap_area(grown, node.children(j).bbox) -
287 overlap_area(node.children(i).bbox, node.children(j).bbox);
288 const Geom_Number enl = grown.area() - node.children(i).bbox.area();
289 const Geom_Number area = node.children(i).bbox.area();
290 if (first or ov < best_ov or
291 (ov == best_ov and (enl < best_enl or (enl == best_enl and area < best_area))))
292 {
293 best = i;
294 best_ov = ov;
295 best_enl = enl;
296 best_area = area;
297 first = false;
298 }
299 }
300 return best;
301 }
302
303 // ---- quadratic split (Guttman) ----------------------------------------
304
308 template <typename EntryT>
310 {
311 const size_t n = entries.size();
314
315 Array<EntryT> items = std::move(entries);
316 entries = Array<EntryT>();
317
318 // PickSeeds: the pair wasting the most area if grouped together.
319 size_t seed1 = 0;
320 size_t seed2 = 1;
322 union_bbox(items(0).bbox, items(1).bbox).area() - items(0).bbox.area() - items(1).bbox.area();
323 for (size_t i = 0; i < n; ++i)
324 for (size_t j = i + 1; j < n; ++j)
325 {
326 const Geom_Number d = union_bbox(items(i).bbox, items(j).bbox).area() -
327 items(i).bbox.area() - items(j).bbox.area();
328 if (d > worst)
329 {
330 worst = d;
331 seed1 = i;
332 seed2 = j;
333 }
334 }
335
336 std::array<bool, MaxEntries + 1> assigned{};
337 Rectangle mbr1 = items(seed1).bbox;
338 Rectangle mbr2 = items(seed2).bbox;
339 assigned[seed1] = true;
340 assigned[seed2] = true;
341 group1.append(std::move(items(seed1)));
342 group2.append(std::move(items(seed2)));
343 size_t remaining = n - 2;
344
345 auto dump_into = [&](Array<EntryT> &group)
346 {
347 for (size_t k = 0; k < n; ++k)
348 if (not assigned[k])
349 {
350 assigned[k] = true;
351 group.append(std::move(items(k)));
352 }
353 remaining = 0;
354 };
355
356 while (remaining > 0)
357 {
358 // Force the rest into a group that would otherwise miss MinEntries.
359 if (group1.size() + remaining == MinEntries)
360 {
362 break;
363 }
364 if (group2.size() + remaining == MinEntries)
365 {
367 break;
368 }
369
370 // PickNext: entry with the strongest preference for one group.
371 size_t pick = n;
375 for (size_t k = 0; k < n; ++k)
376 {
377 if (assigned[k])
378 continue;
379 const Geom_Number d1 = enlargement(mbr1, items(k).bbox);
380 const Geom_Number d2 = enlargement(mbr2, items(k).bbox);
381 const Geom_Number diff = d1 > d2 ? d1 - d2 : d2 - d1;
382 if (pick == n or diff > best_diff)
383 {
384 best_diff = diff;
385 pick = k;
386 pick_d1 = d1;
387 pick_d2 = d2;
388 }
389 }
390
391 // Assign to the group needing less enlargement (ties: smaller area,
392 // then fewer entries).
393 bool to_group1;
394 if (pick_d1 < pick_d2)
395 to_group1 = true;
396 else if (pick_d2 < pick_d1)
397 to_group1 = false;
398 else if (mbr1.area() != mbr2.area())
399 to_group1 = mbr1.area() < mbr2.area();
400 else
401 to_group1 = group1.size() <= group2.size();
402
403 assigned[pick] = true;
404 if (to_group1)
405 {
406 mbr1 = union_bbox(mbr1, items(pick).bbox);
407 group1.append(std::move(items(pick)));
408 }
409 else
410 {
411 mbr2 = union_bbox(mbr2, items(pick).bbox);
412 group2.append(std::move(items(pick)));
413 }
414 --remaining;
415 }
416
417 entries = std::move(group1);
418 return group2;
419 }
420
425 template <typename EntryT>
427 {
428 const size_t n = entries.size();
431
432 Array<EntryT> items = std::move(entries);
433 entries = Array<EntryT>();
434
435 // prefix/suffix are indexed up to and including n (prefix[n], suffix
436 // accessed down to suffix[0]), and n can reach MaxEntries + 1, so both
437 // need MaxEntries + 2 slots -- one more than the MaxEntries + 1 used for
438 // per-entry arrays like `order`.
439 auto make_mbr_cache = [&items, n](const std::array<size_t, MaxEntries + 1> &order)
440 {
441 std::array<Rectangle, MaxEntries + 2> prefix{};
442 std::array<Rectangle, MaxEntries + 2> suffix{};
443 prefix[1] = items(order[0]).bbox;
444 for (size_t k = 2; k <= n; ++k)
445 prefix[k] = union_bbox(prefix[k - 1], items(order[k - 1]).bbox);
446 suffix[n - 1] = items(order[n - 1]).bbox;
447 for (size_t k = n - 1; k > 0; --k)
448 suffix[k - 1] = union_bbox(items(order[k - 1]).bbox, suffix[k]);
449 return std::pair{prefix, suffix};
450 };
451
452 auto make_order = [&items, n](const int axis, const int edge)
453 {
454 std::array<size_t, MaxEntries + 1> order{};
455 for (size_t i = 0; i < n; ++i)
456 order[i] = i;
457 std::sort(order.begin(), order.begin() + n,
458 [&items, axis, edge](const size_t a, const size_t b)
459 {
460 const Rectangle &ra = items(a).bbox;
461 const Rectangle &rb = items(b).bbox;
462 const Geom_Number la = axis == 0 ? ra.get_xmin() : ra.get_ymin();
463 const Geom_Number ua = axis == 0 ? ra.get_xmax() : ra.get_ymax();
464 const Geom_Number lb = axis == 0 ? rb.get_xmin() : rb.get_ymin();
465 const Geom_Number ub = axis == 0 ? rb.get_xmax() : rb.get_ymax();
466 const Geom_Number pa = edge == 0 ? la : ua;
467 const Geom_Number pb = edge == 0 ? lb : ub;
468 if (pa != pb)
469 return pa < pb;
470 return (edge == 0 ? ua : la) < (edge == 0 ? ub : lb);
471 });
472 return order;
473 };
474
475 // ChooseSplitAxis: minimize the summed margin over both edge sortings and
476 // all valid distributions.
477 int chosen_axis = 0;
479 for (int axis = 0; axis < 2; ++axis)
480 {
482 for (int edge = 0; edge < 2; ++edge)
483 {
484 const std::array<size_t, MaxEntries + 1> order = make_order(axis, edge);
485 const auto [prefix, suffix] = make_mbr_cache(order);
486 for (size_t sz = MinEntries; sz <= n - MinEntries; ++sz)
487 margin_sum += prefix[sz].perimeter() + suffix[sz].perimeter();
488 }
490 {
493 }
494 }
495
496 // ChooseSplitIndex: on the chosen axis, minimize overlap area, then area.
497 std::array<size_t, MaxEntries + 1> best_order{};
498 size_t best_sz = MinEntries;
499 bool first = true;
502 for (int edge = 0; edge < 2; ++edge)
503 {
504 const std::array<size_t, MaxEntries + 1> order = make_order(chosen_axis, edge);
505 const auto [prefix, suffix] = make_mbr_cache(order);
506 for (size_t sz = MinEntries; sz <= n - MinEntries; ++sz)
507 {
508 const Rectangle &g1 = prefix[sz];
509 const Rectangle &g2 = suffix[sz];
510 const Geom_Number ov = overlap_area(g1, g2);
511 const Geom_Number ar = g1.area() + g2.area();
512 if (first or ov < best_overlap or (ov == best_overlap and ar < best_area))
513 {
514 first = false;
516 best_area = ar;
517 best_order = order;
518 best_sz = sz;
519 }
520 }
521 }
522
523 for (size_t k = 0; k < best_sz; ++k)
524 group1.append(std::move(items(best_order[k])));
525 for (size_t k = best_sz; k < n; ++k)
526 group2.append(std::move(items(best_order[k])));
527 entries = std::move(group1);
528 return group2;
529 }
530
531 // ---- insertion --------------------------------------------------------
532
534 template <typename EntryT>
536 {
537 if constexpr (Variant == RTreeVariant::RStar)
538 return rstar_split(entries);
539 else
540 return quadratic_split(entries);
541 }
542
547 {
548 const size_t n = node.data.size();
549 const Point centre = compute_mbr(node).center();
550
551 std::array<size_t, MaxEntries + 1> order{};
552 for (size_t i = 0; i < n; ++i)
553 order[i] = i;
554 std::sort(order.begin(), order.begin() + n, [&node, &centre](const size_t a, const size_t b)
555 {
556 return node.data(a).bbox.center().distance_squared_to(centre) >
557 node.data(b).bbox.center().distance_squared_to(centre);
558 });
559
560 size_t p = (3 * MaxEntries) / 10;
561 if (p < 1)
562 p = 1;
563 if (p > MaxEntries + 1 - MinEntries)
564 p = MaxEntries + 1 - MinEntries;
565
566 Array<Entry> kept(n - p);
568 for (size_t k = p; k < n; ++k)
569 kept.append(std::move(node.data(order[k])));
570 for (size_t k = 0; k < p; ++k)
571 rstar_reinsert_buffer_.append(std::move(node.data(order[k])));
572 node.data = std::move(kept);
573 }
574
577 [[nodiscard]] std::unique_ptr<Node> insert_descend(Node &node, Entry entry)
578 {
579 if (node.leaf)
580 {
581 node.data.reserve(MaxEntries + 1);
582 node.data.append(std::move(entry));
583 if (node.data.size() <= MaxEntries)
584 return nullptr;
585
586 if constexpr (Variant == RTreeVariant::RStar)
587 if (rstar_reinsert_available_ and &node != root_.get())
588 {
590 reinsert_farthest(node);
591 return nullptr;
592 }
593
595 auto sibling = std::make_unique<Node>();
596 sibling->leaf = true;
597 sibling->data = std::move(group2);
598 return sibling;
599 }
600
601 const size_t idx = choose_subtree(node, entry.bbox);
602 std::unique_ptr<Node> split = insert_descend(*node.children(idx).child, std::move(entry));
603 node.children(idx).bbox = compute_mbr(*node.children(idx).child);
604 if (split == nullptr)
605 return nullptr;
606
608 node.children.reserve(MaxEntries + 1);
609 node.children.append(Child{std::move(split_mbr), std::move(split)});
610 if (node.children.size() <= MaxEntries)
611 return nullptr;
612
614 auto sibling = std::make_unique<Node>();
615 sibling->leaf = false;
616 sibling->children = std::move(group2);
617 return sibling;
618 }
619
622 void insert_one(Entry entry)
623 {
624 if (root_ == nullptr)
625 {
626 auto root = std::make_unique<Node>();
627 root->leaf = true;
628 root->data.reserve(1);
629 root->data.append(std::move(entry));
630 root_ = std::move(root);
631 height_ = 1;
632 return;
633 }
634
635 std::unique_ptr<Node> split = insert_descend(*root_, std::move(entry));
636 if (split == nullptr)
637 return;
638
639 auto new_root = std::make_unique<Node>();
640 new_root->leaf = false;
641 new_root->children.reserve(2);
642 new_root->children.append(Child{compute_mbr(*root_), std::move(root_)});
643 new_root->children.append(Child{compute_mbr(*split), std::move(split)});
644 root_ = std::move(new_root);
645 ++height_;
646 }
647
651 void add_entry(Entry entry)
652 {
653 if constexpr (Variant == RTreeVariant::RStar)
654 {
656 insert_one(std::move(entry));
657 while (not rstar_reinsert_buffer_.is_empty())
658 insert_one(rstar_reinsert_buffer_.remove_last());
660 }
661 else
662 insert_one(std::move(entry));
663 }
664
665 // ---- deletion ---------------------------------------------------------
666
669 {
670 if (node.leaf)
671 {
672 for (size_t i = 0; i < node.data.size(); ++i)
673 out.append(std::move(node.data(i)));
674 return;
675 }
676 for (size_t i = 0; i < node.children.size(); ++i)
677 collect_data_entries(*node.children(i).child, out);
678 }
679
680 [[nodiscard]] static size_t data_entry_count(const Node &node)
681 {
682 if (node.leaf)
683 return node.data.size();
684 size_t total = 0;
685 for (size_t i = 0; i < node.children.size(); ++i)
686 {
687 const size_t subtree_count = data_entry_count(*node.children(i).child);
688 ah_overflow_error_if(total > std::numeric_limits<size_t>::max() - subtree_count)
689 << "RTree: data entry count would overflow";
691 }
692 return total;
693 }
694
698 void erase_descend(Node &node, const Rectangle &bbox, const Payload &value, Array<Entry> &orphans,
699 bool &removed)
700 requires std::equality_comparable<Payload>
701 {
702 if (node.leaf)
703 {
704 for (size_t i = 0; i < node.data.size(); ++i)
705 if (node.data(i).bbox == bbox and node.data(i).value == value)
706 {
707 Array<Entry> kept(node.data.size() - 1);
708 for (size_t j = 0; j < node.data.size(); ++j)
709 if (j != i)
710 kept.append(std::move(node.data(j)));
711 node.data = std::move(kept);
712 removed = true;
713 return;
714 }
715 return;
716 }
717
718 for (size_t i = 0; i < node.children.size(); ++i)
719 {
720 Child &c = node.children(i);
721 if (not box_contains_box(c.bbox, bbox))
722 continue;
723
725 if (not removed)
726 continue;
727
728 if (entry_count(*c.child) < MinEntries)
729 {
730 const size_t orphan_count = data_entry_count(*c.child);
732 std::numeric_limits<size_t>::max() - orphan_count)
733 << "RTree: orphan collection would overflow";
734 orphans.reserve(orphans.size() + orphan_count);
735 Array<Child> kept(node.children.size() - 1);
737 for (size_t j = 0; j < node.children.size(); ++j)
738 if (j != i)
739 kept.append(std::move(node.children(j)));
740 node.children = std::move(kept);
741 }
742 else
743 c.bbox = compute_mbr(*c.child);
744 return;
745 }
746 }
747
749 [[nodiscard]] static bool box_contains_box(const Rectangle &outer, const Rectangle &inner)
750 {
751 return outer.get_xmin() <= inner.get_xmin() and outer.get_xmax() >= inner.get_xmax() and
752 outer.get_ymin() <= inner.get_ymin() and outer.get_ymax() >= inner.get_ymax();
753 }
754
755 // ---- queries ----------------------------------------------------------
756
757 template <typename F>
758 void for_each_intersecting_rec(const Node &node, const Rectangle &rect, F &f) const
759 {
760 if (node.leaf)
761 {
762 for (size_t i = 0; i < node.data.size(); ++i)
763 if (node.data(i).bbox.intersects(rect))
764 f(node.data(i).bbox, node.data(i).value);
765 return;
766 }
767 for (size_t i = 0; i < node.children.size(); ++i)
768 if (node.children(i).bbox.intersects(rect))
769 for_each_intersecting_rec(*node.children(i).child, rect, f);
770 }
771
772 template <typename F>
773 void for_each_containing_rec(const Node &node, const Point &p, F &f) const
774 {
775 if (node.leaf)
776 {
777 for (size_t i = 0; i < node.data.size(); ++i)
778 if (node.data(i).bbox.contains(p))
779 f(node.data(i).bbox, node.data(i).value);
780 return;
781 }
782 for (size_t i = 0; i < node.children.size(); ++i)
783 if (node.children(i).bbox.contains(p))
784 for_each_containing_rec(*node.children(i).child, p, f);
785 }
786
787 // ---- copy / verify ----------------------------------------------------
788
789 [[nodiscard]] static std::unique_ptr<Node> clone_node(const Node &node)
790 requires (std::is_copy_constructible_v<Payload> and std::movable<Payload>)
791 {
792 auto copy = std::make_unique<Node>();
793 copy->leaf = node.leaf;
794 if (node.leaf)
795 {
796 copy->data.reserve(node.data.size());
797 for (size_t i = 0; i < node.data.size(); ++i)
798 copy->data.append(Entry{node.data(i).bbox, node.data(i).value});
799 }
800 else
801 {
802 copy->children.reserve(node.children.size());
803 for (size_t i = 0; i < node.children.size(); ++i)
804 copy->children.append(Child{node.children(i).bbox, clone_node(*node.children(i).child)});
805 }
806 return copy;
807 }
808
809 [[nodiscard]] bool verify_rec(const Node &node, const size_t depth, const bool is_root,
810 size_t &leaf_depth, size_t &count, Rectangle &out_mbr) const
811 {
812 const size_t n = entry_count(node);
813 if (n > MaxEntries)
814 return false;
815 if (not is_root and n < MinEntries)
816 return false;
817 if (n == 0)
818 return false; // an empty node must never be retained
819
820 if (node.leaf)
821 {
822 if (leaf_depth == std::numeric_limits<size_t>::max())
823 leaf_depth = depth;
824 else if (leaf_depth != depth)
825 return false;
826 out_mbr = compute_mbr(node);
827 if (count > std::numeric_limits<size_t>::max() - n)
828 return false;
829 count += n;
830 return true;
831 }
832
833 if (is_root and n < 2)
834 return false; // a non-leaf root must have at least two children
835
837 for (size_t i = 0; i < node.children.size(); ++i)
838 {
840 if (not verify_rec(*node.children(i).child, depth + 1, false, leaf_depth, count, child_mbr))
841 return false;
842 if (node.children(i).bbox != child_mbr)
843 return false; // stored MBR must be tight
844 acc = i == 0 ? child_mbr : union_bbox(acc, child_mbr);
845 }
846 out_mbr = acc;
847 return true;
848 }
849
850public:
855
865 {
866 other.size_ = 0;
867 other.height_ = 0;
868 other.clear_rstar_reinsert_state();
869 }
870
880 {
881 if (this == &other)
882 return *this;
883 root_ = std::move(other.root_);
884 size_ = other.size_;
885 height_ = other.height_;
887 other.size_ = 0;
888 other.height_ = 0;
889 other.clear_rstar_reinsert_state();
890 return *this;
891 }
892
898 requires (std::is_copy_constructible_v<Payload> and std::movable<Payload>)
900 {
901 if (other.root_ != nullptr)
902 root_ = clone_node(*other.root_);
903 }
904
911 requires (std::is_copy_constructible_v<Payload> and std::movable<Payload>)
912 {
913 if (this == &other)
914 return *this;
915 std::unique_ptr<Node> new_root;
916 if (other.root_ != nullptr)
917 new_root = clone_node(*other.root_);
918 root_ = std::move(new_root);
919 size_ = other.size_;
920 height_ = other.height_;
921 return *this;
922 }
923
929 {
930 return size_ == 0;
931 }
932
938 {
939 return size_;
940 }
941
947 {
948 return height_;
949 }
950
955 {
956 root_.reset();
957 size_ = 0;
958 height_ = 0;
960 }
961
969 void insert(const Rectangle &bbox, const Payload &value)
970 {
971 ah_overflow_error_if(size_ == std::numeric_limits<size_t>::max())
972 << "RTree: size would overflow";
973 add_entry(Entry{bbox, value});
974 ++size_;
975 }
976
984 void insert(const Rectangle &bbox, Payload &&value)
985 {
986 ah_overflow_error_if(size_ == std::numeric_limits<size_t>::max())
987 << "RTree: size would overflow";
988 add_entry(Entry{bbox, std::move(value)});
989 ++size_;
990 }
991
1000 [[nodiscard]] bool erase(const Rectangle &bbox, const Payload &value)
1001 requires std::equality_comparable<Payload>
1002 {
1003 if (root_ == nullptr)
1004 return false;
1005
1007 bool removed = false;
1009 if (not removed)
1010 return false;
1011
1012 --size_;
1013
1014 // Reinsert the entries orphaned by condensing (they remain in the tree, so
1015 // add_entry must not change size_).
1016 for (size_t i = 0; i < orphans.size(); ++i)
1017 add_entry(std::move(orphans(i)));
1018
1019 // Shrink a non-leaf root that ended up with a single child.
1020 while (root_ != nullptr and not root_->leaf and root_->children.size() == 1)
1021 {
1022 root_ = std::move(root_->children(0).child);
1023 --height_;
1024 }
1025 // Drop an emptied leaf root.
1026 if (root_ != nullptr and root_->leaf and root_->data.is_empty())
1027 {
1028 root_.reset();
1029 height_ = 0;
1030 }
1031 return true;
1032 }
1033
1041 template <typename F>
1042 void for_each_intersecting(const Rectangle &rect, F &&f) const
1043 {
1044 if (root_ != nullptr)
1046 }
1047
1054 requires (std::is_copy_constructible_v<Payload> and std::movable<Payload>)
1055 {
1057 for_each_intersecting(rect, [&out](const Rectangle &, const Payload &v)
1058 {
1059 out.append(Payload(v));
1060 });
1061 return out;
1062 }
1063
1070 requires (std::is_copy_constructible_v<Payload> and std::movable<Payload>)
1071 {
1073 auto collect = [&out](const Rectangle &, const Payload &v)
1074 {
1075 out.append(Payload(v));
1076 };
1077 if (root_ != nullptr)
1079 return out;
1080 }
1081
1091 [[nodiscard]] bool verify() const
1092 {
1093 if (root_ == nullptr)
1094 return size_ == 0 and height_ == 0;
1095
1096 size_t leaf_depth = std::numeric_limits<size_t>::max();
1097 size_t count = 0;
1098 Rectangle mbr;
1099 if (not verify_rec(*root_, 1, true, leaf_depth, count, mbr))
1100 return false;
1101 return count == size_ and leaf_depth == height_;
1102 }
1103
1115 {
1117 if (root_ == nullptr)
1118 return snap;
1119
1120 // Each `snap.nodes(out_idx)` access below is by index, re-fetched after
1121 // every recursive call: `Array::append` may reallocate the backing
1122 // storage, so a reference taken before recursing could dangle.
1123 auto dfs = [&](const auto &self, const Node &node, const size_t depth) -> size_t
1124 {
1125 const size_t out_idx = snap.nodes.size();
1126 snap.nodes.append(DebugNode{});
1127 snap.nodes(out_idx).is_leaf = node.leaf;
1128 snap.nodes(out_idx).depth = depth;
1129
1130 if (node.leaf)
1131 {
1132 // Safe to populate in place: this loop does not recurse, so
1133 // `snap.nodes` cannot reallocate between the reference below
1134 // and its last use.
1135 Array<Rectangle> &boxes = snap.nodes(out_idx).entry_boxes;
1136 boxes.reserve(node.data.size());
1137 for (size_t i = 0; i < node.data.size(); ++i)
1138 boxes.append(node.data(i).bbox);
1139 }
1140 else
1141 {
1142 snap.nodes(out_idx).children.reserve(node.children.size());
1143 for (size_t i = 0; i < node.children.size(); ++i)
1144 {
1145 const size_t child_idx = self(self, *node.children(i).child, depth + 1);
1146 snap.nodes(out_idx).children.append(child_idx);
1147 }
1148 }
1149
1150 snap.nodes(out_idx).bbox = compute_mbr(node);
1151 return out_idx;
1152 };
1153
1154 snap.root = dfs(dfs, *root_, 0);
1155 return snap;
1156 }
1157};
1158
1159} // namespace Aleph
1160
1161#endif // TPL_R_TREE_H
Exception handling system with formatted messages for Aleph-w.
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:468
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:221
Dynamic R-tree indexing axis-aligned rectangles by payload.
Definition tpl_r_tree.H:118
size_t height_
Number of node levels (0 when empty).
Definition tpl_r_tree.H:179
static Array< EntryT > quadratic_split(Array< EntryT > &entries)
Split an overflowed entry array in two using the quadratic heuristic.
Definition tpl_r_tree.H:309
static size_t rstar_choose_overlap(const Node &node, const Rectangle &bbox)
R*-tree ChooseSubtree for a node whose children are leaves: pick the child whose growth adds the leas...
Definition tpl_r_tree.H:272
void for_each_intersecting_rec(const Node &node, const Rectangle &rect, F &f) const
Definition tpl_r_tree.H:758
static Array< EntryT > split_entries(Array< EntryT > &entries)
Split the entry array of node according to the active variant.
Definition tpl_r_tree.H:535
static Rectangle union_bbox(const Rectangle &a, const Rectangle &b)
Definition tpl_r_tree.H:194
static Geom_Number enlargement(const Rectangle &base, const Rectangle &added)
Area added to base by growing it to also cover added.
Definition tpl_r_tree.H:204
void reinsert_farthest(Node &node)
Move the R*-tree forced-reinsert candidates (the entries farthest from the node centre) out of the ov...
Definition tpl_r_tree.H:546
void add_entry(Entry entry)
Add a data entry without touching size_ (shared by insert and by erase-time reinsertion).
Definition tpl_r_tree.H:651
static size_t entry_count(const Node &node)
Definition tpl_r_tree.H:223
void clear_rstar_reinsert_state() noexcept
Drop R*-tree transient insertion state.
Definition tpl_r_tree.H:186
static Geom_Number overlap_area(const Rectangle &a, const Rectangle &b)
Area of the overlap between two rectangles (0 if disjoint).
Definition tpl_r_tree.H:210
DebugSnapshot debug_snapshot() const
Capture the full tree structure for visualization/debugging.
void insert_one(Entry entry)
Insert one data entry into the tree (no size_ change, no reinsert-buffer management).
Definition tpl_r_tree.H:622
void insert(const Rectangle &bbox, const Payload &value)
Insert a (bbox, value) entry, copying value.
Definition tpl_r_tree.H:969
RTree(const RTree &other)
Deep-copy other (requires copy-constructible, movable Payload).
Definition tpl_r_tree.H:897
static size_t data_entry_count(const Node &node)
Definition tpl_r_tree.H:680
static bool box_contains_box(const Rectangle &outer, const Rectangle &inner)
True iff outer fully covers inner.
Definition tpl_r_tree.H:749
bool erase(const Rectangle &bbox, const Payload &value)
Remove one entry equal to (bbox, value).
static Rectangle compute_mbr(const Node &node)
Tight bounding box covering every entry of node.
Definition tpl_r_tree.H:229
static size_t choose_subtree(const Node &node, const Rectangle &bbox)
Choose the child of node into which bbox should be inserted.
Definition tpl_r_tree.H:246
void erase_descend(Node &node, const Rectangle &bbox, const Payload &value, Array< Entry > &orphans, bool &removed)
Remove one entry equal to (bbox, value) from node's subtree.
Definition tpl_r_tree.H:698
size_t height() const noexcept
Return the number of node levels (0 when empty, 1 for a lone leaf).
Definition tpl_r_tree.H:946
RTree() noexcept=default
Construct an empty R-tree.
bool rstar_reinsert_available_
Leaf-level reinsert not yet used this insert.
Definition tpl_r_tree.H:182
bool is_empty() const noexcept
Return true when the tree has no entries.
Definition tpl_r_tree.H:928
void insert(const Rectangle &bbox, Payload &&value)
Insert a (bbox, value) entry, moving value.
Definition tpl_r_tree.H:984
Array< Payload > search_intersects(const Rectangle &rect) const
Return the payloads of every entry whose bbox intersects rect.
void for_each_containing_rec(const Node &node, const Point &p, F &f) const
Definition tpl_r_tree.H:773
Array< Payload > search_contains(const Point &p) const
Return the payloads of every entry whose bbox contains p.
size_t size() const noexcept
Return the number of stored entries.
Definition tpl_r_tree.H:937
std::unique_ptr< Node > insert_descend(Node &node, Entry entry)
Insert entry at the leaf level of node.
Definition tpl_r_tree.H:577
RTree & operator=(RTree &&other) noexcept
Move-assign from other, leaving it empty and valid.
Definition tpl_r_tree.H:879
void for_each_intersecting(const Rectangle &rect, F &&f) const
Invoke f for every entry whose bbox intersects rect.
static Array< EntryT > rstar_split(Array< EntryT > &entries)
R*-tree split: choose the split axis minimizing the total margin of the two groups,...
Definition tpl_r_tree.H:426
bool verify_rec(const Node &node, const size_t depth, const bool is_root, size_t &leaf_depth, size_t &count, Rectangle &out_mbr) const
Definition tpl_r_tree.H:809
std::unique_ptr< Node > root_
Definition tpl_r_tree.H:177
static std::unique_ptr< Node > clone_node(const Node &node)
Definition tpl_r_tree.H:789
bool verify() const
Verify the R-tree structural invariants.
void clear() noexcept
Remove all entries.
Definition tpl_r_tree.H:954
size_t size_
Number of stored data entries.
Definition tpl_r_tree.H:178
Array< Entry > rstar_reinsert_buffer_
Entries pending forced reinsertion.
Definition tpl_r_tree.H:183
static void collect_data_entries(Node &node, Array< Entry > &out)
Move every data entry in node's subtree into out.
Definition tpl_r_tree.H:668
An axis-aligned rectangle.
Definition point.H:1789
const Geom_Number & get_xmin() const
Gets the minimum x-coordinate.
Definition point.H:1815
Geom_Number area() const noexcept
Calculates the area of the rectangle.
Definition point.H:1888
const Geom_Number & get_ymax() const
Gets the maximum y-coordinate.
Definition point.H:1830
const Geom_Number & get_ymin() const
Gets the minimum y-coordinate.
Definition point.H:1820
const Geom_Number & get_xmax() const
Gets the maximum x-coordinate.
Definition point.H:1825
Point center() const noexcept
Calculates the center point of the rectangle.
Definition point.H:1906
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4071
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
static void suffix(Node *root, DynList< Node * > &acc)
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
and
Check uniqueness with explicit hash + equality functors.
static void prefix(Node *root, DynList< Node * > &acc)
bool diff(const C1 &c1, const C2 &c2, Eq e=Eq())
Check if two containers differ.
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:113
std::vector< std::string > & split(const std::string &s, const char delim, std::vector< std::string > &elems)
Split a std::string by a single delimiter character.
RTreeVariant
Node-split / insertion strategy for RTree.
Definition tpl_r_tree.H:99
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
2D point and geometric utilities.
Internal-node entry: a child subtree plus its tight bounding box.
Definition tpl_r_tree.H:164
std::unique_ptr< Node > child
Owned child subtree.
Definition tpl_r_tree.H:166
Rectangle bbox
Tight MBR of child.
Definition tpl_r_tree.H:165
One node of a debug_snapshot, independent of Payload.
Definition tpl_r_tree.H:141
size_t depth
Root is 0, increasing towards the leaves.
Definition tpl_r_tree.H:144
bool is_leaf
True for leaf nodes (see entry_boxes).
Definition tpl_r_tree.H:143
Array< Rectangle > entry_boxes
Data-entry boxes stored here (leaves only).
Definition tpl_r_tree.H:146
Rectangle bbox
Tight MBR of this node.
Definition tpl_r_tree.H:142
Array< size_t > children
Indices into DebugSnapshot::nodes (internal nodes).
Definition tpl_r_tree.H:145
Full tree structure captured for visualization/debugging.
Definition tpl_r_tree.H:154
Array< DebugNode > nodes
Every node, in preorder.
Definition tpl_r_tree.H:155
size_t root
Index of the root in nodes.
Definition tpl_r_tree.H:156
A stored (bounding box, payload) pair.
Definition tpl_r_tree.H:129
Rectangle bbox
Axis-aligned bounding box.
Definition tpl_r_tree.H:130
Payload value
User payload associated with bbox.
Definition tpl_r_tree.H:131
A node is a leaf holding data or an internal node holding children.
Definition tpl_r_tree.H:171
Array< Child > children
Child entries (used iff not leaf).
Definition tpl_r_tree.H:174
Array< Entry > data
Data entries (used iff leaf).
Definition tpl_r_tree.H:173
static int * k
Dynamic array container with automatic resizing.