summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/animation.zig41
-rw-r--r--src/draw.zig20
2 files changed, 46 insertions, 15 deletions
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);
diff --git a/src/draw.zig b/src/draw.zig
index 93d5b2d9..b0b16e12 100644
--- a/src/draw.zig
+++ b/src/draw.zig
@@ -723,6 +723,7 @@ fn composedCell(
return switch (animation.charSource(track, col - area.x0, row - area.y0, area)) {
.old => canonical.previous_cells[index],
.byte => |byte| withByte(canonical.cells[index], byte),
+ .edge => |weight| towardGround(canonical.cells[index], canonical.previous_cells[index].style.bg, weight),
// Churn belongs on the glyph, not on the pane's empty space, and it
// borrows whichever side of the diff actually has one.
.churn => |byte| blk: {
@@ -749,6 +750,25 @@ fn composedCell(
};
}
+/// A cell with its ink and ground drawn `weight` of the way toward another
+/// ground, in OKLab. A palette or default colour cannot be mixed, so the
+/// ground steps over at the halfway point instead.
+fn towardGround(cell: Cell, ground: Color, weight: f32) Cell {
+ var out = cell;
+ const to = switch (ground) {
+ .rgb => |rgb| rgb,
+ .default, .index => return if (weight >= 0.5) blk: {
+ out.style.bg = ground;
+ break :blk out;
+ } else out,
+ };
+ inline for (.{ &out.style.fg, &out.style.bg }) |color| switch (color.*) {
+ .rgb => |rgb| color.* = .{ .rgb = colors.mixOklab(rgb, to, weight) },
+ .default, .index => {},
+ };
+ return out;
+}
+
fn withByte(cell: Cell, byte: u8) Cell {
var out = cell;
out.text[0] = byte;