From 98923cf8495ba11b48b53cb5bfda4bdcf8a789e1 Mon Sep 17 00:00:00 2001 From: Nigel Date: Fri, 10 Mar 2023 21:14:38 +0100 Subject: [PATCH] Basic implementation of a QuadTree --- .gitignore | 4 ++ premake5.lua | 16 ++++++ src/QuadTree.h | 136 +++++++++++++++++++++++++++++++++++++++++++++++ src/coordinate.h | 52 ++++++++++++++++++ src/main.cpp | 50 +++++++++++++++++ test.ps1 | 2 + 6 files changed, 260 insertions(+) create mode 100644 .gitignore create mode 100644 premake5.lua create mode 100644 src/QuadTree.h create mode 100644 src/coordinate.h create mode 100644 src/main.cpp create mode 100644 test.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e32445b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/** +obj/** +*.sln +*.vcxproj diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 0000000..8052544 --- /dev/null +++ b/premake5.lua @@ -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" \ No newline at end of file diff --git a/src/QuadTree.h b/src/QuadTree.h new file mode 100644 index 0000000..3c11892 --- /dev/null +++ b/src/QuadTree.h @@ -0,0 +1,136 @@ +#pragma once +#include +#include +#include "coordinate.h" + +class QuadTree { +public: + QuadTree (AABB _boundary) : boundary(_boundary), points(std::vector()) {} + + // 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 queryRange(AABB& range) + { + // prepare a list of results + auto pointsInRange = std::vector(); + + // 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 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 )); + } + + +}; \ No newline at end of file diff --git a/src/coordinate.h b/src/coordinate.h new file mode 100644 index 0000000..912ed69 --- /dev/null +++ b/src/coordinate.h @@ -0,0 +1,52 @@ +#pragma once +#include + +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 << " ]" ; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..db33046 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,50 @@ +#include +#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; + } + + + + + +} \ No newline at end of file diff --git a/test.ps1 b/test.ps1 new file mode 100644 index 0000000..47974d0 --- /dev/null +++ b/test.ps1 @@ -0,0 +1,2 @@ +msbuild .\DataStructures.sln +./bin/Debug/DataStructures.exe \ No newline at end of file