From 1f6f51877a470fe760246cee9ccf45acc5629915 Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Wed, 14 Dec 2016 00:34:53 +0200 Subject: [PATCH] Finished implementing & testing QTree. Implemented pickable marker class. --- src/Main.cpp | 36 +++- src/components/items/Pickable.h | 49 +++++ src/storage/Parsers.cpp | 14 ++ src/storage/Parsers.h | 4 + src/utils/QTree.h | 334 ++++++++++++++++++++++++++------ src/utils/Rect.h | 16 +- 6 files changed, 395 insertions(+), 58 deletions(-) create mode 100644 src/components/items/Pickable.h diff --git a/src/Main.cpp b/src/Main.cpp index 13a483a..fd2de2d 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,10 +1,42 @@ #include #include - #include using namespace farmlands; +#include + +void qtreeTest() +{ + utils::RectF bounds(0, 0, 100, 100); + utils::QTree 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 ret = 0; @@ -32,3 +64,5 @@ int main() return ret; } + +#endif diff --git a/src/components/items/Pickable.h b/src/components/items/Pickable.h new file mode 100644 index 0000000..039bbc2 --- /dev/null +++ b/src/components/items/Pickable.h @@ -0,0 +1,49 @@ +/* + * Pickable.h + * + * Created on: Dec 13, 2016 + * Author: tibi + */ + +#ifndef COMPONENTS_ITEMS_PICKABLE_H_ +#define COMPONENTS_ITEMS_PICKABLE_H_ + +#include + +#include + +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_ */ diff --git a/src/storage/Parsers.cpp b/src/storage/Parsers.cpp index 5f7e32c..932404f 100644 --- a/src/storage/Parsers.cpp +++ b/src/storage/Parsers.cpp @@ -359,6 +359,17 @@ components::items::Item* parse (boost::property_tree::p } +template <> +components::items::Pickable* parse (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 <> components::items::Pickaxe* parse (boost::property_tree::ptree& root) { @@ -580,6 +591,9 @@ model::GameObject* parse (boost::property_tree::ptree& root) else if (child.first == "Item") gameObj->addComponent(parse(child.second)); + else if (child.first == "Pickable") + gameObj->addComponent(parse(child.second)); + else if (child.first == "Pickaxe") gameObj->addComponent(parse(child.second)); diff --git a/src/storage/Parsers.h b/src/storage/Parsers.h index 5b49b20..b35869a 100644 --- a/src/storage/Parsers.h +++ b/src/storage/Parsers.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,9 @@ namespace storage { template <> components::items::Item* parse (boost::property_tree::ptree& root); + template <> + components::items::Pickable* parse (boost::property_tree::ptree& root); + template <> components::items::Pickaxe* parse (boost::property_tree::ptree& root); diff --git a/src/utils/QTree.h b/src/utils/QTree.h index c170657..f4a6eb7 100644 --- a/src/utils/QTree.h +++ b/src/utils/QTree.h @@ -15,9 +15,6 @@ namespace farmlands { namespace utils { -#if 0 - // Commented because no longer needed. - template struct QTreeItem { @@ -25,6 +22,39 @@ namespace utils { float x, y; }; + template + class QTree; + + template + class QTreeIterator + { + public: + QTreeIterator(); + QTreeIterator(const QTreeIterator& other); + QTreeIterator(QTree* tree, const RectF& rect, size_t item = 0); + QTreeIterator& operator= (const QTreeIterator& other); + + bool operator== (const QTreeIterator& other); + bool operator!= (const QTreeIterator& other); + + QTreeItem& operator*(); + QTreeItem* operator->(); + + QTreeIterator& operator++(); + QTreeIterator operator++(int); + + private: + void moveFirst(); + void moveNext(); + ssize_t moveUp(); // Returns index of child + + RectF m_rect; + QTree* m_tree; + ssize_t m_item; + + friend class QTree; + }; + /** * Quad tree */ @@ -32,7 +62,7 @@ namespace utils { class QTree : public INonAssignable { public: - typedef int iterator; + typedef QTreeIterator iterator; QTree(const RectF& bounds); virtual ~QTree(); @@ -49,32 +79,190 @@ namespace utils { void clear(); 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 upper_bound(float x, float y); - iterator upper_bound(float x, float y, float distance); iterator upper_bound(const RectF& area); private: void subdivide(); + void checkMerge(); void merge(); RectF m_bounds; bool m_isSplit; QTree* m_children[4]; - QTreeItem m_items[Capacity]; + QTree* m_parent; + QTreeItem m_items[Capacity]; size_t m_itemsCount; + + friend class QTreeIterator; }; + +/****** Iterator implementation ******/ + + template + QTreeIterator::QTreeIterator() + : m_rect(), + m_tree(nullptr), + m_item(0) + { + } + + template + QTreeIterator::QTreeIterator(const QTreeIterator& other) + : m_rect(other.m_rect), + m_tree(other.m_tree), + m_item(other.m_item) + { + } + + template + QTreeIterator::QTreeIterator(QTree* tree, const RectF& rect, size_t item) + : m_rect(rect), + m_tree(tree), + m_item(item) + { + moveFirst(); + } + + template + QTreeIterator& QTreeIterator::operator=(const QTreeIterator& other) + { + if (&other != this) + { + m_rect = other.m_rect; + m_tree = other.m_tree; + m_item = other.m_item; + } + return *this; + } + + template + bool QTreeIterator::operator==(const QTreeIterator& 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 + bool QTreeIterator::operator!=(const QTreeIterator& other) + { + return !operator==(other); + } + + template + QTreeItem& QTreeIterator::operator*() + { + Assert(m_tree != nullptr, "Bad iterator."); + return m_tree->m_items[m_item]; + } + + template + QTreeItem* QTreeIterator::operator->() + { + Assert(m_tree != nullptr, "Bad iterator."); + return &m_tree->m_items[m_item]; + } + + template + QTreeIterator& QTreeIterator::operator++() + { + moveNext(); + return *this; + } + + template + QTreeIterator QTreeIterator::operator++(int) + { + auto old = *this; + moveNext(); + return old; + } + + template + void QTreeIterator::moveFirst() + { + if (m_tree && m_tree->m_isSplit) + moveNext(); + } + + template + void QTreeIterator::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 + ssize_t QTreeIterator::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 QTree::QTree(const RectF& bounds) : m_bounds(bounds), m_isSplit(false), m_children(), + m_parent(nullptr), m_items(), m_itemsCount(0) { @@ -91,29 +279,40 @@ namespace utils { } template - inline QTree::iterator QTree::begin() + typename QTree::iterator QTree::begin() { + return iterator(this, m_bounds); } template - inline QTree::iterator QTree::end() + typename QTree::iterator QTree::end() { + return iterator(nullptr, m_bounds); } template - inline bool QTree::empty() const + bool QTree::empty() const { - return (m_itemsCount == 0); + return !m_isSplit && (m_itemsCount == 0); } template - inline size_t QTree::size() const + size_t QTree::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; } template - inline void QTree::insert(T element, float x, float y) + void QTree::insert(T element, float x, float y) { Assert(m_bounds.contains(x, y), "Can't add element outside bounds."); @@ -141,18 +340,39 @@ namespace utils { }; m_items[m_itemsCount] = item; + m_itemsCount++; } - - m_itemsCount++; } template - inline void QTree::erase(iterator it) + void QTree::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 - inline void QTree::clear() + void QTree::clear() { if (m_isSplit) { @@ -165,43 +385,25 @@ namespace utils { } template - inline QTree::iterator QTree::find(T element) + typename QTree::iterator QTree::find(T element) { + for (auto it = begin(); it != end(); it++) + if (it->data == element) + return it; + + return end(); } template - inline QTree::iterator QTree::find(float x, float y) + typename QTree::iterator QTree::lower_bound(const RectF& area) { + return iterator(this, area); } template - inline QTree::iterator QTree::lower_bound(float x, float y) - { - } - - template - inline QTree::iterator QTree::lower_bound(float x, float y, float distance) - { - } - - template - inline QTree::iterator QTree::lower_bound(const RectF& area) - { - } - - template - inline QTree::iterator QTree::upper_bound(float x, float y) - { - } - - template - inline QTree::iterator QTree::upper_bound(float x, float y, float distance) - { - } - - template - inline QTree::iterator QTree::upper_bound(const RectF& area) + typename QTree::iterator QTree::upper_bound(const RectF& area) { + return iterator(nullptr, area); } template @@ -222,7 +424,10 @@ namespace utils { // Allocate subtrees for (size_t i = 0; i < 4; i++) + { m_children[i] = new QTree(rects[i]); + m_children[i]->m_parent = this; + } // Set split flag m_isSplit = true; @@ -235,6 +440,25 @@ namespace utils { insert(m_items[i].data, m_items[i].x, m_items[i].y); } + template + void QTree::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 void QTree::merge() { @@ -248,18 +472,20 @@ namespace utils { 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++) - insert(m_children[i].m_items[j].data, m_children[i].m_items[j].x, m_children[i].m_items[j].y); + 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); delete m_children[i]; + m_children[i] = nullptr; } } -#endif } /* namespace utils */ } /* namespace farmlands */ + + #endif /* UTILS_QTREE_H_ */ diff --git a/src/utils/Rect.h b/src/utils/Rect.h index bcc254c..78c1a2b 100644 --- a/src/utils/Rect.h +++ b/src/utils/Rect.h @@ -22,15 +22,15 @@ namespace utils { Rect(T x, T y, T w, T 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 containsY = (y <= px && px <= x + w); + bool containsY = (y <= py && py <= y + h); return containsX && containsY; } - bool intersects(const Rect other) + bool intersects(const Rect& other) const { 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); @@ -38,6 +38,16 @@ namespace utils { return intersectsX && intersectsY; } + bool operator==(const Rect& other) const + { + return (x == other.x) && (y == other.y) && (w == other.w) && (h == other.h); + } + + bool operator!=(const Rect& other) const + { + return !operator==(other); + } + // Values T x, y, w, h; };