# grid/autotile

`import "github.com/matjam/bunyip/grid/autotile"`

Package autotile picks tile frames from a terrain grid, so a map of plain terrain ids draws with matching edges, corners and transitions. The game keeps one small int per cell; a Mapper turns that into frame indices for a tile sheet, whole maps at a time with Apply, one changed cell at a time with Cell, or one changed rectangle at a time with Region. The package is pure logic: frames are ints for gfx.Tilemap or anything else, and -1 means no tile.

Five rule kinds cover the usual tilesets. Edge16 matches the four edge neighbours and needs 16 tiles: walls, pipes, fences. Edge64 is its hexagonal counterpart, matching the six neighbours of a hexagon with 64 tiles. Blob47 matches all eight neighbours, reduced to the 47 distinct cases: the standard blob terrain set. Corner16 is the dual grid: each tile sits on a corner between four cells and needs 16 tiles. Wang matches terrain colours on tile edges, corners or both, for any number of terrains meeting with proper transitions; tilesets authored in the Tiled editor's terrain tool convert to it through the tiled package. ExpandBlob composes the 47 blob tiles from a six-tile template so an artist draws six tiles instead of 47.

Directions follow Tiled's clockwise order from north: N, NE, E, SE, S, SW, W, NW. A Mapper's Layout says where each direction's neighbour lies: Square, hexagons in staggered rows or columns, hexagons in axial coordinates, or an isometric diamond grid whose directions are the tile's directions on screen. A hexagonal layout has six neighbours and no diagonals, so it takes Edge64 or Wang rules; Blob47 and Corner16 need the eight neighbours of a square or isometric grid.

All zero values are usable defaults: a Mapper with only Rules set works on a square grid, treats the map border as continuing each cell's terrain and varies tile variants with seed zero.

## Constants

<a id="DirN"></a>

<a id="DirNE"></a>

<a id="DirE"></a>

<a id="DirSE"></a>

<a id="DirS"></a>

<a id="DirSW"></a>

<a id="DirW"></a>

<a id="DirNW"></a>

```go
const (
	DirN = iota
	DirNE
	DirE
	DirSE
	DirS
	DirSW
	DirW
	DirNW
)
```

Directions, clockwise from north. They index WangTile.Colors and number the bits of a blob mask.

## Functions

<a id="BlobIndex"></a>

### BlobIndex

```go
func BlobIndex(mask uint8) int
```

BlobIndex maps a raw 8-bit neighbour mask (bit d set when the neighbour in direction d connects) to the canonical 0..46 index that Blob47 frames use.

<a id="BlobMasks"></a>

### BlobMasks

```go
func BlobMasks() [47]uint8
```

BlobMasks returns the 47 canonical masks in frame order, for building or checking a sheet layout.

<a id="ExpandBlob"></a>

### ExpandBlob

```go
func ExpandBlob(template image.Image, tile int) (*image.RGBA, [47]int)
```

ExpandBlob composes the 47 blob tiles from a six-tile template, so an artist draws six tiles instead of 47. The template is two tiles wide and three tall, each tile square with the given side:

	inner  preview
	 TL      TR
	 BL      BR

TL, TR, BL and BR are the four corner tiles of a filled two-by-two block, which supply the outer corners, the edges and the interior. The inner tile holds the four inside-corner pieces, drawn as if a hole sat at each diagonal. The preview tile is not read; RPG Maker style autotile blocks put a display tile there.

Every output tile is built from four quarter-tiles chosen by the neighbour mask. The result is a sheet eight tiles wide holding the 47 tiles in canonical Blob47 order, and the matching frames array: upload it, cut it with a sheet of the same tile size, and pass the frames to Blob47. tile must be a positive even pixel size; template must be non-nil and contain at least two tiles across and three down from Bounds().Min.

## Types

<a id="Layout"></a>

### Layout

```go
type Layout int
```

Layout is the shape of the grid a Mapper walks: which of the eight directions a cell has a neighbour in, and which cell that is. The zero value, Square, is a square grid with four edge neighbours and four diagonals.

A hexagonal layout has six neighbours and no diagonals. The rows layouts hold pointy-top hexagons in staggered rows, which is Tiled's stagger axis Y: every cell has an east and a west neighbour, and the four remaining sides run to the rows above and below. The columns layouts hold flat-top hexagons in staggered columns, Tiled's stagger axis X: every cell has a north and a south neighbour. Odd and even say which rows or columns are shifted, matching Tiled's stagger index.

IsoDiamond is a square grid projected as isometric diamonds, so the direction names are the tile's directions on screen: the cell north of (x, y) is (x-1, y-1), and the cell that shares the diamond's upper-right edge is (x, y-1).

<a id="Square"></a>

<a id="HexRowsOdd"></a>

<a id="HexRowsEven"></a>

<a id="HexColsOdd"></a>

<a id="HexColsEven"></a>

<a id="HexAxial"></a>

<a id="IsoDiamond"></a>

```go
const (
	Square      Layout = iota // a square grid: four edges and four diagonals
	HexRowsOdd                // hexagons in staggered rows, the odd rows shifted right
	HexRowsEven               // hexagons in staggered rows, the even rows shifted right
	HexColsOdd                // hexagons in staggered columns, the odd columns shifted down
	HexColsEven               // hexagons in staggered columns, the even columns shifted down
	// HexAxial holds hexagons in axial coordinates, where x runs east and
	// y runs south-east, so the offsets are the same for every cell. It
	// has the same six directions as the rows layouts.
	HexAxial
	IsoDiamond // a square grid projected as diamonds, with screen-relative directions
)
```

<a id="Layout.Dirs"></a>

#### Layout.Dirs

```go
func (l Layout) Dirs() []int
```

Dirs lists the directions the layout has neighbours in, clockwise. A square or isometric layout lists all eight; a hexagonal one lists its six. The result is a fresh slice the caller may keep.

<a id="Layout.Hex"></a>

#### Layout.Hex

```go
func (l Layout) Hex() bool
```

Hex reports whether the layout is hexagonal, so a cell has six neighbours and no diagonals.

<a id="Layout.Neighbour"></a>

#### Layout.Neighbour

```go
func (l Layout) Neighbour(x, y, dir int) (int, int)
```

Neighbour returns the cell one step from (x, y) in a direction. A direction the layout has no neighbour in, such as north on a rows hexagon, returns the cell itself; Dirs lists the directions a layout uses.

<a id="Layout.String"></a>

#### Layout.String

```go
func (l Layout) String() string
```

String names the layout.

<a id="Mapper"></a>

<a id="Mapper.Rules"></a>

<a id="Mapper.Layout"></a>

<a id="Mapper.Seed"></a>

<a id="Mapper.Outside"></a>

<a id="Mapper.OutsideFixed"></a>

### Mapper

```go
type Mapper struct {
	Rules *Rules
	// Layout is the grid's shape, which says where each direction's
	// neighbour lies. The zero value is Square.
	Layout Layout
	// Seed varies which variant each cell picks; the choice is a pure
	// function of Seed and the cell position, so reapplying is stable.
	Seed uint64
	// Outside is the terrain assumed beyond the map edge when
	// OutsideFixed is set. Unset, the outside continues whatever
	// terrain is being matched, so borders connect.
	Outside      int
	OutsideFixed bool
	// contains filtered or unexported fields
}
```

Mapper applies one set of rules to a terrain grid. Rules is the only required field. The zero values of the rest mean: the border continues each cell's own terrain, and variants are seeded with zero. A Mapper reuses scratch storage and is not safe for concurrent or recursive use. Finish modifying its Rules before applying them.

<a id="Mapper.Apply"></a>

#### Mapper.Apply

```go
func (m *Mapper) Apply(w, h int, terrain func(x, y int) int, set func(x, y, frame int))
```

Apply computes a frame for every cell and hands each to set, with -1 for cells the rules leave empty. terrain returns the id at a cell and is only called inside the map. Corner16 rules emit one extra row and column: x and y run to w and h inclusive. A nil Rules makes no calls.

<a id="Mapper.Cell"></a>

#### Mapper.Cell

```go
func (m *Mapper) Cell(x, y, w, h int, terrain func(x, y int) int, set func(x, y, frame int))
```

Cell recomputes the cells a change at (x, y) can affect: the cell and the neighbours the layout gives it, or the four surrounding corners for Corner16 rules. Call it after editing one cell instead of reapplying the map. To update after editing a block of cells, call Region instead, which computes each affected cell once.

<a id="Mapper.Region"></a>

#### Mapper.Region

```go
func (m *Mapper) Region(x, y, rw, rh, w, h int, terrain func(x, y int) int, set func(x, y, frame int))
```

Region recomputes the cells a change anywhere in a rectangle can affect, and hands each to set once. To re-autotile after a brush stroke or a pasted block, call it with the rectangle of edited cells: rw by rh cells with its top-left cell at (x, y), on a w by h map. The frames it sets are the ones calling Cell for every cell of the rectangle would set, but a cell next to several edited cells is computed once instead of once per edited neighbour. Cells go to set in row-major order. Parts of the rectangle outside the map are allowed; a nil Rules or an empty rectangle makes no calls.

<a id="Rules"></a>

### Rules

```go
type Rules struct {
	// contains filtered or unexported fields
}
```

Rules maps a cell's neighbourhood to tile frames. Build one with Edge16, Edge64, Corner16, Blob47 or Wang, then hand it to a Mapper. The zero value has no frames; use a constructor before applying rules.

<a id="Blob47"></a>

#### Blob47

```go
func Blob47(terrain int, frames [47]int) *Rules
```

Blob47 builds rules that match all eight neighbours of cells with the given terrain id, reduced to the 47 distinct cases: a diagonal only matters when both edges beside it connect. frames is in canonical order, ascending by normalised mask; BlobIndex maps any raw mask to its place, and ExpandBlob emits tiles in this order.

<a id="Corner16"></a>

#### Corner16

```go
func Corner16(terrain int, frames [16]int) *Rules
```

Corner16 builds dual-grid rules for cells with the given terrain id. Each output tile sits on a corner between four cells, so Apply and Cell emit a grid one wider and one taller than the map; draw that tilemap offset up and left by half a tile. frames is indexed by a 4-bit mask of matching cells around the corner: north-west 1, north-east 2, south-west 4, south-east 8.

<a id="Edge16"></a>

#### Edge16

```go
func Edge16(terrain int, frames [16]int) *Rules
```

Edge16 builds rules that match the four edge neighbours of cells with the given terrain id. frames is indexed by a 4-bit mask of connected neighbours: north 1, east 2, south 4, west 8. Frame -1 leaves a case empty.

<a id="Edge64"></a>

#### Edge64

```go
func Edge64(terrain int, frames [64]int) *Rules
```

Edge64 builds rules that match the six neighbours of cells with the given terrain id on a hexagonal layout. frames is indexed by a 6-bit mask of connected neighbours, bit i being the i-th direction the layout uses, clockwise: for the rows layouts north-east, east, south-east, south-west, west and north-west, and for the columns layouts north, north-east, south-east, south, south-west and north-west. Frame -1 leaves a case empty.

<a id="Wang"></a>

#### Wang

```go
func Wang(t WangType, tiles []WangTile) *Rules
```

Wang builds rules from a Wang tile set. Cells hold terrain colours, zero for empty. For each cell the mapper works out the colour every relevant position should show, then places the tile matching the most positions; a complete set always matches exactly, an incomplete one falls back to the closest tile. Where two terrains meet at a corner or an edge, the higher colour wins, so higher colours overlap lower ones. A cell whose own and surrounding colours are all zero gets -1.

A hexagonal layout has six sides and no diagonals, so every type matches those six positions, in the direction slots the layout uses. t must be WangCorners, WangEdges or WangFull. The tiles slice is retained, not copied; edits affect later mapping. Use finite weights; nonpositive weights count as 1. An empty set maps every cell to -1.

A Mapper indexes the set by the colours each tile shows, so a cell that some tile matches at every position costs one lookup, however large the set. A cell no tile matches exactly scores every tile. Each call to Apply, Cell or Region first compares the set with the copy the index was built from and rebuilds the index if a tile changed, which costs time in proportion to the number of tiles.

<a id="Rules.Connect"></a>

#### Rules.Connect

```go
func (r *Rules) Connect(terrains ...int) *Rules
```

Connect makes neighbours of other terrains count as connected, so grass rules join up against road cells without drawing their tiles. It returns the rules for chaining.

<a id="Rules.Variant"></a>

#### Rules.Variant

```go
func (r *Rules) Variant(mask, frame int, weight float64) *Rules
```

Variant adds an alternative frame for one neighbourhood, chosen at random by cell position with the given weight; the frame passed to the constructor keeps weight 1. mask is the scheme's mask: 4 bits for Edge16 and Corner16, 6 bits for Edge64, the raw 8-bit mask for Blob47. A nonpositive weight counts as 1. An out-of-range mask is ignored. Wang rules take variants as extra tiles instead.

<a id="WangTile"></a>

<a id="WangTile.Frame"></a>

<a id="WangTile.Colors"></a>

<a id="WangTile.Weight"></a>

### WangTile

```go
type WangTile struct {
	Frame  int
	Colors [8]int
	Weight float64
}
```

WangTile is one tile of a Wang set: its frame and the terrain colour at each of the eight positions, in direction order (DirN through DirNW). Colour zero means the position is empty. Tiles with equal colours are variants; Weight biases the choice and zero counts as 1.

<a id="WangType"></a>

### WangType

```go
type WangType int
```

WangType says which positions of a Wang tile carry terrain colours.

<a id="WangCorners"></a>

<a id="WangEdges"></a>

<a id="WangFull"></a>

```go
const (
	// WangCorners matches colours on the four diagonal positions; the
	// usual form for overlapping terrains.
	WangCorners WangType = iota
	// WangEdges matches colours on the four edge positions; the usual
	// form for paths and pipes.
	WangEdges
	// WangFull matches all eight positions.
	WangFull
)
```
