diff --git a/src/grome/matrix.zig b/src/grome/matrix.zig index f80378a..818d861 100644 --- a/src/grome/matrix.zig +++ b/src/grome/matrix.zig @@ -87,6 +87,7 @@ pub fn Matrix(comptime T: type) type { // set sets the value of the specified location in memory pub fn set(self: *@This(), loc: Point, val: T) !void { if (loc.x >= self.size.width or loc.y >= self.size.height) { + std.debug.print("loc.x ({d}) >= self.size.width ({d}) or loc.y ({d}) >= self.size.height ({d})", .{ loc.x, self.size.width, loc.y, self.size.height }); return Error.OutOfBounds; } @@ -128,13 +129,22 @@ pub fn Matrix(comptime T: type) type { test "TestMatrix" { const testing = std.testing; + const tests = [_]struct { + name: []const u8, + size: Rect, + point: Point, + value: u64, + }{ + .{ .name = "small", .size = .{ .width = 10, .height = 10 }, .point = .{ .x = 1, .y = 1 }, .value = 100 }, + .{ .name = "big", .size = .{ .width = 1000, .height = 1000 }, .point = .{ .x = 546, .y = 45 }, .value = 3472834 }, + }; - var mat = try Matrix(u64).initZero(testing.allocator, Rect{ .width = 10, .height = 10 }); - defer mat.deinit(testing.allocator); - - try mat.set(Point{ .x = 1, .y = 1 }, 100); - try testing.expectEqualSlices(u64, ([_]u64{ 0, 100 } ++ ([_]u64{0} ** 8))[0..], try mat.rowable.row(1)); - try testing.expectEqual(100, try mat.get(Point{ .x = 1, .y = 1 })); + inline for (tests) |case| { + var mat = try Matrix(u64).initZero(testing.allocator, case.size); + defer mat.deinit(testing.allocator); + try mat.set(case.point, case.value); + try testing.expectEqual(case.value, try mat.get(case.point)); + } } // SubMatrix is a subset of a matrix which you can traverse to diff --git a/src/main.zig b/src/main.zig index d98388a..f4e1f94 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,9 +1,11 @@ const std = @import("std"); -const grome = @import("grome"); +const grome = @import("root.zig"); const Io = std.Io; const Rect = grome.matrix.Rect; const DoubleBuffer = grome.draw.DoubleBuffer; +const Duration = Io.Duration; +const Clock = Io.Clock; pub fn main(init: std.process.Init) !void { // This is appropriate for anything that lives as long as the process. @@ -12,34 +14,21 @@ pub fn main(init: std.process.Init) !void { // Initialize the stdout writer var stdout_buffer: [1024]u8 = undefined; var stdout_file_writer: Io.File.Writer = .init(.stdout(), init.io, &stdout_buffer); - const stdout_writer = &stdout_file_writer.interface; // alright lets play with the matrix const size = Rect{ .height = 20, .width = 50 }; - var matrix = try DoubleBuffer.init(arena, size); + var double_buf = try DoubleBuffer.init(arena, size); - const density = (width * height) / 100; - for (0..density) |_| { - var buf: [3]u8 = undefined; - init.io.random(&buf); - matrix.set(@intCast(buf[0] % width), @intCast(buf[1] % height), @intCast((buf[2] % 50) + 93)); + const density = size.area() / 100; + while (true) { + var frame = double_buf.frame(.{ .x = 0, .y = 0 }, size); + for (0..density) |_| { + var buf: [3]u8 = undefined; + init.io.random(&buf); + try frame.set(.{ .x = @intCast(buf[0] % size.width), .y = @intCast(buf[1] % size.height) }, .{ .char = [_]u8{@intCast((buf[2] % 50) + 93)} ** 8, .width = 1 }); + } + + _ = try double_buf.render(&stdout_file_writer.interface); + try init.io.sleep(Duration.fromMilliseconds(100), Clock.real); } - - _ = try stdout_writer.write(" ┌─"); - for (0..width) |_| { - _ = try stdout_writer.write("─"); - } - _ = try stdout_writer.write("─┐ \n"); - - while (iter.next()) |row| { - _ = try stdout_writer.print(" │ {s} │ \n", .{row}); - } - - _ = try stdout_writer.write(" └─"); - for (0..width) |_| { - _ = try stdout_writer.write("─"); - } - _ = try stdout_writer.write("─┘ \n"); - - try stdout_writer.flush(); } diff --git a/src/view/draw.zig b/src/view/draw.zig index d85bd85..e42763a 100644 --- a/src/view/draw.zig +++ b/src/view/draw.zig @@ -1,7 +1,7 @@ const std = @import("std"); const mem = std.mem; -const grome = @import("root"); +const grome = @import("../root.zig"); const Allocator = mem.Allocator; const Matrix = grome.matrix.Matrix; @@ -15,20 +15,27 @@ const Rect = grome.matrix.Rect; /// approach minimizes unnecessary updates by only diffing changed cells. pub const DoubleBuffer = struct { // display holds the existing data. - display: Matrix(Cell), + a: Matrix(Cell), // write is where we are writing the active changes which will be commited // on the next rendering - write: Matrix(Cell), + b: Matrix(Cell), + + // Front and back hold pointers to a and b, they will switch back and forth + // based on what is needed. + front: *Matrix(Cell), + back: *Matrix(Cell), pub fn init(gpa: Allocator, size: Rect) !DoubleBuffer { - const display = try gpa.alloc(Cell, size.area()); - errdefer gpa.free(display); + var a = try Matrix(Cell).init(gpa, .{ .char = [_]u8{' '} ** 8, .width = 1 }, size); + errdefer a.deinit(gpa); - const write = try gpa.alloc(Cell, size.area()); + var b = try Matrix(Cell).init(gpa, .{ .char = [_]u8{' '} ** 8, .width = 1 }, size); return .{ - .display = display, - .write = write, + .a = a, + .b = b, + .front = &a, + .back = &b, }; } @@ -37,19 +44,23 @@ pub const DoubleBuffer = struct { gpa.free(self.write); } - pub fn frame(m: Matrix, loc: Point, size: Rect) Frame { - return .init(m, loc, size); + pub fn frame(self: *@This(), loc: Point, size: Rect) SubMatrix(Cell) { + return .init(self.back, loc, size); } -}; -/// Frame is a location in the matrix so you can change things if you want -pub const Frame = struct { - data: SubMatrix(Cell), + pub fn render(self: *@This(), w: *std.Io.Writer) !usize { + std.mem.swap(*Matrix(Cell), &self.front, &self.back); - pub fn init(m: Matrix, loc: Point, size: Rect) Frame { - return .{ - .data = SubMatrix(Cell).init(m, loc, size), - }; + var i = try w.write("\\033[H\\033[2J"); + var iter = self.front.row_iterator(); + while (iter.next()) |row| { + for (row) |cell| { + i += try cell.render(w); + } + i += try w.write("\n"); + } + + return i; } }; @@ -61,4 +72,8 @@ pub const Cell = struct { pub fn eql(self: *const Cell, b: *const Cell) bool { return mem.eql(u8, self.char, b.char) and self.width == b.width; } + + pub fn render(self: *const @This(), w: *std.Io.Writer) !usize { + return w.write(self.char[0..self.width]); + } };