HelloWorld

This commit is contained in:
abux 2026-07-30 19:48:03 +01:00
commit 10645c98d1
8 changed files with 1023 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.zig-cache/
zig-pkg/

25
README.md Normal file
View file

@ -0,0 +1,25 @@
## NYXMath
Low cortisol comptime generic math library for game dev / game engines. Yes.
Coord System: Vulkan
# Includes
```zig
// --- GENERICS ---
pub const Vec = @import("vec.zig").Vec;
pub const Mat = @import("mat.zig").Mat;
pub const Color = @import("color.zig").Color;
// --- VEC2 ---
pub const Vec2 = Vec(i32, 2);
pub const Vec2f = Vec(f32, 2);
// --- VEC3 ---
pub const Vec3 = Vec(i32, 3);
pub const Vec3f = Vec(f32, 3);
// --- MAT ---
pub const Mat2x2 = Mat(f32, 2, 2);
pub const Mat3x3 = Mat(f32, 3, 3);
pub const Mat4x4 = Mat(f32, 4, 4);
```

24
build.zig Normal file
View file

@ -0,0 +1,24 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
// --- TARGET ---
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// --- MOD ---
const mod = b.addModule("NYXMath", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const mod_tests = b.addTest(.{
.root_module = mod,
});
const run_mod_tests = b.addRunArtifact(mod_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
}

17
build.zig.zon Normal file
View file

@ -0,0 +1,17 @@
.{
.name = .NYXMath,
.version = "0.0.0",
.fingerprint = 0x82eb78004f923e04,
.minimum_zig_version = "0.16.0",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

279
src/color.zig Normal file
View file

@ -0,0 +1,279 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std");
const Vec = @import("vec.zig").Vec;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Color = extern struct {
const Self = @This();
//
// FIELDS
//
r: f32 = 0,
g: f32 = 0,
b: f32 = 0,
a: f32 = 1,
//
// CONSTRUCTORS
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn rgb(r: f32, g: f32, b: f32) Self {
return .{ .r = r, .g = g, .b = b, .a = 1 };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn rgba(r: f32, g: f32, b: f32, a: f32) Self {
return .{ .r = r, .g = g, .b = b, .a = a };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn hex(comptime value: u32) Self {
const r = @as(f32, @as(u8, @truncate(value >> 16))) / 255.0;
const g = @as(f32, @as(u8, @truncate(value >> 8))) / 255.0;
const b = @as(f32, @as(u8, @truncate(value >> 0))) / 255.0;
const a = if (value > 0xFFFFFF)
@as(f32, @as(u8, @truncate(value >> 24))) / 255.0
else
1.0;
return .{ .r = r, .g = g, .b = b, .a = a };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn fromVec4(v: Vec(f32, 4)) Self {
return .{ .r = v.value[0], .g = v.value[1], .b = v.value[2], .a = v.value[3] };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn toVec4(self: Self) Vec(f32, 4) {
return Vec(f32, 4).new(.{ self.r, self.g, self.b, self.a });
}
//
// ARITHMETIC
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn add(self: Self, other: Self) Self {
return .{
.r = self.r + other.r,
.g = self.g + other.g,
.b = self.b + other.b,
.a = self.a + other.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn sub(self: Self, other: Self) Self {
return .{
.r = self.r - other.r,
.g = self.g - other.g,
.b = self.b - other.b,
.a = self.a - other.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn mul(self: Self, other: Self) Self {
return .{
.r = self.r * other.r,
.g = self.g * other.g,
.b = self.b * other.b,
.a = self.a * other.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn div(self: Self, other: Self) Self {
return .{
.r = self.r / other.r,
.g = self.g / other.g,
.b = self.b / other.b,
.a = self.a / other.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn scale(self: Self, scalar: f32) Self {
return .{
.r = self.r * scalar,
.g = self.g * scalar,
.b = self.b * scalar,
.a = self.a * scalar,
};
}
//
// COLOR OPERATIONS
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn lerp(self: Self, other: Self, t: f32) Self {
return .{
.r = self.r + (other.r - self.r) * t,
.g = self.g + (other.g - self.g) * t,
.b = self.b + (other.b - self.b) * t,
.a = self.a + (other.a - self.a) * t,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn blend(self: Self, other: Self) Self {
const out_a = other.a + self.a * (1.0 - other.a);
if (out_a == 0) return .{ .r = 0, .g = 0, .b = 0, .a = 0 };
return .{
.r = (other.r * other.a + self.r * self.a * (1.0 - other.a)) / out_a,
.g = (other.g * other.a + self.g * self.a * (1.0 - other.a)) / out_a,
.b = (other.b * other.a + self.b * self.a * (1.0 - other.a)) / out_a,
.a = out_a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn premultiply(self: Self) Self {
return .{
.r = self.r * self.a,
.g = self.g * self.a,
.b = self.b * self.a,
.a = self.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn luminance(self: Self) f32 {
return self.r * 0.2126 + self.g * 0.7152 + self.b * 0.0722;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn toLinear(self: Self) Self {
const srgb_to_linear = struct {
inline fn map(c: f32) f32 {
if (c <= 0.04045) return c / 12.92;
return std.math.pow(f32, (c + 0.055) / 1.055, 2.4);
}
};
return .{
.r = srgb_to_linear.map(self.r),
.g = srgb_to_linear.map(self.g),
.b = srgb_to_linear.map(self.b),
.a = self.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn toSRGB(self: Self) Self {
const linear_to_srgb = struct {
inline fn map(c: f32) f32 {
if (c <= 0.0031308) return c * 12.92;
return 1.055 * std.math.pow(f32, c, 1.0 / 2.4) - 0.055;
}
};
return .{
.r = linear_to_srgb.map(self.r),
.g = linear_to_srgb.map(self.g),
.b = linear_to_srgb.map(self.b),
.a = self.a,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn toHSV(self: Self) struct { h: f32, s: f32, v: f32 } {
const max = @max(self.r, @max(self.g, self.b));
const min = @min(self.r, @min(self.g, self.b));
const delta = max - min;
const v = max;
const s = if (max == 0) 0 else delta / max;
var h: f32 = 0;
if (delta != 0) {
if (max == self.r) {
h = 60.0 * @mod((self.g - self.b) / delta, 6.0);
} else if (max == self.g) {
h = 60.0 * ((self.b - self.r) / delta + 2.0);
} else {
h = 60.0 * ((self.r - self.g) / delta + 4.0);
}
}
return .{ .h = h, .s = s, .v = v };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn fromHSV(h: f32, s: f32, v: f32) Self {
const c = v * s;
const hp = h / 60.0;
const x = c * (1.0 - @abs(@mod(hp, 2.0) - 1.0));
const m = v - c;
var r: f32 = 0;
var g: f32 = 0;
var b: f32 = 0;
if (hp < 1) {
r = c;
g = x;
} else if (hp < 2) {
r = x;
g = c;
} else if (hp < 3) {
g = c;
b = x;
} else if (hp < 4) {
g = x;
b = c;
} else if (hp < 5) {
r = x;
b = c;
} else {
r = c;
b = x;
}
return .{ .r = r + m, .g = g + m, .b = b + m, .a = 1 };
}
//
// CONSTANTS
//
pub const white = Self{ .r = 1, .g = 1, .b = 1, .a = 1 };
pub const black = Self{ .r = 0, .g = 0, .b = 0, .a = 1 };
pub const transparent = Self{ .r = 0, .g = 0, .b = 0, .a = 0 };
pub const red = Self{ .r = 1, .g = 0, .b = 0, .a = 1 };
pub const green = Self{ .r = 0, .g = 1, .b = 0, .a = 1 };
pub const blue = Self{ .r = 0, .g = 0, .b = 1, .a = 1 };
pub const yellow = Self{ .r = 1, .g = 1, .b = 0, .a = 1 };
pub const cyan = Self{ .r = 0, .g = 1, .b = 1, .a = 1 };
pub const magenta = Self{ .r = 1, .g = 0, .b = 1, .a = 1 };
pub const orange = Self{ .r = 1, .g = 0.55, .b = 0, .a = 1 };
pub const purple = Self{ .r = 0.5, .g = 0, .b = 0.5, .a = 1 };
pub const pink = Self{ .r = 1, .g = 0.75, .b = 0.8, .a = 1 };
pub const brown = Self{ .r = 0.6, .g = 0.3, .b = 0.1, .a = 1 };
pub const gray = Self{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 };
};

370
src/mat.zig Normal file
View file

@ -0,0 +1,370 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std");
const Vec = @import("vec.zig").Vec;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn Mat(
comptime T: type,
comptime Row: usize,
comptime Col: usize,
) type {
return struct {
const Self = @This();
//
// FIELDS
//
value: [Row][Col]T,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn zero() Self {
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = @as(T, 0);
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn identity() Self {
comptime if (Row != Col) @compileError("identity only defined for square matrices");
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = if (r == c) @as(T, 1) else @as(T, 0);
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn fromRows(rows: [Row][Col]T) Self {
return .{ .value = rows };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn fromCols(cols: [Col][Row]T) Self {
var result: Self = undefined;
for (0..Col) |c| {
for (0..Row) |r| {
result.value[r][c] = cols[c][r];
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn row(self: Self, index: usize) Vec(T, Col) {
return Vec(T, Col).new(self.value[index]);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn col(self: Self, index: usize) Vec(T, Row) {
var data: [Row]T = undefined;
for (0..Row) |r| {
data[r] = self.value[r][index];
}
return Vec(T, Row).new(data);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn add(self: Self, other: Self) Self {
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = self.value[r][c] + other.value[r][c];
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn sub(self: Self, other: Self) Self {
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = self.value[r][c] - other.value[r][c];
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn mul(self: Self, other: Self) Self {
comptime if (Row != Col) @compileError("direct matrix multiplication requires square matrices; use mulMat for non-square");
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
var sum: T = @as(T, 0);
for (0..Col) |k| {
sum += self.value[r][k] * other.value[k][c];
}
result.value[r][c] = sum;
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn mulVec(self: Self, v: Vec(T, Col)) Vec(T, Row) {
var result: [Row]T = undefined;
for (0..Row) |r| {
var sum: T = @as(T, 0);
for (0..Col) |c| {
sum += self.value[r][c] * v.value[c];
}
result[r] = sum;
}
return Vec(T, Row).new(result);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn mulScalar(self: Self, scalar: T) Self {
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = self.value[r][c] * scalar;
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn divScalar(self: Self, scalar: T) Self {
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = self.value[r][c] / scalar;
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn transpose(self: Self) Mat(T, Col, Row) {
var result: Mat(T, Col, Row) = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[c][r] = self.value[r][c];
}
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn determinant(self: Self) T {
comptime if (Row != Col or Row < 1) @compileError("determinant only defined for non-empty square matrices");
if (Row == 1) return self.value[0][0];
var det: T = @as(T, 0);
for (0..Col) |c| {
det += self.value[0][c] * self.cofactor(0, c);
}
return det;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn inverse(self: Self) Self {
comptime if (Row != Col) @compileError("inverse only defined for square matrices");
const det = self.determinant();
var result: Self = undefined;
for (0..Row) |r| {
for (0..Col) |c| {
result.value[r][c] = self.cofactor(c, r) / det;
}
}
return result;
}
//
// PRIVATE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
inline fn minor(self: Self, r_idx: usize, c_idx: usize) Mat(T, Row - 1, Col - 1) {
comptime if (Row < 2 or Col < 2) @compileError("minor requires at least 2x2");
var result: Mat(T, Row - 1, Col - 1) = undefined;
var ri: usize = 0;
for (0..Row) |r| {
if (r == r_idx) continue;
var ci: usize = 0;
for (0..Col) |c| {
if (c == c_idx) continue;
result.value[ri][ci] = self.value[r][c];
ci += 1;
}
ri += 1;
}
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
inline fn cofactor(self: Self, r_idx: usize, c_idx: usize) T {
const sign: T = if ((r_idx + c_idx) % 2 == 0) @as(T, 1) else -@as(T, 1);
return sign * self.minor(r_idx, c_idx).determinant();
}
//
// 4X4 TRANSFORMS
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn translate(x: T, y: T, z: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("translate only defined for Mat4x4");
var result = identity();
result.value[0][3] = x;
result.value[1][3] = y;
result.value[2][3] = z;
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn rotateX(angle: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("rotateX only defined for Mat4x4");
const c = @cos(angle);
const s = @sin(angle);
var result = identity();
result.value[1][1] = c;
result.value[1][2] = -s;
result.value[2][1] = s;
result.value[2][2] = c;
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn rotateY(angle: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("rotateY only defined for Mat4x4");
const c = @cos(angle);
const s = @sin(angle);
var result = identity();
result.value[0][0] = c;
result.value[0][2] = s;
result.value[2][0] = -s;
result.value[2][2] = c;
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn rotateZ(angle: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("rotateZ only defined for Mat4x4");
const c = @cos(angle);
const s = @sin(angle);
var result = identity();
result.value[0][0] = c;
result.value[0][1] = -s;
result.value[1][0] = s;
result.value[1][1] = c;
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn scale(x: T, y: T, z: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("scale only defined for Mat4x4");
var result = identity();
result.value[0][0] = x;
result.value[1][1] = y;
result.value[2][2] = z;
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn lookAt(eye: Vec(T, 3), target: Vec(T, 3), up: Vec(T, 3)) Self {
comptime if (Row != 4 or Col != 4) @compileError("lookAt only defined for Mat4x4");
const f = target.sub(eye).normalize();
const s = f.cross(up).normalize();
const u = s.cross(f);
var result = identity();
result.value[0][0] = s.value[0];
result.value[0][1] = s.value[1];
result.value[0][2] = s.value[2];
result.value[1][0] = u.value[0];
result.value[1][1] = u.value[1];
result.value[1][2] = u.value[2];
result.value[2][0] = -f.value[0];
result.value[2][1] = -f.value[1];
result.value[2][2] = -f.value[2];
result.value[0][3] = -s.dot(eye);
result.value[1][3] = -u.dot(eye);
result.value[2][3] = f.dot(eye);
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn perspective(fov: T, aspect: T, near: T, far: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("perspective only defined for Mat4x4");
const f = @as(T, 1) / @tan(fov / @as(T, 2));
const range_inv = @as(T, 1) / (near - far);
var result = zero();
result.value[0][0] = f / aspect;
result.value[1][1] = -f;
result.value[2][2] = far * range_inv;
result.value[2][3] = near * far * range_inv;
result.value[3][2] = -@as(T, 1);
return result;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) Self {
comptime if (Row != 4 or Col != 4) @compileError("orthographic only defined for Mat4x4");
const rml = @as(T, 1) / (right - left);
const tmb = @as(T, 1) / (top - bottom);
const fmn = @as(T, 1) / (far - near);
var result = identity();
result.value[0][0] = @as(T, 2) * rml;
result.value[1][1] = -@as(T, 2) * tmb;
result.value[2][2] = -fmn;
result.value[0][3] = -(right + left) * rml;
result.value[1][3] = (top + bottom) * tmb;
result.value[2][3] = -near * fmn;
return result;
}
};
}

169
src/root.zig Normal file
View file

@ -0,0 +1,169 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
//
// PRIVATE
//
const std = @import("std");
//
// PUBLIC
//
// --- GENERICS ---
pub const Vec = @import("vec.zig").Vec;
pub const Mat = @import("mat.zig").Mat;
pub const Color = @import("color.zig").Color;
// --- VEC2 ---
pub const Vec2 = Vec(i32, 2);
pub const Vec2f = Vec(f32, 2);
// --- VEC3 ---
pub const Vec3 = Vec(i32, 3);
pub const Vec3f = Vec(f32, 3);
// --- MAT ---
pub const Mat2x2 = Mat(f32, 2, 2);
pub const Mat3x3 = Mat(f32, 3, 3);
pub const Mat4x4 = Mat(f32, 4, 4);
//
// TESTS
//
test "init" {
std.debug.print("\x1b[34m--- init ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const vec2: Vec2f = .as(12);
const vec3: Vec3f = .new(.{ 1, 2, 3 });
std.debug.print("Vec2: {}\n", .{vec2.value});
std.debug.print("Vec3: {}\n", .{vec3.value});
}
test "math" {
std.debug.print("\x1b[35m--- math ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const a: Vec3f = .new(.{ 1, 2, 3 });
const b: Vec3f = .new(.{ 2, 3, 4 });
std.debug.print("a = {}\n", .{a.value});
std.debug.print("b = {}\n\n", .{b.value});
std.debug.print("a + b = {}\n", .{a.add(b).value});
std.debug.print("a - b = {}\n", .{a.sub(b).value});
std.debug.print("a * b = {}\n", .{a.mul(b).value});
std.debug.print("a / b = {:.1}\n", .{a.div(b).value});
}
test "mat init" {
std.debug.print("\x1b[34m--- mat init ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const identity: Mat4x4 = .identity();
std.debug.print("identity[0][0] = {d}\n", .{identity.value[0][0]});
std.debug.print("identity[3][3] = {d}\n", .{identity.value[3][3]});
}
test "mat mul" {
std.debug.print("\x1b[35m--- mat mul ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const t: Mat4x4 = .translate(1, 2, 3);
const rz: Mat4x4 = .rotateZ(@as(f32, std.math.pi) / 4.0);
const m = t.mul(rz);
std.debug.print("translate * rotateZ:\n", .{});
for (m.value, 0..) |row, i| {
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
}
}
test "mat det" {
std.debug.print("\x1b[34m--- mat det ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const m: Mat3x3 = .fromRows(.{
.{ 1, 2, 3 },
.{ 4, 5, 6 },
.{ 7, 8, 10 },
});
std.debug.print("det = {d:.2}\n", .{m.determinant()});
}
test "mat inverse" {
std.debug.print("\x1b[35m--- mat inverse ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const m: Mat4x4 = .translate(5, -3, 2);
const inv = m.inverse();
const back = m.mul(inv);
std.debug.print("M * inv(M) should be identity:\n", .{});
for (back.value, 0..) |row, i| {
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
}
}
test "mat perspective" {
std.debug.print("\x1b[34m--- mat perspective (vulkan) ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const p: Mat4x4 = .perspective(@as(f32, std.math.pi) / 4.0, 16.0 / 9.0, 0.1, 100.0);
std.debug.print("perspective:\n", .{});
for (p.value, 0..) |row, i| {
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
}
}
test "mat lookAt" {
std.debug.print("\x1b[35m--- mat lookAt ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const eye: Vec3f = .new(.{ 0, 0, 5 });
const target: Vec3f = .new(.{ 0, 0, 0 });
const up: Vec3f = .new(.{ 0, 1, 0 });
const view: Mat4x4 = .lookAt(eye, target, up);
std.debug.print("lookAt:\n", .{});
for (view.value, 0..) |row, i| {
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
}
}
test "color init" {
std.debug.print("\x1b[34m--- color init ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const c: Color = .rgb(1, 0.5, 0);
std.debug.print("orange: r={d:.1} g={d:.1} b={d:.1} a={d:.1}\n", .{ c.r, c.g, c.b, c.a });
}
test "color hex" {
std.debug.print("\x1b[35m--- color hex ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const c: Color = .hex(0xFF8800);
std.debug.print("0xFF8800: r={d:.4} g={d:.4} b={d:.4} a={d:.1}\n", .{ c.r, c.g, c.b, c.a });
}
test "color blend" {
std.debug.print("\x1b[34m--- color blend ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const over = Color.red.blend(Color.green);
std.debug.print("red over green: r={d:.4} g={d:.4} b={d:.4} a={d:.1}\n", .{ over.r, over.g, over.b, over.a });
}
test "color hsv" {
std.debug.print("\x1b[35m--- color hsv ---\n", .{});
defer std.debug.print("\x1b[0m\n", .{});
const c: Color = .fromHSV(120, 1, 1);
const hsv = c.toHSV();
std.debug.print("fromHSV(120,1,1): r={d:.4} g={d:.4} b={d:.4} -> h={d:.1} s={d:.1} v={d:.1}\n", .{ c.r, c.g, c.b, hsv.h, hsv.s, hsv.v });
}

137
src/vec.zig Normal file
View file

@ -0,0 +1,137 @@
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std");
/// ----------------------------------------------------
/// Generic Vector
/// ----------------------------------------------------
pub fn Vec(
comptime T: type,
comptime Len: usize,
) type {
return struct {
const Self = @This();
//
// FIELDS
//
value: @Vector(Len, T),
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn as(value: T) Self {
return .{
.value = blk: {
var data: [Len]T = undefined;
for (0..data.len) |i| {
data[i] = value;
}
break :blk data;
},
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn new(value: @Vector(Len, T)) Self {
return .{ .value = value };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn add(
self: Self,
other: Self,
) Self {
return .{
.value = self.value + other.value,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn sub(
self: Self,
other: Self,
) Self {
return .{
.value = self.value - other.value,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn mul(
self: Self,
other: Self,
) Self {
return .{
.value = self.value * other.value,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn div(
self: Self,
other: Self,
) Self {
return .{
.value = self.value / other.value,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn scale(self: Self, scalar: T) Self {
return .{
.value = self.value * @as(@Vector(Len, T), @splat(scalar)),
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn negate(self: Self) Self {
return .{ .value = -self.value };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn dot(self: Self, other: Self) T {
return @reduce(.Add, self.value * other.value);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn lengthSquared(self: Self) T {
return self.dot(self);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn length(self: Self) T {
return @sqrt(self.lengthSquared());
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn normalize(self: Self) Self {
return self.scale(@as(T, 1) / self.length());
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub inline fn cross(self: Self, other: Self) Self {
comptime if (Len != 3) @compileError("cross product only defined for Vec3");
return .{
.value = .{
self.value[1] * other.value[2] - self.value[2] * other.value[1],
self.value[2] * other.value[0] - self.value[0] * other.value[2],
self.value[0] * other.value[1] - self.value[1] * other.value[0],
},
};
}
};
}