---
title: "Transform"
description: "The Transform namespace — the engine's Luau API reference for Transform."
section: "API Reference"
slug: "api-transform"
canonical: "https://origozero.ai/docs/api-transform"
updated: "2026-09-05T23:13:47.399964314+00:00"
tags: ["api", "reference"]
---

# Transform

The `Transform` namespace — 75 functions.

## globals/Transform/direction {#globals-transform-direction}

```lua
Transform.direction(fromX: number, fromY: number, fromZ: number, toX: number, toY: number, toZ: number) -> (number, number, number)
```

Normalized direction vector from point A to point B. Returns
zeros when the two points coincide (within ~0.001 units).

**Parameters**

- `fromX` `number` — From x.
- `fromY` `number` — From y.
- `fromZ` `number` — From z.
- `toX` `number` — To x.
- `toY` `number` — To y.
- `toZ` `number` — To z.

**Returns** `(number, number, number)` — Three numbers `dx, dy, dz` — the unit direction.

```lua
local dx, dy, dz = Transform.direction(0, 0, 0, 1, 0, 0)
```

## globals/Transform/directionBetween {#globals-transform-directionbetween}

```lua
Transform.directionBetween(entityA: string | EntityRef, entityB: string | EntityRef) -> (number, number, number)
```

Normalized world-space direction from one entity to another, read
from their world positions. Returns zeros if either entity can't be
resolved.

**Parameters**

- `entityA` `string | EntityRef` — Source entity (id string or proxy).
- `entityB` `string | EntityRef` — Target entity (id string or proxy).

**Returns** `(number, number, number)` — Three numbers `dx, dy, dz` — the unit direction.

```lua
local dx, dy, dz = Transform.directionBetween("cam", "target")
```

## globals/Transform/distance {#globals-transform-distance}

```lua
Transform.distance(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number) -> number
```

Euclidean distance between two world-space positions.

**Parameters**

- `x1` `number` — First point x.
- `y1` `number` — First point y.
- `z1` `number` — First point z.
- `x2` `number` — Second point x.
- `y2` `number` — Second point y.
- `z2` `number` — Second point z.

**Returns** `number` — The Euclidean distance.

```lua
local d = Transform.distance(0, 0, 0, 1, 1, 1)
```

## globals/Transform/distanceBetween {#globals-transform-distancebetween}

```lua
Transform.distanceBetween(entityA: string | EntityRef, entityB: string | EntityRef) -> number?
```

Distance between two entities in world space. Each entity's world
position is what is measured, so a parent's offset counts toward the
distance the way the scene shows it.

**Parameters**

- `entityA` `string | EntityRef` — First entity (id string or proxy).
- `entityB` `string | EntityRef` — Second entity (id string or proxy).

**Returns** `number?` — The Euclidean distance, or `nil` when either entity can't be resolved.

```lua
local d = Transform.distanceBetween("cam", "box")
```

## globals/Transform/euler {#globals-transform-euler}

```lua
Transform.euler(qx: number, qy: number, qz: number, qw: number) -> (number, number, number)
```

Convert quaternion to euler angles (yaw, pitch, roll) in radians.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.

**Returns** `(number, number, number)` — Three numbers `yaw, pitch, roll` (Y, X, Z rotations).

```lua
local yaw, pitch, roll = Transform.euler(0, 0, 0, 1)
```

## globals/Transform/eulerToQuat {#globals-transform-eulertoquat}

```lua
Transform.eulerToQuat(yaw: number, pitch: number?, roll: number?) -> (number, number, number, number)
```

Identity-aware overload of euler-to-quaternion. Uses the negative-yaw
convention shared with `quatFromYaw`, `quatFromYawPitch`, `lookAtQuat`, and
`T.euler` extraction — so `T.euler(T.eulerToQuat(y, p, r))` returns
`(y, p, r)`. Order is yaw (Y) then pitch (X) then roll (Z).

**Parameters**

- `yaw` `number` — Y-axis rotation in radians.
- `pitch` `number` _(optional)_ — X-axis rotation in radians. Defaults to 0.
- `roll` `number` _(optional)_ — Z-axis rotation in radians. Defaults to 0.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw`.

```lua
local qx, qy, qz, qw = Transform.eulerToQuat(math.pi / 2)
```

## globals/Transform/lerp {#globals-transform-lerp}

```lua
Transform.lerp(ax: number, ay: number, az: number, bx: number, by: number, bz: number, t: number) -> (number, number, number)
```

Linearly interpolate between two positions.

**Parameters**

- `ax` `number` — Start x.
- `ay` `number` — Start y.
- `az` `number` — Start z.
- `bx` `number` — End x.
- `by` `number` — End y.
- `bz` `number` — End z.
- `t` `number` — Interpolation factor `[0, 1]`.

**Returns** `(number, number, number)` — Three numbers — the interpolated position.

```lua
local x, y, z = Transform.lerp(0, 0, 0, 1, 1, 1, 0.5)
```

## globals/Transform/lerp1 {#globals-transform-lerp1}

```lua
Transform.lerp1(a: number, b: number, t: number) -> number
```

Linearly interpolate two scalars.

**Parameters**

- `a` `number` — Start value.
- `b` `number` — End value.
- `t` `number` — Interpolation factor `[0, 1]`.

**Returns** `number` — The interpolated scalar.

```lua
local v = Transform.lerp1(0, 10, 0.5)
```

## globals/Transform/lerpAngle {#globals-transform-lerpangle}

```lua
Transform.lerpAngle(a: number, b: number, t: number) -> number
```

Lerp between two angles via the shortest arc; returns a value in `[-pi, pi]`.

**Parameters**

- `a` `number` — Start angle in radians.
- `b` `number` — End angle in radians.
- `t` `number` — Interpolation factor `[0, 1]`.

**Returns** `number` — The interpolated angle, normalized to `[-pi, pi]`.

```lua
local a = Transform.lerpAngle(0, math.pi, 0.5)
```

## globals/Transform/localToWorld {#globals-transform-localtoworld}

```lua
Transform.localToWorld(px: number, py: number, pz: number, pqx: number, pqy: number, pqz: number, pqw: number, lx: number, ly: number, lz: number) -> (number, number, number)
```

Transform a local-space position into world space using a parent pose.

**Parameters**

- `px` `number` — Parent position x.
- `py` `number` — Parent position y.
- `pz` `number` — Parent position z.
- `pqx` `number` — Parent rotation x.
- `pqy` `number` — Parent rotation y.
- `pqz` `number` — Parent rotation z.
- `pqw` `number` — Parent rotation w.
- `lx` `number` — Local x.
- `ly` `number` — Local y.
- `lz` `number` — Local z.

**Returns** `(number, number, number)` — Three numbers `wx, wy, wz` — the world position.

```lua
local wx, wy, wz = Transform.localToWorld(px, py, pz, pqx, pqy, pqz, pqw, lx, ly, lz)
```

## globals/Transform/lookAt {#globals-transform-lookat}

```lua
Transform.lookAt(entityOrId: string | EntityRef, txOrTarget: any?, ty: any?, tz: number?, up: any?) -> (boolean, string?)
```

Make an entity face a world position. The target slot accepts three
explicit coordinates, one point as `{ x, y, z }` / `{ x =, y =, z = }` / a
vector, or an entity — an id string, an entity NAME, or a proxy — whose
WORLD position is resolved. A table carrying an entity id reads as that
entity; any other table reads as the point it spells. The subject slot
takes the three entity spellings.
Everything here is world space: the subject and the target are
read as `entity(id).position` and the aim is written as
`entity(id).rotation`, so a parent under either one moves the entity and
the aim still lands on the point named.
Returns whether the rotation was written, so a caller that named an entity
the scene does not carry learns the aim did not happen instead of reading
a stale orientation back as the answer.

**Parameters**

- `entityOrId` `string | EntityRef` — Entity id, name, or proxy for the entity to rotate.
- `txOrTarget` `any` _(optional)_ — A number (world x), a point table, or an entity id / name /
proxy whose world position is resolved as the look-at target.
- `ty` `any` _(optional)_ — World y of the target. Omitted when `txOrTarget` is a point or an entity.
- `tz` `number` _(optional)_ — World z of the target. Omitted when `txOrTarget` is a point or an entity.
- `up` `any` _(optional)_ — Optional world up hint deciding the roll — `{ x, y, z }`,
`{ x =, y =, z = }` or a vector. World +Y when omitted. It never bends the
aim; it only says which way is up around it. When the target slot is an
entity or a point this is the third argument, and when it is coordinates
the fifth.

**Returns** `(boolean, string?)` — True when the entity's world rotation was written, and nil for the second value. The target and up slots take any value, because naming which of the shapes arrived is this call's own job: a value that is none of them comes back as a reason rather than as an error raised out of the argument check. False plus a reason otherwise: `"unresolved"` when a reference names no entity, `"no-transform"` when one carries no transform, `"incomplete-target"` when the target spells no point — coordinates with a y or z missing, or a table carrying neither three numbers nor x/y/z, `"incomplete-up"` when the up hint spells none either, `"degenerate"` when the two points coincide so no facing direction exists.

```lua
Transform.lookAt("cam", 0, 1, 0)
Transform.lookAt("cam", "box")  -- resolve target entity position
Transform.lookAt(cam, box)      -- entity proxies for both
Transform.lookAt("cam", { 0, 1, 0 })         -- one point table
Transform.lookAt("cam", "box", { 0, 0, 1 })  -- rolled to a +Z up
```

## globals/Transform/lookAtQuat {#globals-transform-lookatquat}

```lua
Transform.lookAtQuat(fx: number, fy: number, fz: number, tx: number, ty: number, tz: number) -> (number?, number?, number?, number?)
```

Compute quaternion to look from origin position toward a target.
Returns four components `(qx, qy, qz, qw)`, or `nil` when the from
and to points are too close to derive a meaningful direction.

**Parameters**

- `fx` `number` — Origin x.
- `fy` `number` — Origin y.
- `fz` `number` — Origin z.
- `tx` `number` — Target x.
- `ty` `number` — Target y.
- `tz` `number` — Target z.

**Returns** `(number?, number?, number?, number?)` — Four numbers `qx, qy, qz, qw` — the look-at quaternion. Nil when degenerate.

```lua
local qx, qy, qz, qw = Transform.lookAtQuat(0, 0, 0, 1, 0, 1)
```

## globals/Transform/lookRotation {#globals-transform-lookrotation}

```lua
Transform.lookRotation(fx: number, fy: number, fz: number, tx: number, ty: number, tz: number, ux: number?, uy: number?, uz: number?) -> (number?, number?, number?, number?)
```

The rotation that aims an entity standing at one world point at another,
with a world up hint deciding the roll. Where `lookAtQuat` derives the aim
from yaw and pitch alone — clamping the pitch just short of vertical, so a
point directly overhead comes back a twentieth of a degree off — this builds
all three axes, so the aim lands on the point at any elevation and straight
up and straight down are ordinary cases.
The aimed axis is the entity's local -Z, the same forward `quatFromBasis`,
`Transform.lookAt` and `entity(id):lookAt` state and the direction
`entity(id).transform.forward` reads back.
The up hint is a world direction the entity's own +Y is turned toward as
far as the aim allows; it never bends the forward axis. A hint parallel to
the aim leaves the roll undetermined, and a hint of no length names no
direction — both fall back to a stable roll rather than a NaN.

**Parameters**

- `fx` `number` — Eye x — where the entity stands.
- `fy` `number` — Eye y.
- `fz` `number` — Eye z.
- `tx` `number` — Target x — the world point it faces.
- `ty` `number` — Target y.
- `tz` `number` — Target z.
- `ux` `number` _(optional)_ — Up hint x. World +Y when the hint is omitted.
- `uy` `number` _(optional)_ — Up hint y.
- `uz` `number` _(optional)_ — Up hint z.

**Returns** `(number?, number?, number?, number?)` — Four numbers `qx, qy, qz, qw`. Nil when the eye and the target coincide, so no facing direction exists.

```lua
local qx, qy, qz, qw = Transform.lookRotation(0, 2, 10, 0, 1, 0)
entity("cam").rotation = { Transform.lookRotation(0, 2, 10, 0, 1, 0) }
-- a dutch tilt: the same aim, rolled by leaning the up hint
local q = { Transform.lookRotation(0, 2, 10, 0, 1, 0, 0.2, 1, 0) }
```

## globals/Transform/normalizeAngle {#globals-transform-normalizeangle}

```lua
Transform.normalizeAngle(a: number) -> number
```

Normalize an angle into `[-pi, pi]`.

**Parameters**

- `a` `number` — The angle in radians.

**Returns** `number` — The same angle wrapped into `[-pi, pi]`.

```lua
local a = Transform.normalizeAngle(3 * math.pi)
```

## globals/Transform/orbit {#globals-transform-orbit}

```lua
Transform.orbit(centerX: number, centerY: number, centerZ: number, radius: number, height: number, angle: number) -> (number, number, number, number, number, number, number)
```

Position + rotation for orbiting around a center point. Returns
the world position followed by the orientation that faces the center.

**Parameters**

- `centerX` `number` — Center x.
- `centerY` `number` — Center y.
- `centerZ` `number` — Center z.
- `radius` `number` — Horizontal distance from the center.
- `height` `number` — Vertical offset from `centerY`.
- `angle` `number` — Orbital angle in radians.

**Returns** `(number, number, number, number, number, number, number)` — Seven numbers `x, y, z, qx, qy, qz, qw`.

```lua
local x, y, z, qx, qy, qz, qw = Transform.orbit(0, 1, 0, 5, 2, t)
```

## globals/Transform/quatFromAxisAngle {#globals-transform-quatfromaxisangle}

```lua
Transform.quatFromAxisAngle(ax: number, ay: number, az: number, angle: number) -> (number, number, number, number)
```

Create quaternion from axis and angle (radians). Returns the
identity quaternion when the axis is degenerate (length < 0.001).

**Parameters**

- `ax` `number` — Axis x.
- `ay` `number` — Axis y.
- `az` `number` — Axis z.
- `angle` `number` — Rotation angle in radians.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw`.

```lua
local qx, qy, qz, qw = Transform.quatFromAxisAngle(0, 1, 0, math.pi)
```

## globals/Transform/quatFromBasis {#globals-transform-quatfrombasis}

```lua
Transform.quatFromBasis(rx: number, ry: number, rz: number, ux: number, uy: number, uz: number, fx: number, fy: number, fz: number) -> (number, number, number, number)
```

Build the rotation whose right, up and forward ARE the given axes. Where
`lookAtQuat` derives a rotation from a direction alone — yaw and pitch, with
pitch clamped just short of straight up or down and no say in the roll — this
states all three axes, so a view straight down has a defined image-up instead
of whatever the yaw implied. The axes are expected orthonormal and are used as
given: `right` and `up` are the entity's local +X and +Y, `forward` its local
-Z (the direction it faces).

**Parameters**

- `rx` `number` — Right axis x.
- `ry` `number` — Right axis y.
- `rz` `number` — Right axis z.
- `ux` `number` — Up axis x.
- `uy` `number` — Up axis y.
- `uz` `number` — Up axis z.
- `fx` `number` — Forward axis x.
- `fy` `number` — Forward axis y.
- `fz` `number` — Forward axis z.

**Returns** `(number, number, number, number)` — x, y, z, w of the rotation quaternion.

```lua
local qx, qy, qz, qw = Transform.quatFromBasis(1,0,0, 0,1,0, 0,0,-1) -- identity
-- looking straight down with the subject's front toward the top of frame
local qx, qy, qz, qw = Transform.quatFromBasis(1,0,0, 0,0,-1, 0,-1,0)
```

## globals/Transform/quatFromYaw {#globals-transform-quatfromyaw}

```lua
Transform.quatFromYaw(yaw: number) -> (number, number, number, number)
```

Create quaternion from yaw (Y-axis rotation) in radians. Uses the
negative-yaw convention shared with `quatFromYawPitch`, `lookAtQuat`,
and `T.euler` extraction — so `T.euler(T.quatFromYaw(y))` round-trips
to `y`.

**Parameters**

- `yaw` `number` — Rotation in radians around the Y axis.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw`.

```lua
local qx, qy, qz, qw = Transform.quatFromYaw(math.pi / 2)
```

## globals/Transform/quatFromYawPitch {#globals-transform-quatfromyawpitch}

```lua
Transform.quatFromYawPitch(yaw: number, pitch: number) -> (number, number, number, number)
```

Create quaternion from yaw and pitch in radians.

**Parameters**

- `yaw` `number` — Y-axis rotation in radians.
- `pitch` `number` — X-axis rotation in radians.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw`.

```lua
local qx, qy, qz, qw = Transform.quatFromYawPitch(0, math.pi / 4)
```

## globals/Transform/quatIdentity {#globals-transform-quatidentity}

```lua
Transform.quatIdentity() -> (number, number, number, number)
```

Identity quaternion (`0, 0, 0, 1`).

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw` — the identity.

```lua
local qx, qy, qz, qw = Transform.quatIdentity()
```

## globals/Transform/quatInverse {#globals-transform-quatinverse}

```lua
Transform.quatInverse(qx: number, qy: number, qz: number, qw: number) -> (number, number, number, number)
```

Quaternion inverse. Equal to the conjugate for unit quaternions.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw` — the inverse.

```lua
local ix, iy, iz, iw = Transform.quatInverse(qx, qy, qz, qw)
```

## globals/Transform/quatMul {#globals-transform-quatmul}

```lua
Transform.quatMul(ax: number, ay: number, az: number, aw: number, bx: number, by: number, bz: number, bw: number) -> (number, number, number, number)
```

Quaternion multiplication: returns `qa * qb` (composition: rotate
by `qb` then `qa`).

**Parameters**

- `ax` `number` — Left quat x.
- `ay` `number` — Left quat y.
- `az` `number` — Left quat z.
- `aw` `number` — Left quat w.
- `bx` `number` — Right quat x.
- `by` `number` — Right quat y.
- `bz` `number` — Right quat z.
- `bw` `number` — Right quat w.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw` — the composed quaternion.

```lua
local qx, qy, qz, qw = Transform.quatMul(ax, ay, az, aw, bx, by, bz, bw)
```

## globals/Transform/quatRotateVec {#globals-transform-quatrotatevec}

```lua
Transform.quatRotateVec(qx: number, qy: number, qz: number, qw: number, vx: number, vy: number, vz: number) -> (number, number, number)
```

Rotate a 3-vector by a quaternion.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.
- `vx` `number` — Vector x.
- `vy` `number` — Vector y.
- `vz` `number` — Vector z.

**Returns** `(number, number, number)` — Three numbers — the rotated vector.

```lua
local rx, ry, rz = Transform.quatRotateVec(qx, qy, qz, qw, 1, 0, 0)
```

## globals/Transform/quatToEuler {#globals-transform-quattoeuler}

```lua
Transform.quatToEuler(qx: number, qy: number, qz: number, qw: number) -> (number, number, number)
```

Convert quaternion to `(yaw, pitch, roll)`. Alias of `euler` with
the explicit name so callers don't have to remember the order.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.

**Returns** `(number, number, number)` — Three numbers `yaw, pitch, roll` (Y, X, Z rotations).

```lua
local yaw, pitch, roll = Transform.quatToEuler(qx, qy, qz, qw)
```

## globals/Transform/readVec3 {#globals-transform-readvec3}

```lua
Transform.readVec3(value: Vec3Input, label: string?) -> { number }
```

Normalize a vector a caller wrote to a plain `{ x, y, z }` array.
Accepts a positional array `{1, 2, 3}`, a keyed table
`{x =, y =, z =}`, or a live vec handle. Missing components read as 0.
Raises when the value is not a vector; `label` names the caller in
that error.

**Parameters**

- `value` `Vec3Input` — The vector to normalize.
- `label` `string` _(optional)_ — Name reported in the error when the value is not a vector. Defaults to "Transform".

**Returns** `{ number }` — A three-element array `{ x, y, z }`.

```lua
local v = Transform.readVec3({ x = 1, y = 2, z = 3 })
```

## globals/Transform/slerp {#globals-transform-slerp}

```lua
Transform.slerp(ax: number, ay: number, az: number, aw: number, bx: number, by: number, bz: number, bw: number, t: number) -> (number, number, number, number)
```

Spherical linear interpolation between two quaternions. Picks
the shortest path (flips sign if dot < 0). Falls back to
lerp+normalize when the two quats are very close (avoids
div-by-zero on near-parallel inputs).

**Parameters**

- `ax` `number` — Start quaternion x.
- `ay` `number` — Start quaternion y.
- `az` `number` — Start quaternion z.
- `aw` `number` — Start quaternion w.
- `bx` `number` — End quaternion x.
- `by` `number` — End quaternion y.
- `bz` `number` — End quaternion z.
- `bw` `number` — End quaternion w.
- `t` `number` — Interpolation factor `[0, 1]`.

**Returns** `(number, number, number, number)` — Four numbers `qx, qy, qz, qw` — the interpolated unit quaternion.

```lua
local qx, qy, qz, qw = Transform.slerp(0, 0, 0, 1, 1, 0, 0, 0, 0.5)
```

## globals/Transform/snapVec3 {#globals-transform-snapvec3}

```lua
Transform.snapVec3(v: { number }, step: number | Vec3Input) -> { number }
```

Quantize each component of a vector to the nearest multiple of
`step` — a number for uniform steps, or a vector for per-axis steps.
A step of 0 on an axis leaves that axis at its exact value.

**Parameters**

- `v` `{ number }` — The vector to quantize, as `{ x, y, z }`.
- `step` `number | Vec3Input` — Uniform step size, or a per-axis vector of step sizes.

**Returns** `{ number }` — A three-element array `{ x, y, z }` snapped to the step grid.

```lua
local v = Transform.snapVec3({ 1.4, 2.6, -0.4 }, 1)
```

## globals/Transform/toQuaternion {#globals-transform-toquaternion}

```lua
Transform.toQuaternion(rotation: any?, label: string?) -> { number }
```

Normalize a rotation a caller wrote to a `{ qx, qy, qz, qw }`
quaternion. Accepts a quaternion (`{x,y,z,w}` or `{x=,y=,z=,w=}`) or
euler DEGREES (`{pitch,yaw,roll}` or `{pitch=,yaw=,roll=}`), so one
call site takes whichever form the caller finds natural. This is the
reading every rotation-taking surface in the engine shares, so a
quaternion and euler degrees mean the same thing at all of them.
Raises when the value matches no form; `label` names the caller in that
error, and a value that is one of the shapes a quaternion helper returns
is named as such along with the packing it goes in as.

**Parameters**

- `rotation` `any` _(optional)_ — The rotation to normalize, in any form of the `RotationInput` union.
- `label` `string` _(optional)_ — Name reported in the error when the value is not a rotation. Defaults to "Transform".

**Returns** `{ number }` — A four-element array `{ qx, qy, qz, qw }`.

```lua
local q = Transform.toQuaternion({ pitch = 0, yaw = 90, roll = 0 })
```

## globals/Transform/tryQuaternion {#globals-transform-tryquaternion}

```lua
Transform.tryQuaternion(rotation: any?, label: string?) -> ({ number }?, string?)
```

Read a rotation a caller wrote WITHOUT raising: returns the
canonical `{ qx, qy, qz, qw }`, or nil and the message describing what
arrived. The forms are the `RotationInput` union — a quaternion
(`{x,y,z,w}` or `{x=,y=,z=,w=}`) or euler DEGREES (`{pitch,yaw,roll}` or
`{pitch=,yaw=,roll=}`). Takes any value because reporting on a value that
is none of those forms is the whole job; a setter built on this raises the
returned message itself, so the error points at the line that wrote the
value rather than at the reading.

**Parameters**

- `rotation` `any` _(optional)_ — The value to read as a rotation.
- `label` `string` _(optional)_ — Name reported in the message. Defaults to "Transform".

**Returns** `({ number }?, string?)` — The quaternion `{ qx, qy, qz, qw }`, or nil and the message.

```lua
local q, why = Transform.tryQuaternion(value, "myTool")
```

## globals/Transform/vec/add {#globals-transform-vec-add}

```lua
Transform.vec.add(ax: number, ay: number, az: number, bx: number, by: number, bz: number) -> (number, number, number)
```

Component-wise vec3 addition.

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

**Returns** `(number, number, number)` — Three numbers — the sum.

```lua
local x, y, z = Transform.vec.add(1, 2, 3, 4, 5, 6)
```

## globals/Transform/vec/cross {#globals-transform-vec-cross}

```lua
Transform.vec.cross(ax: number, ay: number, az: number, bx: number, by: number, bz: number) -> (number, number, number)
```

Cross product `a x b`.

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

**Returns** `(number, number, number)` — Three numbers `cx, cy, cz` — the cross product.

```lua
local cx, cy, cz = Transform.vec.cross(1, 0, 0, 0, 1, 0)
```

## globals/Transform/vec/dot {#globals-transform-vec-dot}

```lua
Transform.vec.dot(ax: number, ay: number, az: number, bx: number, by: number, bz: number) -> number
```

Dot product of two vec3s.

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

**Returns** `number` — The scalar dot product.

```lua
local d = Transform.vec.dot(1, 0, 0, 0, 1, 0)
```

## globals/Transform/vec/length {#globals-transform-vec-length}

```lua
Transform.vec.length(x: number, y: number, z: number) -> number
```

Euclidean length of a vec3.

**Parameters**

- `x` `number` — Vector x.
- `y` `number` — Vector y.
- `z` `number` — Vector z.

**Returns** `number` — The length.

```lua
local len = Transform.vec.length(1, 2, 3)
```

## globals/Transform/vec/normalize {#globals-transform-vec-normalize}

```lua
Transform.vec.normalize(x: number, y: number, z: number) -> (number, number, number)
```

Normalize a vec3. Returns zeros when the input is degenerate
(length < 1e-8).

**Parameters**

- `x` `number` — Vector x.
- `y` `number` — Vector y.
- `z` `number` — Vector z.

**Returns** `(number, number, number)` — Three numbers — the unit-length vec3.

```lua
local nx, ny, nz = Transform.vec.normalize(0, 5, 0)
```

## globals/Transform/vec/scale {#globals-transform-vec-scale}

```lua
Transform.vec.scale(x: number, y: number, z: number, s: number) -> (number, number, number)
```

Component-wise scalar multiplication of a vec3.

**Parameters**

- `x` `number` — Vector x.
- `y` `number` — Vector y.
- `z` `number` — Vector z.
- `s` `number` — Scalar factor.

**Returns** `(number, number, number)` — Three numbers — the scaled vec3.

```lua
local x, y, z = Transform.vec.scale(1, 2, 3, 2)
```

## globals/Transform/vec/sub {#globals-transform-vec-sub}

```lua
Transform.vec.sub(ax: number, ay: number, az: number, bx: number, by: number, bz: number) -> (number, number, number)
```

Component-wise vec3 subtraction (`a - b`).

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

**Returns** `(number, number, number)` — Three numbers — the difference.

```lua
local x, y, z = Transform.vec.sub(4, 5, 6, 1, 2, 3)
```

## globals/Transform/worldToLocal {#globals-transform-worldtolocal}

```lua
Transform.worldToLocal(px: number, py: number, pz: number, pqx: number, pqy: number, pqz: number, pqw: number, wx: number, wy: number, wz: number) -> (number, number, number)
```

Transform a world-space position into a parent's local space.

**Parameters**

- `px` `number` — Parent position x.
- `py` `number` — Parent position y.
- `pz` `number` — Parent position z.
- `pqx` `number` — Parent rotation x.
- `pqy` `number` — Parent rotation y.
- `pqz` `number` — Parent rotation z.
- `pqw` `number` — Parent rotation w.
- `wx` `number` — World x.
- `wy` `number` — World y.
- `wz` `number` — World z.

**Returns** `(number, number, number)` — Three numbers `lx, ly, lz` — the local position.

```lua
local lx, ly, lz = Transform.worldToLocal(px, py, pz, pqx, pqy, pqz, pqw, wx, wy, wz)
```

## modules/Transform/README {#modules-transform-readme}

```lua
Transform (global)
```

Math helpers for positions, rotations, and directions on transforms. Exposed as the global `Transform` table; entity-aware helpers accept an id string or an entity proxy.
Also available as global: Transform

## modules/Transform/direction {#modules-transform-direction}

```lua
direction(fromX: number, fromY: number, fromZ: number, toX: number, toY: number, toZ: number): (number, number, number)
```

Normalized direction vector from point A to point B. Returns
zeros when the two points coincide (within ~0.001 units).

**Parameters**

- `fromX` `number` — From x.
- `fromY` `number` — From y.
- `fromZ` `number` — From z.
- `toX` `number` — To x.
- `toY` `number` — To y.
- `toZ` `number` — To z.

```lua
local dx, dy, dz = Transform.direction(0, 0, 0, 1, 0, 0)
```

## modules/Transform/directionBetween {#modules-transform-directionbetween}

```lua
directionBetween(entityA: string | EntityRef, entityB: string | EntityRef): (number, number, number)
```

Normalized world-space direction from one entity to another, read
from their world positions. Returns zeros if either entity can't be
resolved.

**Parameters**

- `entityA` `string | EntityRef` — Source entity (id string or proxy).
- `entityB` `string | EntityRef` — Target entity (id string or proxy).

```lua
local dx, dy, dz = Transform.directionBetween("cam", "target")
```

## modules/Transform/distance {#modules-transform-distance}

```lua
distance(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number): number
```

Euclidean distance between two world-space positions.

**Parameters**

- `x1` `number` — First point x.
- `y1` `number` — First point y.
- `z1` `number` — First point z.
- `x2` `number` — Second point x.
- `y2` `number` — Second point y.
- `z2` `number` — Second point z.

```lua
local d = Transform.distance(0, 0, 0, 1, 1, 1)
```

## modules/Transform/distanceBetween {#modules-transform-distancebetween}

```lua
distanceBetween(entityA: string | EntityRef, entityB: string | EntityRef): number?
```

Distance between two entities in world space. Each entity's world
position is what is measured, so a parent's offset counts toward the
distance the way the scene shows it.

**Parameters**

- `entityA` `string | EntityRef` — First entity (id string or proxy).
- `entityB` `string | EntityRef` — Second entity (id string or proxy).

```lua
local d = Transform.distanceBetween("cam", "box")
```

## modules/Transform/euler {#modules-transform-euler}

```lua
euler(qx: number, qy: number, qz: number, qw: number): (number, number, number)
```

Convert quaternion to euler angles (yaw, pitch, roll) in radians.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.

```lua
local yaw, pitch, roll = Transform.euler(0, 0, 0, 1)
```

## modules/Transform/eulerToQuat {#modules-transform-eulertoquat}

```lua
eulerToQuat(yaw: number, pitch: number?, roll: number?): (number, number, number, number)
```

Identity-aware overload of euler-to-quaternion. Uses the negative-yaw
convention shared with `quatFromYaw`, `quatFromYawPitch`, `lookAtQuat`, and
`T.euler` extraction — so `T.euler(T.eulerToQuat(y, p, r))` returns
`(y, p, r)`. Order is yaw (Y) then pitch (X) then roll (Z).

**Parameters**

- `yaw` `number` — Y-axis rotation in radians.
- `pitch` `number?` _(optional)_ — X-axis rotation in radians. Defaults to 0.
- `roll` `number?` _(optional)_ — Z-axis rotation in radians. Defaults to 0.

```lua
local qx, qy, qz, qw = Transform.eulerToQuat(math.pi / 2)
```

## modules/Transform/lerp {#modules-transform-lerp}

```lua
lerp(ax: number, ay: number, az: number, bx: number, by: number, bz: number, t: number): (number, number, number)
```

Linearly interpolate between two positions.

**Parameters**

- `ax` `number` — Start x.
- `ay` `number` — Start y.
- `az` `number` — Start z.
- `bx` `number` — End x.
- `by` `number` — End y.
- `bz` `number` — End z.
- `t` `number` — Interpolation factor `[0, 1]`.

```lua
local x, y, z = Transform.lerp(0, 0, 0, 1, 1, 1, 0.5)
```

## modules/Transform/lerp1 {#modules-transform-lerp1}

```lua
lerp1(a: number, b: number, t: number): number
```

Linearly interpolate two scalars.

**Parameters**

- `a` `number` — Start value.
- `b` `number` — End value.
- `t` `number` — Interpolation factor `[0, 1]`.

```lua
local v = Transform.lerp1(0, 10, 0.5)
```

## modules/Transform/lerpAngle {#modules-transform-lerpangle}

```lua
lerpAngle(a: number, b: number, t: number): number
```

Lerp between two angles via the shortest arc; returns a value in `[-pi, pi]`.

**Parameters**

- `a` `number` — Start angle in radians.
- `b` `number` — End angle in radians.
- `t` `number` — Interpolation factor `[0, 1]`.

```lua
local a = Transform.lerpAngle(0, math.pi, 0.5)
```

## modules/Transform/localToWorld {#modules-transform-localtoworld}

```lua
localToWorld(px: number, py: number, pz: number, pqx: number, pqy: number, pqz: number, pqw: number, lx: number, ly: number, lz: number): (number, number, number)
```

Transform a local-space position into world space using a parent pose.

**Parameters**

- `px` `number` — Parent position x.
- `py` `number` — Parent position y.
- `pz` `number` — Parent position z.
- `pqx` `number` — Parent rotation x.
- `pqy` `number` — Parent rotation y.
- `pqz` `number` — Parent rotation z.
- `pqw` `number` — Parent rotation w.
- `lx` `number` — Local x.
- `ly` `number` — Local y.
- `lz` `number` — Local z.

```lua
local wx, wy, wz = Transform.localToWorld(px, py, pz, pqx, pqy, pqz, pqw, lx, ly, lz)
```

## modules/Transform/lookAt {#modules-transform-lookat}

```lua
lookAt(entityOrId: string | EntityRef, txOrTarget: any, ty: any?, tz: number?, up: any?): (boolean, string?)
```

Make an entity face a world position. The target slot accepts three
explicit coordinates, one point as `{ x, y, z }` / `{ x =, y =, z = }` / a
vector, or an entity — an id string, an entity NAME, or a proxy — whose
WORLD position is resolved. A table carrying an entity id reads as that
entity; any other table reads as the point it spells. The subject slot
takes the three entity spellings.
Everything here is world space: the subject and the target are
read as `entity(id).position` and the aim is written as
`entity(id).rotation`, so a parent under either one moves the entity and
the aim still lands on the point named.
Returns whether the rotation was written, so a caller that named an entity
the scene does not carry learns the aim did not happen instead of reading
a stale orientation back as the answer.

**Parameters**

- `entityOrId` `string | EntityRef` — Entity id, name, or proxy for the entity to rotate.
- `txOrTarget` `any` _(optional)_ — A number (world x), a point table, or an entity id / name /
proxy whose world position is resolved as the look-at target.
- `ty` `any?` _(optional)_ — World y of the target. Omitted when `txOrTarget` is a point or an entity.
- `tz` `number?` _(optional)_ — World z of the target. Omitted when `txOrTarget` is a point or an entity.
- `up` `any?` _(optional)_ — Optional world up hint deciding the roll — `{ x, y, z }`,
`{ x =, y =, z = }` or a vector. World +Y when omitted. It never bends the
aim; it only says which way is up around it. When the target slot is an
entity or a point this is the third argument, and when it is coordinates
the fifth.

```lua
Transform.lookAt("cam", 0, 1, 0)
Transform.lookAt("cam", "box")  -- resolve target entity position
Transform.lookAt(cam, box)      -- entity proxies for both
Transform.lookAt("cam", { 0, 1, 0 })         -- one point table
Transform.lookAt("cam", "box", { 0, 0, 1 })  -- rolled to a +Z up
```

## modules/Transform/lookAtQuat {#modules-transform-lookatquat}

```lua
lookAtQuat(fx: number, fy: number, fz: number, tx: number, ty: number, tz: number): (number?, number?, number?, number?)
```

Compute quaternion to look from origin position toward a target.
Returns four components `(qx, qy, qz, qw)`, or `nil` when the from
and to points are too close to derive a meaningful direction.

**Parameters**

- `fx` `number` — Origin x.
- `fy` `number` — Origin y.
- `fz` `number` — Origin z.
- `tx` `number` — Target x.
- `ty` `number` — Target y.
- `tz` `number` — Target z.

```lua
local qx, qy, qz, qw = Transform.lookAtQuat(0, 0, 0, 1, 0, 1)
```

## modules/Transform/lookRotation {#modules-transform-lookrotation}

```lua
lookRotation(fx: number, fy: number, fz: number, tx: number, ty: number, tz: number, ux: number?, uy: number?, uz: number?): (number?, number?, number?, number?)
```

The rotation that aims an entity standing at one world point at another,
with a world up hint deciding the roll. Where `lookAtQuat` derives the aim
from yaw and pitch alone — clamping the pitch just short of vertical, so a
point directly overhead comes back a twentieth of a degree off — this builds
all three axes, so the aim lands on the point at any elevation and straight
up and straight down are ordinary cases.
The aimed axis is the entity's local -Z, the same forward `quatFromBasis`,
`Transform.lookAt` and `entity(id):lookAt` state and the direction
`entity(id).transform.forward` reads back.
The up hint is a world direction the entity's own +Y is turned toward as
far as the aim allows; it never bends the forward axis. A hint parallel to
the aim leaves the roll undetermined, and a hint of no length names no
direction — both fall back to a stable roll rather than a NaN.

**Parameters**

- `fx` `number` — Eye x — where the entity stands.
- `fy` `number` — Eye y.
- `fz` `number` — Eye z.
- `tx` `number` — Target x — the world point it faces.
- `ty` `number` — Target y.
- `tz` `number` — Target z.
- `ux` `number?` _(optional)_ — Up hint x. World +Y when the hint is omitted.
- `uy` `number?` _(optional)_ — Up hint y.
- `uz` `number?` _(optional)_ — Up hint z.

```lua
local qx, qy, qz, qw = Transform.lookRotation(0, 2, 10, 0, 1, 0)
entity("cam").rotation = { Transform.lookRotation(0, 2, 10, 0, 1, 0) }
-- a dutch tilt: the same aim, rolled by leaning the up hint
local q = { Transform.lookRotation(0, 2, 10, 0, 1, 0, 0.2, 1, 0) }
```

## modules/Transform/normalizeAngle {#modules-transform-normalizeangle}

```lua
normalizeAngle(a: number): number
```

Normalize an angle into `[-pi, pi]`.

**Parameters**

- `a` `number` — The angle in radians.

```lua
local a = Transform.normalizeAngle(3 * math.pi)
```

## modules/Transform/orbit {#modules-transform-orbit}

```lua
orbit(centerX: number, centerY: number, centerZ: number, radius: number, height: number, angle: number): (number, number, number, number, number, number, number)
```

Position + rotation for orbiting around a center point. Returns
the world position followed by the orientation that faces the center.

**Parameters**

- `centerX` `number` — Center x.
- `centerY` `number` — Center y.
- `centerZ` `number` — Center z.
- `radius` `number` — Horizontal distance from the center.
- `height` `number` — Vertical offset from `centerY`.
- `angle` `number` — Orbital angle in radians.

```lua
local x, y, z, qx, qy, qz, qw = Transform.orbit(0, 1, 0, 5, 2, t)
```

## modules/Transform/quatFromAxisAngle {#modules-transform-quatfromaxisangle}

```lua
quatFromAxisAngle(ax: number, ay: number, az: number, angle: number): (number, number, number, number)
```

Create quaternion from axis and angle (radians). Returns the
identity quaternion when the axis is degenerate (length < 0.001).

**Parameters**

- `ax` `number` — Axis x.
- `ay` `number` — Axis y.
- `az` `number` — Axis z.
- `angle` `number` — Rotation angle in radians.

```lua
local qx, qy, qz, qw = Transform.quatFromAxisAngle(0, 1, 0, math.pi)
```

## modules/Transform/quatFromBasis {#modules-transform-quatfrombasis}

```lua
quatFromBasis(rx: number, ry: number, rz: number, ux: number, uy: number, uz: number, fx: number, fy: number, fz: number): (number, number, number, number)
```

Build the rotation whose right, up and forward ARE the given axes. Where
`lookAtQuat` derives a rotation from a direction alone — yaw and pitch, with
pitch clamped just short of straight up or down and no say in the roll — this
states all three axes, so a view straight down has a defined image-up instead
of whatever the yaw implied. The axes are expected orthonormal and are used as
given: `right` and `up` are the entity's local +X and +Y, `forward` its local
-Z (the direction it faces).

**Parameters**

- `rx` `number` — Right axis x.
- `ry` `number` — Right axis y.
- `rz` `number` — Right axis z.
- `ux` `number` — Up axis x.
- `uy` `number` — Up axis y.
- `uz` `number` — Up axis z.
- `fx` `number` — Forward axis x.
- `fy` `number` — Forward axis y.
- `fz` `number` — Forward axis z.

```lua
local qx, qy, qz, qw = Transform.quatFromBasis(1,0,0, 0,1,0, 0,0,-1) -- identity
-- looking straight down with the subject's front toward the top of frame
local qx, qy, qz, qw = Transform.quatFromBasis(1,0,0, 0,0,-1, 0,-1,0)
```

## modules/Transform/quatFromYaw {#modules-transform-quatfromyaw}

```lua
quatFromYaw(yaw: number): (number, number, number, number)
```

Create quaternion from yaw (Y-axis rotation) in radians. Uses the
negative-yaw convention shared with `quatFromYawPitch`, `lookAtQuat`,
and `T.euler` extraction — so `T.euler(T.quatFromYaw(y))` round-trips
to `y`.

**Parameters**

- `yaw` `number` — Rotation in radians around the Y axis.

```lua
local qx, qy, qz, qw = Transform.quatFromYaw(math.pi / 2)
```

## modules/Transform/quatFromYawPitch {#modules-transform-quatfromyawpitch}

```lua
quatFromYawPitch(yaw: number, pitch: number): (number, number, number, number)
```

Create quaternion from yaw and pitch in radians.

**Parameters**

- `yaw` `number` — Y-axis rotation in radians.
- `pitch` `number` — X-axis rotation in radians.

```lua
local qx, qy, qz, qw = Transform.quatFromYawPitch(0, math.pi / 4)
```

## modules/Transform/quatIdentity {#modules-transform-quatidentity}

```lua
quatIdentity(): (number, number, number, number)
```

Identity quaternion (`0, 0, 0, 1`).

```lua
local qx, qy, qz, qw = Transform.quatIdentity()
```

## modules/Transform/quatInverse {#modules-transform-quatinverse}

```lua
quatInverse(qx: number, qy: number, qz: number, qw: number): (number, number, number, number)
```

Quaternion inverse. Equal to the conjugate for unit quaternions.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.

```lua
local ix, iy, iz, iw = Transform.quatInverse(qx, qy, qz, qw)
```

## modules/Transform/quatMul {#modules-transform-quatmul}

```lua
quatMul(ax: number, ay: number, az: number, aw: number, bx: number, by: number, bz: number, bw: number): (number, number, number, number)
```

Quaternion multiplication: returns `qa * qb` (composition: rotate
by `qb` then `qa`).

**Parameters**

- `ax` `number` — Left quat x.
- `ay` `number` — Left quat y.
- `az` `number` — Left quat z.
- `aw` `number` — Left quat w.
- `bx` `number` — Right quat x.
- `by` `number` — Right quat y.
- `bz` `number` — Right quat z.
- `bw` `number` — Right quat w.

```lua
local qx, qy, qz, qw = Transform.quatMul(ax, ay, az, aw, bx, by, bz, bw)
```

## modules/Transform/quatRotateVec {#modules-transform-quatrotatevec}

```lua
quatRotateVec(qx: number, qy: number, qz: number, qw: number, vx: number, vy: number, vz: number): (number, number, number)
```

Rotate a 3-vector by a quaternion.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.
- `vx` `number` — Vector x.
- `vy` `number` — Vector y.
- `vz` `number` — Vector z.

```lua
local rx, ry, rz = Transform.quatRotateVec(qx, qy, qz, qw, 1, 0, 0)
```

## modules/Transform/quatToEuler {#modules-transform-quattoeuler}

```lua
quatToEuler(qx: number, qy: number, qz: number, qw: number): (number, number, number)
```

Convert quaternion to `(yaw, pitch, roll)`. Alias of `euler` with
the explicit name so callers don't have to remember the order.

**Parameters**

- `qx` `number` — Quaternion x.
- `qy` `number` — Quaternion y.
- `qz` `number` — Quaternion z.
- `qw` `number` — Quaternion w.

```lua
local yaw, pitch, roll = Transform.quatToEuler(qx, qy, qz, qw)
```

## modules/Transform/readVec3 {#modules-transform-readvec3}

```lua
readVec3(value: Vec3Input, label: string?): { number }
```

Normalize a vector a caller wrote to a plain `{ x, y, z }` array.
Accepts a positional array `{1, 2, 3}`, a keyed table
`{x =, y =, z =}`, or a live vec handle. Missing components read as 0.
Raises when the value is not a vector; `label` names the caller in
that error.

**Parameters**

- `value` `Vec3Input` — The vector to normalize.
- `label` `string?` _(optional)_ — Name reported in the error when the value is not a vector. Defaults to "Transform".

```lua
local v = Transform.readVec3({ x = 1, y = 2, z = 3 })
```

## modules/Transform/slerp {#modules-transform-slerp}

```lua
slerp(ax: number, ay: number, az: number, aw: number, bx: number, by: number, bz: number, bw: number, t: number): (number, number, number, number)
```

Spherical linear interpolation between two quaternions. Picks
the shortest path (flips sign if dot < 0). Falls back to
lerp+normalize when the two quats are very close (avoids
div-by-zero on near-parallel inputs).

**Parameters**

- `ax` `number` — Start quaternion x.
- `ay` `number` — Start quaternion y.
- `az` `number` — Start quaternion z.
- `aw` `number` — Start quaternion w.
- `bx` `number` — End quaternion x.
- `by` `number` — End quaternion y.
- `bz` `number` — End quaternion z.
- `bw` `number` — End quaternion w.
- `t` `number` — Interpolation factor `[0, 1]`.

```lua
local qx, qy, qz, qw = Transform.slerp(0, 0, 0, 1, 1, 0, 0, 0, 0.5)
```

## modules/Transform/snapVec3 {#modules-transform-snapvec3}

```lua
snapVec3(v: { number }, step: number | Vec3Input): { number }
```

Quantize each component of a vector to the nearest multiple of
`step` — a number for uniform steps, or a vector for per-axis steps.
A step of 0 on an axis leaves that axis at its exact value.

**Parameters**

- `v` `{ number }` — The vector to quantize, as `{ x, y, z }`.
- `step` `number | Vec3Input` — Uniform step size, or a per-axis vector of step sizes.

```lua
local v = Transform.snapVec3({ 1.4, 2.6, -0.4 }, 1)
```

## modules/Transform/toQuaternion {#modules-transform-toquaternion}

```lua
toQuaternion(rotation: any, label: string?): { number }
```

Normalize a rotation a caller wrote to a `{ qx, qy, qz, qw }`
quaternion. Accepts a quaternion (`{x,y,z,w}` or `{x=,y=,z=,w=}`) or
euler DEGREES (`{pitch,yaw,roll}` or `{pitch=,yaw=,roll=}`), so one
call site takes whichever form the caller finds natural. This is the
reading every rotation-taking surface in the engine shares, so a
quaternion and euler degrees mean the same thing at all of them.
Raises when the value matches no form; `label` names the caller in that
error, and a value that is one of the shapes a quaternion helper returns
is named as such along with the packing it goes in as.

**Parameters**

- `rotation` `any` _(optional)_ — The rotation to normalize, in any form of the `RotationInput` union.
- `label` `string?` _(optional)_ — Name reported in the error when the value is not a rotation. Defaults to "Transform".

```lua
local q = Transform.toQuaternion({ pitch = 0, yaw = 90, roll = 0 })
```

## modules/Transform/tryQuaternion {#modules-transform-tryquaternion}

```lua
tryQuaternion(rotation: any, label: string?): ({ number }?, string?)
```

Read a rotation a caller wrote WITHOUT raising: returns the
canonical `{ qx, qy, qz, qw }`, or nil and the message describing what
arrived. The forms are the `RotationInput` union — a quaternion
(`{x,y,z,w}` or `{x=,y=,z=,w=}`) or euler DEGREES (`{pitch,yaw,roll}` or
`{pitch=,yaw=,roll=}`). Takes any value because reporting on a value that
is none of those forms is the whole job; a setter built on this raises the
returned message itself, so the error points at the line that wrote the
value rather than at the reading.

**Parameters**

- `rotation` `any` _(optional)_ — The value to read as a rotation.
- `label` `string?` _(optional)_ — Name reported in the message. Defaults to "Transform".

```lua
local q, why = Transform.tryQuaternion(value, "myTool")
```

## modules/Transform/vec.add {#add}

```lua
vec.add(ax: number, ay: number, az: number, bx: number, by: number, bz: number): (number, number, number)
```

Component-wise vec3 addition.

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

```lua
local x, y, z = Transform.vec.add(1, 2, 3, 4, 5, 6)
```

## modules/Transform/vec.cross {#cross}

```lua
vec.cross(ax: number, ay: number, az: number, bx: number, by: number, bz: number): (number, number, number)
```

Cross product `a x b`.

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

```lua
local cx, cy, cz = Transform.vec.cross(1, 0, 0, 0, 1, 0)
```

## modules/Transform/vec.dot {#dot}

```lua
vec.dot(ax: number, ay: number, az: number, bx: number, by: number, bz: number): number
```

Dot product of two vec3s.

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

```lua
local d = Transform.vec.dot(1, 0, 0, 0, 1, 0)
```

## modules/Transform/vec.length {#length}

```lua
vec.length(x: number, y: number, z: number): number
```

Euclidean length of a vec3.

**Parameters**

- `x` `number` — Vector x.
- `y` `number` — Vector y.
- `z` `number` — Vector z.

```lua
local len = Transform.vec.length(1, 2, 3)
```

## modules/Transform/vec.normalize {#normalize}

```lua
vec.normalize(x: number, y: number, z: number): (number, number, number)
```

Normalize a vec3. Returns zeros when the input is degenerate
(length < 1e-8).

**Parameters**

- `x` `number` — Vector x.
- `y` `number` — Vector y.
- `z` `number` — Vector z.

```lua
local nx, ny, nz = Transform.vec.normalize(0, 5, 0)
```

## modules/Transform/vec.scale {#scale}

```lua
vec.scale(x: number, y: number, z: number, s: number): (number, number, number)
```

Component-wise scalar multiplication of a vec3.

**Parameters**

- `x` `number` — Vector x.
- `y` `number` — Vector y.
- `z` `number` — Vector z.
- `s` `number` — Scalar factor.

```lua
local x, y, z = Transform.vec.scale(1, 2, 3, 2)
```

## modules/Transform/vec.sub {#sub}

```lua
vec.sub(ax: number, ay: number, az: number, bx: number, by: number, bz: number): (number, number, number)
```

Component-wise vec3 subtraction (`a - b`).

**Parameters**

- `ax` `number` — First vector x.
- `ay` `number` — First vector y.
- `az` `number` — First vector z.
- `bx` `number` — Second vector x.
- `by` `number` — Second vector y.
- `bz` `number` — Second vector z.

```lua
local x, y, z = Transform.vec.sub(4, 5, 6, 1, 2, 3)
```

## modules/Transform/worldToLocal {#modules-transform-worldtolocal}

```lua
worldToLocal(px: number, py: number, pz: number, pqx: number, pqy: number, pqz: number, pqw: number, wx: number, wy: number, wz: number): (number, number, number)
```

Transform a world-space position into a parent's local space.

**Parameters**

- `px` `number` — Parent position x.
- `py` `number` — Parent position y.
- `pz` `number` — Parent position z.
- `pqx` `number` — Parent rotation x.
- `pqy` `number` — Parent rotation y.
- `pqz` `number` — Parent rotation z.
- `pqw` `number` — Parent rotation w.
- `wx` `number` — World x.
- `wy` `number` — World y.
- `wz` `number` — World z.

```lua
local lx, ly, lz = Transform.worldToLocal(px, py, pz, pqx, pqy, pqz, pqw, wx, wy, wz)
```
