Skip to main content

box2d_rust/body/
access.rs

1// Body public API from body.c, part 3: shape/joint/contact list accessors
2// and connectivity validation (b2Body_*).
3//
4// SPDX-FileCopyrightText: 2023 Erin Catto
5// SPDX-License-Identifier: MIT
6
7use super::get_body_full_id;
8use crate::core::NULL_INDEX;
9use crate::id::{BodyId, JointId, ShapeId};
10use crate::solver_set::{AWAKE_SET, DISABLED_SET, STATIC_SET};
11use crate::types::BodyType;
12use crate::world::World;
13
14/// (b2Body_GetShapeCount)
15pub fn body_get_shape_count(world: &World, body_id: BodyId) -> i32 {
16    let body_index = get_body_full_id(world, body_id);
17    world.bodies[body_index as usize].shape_count
18}
19
20/// Fills at most `capacity` shape ids. (b2Body_GetShapes)
21pub fn body_get_shapes(world: &World, body_id: BodyId, capacity: usize) -> Vec<ShapeId> {
22    let body_index = get_body_full_id(world, body_id);
23    let mut out = Vec::new();
24    let mut shape_id = world.bodies[body_index as usize].head_shape_id;
25    while shape_id != NULL_INDEX && out.len() < capacity {
26        let shape = &world.shapes[shape_id as usize];
27        out.push(ShapeId {
28            index1: shape_id + 1,
29            world0: world.world_id,
30            generation: shape.generation,
31        });
32        shape_id = shape.next_shape_id;
33    }
34    out
35}
36
37/// (b2Body_GetJointCount)
38pub fn body_get_joint_count(world: &World, body_id: BodyId) -> i32 {
39    let body_index = get_body_full_id(world, body_id);
40    world.bodies[body_index as usize].joint_count
41}
42
43/// Fills at most `capacity` joint ids. (b2Body_GetJoints)
44pub fn body_get_joints(world: &World, body_id: BodyId, capacity: usize) -> Vec<JointId> {
45    let body_index = get_body_full_id(world, body_id);
46    let mut out = Vec::new();
47    let mut joint_key = world.bodies[body_index as usize].head_joint_key;
48    while joint_key != NULL_INDEX && out.len() < capacity {
49        let joint_id = joint_key >> 1;
50        let edge_index = joint_key & 1;
51
52        let joint = &world.joints[joint_id as usize];
53        out.push(JointId {
54            index1: joint_id + 1,
55            world0: world.world_id,
56            generation: joint.generation,
57        });
58
59        joint_key = joint.edges[edge_index as usize].next_key;
60    }
61    out
62}
63
64/// Conservative and fast. (b2Body_GetContactCapacity)
65pub fn body_get_contact_capacity(world: &World, body_id: BodyId) -> i32 {
66    debug_assert!(!world.locked);
67    let body_index = get_body_full_id(world, body_id);
68    world.bodies[body_index as usize].contact_count
69}
70
71/// Touching contact data for a body, at most `capacity` entries.
72/// (b2Body_GetContactData)
73pub fn body_get_contact_data(
74    world: &World,
75    body_id: BodyId,
76    capacity: usize,
77) -> Vec<crate::events::ContactData> {
78    debug_assert!(!world.locked);
79    let body_index = get_body_full_id(world, body_id);
80
81    let mut out = Vec::new();
82    let mut contact_key = world.bodies[body_index as usize].head_contact_key;
83    while contact_key != NULL_INDEX && out.len() < capacity {
84        let contact_id = contact_key >> 1;
85        let edge_index = contact_key & 1;
86
87        let contact = &world.contacts[contact_id as usize];
88
89        // Is contact touching?
90        if contact.flags & crate::contact::contact_flags::TOUCHING != 0 {
91            let shape_a = &world.shapes[contact.shape_id_a as usize];
92            let shape_b = &world.shapes[contact.shape_id_b as usize];
93
94            let contact_sim = if contact.set_index == AWAKE_SET && contact.color_index != NULL_INDEX
95            {
96                &world.constraint_graph.colors[contact.color_index as usize].contact_sims
97                    [contact.local_index as usize]
98            } else {
99                &world.solver_sets[contact.set_index as usize].contact_sims
100                    [contact.local_index as usize]
101            };
102
103            out.push(crate::events::ContactData {
104                contact_id: crate::id::ContactId {
105                    index1: contact_id + 1,
106                    world0: world.world_id,
107                    padding: 0,
108                    generation: contact.generation,
109                },
110                shape_id_a: ShapeId {
111                    index1: shape_a.id + 1,
112                    world0: world.world_id,
113                    generation: shape_a.generation,
114                },
115                shape_id_b: ShapeId {
116                    index1: shape_b.id + 1,
117                    world0: world.world_id,
118                    generation: shape_b.generation,
119                },
120                manifold: contact_sim.manifold,
121            });
122        }
123
124        contact_key = contact.edges[edge_index as usize].next_key;
125    }
126
127    out
128}
129
130impl World {
131    /// (b2ValidateConnectivity — C compiles the body under
132    /// B2_ENABLE_VALIDATION; here the whole check runs in debug builds only)
133    pub fn validate_connectivity(&self) {
134        if !cfg!(debug_assertions) {
135            return;
136        }
137
138        for body_index in 0..self.bodies.len() as i32 {
139            let body = &self.bodies[body_index as usize];
140            if body.id == NULL_INDEX {
141                self.body_id_pool.validate_free_id(body_index);
142                continue;
143            }
144
145            self.body_id_pool.validate_used_id(body_index);
146
147            debug_assert!(body_index == body.id);
148
149            let body_island_id = body.island_id;
150            let body_set_index = body.set_index;
151
152            let mut contact_key = body.head_contact_key;
153            while contact_key != NULL_INDEX {
154                let contact_id = contact_key >> 1;
155                let edge_index = contact_key & 1;
156
157                let contact = &self.contacts[contact_id as usize];
158
159                let touching = contact.flags & crate::contact::contact_flags::TOUCHING != 0;
160                if touching {
161                    if body_set_index != STATIC_SET {
162                        debug_assert!(contact.island_id == body_island_id);
163                    }
164                } else {
165                    debug_assert!(contact.island_id == NULL_INDEX);
166                }
167
168                contact_key = contact.edges[edge_index as usize].next_key;
169            }
170
171            let mut joint_key = body.head_joint_key;
172            while joint_key != NULL_INDEX {
173                let joint_id = joint_key >> 1;
174                let edge_index = joint_key & 1;
175
176                let joint = &self.joints[joint_id as usize];
177
178                let other_edge_index = edge_index ^ 1;
179                let other_body =
180                    &self.bodies[joint.edges[other_edge_index as usize].body_id as usize];
181
182                if body_set_index == DISABLED_SET || other_body.set_index == DISABLED_SET {
183                    debug_assert!(joint.island_id == NULL_INDEX);
184                } else if body_set_index == STATIC_SET {
185                    // Intentional nesting
186                    if other_body.set_index == STATIC_SET {
187                        debug_assert!(joint.island_id == NULL_INDEX);
188                    }
189                } else if body.type_ != BodyType::Dynamic && other_body.type_ != BodyType::Dynamic {
190                    debug_assert!(joint.island_id == NULL_INDEX);
191                } else {
192                    debug_assert!(joint.island_id == body_island_id);
193                }
194
195                joint_key = joint.edges[edge_index as usize].next_key;
196            }
197        }
198    }
199}