157 lines
5.3 KiB
C++
157 lines
5.3 KiB
C++
|
|
#include "RectanglePacker.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace msdf_atlas {
|
|
|
|
#define WORST_FIT 0x7fffffff
|
|
|
|
template <typename T>
|
|
static void removeFromUnorderedVector(std::vector<T> &vector, size_t index) {
|
|
if (index != vector.size()-1)
|
|
std::swap(vector[index], vector.back());
|
|
vector.pop_back();
|
|
}
|
|
|
|
int RectanglePacker::rateFit(int w, int h, int sw, int sh) {
|
|
return std::min(sw-w, sh-h);
|
|
}
|
|
|
|
RectanglePacker::RectanglePacker() : RectanglePacker(0, 0) { }
|
|
|
|
RectanglePacker::RectanglePacker(int width, int height) {
|
|
if (width > 0 && height > 0)
|
|
spaces.push_back(Rectangle { 0, 0, width, height });
|
|
}
|
|
|
|
void RectanglePacker::expand(int width, int height) {
|
|
if (width > 0 && height > 0) {
|
|
int oldWidth = 0, oldHeight = 0;
|
|
for (const Rectangle &space : spaces) {
|
|
if (space.x+space.w > oldWidth)
|
|
oldWidth = space.x+space.w;
|
|
if (space.y+space.h > oldHeight)
|
|
oldHeight = space.y+space.h;
|
|
}
|
|
spaces.push_back(Rectangle { 0, 0, width, height });
|
|
splitSpace(int(spaces.size()-1), oldWidth, oldHeight);
|
|
}
|
|
}
|
|
|
|
void RectanglePacker::splitSpace(int index, int w, int h) {
|
|
Rectangle space = spaces[index];
|
|
removeFromUnorderedVector(spaces, index);
|
|
Rectangle a = { space.x, space.y+h, w, space.h-h };
|
|
Rectangle b = { space.x+w, space.y, space.w-w, h };
|
|
if (w*(space.h-h) < h*(space.w-w))
|
|
a.w = space.w;
|
|
else
|
|
b.h = space.h;
|
|
if (a.w > 0 && a.h > 0)
|
|
spaces.push_back(a);
|
|
if (b.w > 0 && b.h > 0)
|
|
spaces.push_back(b);
|
|
}
|
|
|
|
int RectanglePacker::pack(Rectangle *rectangles, int count) {
|
|
std::vector<int> remainingRects(count);
|
|
for (int i = 0; i < count; ++i)
|
|
remainingRects[i] = i;
|
|
while (!remainingRects.empty()) {
|
|
int bestFit = WORST_FIT;
|
|
int bestSpace = -1;
|
|
int bestRect = -1;
|
|
for (size_t i = 0; i < spaces.size(); ++i) {
|
|
const Rectangle &space = spaces[i];
|
|
for (size_t j = 0; j < remainingRects.size(); ++j) {
|
|
const Rectangle &rect = rectangles[remainingRects[j]];
|
|
if (rect.w == space.w && rect.h == space.h) {
|
|
bestSpace = i;
|
|
bestRect = j;
|
|
goto BEST_FIT_FOUND;
|
|
}
|
|
if (rect.w <= space.w && rect.h <= space.h) {
|
|
int fit = rateFit(rect.w, rect.h, space.w, space.h);
|
|
if (fit < bestFit) {
|
|
bestSpace = i;
|
|
bestRect = j;
|
|
bestFit = fit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bestSpace < 0 || bestRect < 0)
|
|
break;
|
|
BEST_FIT_FOUND:
|
|
Rectangle &rect = rectangles[remainingRects[bestRect]];
|
|
rect.x = spaces[bestSpace].x;
|
|
rect.y = spaces[bestSpace].y;
|
|
splitSpace(bestSpace, rect.w, rect.h);
|
|
removeFromUnorderedVector(remainingRects, bestRect);
|
|
}
|
|
return (int) remainingRects.size();
|
|
}
|
|
|
|
int RectanglePacker::pack(OrientedRectangle *rectangles, int count) {
|
|
std::vector<int> remainingRects(count);
|
|
for (int i = 0; i < count; ++i)
|
|
remainingRects[i] = i;
|
|
while (!remainingRects.empty()) {
|
|
int bestFit = WORST_FIT;
|
|
int bestSpace = -1;
|
|
int bestRect = -1;
|
|
bool bestRotated = false;
|
|
for (size_t i = 0; i < spaces.size(); ++i) {
|
|
const Rectangle &space = spaces[i];
|
|
for (size_t j = 0; j < remainingRects.size(); ++j) {
|
|
const OrientedRectangle &rect = rectangles[remainingRects[j]];
|
|
if (rect.w == space.w && rect.h == space.h) {
|
|
bestSpace = i;
|
|
bestRect = j;
|
|
bestRotated = false;
|
|
goto BEST_FIT_FOUND;
|
|
}
|
|
if (rect.h == space.w && rect.w == space.h) {
|
|
bestSpace = i;
|
|
bestRect = j;
|
|
bestRotated = true;
|
|
goto BEST_FIT_FOUND;
|
|
}
|
|
if (rect.w <= space.w && rect.h <= space.h) {
|
|
int fit = rateFit(rect.w, rect.h, space.w, space.h);
|
|
if (fit < bestFit) {
|
|
bestSpace = i;
|
|
bestRect = j;
|
|
bestRotated = false;
|
|
bestFit = fit;
|
|
}
|
|
}
|
|
if (rect.h <= space.w && rect.w <= space.h) {
|
|
int fit = rateFit(rect.h, rect.w, space.w, space.h);
|
|
if (fit < bestFit) {
|
|
bestSpace = i;
|
|
bestRect = j;
|
|
bestRotated = true;
|
|
bestFit = fit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bestSpace < 0 || bestRect < 0)
|
|
break;
|
|
BEST_FIT_FOUND:
|
|
OrientedRectangle &rect = rectangles[remainingRects[bestRect]];
|
|
rect.x = spaces[bestSpace].x;
|
|
rect.y = spaces[bestSpace].y;
|
|
rect.rotated = bestRotated;
|
|
if (bestRotated)
|
|
splitSpace(bestSpace, rect.h, rect.w);
|
|
else
|
|
splitSpace(bestSpace, rect.w, rect.h);
|
|
removeFromUnorderedVector(remainingRects, bestRect);
|
|
}
|
|
return (int) remainingRects.size();
|
|
}
|
|
|
|
}
|