# Joint

> Talos next · Since 0.6

Present on `ctx.joint` while the entity's `joint` component is in force: a hinge, a slide or a weld from this entity's body to another's or to the world.

Null without a `joint` component, and from the moment the joint is gone — its connected entity despawned, which is how a game breaks a joint on purpose. **The coordinate.** `position` is radians about the axis (revolute) or metres along it (prismatic), 0 in the pose the joint was built in — the scene's pose, for a scene's joint — and positive for a right-handed turn about the axis or a slide along it. `limits` and a position motor's target are in the same coordinate. A fixed joint reads 0. **Motors** are acceleration-based: `stiffness` and `damping` are scaled by the bodies' inertia, so one tuning moves a light door and a heavy gate alike; `maxForce` is the physical cap. Defaults: velocity mode damping 50, position mode stiffness 100 and damping 20. A motor or limits change is in force from the next physics step (the top of the next tick). **Refusals.** A NaN or infinite number, a negative `maxForce`/`stiffness`/`damping`, limits with min above max, an unknown `mode`, or a first motor with no `maxForce`, throw a `RangeError`. `setMotor`, `stopMotor` and `setLimits` on a fixed joint, or through a handle kept after its joint went, change nothing, do not throw, and are counted on `GET /perf` → `unwired.joint_ignored`. `ts // A door that swings to 90° when the player is near and closes when they leave. update(ctx) { const near = ctx.entity.distanceTo(ctx.world.find("player")!) < 2 ctx.joint?.setMotor(near ? Math.PI / 2 : 0, { mode: "position", maxForce: 200 }) } ```

## Signature

```ts
export interface Joint {
  /**
   * Drive the free axis: to a speed (`mode: "velocity"`) or to a place (`mode: "position"`). The mode defaults
   * to the current motor's, else velocity; a mode change takes that mode's default stiffness and damping.
   */
  setMotor(target: number, options?: JointMotorOptions): void
  /** Switch the motor off: the axis is free again, within any limits. */
  stopMotor(): void
  /** Replace the limits: `[min, max]` in radians or metres from the rest pose, or null for none. */
  setLimits(limits: Vec2 | null): void
  /** What the joint leaves free; null once the joint is gone. */
  readonly kind: JointKind | null
  /** The connected entity, or null for a joint to the world (and once the joint is gone). */
  readonly connectedTo: EntityHandle | null
  /** Radians (revolute) or metres (prismatic) from the pose the joint was built in. */
  readonly position: number
  /** rad/s or m/s along the free axis. */
  readonly velocity: number
  /** `[min, max]`, or null for none. */
  readonly limits: Vec2 | null
  /** The motor, or null for none. */
  readonly motor: JointMotorState | null
}
```

Import: `import type { Joint } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `setMotor` | `setMotor(target: number, options?: JointMotorOptions): void` | Drive the free axis: to a speed (`mode: "velocity"`) or to a place (`mode: "position"`). The mode defaults to the current motor's, else velocity; a mode change takes that mode's default stiffness and damping. |
| `stopMotor` | `stopMotor(): void` | Switch the motor off: the axis is free again, within any limits. |
| `setLimits` | `setLimits(limits: Vec2 \| null): void` | Replace the limits: `[min, max]` in radians or metres from the rest pose, or null for none. |
| `kind` | `readonly kind: JointKind \| null` | What the joint leaves free; null once the joint is gone. |
| `connectedTo` | `readonly connectedTo: EntityHandle \| null` | The connected entity, or null for a joint to the world (and once the joint is gone). |
| `position` | `readonly position: number` | Radians (revolute) or metres (prismatic) from the pose the joint was built in. |
| `velocity` | `readonly velocity: number` | rad/s or m/s along the free axis. |
| `limits` | `readonly limits: Vec2 \| null` | `[min, max]`, or null for none. |
| `motor` | `readonly motor: JointMotorState \| null` | The motor, or null for none. |

## Example

The tested fixture `fixtures/host/door.ts`.

```ts
import type { Behavior } from "@argus/api"
// docs/API.md, "Joints": a door on a hinge that swings open while the player is near and closes
// behind them. Typechecked by packages/api's tests.
export default {
  update(ctx) {
    const player = ctx.world.find("player")
    const near = player !== null && ctx.entity.distanceTo(player) < 2
    ctx.joint?.setMotor(near ? Math.PI / 2 : 0, { mode: "position", maxForce: 200 })
  },
} satisfies Behavior
```
