Spaces:
Running
Running
| """Vectorized value-noise field for domain warping. | |
| We do NOT pull in a Perlin dependency: a tileable value-noise (random lattice + | |
| bilinear upsample, summed across octaves) is enough to warp Voronoi boundaries | |
| into organic "torn" edges, and it is pure NumPy. | |
| Complexity: building an (H, W) field over `octaves` is | |
| Theta(H * W * octaves) time, | |
| Theta(H * W) space. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| def _bilinear_upsample(grid: np.ndarray, out_h: int, out_w: int) -> np.ndarray: | |
| """Upsample a small lattice to (out_h, out_w) with bilinear interpolation.""" | |
| gh, gw = grid.shape | |
| # Sample positions in lattice space. | |
| ys = np.linspace(0, gh - 1, out_h) | |
| xs = np.linspace(0, gw - 1, out_w) | |
| y0 = np.floor(ys).astype(np.int64) | |
| x0 = np.floor(xs).astype(np.int64) | |
| y1 = np.minimum(y0 + 1, gh - 1) | |
| x1 = np.minimum(x0 + 1, gw - 1) | |
| wy = (ys - y0)[:, None] | |
| wx = (xs - x0)[None, :] | |
| top = grid[y0][:, x0] * (1 - wx) + grid[y0][:, x1] * wx | |
| bot = grid[y1][:, x0] * (1 - wx) + grid[y1][:, x1] * wx | |
| return top * (1 - wy) + bot * wy | |
| def value_noise( | |
| h: int, | |
| w: int, | |
| scale: float, | |
| rng: np.random.Generator, | |
| octaves: int = 3, | |
| persistence: float = 0.5, | |
| ) -> np.ndarray: | |
| """Return an (h, w) float field in roughly [-1, 1]. | |
| `scale` is the wavelength in pixels of the base octave: larger -> smoother. | |
| Octaves add finer detail (fractal/fBm), persistence weights their amplitude. | |
| """ | |
| field = np.zeros((h, w), dtype=np.float32) | |
| amplitude = 1.0 | |
| total_amp = 0.0 | |
| freq_scale = max(scale, 2.0) | |
| for _ in range(max(1, octaves)): | |
| gh = max(2, int(np.ceil(h / freq_scale)) + 1) | |
| gw = max(2, int(np.ceil(w / freq_scale)) + 1) | |
| lattice = rng.uniform(-1.0, 1.0, size=(gh, gw)).astype(np.float32) | |
| field += amplitude * _bilinear_upsample(lattice, h, w) | |
| total_amp += amplitude | |
| amplitude *= persistence | |
| freq_scale = max(freq_scale * 0.5, 2.0) | |
| return field / total_amp | |