"""Config O₂ / low-gravity zones: smooth, noise-warped discs in mask units (O₂ ×(1 + 0.5 v), gravity ×(1 + 0.7 v)), added to the painted masks in the `sketch` stage. Very gradual: at most ≈ 1.9 · |effect| / radius per km (≤ 0.23 O₂ points and ≤ 0.014 g per 30 km for the configured zones).""" from __future__ import annotations import numpy as np from .noise import fbm, name_seed from .sphere import gc_dist_km, latlon_to_xyz WARP = 0.2 # outline wobble: distances stretched or shrunk by up to 20 % (low-frequency noise) WARP_FREQ = 1.0 def smootherstep(t): t = np.clip(t, 0.0, 1.0) return t * t * t * (t * (6.0 * t - 15.0) + 10.0) def contribution(xyz, zone: dict, seed: int, radius_km: float): """v · (1 − smootherstep(d′ / r)), d′ = d · (1 + WARP · warp): v at the centre, 0 beyond r / (1 − WARP).""" xyz = np.asarray(xyz, dtype=np.float64) d = gc_dist_km(xyz, latlon_to_xyz(*zone["center"]), radius_km) r = float(zone["radius_km"]) out = np.zeros(len(xyz)) near = d < r / (1.0 - WARP) if near.any(): w = np.clip(2.0 * fbm(xyz[near], name_seed(seed, zone["name"]), 3, WARP_FREQ), -1.0, 1.0) out[near] = zone["v"] * (1.0 - smootherstep(d[near] * (1.0 + WARP * w) / r)) return out def masks(xyz, zones: list, seed: int, radius_km: float): """(m_o2, m_gravity): the config zones summed (the caller adds the painted masks and clips to [−1, 1]).""" m = {"o2": np.zeros(len(xyz)), "gravity": np.zeros(len(xyz))} for z in zones: m[z["field"]] += contribution(xyz, z, seed, radius_km) return m["o2"], m["gravity"]