///|
/// A multi-octave sampler with configurable normalization and remapping.
pub(all) struct FractalSampler {
  base : ValueNoise
  octaves : Int
  lacunarity : Double
  gain : Double
} derive(Eq, Debug, ToJson)

///|
pub fn FractalSampler::new(
  seed : UInt,
  frequency : Double,
  octaves : Int,
) -> FractalSampler {
  {
    base: ValueNoise::new(frequency, seed),
    octaves,
    lacunarity: 2.0,
    gain: 0.5,
  }
}

///|
pub fn FractalSampler::with_parameters(
  self : FractalSampler,
  lacunarity : Double,
  gain : Double,
) -> FractalSampler {
  { ..self, lacunarity, gain }
}

///|
pub impl NoiseSampler for FractalSampler with fn sample_2d(self, x, y) {
  fractal_noise(
    self.base,
    x,
    y,
    self.octaves,
    lacunarity=self.lacunarity,
    gain=self.gain,
  )
}

///|
pub fn[N : NoiseSampler] turbulence(
  sampler : N,
  x : Double,
  y : Double,
  octaves : Int,
) -> Double {
  fractal_noise(sampler, x, y, octaves).abs()
}

///|
pub fn[N : NoiseSampler] domain_warped_sample(
  sampler : N,
  x : Double,
  y : Double,
  strength : Double,
  scale : Double,
  octaves : Int,
) -> Double {
  let (wx, wy) = warp_point(sampler, x, y, strength, scale)
  fractal_noise(sampler, wx, wy, octaves)
}

///|
pub fn[N : NoiseSampler] vector_field(
  sampler : N,
  width : Int,
  height : Int,
  scale : Double,
) -> Array[Vec2] {
  let result = []
  if width > 0 && height > 0 {
    for y in 0.. Double {
  if input_max == input_min {
    output_min
  } else {
    let t = (value - input_min) / (input_max - input_min)
    output_min + (output_max - output_min) * t
  }
}

///|
pub fn threshold_noise(
  value : Double,
  threshold : Double,
  softness : Double,
) -> Double {
  if softness <= 0.0 {
    if value >= threshold {
      1.0
    } else {
      0.0
    }
  } else {
    smoothstep((value - threshold + softness) / (2.0 * softness))
  }
}

///|
pub fn noise_ramp(
  sampler : ValueNoise,
  count : Int,
  start : Double,
  step : Double,
) -> Array[Double] {
  let result = []
  for i in 0..