Algebra
Vectors
Construction and fields
Construction of vectors are pretty self explanatory, fields that were not passed a value will default to zero. You can also fill a vector with a value using math.broadcast
and construct using a table of names.
Each dimension can be accessed using .x
, .y
, .z
, and .w
or additionally .pitch
, .yaw
, .roll
as an alias for the first three dimensions when used in a rotation context.
You can also slice a vector by using .xyzw
, .xyz
and .xy
.
-- Value construction.
vec2 vec2(float? x, float? y)
vec3 vec3(float? x, float? y, float? z)
vec4 vec4(float? x, float? y, float? z, float? w)
-- Broadcast construction.
vec2 vec2f(float? value)
vec3 vec3f(float? value)
vec4 vec4f(float? value)
vecN math.broadcast(type<vecN> tag, float value)
-- Copies the vector as is, note that Lua passes vectors by reference.
vecN vecN.clone
-- Vector indexing.
float vecN.x
float vecN.y
float vecN.z
float vecN.w
float vecN.pitch
float vecN.yaw
float vecN.roll
-- Vector slicing.
vec2 vecN.xy
vec3 vecN.xyz
vec4 vecN.xyzw
const vec = vec3(1, 2, 3);
print(vec); // Prints "(1.000000, 2.000000, 3.000000)"
print(vec.pitch); // Prints "1"
vec.xyzw = math.broadcast(vec4, 5);
print(vec.xy); // Prints "(5.000000, 5.000000)"
print(vec.xyzw); // Prints "(5.000000, 5.000000, 5.000000, 0.000000)"
print(vec3({ x: 3, z: 1 })); // Prints "(3.000000, 0.000000, 1.000000)"
print(vec2f(2)); // Prints "(2.000000, 2.000000)"
You can query the dimensions of a vector type using math.sizeof
.
-- Returns the dimensions of the given vector value.
int math.sizeof(vecN)
Elements of a vector can also be shuffled in-lane by invocation with a string mask, which can be a very powerful primitive. Valid characters for this mask are xyzwXYZW0
and mask must be 2, 3, or 4 characters long, which respectively results in a vec2
, vec3
, and vec4
return value.
const vec = vec4(1, 2, 3, 4);
print(vec('0w')); // Prints "(0.000000, 4.000000)"
print(vec('x0y')); // Prints "(1.000000, 0.000000, 2.000000)"
print(vec('x0yx')); // Prints "(1.000000, 0.000000, 2.000000, 1.000000)"
print(vec('wzyx')); // Prints "(4.000000, 3.000000, 2.000000, 1.000000)"
Vector objects supports zero-based indexing, invocation, comparison, string conversion and arithmetic with similarly shaped vectors and scalars as metamethods.
float vecN:__index(uint i)
void vecN:__newindex(uint i, float v)
vec2|vec3|vec4 vecN:__call(string mask)
vecN vecN:__add(vecN|float v)
vecN vecN:__sub(vecN|float v)
vecN vecN:__mul(vecN|float v)
vecN vecN:__div(vecN|float v)
vecN vecN:__pow(vecN|float v)
vecN vecN:__unm()
const vec = vec3(1, 2, 3);
print(vec.add(vec).mul(0.5) == vec); // Prints "true"
print(((0.5 * (vec + vec)) as vec3) == vec); // Prints "true"
print(vec[0]); // Prints "1.000000"
You can pass vectors of any sizes to APIs requiring specifically vec2/vec3/vec4 and the input will either be extended by zeroes or shrinked accordingly.
Properties
-- Computes the length or the squared length of the vector.
float vecN.length -- Alias for #vec
float vecN.lengthSq
-- Normalizes the vector to a unit vector of length 1.
vecN vecN.normal
-- Applies the mathematical function on every value.
vecN vecN.saturate -- x = clamp(x, 0, 1)
vecN vecN.rcp -- x = 1/x
vecN vecN.abs -- x = |x|
vecN vecN.ceil -- x = ceil x
vecN vecN.floor -- x = floor x
vecN vecN.round -- x = round x
vecN vecN.trunc -- x = trunc x
vecN vecN.sqrt -- x = sqrt x
vecN vecN.sin -- x = sin x
vecN vecN.cos -- x = cos x
vecN vecN.asin -- x = asin x
vecN vecN.acos -- x = acos x
-- Reduces the vector.
float vecN.reduceAdd
float vecN.reduceMul
float vecN.reduceMin
float vecN.reduceMax
-- Returns the normal [-pi, +pi] form of the Vec3 representing euler angles.
vec3 vec3.enormal
-- Computes the min/max of each element in the vector.
vecN vecN:min(vecN b)
vecN vecN:max(vecN b)
-- Clamps the vector to the given range.
vecN vecN:clamp(vecN vmin, vecN vmax)
-- Interpolates between this vector and b by ratio t.
vecN vecN:lerp(vecN b, float t)
-- Computes the distance or the squared distance to the vector b.
float vecN:distance(vecN b)
float vecN:distanceSq(vecN b)
-- Applies the dot operator.
float vecN:dot(vecN b)
-- Applies the cross operator.
vec3 vec3:cross(vec3 b)
vec4 vec4:cross(vec4 b, vec4 c)
-- Computes the euler angles between this vector and b.
float vec2:angleTo(vec2 b)
vec3 vec3:angleTo(vec3 b)
-- Computes the angle of rotation between this vector and b along an arbitrary
-- axis of rotation, mainly used for field of view computation.
float vec2:fovTo(vec2 b)
float vec3:fovTo(vec3 b)
-- Rotates the vector by the given euler angles.
vec2 vec2:rotateBy(float ang)
vec3 vec3:rotateBy(vec3 ang)
-- Converts from euler angles to a direction vector.
vec3 vec3:toDirection()
-- Converts from a direction vector to euler angles.
vec3 vec3:toEuler()
-- Converts a point to barycentric coordinates given the triangle in cartesian.
vec3 vecN:toBarycentric(vecN a, vecN b, vecN c)
-- Converts from cartesian coordinates (x, y) to polar (r, θ).
vec2 vec2:toPolar()
-- Converts from polar coordinates (r, θ) to cartesian (x, y).
vec2 vec2:toCartesian()
-- Shuffle.
--
ShuffleResult<T> vecN:shuffle<T extends ShuffleMask>(T mask)
-- Unpacking.
{float, float} vec2:unpack()
{float, float, float} vec3:unpack()
{float, float, float, float} vec4:unpack()
Vector length
and lengthSq
can be assigned to change the vector length.
const v = vec2(3, 4);
v.length = -100;
print(v); // Prints `{-60.000000, -80.000000}`
Coordinate normalization
Different games might use different scales and dimensions to describe their worlds, to mitigate the effect of this on generic script development, all inputs and ouputs are transformed to our own coordinate system, which define the up-vector as (0,0,1)
, forward-vector as (1,0,0)
and right-vector as (0,1,0)
. These constants can be found in the World
namespace.
Additionally we define 1 unit to be roughly 1 cm.
-- Directional world constants.
vec3 World.UP -- (0,0,1)
vec3 World.RIGHT -- (0,1,0)
vec3 World.FORWARD -- (1,0,0)

Projection
Two and three dimensional vector types also define special members for projecting and unprojecting coordinates from the world and the screen.
-- Given the world coordinates returns (ScreenX, ScreenY, Depth, W).
-- - If W is negative, the coordinates are behind the camera.
--
vec4 vec3:project()
-- Given the normalized device coordinates coordinates and the distance from camera origin,
-- calculates the unprojected world coordinates, if no distance is given, the direction is returned.
--
vec3 vec2:unproject(float? dist)
-- Converts from normalized device coordinates [-1,1] to pixel coordinates and vice-versa.
--
vec2 vec2:toViewport()
vec2 vec2:toScreen()
Quaternions
The vec4
type can also be used as a quaternion. 3D and 4D vector types define the following helpers for quaternion algebra.
-- Converts euler angles to a quaternion rotation.
vec4 vec3:toQuaternion()
-- Converts the rotation from this vector to b into a quaternion.
vec4 vec3:quaternionTo(vec3 b)
-- Inversed quaternion.
vec4 vec4.inverse
-- Spherically interpolates between this quaternion and b by ratio t.
vec4 vec4:slerp(vec4 b, float t)
-- Converts the quaternion rotation to euler angles.
vec3 vec4:toEuler()
-- Converts the quaternion rotation to a direction vector.
vec3 vec4:toDirection()
-- Combines quaternion rotations.
vec4 vec4:rotateBy(vec4 b)
-- Rotates a three dimensional direction vector by the given quaternion.
vec3 vec3:rotateByQ(vec4 q)
Matrices
Construction and fields
Matrix type can be found under the namespace Matrix
and can be constructed using the following functions.
-- Creates a new matrix with the given values.
matrix mat2x2(float... values)
matrix mat3x3(float... values)
matrix mat4x4(float... values)
-- Creates a new matrix with the given rows.
matrix Matrix.From(vec2 _0, vec2 _1)
matrix Matrix.From(vec3 _0, vec3 _1, vec3 _2)
matrix Matrix.From(vec4 _0, vec4 _1, vec4 _2, vec4 _3)
-- Creates a new identity matrix.
matrix Matrix.Identity()
-- Creates a translation matrix given the vector.
matrix Matrix.Translation(vec3 v)
matrix Matrix.Translation2D(vec2 v)
-- Creates a scaling matrix given the vector.
matrix Matrix.Scaling(vec3 v)
matrix Matrix.Scaling2D(vec2 v)
-- Creates a rotation matrix given the euler angles.
matrix Matrix.Rotation(vec3 v, vec3? rotOrigin)
matrix Matrix.Rotation2D(float v, vec2? rotOrigin)
-- Creates a rotation matrix given an angle and the axis of rotation.
matrix Matrix.AngleAxis(float t, vec3 v, vec3? rotOrigin)
-- Creates a rotation matrix given the quaternion.
matrix Matrix.Quaternion(vec4 v, vec3? rotOrigin)
-- Shorthand for creating a complete transformation matrix.
matrix Matrix.Transform(vec3? translation, vec4? quaternionRotation, vec3? scaling)
matrix Matrix.Transform2D(vec2? translation, float? eulerRotation, vec2? scaling)
-- Creates a perspective projection matrix.
matrix Matrix.Perspective(float fovX, float? zNear)
matrix Matrix.PerspectiveAspect(float fovX, float aspect, float? zNear)
-- Creates a look-at view matrix.
matrix Matrix.LookAt(vec3 eye, vec3 at)
-- Converts the rotation from this vector to b into a matrix.
matrix vec3:rotationTo(vec3 b)
-- Converts the euler angles to a matrix.
matrix vec3:toMatrix()
-- Converts the quaternion to a matrix.
matrix vec4:toMatrix()
-- Copies the matrix as is, note that Lua passes matrices by reference.
matrix matrix.clone
-- Raw fields.
float matrix._00
float matrix._01
float matrix._02
float matrix._03
float matrix._10
float matrix._11
float matrix._12
float matrix._13
float matrix._20
float matrix._21
float matrix._22
float matrix._23
float matrix._30
float matrix._31
float matrix._32
float matrix._33
Matrix objects supports zero-based indexing, comparison, string conversion and arithmetic with other matrices and scalars as metamethods.
vec4 matrix:__index(uint i)
void matrix:__newindex(uint i, vec4 v)
matrix matrix:__add(matrix|float v)
matrix matrix:__sub(matrix|float v)
matrix matrix:__mul(matrix|float v)
matrix matrix:__div(matrix|float v)
matrix matrix:__unm()
const mat = Matrix.Translation(vec3(1, 2, 3));
print(mat);
/* Prints:
| 1.000000 0.000000 0.000000 0.000000 |
| 0.000000 1.000000 0.000000 0.000000 |
| 0.000000 0.000000 1.000000 0.000000 |
| 1.000000 2.000000 3.000000 1.000000 |
*/
print(mat[0]); // Prints `{1.000000, 0.000000, 0.000000, 0.000000}`
// Prints true.
print(mat.inverse.mul(mat) == Matrix.Identity());
For assigning a value to a specific cell in a matrix, you will need to assign the whole vector as __index
metamethod returns the row by value. Alternatively you can use the underscore syntax if the index is known before run-time.
const mat = Matrix.Identity();
mat[0] = vec4(1, 1, 1, 1);
mat._01 = 2;
mat[0][2] = 3; // Bug!
local mat = Matrix.Identity()
mat[0] = vec4(1,1,1,1)
mat._01 = 2
mat[0][2] = 3 -- Bug!
Properties
-- Transforms the given vector by the matrix.
vec4 vec2:product(matrix mat)
vec4 vec3:product(matrix mat)
vec4 vec4:product(matrix mat)
-- Converts a rotation matrix to euler angles / quaternion or a direction vector.
vec3 matrix:toEuler()
vec4 matrix:toQuaternion()
vec3 matrix:toDirection()
-- Applies scaling to the transformation matrix in a fast way.
matrix matrix:scaleBy(vec3 s)
-- Applies a translation to the transformation matrix in a fast way.
matrix matrix:translate(vec3 p)
-- Extracts the scale from a transformation matrix.
vec3 matrix.scale
-- Extracts the translation from a transformation matrix.
vec3 matrix.translation
-- Computes the normalized matrix with no scaling.
matrix matrix.normal
-- Computes the determinant of the matrix.
float matrix.determinant
-- Computes the inverse matrix.
matrix matrix.inverse
-- Tranposes the matrix.
matrix matrix.transpose
Geometry
For the sake of simplifying logic, we provide a bare-bones geometry library under the namespace Geo
.
-- Determines if the ray intersects with the plane, returns the time of intersection if it does.
-- Intersection point can be found by multiplying the direction with the time.
--
float? Geo.RayPlane(vec3 rayOrigin, vec3 rayDirection, vec3 planePoint, vec3 planeNormal)
-- Determines if the ray intersects with the sphere, returns the time of intersection if it does.
-- Intersection point can be found by multiplying the direction with the time.
--
float? Geo.RaySphere(vec3 rayOrigin, vec3 rayDirection, vec3 sphereOrigin, float sphereRadius)
-- Determines if the ray intersects with the box, returns the time of intersection if it does.
-- Intersection point can be found by multiplying the direction with the time. Optionally, allows
-- box to be rotated given the euler angles around a specified point which deafults to boxMin.
--
float? Geo.RayBox(vec3 rayOrigin, vec3 rayDirection, vec3 boxMin, vec3 boxMax, vec3? rot, vec3? rotAround)
-- Determines if the ray intersects with the box, returns the time of intersection if it does.
-- Intersection point can be found by multiplying the direction with the time. Optionally, allows
-- box to be rotated given the euler angles around the box center.
--
float? Geo.RayCBox(vec3 rayOrigin, vec3 rayDirection, vec3 boxMin, vec3 boxMax, vec3? rot)
-- Determines if the two axis-aligned boxes intersect, returns true if so.
--
bool Geo.AABB(vec3 aMin, vec3 aMax, vec3 bMin, vec3 bMax)
-- Projects the point on the ray and returns the closest point on the ray and time travelled on the ray.
--
{vec3, float} Geo.ProjectOnRay(vec3 rayOrigin, vec3 rayDirection, vec3 point)
-- Returns a minimally enclosing circle for the given points.
--
{vec2, float} Geo.FindMEC(vec2[] points)
Miscellaneous library extensions
We also extend the standard math with a few useful functions.
-- Floating point constants.
float math.epsilon -- Smallest number such that (1+x) != x.
float math.infinity -- Infinity.
float math.nan -- Quiet NaN.
-- Clamps the number between min and max.
float math.clamp(float x, float min, float max)
-- Rounds the number to the nearest integer.
float math.round(float x)
-- Copies the sign of S with the magnitude M.
float math.copysign(float m, float s)
-- Computes (sin x, cos x)
{float, float} math.sincos(float x)
-- Lerps between numbers.
float math.lerp(float a, float b, float t)
-- Gets the sign of a number (-1/+1/0).
float math.sign(float n)
-- Beizer and catmull curves.
vecN math.beizer(float t, vecN[] points)
vecN math.catmull(float t, vecN p0, vecN p1, vecN p2, vecN p3, float? alpha)
-- Fit a polynomial p(x) = p[0] * x**deg + ... + p[deg] of degree deg to points (x, y). Returns a vector of coefficients.
--
float[] math.polyfit(float[] x, float[] y, uint deg)