///|
/// Deterministic pseudo-random stream for reproducible artwork and benchmarks.
pub(all) struct Random {
mut state : UInt
} derive(Debug)
///|
pub fn Random::new(seed : UInt) -> Random {
{ state: if seed == 0U { 0x9e3779b9U } else { seed } }
}
///|
pub fn Random::next_u32(self : Random) -> UInt {
self.state = self.state ^ (self.state << 13)
self.state = self.state ^ (self.state >> 17)
self.state = self.state ^ (self.state << 5)
self.state
}
///|
pub fn Random::next_float(self : Random) -> Double {
self.next_u32().to_double() / 4294967295.0
}
///|
pub fn Random::range(self : Random, low : Double, high : Double) -> Double {
low + (high - low) * self.next_float()
}
///|
pub fn Random::int_range(self : Random, low : Int, high : Int) -> Int {
if high <= low {
low
} else {
low + (self.next_float() * (high - low).to_double()).to_int()
}
}
///|
pub fn[T] Random::choose(self : Random, values : Array[T]) -> T? {
if values.length() == 0 {
None
} else {
Some(values[self.int_range(0, values.length())])
}
}
///|
pub fn hash2d(x : Int, y : Int, seed? : UInt = 0U) -> UInt {
let mut h = seed ^ 0x811c9dc5U
h = (h ^ x.reinterpret_as_uint()) * 0x01000193U
h = (h ^ y.reinterpret_as_uint()) * 0x01000193U
h ^ (h >> 16)
}
///|
pub fn hash2d_float(x : Int, y : Int, seed? : UInt = 0U) -> Double {
hash2d(x, y, seed~).to_double() / 4294967295.0
}
///|
pub fn smoothstep(value : Double) -> Double {
let t = if value < 0.0 { 0.0 } else if value > 1.0 { 1.0 } else { value }
t * t * (3.0 - 2.0 * t)
}
///|
/// A deterministic value-noise sampler with bilinear interpolation.
pub(all) struct ValueNoise {
frequency : Double
seed : UInt
} derive(Eq, Debug, ToJson)
///|
pub fn ValueNoise::new(frequency : Double, seed : UInt) -> ValueNoise {
{ frequency, seed }
}
///|
pub impl NoiseSampler for ValueNoise with fn sample_2d(self, x, y) {
let px = x * self.frequency
let py = y * self.frequency
let x0 = px.to_int()
let y0 = py.to_int()
let fx = smoothstep(px - x0.to_double())
let fy = smoothstep(py - y0.to_double())
let a = hash2d_float(x0, y0, seed=self.seed) * 2.0 - 1.0
let b = hash2d_float(x0 + 1, y0, seed=self.seed) * 2.0 - 1.0
let c = hash2d_float(x0, y0 + 1, seed=self.seed) * 2.0 - 1.0
let d = hash2d_float(x0 + 1, y0 + 1, seed=self.seed) * 2.0 - 1.0
let top = a + (b - a) * fx
let bottom = c + (d - c) * fx
top + (bottom - top) * fy
}
///|
/// Fractal Brownian motion combines several noise octaves.
pub fn[N : NoiseSampler] fractal_noise(
sampler : N,
x : Double,
y : Double,
octaves : Int,
lacunarity? : Double = 2.0,
gain? : Double = 0.5,
) -> Double {
let mut amplitude = 1.0
let mut frequency = 1.0
let mut total = 0.0
let mut normalizer = 0.0
for _ in 0.. Double {
let value = fractal_noise(sampler, x, y, octaves)
1.0 - value.abs()
}