Skip to main content

boxdd/
collision.rs

1//! Standalone low-level collision geometry helpers.
2//!
3//! This module wraps Box2D's standalone collision algorithms without exposing raw FFI
4//! structs. It is intentionally more explicit than the high-level `World` query API and
5//! is useful when you want to run geometric tests or contact-manifold generation without
6//! a world instance.
7
8use crate::{
9    core::math::{Rot, Transform},
10    error::{ApiError, ApiResult},
11    query::Aabb,
12    shapes::{Capsule, ChainSegment, Circle, Polygon, Segment},
13    types::{Manifold, Vec2},
14};
15use boxdd_sys::ffi;
16use core::fmt;
17
18/// Maximum number of points supported by a Box2D shape proxy.
19pub const MAX_SHAPE_PROXY_POINTS: usize = ffi::B2_MAX_POLYGON_VERTICES as usize;
20
21const _: () = {
22    assert!(core::mem::size_of::<Vec2>() == core::mem::size_of::<ffi::b2Vec2>());
23    assert!(core::mem::align_of::<Vec2>() == core::mem::align_of::<ffi::b2Vec2>());
24};
25
26#[inline]
27fn check_collision_vec2_valid(value: Vec2) -> ApiResult<()> {
28    if value.is_valid() {
29        Ok(())
30    } else {
31        Err(ApiError::InvalidArgument)
32    }
33}
34
35#[inline]
36fn check_collision_rot_valid(value: Rot) -> ApiResult<()> {
37    if value.is_valid() {
38        Ok(())
39    } else {
40        Err(ApiError::InvalidArgument)
41    }
42}
43
44#[inline]
45fn check_collision_transform_valid(value: Transform) -> ApiResult<()> {
46    if value.is_valid() {
47        Ok(())
48    } else {
49        Err(ApiError::InvalidArgument)
50    }
51}
52
53#[inline]
54fn check_collision_non_negative_finite_scalar(value: f32) -> ApiResult<()> {
55    if crate::is_valid_float(value) && value >= 0.0 {
56        Ok(())
57    } else {
58        Err(ApiError::InvalidArgument)
59    }
60}
61
62#[inline]
63fn check_collision_unit_interval_scalar(value: f32) -> ApiResult<()> {
64    if crate::is_valid_float(value) && (0.0..=1.0).contains(&value) {
65        Ok(())
66    } else {
67        Err(ApiError::InvalidArgument)
68    }
69}
70
71#[inline]
72fn assert_collision_input_valid(name: &str, valid: bool) {
73    assert!(valid, "{name} contains invalid Box2D input");
74}
75
76struct RayCastAxisInput {
77    origin: f32,
78    translation: f32,
79    lower: f32,
80    upper: f32,
81    enter_normal: Vec2,
82    exit_normal: Vec2,
83}
84
85struct RayCastAxisState {
86    tmin: f32,
87    tmax: f32,
88    normal: Vec2,
89}
90
91#[inline]
92fn ray_cast_axis(input: RayCastAxisInput, state: &mut RayCastAxisState) -> bool {
93    if input.translation.abs() < f32::EPSILON {
94        return input.lower <= input.origin && input.origin <= input.upper;
95    }
96
97    let inv_translation = 1.0 / input.translation;
98    let mut t1 = (input.lower - input.origin) * inv_translation;
99    let mut t2 = (input.upper - input.origin) * inv_translation;
100    let mut n1 = input.enter_normal;
101    let mut n2 = input.exit_normal;
102
103    if t1 > t2 {
104        core::mem::swap(&mut t1, &mut t2);
105        core::mem::swap(&mut n1, &mut n2);
106    }
107
108    if t1 > state.tmin {
109        state.tmin = t1;
110        state.normal = n1;
111    }
112
113    if t2 < state.tmax {
114        state.tmax = t2;
115    }
116
117    state.tmin <= state.tmax
118}
119
120/// A Box2D point-cloud proxy used by distance, shape-cast, and TOI algorithms.
121///
122/// Returns `None` from [`ShapeProxy::new`] when the iterator is empty, contains more than
123/// [`MAX_SHAPE_PROXY_POINTS`] points, or contains invalid Box2D coordinates/radius data.
124#[doc(alias = "shape_proxy")]
125#[derive(Copy, Clone)]
126pub struct ShapeProxy {
127    raw: ffi::b2ShapeProxy,
128}
129
130impl ShapeProxy {
131    #[inline]
132    pub(crate) const fn from_raw(raw: ffi::b2ShapeProxy) -> Self {
133        Self { raw }
134    }
135
136    /// Build a proxy from `1..=MAX_SHAPE_PROXY_POINTS` points and an external radius.
137    pub fn new<I, P>(points: I, radius: f32) -> Option<Self>
138    where
139        I: IntoIterator<Item = P>,
140        P: Into<Vec2>,
141    {
142        Self::try_new(points, radius).ok()
143    }
144
145    /// Build a proxy from `1..=MAX_SHAPE_PROXY_POINTS` valid points and an external radius.
146    pub fn try_new<I, P>(points: I, radius: f32) -> ApiResult<Self>
147    where
148        I: IntoIterator<Item = P>,
149        P: Into<Vec2>,
150    {
151        check_collision_non_negative_finite_scalar(radius)?;
152        let mut raw_points = [ffi::b2Vec2 { x: 0.0, y: 0.0 }; MAX_SHAPE_PROXY_POINTS];
153        let mut count = 0usize;
154
155        for point in points {
156            if count == MAX_SHAPE_PROXY_POINTS {
157                return Err(ApiError::InvalidArgument);
158            }
159            let point = point.into();
160            check_collision_vec2_valid(point)?;
161            raw_points[count] = point.into_raw();
162            count += 1;
163        }
164
165        if count == 0 {
166            return Err(ApiError::InvalidArgument);
167        }
168
169        let raw = unsafe { ffi::b2MakeProxy(raw_points.as_ptr(), count as i32, radius) };
170        Ok(Self { raw })
171    }
172
173    /// The points stored in this proxy.
174    #[inline]
175    pub fn points(&self) -> &[Vec2] {
176        let count = self.count();
177        unsafe { core::slice::from_raw_parts(self.raw.points.as_ptr().cast::<Vec2>(), count) }
178    }
179
180    /// The number of points stored in this proxy.
181    #[inline]
182    pub fn count(&self) -> usize {
183        self.raw.count.clamp(0, MAX_SHAPE_PROXY_POINTS as i32) as usize
184    }
185
186    /// The proxy's external radius.
187    #[inline]
188    pub fn radius(&self) -> f32 {
189        self.raw.radius
190    }
191
192    /// Validate this proxy for Box2D standalone collision algorithms.
193    pub fn validate(&self) -> ApiResult<()> {
194        if !(1..=MAX_SHAPE_PROXY_POINTS as i32).contains(&self.raw.count) {
195            return Err(ApiError::InvalidArgument);
196        }
197        check_collision_non_negative_finite_scalar(self.raw.radius)?;
198        for point in self.points().iter().copied() {
199            check_collision_vec2_valid(point)?;
200        }
201        Ok(())
202    }
203
204    #[inline]
205    pub(crate) fn into_raw(self) -> ffi::b2ShapeProxy {
206        self.raw
207    }
208
209    #[inline]
210    fn raw(self) -> ffi::b2ShapeProxy {
211        self.into_raw()
212    }
213}
214
215impl fmt::Debug for ShapeProxy {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        f.debug_struct("ShapeProxy")
218            .field("points", &self.points())
219            .field("radius", &self.radius())
220            .finish()
221    }
222}
223
224/// Input for shape-specific casts against circles, capsules, segments, and polygons.
225#[doc(alias = "shape_cast_input")]
226#[derive(Copy, Clone, Debug)]
227pub struct ShapeCastInput {
228    pub proxy: ShapeProxy,
229    pub translation: Vec2,
230    pub max_fraction: f32,
231    pub can_encroach: bool,
232}
233
234impl ShapeCastInput {
235    /// Build a shape cast over `proxy` moving by `translation`.
236    #[inline]
237    pub fn new<T: Into<Vec2>>(proxy: ShapeProxy, translation: T) -> Self {
238        Self {
239            proxy,
240            translation: translation.into(),
241            max_fraction: 1.0,
242            can_encroach: false,
243        }
244    }
245
246    /// Limit the portion of `translation` considered by the cast.
247    #[inline]
248    pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
249        self.max_fraction = max_fraction;
250        self
251    }
252
253    /// Allow encroachment when initially touching.
254    #[inline]
255    pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
256        self.can_encroach = can_encroach;
257        self
258    }
259
260    /// Validate this input before crossing the Box2D FFI boundary.
261    pub fn validate(&self) -> ApiResult<()> {
262        self.proxy.validate()?;
263        check_collision_vec2_valid(self.translation)?;
264        check_collision_unit_interval_scalar(self.max_fraction)
265    }
266
267    #[inline]
268    pub fn into_raw(self) -> ffi::b2ShapeCastInput {
269        ffi::b2ShapeCastInput {
270            proxy: self.proxy.into_raw(),
271            translation: self.translation.into_raw(),
272            maxFraction: self.max_fraction,
273            canEncroach: self.can_encroach,
274        }
275    }
276}
277
278/// Warm-start cache for repeated GJK distance calls.
279#[doc(alias = "simplex_cache")]
280#[derive(Copy, Clone)]
281pub struct SimplexCache {
282    raw: ffi::b2SimplexCache,
283}
284
285impl Default for SimplexCache {
286    fn default() -> Self {
287        Self {
288            raw: ffi::b2SimplexCache {
289                count: 0,
290                indexA: [0; 3],
291                indexB: [0; 3],
292            },
293        }
294    }
295}
296
297impl SimplexCache {
298    /// Create a zeroed cache for the first distance query.
299    #[inline]
300    pub fn new() -> Self {
301        Self::default()
302    }
303
304    /// Reset the cache to its initial zeroed state.
305    #[inline]
306    pub fn clear(&mut self) {
307        *self = Self::default();
308    }
309
310    /// The number of cached simplex points.
311    #[inline]
312    pub fn count(&self) -> usize {
313        self.raw.count.min(3) as usize
314    }
315
316    /// Cached simplex indices for shape A.
317    #[inline]
318    pub fn index_a(&self) -> &[u8] {
319        &self.raw.indexA[..self.count()]
320    }
321
322    /// Cached simplex indices for shape B.
323    #[inline]
324    pub fn index_b(&self) -> &[u8] {
325        &self.raw.indexB[..self.count()]
326    }
327
328    #[inline]
329    fn raw_mut(&mut self) -> *mut ffi::b2SimplexCache {
330        &mut self.raw
331    }
332}
333
334impl fmt::Debug for SimplexCache {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        f.debug_struct("SimplexCache")
337            .field("count", &self.count())
338            .field("index_a", &self.index_a())
339            .field("index_b", &self.index_b())
340            .finish()
341    }
342}
343
344/// Result of [`segment_distance`].
345#[doc(alias = "segment_distance_result")]
346#[derive(Copy, Clone, Debug)]
347pub struct SegmentDistanceResult {
348    pub closest1: Vec2,
349    pub closest2: Vec2,
350    pub fraction1: f32,
351    pub fraction2: f32,
352    pub distance_squared: f32,
353}
354
355impl SegmentDistanceResult {
356    #[inline]
357    pub fn from_raw(raw: ffi::b2SegmentDistanceResult) -> Self {
358        Self {
359            closest1: Vec2::from_raw(raw.closest1),
360            closest2: Vec2::from_raw(raw.closest2),
361            fraction1: raw.fraction1,
362            fraction2: raw.fraction2,
363            distance_squared: raw.distanceSquared,
364        }
365    }
366}
367
368/// Low-level ray-cast or shape-cast output.
369#[doc(alias = "cast_output")]
370#[derive(Copy, Clone, Debug)]
371pub struct CastOutput {
372    pub normal: Vec2,
373    pub point: Vec2,
374    pub fraction: f32,
375    pub iterations: i32,
376    pub hit: bool,
377}
378
379impl CastOutput {
380    pub const MISS: Self = Self {
381        normal: Vec2::ZERO,
382        point: Vec2::ZERO,
383        fraction: 0.0,
384        iterations: 0,
385        hit: false,
386    };
387
388    #[inline]
389    pub fn from_raw(raw: ffi::b2CastOutput) -> Self {
390        Self {
391            normal: Vec2::from_raw(raw.normal),
392            point: Vec2::from_raw(raw.point),
393            fraction: raw.fraction,
394            iterations: raw.iterations,
395            hit: raw.hit,
396        }
397    }
398}
399
400/// Input for [`shape_distance`].
401#[doc(alias = "distance_input")]
402#[derive(Copy, Clone, Debug)]
403pub struct DistanceInput {
404    pub proxy_a: ShapeProxy,
405    pub proxy_b: ShapeProxy,
406    pub transform_a: Transform,
407    pub transform_b: Transform,
408    pub use_radii: bool,
409}
410
411impl DistanceInput {
412    /// Build distance input with `use_radii = false`.
413    #[inline]
414    pub fn new(
415        proxy_a: ShapeProxy,
416        proxy_b: ShapeProxy,
417        transform_a: Transform,
418        transform_b: Transform,
419    ) -> Self {
420        Self {
421            proxy_a,
422            proxy_b,
423            transform_a,
424            transform_b,
425            use_radii: false,
426        }
427    }
428
429    /// Set whether proxy radii should affect the distance result.
430    #[inline]
431    pub fn with_radii(mut self, use_radii: bool) -> Self {
432        self.use_radii = use_radii;
433        self
434    }
435
436    /// Validate this input before crossing the Box2D FFI boundary.
437    pub fn validate(&self) -> ApiResult<()> {
438        self.proxy_a.validate()?;
439        self.proxy_b.validate()?;
440        check_collision_transform_valid(self.transform_a)?;
441        check_collision_transform_valid(self.transform_b)?;
442        Ok(())
443    }
444
445    #[inline]
446    pub fn into_raw(self) -> ffi::b2DistanceInput {
447        ffi::b2DistanceInput {
448            proxyA: self.proxy_a.raw(),
449            proxyB: self.proxy_b.raw(),
450            transformA: self.transform_a.into_raw(),
451            transformB: self.transform_b.into_raw(),
452            useRadii: self.use_radii,
453        }
454    }
455}
456
457/// Output from [`shape_distance`].
458#[doc(alias = "distance_output")]
459#[derive(Copy, Clone, Debug)]
460pub struct DistanceOutput {
461    pub point_a: Vec2,
462    pub point_b: Vec2,
463    pub normal: Vec2,
464    pub distance: f32,
465    pub iterations: i32,
466    pub simplex_count: i32,
467}
468
469impl DistanceOutput {
470    #[inline]
471    pub fn from_raw(raw: ffi::b2DistanceOutput) -> Self {
472        Self {
473            point_a: Vec2::from_raw(raw.pointA),
474            point_b: Vec2::from_raw(raw.pointB),
475            normal: Vec2::from_raw(raw.normal),
476            distance: raw.distance,
477            iterations: raw.iterations,
478            simplex_count: raw.simplexCount,
479        }
480    }
481}
482
483/// Input for [`shape_cast`].
484#[doc(alias = "shape_cast_pair_input")]
485#[derive(Copy, Clone, Debug)]
486pub struct ShapeCastPairInput {
487    pub proxy_a: ShapeProxy,
488    pub proxy_b: ShapeProxy,
489    pub transform_a: Transform,
490    pub transform_b: Transform,
491    pub translation_b: Vec2,
492    pub max_fraction: f32,
493    pub can_encroach: bool,
494}
495
496impl ShapeCastPairInput {
497    /// Build a shape cast where shape B moves by `translation_b`.
498    #[inline]
499    pub fn new<V: Into<Vec2>>(
500        proxy_a: ShapeProxy,
501        proxy_b: ShapeProxy,
502        transform_a: Transform,
503        transform_b: Transform,
504        translation_b: V,
505    ) -> Self {
506        Self {
507            proxy_a,
508            proxy_b,
509            transform_a,
510            transform_b,
511            translation_b: translation_b.into(),
512            max_fraction: 1.0,
513            can_encroach: false,
514        }
515    }
516
517    /// Limit the portion of `translation_b` considered by the cast.
518    #[inline]
519    pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
520        self.max_fraction = max_fraction;
521        self
522    }
523
524    /// Allow shapes with radius to encroach slightly when initially touching.
525    #[inline]
526    pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
527        self.can_encroach = can_encroach;
528        self
529    }
530
531    /// Validate this input before crossing the Box2D FFI boundary.
532    pub fn validate(&self) -> ApiResult<()> {
533        self.proxy_a.validate()?;
534        self.proxy_b.validate()?;
535        check_collision_transform_valid(self.transform_a)?;
536        check_collision_transform_valid(self.transform_b)?;
537        check_collision_vec2_valid(self.translation_b)?;
538        check_collision_unit_interval_scalar(self.max_fraction)?;
539        Ok(())
540    }
541
542    #[inline]
543    pub fn into_raw(self) -> ffi::b2ShapeCastPairInput {
544        ffi::b2ShapeCastPairInput {
545            proxyA: self.proxy_a.raw(),
546            proxyB: self.proxy_b.raw(),
547            transformA: self.transform_a.into_raw(),
548            transformB: self.transform_b.into_raw(),
549            translationB: self.translation_b.into_raw(),
550            maxFraction: self.max_fraction,
551            canEncroach: self.can_encroach,
552        }
553    }
554}
555
556/// Sweep input used by continuous collision algorithms.
557#[doc(alias = "sweep")]
558#[derive(Copy, Clone, Debug)]
559pub struct Sweep {
560    pub local_center: Vec2,
561    pub c1: Vec2,
562    pub c2: Vec2,
563    pub q1: Rot,
564    pub q2: Rot,
565}
566
567impl Sweep {
568    #[inline]
569    pub fn new<LC: Into<Vec2>, C1: Into<Vec2>, C2: Into<Vec2>>(
570        local_center: LC,
571        c1: C1,
572        c2: C2,
573        q1: Rot,
574        q2: Rot,
575    ) -> Self {
576        Self {
577            local_center: local_center.into(),
578            c1: c1.into(),
579            c2: c2.into(),
580            q1,
581            q2,
582        }
583    }
584
585    #[inline]
586    pub fn from_raw(raw: ffi::b2Sweep) -> Self {
587        Self {
588            local_center: Vec2::from_raw(raw.localCenter),
589            c1: Vec2::from_raw(raw.c1),
590            c2: Vec2::from_raw(raw.c2),
591            q1: Rot::from_raw(raw.q1),
592            q2: Rot::from_raw(raw.q2),
593        }
594    }
595
596    #[inline]
597    pub fn into_raw(self) -> ffi::b2Sweep {
598        ffi::b2Sweep {
599            localCenter: self.local_center.into_raw(),
600            c1: self.c1.into_raw(),
601            c2: self.c2.into_raw(),
602            q1: self.q1.into_raw(),
603            q2: self.q2.into_raw(),
604        }
605    }
606
607    /// Validate this sweep for Box2D continuous-collision algorithms.
608    pub fn validate(&self) -> ApiResult<()> {
609        check_collision_vec2_valid(self.local_center)?;
610        check_collision_vec2_valid(self.c1)?;
611        check_collision_vec2_valid(self.c2)?;
612        check_collision_rot_valid(self.q1)?;
613        check_collision_rot_valid(self.q2)?;
614        Ok(())
615    }
616
617    /// Evaluate the sweep transform at `time` in the `[0, 1]` interval.
618    #[inline]
619    pub fn transform_at(self, time: f32) -> Transform {
620        let raw = self.into_raw();
621        Transform::from_raw(unsafe { ffi::b2GetSweepTransform(&raw, time) })
622    }
623}
624
625/// Input for [`time_of_impact`].
626#[doc(alias = "toi_input")]
627#[derive(Copy, Clone, Debug)]
628pub struct ToiInput {
629    pub proxy_a: ShapeProxy,
630    pub proxy_b: ShapeProxy,
631    pub sweep_a: Sweep,
632    pub sweep_b: Sweep,
633    pub max_fraction: f32,
634}
635
636impl ToiInput {
637    /// Build TOI input with `max_fraction = 1.0`.
638    #[inline]
639    pub fn new(proxy_a: ShapeProxy, proxy_b: ShapeProxy, sweep_a: Sweep, sweep_b: Sweep) -> Self {
640        Self {
641            proxy_a,
642            proxy_b,
643            sweep_a,
644            sweep_b,
645            max_fraction: 1.0,
646        }
647    }
648
649    /// Limit the sweep interval to `[0, max_fraction]`.
650    #[inline]
651    pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
652        self.max_fraction = max_fraction;
653        self
654    }
655
656    /// Validate this input before crossing the Box2D FFI boundary.
657    pub fn validate(&self) -> ApiResult<()> {
658        self.proxy_a.validate()?;
659        self.proxy_b.validate()?;
660        self.sweep_a.validate()?;
661        self.sweep_b.validate()?;
662        check_collision_unit_interval_scalar(self.max_fraction)?;
663        Ok(())
664    }
665
666    #[inline]
667    pub fn into_raw(self) -> ffi::b2TOIInput {
668        ffi::b2TOIInput {
669            proxyA: self.proxy_a.raw(),
670            proxyB: self.proxy_b.raw(),
671            sweepA: self.sweep_a.into_raw(),
672            sweepB: self.sweep_b.into_raw(),
673            maxFraction: self.max_fraction,
674        }
675    }
676}
677
678/// Result state from [`time_of_impact`].
679#[doc(alias = "toi_state")]
680#[repr(u32)]
681#[derive(Copy, Clone, Debug, Eq, PartialEq)]
682pub enum ToiState {
683    Unknown = ffi::b2TOIState_b2_toiStateUnknown,
684    Failed = ffi::b2TOIState_b2_toiStateFailed,
685    Overlapped = ffi::b2TOIState_b2_toiStateOverlapped,
686    Hit = ffi::b2TOIState_b2_toiStateHit,
687    Separated = ffi::b2TOIState_b2_toiStateSeparated,
688}
689
690impl ToiState {
691    #[inline]
692    pub const fn from_raw(raw: ffi::b2TOIState) -> Self {
693        match raw {
694            ffi::b2TOIState_b2_toiStateFailed => Self::Failed,
695            ffi::b2TOIState_b2_toiStateOverlapped => Self::Overlapped,
696            ffi::b2TOIState_b2_toiStateHit => Self::Hit,
697            ffi::b2TOIState_b2_toiStateSeparated => Self::Separated,
698            _ => Self::Unknown,
699        }
700    }
701}
702
703/// Output from [`time_of_impact`].
704#[doc(alias = "toi_output")]
705#[derive(Copy, Clone, Debug)]
706pub struct ToiOutput {
707    pub state: ToiState,
708    pub point: Vec2,
709    pub normal: Vec2,
710    pub fraction: f32,
711}
712
713impl ToiOutput {
714    #[inline]
715    pub fn from_raw(raw: ffi::b2TOIOutput) -> Self {
716        Self {
717            state: ToiState::from_raw(raw.state),
718            point: Vec2::from_raw(raw.point),
719            normal: Vec2::from_raw(raw.normal),
720            fraction: raw.fraction,
721        }
722    }
723}
724
725/// Compute the closest points between two line segments.
726pub fn segment_distance<P1, Q1, P2, Q2>(p1: P1, q1: Q1, p2: P2, q2: Q2) -> SegmentDistanceResult
727where
728    P1: Into<Vec2>,
729    Q1: Into<Vec2>,
730    P2: Into<Vec2>,
731    Q2: Into<Vec2>,
732{
733    let p1 = p1.into();
734    let q1 = q1.into();
735    let p2 = p2.into();
736    let q2 = q2.into();
737    assert_collision_input_valid(
738        "segment_distance p1",
739        check_collision_vec2_valid(p1).is_ok(),
740    );
741    assert_collision_input_valid(
742        "segment_distance q1",
743        check_collision_vec2_valid(q1).is_ok(),
744    );
745    assert_collision_input_valid(
746        "segment_distance p2",
747        check_collision_vec2_valid(p2).is_ok(),
748    );
749    assert_collision_input_valid(
750        "segment_distance q2",
751        check_collision_vec2_valid(q2).is_ok(),
752    );
753    SegmentDistanceResult::from_raw(unsafe {
754        ffi::b2SegmentDistance(p1.into_raw(), q1.into_raw(), p2.into_raw(), q2.into_raw())
755    })
756}
757
758/// Compute the closest points between two line segments with recoverable validation.
759pub fn try_segment_distance<P1, Q1, P2, Q2>(
760    p1: P1,
761    q1: Q1,
762    p2: P2,
763    q2: Q2,
764) -> ApiResult<SegmentDistanceResult>
765where
766    P1: Into<Vec2>,
767    Q1: Into<Vec2>,
768    P2: Into<Vec2>,
769    Q2: Into<Vec2>,
770{
771    let p1 = p1.into();
772    let q1 = q1.into();
773    let p2 = p2.into();
774    let q2 = q2.into();
775    check_collision_vec2_valid(p1)?;
776    check_collision_vec2_valid(q1)?;
777    check_collision_vec2_valid(p2)?;
778    check_collision_vec2_valid(q2)?;
779    Ok(SegmentDistanceResult::from_raw(unsafe {
780        ffi::b2SegmentDistance(p1.into_raw(), q1.into_raw(), p2.into_raw(), q2.into_raw())
781    }))
782}
783
784/// Compute the closest distance between two shape proxies.
785pub fn shape_distance(input: DistanceInput, cache: &mut SimplexCache) -> DistanceOutput {
786    assert_collision_input_valid("shape_distance input", input.validate().is_ok());
787    let raw_input = input.into_raw();
788    DistanceOutput::from_raw(unsafe {
789        ffi::b2ShapeDistance(&raw_input, cache.raw_mut(), core::ptr::null_mut(), 0)
790    })
791}
792
793/// Compute the closest distance between two shape proxies with recoverable validation.
794pub fn try_shape_distance(
795    input: DistanceInput,
796    cache: &mut SimplexCache,
797) -> ApiResult<DistanceOutput> {
798    input.validate()?;
799    let raw_input = input.into_raw();
800    Ok(DistanceOutput::from_raw(unsafe {
801        ffi::b2ShapeDistance(&raw_input, cache.raw_mut(), core::ptr::null_mut(), 0)
802    }))
803}
804
805/// Cast shape B against shape A.
806pub fn shape_cast(input: ShapeCastPairInput) -> CastOutput {
807    assert_collision_input_valid("shape_cast input", input.validate().is_ok());
808    let raw_input = input.into_raw();
809    CastOutput::from_raw(unsafe { ffi::b2ShapeCast(&raw_input) })
810}
811
812/// Cast shape B against shape A with recoverable validation.
813pub fn try_shape_cast(input: ShapeCastPairInput) -> ApiResult<CastOutput> {
814    input.validate()?;
815    let raw_input = input.into_raw();
816    Ok(CastOutput::from_raw(unsafe {
817        ffi::b2ShapeCast(&raw_input)
818    }))
819}
820
821/// Compute the time of impact between two moving shape proxies.
822pub fn time_of_impact(input: ToiInput) -> ToiOutput {
823    assert_collision_input_valid("time_of_impact input", input.validate().is_ok());
824    let raw_input = input.into_raw();
825    ToiOutput::from_raw(unsafe { ffi::b2TimeOfImpact(&raw_input) })
826}
827
828/// Compute the time of impact between two moving shape proxies with recoverable validation.
829pub fn try_time_of_impact(input: ToiInput) -> ApiResult<ToiOutput> {
830    input.validate()?;
831    let raw_input = input.into_raw();
832    Ok(ToiOutput::from_raw(unsafe {
833        ffi::b2TimeOfImpact(&raw_input)
834    }))
835}
836
837/// Compute the contact manifold between two circles.
838#[doc(alias = "b2CollideCircles")]
839pub fn collide_circles(
840    circle_a: Circle,
841    transform_a: Transform,
842    circle_b: Circle,
843    transform_b: Transform,
844) -> Manifold {
845    assert_collision_input_valid("circle_a", circle_a.validate().is_ok());
846    assert_collision_input_valid(
847        "transform_a",
848        check_collision_transform_valid(transform_a).is_ok(),
849    );
850    assert_collision_input_valid("circle_b", circle_b.validate().is_ok());
851    assert_collision_input_valid(
852        "transform_b",
853        check_collision_transform_valid(transform_b).is_ok(),
854    );
855    let raw_a = circle_a.into_raw();
856    let raw_b = circle_b.into_raw();
857    Manifold::from_raw(unsafe {
858        ffi::b2CollideCircles(
859            &raw_a,
860            transform_a.into_raw(),
861            &raw_b,
862            transform_b.into_raw(),
863        )
864    })
865}
866
867/// Compute the contact manifold between two circles with recoverable validation.
868pub fn try_collide_circles(
869    circle_a: Circle,
870    transform_a: Transform,
871    circle_b: Circle,
872    transform_b: Transform,
873) -> ApiResult<Manifold> {
874    circle_a.validate()?;
875    check_collision_transform_valid(transform_a)?;
876    circle_b.validate()?;
877    check_collision_transform_valid(transform_b)?;
878    let raw_a = circle_a.into_raw();
879    let raw_b = circle_b.into_raw();
880    Ok(Manifold::from_raw(unsafe {
881        ffi::b2CollideCircles(
882            &raw_a,
883            transform_a.into_raw(),
884            &raw_b,
885            transform_b.into_raw(),
886        )
887    }))
888}
889
890/// Compute the contact manifold between a capsule and a circle.
891#[doc(alias = "b2CollideCapsuleAndCircle")]
892pub fn collide_capsule_and_circle(
893    capsule_a: Capsule,
894    transform_a: Transform,
895    circle_b: Circle,
896    transform_b: Transform,
897) -> Manifold {
898    assert_collision_input_valid("capsule_a", capsule_a.validate().is_ok());
899    assert_collision_input_valid(
900        "transform_a",
901        check_collision_transform_valid(transform_a).is_ok(),
902    );
903    assert_collision_input_valid("circle_b", circle_b.validate().is_ok());
904    assert_collision_input_valid(
905        "transform_b",
906        check_collision_transform_valid(transform_b).is_ok(),
907    );
908    let raw_a = capsule_a.into_raw();
909    let raw_b = circle_b.into_raw();
910    Manifold::from_raw(unsafe {
911        ffi::b2CollideCapsuleAndCircle(
912            &raw_a,
913            transform_a.into_raw(),
914            &raw_b,
915            transform_b.into_raw(),
916        )
917    })
918}
919
920/// Compute the contact manifold between a capsule and a circle with recoverable validation.
921pub fn try_collide_capsule_and_circle(
922    capsule_a: Capsule,
923    transform_a: Transform,
924    circle_b: Circle,
925    transform_b: Transform,
926) -> ApiResult<Manifold> {
927    capsule_a.validate()?;
928    check_collision_transform_valid(transform_a)?;
929    circle_b.validate()?;
930    check_collision_transform_valid(transform_b)?;
931    let raw_a = capsule_a.into_raw();
932    let raw_b = circle_b.into_raw();
933    Ok(Manifold::from_raw(unsafe {
934        ffi::b2CollideCapsuleAndCircle(
935            &raw_a,
936            transform_a.into_raw(),
937            &raw_b,
938            transform_b.into_raw(),
939        )
940    }))
941}
942
943/// Compute the contact manifold between a segment and a circle.
944#[doc(alias = "b2CollideSegmentAndCircle")]
945pub fn collide_segment_and_circle(
946    segment_a: Segment,
947    transform_a: Transform,
948    circle_b: Circle,
949    transform_b: Transform,
950) -> Manifold {
951    assert_collision_input_valid("segment_a", segment_a.validate().is_ok());
952    assert_collision_input_valid(
953        "transform_a",
954        check_collision_transform_valid(transform_a).is_ok(),
955    );
956    assert_collision_input_valid("circle_b", circle_b.validate().is_ok());
957    assert_collision_input_valid(
958        "transform_b",
959        check_collision_transform_valid(transform_b).is_ok(),
960    );
961    let raw_a = segment_a.into_raw();
962    let raw_b = circle_b.into_raw();
963    Manifold::from_raw(unsafe {
964        ffi::b2CollideSegmentAndCircle(
965            &raw_a,
966            transform_a.into_raw(),
967            &raw_b,
968            transform_b.into_raw(),
969        )
970    })
971}
972
973/// Compute the contact manifold between a segment and a circle with recoverable validation.
974pub fn try_collide_segment_and_circle(
975    segment_a: Segment,
976    transform_a: Transform,
977    circle_b: Circle,
978    transform_b: Transform,
979) -> ApiResult<Manifold> {
980    segment_a.validate()?;
981    check_collision_transform_valid(transform_a)?;
982    circle_b.validate()?;
983    check_collision_transform_valid(transform_b)?;
984    let raw_a = segment_a.into_raw();
985    let raw_b = circle_b.into_raw();
986    Ok(Manifold::from_raw(unsafe {
987        ffi::b2CollideSegmentAndCircle(
988            &raw_a,
989            transform_a.into_raw(),
990            &raw_b,
991            transform_b.into_raw(),
992        )
993    }))
994}
995
996/// Compute the contact manifold between a polygon and a circle.
997#[doc(alias = "b2CollidePolygonAndCircle")]
998pub fn collide_polygon_and_circle(
999    polygon_a: Polygon,
1000    transform_a: Transform,
1001    circle_b: Circle,
1002    transform_b: Transform,
1003) -> Manifold {
1004    assert_collision_input_valid("polygon_a", polygon_a.validate().is_ok());
1005    assert_collision_input_valid(
1006        "transform_a",
1007        check_collision_transform_valid(transform_a).is_ok(),
1008    );
1009    assert_collision_input_valid("circle_b", circle_b.validate().is_ok());
1010    assert_collision_input_valid(
1011        "transform_b",
1012        check_collision_transform_valid(transform_b).is_ok(),
1013    );
1014    let raw_a = polygon_a.into_raw();
1015    let raw_b = circle_b.into_raw();
1016    Manifold::from_raw(unsafe {
1017        ffi::b2CollidePolygonAndCircle(
1018            &raw_a,
1019            transform_a.into_raw(),
1020            &raw_b,
1021            transform_b.into_raw(),
1022        )
1023    })
1024}
1025
1026/// Compute the contact manifold between a polygon and a circle with recoverable validation.
1027pub fn try_collide_polygon_and_circle(
1028    polygon_a: Polygon,
1029    transform_a: Transform,
1030    circle_b: Circle,
1031    transform_b: Transform,
1032) -> ApiResult<Manifold> {
1033    polygon_a.validate()?;
1034    check_collision_transform_valid(transform_a)?;
1035    circle_b.validate()?;
1036    check_collision_transform_valid(transform_b)?;
1037    let raw_a = polygon_a.into_raw();
1038    let raw_b = circle_b.into_raw();
1039    Ok(Manifold::from_raw(unsafe {
1040        ffi::b2CollidePolygonAndCircle(
1041            &raw_a,
1042            transform_a.into_raw(),
1043            &raw_b,
1044            transform_b.into_raw(),
1045        )
1046    }))
1047}
1048
1049/// Compute the contact manifold between two capsules.
1050#[doc(alias = "b2CollideCapsules")]
1051pub fn collide_capsules(
1052    capsule_a: Capsule,
1053    transform_a: Transform,
1054    capsule_b: Capsule,
1055    transform_b: Transform,
1056) -> Manifold {
1057    assert_collision_input_valid("capsule_a", capsule_a.validate().is_ok());
1058    assert_collision_input_valid(
1059        "transform_a",
1060        check_collision_transform_valid(transform_a).is_ok(),
1061    );
1062    assert_collision_input_valid("capsule_b", capsule_b.validate().is_ok());
1063    assert_collision_input_valid(
1064        "transform_b",
1065        check_collision_transform_valid(transform_b).is_ok(),
1066    );
1067    let raw_a = capsule_a.into_raw();
1068    let raw_b = capsule_b.into_raw();
1069    Manifold::from_raw(unsafe {
1070        ffi::b2CollideCapsules(
1071            &raw_a,
1072            transform_a.into_raw(),
1073            &raw_b,
1074            transform_b.into_raw(),
1075        )
1076    })
1077}
1078
1079/// Compute the contact manifold between two capsules with recoverable validation.
1080pub fn try_collide_capsules(
1081    capsule_a: Capsule,
1082    transform_a: Transform,
1083    capsule_b: Capsule,
1084    transform_b: Transform,
1085) -> ApiResult<Manifold> {
1086    capsule_a.validate()?;
1087    check_collision_transform_valid(transform_a)?;
1088    capsule_b.validate()?;
1089    check_collision_transform_valid(transform_b)?;
1090    let raw_a = capsule_a.into_raw();
1091    let raw_b = capsule_b.into_raw();
1092    Ok(Manifold::from_raw(unsafe {
1093        ffi::b2CollideCapsules(
1094            &raw_a,
1095            transform_a.into_raw(),
1096            &raw_b,
1097            transform_b.into_raw(),
1098        )
1099    }))
1100}
1101
1102/// Compute the contact manifold between a segment and a capsule.
1103#[doc(alias = "b2CollideSegmentAndCapsule")]
1104pub fn collide_segment_and_capsule(
1105    segment_a: Segment,
1106    transform_a: Transform,
1107    capsule_b: Capsule,
1108    transform_b: Transform,
1109) -> Manifold {
1110    assert_collision_input_valid("segment_a", segment_a.validate().is_ok());
1111    assert_collision_input_valid(
1112        "transform_a",
1113        check_collision_transform_valid(transform_a).is_ok(),
1114    );
1115    assert_collision_input_valid("capsule_b", capsule_b.validate().is_ok());
1116    assert_collision_input_valid(
1117        "transform_b",
1118        check_collision_transform_valid(transform_b).is_ok(),
1119    );
1120    let raw_a = segment_a.into_raw();
1121    let raw_b = capsule_b.into_raw();
1122    Manifold::from_raw(unsafe {
1123        ffi::b2CollideSegmentAndCapsule(
1124            &raw_a,
1125            transform_a.into_raw(),
1126            &raw_b,
1127            transform_b.into_raw(),
1128        )
1129    })
1130}
1131
1132/// Compute the contact manifold between a segment and a capsule with recoverable validation.
1133pub fn try_collide_segment_and_capsule(
1134    segment_a: Segment,
1135    transform_a: Transform,
1136    capsule_b: Capsule,
1137    transform_b: Transform,
1138) -> ApiResult<Manifold> {
1139    segment_a.validate()?;
1140    check_collision_transform_valid(transform_a)?;
1141    capsule_b.validate()?;
1142    check_collision_transform_valid(transform_b)?;
1143    let raw_a = segment_a.into_raw();
1144    let raw_b = capsule_b.into_raw();
1145    Ok(Manifold::from_raw(unsafe {
1146        ffi::b2CollideSegmentAndCapsule(
1147            &raw_a,
1148            transform_a.into_raw(),
1149            &raw_b,
1150            transform_b.into_raw(),
1151        )
1152    }))
1153}
1154
1155/// Compute the contact manifold between a polygon and a capsule.
1156#[doc(alias = "b2CollidePolygonAndCapsule")]
1157pub fn collide_polygon_and_capsule(
1158    polygon_a: Polygon,
1159    transform_a: Transform,
1160    capsule_b: Capsule,
1161    transform_b: Transform,
1162) -> Manifold {
1163    assert_collision_input_valid("polygon_a", polygon_a.validate().is_ok());
1164    assert_collision_input_valid(
1165        "transform_a",
1166        check_collision_transform_valid(transform_a).is_ok(),
1167    );
1168    assert_collision_input_valid("capsule_b", capsule_b.validate().is_ok());
1169    assert_collision_input_valid(
1170        "transform_b",
1171        check_collision_transform_valid(transform_b).is_ok(),
1172    );
1173    let raw_a = polygon_a.into_raw();
1174    let raw_b = capsule_b.into_raw();
1175    Manifold::from_raw(unsafe {
1176        ffi::b2CollidePolygonAndCapsule(
1177            &raw_a,
1178            transform_a.into_raw(),
1179            &raw_b,
1180            transform_b.into_raw(),
1181        )
1182    })
1183}
1184
1185/// Compute the contact manifold between a polygon and a capsule with recoverable validation.
1186pub fn try_collide_polygon_and_capsule(
1187    polygon_a: Polygon,
1188    transform_a: Transform,
1189    capsule_b: Capsule,
1190    transform_b: Transform,
1191) -> ApiResult<Manifold> {
1192    polygon_a.validate()?;
1193    check_collision_transform_valid(transform_a)?;
1194    capsule_b.validate()?;
1195    check_collision_transform_valid(transform_b)?;
1196    let raw_a = polygon_a.into_raw();
1197    let raw_b = capsule_b.into_raw();
1198    Ok(Manifold::from_raw(unsafe {
1199        ffi::b2CollidePolygonAndCapsule(
1200            &raw_a,
1201            transform_a.into_raw(),
1202            &raw_b,
1203            transform_b.into_raw(),
1204        )
1205    }))
1206}
1207
1208/// Compute the contact manifold between two polygons.
1209#[doc(alias = "b2CollidePolygons")]
1210pub fn collide_polygons(
1211    polygon_a: Polygon,
1212    transform_a: Transform,
1213    polygon_b: Polygon,
1214    transform_b: Transform,
1215) -> Manifold {
1216    assert_collision_input_valid("polygon_a", polygon_a.validate().is_ok());
1217    assert_collision_input_valid(
1218        "transform_a",
1219        check_collision_transform_valid(transform_a).is_ok(),
1220    );
1221    assert_collision_input_valid("polygon_b", polygon_b.validate().is_ok());
1222    assert_collision_input_valid(
1223        "transform_b",
1224        check_collision_transform_valid(transform_b).is_ok(),
1225    );
1226    let raw_a = polygon_a.into_raw();
1227    let raw_b = polygon_b.into_raw();
1228    Manifold::from_raw(unsafe {
1229        ffi::b2CollidePolygons(
1230            &raw_a,
1231            transform_a.into_raw(),
1232            &raw_b,
1233            transform_b.into_raw(),
1234        )
1235    })
1236}
1237
1238/// Compute the contact manifold between two polygons with recoverable validation.
1239pub fn try_collide_polygons(
1240    polygon_a: Polygon,
1241    transform_a: Transform,
1242    polygon_b: Polygon,
1243    transform_b: Transform,
1244) -> ApiResult<Manifold> {
1245    polygon_a.validate()?;
1246    check_collision_transform_valid(transform_a)?;
1247    polygon_b.validate()?;
1248    check_collision_transform_valid(transform_b)?;
1249    let raw_a = polygon_a.into_raw();
1250    let raw_b = polygon_b.into_raw();
1251    Ok(Manifold::from_raw(unsafe {
1252        ffi::b2CollidePolygons(
1253            &raw_a,
1254            transform_a.into_raw(),
1255            &raw_b,
1256            transform_b.into_raw(),
1257        )
1258    }))
1259}
1260
1261/// Compute the contact manifold between a segment and a polygon.
1262#[doc(alias = "b2CollideSegmentAndPolygon")]
1263pub fn collide_segment_and_polygon(
1264    segment_a: Segment,
1265    transform_a: Transform,
1266    polygon_b: Polygon,
1267    transform_b: Transform,
1268) -> Manifold {
1269    assert_collision_input_valid("segment_a", segment_a.validate().is_ok());
1270    assert_collision_input_valid(
1271        "transform_a",
1272        check_collision_transform_valid(transform_a).is_ok(),
1273    );
1274    assert_collision_input_valid("polygon_b", polygon_b.validate().is_ok());
1275    assert_collision_input_valid(
1276        "transform_b",
1277        check_collision_transform_valid(transform_b).is_ok(),
1278    );
1279    let raw_a = segment_a.into_raw();
1280    let raw_b = polygon_b.into_raw();
1281    Manifold::from_raw(unsafe {
1282        ffi::b2CollideSegmentAndPolygon(
1283            &raw_a,
1284            transform_a.into_raw(),
1285            &raw_b,
1286            transform_b.into_raw(),
1287        )
1288    })
1289}
1290
1291/// Compute the contact manifold between a segment and a polygon with recoverable validation.
1292pub fn try_collide_segment_and_polygon(
1293    segment_a: Segment,
1294    transform_a: Transform,
1295    polygon_b: Polygon,
1296    transform_b: Transform,
1297) -> ApiResult<Manifold> {
1298    segment_a.validate()?;
1299    check_collision_transform_valid(transform_a)?;
1300    polygon_b.validate()?;
1301    check_collision_transform_valid(transform_b)?;
1302    let raw_a = segment_a.into_raw();
1303    let raw_b = polygon_b.into_raw();
1304    Ok(Manifold::from_raw(unsafe {
1305        ffi::b2CollideSegmentAndPolygon(
1306            &raw_a,
1307            transform_a.into_raw(),
1308            &raw_b,
1309            transform_b.into_raw(),
1310        )
1311    }))
1312}
1313
1314/// Compute the contact manifold between a chain segment and a circle.
1315#[doc(alias = "b2CollideChainSegmentAndCircle")]
1316pub fn collide_chain_segment_and_circle(
1317    segment_a: ChainSegment,
1318    transform_a: Transform,
1319    circle_b: Circle,
1320    transform_b: Transform,
1321) -> Manifold {
1322    assert_collision_input_valid("segment_a", segment_a.validate().is_ok());
1323    assert_collision_input_valid(
1324        "transform_a",
1325        check_collision_transform_valid(transform_a).is_ok(),
1326    );
1327    assert_collision_input_valid("circle_b", circle_b.validate().is_ok());
1328    assert_collision_input_valid(
1329        "transform_b",
1330        check_collision_transform_valid(transform_b).is_ok(),
1331    );
1332    let raw_a = segment_a.into_raw();
1333    let raw_b = circle_b.into_raw();
1334    Manifold::from_raw(unsafe {
1335        ffi::b2CollideChainSegmentAndCircle(
1336            &raw_a,
1337            transform_a.into_raw(),
1338            &raw_b,
1339            transform_b.into_raw(),
1340        )
1341    })
1342}
1343
1344/// Compute the contact manifold between a chain segment and a circle with recoverable validation.
1345pub fn try_collide_chain_segment_and_circle(
1346    segment_a: ChainSegment,
1347    transform_a: Transform,
1348    circle_b: Circle,
1349    transform_b: Transform,
1350) -> ApiResult<Manifold> {
1351    segment_a.validate()?;
1352    check_collision_transform_valid(transform_a)?;
1353    circle_b.validate()?;
1354    check_collision_transform_valid(transform_b)?;
1355    let raw_a = segment_a.into_raw();
1356    let raw_b = circle_b.into_raw();
1357    Ok(Manifold::from_raw(unsafe {
1358        ffi::b2CollideChainSegmentAndCircle(
1359            &raw_a,
1360            transform_a.into_raw(),
1361            &raw_b,
1362            transform_b.into_raw(),
1363        )
1364    }))
1365}
1366
1367/// Compute the contact manifold between a chain segment and a capsule.
1368///
1369/// Provide `cache` when repeatedly colliding against nearby rounded shapes to
1370/// warm-start the internal edge solver.
1371#[doc(alias = "b2CollideChainSegmentAndCapsule")]
1372pub fn collide_chain_segment_and_capsule(
1373    segment_a: ChainSegment,
1374    transform_a: Transform,
1375    capsule_b: Capsule,
1376    transform_b: Transform,
1377    cache: Option<&mut SimplexCache>,
1378) -> Manifold {
1379    assert_collision_input_valid("segment_a", segment_a.validate().is_ok());
1380    assert_collision_input_valid(
1381        "transform_a",
1382        check_collision_transform_valid(transform_a).is_ok(),
1383    );
1384    assert_collision_input_valid("capsule_b", capsule_b.validate().is_ok());
1385    assert_collision_input_valid(
1386        "transform_b",
1387        check_collision_transform_valid(transform_b).is_ok(),
1388    );
1389    let raw_a = segment_a.into_raw();
1390    let raw_b = capsule_b.into_raw();
1391    let cache_ptr = match cache {
1392        Some(cache) => cache.raw_mut(),
1393        None => core::ptr::null_mut(),
1394    };
1395    Manifold::from_raw(unsafe {
1396        ffi::b2CollideChainSegmentAndCapsule(
1397            &raw_a,
1398            transform_a.into_raw(),
1399            &raw_b,
1400            transform_b.into_raw(),
1401            cache_ptr,
1402        )
1403    })
1404}
1405
1406/// Compute the contact manifold between a chain segment and a capsule with recoverable validation.
1407pub fn try_collide_chain_segment_and_capsule(
1408    segment_a: ChainSegment,
1409    transform_a: Transform,
1410    capsule_b: Capsule,
1411    transform_b: Transform,
1412    cache: Option<&mut SimplexCache>,
1413) -> ApiResult<Manifold> {
1414    segment_a.validate()?;
1415    check_collision_transform_valid(transform_a)?;
1416    capsule_b.validate()?;
1417    check_collision_transform_valid(transform_b)?;
1418    let raw_a = segment_a.into_raw();
1419    let raw_b = capsule_b.into_raw();
1420    let cache_ptr = match cache {
1421        Some(cache) => cache.raw_mut(),
1422        None => core::ptr::null_mut(),
1423    };
1424    Ok(Manifold::from_raw(unsafe {
1425        ffi::b2CollideChainSegmentAndCapsule(
1426            &raw_a,
1427            transform_a.into_raw(),
1428            &raw_b,
1429            transform_b.into_raw(),
1430            cache_ptr,
1431        )
1432    }))
1433}
1434
1435/// Compute the contact manifold between a chain segment and a polygon.
1436///
1437/// Provide `cache` when repeatedly colliding against nearby rounded polygons to
1438/// warm-start the internal edge solver.
1439#[doc(alias = "b2CollideChainSegmentAndPolygon")]
1440pub fn collide_chain_segment_and_polygon(
1441    segment_a: ChainSegment,
1442    transform_a: Transform,
1443    polygon_b: Polygon,
1444    transform_b: Transform,
1445    cache: Option<&mut SimplexCache>,
1446) -> Manifold {
1447    assert_collision_input_valid("segment_a", segment_a.validate().is_ok());
1448    assert_collision_input_valid(
1449        "transform_a",
1450        check_collision_transform_valid(transform_a).is_ok(),
1451    );
1452    assert_collision_input_valid("polygon_b", polygon_b.validate().is_ok());
1453    assert_collision_input_valid(
1454        "transform_b",
1455        check_collision_transform_valid(transform_b).is_ok(),
1456    );
1457    let raw_a = segment_a.into_raw();
1458    let raw_b = polygon_b.into_raw();
1459    let cache_ptr = match cache {
1460        Some(cache) => cache.raw_mut(),
1461        None => core::ptr::null_mut(),
1462    };
1463    Manifold::from_raw(unsafe {
1464        ffi::b2CollideChainSegmentAndPolygon(
1465            &raw_a,
1466            transform_a.into_raw(),
1467            &raw_b,
1468            transform_b.into_raw(),
1469            cache_ptr,
1470        )
1471    })
1472}
1473
1474/// Compute the contact manifold between a chain segment and a polygon with recoverable validation.
1475pub fn try_collide_chain_segment_and_polygon(
1476    segment_a: ChainSegment,
1477    transform_a: Transform,
1478    polygon_b: Polygon,
1479    transform_b: Transform,
1480    cache: Option<&mut SimplexCache>,
1481) -> ApiResult<Manifold> {
1482    segment_a.validate()?;
1483    check_collision_transform_valid(transform_a)?;
1484    polygon_b.validate()?;
1485    check_collision_transform_valid(transform_b)?;
1486    let raw_a = segment_a.into_raw();
1487    let raw_b = polygon_b.into_raw();
1488    let cache_ptr = match cache {
1489        Some(cache) => cache.raw_mut(),
1490        None => core::ptr::null_mut(),
1491    };
1492    Ok(Manifold::from_raw(unsafe {
1493        ffi::b2CollideChainSegmentAndPolygon(
1494            &raw_a,
1495            transform_a.into_raw(),
1496            &raw_b,
1497            transform_b.into_raw(),
1498            cache_ptr,
1499        )
1500    }))
1501}
1502
1503impl Aabb {
1504    /// Check whether this AABB is valid for Box2D queries.
1505    #[inline]
1506    pub fn is_valid(self) -> bool {
1507        unsafe { ffi::b2IsValidAABB(self.into_raw()) }
1508    }
1509
1510    /// Ray cast against this AABB using Box2D-style `origin + translation`.
1511    ///
1512    /// Initial overlap returns a hit with zero fraction, zero normal, and `point = origin`.
1513    pub fn ray_cast<VO: Into<Vec2>, VT: Into<Vec2>>(
1514        self,
1515        origin: VO,
1516        translation: VT,
1517    ) -> CastOutput {
1518        if !self.is_valid() {
1519            return CastOutput::MISS;
1520        }
1521
1522        let origin = origin.into();
1523        let translation = translation.into();
1524        let mut axis_state = RayCastAxisState {
1525            tmin: 0.0,
1526            tmax: 1.0,
1527            normal: Vec2::ZERO,
1528        };
1529
1530        if !ray_cast_axis(
1531            RayCastAxisInput {
1532                origin: origin.x,
1533                translation: translation.x,
1534                lower: self.lower.x,
1535                upper: self.upper.x,
1536                enter_normal: Vec2::new(-1.0, 0.0),
1537                exit_normal: Vec2::new(1.0, 0.0),
1538            },
1539            &mut axis_state,
1540        ) {
1541            return CastOutput::MISS;
1542        }
1543
1544        if !ray_cast_axis(
1545            RayCastAxisInput {
1546                origin: origin.y,
1547                translation: translation.y,
1548                lower: self.lower.y,
1549                upper: self.upper.y,
1550                enter_normal: Vec2::new(0.0, -1.0),
1551                exit_normal: Vec2::new(0.0, 1.0),
1552            },
1553            &mut axis_state,
1554        ) {
1555            return CastOutput::MISS;
1556        }
1557
1558        if !(0.0..=1.0).contains(&axis_state.tmin) {
1559            return CastOutput::MISS;
1560        }
1561
1562        CastOutput {
1563            normal: axis_state.normal,
1564            point: Vec2::new(
1565                origin.x + axis_state.tmin * translation.x,
1566                origin.y + axis_state.tmin * translation.y,
1567            ),
1568            fraction: axis_state.tmin,
1569            iterations: 0,
1570            hit: true,
1571        }
1572    }
1573}