iced_graphics/
damage.rs

1//! Compute the damage between frames.
2use crate::core::{Point, Rectangle};
3
4/// Diffs the damage regions given some previous and current primitives.
5pub fn diff<T>(
6    previous: &[T],
7    current: &[T],
8    bounds: impl Fn(&T) -> Vec<Rectangle>,
9    diff: impl Fn(&T, &T) -> Vec<Rectangle>,
10) -> Vec<Rectangle> {
11    let damage = previous.iter().zip(current).flat_map(|(a, b)| diff(a, b));
12
13    if previous.len() == current.len() {
14        damage.collect()
15    } else {
16        let (smaller, bigger) = if previous.len() < current.len() {
17            (previous, current)
18        } else {
19            (current, previous)
20        };
21
22        // Extend damage by the added/removed primitives
23        damage
24            .chain(bigger[smaller.len()..].iter().flat_map(bounds))
25            .collect()
26    }
27}
28
29/// Computes the damage regions given some previous and current primitives.
30pub fn list<T>(
31    previous: &[T],
32    current: &[T],
33    bounds: impl Fn(&T) -> Vec<Rectangle>,
34    are_equal: impl Fn(&T, &T) -> bool,
35) -> Vec<Rectangle> {
36    diff(previous, current, &bounds, |a, b| {
37        if are_equal(a, b) {
38            vec![]
39        } else {
40            bounds(a).into_iter().chain(bounds(b)).collect()
41        }
42    })
43}
44
45/// Groups the given damage regions that are close together inside the given
46/// bounds.
47pub fn group(mut damage: Vec<Rectangle>, bounds: Rectangle) -> Vec<Rectangle> {
48    const AREA_THRESHOLD: f32 = 20_000.0;
49
50    damage.sort_by(|a, b| {
51        a.center()
52            .distance(Point::ORIGIN)
53            .total_cmp(&b.center().distance(Point::ORIGIN))
54    });
55
56    let mut output = Vec::new();
57    let mut scaled = damage
58        .into_iter()
59        .filter_map(|region| region.intersection(&bounds))
60        .filter(|region| region.width >= 1.0 && region.height >= 1.0);
61
62    if let Some(mut current) = scaled.next() {
63        for region in scaled {
64            let union = current.union(&region);
65
66            if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
67                current = union;
68            } else {
69                output.push(current);
70                current = region;
71            }
72        }
73
74        output.push(current);
75    }
76
77    output
78}