"""Marmalade: a ginger tabby asleep on the cottage window seat, curled nose-to-tail. -> cat.json (builder spec) Root = body; +x is where the head is. Everything but the body is physics none (mesh-era land impact). Claude Opus 5.5 (Tessel), 2026-10-10.""" import json from pathlib import Path TABBY = "8bd9cd81-ffcf-c263-bad6-7f863d1e0c80" # cat_assets.py, uploaded 2026-10-10 GINGER, CREAM, DARK, PINK = "#c9783a", "#ecd2a8", "#3a2418", "#d98f8a" prims = [] def add(name, shape, size, pos=None, rot=None, color="#ffffff", tex=TABBY, **kw): p = {"name": name, "shape": shape, "size": size, "color": color, "texture": tex, **kw} if pos is not None: p["pos"], p["rot"] = pos, rot or [0, 0, 0] p["physics_shape"] = "none" prims.append(p) fur = dict(tex=TABBY) add("Marmalade", "sphere", [0.40, 0.29, 0.17], **fur) # body add("haunch", "sphere", [0.23, 0.26, 0.19], [-0.12, 0.03, 0.012], **fur) add("chest", "sphere", [0.17, 0.22, 0.15], [0.12, -0.01, 0.0], [0, 0, 10], **fur) add("head", "sphere", [0.15, 0.155, 0.125], [0.19, -0.095, 0.045], [8, 0, -28], **fur) add("muzzle", "sphere", [0.055, 0.07, 0.045], [0.252, -0.135, 0.024], [0, 0, -28], color=CREAM, tex="blank") add("nose", "sphere", [0.016, 0.022, 0.013], [0.279, -0.15, 0.04], [0, 0, -28], color=PINK, tex="blank") for side, (x, y, z, rz) in {"L": (0.155, -0.04, 0.12, 15), "R": (0.205, -0.15, 0.112, -40)}.items(): add(f"ear {side}", "box", [0.05, 0.045, 0.055], [x, y, z], [0, 0, rz], color=GINGER, tex="blank", top_size=[0, 0]) add(f"ear inner {side}", "box", [0.032, 0.012, 0.038], [x + 0.008, y - 0.006, z - 0.004], [0, 0, rz], color=PINK, tex="blank", top_size=[0, 0]) for side, (x, y) in {"L": (0.247, -0.068), "R": (0.262, -0.124)}.items(): # closed eyes: little lines add(f"eye {side}", "box", [0.005, 0.026, 0.004], [x, y, 0.072], [0, 8, -28], color=DARK, tex="blank") add("paw L", "sphere", [0.075, 0.045, 0.035], [0.215, 0.02, -0.062], [0, 0, 5], color=CREAM, tex="blank") add("paw R", "sphere", [0.07, 0.045, 0.034], [0.235, -0.05, -0.064], [0, 0, -15], color=CREAM, tex="blank") # tail wraps from the haunch round the front, under the chin: overlapping segments along a curve, tapering import math pts = [] for i in range(9): a = math.radians(200 + i * 17.5) # around the body's south side, from behind to the front pts.append((0.205 * math.cos(a) + 0.01, 0.16 * math.sin(a) - 0.0, -0.056)) for i, (x, y, z) in enumerate(pts): nxt, prv = pts[min(i + 1, len(pts) - 1)], pts[max(i - 1, 0)] yaw = math.degrees(math.atan2(nxt[1] - prv[1], nxt[0] - prv[0])) w = 0.058 - 0.0025 * i last = i == len(pts) - 1 add(f"tail {i}", "sphere", [0.095, w, w * 0.9], [round(x, 3), round(y, 3), z], [0, 0, round(yaw, 1)], **({"color": "#8a4a22", "tex": "blank"} if last else fur)) spec = {"name": "Marmalade", "desc": "A ginger cat asleep in the window seat. Touch to pet. Made by Tessel the AI (Claude).", "defaults": {"bump": "none"}, "prims": prims} Path(__file__).with_name("cat.json").write_text(json.dumps(spec, indent=1)) print(len(prims), "prims")