package sprite
import (
"fmt"
"image/color"
)
// Sheet lays out several equally sized sprites in a row-major grid:
// index 0 is top-left, then left-to-right, then next row. A 1D strip is
// simply a sheet with one row (or one column).
type Sheet struct {
Sprites []*Sprite
Cols, Rows int
CellW, CellH int
}
// NewSheet composes sprites into a sheet with the given number of columns.
// cols <= 0 puts everything in a single row. Every sprite must have the
// same dimensions so the sheet can be indexed cell by cell.
func NewSheet(sprites []*Sprite, cols int) (*Sheet, error) {
if len(sprites) == 0 {
return nil, fmt.Errorf("a sheet needs at least one sprite")
}
w, h := sprites[0].Bounds()
for _, sp := range sprites[1:] {
sw, sh := sp.Bounds()
if sw != w || sh != h {
return nil, fmt.Errorf("sprite %q is %dx%d but %q is %dx%d — all sprites in a sheet must be the same size",
sp.Name, sw, sh, sprites[0].Name, w, h)
}
}
if cols <= 0 || cols > len(sprites) {
cols = len(sprites)
}
rows := (len(sprites) + cols - 1) / cols
return &Sheet{Sprites: sprites, Cols: cols, Rows: rows, CellW: w, CellH: h}, nil
}
// Bounds returns the total sheet size in pixels.
func (sh *Sheet) Bounds() (w, h int) { return sh.Cols * sh.CellW, sh.Rows * sh.CellH }
// At returns the pixel at (x, y). Cells past the last sprite (when the
// sprite count doesn't fill the grid) are transparent.
func (sh *Sheet) At(x, y int) color.NRGBA {
col, row := x/sh.CellW, y/sh.CellH
idx := row*sh.Cols + col
if idx >= len(sh.Sprites) {
return Transparent
}
return sh.Sprites[idx].At(x%sh.CellW, y%sh.CellH)
}
// FileBase appends the layout to an output name so the file itself
// documents cell size and orientation: _x_x
// e.g. walk_16x16_4x2 = 16x16 cells, 4 columns, 2 rows, read row by row.
func (sh *Sheet) FileBase(base string) string {
return fmt.Sprintf("%s_%dx%d_%dx%d", base, sh.CellW, sh.CellH, sh.Cols, sh.Rows)
}