mod plugin;
pub use plugin::{JointComponentId, JointGraphPlugin};
use crate::{
data_structures::{
graph::{EdgeIndex, NodeIndex},
sparse_secondary_map::SparseSecondaryEntityMap,
stable_graph::StableUnGraph,
},
dynamics::solver::islands::IslandNode,
};
use bevy::prelude::*;
#[derive(Resource, Clone, Debug, Default)]
pub struct JointGraph {
graph: StableUnGraph<Entity, JointGraphEdge>,
entity_to_body: SparseSecondaryEntityMap<NodeIndex>,
entity_to_joint: SparseSecondaryEntityMap<EdgeIndex>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub struct JointId(pub u32);
impl JointId {
pub const PLACEHOLDER: Self = Self(u32::MAX);
}
impl From<JointId> for EdgeIndex {
fn from(id: JointId) -> Self {
Self(id.0)
}
}
impl From<EdgeIndex> for JointId {
fn from(id: EdgeIndex) -> Self {
Self(id.0)
}
}
impl core::fmt::Display for JointId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "JointId({})", self.0)
}
}
#[derive(Clone, Debug, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug)]
pub struct JointGraphEdge {
pub id: JointId,
pub entity: Entity,
pub body1: Entity,
pub body2: Entity,
pub collision_disabled: bool,
pub island: IslandNode<JointId>,
}
impl JointGraphEdge {
#[inline]
pub fn new(entity: Entity, body1: Entity, body2: Entity, collision_disabled: bool) -> Self {
Self {
id: JointId::PLACEHOLDER,
entity,
body1,
body2,
collision_disabled,
island: IslandNode::default(),
}
}
}
impl JointGraph {
#[inline]
pub fn graph(&self) -> &StableUnGraph<Entity, JointGraphEdge> {
&self.graph
}
#[inline]
pub fn entity_to_body(&self, entity: Entity) -> Option<NodeIndex> {
self.entity_to_body.get(entity).copied()
}
#[inline]
pub fn entity_to_joint(&self, entity: Entity) -> Option<JointId> {
self.entity_to_joint.get(entity).copied().map(JointId::from)
}
#[inline]
pub fn get(&self, joint: Entity) -> Option<&JointGraphEdge> {
let joint_index = self.entity_to_joint(joint)?;
self.get_by_id(joint_index)
}
#[inline]
pub fn get_by_id(&self, joint_id: JointId) -> Option<&JointGraphEdge> {
self.graph.edge_weight(joint_id.into())
}
#[inline]
pub fn get_mut(&mut self, joint: Entity) -> Option<&mut JointGraphEdge> {
let joint_index = self.entity_to_joint(joint)?;
self.get_mut_by_id(joint_index)
}
#[inline]
pub fn get_mut_by_id(&mut self, joint_id: JointId) -> Option<&mut JointGraphEdge> {
self.graph.edge_weight_mut(joint_id.into())
}
#[inline]
pub fn joints_between(
&self,
body1: Entity,
body2: Entity,
) -> impl Iterator<Item = &JointGraphEdge> {
let (Some(index1), Some(index2)) = (self.entity_to_body(body1), self.entity_to_body(body2))
else {
return itertools::Either::Left(core::iter::empty());
};
let joints = self.graph.edges_between(index1, index2).map(|e| e.weight());
itertools::Either::Right(joints)
}
#[inline]
pub fn joints_of(&self, body: Entity) -> impl Iterator<Item = &JointGraphEdge> {
let index = self.entity_to_body(body);
if let Some(index) = index {
itertools::Either::Left(self.graph.edge_weights(index))
} else {
itertools::Either::Right(core::iter::empty())
}
}
#[inline]
pub fn joints_of_mut(&mut self, body: Entity) -> impl Iterator<Item = &mut JointGraphEdge> {
let index = self.entity_to_body(body);
if let Some(index) = index {
itertools::Either::Left(self.graph.edge_weights_mut(index))
} else {
itertools::Either::Right(core::iter::empty())
}
}
#[inline]
pub fn bodies_of(&self, joint: Entity) -> Option<[Entity; 2]> {
let joint_index = self.entity_to_joint(joint)?;
let (body1_index, body2_index) = self.graph.edge_endpoints(joint_index.into())?;
Some([
*self.graph.node_weight(body1_index)?,
*self.graph.node_weight(body2_index)?,
])
}
#[inline]
pub fn bodies_attached_to(&self, body: Entity) -> impl Iterator<Item = Entity> + '_ {
self.entity_to_body
.get(body)
.into_iter()
.flat_map(move |&index| {
self.graph
.neighbors(index)
.map(|index| *self.graph.node_weight(index).unwrap())
})
}
#[inline]
pub fn add_joint(
&mut self,
body1: Entity,
body2: Entity,
joint_edge: JointGraphEdge,
) -> JointId {
let body1_index = self
.entity_to_body
.get_or_insert_with(body1, || self.graph.add_node(body1));
let body2_index = self
.entity_to_body
.get_or_insert_with(body2, || self.graph.add_node(body2));
let joint_entity = joint_edge.entity;
let edge_id = JointId(self.graph.add_edge(body1_index, body2_index, joint_edge).0);
self.entity_to_joint
.get_or_insert_with(joint_entity, || edge_id.into());
let edge = self.graph.edge_weight_mut(edge_id.into()).unwrap();
edge.id = edge_id;
edge_id
}
#[inline]
pub fn remove_joint(&mut self, joint_entity: Entity) -> Option<JointGraphEdge> {
let joint_index = self.entity_to_joint.remove(joint_entity)?;
self.graph.remove_edge(joint_index)
}
#[inline]
pub fn remove_body_with<F>(&mut self, entity: Entity, edge_callback: F)
where
F: FnMut(&mut StableUnGraph<Entity, JointGraphEdge>, EdgeIndex),
{
let Some(index) = self.entity_to_body.remove(entity) else {
return;
};
self.graph.remove_node_with(index, edge_callback);
if let Some(swapped) = self.graph.node_weight(index).copied() {
let swapped_index = self
.entity_to_body
.get_mut(swapped)
.expect("swapped entity has no entity-to-node mapping");
*swapped_index = index;
}
}
}