/// Utility functions module
/// Provides auxiliary functions for vector operations, unit conversions, numerical calculations, etc.
///|
/// 3D vector structure
pub struct Vec3 {
x : Double
y : Double
z : Double
}
///|
/// Create a 3D vector
pub fn vec3(x : Double, y : Double, z : Double) -> Vec3 {
Vec3::{ x, y, z }
}
///|
/// Zero vector
pub fn vec3_zero() -> Vec3 {
Vec3::{ x: 0.0, y: 0.0, z: 0.0 }
}
///|
/// Unit vector (x direction)
pub fn vec3_unit_x() -> Vec3 {
Vec3::{ x: 1.0, y: 0.0, z: 0.0 }
}
///|
/// Unit vector (y direction)
pub fn vec3_unit_y() -> Vec3 {
Vec3::{ x: 0.0, y: 1.0, z: 0.0 }
}
///|
/// Unit vector (z direction)
pub fn vec3_unit_z() -> Vec3 {
Vec3::{ x: 0.0, y: 0.0, z: 1.0 }
}
///|
/// Vector addition
pub fn vec3_add(a : Vec3, b : Vec3) -> Vec3 {
Vec3::{ x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }
}
///|
/// Vector subtraction
pub fn vec3_sub(a : Vec3, b : Vec3) -> Vec3 {
Vec3::{ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }
}
///|
/// Vector scalar multiplication
pub fn vec3_scale(v : Vec3, s : Double) -> Vec3 {
Vec3::{ x: v.x * s, y: v.y * s, z: v.z * s }
}
///|
/// Vector dot product (inner product)
pub fn vec3_dot(a : Vec3, b : Vec3) -> Double {
a.x * b.x + a.y * b.y + a.z * b.z
}
///|
/// Vector cross product (outer product)
pub fn vec3_cross(a : Vec3, b : Vec3) -> 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,
}
}
///|
/// Vector magnitude (length)
pub fn vec3_magnitude(v : Vec3) -> Double {
Double::sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
}
///|
/// Square of vector magnitude (avoids square root operation)
pub fn vec3_magnitude_sq(v : Vec3) -> Double {
v.x * v.x + v.y * v.y + v.z * v.z
}
///|
/// Vector normalization (unit vector)
pub fn vec3_normalize(v : Vec3) -> Vec3 {
let mag = vec3_magnitude(v)
if mag > 0.0 {
Vec3::{ x: v.x / mag, y: v.y / mag, z: v.z / mag }
} else {
vec3_zero()
}
}
///|
/// Angle between vectors (radians)
pub fn vec3_angle(a : Vec3, b : Vec3) -> Double {
let dot = vec3_dot(a, b)
let mag_a = vec3_magnitude(a)
let mag_b = vec3_magnitude(b)
if mag_a > 0.0 && mag_b > 0.0 {
let cos_angle = dot / (mag_a * mag_b)
// Clamp to [-1, 1] range to avoid numerical errors
let clamped = if cos_angle > 1.0 {
1.0
} else if cos_angle < -1.0 {
-1.0
} else {
cos_angle
}
@math.acos(clamped)
} else {
0.0
}
}
///|
/// Convert degrees to radians
pub fn deg_to_rad(degrees : Double) -> Double {
degrees * 3.141592653589793 / 180.0
}
///|
/// Convert radians to degrees
pub fn rad_to_deg(radians : Double) -> Double {
radians * 180.0 / 3.141592653589793
}
///|
/// Convert meters to kilometers
pub fn m_to_km(meters : Double) -> Double {
meters / 1000.0
}
///|
/// Convert kilometers to meters
pub fn km_to_m(kilometers : Double) -> Double {
kilometers * 1000.0
}
///|
/// Convert kilograms to grams
pub fn kg_to_g(kilograms : Double) -> Double {
kilograms * 1000.0
}
///|
/// Convert grams to kilograms
pub fn g_to_kg(grams : Double) -> Double {
grams / 1000.0
}
///|
/// Convert seconds to hours
pub fn s_to_h(seconds : Double) -> Double {
seconds / 3600.0
}
///|
/// Convert hours to seconds
pub fn h_to_s(hours : Double) -> Double {
hours * 3600.0
}
///|
/// Sign function
pub fn sign(x : Double) -> Int {
if x > 0.0 {
1
} else if x < 0.0 {
-1
} else {
0
}
}
///|
/// Clamp value to [min, max] range
pub fn clamp(value : Double, min_val : Double, max_val : Double) -> Double {
if value < min_val {
min_val
} else if value > max_val {
max_val
} else {
value
}
}
///|
/// Linear interpolation
pub fn lerp(a : Double, b : Double, t : Double) -> Double {
a + (b - a) * t
}
///|
/// Check if two floating-point numbers are approximately equal (considering floating-point errors)
pub fn approx_eq(a : Double, b : Double, epsilon : Double) -> Bool {
Double::abs(a - b) < epsilon
}
///|
/// Approximate equality check with default precision (epsilon = 1e-9)
pub fn approx_equal(a : Double, b : Double) -> Bool {
approx_eq(a, b, 1.0e-9)
}
///|
/// Vector projection: project vector a onto vector b
pub fn vec3_project(a : Vec3, b : Vec3) -> Vec3 {
let mag_b_sq = vec3_magnitude_sq(b)
if mag_b_sq > 0.0 {
let scale = vec3_dot(a, b) / mag_b_sq
vec3_scale(b, scale)
} else {
vec3_zero()
}
}
///|
/// Vector rejection: component of a perpendicular to b
pub fn vec3_reject(a : Vec3, b : Vec3) -> Vec3 {
vec3_sub(a, vec3_project(a, b))
}
///|
/// Vector reflection: reflect vector a about normal n
pub fn vec3_reflect(a : Vec3, normal : Vec3) -> Vec3 {
let n = vec3_normalize(normal)
let dot = vec3_dot(a, n)
vec3_sub(a, vec3_scale(n, 2.0 * dot))
}
///|
/// Distance between two points (3D)
pub fn vec3_distance(a : Vec3, b : Vec3) -> Double {
vec3_magnitude(vec3_sub(b, a))
}
///|
/// Square of distance between two points (avoids square root)
pub fn vec3_distance_sq(a : Vec3, b : Vec3) -> Double {
vec3_magnitude_sq(vec3_sub(b, a))
}
///|
/// Linear interpolation between two vectors
pub fn vec3_lerp(a : Vec3, b : Vec3, t : Double) -> Vec3 {
Vec3::{ x: lerp(a.x, b.x, t), y: lerp(a.y, b.y, t), z: lerp(a.z, b.z, t) }
}
///|
/// Vector component along direction
pub fn vec3_component(v : Vec3, direction : Vec3) -> Double {
let dir_norm = vec3_normalize(direction)
vec3_dot(v, dir_norm)
}
///|
/// Convert meters per second to kilometers per hour
pub fn mps_to_kmph(mps : Double) -> Double {
mps * 3.6
}
///|
/// Convert kilometers per hour to meters per second
pub fn kmph_to_mps(kmph : Double) -> Double {
kmph / 3.6
}
///|
/// Convert Newtons to pounds-force
pub fn n_to_lbf(newtons : Double) -> Double {
newtons / 4.44822
}
///|
/// Convert pounds-force to Newtons
pub fn lbf_to_n(pounds : Double) -> Double {
pounds * 4.44822
}
///|
/// Convert joules to calories
pub fn j_to_cal(joules : Double) -> Double {
joules / 4.184
}
///|
/// Convert calories to joules
pub fn cal_to_j(calories : Double) -> Double {
calories * 4.184
}
///|
/// Convert watts to horsepower
pub fn w_to_hp(watts : Double) -> Double {
watts / 745.7
}
///|
/// Convert horsepower to watts
pub fn hp_to_w(horsepower : Double) -> Double {
horsepower * 745.7
}
///|
/// Convert Celsius to Kelvin
pub fn celsius_to_kelvin(celsius : Double) -> Double {
celsius + 273.15
}
///|
/// Convert Kelvin to Celsius
pub fn kelvin_to_celsius(kelvin : Double) -> Double {
kelvin - 273.15
}
///|
/// Convert Fahrenheit to Celsius
pub fn fahrenheit_to_celsius(fahrenheit : Double) -> Double {
(fahrenheit - 32.0) * 5.0 / 9.0
}
///|
/// Convert Celsius to Fahrenheit
pub fn celsius_to_fahrenheit(celsius : Double) -> Double {
celsius * 9.0 / 5.0 + 32.0
}
///|
/// Maximum value in array
pub fn max_value(values : Array[Double]) -> Double {
if values.length() == 0 {
0.0
} else {
let mut max_val = values[0]
for i = 1; i < values.length(); i = i + 1 {
if values[i] > max_val {
max_val = values[i]
}
}
max_val
}
}
///|
/// Minimum value in array
pub fn min_value(values : Array[Double]) -> Double {
if values.length() == 0 {
0.0
} else {
let mut min_val = values[0]
for i = 1; i < values.length(); i = i + 1 {
if values[i] < min_val {
min_val = values[i]
}
}
min_val
}
}
///|
/// Absolute maximum value
pub fn abs_max(a : Double, b : Double) -> Double {
if Double::abs(a) > Double::abs(b) {
Double::abs(a)
} else {
Double::abs(b)
}
}
///|
/// Absolute minimum value
pub fn abs_min(a : Double, b : Double) -> Double {
if Double::abs(a) < Double::abs(b) {
Double::abs(a)
} else {
Double::abs(b)
}
}
///|
/// Smooth step interpolation (smooth transition between 0 and 1)
pub fn smoothstep(edge0 : Double, edge1 : Double, x : Double) -> Double {
let t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
t * t * (3.0 - 2.0 * t)
}
///|
/// Check if value is within range [min, max]
pub fn in_range(value : Double, min_val : Double, max_val : Double) -> Bool {
value >= min_val && value <= max_val
}
///|
/// Map value from one range to another
pub fn map_range(
value : Double,
from_min : Double,
from_max : Double,
to_min : Double,
to_max : Double,
) -> Double {
if from_max != from_min {
let normalized = (value - from_min) / (from_max - from_min)
to_min + normalized * (to_max - to_min)
} else {
to_min
}
}