feat(ludic.lab): the visual lab as a package - a scene on a plate, never a world
A scene is an entry in the open registry LabScenes, shown on a plate: a flat lit disc, the package's own small sky with the sun at one hour, and a frame clock. Headless each camera (lab_shot, lab_shot_at) settles and is written as build/lab/<scene>/<shot>.png (a stored PNG, written here); in a window N and P step through them. tools/lab/run.sh and sheet.py make the contact sheet; tools/lab/plate_build.py makes plate/. ludic.render3d gains r3d_plate_mode (no terrain, grass or water is made) and r3d_sky_path. quit() in a program with no frame loop links. The example takes ~120 MB resident, 417 MiB peak footprint. Reseed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
cc930a114f
commit
b401f7acc1
35 changed files with 4184 additions and 2986 deletions
29
tools/lab/sheet.py
Executable file
29
tools/lab/sheet.py
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
"""sheet.py DIR [DIR ...] - a contact sheet of a lab scene's shots: DIR/sheet.png, the PNGs in DIR
|
||||
(sheet.png itself excepted) in name order, two to a row at 640 wide, each captioned with its name."""
|
||||
import glob, os, sys
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
def sheet(out):
|
||||
paths = [p for p in sorted(glob.glob(os.path.join(out, '*.png'))) if os.path.basename(p) != 'sheet.png']
|
||||
if not paths:
|
||||
print(f'lab: {out}: no shots')
|
||||
return 1
|
||||
ims = [Image.open(p).convert('RGB') for p in paths]
|
||||
w = 640
|
||||
h = w * ims[0].height // ims[0].width
|
||||
cols = 2 if len(ims) > 1 else 1
|
||||
rows = (len(ims) + cols - 1) // cols
|
||||
s = Image.new('RGB', (w * cols, h * rows), (0, 0, 0))
|
||||
d = ImageDraw.Draw(s)
|
||||
for i, (p, im) in enumerate(zip(paths, ims)):
|
||||
x, y = (i % cols) * w, (i // cols) * h
|
||||
s.paste(im.resize((w, h)), (x, y))
|
||||
d.rectangle((x, y, x + w, y + 18), fill=(0, 0, 0))
|
||||
d.text((x + 6, y + 3), os.path.splitext(os.path.basename(p))[0], fill=(255, 255, 255))
|
||||
s.save(os.path.join(out, 'sheet.png'))
|
||||
print(f'lab: {out}: {len(ims)} shots, sheet.png')
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(max([sheet(d) for d in sys.argv[1:]] or [2]))
|
||||
Loading…
Add table
Add a link
Reference in a new issue