Since Rust version 1.81, sort_by will panic if the provided comparison function does not implement a total order. See https://github.com/rust/lang/rust/issues/129561 for more details. The simplest fix seems to be to use total_cmp instead of partial_cmp.
78 lines
2.2 KiB
Rust
78 lines
2.2 KiB
Rust
//! Compute the damage between frames.
|
|
use crate::core::{Point, 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> {
|
|
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
|
|
}
|