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
|
//! Compute the damage between frames.
use crate::core::Rectangle;
/// Diffs the damage regions given some previous and current primitives.
pub fn diff<T>(
previous: &[T],
current: &[T],
bounds: impl Fn(&T) -> Vec<Rectangle>,
diff: impl Fn(&T, &T) -> Vec<Rectangle>,
) -> Vec<Rectangle> {
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<T>(
previous: &[T],
current: &[T],
bounds: impl Fn(&T) -> Vec<Rectangle>,
are_equal: impl Fn(&T, &T) -> bool,
) -> Vec<Rectangle> {
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<Rectangle>, bounds: Rectangle) -> Vec<Rectangle> {
use std::cmp::Ordering;
const AREA_THRESHOLD: f32 = 20_000.0;
damage.sort_by(|a, b| {
a.x.partial_cmp(&b.x)
.unwrap_or(Ordering::Equal)
.then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
});
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
}
|