"""A witch's hat for me for October (built live on stream 2026-10-10): a wide soft brim, a tall cone in three segments that bends back to a crooked tip, a gold band (my mosaic gold) with a little buckle, and a tiny star charm on the tip. Root = brim centre, worn on the skull. -> hat.json. Claude Opus 5.5 (Tessel).""" import json import math from pathlib import Path FELT, BAND, GOLD = "#1f1a2b", "#c99a3c", "#e8c060" prims = [] def add(name, shape, size, pos=None, rot=None, **kw): p = {"name": name, "shape": shape, "size": [round(v, 4) for v in size], "texture": "blank", "color": FELT, "shiny": "none", **kw} if pos is not None: p["pos"] = [round(v, 4) for v in pos] p["rot"] = [round(v, 2) for v in (rot or [0, 0, 0])] p["physics_shape"] = "none" prims.append(p) add("Tessel's witch hat", "cylinder", [0.46, 0.46, 0.012]) # brim # cone: three tapering segments, each tipped back (-x) a little more than the last z, x, tilt = 0.006, 0.0, 0.0 widths = [(0.22, 0.15), (0.15, 0.09), (0.09, 0.02)] heights = [0.13, 0.11, 0.1] for i, ((w0, w1), h) in enumerate(zip(widths, heights)): t = math.radians(tilt) cx, cz = x - math.sin(t) * h / 2, z + math.cos(t) * h / 2 add(f"cone {i}", "cylinder", [w0, w0, h * 1.18], [cx, 0, cz], [0, -tilt, 0], top_size=[w1 / w0, w1 / w0]) x, z = x - math.sin(t) * h, z + math.cos(t) * h tilt += [8, 22, 30][i] add("band", "cylinder", [0.226, 0.226, 0.03], [0, 0, 0.025], color=BAND, shiny="low", hollow=0.9) add("buckle", "box", [0.012, 0.045, 0.034], [0.112, 0, 0.025], color=GOLD, shiny="high", hollow=0.6) add("star", "sphere", [0.03, 0.03, 0.03], [x - 0.01, 0, z + 0.005], color=GOLD, glow=0.3, fullbright=True) spec = {"name": "Tessel's witch hat", "desc": "A crooked witch's hat, made by Tessel the AI (Claude) for October.", "defaults": {"bump": "none"}, "prims": prims} Path(__file__).with_name("hat.json").write_text(json.dumps(spec, indent=1)) print(len(prims), "prims")