use rand::prelude::*;
use rand::distributions::WeightedIndex;
use rand_distr::Distribution;
use crate::transform;
use crate::util::*;
use crate::transform::*;
use crate::image::*;
pub struct IFS {
pub transforms: Vec<Transform>,
num_transforms: usize,
total_weight: f32,
distribution: WeightedIndex<f32>
}
impl IFS{
pub fn new() -> IFS {
IFS{transforms: vec![],
num_transforms: 0,
total_weight: 0.,
distribution: WeightedIndex::new([1.]).unwrap()}
}
pub fn randomize(&mut self) {
self.transforms = self.transforms.iter().map(|t| transform_from_str(t.get_name())).collect();
self.distribution = WeightedIndex::new(self.transforms.iter().map(|t| t.get_weight())).unwrap();
self.total_weight = self.transforms.iter().map(|t| t.get_weight()).sum();
}
pub fn len(&self) -> usize {
self.num_transforms
}
pub fn is_empty(&self) -> bool {
self.num_transforms == 0
}
pub fn get_transform(&self, i: usize) -> Transform {
if i < self.len() {
*self.transforms.get(i).unwrap()
} else {
panic!("i is greater than the number of transforms")
}
}
pub fn check_transforms_match(&self, other: &Self) -> bool {
if self.transforms.len() != other.transforms.len() {
false
} else {
for i in 0..self.transforms.len() {
if self.transforms.get(i).unwrap().get_name() != other.transforms.get(i).unwrap().get_name() {
return false
}
}
true
}
}
pub fn add_transform(&mut self, transform: Transform) {
self.total_weight += transform.get_weight();
self.transforms.insert(self.num_transforms, transform);
self.num_transforms += 1;
self.distribution = WeightedIndex::new(self.transforms.iter().map(|t| t.get_weight())).unwrap();
}
pub fn delete_transform(&mut self, index: usize) {
let transform = self.transforms.get(index).unwrap();
self.total_weight -= transform.get_weight();
self.num_transforms -= 1;
self.transforms.remove(index);
self.distribution = WeightedIndex::new(self.transforms.iter().map(|t| t.get_weight())).unwrap();
}
fn choose_transform(&self) -> &Transform {
let mut rng = thread_rng();
self.transforms.get(self.distribution.sample(&mut rng)).unwrap()
}
pub fn evaluate(&self, image: &mut Image, num_points: usize, num_iterations: usize) {
for _ in 0..num_points {
self.single_point_evaluation(image, num_iterations)
}
}
fn single_point_evaluation(&self, image: &mut Image, num_iterations: usize) {
let mut rng = rand::thread_rng();
let mut px: f32 = rng.gen::<f32>() * 2. - 1.;
let mut py: f32 = rng.gen::<f32>() * 2. - 1.;
let mut color = Color{r: 0.0, g: 0.0, b: 0.0};
for _ in 0..num_iterations {
let t = self.choose_transform();
let new_point = t.transform_point(Point{x: px, y: py});
px = new_point.x;
py = new_point.y;
color = t.transform_color(color);
let (fx, fy) = final_transform(px, py);
let x = ((fx + 1.0) * (image.width() as f32 / 2.0)) as usize;
let y = ((fy + 1.0) * (image.height() as f32 / 2.0)) as usize;
image.add_radiance(x, y, color);
}
}
pub fn morph(&self, other: &Self, pct: f32) -> Self {
if !self.check_transforms_match(other) {
panic!("Transforms must match");
} else {
let mut out = IFS::new();
for i in 0..self.transforms.len() {
let a = *self.transforms.get(i).unwrap();
let b = *other.transforms.get(i).unwrap();
let new = a.morph(b, pct);
out.add_transform(new);
}
out
}
}
}
impl Default for IFS {
fn default() -> Self {
Self::new()
}
}