Renamed padding to spacing

This commit is contained in:
Chlumsky 2024-03-10 00:01:51 +01:00
parent b716311880
commit 3a4f181674
11 changed files with 83 additions and 83 deletions

View File

@ -37,7 +37,7 @@ public:
private:
int side;
int padding;
int spacing;
int glyphCount;
int totalArea;
RectanglePacker packer;

View File

@ -14,14 +14,14 @@ static int ceilPOT(int x) {
}
template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : side(0), padding(0), glyphCount(0), totalArea(0) { }
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : side(0), spacing(0), glyphCount(0), totalArea(0) { }
template <class AtlasGenerator>
template <typename... ARGS>
DynamicAtlas<AtlasGenerator>::DynamicAtlas(int minSide, ARGS... args) : side(ceilPOT(minSide)), padding(0), glyphCount(0), totalArea(0), packer(side+padding, side+padding), generator(side, side, args...) { }
DynamicAtlas<AtlasGenerator>::DynamicAtlas(int minSide, ARGS... args) : side(ceilPOT(minSide)), spacing(0), glyphCount(0), totalArea(0), packer(side+spacing, side+spacing), generator(side, side, args...) { }
template <class AtlasGenerator>
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : side(0), padding(0), glyphCount(0), totalArea(0), generator((AtlasGenerator &&) generator) { }
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : side(0), spacing(0), glyphCount(0), totalArea(0), generator((AtlasGenerator &&) generator) { }
template <class AtlasGenerator>
typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count, bool allowRearrange) {
@ -31,14 +31,14 @@ typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>:
if (!glyphs[i].isWhitespace()) {
int w, h;
glyphs[i].getBoxSize(w, h);
Rectangle rect = { 0, 0, w+padding, h+padding };
Rectangle rect = { 0, 0, w+spacing, h+spacing };
rectangles.push_back(rect);
Remap remapEntry = { };
remapEntry.index = glyphCount+i;
remapEntry.width = w;
remapEntry.height = h;
remapBuffer.push_back(remapEntry);
totalArea += (w+padding)*(h+padding);
totalArea += (w+spacing)*(h+spacing);
}
}
if ((int) rectangles.size() > start) {
@ -49,10 +49,10 @@ typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>:
while (side*side < totalArea)
side <<= 1;
if (allowRearrange) {
packer = RectanglePacker(side+padding, side+padding);
packer = RectanglePacker(side+spacing, side+spacing);
packerStart = 0;
} else {
packer.expand(side+padding, side+padding);
packer.expand(side+spacing, side+spacing);
packerStart = rectangles.size()-remaining;
}
changeFlags |= RESIZED;

View File

@ -91,7 +91,7 @@ GridAtlasPacker::GridAtlasPacker() :
columns(-1), rows(-1),
width(-1), height(-1),
cellWidth(-1), cellHeight(-1),
padding(0),
spacing(0),
dimensionsConstraint(DimensionsConstraint::NONE),
cellDimensionsConstraint(DimensionsConstraint::NONE),
hFixed(false), vFixed(false),
@ -152,7 +152,7 @@ double GridAtlasPacker::scaleToFit(GlyphGeometry *glyphs, int count, int cellWid
if (cellHeight <= 0)
cellHeight = BIG_VALUE;
--cellWidth, --cellHeight; // Implicit half-pixel padding from each side to make sure that no representable values are beyond outermost pixel centers
cellWidth -= padding, cellHeight -= padding;
cellWidth -= spacing, cellHeight -= spacing;
bool lastResult = false;
#define TRY_FIT(scale) (maxWidth = 0, maxHeight = 0, maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, (scale), unitRange+pxRange/(scale)), lastResult = maxWidth <= cellWidth && maxHeight <= cellHeight)
double minScale = 1, maxScale = 1;
@ -197,7 +197,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
else if (rows > 0)
columns = (cellCount+rows-1)/rows;
else if (width > 0 && cellWidth > 0) {
columns = (width+padding)/cellWidth;
columns = (width+spacing)/cellWidth;
rows = (cellCount+columns-1)/columns;
}
}
@ -210,9 +210,9 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
raiseToConstraint(width, height, dimensionsConstraint);
if (cellWidth < 0 && width > 0 && columns > 0)
cellWidth = (width+padding)/columns;
cellWidth = (width+spacing)/columns;
if (cellHeight < 0 && height > 0 && rows > 0)
cellHeight = (height+padding)/rows;
cellHeight = (height+spacing)/rows;
if (cellWidth != initial.cellWidth || cellHeight != initial.cellHeight) {
bool positiveCellWidth = cellWidth > 0, positiveCellHeight = cellHeight > 0;
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
@ -220,7 +220,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
return -1;
}
if ((cellWidth > 0 && cellWidth-padding-1 <= pxRange) || (cellHeight > 0 && cellHeight-padding-1 <= pxRange)) // cells definitely too small
if ((cellWidth > 0 && cellWidth-spacing-1 <= pxRange) || (cellHeight > 0 && cellHeight-spacing-1 <= pxRange)) // cells definitely too small
return -1;
msdfgen::Shape::Bounds maxBounds = { };
@ -246,8 +246,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
for (int q = (int) sqrt(cellCount)+1; q > 0; --q) {
int cols = q;
int rows = (cellCount+cols-1)/cols;
int tWidth = (width+padding)/cols;
int tHeight = (height+padding)/rows;
int tWidth = (width+spacing)/cols;
int tHeight = (height+spacing)/rows;
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
if (tWidth > 0 && tHeight > 0) {
double curScale = scaleToFit(glyphs, count, tWidth, tHeight, maxBounds, maxWidth, maxHeight);
@ -262,8 +262,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
}
cols = (cellCount+q-1)/q;
rows = (cellCount+cols-1)/cols;
tWidth = (width+padding)/cols;
tHeight = (height+padding)/rows;
tWidth = (width+spacing)/cols;
tHeight = (height+spacing)/rows;
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
if (tWidth > 0 && tHeight > 0) {
double curScale = scaleToFit(glyphs, count, tWidth, tHeight, maxBounds, maxWidth, maxHeight);
@ -287,8 +287,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
columns = bestCols;
rows = (cellCount+columns-1)/columns;
cellWidth = (width+padding)/columns;
cellHeight = (height+padding)/rows;
cellWidth = (width+spacing)/columns;
cellHeight = (height+spacing)/rows;
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
if (scale < minScale)
@ -297,8 +297,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
if (scale <= 0) {
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, minScale, unitRange+pxRange/minScale);
cellWidth = (int) ceil(maxWidth)+padding+1;
cellHeight = (int) ceil(maxHeight)+padding+1;
cellWidth = (int) ceil(maxWidth)+spacing+1;
cellHeight = (int) ceil(maxHeight)+spacing+1;
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
if (scale < minScale)
@ -306,7 +306,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
}
if (initial.rows < 0 && initial.cellHeight < 0) {
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(maxHeight)+padding+1;
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
raiseToConstraint(optimalCellWidth, optimalCellHeight, cellDimensionsConstraint);
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
cellWidth = optimalCellWidth;
@ -329,9 +329,9 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
double hScale = 0, vScale = 0;
if (cellWidth > 0)
hScale = (cellWidth-hSlack-padding-1-pxRange)/maxWidth;
hScale = (cellWidth-hSlack-spacing-1-pxRange)/maxWidth;
if (cellHeight > 0)
vScale = (cellHeight-vSlack-padding-1-pxRange)/maxHeight;
vScale = (cellHeight-vSlack-spacing-1-pxRange)/maxHeight;
if (hScale || vScale) {
if (hScale && vScale)
scale = std::min(hScale, vScale);
@ -349,12 +349,12 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
// TODO optimize to only test up to sqrt(cellCount) cols and rows like in the above branch (for (int q = (int) sqrt(cellCount)+1; ...)
for (int cols = 1; cols < width; ++cols) {
int rows = (cellCount+cols-1)/cols;
int tWidth = (width+padding)/cols;
int tHeight = (height+padding)/rows;
int tWidth = (width+spacing)/cols;
int tHeight = (height+spacing)/rows;
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
if (tWidth > 0 && tHeight > 0) {
hScale = (tWidth-hSlack-padding-1-pxRange)/maxWidth;
vScale = (tHeight-vSlack-padding-1-pxRange)/maxHeight;
hScale = (tWidth-hSlack-spacing-1-pxRange)/maxWidth;
vScale = (tHeight-vSlack-spacing-1-pxRange)/maxHeight;
double curScale = std::min(hScale, vScale);
if (curScale > scale) {
scale = curScale;
@ -376,24 +376,24 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
columns = bestCols;
rows = (cellCount+columns-1)/columns;
cellWidth = (width+padding)/columns;
cellHeight = (height+padding)/rows;
cellWidth = (width+spacing)/columns;
cellHeight = (height+spacing)/rows;
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
if (scale < minScale)
scale = -1;
}
if (scale <= 0) {
cellWidth = (int) ceil(minScale*maxWidth+pxRange)+hSlack+padding+1;
cellHeight = (int) ceil(minScale*maxHeight+pxRange)+vSlack+padding+1;
cellWidth = (int) ceil(minScale*maxWidth+pxRange)+hSlack+spacing+1;
cellHeight = (int) ceil(minScale*maxHeight+pxRange)+vSlack+spacing+1;
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
hScale = (cellWidth-hSlack-padding-1-pxRange)/maxWidth;
vScale = (cellHeight-vSlack-padding-1-pxRange)/maxHeight;
hScale = (cellWidth-hSlack-spacing-1-pxRange)/maxWidth;
vScale = (cellHeight-vSlack-spacing-1-pxRange)/maxHeight;
scale = std::min(hScale, vScale);
}
if (initial.rows < 0 && initial.cellHeight < 0) {
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(scale*maxHeight+pxRange)+vSlack+padding+1;
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(scale*maxHeight+pxRange)+vSlack+spacing+1;
raiseToConstraint(optimalCellWidth, optimalCellHeight, cellDimensionsConstraint);
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
cellWidth = optimalCellWidth;
@ -408,8 +408,8 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
} else {
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, unitRange+pxRange/scale);
int optimalCellWidth = (int) ceil(maxWidth)+padding+1;
int optimalCellHeight = (int) ceil(maxHeight)+padding+1;
int optimalCellWidth = (int) ceil(maxWidth)+spacing+1;
int optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
if (cellWidth < 0 || cellHeight < 0) {
cellWidth = optimalCellWidth;
cellHeight = optimalCellHeight;
@ -423,17 +423,17 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
if (pxAlignOriginX) {
int sl = (int) floor(maxBounds.l-.5);
int sr = (int) ceil(maxBounds.r+.5);
fixedX = (-sl+(cellWidth-padding-(sr-sl))/2)/scale;
fixedX = (-sl+(cellWidth-spacing-(sr-sl))/2)/scale;
} else
fixedX = (-maxBounds.l+.5*(cellWidth-padding-maxWidth))/scale;
fixedX = (-maxBounds.l+.5*(cellWidth-spacing-maxWidth))/scale;
}
if (vFixed) {
if (pxAlignOriginY) {
int sb = (int) floor(maxBounds.b-.5);
int st = (int) ceil(maxBounds.t+.5);
fixedY = (-sb+(cellHeight-padding-(st-sb))/2)/scale;
fixedY = (-sb+(cellHeight-spacing-(st-sb))/2)/scale;
} else
fixedY = (-maxBounds.b+.5*(cellHeight-padding-maxHeight))/scale;
fixedY = (-maxBounds.b+.5*(cellHeight-spacing-maxHeight))/scale;
}
if (width < 0 || height < 0) {
@ -475,7 +475,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
}
if (columns < 0) {
columns = (width+padding)/cellWidth;
columns = (width+spacing)/cellWidth;
rows = (cellCount+columns-1)/columns;
}
if (rows*cellHeight > height)
@ -484,7 +484,7 @@ int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
int col = 0, row = 0;
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
if (!glyph->isWhitespace()) {
glyph->frameBox(scale, unitRange+pxRange/scale, miterLimit, cellWidth-padding, cellHeight-padding, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr, pxAlignOriginX, pxAlignOriginY);
glyph->frameBox(scale, unitRange+pxRange/scale, miterLimit, cellWidth-spacing, cellHeight-spacing, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr, pxAlignOriginX, pxAlignOriginY);
glyph->placeBox(col*cellWidth, height-(row+1)*cellHeight);
if (++col >= columns) {
if (++row >= rows) {
@ -542,8 +542,8 @@ void GridAtlasPacker::setDimensionsConstraint(DimensionsConstraint dimensionsCon
this->dimensionsConstraint = dimensionsConstraint;
}
void GridAtlasPacker::setPadding(int padding) {
this->padding = padding;
void GridAtlasPacker::setSpacing(int spacing) {
this->spacing = spacing;
}
void GridAtlasPacker::setScale(double scale) {

View File

@ -33,8 +33,8 @@ public:
void unsetDimensions();
/// Sets the constraint to be used when determining dimensions
void setDimensionsConstraint(DimensionsConstraint dimensionsConstraint);
/// Sets the padding between glyph boxes
void setPadding(int padding);
/// Sets the spacing between glyph boxes
void setSpacing(int spacing);
/// Sets fixed glyph scale
void setScale(double scale);
/// Sets the minimum glyph scale
@ -70,7 +70,7 @@ private:
int columns, rows;
int width, height;
int cellWidth, cellHeight;
int padding;
int spacing;
DimensionsConstraint dimensionsConstraint;
DimensionsConstraint cellDimensionsConstraint;
bool hFixed, vFixed;

View File

@ -10,7 +10,7 @@ namespace msdf_atlas {
TightAtlasPacker::TightAtlasPacker() :
width(-1), height(-1),
padding(0),
spacing(0),
dimensionsConstraint(DimensionsConstraint::POWER_OF_TWO_SQUARE),
scale(-1),
minScale(1),
@ -50,27 +50,27 @@ int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstr
std::pair<int, int> dimensions = std::make_pair(width, height);
switch (dimensionsConstraint) {
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
dimensions = packRectangles<SquarePowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), padding);
dimensions = packRectangles<SquarePowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), spacing);
break;
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
dimensions = packRectangles<PowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), padding);
dimensions = packRectangles<PowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), spacing);
break;
case DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
dimensions = packRectangles<SquareSizeSelector<4> >(rectangles.data(), rectangles.size(), padding);
dimensions = packRectangles<SquareSizeSelector<4> >(rectangles.data(), rectangles.size(), spacing);
break;
case DimensionsConstraint::EVEN_SQUARE:
dimensions = packRectangles<SquareSizeSelector<2> >(rectangles.data(), rectangles.size(), padding);
dimensions = packRectangles<SquareSizeSelector<2> >(rectangles.data(), rectangles.size(), spacing);
break;
case DimensionsConstraint::SQUARE:
default:
dimensions = packRectangles<SquareSizeSelector<> >(rectangles.data(), rectangles.size(), padding);
dimensions = packRectangles<SquareSizeSelector<> >(rectangles.data(), rectangles.size(), spacing);
break;
}
if (!(dimensions.first > 0 && dimensions.second > 0))
return -1;
width = dimensions.first, height = dimensions.second;
} else {
if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, padding))
if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, spacing))
return result;
}
// Set glyph box placement
@ -131,8 +131,8 @@ void TightAtlasPacker::setDimensionsConstraint(DimensionsConstraint dimensionsCo
this->dimensionsConstraint = dimensionsConstraint;
}
void TightAtlasPacker::setPadding(int padding) {
this->padding = padding;
void TightAtlasPacker::setSpacing(int spacing) {
this->spacing = spacing;
}
void TightAtlasPacker::setScale(double scale) {

View File

@ -24,8 +24,8 @@ public:
void unsetDimensions();
/// Sets the constraint to be used when determining dimensions
void setDimensionsConstraint(DimensionsConstraint dimensionsConstraint);
/// Sets the padding between glyph boxes
void setPadding(int padding);
/// Sets the spacing between glyph boxes
void setSpacing(int spacing);
/// Sets fixed glyph scale
void setScale(double scale);
/// Sets the minimum glyph scale
@ -49,7 +49,7 @@ public:
private:
int width, height;
int padding;
int spacing;
DimensionsConstraint dimensionsConstraint;
double scale;
double minScale;

View File

@ -87,7 +87,7 @@ bool exportJSON(const FontGeometry *fonts, int fontCount, ImageType imageType, c
fprintf(f, ",\"originY\":%.17g", *metrics.grid->originY);
break;
case YDirection::TOP_DOWN:
fprintf(f, ",\"originY\":%.17g", (metrics.grid->cellHeight-metrics.grid->padding-1)/metrics.size-*metrics.grid->originY);
fprintf(f, ",\"originY\":%.17g", (metrics.grid->cellHeight-metrics.grid->spacing-1)/metrics.size-*metrics.grid->originY);
break;
}
}

View File

@ -13,7 +13,7 @@ struct JsonAtlasMetrics {
int cellWidth, cellHeight;
int columns, rows;
const double *originX, *originY;
int padding;
int spacing;
};
double distanceRange;
double size;

View File

@ -929,8 +929,8 @@ int main(int argc, const char *const *argv) {
config.imageFormat == ImageFormat::BINARY_FLOAT ||
config.imageFormat == ImageFormat::BINARY_FLOAT_BE
);
// TODO: In this case (if padding is -1), the border pixels of each glyph are black, but still computed. For floating-point output, this may play a role.
int padding = config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF ? 0 : -1;
// TODO: In this case (if spacing is -1), the border pixels of each glyph are black, but still computed. For floating-point output, this may play a role.
int spacing = config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF ? 0 : -1;
double uniformOriginX, uniformOriginY;
// Load fonts
@ -1064,7 +1064,7 @@ int main(int argc, const char *const *argv) {
atlasPacker.setDimensions(fixedWidth, fixedHeight);
else
atlasPacker.setDimensionsConstraint(atlasSizeConstraint);
atlasPacker.setPadding(padding);
atlasPacker.setSpacing(spacing);
if (fixedScale)
atlasPacker.setScale(config.emSize);
else
@ -1106,7 +1106,7 @@ int main(int argc, const char *const *argv) {
atlasPacker.setDimensions(fixedWidth, fixedHeight);
else
atlasPacker.setDimensionsConstraint(atlasSizeConstraint);
atlasPacker.setPadding(padding);
atlasPacker.setSpacing(spacing);
if (fixedScale)
atlasPacker.setScale(config.emSize);
else
@ -1148,7 +1148,7 @@ int main(int argc, const char *const *argv) {
printf("Y = %.9g", uniformOriginY);
break;
case YDirection::TOP_DOWN:
printf("Y = %.9g", (config.grid.cellHeight-padding-1)/config.emSize-uniformOriginY);
printf("Y = %.9g", (config.grid.cellHeight-spacing-1)/config.emSize-uniformOriginY);
break;
}
}
@ -1243,7 +1243,7 @@ int main(int argc, const char *const *argv) {
gridMetrics.originX = &uniformOriginX;
if (config.grid.fixedOriginY)
gridMetrics.originY = &uniformOriginY;
gridMetrics.padding = padding;
gridMetrics.spacing = spacing;
jsonMetrics.grid = &gridMetrics;
}
if (exportJSON(fonts.data(), fonts.size(), config.imageType, jsonMetrics, config.jsonFilename, config.kerning))

View File

@ -8,11 +8,11 @@ namespace msdf_atlas {
/// Packs the rectangle array into an atlas with fixed dimensions, returns how many didn't fit (0 on success)
template <typename RectangleType>
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding = 0);
int packRectangles(RectangleType *rectangles, int count, int width, int height, int spacing = 0);
/// Packs the rectangle array into an atlas of unknown size, returns the minimum required dimensions constrained by SizeSelector
template <class SizeSelector, typename RectangleType>
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding = 0);
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int spacing = 0);
}

View File

@ -18,35 +18,35 @@ static void copyRectanglePlacement(OrientedRectangle &dst, const OrientedRectang
}
template <typename RectangleType>
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding) {
if (padding)
int packRectangles(RectangleType *rectangles, int count, int width, int height, int spacing) {
if (spacing)
for (int i = 0; i < count; ++i) {
rectangles[i].w += padding;
rectangles[i].h += padding;
rectangles[i].w += spacing;
rectangles[i].h += spacing;
}
int result = RectanglePacker(width+padding, height+padding).pack(rectangles, count);
if (padding)
int result = RectanglePacker(width+spacing, height+spacing).pack(rectangles, count);
if (spacing)
for (int i = 0; i < count; ++i) {
rectangles[i].w -= padding;
rectangles[i].h -= padding;
rectangles[i].w -= spacing;
rectangles[i].h -= spacing;
}
return result;
}
template <class SizeSelector, typename RectangleType>
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding) {
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int spacing) {
std::vector<RectangleType> rectanglesCopy(count);
int totalArea = 0;
for (int i = 0; i < count; ++i) {
rectanglesCopy[i].w = rectangles[i].w+padding;
rectanglesCopy[i].h = rectangles[i].h+padding;
rectanglesCopy[i].w = rectangles[i].w+spacing;
rectanglesCopy[i].h = rectangles[i].h+spacing;
totalArea += rectangles[i].w*rectangles[i].h;
}
std::pair<int, int> dimensions;
SizeSelector sizeSelector(totalArea);
int width, height;
while (sizeSelector(width, height)) {
if (!RectanglePacker(width+padding, height+padding).pack(rectanglesCopy.data(), count)) {
if (!RectanglePacker(width+spacing, height+spacing).pack(rectanglesCopy.data(), count)) {
dimensions.first = width;
dimensions.second = height;
for (int i = 0; i < count; ++i)