All checks were successful
docs / build-and-deploy (push) Successful in 2s
A Noise.* namespace for procedural generation, implemented entirely in Q16.16 fixed point over an integer permutation hash so a seed reproduces the exact same field on every platform and run (native/headless/wasm) — the determinism edge over float noise that drifts across CPUs. - value2 / perlin2 / simplex2 — value, gradient, and simplex noise -> [-1,1] - fbm2(x,y,seed,octaves) — fractal Brownian motion (octaves of simplex) - cellular2 / cellular2_id — Worley F1 distance + nearest-cell id - unit(n) — remap [-1,1] -> [0,1] Covers issue phases 1–2 fully plus cellular from phase 3; domain warp, ridged/ billow, and sample1/sample3 remain as follow-ups. Pure integer IR, C-free; cellular/fbm reuse the math prelude's fx_sqrt. - examples/library/noise.ludic: asserts the invariants a fixed-point generator must hold (Perlin == 0 at lattice points, every sampler within [-1,1], reproducibility, seed sensitivity, non-negative cellular distance). Wired into `x test` (now 52 passed). - docs: a new Noise section + per-symbol pages; inventory and coverage pass. - seed regenerated; `x bootstrap-cfree` fixpoint holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 lines
2 KiB
Text
43 lines
2 KiB
Text
# noise.ludic — Noise.* determinism and structural invariants. Fixed-point noise
|
|
# can't be checked against a float reference bit-for-bit (that difference is the
|
|
# whole point), so we assert the properties that must hold: Perlin is exactly 0
|
|
# at integer lattice points, every sampler stays within [-1,1] (±65536 fixed),
|
|
# the same (x,y,seed) always reproduces, the seed changes the field, and cellular
|
|
# distance is non-negative. Running it prints: 1 2 3 4 5 6 7 8 9 10 11
|
|
program Noise {
|
|
entry {
|
|
let seed = 1337
|
|
|
|
# gradient noise is exactly 0 at integer lattice points (zero offset vectors)
|
|
if Noise.perlin2(fixed(0), fixed(0), seed) == 0 { print(1) }
|
|
if Noise.perlin2(fixed(3), fixed(5), seed) == 0 { print(2) }
|
|
|
|
# determinism: identical inputs -> identical output
|
|
let a = Noise.simplex2(20000, 100, seed)
|
|
let b = Noise.simplex2(20000, 100, seed)
|
|
if a == b { print(3) }
|
|
|
|
# range: every sampler stays within [-1, 1] (comparisons are in the fixed domain)
|
|
let lo = fixed(0) - fixed(1)
|
|
let hi = fixed(1)
|
|
let p = Noise.perlin2(12345, 54321, seed)
|
|
if p >= lo and p <= hi { print(4) }
|
|
let s = Noise.simplex2(12345, 54321, seed)
|
|
if s >= lo and s <= hi { print(5) }
|
|
let v = Noise.value2(12345, 54321, seed)
|
|
if v >= lo and v <= hi { print(6) }
|
|
let f = Noise.fbm2(12345, 54321, seed, 5)
|
|
if f >= lo and f <= hi { print(7) }
|
|
|
|
# unit() remaps [-1,1] -> [0,1]: endpoints and midpoint (all in the fixed domain)
|
|
let half = fixed(1) / fixed(2)
|
|
if Noise.unit(fixed(1)) == fixed(1) and Noise.unit(fixed(0) - fixed(1)) == fixed(0) and Noise.unit(fixed(0)) == half { print(8) }
|
|
|
|
# the seed selects the world: different seeds -> different fields
|
|
if Noise.value2(12345, 54321, 1) != Noise.value2(12345, 54321, 2) { print(9) }
|
|
|
|
# cellular F1 distance is non-negative; the cell id is deterministic
|
|
if Noise.cellular2(12345, 54321, seed) >= 0 { print(10) }
|
|
if Noise.cellular2_id(12345, 54321, seed) == Noise.cellular2_id(12345, 54321, seed) { print(11) }
|
|
}
|
|
}
|