diff options
Diffstat (limited to 'src/draw.zig')
| -rw-r--r-- | src/draw.zig | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/src/draw.zig b/src/draw.zig index 1f30a40b..9df76cfd 100644 --- a/src/draw.zig +++ b/src/draw.zig @@ -134,11 +134,12 @@ pub fn place(p: *Pardes, s: *Surface) void { // snaps (it has no in-between), and so does nothing that shows it. const shown = lift != .off or inactiveDim(p) != 0; const motion = animation.Motion.of(if (shown and builtins.capabilities.lift) p.settings.motion else .off); - spring.retarget(if (id == p.active) 1 else 0, p.now_ns, motion); + // A flavour exaggerates what is only a few pixels (Motion.gain). + spring.retarget(if (id == p.active) animation.Motion.of(p.settings.motion).gain else 0, p.now_ns, motion); _ = spring.step(p.now_ns); } for (s.regions[0..s.nregions]) |*region| switch (region.kind) { - .grip, .tag, .body, .rail => region.lift = if (lift != .off) p.lifts[region.owner].value(p.now_ns) else 0, + .grip, .tag, .body, .rail => region.lift = if (lift != .off) std.math.clamp(p.lifts[region.owner].value(p.now_ns), -1, 2) else 0, .notice => region.lift = if (lift != .off) 1 else 0, else => {}, }; @@ -416,7 +417,8 @@ pub fn render(p: *Pardes, arena: std.mem.Allocator) !*Surface { for (p.panes, p.lifts, 0..) |slot, spring, id| { if (slot == null or id == p.active) continue; - const amount = @as(f32, @floatFromInt(inactiveDim(p))) / 100 * std.math.clamp(1 - spring.value(p.now_ns), 0, 1); + const focus = spring.value(p.now_ns) / animation.Motion.of(p.settings.motion).gain; + const amount = @as(f32, @floatFromInt(inactiveDim(p))) / 100 * std.math.clamp(1 - focus, 0, 1); if (amount > 0.001) recede(p, s, id, amount); } @@ -1604,6 +1606,46 @@ test "with no lift and no dim a focus switch asks for no frames" { try std.testing.expect(!p.continuous()); } +test "each Motion flavour reaches the lift as its own trajectory" { + // A grid snaps; the flavours are a pixel shell's. + if (!builtins.capabilities.lift) return error.SkipZigTest; + const p = try Pardes.init(std.testing.allocator, .{ .cols = 80, .rows = 24 }); + defer p.deinit(); + _ = try p.setTestFile("text\n"); + p.newScratchBelow(0); + var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); + defer arena.deinit(); + p.settings.lift = .shadow; + var peaks: [5]f32 = undefined; + const ms = std.time.ns_per_ms; + var start: u64 = std.time.ns_per_s; + for (std.enums.values(animation.Motion.Flavour), &peaks) |flavour, *peak| { + p.settings.motion = flavour; + p.active = 1; + p.advance(start); + _ = arena.reset(.retain_capacity); + _ = try p.render(arena.allocator()); + start += 3 * std.time.ns_per_s; + p.advance(start); + _ = try p.render(arena.allocator()); + p.active = 0; + peak.* = 0; + var t: u64 = 0; + while (t <= 1500 * ms) : (t += 4 * ms) { + p.advance(start + t); + _ = arena.reset(.retain_capacity); + const s = try p.render(arena.allocator()); + const lift = for (s.regionList()) |region| { + if (region.kind == .grip and region.owner == 0) break region.lift; + } else unreachable; + peak.* = @max(peak.*, lift); + } + start += 3 * std.time.ns_per_s; + } + // Bouncy and playful pass their (exaggerated) mark; smooth never does. + try std.testing.expect(peaks[2] <= 1.001 and peaks[3] > 1.25 and peaks[4] > peaks[3]); +} + test "a monkey over notices, prompts, tags and tiny panes at a gui's metrics never panics" { // The snapshot harness runs the tty, whose tagline pitch is the body's, // so it never reaches what a narrower tagline does to a notice's band: |
