Basic implementation of a QuadTree
This commit is contained in:
commit
98923cf849
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
bin/**
|
||||||
|
obj/**
|
||||||
|
*.sln
|
||||||
|
*.vcxproj
|
16
premake5.lua
Normal file
16
premake5.lua
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
workspace "DataStructures"
|
||||||
|
configurations { "Debug", "Release"}
|
||||||
|
|
||||||
|
project "DataStructures"
|
||||||
|
kind "ConsoleApp"
|
||||||
|
language "C++"
|
||||||
|
targetdir "bin/%{cfg.buildcfg}"
|
||||||
|
|
||||||
|
files{"src/**.cpp", "src/**.h"}
|
||||||
|
|
||||||
|
filter "configurations:Debug"
|
||||||
|
defines {"DEBUG" }
|
||||||
|
symbols "On"
|
||||||
|
filter "configurations:Release"
|
||||||
|
defines {"NDEBUG"}
|
||||||
|
optimize "On"
|
136
src/QuadTree.h
Normal file
136
src/QuadTree.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "coordinate.h"
|
||||||
|
|
||||||
|
class QuadTree {
|
||||||
|
public:
|
||||||
|
QuadTree (AABB _boundary) : boundary(_boundary), points(std::vector<XY>()) {}
|
||||||
|
|
||||||
|
// Insert a point into the quadtree
|
||||||
|
bool insert (XY p)
|
||||||
|
{
|
||||||
|
// Ignore this point if it is outside the bounds of this tree
|
||||||
|
if(!boundary.containsPoint(p)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is space in this quad tree and if it doenst have subdivisions add the object
|
||||||
|
if(points.size() < 4 && northWest == nullptr){
|
||||||
|
points.push_back(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise , subdivide and add the point to whichever node accepts it
|
||||||
|
if(northWest == nullptr)
|
||||||
|
{
|
||||||
|
subdivide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(northWest->insert(p)) return true;
|
||||||
|
if(northEast->insert(p)) return true;
|
||||||
|
if(southWest->insert(p)) return true;
|
||||||
|
if(southEast->insert(p)) return true;
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<XY> queryRange(AABB& range)
|
||||||
|
{
|
||||||
|
// prepare a list of results
|
||||||
|
auto pointsInRange = std::vector<XY>();
|
||||||
|
|
||||||
|
// abort if the range does not intersect with this quad
|
||||||
|
if(!boundary.intersectsAABB(range))
|
||||||
|
return pointsInRange;
|
||||||
|
|
||||||
|
// check object at this level
|
||||||
|
for (unsigned int p = 0; p < points.size(); p++ )
|
||||||
|
{
|
||||||
|
if(range.containsPoint(points[p]))
|
||||||
|
pointsInRange.push_back(points[p]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit if there are no children
|
||||||
|
if(northWest == nullptr)
|
||||||
|
return pointsInRange;
|
||||||
|
|
||||||
|
// Otherwise, add the points from the children
|
||||||
|
auto points = northWest->queryRange(range);
|
||||||
|
for ( auto point : points){
|
||||||
|
pointsInRange.push_back(point);
|
||||||
|
}
|
||||||
|
points = northEast->queryRange(range);
|
||||||
|
for(auto point : points){
|
||||||
|
pointsInRange.push_back(point);
|
||||||
|
}
|
||||||
|
points = southWest->queryRange(range);
|
||||||
|
for( auto point : points){
|
||||||
|
pointsInRange.push_back(point);
|
||||||
|
}
|
||||||
|
points = southEast->queryRange(range);
|
||||||
|
for(auto point : points){
|
||||||
|
pointsInRange.push_back(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointsInRange;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void print(int indent = 0) {
|
||||||
|
if( indent == 0)
|
||||||
|
std::cout << "==== Printing QuadTree ====" << std::endl;
|
||||||
|
|
||||||
|
for ( int i = 0; i < indent; i++)
|
||||||
|
std::cout << "*";
|
||||||
|
std::cout << "* " << boundary << std::endl;
|
||||||
|
|
||||||
|
// list points
|
||||||
|
for( auto point : points ){
|
||||||
|
std::cout << "| " << point << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(northWest == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
northWest->print(indent +1);
|
||||||
|
northEast->print(indent +1);
|
||||||
|
southWest->print(indent +1);
|
||||||
|
southEast->print(indent +1);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
const int NODE_CAPACITY = 4;
|
||||||
|
|
||||||
|
// Bounding box representing the boundaries of this
|
||||||
|
// quad tree;
|
||||||
|
AABB boundary ;
|
||||||
|
|
||||||
|
|
||||||
|
// Points in this tree
|
||||||
|
std::vector<XY> points;
|
||||||
|
|
||||||
|
QuadTree* northWest = nullptr;
|
||||||
|
QuadTree* northEast = nullptr;
|
||||||
|
QuadTree* southWest = nullptr;
|
||||||
|
QuadTree* southEast = nullptr;
|
||||||
|
|
||||||
|
// Create four children that fully divide this quad into four quads of equal area
|
||||||
|
void subdivide()
|
||||||
|
{
|
||||||
|
float Quarter_halfDimension = boundary.halfDimension / 4;
|
||||||
|
float x = boundary.center.x;
|
||||||
|
float y = boundary.center.y;
|
||||||
|
northWest = new QuadTree(AABB({x - (boundary.halfDimension / 2), y + (boundary.halfDimension / 2)}, Quarter_halfDimension));
|
||||||
|
southWest = new QuadTree(AABB({x - (boundary.halfDimension / 2), y - (boundary.halfDimension / 2) }, Quarter_halfDimension ));
|
||||||
|
northEast = new QuadTree(AABB({x + (boundary.halfDimension / 2), y + (boundary.halfDimension / 2) }, Quarter_halfDimension ));
|
||||||
|
southEast = new QuadTree(AABB({x + (boundary.halfDimension / 2), y - (boundary.halfDimension / 2) }, Quarter_halfDimension ));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
52
src/coordinate.h
Normal file
52
src/coordinate.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
struct XY {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
|
||||||
|
XY() = default;
|
||||||
|
XY(float _x , float _y): x(_x) , y(_y){}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<< (std::ostream& stream, const XY& point){
|
||||||
|
return stream << "(x: " << point.x << ", y:" << point.y << " )" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct AABB{
|
||||||
|
XY center;
|
||||||
|
float halfDimension;
|
||||||
|
|
||||||
|
AABB () = default;
|
||||||
|
AABB (XY center, float halfDimension) : center(center), halfDimension(halfDimension) { }
|
||||||
|
|
||||||
|
bool containsPoint (XY point )
|
||||||
|
{
|
||||||
|
|
||||||
|
if( point.x > (center.x + halfDimension))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(point.x < (center.x - halfDimension))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (point.y > (center.y + halfDimension))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (point.y < (center.y - halfDimension))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool intersectsAABB(const AABB& other){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator<< (std::ostream& stream, const AABB& bounds ){
|
||||||
|
return stream << "[center "<< bounds.center << ", half-dimension: " << bounds.halfDimension << " ]" ;
|
||||||
|
}
|
50
src/main.cpp
Normal file
50
src/main.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "QuadTree.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main (){
|
||||||
|
auto boundary = AABB( XY(4,8), 4);
|
||||||
|
auto tree = QuadTree(boundary);
|
||||||
|
|
||||||
|
//tree.print();
|
||||||
|
|
||||||
|
std::cout << "inserting random points into tree !!" << std::endl;
|
||||||
|
|
||||||
|
if(!tree.insert({2,7}))
|
||||||
|
std::cout << "insert failed!" << std::endl;
|
||||||
|
|
||||||
|
if(!tree.insert({3,11}))
|
||||||
|
std::cout << "insert failed!" << std::endl;
|
||||||
|
|
||||||
|
if(!tree.insert({2,6}))
|
||||||
|
std::cout << "insert failed!" << std::endl;
|
||||||
|
|
||||||
|
if(!tree.insert({1,4}))
|
||||||
|
std::cout << "insert failed!" << std::endl;
|
||||||
|
|
||||||
|
// subdivision will now be necessary
|
||||||
|
|
||||||
|
if(!tree.insert({6,5}))
|
||||||
|
std::cout << "insert failed!" << std::endl;
|
||||||
|
|
||||||
|
if(!tree.insert({7,10}))
|
||||||
|
std::cout << "insert failed!" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "print tree " << std::endl;
|
||||||
|
tree.print();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "Get points within bounds " << std::endl;
|
||||||
|
auto pointInRange = tree.queryRange(AABB {XY{ 4, 6}, 2 });
|
||||||
|
for ( auto point : pointInRange ){
|
||||||
|
std::cout << point << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user