# `ExMujoco`
[🔗](https://github.com/d-led/ex.mujoco/blob/main/lib/ex_mujoco.ex#L1)

MuJoCo, from Elixir.

    {:ok, model} = ExMujoco.load_xml("model/scene.xml")
    data = ExMujoco.data(model)

    ExMujoco.forward(data)
    ExMujoco.step(data)

    ExMujoco.Data.qpos(data) |> ExMujoco.Tensor.to_list()
    ExMujoco.Model.geom_size(model) |> ExMujoco.Tensor.to_list()

A model is compiled and immutable; a data block is the state that moves. Both
are resources, released by the garbage collector, so there is no lifetime to get
wrong: a `Data` holds the `Model` it came from, and neither outlives its user.

Fields are read one at a time — `ExMujoco.Model` and `ExMujoco.Data` have a
function per field of `mjModel` and `mjData`, named as MuJoCo names them, and
answering either a number or an `ExMujoco.Tensor`. Reading copies, so what you
hold does not change when the next step runs.

## Where the code comes from

The NIF is generated from MuJoCo's own headers: one binding per field and per
C function in `c_src/manifest.exs`. `docs/coverage.md` says what is bound and
what is not, and why. The hand-written half —
[`c_src/ex_mujoco_nif.cpp`](https://github.com/d-led/ex.mujoco) — is the part
where each call needs a decision: which buffer, whose lifetime.

## What is not here yet

`mjSpec` — building a model in memory, rather than compiling XML — and offscreen
rendering. Both are in `docs/plan.md` with the reason they are their own
phase: a scene needs a GL context per platform, and that is a different job
from stepping physics.

## Errors

MuJoCo reports a fatal error by not returning, which inside a NIF would abort
the VM. It is intercepted and arrives as `ExMujoco.Error` with MuJoCo's own
message, so a bad model is an exception in one process and not a dead node.

# `data`

```elixir
@type data() :: ExMujoco.Data.t()
```

An `mjData` and the model it belongs to.

# `model`

```elixir
@type model() :: ExMujoco.Model.t()
```

A compiled `mjModel`.

# `contact_force`

```elixir
@spec contact_force(data(), non_neg_integer()) :: ExMujoco.Tensor.t()
```

The six numbers of one contact's force, as an `ExMujoco.Tensor`.

MuJoCo writes them into an array the caller owns, which is why this is not a
generated binding: there is no array to hand it.

# `data`

```elixir
@spec data(model()) :: data()
```

A fresh `mjData` for a model, at the model's initial state.

# `forward`

```elixir
@spec forward(data()) :: :ok
```

Position and velocity kinematics without stepping — `mj_forward`.

What to call after writing `qpos`, `qvel` or `ctrl` and before reading anything
that depends on them.

# `header_version`

```elixir
@spec header_version() :: non_neg_integer()
```

The version of the headers this NIF was generated from.

The two are compared at load; if they disagree the NIF does not load.

# `load_string`

```elixir
@spec load_string(String.t()) :: {:ok, model()} | {:error, ExMujoco.Error.t()}
```

Compiles a model from MJCF in memory.

`load_xml/1` is the better choice when the model can name its own files; this
one has no directory to resolve includes against.

# `load_xml`

```elixir
@spec load_xml(Path.t()) :: {:ok, model()} | {:error, ExMujoco.Error.t()}
```

Compiles a model from an MJCF file.

Every file it includes is resolved relative to its own directory, as MuJoCo
does.

# `put_state`

```elixir
@spec put_state(data(), integer(), binary()) :: :ok
```

Writes a state read by `state/2` back into the data.

The binary has to be the size that signature implies; anything else is refused
with an `ArgumentError` naming both sizes, rather than written past the end of
it.

# `reset`

```elixir
@spec reset(data()) :: :ok
```

Puts the data back to the model's initial state.

# `state`

```elixir
@spec state(data(), integer()) :: binary()
```

The whole integration state, as a binary of float64.

The default signature is `mjSTATE_INTEGRATION`, which is every element MuJoCo
has a slot for; `state/2` takes any other `mjtState` combination, e.g.
`ExMujoco.Types.State.physics()` for just the generalized coordinates.

# `state_size`

```elixir
@spec state_size(model(), integer()) :: non_neg_integer()
```

How many float64 values a state signature holds.

# `step`

```elixir
@spec step(data()) :: :ok
```

One simulation step — `mj_step`.

# `version`

```elixir
@spec version() :: non_neg_integer()
```

The MuJoCo library's version, as MuJoCo numbers it: `4000001` is 4.0.1.

---

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