From ab31baff1fbfd38180be8eed921b97082184f185 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 09:17:19 -0300 Subject: T3: PanelCurtain wipes behind a soft edge two cells wide The curtain's glyph slide becomes a wipe: the new grid behind a head that crosses the pane, the old ahead, and between them two columns whose colours step from the old ground to the new in OKLab (CharSource.edge). Only the head's columns change from frame to frame. --- src/animation.zig | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'src/animation.zig') diff --git a/src/animation.zig b/src/animation.zig index b682c770..840412f3 100644 --- a/src/animation.zig +++ b/src/animation.zig @@ -359,6 +359,9 @@ pub const CharSource = union(enum) { /// that cell holds — a caret marching over empty space is still a caret. byte: u8, churn: u8, + /// The destination cell with its colours still this far (0..1) toward + /// the frozen old cell's ground: a soft edge between the two grids. + edge: f32, pub const Offset = struct { cols: i32 = 0, rows: i32 = 0 }; @@ -390,12 +393,16 @@ pub fn charSource(track: Track, col: u16, row: u16, area: CellArea) CharSource { .wave => .{ .at = .{ .rows = cellsOf(remaining * @min(4.0, h) * @sin(c * 0.55 - t * 9.0)), } }, - // A curtain of glyphs marches in from the right, column by column, left - // to right; each column still has a short slide of its own. + // A wipe, left to right: the new grid behind the head, the old one + // ahead of it, and between them a soft edge two cells wide whose + // colours step from the old ground to the new. Only the head's + // columns change from one frame to the next. .curtain => blk: { - const lead = t * (w + 1.0) - c; + const lead = t * (w + 2.0) - c; if (lead <= 0.0) break :blk .old; - break :blk .{ .at = .{ .cols = -cellsOf(@max(0.0, 3.0 - lead)) } }; + if (lead <= 1.0) break :blk .{ .edge = 2.0 / 3.0 }; + if (lead <= 2.0) break :blk .{ .edge = 1.0 / 3.0 }; + break :blk .settled; }, // Every cell churns through printable ASCII and locks onto its final // glyph at its own stable threshold: the pane resolves out of noise. @@ -524,7 +531,7 @@ test "each character effect moves glyphs along its own axis" { var col: u16 = 0; while (col < area.cols) : (col += 1) switch (charSource(fall, col, 0, area)) { .old => {}, - .byte, .churn => return error.FallShouldNotChurn, + .byte, .churn, .edge => return error.FallShouldNotChurn, .at => |offset| { try std.testing.expectEqual(@as(i32, 0), offset.cols); try std.testing.expect(offset.rows >= 0); @@ -546,20 +553,24 @@ test "each character effect moves glyphs along its own axis" { } try std.testing.expect(above and below); - // The curtain has a head: columns behind it hold the old grid, columns the - // head has passed are settled, and the head itself is still sliding. + // The curtain wipes: columns ahead of its head hold the old grid, + // columns it has passed are settled, and between them a soft edge of + // exactly two columns steps from the old ground to the new. const curtain: Track = .{ .effect = .curtain, .frame = 5, .to = box }; try std.testing.expectEqual(CharSource.settled, charSource(curtain, 0, 0, area)); try std.testing.expectEqual(CharSource{ .old = {} }, charSource(curtain, 29, 0, area)); - var sliding = false; + var wiped: [2]f32 = undefined; + var at_edge: usize = 0; col = 0; while (col < area.cols) : (col += 1) switch (charSource(curtain, col, 0, area)) { - .at => |offset| if (offset.cols < 0) { - sliding = true; + .edge => |weight| { + wiped[at_edge] = weight; + at_edge += 1; }, - .old, .byte, .churn => {}, + .old, .at, .byte, .churn => {}, }; - try std.testing.expect(sliding); + try std.testing.expectEqual(@as(usize, 2), at_edge); + try std.testing.expect(wiped[0] < wiped[1]); var scramble: Track = .{ .effect = .scramble, .frame = 3, .to = box }; var churning: usize = 0; @@ -573,12 +584,12 @@ test "each character effect moves glyphs along its own axis" { scramble.frame = 4; switch (charSource(scramble, col, 0, area)) { .churn => |next| changed = changed or next != byte, - .old, .at, .byte => {}, + .old, .at, .byte, .edge => {}, } scramble.frame = 3; }, .at => locked += 1, - .old, .byte => return error.ScrambleShouldNotFreeze, + .old, .byte, .edge => return error.ScrambleShouldNotFreeze, }; try std.testing.expect(churning > 0 and locked > 0 and changed); @@ -598,7 +609,7 @@ test "each character effect moves glyphs along its own axis" { try std.testing.expectEqual(@as(u8, '_'), byte); carets += 1; }, - .old, .at, .churn => {}, + .old, .at, .churn, .edge => {}, }; } try std.testing.expectEqual(@as(usize, 1), carets); -- cgit v1.3