///|
/// Small, allocation-conscious math primitives used by asset diagnostics.
/// They deliberately stay independent from a renderer so the same analysis
/// works in native and wasm-gc builds.
///|
pub(all) struct Vec2 {
x : Double
y : Double
} derive(Debug, ToJson, Eq)
///|
pub(all) struct Mat4 {
values : Array[Double]
} derive(Debug, ToJson, Eq)
///|
pub(all) struct Bounds3 {
min : Vec3
max : Vec3
valid : Bool
} derive(Debug, ToJson, Eq)
///|
pub fn vec2(x : Double, y : Double) -> Vec2 {
{ x, y }
}
///|
pub fn vec3(x : Double, y : Double, z : Double) -> Vec3 {
{ x, y, z }
}
///|
pub fn quat(x : Double, y : Double, z : Double, w : Double) -> Quat {
{ x, y, z, w }
}
///|
pub fn vec2_add(a : Vec2, b : Vec2) -> Vec2 {
{ x: a.x + b.x, y: a.y + b.y }
}
///|
pub fn vec2_sub(a : Vec2, b : Vec2) -> Vec2 {
{ x: a.x - b.x, y: a.y - b.y }
}
///|
pub fn vec2_scale(a : Vec2, factor : Double) -> Vec2 {
{ x: a.x * factor, y: a.y * factor }
}
///|
pub fn vec2_dot(a : Vec2, b : Vec2) -> Double {
a.x * b.x + a.y * b.y
}
///|
pub fn vec2_length_squared(a : Vec2) -> Double {
vec2_dot(a, a)
}
///|
pub fn vec3_add(a : Vec3, b : Vec3) -> Vec3 {
{ x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }
}
///|
pub fn vec3_sub(a : Vec3, b : Vec3) -> Vec3 {
{ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }
}
///|
pub fn vec3_scale(a : Vec3, factor : Double) -> Vec3 {
{ x: a.x * factor, y: a.y * factor, z: a.z * factor }
}
///|
pub fn vec3_dot(a : Vec3, b : Vec3) -> Double {
a.x * b.x + a.y * b.y + a.z * b.z
}
///|
pub fn vec3_cross(a : Vec3, b : Vec3) -> Vec3 {
{
x: a.y * b.z - a.z * b.y,
y: a.z * b.x - a.x * b.z,
z: a.x * b.y - a.y * b.x,
}
}
///|
pub fn vec3_length_squared(a : Vec3) -> Double {
vec3_dot(a, a)
}
///|
pub fn vec3_min(a : Vec3, b : Vec3) -> Vec3 {
{
x: if a.x < b.x {
a.x
} else {
b.x
},
y: if a.y < b.y {
a.y
} else {
b.y
},
z: if a.z < b.z {
a.z
} else {
b.z
},
}
}
///|
pub fn vec3_max(a : Vec3, b : Vec3) -> Vec3 {
{
x: if a.x > b.x {
a.x
} else {
b.x
},
y: if a.y > b.y {
a.y
} else {
b.y
},
z: if a.z > b.z {
a.z
} else {
b.z
},
}
}
///|
pub fn identity_mat4() -> Mat4 {
{
values: [
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
1.0,
],
}
}
///|
pub fn mat4_from_trs(translation : Vec3, rotation : Quat, scale : Vec3) -> Mat4 {
let x2 = rotation.x + rotation.x
let y2 = rotation.y + rotation.y
let z2 = rotation.z + rotation.z
let xx = rotation.x * x2
let xy = rotation.x * y2
let xz = rotation.x * z2
let yy = rotation.y * y2
let yz = rotation.y * z2
let zz = rotation.z * z2
let wx = rotation.w * x2
let wy = rotation.w * y2
let wz = rotation.w * z2
{
values: [
(1.0 - (yy + zz)) * scale.x,
(xy + wz) * scale.x,
(xz - wy) * scale.x,
0.0,
(xy - wz) * scale.y,
(1.0 - (xx + zz)) * scale.y,
(yz + wx) * scale.y,
0.0,
(xz + wy) * scale.z,
(yz - wx) * scale.z,
(1.0 - (xx + yy)) * scale.z,
0.0,
translation.x,
translation.y,
translation.z,
1.0,
],
}
}
///|
pub fn mat4_mul(a : Mat4, b : Mat4) -> Mat4 {
let values : Array[Double] = []
for row in 0..<4 {
for col in 0..<4 {
let mut sum = 0.0
for k in 0..<4 {
sum += a.values[row * 4 + k] * b.values[k * 4 + col]
}
values.push(sum)
}
}
{ values, }
}
///|
pub fn mat4_transform_point(m : Mat4, point : Vec3) -> Vec3 {
let x = m.values[0] * point.x +
m.values[4] * point.y +
m.values[8] * point.z +
m.values[12]
let y = m.values[1] * point.x +
m.values[5] * point.y +
m.values[9] * point.z +
m.values[13]
let z = m.values[2] * point.x +
m.values[6] * point.y +
m.values[10] * point.z +
m.values[14]
let w = m.values[3] * point.x +
m.values[7] * point.y +
m.values[11] * point.z +
m.values[15]
if w != 0.0 && w != 1.0 {
{ x: x / w, y: y / w, z: z / w }
} else {
{ x, y, z }
}
}
///|
pub fn bounds_empty() -> Bounds3 {
{
min: { x: 0.0, y: 0.0, z: 0.0 },
max: { x: 0.0, y: 0.0, z: 0.0 },
valid: false,
}
}
///|
pub fn bounds_include(bounds : Bounds3, point : Vec3) -> Bounds3 {
if bounds.valid {
{
min: vec3_min(bounds.min, point),
max: vec3_max(bounds.max, point),
valid: true,
}
} else {
{ min: point, max: point, valid: true }
}
}
///|
pub fn bounds_union(a : Bounds3, b : Bounds3) -> Bounds3 {
if !a.valid {
b
} else if !b.valid {
a
} else {
{ min: vec3_min(a.min, b.min), max: vec3_max(a.max, b.max), valid: true }
}
}
///|
pub fn bounds_center(bounds : Bounds3) -> Vec3 {
{
x: (bounds.min.x + bounds.max.x) / 2.0,
y: (bounds.min.y + bounds.max.y) / 2.0,
z: (bounds.min.z + bounds.max.z) / 2.0,
}
}
///|
pub fn bounds_extent(bounds : Bounds3) -> Vec3 {
vec3_sub(bounds.max, bounds.min)
}
///|
pub fn bounds_surface_area(bounds : Bounds3) -> Double {
let e = bounds_extent(bounds)
2.0 * (e.x * e.y + e.y * e.z + e.z * e.x)
}