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 #[inline]
132 pub(crate) const fn from_raw(raw: ffi::b2ShapeProxy) -> Self {
133 Self { raw }
134 }
135
136 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 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 #[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 #[inline]
182 pub fn count(&self) -> usize {
183 self.raw.count.clamp(0, MAX_SHAPE_PROXY_POINTS as i32) as usize
184 }
185
186 #[inline]
188 pub fn radius(&self) -> f32 {
189 self.raw.radius
190 }
191
192 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#[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 #[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 #[inline]
248 pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
249 self.max_fraction = max_fraction;
250 self
251 }
252
253 #[inline]
255 pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
256 self.can_encroach = can_encroach;
257 self
258 }
259
260 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#[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 #[inline]
300 pub fn new() -> Self {
301 Self::default()
302 }
303
304 #[inline]
306 pub fn clear(&mut self) {
307 *self = Self::default();
308 }
309
310 #[inline]
312 pub fn count(&self) -> usize {
313 self.raw.count.min(3) as usize
314 }
315
316 #[inline]
318 pub fn index_a(&self) -> &[u8] {
319 &self.raw.indexA[..self.count()]
320 }
321
322 #[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#[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#[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#[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 #[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 #[inline]
431 pub fn with_radii(mut self, use_radii: bool) -> Self {
432 self.use_radii = use_radii;
433 self
434 }
435
436 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#[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#[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 #[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 #[inline]
519 pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
520 self.max_fraction = max_fraction;
521 self
522 }
523
524 #[inline]
526 pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
527 self.can_encroach = can_encroach;
528 self
529 }
530
531 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#[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 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 #[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#[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 #[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 #[inline]
651 pub fn with_max_fraction(mut self, max_fraction: f32) -> Self {
652 self.max_fraction = max_fraction;
653 self
654 }
655
656 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#[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#[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
725pub 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
758pub 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
784pub 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
793pub 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
805pub 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
812pub 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
821pub 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
828pub 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#[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
867pub 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#[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
920pub 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#[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
973pub 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#[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
1026pub 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#[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
1079pub 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#[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
1132pub 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#[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
1185pub 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#[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
1238pub 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#[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
1291pub 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#[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
1344pub 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#[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
1406pub 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#[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
1474pub 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 #[inline]
1506 pub fn is_valid(self) -> bool {
1507 unsafe { ffi::b2IsValidAABB(self.into_raw()) }
1508 }
1509
1510 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}