# Quat

> Talos 0.7 · Since 0.6

Quaternion `[x, y, z, w]`.

## Signature

```ts
export type Quat = [number, number, number, number]
```

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

## Example

The tested fixture `crates/editor/templates/behaviours/rotate.ts`.

```ts
import type { Behavior, Quat } from "@argus/api"

/**
 * Spins the entity it is attached to, at a steady rate, about one axis.
 *
 * The rotation is written as a whole quaternion rather than accumulated onto the one that is
 * there: a quaternion multiplied by itself sixty times a second drifts off the unit sphere, and
 * an entity whose rotation is no longer a unit quaternion draws sheared. The angle is kept in
 * `ctx.state` and the quaternion is built from it each tick, so the value on the entity is
 * always exact and a hot reload picks the spin up where it left off.
 *
 * On a static collider this moves the mesh and not the navmesh, which is built once from the
 * scene as authored — so spin decoration, not the floor somebody has to walk on.
 */
interface State {
  /** Radians turned so far, wrapped to one turn so it cannot grow without bound. */
  angle: number
}

interface Props {
  /** Degrees per second. Negative spins the other way. Default 90. */
  degreesPerSecond?: number
  /** Axis to spin about: `x`, `y` or `z`. Default `y`. */
  axis?: "x" | "y" | "z"
}

const TAU = Math.PI * 2

/** A unit quaternion of `angle` radians about one axis, as `[x, y, z, w]`. */
function about(axis: "x" | "y" | "z", angle: number): Quat {
  const half = angle / 2
  const s = Math.sin(half)
  const w = Math.cos(half)
  if (axis === "x") return [s, 0, 0, w]
  if (axis === "z") return [0, 0, s, w]
  return [0, s, 0, w]
}

export default {
  init(ctx) {
    if (typeof ctx.state.angle !== "number") ctx.state.angle = 0
  },
  update(ctx, dt) {
    const rate = typeof ctx.props.degreesPerSecond === "number" ? ctx.props.degreesPerSecond : 90
    ctx.state.angle = (ctx.state.angle + (rate * Math.PI) / 180 * dt) % TAU
    ctx.entity.rotation = about(ctx.props.axis ?? "y", ctx.state.angle)
  },
} satisfies Behavior<State, Props>
```
