/* Vec is a generic set of N named values of type T (aka. a mathematical vector) The values can be accessed via array index or their common component names: - u, v, d - x, y, z, w - r, g, b, a - x, y, width, height - min_x, min_y, max_x, max_y - 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) Sadly, Vec does *NOT* solve the problem of interfacing with external vector types (including Jai's 'Math' module). To fix this, create two helper macros: to_jai :: (v: Vec4) -> jmath.Vector4 #expand { return v.(jmath.Vector4,force); } to_jai :: (v: *Vec4) -> *jmath.Vector4 #expand { return v.(*jmath.Vector4); } */ 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)"; } { #assert (N > 0) "Vec N cannot be <= 0"; // Vecs are backed by an array internally. The #insert block below // generates #place'd unions of named fields or cN fields when N is > 4. #as components: [N]T; #insert -> string { b: basic.String_Builder; basic.append(*b, "#place components;\n"); fields :: []string.[ string.[ "x", "r", "u", "min_x", "" ], string.[ "y", "g", "v", "min_y", "" ], string.[ "z", "b", "d", "max_x", "width" ], string.[ "w", "a", "" , "max_y", "height" ], ]; 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) = ---; // @todo(judah): unroll for N <= 4 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) = ---; // @todo(judah): unroll for N <= 4 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) = ---; // @todo(judah): unroll for N <= 4 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) = ---; // @todo(judah): unroll for N <= 4 for l res[it_index] = it / r[it_index]; return res; } operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric #modify { return is_type_scalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; for l res[it_index] = it + r; return res; } operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #modify { return is_type_scalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; for l res[it_index] = it - r; return res; } operator - :: inline (l: $R, r: Vec) -> Vec(l.N, l.T) #no_abc #modify { return is_type_scalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; for l res[it_index] = r - it; return res; } operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric #modify { return is_type_scalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; for l res[it_index] = it*r; return res; } operator / :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #modify { return is_type_scalar(R), "type is not integer or float"; } { res: Vec(l.N, l.T) = ---; for l res[it_index] = it/r; return res; } operator == :: inline (l: Vec, r: Vec(l.N, l.T)) -> bool #no_abc { for l if it != r[it_index] return false; return true; } min :: (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { res: Vec(l.N, l.T) = ---; n := l.N - 1; while n >= 0 { if l[n] < r[n] res[n] = l[n]; else res[n] = r[n]; n -= 1; } return res; } max :: (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { res: Vec(l.N, l.T) = ---; n := l.N - 1; while n >= 0 { if l[n] > r[n] res[n] = l[n]; else res[n] = r[n]; n -= 1; } return res; } ceil :: (l: Vec, x: l.T) -> Vec(l.N, l.T) #no_abc { r: Vec(l.N, l.T) = ---; n := l.N - 1; while n >= 0 { if x < l[n] r[n] = x; else r[n] = l[n]; n -= 1; } return r; } floor :: (l: Vec, x: l.T) -> Vec(l.N, l.T) #no_abc { r: Vec(l.N, l.T) = ---; n := l.N - 1; while n >= 0 { if x > l[n] r[n] = x; else r[n] = l[n]; n -= 1; } return r; } clamp :: (v: Vec, low: v.T, high: v.T) -> Vec(v.N, v.T) #no_abc { r: Vec(v.N, v.T) = ---; n := v.N - 1; while n >= 0 { if v[n] < low r[n] = low; else if v[n] > high r[n] = high; else r[n] = v[n]; n -= 1; } return r; } dot :: (a: Vec, b: Vec(a.N, a.T)) -> a.T #no_abc { sum: a.T; n := a.N - 1; while n >= 0 { sum += a[n]*b[n]; n -= 1; } return sum; } length_squared :: (v: Vec) -> float #no_abc { return dot(v, v); } length :: (v: Vec) -> float #no_abc { return math.sqrt(dot(v, v)); } abs :: (v: Vec) -> Vec(v.N, v.T) #no_abc { r: Vec(v.N, v.T) = ---; n := v.N - 1; while n >= 0 { if v[n] < 0 r[n] = -v[n]; else r[n] = v[n]; n -= 1; } return r; } norm :: normalize; normalize :: (v: Vec) -> Vec(v.N, v.T) #no_abc { return v/length(v); } lerp :: (a: Vec, b: Vec(a.N, a.T), t: float) -> Vec(a.N, a.T) #no_abc { r: Vec(a.N, a.T) = ---; n := a.N - 1; while n >= 0 { r[n] = a[n] + t*(b[n] - a[n]); n -= 1; } return r; } // Note(Jesse): I don't think this is needed for bigger vectors reflect :: (v: Vec3, p: Vec3) -> Vec3 #no_abc { projection := p*dot(v, p)/length_squared(p); return 2*projection - v; } reflect :: (v: Vec2, p: Vec2) -> Vec2 #no_abc { projection := p*dot(v, p)/length_squared(p); return 2*projection - v; } round :: (v: Vec($N, $T)) -> Vec(N, T) #no_abc #modify { return is_type_float(T), "Used non-float vector on round"; } { r: Vec(N, T) = ---; n := N - 1; while n >= 0 { if v[n] < 0 r[n] = (v[n] - 0.5).(int).(float); else r[n] = (v[n] + 0.5).(int).(float); n -= 1; } return r; } // 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 is_type_float(T), "use v2i for integer arguments"; } #expand { return .{ x = x, y = y }; } v2i :: (x: $T = 0, y: T = 0) -> Vec(2, T) #modify { return is_type_integer(T), "use v2f for float arguments"; } #expand { return .{ x = x, y = y }; } v3f :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec3 #modify { return is_type_float(T), "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 is_type_integer(T), "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 is_type_float(T), "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 is_type_integer(T), "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 }; } #scope_file math :: #import "Math"; is_type_integer :: (t: Type) -> bool { return t.(*Type_Info).type == .INTEGER; } is_type_float :: (t: Type) -> bool { return t.(*Type_Info).type == .FLOAT; } is_type_scalar :: (t: Type) -> bool { return is_type_integer(t) || is_type_float(t); }