From a393271f0426f4e97cb2ae05e9ac47bb1bd0cead Mon Sep 17 00:00:00 2001 From: Lynix Date: Wed, 10 Dec 2014 14:38:32 +0100 Subject: [PATCH] Improved GuillotineBinPack Added explicit copy/move constructor/operator Added Expand method Added Insert overloads (you are now able to query which rectangles were inserted) Made occupancy computation more precise Overloaded methods taking a unsigned int pair with Vector2ui Former-commit-id: f063c04a1aea0d26594db642c2466264fe139450 --- include/Nazara/Core/GuillotineBinPack.hpp | 14 ++++- src/Nazara/Core/GuillotineBinPack.cpp | 67 +++++++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/include/Nazara/Core/GuillotineBinPack.hpp b/include/Nazara/Core/GuillotineBinPack.hpp index f0464a3f7..fecac3d00 100644 --- a/include/Nazara/Core/GuillotineBinPack.hpp +++ b/include/Nazara/Core/GuillotineBinPack.hpp @@ -40,10 +40,16 @@ class NAZARA_API NzGuillotineBinPack NzGuillotineBinPack(); NzGuillotineBinPack(unsigned int width, unsigned int height); + NzGuillotineBinPack(const NzVector2ui& size); + NzGuillotineBinPack(const NzGuillotineBinPack&) = default; + NzGuillotineBinPack(NzGuillotineBinPack&&) = default; ~NzGuillotineBinPack() = default; void Clear(); + void Expand(unsigned int newWidth, unsigned newHeight); + void Expand(const NzVector2ui& newSize); + void FreeRectangle(const NzRectui& rect); unsigned int GetHeight() const; @@ -51,12 +57,18 @@ class NAZARA_API NzGuillotineBinPack NzVector2ui GetSize() const; unsigned int GetWidth() const; + bool Insert(NzRectui* rects, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod); bool Insert(NzRectui* rects, bool* flipped, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod); + bool Insert(NzRectui* rects, bool* flipped, bool* inserted, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod); bool MergeFreeRectangles(); void Reset(); void Reset(unsigned int width, unsigned int height); + void Reset(const NzVector2ui& size); + + NzGuillotineBinPack& operator=(const NzGuillotineBinPack&) = default; + NzGuillotineBinPack& operator=(NzGuillotineBinPack&&) = default; private: void SplitFreeRectAlongAxis(const NzRectui& freeRect, const NzRectui& placedRect, bool splitHorizontal); @@ -65,8 +77,8 @@ class NAZARA_API NzGuillotineBinPack static int ScoreByHeuristic(int width, int height, const NzRectui& freeRect, FreeRectChoiceHeuristic rectChoice); std::vector m_freeRectangles; - float m_occupancy; unsigned int m_height; + unsigned int m_usedArea; unsigned int m_width; }; diff --git a/src/Nazara/Core/GuillotineBinPack.cpp b/src/Nazara/Core/GuillotineBinPack.cpp index 6cbae97aa..7a32f3ae1 100644 --- a/src/Nazara/Core/GuillotineBinPack.cpp +++ b/src/Nazara/Core/GuillotineBinPack.cpp @@ -7,6 +7,7 @@ // Je n'ai vraiment fait qu'adapter le code au moteur (Avec quelques améliorations), je n'ai aucun mérite sur le code ci-dessous #include +#include #include #include #include @@ -63,12 +64,40 @@ NzGuillotineBinPack::NzGuillotineBinPack(unsigned int width, unsigned int height Reset(width, height); } +NzGuillotineBinPack::NzGuillotineBinPack(const NzVector2ui& size) +{ + Reset(size); +} + void NzGuillotineBinPack::Clear() { m_freeRectangles.clear(); m_freeRectangles.push_back(NzRectui(0, 0, m_width, m_height)); - m_occupancy = 0.f; + m_usedArea = 0; +} + +void NzGuillotineBinPack::Expand(unsigned int newWidth, unsigned newHeight) +{ + unsigned int oldWidth = m_width; + unsigned int oldHeight = m_height; + + m_width = std::max(newWidth, m_width); + m_height = std::max(newHeight, m_height); + + if (m_width > oldWidth) + m_freeRectangles.push_back(NzRectui(oldWidth, 0, m_width - oldWidth, oldHeight)); + + if (m_height > oldHeight) + m_freeRectangles.push_back(NzRectui(0, oldHeight, m_width, m_height - oldHeight)); + + // On va ensuite fusionner les rectangles tant que possible + while (MergeFreeRectangles()); +} + +void NzGuillotineBinPack::Expand(const NzVector2ui& newSize) +{ + Expand(newSize.x, newSize.y); } void NzGuillotineBinPack::FreeRectangle(const NzRectui& rect) @@ -76,7 +105,7 @@ void NzGuillotineBinPack::FreeRectangle(const NzRectui& rect) ///DOC: Cette méthode ne devrait recevoir que des rectangles calculés par la méthode Insert et peut provoquer de la fragmentation m_freeRectangles.push_back(rect); - m_occupancy -= static_cast(rect.width * rect.height) / (m_width*m_height); + m_usedArea -= rect.width * rect.height; } unsigned int NzGuillotineBinPack::GetHeight() const @@ -86,7 +115,7 @@ unsigned int NzGuillotineBinPack::GetHeight() const float NzGuillotineBinPack::GetOccupancy() const { - return m_occupancy; + return static_cast(m_usedArea)/(m_width*m_height); } NzVector2ui NzGuillotineBinPack::GetSize() const @@ -99,7 +128,17 @@ unsigned int NzGuillotineBinPack::GetWidth() const return m_width; } +bool NzGuillotineBinPack::Insert(NzRectui* rects, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) +{ + return Insert(rects, nullptr, nullptr, count, merge, rectChoice, splitMethod); +} + bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) +{ + return Insert(rects, flipped, nullptr, count, merge, rectChoice, splitMethod); +} + +bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, bool* inserted, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { std::vector remainingRects(count); // La position du rectangle for (unsigned int i = 0; i < count; ++i) @@ -171,7 +210,19 @@ bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, unsigned int co // If we didn't manage to find any rectangle to pack, abort. if (bestScore == std::numeric_limits::max()) + { + // Si nous le pouvons, on marque les rectangles n'ayant pas pu être insérés + if (inserted) + { + for (NzRectui* rect : remainingRects) + { + unsigned int position = rect - rects; + inserted[position] = false; + } + } + return false; + } // Otherwise, we're good to go and do the actual packing. unsigned int position = remainingRects[bestRect] - rects; @@ -185,6 +236,9 @@ bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, unsigned int co if (flipped) flipped[position] = bestFlipped; + if (inserted) + inserted[position] = true; + // Remove the free space we lost in the bin. SplitFreeRectByHeuristic(m_freeRectangles[bestFreeRect], rect, splitMethod); m_freeRectangles.erase(m_freeRectangles.begin() + bestFreeRect); @@ -196,7 +250,7 @@ bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, unsigned int co if (merge) MergeFreeRectangles(); - m_occupancy += static_cast(rect.width * rect.height) / (m_width*m_height); + m_usedArea += rect.width * rect.height; } return true; @@ -271,6 +325,11 @@ void NzGuillotineBinPack::Reset(unsigned int width, unsigned int height) Clear(); } +void NzGuillotineBinPack::Reset(const NzVector2ui& size) +{ + Reset(size.x, size.y); +} + void NzGuillotineBinPack::SplitFreeRectAlongAxis(const NzRectui& freeRect, const NzRectui& placedRect, bool splitHorizontal) { // Form the two new rectangles.