440 lines
11 KiB
Zig
440 lines
11 KiB
Zig
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);
|
|
}
|