""" Shell3D renderer: the nautilus as a real 3D object with iridescent nacre. Consumes a SceneGraph and emits a self-contained Three.js scene (in an iframe) that the user can orbit. The shell GEOMETRY is generated from ShellState (turns, growth, knots, aperture) so it stays traceable; the MATERIAL is a real physically-based iridescent surface (mother-of-pearl) whose strength comes from ShellState.iridescence and whose colours come from the emotional palette. Knots (dead ends) become raised nubs on the shell body; the aperture (breakthrough) glows. No API key, no build step: Three.js is loaded from a CDN inside the iframe. WebGL is the only requirement; available() reports that honestly. """ from __future__ import annotations import html as _html import json from scene_graph import SceneGraph class Shell3DRenderer: id = "shell3d" label = "The shell, in 3D" description = "Your nautilus as a real object you can turn in the light — iridescent nacre." supported_versions = ("1.0",) requires_generation = False def available(self) -> tuple[bool, str]: # Always offerable; WebGL support is a client-side capability we cannot # detect server-side. The iframe degrades to a message if WebGL is absent. return (True, "") def render(self, scene: SceneGraph) -> dict: params = { "turns": scene.shell.turns, "growth": scene.shell.growth_curve, "knots": scene.shell.knots, "jewels": scene.shell.jewels, "aperture": scene.shell.aperture, "iridescence": scene.shell.iridescence, "palette": scene.shell.palette, "session": scene.session_id, } doc = _THREE_DOC.replace("__PARAMS__", json.dumps(params)) escaped = _html.escape(doc, quote=True) iframe = ( f'' ) return {"kind": "iframe", "html": iframe, "notes": ""} # The Three.js scene. A log-spiral tube of growing radius (the nautilus), with a # real iridescent material (KHR-style thin-film via onBeforeCompile is heavy; we # use MeshPhysicalMaterial.iridescence which three r150+ supports natively), knots # as small spheres on the centerline, and a glowing aperture sphere at the tip. _THREE_DOC = r"""