use std::ops::AddAssign;
#[cfg(feature = "serialize")]
extern crate serde;
#[macro_use]
#[cfg(feature = "serialize")]
extern crate serde_derive;
pub mod mutation;
pub mod param_set;
pub type Param = f64;
pub trait ParamHolder {
fn param_count(&self) -> usize;
fn get_param(&mut self, index: usize) -> &mut RangedParam;
}
pub trait RangedParam {
fn range(&self) -> (Param, Param) {
(0.0, 1.0) }
fn get(&self) -> Param;
fn get_mut(&mut self) -> &mut Param;
fn get_scaled(&self) -> Param {
let (min, max) = self.range();
(max - min) * self.get() + min
}
}
impl<'a> AddAssign<Param> for &'a mut RangedParam {
fn add_assign(&mut self, rhs: Param) {
let clamped = {
let val = *self.get_mut() + rhs;
if val < 0.0 {
0.0
} else if val > 1.0 {
1.0
} else {
val
}
};
*self.get_mut() = clamped;
}
}
#[cfg(test)]
macro_rules! assert_feq {
($a:expr, $b:expr) => {{
let (a, b) = (&$a, &$b);
let diff = (a - b).abs();
assert!(diff < 0.00001, "{} !~= {}", a, b);
}};
}
#[cfg(test)]
mod tests {
use super::{mutation::*, param_set::*, *};
use std::cell::RefCell;
use std::rc::Rc;
struct TestParam(Param);
struct TestHolder {
x: TestParam,
}
impl ParamHolder for TestHolder {
fn param_count(&self) -> usize {
1
}
fn get_param(&mut self, index: usize) -> &mut RangedParam {
match index {
0 => &mut self.x,
_ => panic!("Bad param index"),
}
}
}
impl RangedParam for TestParam {
fn range(&self) -> (Param, Param) {
(0.0, 20.0)
}
fn get(&self) -> Param {
self.0
}
fn get_mut(&mut self) -> &mut Param {
&mut self.0
}
}
struct ConstGen(Param);
impl MutationGen for ConstGen {
fn gen(&mut self) -> Param {
self.0
}
}
#[test]
fn test_mutate() {
let holder = Rc::new(RefCell::new(TestHolder {
x: TestParam { 0: 0.0 },
}));
mutate(holder.clone(), &mut ConstGen { 0: 0.5 });
assert_feq!(holder.borrow().x.get_scaled(), 10.0);
}
#[test]
fn test_clamp() {
let holder = Rc::new(RefCell::new(TestHolder { x: TestParam(0.0) }));
mutate(holder.clone(), &mut ConstGen { 0: -0.5 });
assert_feq!(holder.borrow().x.get_scaled(), 0.0);
mutate(holder.clone(), &mut ConstGen { 0: 1.5 });
assert_feq!(holder.borrow().x.get_scaled(), 20.0);
}
#[derive(Debug)]
struct Pos(Param);
#[derive(Debug)]
struct MultiShape(ParamSet3d<Pos>);
impl RangedParam for Pos {
fn range(&self) -> (Param, Param) {
(0.0, 10.0)
}
fn get(&self) -> Param {
self.0
}
fn get_mut(&mut self) -> &mut Param {
&mut self.0
}
}
impl ParamHolder for MultiShape {
fn param_count(&self) -> usize {
3
}
fn get_param(&mut self, index: usize) -> &mut RangedParam {
match index {
0...2 => self.0.get_param(index),
_ => panic!("Bad param index"),
}
}
}
#[test]
fn test_paramset() {
let holder = Rc::new(RefCell::new(MultiShape(ParamSet3d::new(
Pos(0.1),
Pos(0.1),
Pos(0.1),
))));
mutate(holder.clone(), &mut ConstGen { 0: 0.15 });
let expected = 2.5; let pos = &holder.borrow().0;
assert_feq!(pos.x.get_scaled(), expected);
assert_feq!(pos.y.get_scaled(), expected);
assert_feq!(pos.z.get_scaled(), expected);
}
}