1use 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
18pub 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#[doc(alias = "shape_proxy")]
125#[derive(Copy, Clone)]
126pub struct ShapeProxy {
127 raw: ffi::b2ShapeProxy,
128}
129
130impl ShapeProxy {
131 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 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 #[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 #[inline]
177 pub fn count(&self) -> usize {
178 self.raw.count.clamp(0, MAX_SHAPE_PROXY_POINTS as i32) as usize
179 }
180
181 #[inline]
183 pub fn radius(&self) -> f32 {
184 self.raw.radius
185 }
186
187 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#[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 #[inline]
236 pub fn new() -> Self {
237 Self::default()
238 }
239
240 #[inline]
242 pub fn clear(&mut self) {
243 *self = Self::default();
244 }
245
246 #[inline]
248 pub fn count(&self) -> usize {
249 self.raw.count.min(3) as usize
250 }
251
252 #[inline]
254 pub fn index_a(&self) -> &[u8] {
255 &self.raw.indexA[..self.count()]
256 }
257
258 #[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#[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#[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#[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 #[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 #[inline]
367 pub fn with_radii(mut self, use_radii: bool) -> Self {
368 self.use_radii = use_radii;
369 self
370 }
371
372 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#[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#[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 #[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 #[inline]
455 pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
456 self.max_fraction = max_fraction;
457 self
458 }
459
460 #[inline]
462 pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
463 self.can_encroach = can_encroach;
464 self
465 }
466
467 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#[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 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 #[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#[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 #[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 #[inline]
587 pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
588 self.max_fraction = max_fraction;
589 self
590 }
591
592 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#[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#[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
661pub 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
694pub 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
720pub 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
729pub 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
741pub 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
748pub 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
757pub 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
764pub 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#[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
803pub 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#[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
856pub 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#[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
909pub 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#[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
962pub 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#[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
1015pub 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#[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
1068pub 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#[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
1121pub 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#[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
1174pub 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#[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
1227pub 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#[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
1280pub 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#[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
1342pub 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#[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
1410pub 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 #[inline]
1442 pub fn is_valid(self) -> bool {
1443 unsafe { ffi::b2IsValidAABB(self.into_raw()) }
1444 }
1445
1446 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}