Finished implementing & testing QTree. Implemented pickable marker class.

This commit is contained in:
Tiberiu Chibici 2016-12-14 00:34:53 +02:00
parent 6a35b11ec2
commit 1f6f51877a
6 changed files with 395 additions and 58 deletions

View File

@ -1,10 +1,42 @@
#include <controller/FarmlandsGame.h> #include <controller/FarmlandsGame.h>
#include <utils/Exceptions.h> #include <utils/Exceptions.h>
#include <iostream> #include <iostream>
using namespace farmlands; using namespace farmlands;
#include <utils/QTree.h>
void qtreeTest()
{
utils::RectF bounds(0, 0, 100, 100);
utils::QTree<std::string, 10> tree(bounds);
Assert(tree.empty(), "Tree should be empty.");
Assert(tree.size() == 0, "Tree should be empty.");
srand(10);
for (int i = 0; i < 100; i++)
{
tree.insert("Random", rand() % 100, rand() % 100);
}
Assert(!tree.empty(), "Tree should be empty.");
Assert(tree.size() == 100, "Tree should be empty.");
while (tree.size() > 2)
tree.erase(tree.begin());
utils::RectF search(-10, -10, 120, 120);
for (auto it = tree.lower_bound(search); it != tree.upper_bound(search); it++)
std::cout << it->data << ": " << it->x << ", " << it->y << "\n";
}
int main()
{
qtreeTest();
}
#if 0
int main() int main()
{ {
int ret = 0; int ret = 0;
@ -32,3 +64,5 @@ int main()
return ret; return ret;
} }
#endif

View File

@ -0,0 +1,49 @@
/*
* Pickable.h
*
* Created on: Dec 13, 2016
* Author: tibi
*/
#ifndef COMPONENTS_ITEMS_PICKABLE_H_
#define COMPONENTS_ITEMS_PICKABLE_H_
#include <model/Component.h>
#include <iostream>
namespace farmlands {
namespace components {
namespace items {
/**
* Marks object as pickable.
*
* The class is simply a marker class and doesn't do anything.
*/
class Pickable: public model::Component
{
public:
virtual ~Pickable() { }
virtual model::Component* clone() override;
virtual void dump(unsigned level) override;
};
inline model::Component* Pickable::clone()
{
return new Pickable();
}
inline void Pickable::dump(unsigned level)
{
for (unsigned i = 0; i < level; i++)
std::cout<<" ";
std::cout << " .Component: Pickable\n";
}
} /* namespace items */
} /* namespace components */
} /* namespace farmlands */
#endif /* COMPONENTS_ITEMS_PICKABLE_H_ */

View File

@ -359,6 +359,17 @@ components::items::Item* parse<components::items::Item> (boost::property_tree::p
} }
template <>
components::items::Pickable* parse<components::items::Pickable> (boost::property_tree::ptree& root)
{
// Ensure we are on the correct node (property tree seems to add root of its own)
if (root.size() > 0 && root.front().first == "Pickaxe")
root = root.front().second;
// Parse
return new components::items::Pickable();
}
template <> template <>
components::items::Pickaxe* parse<components::items::Pickaxe> (boost::property_tree::ptree& root) components::items::Pickaxe* parse<components::items::Pickaxe> (boost::property_tree::ptree& root)
{ {
@ -580,6 +591,9 @@ model::GameObject* parse<model::GameObject> (boost::property_tree::ptree& root)
else if (child.first == "Item") else if (child.first == "Item")
gameObj->addComponent(parse<components::items::Item>(child.second)); gameObj->addComponent(parse<components::items::Item>(child.second));
else if (child.first == "Pickable")
gameObj->addComponent(parse<components::items::Pickable>(child.second));
else if (child.first == "Pickaxe") else if (child.first == "Pickaxe")
gameObj->addComponent(parse<components::items::Pickaxe>(child.second)); gameObj->addComponent(parse<components::items::Pickaxe>(child.second));

View File

@ -21,6 +21,7 @@
#include <components/items/Giftable.h> #include <components/items/Giftable.h>
#include <components/items/Hoe.h> #include <components/items/Hoe.h>
#include <components/items/Item.h> #include <components/items/Item.h>
#include <components/items/Pickable.h>
#include <components/items/Pickaxe.h> #include <components/items/Pickaxe.h>
#include <components/items/Scythe.h> #include <components/items/Scythe.h>
#include <components/items/WateringCan.h> #include <components/items/WateringCan.h>
@ -91,6 +92,9 @@ namespace storage {
template <> template <>
components::items::Item* parse<components::items::Item> (boost::property_tree::ptree& root); components::items::Item* parse<components::items::Item> (boost::property_tree::ptree& root);
template <>
components::items::Pickable* parse<components::items::Pickable> (boost::property_tree::ptree& root);
template <> template <>
components::items::Pickaxe* parse<components::items::Pickaxe> (boost::property_tree::ptree& root); components::items::Pickaxe* parse<components::items::Pickaxe> (boost::property_tree::ptree& root);

View File

@ -15,9 +15,6 @@
namespace farmlands { namespace farmlands {
namespace utils { namespace utils {
#if 0
// Commented because no longer needed.
template <typename T> template <typename T>
struct QTreeItem struct QTreeItem
{ {
@ -25,6 +22,39 @@ namespace utils {
float x, y; float x, y;
}; };
template <typename T, size_t Capacity>
class QTree;
template <typename T, size_t Capacity>
class QTreeIterator
{
public:
QTreeIterator();
QTreeIterator(const QTreeIterator& other);
QTreeIterator(QTree<T, Capacity>* tree, const RectF& rect, size_t item = 0);
QTreeIterator& operator= (const QTreeIterator& other);
bool operator== (const QTreeIterator& other);
bool operator!= (const QTreeIterator& other);
QTreeItem<T>& operator*();
QTreeItem<T>* operator->();
QTreeIterator& operator++();
QTreeIterator operator++(int);
private:
void moveFirst();
void moveNext();
ssize_t moveUp(); // Returns index of child
RectF m_rect;
QTree<T, Capacity>* m_tree;
ssize_t m_item;
friend class QTree<T, Capacity>;
};
/** /**
* Quad tree * Quad tree
*/ */
@ -32,7 +62,7 @@ namespace utils {
class QTree : public INonAssignable class QTree : public INonAssignable
{ {
public: public:
typedef int iterator; typedef QTreeIterator<T, Capacity> iterator;
QTree(const RectF& bounds); QTree(const RectF& bounds);
virtual ~QTree(); virtual ~QTree();
@ -49,32 +79,190 @@ namespace utils {
void clear(); void clear();
iterator find(T element); iterator find(T element);
iterator find(float x, float y);
iterator lower_bound(float x, float y);
iterator lower_bound(float x, float y, float distance);
iterator lower_bound(const RectF& area); iterator lower_bound(const RectF& area);
iterator upper_bound(float x, float y);
iterator upper_bound(float x, float y, float distance);
iterator upper_bound(const RectF& area); iterator upper_bound(const RectF& area);
private: private:
void subdivide(); void subdivide();
void checkMerge();
void merge(); void merge();
RectF m_bounds; RectF m_bounds;
bool m_isSplit; bool m_isSplit;
QTree* m_children[4]; QTree* m_children[4];
QTreeItem m_items[Capacity]; QTree* m_parent;
QTreeItem<T> m_items[Capacity];
size_t m_itemsCount; size_t m_itemsCount;
friend class QTreeIterator<T, Capacity>;
}; };
/****** Iterator implementation ******/
template<typename T, size_t Capacity>
QTreeIterator<T, Capacity>::QTreeIterator()
: m_rect(),
m_tree(nullptr),
m_item(0)
{
}
template<typename T, size_t Capacity>
QTreeIterator<T, Capacity>::QTreeIterator(const QTreeIterator<T, Capacity>& other)
: m_rect(other.m_rect),
m_tree(other.m_tree),
m_item(other.m_item)
{
}
template<typename T, size_t Capacity>
QTreeIterator<T, Capacity>::QTreeIterator(QTree<T, Capacity>* tree, const RectF& rect, size_t item)
: m_rect(rect),
m_tree(tree),
m_item(item)
{
moveFirst();
}
template<typename T, size_t Capacity>
QTreeIterator<T, Capacity>& QTreeIterator<T, Capacity>::operator=(const QTreeIterator<T, Capacity>& other)
{
if (&other != this)
{
m_rect = other.m_rect;
m_tree = other.m_tree;
m_item = other.m_item;
}
return *this;
}
template<typename T, size_t Capacity>
bool QTreeIterator<T, Capacity>::operator==(const QTreeIterator<T, Capacity>& other)
{
bool equal = (m_rect == other.m_rect && m_tree == other.m_tree);
if (equal && m_tree != nullptr)
equal &= (m_item == other.m_item);
return equal;
}
template<typename T, size_t Capacity>
bool QTreeIterator<T, Capacity>::operator!=(const QTreeIterator<T, Capacity>& other)
{
return !operator==(other);
}
template<typename T, size_t Capacity>
QTreeItem<T>& QTreeIterator<T, Capacity>::operator*()
{
Assert(m_tree != nullptr, "Bad iterator.");
return m_tree->m_items[m_item];
}
template<typename T, size_t Capacity>
QTreeItem<T>* QTreeIterator<T, Capacity>::operator->()
{
Assert(m_tree != nullptr, "Bad iterator.");
return &m_tree->m_items[m_item];
}
template<typename T, size_t Capacity>
QTreeIterator<T, Capacity>& QTreeIterator<T, Capacity>::operator++()
{
moveNext();
return *this;
}
template<typename T, size_t Capacity>
QTreeIterator<T, Capacity> QTreeIterator<T, Capacity>::operator++(int)
{
auto old = *this;
moveNext();
return old;
}
template<typename T, size_t Capacity>
void QTreeIterator<T, Capacity>::moveFirst()
{
if (m_tree && m_tree->m_isSplit)
moveNext();
}
template<typename T, size_t Capacity>
void QTreeIterator<T, Capacity>::moveNext()
{
Assert(m_tree != nullptr, "Bad iterator");
bool done = false;
ssize_t childIndex = -1;
do
{
if (m_tree->m_isSplit)
{
// Move to next child
++childIndex;
// Reached end of current node?
if (childIndex >= 4)
{
childIndex = moveUp();
}
else if (m_rect.intersects(m_tree->m_children[childIndex]->m_bounds))
{
// Move down
m_tree = m_tree->m_children[childIndex];
m_item = -1;
childIndex = -1;
}
}
else
{
// Move on to next element
++m_item;
// Reached end of current node?
if (m_item >= m_tree->m_itemsCount)
{
childIndex = moveUp();
continue;
}
// Reached the end when there are no more nodes, or when we found a good item
done = m_rect.contains(m_tree->m_items[m_item].x, m_tree->m_items[m_item].y);
}
} while (!done && m_tree);
}
template<typename T, size_t Capacity>
ssize_t QTreeIterator<T, Capacity>::moveUp()
{
auto child = m_tree;
m_tree = m_tree->m_parent;
m_item = -1;
if (m_tree == nullptr)
return -1;
// Figure out which node was the child
for (ssize_t i = 0; i < 4; i++)
if (m_tree->m_children[i] == child)
return i;
return -1;
}
/****** Container implementation ******/
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
QTree<T, Capacity>::QTree(const RectF& bounds) QTree<T, Capacity>::QTree(const RectF& bounds)
: m_bounds(bounds), : m_bounds(bounds),
m_isSplit(false), m_isSplit(false),
m_children(), m_children(),
m_parent(nullptr),
m_items(), m_items(),
m_itemsCount(0) m_itemsCount(0)
{ {
@ -91,29 +279,40 @@ namespace utils {
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::begin() typename QTree<T, Capacity>::iterator QTree<T, Capacity>::begin()
{ {
return iterator(this, m_bounds);
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::end() typename QTree<T, Capacity>::iterator QTree<T, Capacity>::end()
{ {
return iterator(nullptr, m_bounds);
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline bool QTree<T, Capacity>::empty() const bool QTree<T, Capacity>::empty() const
{ {
return (m_itemsCount == 0); return !m_isSplit && (m_itemsCount == 0);
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline size_t QTree<T, Capacity>::size() const size_t QTree<T, Capacity>::size() const
{ {
if (m_isSplit)
{
size_t size = 0;
for (size_t i = 0; i < 4; i++)
size += m_children[i]->size();
return size;
}
return m_itemsCount; return m_itemsCount;
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline void QTree<T, Capacity>::insert(T element, float x, float y) void QTree<T, Capacity>::insert(T element, float x, float y)
{ {
Assert(m_bounds.contains(x, y), "Can't add element outside bounds."); Assert(m_bounds.contains(x, y), "Can't add element outside bounds.");
@ -141,18 +340,39 @@ namespace utils {
}; };
m_items[m_itemsCount] = item; m_items[m_itemsCount] = item;
}
m_itemsCount++; m_itemsCount++;
} }
template<typename T, size_t Capacity>
inline void QTree<T, Capacity>::erase(iterator it)
{
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline void QTree<T, Capacity>::clear() void QTree<T, Capacity>::erase(iterator it)
{
// Tell correct container to erase item
if (it.m_tree == this)
{
Assert(!m_isSplit, "Container modified. Cannot erase element.");
Assert(it.m_item < m_itemsCount, "Container modified. Cannot erase element.");
// Move another element in the erased position
if (it.m_item < m_itemsCount - 1)
{
m_items[it.m_item] = m_items[m_itemsCount - 1];
}
--m_itemsCount;
if (m_parent)
m_parent->checkMerge();
}
else
{
Assert(m_isSplit, "Container modified or bad iterator. Cannot erase element.");
it.m_tree->erase(it);
}
}
template<typename T, size_t Capacity>
void QTree<T, Capacity>::clear()
{ {
if (m_isSplit) if (m_isSplit)
{ {
@ -165,43 +385,25 @@ namespace utils {
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::find(T element) typename QTree<T, Capacity>::iterator QTree<T, Capacity>::find(T element)
{ {
for (auto it = begin(); it != end(); it++)
if (it->data == element)
return it;
return end();
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::find(float x, float y) typename QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(const RectF& area)
{ {
return iterator(this, area);
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(float x, float y) typename QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(const RectF& area)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(float x, float y, float distance)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::lower_bound(const RectF& area)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(float x, float y)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(float x, float y, float distance)
{
}
template<typename T, size_t Capacity>
inline QTree<T, Capacity>::iterator QTree<T, Capacity>::upper_bound(const RectF& area)
{ {
return iterator(nullptr, area);
} }
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
@ -222,7 +424,10 @@ namespace utils {
// Allocate subtrees // Allocate subtrees
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
{
m_children[i] = new QTree<T, Capacity>(rects[i]); m_children[i] = new QTree<T, Capacity>(rects[i]);
m_children[i]->m_parent = this;
}
// Set split flag // Set split flag
m_isSplit = true; m_isSplit = true;
@ -235,6 +440,25 @@ namespace utils {
insert(m_items[i].data, m_items[i].x, m_items[i].y); insert(m_items[i].data, m_items[i].x, m_items[i].y);
} }
template<typename T, size_t Capacity>
void QTree<T, Capacity>::checkMerge()
{
// The rules for merging are:
// - none of the children is split
// - the total number of elements is less than half the capacity
bool canMerge = true;
size_t accumSize = 0;
for (size_t i = 0; i < 4; i++)
{
canMerge = canMerge && (!m_children[i]->m_isSplit);
accumSize += m_children[i]->m_itemsCount;
}
if (canMerge && accumSize < Capacity / 2)
merge();
}
template<typename T, size_t Capacity> template<typename T, size_t Capacity>
void QTree<T, Capacity>::merge() void QTree<T, Capacity>::merge()
{ {
@ -248,18 +472,20 @@ namespace utils {
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
{ {
Assert(!m_children[i].m_isSplit, "Cannot merge if children are split!!!"); Assert(!m_children[i]->m_isSplit, "Cannot merge if children are split!!!");
for (size_t j = 0; j < m_children[i].m_itemsCount; j++) for (size_t j = 0; j < m_children[i]->m_itemsCount; j++)
insert(m_children[i].m_items[j].data, m_children[i].m_items[j].x, m_children[i].m_items[j].y); insert(m_children[i]->m_items[j].data, m_children[i]->m_items[j].x, m_children[i]->m_items[j].y);
delete m_children[i]; delete m_children[i];
m_children[i] = nullptr;
} }
} }
#endif
} /* namespace utils */ } /* namespace utils */
} /* namespace farmlands */ } /* namespace farmlands */
#endif /* UTILS_QTREE_H_ */ #endif /* UTILS_QTREE_H_ */

View File

@ -22,15 +22,15 @@ namespace utils {
Rect(T x, T y, T w, T h) Rect(T x, T y, T w, T h)
: x(x), y(y), w(w), h(h) { } : x(x), y(y), w(w), h(h) { }
bool contains(T px, T py) bool contains(T px, T py) const
{ {
bool containsX = (x <= px && px <= x + w); bool containsX = (x <= px && px <= x + w);
bool containsY = (y <= px && px <= x + w); bool containsY = (y <= py && py <= y + h);
return containsX && containsY; return containsX && containsY;
} }
bool intersects(const Rect<T> other) bool intersects(const Rect<T>& other) const
{ {
bool intersectsX = (x <= other.x && other.x <= x + w) || (other.x <= x && other.x + other.w >= x); bool intersectsX = (x <= other.x && other.x <= x + w) || (other.x <= x && other.x + other.w >= x);
bool intersectsY = (y <= other.y && other.y <= y + h) || (other.y <= y && other.y + other.h >= y); bool intersectsY = (y <= other.y && other.y <= y + h) || (other.y <= y && other.y + other.h >= y);
@ -38,6 +38,16 @@ namespace utils {
return intersectsX && intersectsY; return intersectsX && intersectsY;
} }
bool operator==(const Rect<T>& other) const
{
return (x == other.x) && (y == other.y) && (w == other.w) && (h == other.h);
}
bool operator!=(const Rect<T>& other) const
{
return !operator==(other);
}
// Values // Values
T x, y, w, h; T x, y, w, h;
}; };