///|
/// Interface for external or custom 2D noise samplers
pub trait NoiseSampler {
fn sample_2d(self : Self, x : Double, y : Double) -> Double
}
///|
/// A default trigonometric pseudo-noise generator that implements NoiseSampler
/// Useful as a zero-dependency fallback.
pub(all) struct TrigNoise {
frequency : Double
} derive(Eq, Debug, ToJson)
///|
/// Create a new TrigNoise sampler
pub fn TrigNoise::new(frequency : Double) -> TrigNoise {
{ frequency, }
}
///|
/// Implementation of NoiseSampler for TrigNoise
pub impl NoiseSampler for TrigNoise with fn sample_2d(self, x, y) {
let nx = x * self.frequency
let ny = y * self.frequency
// Combine multiple sin/cos waves at different octaves/scales
@math.sin(nx) * 0.5 +
@math.cos(ny) * 0.3 +
@math.sin(nx * 2.3 + ny * 1.7) * 0.2
}
///|
/// Wrap a simple function as a NoiseSampler
pub(all) struct FnNoise {
f : (Double, Double) -> Double
}
///|
/// Create a NoiseSampler from a function closure
pub fn FnNoise::new(f : (Double, Double) -> Double) -> FnNoise {
{ f, }
}
///|
/// Implementation of NoiseSampler for FnNoise
pub impl NoiseSampler for FnNoise with fn sample_2d(self, x, y) {
(self.f)(x, y)
}
///|
extend ValueNoise with NoiseSampler::{sample_2d}
///|
extend TrigNoise with NoiseSampler::{sample_2d}
///|
/// Domain warping: distorts the (x, y) coordinates using noise
pub fn[N : NoiseSampler] warp_point(
sampler : N,
x : Double,
y : Double,
warp_strength : Double,
warp_scale : Double,
) -> (Double, Double) {
let dx = sampler.sample_2d(x * warp_scale + 5.2, y * warp_scale + 1.3) *
warp_strength
let dy = sampler.sample_2d(x * warp_scale + 1.7, y * warp_scale + 9.1) *
warp_strength
(x + dx, y + dy)
}
///|
/// Computes a flow field direction angle (in radians) at (x, y)
pub fn[N : NoiseSampler] flow_field_angle(
sampler : N,
x : Double,
y : Double,
scale : Double,
) -> Double {
let val = sampler.sample_2d(x * scale, y * scale)
// map [-1.0, 1.0] range to [0.0, 2.0 * PI]
let norm = (val + 1.0) / 2.0
norm * 2.0 * @math.PI
}