diff options
| -rw-r--r-- | docs/effects.md | 6 | ||||
| -rw-r--r-- | src/animation.zig | 19 |
2 files changed, 24 insertions, 1 deletions
diff --git a/docs/effects.md b/docs/effects.md index 02602a23..ac202579 100644 --- a/docs/effects.md +++ b/docs/effects.md @@ -264,3 +264,9 @@ it does in a pixel shell. | # | effect | switch | state | review material | verdict | |---|---|---|---|---|---| | T2 | cursor jump trail | `JumpTrail on\|off` (off) | a jump of three cells or more leaves 3 to 5 cells of trail behind the cursor, their ground drawn toward the text's colour in OKLab, most beside it, fading in 120 ms; truecolor terminals (vaxis's answer or COLORTERM); never the cursor's cell, a selection or outside the focused body; off under Motion off; a few cells a frame | .scratch/render/tty/trail/ (pyte replay of a real tty session at 60 fps: trail-forge.mp4, zooms) | pending | +| T4 ordered swap | `PanelDissolve` (off) | the terminal's dissolve swaps each changed cell once at its place in a 4x4 Bayer matrix (shifted per pane), an even patterned screen instead of noise; hit-testing follows the same order | .scratch/render/tty/dissolve/ (ordered-swap-sim: the matrix applied to two real frames; see note) | pending | + +Note on T4's material: a pane opening in a tty session recorded through +pyte did not animate (PanelSlide did not either there), so the review +material applies the same matrix to two real frames of that session; the +unit test holds the order (a 4x4 block reveals exactly t x 16 cells). diff --git a/src/animation.zig b/src/animation.zig index 713a8a30..b682c770 100644 --- a/src/animation.zig +++ b/src/animation.zig @@ -313,11 +313,18 @@ pub fn cellNoise(serial: u32, col: u16, row: u16) f32 { /// Whether a changed dissolve cell has crossed from the frozen old grid to /// the new one. Exact endpoints are part of the presentation contract. +/// T4 (ยง9.2): an ordered swap, each cell at its place in a 4x4 Bayer +/// matrix (shifted by the pane's serial so two panes differ), so the new +/// content comes in as an even, patterned screen rather than noise, and +/// each cell swaps once. pub fn dissolveRevealed(serial: u32, col: u16, row: u16, raw_progress: f32) bool { const t = std.math.clamp(raw_progress, 0.0, 1.0); if (t <= 0) return false; if (t >= 1) return true; - return cellNoise(serial, col, row) < t; + const bayer = [4][4]u8{ .{ 0, 8, 2, 10 }, .{ 12, 4, 14, 6 }, .{ 3, 11, 1, 9 }, .{ 15, 7, 13, 5 } }; + const x = (@as(u32, col) +% serial) % 4; + const y = (@as(u32, row) +% serial / 4) % 4; + return (@as(f32, @floatFromInt(bayer[y][x])) + 0.5) / 16 < t; } pub const CellArea = struct { @@ -624,6 +631,16 @@ test "opening presets separate geometry and content transitions" { try std.testing.expect(closing.amount() < progress(.vertical, closing.frame)); } +test "the ordered dissolve reveals a 4x4 block evenly, each cell once" { + for ([_]f32{ 0.25, 0.5, 0.75 }) |t| { + var revealed: usize = 0; + for (0..4) |y| for (0..4) |x| { + if (dissolveRevealed(7, @intCast(x), @intCast(y), t)) revealed += 1; + }; + try std.testing.expectEqual(@as(usize, @intFromFloat(t * 16)), revealed); + } +} + test "dissolve has exact stable endpoints" { for (0..64) |col| { const x: u16 = @intCast(col); |
