Files
llamamod/tools/floorre/explain_t1l4m002.py
T

120 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Usage: python3 tools/floorre/explain_t1l4m002.py tools/floorre OUT.png (WAD via env WAD, default t1l4m002 path in floor.py; needs Pillow, numpy)
# Explainer: Kuzco floating next to the crate in t1l4m002 (builds on floorre/plot.py)
import sys, math
sys.path.insert(0, sys.argv[1]) # floorre dir (floor.py, corners.py)
OUT = sys.argv[2]
sys.argv = sys.argv[:1]
from floor import * # mp, trak, floor_test
from corners import corners
from PIL import Image, ImageDraw, ImageFont
SS = 2 # supersampling
F = lambda n, b=False: ImageFont.truetype('DejaVuSans-Bold.ttf' if b else 'DejaVuSans.ttf', n * SS)
K = (57.204, 2.474, 43.733)
T = 443
TX, TY, TZ, ROT, MI, _ = mp['A'][T]
recs = [r for g in trak[MI].coll for r in g]
def xf(B):
rot = B[1]; tx, ty, tz = B[3] / 4096, B[4] / 4096, B[5] / 4096
a = -rot / 4096 * 2 * math.pi; s, c = math.sin(a), math.cos(a)
return lambda p: (tx + c * p[0] - s * p[2], ty + p[1], tz + s * p[0] + c * p[2])
RED, BLUE, MAG, PINK = (215, 25, 25), (20, 90, 230), (210, 0, 210), (255, 70, 150)
def panel(X0, Z0, span, size, detail):
S = size / span
img = Image.new('RGB', (size * SS, size * SS), 'white'); d = ImageDraw.Draw(img, 'RGBA')
P = lambda x, z: ((x - X0) * S * SS, (size - (z - Z0) * S) * SS)
# rendered terrain coloured by height (like the agent's plot)
for t in range(mp['N']):
B = mp['B'][t]
if not (X0 - 2 <= B[3] / 4096 <= X0 + span + 2 and Z0 - 2 <= B[5] / 4096 <= Z0 + span + 2): continue
m = trak[mp['midx'][t]]; f = xf(B)
for fc in sorted(m.faces, key=lambda fc: sum(m.verts[v][1] for v in fc['v'])):
Q = [f((m.verts[v][0], m.verts[v][1], -m.verts[v][2])) for v in fc['v']]
h = sum(p[1] for p in Q) / len(Q); g = int(max(0, min(255, (h - 1.5) / 2 * 255)))
d.polygon([P(p[0], p[2]) for p in Q], fill=(g, 200 - g // 2, 255 - g, 255),
outline=(130, 130, 130) if not detail else None)
# cell grid
for k in range(int(X0) - 1, int(X0 + span) + 2):
d.line([P(k, Z0 - 1), P(k, Z0 + span + 1)], fill=(0, 140, 0), width=SS * (2 if detail else 1))
for k in range(int(Z0) - 1, int(Z0 + span) + 2):
d.line([P(X0 - 1, k), P(X0 + span + 1, k)], fill=(0, 140, 0), width=SS * (2 if detail else 1))
return img, d, P, S
def collision(d, P, labels, sliver=None, fsz=12):
f = xf(mp['B'][T])
if sliver:
for (x, z, w, i) in sliver:
a, b = P(x, z), P(x + w, z - w)
d.rectangle([a[0], a[1], b[0], b[1]], fill=PINK + (170,))
for i, r in enumerate(recs):
C = [f(p) for p in corners(r)]
col = RED if i in (11, 15) else BLUE if i == 2 else (20, 20, 20)
wid = 3 if i in (2, 11, 15) else 1
pts = [P(p[0], p[2]) for p in C]
if i in (11, 15): d.polygon(pts, fill=RED + (70,))
d.line(pts + pts[:1], fill=col, width=wid * SS)
if labels:
cx = sum(p[0] for p in pts) / len(pts); cy = sum(p[1] for p in pts) / len(pts)
lim = d.im.size
if not (10 * SS < cx < lim[0] - 10 * SS and 10 * SS < cy < lim[1] - (80 if labels == 'zoom' else 10) * SS):
continue
txt = f'poly {i} · mat {r[1]}' if labels == 'zoom' and i in (2, 11, 15) else str(i)
d.text((cx, cy), txt, fill=col, font=F(fsz, i in (2, 11, 15)), anchor='mm',
stroke_width=SS * 2, stroke_fill='white')
def sliver_cells(x0, z0, x1, z1, step):
out = []; tx, ty, tz = int(round(TX * 4096)), int(round(TY * 4096)), int(round(-TZ * 4096))
z = z0
while z < z1:
x = x0
while x < x1:
if int(x) == 57 and int(z) == 43:
_, res = floor_test(int(round(x * 4096)), int(round(2.27 * 4096)), int(round(z * 4096)), tx, ty, tz, ROT, recs)
best = None
for i, i7, ok, vals, tol, Y in res:
if ok and i7 >= -0x80 and (best is None or i7 < best[0]): best = (i7, i)
if best and best[1] in (11, 15): out.append((x, z, step, best[1]))
x += step
z += step
return out
W = 1500
# --- left: overview (3 x 3 cells) ---
ov, d, P, S = panel(56.0, 42.0, 3.0, 640, False)
collision(d, P, 'all', fsz=11)
for t in range(mp['N']):
B = mp['B'][t]; x, z = B[3] / 4096, B[5] / 4096
if 56 <= x <= 59 and 42 <= z <= 45:
d.text(P(x - 0.45, z - 0.38), f'tile {t}', fill=(110, 0, 140), font=F(12, True), stroke_width=SS * 2, stroke_fill='white')
k = P(K[0], K[2]); d.ellipse([k[0] - 7 * SS, k[1] - 7 * SS, k[0] + 7 * SS, k[1] + 7 * SS], fill=MAG, outline='black', width=SS)
ZX0, ZZ0, ZSP = 57.06, 43.16, 0.76
a, b = P(ZX0, ZZ0 + ZSP), P(ZX0 + ZSP, ZZ0)
d.rectangle([a, b], outline=(255, 140, 0), width=3 * SS)
ov = ov.resize((640, 640), Image.LANCZOS)
# --- right: zoom on the crate's side with both needles ---
zm, d, P, S = panel(ZX0, ZZ0, ZSP, 820, True)
collision(d, P, 'zoom', sliver=sliver_cells(ZX0, ZZ0, ZX0 + ZSP, ZZ0 + ZSP, 0.0012), fsz=15)
k = P(K[0], K[2]); r = 9 * SS
d.ellipse([k[0] - r, k[1] - r, k[0] + r, k[1] + r], fill=MAG, outline='black', width=2 * SS)
d.text((k[0], k[1] + 30 * SS), 'Kuzco\n57.204 / 43.733\n(measured live)', fill=MAG, font=F(15, True), anchor='ma',
align='center', stroke_width=SS * 2, stroke_fill='white')
tip = P(57.315, 43.729)
y = tip[1] - 34 * SS
d.line([(k[0], y), (tip[0], y)], fill='black', width=2 * SS)
for xq in (k[0], tip[0]): d.line([(xq, y - 8 * SS), (xq, y + 8 * SS)], fill='black', width=2 * SS)
d.text(((k[0] + tip[0]) / 2, y - 6 * SS), '0.11 outside poly 15', fill='black', font=F(14, True), anchor='md',
stroke_width=SS * 2, stroke_fill='white')
d.text((P(57.38, 43.80)), 'needle tips 13.5°', fill=RED, font=F(14, True), stroke_width=SS * 2, stroke_fill='white')
d.text((P(ZX0 + 0.01, ZZ0 + 0.035)), 'pink = allowance: where the engine still takes the needle as floor\n(edge tolerance ≈ 0.02 → ≈ 0.19 long, 0.01 wide at a 13.5° tip)',
fill=(190, 20, 110), font=F(14, True), stroke_width=SS * 2, stroke_fill='white')
zm = zm.resize((820, 820), Image.LANCZOS)
out = Image.new('RGB', (20 + 640 + 20 + 820 + 20, 820 + 40), 'white')
d = ImageDraw.Draw(out)
out.paste(ov, (20, 20 + 90)); out.paste(zm, (680, 20))
d.text((20, 20 + 60), 'Overview (3 × 3 cells), orange box = zoom →', fill='black', font=ImageFont.truetype('DejaVuSans-Bold.ttf', 17))
out.save(OUT); print('saved', OUT, out.size)