use core::cmp::Ordering;
use crate::physics::BodyHandle;
use crate::physics::sim::broadphase::Pair;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct Overlap {
pub(crate) pair: Pair,
pub(crate) a: BodyHandle,
pub(crate) b: BodyHandle,
}
impl Overlap {
fn same_bodies(&self, other: &Overlap) -> bool {
self.a == other.a && self.b == other.b
}
}
pub(crate) fn transitions(
previous: &[Overlap],
current: &[Overlap],
mut report: impl FnMut(&Overlap, bool),
) {
let (mut was, mut now) = (0usize, 0usize);
while was < previous.len() && now < current.len() {
let (left, right) = (&previous[was], ¤t[now]);
match left.pair.cmp(&right.pair) {
Ordering::Less => {
report(left, false);
was += 1;
}
Ordering::Greater => {
report(right, true);
now += 1;
}
Ordering::Equal => {
if !left.same_bodies(right) {
report(left, false);
report(right, true);
}
was += 1;
now += 1;
}
}
}
for left in &previous[was..] {
report(left, false);
}
for right in ¤t[now..] {
report(right, true);
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use super::*;
fn handle(slot: u32, generation: u32) -> BodyHandle {
BodyHandle::from_parts(slot, generation)
}
fn overlap(a: u32, b: u32) -> Overlap {
Overlap {
pair: (a, b),
a: handle(a, 0),
b: handle(b, 0),
}
}
fn crossed(previous: &[Overlap], current: &[Overlap]) -> Vec<(Pair, bool)> {
let mut out = Vec::new();
transitions(previous, current, |overlap, entered| {
out.push((overlap.pair, entered));
});
out
}
#[test]
fn a_pair_that_arrives_enters_and_a_pair_that_goes_leaves() {
assert_eq!(crossed(&[], &[overlap(0, 1)]), [((0, 1), true)]);
assert_eq!(crossed(&[overlap(0, 1)], &[]), [((0, 1), false)]);
}
#[test]
fn a_pair_that_stayed_reports_nothing() {
assert!(crossed(&[overlap(0, 1)], &[overlap(0, 1)]).is_empty());
}
#[test]
fn the_merge_reports_both_sides_in_slot_order() {
let previous = [overlap(0, 1), overlap(0, 5), overlap(3, 4)];
let current = [overlap(0, 2), overlap(0, 5), overlap(3, 9)];
assert_eq!(
crossed(&previous, ¤t),
[
((0, 1), false),
((0, 2), true),
((3, 4), false),
((3, 9), true),
]
);
}
#[test]
fn a_reused_slot_leaves_and_arrives_rather_than_staying() {
let previous = [overlap(0, 1)];
let current = [Overlap {
pair: (0, 1),
a: handle(0, 0),
b: handle(1, 7),
}];
assert_eq!(
crossed(&previous, ¤t),
[((0, 1), false), ((0, 1), true)]
);
}
#[test]
fn two_empty_lists_report_nothing() {
assert!(crossed(&[], &[]).is_empty());
}
#[test]
fn the_tail_of_the_longer_list_is_still_reported() {
assert_eq!(
crossed(
&[overlap(0, 1)],
&[overlap(0, 1), overlap(2, 3), overlap(4, 5)]
),
[((2, 3), true), ((4, 5), true)]
);
assert_eq!(
crossed(
&[overlap(0, 1), overlap(2, 3), overlap(4, 5)],
&[overlap(0, 1)]
),
[((2, 3), false), ((4, 5), false)]
);
}
}