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
This commit is contained in:
parent
d22c4a5ac9
commit
a393271f04
|
|
@ -40,10 +40,16 @@ class NAZARA_API NzGuillotineBinPack
|
||||||
|
|
||||||
NzGuillotineBinPack();
|
NzGuillotineBinPack();
|
||||||
NzGuillotineBinPack(unsigned int width, unsigned int height);
|
NzGuillotineBinPack(unsigned int width, unsigned int height);
|
||||||
|
NzGuillotineBinPack(const NzVector2ui& size);
|
||||||
|
NzGuillotineBinPack(const NzGuillotineBinPack&) = default;
|
||||||
|
NzGuillotineBinPack(NzGuillotineBinPack&&) = default;
|
||||||
~NzGuillotineBinPack() = default;
|
~NzGuillotineBinPack() = default;
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
void Expand(unsigned int newWidth, unsigned newHeight);
|
||||||
|
void Expand(const NzVector2ui& newSize);
|
||||||
|
|
||||||
void FreeRectangle(const NzRectui& rect);
|
void FreeRectangle(const NzRectui& rect);
|
||||||
|
|
||||||
unsigned int GetHeight() const;
|
unsigned int GetHeight() const;
|
||||||
|
|
@ -51,12 +57,18 @@ class NAZARA_API NzGuillotineBinPack
|
||||||
NzVector2ui GetSize() const;
|
NzVector2ui GetSize() const;
|
||||||
unsigned int GetWidth() 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, 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();
|
bool MergeFreeRectangles();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
void Reset(unsigned int width, unsigned int height);
|
void Reset(unsigned int width, unsigned int height);
|
||||||
|
void Reset(const NzVector2ui& size);
|
||||||
|
|
||||||
|
NzGuillotineBinPack& operator=(const NzGuillotineBinPack&) = default;
|
||||||
|
NzGuillotineBinPack& operator=(NzGuillotineBinPack&&) = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SplitFreeRectAlongAxis(const NzRectui& freeRect, const NzRectui& placedRect, bool splitHorizontal);
|
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);
|
static int ScoreByHeuristic(int width, int height, const NzRectui& freeRect, FreeRectChoiceHeuristic rectChoice);
|
||||||
|
|
||||||
std::vector<NzRectui> m_freeRectangles;
|
std::vector<NzRectui> m_freeRectangles;
|
||||||
float m_occupancy;
|
|
||||||
unsigned int m_height;
|
unsigned int m_height;
|
||||||
|
unsigned int m_usedArea;
|
||||||
unsigned int m_width;
|
unsigned int m_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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 <Nazara/Core/GuillotineBinPack.hpp>
|
#include <Nazara/Core/GuillotineBinPack.hpp>
|
||||||
|
#include <Nazara/Core/Config.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
@ -63,12 +64,40 @@ NzGuillotineBinPack::NzGuillotineBinPack(unsigned int width, unsigned int height
|
||||||
Reset(width, height);
|
Reset(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NzGuillotineBinPack::NzGuillotineBinPack(const NzVector2ui& size)
|
||||||
|
{
|
||||||
|
Reset(size);
|
||||||
|
}
|
||||||
|
|
||||||
void NzGuillotineBinPack::Clear()
|
void NzGuillotineBinPack::Clear()
|
||||||
{
|
{
|
||||||
m_freeRectangles.clear();
|
m_freeRectangles.clear();
|
||||||
m_freeRectangles.push_back(NzRectui(0, 0, m_width, m_height));
|
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)
|
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
|
///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_freeRectangles.push_back(rect);
|
||||||
|
|
||||||
m_occupancy -= static_cast<float>(rect.width * rect.height) / (m_width*m_height);
|
m_usedArea -= rect.width * rect.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzGuillotineBinPack::GetHeight() const
|
unsigned int NzGuillotineBinPack::GetHeight() const
|
||||||
|
|
@ -86,7 +115,7 @@ unsigned int NzGuillotineBinPack::GetHeight() const
|
||||||
|
|
||||||
float NzGuillotineBinPack::GetOccupancy() const
|
float NzGuillotineBinPack::GetOccupancy() const
|
||||||
{
|
{
|
||||||
return m_occupancy;
|
return static_cast<float>(m_usedArea)/(m_width*m_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
NzVector2ui NzGuillotineBinPack::GetSize() const
|
NzVector2ui NzGuillotineBinPack::GetSize() const
|
||||||
|
|
@ -99,7 +128,17 @@ unsigned int NzGuillotineBinPack::GetWidth() const
|
||||||
return m_width;
|
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)
|
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<NzRectui*> remainingRects(count); // La position du rectangle
|
std::vector<NzRectui*> remainingRects(count); // La position du rectangle
|
||||||
for (unsigned int i = 0; i < count; ++i)
|
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 we didn't manage to find any rectangle to pack, abort.
|
||||||
if (bestScore == std::numeric_limits<int>::max())
|
if (bestScore == std::numeric_limits<int>::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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise, we're good to go and do the actual packing.
|
// Otherwise, we're good to go and do the actual packing.
|
||||||
unsigned int position = remainingRects[bestRect] - rects;
|
unsigned int position = remainingRects[bestRect] - rects;
|
||||||
|
|
@ -185,6 +236,9 @@ bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, unsigned int co
|
||||||
if (flipped)
|
if (flipped)
|
||||||
flipped[position] = bestFlipped;
|
flipped[position] = bestFlipped;
|
||||||
|
|
||||||
|
if (inserted)
|
||||||
|
inserted[position] = true;
|
||||||
|
|
||||||
// Remove the free space we lost in the bin.
|
// Remove the free space we lost in the bin.
|
||||||
SplitFreeRectByHeuristic(m_freeRectangles[bestFreeRect], rect, splitMethod);
|
SplitFreeRectByHeuristic(m_freeRectangles[bestFreeRect], rect, splitMethod);
|
||||||
m_freeRectangles.erase(m_freeRectangles.begin() + bestFreeRect);
|
m_freeRectangles.erase(m_freeRectangles.begin() + bestFreeRect);
|
||||||
|
|
@ -196,7 +250,7 @@ bool NzGuillotineBinPack::Insert(NzRectui* rects, bool* flipped, unsigned int co
|
||||||
if (merge)
|
if (merge)
|
||||||
MergeFreeRectangles();
|
MergeFreeRectangles();
|
||||||
|
|
||||||
m_occupancy += static_cast<float>(rect.width * rect.height) / (m_width*m_height);
|
m_usedArea += rect.width * rect.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -271,6 +325,11 @@ void NzGuillotineBinPack::Reset(unsigned int width, unsigned int height)
|
||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NzGuillotineBinPack::Reset(const NzVector2ui& size)
|
||||||
|
{
|
||||||
|
Reset(size.x, size.y);
|
||||||
|
}
|
||||||
|
|
||||||
void NzGuillotineBinPack::SplitFreeRectAlongAxis(const NzRectui& freeRect, const NzRectui& placedRect, bool splitHorizontal)
|
void NzGuillotineBinPack::SplitFreeRectAlongAxis(const NzRectui& freeRect, const NzRectui& placedRect, bool splitHorizontal)
|
||||||
{
|
{
|
||||||
// Form the two new rectangles.
|
// Form the two new rectangles.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue