added common.jai for common math procedures Some common procedures for smaller fixed vector sizes were made more optimal. SIMD coming later Added tests to the math/module.jai
379 lines
13 KiB
Text
379 lines
13 KiB
Text
#module_parameters(
|
|
UNITS: enum { radians; degrees; turns; } = .turns,
|
|
RUN_TESTS := false
|
|
);
|
|
|
|
// @todo(judah): dumb we can't use meta.range_for here.
|
|
|
|
U8_Min, U8_Max :: #run meta.lo_for(u8), #run meta.hi_for(u8);
|
|
U16_Min, U16_Max :: #run meta.lo_for(u16), #run meta.hi_for(u16);
|
|
U32_Min, U32_Max :: #run meta.lo_for(u32), #run meta.hi_for(u32);
|
|
U64_Min, U64_Max :: #run meta.lo_for(u64), #run meta.hi_for(u64);
|
|
|
|
S8_Min, S8_Max :: #run meta.lo_for(s8), #run meta.hi_for(s8);
|
|
S16_Min, S16_Max :: #run meta.lo_for(s16), #run meta.hi_for(s16);
|
|
S32_Min, S32_Max :: #run meta.lo_for(s32), #run meta.hi_for(s32);
|
|
S64_Min, S64_Max :: #run meta.lo_for(s64), #run meta.hi_for(s64);
|
|
|
|
F32_Min, F32_Max :: #run meta.lo_for(float32), #run meta.hi_for(float32);
|
|
F64_Min, F64_Max :: #run meta.lo_for(float64), #run meta.hi_for(float64);
|
|
|
|
#load "vec.jai";
|
|
#load "mat.jai";
|
|
#load "ease.jai";
|
|
#load "common.jai";
|
|
|
|
#scope_module;
|
|
|
|
#if RUN_TESTS #run {
|
|
test :: #import "jc/test";
|
|
|
|
vec2_tests();
|
|
vec3_tests();
|
|
vec4_tests();
|
|
vecn_tests();
|
|
mat2_tests();
|
|
mat4_tests();
|
|
quat_tests();
|
|
}
|
|
|
|
#scope_file;
|
|
|
|
meta :: #import "jc/meta";
|
|
basic :: #import "Basic"; // @future
|
|
|
|
math :: #import "Math";
|
|
|
|
test :: #import "jc/test";
|
|
|
|
vec2_tests :: () {
|
|
dot_print("Vec2 % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
a: Vec2 = v2f(0.0, 1.0);
|
|
b: Vec2 = v2f(1.0, 2.0);
|
|
test.expect(*t, a + b == v2f(1.0, 3.0));
|
|
test.expect(*t, b - a == v2f(1.0, 1.0));
|
|
test.expect(*t, a*b == v2f(0.0, 2.0));
|
|
test.expect(*t, a/b == v2f(0.0, 0.5));
|
|
}
|
|
|
|
{
|
|
a: Vec(2, int) = v2i(2, 1);
|
|
b: Vec(2, int) = v2i(1, 0);
|
|
test.expect(*t, a + b == v2i(3, 1));
|
|
test.expect(*t, b - a == v2i(-1, -1));
|
|
test.expect(*t, a*b == v2i(2, 0));
|
|
test.expect(*t, b/a == v2i(0, 0));
|
|
}
|
|
{
|
|
a: Vec2 = v2f(2.3, -4.1);
|
|
b: Vec2 = v2f(1.0, 3.6);
|
|
c := min(a, b);
|
|
test.expect(*t, c == v2f(1.0, -4.1));
|
|
c = max(a, b);
|
|
test.expect(*t, c == v2f(2.3, 3.6));
|
|
}
|
|
}
|
|
|
|
vec3_tests :: () {
|
|
dot_print("Vec3 % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
a: Vec3 = v3f(0.0, 1.0, 2.0);
|
|
b: Vec3 = v3f(1.0, 2.0, 3.0);
|
|
test.expect(*t, a + b == v3f(1.0, 3.0, 5.0));
|
|
test.expect(*t, b - a == v3f(1.0, 1.0, 1.0));
|
|
test.expect(*t, a*b == v3f(0.0, 2.0, 6.0));
|
|
test.expect(*t, a/b == v3f(0.0, 0.5, 0.66666667));
|
|
|
|
a = v3f(1.0, 1.0, 0.0);
|
|
b = v3f(1.0, 0.0, 0.0);
|
|
test.expect(*t, reflect(a, b) == v3f(1.0, -1.0, 0.0));
|
|
test.expect(*t, round(v3f(1.2, 1.7, 1.5)) == v3f(1.0, 2.0, 2.0));
|
|
test.expect(*t, round(v3f(-1.2, -1.7, -1.5)) == v3f(-1.0, -2.0, -2.0));
|
|
|
|
a = v3f(1.0, 0.0, 0.0);
|
|
b = v3f(0.0, 1.0, 0.0);
|
|
test.expect(*t, cross(a, b) == v3f(0.0, 0.0, 1.0));
|
|
}
|
|
{
|
|
a: Vec3 = v3f(2.3, 4.1, 9.0);
|
|
b: Vec3 = v3f(1.0, -3.6, 5.0);
|
|
c := min(a, b);
|
|
test.expect(*t, c == v3f(1.0, -3.6, 5.0));
|
|
c = max(a, b);
|
|
test.expect(*t, c == v3f(2.3, 4.1, 9.0));
|
|
}
|
|
}
|
|
|
|
vec4_tests :: () {
|
|
dot_print("Vec4 % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
a: Vec4 = v4f(2.25, 1.0, 2.0, 1.0);
|
|
b: Vec4 = v4f(4.0, 2.0, 3.0, 1.0);
|
|
test.expect(*t, a + b == v4f(6.25, 3.0, 5.0, 2.0));
|
|
test.expect(*t, b - a == v4f(1.75, 1.0, 1.0, 0.0));
|
|
test.expect(*t, a*b == v4f(9.0, 2.0, 6.0, 1.0));
|
|
test.expect(*t, a/b == v4f(0.5625, 0.5, 2.0/3.0, 1.0));
|
|
}
|
|
}
|
|
|
|
vecn_tests :: () {
|
|
dot_print("VecN % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
a: Vec(16, float);
|
|
b: Vec(16, float);
|
|
for *a {
|
|
it.* = xx it_index;
|
|
}
|
|
for *b {
|
|
it.* = xx(it_index + 1);
|
|
}
|
|
test.expect(*t, a + b == Vec(16, float).{.[1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0, 25.0, 27.0, 29.0, 31.0]});
|
|
test.expect(*t, b - a == Vec(16, float).{.[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]});
|
|
test.expect(*t, a*b == Vec(16, float).{.[0.0, 2.0, 6.0, 12.0, 20.0, 30.0, 42.0, 56.0, 72.0, 90.0, 110.0, 132.0, 156.0, 182.0, 210.0, 240.0]});
|
|
|
|
test.expect(*t, min(a, b) == a);
|
|
test.expect(*t, max(a, b) == b);
|
|
test.expect(*t, max(a, 12) == Vec(16, float).{.[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 12.0, 12.0, 12.0]});
|
|
test.expect(*t, min(a, 13.2) == Vec(16, float).{.[13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 14.0, 15.0]});
|
|
test.expect(*t, clamp(a, 7.25, 12.0) == Vec(16, float).{.[7.25, 7.25, 7.25, 7.25, 7.25, 7.25, 7.25, 7.25, 8, 9, 10, 11, 12, 12, 12, 12]});
|
|
|
|
a1: Vec(16, float) = Vec(16, float).{.[1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 4.7, -1.2, -1.0, -1.5, 11.2, 14.0, 15.0, 14.0, 15.0, 65536.2]};
|
|
basic.print(">> a1: %\n", a1);
|
|
basic.print(">> ceil(a1): %\n", ceil(a1));
|
|
basic.print(">> floor(a1): %\n", floor(a1));
|
|
test.expect(*t, ceil(a1) == Vec(16, float).{.[2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 5.0, -1.0, -1.0, -1.0, 12.0, 14.0, 15.0, 14.0, 15.0, 65537]});
|
|
test.expect(*t, floor(a1) == Vec(16, float).{.[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 4.0, -2.0, -1.0, -2.0, 11.0, 14.0, 15.0, 14.0, 15.0, 65536]});
|
|
|
|
test.expect(*t, dot(a, b) == 1360.0);
|
|
test.expect(*t, abs(a) == a);
|
|
c := a;
|
|
for *c { // Check making every other component negative
|
|
if it_index%2 == 0
|
|
it.* = -it.*;
|
|
}
|
|
test.expect(*t, abs(c) == a);
|
|
test.expect(*t, float_eq(length(normalize(a)), 1));
|
|
test.expect(*t, a + 2 == Vec(16, float).{.[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]});
|
|
test.expect(*t, a - 2 == Vec(16, float).{.[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]});
|
|
test.expect(*t, a*2 == Vec(16, float).{.[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]});
|
|
}
|
|
}
|
|
|
|
mat2_tests :: () {
|
|
dot_print("Mat2 % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
identity := m2_identity;
|
|
a: Mat2 = Mat2.{.[1, 2, 3, 4]};
|
|
test.expect(*t, a*identity == a);
|
|
}
|
|
{
|
|
basic.print("rotation matrix\n");
|
|
rotator := rotation_mat2(from_rad(PI));
|
|
v2 := v2f(1.0, 0.5);
|
|
basic.print("rotating: %\n", v2);
|
|
v2 = rotator*v2;
|
|
basic.print("after rotate: %\n", v2);
|
|
test.expect(*t, v2_eq(v2, v2f(-1.0, -0.5)));
|
|
v2 = rotator*v2;
|
|
basic.print("rotate back: %\n", v2);
|
|
test.expect(*t, v2_eq(v2, v2f(1.0, 0.5)));
|
|
}
|
|
{
|
|
basic.print("determinate\n");
|
|
m := m2(1.0, 3.0, 2.0, 2.0);
|
|
basic.print("m2: %\n", m);
|
|
det := determinate(m);
|
|
basic.print("determinate: %\n", det);
|
|
test.expect(*t, det == -4);
|
|
}
|
|
{
|
|
basic.print("inverse test\n");
|
|
rotator := rotation_mat2(from_rad(PI));
|
|
inv_rotator := inverse(rotator);
|
|
v2 := v2f(0.1, 1.0);
|
|
basic.print(" : %\n", rotator);
|
|
basic.print("inv: %\n", inv_rotator);
|
|
|
|
basic.print("inv_mat*mat = %\n", inv_rotator*rotator);
|
|
test.expect(*t, inv_rotator*rotator == m2_identity);
|
|
|
|
test.expect(*t, inv_rotator*(rotator*v2) == v2);
|
|
basic.print("v2 : %\n", v2);
|
|
v2 = rotator*v2;
|
|
basic.print("rot : %\n", v2);
|
|
v2 = inv_rotator*v2;
|
|
basic.print("inv : %\n", v2);
|
|
}
|
|
}
|
|
|
|
mat4_tests :: () {
|
|
dot_print("Mat4 % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
identity := m4_identity;
|
|
a: Mat4 = Mat4.{.[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]};
|
|
test.expect(*t, a*identity == a);
|
|
}
|
|
{
|
|
UP_VECTOR :: Vec3.{.[0.0, 1.0, 0.0]};
|
|
camera := v3f(1.0, 0.0, 1.0);
|
|
looking_at := v3f(0.0, 0.0, 0.0);
|
|
look_at := make_look_at(camera, looking_at, UP_VECTOR);
|
|
basic.print("lookat matrix: %\n", look_at);
|
|
basic.print("lookat from 0,1,0: %\n", look_at*v4f(0.0, 1.0, 0.0, 1.0));
|
|
}
|
|
{
|
|
translator := translate(v3f(10.0, 5.0, 2.0));
|
|
v3 := v3f(1.0, 2.0, 1.0);
|
|
v4 := v4f(v3, 1.0);
|
|
test.expect(*t, v4 == v4f(1.0, 2.0, 1.0, 1.0));
|
|
test.expect(*t, translator*v4 == v4f(11.0, 7.0, 3.0, 1.0));
|
|
}
|
|
{
|
|
basic.print("rotation matrix\n");
|
|
rotator := rotation_mat4(v3f(0.0, 1.0, 0.0), from_rad(PI));
|
|
v4 := v4f(1.0, 0.5, 0.1, 1.0);
|
|
basic.print("rotating: %\n", v4);
|
|
v4 = rotator*v4;
|
|
basic.print("after rotate: %\n", v4);
|
|
test.expect(*t, v4_eq(v4, v4f(-1.0, 0.5, -0.1, 1.0)));
|
|
v4 = rotator*v4;
|
|
basic.print("rotate back: %\n", v4);
|
|
test.expect(*t, v4_eq(v4, v4f(1.0, 0.5, 0.1, 1.0)));
|
|
}
|
|
{
|
|
rotator_x := rotation_mat4(v3f(1.0, 0.0, 0.0), from_rad(0.4*PI));
|
|
rotator_y := rotation_mat4(v3f(0.0, 1.0, 0.0), from_rad(PI));
|
|
camera_rotator := rotator_x*rotator_y;
|
|
basic.print("big rotator: %\n", camera_rotator);
|
|
det := determinate(camera_rotator);
|
|
basic.print("determinate: %\n", det);
|
|
}
|
|
{
|
|
basic.print("inverse test\n");
|
|
rotator := rotation_mat4(v3f(0.0, 0.0, 1.0), from_rad(PI));
|
|
inv_rotator := inverse(rotator);
|
|
v4 := v4f(0.1, 1.0, 0.0, 1.0);
|
|
basic.print(" : %\n", rotator);
|
|
basic.print("inv: %\n", inv_rotator);
|
|
|
|
basic.print("inv_mat*mat = %\n", inv_rotator*rotator);
|
|
test.expect(*t, inv_rotator*rotator == m4_identity);
|
|
|
|
basic.print("v4 : %\n", v4);
|
|
v4 = rotator*v4;
|
|
basic.print("rot : %\n", v4);
|
|
v4 = inv_rotator*v4;
|
|
basic.print("inv : %\n", v4);
|
|
}
|
|
}
|
|
|
|
quat_tests :: () {
|
|
dot_print("Quaternion % tests", UNITS);
|
|
t: test.T;
|
|
{
|
|
qi := quat_identity;
|
|
basic.print("qi lensq: %\n", length_squared(qi));
|
|
basic.print("qi len : %\n", length(qi));
|
|
q: Quat = rotation_quat(from_rad(PI), v3f(0.0, 1.0, 0.0));
|
|
q2: Quat = rotation_quat(from_rad(PI), v3f(1.0, 0.0, 1.0));
|
|
basic.print("q : %\n", q);
|
|
basic.print("q2 : %\n", q2);
|
|
basic.print("dot : %\n", dot(q, q2));
|
|
basic.print("mul : %\n", q*q2);
|
|
qc := conjugate(q);
|
|
basic.print("conjugate : %\n", qc);
|
|
inv_q := inverse(q);
|
|
basic.print("inverse : %\n", inv_q);
|
|
test.expect(*t, q*inv_q == qi);
|
|
|
|
q1 := quat(2, 0, 0, 0);
|
|
q2 = quat(1, 1, -1, 0);
|
|
basic.print("q1: %\n", q1);
|
|
basic.print("q2: %\n", q2);
|
|
basic.print("q1*q2: %\n", q1*q2);
|
|
basic.print("dot(q2,q2): %\n", dot(q2, q2));
|
|
basic.print("dot(q1,q2): %\n", dot(q1, q2));
|
|
basic.print("q1*q2*conj(q1): %\n", q1*q2*conjugate(q1));
|
|
c := q1*q2*conjugate(q1);
|
|
test.expect(*t, float_eq(c.w, 0.0));
|
|
|
|
q = rotation_quat(from_rad(PI/4.0), v3f(0.0, 0.0, 1.0));
|
|
p := v3f(2.0, 0.0, 0.0);
|
|
basic.print("q: %\n", q);
|
|
basic.print("p: %\n", p);
|
|
c1 := q*quat(p, 0)*conjugate(q);
|
|
basic.print("q*quat(p, 0)*conjugate(q): %\n", c1);
|
|
c2 := q*p;
|
|
basic.print("q*p : %\n", c2);
|
|
test.expect(*t, v3_eq(c2, v3f(math.sqrt(2.0), math.sqrt(2.0), 0.0)));
|
|
test.expect(*t, v3_eq(c1.xyz, c2));
|
|
|
|
basic.print("quaternion to matrix rotation\n");
|
|
q = rotation_quat(from_rad(PI), v3f(0.0, 1.0, 0.0));
|
|
basic.print("q: %\n", q);
|
|
m := rotation_mat4(q);
|
|
print_mat4(m);
|
|
p1 := v4f(2.0, 0.0, 0.0, 1.0);
|
|
basic.print("p : %\n", p1);
|
|
basic.print("p': %\n", m*p1);
|
|
test.expect(*t, v4_eq(m*p1, v4f(-2.0, 0.0, -0.0, 1.0)));
|
|
|
|
q1 = rotation_quat(from_rad(PI), v3f(0.0, 1.0, 0.0));
|
|
q2 = rotation_quat(from_rad(2.0*PI), v3f(0.0, 1.0, 0.0));
|
|
perc := 0.5;
|
|
q = slerp(q1, q2, perc);
|
|
basic.print("q1: %\n", q1);
|
|
basic.print("q2: %\n", q2);
|
|
basic.print("slerp q1, q2, %: %\n", perc, q);
|
|
|
|
q = rotation_quat(from_rad(PI/4.0), v3f(0.0, 0.0, 1.0));
|
|
print_mat4(rotation_mat4(q));
|
|
}
|
|
}
|
|
|
|
dot_print :: (fmt: string, args: ..Any, width: int = 30) {
|
|
dots := ".................................................................";
|
|
str := basic.tprint(fmt, args);
|
|
dots.count = width - str.count;
|
|
basic.print("%0%", str, dots);
|
|
basic.print("\n");
|
|
}
|
|
|
|
check :: (cond: bool, loc := #caller_location) -> bool {
|
|
if !cond {
|
|
basic.print("\tTest failed at %:%\n", loc.fully_pathed_filename, loc.line_number);
|
|
}
|
|
return cond;
|
|
}
|
|
|
|
v2_eq :: (a: Vec2, b: Vec2) -> bool {
|
|
return float_eq(a.x, b.x) && float_eq(a.y, b.y);
|
|
}
|
|
|
|
v3_eq :: (a: Vec3, b: Vec3) -> bool {
|
|
return float_eq(a.x, b.x) && float_eq(a.y, b.y) && float_eq(a.z, b.z);
|
|
}
|
|
|
|
v4_eq :: (a: Vec4, b: Vec4) -> bool {
|
|
return float_eq(a.x, b.x) && float_eq(a.y, b.y) && float_eq(a.z, b.z) && float_eq(a.w, b.w);
|
|
}
|
|
|
|
// Smallest difference where a float is basically that value
|
|
EPSILON :: 0.001;
|
|
float_eq :: (f: float, with: float) -> bool {
|
|
return f > with - EPSILON && f < with + EPSILON;
|
|
}
|
|
|
|
print_mat4 :: (m: Mat4) {
|
|
basic.print("| % % % % |\n", m._00, m._01, m._02, m._03);
|
|
basic.print("| % % % % |\n", m._10, m._11, m._12, m._13);
|
|
basic.print("| % % % % |\n", m._20, m._21, m._22, m._23);
|
|
basic.print("| % % % % |\n", m._30, m._31, m._32, m._33);
|
|
}
|