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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
2
test.ps1
Normal file
2
test.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
msbuild .\DataStructures.sln
|
||||
./bin/Debug/DataStructures.exe
|
Loading…
x
Reference in New Issue
Block a user