# `BB.TUI.Panels.Joints`
[🔗](https://github.com/mcass19/bb_tui/blob/v0.3.0/lib/bb/tui/panels/joints.ex#L1)

Joint control panel — displays joint positions with type, units, and
visual position bars.

Shows each joint's name, type (rev/pri/con), current position in
human-readable units (degrees or mm), and a bar indicating position
within the joint's limits. Simulated joints are marked with a SIM tag.

Pure function — takes state, returns a widget struct.

# `format_limit`

```elixir
@spec format_limit(number(), map()) :: String.t()
```

Formats a joint limit value in human-readable units (degrees or mm).

## Examples

    iex> BB.TUI.Panels.Joints.format_limit(1.5708, %{type: :revolute})
    "90"

    iex> BB.TUI.Panels.Joints.format_limit(-1.5708, %{type: :revolute})
    "-90"

    iex> BB.TUI.Panels.Joints.format_limit(0.037, %{type: :prismatic})
    "37"

# `format_name`

```elixir
@spec format_name(atom(), map()) :: String.t()
```

Formats a joint name, appending SIM tag for simulated joints.

A joint is simulated when it has an empty actuators list.

## Examples

    iex> BB.TUI.Panels.Joints.format_name(:elbow, %{actuators: []})
    "elbow SIM"

    iex> BB.TUI.Panels.Joints.format_name(:elbow, %{actuators: [:motor]})
    "elbow"

    iex> BB.TUI.Panels.Joints.format_name(:elbow, %{})
    "elbow"

# `format_position`

```elixir
@spec format_position(number() | nil, map()) :: String.t()
```

Formats position with appropriate units based on joint type.

Revolute/continuous joints show degrees, prismatic joints show millimeters.

## Examples

    iex> BB.TUI.Panels.Joints.format_position(1.5708, %{type: :revolute})
    "90.0°"

    iex> BB.TUI.Panels.Joints.format_position(0.030, %{type: :prismatic})
    "30.0 mm"

    iex> BB.TUI.Panels.Joints.format_position(nil, %{type: :revolute})
    "N/A"

# `format_type`

```elixir
@spec format_type(map()) :: String.t()
```

Formats the joint type as a short label.

## Examples

    iex> BB.TUI.Panels.Joints.format_type(%{type: :revolute})
    "rev"

    iex> BB.TUI.Panels.Joints.format_type(%{type: :prismatic})
    "pri"

    iex> BB.TUI.Panels.Joints.format_type(%{type: :continuous})
    "con"

    iex> BB.TUI.Panels.Joints.format_type(%{type: :fixed})
    "fix"

    iex> BB.TUI.Panels.Joints.format_type(%{})
    "-"

# `render`

```elixir
@spec render(BB.TUI.State.t(), boolean()) :: struct()
```

Renders the joints panel as a Table widget with columns for
name, type, position with units, and a visual position bar.

## Examples

    iex> entries = %{shoulder: %{joint: %{name: :shoulder, type: :revolute, limits: %{lower: -1.57, upper: 1.57}}, position: 0.0}}
    iex> state = %BB.TUI.State{joints: %BB.TUI.State.Joints{entries: entries}}
    iex> %ExRatatui.Widgets.Table{header: header} = BB.TUI.Panels.Joints.render(state, false)
    iex> header
    ["Joint", "Type", "Position", "Target"]

---

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