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