Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1148)

Unified Diff: ui/gfx/geometry/r_tree.cc

Issue 245763002: Adds an RTree data structure to gfx/geometry (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: adds OWNERS change Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/geometry/r_tree.cc
diff --git a/ui/gfx/geometry/r_tree.cc b/ui/gfx/geometry/r_tree.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d00d4d997dd541b1ad19351fb4bea43cc550b515
--- /dev/null
+++ b/ui/gfx/geometry/r_tree.cc
@@ -0,0 +1,778 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/r_tree.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+
+namespace gfx {
+
+RTree::Node::Node(int level)
+ : base::LinkNode<Node>(),
+ level_(level),
+ count_(0),
+ parent_(NULL),
+ key_(NULL) {}
+
+RTree::Node::Node(const Rect& rect, void* key)
+ : base::LinkNode<Node>(),
+ rect_(rect),
+ level_(-1),
+ count_(0),
+ parent_(NULL),
+ key_(key) {}
+
+RTree::Node::~Node() {
+ Clear();
+}
+
+void RTree::Node::Clear() {
+ // Iterate through children and delete them all.
+ while (children_.head() != children_.end()) {
+ base::LinkNode<Node>* node = children_.head();
+ node->RemoveFromList();
+ delete node->value();
+ }
+ key_ = NULL;
+}
+
+void RTree::Node::Query(
+ const Rect& query_rect, std::set<void*>* matches_out) const {
+ // Check own bounding box for intersection, can cull all children if no
+ // intersection.
+ if (!rect_.Intersects(query_rect)) {
+ return;
+ }
+
+ // Conversely if we are completely contained within the query rect we can
+ // confidently skip all bounds checks for ourselves and all our children.
+ if (query_rect.Contains(rect_)) {
+ GetAllValues(matches_out);
+ return;
+ }
+
+ // We intersect the query rect but we are not are not contained within it.
+ // If we are a record node, then add our record value. Otherwise we must
+ // query each of our children in turn.
+ if (key_) {
+ DCHECK_EQ(level_, -1);
+ matches_out->insert(key_);
+ } else {
+ for (base::LinkNode<Node>* link_node = children_.head();
+ link_node != children_.end();
+ link_node = link_node->next()) {
+ Node* node = link_node->value();
+ // Sanity-check our children.
+ DCHECK_EQ(node->parent_, this);
+ DCHECK_EQ(level_ - 1, node->level_);
+ DCHECK(rect_.Contains(node->rect_));
+ node->Query(query_rect, matches_out);
+ }
+ }
+}
+
+void RTree::Node::RecomputeBounds() {
+ RecomputeBoundsNoParents();
+ // Recompute our parent's bounds recursively up to the root.
+ if (parent_) {
+ parent_->RecomputeBounds();
+ }
+}
+
+void RTree::Node::RemoveNodesForReinsert(
+ size_t number_to_remove, std::list<Node*>* nodes) {
piman 2014/04/21 23:59:31 Why not std::vector<Node*>? You know the size ahea
luken 2014/04/28 01:33:34 Done.
+ DCHECK_GE(count_, number_to_remove);
+ // Sort our children in descending order of distance from our bounding
+ // rect center to their bounding rect center.
+ std::vector<Node*> center_sort;
+ center_sort.reserve(count_);
+ // copy points to our children into each array.
+ for (base::LinkNode<Node>* link_node = children_.head();
+ link_node != children_.end();
+ link_node = link_node->next()) {
+ center_sort.push_back(link_node->value());
+ }
+ DCHECK_EQ(count_, center_sort.size());
+ std::sort(center_sort.begin(),
+ center_sort.end(),
+ &RTree::Node::CompareCenterDistanceFromParent);
+ // Remove the highest distance nodes from our children list, and then add them
+ // to the returned list.
+ for (size_t i = 0; i < number_to_remove; ++i) {
+ center_sort[i]->RemoveFromList();
+ nodes->push_back(center_sort[i]);
+ }
+ // Update our internal references including bounds.
+ count_ = count_ - number_to_remove;
+}
+
+size_t RTree::Node::RemoveChild(Node* child_node, std::list<Node*>* orphans) {
piman 2014/04/21 23:59:31 std::vector here too? You also know the size.
luken 2014/04/28 01:33:34 Done.
+ // Should actually be one of our children.
+ DCHECK_EQ(child_node->parent_, this);
+ // Remove any children of this child and append to orphans list.
+ Node* orphan = child_node->RemoveAndReturnFirstChild();
+ while (orphan) {
+ orphans->push_back(orphan);
+ orphan = child_node->RemoveAndReturnFirstChild();
+ }
piman 2014/04/21 23:59:31 It seems like we could simply walk the list of chi
luken 2014/04/28 01:33:34 Done.
+ child_node->RemoveFromList();
+ count_--;
+ return count_;
+}
+
+RTree::Node* RTree::Node::RemoveAndReturnFirstChild() {
+ if (children_.head() == children_.end()) {
+ return NULL;
+ }
+ Node* first_child = children_.head()->value();
+ DCHECK_EQ(first_child->parent_, this);
+ first_child->RemoveFromList();
piman 2014/04/21 23:59:31 Do we need to --count_ ?
luken 2014/04/28 01:33:34 Done.
+ // Invalidate parent, as this child may even become the new root.
+ first_child->parent_ = NULL;
+ return first_child;
+}
+
+// Uses the R*-Tree algorithm CHOOSELEAF proposed by Beckmann et al.
+RTree::Node* RTree::Node::ChooseSubtree(Node* node) {
+ // Should never be called on a record node.
+ DCHECK(!key_);
+ DCHECK(level_ >= 0);
+ DCHECK(node);
+
+ // If we are a parent of nodes on the provided node level, we are done.
+ if (level_ == node->level_ + 1) {
+ return this;
+ }
+ // We are an internal node, and thus guaranteed to have children.
+ DCHECK(children_.head() != children_.end());
+
+ // Iterate over all children to find best candidate for insertion.
+ Node* best_candidate = NULL;
+ int least_overlap_increase = std::numeric_limits<int>::max();
+ int least_area_change = std::numeric_limits<int>::max();
+ for (base::LinkNode<Node>* link_node = children_.head();
+ link_node != children_.end();
+ link_node = link_node->next()) {
+ Node* candidate_node = link_node->value();
+ // All of our children should be at our level - 1.
+ DCHECK_EQ(level_ - 1, candidate_node->level_);
+ // All of our children should have us as a parent.
+ DCHECK_EQ(this, candidate_node->parent_);
+ int overlap_increase = std::numeric_limits<int>::max();
+ // For Nodes whose children are leaves, we calculate minimum overlap
+ // increase caused by addition of this new record Node.
+ if (!candidate_node->level_) {
piman 2014/04/21 23:59:31 This is a loop constant (given checks above). Can
luken 2014/04/28 01:33:34 So the algorithm calls for node selection using mi
+ // Expand candidate rect to include the new node rect.
+ Rect expanded_rect = candidate_node->rect_;
+ expanded_rect.Union(node->rect_);
piman 2014/04/21 23:59:31 If node->rect_ is fully contained in candidate_nod
luken 2014/04/28 01:33:34 Yes, good observation, done.
+ int total_original_overlap = 0;
+ int total_expanded_overlap = 0;
+
+ // Now calculate overlap with all other rects in this node.
+ for (base::LinkNode<Node>* overlap_link_node = children_.head();
+ overlap_link_node != children_.end();
+ overlap_link_node = overlap_link_node->next()) {
+ Node* overlap_node = overlap_link_node->value();
+ // Skip calculating overlap with the candidate rect.
+ if (overlap_node == candidate_node) {
+ continue;
+ }
+ Rect overlap_rect = candidate_node->rect_;
+ overlap_rect.Intersect(overlap_node->rect_);
+ total_original_overlap += overlap_rect.width() * overlap_rect.height();
piman 2014/04/21 23:59:31 nit: overlap_rect.size().GetArea() (other places t
luken 2014/04/28 01:33:34 Done.
+ Rect expanded_overlap_rect = expanded_rect;
+ expanded_overlap_rect.Intersect(overlap_node->value()->rect_);
piman 2014/04/21 23:59:31 overlap_node->rect_?
luken 2014/04/28 01:33:34 Done.
+ total_expanded_overlap +=
+ expanded_overlap_rect.width() * expanded_overlap_rect.height();
+ }
+
+ // Compare this overlap increase with best one to date, update best.
+ overlap_increase = total_expanded_overlap - total_original_overlap;
+ if (overlap_increase < least_overlap_increase) {
+ best_candidate = candidate_node->value();
piman 2014/04/21 23:59:31 no ->value()
luken 2014/04/28 01:33:34 Done.
+ least_overlap_increase = overlap_increase;
+ least_area_change = AreaChangeToAdd(candidate_node->rect_, node->rect_);
piman 2014/04/21 23:59:31 AreaChangeToAdd computes the union bounds, that we
piman 2014/04/21 23:59:31 you probably want a continue here. the rest of the
luken 2014/04/28 01:33:34 I don't quite understand this. This code is not in
luken 2014/04/28 01:33:34 Done.
+ }
+ }
+
+ // For non-leaf internal nodes, or for leafs that are tied, we choose least
+ // area enlargement required to add this rect.
+ if (candidate_node->level_ > 0 ||
+ overlap_increase == least_overlap_increase) {
piman 2014/04/21 23:59:31 This is always true if candidate_node->level_ > 0
luken 2014/04/28 01:33:34 The candidadate_node->level_ == 0 case needs this
+ int candidate_area_change =
+ AreaChangeToAdd(candidate_node->rect_, node->rect_);
piman 2014/04/21 23:59:31 You already computed expanded_rect in the (candida
luken 2014/04/28 01:33:34 Done.
+ if (candidate_area_change < least_area_change) {
+ best_candidate = candidate_node;
+ least_area_change = candidate_area_change;
+ } else if (candidate_area_change == least_area_change) {
+ // It is assumed we will always tie with a valid candidate.
+ DCHECK(best_candidate);
+ // Ties are broken by choosing entry with least area
+ if (candidate_node->rect_.width() * candidate_node->rect_.height() <
+ best_candidate->rect_.width() * best_candidate->rect_.height()) {
+ best_candidate = candidate_node;
+ }
+ }
+ }
+ }
+
+ DCHECK(best_candidate);
+ return best_candidate->ChooseSubtree(node);
+}
+
+size_t RTree::Node::AddNode(Node* node) {
+ DCHECK(node);
+ // Sanity-check that the level of the child being added is one more than ours.
+ DCHECK_EQ(level_ - 1, node->level_);
+ node->parent_ = this;
+ children_.Append(node);
+ ++count_;
+ rect_.Union(node->rect_);
+ return count_;
+}
+
+RTree::Node* RTree::Node::Split(size_t min_children, size_t max_children) {
+ // Please don't attempt to split a record Node.
+ DCHECK(!key_);
+ // We should have the correct number of children to begin with.
+ DCHECK_GT(count_, max_children);
+ // First determine if splitting along the horizontal or vertical axis. We
+ // sort the rectangles of our children by lower then upper values along each
+ // axis.
+ std::vector<Node*> horizontal_sort;
+ std::vector<Node*> vertical_sort;
+ horizontal_sort.reserve(count_);
+ vertical_sort.reserve(count_);
+ // Copy pointers to our children into each array.
+ for (base::LinkNode<Node>* link_node = children_.head();
+ link_node != children_.end();
+ link_node = link_node->next()) {
+ Node* node = link_node->value();
+ DCHECK_EQ(node->parent_, this);
+ DCHECK_EQ(level_ - 1, node->level_);
+ horizontal_sort.push_back(node);
+ vertical_sort.push_back(node);
+ }
+ DCHECK_EQ(count_, horizontal_sort.size());
+ std::sort(horizontal_sort.begin(),
+ horizontal_sort.end(),
+ &RTree::Node::CompareHorizontal);
+ std::sort(vertical_sort.begin(),
+ vertical_sort.end(),
+ &RTree::Node::CompareVertical);
+
+ // We will be examining the bounding boxes of different splits of our children
+ // sorted along each axis. Here we precompute the bounding boxes of these
+ // distributions. For the low bounds the ith element can be considered the
+ // union of all rects [0,i] in the relevant sorted axis array.
+ std::vector<Rect> low_horizontal_bounds;
+ std::vector<Rect> low_vertical_bounds;
+ Rect horizontal_bounds;
+ Rect vertical_bounds;
+ low_horizontal_bounds.reserve(count_);
+ low_vertical_bounds.reserve(count_);
+ for (size_t i = 0; i < count_; ++i) {
+ horizontal_bounds.Union(horizontal_sort[i]->rect_);
+ vertical_bounds.Union(vertical_sort[i]->rect_);
+ low_horizontal_bounds.push_back(horizontal_bounds);
+ low_vertical_bounds.push_back(vertical_bounds);
+ }
+ // For the high bounds the ith element can be considered the union of all
+ // rects [i, count_) in the relevant sorted axis array.
+ std::vector<Rect> high_horizontal_bounds;
+ std::vector<Rect> high_vertical_bounds;
+ horizontal_bounds.SetRect(0, 0, 0, 0);
+ vertical_bounds.SetRect(0, 0, 0, 0);
+ high_horizontal_bounds.resize(count_);
+ high_vertical_bounds.resize(count_);
+ for (int j = static_cast<int>(count_) - 1; j >= 0; --j) {
+ horizontal_bounds.Union(horizontal_sort[j]->rect_);
+ vertical_bounds.Union(vertical_sort[j]->rect_);
+ high_horizontal_bounds[j] = horizontal_bounds;
+ high_vertical_bounds[j] = vertical_bounds;
+ }
+
+ // Examine the possible distributions of each sorted list by iterating through
+ // valid split points p, min_children <= p <= max_children - min_children, and
+ // computing the sums of the margins of the required bounding boxes in each
+ // group. Smallest margin sum will determine split axis.
+ int smallest_horizontal_margin_sum = std::numeric_limits<int>::max();
+ int smallest_vertical_margin_sum = std::numeric_limits<int>::max();
+ for (size_t p = min_children; p < max_children - min_children; ++p) {
+ int horizontal_margin_sum =
+ low_horizontal_bounds[p].width() + low_horizontal_bounds[p].height() +
+ high_horizontal_bounds[p].width() + high_horizontal_bounds[p].height();
+ int vertical_margin_sum =
+ low_vertical_bounds[p].width() + low_vertical_bounds[p].height() +
+ high_vertical_bounds[p].width() + high_vertical_bounds[p].height();
+ // Update margin minima if necessary.
+ smallest_horizontal_margin_sum =
+ std::min(horizontal_margin_sum, smallest_horizontal_margin_sum);
+ smallest_vertical_margin_sum =
+ std::min(vertical_margin_sum, smallest_vertical_margin_sum);
+ }
+
+ // Split along the axis perpendicular to the axis with the overall smallest
+ // margin sum. Meaning the axis sort resulting in two boxes with the smallest
+ // combined margin will become the axis along which the sorted rectangles are
+ // distributed to the two Nodes.
+ bool is_vertical_split =
+ smallest_vertical_margin_sum < smallest_horizontal_margin_sum;
+
+ // Lastly we determine optimal index.
+ size_t optimal_split_index;
+ if (is_vertical_split) {
+ optimal_split_index = CalculateOptimalSplitIndex(min_children,
+ max_children,
+ &low_vertical_bounds,
+ &high_vertical_bounds);
+ } else {
+ optimal_split_index = CalculateOptimalSplitIndex(min_children,
+ max_children,
+ &low_horizontal_bounds,
+ &high_horizontal_bounds);
+ }
+
+ // We have the optimal sort axis as well as the optimal index. Create the
+ // new Node and give it the latter half of our children.
+ Node* sibling = new Node(level_);
+ sibling->parent_ = parent_;
+ sibling->count_ = count_ - optimal_split_index - 1;
+ count_ = optimal_split_index + 1;
+ if (is_vertical_split) {
+ rect_ = low_vertical_bounds[optimal_split_index];
+ sibling->rect_ = high_vertical_bounds[optimal_split_index + 1];
+ // Give these children to our sibling.
+ for (size_t i = optimal_split_index + 1; i < vertical_sort.size(); ++i) {
+ Node* neice = vertical_sort[i];
+ neice->RemoveFromList();
+ neice->parent_ = sibling;
+ sibling->children_.Append(neice);
+ }
+ } else {
+ rect_ = low_horizontal_bounds[optimal_split_index];
+ sibling->rect_ = high_horizontal_bounds[optimal_split_index + 1];
+ for (size_t i = optimal_split_index + 1; i < horizontal_sort.size(); ++i) {
+ Node* nephew = horizontal_sort[i];
+ nephew->RemoveFromList();
+ nephew->parent_ = sibling;
+ sibling->children_.Append(nephew);
+ }
+ }
+
+ return sibling;
+}
+
+void RTree::Node::SetRect(const Rect& rect) {
+ // Record nodes only, please.
+ DCHECK(key_);
+ rect_ = rect;
+}
+
+RTree::Node* RTree::Node::parent() const {
+ return parent_;
+}
+
+int RTree::Node::level() const {
+ return level_;
+}
+
+const Rect& RTree::Node::rect() const {
+ return rect_;
+}
+
+size_t RTree::Node::count() const {
+ return count_;
+}
+
+bool RTree::Node::Validate(size_t min_children, size_t max_children) const {
piman 2014/04/21 23:59:31 You can never return anything but true (by inducti
luken 2014/04/28 01:33:34 Yeah I didn't know that we run unit tests in relea
+ // We don't call Validate() directly on Record nodes.
+ DCHECK(!key_);
+ // Need to have at least one child.
+ DCHECK_NE(children_.head(), children_.end());
+ // Independently compute a bounding rectangle, and make sure it is identical
+ // to our current bounding rectangle.
+ Rect bounds(0, 0, 0, 0);
+ size_t check_count = 0;
+ for (base::LinkNode<Node>* link_node = children_.head();
+ link_node != children_.end();
+ link_node = link_node->next()) {
+ check_count++;
+ Node* node = link_node->value();
+ bounds.Union(node->rect_);
+ DCHECK_EQ(node->parent_, this);
+ DCHECK_LE(node->count_, max_children);
+ DCHECK_EQ(level_ - 1, node->level_);
+ if (level_) {
+ DCHECK_GE(node->count_, min_children);
+ if (!node->Validate(min_children, max_children)) {
+ return false;
+ }
+ } else {
+ DCHECK_EQ(node->count_, 0U);
+ DCHECK_EQ(node->children_.head(), node->children_.end());
+ DCHECK(node->key_);
+ }
+ }
+ DCHECK_EQ(count_, check_count);
+ DCHECK(!rect_.IsEmpty());
+ DCHECK(rect_ == bounds);
+ return true;
+}
+
+// Returns all contained record_node values for this node and all children.
+void RTree::Node::GetAllValues(std::set<void*>* matches_out) const {
+ if (key_) {
+ DCHECK_EQ(level_, -1);
+ matches_out->insert(key_);
+ } else {
+ for (base::LinkNode<Node>* link_node = children_.head();
+ link_node != children_.end();
+ link_node = link_node->next()) {
+ Node* node = link_node->value();
+ // Sanity-check our children.
+ DCHECK_EQ(node->parent_, this);
+ DCHECK_EQ(level_ - 1, node->level_);
+ DCHECK(rect_.Contains(node->rect_));
+ node->GetAllValues(matches_out);
+ }
+ }
+}
+
+// static
+int RTree::Node::AreaChangeToAdd(const Rect& bounds, const Rect& rect) {
piman 2014/04/21 23:59:31 nit: might as well make it a free function (in ano
luken 2014/04/28 01:33:34 Done.
+ // Quick test for containment of rect in bounds, resulting in 0 area change.
+ if (bounds.Contains(rect)) {
+ return 0;
+ }
+ int current_area = bounds.width() * bounds.height();
+ Rect test_rect(bounds);
+ // Expand a copy of bounds to contain the new rectangle.
+ test_rect.Union(rect);
+ return (test_rect.width() * test_rect.height()) - current_area;
+}
+
+// static
+bool RTree::Node::CompareVertical(Node* a, Node* b) {
+ // Sort nodes by top coordinate first.
+ if (a->rect_.y() < b->rect_.y()) {
+ return true;
+ } else if (a->rect_.y() == b->rect_.y()) {
+ // If top coordinate is equal, sort by lowest bottom coordinate.
+ return a->rect_.bottom() < b->rect_.bottom();
piman 2014/04/21 23:59:31 test height() instead of bottom(), since the latte
luken 2014/04/28 01:33:34 Done.
+ }
+ return false;
+}
+
+// static
+bool RTree::Node::CompareHorizontal(Node* a, Node* b) {
+ // Sort nodes by left coordinate first.
+ if (a->rect_.x() < b->rect_.x()) {
+ return true;
+ } else if (a->rect_.x() == b->rect_.x()) {
+ // If left coordinate is equal, sort by lowest right coordinate.
+ return a->rect_.right() < b->rect_.right();
piman 2014/04/21 23:59:31 test width() instead of right().
luken 2014/04/28 01:33:34 Done.
+ }
+ return false;
+}
+
+// Sort these two nodes by the distance of the center of their rects from the
+// center of their parent's rect. We don't bother with square roots because we
+// are only comparing the two values for sorting purposes.
+// static
+bool RTree::Node::CompareCenterDistanceFromParent(Node* a, Node* b) {
+ // This comparison assumes the nodes have the same parent.
+ DCHECK(a->parent_ == b->parent_);
+ // This comparison requires that each node have a parent.
+ DCHECK(a->parent_);
+ // Sanity-check level_ of these nodes is equal.
+ DCHECK_EQ(a->level_, b->level_);
+ // Also the parent of the nodes should have level one higher.
+ DCHECK_EQ(a->level_, a->parent_->level_ - 1);
+
+ // Find the parent.
+ Node* p = a->parent();
+
+ // First we calculate the center of the parent rectangle.
+ int p_center_x = p->rect_.x() + (p->rect_.width() / 2);
+ int p_center_y = p->rect_.y() + (p->rect_.height() / 2);
piman 2014/04/21 23:59:31 nit: it would be more readable to make a free inli
luken 2014/04/28 01:33:34 Done.
+
+ // Next we calculate the squared distance of a's center to p's center.
+ int a_dist_sq_x = a->rect_.x() + (a->rect_.width() / 2) - p_center_x;
+ a_dist_sq_x *= a_dist_sq_x;
+ int a_dist_sq_y = a->rect_.y() + (a->rect_.height() / 2) - p_center_y;
+ a_dist_sq_y *= a_dist_sq_y;
+
+ // Now we do the same for b.
+ int b_dist_sq_x = b->rect_.x() + (b->rect_.width() / 2) - p_center_x;
+ b_dist_sq_x *= b_dist_sq_x;
+ int b_dist_sq_y = b->rect_.y() + (b->rect_.height() / 2) - p_center_y;
+ b_dist_sq_y *= b_dist_sq_y;
+
+ // We are sorting by greatest to least distance so we invert the logic on the
+ // comparison.
+ return (a_dist_sq_x + a_dist_sq_y) > (b_dist_sq_x + b_dist_sq_y);
+}
+
+size_t RTree::Node::CalculateOptimalSplitIndex(
+ size_t min_children,
+ size_t max_children,
+ const std::vector<Rect>* low_bounds,
+ const std::vector<Rect>* high_bounds) {
+ int smallest_overlap_area = std::numeric_limits<int>::max();
+ int smallest_combined_area = std::numeric_limits<int>::max();
+ size_t optimal_split_index = 0;
+ for (size_t p = min_children; p < max_children - min_children; ++p) {
+ Rect overlap_bounds = low_bounds->at(p);
+ overlap_bounds.Union(high_bounds->at(p));
+ int overlap_area = overlap_bounds.width() * overlap_bounds.height();
+ if (overlap_area < smallest_overlap_area) {
+ smallest_overlap_area = overlap_area;
+ smallest_combined_area =
+ (low_bounds->at(p).width() * low_bounds->at(p).height()) +
+ (high_bounds->at(p).width() * high_bounds->at(p).height());
+ optimal_split_index = p;
+ } else if (overlap_area == smallest_overlap_area) {
+ // Break ties with smallest combined area of the two bounding boxes.
+ int combined_area =
+ (low_bounds->at(p).width() * low_bounds->at(p).height()) +
+ (high_bounds->at(p).width() * high_bounds->at(p).height());
+ if (combined_area < smallest_combined_area) {
+ smallest_combined_area = combined_area;
+ optimal_split_index = p;
+ }
+ }
+ }
+
+ return optimal_split_index;
+}
+
+void RTree::Node::RecomputeBoundsNoParents() {
+ // Clear our rectangle, then reset it to union of our children.
+ rect_.SetRect(0, 0, 0, 0);
+ for (base::LinkNode<Node>* node = children_.head(); node != children_.end();
+ node = node->next()) {
+ rect_.Union(node->value()->rect_);
+ }
+}
+
+RTree::RTree(size_t min_children, size_t max_children)
+ : root_(NULL), min_children_(min_children), max_children_(max_children) {
+ // R-Trees require min_children >= 2
+ DCHECK_GE(min_children_, 2U);
+ // R-Trees also require min_children <= max_children / 2
+ DCHECK_LE(min_children_, max_children_ / 2U);
+}
+
+RTree::~RTree() {
+ Clear();
+}
+
+void RTree::Insert(const Rect& rect, void* key) {
+ // Non-NULL keys, please.
+ DCHECK(key);
+
+ // An empty rectangle will never intersect with any other rectangle, and so
+ // has no purpose within a spatial database.
+ if (rect.IsEmpty())
+ return;
piman 2014/04/21 23:59:31 That goes contrary to the tests below. Either remo
luken 2014/04/28 01:33:34 yes, this one is a bug, good catch, removed. I add
+
+ Node* record_node = NULL;
+ // Check if this key is already present in the tree.
+ std::map<void*, Node*>::iterator it = record_map_.find(key);
+ if (it != record_map_.end()) {
+ // We will re-use this node structure, regardless of re-insert or return.
+ record_node = it->second;
+ // If the new rect and the current rect are identical we can skip rest of
+ // Insert() as nothing has changed.
+ if (record_node->rect() == rect) {
+ return;
+ }
+
+ // Remove the node from the tree in its current position.
+ Remove(record_node);
+
+ // If we are replacing this key with an empty rectangle we just remove the
+ // old node from the list and return, thus preventing insertion of empty
+ // rectangles into our spatial database.
+ if (rect.IsEmpty()) {
+ record_map_.erase(it);
+ delete record_node;
+ return;
+ }
+
+ // Reset the rectangle to the new value.
+ record_node->SetRect(rect);
+ } else {
+ if (rect.IsEmpty()) {
+ return;
+ }
+ // Build a new record Node for insertion in to tree.
+ record_node = new Node(rect, key);
+ // Add this new node to our map, for easy retrieval later.
+ record_map_.insert(std::make_pair(key, record_node));
+ }
+
+ // Make a root_ if needed.
+ if (!root_) {
+ root_ = new Node(0);
piman 2014/04/21 23:59:31 Should we just do this in the constructor, and sav
luken 2014/04/28 01:33:34 Done.
+ }
+
+ // Call internal Insert with this new node and no re-inserts for this
+ // of a data node having happened yet.
+ int starting_level = -1;
+ Insert(record_node, &starting_level);
+}
+
+void RTree::Remove(void* key) {
+ // Search the map for the leaf parent that has the provided record.
+ std::map<void*, Node*>::iterator it = record_map_.find(key);
+ // If not in the map it's not in the tree, we're done.
+ if (it == record_map_.end()) {
+ return;
+ }
+ Node* node = it->second;
+ // Remove this node from the map.
+ record_map_.erase(it);
+ // Now remove it from the RTree.
+ Remove(node);
+ delete node;
+
+ // Lastly check the root. If it has only one non-leaf child, delete it and
+ // replace it with its child. If it has no children, we can delete the
+ // entire empty tree.
+ if (root_->count() == 1 && root_->level() > 0) {
+ Node* new_root = root_->RemoveAndReturnFirstChild();
+ DCHECK(new_root);
+ delete root_;
+ root_ = new_root;
+ } else if (root_->count() == 0) {
+ delete root_;
+ root_ = NULL;
+ }
+}
+
+void RTree::Query(const Rect& query_rect, std::set<void*>* matches_out) const {
+ if (root_) {
+ root_->Query(query_rect, matches_out);
+ }
+}
+
+void RTree::Clear() {
+ record_map_.clear();
+ delete root_;
+ root_ = NULL;
+}
+
+void RTree::Insert(Node* node, int* highest_reinsert_level) {
+ // Internal Insert always assumes that a root has been created.
+ DCHECK(root_);
+ // Find the most appropriate parent to insert node into.
+ Node* parent = root_->ChooseSubtree(node);
+ DCHECK(parent);
+ // Verify ChooseSubtree returned a Node at the correct level.
+ DCHECK_EQ(parent->level(), node->level() + 1);
+ Node* insert_node = node;
+ Node* insert_parent = parent;
+ Node* needs_bounds_recomputed = insert_parent->parent();
+ std::list<Node*> reinserts;
+ // Attempt to insert the Node, if this overflows the Node we must handle it.
+ while (insert_parent && insert_parent->AddNode(insert_node) > max_children_) {
+ // If we have yet to re-insert nodes at this level during this data insert,
+ // and we're not at the root, R*-Tree calls for re-insertion of some of the
+ // nodes, resulting in a better balance on the tree.
+ if (insert_parent->parent() &&
+ insert_parent->level() > *highest_reinsert_level) {
+ insert_parent->RemoveNodesForReinsert(max_children_ / 3, &reinserts);
+ // Adjust highest_reinsert_level to this level.
+ *highest_reinsert_level = insert_parent->level();
+ // We didn't create any new nodes so we have nothing new to insert.
+ insert_node = NULL;
+ // RemoveNodesForReinsert() does not recompute bounds, so mark it.
+ needs_bounds_recomputed = insert_parent;
+ break;
+ }
+
+ // Split() will create a sibling to insert_parent both of which will have
+ // valid bounds, but this invalidates their parent's bounds.
+ insert_node = insert_parent->Split(min_children_, max_children_);
+ insert_parent = insert_parent->parent();
+ needs_bounds_recomputed = insert_parent;
+ }
+
+ // If we have a Node to insert, and we hit the root of the current tree,
+ // we create a new root which is the parent of the current root and the
+ // insert_node
+ if (!insert_parent && insert_node) {
+ Node* old_root = root_;
+ root_ = new Node(old_root->level() + 1);
+ root_->AddNode(old_root);
+ root_->AddNode(insert_node);
+ }
+
+ // Recompute bounds along insertion path.
+ if (needs_bounds_recomputed) {
piman 2014/04/21 23:59:31 In which case can this be NULL?
luken 2014/04/28 01:33:34 Two I can think of: a) inserting direct child of
+ needs_bounds_recomputed->RecomputeBounds();
piman 2014/04/21 23:59:31 If we have reinserts, we'll recompute bounds for p
luken 2014/04/28 01:33:34 Insert() and Remove() as described in both the R-T
+ }
+
+ // Complete reinserts, if any.
+ for (std::list<Node*>::iterator it = reinserts.begin();
+ it != reinserts.end();
+ it++) {
+ Insert((*it), highest_reinsert_level);
piman 2014/04/21 23:59:31 nit: () superfluous
luken 2014/04/28 01:33:34 Done.
+ }
+}
+
+void RTree::Remove(Node* node) {
+ // We need to remove this node from its parent.
+ Node* parent = node->parent();
+ // Record nodes are never allowed as the root, so we should always have a
+ // parent.
+ DCHECK(parent);
+ // Should always be a leaf that had the record.
+ DCHECK_EQ(parent->level(), 0);
+ std::list<Node*> orphans;
+ Node* child = node;
+
+ // Traverse up the tree, removing the child from each parent and deleting
+ // parent nodes, until we either encounter the root of the tree or a parent
+ // that still has sufficient children.
+ while (parent && parent->RemoveChild(child, &orphans) < min_children_) {
+ if (child != node) {
+ delete child;
+ }
+ child = parent;
+ parent = parent->parent();
+ }
+
+ // If we stopped deleting nodes up the tree before encountering the root,
+ // we'll need to fix up the bounds from the first parent we didn't delete
+ // up to the root.
+ if (parent) {
+ parent->RecomputeBounds();
+ }
+
+ // Now re-insert each of the orphaned nodes back into the tree.
+ for (std::list<Node*>::iterator it = orphans.begin(); it != orphans.end();
+ it++) {
+ int starting_level = -1;
+ Insert((*it), &starting_level);
piman 2014/04/21 23:59:31 nit: no need for extra ()
luken 2014/04/28 01:33:34 Done.
+ }
+}
+
+bool RTree::Validate() const {
+ if (!root_) {
+ return true;
+ }
+ // Root is special in that it needs to have at least one child but can
+ // have less than min_children.
+ DCHECK_GE(root_->count(), 1U);
+ DCHECK_LE(root_->count(), max_children_);
+ return root_->Validate(min_children_, max_children_);
+}
+
+} // namespace gfx

Powered by Google App Engine
This is Rietveld 408576698