summaryrefslogtreecommitdiff
path: root/src/Presentation.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 07:41:35 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit411d0b6059192b75853e220e1242b694ae461a3e (patch)
tree50f8bb207f3cdda497bdd84658a2d3ea74b4bc2b /src/Presentation.zig
parentda920ca49e3f5d1649e811bbdc3ddba482c6aae4 (diff)
parent5526c68beec454cfe607a579723b97b4848e7d3a (diff)
downloadpardes-411d0b6059192b75853e220e1242b694ae461a3e.tar.gz
pardes-411d0b6059192b75853e220e1242b694ae461a3e.zip
Merge: 9P rounds 9-17 + render DPI, dark tints, G4-G10
Diffstat (limited to 'src/Presentation.zig')
-rw-r--r--src/Presentation.zig39
1 files changed, 38 insertions, 1 deletions
diff --git a/src/Presentation.zig b/src/Presentation.zig
index 15ed9ea6..5cec73e0 100644
--- a/src/Presentation.zig
+++ b/src/Presentation.zig
@@ -183,7 +183,7 @@ pub fn sync(self: *Presentation, p: *Pardes) void {
prior.?.from
else
old.box;
- const next = stamp(p, .{
+ var next = stamp(p, .{
.serial = pane.serial,
.pane = @intCast(id),
.phase = if (!same_lifetime or
@@ -192,6 +192,10 @@ pub fn sync(self: *Presentation, p: *Pardes) void {
.from = from,
.to = target,
});
+ // Retargeted mid-way from where it shows: it keeps its speed.
+ if (same_lifetime and shown != null) if (prior) |old_move| {
+ next.launch = carriedLaunch(old_move, next);
+ };
self.tracks[id] = next;
changed = true;
animated_change = true;
@@ -230,6 +234,23 @@ fn stamp(p: *const Pardes, track: Track) Track {
return stamped;
}
+/// A retargeted move's starting speed, in its own lengths per unit of
+/// progress, from the old move's speed now (cells per frame), along the
+/// new move's longest side; within four lengths per unit either way.
+fn carriedLaunch(old_move: Track, next: Track) f32 {
+ const v = old_move.velocity();
+ if (next.frames() <= 1) return 0;
+ const steps: f32 = @floatFromInt(next.frames() - 1);
+ const d: [4]f32 = .{ next.to.x - next.from.x, next.to.y - next.from.y, next.to.w - next.from.w, next.to.h - next.from.h };
+ const vs: [4]f32 = .{ v.x, v.y, v.w, v.h };
+ var axis: usize = 0;
+ for (d, 0..) |value, i| if (@abs(value) > @abs(d[axis])) {
+ axis = i;
+ };
+ if (@abs(d[axis]) < 1e-3) return 0;
+ return std.math.clamp(vs[axis] * steps / d[axis], -4, 4);
+}
+
fn appendClosing(self: *Presentation, track: Track) bool {
std.debug.assert(track.phase == .closing);
const baseline = self.previous_layout[track.pane] orelse return false;
@@ -784,3 +805,19 @@ test "presentation snapshots own compact body and tag rows across later edits" {
try std.testing.expectEqual(@as(u8, 'B'), state.previous_body_layers[0].cells[0].text[0]);
try std.testing.expectEqual(@as(u8, 'U'), state.previous_tag_layers[0].cells[0].text[0]);
}
+
+test "a move retargeted mid-way carries its speed into the new one" {
+ const smooth = @intFromEnum(animation.Motion.Flavour.smooth) + 1;
+ const first: Track = .{ .serial = 1, .phase = .moving, .effect = .slide, .motion = smooth, .frame = 4, .frame_count = 14, .from = .{ .x = 0, .y = 1, .w = 40, .h = 20 }, .to = .{ .x = 40, .y = 1, .w = 40, .h = 20 } };
+ const was = first.velocity();
+ try std.testing.expect(was.x > 0.5);
+ var next: Track = .{ .serial = 1, .phase = .moving, .effect = .slide, .motion = smooth, .frame_count = 14, .from = first.presented(), .to = .{ .x = 60, .y = 1, .w = 40, .h = 20 } };
+ next.launch = carriedLaunch(first, next);
+ try std.testing.expect(next.launch > 0);
+ // It sets off at the speed the old one had, not from a stop.
+ const now = next.velocity();
+ try std.testing.expectApproxEqRel(was.x, now.x, 0.15);
+ // And still lands exactly.
+ next.frame = 13;
+ try std.testing.expectEqual(next.to.x, next.presented().x);
+}