use crate::traits::*;
use glam::I16Vec2;
fn check_physical_collision<E1, E2>(one: &E1, other: &E2) -> Option<(ColliderType, ColliderType)>
where
E1: Entity,
E2: Entity,
{
if one.previous_position() == one.position() {
return None;
}
let path = one.previous_position().as_i16vec2() - one.position().as_i16vec2();
let other_position = other.position().as_i16vec2();
let other_hit_box = other.hit_box();
let offsets: Box<dyn Iterator<Item = I16Vec2>> = if path.x != 0 {
let slope = path.y as f32 / path.x as f32;
let xs: Box<dyn Iterator<Item = i16>> = if path.x > 0 {
Box::new(0..=path.x)
} else {
Box::new(path.x..=0)
};
Box::new(xs.map(move |x| I16Vec2::new(x, (slope * x as f32).round() as i16)))
} else {
let ys: Box<dyn Iterator<Item = i16>> = if path.y > 0 {
Box::new(0..=path.y)
} else {
Box::new(path.y..=0)
};
Box::new(ys.map(|y| I16Vec2::new(0, y)))
};
for offset in offsets {
for (&point, &one_collider_type) in one.hit_box().iter() {
let hit_box_position = (one.position() + point).as_i16vec2() + offset;
if hit_box_position.x < other_position.x || hit_box_position.y < other_position.y {
continue;
}
let g_point = (hit_box_position - other_position).as_u16vec2();
if let Some(&other_collider_type) = other_hit_box.get(&g_point) {
return Some((one_collider_type, other_collider_type));
}
}
}
None
}
fn check_granular_phase_collision<E1, E2>(
one: &E1,
other: &E2,
) -> Option<(ColliderType, ColliderType)>
where
E1: Entity,
E2: Entity,
{
for (&point, &one_collider_type) in one.hit_box().iter() {
let hit_box_position = one.position() + point;
let other_position = other.position();
if hit_box_position.x < other_position.x || hit_box_position.y < other_position.y {
continue;
}
let g_point = hit_box_position - other_position;
if let Some(&other_collider_type) = other.hit_box().get(&g_point) {
return Some((one_collider_type, other_collider_type));
}
}
None
}
pub fn check_broad_phase_collision<E1, E2>(one: &E1, other: &E2) -> bool
where
E1: Entity,
E2: Entity,
{
let (s1_min, s1_max) = (
one.previous_position(),
one.previous_position() + one.size(),
);
let (o1_min, o1_max) = (
other.previous_position(),
other.previous_position() + other.size(),
);
let (s2_min, s2_max) = (one.position(), one.position() + one.size());
let (o2_min, o2_max) = (other.position(), other.position() + other.size());
if (s1_min.x > o1_max.x && s2_min.x > o2_max.x)
|| (o1_min.x > s1_max.x && o2_min.x > s2_max.x)
|| (s1_min.y > o1_max.y && s2_min.y > o2_max.y)
|| (o1_min.y > s1_max.y && o2_min.y > s2_max.y)
{
return false;
}
true
}
pub fn are_colliding<E1, E2>(one: &E1, other: &E2) -> Option<(ColliderType, ColliderType)>
where
E1: Entity,
E2: Entity,
{
if !check_broad_phase_collision(one, other) {
return None;
}
if let Some(colliders) = check_granular_phase_collision(one, other) {
return Some(colliders);
}
if let Some(colliders) = check_physical_collision(one, other) {
log::debug!(
"Found physical collision! {}->{} hit {:#?}",
one.previous_position(),
one.position(),
other.rect()
);
return Some(colliders);
}
if let Some(colliders) = check_physical_collision(other, one) {
log::debug!(
"Found physical collision! {}->{} hit {:#?}",
other.previous_position(),
other.position(),
one.rect()
);
return Some((colliders.1, colliders.0));
}
None
}
pub fn inelastic_collision<E1, E2>(one: &mut E1, other: &mut E2, coefficient_of_restituion: f32)
where
E1: Entity,
E2: Entity,
{
one.set_position(one.previous_position());
other.set_position(other.previous_position());
let v2_one = if one.mass() == f32::INFINITY {
one.velocity()
} else if other.mass() == f32::INFINITY {
coefficient_of_restituion * (other.velocity() - one.velocity()) + other.velocity()
} else {
(coefficient_of_restituion * other.mass() * (other.velocity() - one.velocity())
+ one.mass() * one.velocity()
+ other.mass() * other.velocity())
/ (one.mass() + other.mass())
};
let v2_other = if one.mass() == f32::INFINITY {
coefficient_of_restituion * (one.velocity() - other.velocity()) + one.velocity()
} else if other.mass() == f32::INFINITY {
other.velocity()
} else {
(coefficient_of_restituion * one.mass() * (one.velocity() - other.velocity())
+ one.mass() * one.velocity()
+ other.mass() * other.velocity())
/ (one.mass() + other.mass())
};
one.set_velocity(v2_one);
other.set_velocity(v2_other);
}