use std;
use stash::Stash;
use vec_map::VecMap;
#[cfg(feature = "derive_serdes")]
use serde::{Deserialize, Serialize};
use crate::{constraint, event, math, object};
use super::{CONTACT_DISTANCE, InternalId, ObjectPair, Proximity};
pub (crate) mod group;
pub (crate) use self::group::Group;
#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Contact {
pub constraint : constraint::Planar
}
#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct Colliding {
pub contact : Contact,
pub restitution : math::Normalized <f64>
}
#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
#[derive(Clone, Debug)]
pub (crate) struct Manager {
pub contact_groups : Option <Stash <Group>>,
object_contacts_static : VecMap <u32>,
object_group_contacts_dynamic : VecMap <(group::KeyType, u32)>
}
impl Manager {
pub(super) fn get_group (&self, key : group::KeyType) -> Option <&Group> {
self.contact_groups.as_ref().unwrap().get (key as usize)
}
pub(super) fn get_group_mut (&mut self, key : group::KeyType) -> Option <&mut Group> {
self.contact_groups.as_mut().unwrap().get_mut (key as usize)
}
pub(super) fn remove_group (&mut self, key : group::KeyType) -> Option <Group> {
self.contact_groups.as_mut().unwrap().take (key as usize)
}
pub(super) fn add_contact (&mut self, object_pair : ObjectPair, contact : Contact) {
let (object_id_a, object_id_b) = object_pair.into();
let group = match (
self.get_group_key (object_id_a), self.get_group_key (object_id_b)
) {
(Some (group_a), None) => {
self.increment_contact (object_id_a);
self.assign_dynamic_group_key (object_id_b, group_a);
self.get_group_mut (group_a).unwrap()
}
(None, Some (group_b)) => {
if object_id_a.kind() == object::Kind::Dynamic {
self.assign_dynamic_group_key (object_id_a, group_b);
} else {
self.increment_contact (object_id_a);
}
self.increment_contact (object_id_b);
self.get_group_mut (group_b).unwrap()
}
(Some (group_a), Some (group_b)) => {
self.increment_contact (object_id_a);
self.increment_contact (object_id_b);
if group_a == group_b {
self.get_group_mut (group_a).unwrap()
} else {
let group_b = self.remove_group (group_b).unwrap();
for (pair, _) in group_b.contacts.iter() {
let (id_a, id_b) = (*pair).into();
if id_a.kind() == object::Kind::Dynamic {
self.change_dynamic_group_key (id_a, group_a);
}
self.change_dynamic_group_key (id_b, group_a);
}
let group_a = self.get_group_mut (group_a).unwrap();
group_a.contacts.extend (group_b.contacts);
group_a
}
}
(None, None) => {
let new_group_index = self.contact_groups.as_mut().unwrap()
.put (Group::default());
debug_assert!(new_group_index < group::KeyType::MAX as usize);
let new_group_key = new_group_index as group::KeyType;
if object_id_a.kind() == object::Kind::Dynamic {
self.assign_dynamic_group_key (object_id_a, new_group_key);
} else {
self.increment_contact (object_id_a);
}
self.assign_dynamic_group_key (object_id_b, new_group_key);
self.get_group_mut (new_group_key).unwrap()
}
};
group.contacts.push ((object_pair, contact));
}
#[must_use]
pub(super) fn remove_object (&mut self, object_id : InternalId) -> bool {
if let Some (group_key) = self.get_group_key (object_id) {
debug_assert_eq!(object_id.kind(), object::Kind::Dynamic);
let group = self.get_group_mut (group_key).unwrap();
let removed = group.remove_object (object_id);
let empty = group.contacts.is_empty();
if empty {
let _ = self.remove_group (group_key).unwrap();
}
for id in removed {
self.decrement_contact (id);
}
self.object_group_contacts_dynamic.remove (object_id.key().index()).unwrap();
true
} else if object_id.kind() == object::Kind::Static {
let index = object_id.key().index();
if let Some (mut contact_count) = self.object_contacts_static.remove (index) {
debug_assert!(contact_count > 0);
let mut empty = vec![];
let mut contact_groups = self.contact_groups.take().unwrap();
for (i, group) in contact_groups.iter_mut() {
let removed = group.remove_object (object_id);
if group.contacts.is_empty() {
empty.push (i);
}
contact_count -= removed.len() as u32;
for id in removed {
self.decrement_contact (id);
}
if contact_count == 0 {
break
}
}
for i in empty {
contact_groups.take (i);
}
self.contact_groups = Some (contact_groups);
true
} else {
false
}
} else {
false
}
}
pub(super) fn output_contacts (&self, output : &mut Vec <event::Output>) {
for group in self.contact_groups.as_ref().unwrap().values() {
for (object_pair, contact) in group.contacts.iter().cloned() {
let (id_a, id_b) = object_pair.into();
output.push (event::Contact {
object_id_a: id_a.into(),
object_id_b: id_b.into(),
contact
}.into())
}
}
}
pub(super) fn remove_contacts (&mut self, group : &mut Group, remove_list : &[u32]) {
debug_assert!(!remove_list.is_empty());
group.contacts = group.contacts.drain (..).enumerate().filter_map (
|(i, contact@(object_pair, _))|
if remove_list.contains (&(i as u32)) {
let (object_id_a, object_id_b) = object_pair.into();
self.decrement_contact (object_id_a);
self.decrement_contact (object_id_b);
None
} else {
Some (contact)
}
).collect();
}
pub(super) fn get_contact_count (&self, object_id : InternalId) -> Option <u32> {
let index = object_id.key().index();
match object_id.kind() {
object::Kind::Static => self.object_contacts_static.get (index).copied(),
object::Kind::Dynamic => self.object_group_contacts_dynamic.get (index)
.map (|g| g.1),
object::Kind::Nodetect => unreachable!()
}
}
pub(super) fn get_group_key (&self, object_id : InternalId)
-> Option <group::KeyType>
{
let index = object_id.key().index();
match object_id.kind() {
object::Kind::Static => None,
object::Kind::Dynamic => self.object_group_contacts_dynamic.get (index)
.map (|g| g.0),
object::Kind::Nodetect => unreachable!()
}
}
pub(super) fn change_dynamic_group_key (&mut self,
object_id : InternalId, group_key : group::KeyType
) {
match object_id.kind() {
object::Kind::Static => unreachable!(),
object::Kind::Dynamic => {
let index = object_id.key().index();
self.object_group_contacts_dynamic[index].0 = group_key;
}
object::Kind::Nodetect => unimplemented!()
}
}
fn assign_dynamic_group_key (&mut self,
object_id : InternalId, group_key : group::KeyType
) {
let index = object_id.key().index();
match object_id.kind() {
object::Kind::Static => unreachable!("call increment_contact instead"),
object::Kind::Dynamic =>
assert!(self.object_group_contacts_dynamic.insert (index, (group_key, 1))
.is_none()),
object::Kind::Nodetect => unimplemented!()
}
}
fn increment_contact (&mut self, object_id : InternalId) {
let index = object_id.key().index();
match object_id.kind() {
object::Kind::Static =>
*self.object_contacts_static.entry (index).or_insert (0) += 1,
object::Kind::Dynamic => self.object_group_contacts_dynamic[index].1 += 1,
object::Kind::Nodetect => unimplemented!()
}
}
fn decrement_contact (&mut self, object_id : InternalId) {
let index = object_id.key().index();
match object_id.kind() {
object::Kind::Dynamic => {
let count = &mut self.object_group_contacts_dynamic.get_mut (index).unwrap().1;
*count -= 1;
if *count == 0 {
self.object_group_contacts_dynamic.remove (index);
}
}
object::Kind::Static => {
let count = self.object_contacts_static.get_mut (index).unwrap();
*count -= 1;
if *count == 0 {
self.object_contacts_static.remove (index);
}
}
object::Kind::Nodetect => unreachable!()
}
}
}
impl Default for Manager {
fn default() -> Self {
Manager {
contact_groups: Some (Stash::default()),
object_contacts_static: VecMap::default(),
object_group_contacts_dynamic: VecMap::default()
}
}
}
impl TryFrom <Proximity> for Contact {
type Error = ();
fn try_from (proximity : Proximity) -> Result <Self, Self::Error> {
if proximity.distance >= 0.0 && proximity.distance < CONTACT_DISTANCE {
Ok (Contact { constraint: proximity.into() })
} else {
Err (())
}
}
}
impl std::ops::Deref for Colliding {
type Target = Contact;
fn deref (&self) -> &Contact {
&self.contact
}
}
impl Eq for Colliding { }
#[expect(clippy::non_canonical_partial_ord_impl)]
impl PartialOrd for Colliding {
fn partial_cmp (&self, _rhs : &Self) -> Option <std::cmp::Ordering> {
unreachable!()
}
}
impl Ord for Colliding {
fn cmp (&self, _other : &Self) -> std::cmp::Ordering {
unreachable!()
}
}