THE TEST PATTERN: cap7's bench scene recovered exactly (137 triangles)
tri_recover.py parses each payload's stride-0x10 edge groups {scale,A,B,C}
(GOODEQNS edgeize output), solves the three edge equations pairwise into true
vertices, and fills the triangles. cap7's bench = a triangulated calibration
grid + a multi-part test model + triangle strips, in global screen coords --
the image Division's engineers used to validate VelociRender boards, exact to
the compiled coefficients. The earlier 9x5 patch was one corner of this scene.
Readout gains §06 (test pattern) with trek/klng scenes moving to §07. The demo
captures' streak/point payloads use a different layout (parser refinement
pending) -- the bench is pure triangles and recovers perfectly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,112 @@
|
||||
"""EXACT scene recovery: payloads = triangles as 3 edge equations {scale,A,B,C} in
|
||||
stride-0x10 groups. Solve pairwise intersections -> true vertices; auto-detect
|
||||
tile-local vs global coords; fill triangles. Usage: tri_recover.py <dump.pkl> <out>"""
|
||||
import sys, pickle, struct, collections
|
||||
S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad'
|
||||
dump = sys.argv[1]; outb = sys.argv[2]
|
||||
mem = pickle.load(open(S + '\\' + dump, 'rb'))['mem']
|
||||
|
||||
def asf(w):
|
||||
return struct.unpack('<f', struct.pack('<I', w & 0xffffffff))[0]
|
||||
def isf(w):
|
||||
e = (w >> 23) & 0xff
|
||||
return 1 < e < 254 and 1e-7 < abs(asf(w)) < 1e7
|
||||
|
||||
# chains with tile ids
|
||||
regions = []
|
||||
for page in sorted(set(a & ~0xfff for a in mem if 0x0801e000 <= a < 0x08030000)):
|
||||
sends = []; tile = None
|
||||
a = page
|
||||
while a < page + 0x800:
|
||||
w0 = mem.get(a); w1 = mem.get(a + 4)
|
||||
if w0 is None or w1 is None:
|
||||
a += 8; continue
|
||||
c = (w1 >> 28) & 0xf
|
||||
if c in (1, 9) and w0 >= 0x08030000:
|
||||
sends.append((w0, w1 & 0x7f))
|
||||
elif c == 2:
|
||||
tile = w0
|
||||
a += 8
|
||||
if sends:
|
||||
regions.append((page, tile, sends))
|
||||
|
||||
def parse_edges(addr, size):
|
||||
"""Find stride-0x10 groups {scale, A, B, C}: scale small (~1e-3..1e-1), A/B/C larger."""
|
||||
edges = []
|
||||
off = 0
|
||||
while off + 0x10 <= size * 4 + 4:
|
||||
ws = [mem.get(addr + off + k) for k in (0, 4, 8, 12)]
|
||||
if all(w is not None and isf(w) for w in ws):
|
||||
s, A, B, C = (asf(w) for w in ws)
|
||||
if 1e-4 < abs(s) < 0.1 and (abs(A) > 0.5 or abs(B) > 0.5):
|
||||
edges.append((A, B, C))
|
||||
off += 0x10
|
||||
continue
|
||||
off += 4
|
||||
return edges
|
||||
|
||||
def isect(e1, e2):
|
||||
(a1, b1, c1), (a2, b2, c2) = e1, e2
|
||||
d = a1 * b2 - a2 * b1
|
||||
if abs(d) < 1e-9: return None
|
||||
return ((-c1 * b2 + c2 * b1) / d, (-a1 * c2 + a2 * c1) / d)
|
||||
|
||||
tris = [] # (tile, [(x,y)x3])
|
||||
for page, tile, sends in regions:
|
||||
for addr, size in sends:
|
||||
edges = parse_edges(addr, size)
|
||||
# group consecutive edge triples into triangles
|
||||
for k in range(0, len(edges) - 2, 3):
|
||||
e = edges[k:k+3]
|
||||
pts = [isect(e[0], e[1]), isect(e[1], e[2]), isect(e[2], e[0])]
|
||||
if all(p is not None for p in pts):
|
||||
tris.append((tile, pts))
|
||||
|
||||
xs = [p[0] for _, ps in tris for p in ps]
|
||||
ys = [p[1] for _, ps in tris for p in ps]
|
||||
if not xs:
|
||||
print("no triangles solved"); sys.exit()
|
||||
print("triangles: %d vertex range x[%.1f..%.1f] y[%.1f..%.1f]" %
|
||||
(len(tris), min(xs), max(xs), min(ys), max(ys)))
|
||||
# auto-detect: if 95% of coords within [−16, 160] treat as tile-local
|
||||
loc = sum(1 for v in xs + ys if -16 <= v <= 160) / len(xs + ys)
|
||||
tile_local = loc > 0.9
|
||||
print("tile-local coords: %s (%.0f%% in range)" % (tile_local, loc * 100))
|
||||
|
||||
W, H = 832, 512
|
||||
img = [[(6, 9, 14) for _ in range(W)] for _ in range(H)]
|
||||
PAL = [(65,255,142),(95,208,255),(255,176,32),(255,90,106),(234,255,239),
|
||||
(160,120,255),(200,200,90),(90,200,160),(255,140,110),(120,180,230)]
|
||||
def fill_tri(pts, col):
|
||||
xs_ = [p[0] for p in pts]; ys_ = [p[1] for p in pts]
|
||||
x0, x1 = max(0, int(min(xs_))), min(W - 1, int(max(xs_)) + 1)
|
||||
y0, y1 = max(0, int(min(ys_))), min(H - 1, int(max(ys_)) + 1)
|
||||
if x1 - x0 > 500 or y1 - y0 > 500: return # reject degenerate giants
|
||||
(ax, ay), (bx, by), (cx, cy) = pts
|
||||
den = (bx - ax) * (cy - ay) - (cx - ax) * (by - ay)
|
||||
if abs(den) < 1e-9: return
|
||||
for y in range(y0, y1 + 1):
|
||||
for x in range(x0, x1 + 1):
|
||||
w0 = ((bx - x) * (cy - y) - (cx - x) * (by - y)) / den
|
||||
w1 = ((cx - x) * (ay - y) - (ax - x) * (cy - y)) / den
|
||||
w2 = 1 - w0 - w1
|
||||
if w0 >= -0.02 and w1 >= -0.02 and w2 >= -0.02:
|
||||
r0, g0, b0 = img[y][x]
|
||||
img[y][x] = (min(255, r0 + col[0] // 3), min(255, g0 + col[1] // 3),
|
||||
min(255, b0 + col[2] // 3))
|
||||
drawn = 0
|
||||
for i, (tile, pts) in enumerate(tris):
|
||||
if tile_local and tile is not None:
|
||||
ox = (tile & 0x1f) * 64; oy = ((tile >> 5) & 0x1f) * 128
|
||||
pts = [(p[0] + ox, p[1] + oy) for p in pts]
|
||||
fill_tri(pts, PAL[i % len(PAL)])
|
||||
drawn += 1
|
||||
print("drawn:", drawn)
|
||||
buf = bytearray()
|
||||
for row in img:
|
||||
for r, g, b in row:
|
||||
buf += bytes((r, g, b))
|
||||
open(S + '\\' + outb + '.ppm', 'wb').write(b'P6\n%d %d\n255\n' % (W, H) + bytes(buf))
|
||||
from PIL import Image
|
||||
Image.open(S + '\\' + outb + '.ppm').save(S + '\\' + outb + '.png')
|
||||
print("wrote %s.png" % outb)
|
||||
Reference in New Issue
Block a user