Language: English | 繁體中文
A lightweight PHP micro-library for grid slicing. It divides a fixed-size plane (e.g., 1920×1080 canvas, video wall, media composition, dashboard, or layout system) into a grid according to specified proportions, then sequentially "fills" rectangular slices into available cells and returns their pixel coordinates and sizes.
GridMap uses a simple row-major first-fit scanning algorithm to find the first available area that can fit a slice of the requested column/row span. If successful, the area is marked as occupied and the next slice is processed. If a slice cannot fit, or if the grid is not fully occupied at the end, an exception is thrown.
- ✅ Single static class, easy to understand and integrate
- ✅ Pure PHP ≥ 8.0 with typed, immutable DTOs
- ✅ Define slices in "grid units" and automatically convert to pixel coordinates
x, y, width, height - ✅ Deterministic first-fit placement, reproducible results
- ✅ Uses integer division
intdivto avoid floating-point rounding errors - ✅ Automatically detects unplaceable or unfilled slices and throws clear exceptions
- ✅ Suitable for dynamic layouts, video walls, auto-layout suggestions
composer require reallifekip/grid-mapuse ReallifeKip\GridMap\GridMap;
use ReallifeKip\GridMap\Objects\DTOs\Config;
// Canvas: 1920×1080, divided into a 24×12 grid (typical 16:9 split)
$result = GridMap::slice(
Config::fromArray([
'imageWidth' => 1920,
'imageHeight' => 1080,
'columns' => 24,
'rows' => 12,
'cells' => [
['colSpan' => 6, 'rowSpan' => 6],
['colSpan' => 6, 'rowSpan' => 6],
['colSpan' => 6, 'rowSpan' => 6],
['colSpan' => 6, 'rowSpan' => 6],
['colSpan' => 12, 'rowSpan' => 6],
['colSpan' => 12, 'rowSpan' => 6],
],
])
);
print_r($result);Example output:
Array
(
[0] => Slice Object ( [x] => 0 [y] => 0 [width] => 480 [height] => 540 )
[1] => Slice Object ( [x] => 480 [y] => 0 [width] => 480 [height] => 540 )
[2] => Slice Object ( [x] => 960 [y] => 0 [width] => 480 [height] => 540 )
[3] => Slice Object ( [x] => 1440 [y] => 0 [width] => 480 [height] => 540 )
[4] => Slice Object ( [x] => 0 [y] => 540 [width] => 960 [height] => 540 )
[5] => Slice Object ( [x] => 960 [y] => 540 [width] => 960 [height] => 540 )
)
Width/height are actual pixels, calculated via
intdiv. For example:1920/24 = 80,1080/12 = 90. A 6-column span = 6 × 80 = 480 px; a 6-row span = 6 × 90 = 540 px.
| Term | Description |
|---|---|
imageWidth, imageHeight |
Total canvas size (pixels) |
columns, rows |
Number of grid divisions (horizontal / vertical) |
Cell colSpan, rowSpan |
Slice rectangle defined in grid units, not pixels |
return Slice[] |
Each placed slice object with x, y, width, height (px) |
| Property | Default |
|---|---|
imageWidth |
2500 |
imageHeight |
1686 |
columns |
24 |
rows |
12 |
Steps (simplified):
- Compute all grid line coordinates:
cols[x] = intdiv(x * imageWidth, columns),rows[y] = intdiv(y * imageHeight, rows) - Use a 1D array to mark whether each cell is occupied
- For each
Cell:- Scan row-major for the first fitting location
- Check that all covered cells are free
- Mark occupied and convert to a pixel
Slice
- Throw
\Exceptionif a cell cannot be placed - Throw
\Exceptionif the grid is not fully occupied after all cells are placed
| Use Case | Description |
|---|---|
| Media/Video wall | Automatically arrange multiple video sources |
| Video compositing | Map multiple tracks into fixed canvas coordinates |
| Realtime dashboard | Auto-generate initial layouts for widget cards |
| Game/Level editor | Plan map or scene block placements |
| Ad screen layout | Place multiple ad creatives into grid layout |
use ReallifeKip\GridMap\GridMap;
use ReallifeKip\GridMap\Objects\DTOs\Config;
try {
$result = GridMap::slice(
Config::fromArray([
'imageWidth' => 1200,
'imageHeight' => 800,
'columns' => 20,
'rows' => 10,
'cells' => [
['colSpan' => 4, 'rowSpan' => 4], // A
['colSpan' => 8, 'rowSpan' => 4], // B
['colSpan' => 8, 'rowSpan' => 4], // C — must fully fill the grid
['colSpan' => 20, 'rowSpan' => 6], // D
],
])
);
} catch (\Exception $e) {
// Thrown if a slice cannot be placed, or if the grid is left partially unfilled
echo 'Slice failed: ' . $e->getMessage();
}- Each
Cellmust have two positive integers:colSpanandrowSpan colSpancannot exceedcolumns;rowSpancannot exceedrows- Strategy is "first feasible placement": not optimized for space, just deterministic
- For "optimal packing / rotation / reordering", preprocess cells (e.g., sort by size)
- The returned
Slice[]array indexes correspond 1-to-1 with the inputcellsorder - All cells must collectively fill the entire grid — a partially filled grid throws an
\Exception
Returns an array of Slice objects:
/** @var ReallifeKip\GridMap\Objects\DTOs\Slice[] $result */
$result[0]->x; // int — left edge in pixels
$result[0]->y; // int — top edge in pixels
$result[0]->width; // int — width in pixels
$result[0]->height; // int — height in pixelsYou may write PHPUnit tests to verify:
- Slice count matches returned array count
- No overlapping rectangles (check cell intersections)
- When all cells fit, occupied grid count =
columns * rows - Oversized cells trigger
\Exception - Partially filled grids trigger
\Exception
This package uses the MIT License – Free for commercial use / modification / distribution, just keep copyright notice.
Author: Kip (bill402099@gmail.com) GitHub: @ReallifeKip
If this project helps you: feel free to Star, share, or suggest improvements!