diff --git a/_run_all_tests.jai b/_run_all_tests.jai index 0fec6bf..93c6518 100644 --- a/_run_all_tests.jai +++ b/_run_all_tests.jai @@ -5,6 +5,9 @@ #import,file "./module.jai"(true); #import,file "./encoding/module.jai"(true); #import,file "./hash/module.jai"(true); + rmath :: #import,file "./math/module.jai"(.radians, true); + dmath :: #import,file "./math/module.jai"(.degrees, true); + tmath :: #import,file "./math/module.jai"(.turns, true); } diff --git a/math/mat.jai b/math/mat.jai new file mode 100644 index 0000000..8702d32 --- /dev/null +++ b/math/mat.jai @@ -0,0 +1,43 @@ +Mat4 :: #type,distinct Vec(4 * 4, float); + +m4 :: (r0: Vec4, r1: Vec4, r2: Vec4, r3: Vec4) -> Mat4 #expand { + return .{ + components = .[ + r0.x, r0.y, r0.z, r0.w, + r1.x, r1.y, r1.z, r1.w, + r2.x, r2.y, r2.z, r2.w, + r3.x, r3.y, r3.z, r3.w, + ], + }; +} + +m4_identity :: #bake_arguments m4d(diag = 1); + +m4d :: (diag: float) -> Mat4 #expand { + res: Mat4; + res[0][0] = diag; + res[1][1] = diag; + res[2][2] = diag; + res[3][3] = diag; + return res; +} + +operator [] :: inline (m: Mat4, idx: int) -> Vec4 #expand { + bounds_check_index(idx * 4, m.N); + return (m.components[idx * 4]).(*Vec4).*; +} + +operator *[] :: inline (m: *Mat4, $$idx: int) -> *Vec4 #no_abc { + N :: Mat4.{}.N; // @note(judah): dumb workaround for not being able to access pointer type field constants + bounds_check_index(idx * 4, N); + return (*m.components[idx * 4]).(*Vec4); +} + +operator []= :: inline (m: *Mat4, $$idx: int, value: Vec4) #no_abc { + N :: Mat4.{}.N; + bounds_check_index(idx * 4, N); + + ptr := (*m.components[idx * 4]).(*Vec4); + ptr.*= value; +} + diff --git a/math/module.jai b/math/module.jai new file mode 100644 index 0000000..00d0bbf --- /dev/null +++ b/math/module.jai @@ -0,0 +1,25 @@ +#module_parameters( + UNITS: enum { radians; degrees; turns; } = .turns, + RUN_TESTS := false +); + +#load "vec.jai"; +#load "mat.jai"; + +#scope_module; + +bounds_check_index :: ($$idx: int, $count: int, loc := #caller_location) #expand { + #if is_constant(idx) { + #assert (idx >= 0 && idx < count) "bounds check failed"; + } + else { + basic.assert(idx >= 0 && idx < count, "bounds check failed! index: % (min: 0, max: %)", idx, count, loc = loc); + } +} + + +basic :: #import "Basic"; + +#if RUN_TESTS { + test :: #import,file "../test/module.jai"; +} diff --git a/math/vec.jai b/math/vec.jai new file mode 100644 index 0000000..eb3b3f7 --- /dev/null +++ b/math/vec.jai @@ -0,0 +1,150 @@ +/* + Vec is a generic set of N named values of type T. + + The values can be accessed via array index or their common component names: + + - u, v + - x, y, z, w + - r, g, b, a + - c0, c1, c2, c3, ... cN + + For most use cases, opt for the named variants of this type: + + Vec2 : Vec(2, float) + Vec3 : Vec(3, float) + Vec4 : Vec(4, float) + Quat : Vec(4, float) +*/ +Vec :: struct(N: int, T: Type) +#modify { + info := T.(*Type_Info); + return info.type == .INTEGER || info.type == .FLOAT, "Vec T must be a numeric type (int or float)"; +} { + #as components: [N]T; + #insert -> string { + b: basic.String_Builder; + basic.append(*b, "#place components;\n"); + + fields :: []string.[ + string.[ "x", "r", "u" ], + string.[ "y", "g", "v" ], + string.[ "z", "b", "" ], + string.[ "w", "a", "" ], + ]; + + for i: 0..N - 1 { + if i < fields.count { + basic.append(*b, "union {\n"); + for field: fields[i] if field.count != 0 { + basic.print_to_builder(*b, "\t%: T = ---;\n", field); + } + basic.print_to_builder(*b, "\tc%: T = ---;\n", i); + basic.append(*b, "};\n"); + } + else { + basic.print_to_builder(*b, "c%: T = ---;\n", i); + } + } + + return basic.builder_to_string(*b); + }; +} + +operator [] :: (v: Vec, $$idx: int) -> v.T #no_abc { + bounds_check_index(idx, v.N); + return v.components[idx]; +} + +operator *[] :: (v: *Vec, $$idx: int) -> *v.T #no_abc { + bounds_check_index(idx, v.N); + return *v.components[idx]; +} + +operator []= :: (v: *Vec, $$idx: int, value: v.T) #no_abc { + bounds_check_index(idx, v.N); + v.components[idx] = value; +} + +for_expansion :: (v: *Vec, body: Code, flags: For_Flags) #expand { + for i: 0..v.N - 1 { + `it_index := i; + #if flags & .POINTER { + `it := *v.components[i]; + } else { + `it := v.components[i]; + } + + #insert,scope(body) body; + } +} + +operator + :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { + res: Vec(l.N, l.T) = ---; + for l res[it_index] = it + r[it_index]; + return res; +} + +operator - :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { + res: Vec(l.N, l.T) = ---; + for l res[it_index] = it - r[it_index]; + return res; +} + +operator * :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { + res: Vec(l.N, l.T) = ---; + for l res[it_index] = it * r[it_index]; + return res; +} + +operator / :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { + res: Vec(l.N, l.T) = ---; + for l res[it_index] = it / r[it_index]; + return res; +} + +// Concrete vector types (the usual cases) + +Vec2 :: Vec(2, float); +Vec3 :: Vec(3, float); +Vec4 :: Vec(4, float); +Quat :: Vec4; + +v2f :: (x: $T = 0, y: T = 0) -> Vec2 +#modify { return T.(*Type_Info).type == .FLOAT, "use v2i for integer arguments"; } +#expand { + return .{ x = x, y = y }; +} + +v2i :: (x: $T = 0, y: T = 0) -> Vec(2, T) +#modify { return T.(*Type_Info).type == .INTEGER, "use v2f for float arguments"; } +#expand { + return .{ x = x, y = y }; +} + +v3f :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec3 +#modify { return T.(*Type_Info).type == .FLOAT, "use v3i for integer arguments"; } +#expand { + return .{ x = x, y = y, z = z }; +} + +v3i :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec(3, T) +#modify { return T.(*Type_Info).type == .INTEGER, "use v3f for float arguments"; } +#expand { + return .{ x = x, y = y, z = z }; +} + +v4f :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec4 +#modify { return T.(*Type_Info).type == .FLOAT, "use v4i for integer arguments"; } +#expand { + return .{ x = x, y = y, z = z, w = w }; +} + +v4i :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec(4, T) +#modify { return T.(*Type_Info).type == .INTEGER, "use v4f for float arguments"; } +#expand { + return .{ x = x, y = y, z = z, w = w }; +} + +quat :: (x: float = 0, y: float = 0, z: float = 0, w: float = 0) -> Quat #expand { + return .{ x = x, y = y, z = z, w = w }; +}