///|
/// Summary statistics for sampled procedural fields.
pub(all) struct SampleStats {
minimum : Double
maximum : Double
mean : Double
variance : Double
count : Int
} derive(Eq, Debug, ToJson)
///|
pub fn sample_stats(values : Array[Double]) -> SampleStats {
if values.length() == 0 {
{ minimum: 0.0, maximum: 0.0, mean: 0.0, variance: 0.0, count: 0 }
} else {
let mut minimum = values[0]
let mut maximum = values[0]
let mut sum = 0.0
for value in values {
if value < minimum {
minimum = value
}
if value > maximum {
maximum = value
}
sum += value
}
let mean = sum / values.length().to_double()
let mut squared = 0.0
for value in values {
let delta = value - mean
squared += delta * delta
}
{
minimum,
maximum,
mean,
variance: squared / values.length().to_double(),
count: values.length(),
}
}
}
///|
pub fn[N : NoiseSampler] sample_noise_stats(
sampler : N,
width : Int,
height : Int,
scale : Double,
) -> SampleStats {
let values = []
if width > 0 && height > 0 {
for y in 0.. Array[Int] {
let result = []
if bins <= 0 {
result
} else {
for _ in 0.. minimum {
for value in values {
let position = ((value - minimum) /
(maximum - minimum) *
bins.to_double()).to_int()
let index = if position < 0 {
0
} else if position >= bins {
bins - 1
} else {
position
}
result[index] += 1
}
}
result
}
}
///|
/// Calculates a smoothness estimate from adjacent samples in a grid.
pub fn field_smoothness(
values : Array[Double],
width : Int,
height : Int,
) -> Double {
if width < 2 || height < 2 || values.length() < width * height {
0.0
} else {
let mut difference = 0.0
let mut pairs = 0
for y in 0.. Color {
let light_ratio = background.contrast_ratio(light)
let dark_ratio = background.contrast_ratio(dark)
if light_ratio >= dark_ratio {
light
} else {
dark
}
}
///|
/// Generates colors from a noise field while keeping alpha stable.
pub fn[N : NoiseSampler] noise_palette(
sampler : N,
count : Int,
low : Color,
high : Color,
) -> Array[Color] {
let result = []
for i in 0..