# `Linocut.Pose`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/linocut/lib/linocut/pose.ex#L1)

A figure's joints, and the OpenPose skeleton a ControlNet reads them as.

    Linocut.Pose.skeleton(Linocut.Pose.walk(0.25), {768, 1024})

A pose is a map of joint name to `{x, y}` with both between `0.0` and `1.0`, measured
from the picture's top-left. `skeleton/3` draws it in OpenPose's own eighteen-point
colours on black, which is what `openpose` ControlNets are trained on; feed that picture
to a model with `preprocess: "as_is"` so it is used as drawn rather than edge-detected.

`walk/2` gives the joints of a side-on walk at a point in the cycle, so a caller can ask
for as many frames as it wants and get a cycle that closes.

# `joint`

```elixir
@type joint() ::
  :nose
  | :neck
  | :right_shoulder
  | :right_elbow
  | :right_wrist
  | :left_shoulder
  | :left_elbow
  | :left_wrist
  | :right_hip
  | :right_knee
  | :right_ankle
  | :left_hip
  | :left_knee
  | :left_ankle
  | :right_eye
  | :left_eye
  | :right_ear
  | :left_ear
```

# `t`

```elixir
@type t() :: %{required(joint()) =&gt; {float(), float()}}
```

# `joints`

```elixir
@spec joints() :: [joint()]
```

The joints a pose names, in OpenPose's own order.

# `skeleton`

```elixir
@spec skeleton(t(), {pos_integer(), pos_integer()}, keyword()) ::
  FrenchCurve.Raster.t()
```

The pose drawn as an OpenPose skeleton on black, `width` by `height` pixels.

## Options

  * `:thickness` — how wide a limb is drawn, as a share of the picture's smaller side.
    Default `0.018`

# `walk`

```elixir
@spec walk(number(), keyword()) :: t()
```

The joints of a side-on walk at `phase`, a point in the cycle between `0.0` and `1.0`.

The figure faces `+x`. The cycle closes: `walk(0.0)` and `walk(1.0)` are the same pose.

## Options

  * `:at` — `{x, y}` the hips sit at. Default `{0.5, 0.52}`

---

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