WIP (quad tree)

This commit is contained in:
Matthias Koefferlein
2025-03-16 13:35:51 +01:00
parent f136fdcde6
commit 54b5d9f5d6
2 changed files with 310 additions and 33 deletions
+126 -30
View File
@@ -24,6 +24,7 @@
#define HDR_dbQuadTree #define HDR_dbQuadTree
#include "dbBox.h" #include "dbBox.h"
#include "tlLog.h"
#include <vector> #include <vector>
namespace db namespace db
@@ -38,6 +39,7 @@ public:
typedef db::point<coord_type> point_type; typedef db::point<coord_type> point_type;
typedef db::vector<coord_type> vector_type; typedef db::vector<coord_type> vector_type;
typedef std::vector<T> objects_vector; typedef std::vector<T> objects_vector;
typedef db::coord_traits<coord_type> coord_traits;
quad_tree_node (const point_type &center) quad_tree_node (const point_type &center)
: m_split (false), m_center (center) : m_split (false), m_center (center)
@@ -65,23 +67,31 @@ public:
insert (value, propose_ucenter (total_box)); insert (value, propose_ucenter (total_box));
} }
bool remove (const T &value) bool erase (const T &value)
{ {
box_type b = BC () (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) { for (auto i = m_objects.begin (); i != m_objects.end (); ++i) {
if (*i == value) { if (*i == value) {
m_objects.erase (i); m_objects.erase (i);
return true; return true;
} }
} }
}
for (unsigned int i = 0; i < 4; ++i) { } else if (m_q[n]) {
if (m_q[i] && b.inside (m_q[i]->box (m_center))) {
return b.remove (value); if (m_q[n]->erase (value)) {
if (m_q[n]->empty ()) {
delete m_q[n];
m_q[n] = 0;
}
return true;
} }
} }
return false; return false;
@@ -131,12 +141,39 @@ public:
return count; 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: private:
bool m_split; bool m_split;
point_type m_center; point_type m_center;
quad_tree_node *m_q [4]; quad_tree_node *m_q [4];
objects_vector m_objects; 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 box_type box (const point_type &ucenter) const
{ {
return box_type (ucenter, ucenter - (ucenter - m_center) * 2.0); return box_type (ucenter, ucenter - (ucenter - m_center) * 2.0);
@@ -185,33 +222,24 @@ private:
} }
box_type b = BC () (value); box_type b = BC () (value);
// @@@ should exclude m_center on box int n = quad_for (b);
if (b.contains (m_center)) {
if (n < 0) {
m_objects.push_back (value); m_objects.push_back (value);
return; return;
} }
for (unsigned int i = 0; i < 4; ++i) { if (b.inside (box (ucenter))) {
box_type bq = q (i, ucenter); if (! m_q[n]) {
if (b.inside (bq)) { box_type bq = q (n, ucenter);
if (! m_q[i]) { m_q[n] = new quad_tree_node (bq.center ());
m_q[i] = new quad_tree_node (bq.center ());
}
m_q[i]->insert (value, m_center);
return;
} }
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 ())); 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); 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> 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; int &n = m_stack.back ().second;
while (++n < 4) { while (++n < 4) {
@@ -329,6 +412,8 @@ public:
} }
} }
m_stack.pop_back ();
} }
} }
}; };
@@ -415,6 +500,7 @@ private:
box_type m_box; box_type m_box;
}; };
// @@@ TODO: copy, assignment, move, swap
template <class T, class BC, size_t thr> template <class T, class BC, size_t thr>
class quad_tree class quad_tree
{ {
@@ -445,6 +531,16 @@ public:
return m_root.size (); 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) void insert (const T &value)
{ {
box_type b = BC () (value); box_type b = BC () (value);
@@ -456,9 +552,9 @@ public:
m_root.insert_top (value, m_total_box); 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 quad_tree_flat_iterator begin () const
+184 -3
View File
@@ -28,6 +28,18 @@
typedef db::quad_tree<db::DBox, db::box_convert<db::DBox>, size_t (1)> my_quad_tree; 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::string find_touching (const my_quad_tree &qt, const db::DBox &box)
{ {
std::vector<std::string> v; 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, "/"); 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::string find_overlapping (const my_quad_tree &qt, const db::DBox &box)
{ {
std::vector<std::string> v; 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, "/"); 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) TEST(basic)
{ {
my_quad_tree tree; my_quad_tree tree;
EXPECT_EQ (tree.empty (), true); EXPECT_EQ (tree.empty (), true);
EXPECT_EQ (tree.size (), size_t (0)); EXPECT_EQ (tree.size (), size_t (0));
EXPECT_EQ (tree.check (), true);
EXPECT_EQ (tree.levels (), size_t (1));
tree.insert (db::DBox ()); tree.insert (db::DBox ());
EXPECT_EQ (tree.empty (), true); EXPECT_EQ (tree.empty (), true);
EXPECT_EQ (tree.size (), size_t (0)); 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)); tree.insert (db::DBox (-1, -2, 3, 4));
EXPECT_EQ (tree.empty (), false); EXPECT_EQ (tree.empty (), false);
EXPECT_EQ (tree.size (), size_t (1)); 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)); tree.insert (db::DBox (-1, -3, 3, 0));
EXPECT_EQ (tree.empty (), false); 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));
} }