···11# mitochondria
2233-Zig utility library providing data structures, linear algebra, and helper functions.
33+Zig utility library providing data structures, and helper functions.
4455Until Zig has reached a stable 1.0.0 release, this library will be rolling release.
-436
src/la.zig
···11-const std = @import("std");
22-const math = std.math;
33-const assert = std.debug.assert;
44-55-/// Create a `columns`x`rows` matrix of `T`
66-pub fn Matrix(comptime T: type, rows: usize, columns: usize) type {
77- assert(rows > 0);
88- assert(columns > 0);
99-1010- {
1111- const t = @typeInfo(T);
1212-1313- switch (t) {
1414- .int, .float => {},
1515- else => @compileError("`T` should be float or int"),
1616- }
1717- }
1818-1919- return struct {
2020- const Self = @This();
2121-2222- /// A row of the matrix
2323- pub const Row = @Vector(rows, T);
2424-2525- /// An array representation of `Row`
2626- pub const RawRow = [rows]T;
2727-2828- /// The columns of rows of the matrix
2929- pub const InnerMat = [columns]Row;
3030-3131- /// A 2D-array representation of the matrix
3232- pub const ArrayRep = [columns][rows]T;
3333-3434- inner: InnerMat,
3535-3636- /// Initialize a matrix
3737- pub fn init(table: InnerMat) Self {
3838- return Self{ .inner = table };
3939- }
4040-4141- /// Initialize from a 2D-array representation of the matrix
4242- pub fn initRaw(table: ArrayRep) Self {
4343- return Self{ .inner = table };
4444- }
4545-4646- /// Return a matrix filled with zeroes
4747- pub fn zeroed() Self {
4848- var cols: InnerMat = undefined;
4949- @memset(&cols, @splat(0));
5050- return Self.init(cols);
5151- }
5252-5353- /// Copy the matrix, creating an identical clone
5454- pub fn copy(self: *const Self) Self {
5555- return Self{ .inner = self.inner };
5656- }
5757-5858- /// Return whether this matrix is also a plain vector (1 column).
5959- pub fn isVector() bool {
6060- return columns == 1;
6161- }
6262-6363- /// Add all elements of the matrix to another matrix
6464- pub fn add(self: *const Self, other: *const Self) Self {
6565- var out = self.copy();
6666-6767- for (out.inner, 0..) |row, i| {
6868- out.inner[i] = row + other.inner[i];
6969- }
7070-7171- return out;
7272- }
7373-7474- /// Subtract all elements of the matrix from another matrix
7575- pub fn sub(self: *const Self, other: *const Self) Self {
7676- var out = self.copy();
7777-7878- for (out.inner, 0..) |row, i| {
7979- out.inner[i] = row - other.inner[i];
8080- }
8181-8282- return out;
8383- }
8484-8585- /// Multiply all elements of the matrix by a scalar
8686- pub fn mul(self: *const Self, scalar: T) Self {
8787- var out = self.copy();
8888-8989- for (out.inner, 0..) |row, i| {
9090- out.inner[i] = row * @as(Row, @splat(scalar));
9191- }
9292-9393- return out;
9494- }
9595-9696- /// Multiply a matrix with another matrix
9797- /// Each element in the matrix is multiplied by the
9898- /// element at the same index of the other matrix
9999- pub fn mul2(self: *const Self, other: *const Self) Self {
100100- var out = self.copy();
101101-102102- for (out.inner, other.inner, 0..) |r1, r2, i| {
103103- out.inner[i] = r1 * r2;
104104- }
105105-106106- return out;
107107- }
108108-109109- /// Divide all elements of the matrix by a scalar
110110- pub fn div(self: *const Self, scalar: T) Self {
111111- var out = self.copy();
112112-113113- for (out.inner, 0..) |row, i| {
114114- out.inner[i] = row / @as(Row, @splat(scalar));
115115- }
116116-117117- return out;
118118- }
119119-120120- /// Divide a matrix by another matrix
121121- /// Each element in the matrix is divided by the
122122- /// element at the same index of the other matrix
123123- pub fn div2(self: *const Self, other: *const Self) Self {
124124- var out = self.copy();
125125-126126- for (out.inner, other.inner, 0..) |r1, r2, i| {
127127- out.inner[i] = r1 / r2;
128128- }
129129-130130- return out;
131131- }
132132-133133- /// Get the magnitude of the vector
134134- pub fn magnitude(self: *const Self) T {
135135- var mag: T = 0;
136136-137137- for (self.inner) |r| {
138138- for (@as(RawRow, r)) |s| {
139139- mag += math.pow(T, s, 2);
140140- }
141141- }
142142-143143- return math.sqrt(mag);
144144- }
145145-146146- /// Get the distance between 2 vectors
147147- pub fn distance(self: *const Self, other: *const Self) T {
148148- return other.sub(self).magnitude();
149149- }
150150-151151- /// Normalize the vector
152152- pub fn normalize(self: *const Self) Self {
153153- var normalized = self.copy();
154154- var unorm: T = 0;
155155-156156- for (self.inner) |r| {
157157- for (@as(RawRow, r)) |s| {
158158- unorm += math.pow(T, s, 2);
159159- }
160160- }
161161-162162- for (normalized.inner, 0..) |r, i| {
163163- for (@as(RawRow, r), 0..) |s, j| {
164164- normalized.inner[i][j] = s / math.sqrt(unorm);
165165- }
166166- }
167167-168168- return normalized;
169169- }
170170-171171- /// Return the dot product of 2 vectors, returns `null` if not a vector.
172172- pub fn dot(self: *const Self, other: *const Self) ?T {
173173- if (!Self.isVector()) {
174174- return null;
175175- }
176176-177177- var product: T = 0;
178178-179179- for (self.inner, other.inner) |r1, r2| {
180180- for (@as(RawRow, r1), @as(RawRow, r2)) |s1, s2| {
181181- product += s1 * s2;
182182- }
183183- }
184184-185185- return product;
186186- }
187187-188188- // TODO: add cross product
189189-190190- /// Flatten all the elements of the matrix into a single array
191191- pub fn flatten(self: *const Self) [rows * columns]T {
192192- var output: [rows * columns]T = undefined;
193193-194194- var k: usize = 0;
195195- for (self.inner) |r| {
196196- for (@as([rows]T, r)) |s| {
197197- output[k] = s;
198198- k += 1;
199199- }
200200- }
201201-202202- return output;
203203- }
204204-205205- /// Call `func` on all the elements of the matrix
206206- pub fn map(self: *const Self, func: *const fn (T) T) Self {
207207- var out = self.copy();
208208-209209- for (0..out.inner.len) |i| {
210210- for (0..out.inner[i].len) |j| {
211211- out.inner[i][j] = func(out.inner[i][j]);
212212- }
213213- }
214214-215215- return out;
216216- }
217217-218218- /// Return a X unit matrix
219219- pub fn unitX() Self {
220220- var out = Self.zeroed();
221221-222222- for (&out.inner) |*row| {
223223- row[0] = 1;
224224- }
225225-226226- return out;
227227- }
228228-229229- /// Return a Y unit matrix
230230- pub fn unitY() Self {
231231- assert(rows >= 2);
232232-233233- var out = Self.zeroed();
234234-235235- for (&out.inner) |*row| {
236236- row[1] = 1;
237237- }
238238-239239- return out;
240240- }
241241-242242- /// Return a Z unit matrix
243243- pub fn unitZ() Self {
244244- assert(rows >= 3);
245245-246246- var out = Self.zeroed();
247247-248248- for (&out.inner) |*row| {
249249- row[2] = 1;
250250- }
251251-252252- return out;
253253- }
254254-255255- /// Return a W unit matrix
256256- pub fn unitW() Self {
257257- assert(rows >= 4);
258258-259259- var out = Self.zeroed();
260260-261261- for (&out.inner) |*row| {
262262- row[3] = 1;
263263- }
264264-265265- return out;
266266- }
267267- };
268268-}
269269-270270-/// Create a vector of `T` scalars with `components` components
271271-pub fn Vector(comptime T: type, components: usize) type {
272272- return Matrix(T, components, 1);
273273-}
274274-275275-/// 2-dimensional vector of `f32`
276276-pub const Vec2F = Vector(f32, 2);
277277-/// 2-dimensional vector of `f64`
278278-pub const Vec2D = Vector(f64, 2);
279279-280280-/// 3-dimensional vector of `f32`
281281-pub const Vec3F = Vector(f32, 3);
282282-/// 3-dimensional vector of `f64`
283283-pub const Vec3D = Vector(f64, 3);
284284-285285-/// 4-dimensional vector of `f32`
286286-pub const Vec4F = Vector(f32, 4);
287287-/// 4-dimensional vector of `f64`
288288-pub const Vec4D = Vector(f64, 4);
289289-290290-/// 2x2 matrix of `f32`
291291-pub const Mat2F = Matrix(f32, 2, 2);
292292-/// 2x2 matrix of `f64`
293293-pub const Mat2D = Matrix(f64, 2, 2);
294294-295295-/// 3x3 matrix of `f32`
296296-pub const Mat3F = Matrix(f32, 3, 3);
297297-/// 3x3 matrix of `f64`
298298-pub const Mat3D = Matrix(f64, 3, 3);
299299-300300-/// 4x4 matrix of `f32`
301301-pub const Mat4F = Matrix(f32, 4, 4);
302302-/// 4x4 matrix of `f64`
303303-pub const Mat4D = Matrix(f64, 4, 4);
304304-305305-// TODO: idk how the fuck i will, but add quaternions eventually.
306306-307307-pub fn vec(comptime T: type, comptime components: usize, v: @Vector(components, T)) Vector(T, components) {
308308- return Vector(T, components).init(.{v});
309309-}
310310-311311-pub fn mat(
312312- comptime T: type,
313313- comptime rows: usize,
314314- comptime columns: usize,
315315- rep: [columns]@Vector(rows, T),
316316-) Matrix(T, rows, columns) {
317317- return Matrix(T, rows, columns).init(rep);
318318-}
319319-320320-test "mul" {
321321- const x = Matrix(f32, 3, 5).init(.{
322322- .{ 1, 2, 3 },
323323- .{ 2, 4, 6 },
324324- .{ 3, 6, 9 },
325325- .{ 4, 8, 12 },
326326- .{ 5, 10, 15 },
327327- });
328328-329329- const new_mat = x.mul(3);
330330-331331- try std.testing.expectEqual(.{ 3, 6, 9 }, new_mat.inner[0]);
332332-}
333333-334334-test "flatten" {
335335- const x = Matrix(f32, 3, 3).init(.{
336336- .{ 1, 2, 3 },
337337- .{ 4, 5, 6 },
338338- .{ 7, 8, 9 },
339339- });
340340-341341- const array = x.flatten();
342342-343343- try std.testing.expectEqual(.{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, array);
344344-}
345345-346346-test "magnitude" {
347347- const magnitude = Vec3F.init(.{.{ 394, 329, 684 }}).magnitude();
348348- try std.testing.expectEqual(855.1801, magnitude);
349349-}
350350-351351-test "normalized" {
352352- const normalized = Vec2F.init(.{.{ 6, 9 }}).normalize();
353353- try std.testing.expectEqual(1, normalized.magnitude());
354354-}
355355-356356-test "dot" {
357357- const x = Vec2F.init(.{.{ 3, 4 }});
358358- const y = Vec2F.init(.{.{ 6, 9 }});
359359-360360- try std.testing.expectEqual(54, x.dot(&y));
361361-}
362362-363363-test "dist" {
364364- const x = Vec2F.init(.{.{ 4, 1 }});
365365- const y = Vec2F.init(.{.{ 1, 4 }});
366366- try std.testing.expectEqual(4.2426405, x.distance(&y));
367367-}
368368-369369-test "large" {
370370- const m = Matrix(f32, 10, 5).init(.{
371371- .{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
372372- .{ 14, 13, 43, 5, 6, 8, 1, 9, 3, 4 },
373373- .{ 4, 6, 3, 8, 9, 4, 3, 6, 1, 7 },
374374- .{ 5, 4, 6, 6, 8, 3, 5, 7, 13, 17 },
375375- .{ 54, 48, 43, 69, 74, 97, 59, 32, 12, 121 },
376376- });
377377-378378- for (0..1000000) |_| {
379379- _ = m.mul(5);
380380- }
381381-}
382382-383383-test "unit" {
384384- const v = Vec3F.zeroed();
385385- try std.testing.expectEqual(.{.{ 0, 0, 0 }}, v.inner);
386386-387387- const m = Mat3F.zeroed();
388388- try std.testing.expectEqual(.{
389389- .{ 0, 0, 0 },
390390- .{ 0, 0, 0 },
391391- .{ 0, 0, 0 },
392392- }, m.inner);
393393-394394- const vy = Vec3F.unitY();
395395- try std.testing.expectEqual(.{.{ 0, 1, 0 }}, vy.inner);
396396-397397- const vw = Vec4F.unitW();
398398- try std.testing.expectEqual(.{.{ 0, 0, 0, 1 }}, vw.inner);
399399-400400- const vyw = Vec4F.unitX().add(&Vec4F.unitY().add(&Vec4F.unitW()));
401401- try std.testing.expectEqual(.{.{ 1, 1, 0, 1 }}, vyw.inner);
402402-}
403403-404404-test "arith" {
405405- const v = Vec2F.zeroed();
406406- const v2 = v.add(&Vec2F.init(.{.{ -3, 8 }}));
407407- const v3 = v2.mul(4);
408408- const v4 = v3.dot(&v3);
409409-410410- try std.testing.expectEqual(1168, v4);
411411-}
412412-413413-test "mul2 & div2" {
414414- const m1 = Mat2F.init(.{
415415- .{ 3, 5 },
416416- .{ 7, 9 },
417417- });
418418-419419- const m2 = Mat2F.init(.{
420420- .{ 9, 6 },
421421- .{ -5, 4 },
422422- });
423423-424424- const rm = m1.mul2(&m2);
425425-426426- try std.testing.expectEqual(rm.inner[0], .{ 27, 30 });
427427- try std.testing.expectEqual(rm.inner[1], .{ -35, 36 });
428428-}
429429-430430-test "shorthand" {
431431- _ = vec(f32, 2, .{ 3, 4 });
432432- _ = mat(f32, 2, 2, .{
433433- .{ 3, 4 },
434434- .{ 5, 6 },
435435- });
436436-}