# `Cauldron2D.Projection.Iso`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/cauldron_2d/lib/cauldron_2d/projection/iso.ex#L1)

Composes a view of the world as an isometric picture: floors as diamonds, standing
arts as blocks with a top and two shaded sides, movers and flat overlays as upright
sprites, painted back to front.

    Cauldron2D.Projection.Iso.compose(:my_game,
      focus: {12.5, 8.5},
      cell: &MyGame.cell/1,
      size: {320, 200},
      facing: 1,
      movers: [{:hero, {12.5, 8.5}, heading}]
    )

A tile of `t` pixels is drawn as a diamond `2t` wide and `t * tilt` tall, rounded to an
even number of pixels so neighbouring diamonds meet, and a storey is `t` tall. The
focus sits at the centre of the picture. `facing` turns the world a quarter turn at a
time by default: at facing `0` world `+x` runs down-right and `+y` down-left.
`facings` makes a turn out of more steps than four.

Up to `32` facings the view is blitted from stamps, one set per art per facing, built
on first use and kept in the atlas cache; a `:facing` between two steps is drawn at the
nearer. Above `32`, and with `facings: :free`, the ground and the standing arts are
sampled a pixel at a time instead, which turns to any angle at all and costs upwards of
fifteen times as much a frame.

## Options

  * `:focus` — `{x, y}` in tiles, the world point at the picture's centre, required
  * `:cell` — `fn {x, y} -> cell end`, as `Cauldron2D.Surface` takes cells, required
  * `:size` — `{width, height}` of the picture in pixels, required
  * `:facing` — which step of a turn the view is at, `0` to `facings - 1`, clockwise.
    A fraction is drawn at the nearer step, unless the view samples, where it turns by
    part of a step. Default `0`
  * `:facings` — how many steps make a full turn, at least `4`; `:free` for a view that
    turns to any angle. Up to `32` the view is stamped, above it sampled. Default `4`
  * `:tilt` — the diamond's height as a fraction of the tile size, `0.5..1.5`.
    Default `1.0`, a 2:1 diamond
  * `:shrink` — `1`, `2` or `4`; the whole picture at that fraction of its size, so
    the same pixels show that many more tiles. Default `1`
  * `:movers` — `[{art, {x, y}}]` or `[{art, {x, y}, heading}]` in tiles, `heading` in
    radians with `0` along `+x`. A mover shows its `:front` pose when heading
    towards the camera and `:back` when away, mirrored when heading left; with no
    heading it faces the camera. Default `[]`
  * `:labels` — `[{text, {x, y}, colour}]` or with a fourth element of options as
    `Cauldron2D.Surface.compose/4` takes them, positioned by the tile. Default `[]`
  * `:volumes` — coloured regions of space, as `Cauldron2D.Projection.Volume`
    describes them, each tinting what is drawn behind it. Default `[]`
  * `:at` — the time in milliseconds an animated art's frame is chosen for. Default `0`

`project/2`, `unproject/2` and `tile_at/2` map between picture pixels and world tiles
for the same options.

# `option`

```elixir
@type option() ::
  {:focus, {number(), number()}}
  | {:cell, ({integer(), integer()} -&gt; Cauldron2D.Surface.cell())}
  | {:size, {pos_integer(), pos_integer()}}
  | {:facing, 0..3}
  | {:tilt, number()}
  | {:shrink, 1 | 2 | 4}
  | {:movers, [tuple()]}
  | {:labels, [tuple()]}
  | {:volumes, [map()]}
  | {:at, integer()}
```

# `cells`

```elixir
@spec cells(Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(), [option()]) :: [
  {{integer(), integer()}, term()}
]
```

The cells this picture shows, each as `{position, tile}` with the tile the `:cell`
function gave for it.

Cells the function answered `:void` for are left out, and so is everything outside the
picture.

# `compose`

```elixir
@spec compose(Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(), [option()]) ::
  FrenchCurve.Raster.t()
```

The picture, as the module documentation describes.

# `ground_map`

```elixir
@spec ground_map(Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(), [option()]) ::
  {{float(), float()}, {float(), float()}, {float(), float()}}
```

The ground as a straight walk across the picture: `{origin, across, down}`, the world
point at the top-left pixel and the world it moves one pixel right and one pixel down.

The ground map is exact, so `origin + px * across + py * down` is the world point
`unproject/3` answers for `{px, py}`. A caller drawing a grid can take the map once and
add.

# `project`

```elixir
@spec project(Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(), {number(), number()}, [
  option()
]) ::
  {float(), float()}
```

Where world point `{x, y}` lands in the picture, in pixels from its top-left corner.

# `tile_at`

```elixir
@spec tile_at(Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(), {number(), number()}, [
  option()
]) ::
  {integer(), integer()}
```

The tile under picture pixel `{px, py}`.

# `unproject`

```elixir
@spec unproject(
  Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(),
  {number(), number()},
  [option()]
) ::
  {float(), float()}
```

The world point under picture pixel `{px, py}`, on the ground.

# `up`

```elixir
@spec up([option()]) :: float()
```

The heading in radians, `0` along `+x`, a game walks for the `:up` action at the
`:facing` and `:facings` given, so that pressing up moves the same way across the
picture however far the view has turned. `:down` is against it and `:left` and
`:right` across it.

    Cauldron2D.Projection.Iso.up(facing: 5, facings: 16)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
