Extended Math API
This API provides vectors, quaternions, matrices, math constants, and helper functions found in modern game engines.
📘 Contents
Math Constants
Global Math Helpers
vector2
vector3
quaternion
matrix4x4
mat4 Namespace
🔢 Math Constants
These are available globally:
M_PI
3.141592653589793
M_TAU
6.283185307179586 (2π)
M_PI_2
π/2
M_PI_4
π/4
DEG2RAD
π/180
RAD2DEG
180/π
PI_OVER_180
π/180
INV_PI_OVER_180
180/π
M_ZERO
0
M_ONE
1
M_EPSILON
0.000001
Usage:
local angle = 45 * DEG2RAD
local circumference = 2 * M_PI * radius🛠 Global Math Helpers
✔ clamp(x, min, max)
Clamps a value between two bounds.
local v = clamp(5, 0, 1) -- 1✔ saturate(x)
Clamps value to [0,1].
local t = saturate(1.5) -- 1✔ sign(x)
Returns -1, 0, or 1.
✔ round(x), round_up(x), round_down(x)
Standard rounding helpers.
✔ fract(x)
Fractional part (always 0–1, even for negatives).
✔ wrap(x, min, max)
Wraps a number into a cyclic range.
wrap(5, 0, 4) --> 1✔ lerp(a, b, t)
Linear interpolation.
✔ inverse_lerp(a, b, v)
Inverse of lerp.
✔ remap(a1, b1, a2, b2, v)
Maps a value from one range to another.
✔ smoothstep(edge0, edge1, x)
Smooth Hermite interpolation.
✔ step(edge, x)
Returns 0 if x < edge, else 1.
✔ is_nan(x), is_inf(x)
Checks numeric state.
🟦 vector2
A 2D double-precision vector.
✔ Create
local v = vector2(x, y)
local v = vector2() -- 0,0✔ Fields
v.xv.y
✔ Operators
add
v1 + v2
subtract
v1 - v2
multiply (scalar)
v * 2
divide (scalar)
v / 2
unary minus
-v
equality
v1 == v2
✔ Methods
v:length()
v:distance(other)
v:distance_to(other)
v:lerp(other, t)
v:min(other)
v:max(other)✔ Memory I/O
v:readas_float(proc, addr)
v:readas_double(proc, addr)
v:writeas_float(proc, addr)
v:writeas_double(proc, addr)🟩 vector3
A 3D double-precision vector.
✔ Create
local v = vector3(x, y, z)
local v = vector3()✔ Fields
v.x, v.y, v.z
✔ Operators
Same as vector2, plus:
v:dot(other)v:cross_product(other)
✔ Methods
v:length()
v:length2d()
v:distance(other)
v:distance2d(other)
v:distance_to(other)
v:distance2d_to(other)
v:lerp(other, t)
v:min(other)
v:max(other)
v:dot_product(other)
v:cross_product(other)✔ Memory I/O
Same pattern as vector2.
🟪 quaternion
A 4D double-precision quaternion (x, y, z, w).
✔ Create
local q = quaternion(x, y, z, w)
local q = quaternion() -- 0,0,0,1
local q = quat_from_euler(pitch, yaw, roll)✔ Fields
q.x, q.y, q.z, q.w
✔ Operators
multiply quaternion
q1 * q2
multiply scalar
q * 2
divide scalar
q / 2
add
q1 + q2
subtract
q1 - q2
negate
-q
equality
q1 == q2
✔ Methods
q:length()
q:normalized()
q:dot(other)
q:conjugate()
q:inverse()
q:to_euler() -- returns pitch,yaw,roll
q:rotate(vec3) -- rotates a vector✔ Memory I/O
q:readas_double(proc, addr)
q:writeas_double(proc, addr)
q:readas_float(proc, addr)
q:writeas_float(proc, addr)🟥 matrix4x4
A 4×4 float matrix (row-major), used for transforms and 3D math.
✔ Create
local m = matrix4x4() -- zero matrix✔ Indexing
m[1] .. m[16]✔ Operators
local M = A * B✔ Methods (instance)
m:transform(vec3)
m:read(proc, addr)
m:write(proc, addr)✔ tostring()
Pretty-prints matrix for debugging.
🟧 mat4 Namespace
Convenient static constructors:
local I = mat4.identity()
local Z = mat4.zero()
local T = mat4.translate(tx, ty, tz)
local S = mat4.scale(sx, sy, sz)
local R = mat4.rotate_euler(pitch, yaw, roll)
local Mq = mat4.from_quaternion(q)📘 Examples
TRS (Translate → Rotate → Scale)
local T = mat4.translate(10, 5, 0)
local R = mat4.rotate_euler(0, 90, 0)
local S = mat4.scale(2, 2, 2)
local M = T * R * S
local out = M:transform(vector3(1,0,0))
log(out)Quaternion Rotation
local q = quat_from_euler(0, 0, 90)
local v = vector3(1,0,0)
local r = q:rotate(v) -- (0,1,0)Remapping Values
local t = inverse_lerp(0, 100, 25) -- 0.25
local v = remap(0, 1, -1, 1, t) -- -0.5Full API Test
log("=========== MATH HELPERS TESTS START ===========")
local function approx(a,b,eps)
eps = eps or 0.000001
return math.abs(a-b) <= eps
end
local function assert_eq(a,b,msg)
if a ~= b then
log("[ASSERT FAIL] "..msg.." | expected="..tostring(b).." got="..tostring(a))
else
log("[PASS] "..msg)
end
end
local function assert_approx(a,b,msg)
if approx(a,b) then
log("[PASS] "..msg)
else
log("[ASSERT FAIL] "..msg.." | expected="..tostring(b).." got="..tostring(a))
end
end
----------------------------------------------------------------
-- clamp(x, min, max)
----------------------------------------------------------------
assert_approx(clamp(-1, 0, 1), 0, "clamp below")
assert_approx(clamp(0.5, 0, 1), 0.5,"clamp inside")
assert_approx(clamp(2, 0, 1), 1, "clamp above")
-- swapped min/max should still work
assert_approx(clamp(5, 10, 0), 5, "clamp swapped bounds")
----------------------------------------------------------------
-- saturate(x) -> [0,1]
----------------------------------------------------------------
assert_approx(saturate(-1), 0, "saturate below")
assert_approx(saturate(0.3), 0.3, "saturate inside")
assert_approx(saturate(5), 1, "saturate above")
----------------------------------------------------------------
-- sign(x)
----------------------------------------------------------------
assert_eq(sign( 5), 1, "sign positive")
assert_eq(sign(-2), -1, "sign negative")
assert_eq(sign( 0), 0, "sign zero")
----------------------------------------------------------------
-- round, round_up, round_down
----------------------------------------------------------------
assert_approx(round(2.3), 2, "round 2.3")
assert_approx(round(2.5), 3, "round 2.5")
assert_approx(round(-2.3), -2, "round -2.3")
assert_approx(round(-2.5), -3, "round -2.5")
assert_approx(round_up(2.1), 3, "round_up 2.1")
assert_approx(round_up(-2.1), -2, "round_up -2.1")
assert_approx(round_down(2.9), 2, "round_down 2.9")
assert_approx(round_down(-2.1), -3, "round_down -2.1")
----------------------------------------------------------------
-- fract(x)
----------------------------------------------------------------
assert_approx(fract(1.25), 0.25, "fract 1.25")
-- our implementation keeps negative fractional part in [0,1)
assert_approx(fract(-1.25), 0.75, "fract -1.25")
----------------------------------------------------------------
-- wrap(x, min, max)
----------------------------------------------------------------
assert_approx(wrap(5, 0, 4), 1, "wrap 5 in [0,4]")
assert_approx(wrap(-1, 0, 4), 3, "wrap -1 in [0,4]")
assert_approx(wrap(9, 2, 6), 5, "wrap 9 in [2,6]")
assert_approx(wrap(2, 2, 6), 2, "wrap 2 (edge)")
----------------------------------------------------------------
-- lerp / inverse_lerp
----------------------------------------------------------------
assert_approx(lerp(0, 10, 0.0), 0, "lerp t=0")
assert_approx(lerp(0, 10, 1.0), 10, "lerp t=1")
assert_approx(lerp(0, 10, 0.25), 2.5,"lerp t=0.25")
assert_approx(inverse_lerp(0, 10, 2.5), 0.25, "inverse_lerp 2.5 in [0,10]")
assert_approx(inverse_lerp(0, 10, 0), 0.0, "inverse_lerp 0")
assert_approx(inverse_lerp(0, 10, 10), 1.0, "inverse_lerp 10")
----------------------------------------------------------------
-- remap(a1,b1,a2,b2,v)
----------------------------------------------------------------
assert_approx(remap(0, 10, 0, 100, 2.5), 25, "remap 2.5 [0,10]->[0,100]")
assert_approx(remap(0, 1, -1, 1, 0.5), 0, "remap 0.5 [0,1]->[-1,1]")
assert_approx(remap(-1, 1, 0, 1, 0), 0.5, "remap 0 [-1,1]->[0,1]")
----------------------------------------------------------------
-- smoothstep(edge0,edge1,x)
----------------------------------------------------------------
assert_approx(smoothstep(0,1, -0.5), 0.0, "smoothstep below")
assert_approx(smoothstep(0,1, 0.0), 0.0, "smoothstep 0")
assert_approx(smoothstep(0,1, 1.0), 1.0, "smoothstep 1")
-- at x=0.5 we expect 0.5 for 3t^2-2t^3
local s05 = smoothstep(0,1,0.5)
assert_approx(s05, 0.5, "smoothstep 0.5")
----------------------------------------------------------------
-- step(edge, x)
----------------------------------------------------------------
assert_approx(step(0.5, 0.3), 0.0, "step below")
assert_approx(step(0.5, 0.5), 1.0, "step equal")
assert_approx(step(0.5, 0.9), 1.0, "step above")
----------------------------------------------------------------
-- is_nan / is_inf
----------------------------------------------------------------
local nan = 0/0
local pinf = 1/0
local ninf = -1/0
assert_eq(is_nan(nan), true, "is_nan on NaN")
assert_eq(is_nan(0), false, "is_nan on 0")
assert_eq(is_inf(pinf), true, "is_inf +inf")
assert_eq(is_inf(ninf), true, "is_inf -inf")
assert_eq(is_inf(123), false, "is_inf 123")
log("=========== MATH HELPERS TESTS END ===========")
function main()
return 1;
end
Last updated