Basic implementation of a QuadTree

main
Nigel Barink 2023-03-10 21:14:38 +01:00
commit 98923cf849
6 changed files with 260 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
bin/**
obj/**
*.sln
*.vcxproj

16
premake5.lua Normal file
View 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
View 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
View 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
View 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;
}
}

2
test.ps1 Normal file
View File

@ -0,0 +1,2 @@
msbuild .\DataStructures.sln
./bin/Debug/DataStructures.exe