//! Compute the damage between frames. use crate::core::{Point, Rectangle}; /// Diffs the damage regions given some previous and current primitives. pub fn diff( previous: &[T], current: &[T], bounds: impl Fn(&T) -> Vec, diff: impl Fn(&T, &T) -> Vec, ) -> Vec { let damage = previous.iter().zip(current).flat_map(|(a, b)| diff(a, b)); if previous.len() == current.len() { damage.collect() } else { let (smaller, bigger) = if previous.len() < current.len() { (previous, current) } else { (current, previous) }; // Extend damage by the added/removed primitives damage .chain(bigger[smaller.len()..].iter().flat_map(bounds)) .collect() } } /// Computes the damage regions given some previous and current primitives. pub fn list( previous: &[T], current: &[T], bounds: impl Fn(&T) -> Vec, are_equal: impl Fn(&T, &T) -> bool, ) -> Vec { diff(previous, current, &bounds, |a, b| { if are_equal(a, b) { vec![] } else { bounds(a).into_iter().chain(bounds(b)).collect() } }) } /// Groups the given damage regions that are close together inside the given /// bounds. pub fn group(mut damage: Vec, bounds: Rectangle) -> Vec { const AREA_THRESHOLD: f32 = 20_000.0; damage.sort_by(|a, b| { a.center() .distance(Point::ORIGIN) .total_cmp(&b.center().distance(Point::ORIGIN)) }); let mut output = Vec::new(); let mut scaled = damage .into_iter() .filter_map(|region| region.intersection(&bounds)) .filter(|region| region.width >= 1.0 && region.height >= 1.0); if let Some(mut current) = scaled.next() { for region in scaled { let union = current.union(®ion); if union.area() - current.area() - region.area() <= AREA_THRESHOLD { current = union; } else { output.push(current); current = region; } } output.push(current); } output }