Zig utility library
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove linear algebra modules

That stuff should be handled by someone who actually knows what they're doing.

IamPyu 7b79ffc2 ec4e6a62

+11 -515
+1 -1
README.md
··· 1 1 # mitochondria 2 2 3 - Zig utility library providing data structures, linear algebra, and helper functions. 3 + Zig utility library providing data structures, and helper functions. 4 4 5 5 Until Zig has reached a stable 1.0.0 release, this library will be rolling release.
-436
src/la.zig
··· 1 - const std = @import("std"); 2 - const math = std.math; 3 - const assert = std.debug.assert; 4 - 5 - /// Create a `columns`x`rows` matrix of `T` 6 - pub fn Matrix(comptime T: type, rows: usize, columns: usize) type { 7 - assert(rows > 0); 8 - assert(columns > 0); 9 - 10 - { 11 - const t = @typeInfo(T); 12 - 13 - switch (t) { 14 - .int, .float => {}, 15 - else => @compileError("`T` should be float or int"), 16 - } 17 - } 18 - 19 - return struct { 20 - const Self = @This(); 21 - 22 - /// A row of the matrix 23 - pub const Row = @Vector(rows, T); 24 - 25 - /// An array representation of `Row` 26 - pub const RawRow = [rows]T; 27 - 28 - /// The columns of rows of the matrix 29 - pub const InnerMat = [columns]Row; 30 - 31 - /// A 2D-array representation of the matrix 32 - pub const ArrayRep = [columns][rows]T; 33 - 34 - inner: InnerMat, 35 - 36 - /// Initialize a matrix 37 - pub fn init(table: InnerMat) Self { 38 - return Self{ .inner = table }; 39 - } 40 - 41 - /// Initialize from a 2D-array representation of the matrix 42 - pub fn initRaw(table: ArrayRep) Self { 43 - return Self{ .inner = table }; 44 - } 45 - 46 - /// Return a matrix filled with zeroes 47 - pub fn zeroed() Self { 48 - var cols: InnerMat = undefined; 49 - @memset(&cols, @splat(0)); 50 - return Self.init(cols); 51 - } 52 - 53 - /// Copy the matrix, creating an identical clone 54 - pub fn copy(self: *const Self) Self { 55 - return Self{ .inner = self.inner }; 56 - } 57 - 58 - /// Return whether this matrix is also a plain vector (1 column). 59 - pub fn isVector() bool { 60 - return columns == 1; 61 - } 62 - 63 - /// Add all elements of the matrix to another matrix 64 - pub fn add(self: *const Self, other: *const Self) Self { 65 - var out = self.copy(); 66 - 67 - for (out.inner, 0..) |row, i| { 68 - out.inner[i] = row + other.inner[i]; 69 - } 70 - 71 - return out; 72 - } 73 - 74 - /// Subtract all elements of the matrix from another matrix 75 - pub fn sub(self: *const Self, other: *const Self) Self { 76 - var out = self.copy(); 77 - 78 - for (out.inner, 0..) |row, i| { 79 - out.inner[i] = row - other.inner[i]; 80 - } 81 - 82 - return out; 83 - } 84 - 85 - /// Multiply all elements of the matrix by a scalar 86 - pub fn mul(self: *const Self, scalar: T) Self { 87 - var out = self.copy(); 88 - 89 - for (out.inner, 0..) |row, i| { 90 - out.inner[i] = row * @as(Row, @splat(scalar)); 91 - } 92 - 93 - return out; 94 - } 95 - 96 - /// Multiply a matrix with another matrix 97 - /// Each element in the matrix is multiplied by the 98 - /// element at the same index of the other matrix 99 - pub fn mul2(self: *const Self, other: *const Self) Self { 100 - var out = self.copy(); 101 - 102 - for (out.inner, other.inner, 0..) |r1, r2, i| { 103 - out.inner[i] = r1 * r2; 104 - } 105 - 106 - return out; 107 - } 108 - 109 - /// Divide all elements of the matrix by a scalar 110 - pub fn div(self: *const Self, scalar: T) Self { 111 - var out = self.copy(); 112 - 113 - for (out.inner, 0..) |row, i| { 114 - out.inner[i] = row / @as(Row, @splat(scalar)); 115 - } 116 - 117 - return out; 118 - } 119 - 120 - /// Divide a matrix by another matrix 121 - /// Each element in the matrix is divided by the 122 - /// element at the same index of the other matrix 123 - pub fn div2(self: *const Self, other: *const Self) Self { 124 - var out = self.copy(); 125 - 126 - for (out.inner, other.inner, 0..) |r1, r2, i| { 127 - out.inner[i] = r1 / r2; 128 - } 129 - 130 - return out; 131 - } 132 - 133 - /// Get the magnitude of the vector 134 - pub fn magnitude(self: *const Self) T { 135 - var mag: T = 0; 136 - 137 - for (self.inner) |r| { 138 - for (@as(RawRow, r)) |s| { 139 - mag += math.pow(T, s, 2); 140 - } 141 - } 142 - 143 - return math.sqrt(mag); 144 - } 145 - 146 - /// Get the distance between 2 vectors 147 - pub fn distance(self: *const Self, other: *const Self) T { 148 - return other.sub(self).magnitude(); 149 - } 150 - 151 - /// Normalize the vector 152 - pub fn normalize(self: *const Self) Self { 153 - var normalized = self.copy(); 154 - var unorm: T = 0; 155 - 156 - for (self.inner) |r| { 157 - for (@as(RawRow, r)) |s| { 158 - unorm += math.pow(T, s, 2); 159 - } 160 - } 161 - 162 - for (normalized.inner, 0..) |r, i| { 163 - for (@as(RawRow, r), 0..) |s, j| { 164 - normalized.inner[i][j] = s / math.sqrt(unorm); 165 - } 166 - } 167 - 168 - return normalized; 169 - } 170 - 171 - /// Return the dot product of 2 vectors, returns `null` if not a vector. 172 - pub fn dot(self: *const Self, other: *const Self) ?T { 173 - if (!Self.isVector()) { 174 - return null; 175 - } 176 - 177 - var product: T = 0; 178 - 179 - for (self.inner, other.inner) |r1, r2| { 180 - for (@as(RawRow, r1), @as(RawRow, r2)) |s1, s2| { 181 - product += s1 * s2; 182 - } 183 - } 184 - 185 - return product; 186 - } 187 - 188 - // TODO: add cross product 189 - 190 - /// Flatten all the elements of the matrix into a single array 191 - pub fn flatten(self: *const Self) [rows * columns]T { 192 - var output: [rows * columns]T = undefined; 193 - 194 - var k: usize = 0; 195 - for (self.inner) |r| { 196 - for (@as([rows]T, r)) |s| { 197 - output[k] = s; 198 - k += 1; 199 - } 200 - } 201 - 202 - return output; 203 - } 204 - 205 - /// Call `func` on all the elements of the matrix 206 - pub fn map(self: *const Self, func: *const fn (T) T) Self { 207 - var out = self.copy(); 208 - 209 - for (0..out.inner.len) |i| { 210 - for (0..out.inner[i].len) |j| { 211 - out.inner[i][j] = func(out.inner[i][j]); 212 - } 213 - } 214 - 215 - return out; 216 - } 217 - 218 - /// Return a X unit matrix 219 - pub fn unitX() Self { 220 - var out = Self.zeroed(); 221 - 222 - for (&out.inner) |*row| { 223 - row[0] = 1; 224 - } 225 - 226 - return out; 227 - } 228 - 229 - /// Return a Y unit matrix 230 - pub fn unitY() Self { 231 - assert(rows >= 2); 232 - 233 - var out = Self.zeroed(); 234 - 235 - for (&out.inner) |*row| { 236 - row[1] = 1; 237 - } 238 - 239 - return out; 240 - } 241 - 242 - /// Return a Z unit matrix 243 - pub fn unitZ() Self { 244 - assert(rows >= 3); 245 - 246 - var out = Self.zeroed(); 247 - 248 - for (&out.inner) |*row| { 249 - row[2] = 1; 250 - } 251 - 252 - return out; 253 - } 254 - 255 - /// Return a W unit matrix 256 - pub fn unitW() Self { 257 - assert(rows >= 4); 258 - 259 - var out = Self.zeroed(); 260 - 261 - for (&out.inner) |*row| { 262 - row[3] = 1; 263 - } 264 - 265 - return out; 266 - } 267 - }; 268 - } 269 - 270 - /// Create a vector of `T` scalars with `components` components 271 - pub fn Vector(comptime T: type, components: usize) type { 272 - return Matrix(T, components, 1); 273 - } 274 - 275 - /// 2-dimensional vector of `f32` 276 - pub const Vec2F = Vector(f32, 2); 277 - /// 2-dimensional vector of `f64` 278 - pub const Vec2D = Vector(f64, 2); 279 - 280 - /// 3-dimensional vector of `f32` 281 - pub const Vec3F = Vector(f32, 3); 282 - /// 3-dimensional vector of `f64` 283 - pub const Vec3D = Vector(f64, 3); 284 - 285 - /// 4-dimensional vector of `f32` 286 - pub const Vec4F = Vector(f32, 4); 287 - /// 4-dimensional vector of `f64` 288 - pub const Vec4D = Vector(f64, 4); 289 - 290 - /// 2x2 matrix of `f32` 291 - pub const Mat2F = Matrix(f32, 2, 2); 292 - /// 2x2 matrix of `f64` 293 - pub const Mat2D = Matrix(f64, 2, 2); 294 - 295 - /// 3x3 matrix of `f32` 296 - pub const Mat3F = Matrix(f32, 3, 3); 297 - /// 3x3 matrix of `f64` 298 - pub const Mat3D = Matrix(f64, 3, 3); 299 - 300 - /// 4x4 matrix of `f32` 301 - pub const Mat4F = Matrix(f32, 4, 4); 302 - /// 4x4 matrix of `f64` 303 - pub const Mat4D = Matrix(f64, 4, 4); 304 - 305 - // TODO: idk how the fuck i will, but add quaternions eventually. 306 - 307 - pub fn vec(comptime T: type, comptime components: usize, v: @Vector(components, T)) Vector(T, components) { 308 - return Vector(T, components).init(.{v}); 309 - } 310 - 311 - pub fn mat( 312 - comptime T: type, 313 - comptime rows: usize, 314 - comptime columns: usize, 315 - rep: [columns]@Vector(rows, T), 316 - ) Matrix(T, rows, columns) { 317 - return Matrix(T, rows, columns).init(rep); 318 - } 319 - 320 - test "mul" { 321 - const x = Matrix(f32, 3, 5).init(.{ 322 - .{ 1, 2, 3 }, 323 - .{ 2, 4, 6 }, 324 - .{ 3, 6, 9 }, 325 - .{ 4, 8, 12 }, 326 - .{ 5, 10, 15 }, 327 - }); 328 - 329 - const new_mat = x.mul(3); 330 - 331 - try std.testing.expectEqual(.{ 3, 6, 9 }, new_mat.inner[0]); 332 - } 333 - 334 - test "flatten" { 335 - const x = Matrix(f32, 3, 3).init(.{ 336 - .{ 1, 2, 3 }, 337 - .{ 4, 5, 6 }, 338 - .{ 7, 8, 9 }, 339 - }); 340 - 341 - const array = x.flatten(); 342 - 343 - try std.testing.expectEqual(.{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, array); 344 - } 345 - 346 - test "magnitude" { 347 - const magnitude = Vec3F.init(.{.{ 394, 329, 684 }}).magnitude(); 348 - try std.testing.expectEqual(855.1801, magnitude); 349 - } 350 - 351 - test "normalized" { 352 - const normalized = Vec2F.init(.{.{ 6, 9 }}).normalize(); 353 - try std.testing.expectEqual(1, normalized.magnitude()); 354 - } 355 - 356 - test "dot" { 357 - const x = Vec2F.init(.{.{ 3, 4 }}); 358 - const y = Vec2F.init(.{.{ 6, 9 }}); 359 - 360 - try std.testing.expectEqual(54, x.dot(&y)); 361 - } 362 - 363 - test "dist" { 364 - const x = Vec2F.init(.{.{ 4, 1 }}); 365 - const y = Vec2F.init(.{.{ 1, 4 }}); 366 - try std.testing.expectEqual(4.2426405, x.distance(&y)); 367 - } 368 - 369 - test "large" { 370 - const m = Matrix(f32, 10, 5).init(.{ 371 - .{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 372 - .{ 14, 13, 43, 5, 6, 8, 1, 9, 3, 4 }, 373 - .{ 4, 6, 3, 8, 9, 4, 3, 6, 1, 7 }, 374 - .{ 5, 4, 6, 6, 8, 3, 5, 7, 13, 17 }, 375 - .{ 54, 48, 43, 69, 74, 97, 59, 32, 12, 121 }, 376 - }); 377 - 378 - for (0..1000000) |_| { 379 - _ = m.mul(5); 380 - } 381 - } 382 - 383 - test "unit" { 384 - const v = Vec3F.zeroed(); 385 - try std.testing.expectEqual(.{.{ 0, 0, 0 }}, v.inner); 386 - 387 - const m = Mat3F.zeroed(); 388 - try std.testing.expectEqual(.{ 389 - .{ 0, 0, 0 }, 390 - .{ 0, 0, 0 }, 391 - .{ 0, 0, 0 }, 392 - }, m.inner); 393 - 394 - const vy = Vec3F.unitY(); 395 - try std.testing.expectEqual(.{.{ 0, 1, 0 }}, vy.inner); 396 - 397 - const vw = Vec4F.unitW(); 398 - try std.testing.expectEqual(.{.{ 0, 0, 0, 1 }}, vw.inner); 399 - 400 - const vyw = Vec4F.unitX().add(&Vec4F.unitY().add(&Vec4F.unitW())); 401 - try std.testing.expectEqual(.{.{ 1, 1, 0, 1 }}, vyw.inner); 402 - } 403 - 404 - test "arith" { 405 - const v = Vec2F.zeroed(); 406 - const v2 = v.add(&Vec2F.init(.{.{ -3, 8 }})); 407 - const v3 = v2.mul(4); 408 - const v4 = v3.dot(&v3); 409 - 410 - try std.testing.expectEqual(1168, v4); 411 - } 412 - 413 - test "mul2 & div2" { 414 - const m1 = Mat2F.init(.{ 415 - .{ 3, 5 }, 416 - .{ 7, 9 }, 417 - }); 418 - 419 - const m2 = Mat2F.init(.{ 420 - .{ 9, 6 }, 421 - .{ -5, 4 }, 422 - }); 423 - 424 - const rm = m1.mul2(&m2); 425 - 426 - try std.testing.expectEqual(rm.inner[0], .{ 27, 30 }); 427 - try std.testing.expectEqual(rm.inner[1], .{ -35, 36 }); 428 - } 429 - 430 - test "shorthand" { 431 - _ = vec(f32, 2, .{ 3, 4 }); 432 - _ = mat(f32, 2, 2, .{ 433 - .{ 3, 4 }, 434 - .{ 5, 6 }, 435 - }); 436 - }
+8 -76
src/math.zig
··· 1 1 const std = @import("std"); 2 2 const math = std.math; 3 3 4 - const la = @import("mitochondria").la; 5 - const Matrix = la.Matrix; 6 - const Vector = la.Vector; 7 - const vec = la.vec; 8 - const mat = la.mat; 9 - 10 4 pub const AngleType = enum { 11 5 Rad, 12 6 Deg, ··· 20 14 Deg: T, 21 15 22 16 /// Initialize an `Angle` from degrees 23 - pub fn initDeg(v: T) Self { 17 + pub fn initDegrees(v: T) Self { 24 18 return Self{ .Deg = v }; 25 19 } 26 20 27 21 /// Initialize an `Angle` from radians 28 - pub fn initRad(v: T) Self { 22 + pub fn initRadians(v: T) Self { 29 23 return Self{ .Rad = v }; 30 24 } 31 25 ··· 48 42 } 49 43 50 44 pub fn angleDeg(v: anytype) Angle(@TypeOf(v)) { 51 - return Angle(@TypeOf(v)).initDeg(v); 45 + return Angle(@TypeOf(v)).initDegrees(v); 52 46 } 53 47 54 48 pub fn angleRad(v: anytype) Angle(@TypeOf(v)) { 55 - return Angle(@TypeOf(v)).initRad(v); 56 - } 57 - 58 - /// Create a perspective projection matrix 59 - pub fn perspective( 60 - comptime T: type, 61 - fovy: Angle(T), 62 - aspect: T, 63 - near: T, 64 - far: T, 65 - ) Matrix(T, 4, 4) { 66 - switch (@typeInfo(T)) { 67 - .float => {}, 68 - else => @panic("`T` should be float"), 69 - } 70 - 71 - const sinfov = math.sin(0.5 * fovy.radians()); 72 - const cosfov = math.cos(0.5 * fovy.radians()); 73 - 74 - const h = cosfov / sinfov; 75 - const w = h / aspect; 76 - const r = near - far; 77 - return mat(T, 4, 4, .{ 78 - .{ w, 0.0, 0.0, 0.0 }, 79 - .{ 0.0, h, 0.0, 0.0 }, 80 - .{ 0.0, 0.0, (near + far) / r, -1.0 }, 81 - .{ 0.0, 0.0, 2.0 * near * far / r, 0.0 }, 82 - }); 83 - } 84 - 85 - /// Create an orthographic projection matrix 86 - pub fn orthographic( 87 - comptime T: type, 88 - w: T, 89 - h: T, 90 - near: T, 91 - far: T, 92 - ) Matrix(T, 4, 4) { 93 - switch (@typeInfo(T)) { 94 - .float => {}, 95 - else => @panic("`T` should be float"), 96 - } 97 - 98 - const r = near - far; 99 - return mat(T, 4, 4, .{ 100 - .{ 2 / w, 0.0, 0.0, 0.0 }, 101 - .{ 0.0, 2 / h, 0.0, 0.0 }, 102 - .{ 0.0, 0.0, 2 / r, 0.0 }, 103 - .{ 0.0, 0.0, (near + far) / r, 1.0 }, 104 - }); 49 + return Angle(@TypeOf(v)).initRadians(v); 105 50 } 106 51 107 - test "perspective" { 108 - const persp = perspective( 109 - f32, 110 - angleDeg(@as(f32, 45.0)), 111 - 1.777, 112 - 0.1, 113 - 100.0, 114 - ); 52 + test "angle" { 53 + const A = Angle(f32); 115 54 116 - for (persp.inner) |r| 117 - std.debug.print("{any}\n", .{r}); 118 - std.debug.print("\n", .{}); 119 - } 55 + const d = A.initDegrees(180); 120 56 121 - test "orthographic" { 122 - const ortho = orthographic(f32, 43, 54, 0.1, 100.0); 123 - for (ortho.inner) |r| 124 - std.debug.print("{any}\n", .{r}); 125 - std.debug.print("\n", .{}); 57 + try std.testing.expectApproxEqAbs(3.14159, d.radians(), 0.001); 126 58 }
+2 -2
src/root.zig
··· 1 1 const std = @import("std"); 2 2 3 - pub const la = @import("./la.zig"); 4 3 pub const math = @import("./math.zig"); 5 4 6 5 pub const packet = @import("./packet.zig"); ··· 19 18 pub const queue = @import("./queue.zig"); 20 19 pub const binary_tree = @import("./binary_tree.zig"); 21 20 21 + // TODO: rope data structure based on `BinaryTree` 22 + 22 23 test { 23 - _ = la; 24 24 _ = math; 25 25 _ = packet; 26 26 _ = sparse_set;