Recently I worked on a feature where I had to generate an image sprite for a number of equally sized images. Even though you can simply put all of these images below each other, I found it a challenge to generate an image sprite that was as closest to a square and with as little white-spacing as possible. What I ended up with is a function that calculates the optimal size of the grid (columns and rows) for any number of items.

An easy solution would be for 25 items: you’ll get an image sprite of 5 columns and 5 rows.

It becomes harder if you look at 19: the only solution without white-spaces is a sheet of 19 columns and 1 row.

But if you want the result to be as square as possible you would end up with 5 columns and 4 rows, leaving you with 1 white-space.

I wrote a function that calculates the optimal square-like grid size for any number of items. Additionally you can pass the minimum ratio you want; the function will loosen the threshold of white-spaces every time no grid can be created with a ratio above it.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
/** * Get the number of rows and columns needed to make a square for the given number of items. * * @param int $n * @param float $minRatio = 0 The minimum ratio of the generated rectangle * @return array [nCols, nRows] */ function calcGridSizeForItems(int $n, float $minRatio = 0): array { $sqrt = sqrt($n); $threshold = 1; $i = 0; $tryAgain = true; while ($threshold > 0 && $tryAgain) { $nCols = floor($sqrt) - $i; // If the ratio of the rectangle falls below the given one, we lower the // threshold, reset the counter and try again. If not, we test if the // amount of 'left-over' items in the last row is below the threshold. if ($nCols / ceil($n / $nCols) < $minRatio) { $threshold -= 0.1; $i = 0; } else { $tryAgain = $n % $nCols > 0 && $n % $nCols < $nCols * $threshold; $i++; } } return [$nCols, ceil($n / $nCols)]; } |