const std = @import("std"); const assert = std.debug.assert; pub const bytes_per_kib = 1024; pub const bytes_per_mib = bytes_per_kib * 1024; pub const bytes_per_gib = bytes_per_mib * 1024; pub const bytes_per_kb = 1000; pub const bytes_per_mb = bytes_per_kb * 1000; pub const bytes_per_gb = bytes_per_mb * 1000; pub const Range = struct { from: f32, to: f32, pub const zero = init(0, 0); pub fn init(from: f32, to: f32) Range { return Range{ .from = from, .to = to }; } pub fn getSize(self: Range) f32 { return @abs(self.from - self.to); } pub fn random(self: Range, rng: std.Random) f32 { return self.from + rng.float(f32) * (self.to - self.from); } }; pub const Vec2 = extern struct { x: f32, y: f32, pub const zero = init(0, 0); pub fn init(x: f32, y: f32) Vec2 { return Vec2{ .x = x, .y = y, }; } pub fn initFromInt(T: type, x: T, y: T) Vec2 { return .init(@floatFromInt(x), @floatFromInt(y)); } pub fn initAngle(angle: f32) Vec2 { return Vec2{ .x = @cos(angle), .y = @sin(angle), }; } pub fn rotateLeft90(self: Vec2) Vec2 { return Vec2.init(self.y, -self.x); } pub fn rotateRight90(self: Vec2) Vec2 { return Vec2.init(-self.y, self.x); } pub fn flip(self: Vec2) Vec2 { return Vec2.init(-self.x, -self.y); } pub fn add(self: Vec2, other: Vec2) Vec2 { return Vec2.init( self.x + other.x, self.y + other.y, ); } pub fn sub(self: Vec2, other: Vec2) Vec2 { return Vec2.init( self.x - other.x, self.y - other.y, ); } pub fn multiplyScalar(self: Vec2, value: f32) Vec2 { return Vec2.init( self.x * value, self.y * value, ); } pub fn multiply(self: Vec2, other: Vec2) Vec2 { return Vec2.init( self.x * other.x, self.y * other.y, ); } pub fn divide(self: Vec2, other: Vec2) Vec2 { return Vec2.init( self.x / other.x, self.y / other.y, ); } pub fn divideScalar(self: Vec2, value: f32) Vec2 { return Vec2.init( self.x / value, self.y / value, ); } pub fn length(self: Vec2) f32 { return @sqrt(self.x*self.x + self.y*self.y); } pub fn distance(self: Vec2, other: Vec2) f32 { return self.sub(other).length(); } pub fn limitLength(self: Vec2, max_length: f32) Vec2 { const self_length = self.length(); if (self_length > max_length) { return Vec2.init(self.x / self_length * max_length, self.y / self_length * max_length); } else { return self; } } pub fn normalized(self: Vec2) Vec2 { const self_length = self.length(); if (self_length == 0) { return Vec2.init(0, 0); } return Vec2.init(self.x / self_length, self.y / self_length); } pub fn initScalar(value: f32) Vec2 { return Vec2.init(value, value); } pub fn eql(self: Vec2, other: Vec2) bool { return self.x == other.x and self.y == other.y; } pub fn format(self: Vec2, writer: *std.io.Writer) std.io.Writer.Error!void { try writer.print("Vec2{{ {d}, {d} }}", .{ self.x, self.y }); } }; pub const Vec3 = extern struct { x: f32, y: f32, z: f32, pub const zero = init(0, 0, 0); pub fn init(x: f32, y: f32, z: f32) Vec3 { return Vec3{ .x = x, .y = y, .z = z, }; } pub fn initScalar(value: f32) Vec3 { return Vec3.init(value, value, value); } pub fn asArray(self: *Vec3) []f32 { const ptr: [*]f32 = @alignCast(@ptrCast(@as(*anyopaque, @ptrCast(self)))); return ptr[0..3]; } pub fn lerp(a: Vec3, b: Vec3, t: f32) Vec3 { return Vec3.init( std.math.lerp(a.x, b.x, t), std.math.lerp(a.y, b.y, t), std.math.lerp(a.z, b.z, t), ); } pub fn clamp(self: Vec3, min_value: f32, max_value: f32) Vec3 { return Vec3.init( std.math.clamp(self.x, min_value, max_value), std.math.clamp(self.y, min_value, max_value), std.math.clamp(self.z, min_value, max_value), ); } }; pub const Vec4 = extern struct { x: f32, y: f32, z: f32, w: f32, pub const zero = init(0, 0, 0, 0); pub fn init(x: f32, y: f32, z: f32, w: f32) Vec4 { return Vec4{ .x = x, .y = y, .z = z, .w = w }; } pub fn initVec3XYZ(vec3: Vec3, w: f32) Vec4 { return init(vec3.x, vec3.y, vec3.z, w); } pub fn initScalar(value: f32) Vec4 { return Vec4.init(value, value, value, value); } pub fn multiplyMat4(left: Vec4, right: Mat4) Vec4 { var result: Vec4 = undefined; // TODO: SIMD result.x = left.x * right.columns[0][0]; result.y = left.x * right.columns[0][1]; result.z = left.x * right.columns[0][2]; result.w = left.x * right.columns[0][3]; result.x += left.y * right.columns[1][0]; result.y += left.y * right.columns[1][1]; result.z += left.y * right.columns[1][2]; result.w += left.y * right.columns[1][3]; result.x += left.z * right.columns[2][0]; result.y += left.z * right.columns[2][1]; result.z += left.z * right.columns[2][2]; result.w += left.z * right.columns[2][3]; result.x += left.w * right.columns[3][0]; result.y += left.w * right.columns[3][1]; result.z += left.w * right.columns[3][2]; result.w += left.w * right.columns[3][3]; return result; } pub fn multiply(left: Vec4, right: Vec4) Vec4 { return init( left.x * right.x, left.y * right.y, left.z * right.z, left.w * right.w ); } pub fn asArray(self: *Vec4) []f32 { const ptr: [*]f32 = @alignCast(@ptrCast(@as(*anyopaque, @ptrCast(self)))); return ptr[0..4]; } pub fn initArray(array: []const f32) Vec4 { return Vec4.init(array[0], array[1], array[2], array[3]); } pub fn lerp(a: Vec4, b: Vec4, t: f32) Vec4 { return Vec4.init( std.math.lerp(a.x, b.x, t), std.math.lerp(a.y, b.y, t), std.math.lerp(a.z, b.z, t), std.math.lerp(a.w, b.w, t), ); } pub fn clamp(self: Vec4, min_value: f32, max_value: f32) Vec4 { return Vec4.init( std.math.clamp(self.x, min_value, max_value), std.math.clamp(self.y, min_value, max_value), std.math.clamp(self.z, min_value, max_value), std.math.clamp(self.w, min_value, max_value), ); } pub fn toVec3XYZ(self: Vec4) Vec3 { return Vec3.init(self.x, self.y, self.z); } }; pub const Mat4 = extern struct { columns: [4][4]f32, pub fn initZero() Mat4 { var self: Mat4 = undefined; @memset(self.asArray(), 0); return self; } pub fn initIdentity() Mat4 { return Mat4.initDiagonal(1); } pub fn initDiagonal(value: f32) Mat4 { var self = Mat4.initZero(); self.columns[0][0] = value; self.columns[1][1] = value; self.columns[2][2] = value; self.columns[3][3] = value; return self; } pub fn multiply(left: Mat4, right: Mat4) Mat4 { var self: Mat4 = undefined; inline for (.{ 0, 1, 2, 3 }) |i| { var column = Vec4.initArray(&right.columns[i]).multiplyMat4(left); @memcpy(&self.columns[i], column.asArray()); } return self; } pub fn initScale(scale: Vec3) Mat4 { var self = Mat4.initIdentity(); self.columns[0][0] = scale.x; self.columns[1][1] = scale.y; self.columns[2][2] = scale.z; return self; } pub fn initTranslate(offset: Vec3) Mat4 { var self = Mat4.initIdentity(); self.columns[3][0] = offset.x; self.columns[3][1] = offset.y; self.columns[3][2] = offset.z; return self; } pub fn asArray(self: *Mat4) []f32 { const ptr: [*]f32 = @alignCast(@ptrCast(@as(*anyopaque, @ptrCast(&self.columns)))); return ptr[0..16]; } }; pub const Rect = struct { pos: Vec2, size: Vec2, pub const zero = Rect{ .pos = Vec2.zero, .size = Vec2.zero }; pub fn init(x: f32, y: f32, width: f32, height: f32) Rect { return Rect{ .pos = Vec2.init(x, y), .size = Vec2.init(width, height) }; } pub fn clip(self: Rect, other: Rect) Rect { const left_edge = @max(self.left(), other.left()); const right_edge = @min(self.right(), other.right()); const top_edge = @max(self.top(), other.top()); const bottom_edge = @min(self.bottom(), other.bottom()); return Rect.init( left_edge, top_edge, right_edge - left_edge, bottom_edge - top_edge ); } pub fn left(self: Rect) f32 { return self.pos.x; } pub fn right(self: Rect) f32 { return self.pos.x + self.size.x; } pub fn top(self: Rect) f32 { return self.pos.y; } pub fn bottom(self: Rect) f32 { return self.pos.y + self.size.y; } pub fn multiply(self: Rect, xy: Vec2) Rect { return Rect{ .pos = self.pos.multiply(xy), .size = self.size.multiply(xy), }; } pub fn divide(self: Rect, xy: Vec2) Rect { return Rect{ .pos = self.pos.divide(xy), .size = self.size.divide(xy), }; } pub fn isInside(self: Rect, pos: Vec2) bool { const x_overlap = self.pos.x <= pos.x and pos.x < self.pos.x + self.size.x; const y_overlap = self.pos.y <= pos.y and pos.y < self.pos.y + self.size.y; return x_overlap and y_overlap; } }; pub const Line = struct { p0: Vec2, p1: Vec2 }; pub fn isInsideRect(rect_pos: Vec2, rect_size: Vec2, pos: Vec2) bool { const rect = Rect{ .pos = rect_pos, .size = rect_size }; return rect.isInside(pos); } pub fn rgba(r: u8, g: u8, b: u8, a: f32) Vec4 { assert(0 <= a and a <= 1); return Vec4.init( @as(f32, @floatFromInt(r)) / 255, @as(f32, @floatFromInt(g)) / 255, @as(f32, @floatFromInt(b)) / 255, a, ); } pub fn rgb(r: u8, g: u8, b: u8) Vec4 { return rgba(r, g, b, 1); } pub fn rgb_hex(text: []const u8) ?Vec4 { if (text.len != 7) { return null; } if (text[0] != '#') { return null; } const r = std.fmt.parseInt(u8, text[1..3], 16) catch return null; const g = std.fmt.parseInt(u8, text[3..5], 16) catch return null; const b = std.fmt.parseInt(u8, text[5..7], 16) catch return null; return rgb(r, g, b); }