use noise_functions::{modifiers::Seeded, Noise as NoiseTrait, Sample};
use crate::{
color::Okhsv,
layout::{Layout1d, Layout2d, Layout3d},
markers::{Dim1d, Dim2d, Dim3d},
pattern::Pattern,
};
pub mod noise_fns {
pub use noise_functions::{OpenSimplex2, Perlin, Simplex};
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NoiseParams {
pub time_scalar: f32,
pub position_scalar: f32,
}
impl Default for NoiseParams {
fn default() -> Self {
const MILLISECONDS_PER_SECOND: f32 = 1e3;
Self {
time_scalar: 0.75 / MILLISECONDS_PER_SECOND,
position_scalar: 0.5,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Noise1d<Noise> {
hue_noise: Seeded<Noise>,
value_noise: Seeded<Noise>,
params: NoiseParams,
}
impl<Layout, Noise> Pattern<Dim1d, Layout> for Noise1d<Noise>
where
Layout: Layout1d,
Noise: NoiseTrait + Sample<2> + Default,
{
type Params = NoiseParams;
type Color = Okhsv;
fn new(params: Self::Params) -> Self {
Self {
hue_noise: Noise::default().seed(0),
value_noise: Noise::default().seed(1),
params,
}
}
fn tick(&self, time_in_ms: u64) -> impl Iterator<Item = Self::Color> {
let Self {
hue_noise,
value_noise,
params,
} = self;
let NoiseParams {
time_scalar,
position_scalar,
} = params;
let noise_time = time_in_ms as f32 * time_scalar;
Layout::points().map(move |x| {
let noise_args = [position_scalar * x, noise_time];
let hue = hue_noise.sample2(noise_args);
let saturation = 1.;
let value = 0.75 + 0.25 * value_noise.sample2(noise_args);
Okhsv::new(hue, saturation, value)
})
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Noise2d<Noise> {
hue_noise: Seeded<Noise>,
value_noise: Seeded<Noise>,
params: NoiseParams,
}
impl<Layout, Noise> Pattern<Dim2d, Layout> for Noise2d<Noise>
where
Layout: Layout2d,
Noise: NoiseTrait + Sample<3> + Default,
{
type Params = NoiseParams;
type Color = Okhsv;
fn new(params: Self::Params) -> Self {
Self {
hue_noise: Noise::default().seed(0),
value_noise: Noise::default().seed(1),
params,
}
}
fn tick(&self, time_in_ms: u64) -> impl Iterator<Item = Self::Color> {
let Self {
hue_noise,
value_noise,
params,
} = self;
let NoiseParams {
time_scalar,
position_scalar,
} = params;
let noise_time = time_in_ms as f32 * time_scalar;
Layout::points().map(move |point| {
let noise_args = [
position_scalar * point.x,
position_scalar * point.y,
noise_time,
];
let hue = hue_noise.sample3(noise_args);
let saturation = 1.;
let value = 0.75 + 0.25 * value_noise.sample3(noise_args);
Okhsv::new(hue, saturation, value)
})
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Noise3d<Noise>
where
Noise: NoiseTrait,
{
hue_noise: Seeded<Noise>,
value_noise: Seeded<Noise>,
params: NoiseParams,
}
impl<Layout, Noise> Pattern<Dim3d, Layout> for Noise3d<Noise>
where
Layout: Layout3d,
Noise: NoiseTrait + Sample<4> + Default,
{
type Params = NoiseParams;
type Color = Okhsv;
fn new(params: Self::Params) -> Self {
Self {
hue_noise: Noise::default().seed(0),
value_noise: Noise::default().seed(1),
params,
}
}
fn tick(&self, time_in_ms: u64) -> impl Iterator<Item = Self::Color> {
let Self {
hue_noise,
value_noise,
params,
} = self;
let NoiseParams {
time_scalar,
position_scalar,
} = params;
let noise_time = time_in_ms as f32 * time_scalar;
Layout::points().map(move |point| {
let noise_args = [
position_scalar * point.x,
position_scalar * point.y,
position_scalar * point.z,
noise_time,
];
let hue = hue_noise.sample4(noise_args);
let saturation = 1.;
let value = 0.75 + 0.25 * value_noise.sample4(noise_args);
Okhsv::new(hue, saturation, value)
})
}
}