summaryrefslogtreecommitdiff
path: root/src/animation.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-06 18:11:36 -0300
committerGabriel Schneider <[email protected]>2026-09-07 13:59:12 -0300
commit60367d8fe23f6af98ec28e3cf6c2094dfe332df0 (patch)
tree310fc734173cf771881f4691c71909135fadde97 /src/animation.zig
parentfa82cac885cb4738fe36d1e49b4749b5a3e31a4a (diff)
downloadpardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.tar.gz
pardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.zip
Refactor panes and filesystem; replace FUSE with 9P
Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples. Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
Diffstat (limited to 'src/animation.zig')
-rw-r--r--src/animation.zig189
1 files changed, 0 insertions, 189 deletions
diff --git a/src/animation.zig b/src/animation.zig
deleted file mode 100644
index c22396b8..00000000
--- a/src/animation.zig
+++ /dev/null
@@ -1,189 +0,0 @@
-//! Small, backend-neutral fixed-step animations.
-//!
-//! A transition always interpolates from its saved endpoints. It never folds
-//! the rounded value from one frame into the next, so channels are monotonic,
-//! completion is exact, and a different backend cadence cannot accumulate a
-//! different rounding error. Values opt in by providing
-//! `interpolate(from, to, step, steps)`.
-const std = @import("std");
-
-/// Frontends aim for one animation step per display frame. Ten 16 ms steps is
-/// deliberately short: enough to make a palette change legible without
-/// turning theme browsing into something the user has to wait through.
-pub const frame_ms: u32 = 16;
-pub const frame_ns: u64 = frame_ms * std.time.ns_per_ms;
-pub const transition_steps: u16 = 10;
-
-pub fn Transition(comptime Value: type) type {
- return struct {
- const Self = @This();
-
- from: Value,
- to: Value,
- displayed: Value,
- step: u16 = transition_steps,
-
- pub fn init(value: Value) Self {
- return .{ .from = value, .to = value, .displayed = value };
- }
-
- pub fn isActive(a: *const Self) bool {
- return a.step < transition_steps;
- }
-
- /// Begin again from the value on screen, not the old target. This is
- /// what makes a mid-flight retarget continuous.
- pub fn retarget(a: *Self, target: Value) void {
- a.from = a.displayed;
- a.to = target;
- a.step = if (std.meta.eql(a.from, target)) transition_steps else 0;
- if (a.step == transition_steps) a.displayed = target;
- }
-
- pub fn advance(a: *Self) void {
- if (!a.isActive()) return;
- a.step += 1;
- // Assign the endpoint directly. Besides documenting the contract,
- // this keeps exact completion independent of an interpolator's
- // internal rounding choices.
- a.displayed = if (a.step == transition_steps)
- a.to
- else
- Value.interpolate(a.from, a.to, a.step, transition_steps);
- }
-
- /// Initialization and dump restore use snap: their first frame is the
- /// selected theme, never an animation from a compiled-in default.
- pub fn snap(a: *Self, value: Value) void {
- a.* = init(value);
- }
- };
-}
-
-/// `Transition`'s interface with the animation taken OUT: a value that is only ever the one it was
-/// last set to.
-///
-/// This exists so that a build which never fades does not carry the machinery for fading. A runtime
-/// flag around the same `Transition` cannot achieve that - the endpoints stay in the struct and
-/// `Value.interpolate` stays in the binary, reachable and therefore emitted. Selecting a different
-/// type at comptime is what makes the interpolator genuinely unreachable, and on a target whose whole
-/// display is a 115200-baud serial line, absent code and unspent frames are the same saving twice.
-///
-/// Every method here is the trivial one, and `retarget` is deliberately `snap` rather than an error:
-/// callers ask for a new palette and get it, on the next frame, in one step. Nothing about the
-/// interface says how many frames the arrival takes.
-pub fn Immediate(comptime Value: type) type {
- return struct {
- const Self = @This();
-
- displayed: Value,
-
- pub fn init(value: Value) Self {
- return .{ .displayed = value };
- }
-
- pub fn isActive(_: *const Self) bool {
- return false;
- }
-
- pub fn retarget(a: *Self, target: Value) void {
- a.displayed = target;
- }
-
- pub fn advance(_: *Self) void {}
-
- pub fn snap(a: *Self, value: Value) void {
- a.displayed = value;
- }
- };
-}
-
-/// Linear RGB interpolation with nearest-integer rounding. The weighted-sum
-/// form stays unsigned for both rising and falling channels.
-pub fn interpolateRgb(from: [3]u8, to: [3]u8, step: u16, steps: u16) [3]u8 {
- if (step == 0) return from;
- if (step >= steps) return to;
- var out: [3]u8 = undefined;
- for (&out, from, to) |*dst, a, b| {
- const numerator = @as(u32, a) * (steps - step) + @as(u32, b) * step;
- dst.* = @intCast((numerator + steps / 2) / steps);
- }
- return out;
-}
-
-const TestColor = struct {
- rgb: [3]u8,
-
- pub fn interpolate(from: TestColor, to: TestColor, step: u16, steps: u16) TestColor {
- return .{ .rgb = interpolateRgb(from.rgb, to.rgb, step, steps) };
- }
-};
-
-// The substitute has to be interchangeable, and the property that matters is the one a caller could
-// otherwise get wrong: it must arrive at the SAME palette a completed fade arrives at. A fade whose
-// endpoint differed by a rounding step would make the build option a visible change of colors rather
-// than a change of how long they take.
-test "Immediate lands where a completed Transition lands" {
- const from: TestColor = .{ .rgb = .{ 240, 3, 90 } };
- const to: TestColor = .{ .rgb = .{ 5, 222, 90 } };
-
- var faded = Transition(TestColor).init(from);
- faded.retarget(to);
- for (0..transition_steps) |_| faded.advance();
-
- var instant = Immediate(TestColor).init(from);
- try std.testing.expect(!instant.isActive());
- instant.retarget(to);
- try std.testing.expectEqual(faded.displayed, instant.displayed);
-
- // Never active, so a frontend that renders only while something is animating stops immediately
- // rather than spending ten frames discovering there is nothing to draw.
- try std.testing.expect(!instant.isActive());
- instant.advance();
- try std.testing.expectEqual(to, instant.displayed);
-
- instant.snap(from);
- try std.testing.expectEqual(from, instant.displayed);
-}
-
-test "fixed-step interpolation has exact monotonic endpoints" {
- const Tween = Transition(TestColor);
- const from: TestColor = .{ .rgb = .{ 240, 3, 90 } };
- const to: TestColor = .{ .rgb = .{ 5, 222, 90 } };
- var tween = Tween.init(from);
- tween.retarget(to);
- try std.testing.expectEqual(from, tween.displayed);
-
- var previous = tween.displayed;
- for (0..transition_steps) |_| {
- tween.advance();
- try std.testing.expect(tween.displayed.rgb[0] <= previous.rgb[0]);
- try std.testing.expect(tween.displayed.rgb[1] >= previous.rgb[1]);
- try std.testing.expectEqual(@as(u8, 90), tween.displayed.rgb[2]);
- previous = tween.displayed;
- }
- try std.testing.expect(!tween.isActive());
- try std.testing.expectEqual(to, tween.displayed);
- tween.advance();
- try std.testing.expectEqual(to, tween.displayed);
-}
-
-test "retarget starts at the currently displayed value" {
- const Tween = Transition(TestColor);
- const first: TestColor = .{ .rgb = .{ 0, 40, 200 } };
- const second: TestColor = .{ .rgb = .{ 200, 140, 0 } };
- const third: TestColor = .{ .rgb = .{ 20, 10, 250 } };
- var tween = Tween.init(first);
- tween.retarget(second);
- tween.advance();
- tween.advance();
- tween.advance();
- const on_screen = tween.displayed;
-
- tween.retarget(third);
- try std.testing.expectEqual(on_screen, tween.from);
- try std.testing.expectEqual(on_screen, tween.displayed);
- try std.testing.expect(tween.isActive());
- for (0..transition_steps) |_| tween.advance();
- try std.testing.expectEqual(third, tween.displayed);
-}