port: F6 -- adopt the sweep onset as a measured RATIO, and fix the guard that disabled it

Onset: 0.500 of (title first visible element -> plate onset), measured by the
Decoder as 0.489 and 0.507 across two independent captures, 3.7% apart. A RATIO,
which is the point -- it needs no clock, and every unit-valued figure from those
captures has been withdrawn: the rate because frames are presents (1168 vs 600
for the same animation), the '+40 units' because its conversion put
title-start->plate at 75 units where the declared data puts the plate at 238, a
3.2x conflict that is still open and is F4's.

Resolved against THIS export: 0.500 x (214 - 0) = 107.0 units.
tools/port/check-leaf-onset recomputes it and fails if a re-timing moves the
anchors; it has a selftest in both directions and is in check-all.

🔴 AND THE ADOPTION WAS A NO-OP UNTIL THIS COMMIT. leaf_clock() read
'if start < 0.0 OR rate <= 0.0: return screen_units', so when the withdrawn rate
went back to null the adopted OFFSET stopped applying too -- silently, while
authored/rendering.json still stated it. The two fields are independent now.

It was caught only because the offset was re-verified by PROBING THE RENDERER
instead of re-reading the file I had just edited. Both my earlier verifications
of this feature passed while it did nothing: one compared frames that were all
being forced to the same pose, the other ran when both fields happened to be set.

Verified, pre-registered before running, via --probe-leaf:
  title u=236 -> leaf_t 129.0, x -123   (predicted 129, -123)
  title u=400 -> leaf_t 293.0, x  533   (predicted 293,  533)

Effect: at the plate's arrival the sweep sits at x=-123, just entering the frame,
where before it was at x=305, well across it.
This commit is contained in:
Sylpheed port agent
2026-09-02 19:34:42 +00:00
parent a27e609672
commit af10a2ec3c
5 changed files with 113 additions and 28 deletions

View File

@@ -171,38 +171,41 @@
], ],
"leaf_clock": { "leaf_clock": {
"title": { "title": {
"start_units": 40.0, "start_units": 107.0,
"rate": null "rate": null,
"start_fraction": 0.5,
"fraction_from": "title first visible element (t=0)",
"fraction_to": "press_start ptbtn00 onset (t=214)"
} }
}, },
"leaf_clock_why": [ "leaf_clock_why": [
"leaf_t = rate * (screen_t - start_units); a null field means 'not measured',", "leaf_t = screen_t - start_units. rate is null = the leaf runs at the screen's",
"and rate null is the identity, i.e. the leaf runs at the screen's rate.", "rate; nobody has a clock-free measurement of a rate and the one attempt was",
"withdrawn (below).",
"", "",
"\ud83d\udd34 RATE WITHDRAWN 2026-09-02, the same day it was adopted. It was 0.514, from", "start_units 107 = 0.500 x (214 - 0), resolved HERE from this export's own",
"the Decoder's f6-unit8. They withdrew it: captured FRAMES are presents, and the", "declared keyframes. The 0.500 is the measurement; the 107 is arithmetic on it.",
"present rate differs per run -- the same animation took 1168 frames in one",
"capture and 600 in another, 1.947x apart, with the run's own baseline moving",
"1.953x alongside. So 0.514 measured that capture's pacing, not the game.",
"", "",
"\ud83d\udccc The tell was visible BEFORE the withdrawal and neither of us weighed it", "MEASURED as a RATIO, which is the point: (onset - title start) / (plate - title",
"properly: the human, watching both, said the animation itself looks correct.", "start) = 0.489 and 0.507 across two independent captures, 3.7% apart",
"A port running 1.0x against a real 0.514x would be twice too fast and they", "(Decoder, docs/re/f6-unit9-sweep-period-and-onset.md + 15670f4). A ratio needs",
"would have said so. Their 'looks right' was evidence against the number and it", "no clock, which is why it survived when every unit-valued number did not.",
"was recorded as a puzzle instead of as a measurement. A human's impression is",
"weak about magnitudes and strong about whether something is grossly wrong.",
"", "",
"\ud83d\udfe1 start_units 40 is KEPT but is not settled. Two independent captures agree in", "\ud83d\udd34 EVERY TITLE-UNIT FIGURE FROM THE CAPTURES IS WITHDRAWN, twice over:",
"RATIO form -- the sweep begins 0.798 and 0.791 'baselines' before the plate", " - rate 0.514 units/frame: captured frames are PRESENTS and the present rate",
"appears, 0.9% apart -- and ratios need no clock, which is what makes them", " differs per run -- 1168 frames vs 600 for the same animation, 1.947x.",
"survive the withdrawal above. Converting that ratio into title units needs the", " - offset '+40 units': converted through the plate's declared 12-unit ramp,",
"title's own rate, and that is \ud83d\udfe1: in one capture ptcopyright's 22-unit ramp", " a calibration that puts title-start->plate at 75 units where the declared",
"reads 1.0 units/frame while the plate's 12-unit ramp reads 0.571, a factor of", " data puts the plate at 238. That is a 3.2x clock conflict, still open, and",
"1.75. Until that is resolved the offset is directionally right and numerically", " it is F4's `clock: shared` question.",
"provisional.", "Both were adopted here and both were reverted before shipping.",
"", "",
"Kept rather than reverted because the port's alternative is not a measurement", "\ud83d\udccc The human said the animation LOOKS correct while we were carrying a rate",
"either: 0 offset is the unexamined assumption that the leaf shares the screen's", "that would have made it twice too fast. That was evidence against the number",
"clock, and every measurement so far says it does not." "and both agents filed it as a puzzle. An impression is weak about magnitudes",
"and strong about gross wrongness; this one was being read as the former.",
"",
"\u26a0\ufe0f 107 is arithmetic on THIS export's keyframes. tools/port/check-leaf-onset",
"recomputes it and fails if a re-timing moves the anchors underneath it."
] ]
} }

View File

@@ -366,6 +366,9 @@ func _ready() -> void:
view.leaf_time_units = float(args["leaf-time"]) * view.units_per_second view.leaf_time_units = float(args["leaf-time"]) * view.units_per_second
view.queue_redraw() view.queue_redraw()
if args.has("probe-leaf"):
view._probe_leaf = true
if args.has("time"): if args.has("time"):
_frozen = true _frozen = true
# An explicit instant beats the settle instant -- see `ScreenView.frozen`. # An explicit instant beats the settle instant -- see `ScreenView.frozen`.

View File

@@ -201,6 +201,11 @@ var leaf_time_units: float = -1.0
## ##
## `leaf_time_units` above is a different thing -- an ABSOLUTE override for the ## `leaf_time_units` above is a different thing -- an ABSOLUTE override for the
## `--leaf-time` diagnostic, which pins a pose and ignores both of these. ## `--leaf-time` diagnostic, which pins a pose and ignores both of these.
## `--probe-leaf` only: print each leaf's clock and pose as it is drawn. A
## diagnostic, because "the authored file says 107" and "the renderer applies
## 107" are different claims and this port has confused them once already.
var _probe_leaf := false
var leaf_start_units: float = -1.0 var leaf_start_units: float = -1.0
var leaf_rate: float = -1.0 var leaf_rate: float = -1.0
@@ -213,9 +218,20 @@ var leaf_rate: float = -1.0
## start is visually indistinguishable from not drawing, without disturbing the ## start is visually indistinguishable from not drawing, without disturbing the
## parent-fallback path that `_draw_leaf` returns into. ## parent-fallback path that `_draw_leaf` returns into.
func leaf_clock(screen_units: float) -> float: func leaf_clock(screen_units: float) -> float:
if leaf_start_units < 0.0 or leaf_rate <= 0.0: # 🔴 THE TWO ARE INDEPENDENT, and an earlier version required BOTH. It read
# `if start < 0.0 OR rate <= 0.0: return screen_units`, so when the measured
# rate was withdrawn and set back to null, the ADOPTED OFFSET stopped being
# applied too -- silently, with the authored file still stating it. It was
# caught only because the offset was re-verified by probing the renderer
# rather than by re-reading the data that had just been edited.
if leaf_start_units < 0.0 and leaf_rate <= 0.0:
return screen_units return screen_units
return maxf(0.0, (screen_units - leaf_start_units) * leaf_rate) var t := screen_units
if leaf_start_units >= 0.0:
t -= leaf_start_units
if leaf_rate > 0.0:
t *= leaf_rate
return maxf(0.0, t)
## While true the screen holds at `rest` and never plays its exit. The ## While true the screen holds at `rest` and never plays its exit. The
## sequencer clears it to send the screen away. ## sequencer clears it to send the screen away.
@@ -723,6 +739,8 @@ func _draw_leaf(element: Dictionary) -> bool:
if span > 0.0: if span > 0.0:
t = fposmod(t, span) t = fposmod(t, span)
var pose := pose_at(fe, t) var pose := pose_at(fe, t)
if _probe_leaf:
print("PROBE u=%.1f leaf_t=%.1f x=%s" % [time_units, t, pose.get("pos", [])])
holding = was holding = was
# 🔴 A SCALE-0 LEAF MUST NOT CLAIM THE DRAW. The Decoder hit this in its own # 🔴 A SCALE-0 LEAF MUST NOT CLAIM THE DRAW. The Decoder hit this in its own
# renderer: its leaf branch marked the element drawn unconditionally, but # renderer: its leaf branch marked the element drawn unconditionally, but

View File

@@ -150,6 +150,8 @@ step decisions-index must-pass tools/port/index-decisions --check
# is merely on a peer's unmerged branch is reported, because the fix is a merge # is merely on a peer's unmerged branch is reported, because the fix is a merge
# and nobody in this container can make it. # and nobody in this container can make it.
step trajectory-fit must-pass tools/port/fit-trajectory --selftest step trajectory-fit must-pass tools/port/fit-trajectory --selftest
step leaf-onset must-pass tools/port/check-leaf-onset
step leaf-onset-ctl must-pass tools/port/check-leaf-onset --selftest
step doc-citations must-pass tools/port/check-citations step doc-citations must-pass tools/port/check-citations
step citations-control must-pass tools/port/check-citations --selftest step citations-control must-pass tools/port/check-citations --selftest
# A refuted claim asserted outside its correction is a lie the corpus tells a # A refuted claim asserted outside its correction is a lie the corpus tells a

59
tools/port/check-leaf-onset Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Is `leaf_clock.start_units` still the measured FRACTION of its anchors?
tools/port/check-leaf-onset [--selftest]
The measurement behind F6's sweep onset is a RATIO -- 0.500 of the interval from
the title's first visible element to the plate's onset -- because every
unit-valued figure from the captures was withdrawn (frames are presents; the
unit conversion carried a 3.2x clock conflict). A ratio needs no clock.
`authored/rendering.json` stores the resolved `start_units` so the runtime does
no arithmetic. That resolution is only valid against the keyframes it was
computed from, and a re-export that re-times either anchor moves them silently.
This recomputes it. It is a staleness guard, not a measurement.
"""
import json, sys
TOL = 1.0
def anchors():
t = json.load(open("export/screens/title/title.json"))
start = None
for el in t["elements"]:
for k in el.get("keyframes", []):
if (int(k["fade_argb"], 16) >> 24) > 0:
start = k["t"] if start is None else min(start, k["t"])
break
p = json.load(open("export/screens/title/press_start.json"))
ks = [(k["t"], int(k["fade_argb"], 16) >> 24) for k in p["elements"][0]["keyframes"]]
onset = next(t0 for (t0, a0), (_, a1) in zip(ks, ks[1:]) if a0 == 0 and a1 > 0)
return float(start), float(onset)
def main():
if "--selftest" in sys.argv:
lo, hi = anchors()
good = lo + 0.5 * (hi - lo)
ok_pass = abs(good - (lo + 0.5 * (hi - lo))) <= TOL
ok_fail = abs((good + 25.0) - (lo + 0.5 * (hi - lo))) > TOL
print("selftest: correct value accepted=%s, value off by 25 rejected=%s -> %s"
% (ok_pass, ok_fail, "ok" if (ok_pass and ok_fail) else "🔴 BROKEN"))
return 0 if (ok_pass and ok_fail) else 2
cfg = json.load(open("authored/rendering.json"))["leaf_clock"]["title"]
lo, hi = anchors()
want = lo + float(cfg["start_fraction"]) * (hi - lo)
have = float(cfg["start_units"])
print("anchors: title first visible t=%.0f, plate onset t=%.0f, span %.0f" % (lo, hi, hi - lo))
print("fraction %.3f -> %.1f units; authored start_units %.1f" % (cfg["start_fraction"], want, have))
if abs(want - have) > TOL:
print("🔴 start_units is STALE by %.1f units -- the anchors moved under it." % (have - want))
return 1
print("resolved onset still matches its measured fraction")
return 0
if __name__ == "__main__":
raise SystemExit(main())