|
|
|
|
/*
|
|
|
|
|
Quirky Quad Trees Part #1 - Static Quad Tree Implementation
|
|
|
|
|
"War... huh... What is it good for? Absolutely nothin..." - javidx9
|
|
|
|
|
|
|
|
|
|
License (OLC-3)
|
|
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
|
Copyright 2018 - 2022 OneLoneCoder.com
|
|
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
|
are met:
|
|
|
|
|
|
|
|
|
|
1. Redistributions or derivations of source code must retain the above
|
|
|
|
|
copyright notice, this list of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
|
|
2. Redistributions or derivative works in binary form must reproduce
|
|
|
|
|
the above copyright notice. This list of conditions and the following
|
|
|
|
|
disclaimer must be reproduced in the documentation and/or other
|
|
|
|
|
materials provided with the distribution.
|
|
|
|
|
|
|
|
|
|
3. Neither the name of the copyright holder nor the names of its
|
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
Video:
|
|
|
|
|
~~~~~~
|
|
|
|
|
https://youtu.be/ASAowY6yJII
|
|
|
|
|
|
|
|
|
|
Pan & Zoom with middle mouse, TAB to switch between methods
|
|
|
|
|
|
|
|
|
|
Links
|
|
|
|
|
~~~~~
|
|
|
|
|
YouTube: https://www.youtube.com/javidx9
|
|
|
|
|
https://www.youtube.com/javidx9extra
|
|
|
|
|
Discord: https://discord.gg/WhwHUMV
|
|
|
|
|
Twitter: https://www.twitter.com/javidx9
|
|
|
|
|
Twitch: https://www.twitch.tv/javidx9
|
|
|
|
|
GitHub: https://www.github.com/onelonecoder
|
|
|
|
|
Homepage: https://www.onelonecoder.com
|
|
|
|
|
|
|
|
|
|
Author
|
|
|
|
|
~~~~~~
|
|
|
|
|
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2019, 2020, 2021, 2022
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define OLC_PGE_APPLICATION
|
|
|
|
|
#include "pixelGameEngine.h"
|
|
|
|
|
|
|
|
|
|
#define OLC_PGEX_TRANSFORMEDVIEW
|
|
|
|
|
#include "transformedView.h"
|
|
|
|
|
|
|
|
|
|
#include "quadtree.h"
|
|
|
|
|
|
|
|
|
|
// The Example!
|
|
|
|
|
class Example_StaticQuadTree : public olc::PixelGameEngine
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Example_StaticQuadTree()
|
|
|
|
|
{
|
|
|
|
|
sAppName = "Static QuadTree";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
olc::TransformedView tv;
|
|
|
|
|
|
|
|
|
|
// An example object of something in 2D space
|
|
|
|
|
struct SomeObjectWithArea
|
|
|
|
|
{
|
|
|
|
|
olc::vf2d vPos;
|
|
|
|
|
olc::vf2d vVel;
|
|
|
|
|
olc::vf2d vSize;
|
|
|
|
|
olc::Pixel colour;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A regular list of the objects
|
|
|
|
|
std::list<SomeObjectWithArea> vecObjects;
|
|
|
|
|
|
|
|
|
|
// An equivalent quad tree of the objects
|
|
|
|
|
olc::utils::QuadTreeContainer<SomeObjectWithArea> treeObjects;
|
|
|
|
|
|
|
|
|
|
// The "length" of one side of the "world" the objects reside in
|
|
|
|
|
float fArea = 100000.0f;
|
|
|
|
|
|
|
|
|
|
bool bUseQuadTree = true;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
bool OnUserCreate() override
|
|
|
|
|
{
|
|
|
|
|
// Transform View - enables Pan & Zoom
|
|
|
|
|
tv.Initialise({ ScreenWidth(), ScreenHeight() });
|
|
|
|
|
|
|
|
|
|
// Create the tree, and size it to the world
|
|
|
|
|
treeObjects.resize(olc::utils::geom2d::rect<float>({ 0.0f, 0.0f }, { fArea, fArea }));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Dirty random float generator
|
|
|
|
|
auto rand_float = [](const float a, const float b)
|
|
|
|
|
{
|
|
|
|
|
return float(rand()) / float(RAND_MAX) * (b - a) + a;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create 1,000,000 objects, push into both containers (so 2,000,000 I suppose :P )
|
|
|
|
|
for (int i = 0; i < 1000000; i++)
|
|
|
|
|
{
|
|
|
|
|
SomeObjectWithArea ob;
|
|
|
|
|
ob.vPos = { rand_float(0.0f, fArea), rand_float(0.0f, fArea) };
|
|
|
|
|
ob.vSize = { rand_float(0.1f, 100.0f), rand_float(0.1f, 100.0f) };
|
|
|
|
|
ob.colour = olc::Pixel(rand() % 256, rand() % 256, rand() % 256);
|
|
|
|
|
|
|
|
|
|
treeObjects.insert(ob, olc::utils::geom2d::rect(ob.vPos, ob.vSize));
|
|
|
|
|
vecObjects.push_back(ob);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool OnUserUpdate(float fElapsedTime) override
|
|
|
|
|
{
|
|
|
|
|
// Tab switches between modes
|
|
|
|
|
if (GetKey(olc::Key::TAB).bPressed)
|
|
|
|
|
bUseQuadTree = !bUseQuadTree;
|
|
|
|
|
|
|
|
|
|
tv.HandlePanAndZoom();
|
|
|
|
|
|
|
|
|
|
// Get rectangle that equates to screen in world space
|
|
|
|
|
olc::utils::geom2d::rect rScreen = { tv.GetWorldTL(), tv.GetWorldBR() - tv.GetWorldTL() };
|
|
|
|
|
size_t nObjectCount = 0;
|
|
|
|
|
|
|
|
|
|
if (bUseQuadTree)
|
|
|
|
|
{
|
|
|
|
|
// QUAD TREE MODE
|
|
|
|
|
auto tpStart = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
|
|
// Use search function to return list of pointers to objects in that area
|
|
|
|
|
for (const auto& object : treeObjects.search(rScreen))
|
|
|
|
|
{
|
|
|
|
|
tv.FillRectDecal(object->item.vPos, object->item.vSize, object->item.colour);
|
|
|
|
|
nObjectCount++;
|
|
|
|
|
}
|
|
|
|
|
std::chrono::duration<float> duration = std::chrono::system_clock::now() - tpStart;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string sOutput = "Quadtree " + std::to_string(nObjectCount) + "/" + std::to_string(vecObjects.size()) + " in " + std::to_string(duration.count());
|
|
|
|
|
DrawStringDecal({ 4, 4 }, sOutput, olc::BLACK, { 4.0f, 8.0f });
|
|
|
|
|
DrawStringDecal({ 2, 2 }, sOutput, olc::WHITE, { 4.0f, 8.0f });
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// LINEAR SEARCH MODE
|
|
|
|
|
auto tpStart = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
|
|
// Blindly check all objects to see if they overlap with screen
|
|
|
|
|
for (const auto& object : vecObjects)
|
|
|
|
|
{
|
|
|
|
|
if (olc::utils::geom2d::overlaps(rScreen,olc::utils::geom2d::rect<float>{ object.vPos, object.vSize }))
|
|
|
|
|
{
|
|
|
|
|
tv.FillRectDecal(object.vPos, object.vSize, object.colour);
|
|
|
|
|
nObjectCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
std::chrono::duration<float> duration = std::chrono::system_clock::now() - tpStart;
|
|
|
|
|
|
|
|
|
|
std::string sOutput = "Linear " + std::to_string(nObjectCount) + "/" + std::to_string(vecObjects.size()) + " in " + std::to_string(duration.count());
|
|
|
|
|
DrawStringDecal({ 4, 4 }, sOutput, olc::BLACK, { 4.0f, 8.0f });
|
|
|
|
|
DrawStringDecal({ 2, 2 }, sOutput, olc::WHITE, { 4.0f, 8.0f });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
|
{
|
|
|
|
|
Example_StaticQuadTree demo;
|
|
|
|
|
if (demo.Construct(1280, 960, 1, 1, false, false))
|
|
|
|
|
demo.Start();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|