#ifndef CC_BASE_RTREE_H_
#define CC_BASE_RTREE_H_
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <map>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/clamped_math.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
template <typename T>
class RTree {
public:
RTree();
RTree(const RTree&) = delete;
~RTree();
RTree& operator=(const RTree&) = delete;
template <typename Container>
void Build(const Container& items);
template <typename Container, typename BoundsFunctor, typename PayloadFunctor>
void Build(const Container& items,
const BoundsFunctor& bounds_getter,
const PayloadFunctor& payload_getter);
bool has_valid_bounds() const { return has_valid_bounds_; }
void Search(const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
void SearchRefs(const gfx::Rect& query, std::vector<const T*>* results) const;
gfx::Rect GetBoundsOrDie() const;
std::map<T, gfx::Rect> GetAllBoundsForTracing() const;
void Reset();
private:
static constexpr int kMinChildren = 6;
static constexpr int kMaxChildren = 11;
template <typename U>
struct Node;
template <typename U>
struct Branch {
raw_ptr<Node<U>> subtree;
U payload;
gfx::Rect bounds;
Branch() = default;
Branch(U payload, const gfx::Rect& bounds)
: payload(std::move(payload)), bounds(bounds) {}
};
template <typename U>
struct Node {
uint16_t num_children = 0u;
uint16_t level = 0u;
Branch<U> children[kMaxChildren];
explicit Node(uint16_t level) : level(level) {}
};
void SearchRecursive(Node<T>* root,
const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
void SearchRefsRecursive(Node<T>* root,
const gfx::Rect& query,
std::vector<const T*>* results) const;
void SearchRecursiveFallback(Node<T>* root,
const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
void SearchRefsRecursiveFallback(Node<T>* root,
const gfx::Rect& query,
std::vector<const T*>* results) const;
Branch<T> BuildRecursive(std::vector<Branch<T>>* branches, int level);
Node<T>* AllocateNodeAtLevel(int level);
void GetAllBoundsRecursive(Node<T>* root,
std::map<T, gfx::Rect>* results) const;
size_t num_data_elements_ = 0u;
std::vector<Node<T>> nodes_;
Branch<T> root_;
bool has_valid_bounds_ = true;
};
template <typename T>
RTree<T>::RTree() = default;
template <typename T>
RTree<T>::~RTree() = default;
template <typename T>
template <typename Container>
void RTree<T>::Build(const Container& items) {
Build(items,
[](const Container& items, size_t index) { return items[index]; },
[](const Container& items, size_t index) { return index; });
}
template <typename T>
template <typename Container, typename BoundsFunctor, typename PayloadFunctor>
void RTree<T>::Build(const Container& items,
const BoundsFunctor& bounds_getter,
const PayloadFunctor& payload_getter) {
DCHECK_EQ(0u, num_data_elements_);
std::vector<Branch<T>> branches;
branches.reserve(items.size());
for (size_t i = 0; i < items.size(); i++) {
const gfx::Rect& bounds = bounds_getter(items, i);
if (bounds.IsEmpty())
continue;
branches.emplace_back(payload_getter(items, i), bounds);
}
num_data_elements_ = branches.size();
if (num_data_elements_ == 1u) {
nodes_.reserve(1);
Node<T>* node = AllocateNodeAtLevel(0);
root_.subtree = node;
root_.bounds = branches[0].bounds;
node->num_children = 1;
node->children[0] = std::move(branches[0]);
} else if (num_data_elements_ > 1u) {
size_t branch_count = kMaxChildren;
double depth = log(branches.size()) / log(branch_count);
size_t node_count =
static_cast<size_t>((std::pow(branch_count, depth) - 1) /
(branch_count - 1)) +
kMinChildren;
nodes_.reserve(node_count);
root_ = BuildRecursive(&branches, 0);
}
DCHECK_LE(nodes_.capacity() - nodes_.size(),
static_cast<size_t>(kMinChildren));
}
template <typename T>
auto RTree<T>::AllocateNodeAtLevel(int level) -> Node<T>* {
DCHECK_GT(nodes_.capacity(), nodes_.size());
nodes_.emplace_back(level);
return &nodes_.back();
}
template <typename T>
auto RTree<T>::BuildRecursive(std::vector<Branch<T>>* branches, int level)
-> Branch<T> {
if (branches->size() == 1)
return std::move((*branches)[0]);
int remainder = static_cast<int>(branches->size() % kMaxChildren);
if (remainder > 0) {
if (remainder >= kMinChildren)
remainder = 0;
else
remainder = kMinChildren - remainder;
}
size_t current_branch = 0;
size_t new_branch_index = 0;
while (current_branch < branches->size()) {
int increment_by = kMaxChildren;
if (remainder != 0) {
if (remainder <= kMaxChildren - kMinChildren) {
increment_by -= remainder;
remainder = 0;
} else {
increment_by = kMinChildren;
remainder -= kMaxChildren - kMinChildren;
}
}
Node<T>* node = AllocateNodeAtLevel(level);
node->num_children = 1;
node->children[0] = (*branches)[current_branch];
Branch<T> branch;
branch.bounds = (*branches)[current_branch].bounds;
branch.subtree = node;
++current_branch;
int x = branch.bounds.x();
int y = branch.bounds.y();
int right = branch.bounds.right();
int bottom = branch.bounds.bottom();
for (int k = 1; k < increment_by && current_branch < branches->size();
++k) {
auto& bounds = (*branches)[current_branch].bounds;
x = std::min(x, bounds.x());
y = std::min(y, bounds.y());
right = std::max(right, bounds.right());
bottom = std::max(bottom, bounds.bottom());
node->children[k] = (*branches)[current_branch];
++node->num_children;
++current_branch;
}
branch.bounds.SetRect(x, y, base::ClampSub(right, x),
base::ClampSub(bottom, y));
bool overflow =
branch.bounds.right() != right || branch.bounds.bottom() != bottom;
has_valid_bounds_ &= !overflow;
DCHECK_LT(new_branch_index, current_branch);
(*branches)[new_branch_index] = std::move(branch);
++new_branch_index;
}
branches->resize(new_branch_index);
return BuildRecursive(branches, level + 1);
}
template <typename T>
void RTree<T>::Search(const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
results->clear();
if (num_data_elements_ == 0)
return;
if (!has_valid_bounds_) {
SearchRecursiveFallback(root_.subtree.get(), query, results, rects);
} else if (query.Intersects(root_.bounds)) {
SearchRecursive(root_.subtree.get(), query, results, rects);
}
}
template <typename T>
void RTree<T>::SearchRefs(const gfx::Rect& query,
std::vector<const T*>* results) const {
results->clear();
if (num_data_elements_ == 0)
return;
if (!has_valid_bounds_) {
SearchRefsRecursiveFallback(root_.subtree.get(), query, results);
} else if (query.Intersects(root_.bounds)) {
SearchRefsRecursive(root_.subtree.get(), query, results);
}
}
template <typename T>
void RTree<T>::SearchRecursive(Node<T>* node,
const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (query.Intersects(node->children[i].bounds)) {
if (node->level == 0) {
results->push_back(node->children[i].payload);
if (rects)
rects->push_back(node->children[i].bounds);
} else {
SearchRecursive(node->children[i].subtree.get(), query, results, rects);
}
}
}
}
template <typename T>
void RTree<T>::SearchRefsRecursive(Node<T>* node,
const gfx::Rect& query,
std::vector<const T*>* results) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (query.Intersects(node->children[i].bounds)) {
if (node->level == 0)
results->push_back(&node->children[i].payload);
else
SearchRefsRecursive(node->children[i].subtree.get(), query, results);
}
}
}
template <typename T>
void RTree<T>::SearchRecursiveFallback(Node<T>* node,
const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (node->level == 0) {
if (query.Intersects(node->children[i].bounds)) {
results->push_back(node->children[i].payload);
if (rects)
rects->push_back(node->children[i].bounds);
}
} else {
SearchRecursive(node->children[i].subtree.get(), query, results, rects);
}
}
}
template <typename T>
void RTree<T>::SearchRefsRecursiveFallback(
Node<T>* node,
const gfx::Rect& query,
std::vector<const T*>* results) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (node->level == 0) {
if (query.Intersects(node->children[i].bounds))
results->push_back(&node->children[i].payload);
} else {
SearchRefsRecursive(node->children[i].subtree.get(), query, results);
}
}
}
template <typename T>
gfx::Rect RTree<T>::GetBoundsOrDie() const {
CHECK(has_valid_bounds_);
return root_.bounds;
}
template <typename T>
std::map<T, gfx::Rect> RTree<T>::GetAllBoundsForTracing() const {
std::map<T, gfx::Rect> results;
if (num_data_elements_ > 0)
GetAllBoundsRecursive(root_.subtree.get(), &results);
return results;
}
template <typename T>
void RTree<T>::GetAllBoundsRecursive(Node<T>* node,
std::map<T, gfx::Rect>* results) const {
for (uint16_t i = 0; i < node->num_children; ++i) {
if (node->level == 0)
(*results)[node->children[i].payload] = node->children[i].bounds;
else
GetAllBoundsRecursive(node->children[i].subtree.get(), results);
}
}
template <typename T>
void RTree<T>::Reset() {
num_data_elements_ = 0;
root_.subtree = nullptr;
nodes_.clear();
root_.bounds = gfx::Rect();
has_valid_bounds_ = true;
}
}
#endif