1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! This crate provides a way to show slow change in several variables at once.
//! This change is done in such a way that should explore the value space fairly
//! well while also appearing natural and random.
//! 
//! One place this might be useful is in code that demonstrates how changing certain
//! parameters changes a model.
//! 
//! The variables yielded by this crate will all have values between 0 and 1, so you
//! should scale them to suit your purposes.
//!
//! # How it Works
//!
//! For each variable, there is a separate function that determines its motion.
//! This function is given by the average of three sinusoidal functions.
//! 
//! ```no_run
//! use meander::rand;
//! use meander::typenum::U3;
//! 
//! use meander::Meander;
//! 
//! struct Color {
//!     r: u8,
//!     g: u8,
//!     b: u8,
//! }
//!
//! fn random_colors() -> impl Iterator<Item=Color> {
//!     rand::random::<Meander<U3>>()
//!         .into_time_steps(0.01).map(|a| {
//!             match a.as_slice() {
//!                 // The variables yielded by `Meander` are floats between 0 and 1,
//!                 // so we multiply by 256 and cast to `u8` to get the range we want.
//!                 &[r, g, b] => Color {
//!                     r: (r*256.0) as u8,
//!                     g: (g*256.0) as u8,
//!                     b: (b*256.0) as u8,
//!                 },
//!                 _ => unreachable!()
//!             }
//!         })
//! }
//! ```

#![deny(missing_docs)]

pub use rand;
pub use generic_array;
pub use generic_array::typenum;

use generic_array::{GenericArray, ArrayLength};
use generic_array::functional::FunctionalSequence;
use generic_array::sequence::GenericSequence;

use rand::Rng;
use rand::distributions::{Distribution, Standard};

const PI2: f64 = 2.0 * std::f64::consts::PI;

/// Represents a sinusoid that varies between 0 and 1.
///
/// This can be generated randomly using `rand::random()`.
#[derive(Clone, Copy, Debug)]
pub struct UnitSinusoid {
    /// The number of cycles the function makes per unit time.
    pub frequency: f64,
    /// The location in the cycle the function is `t = 0`.
    pub phase: f64,
}

impl Distribution<UnitSinusoid> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UnitSinusoid {
        let frequency: f64 = rng.gen_range(1.0, 10.0);
        let phase = rng.gen_range(0.0, frequency.recip());
        UnitSinusoid { frequency, phase }
    }
}

impl UnitSinusoid {
    fn haversin(theta: f64) -> f64 {
        (1.0 - theta.cos()) / 2.0
    }
    /// Find the value of the sinusoid at a given point in time.
    pub fn evaluate(self, t: f64) -> f64 {
        Self::haversin(PI2 * self.frequency * (t + self.phase))
    }
}

/// Represents a curve that meanders through 1-dimensional space. Consists of 3
/// sinusoids whose values are averaged.
///
/// This can be generated randomly using `rand::random()`.
#[derive(Clone, Copy, Debug)]
pub struct Meander1D(pub UnitSinusoid, pub UnitSinusoid, pub UnitSinusoid);

impl Meander1D {
    /// Find the value of the curve at a given point in time.
    pub fn evaluate(self, t: f64) -> f64 {
        ( (self.0).evaluate(t)
        + (self.1).evaluate(t)
        + (self.2).evaluate(t)
        ) / 3.0
    }
}

impl Distribution<Meander1D> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Meander1D {
        Meander1D(rng.gen(), rng.gen(), rng.gen())
    }
}

/// Represents a curve that meanders through `D`-dimensional space.
///
/// This can be generated randomly using `rand::random()`.
#[derive(Clone, Debug)]
pub struct Meander<D: ArrayLength<Meander1D>> {
    /// Each variable is controlled by a separate 1-dimensional function defined here.
    pub curves: GenericArray<Meander1D, D>,
}

impl<D: ArrayLength<Meander1D>> Distribution<Meander<D>> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Meander<D> {
        Meander {
            curves: <GenericArray<_, _> as GenericSequence<_>>::generate(|_| rng.gen()),
        }
    }
}

impl<D: ArrayLength<Meander1D> + ArrayLength<f64>> Meander<D> {
    /// Find the value of each of the variables at a particular point in time.
    pub fn evaluate(&self, t: f64) -> GenericArray<f64, D> {
        (&self).curves.clone().map(|c| c.evaluate(t))
    }
    /// Return an iterator yielding the values of the variables at intervals of `dt`.
    pub fn time_steps<'a>(&'a self, dt: f64) -> impl Iterator<Item=GenericArray<f64, D>> + 'a {
        (0..).map(move |i| self.evaluate(i as f64 * dt))
    }
    /// Return an iterator yielding the values of the variables at intervals of `dt`.
    /// Consumes `self`.
    pub fn into_time_steps(self, dt: f64) -> impl Iterator<Item=GenericArray<f64, D>> {
        (0..).map(move |i| self.evaluate(i as f64 * dt))
    }
}