# `Monty.Frameworks.Models.Salience.MotorPolicy.LookAtGoal`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/frameworks/models/salience/motor_policy/look_at_goal.ex#L1)

A policy that turns to look at a goal: the agent yaws, the sensor pitches.

Mirrors `tbp/monty/frameworks/models/salience/motor_policy.py::LookAtGoal`,
and assumes the body it drives is a two-axis gimbal — the agent turns left and
right about the vertical axis, the sensor on it looks up and down about the
horizontal one. That is the distant agent: a camera on a neck.

Two actions come out of one step, and only ever these two: `TurnLeft` by the
target's azimuth, and `LookUp` by the target's elevation measured *from where
the sensor is already pointing*. Turning right and looking down are negative
degrees on the same actions, which is why the action list has two entries and
not four.

Everything is computed in frames, not in degrees, until the last line: the
target is moved into the agent's frame by the agent's inverse rotation, and
then into the sensor's frame by the sensor's, because the sensor may be
pitched relative to the body it sits on.

# `t`

```elixir
@type t() :: %Monty.Frameworks.Models.Salience.MotorPolicy.LookAtGoal{
  agent_id: atom() | String.t(),
  sensor_id: atom() | String.t()
}
```

# `call`

```elixir
@spec call(
  t(),
  Monty.Context.t(),
  map() | nil,
  Monty.Frameworks.Models.MotorSystemState.t(),
  Monty.Cmp.Message.t() | nil,
  Monty.Cmp.Goal.t() | nil
) :: {Monty.Frameworks.Models.MotorPolicies.MotorPolicyResult.t(), t()}
```

The turn and the pitch that put the sensor on the goal.

A goal at the sensor is refused: there is no azimuth to turn to. Both refusals
and the missing goal become a warning and no action when the run suppresses
runtime errors.

# `new`

```elixir
@spec new(keyword()) :: t()
```

A look-at policy for `:agent_id`, aiming the sensor `:sensor_id`.

# `reset`

```elixir
@spec reset(t()) :: t()
```

Nothing to forget: where the sensor points is the world's business.

# `state_dict`

```elixir
@spec state_dict(t()) :: map()
```

Which body and which sensor it aims.

---

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