/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #ifndef QUADTREE_H #define QUADTREE_H // This file implements a quad tree used for committing 2D axis aligned // bounding boxes and then retrieving them by 2D point. NEXTPNR_NAMESPACE_BEGIN // A node of a QuadTree. Internal. template class QuadTreeNode { public: class BoundingBox { friend class QuadTreeNode; private: CoordinateT x0_, y0_, x1_, y1_; static constexpr float pinf = std::numeric_limits::infinity(); static constexpr float ninf = -std::numeric_limits::infinity(); public: // Standard constructor for a given (x0,y0), (x1,y1) bounding box // // @param x0 x coordinate of top-left corner of box // @param y0 y coordinate of top-left corner of box // @param x1 x coordinate of bottom-right corner of box // @param y1 y coordinate of bottom-right corner of box BoundingBox(CoordinateT x0, CoordinateT y0, CoordinateT x1, CoordinateT y1) : x0_(x0), y0_(y0), x1_(x1), y1_(y1) { } BoundingBox() : x0_(pinf), y0_(pinf), x1_(ninf), y1_(ninf) {} // Whether a bounding box contains a given points. // A point is defined to be in a bounding box when it's not lesser than // the lower coordinate or greater than the higher coordinate, eg: // A BoundingBox of x0: 20, y0: 30, x1: 100, y1: 130 fits the following // points: // [ (50, 50), (20, 50), (20, 30), (100, 130) ] inline bool contains(CoordinateT x, CoordinateT y) const { if (x < x0_ || x > x1_) return false; if (y < y0_ || y > y1_) return false; return true; } // Sort the bounding box coordinates. void fixup() { if (x1_ < x0_) std::swap(x0_, x1_); if (y1_ < y0_) std::swap(y0_, y1_); } CoordinateT x0() const { return x0_; } CoordinateT y0() const { return y0_; } CoordinateT x1() const { return x1_; } CoordinateT y1() const { return y1_; } void setX0(CoordinateT v) { x0_ = v; } void setY0(CoordinateT v) { y0_ = v; } void setX1(CoordinateT v) { x1_ = v; } void setY1(CoordinateT v) { y1_ = v; } void clear() { x0_ = pinf; y0_ = pinf; x1_ = ninf; y1_ = ninf; } CoordinateT w() const { return x1_ - x0_; } CoordinateT h() const { return y1_ - y0_; } }; private: // A pair of Element and BoundingBox that contains it. class BoundElement { friend class QuadTreeNode; private: BoundingBox bb_; ElementT elem_; public: BoundElement(BoundingBox bb, ElementT elem) : bb_(bb), elem_(elem) {} }; // The bounding box that this node describes. BoundingBox bound_; // How many elements should be contained in this node until it splits into // sub-nodes. const size_t max_elems_; // Four sub-nodes or nullptr if it hasn't split yet. std::unique_ptr[]> children_ = nullptr; // Coordinates of the split. // Anything < split_x is west. CoordinateT splitx_; // Anything < split_y is north. CoordinateT splity_; // Elements contained directly within this node and not part of children // nodes. std::vector elems_; // Depth at which this node is - root is at 0, first level at 1, etc. int depth_; // Checks whether a given bounding box fits within this node - used for // sanity checking on insertion. // @param b bounding box to check // @returns whether b fits in this node entirely bool fits(const BoundingBox &b) const { if (b.x0_ < bound_.x0_ || b.x0_ > bound_.x1_) { return false; } else if (b.x1_ < bound_.x0_ || b.x1_ > bound_.x1_) { return false; } else if (b.y0_ < bound_.y0_ || b.y0_ > bound_.y1_) { return false; } else if (b.y1_ < bound_.y0_ || b.y1_ > bound_.y1_) { return false; } return true; } // Used to describe one of 5 possible places an element can exist: // - the node itself (THIS) // - any of the 4 children nodes. enum Quadrant { THIS_NODE = -1, NW = 0, NE = 1, SW = 2, SE = 3 }; // Finds the quadrant to which a bounding box should go (if the node // is / were to be split). // @param b bounding box to check // @returns quadrant in which b belongs to if the node is were to be split Quadrant quadrant(const BoundingBox &b) const { if (children_ == nullptr) { return THIS_NODE; } bool west0 = b.x0_ < splitx_; bool west1 = b.x1_ < splitx_; bool north0 = b.y0_ < splity_; bool north1 = b.y1_ < splity_; if (west0 && west1 && north0 && north1) return NW; if (!west0 && !west1 && north0 && north1) return NE; if (west0 && west1 && !north0 && !north1) return SW; if (!west0 && !west1 && !north0 && !north1) return SE; return THIS_NODE; } // Checks whether this node should split. bool should_split() const { // The node shouldn't split if it's not large enough to merit it. if (elems_.size() < max_elems_) return false; // The node shouldn't split if its' level is too deep (this is true for // 100k+ entries, where the amount of splits causes us to lose // significant CPU time on traversing the tree, or worse yet causes a // stack overflow). if (depth_ > 5) return false; return true; } public: // Standard constructor for node. // @param b BoundingBox this node covers. // @param depth depth at which this node is in the tree // @max_elems how many elements should this node contain before it splits QuadTreeNode(BoundingBox b, int depth, size_t max_elems = 4) : bound_(b), max_elems_(max_elems), depth_(depth) {} // Disallow copies. QuadTreeNode(const QuadTreeNode &other) = delete; QuadTreeNode &operator=(const QuadTreeNode &other) = delete; // Allow moves. QuadTreeNode(QuadTreeNode &&other) : bound_(other.bound_), max_elems_(other.max_elems_), children_(std::move(other.children_)), splitx_(other.splitx_), splity_(other.splity_), elems_(std::move(other.elems_)), depth_(other.depth_) { other.children_ = nullptr; } QuadTreeNode &operator=(QuadTreeNode &&other) { if (this == &other) return *this; bound_ = other.bound_; max_elems_ = other.max_elems_; children_ = other.max_children_; children_ = other.children_; splitx_ = other.splitx_; splity_ = other.splity_; elems_ = std::move(other.elems_); depth_ = other.depth_; other.children_ = nullptr; return *this; } // Insert an element at a given bounding box. bool insert(const BoundingBox &k, ElementT v) { // Fail early if this BB doesn't fit us at all. if (!fits(k)) { return false; } // Do we have children? if (children_ != nullptr) { // Put the element either recursively into a child if it fits // entirely or keep it for ourselves if not. auto quad = quadrant(k); if (quad == THIS_NODE) { elems_.push_back(BoundElement(k, std::move(v))); } else { return children_[quad].insert(k, std::move(v)); } } else { // No children and not about to have any. if (!should_split()) { elems_.push_back(BoundElement(k, std::move(v))); return true; } // Splitting. Calculate the split point. splitx_ = (bound_.x1_ - bound_.x0_) / 2 + bound_.x0_; splity_ = (bound_.y1_ - bound_.y0_) / 2 + bound_.y0_; // Create the new children. children_ = decltype(children_)(new QuadTreeNode[4] { // Note: not using [NW] = QuadTreeNode because that seems to // crash g++ 7.3.0. /* NW */ QuadTreeNode(BoundingBox(bound_.x0_, bound_.y0_, splitx_, splity_), depth_ + 1, max_elems_), /* NE */ QuadTreeNode(BoundingBox(splitx_, bound_.y0_, bound_.x1_, splity_), depth_ + 1, max_elems_), /* SW */ QuadTreeNode(BoundingBox(bound_.x0_, splity_, splitx_, bound_.y1_), depth_ + 1, max_elems_), /* SE */ QuadTreeNode(BoundingBox(splitx_, splity_, bound_.x1_, bound_.y1_), depth_ + 1, max_elems_), }); // Move all elements to where they belong. auto it = elems_.begin(); while (it != elems_.end()) { auto quad = quadrant(it->bb_); if (quad != THIS_NODE) { // Move to one of the children. if (!children_[quad].insert(it->bb_, std::move(it->elem_))) return false; // Delete from ourselves. it = elems_.erase(it); } else { // Keep for ourselves. it++; } } // Insert the actual element now that we've split. return insert(k, std::move(v)); } return true; } // Dump a human-readable representation of the tree to stdout. void dump(int level) const { for (int i = 0; i < level; i++) printf(" "); printf("loc: % 3d % 3d % 3d % 3d\n", bound_.x0_, bound_.y0_, bound_.x1_, bound_.y1_); if (elems_.size() != 0) { for (int i = 0; i < level; i++) printf(" "); printf("elems: %zu\n", elems_.size()); } if (children_ != nullptr) { for (int i = 0; i < level; i++) printf(" "); printf("children:\n"); children_[NW].dump(level + 1); children_[NE].dump(level + 1); children_[SW].dump(level + 1); children_[SE].dump(level + 1); } } // Return count of BoundingBoxes/Elements contained. // @returns count of elements contained. size_t size() const { size_t res = elems_.size(); if (children_ != nullptr) { res += children_[NW].size(); res += children_[NE].size(); res += children_[SW].size(); res += children_[SE].size(); } return res; } // Retrieve elements whose bounding boxes cover the given coordinates. // // @param x X coordinate of points to query. // @param y Y coordinate of points to query. // @returns vector of found bounding boxes void get(CoordinateT x, CoordinateT y, std::vector &res) const { if (!bound_.contains(x, y)) return; for (const auto &elem : elems_) { const auto &bb = elem.bb_; if (bb.contains(x, y)) { res.push_back(elem.elem_); } } if (children_ != nullptr) { children_[NW].get(x, y, res); children_[NE].get(x, y, res); children_[SW].get(x, y, res); children_[SE].get(x, y, res); } } }; // User facing method to manage a quad tree. // // @param CoodinateT scalar type of the coordinate system - int, float, ... // @param ElementT type of the contained element. Must be movable or copiable. template class QuadTree { private: // Root of the tree. QuadTreeNode root_; public: // To let user create bounding boxes of the correct type. // Bounding boxes are composed of two 2D points, which designate their // top-left and bottom-right corners. All its' edges are axis aligned. using BoundingBox = typename QuadTreeNode::BoundingBox; // Standard constructor. // // @param b Bounding box of the entire tree - all committed elements must // fit within in. QuadTree(BoundingBox b) : root_(b, 0) {} // Inserts a new value at a given bounding box.e // BoundingBoxes are not deduplicated - if two are pushed with the same // coordinates, the first one will take precedence. // // @param k Bounding box at which to store value. // @param v Value at a given bounding box. // @returns Whether the insert was successful. bool insert(BoundingBox k, ElementT v) { k.fixup(); return root_.insert(k, v); } // Dump a human-readable representation of the tree to stdout. void dump() const { root_.dump(0); } // Return count of BoundingBoxes/Elements contained. // @returns count of elements contained. size_t size() const { return root_.size(); } // Retrieve elements whose bounding boxes cover the given coordinates. // // @param x X coordinate of points to query. // @param y Y coordinate of points to query. // @returns vector of found bounding boxes std::vector get(CoordinateT x, CoordinateT y) const { std::vector res; root_.get(x, y, res); return res; } }; NEXTPNR_NAMESPACE_END #endif