It works kinda
This commit is contained in:
+26
-20
@@ -63,46 +63,52 @@ pub fn Matrix(comptime T: type) type {
|
||||
/// defines the size of the matrix
|
||||
size: Rect,
|
||||
|
||||
/// Specifies the zero value to display when reseting the matrix
|
||||
zero: T,
|
||||
|
||||
rowable: Rowable(T),
|
||||
|
||||
/// init creates the chunk, storing the bytes in memory.
|
||||
pub fn initZero(gpa: Allocator, size: Rect) !@This() {
|
||||
const data = try gpa.alloc(T, size.area());
|
||||
@memset(data, mem.zeroes(T));
|
||||
return .{ .data = data, .size = size, .rowable = .{ .rowFn = Matrix(T).row } };
|
||||
return @This().init(gpa, mem.zeroes(T), size);
|
||||
}
|
||||
|
||||
/// init creates the chunk, storing the bytes in memory.
|
||||
pub fn init(gpa: Allocator, zero: T, size: Rect) !@This() {
|
||||
const data = try gpa.alloc(T, size.area());
|
||||
@memset(data, zero);
|
||||
return .{ .data = data, .size = size, .rowable = .{ .rowFn = Matrix(T).row } };
|
||||
var val = @This(){ .data = data, .size = size, .zero = zero, .rowable = .{ .rowFn = Matrix(T).row } };
|
||||
val.reset();
|
||||
return val;
|
||||
}
|
||||
|
||||
/// reset sets all values in the matrix back to the zero value.
|
||||
pub fn reset(self: *@This()) void {
|
||||
@memset(self.data, self.zero);
|
||||
}
|
||||
|
||||
/// deinit frees the memory utilized to create the chunk
|
||||
pub fn deinit(self: *@This(), gpa: Allocator) void {
|
||||
pub fn deinit(self: *const @This(), gpa: Allocator) void {
|
||||
gpa.free(self.data);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const index = (self.size.width * loc.y) + loc.x;
|
||||
self.data[index] = val;
|
||||
(try self.getPtr(loc)).* = val;
|
||||
}
|
||||
|
||||
// set sets the value of the specified location in memory
|
||||
pub fn get(self: *const @This(), loc: Point) !T {
|
||||
pub fn getPtr(self: *const @This(), loc: Point) !*T {
|
||||
if (loc.x >= self.size.width or loc.y >= self.size.height) {
|
||||
return Error.OutOfBounds;
|
||||
}
|
||||
|
||||
const index = (self.size.width * loc.y) + loc.x;
|
||||
return self.data[index];
|
||||
return &self.data[index];
|
||||
}
|
||||
|
||||
pub fn get(self: *const @This(), loc: Point) !T {
|
||||
return (try self.getPtr(loc)).*;
|
||||
}
|
||||
|
||||
/// submatrix returns a [SubMatrix]
|
||||
@@ -164,19 +170,19 @@ pub fn SubMatrix(comptime T: type) type {
|
||||
}
|
||||
|
||||
pub fn get(self: *const @This(), loc: Point) !T {
|
||||
return (try self.getPtr(loc)).*;
|
||||
}
|
||||
|
||||
pub fn getPtr(self: *const @This(), loc: Point) !*T {
|
||||
if (loc.x >= self._mask.width or loc.y >= self._mask.height) {
|
||||
return Error.OutOfBounds;
|
||||
}
|
||||
|
||||
try self._m.get(self._offset.add(loc));
|
||||
return self._m.getPtr(self._offset.add(loc));
|
||||
}
|
||||
|
||||
pub fn set(self: *@This(), loc: Point, val: T) !void {
|
||||
if (loc.x >= self._mask.width or loc.y >= self._mask.height) {
|
||||
return Error.OutOfBounds;
|
||||
}
|
||||
|
||||
try self._m.set(self._offset.add(loc), val);
|
||||
(try self.getPtr(loc)).* = val;
|
||||
}
|
||||
|
||||
pub fn row(interface: *Rowable(T), line: usize) ![]T {
|
||||
|
||||
+26
-9
@@ -14,21 +14,38 @@ 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);
|
||||
var w = &stdout_file_writer.interface;
|
||||
|
||||
// alright lets play with the matrix
|
||||
const size = Rect{ .height = 20, .width = 50 };
|
||||
const size = Rect{ .height = 70, .width = 120 };
|
||||
var double_buf = try DoubleBuffer.init(arena, size);
|
||||
defer double_buf.deinit(arena);
|
||||
try double_buf.hard_refresh(w);
|
||||
|
||||
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 });
|
||||
var frame = double_buf.frame(.{ .x = 0, .y = 10 }, .{ .height = 1, .width = 120 });
|
||||
const row = try frame.rowable.row(0);
|
||||
var fill_next: bool = false;
|
||||
var unfilled: bool = true;
|
||||
|
||||
for (row) |*cell| {
|
||||
if (cell.char[0] == '>') {
|
||||
fill_next = true;
|
||||
cell.char[0] = ' ';
|
||||
} else {
|
||||
if (fill_next) {
|
||||
fill_next = false;
|
||||
cell.char[0] = '>';
|
||||
unfilled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unfilled) {
|
||||
row[0].char[0] = '>';
|
||||
}
|
||||
|
||||
_ = try double_buf.render(&stdout_file_writer.interface);
|
||||
try init.io.sleep(Duration.fromMilliseconds(100), Clock.real);
|
||||
try double_buf.refresh(w);
|
||||
try w.flush();
|
||||
try init.io.sleep(Duration.fromMilliseconds(10), Clock.real);
|
||||
}
|
||||
}
|
||||
|
||||
+63
-28
@@ -15,52 +15,87 @@ const Rect = grome.matrix.Rect;
|
||||
/// approach minimizes unnecessary updates by only diffing changed cells.
|
||||
pub const DoubleBuffer = struct {
|
||||
// display holds the existing data.
|
||||
a: Matrix(Cell),
|
||||
front: Matrix(Cell),
|
||||
// write is where we are writing the active changes which will be commited
|
||||
// on the next rendering
|
||||
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),
|
||||
back: Matrix(Cell),
|
||||
|
||||
pub fn init(gpa: Allocator, size: Rect) !DoubleBuffer {
|
||||
var a = try Matrix(Cell).init(gpa, .{ .char = [_]u8{' '} ** 8, .width = 1 }, size);
|
||||
const a = try Matrix(Cell).init(gpa, .{ .char = [_]u8{' '} ** 8, .width = 1 }, size);
|
||||
errdefer a.deinit(gpa);
|
||||
|
||||
var b = try Matrix(Cell).init(gpa, .{ .char = [_]u8{' '} ** 8, .width = 1 }, size);
|
||||
const b = try Matrix(Cell).init(gpa, .{ .char = [_]u8{' '} ** 8, .width = 1 }, size);
|
||||
|
||||
return .{
|
||||
.a = a,
|
||||
.b = b,
|
||||
.front = &a,
|
||||
.back = &b,
|
||||
.front = a,
|
||||
.back = b,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This(), gpa: Allocator) void {
|
||||
gpa.free(self.a);
|
||||
gpa.free(self.b);
|
||||
self.front.deinit(gpa);
|
||||
self.back.deinit(gpa);
|
||||
}
|
||||
|
||||
// frame returns a subset of the backbuffer where `loc` is the top left point of the
|
||||
// subsection, and `size` is the area or size of the subset.
|
||||
pub fn frame(self: *@This(), loc: Point, size: Rect) SubMatrix(Cell) {
|
||||
return .init(self.back, loc, size);
|
||||
return .init(&self.back, loc, size);
|
||||
}
|
||||
|
||||
pub fn render(self: *@This(), w: *std.Io.Writer) !usize {
|
||||
std.mem.swap(*Matrix(Cell), &self.front, &self.back);
|
||||
// swap the front and back. This should be called right before rendering
|
||||
fn swap(self: *@This()) void {
|
||||
std.mem.swap(Matrix(Cell), &self.front, &self.back);
|
||||
}
|
||||
|
||||
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);
|
||||
// render outputs contents of the backbuffer to the writer. This function **does not**
|
||||
// call `flush` so you should!
|
||||
pub fn hard_refresh(self: *@This(), w: *std.Io.Writer) !void {
|
||||
_ = try w.write("\x1b[H\x1b[2J");
|
||||
var back_iter = self.back.row_iterator();
|
||||
|
||||
while (back_iter.next()) |back_row| {
|
||||
for (back_row) |cell| {
|
||||
_ = try cell.render(w);
|
||||
}
|
||||
i += try w.write("\n");
|
||||
_ = try w.write("\n");
|
||||
}
|
||||
|
||||
return i;
|
||||
self.swap();
|
||||
}
|
||||
|
||||
pub fn refresh(self: *@This(), w: *std.Io.Writer) !void {
|
||||
_ = try w.write("\x1b[H");
|
||||
|
||||
var back_iter = self.back.row_iterator();
|
||||
var front_iter = self.front.row_iterator();
|
||||
var skip_lines: usize = 0;
|
||||
|
||||
while (true) {
|
||||
var back_row = back_iter.next() orelse break;
|
||||
const front_row = front_iter.next() orelse break;
|
||||
|
||||
for (0..back_row.len) |x| {
|
||||
if (back_row[x].eql(&front_row[x])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's a new character then write out the rest of the line and then
|
||||
// continue
|
||||
try w.print("\x1b[{d};{d}H", .{ skip_lines, x + 1 });
|
||||
for (back_row[x..]) |*cell| {
|
||||
try cell.render(w);
|
||||
}
|
||||
_ = try w.write("\n");
|
||||
skip_lines = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
skip_lines += 1;
|
||||
}
|
||||
|
||||
_ = try w.write("\x1b[?25l");
|
||||
self.swap();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,10 +105,10 @@ pub const Cell = struct {
|
||||
|
||||
/// eql returns
|
||||
pub fn eql(self: *const Cell, b: *const Cell) bool {
|
||||
return mem.eql(u8, self.char, b.char) and self.width == b.width;
|
||||
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]);
|
||||
pub fn render(self: *const @This(), w: *std.Io.Writer) !void {
|
||||
_ = try w.write(self.char[0..self.width]);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user