boxdd/
debug_draw.rs

1//! Debug Draw bridge to Box2D v3 callbacks.
2//!
3//! Implement the `DebugDraw` trait to receive drawing commands and call `World::debug_draw` each
4//! step with `DebugDrawOptions` to render. Color is a packed integer (`b2HexColor`), compatible with
5//! Box2D's debug draw convention.
6//!
7//! Example
8//! ```no_run
9//! use boxdd::{World, WorldDef, DebugDraw, DebugDrawOptions, Vec2};
10//! use boxdd_sys::ffi;
11//! use std::ffi::CStr;
12//! struct Printer;
13//! impl DebugDraw for Printer {
14//!     fn draw_polygon(&mut self, vertices: &[Vec2], color: u32) {
15//!         println!("poly {} color={:#x}", vertices.len(), color);
16//!     }
17//! }
18//! # let def = WorldDef::builder().build();
19//! # let mut world = World::new(def).unwrap();
20//! let mut drawer = Printer;
21//! for cmd in world.debug_draw_collect(DebugDrawOptions::default()) {
22//!     let _ = cmd;
23//! }
24//! ```
25use crate::Transform;
26use crate::types::Vec2;
27use crate::world::World;
28use boxdd_sys::ffi;
29use smallvec::SmallVec;
30use std::any::Any;
31use std::ffi::CStr;
32
33pub type HexColor = ffi::b2HexColor;
34
35#[derive(Clone, Debug)]
36pub enum DebugDrawCmd {
37    Polygon {
38        vertices: Vec<Vec2>,
39        color: HexColor,
40    },
41    SolidPolygon {
42        transform: Transform,
43        vertices: Vec<Vec2>,
44        radius: f32,
45        color: HexColor,
46    },
47    Circle {
48        center: Vec2,
49        radius: f32,
50        color: HexColor,
51    },
52    SolidCircle {
53        transform: Transform,
54        radius: f32,
55        color: HexColor,
56    },
57    SolidCapsule {
58        p1: Vec2,
59        p2: Vec2,
60        radius: f32,
61        color: HexColor,
62    },
63    Segment {
64        p1: Vec2,
65        p2: Vec2,
66        color: HexColor,
67    },
68    Transform(Transform),
69    Point {
70        p: Vec2,
71        size: f32,
72        color: HexColor,
73    },
74    String {
75        p: Vec2,
76        s: String,
77        color: HexColor,
78    },
79}
80
81// Safe debug draw trait (no ffi types)
82pub trait DebugDraw {
83    fn draw_polygon(&mut self, _vertices: &[Vec2], _color: HexColor) {}
84    fn draw_solid_polygon(
85        &mut self,
86        _transform: Transform,
87        _vertices: &[Vec2],
88        _radius: f32,
89        _color: HexColor,
90    ) {
91    }
92    fn draw_circle(&mut self, _center: Vec2, _radius: f32, _color: HexColor) {}
93    fn draw_solid_circle(&mut self, _transform: Transform, _radius: f32, _color: HexColor) {}
94    fn draw_solid_capsule(&mut self, _p1: Vec2, _p2: Vec2, _radius: f32, _color: HexColor) {}
95    fn draw_segment(&mut self, _p1: Vec2, _p2: Vec2, _color: HexColor) {}
96    fn draw_transform(&mut self, _transform: Transform) {}
97    fn draw_point(&mut self, _p: Vec2, _size: f32, _color: HexColor) {}
98    fn draw_string(&mut self, _p: Vec2, _s: &str, _color: HexColor) {}
99}
100
101// Raw low-level trait (kept for performance/zero-copy use-cases)
102pub trait RawDebugDraw {
103    fn draw_polygon(&mut self, _vertices: &[ffi::b2Vec2], _color: HexColor) {}
104    fn draw_solid_polygon(
105        &mut self,
106        _transform: ffi::b2Transform,
107        _vertices: &[ffi::b2Vec2],
108        _radius: f32,
109        _color: HexColor,
110    ) {
111    }
112    fn draw_circle(&mut self, _center: ffi::b2Vec2, _radius: f32, _color: HexColor) {}
113    fn draw_solid_circle(&mut self, _transform: ffi::b2Transform, _radius: f32, _color: HexColor) {}
114    fn draw_solid_capsule(
115        &mut self,
116        _p1: ffi::b2Vec2,
117        _p2: ffi::b2Vec2,
118        _radius: f32,
119        _color: HexColor,
120    ) {
121    }
122    fn draw_segment(&mut self, _p1: ffi::b2Vec2, _p2: ffi::b2Vec2, _color: HexColor) {}
123    fn draw_transform(&mut self, _transform: ffi::b2Transform) {}
124    fn draw_point(&mut self, _p: ffi::b2Vec2, _size: f32, _color: HexColor) {}
125    fn draw_string(&mut self, _p: ffi::b2Vec2, _s: &CStr, _color: HexColor) {}
126}
127
128#[derive(Copy, Clone, Debug)]
129pub struct DebugDrawOptions {
130    pub drawing_bounds: ffi::b2AABB,
131    pub force_scale: f32,
132    pub joint_scale: f32,
133    pub draw_shapes: bool,
134    pub draw_joints: bool,
135    pub draw_joint_extras: bool,
136    pub draw_bounds: bool,
137    pub draw_mass: bool,
138    pub draw_body_names: bool,
139    /// Draw contact points (upstream name: `drawContactPoints`).
140    pub draw_contacts: bool,
141    pub draw_graph_colors: bool,
142    pub draw_contact_features: bool,
143    pub draw_contact_normals: bool,
144    pub draw_contact_forces: bool,
145    pub draw_friction_forces: bool,
146    pub draw_islands: bool,
147}
148
149impl Default for DebugDrawOptions {
150    fn default() -> Self {
151        Self {
152            drawing_bounds: ffi::b2AABB {
153                lowerBound: ffi::b2Vec2 {
154                    x: -1.0e9,
155                    y: -1.0e9,
156                },
157                upperBound: ffi::b2Vec2 { x: 1.0e9, y: 1.0e9 },
158            },
159            force_scale: 1.0,
160            joint_scale: 1.0,
161            draw_shapes: true,
162            draw_joints: true,
163            draw_joint_extras: false,
164            draw_bounds: false,
165            draw_mass: false,
166            draw_body_names: false,
167            draw_contacts: false,
168            draw_graph_colors: false,
169            draw_contact_features: false,
170            draw_contact_normals: false,
171            draw_contact_forces: false,
172            draw_friction_forces: false,
173            draw_islands: false,
174        }
175    }
176}
177
178struct DebugCtx<'a> {
179    drawer: &'a mut dyn DebugDraw,
180    panicked: &'a mut bool,
181    panic: &'a mut Option<Box<dyn Any + Send + 'static>>,
182}
183struct RawDebugCtx<'a> {
184    drawer: &'a mut dyn RawDebugDraw,
185    panicked: &'a mut bool,
186    panic: &'a mut Option<Box<dyn Any + Send + 'static>>,
187}
188
189impl World {
190    /// Collect debug draw commands into a vector (fully safe).
191    ///
192    /// This calls into Box2D debug draw but does not invoke user code during the draw.
193    pub fn debug_draw_collect(&mut self, opts: DebugDrawOptions) -> Vec<DebugDrawCmd> {
194        crate::core::callback_state::assert_not_in_callback();
195        struct Collector {
196            cmds: Vec<DebugDrawCmd>,
197        }
198        impl DebugDraw for Collector {
199            fn draw_polygon(&mut self, vertices: &[Vec2], color: HexColor) {
200                self.cmds.push(DebugDrawCmd::Polygon {
201                    vertices: vertices.to_vec(),
202                    color,
203                });
204            }
205            fn draw_solid_polygon(
206                &mut self,
207                transform: Transform,
208                vertices: &[Vec2],
209                radius: f32,
210                color: HexColor,
211            ) {
212                self.cmds.push(DebugDrawCmd::SolidPolygon {
213                    transform,
214                    vertices: vertices.to_vec(),
215                    radius,
216                    color,
217                });
218            }
219            fn draw_circle(&mut self, center: Vec2, radius: f32, color: HexColor) {
220                self.cmds.push(DebugDrawCmd::Circle {
221                    center,
222                    radius,
223                    color,
224                });
225            }
226            fn draw_solid_circle(&mut self, transform: Transform, radius: f32, color: HexColor) {
227                self.cmds.push(DebugDrawCmd::SolidCircle {
228                    transform,
229                    radius,
230                    color,
231                });
232            }
233            fn draw_solid_capsule(&mut self, p1: Vec2, p2: Vec2, radius: f32, color: HexColor) {
234                self.cmds.push(DebugDrawCmd::SolidCapsule {
235                    p1,
236                    p2,
237                    radius,
238                    color,
239                });
240            }
241            fn draw_segment(&mut self, p1: Vec2, p2: Vec2, color: HexColor) {
242                self.cmds.push(DebugDrawCmd::Segment { p1, p2, color });
243            }
244            fn draw_transform(&mut self, transform: Transform) {
245                self.cmds.push(DebugDrawCmd::Transform(transform));
246            }
247            fn draw_point(&mut self, p: Vec2, size: f32, color: HexColor) {
248                self.cmds.push(DebugDrawCmd::Point { p, size, color });
249            }
250            fn draw_string(&mut self, p: Vec2, s: &str, color: HexColor) {
251                self.cmds.push(DebugDrawCmd::String {
252                    p,
253                    s: s.to_owned(),
254                    color,
255                });
256            }
257        }
258
259        let mut c = Collector { cmds: Vec::new() };
260        self.debug_draw(&mut c, opts);
261        c.cmds
262    }
263
264    // Safe wrapper: converts to Vec2/Transform and &str
265    ///
266    /// Box2D invokes the draw callbacks while traversing internal world state. During this call,
267    /// any attempt to call into the Box2D world through `boxdd` will panic, since the world is
268    /// considered locked by Box2D.
269    pub fn debug_draw(&mut self, drawer: &mut impl DebugDraw, opts: DebugDrawOptions) {
270        crate::core::callback_state::assert_not_in_callback();
271        let mut panicked = false;
272        let mut panic: Option<Box<dyn Any + Send + 'static>> = None;
273        let mut ctx = DebugCtx {
274            drawer,
275            panicked: &mut panicked,
276            panic: &mut panic,
277        };
278        let mut dd = unsafe { ffi::b2DefaultDebugDraw() };
279        // Hook callbacks
280        unsafe extern "C" fn draw_polygon_cb(
281            vertices: *const ffi::b2Vec2,
282            count: i32,
283            color: HexColor,
284            context: *mut core::ffi::c_void,
285        ) {
286            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
287            if *ctx.panicked {
288                return;
289            }
290            let n = count.max(0) as usize;
291            if n == 0 || vertices.is_null() {
292                return;
293            }
294            let src = unsafe { core::slice::from_raw_parts(vertices, n) };
295            let mut verts: SmallVec<[Vec2; 8]> = SmallVec::with_capacity(src.len().min(8));
296            for v in src.iter().copied() {
297                verts.push(Vec2::from(v));
298            }
299            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
300                let _g = crate::core::callback_state::CallbackGuard::enter();
301                ctx.drawer.draw_polygon(&verts, color);
302            }));
303            if let Err(p) = r {
304                *ctx.panicked = true;
305                *ctx.panic = Some(p);
306            }
307        }
308        unsafe extern "C" fn draw_solid_polygon_cb(
309            transform: ffi::b2Transform,
310            vertices: *const ffi::b2Vec2,
311            count: i32,
312            radius: f32,
313            color: HexColor,
314            context: *mut core::ffi::c_void,
315        ) {
316            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
317            if *ctx.panicked {
318                return;
319            }
320            let n = count.max(0) as usize;
321            if n == 0 || vertices.is_null() {
322                return;
323            }
324            let src = unsafe { core::slice::from_raw_parts(vertices, n) };
325            let mut verts: SmallVec<[Vec2; 8]> = SmallVec::with_capacity(src.len().min(8));
326            for v in src.iter().copied() {
327                verts.push(Vec2::from(v));
328            }
329            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
330                let _g = crate::core::callback_state::CallbackGuard::enter();
331                ctx.drawer
332                    .draw_solid_polygon(Transform::from(transform), &verts, radius, color);
333            }));
334            if let Err(p) = r {
335                *ctx.panicked = true;
336                *ctx.panic = Some(p);
337            }
338        }
339        unsafe extern "C" fn draw_circle_cb(
340            center: ffi::b2Vec2,
341            radius: f32,
342            color: HexColor,
343            context: *mut core::ffi::c_void,
344        ) {
345            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
346            if *ctx.panicked {
347                return;
348            }
349            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
350                let _g = crate::core::callback_state::CallbackGuard::enter();
351                ctx.drawer.draw_circle(Vec2::from(center), radius, color);
352            }));
353            if let Err(p) = r {
354                *ctx.panicked = true;
355                *ctx.panic = Some(p);
356            }
357        }
358        unsafe extern "C" fn draw_solid_circle_cb(
359            transform: ffi::b2Transform,
360            radius: f32,
361            color: HexColor,
362            context: *mut core::ffi::c_void,
363        ) {
364            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
365            if *ctx.panicked {
366                return;
367            }
368            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
369                let _g = crate::core::callback_state::CallbackGuard::enter();
370                ctx.drawer
371                    .draw_solid_circle(Transform::from(transform), radius, color);
372            }));
373            if let Err(p) = r {
374                *ctx.panicked = true;
375                *ctx.panic = Some(p);
376            }
377        }
378        unsafe extern "C" fn draw_solid_capsule_cb(
379            p1: ffi::b2Vec2,
380            p2: ffi::b2Vec2,
381            radius: f32,
382            color: HexColor,
383            context: *mut core::ffi::c_void,
384        ) {
385            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
386            if *ctx.panicked {
387                return;
388            }
389            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
390                let _g = crate::core::callback_state::CallbackGuard::enter();
391                ctx.drawer
392                    .draw_solid_capsule(Vec2::from(p1), Vec2::from(p2), radius, color);
393            }));
394            if let Err(p) = r {
395                *ctx.panicked = true;
396                *ctx.panic = Some(p);
397            }
398        }
399        unsafe extern "C" fn draw_line_cb(
400            p1: ffi::b2Vec2,
401            p2: ffi::b2Vec2,
402            color: HexColor,
403            context: *mut core::ffi::c_void,
404        ) {
405            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
406            if *ctx.panicked {
407                return;
408            }
409            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
410                let _g = crate::core::callback_state::CallbackGuard::enter();
411                ctx.drawer
412                    .draw_segment(Vec2::from(p1), Vec2::from(p2), color);
413            }));
414            if let Err(p) = r {
415                *ctx.panicked = true;
416                *ctx.panic = Some(p);
417            }
418        }
419        unsafe extern "C" fn draw_transform_cb(
420            transform: ffi::b2Transform,
421            context: *mut core::ffi::c_void,
422        ) {
423            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
424            if *ctx.panicked {
425                return;
426            }
427            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
428                let _g = crate::core::callback_state::CallbackGuard::enter();
429                ctx.drawer.draw_transform(Transform::from(transform));
430            }));
431            if let Err(p) = r {
432                *ctx.panicked = true;
433                *ctx.panic = Some(p);
434            }
435        }
436        unsafe extern "C" fn draw_point_cb(
437            p: ffi::b2Vec2,
438            size: f32,
439            color: HexColor,
440            context: *mut core::ffi::c_void,
441        ) {
442            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
443            if *ctx.panicked {
444                return;
445            }
446            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
447                let _g = crate::core::callback_state::CallbackGuard::enter();
448                ctx.drawer.draw_point(Vec2::from(p), size, color);
449            }));
450            if let Err(p) = r {
451                *ctx.panicked = true;
452                *ctx.panic = Some(p);
453            }
454        }
455        unsafe extern "C" fn draw_string_cb(
456            p: ffi::b2Vec2,
457            s: *const core::ffi::c_char,
458            color: HexColor,
459            context: *mut core::ffi::c_void,
460        ) {
461            let ctx = unsafe { &mut *(context as *mut DebugCtx) };
462            if *ctx.panicked {
463                return;
464            }
465            if !s.is_null() {
466                let cs = unsafe { CStr::from_ptr(s) };
467                let s = cs.to_string_lossy();
468                let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
469                    let _g = crate::core::callback_state::CallbackGuard::enter();
470                    ctx.drawer.draw_string(Vec2::from(p), &s, color);
471                }));
472                if let Err(p) = r {
473                    *ctx.panicked = true;
474                    *ctx.panic = Some(p);
475                }
476            }
477        }
478
479        dd.DrawPolygonFcn = Some(draw_polygon_cb);
480        dd.DrawSolidPolygonFcn = Some(draw_solid_polygon_cb);
481        dd.DrawCircleFcn = Some(draw_circle_cb);
482        dd.DrawSolidCircleFcn = Some(draw_solid_circle_cb);
483        dd.DrawSolidCapsuleFcn = Some(draw_solid_capsule_cb);
484        dd.DrawLineFcn = Some(draw_line_cb);
485        dd.DrawTransformFcn = Some(draw_transform_cb);
486        dd.DrawPointFcn = Some(draw_point_cb);
487        dd.DrawStringFcn = Some(draw_string_cb);
488
489        // Options
490        dd.drawingBounds = opts.drawing_bounds;
491        dd.forceScale = opts.force_scale;
492        dd.jointScale = opts.joint_scale;
493        dd.drawShapes = opts.draw_shapes;
494        dd.drawJoints = opts.draw_joints;
495        dd.drawJointExtras = opts.draw_joint_extras;
496        dd.drawBounds = opts.draw_bounds;
497        dd.drawMass = opts.draw_mass;
498        dd.drawBodyNames = opts.draw_body_names;
499        dd.drawContactPoints = opts.draw_contacts;
500        dd.drawGraphColors = opts.draw_graph_colors;
501        dd.drawContactFeatures = opts.draw_contact_features;
502        dd.drawContactNormals = opts.draw_contact_normals;
503        dd.drawContactForces = opts.draw_contact_forces;
504        dd.drawFrictionForces = opts.draw_friction_forces;
505        dd.drawIslands = opts.draw_islands;
506        dd.context = &mut ctx as *mut _ as *mut _;
507
508        unsafe { ffi::b2World_Draw(self.raw(), &mut dd) };
509
510        // Flush deferred destroys scheduled from draw callbacks.
511        self.core_arc().process_deferred_destroys();
512
513        if let Some(p) = panic.take() {
514            std::panic::resume_unwind(p);
515        }
516    }
517
518    // Raw path: zero-copy FFI types to trait
519    ///
520    /// Box2D invokes the draw callbacks while traversing internal world state. During this call,
521    /// any attempt to call into the Box2D world through `boxdd` will panic, since the world is
522    /// considered locked by Box2D.
523    pub fn debug_draw_raw(&mut self, drawer: &mut impl RawDebugDraw, opts: DebugDrawOptions) {
524        crate::core::callback_state::assert_not_in_callback();
525        let mut panicked = false;
526        let mut panic: Option<Box<dyn Any + Send + 'static>> = None;
527        let mut ctx = RawDebugCtx {
528            drawer,
529            panicked: &mut panicked,
530            panic: &mut panic,
531        };
532        let mut dd = unsafe { ffi::b2DefaultDebugDraw() };
533        unsafe extern "C" fn draw_polygon_cb(
534            vertices: *const ffi::b2Vec2,
535            count: i32,
536            color: HexColor,
537            context: *mut core::ffi::c_void,
538        ) {
539            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
540            if *ctx.panicked {
541                return;
542            }
543            let n = count.max(0) as usize;
544            if n == 0 || vertices.is_null() {
545                return;
546            }
547            let slice = unsafe { core::slice::from_raw_parts(vertices, n) };
548            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
549                let _g = crate::core::callback_state::CallbackGuard::enter();
550                ctx.drawer.draw_polygon(slice, color);
551            }));
552            if let Err(p) = r {
553                *ctx.panicked = true;
554                *ctx.panic = Some(p);
555            }
556        }
557        unsafe extern "C" fn draw_solid_polygon_cb(
558            transform: ffi::b2Transform,
559            vertices: *const ffi::b2Vec2,
560            count: i32,
561            radius: f32,
562            color: HexColor,
563            context: *mut core::ffi::c_void,
564        ) {
565            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
566            if *ctx.panicked {
567                return;
568            }
569            let n = count.max(0) as usize;
570            if n == 0 || vertices.is_null() {
571                return;
572            }
573            let slice = unsafe { core::slice::from_raw_parts(vertices, n) };
574            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
575                let _g = crate::core::callback_state::CallbackGuard::enter();
576                ctx.drawer
577                    .draw_solid_polygon(transform, slice, radius, color);
578            }));
579            if let Err(p) = r {
580                *ctx.panicked = true;
581                *ctx.panic = Some(p);
582            }
583        }
584        unsafe extern "C" fn draw_circle_cb(
585            center: ffi::b2Vec2,
586            radius: f32,
587            color: HexColor,
588            context: *mut core::ffi::c_void,
589        ) {
590            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
591            if *ctx.panicked {
592                return;
593            }
594            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
595                let _g = crate::core::callback_state::CallbackGuard::enter();
596                ctx.drawer.draw_circle(center, radius, color);
597            }));
598            if let Err(p) = r {
599                *ctx.panicked = true;
600                *ctx.panic = Some(p);
601            }
602        }
603        unsafe extern "C" fn draw_solid_circle_cb(
604            transform: ffi::b2Transform,
605            radius: f32,
606            color: HexColor,
607            context: *mut core::ffi::c_void,
608        ) {
609            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
610            if *ctx.panicked {
611                return;
612            }
613            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
614                let _g = crate::core::callback_state::CallbackGuard::enter();
615                ctx.drawer.draw_solid_circle(transform, radius, color);
616            }));
617            if let Err(p) = r {
618                *ctx.panicked = true;
619                *ctx.panic = Some(p);
620            }
621        }
622        unsafe extern "C" fn draw_solid_capsule_cb(
623            p1: ffi::b2Vec2,
624            p2: ffi::b2Vec2,
625            radius: f32,
626            color: HexColor,
627            context: *mut core::ffi::c_void,
628        ) {
629            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
630            if *ctx.panicked {
631                return;
632            }
633            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
634                let _g = crate::core::callback_state::CallbackGuard::enter();
635                ctx.drawer.draw_solid_capsule(p1, p2, radius, color);
636            }));
637            if let Err(p) = r {
638                *ctx.panicked = true;
639                *ctx.panic = Some(p);
640            }
641        }
642        unsafe extern "C" fn draw_line_cb(
643            p1: ffi::b2Vec2,
644            p2: ffi::b2Vec2,
645            color: HexColor,
646            context: *mut core::ffi::c_void,
647        ) {
648            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
649            if *ctx.panicked {
650                return;
651            }
652            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
653                let _g = crate::core::callback_state::CallbackGuard::enter();
654                ctx.drawer.draw_segment(p1, p2, color);
655            }));
656            if let Err(p) = r {
657                *ctx.panicked = true;
658                *ctx.panic = Some(p);
659            }
660        }
661        unsafe extern "C" fn draw_transform_cb(
662            transform: ffi::b2Transform,
663            context: *mut core::ffi::c_void,
664        ) {
665            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
666            if *ctx.panicked {
667                return;
668            }
669            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
670                let _g = crate::core::callback_state::CallbackGuard::enter();
671                ctx.drawer.draw_transform(transform);
672            }));
673            if let Err(p) = r {
674                *ctx.panicked = true;
675                *ctx.panic = Some(p);
676            }
677        }
678        unsafe extern "C" fn draw_point_cb(
679            p: ffi::b2Vec2,
680            size: f32,
681            color: HexColor,
682            context: *mut core::ffi::c_void,
683        ) {
684            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
685            if *ctx.panicked {
686                return;
687            }
688            let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
689                let _g = crate::core::callback_state::CallbackGuard::enter();
690                ctx.drawer.draw_point(p, size, color);
691            }));
692            if let Err(p) = r {
693                *ctx.panicked = true;
694                *ctx.panic = Some(p);
695            }
696        }
697        unsafe extern "C" fn draw_string_cb(
698            p: ffi::b2Vec2,
699            s: *const core::ffi::c_char,
700            color: HexColor,
701            context: *mut core::ffi::c_void,
702        ) {
703            let ctx = unsafe { &mut *(context as *mut RawDebugCtx) };
704            if *ctx.panicked {
705                return;
706            }
707            if !s.is_null() {
708                let cs = unsafe { CStr::from_ptr(s) };
709                let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
710                    let _g = crate::core::callback_state::CallbackGuard::enter();
711                    ctx.drawer.draw_string(p, cs, color);
712                }));
713                if let Err(p) = r {
714                    *ctx.panicked = true;
715                    *ctx.panic = Some(p);
716                }
717            }
718        }
719        dd.DrawPolygonFcn = Some(draw_polygon_cb);
720        dd.DrawSolidPolygonFcn = Some(draw_solid_polygon_cb);
721        dd.DrawCircleFcn = Some(draw_circle_cb);
722        dd.DrawSolidCircleFcn = Some(draw_solid_circle_cb);
723        dd.DrawSolidCapsuleFcn = Some(draw_solid_capsule_cb);
724        dd.DrawLineFcn = Some(draw_line_cb);
725        dd.DrawTransformFcn = Some(draw_transform_cb);
726        dd.DrawPointFcn = Some(draw_point_cb);
727        dd.DrawStringFcn = Some(draw_string_cb);
728        dd.drawingBounds = opts.drawing_bounds;
729        dd.forceScale = opts.force_scale;
730        dd.jointScale = opts.joint_scale;
731        dd.drawShapes = opts.draw_shapes;
732        dd.drawJoints = opts.draw_joints;
733        dd.drawJointExtras = opts.draw_joint_extras;
734        dd.drawBounds = opts.draw_bounds;
735        dd.drawMass = opts.draw_mass;
736        dd.drawBodyNames = opts.draw_body_names;
737        dd.drawContactPoints = opts.draw_contacts;
738        dd.drawGraphColors = opts.draw_graph_colors;
739        dd.drawContactFeatures = opts.draw_contact_features;
740        dd.drawContactNormals = opts.draw_contact_normals;
741        dd.drawContactForces = opts.draw_contact_forces;
742        dd.drawFrictionForces = opts.draw_friction_forces;
743        dd.drawIslands = opts.draw_islands;
744        dd.context = &mut ctx as *mut _ as *mut _;
745        unsafe { ffi::b2World_Draw(self.raw(), &mut dd) };
746
747        // Flush deferred destroys scheduled from draw callbacks.
748        self.core_arc().process_deferred_destroys();
749
750        if let Some(p) = panic.take() {
751            std::panic::resume_unwind(p);
752        }
753    }
754}