Log inGet started

Transform

Updated 5 September 2026

The Transform namespace — 75 functions.

globals/Transform/direction

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.

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

globals/Transform/directionBetween

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.

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

globals/Transform/distance

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.

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

globals/Transform/distanceBetween

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.

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

globals/Transform/euler

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).

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

globals/Transform/eulerToQuat

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.

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

globals/Transform/lerp

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.

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

globals/Transform/lerp1

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.

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

globals/Transform/lerpAngle

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].

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

globals/Transform/localToWorld

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.

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

globals/Transform/lookAt

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.

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

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.

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

globals/Transform/lookRotation

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.

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

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].

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

globals/Transform/orbit

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.

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

globals/Transform/quatFromAxisAngle

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.

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

globals/Transform/quatFromBasis

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.

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

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.

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

globals/Transform/quatFromYawPitch

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.

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

globals/Transform/quatIdentity

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.

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

globals/Transform/quatInverse

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.

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

globals/Transform/quatMul

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.

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

globals/Transform/quatRotateVec

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.

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

globals/Transform/quatToEuler

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).

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

globals/Transform/readVec3

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 }.

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

globals/Transform/slerp

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.

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

globals/Transform/snapVec3

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.

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

globals/Transform/toQuaternion

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 }.

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

globals/Transform/tryQuaternion

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.

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

globals/Transform/vec/add

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.

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

globals/Transform/vec/cross

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.

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

globals/Transform/vec/dot

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.

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

globals/Transform/vec/length

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.

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

globals/Transform/vec/normalize

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.

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

globals/Transform/vec/scale

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.

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

globals/Transform/vec/sub

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.

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

globals/Transform/worldToLocal

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.

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

modules/Transform/README

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

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.
local dx, dy, dz = Transform.direction(0, 0, 0, 1, 0, 0)

modules/Transform/directionBetween

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).
local dx, dy, dz = Transform.directionBetween("cam", "target")

modules/Transform/distance

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.
local d = Transform.distance(0, 0, 0, 1, 1, 1)

modules/Transform/distanceBetween

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).
local d = Transform.distanceBetween("cam", "box")

modules/Transform/euler

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.
local yaw, pitch, roll = Transform.euler(0, 0, 0, 1)

modules/Transform/eulerToQuat

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.
local qx, qy, qz, qw = Transform.eulerToQuat(math.pi / 2)

modules/Transform/lerp

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].
local x, y, z = Transform.lerp(0, 0, 0, 1, 1, 1, 0.5)

modules/Transform/lerp1

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].
local v = Transform.lerp1(0, 10, 0.5)

modules/Transform/lerpAngle

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].
local a = Transform.lerpAngle(0, math.pi, 0.5)

modules/Transform/localToWorld

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.
local wx, wy, wz = Transform.localToWorld(px, py, pz, pqx, pqy, pqz, pqw, lx, ly, lz)

modules/Transform/lookAt

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.
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

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.
local qx, qy, qz, qw = Transform.lookAtQuat(0, 0, 0, 1, 0, 1)

modules/Transform/lookRotation

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.
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

normalizeAngle(a: number): number

Normalize an angle into [-pi, pi].

Parameters

  • a number — The angle in radians.
local a = Transform.normalizeAngle(3 * math.pi)

modules/Transform/orbit

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.
local x, y, z, qx, qy, qz, qw = Transform.orbit(0, 1, 0, 5, 2, t)

modules/Transform/quatFromAxisAngle

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.
local qx, qy, qz, qw = Transform.quatFromAxisAngle(0, 1, 0, math.pi)

modules/Transform/quatFromBasis

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.
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

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.
local qx, qy, qz, qw = Transform.quatFromYaw(math.pi / 2)

modules/Transform/quatFromYawPitch

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.
local qx, qy, qz, qw = Transform.quatFromYawPitch(0, math.pi / 4)

modules/Transform/quatIdentity

quatIdentity(): (number, number, number, number)

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

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

modules/Transform/quatInverse

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.
local ix, iy, iz, iw = Transform.quatInverse(qx, qy, qz, qw)

modules/Transform/quatMul

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.
local qx, qy, qz, qw = Transform.quatMul(ax, ay, az, aw, bx, by, bz, bw)

modules/Transform/quatRotateVec

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.
local rx, ry, rz = Transform.quatRotateVec(qx, qy, qz, qw, 1, 0, 0)

modules/Transform/quatToEuler

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.
local yaw, pitch, roll = Transform.quatToEuler(qx, qy, qz, qw)

modules/Transform/readVec3

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".
local v = Transform.readVec3({ x = 1, y = 2, z = 3 })

modules/Transform/slerp

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].
local qx, qy, qz, qw = Transform.slerp(0, 0, 0, 1, 1, 0, 0, 0, 0.5)

modules/Transform/snapVec3

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.
local v = Transform.snapVec3({ 1.4, 2.6, -0.4 }, 1)

modules/Transform/toQuaternion

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".
local q = Transform.toQuaternion({ pitch = 0, yaw = 90, roll = 0 })

modules/Transform/tryQuaternion

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".
local q, why = Transform.tryQuaternion(value, "myTool")

modules/Transform/vec.add

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.
local x, y, z = Transform.vec.add(1, 2, 3, 4, 5, 6)

modules/Transform/vec.cross

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.
local cx, cy, cz = Transform.vec.cross(1, 0, 0, 0, 1, 0)

modules/Transform/vec.dot

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.
local d = Transform.vec.dot(1, 0, 0, 0, 1, 0)

modules/Transform/vec.length

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.
local len = Transform.vec.length(1, 2, 3)

modules/Transform/vec.normalize

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.
local nx, ny, nz = Transform.vec.normalize(0, 5, 0)

modules/Transform/vec.scale

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.
local x, y, z = Transform.vec.scale(1, 2, 3, 2)

modules/Transform/vec.sub

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.
local x, y, z = Transform.vec.sub(4, 5, 6, 1, 2, 3)

modules/Transform/worldToLocal

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.
local lx, ly, lz = Transform.worldToLocal(px, py, pz, pqx, pqy, pqz, pqw, wx, wy, wz)
  • api
  • reference