"""Generate challenges//scene.json, the interactive 3D view of each challenge's geometry shown on the results site, from its challenge.json. Run from the repository root: python3 scripts/make_scenes.py""" import json import math from pathlib import Path ROOT = Path(__file__).resolve().parent.parent NR, NT = 49, 144 # radial and angular samples of the mid-surface def profile(p): R, rb, hb, H = p["disk_radius"], p.get("bell_radius", 0.0), p.get("bell_height", 0.0), p.get("bow_height", 0.0) def Z(r): z = H * (1.0 - r * r / (R * R)) if rb and r < rb: z += 0.5 * hb * (1.0 + math.cos(math.pi * r / rb)) return z def h(r): if "thickness" in p: return p["thickness"] hc, he = p["thickness_center"], p["thickness_rim"] return hc if r <= rb else hc + (he - hc) * (r - rb) / (R - rb) return Z, h def main(): for cj in sorted(ROOT.glob("challenges/*/challenge.json")): ch = json.load(open(cj)) p = ch["parameters"] Z, h = profile(p) R, rh = p["disk_radius"], p["hole_radius"] verts, thick = [], [] for i in range(NR): r = rh + (R - rh) * i / (NR - 1) for j in range(NT): th = 2 * math.pi * j / NT verts.append([round(r * math.cos(th), 5), round(r * math.sin(th), 5), round(Z(r), 5)]) thick.append(round(1e3 * h(r), 4)) names = ["strike 1 (bell), t = 0 s", "strike 2 (mid), t = 2 s", "strike 3 (rim), t = 4 s"] if ch.get("geometry", {}).get("type") != "flat" else ["strike 1, t = 0 s", "strike 2, t = 2 s", "strike 3, t = 4 s"] points = [{"name": names[k], "position": [s["x"], s["y"], round(Z(math.hypot(s["x"], s["y"])), 5)], "radius": p["strike_radius"], "color": [0.85, 0.2, 0.15]} for k, s in enumerate(ch["strikes"])] L = [p["listener_x"], p["listener_y"], p["listener_z"]] points.append({"name": "listener", "position": L, "radius": 0.02, "color": [0.15, 0.35, 0.85], "far": True}) scene = { "units": "m", "note": "The mid-surface, colored by thickness; the three strike footprints; the listener 1.5 m away (far point). The z axis points up; the hole is at the center.", "surfaces": [{"name": "mid-surface", "vertices": verts, "grid": [NR, NT], "periodic": True, "scalar": {"name": "thickness (mm)", "values": thick}}], "points": points, "lines": [{"from": [0, 0, 0], "to": L}], "up": "z", } out = cj.parent / "scene.json" with open(out, "w") as f: json.dump(scene, f, separators=(",", ":")) print(out, f"{out.stat().st_size / 1e3:.0f} kB") if __name__ == "__main__": main()