Last iteration I asserted F5 was unaffected by the truncating-reader bug because it compared like with like. Asserting that is the move that produced the bug, so this measures it. The new reader sees 9.7 quads/frame vs ~7.5. Scalar that needs no element identification: quads mid-ramp (0<a<250) per frame goes 6,4,4,4,2,1,4,3 -> 0 at f436, while the control never reaches 0 anywhere in 48 frames of build-in. One frame with nothing part-way through a ramp is the cut. Bonus the old reader could not show: both sweeps enter at f436-438 at their declared opening alphas -- pteff03 at 255, pteff03a at 1,2,3,4,6,11,17 from its declared 0. Refutation attempt on the port's "clock jumps to 236.0": tried and failed. My bound is [100,238), which contains 236 -- consistent, not independent confirmation. Adds tools/re-capture/read_draws.py so the truncating regex is not re-rolled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
68 lines
2.7 KiB
Python
Executable File
68 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Read a Xenia UI draw log -- EVERY quad, not the first vertex of each draw.
|
|
|
|
⚠️ THIS EXISTS BECAUSE THE OBVIOUS READER IS WRONG. A draw line carries
|
|
`indices=N` vertices on ONE `v:` line, and N is routinely 8 -- two quads batched
|
|
into a single draw. A reader that takes the first `v: [...]` match per line sees
|
|
one of them and silently drops the rest.
|
|
|
|
That produced a clean, complete-looking negative twice in this corpus
|
|
(`f6-unit5-pteff03a-never-drawn.md`, `f6-unit6-...`), both refuted by
|
|
`f6-unit11-pteff03a-IS-drawn.md`. `REFUTED.md` L170 had already recorded a draw
|
|
carrying two rotated parallelograms. Use this reader; do not re-roll the regex.
|
|
|
|
from read_draws import read
|
|
frames = read(path) # {frame: [Quad, ...]} Quad = (page, verts, alpha, blend, cx)
|
|
"""
|
|
import re, collections
|
|
|
|
_F = re.compile(r'^--- frame (\d+) ')
|
|
_TEX = re.compile(r'tex\[base=(0x[0-9A-F]+) (\d+)x(\d+) fmt=(\d+)(?: h=([0-9A-F]+))?\]')
|
|
_IDX = re.compile(r'indices=(\d+)')
|
|
_V = re.compile(r'\[(-?\d+\.\d+),(-?\d+\.\d+),z=[-\d.]+,col=([0-9A-F]{8})\]')
|
|
|
|
class Quad(tuple):
|
|
__slots__ = ()
|
|
def __new__(cls, page, verts, alpha, blend, cx):
|
|
return tuple.__new__(cls, (page, verts, alpha, blend, cx))
|
|
page = property(lambda s: s[0])
|
|
verts = property(lambda s: s[1])
|
|
alpha = property(lambda s: s[2])
|
|
blend = property(lambda s: s[3])
|
|
cx = property(lambda s: s[4])
|
|
|
|
def read(path):
|
|
frames = collections.defaultdict(list)
|
|
frame = page = blend = None
|
|
idx = 0
|
|
for line in open(path, errors='replace'):
|
|
m = _F.match(line)
|
|
if m:
|
|
frame = int(m.group(1)); continue
|
|
if frame is None:
|
|
continue
|
|
mt = _TEX.search(line)
|
|
if mt:
|
|
page = mt.group(5) or mt.group(1)
|
|
mi = _IDX.search(line); idx = int(mi.group(1)) if mi else 0
|
|
mb = re.search(r'blend=(0x[0-9A-F]+)', line); blend = mb.group(1) if mb else None
|
|
continue
|
|
if page is not None and ' v: ' in line:
|
|
vs = _V.findall(line)
|
|
# every group of 4 vertices is one quad; a partial tail is dropped
|
|
for q in range(len(vs) // 4):
|
|
quad = vs[q*4:(q+1)*4]
|
|
cx = sum(float(v[0]) for v in quad) / 4
|
|
frames[frame].append(Quad(page, [(float(a), float(b)) for a, b, _ in quad],
|
|
int(quad[0][2][:2], 16), blend, round(cx, 3)))
|
|
page = None
|
|
return dict(frames)
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
fr = read(sys.argv[1])
|
|
ks = sorted(fr)
|
|
tot = sum(len(v) for v in fr.values())
|
|
print(f"{len(ks)} frames, {tot} quads, frames {ks[0]}..{ks[-1]}")
|
|
print(f"mean quads/frame {tot/len(ks):.2f}")
|