# `Monty.Frameworks.Utils.SpatialArithmetics.TangentFrame`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/frameworks/utils/spatial_arithmetics/tangent_frame.ex#L1)

An orthonormal `(u, v, n)` frame riding on a surface.

Mirrors `tbp/monty/frameworks/utils/spatial_arithmetics.py::TangentFrame`.
`n` is the surface normal, `u` a tangent direction, `v` completes the
right-handed set. As the sensor moves over a curved surface, `transport/2`
turns the frame by exactly the rotation that keeps it in the new tangent
plane — parallel transport, which is what lets Monty "unroll" a curved
surface into a flat one.

A surface normal names a plane but not a basis within it, so the initial `u`
is picked by crossing an arbitrary axis with the normal. That choice is
invisible to Monty, which only ever asks for relative directions.

# `t`

```elixir
@type t() :: %Monty.Frameworks.Utils.SpatialArithmetics.TangentFrame{
  basis_u: vector(),
  basis_v: vector(),
  normal: vector()
}
```

# `vector`

```elixir
@type vector() :: [float()]
```

# `basis_u`

```elixir
@spec basis_u(t()) :: vector()
```

The horizontal tangent direction.

# `basis_v`

```elixir
@spec basis_v(t()) :: vector()
```

The vertical tangent direction.

# `new`

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

The frame whose normal is `surface_normal`.

When `surface_normal` is (anti)parallel to the arbitrary axis, crossing them
would give zero, so a second axis is used instead.

# `normal`

```elixir
@spec normal(t()) :: vector()
```

The surface normal this frame rides on.

# `transport`

```elixir
@spec transport(t(), vector()) :: t()
```

The frame at `new_normal`, with its basis carried over by the rotation between
the two normals.

Normals that already agree need no rotation, and `u` is left alone. Otherwise
`u` is turned about the axis shared by both tangent planes — the axis
perpendicular to both normals — so it stays in the plane as the plane tilts.

The result is re-orthonormalised against `new_normal`: a thousand transports
over a curved surface would otherwise accumulate enough floating point drift
for `u` to leave the tangent plane.

---

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