mirror of https://github.com/KLayout/klayout.git
WIP (quad tree)
This commit is contained in:
parent
f136fdcde6
commit
54b5d9f5d6
|
|
@ -24,6 +24,7 @@
|
|||
#define HDR_dbQuadTree
|
||||
|
||||
#include "dbBox.h"
|
||||
#include "tlLog.h"
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
|
|
@ -38,6 +39,7 @@ public:
|
|||
typedef db::point<coord_type> point_type;
|
||||
typedef db::vector<coord_type> vector_type;
|
||||
typedef std::vector<T> objects_vector;
|
||||
typedef db::coord_traits<coord_type> coord_traits;
|
||||
|
||||
quad_tree_node (const point_type ¢er)
|
||||
: m_split (false), m_center (center)
|
||||
|
|
@ -65,23 +67,31 @@ public:
|
|||
insert (value, propose_ucenter (total_box));
|
||||
}
|
||||
|
||||
bool remove (const T &value)
|
||||
bool erase (const T &value)
|
||||
{
|
||||
box_type b = BC () (value);
|
||||
|
||||
if (! m_split || b.contains (m_center)) {
|
||||
int n = quad_for (b);
|
||||
|
||||
if (! m_split || n < 0) {
|
||||
|
||||
for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
|
||||
if (*i == value) {
|
||||
m_objects.erase (i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (m_q[i] && b.inside (m_q[i]->box (m_center))) {
|
||||
return b.remove (value);
|
||||
} else if (m_q[n]) {
|
||||
|
||||
if (m_q[n]->erase (value)) {
|
||||
if (m_q[n]->empty ()) {
|
||||
delete m_q[n];
|
||||
m_q[n] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -131,12 +141,39 @@ public:
|
|||
return count;
|
||||
}
|
||||
|
||||
size_t levels () const
|
||||
{
|
||||
size_t l = 1;
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n]) {
|
||||
l = std::max (l, m_q[n]->levels () + 1);
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
bool check_top (const box_type &total_box) const
|
||||
{
|
||||
return check (propose_ucenter (total_box));
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_split;
|
||||
point_type m_center;
|
||||
quad_tree_node *m_q [4];
|
||||
objects_vector m_objects;
|
||||
|
||||
int quad_for (const box_type &box) const
|
||||
{
|
||||
int sx = coord_traits::less (box.right (), m_center.x ()) ? 0 : (coord_traits::less (m_center.x (), box.left ()) ? 1 : -1);
|
||||
int sy = coord_traits::less (box.top (), m_center.y ()) ? 0 : (coord_traits::less (m_center.y (), box.bottom ()) ? 2 : -1);
|
||||
if (sx < 0 || sy < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return sx + sy;
|
||||
}
|
||||
}
|
||||
|
||||
box_type box (const point_type &ucenter) const
|
||||
{
|
||||
return box_type (ucenter, ucenter - (ucenter - m_center) * 2.0);
|
||||
|
|
@ -185,33 +222,24 @@ private:
|
|||
}
|
||||
|
||||
box_type b = BC () (value);
|
||||
// @@@ should exclude m_center on box
|
||||
if (b.contains (m_center)) {
|
||||
int n = quad_for (b);
|
||||
|
||||
if (n < 0) {
|
||||
m_objects.push_back (value);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
box_type bq = q (i, ucenter);
|
||||
if (b.inside (bq)) {
|
||||
if (! m_q[i]) {
|
||||
m_q[i] = new quad_tree_node (bq.center ());
|
||||
}
|
||||
m_q[i]->insert (value, m_center);
|
||||
return;
|
||||
if (b.inside (box (ucenter))) {
|
||||
if (! m_q[n]) {
|
||||
box_type bq = q (n, ucenter);
|
||||
m_q[n] = new quad_tree_node (bq.center ());
|
||||
}
|
||||
m_q[n]->insert (value, m_center);
|
||||
} else {
|
||||
grow (m_center - (m_center - ucenter) * 2.0);
|
||||
insert (value, ucenter);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (m_q[i]) {
|
||||
grow (m_center - (m_center - m_q[i]->center ()) * 2.0);
|
||||
insert (value, ucenter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tl_assert (false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +266,61 @@ private:
|
|||
coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ()));
|
||||
return m_center - vector_type (dx, dy);
|
||||
}
|
||||
|
||||
bool check (const point_type &ucenter) const
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
box_type bq = box (ucenter);
|
||||
|
||||
for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
|
||||
box_type b = BC () (*i);
|
||||
if (! b.inside (bq)) {
|
||||
tl::error << "Box " << b.to_string () << " not inside quad box " << bq.to_string ();
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_split) {
|
||||
|
||||
for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
|
||||
box_type b = BC () (*i);
|
||||
int n = quad_for (b);
|
||||
if (n >= 0) {
|
||||
tl::error << "Box " << b.to_string () << " on quad level not overlapping multiple quads";
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n]) {
|
||||
m_q[n]->check (m_center);
|
||||
box_type bbq = m_q[n]->box (m_center);
|
||||
if (bbq != q (n, ucenter)) {
|
||||
tl::error << "Quad not centered (quad box is " << bbq.to_string () << ", should be " << q (n, ucenter).to_string ();
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (m_objects.size () > thr) {
|
||||
tl::error << "Non-split object count exceeds threshold " << m_objects.size () << " > " << thr;
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (unsigned int n = 0; n < 4; ++n) {
|
||||
if (m_q[n]) {
|
||||
tl::error << "Non-split node has child nodes";
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class BC, size_t thr, class S>
|
||||
|
|
@ -315,9 +398,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
while (! m_stack.empty ()) {
|
||||
m_stack.pop_back ();
|
||||
|
||||
m_stack.pop_back ();
|
||||
while (! m_stack.empty ()) {
|
||||
|
||||
int &n = m_stack.back ().second;
|
||||
while (++n < 4) {
|
||||
|
|
@ -329,6 +412,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
m_stack.pop_back ();
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -415,6 +500,7 @@ private:
|
|||
box_type m_box;
|
||||
};
|
||||
|
||||
// @@@ TODO: copy, assignment, move, swap
|
||||
template <class T, class BC, size_t thr>
|
||||
class quad_tree
|
||||
{
|
||||
|
|
@ -445,6 +531,16 @@ public:
|
|||
return m_root.size ();
|
||||
}
|
||||
|
||||
size_t levels () const
|
||||
{
|
||||
return m_root.levels ();
|
||||
}
|
||||
|
||||
bool check () const
|
||||
{
|
||||
return m_root.check_top (m_total_box);
|
||||
}
|
||||
|
||||
void insert (const T &value)
|
||||
{
|
||||
box_type b = BC () (value);
|
||||
|
|
@ -456,9 +552,9 @@ public:
|
|||
m_root.insert_top (value, m_total_box);
|
||||
}
|
||||
|
||||
void erase (const T &value)
|
||||
bool erase (const T &value)
|
||||
{
|
||||
m_root.remove (value);
|
||||
return m_root.erase (value);
|
||||
}
|
||||
|
||||
quad_tree_flat_iterator begin () const
|
||||
|
|
|
|||
|
|
@ -28,6 +28,18 @@
|
|||
|
||||
typedef db::quad_tree<db::DBox, db::box_convert<db::DBox>, size_t (1)> my_quad_tree;
|
||||
|
||||
std::string find_all (const my_quad_tree &qt)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin ();
|
||||
while (! i.at_end ()) {
|
||||
v.push_back (i->to_string ());
|
||||
++i;
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_touching (const my_quad_tree &qt, const db::DBox &box)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
|
@ -40,6 +52,20 @@ std::string find_touching (const my_quad_tree &qt, const db::DBox &box)
|
|||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_touching_from_all (const my_quad_tree &qt, const db::DBox &box)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin ();
|
||||
while (! i.at_end ()) {
|
||||
if (i->touches (box)) {
|
||||
v.push_back (i->to_string ());
|
||||
}
|
||||
++i;
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
|
@ -52,26 +78,181 @@ std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box)
|
|||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
std::string find_overlapping_from_all (const my_quad_tree &qt, const db::DBox &box)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
auto i = qt.begin ();
|
||||
while (! i.at_end ()) {
|
||||
if (i->overlaps (box)) {
|
||||
v.push_back (i->to_string ());
|
||||
}
|
||||
++i;
|
||||
}
|
||||
std::sort (v.begin (), v.end ());
|
||||
return tl::join (v, "/");
|
||||
}
|
||||
|
||||
TEST(basic)
|
||||
{
|
||||
my_quad_tree tree;
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
tree.insert (db::DBox ());
|
||||
EXPECT_EQ (tree.empty (), true);
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (1));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (find_touching (tree, db::DBox (-2, 0, -1, 0)), "...");
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)");
|
||||
|
||||
db::DBox bx;
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (1));
|
||||
EXPECT_EQ (tree.size (), size_t (2));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
|
||||
EXPECT_EQ (find_touching (tree, db::DBox (-2, 0, -1, 0)), "...");
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;3,0)");
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (3));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (2));
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;3,0)");
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
EXPECT_EQ (tree.empty (), false);
|
||||
EXPECT_EQ (tree.size (), size_t (4));
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
EXPECT_EQ (tree.levels (), size_t (2));
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
bx = db::DBox (-2, 0, -1, 0);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, -3, -1, -2.5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 4.5, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
bx = db::DBox (-2, 3, -1.5, 5);
|
||||
EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx));
|
||||
EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx));
|
||||
}
|
||||
|
||||
TEST(remove)
|
||||
{
|
||||
my_quad_tree tree;
|
||||
tree.insert (db::DBox (-1, -2, 3, 4));
|
||||
tree.insert (db::DBox (-1, -3, 3, 0));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, -2));
|
||||
tree.insert (db::DBox (-1, -3, -0.5, 2));
|
||||
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
EXPECT_EQ (tree.erase (db::DBox (-1, -3, -0.5, -1)), false);
|
||||
EXPECT_EQ (tree.erase (db::DBox (-1, -3, -0.5, -2)), true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
|
||||
EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,2)/(-1,-3;3,0)");
|
||||
|
||||
while (! tree.empty ()) {
|
||||
EXPECT_EQ (tree.erase (*tree.begin ()), true);
|
||||
EXPECT_EQ (tree.check (), true);
|
||||
}
|
||||
|
||||
EXPECT_EQ (tree.size (), size_t (0));
|
||||
EXPECT_EQ (tree.levels (), size_t (1));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue