boxdd/shapes/
mod.rs

1//! Shapes API
2//!
3//! Safe wrappers around Box2D shapes. Shapes are attached to bodies and can be
4//! modified at runtime. Use `ShapeDef` and `Body::create_*_shape` helpers to
5//! create shapes.
6use std::marker::PhantomData;
7pub mod chain;
8pub mod helpers;
9
10use crate::body::Body;
11use crate::error::ApiResult;
12use crate::filter::Filter;
13use crate::types::{BodyId, ChainId, ShapeId, Vec2};
14use crate::world::World;
15use boxdd_sys::ffi;
16use std::os::raw::c_void;
17use std::rc::Rc;
18use std::sync::Arc;
19
20/// A scoped shape handle tied to a mutable borrow of the world.
21pub struct Shape<'w> {
22    pub(crate) id: ShapeId,
23    #[allow(dead_code)]
24    pub(crate) core: Arc<crate::core::world_core::WorldCore>,
25    _world: PhantomData<&'w World>,
26}
27
28/// A RAII-owned shape that is destroyed on drop.
29pub struct OwnedShape {
30    id: ShapeId,
31    core: Arc<crate::core::world_core::WorldCore>,
32    destroy_on_drop: bool,
33    update_body_mass_on_drop: bool,
34    _not_send: PhantomData<Rc<()>>,
35}
36
37impl OwnedShape {
38    pub(crate) fn new(core: Arc<crate::core::world_core::WorldCore>, id: ShapeId) -> Self {
39        core.owned_shapes
40            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
41        Self {
42            id,
43            core,
44            destroy_on_drop: true,
45            update_body_mass_on_drop: true,
46            _not_send: PhantomData,
47        }
48    }
49
50    pub fn id(&self) -> ShapeId {
51        self.id
52    }
53
54    pub fn world_id(&self) -> ffi::b2WorldId {
55        self.assert_valid();
56        unsafe { ffi::b2Shape_GetWorld(self.id) }
57    }
58
59    pub fn try_world_id(&self) -> ApiResult<ffi::b2WorldId> {
60        self.check_valid()?;
61        Ok(unsafe { ffi::b2Shape_GetWorld(self.id) })
62    }
63
64    pub fn parent_chain_id(&self) -> Option<ChainId> {
65        self.assert_valid();
66        let cid = unsafe { ffi::b2Shape_GetParentChain(self.id) };
67        if unsafe { ffi::b2Chain_IsValid(cid) } {
68            Some(cid)
69        } else {
70            None
71        }
72    }
73
74    pub fn try_parent_chain_id(&self) -> ApiResult<Option<ChainId>> {
75        self.check_valid()?;
76        let cid = unsafe { ffi::b2Shape_GetParentChain(self.id) };
77        if unsafe { ffi::b2Chain_IsValid(cid) } {
78            Ok(Some(cid))
79        } else {
80            Ok(None)
81        }
82    }
83
84    pub fn is_valid(&self) -> bool {
85        crate::core::callback_state::assert_not_in_callback();
86        unsafe { ffi::b2Shape_IsValid(self.id) }
87    }
88
89    pub fn try_is_valid(&self) -> ApiResult<bool> {
90        crate::core::callback_state::check_not_in_callback()?;
91        Ok(unsafe { ffi::b2Shape_IsValid(self.id) })
92    }
93
94    #[inline]
95    fn assert_valid(&self) {
96        crate::core::debug_checks::assert_shape_valid(self.id);
97    }
98
99    #[inline]
100    fn check_valid(&self) -> ApiResult<()> {
101        crate::core::debug_checks::check_shape_valid(self.id)
102    }
103
104    /// Borrow the raw id for ID-style APIs.
105    pub fn as_id(&self) -> ShapeId {
106        self.id
107    }
108
109    pub fn is_sensor(&self) -> bool {
110        self.assert_valid();
111        unsafe { ffi::b2Shape_IsSensor(self.id) }
112    }
113
114    pub fn try_is_sensor(&self) -> ApiResult<bool> {
115        self.check_valid()?;
116        Ok(unsafe { ffi::b2Shape_IsSensor(self.id) })
117    }
118
119    pub fn shape_type(&self) -> ffi::b2ShapeType {
120        self.assert_valid();
121        unsafe { ffi::b2Shape_GetType(self.id) }
122    }
123
124    pub fn try_shape_type(&self) -> ApiResult<ffi::b2ShapeType> {
125        self.check_valid()?;
126        Ok(unsafe { ffi::b2Shape_GetType(self.id) })
127    }
128
129    pub fn body_id(&self) -> BodyId {
130        self.assert_valid();
131        unsafe { ffi::b2Shape_GetBody(self.id) }
132    }
133
134    pub fn try_body_id(&self) -> ApiResult<BodyId> {
135        self.check_valid()?;
136        Ok(unsafe { ffi::b2Shape_GetBody(self.id) })
137    }
138
139    // Geometry
140    pub fn circle(&self) -> ffi::b2Circle {
141        self.assert_valid();
142        unsafe { ffi::b2Shape_GetCircle(self.id) }
143    }
144    pub fn segment(&self) -> ffi::b2Segment {
145        self.assert_valid();
146        unsafe { ffi::b2Shape_GetSegment(self.id) }
147    }
148    pub fn capsule(&self) -> ffi::b2Capsule {
149        self.assert_valid();
150        unsafe { ffi::b2Shape_GetCapsule(self.id) }
151    }
152    pub fn polygon(&self) -> ffi::b2Polygon {
153        self.assert_valid();
154        unsafe { ffi::b2Shape_GetPolygon(self.id) }
155    }
156
157    /// Return the closest point on this shape to `target` (in world coordinates).
158    pub fn closest_point<V: Into<Vec2>>(&self, target: V) -> Vec2 {
159        self.assert_valid();
160        let t: ffi::b2Vec2 = target.into().into();
161        Vec2::from(unsafe { ffi::b2Shape_GetClosestPoint(self.id, t) })
162    }
163
164    pub fn try_closest_point<V: Into<Vec2>>(&self, target: V) -> ApiResult<Vec2> {
165        self.check_valid()?;
166        let t: ffi::b2Vec2 = target.into().into();
167        Ok(Vec2::from(unsafe {
168            ffi::b2Shape_GetClosestPoint(self.id, t)
169        }))
170    }
171
172    /// Apply wind force/torque approximation to the shape.
173    pub fn apply_wind<V: Into<Vec2>>(&mut self, wind: V, drag: f32, lift: f32, wake: bool) {
174        self.assert_valid();
175        let w: ffi::b2Vec2 = wind.into().into();
176        unsafe { ffi::b2Shape_ApplyWind(self.id, w, drag, lift, wake) }
177    }
178
179    pub fn try_apply_wind<V: Into<Vec2>>(
180        &mut self,
181        wind: V,
182        drag: f32,
183        lift: f32,
184        wake: bool,
185    ) -> ApiResult<()> {
186        self.check_valid()?;
187        let w: ffi::b2Vec2 = wind.into().into();
188        unsafe { ffi::b2Shape_ApplyWind(self.id, w, drag, lift, wake) }
189        Ok(())
190    }
191
192    pub fn set_circle(&mut self, c: &ffi::b2Circle) {
193        self.assert_valid();
194        unsafe { ffi::b2Shape_SetCircle(self.id, c) }
195    }
196    pub fn try_set_circle(&mut self, c: &ffi::b2Circle) -> ApiResult<()> {
197        self.check_valid()?;
198        unsafe { ffi::b2Shape_SetCircle(self.id, c) }
199        Ok(())
200    }
201    pub fn set_segment(&mut self, s: &ffi::b2Segment) {
202        self.assert_valid();
203        unsafe { ffi::b2Shape_SetSegment(self.id, s) }
204    }
205    pub fn try_set_segment(&mut self, s: &ffi::b2Segment) -> ApiResult<()> {
206        self.check_valid()?;
207        unsafe { ffi::b2Shape_SetSegment(self.id, s) }
208        Ok(())
209    }
210    pub fn set_capsule(&mut self, c: &ffi::b2Capsule) {
211        self.assert_valid();
212        unsafe { ffi::b2Shape_SetCapsule(self.id, c) }
213    }
214    pub fn try_set_capsule(&mut self, c: &ffi::b2Capsule) -> ApiResult<()> {
215        self.check_valid()?;
216        unsafe { ffi::b2Shape_SetCapsule(self.id, c) }
217        Ok(())
218    }
219    pub fn set_polygon(&mut self, p: &ffi::b2Polygon) {
220        self.assert_valid();
221        unsafe { ffi::b2Shape_SetPolygon(self.id, p) }
222    }
223    pub fn try_set_polygon(&mut self, p: &ffi::b2Polygon) -> ApiResult<()> {
224        self.check_valid()?;
225        unsafe { ffi::b2Shape_SetPolygon(self.id, p) }
226        Ok(())
227    }
228
229    pub fn filter(&self) -> Filter {
230        self.assert_valid();
231        Filter::from(unsafe { ffi::b2Shape_GetFilter(self.id) })
232    }
233    pub fn try_filter(&self) -> ApiResult<Filter> {
234        self.check_valid()?;
235        Ok(Filter::from(unsafe { ffi::b2Shape_GetFilter(self.id) }))
236    }
237    pub fn set_filter(&mut self, f: Filter) {
238        self.assert_valid();
239        unsafe { ffi::b2Shape_SetFilter(self.id, f.into()) }
240    }
241    pub fn try_set_filter(&mut self, f: Filter) -> ApiResult<()> {
242        self.check_valid()?;
243        unsafe { ffi::b2Shape_SetFilter(self.id, f.into()) }
244        Ok(())
245    }
246
247    pub fn set_density(&mut self, density: f32, update_body_mass: bool) {
248        self.assert_valid();
249        unsafe { ffi::b2Shape_SetDensity(self.id, density, update_body_mass) }
250    }
251    pub fn try_set_density(&mut self, density: f32, update_body_mass: bool) -> ApiResult<()> {
252        self.check_valid()?;
253        unsafe { ffi::b2Shape_SetDensity(self.id, density, update_body_mass) }
254        Ok(())
255    }
256    pub fn density(&self) -> f32 {
257        self.assert_valid();
258        unsafe { ffi::b2Shape_GetDensity(self.id) }
259    }
260    pub fn try_density(&self) -> ApiResult<f32> {
261        self.check_valid()?;
262        Ok(unsafe { ffi::b2Shape_GetDensity(self.id) })
263    }
264
265    pub fn set_friction(&mut self, friction: f32) {
266        self.assert_valid();
267        unsafe { ffi::b2Shape_SetFriction(self.id, friction) }
268    }
269    pub fn try_set_friction(&mut self, friction: f32) -> ApiResult<()> {
270        self.check_valid()?;
271        unsafe { ffi::b2Shape_SetFriction(self.id, friction) }
272        Ok(())
273    }
274    pub fn friction(&self) -> f32 {
275        self.assert_valid();
276        unsafe { ffi::b2Shape_GetFriction(self.id) }
277    }
278    pub fn try_friction(&self) -> ApiResult<f32> {
279        self.check_valid()?;
280        Ok(unsafe { ffi::b2Shape_GetFriction(self.id) })
281    }
282
283    pub fn set_restitution(&mut self, restitution: f32) {
284        self.assert_valid();
285        unsafe { ffi::b2Shape_SetRestitution(self.id, restitution) }
286    }
287    pub fn try_set_restitution(&mut self, restitution: f32) -> ApiResult<()> {
288        self.check_valid()?;
289        unsafe { ffi::b2Shape_SetRestitution(self.id, restitution) }
290        Ok(())
291    }
292    pub fn restitution(&self) -> f32 {
293        self.assert_valid();
294        unsafe { ffi::b2Shape_GetRestitution(self.id) }
295    }
296    pub fn try_restitution(&self) -> ApiResult<f32> {
297        self.check_valid()?;
298        Ok(unsafe { ffi::b2Shape_GetRestitution(self.id) })
299    }
300
301    pub fn set_user_material(&mut self, material: u64) {
302        self.assert_valid();
303        unsafe { ffi::b2Shape_SetUserMaterial(self.id, material) }
304    }
305    pub fn try_set_user_material(&mut self, material: u64) -> ApiResult<()> {
306        self.check_valid()?;
307        unsafe { ffi::b2Shape_SetUserMaterial(self.id, material) }
308        Ok(())
309    }
310    pub fn user_material(&self) -> u64 {
311        self.assert_valid();
312        unsafe { ffi::b2Shape_GetUserMaterial(self.id) }
313    }
314    pub fn try_user_material(&self) -> ApiResult<u64> {
315        self.check_valid()?;
316        Ok(unsafe { ffi::b2Shape_GetUserMaterial(self.id) })
317    }
318
319    pub fn set_surface_material(&mut self, material: &SurfaceMaterial) {
320        self.assert_valid();
321        unsafe { ffi::b2Shape_SetSurfaceMaterial(self.id, &material.0) }
322    }
323    pub fn try_set_surface_material(&mut self, material: &SurfaceMaterial) -> ApiResult<()> {
324        self.check_valid()?;
325        unsafe { ffi::b2Shape_SetSurfaceMaterial(self.id, &material.0) }
326        Ok(())
327    }
328    pub fn surface_material(&self) -> SurfaceMaterial {
329        self.assert_valid();
330        SurfaceMaterial(unsafe { ffi::b2Shape_GetSurfaceMaterial(self.id) })
331    }
332    pub fn try_surface_material(&self) -> ApiResult<SurfaceMaterial> {
333        self.check_valid()?;
334        Ok(SurfaceMaterial(unsafe {
335            ffi::b2Shape_GetSurfaceMaterial(self.id)
336        }))
337    }
338
339    pub fn contact_data(&self) -> Vec<ffi::b2ContactData> {
340        self.assert_valid();
341        let cap = unsafe { ffi::b2Shape_GetContactCapacity(self.id) }.max(0) as usize;
342        if cap == 0 {
343            return Vec::new();
344        }
345        let mut vec: Vec<ffi::b2ContactData> = Vec::with_capacity(cap);
346        let wrote = unsafe { ffi::b2Shape_GetContactData(self.id, vec.as_mut_ptr(), cap as i32) }
347            .max(0) as usize;
348        unsafe { vec.set_len(wrote.min(cap)) };
349        vec
350    }
351
352    pub fn try_contact_data(&self) -> ApiResult<Vec<ffi::b2ContactData>> {
353        self.check_valid()?;
354        let cap = unsafe { ffi::b2Shape_GetContactCapacity(self.id) }.max(0) as usize;
355        if cap == 0 {
356            return Ok(Vec::new());
357        }
358        let mut vec: Vec<ffi::b2ContactData> = Vec::with_capacity(cap);
359        let wrote = unsafe { ffi::b2Shape_GetContactData(self.id, vec.as_mut_ptr(), cap as i32) }
360            .max(0) as usize;
361        unsafe { vec.set_len(wrote.min(cap)) };
362        Ok(vec)
363    }
364
365    /// Get the maximum capacity required for retrieving all overlapped shapes on this sensor shape.
366    pub fn sensor_capacity(&self) -> i32 {
367        self.assert_valid();
368        unsafe { ffi::b2Shape_GetSensorCapacity(self.id) }
369    }
370
371    pub fn try_sensor_capacity(&self) -> ApiResult<i32> {
372        self.check_valid()?;
373        Ok(unsafe { ffi::b2Shape_GetSensorCapacity(self.id) })
374    }
375
376    pub fn sensor_overlaps(&self) -> Vec<ShapeId> {
377        self.assert_valid();
378        let cap = self.sensor_capacity();
379        if cap <= 0 {
380            return Vec::new();
381        }
382        let mut ids: Vec<ShapeId> = Vec::with_capacity(cap as usize);
383        let wrote =
384            unsafe { ffi::b2Shape_GetSensorData(self.id, ids.as_mut_ptr(), cap) }.max(0) as usize;
385        unsafe { ids.set_len(wrote.min(cap as usize)) };
386        ids
387    }
388
389    pub fn try_sensor_overlaps(&self) -> ApiResult<Vec<ShapeId>> {
390        self.check_valid()?;
391        let cap = unsafe { ffi::b2Shape_GetSensorCapacity(self.id) };
392        if cap <= 0 {
393            return Ok(Vec::new());
394        }
395        let mut ids: Vec<ShapeId> = Vec::with_capacity(cap as usize);
396        let wrote =
397            unsafe { ffi::b2Shape_GetSensorData(self.id, ids.as_mut_ptr(), cap) }.max(0) as usize;
398        unsafe { ids.set_len(wrote.min(cap as usize)) };
399        Ok(ids)
400    }
401
402    pub fn sensor_overlaps_valid(&self) -> Vec<ShapeId> {
403        self.sensor_overlaps()
404            .into_iter()
405            .filter(|&sid| unsafe { ffi::b2Shape_IsValid(sid) })
406            .collect()
407    }
408
409    /// Set an opaque user data pointer on this shape.
410    ///
411    /// # Safety
412    /// The caller must ensure that `p` is valid for as long as the engine may
413    /// read it and that any aliasing/lifetime constraints are upheld. Box2D stores this
414    /// pointer and may access it during simulation callbacks.
415    ///
416    /// If typed user data was previously set via `set_user_data`, it will be cleared and dropped.
417    pub unsafe fn set_user_data_ptr(&mut self, p: *mut c_void) {
418        self.assert_valid();
419        let _ = self.core.clear_shape_user_data(self.id);
420        unsafe { ffi::b2Shape_SetUserData(self.id, p) }
421    }
422    /// Set an opaque user data pointer on this shape.
423    ///
424    /// # Safety
425    /// Same safety contract as `set_user_data_ptr`.
426    ///
427    /// If typed user data was previously set via `set_user_data`, it will be cleared and dropped.
428    pub unsafe fn try_set_user_data_ptr(&mut self, p: *mut c_void) -> ApiResult<()> {
429        self.check_valid()?;
430        let _ = self.core.clear_shape_user_data(self.id);
431        unsafe { ffi::b2Shape_SetUserData(self.id, p) }
432        Ok(())
433    }
434    pub fn user_data_ptr(&self) -> *mut c_void {
435        self.assert_valid();
436        unsafe { ffi::b2Shape_GetUserData(self.id) }
437    }
438
439    pub fn try_user_data_ptr(&self) -> ApiResult<*mut c_void> {
440        self.check_valid()?;
441        Ok(unsafe { ffi::b2Shape_GetUserData(self.id) })
442    }
443
444    /// Set typed user data on this shape.
445    ///
446    /// This stores a `Box<T>` internally and sets Box2D's user data pointer to it. The allocation
447    /// is automatically freed when cleared or when the shape is destroyed.
448    pub fn set_user_data<T: 'static>(&mut self, value: T) {
449        self.assert_valid();
450        let p = self.core.set_shape_user_data(self.id, value);
451        unsafe { ffi::b2Shape_SetUserData(self.id, p) };
452    }
453
454    pub fn try_set_user_data<T: 'static>(&mut self, value: T) -> ApiResult<()> {
455        self.check_valid()?;
456        let p = self.core.set_shape_user_data(self.id, value);
457        unsafe { ffi::b2Shape_SetUserData(self.id, p) };
458        Ok(())
459    }
460
461    /// Clear typed user data on this shape. Returns whether any typed data was present.
462    pub fn clear_user_data(&mut self) -> bool {
463        self.assert_valid();
464        let had = self.core.clear_shape_user_data(self.id);
465        if had {
466            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
467        }
468        had
469    }
470
471    pub fn try_clear_user_data(&mut self) -> ApiResult<bool> {
472        self.check_valid()?;
473        let had = self.core.clear_shape_user_data(self.id);
474        if had {
475            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
476        }
477        Ok(had)
478    }
479
480    pub fn with_user_data<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
481        self.assert_valid();
482        self.core
483            .try_with_shape_user_data(self.id, f)
484            .expect("user data type mismatch")
485    }
486
487    pub fn try_with_user_data<T: 'static, R>(
488        &self,
489        f: impl FnOnce(&T) -> R,
490    ) -> ApiResult<Option<R>> {
491        self.check_valid()?;
492        self.core.try_with_shape_user_data(self.id, f)
493    }
494
495    pub fn with_user_data_mut<T: 'static, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
496        self.assert_valid();
497        self.core
498            .try_with_shape_user_data_mut(self.id, f)
499            .expect("user data type mismatch")
500    }
501
502    pub fn try_with_user_data_mut<T: 'static, R>(
503        &mut self,
504        f: impl FnOnce(&mut T) -> R,
505    ) -> ApiResult<Option<R>> {
506        self.check_valid()?;
507        self.core.try_with_shape_user_data_mut(self.id, f)
508    }
509
510    pub fn take_user_data<T: 'static>(&mut self) -> Option<T> {
511        self.assert_valid();
512        let v = self
513            .core
514            .take_shape_user_data::<T>(self.id)
515            .expect("user data type mismatch");
516        if v.is_some() {
517            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
518        }
519        v
520    }
521
522    pub fn try_take_user_data<T: 'static>(&mut self) -> ApiResult<Option<T>> {
523        self.check_valid()?;
524        let v = self.core.take_shape_user_data::<T>(self.id)?;
525        if v.is_some() {
526            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
527        }
528        Ok(v)
529    }
530
531    pub fn update_body_mass_on_drop(mut self, flag: bool) -> Self {
532        self.update_body_mass_on_drop = flag;
533        self
534    }
535
536    /// Disarm RAII and return the raw id for manual lifetime management.
537    pub fn into_id(mut self) -> ShapeId {
538        self.destroy_on_drop = false;
539        self.id
540    }
541
542    /// Destroy the shape immediately and disarm drop.
543    pub fn destroy(mut self, update_body_mass: bool) {
544        if self.destroy_on_drop && unsafe { ffi::b2Shape_IsValid(self.id) } {
545            if crate::core::callback_state::in_callback() || self.core.events_buffers_are_borrowed()
546            {
547                self.core
548                    .defer_destroy(crate::core::world_core::DeferredDestroy::Shape {
549                        id: self.id,
550                        update_body_mass,
551                    });
552            } else {
553                unsafe { ffi::b2DestroyShape(self.id, update_body_mass) };
554                let _ = self.core.clear_shape_user_data(self.id);
555                #[cfg(feature = "serialize")]
556                self.core.remove_shape_flags(self.id);
557            }
558        }
559        self.destroy_on_drop = false;
560    }
561}
562
563impl Drop for OwnedShape {
564    fn drop(&mut self) {
565        let _ = self.core.id;
566        let prev = self
567            .core
568            .owned_shapes
569            .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
570        debug_assert!(prev > 0, "owned shape counter underflow");
571        if self.destroy_on_drop && unsafe { ffi::b2Shape_IsValid(self.id) } {
572            if crate::core::callback_state::in_callback() || self.core.events_buffers_are_borrowed()
573            {
574                self.core
575                    .defer_destroy(crate::core::world_core::DeferredDestroy::Shape {
576                        id: self.id,
577                        update_body_mass: self.update_body_mass_on_drop,
578                    });
579            } else {
580                unsafe { ffi::b2DestroyShape(self.id, self.update_body_mass_on_drop) };
581                let _ = self.core.clear_shape_user_data(self.id);
582                #[cfg(feature = "serialize")]
583                self.core.remove_shape_flags(self.id);
584            }
585        }
586    }
587}
588
589impl<'w> Shape<'w> {
590    pub(crate) fn new(core: Arc<crate::core::world_core::WorldCore>, id: ShapeId) -> Self {
591        Self {
592            id,
593            core,
594            _world: PhantomData,
595        }
596    }
597
598    #[inline]
599    fn assert_valid(&self) {
600        crate::core::debug_checks::assert_shape_valid(self.id);
601    }
602
603    #[inline]
604    fn check_valid(&self) -> ApiResult<()> {
605        crate::core::debug_checks::check_shape_valid(self.id)
606    }
607
608    pub fn id(&self) -> ShapeId {
609        self.id
610    }
611
612    pub fn world_id(&self) -> ffi::b2WorldId {
613        self.assert_valid();
614        unsafe { ffi::b2Shape_GetWorld(self.id) }
615    }
616
617    pub fn try_world_id(&self) -> ApiResult<ffi::b2WorldId> {
618        self.check_valid()?;
619        Ok(unsafe { ffi::b2Shape_GetWorld(self.id) })
620    }
621
622    pub fn parent_chain_id(&self) -> Option<ChainId> {
623        self.assert_valid();
624        let cid = unsafe { ffi::b2Shape_GetParentChain(self.id) };
625        if unsafe { ffi::b2Chain_IsValid(cid) } {
626            Some(cid)
627        } else {
628            None
629        }
630    }
631
632    pub fn try_parent_chain_id(&self) -> ApiResult<Option<ChainId>> {
633        self.check_valid()?;
634        let cid = unsafe { ffi::b2Shape_GetParentChain(self.id) };
635        if unsafe { ffi::b2Chain_IsValid(cid) } {
636            Ok(Some(cid))
637        } else {
638            Ok(None)
639        }
640    }
641
642    pub fn is_valid(&self) -> bool {
643        crate::core::callback_state::assert_not_in_callback();
644        unsafe { ffi::b2Shape_IsValid(self.id) }
645    }
646
647    pub fn try_is_valid(&self) -> ApiResult<bool> {
648        crate::core::callback_state::check_not_in_callback()?;
649        Ok(unsafe { ffi::b2Shape_IsValid(self.id) })
650    }
651
652    pub fn shape_type(&self) -> ffi::b2ShapeType {
653        self.assert_valid();
654        unsafe { ffi::b2Shape_GetType(self.id) }
655    }
656
657    pub fn try_shape_type(&self) -> ApiResult<ffi::b2ShapeType> {
658        self.check_valid()?;
659        Ok(unsafe { ffi::b2Shape_GetType(self.id) })
660    }
661
662    pub fn body_id(&self) -> BodyId {
663        self.assert_valid();
664        unsafe { ffi::b2Shape_GetBody(self.id) }
665    }
666
667    pub fn try_body_id(&self) -> ApiResult<BodyId> {
668        self.check_valid()?;
669        Ok(unsafe { ffi::b2Shape_GetBody(self.id) })
670    }
671
672    // Getters
673    pub fn circle(&self) -> ffi::b2Circle {
674        self.assert_valid();
675        unsafe { ffi::b2Shape_GetCircle(self.id) }
676    }
677    pub fn segment(&self) -> ffi::b2Segment {
678        self.assert_valid();
679        unsafe { ffi::b2Shape_GetSegment(self.id) }
680    }
681    pub fn capsule(&self) -> ffi::b2Capsule {
682        self.assert_valid();
683        unsafe { ffi::b2Shape_GetCapsule(self.id) }
684    }
685    pub fn polygon(&self) -> ffi::b2Polygon {
686        self.assert_valid();
687        unsafe { ffi::b2Shape_GetPolygon(self.id) }
688    }
689
690    /// Return the closest point on this shape to `target` (in world coordinates).
691    pub fn closest_point<V: Into<Vec2>>(&self, target: V) -> Vec2 {
692        self.assert_valid();
693        let t: ffi::b2Vec2 = target.into().into();
694        Vec2::from(unsafe { ffi::b2Shape_GetClosestPoint(self.id, t) })
695    }
696
697    pub fn try_closest_point<V: Into<Vec2>>(&self, target: V) -> ApiResult<Vec2> {
698        self.check_valid()?;
699        let t: ffi::b2Vec2 = target.into().into();
700        Ok(Vec2::from(unsafe {
701            ffi::b2Shape_GetClosestPoint(self.id, t)
702        }))
703    }
704
705    /// Apply wind force/torque approximation to the shape.
706    pub fn apply_wind<V: Into<Vec2>>(&mut self, wind: V, drag: f32, lift: f32, wake: bool) {
707        self.assert_valid();
708        let w: ffi::b2Vec2 = wind.into().into();
709        unsafe { ffi::b2Shape_ApplyWind(self.id, w, drag, lift, wake) }
710    }
711
712    pub fn try_apply_wind<V: Into<Vec2>>(
713        &mut self,
714        wind: V,
715        drag: f32,
716        lift: f32,
717        wake: bool,
718    ) -> ApiResult<()> {
719        self.check_valid()?;
720        let w: ffi::b2Vec2 = wind.into().into();
721        unsafe { ffi::b2Shape_ApplyWind(self.id, w, drag, lift, wake) }
722        Ok(())
723    }
724
725    // Setters
726    pub fn set_circle(&mut self, c: &ffi::b2Circle) {
727        self.assert_valid();
728        unsafe { ffi::b2Shape_SetCircle(self.id, c) }
729    }
730    pub fn try_set_circle(&mut self, c: &ffi::b2Circle) -> ApiResult<()> {
731        self.check_valid()?;
732        unsafe { ffi::b2Shape_SetCircle(self.id, c) }
733        Ok(())
734    }
735    pub fn set_segment(&mut self, s: &ffi::b2Segment) {
736        self.assert_valid();
737        unsafe { ffi::b2Shape_SetSegment(self.id, s) }
738    }
739    pub fn try_set_segment(&mut self, s: &ffi::b2Segment) -> ApiResult<()> {
740        self.check_valid()?;
741        unsafe { ffi::b2Shape_SetSegment(self.id, s) }
742        Ok(())
743    }
744    pub fn set_capsule(&mut self, c: &ffi::b2Capsule) {
745        self.assert_valid();
746        unsafe { ffi::b2Shape_SetCapsule(self.id, c) }
747    }
748    pub fn try_set_capsule(&mut self, c: &ffi::b2Capsule) -> ApiResult<()> {
749        self.check_valid()?;
750        unsafe { ffi::b2Shape_SetCapsule(self.id, c) }
751        Ok(())
752    }
753    pub fn set_polygon(&mut self, p: &ffi::b2Polygon) {
754        self.assert_valid();
755        unsafe { ffi::b2Shape_SetPolygon(self.id, p) }
756    }
757    pub fn try_set_polygon(&mut self, p: &ffi::b2Polygon) -> ApiResult<()> {
758        self.check_valid()?;
759        unsafe { ffi::b2Shape_SetPolygon(self.id, p) }
760        Ok(())
761    }
762
763    pub fn filter(&self) -> Filter {
764        self.assert_valid();
765        Filter::from(unsafe { ffi::b2Shape_GetFilter(self.id) })
766    }
767    pub fn try_filter(&self) -> ApiResult<Filter> {
768        self.check_valid()?;
769        Ok(Filter::from(unsafe { ffi::b2Shape_GetFilter(self.id) }))
770    }
771    pub fn set_filter(&mut self, f: Filter) {
772        self.assert_valid();
773        unsafe { ffi::b2Shape_SetFilter(self.id, f.into()) }
774    }
775    pub fn try_set_filter(&mut self, f: Filter) -> ApiResult<()> {
776        self.check_valid()?;
777        unsafe { ffi::b2Shape_SetFilter(self.id, f.into()) }
778        Ok(())
779    }
780
781    // Material and physical properties
782    pub fn is_sensor(&self) -> bool {
783        self.assert_valid();
784        unsafe { ffi::b2Shape_IsSensor(self.id) }
785    }
786    pub fn try_is_sensor(&self) -> ApiResult<bool> {
787        self.check_valid()?;
788        Ok(unsafe { ffi::b2Shape_IsSensor(self.id) })
789    }
790    pub fn set_density(&mut self, density: f32, update_body_mass: bool) {
791        self.assert_valid();
792        unsafe { ffi::b2Shape_SetDensity(self.id, density, update_body_mass) }
793    }
794    pub fn try_set_density(&mut self, density: f32, update_body_mass: bool) -> ApiResult<()> {
795        self.check_valid()?;
796        unsafe { ffi::b2Shape_SetDensity(self.id, density, update_body_mass) }
797        Ok(())
798    }
799    pub fn density(&self) -> f32 {
800        self.assert_valid();
801        unsafe { ffi::b2Shape_GetDensity(self.id) }
802    }
803    pub fn try_density(&self) -> ApiResult<f32> {
804        self.check_valid()?;
805        Ok(unsafe { ffi::b2Shape_GetDensity(self.id) })
806    }
807    pub fn set_friction(&mut self, friction: f32) {
808        self.assert_valid();
809        unsafe { ffi::b2Shape_SetFriction(self.id, friction) }
810    }
811    pub fn try_set_friction(&mut self, friction: f32) -> ApiResult<()> {
812        self.check_valid()?;
813        unsafe { ffi::b2Shape_SetFriction(self.id, friction) }
814        Ok(())
815    }
816    pub fn friction(&self) -> f32 {
817        self.assert_valid();
818        unsafe { ffi::b2Shape_GetFriction(self.id) }
819    }
820    pub fn try_friction(&self) -> ApiResult<f32> {
821        self.check_valid()?;
822        Ok(unsafe { ffi::b2Shape_GetFriction(self.id) })
823    }
824    pub fn set_restitution(&mut self, restitution: f32) {
825        self.assert_valid();
826        unsafe { ffi::b2Shape_SetRestitution(self.id, restitution) }
827    }
828    pub fn try_set_restitution(&mut self, restitution: f32) -> ApiResult<()> {
829        self.check_valid()?;
830        unsafe { ffi::b2Shape_SetRestitution(self.id, restitution) }
831        Ok(())
832    }
833    pub fn restitution(&self) -> f32 {
834        self.assert_valid();
835        unsafe { ffi::b2Shape_GetRestitution(self.id) }
836    }
837    pub fn try_restitution(&self) -> ApiResult<f32> {
838        self.check_valid()?;
839        Ok(unsafe { ffi::b2Shape_GetRestitution(self.id) })
840    }
841    pub fn set_user_material(&mut self, material: u64) {
842        self.assert_valid();
843        unsafe { ffi::b2Shape_SetUserMaterial(self.id, material) }
844    }
845    pub fn try_set_user_material(&mut self, material: u64) -> ApiResult<()> {
846        self.check_valid()?;
847        unsafe { ffi::b2Shape_SetUserMaterial(self.id, material) }
848        Ok(())
849    }
850    pub fn user_material(&self) -> u64 {
851        self.assert_valid();
852        unsafe { ffi::b2Shape_GetUserMaterial(self.id) }
853    }
854    pub fn try_user_material(&self) -> ApiResult<u64> {
855        self.check_valid()?;
856        Ok(unsafe { ffi::b2Shape_GetUserMaterial(self.id) })
857    }
858    pub fn set_surface_material(&mut self, material: &SurfaceMaterial) {
859        self.assert_valid();
860        unsafe { ffi::b2Shape_SetSurfaceMaterial(self.id, &material.0) }
861    }
862    pub fn try_set_surface_material(&mut self, material: &SurfaceMaterial) -> ApiResult<()> {
863        self.check_valid()?;
864        unsafe { ffi::b2Shape_SetSurfaceMaterial(self.id, &material.0) }
865        Ok(())
866    }
867    pub fn surface_material(&self) -> SurfaceMaterial {
868        self.assert_valid();
869        SurfaceMaterial(unsafe { ffi::b2Shape_GetSurfaceMaterial(self.id) })
870    }
871    pub fn try_surface_material(&self) -> ApiResult<SurfaceMaterial> {
872        self.check_valid()?;
873        Ok(SurfaceMaterial(unsafe {
874            ffi::b2Shape_GetSurfaceMaterial(self.id)
875        }))
876    }
877
878    // Opaque user pointer (engine-owned)
879    /// Set an opaque user data pointer on this shape.
880    ///
881    /// # Safety
882    /// The caller must ensure that `p` is valid for as long as the engine may
883    /// read it and that any aliasing/lifetime constraints are upheld. Box2D stores this
884    /// pointer and may access it during simulation callbacks.
885    ///
886    /// If typed user data was previously set via `set_user_data`, it will be cleared and dropped.
887    pub unsafe fn set_user_data_ptr(&mut self, p: *mut core::ffi::c_void) {
888        self.assert_valid();
889        let _ = self.core.clear_shape_user_data(self.id);
890        unsafe { ffi::b2Shape_SetUserData(self.id, p) }
891    }
892    /// Set an opaque user data pointer on this shape.
893    ///
894    /// # Safety
895    /// Same safety contract as `set_user_data_ptr`.
896    ///
897    /// If typed user data was previously set via `set_user_data`, it will be cleared and dropped.
898    pub unsafe fn try_set_user_data_ptr(&mut self, p: *mut core::ffi::c_void) -> ApiResult<()> {
899        self.check_valid()?;
900        let _ = self.core.clear_shape_user_data(self.id);
901        unsafe { ffi::b2Shape_SetUserData(self.id, p) }
902        Ok(())
903    }
904    pub fn user_data_ptr(&self) -> *mut core::ffi::c_void {
905        self.assert_valid();
906        unsafe { ffi::b2Shape_GetUserData(self.id) }
907    }
908
909    pub fn try_user_data_ptr(&self) -> ApiResult<*mut core::ffi::c_void> {
910        self.check_valid()?;
911        Ok(unsafe { ffi::b2Shape_GetUserData(self.id) })
912    }
913
914    /// Set typed user data on this shape.
915    ///
916    /// This stores a `Box<T>` internally and sets Box2D's user data pointer to it. The allocation
917    /// is automatically freed when cleared or when the shape is destroyed.
918    pub fn set_user_data<T: 'static>(&mut self, value: T) {
919        self.assert_valid();
920        let p = self.core.set_shape_user_data(self.id, value);
921        unsafe { ffi::b2Shape_SetUserData(self.id, p) };
922    }
923
924    pub fn try_set_user_data<T: 'static>(&mut self, value: T) -> ApiResult<()> {
925        self.check_valid()?;
926        let p = self.core.set_shape_user_data(self.id, value);
927        unsafe { ffi::b2Shape_SetUserData(self.id, p) };
928        Ok(())
929    }
930
931    /// Clear typed user data on this shape. Returns whether any typed data was present.
932    pub fn clear_user_data(&mut self) -> bool {
933        self.assert_valid();
934        let had = self.core.clear_shape_user_data(self.id);
935        if had {
936            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
937        }
938        had
939    }
940
941    pub fn try_clear_user_data(&mut self) -> ApiResult<bool> {
942        self.check_valid()?;
943        let had = self.core.clear_shape_user_data(self.id);
944        if had {
945            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
946        }
947        Ok(had)
948    }
949
950    pub fn with_user_data<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
951        self.assert_valid();
952        self.core
953            .try_with_shape_user_data(self.id, f)
954            .expect("user data type mismatch")
955    }
956
957    pub fn try_with_user_data<T: 'static, R>(
958        &self,
959        f: impl FnOnce(&T) -> R,
960    ) -> ApiResult<Option<R>> {
961        self.check_valid()?;
962        self.core.try_with_shape_user_data(self.id, f)
963    }
964
965    pub fn with_user_data_mut<T: 'static, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
966        self.assert_valid();
967        self.core
968            .try_with_shape_user_data_mut(self.id, f)
969            .expect("user data type mismatch")
970    }
971
972    pub fn try_with_user_data_mut<T: 'static, R>(
973        &mut self,
974        f: impl FnOnce(&mut T) -> R,
975    ) -> ApiResult<Option<R>> {
976        self.check_valid()?;
977        self.core.try_with_shape_user_data_mut(self.id, f)
978    }
979
980    pub fn take_user_data<T: 'static>(&mut self) -> Option<T> {
981        self.assert_valid();
982        let v = self
983            .core
984            .take_shape_user_data::<T>(self.id)
985            .expect("user data type mismatch");
986        if v.is_some() {
987            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
988        }
989        v
990    }
991
992    pub fn try_take_user_data<T: 'static>(&mut self) -> ApiResult<Option<T>> {
993        self.check_valid()?;
994        let v = self.core.take_shape_user_data::<T>(self.id)?;
995        if v.is_some() {
996            unsafe { ffi::b2Shape_SetUserData(self.id, core::ptr::null_mut()) };
997        }
998        Ok(v)
999    }
1000
1001    pub fn contact_data(&self) -> Vec<ffi::b2ContactData> {
1002        self.assert_valid();
1003        let cap = unsafe { ffi::b2Shape_GetContactCapacity(self.id) }.max(0) as usize;
1004        if cap == 0 {
1005            return Vec::new();
1006        }
1007        let mut vec: Vec<ffi::b2ContactData> = Vec::with_capacity(cap);
1008        let wrote = unsafe { ffi::b2Shape_GetContactData(self.id, vec.as_mut_ptr(), cap as i32) }
1009            .max(0) as usize;
1010        unsafe { vec.set_len(wrote.min(cap)) };
1011        vec
1012    }
1013
1014    pub fn try_contact_data(&self) -> ApiResult<Vec<ffi::b2ContactData>> {
1015        self.check_valid()?;
1016        let cap = unsafe { ffi::b2Shape_GetContactCapacity(self.id) }.max(0) as usize;
1017        if cap == 0 {
1018            return Ok(Vec::new());
1019        }
1020        let mut vec: Vec<ffi::b2ContactData> = Vec::with_capacity(cap);
1021        let wrote = unsafe { ffi::b2Shape_GetContactData(self.id, vec.as_mut_ptr(), cap as i32) }
1022            .max(0) as usize;
1023        unsafe { vec.set_len(wrote.min(cap)) };
1024        Ok(vec)
1025    }
1026
1027    /// Get the maximum capacity required for retrieving all the overlapped shapes on this sensor shape.
1028    /// Returns 0 if this shape is not a sensor.
1029    pub fn sensor_capacity(&self) -> i32 {
1030        self.assert_valid();
1031        unsafe { ffi::b2Shape_GetSensorCapacity(self.id) }
1032    }
1033
1034    pub fn try_sensor_capacity(&self) -> ApiResult<i32> {
1035        self.check_valid()?;
1036        Ok(unsafe { ffi::b2Shape_GetSensorCapacity(self.id) })
1037    }
1038
1039    /// Get overlapped shapes for this sensor shape. If this is not a sensor, returns empty.
1040    /// Note: overlaps may contain destroyed shapes; use `sensor_overlaps_valid` to filter.
1041    pub fn sensor_overlaps(&self) -> Vec<ShapeId> {
1042        self.assert_valid();
1043        let cap = self.sensor_capacity();
1044        if cap <= 0 {
1045            return Vec::new();
1046        }
1047        let mut ids: Vec<ShapeId> = Vec::with_capacity(cap as usize);
1048        let wrote =
1049            unsafe { ffi::b2Shape_GetSensorData(self.id, ids.as_mut_ptr(), cap) }.max(0) as usize;
1050        unsafe { ids.set_len(wrote.min(cap as usize)) };
1051        ids
1052    }
1053
1054    pub fn try_sensor_overlaps(&self) -> ApiResult<Vec<ShapeId>> {
1055        self.check_valid()?;
1056        let cap = unsafe { ffi::b2Shape_GetSensorCapacity(self.id) };
1057        if cap <= 0 {
1058            return Ok(Vec::new());
1059        }
1060        let mut ids: Vec<ShapeId> = Vec::with_capacity(cap as usize);
1061        let wrote =
1062            unsafe { ffi::b2Shape_GetSensorData(self.id, ids.as_mut_ptr(), cap) }.max(0) as usize;
1063        unsafe { ids.set_len(wrote.min(cap as usize)) };
1064        Ok(ids)
1065    }
1066
1067    /// Get overlapped shapes and filter out invalid (destroyed) shape ids.
1068    pub fn sensor_overlaps_valid(&self) -> Vec<ShapeId> {
1069        self.sensor_overlaps()
1070            .into_iter()
1071            .filter(|&sid| unsafe { ffi::b2Shape_IsValid(sid) })
1072            .collect()
1073    }
1074
1075    /// Destroy this shape immediately.
1076    ///
1077    /// After destruction, any previously stored `ShapeId` referring to this shape becomes invalid.
1078    pub fn destroy(self, update_body_mass: bool) {
1079        crate::core::callback_state::assert_not_in_callback();
1080        if unsafe { ffi::b2Shape_IsValid(self.id) } {
1081            unsafe { ffi::b2DestroyShape(self.id, update_body_mass) };
1082            let _ = self.core.clear_shape_user_data(self.id);
1083            #[cfg(feature = "serialize")]
1084            self.core.remove_shape_flags(self.id);
1085        }
1086    }
1087
1088    pub fn try_destroy(self, update_body_mass: bool) -> ApiResult<()> {
1089        self.check_valid()?;
1090        if unsafe { ffi::b2Shape_IsValid(self.id) } {
1091            unsafe { ffi::b2DestroyShape(self.id, update_body_mass) };
1092            let _ = self.core.clear_shape_user_data(self.id);
1093            #[cfg(feature = "serialize")]
1094            self.core.remove_shape_flags(self.id);
1095        }
1096        Ok(())
1097    }
1098}
1099
1100/// Shape surface material parameters.
1101#[derive(Clone, Debug)]
1102pub struct SurfaceMaterial(pub(crate) ffi::b2SurfaceMaterial);
1103
1104impl Default for SurfaceMaterial {
1105    fn default() -> Self {
1106        Self(unsafe { ffi::b2DefaultSurfaceMaterial() })
1107    }
1108}
1109
1110impl SurfaceMaterial {
1111    pub fn friction(mut self, v: f32) -> Self {
1112        self.0.friction = v;
1113        self
1114    }
1115    pub fn restitution(mut self, v: f32) -> Self {
1116        self.0.restitution = v;
1117        self
1118    }
1119    pub fn rolling_resistance(mut self, v: f32) -> Self {
1120        self.0.rollingResistance = v;
1121        self
1122    }
1123    pub fn tangent_speed(mut self, v: f32) -> Self {
1124        self.0.tangentSpeed = v;
1125        self
1126    }
1127    pub fn user_material_id(mut self, v: u64) -> Self {
1128        self.0.userMaterialId = v;
1129        self
1130    }
1131    pub fn custom_color(mut self, rgba: u32) -> Self {
1132        self.0.customColor = rgba;
1133        self
1134    }
1135}
1136
1137/// Shape definition with Builder pattern.
1138#[doc(alias = "shape_def")]
1139#[doc(alias = "shapedef")]
1140#[derive(Clone, Debug)]
1141pub struct ShapeDef(pub(crate) ffi::b2ShapeDef);
1142
1143impl Default for ShapeDef {
1144    fn default() -> Self {
1145        Self(unsafe { ffi::b2DefaultShapeDef() })
1146    }
1147}
1148
1149impl ShapeDef {
1150    pub fn builder() -> ShapeDefBuilder {
1151        ShapeDefBuilder {
1152            def: Self::default(),
1153        }
1154    }
1155}
1156
1157#[doc(alias = "shape_builder")]
1158#[doc(alias = "shapebuilder")]
1159#[derive(Clone, Debug)]
1160pub struct ShapeDefBuilder {
1161    def: ShapeDef,
1162}
1163
1164impl ShapeDefBuilder {
1165    /// Set the surface material (friction, restitution, etc.).
1166    pub fn material(mut self, mat: SurfaceMaterial) -> Self {
1167        self.def.0.material = mat.0;
1168        self
1169    }
1170    /// Density in kg/m². Affects mass.
1171    pub fn density(mut self, v: f32) -> Self {
1172        self.def.0.density = v;
1173        self
1174    }
1175    /// Low-level filter (category/mask/group).
1176    pub fn filter(mut self, f: ffi::b2Filter) -> Self {
1177        self.def.0.filter = f;
1178        self
1179    }
1180    /// High-level filter wrapper.
1181    pub fn filter_ex(mut self, f: Filter) -> Self {
1182        self.def.0.filter = f.into();
1183        self
1184    }
1185    /// Enable user-provided filtering callback.
1186    ///
1187    /// Note: To receive custom filter calls you must also register a world-level
1188    /// callback via `World::set_custom_filter_callback` or `World::set_custom_filter_with_ctx`.
1189    pub fn enable_custom_filtering(mut self, flag: bool) -> Self {
1190        self.def.0.enableCustomFiltering = flag;
1191        self
1192    }
1193    /// Mark as sensor (no collision response).
1194    pub fn sensor(mut self, flag: bool) -> Self {
1195        self.def.0.isSensor = flag;
1196        self
1197    }
1198    /// Emit sensor begin/end touch events.
1199    pub fn enable_sensor_events(mut self, flag: bool) -> Self {
1200        self.def.0.enableSensorEvents = flag;
1201        self
1202    }
1203    /// Emit contact begin/end events.
1204    pub fn enable_contact_events(mut self, flag: bool) -> Self {
1205        self.def.0.enableContactEvents = flag;
1206        self
1207    }
1208    /// Emit impact hit events when above threshold.
1209    pub fn enable_hit_events(mut self, flag: bool) -> Self {
1210        self.def.0.enableHitEvents = flag;
1211        self
1212    }
1213    /// Emit pre-solve events (advanced).
1214    ///
1215    /// Note: To receive pre-solve events you must also register a world-level
1216    /// callback via `World::set_pre_solve_callback` or `World::set_pre_solve_with_ctx`.
1217    pub fn enable_pre_solve_events(mut self, flag: bool) -> Self {
1218        self.def.0.enablePreSolveEvents = flag;
1219        self
1220    }
1221    /// Invoke user callback on contact creation.
1222    pub fn invoke_contact_creation(mut self, flag: bool) -> Self {
1223        self.def.0.invokeContactCreation = flag;
1224        self
1225    }
1226    /// Recompute body mass when adding/removing this shape.
1227    pub fn update_body_mass(mut self, flag: bool) -> Self {
1228        self.def.0.updateBodyMass = flag;
1229        self
1230    }
1231    #[must_use]
1232    pub fn build(self) -> ShapeDef {
1233        self.def
1234    }
1235}
1236
1237// serde for SurfaceMaterial and ShapeDef via lightweight representations
1238#[cfg(feature = "serde")]
1239impl serde::Serialize for SurfaceMaterial {
1240    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1241    where
1242        S: serde::Serializer,
1243    {
1244        #[derive(serde::Serialize)]
1245        struct Repr {
1246            friction: f32,
1247            restitution: f32,
1248            rolling_resistance: f32,
1249            tangent_speed: f32,
1250            user_material_id: u64,
1251            custom_color: u32,
1252        }
1253        let r = Repr {
1254            friction: self.0.friction,
1255            restitution: self.0.restitution,
1256            rolling_resistance: self.0.rollingResistance,
1257            tangent_speed: self.0.tangentSpeed,
1258            user_material_id: self.0.userMaterialId,
1259            custom_color: self.0.customColor,
1260        };
1261        r.serialize(serializer)
1262    }
1263}
1264
1265#[cfg(feature = "serde")]
1266impl<'de> serde::Deserialize<'de> for SurfaceMaterial {
1267    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1268    where
1269        D: serde::Deserializer<'de>,
1270    {
1271        #[derive(serde::Deserialize)]
1272        struct Repr {
1273            #[serde(default)]
1274            friction: f32,
1275            #[serde(default)]
1276            restitution: f32,
1277            #[serde(default)]
1278            rolling_resistance: f32,
1279            #[serde(default)]
1280            tangent_speed: f32,
1281            #[serde(default)]
1282            user_material_id: u64,
1283            #[serde(default)]
1284            custom_color: u32,
1285        }
1286        let r = Repr::deserialize(deserializer)?;
1287        let mut sm = SurfaceMaterial::default();
1288        sm = sm
1289            .friction(r.friction)
1290            .restitution(r.restitution)
1291            .rolling_resistance(r.rolling_resistance)
1292            .tangent_speed(r.tangent_speed)
1293            .user_material_id(r.user_material_id)
1294            .custom_color(r.custom_color);
1295        Ok(sm)
1296    }
1297}
1298
1299#[cfg(feature = "serde")]
1300impl serde::Serialize for ShapeDef {
1301    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1302    where
1303        S: serde::Serializer,
1304    {
1305        #[derive(serde::Serialize)]
1306        struct Repr {
1307            material: SurfaceMaterial,
1308            density: f32,
1309            filter: Filter,
1310            enable_custom_filtering: bool,
1311            is_sensor: bool,
1312            enable_sensor_events: bool,
1313            enable_contact_events: bool,
1314            enable_hit_events: bool,
1315            enable_pre_solve_events: bool,
1316            invoke_contact_creation: bool,
1317            update_body_mass: bool,
1318        }
1319        let r = Repr {
1320            material: SurfaceMaterial(self.0.material),
1321            density: self.0.density,
1322            filter: Filter::from(self.0.filter),
1323            enable_custom_filtering: self.0.enableCustomFiltering,
1324            is_sensor: self.0.isSensor,
1325            enable_sensor_events: self.0.enableSensorEvents,
1326            enable_contact_events: self.0.enableContactEvents,
1327            enable_hit_events: self.0.enableHitEvents,
1328            enable_pre_solve_events: self.0.enablePreSolveEvents,
1329            invoke_contact_creation: self.0.invokeContactCreation,
1330            update_body_mass: self.0.updateBodyMass,
1331        };
1332        r.serialize(serializer)
1333    }
1334}
1335
1336#[cfg(feature = "serde")]
1337impl<'de> serde::Deserialize<'de> for ShapeDef {
1338    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1339    where
1340        D: serde::Deserializer<'de>,
1341    {
1342        #[derive(serde::Deserialize)]
1343        struct Repr {
1344            #[serde(default)]
1345            material: Option<SurfaceMaterial>,
1346            #[serde(default)]
1347            density: f32,
1348            #[serde(default)]
1349            filter: Option<Filter>,
1350            #[serde(default)]
1351            enable_custom_filtering: bool,
1352            #[serde(default)]
1353            is_sensor: bool,
1354            #[serde(default)]
1355            enable_sensor_events: bool,
1356            #[serde(default)]
1357            enable_contact_events: bool,
1358            #[serde(default)]
1359            enable_hit_events: bool,
1360            #[serde(default)]
1361            enable_pre_solve_events: bool,
1362            #[serde(default)]
1363            invoke_contact_creation: bool,
1364            #[serde(default)]
1365            update_body_mass: bool,
1366        }
1367        let r = Repr::deserialize(deserializer)?;
1368        let mut b = ShapeDef::builder();
1369        if let Some(mat) = r.material {
1370            b = b.material(mat);
1371        }
1372        if let Some(f) = r.filter {
1373            b = b.filter_ex(f);
1374        }
1375        b = b
1376            .density(r.density)
1377            .enable_custom_filtering(r.enable_custom_filtering)
1378            .sensor(r.is_sensor)
1379            .enable_sensor_events(r.enable_sensor_events)
1380            .enable_contact_events(r.enable_contact_events)
1381            .enable_hit_events(r.enable_hit_events)
1382            .enable_pre_solve_events(r.enable_pre_solve_events)
1383            .invoke_contact_creation(r.invoke_contact_creation)
1384            .update_body_mass(r.update_body_mass);
1385        Ok(b.build())
1386    }
1387}
1388
1389/// Circle primitive helper
1390#[inline]
1391pub fn circle<V: Into<crate::types::Vec2>>(center: V, radius: f32) -> ffi::b2Circle {
1392    ffi::b2Circle {
1393        center: ffi::b2Vec2::from(center.into()),
1394        radius,
1395    }
1396}
1397
1398/// Segment primitive helper
1399#[inline]
1400pub fn segment<V: Into<crate::types::Vec2>>(p1: V, p2: V) -> ffi::b2Segment {
1401    ffi::b2Segment {
1402        point1: ffi::b2Vec2::from(p1.into()),
1403        point2: ffi::b2Vec2::from(p2.into()),
1404    }
1405}
1406
1407/// Helper constructors (re-exported): `capsule`, `box_polygon`, `polygon_from_points`.
1408pub use helpers::{box_polygon, capsule, polygon_from_points};
1409
1410// With math interop enabled (`mint`/`cgmath`/`nalgebra`/`glam`), `polygon_from_points` accepts the
1411// corresponding 2D vector/point types via `Into<Vec2>`.
1412
1413impl<'w> Body<'w> {
1414    pub fn create_circle_shape(&mut self, def: &ShapeDef, c: &ffi::b2Circle) -> Shape<'w> {
1415        crate::core::debug_checks::assert_body_valid(self.id);
1416        let id = unsafe { ffi::b2CreateCircleShape(self.id, &def.0, c) };
1417        #[cfg(feature = "serialize")]
1418        self.core.record_shape_flags(id, &def.0);
1419        Shape::new(Arc::clone(&self.core), id)
1420    }
1421    pub fn create_segment_shape(&mut self, def: &ShapeDef, s: &ffi::b2Segment) -> Shape<'w> {
1422        crate::core::debug_checks::assert_body_valid(self.id);
1423        let id = unsafe { ffi::b2CreateSegmentShape(self.id, &def.0, s) };
1424        #[cfg(feature = "serialize")]
1425        self.core.record_shape_flags(id, &def.0);
1426        Shape::new(Arc::clone(&self.core), id)
1427    }
1428    pub fn create_capsule_shape(&mut self, def: &ShapeDef, c: &ffi::b2Capsule) -> Shape<'w> {
1429        crate::core::debug_checks::assert_body_valid(self.id);
1430        let id = unsafe { ffi::b2CreateCapsuleShape(self.id, &def.0, c) };
1431        #[cfg(feature = "serialize")]
1432        self.core.record_shape_flags(id, &def.0);
1433        Shape::new(Arc::clone(&self.core), id)
1434    }
1435    pub fn create_polygon_shape(&mut self, def: &ShapeDef, p: &ffi::b2Polygon) -> Shape<'w> {
1436        crate::core::debug_checks::assert_body_valid(self.id);
1437        let id = unsafe { ffi::b2CreatePolygonShape(self.id, &def.0, p) };
1438        #[cfg(feature = "serialize")]
1439        self.core.record_shape_flags(id, &def.0);
1440        Shape::new(Arc::clone(&self.core), id)
1441    }
1442
1443    // Convenience creators
1444    pub fn create_box(&mut self, def: &ShapeDef, half_w: f32, half_h: f32) -> Shape<'w> {
1445        let poly = unsafe { ffi::b2MakeBox(half_w, half_h) };
1446        self.create_polygon_shape(def, &poly)
1447    }
1448    pub fn create_circle_simple(&mut self, def: &ShapeDef, radius: f32) -> Shape<'w> {
1449        let c = ffi::b2Circle {
1450            center: ffi::b2Vec2 { x: 0.0, y: 0.0 },
1451            radius,
1452        };
1453        self.create_circle_shape(def, &c)
1454    }
1455    pub fn create_segment_simple<V: Into<crate::types::Vec2>>(
1456        &mut self,
1457        def: &ShapeDef,
1458        p1: V,
1459        p2: V,
1460    ) -> Shape<'w> {
1461        let seg = ffi::b2Segment {
1462            point1: ffi::b2Vec2::from(p1.into()),
1463            point2: ffi::b2Vec2::from(p2.into()),
1464        };
1465        self.create_segment_shape(def, &seg)
1466    }
1467    pub fn create_capsule_simple<V: Into<crate::types::Vec2>>(
1468        &mut self,
1469        def: &ShapeDef,
1470        c1: V,
1471        c2: V,
1472        radius: f32,
1473    ) -> Shape<'w> {
1474        let cap = ffi::b2Capsule {
1475            center1: ffi::b2Vec2::from(c1.into()),
1476            center2: ffi::b2Vec2::from(c2.into()),
1477            radius,
1478        };
1479        self.create_capsule_shape(def, &cap)
1480    }
1481    pub fn create_polygon_from_points<I, P>(
1482        &mut self,
1483        def: &ShapeDef,
1484        points: I,
1485        radius: f32,
1486    ) -> Option<Shape<'w>>
1487    where
1488        I: IntoIterator<Item = P>,
1489        P: Into<crate::types::Vec2>,
1490    {
1491        let poly = crate::shapes::polygon_from_points(points, radius)?;
1492        Some(self.create_polygon_shape(def, &poly))
1493    }
1494}
1495// Shapes: module note moved to top-level doc above.