port: add --leaf-time, and the title's residual is the sweep phase (~400 units, not 357.7)

The Decoder's refined sweep fit had never been testable: verify-capture passed
it as a whole-screen --time that pose_at discarded, and asking for it honestly
poses past the title's group end. --leaf-time separates the leaf's clock from
the screen's.

Controls: the renderer is deterministic (3 runs bit-identical) and the sweeps
move 0.40% of the frame between phases, so the comparison can see them.

Sweeping the full 600-unit span gives a sharp basin at 390-415 units (0.0124%)
against 0.2532% at t=357.7 -- 20x. So the title's 0.21% residual is the sweep
phase, not structure: at the fitted phase it matches the capture as well as the
splashes do.

NOT adopted: the port loops the leaf freely and re-posing the harness to the
fitted value would be tuning until they match. Filed instead, with the question
of whether 357.7 and this are even the same quantity.

Also verified last iteration's settle-window change was surgical: only
press_start and its twin moved, 14 screens unchanged including title's
Decoder-confirmed [160,236]. Settle-window ties exist on 4 screens but all sit
under the 30-unit bar, so the arbitrary tie-break never reaches the runtime.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-30 00:46:25 +00:00
parent 3c71962698
commit 96fe0eac51
5 changed files with 110 additions and 3 deletions

View File

@@ -239,6 +239,12 @@ func _ready() -> void:
name, view.screen["elements"].size(), view.screen["paint_order"].size(),
design[0], design[1], settle, settle / view.units_per_second])
# `--leaf-time=<seconds>` places the LEAF alone, leaving the screen settled.
# See `ScreenView.leaf_time_units`.
if args.has("leaf-time"):
view.leaf_time_units = float(args["leaf-time"]) * view.units_per_second
view.queue_redraw()
if args.has("time"):
_frozen = true
# An explicit instant beats the settle instant -- see `ScreenView.frozen`.

View File

@@ -113,6 +113,19 @@ var draw_leaf_for: Array = []
## Whether this screen replays a leaf's group. See `authored/rendering.json`.
var loop_leaf := false
## Pin the LEAF's phase independently of the screen's pose, in units. -1 = off.
##
## Built because a measured value could not be tested. The Decoder's refined fit
## for the `ptloop` sweeps is t=357.7 units, and `verify-capture` passed it as
## `--time=5.9617` -- which poses the WHOLE SCREEN there. The title's own group
## ends at t=269, so that fades everything out and scores 30.97 % against the
## capture. The instant was only ever about the sweeps, whose leaf runs to t=600.
##
## So the fit was untestable: the only way to ask for it also destroyed the rest
## of the frame. This separates the two clocks -- the screen sits at its settled
## pose, the leaf is placed at whatever phase is being tested.
var leaf_time_units: float = -1.0
## While true the screen holds at `rest` and never plays its exit. The
## sequencer clears it to send the screen away.
var holding: bool = true
@@ -509,14 +522,14 @@ func _draw_leaf(element: Dictionary) -> bool:
# cannot tell "loops at 600" from "runs once for 600 and stops".
var was := holding
holding = false
var t := time_units
var t := leaf_time_units if leaf_time_units >= 0.0 else time_units
if loop_leaf:
var span := 0.0
for k: Dictionary in fe.get("keyframes", []):
if k.has("t"):
span = maxf(span, float(k["t"]))
if span > 0.0:
t = fposmod(time_units, span)
t = fposmod(t, span)
var pose := pose_at(fe, t)
holding = was
# 🔴 A SCALE-0 LEAF MUST NOT CLAIM THE DRAW. The Decoder hit this in its own