"""to_glb.py -- an .ldr frame as a .glb a browser can spin (Ivy, Oct 23 2026: "Can you make it so I can rotate and pan and
zoom the building legos?"). Parts are LDraw meshes (~/lego/ldraw.py, mm, z up), each in its LDraw colour.
    python3 to_glb.py runs/me1          # every frame_*.ldr in a run that has no .glb yet"""
import sys, os, glob
import numpy as np, trimesh
sys.path.insert(0, os.path.expanduser('~/projects/brick_reef/joins')); sys.path.insert(0, os.path.expanduser('~/lego'))
import kit; kit.ALLOW_ANY = True
from facts import read, mesh
RGB = {4: 'C91A09', 1: '0055BF', 14: 'F2CD37', 2: '237841', 25: 'FE8A18', 5: 'C870A0', 22: '81007B', 71: 'A0A5A9', 27: 'BBE90B',
       73: '5A93DB', 72: '6C6E68', 19: 'E4CD9E', 320: '720E0F', 272: '0A3463', 70: '582A12', 0: '1B2A34', 15: 'F4F4F4', 16: 'A0A5A9'}
def view_matrix(meshes):
    """our frame (mm, z up) -> the page's 3D frame (y up, build centred). Saved next to the .glb so the page can place ghost
    pieces and turn a mouse hit back into a point in our frame."""
    R = trimesh.transformations.rotation_matrix(-np.pi / 2, [1, 0, 0])
    lo = np.min([m.bounds[0] for m in meshes], 0); hi = np.max([m.bounds[1] for m in meshes], 0)
    c = (R @ np.r_[(lo + hi) / 2, 1])[:3]; V = R.copy(); V[:3, 3] -= c; return V
def ldr_to_glb(ldr, out=None):
    """one named node per LDraw line ('k<line index>'), so a click on the page knows WHICH brick it hit (Ivy, Oct 23 2026:
    repaint on her turn by clicking a brick)"""
    import json
    out = out or ldr[:-4] + '.glb'; parts = []
    for j, (colour, name, T) in enumerate(read(ldr)):
        m = mesh(name).copy(); m.apply_transform(T)
        rgba = [int(RGB.get(colour, 'A0A5A9')[i:i + 2], 16) for i in (0, 2, 4)] + [255]
        m.visual = trimesh.visual.ColorVisuals(m, face_colors=np.tile(rgba, (len(m.faces), 1)).astype(np.uint8))
        parts.append((j, m))
    if not parts: return None
    V = view_matrix([m for _, m in parts]); sc = trimesh.Scene()
    for j, m in parts:
        m.apply_transform(V); sc.add_geometry(m, node_name=f'k{j}', geom_name=f'k{j}')
    sc.export(out); json.dump({'view': V.tolist()}, open(out[:-4] + '.view.json', 'w')); return out
def part_glb(name, out):
    """one piece, in OUR frame, for the page's ghost (the page multiplies it by the frame's view matrix)"""
    m = mesh(name).copy(); m.visual = trimesh.visual.ColorVisuals(m, face_colors=np.tile([120, 220, 140, 255], (len(m.faces), 1)).astype(np.uint8))
    m.export(out); return out
if __name__ == '__main__':
    for run in sys.argv[1:]:
        for l in sorted(glob.glob(os.path.join(run, 'frame_*.ldr'))):
            if not os.path.exists(l[:-4] + '.glb'): print(ldr_to_glb(l), flush=True)
