1"""Generate challenges/<id>/scene.json, the interactive 3D view of each challenge's geometry shown on the results site,
2from its challenge.json. Run from the repository root: python3 scripts/make_scenes.py"""
3import json
4import math
5from pathlib import Path
7ROOT = Path(__file__).resolve().parent.parent
8NR, NT = 49, 144 # radial and angular samples of the mid-surface
11def profile(p):
12 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)
14 def Z(r):
15 z = H * (1.0 - r * r / (R * R))
16 if rb and r < rb:
17 z += 0.5 * hb * (1.0 + math.cos(math.pi * r / rb))
18 return z
20 def h(r):
21 if "thickness" in p:
22 return p["thickness"]
23 hc, he = p["thickness_center"], p["thickness_rim"]
24 return hc if r <= rb else hc + (he - hc) * (r - rb) / (R - rb)
26 return Z, h
29def main():
30 for cj in sorted(ROOT.glob("challenges/*/challenge.json")):
31 ch = json.load(open(cj))
32 p = ch["parameters"]
33 Z, h = profile(p)
34 R, rh = p["disk_radius"], p["hole_radius"]
35 verts, thick = [], []
36 for i in range(NR):
37 r = rh + (R - rh) * i / (NR - 1)
38 for j in range(NT):
39 th = 2 * math.pi * j / NT
40 verts.append([round(r * math.cos(th), 5), round(r * math.sin(th), 5), round(Z(r), 5)])
41 thick.append(round(1e3 * h(r), 4))
42 names = ["strike 1 (bell), t = 0 s", "strike 2 (bow), 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"]
43 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]}
44 for k, s in enumerate(ch["strikes"])]
45 L = [p["listener_x"], p["listener_y"], p["listener_z"]]
46 points.append({"name": "listener", "position": L, "radius": 0.02, "color": [0.15, 0.35, 0.85], "far": True})
47 scene = {
48 "units": "m",
49 "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.",
50 "surfaces": [{"name": "mid-surface", "vertices": verts, "grid": [NR, NT], "periodic": True, "scalar": {"name": "thickness (mm)", "values": thick}}],
51 "points": points,
52 "lines": [{"from": [0, 0, 0], "to": L}],
53 "up": "z",
54 }
55 out = cj.parent / "scene.json"
56 with open(out, "w") as f:
57 json.dump(scene, f, separators=(",", ":"))
58 print(out, f"{out.stat().st_size / 1e3:.0f} kB")
61if __name__ == "__main__":
62 main()