Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 11 additions & 37 deletions GuillotineBinPack.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/** @file GuillotineBinPack.cpp
@author Jukka Jyl�nki
@author Jukka Jyl�nki
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check to avoid garbling the UTF-8 encoding on the files?


@brief Implements different bin packer algorithms that use the GUILLOTINE data structure.

This work is released to Public Domain, do whatever you want with it.
*/
#include <algorithm>
#include <utility>
#include <iostream>
#include <limits>
Expand All @@ -19,25 +20,20 @@ namespace rbp {

using namespace std;

GuillotineBinPack::GuillotineBinPack()
:binWidth(0),
binHeight(0)
GuillotineBinPack::GuillotineBinPack() : binWidth(0), binHeight(0)
{
}

GuillotineBinPack::GuillotineBinPack(int width, int height)
GuillotineBinPack::GuillotineBinPack(int width, int height, bool allowFlip)
{
Init(width, height);
Init(width, height, allowFlip);
}

void GuillotineBinPack::Init(int width, int height)
void GuillotineBinPack::Init(int width, int height, bool allowFlip)
{
binWidth = width;
binHeight = height;

#ifdef _DEBUG
disjointRects.Clear();
#endif
binAllowFlip = allowFlip;

// Clear any memory of previously packed rectangles.
usedRectangles.clear();
Expand Down Expand Up @@ -83,7 +79,7 @@ void GuillotineBinPack::Insert(std::vector<RectSize> &rects, bool merge,
break;
}
// If flipping this rectangle is a perfect match, pick that then.
else if (rects[j].height == freeRectangles[i].width && rects[j].width == freeRectangles[i].height)
else if (binAllowFlip && rects[j].height == freeRectangles[i].width && rects[j].width == freeRectangles[i].height)
{
bestFreeRect = i;
bestRect = j;
Expand All @@ -105,7 +101,7 @@ void GuillotineBinPack::Insert(std::vector<RectSize> &rects, bool merge,
}
}
// If not, then perhaps flipping sideways will make it fit?
else if (rects[j].height <= freeRectangles[i].width && rects[j].width <= freeRectangles[i].height)
else if (binAllowFlip && rects[j].height <= freeRectangles[i].width && rects[j].width <= freeRectangles[i].height)
{
int score = ScoreByHeuristic(rects[j].height, rects[j].width, freeRectangles[i], rectChoice);
if (score < bestScore)
Expand Down Expand Up @@ -147,8 +143,6 @@ void GuillotineBinPack::Insert(std::vector<RectSize> &rects, bool merge,
// Remember the new used rectangle.
usedRectangles.push_back(newNode);

// Check that we're really producing correct packings here.
debug_assert(disjointRects.Add(newNode) == true);
}
}

Expand Down Expand Up @@ -358,9 +352,6 @@ Rect GuillotineBinPack::Insert(int width, int height, bool merge, FreeRectChoice
// Remember the new used rectangle.
usedRectangles.push_back(newRect);

// Check that we're really producing correct packings here.
debug_assert(disjointRects.Add(newRect) == true);

return newRect;
}

Expand Down Expand Up @@ -446,19 +437,17 @@ Rect GuillotineBinPack::FindPositionForNewNode(int width, int height, FreeRectCh
bestNode.height = height;
bestScore = std::numeric_limits<int>::min();
*nodeIndex = i;
debug_assert(disjointRects.Disjoint(bestNode));
break;
}
// If this is a perfect fit sideways, choose it.
else if (height == freeRectangles[i].width && width == freeRectangles[i].height)
else if (binAllowFlip && height == freeRectangles[i].width && width == freeRectangles[i].height)
{
bestNode.x = freeRectangles[i].x;
bestNode.y = freeRectangles[i].y;
bestNode.width = height;
bestNode.height = width;
bestScore = std::numeric_limits<int>::min();
*nodeIndex = i;
debug_assert(disjointRects.Disjoint(bestNode));
break;
}
// Does the rectangle fit upright?
Expand All @@ -474,11 +463,10 @@ Rect GuillotineBinPack::FindPositionForNewNode(int width, int height, FreeRectCh
bestNode.height = height;
bestScore = score;
*nodeIndex = i;
debug_assert(disjointRects.Disjoint(bestNode));
}
}
// Does the rectangle fit sideways?
else if (height <= freeRectangles[i].width && width <= freeRectangles[i].height)
else if (binAllowFlip && height <= freeRectangles[i].width && width <= freeRectangles[i].height)
{
int score = ScoreByHeuristic(height, width, freeRectangles[i], rectChoice);

Expand All @@ -490,7 +478,6 @@ Rect GuillotineBinPack::FindPositionForNewNode(int width, int height, FreeRectCh
bestNode.height = width;
bestScore = score;
*nodeIndex = i;
debug_assert(disjointRects.Disjoint(bestNode));
}
}
}
Expand Down Expand Up @@ -579,18 +566,10 @@ void GuillotineBinPack::SplitFreeRectAlongAxis(const Rect &freeRect, const Rect
if (right.width > 0 && right.height > 0)
freeRectangles.push_back(right);

debug_assert(disjointRects.Disjoint(bottom));
debug_assert(disjointRects.Disjoint(right));
}

void GuillotineBinPack::MergeFreeList()
{
#ifdef _DEBUG
DisjointRectCollection test;
for(size_t i = 0; i < freeRectangles.size(); ++i)
assert(test.Add(freeRectangles[i]) == true);
#endif

// Do a Theta(n^2) loop to see if any pair of free rectangles could me merged into one.
// Note that we miss any opportunities to merge three rectangles into one. (should call this function again to detect that)
for(size_t i = 0; i < freeRectangles.size(); ++i)
Expand Down Expand Up @@ -630,11 +609,6 @@ void GuillotineBinPack::MergeFreeList()
}
}

#ifdef _DEBUG
test.Clear();
for(size_t i = 0; i < freeRectangles.size(); ++i)
assert(test.Add(freeRectangles[i]) == true);
#endif
}

}
13 changes: 5 additions & 8 deletions GuillotineBinPack.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @file GuillotineBinPack.h
@author Jukka Jyl�nki
@author Jukka Jyl�nki

@brief Implements different bin packer algorithms that use the GUILLOTINE data structure.

Expand All @@ -22,11 +22,11 @@ class GuillotineBinPack
GuillotineBinPack();

/// Initializes a new bin of the given size.
GuillotineBinPack(int width, int height);
GuillotineBinPack(int width, int height, bool allowFlip = true);

/// (Re)initializes the packer to an empty bin of width x height units. Call whenever
/// you need to restart with a new bin.
void Init(int width, int height);
void Init(int width, int height, bool allowFlip = true);

/// Specifies the different choice heuristics that can be used when deciding which of the free subrectangles
/// to place the to-be-packed rectangle into.
Expand Down Expand Up @@ -92,18 +92,15 @@ class GuillotineBinPack
int binWidth;
int binHeight;

bool binAllowFlip;

/// Stores a list of all the rectangles that we have packed so far. This is used only to compute the Occupancy ratio,
/// so if you want to have the packer consume less memory, this can be removed.
std::vector<Rect> usedRectangles;

/// Stores a list of rectangles that represents the free area of the bin. This rectangles in this list are disjoint.
std::vector<Rect> freeRectangles;

#ifdef _DEBUG
/// Used to track that the packer produces proper packings.
DisjointRectCollection disjointRects;
#endif

/// Goes through the list of free rectangles and finds the best one to place a rectangle of given size into.
/// Running time is Theta(|freeRectangles|).
/// @param nodeIndex [out] The index of the free rectangle in the freeRectangles array into which the new
Expand Down