avian2d/dynamics/solver/joint_graph/
mod.rs1mod plugin;
7pub use plugin::{JointComponentId, JointGraphPlugin};
8
9use crate::{
10 data_structures::{
11 graph::{EdgeIndex, NodeIndex},
12 sparse_secondary_map::SparseSecondaryEntityMap,
13 stable_graph::StableUnGraph,
14 },
15 dynamics::solver::islands::IslandNode,
16};
17use bevy::prelude::*;
18
19#[derive(Resource, Clone, Debug, Default)]
26pub struct JointGraph {
27 graph: StableUnGraph<Entity, JointGraphEdge>,
28 entity_to_body: SparseSecondaryEntityMap<NodeIndex>,
29 entity_to_joint: SparseSecondaryEntityMap<EdgeIndex>,
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Reflect)]
34#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
35#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
36#[reflect(Debug, PartialEq)]
37pub struct JointId(pub u32);
38
39impl JointId {
40 pub const PLACEHOLDER: Self = Self(u32::MAX);
42}
43
44impl From<JointId> for EdgeIndex {
45 fn from(id: JointId) -> Self {
46 Self(id.0)
47 }
48}
49
50impl From<EdgeIndex> for JointId {
51 fn from(id: EdgeIndex) -> Self {
52 Self(id.0)
53 }
54}
55
56impl core::fmt::Display for JointId {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 write!(f, "JointId({})", self.0)
59 }
60}
61
62#[derive(Clone, Debug, Reflect)]
64#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
65#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
66#[reflect(Debug)]
67pub struct JointGraphEdge {
68 pub id: JointId,
70
71 pub entity: Entity,
73
74 pub body1: Entity,
78
79 pub body2: Entity,
83
84 pub collision_disabled: bool,
90
91 pub island: IslandNode<JointId>,
93}
94
95impl JointGraphEdge {
96 #[inline]
98 pub fn new(entity: Entity, body1: Entity, body2: Entity, collision_disabled: bool) -> Self {
99 Self {
100 id: JointId::PLACEHOLDER,
102 entity,
103 body1,
104 body2,
105 collision_disabled,
106 island: IslandNode::default(),
107 }
108 }
109}
110
111impl JointGraph {
112 #[inline]
114 pub fn graph(&self) -> &StableUnGraph<Entity, JointGraphEdge> {
115 &self.graph
116 }
117
118 #[inline]
122 pub fn entity_to_body(&self, entity: Entity) -> Option<NodeIndex> {
123 self.entity_to_body.get(entity).copied()
124 }
125
126 #[inline]
130 pub fn entity_to_joint(&self, entity: Entity) -> Option<JointId> {
131 self.entity_to_joint.get(entity).copied().map(JointId::from)
132 }
133
134 #[inline]
137 pub fn get(&self, joint: Entity) -> Option<&JointGraphEdge> {
138 let joint_index = self.entity_to_joint(joint)?;
139 self.get_by_id(joint_index)
140 }
141
142 #[inline]
145 pub fn get_by_id(&self, joint_id: JointId) -> Option<&JointGraphEdge> {
146 self.graph.edge_weight(joint_id.into())
147 }
148
149 #[inline]
152 pub fn get_mut(&mut self, joint: Entity) -> Option<&mut JointGraphEdge> {
153 let joint_index = self.entity_to_joint(joint)?;
154 self.get_mut_by_id(joint_index)
155 }
156
157 #[inline]
160 pub fn get_mut_by_id(&mut self, joint_id: JointId) -> Option<&mut JointGraphEdge> {
161 self.graph.edge_weight_mut(joint_id.into())
162 }
163
164 #[inline]
167 pub fn joints_between(
168 &self,
169 body1: Entity,
170 body2: Entity,
171 ) -> impl Iterator<Item = &JointGraphEdge> {
172 let (Some(index1), Some(index2)) = (self.entity_to_body(body1), self.entity_to_body(body2))
173 else {
174 return itertools::Either::Left(core::iter::empty());
175 };
176
177 let joints = self.graph.edges_between(index1, index2).map(|e| e.weight());
178 itertools::Either::Right(joints)
179 }
180
181 #[inline]
183 pub fn joints_of(&self, body: Entity) -> impl Iterator<Item = &JointGraphEdge> {
184 let index = self.entity_to_body(body);
185 if let Some(index) = index {
186 itertools::Either::Left(self.graph.edge_weights(index))
187 } else {
188 itertools::Either::Right(core::iter::empty())
189 }
190 }
191
192 #[inline]
194 pub fn joints_of_mut(&mut self, body: Entity) -> impl Iterator<Item = &mut JointGraphEdge> {
195 let index = self.entity_to_body(body);
196 if let Some(index) = index {
197 itertools::Either::Left(self.graph.edge_weights_mut(index))
198 } else {
199 itertools::Either::Right(core::iter::empty())
200 }
201 }
202
203 #[inline]
206 pub fn bodies_of(&self, joint: Entity) -> Option<[Entity; 2]> {
207 let joint_index = self.entity_to_joint(joint)?;
208 let (body1_index, body2_index) = self.graph.edge_endpoints(joint_index.into())?;
209
210 Some([
211 *self.graph.node_weight(body1_index)?,
212 *self.graph.node_weight(body2_index)?,
213 ])
214 }
215
216 #[inline]
219 pub fn bodies_attached_to(&self, body: Entity) -> impl Iterator<Item = Entity> + '_ {
220 self.entity_to_body
221 .get(body)
222 .into_iter()
223 .flat_map(move |&index| {
224 self.graph
225 .neighbors(index)
226 .map(|index| *self.graph.node_weight(index).unwrap())
227 })
228 }
229
230 #[inline]
238 pub fn add_joint(
239 &mut self,
240 body1: Entity,
241 body2: Entity,
242 joint_edge: JointGraphEdge,
243 ) -> JointId {
244 let body1_index = self
246 .entity_to_body
247 .get_or_insert_with(body1, || self.graph.add_node(body1));
248 let body2_index = self
249 .entity_to_body
250 .get_or_insert_with(body2, || self.graph.add_node(body2));
251
252 let joint_entity = joint_edge.entity;
254 let edge_id = JointId(self.graph.add_edge(body1_index, body2_index, joint_edge).0);
255
256 self.entity_to_joint
258 .get_or_insert_with(joint_entity, || edge_id.into());
259
260 let edge = self.graph.edge_weight_mut(edge_id.into()).unwrap();
262 edge.id = edge_id;
263
264 edge_id
265 }
266
267 #[inline]
274 pub fn remove_joint(&mut self, joint_entity: Entity) -> Option<JointGraphEdge> {
275 let joint_index = self.entity_to_joint.remove(joint_entity)?;
276 self.graph.remove_edge(joint_index)
277 }
278
279 #[inline]
287 pub fn remove_body_with<F>(&mut self, entity: Entity, edge_callback: F)
288 where
289 F: FnMut(&mut StableUnGraph<Entity, JointGraphEdge>, EdgeIndex),
290 {
291 let Some(index) = self.entity_to_body.remove(entity) else {
294 return;
295 };
296
297 self.graph.remove_node_with(index, edge_callback);
300
301 if let Some(swapped) = self.graph.node_weight(index).copied() {
304 let swapped_index = self
305 .entity_to_body
306 .get_mut(swapped)
307 .expect("swapped entity has no entity-to-node mapping");
309 *swapped_index = index;
310 }
311 }
312}