Skip to main content

box3d_rust/
id.rs

1//! Port of box3d-cpp-reference/include/box3d/id.h
2//!
3//! These ids are opaque handles to internal Box3D objects, passed by value. All
4//! ids are null when zero-initialized. The store/load helpers pack and unpack a
5//! handle into a plain integer; the bit layout is reproduced exactly so handles
6//! round-trip identically to the C library.
7//!
8//! Box3D has no chain id (unlike Box2D). Contact ids pack into three `u32`
9//! values rather than a single `u64`.
10//!
11//! SPDX-FileCopyrightText: 2026 Erin Catto
12//! SPDX-License-Identifier: MIT
13
14/// World id references a world instance. Treat as an opaque handle.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub struct WorldId {
17    pub index1: u16,
18    pub generation: u16,
19}
20
21/// Body id references a body instance. Treat as an opaque handle.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub struct BodyId {
24    pub index1: i32,
25    pub world0: u16,
26    pub generation: u16,
27}
28
29/// Shape id references a shape instance. Treat as an opaque handle.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31pub struct ShapeId {
32    pub index1: i32,
33    pub world0: u16,
34    pub generation: u16,
35}
36
37/// Joint id references a joint instance. Treat as an opaque handle.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub struct JointId {
40    pub index1: i32,
41    pub world0: u16,
42    pub generation: u16,
43}
44
45/// Contact id references a contact instance. Treat as an opaque handle.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
47pub struct ContactId {
48    pub index1: i32,
49    pub world0: u16,
50    pub padding: i16,
51    pub generation: u32,
52}
53
54/// A null world id. (`b3_nullWorldId`)
55pub const NULL_WORLD_ID: WorldId = WorldId {
56    index1: 0,
57    generation: 0,
58};
59
60/// A null body id. (`b3_nullBodyId`)
61pub const NULL_BODY_ID: BodyId = BodyId {
62    index1: 0,
63    world0: 0,
64    generation: 0,
65};
66
67/// A null shape id. (`b3_nullShapeId`)
68pub const NULL_SHAPE_ID: ShapeId = ShapeId {
69    index1: 0,
70    world0: 0,
71    generation: 0,
72};
73
74/// A null joint id. (`b3_nullJointId`)
75pub const NULL_JOINT_ID: JointId = JointId {
76    index1: 0,
77    world0: 0,
78    generation: 0,
79};
80
81/// A null contact id. (`b3_nullContactId`)
82pub const NULL_CONTACT_ID: ContactId = ContactId {
83    index1: 0,
84    world0: 0,
85    padding: 0,
86    generation: 0,
87};
88
89impl WorldId {
90    /// Store a world id into a u32. (`b3StoreWorldId`)
91    pub fn store(self) -> u32 {
92        ((self.index1 as u32) << 16) | (self.generation as u32)
93    }
94
95    /// Load a u32 into a world id. (`b3LoadWorldId`)
96    pub fn load(x: u32) -> WorldId {
97        WorldId {
98            index1: (x >> 16) as u16,
99            generation: x as u16,
100        }
101    }
102
103    /// True if this id is null (`index1 == 0`). (`B3_IS_NULL`)
104    pub fn is_null(self) -> bool {
105        self.index1 == 0
106    }
107
108    /// True if this id is non-null. (`B3_IS_NON_NULL`)
109    pub fn is_non_null(self) -> bool {
110        self.index1 != 0
111    }
112}
113
114// Body, shape, and joint ids share the same 64-bit layout, so a macro generates
115// their identical store/load/null helpers.
116macro_rules! impl_u64_id {
117    ($ty:ident) => {
118        impl $ty {
119            /// Store this id into a u64.
120            pub fn store(self) -> u64 {
121                ((self.index1 as u64) << 32)
122                    | ((self.world0 as u64) << 16)
123                    | (self.generation as u64)
124            }
125
126            /// Load a u64 into this id type.
127            pub fn load(x: u64) -> $ty {
128                $ty {
129                    index1: (x >> 32) as i32,
130                    world0: (x >> 16) as u16,
131                    generation: x as u16,
132                }
133            }
134
135            /// True if this id is null (`index1 == 0`). (`B3_IS_NULL`)
136            pub fn is_null(self) -> bool {
137                self.index1 == 0
138            }
139
140            /// True if this id is non-null. (`B3_IS_NON_NULL`)
141            pub fn is_non_null(self) -> bool {
142                self.index1 != 0
143            }
144
145            /// Compare two ids for equality. (`B3_ID_EQUALS`)
146            pub fn id_equals(self, other: Self) -> bool {
147                self.index1 == other.index1
148                    && self.world0 == other.world0
149                    && self.generation == other.generation
150            }
151        }
152    };
153}
154
155impl_u64_id!(BodyId);
156impl_u64_id!(ShapeId);
157impl_u64_id!(JointId);
158
159impl ContactId {
160    /// Store a contact id into three u32 values. (`b3StoreContactId`)
161    pub fn store(self) -> [u32; 3] {
162        [self.index1 as u32, self.world0 as u32, self.generation]
163    }
164
165    /// Load three u32 values into a contact id. (`b3LoadContactId`)
166    pub fn load(values: [u32; 3]) -> ContactId {
167        ContactId {
168            index1: values[0] as i32,
169            world0: values[1] as u16,
170            padding: 0,
171            generation: values[2],
172        }
173    }
174
175    /// True if this id is null (`index1 == 0`). (`B3_IS_NULL`)
176    pub fn is_null(self) -> bool {
177        self.index1 == 0
178    }
179
180    /// True if this id is non-null. (`B3_IS_NON_NULL`)
181    pub fn is_non_null(self) -> bool {
182        self.index1 != 0
183    }
184
185    /// Compare two contact ids for equality. (`B3_ID_EQUALS`)
186    pub fn id_equals(self, other: Self) -> bool {
187        self.index1 == other.index1
188            && self.world0 == other.world0
189            && self.generation == other.generation
190    }
191}