1use crate::{
13 core::{
14 foundation::transient_native_lease,
15 math::{Rot, Transform},
16 },
17 error::{Error, Result},
18 query::Aabb,
19 shapes::{Capsule, ChainSegment, Circle, Polygon, Segment},
20 types::Vec2,
21};
22use boxdd_sys::ffi;
23use core::fmt;
24
25pub const MAX_SHAPE_PROXY_POINTS: usize = ffi::B2_MAX_POLYGON_VERTICES as usize;
27
28pub const MAX_LOCAL_MANIFOLD_POINTS: usize = 2;
30
31const _: () = {
32 assert!(core::mem::size_of::<Vec2>() == core::mem::size_of::<ffi::b2Vec2>());
33 assert!(core::mem::align_of::<Vec2>() == core::mem::align_of::<ffi::b2Vec2>());
34};
35
36#[inline]
37fn check_collision_vec2_valid(
38 operation: &'static str,
39 argument: &'static str,
40 value: Vec2,
41) -> Result<()> {
42 if value.is_valid() {
43 Ok(())
44 } else {
45 Err(Error::invalid_argument(
46 operation,
47 argument,
48 "a finite vector",
49 ))
50 }
51}
52
53#[inline]
54fn check_collision_rot_valid(
55 operation: &'static str,
56 argument: &'static str,
57 value: Rot,
58) -> Result<()> {
59 if value.is_valid() {
60 Ok(())
61 } else {
62 Err(Error::invalid_argument(
63 operation,
64 argument,
65 "a normalized finite rotation",
66 ))
67 }
68}
69
70#[inline]
71fn check_collision_transform_valid(operation: &'static str, value: Transform) -> Result<()> {
72 if value.is_valid() {
73 Ok(())
74 } else {
75 Err(Error::invalid_argument(
76 operation,
77 "transform_b_in_a",
78 "a finite rigid transform",
79 ))
80 }
81}
82
83#[inline]
84fn check_collision_non_negative_finite_scalar(
85 operation: &'static str,
86 argument: &'static str,
87 value: f32,
88) -> Result<()> {
89 if crate::is_valid_float(value) && value >= 0.0 {
90 Ok(())
91 } else {
92 Err(Error::invalid_argument(
93 operation,
94 argument,
95 "a finite value greater than or equal to zero",
96 ))
97 }
98}
99
100#[inline]
101fn check_collision_finite_scalar(
102 operation: &'static str,
103 argument: &'static str,
104 value: f32,
105) -> Result<()> {
106 if crate::is_valid_float(value) {
107 Ok(())
108 } else {
109 Err(Error::invalid_argument(
110 operation,
111 argument,
112 "a finite value",
113 ))
114 }
115}
116
117#[inline]
118fn check_collision_non_negative_int(
119 operation: &'static str,
120 argument: &'static str,
121 value: i32,
122) -> Result<()> {
123 if value >= 0 {
124 Ok(())
125 } else {
126 Err(Error::invalid_argument(
127 operation,
128 argument,
129 "a non-negative native int",
130 ))
131 }
132}
133
134#[inline]
135fn collision_unit_vector_is_valid(value: Vec2) -> bool {
136 value.is_valid() && (1.0 - (value.x * value.x + value.y * value.y)).abs() < 100.0 * f32::EPSILON
137}
138
139#[inline]
140fn check_collision_unit_interval_scalar(
141 operation: &'static str,
142 argument: &'static str,
143 value: f32,
144) -> Result<()> {
145 if crate::is_valid_float(value) && (0.0..=1.0).contains(&value) {
146 Ok(())
147 } else {
148 Err(Error::invalid_argument(
149 operation,
150 argument,
151 "a finite value in 0.0..=1.0",
152 ))
153 }
154}
155
156struct RayCastAxisInput {
157 origin: f32,
158 translation: f32,
159 lower: f32,
160 upper: f32,
161 enter_normal: Vec2,
162 exit_normal: Vec2,
163}
164
165struct RayCastAxisState {
166 tmin: f32,
167 tmax: f32,
168 normal: Vec2,
169}
170
171#[inline]
172fn ray_cast_axis(input: RayCastAxisInput, state: &mut RayCastAxisState) -> bool {
173 if input.translation.abs() < f32::EPSILON {
174 return input.lower <= input.origin && input.origin <= input.upper;
175 }
176
177 let inv_translation = 1.0 / input.translation;
178 let mut t1 = (input.lower - input.origin) * inv_translation;
179 let mut t2 = (input.upper - input.origin) * inv_translation;
180 let mut n1 = input.enter_normal;
181 let mut n2 = input.exit_normal;
182
183 if t1 > t2 {
184 core::mem::swap(&mut t1, &mut t2);
185 core::mem::swap(&mut n1, &mut n2);
186 }
187
188 if t1 > state.tmin {
189 state.tmin = t1;
190 state.normal = n1;
191 }
192
193 if t2 < state.tmax {
194 state.tmax = t2;
195 }
196
197 state.tmin <= state.tmax
198}
199
200#[doc(alias = "shape_proxy")]
205#[derive(Copy, Clone)]
206pub struct ShapeProxy {
207 raw: ffi::b2ShapeProxy,
208}
209
210impl ShapeProxy {
211 pub fn new<I, P>(points: I, radius: f32) -> Result<Self>
213 where
214 I: IntoIterator<Item = P>,
215 P: Into<Vec2>,
216 {
217 let (raw_points, count) = collect_shape_proxy_points("ShapeProxy::new", points, radius)?;
218 Ok(Self {
219 raw: ffi::b2ShapeProxy {
220 points: raw_points,
221 count,
222 radius,
223 },
224 })
225 }
226
227 #[doc(alias = "b2MakeOffsetProxy")]
229 pub fn offset_from_points<I, P>(points: I, radius: f32, transform: Transform) -> Result<Self>
230 where
231 I: IntoIterator<Item = P>,
232 P: Into<Vec2>,
233 {
234 Self::offset_from_points_for(
235 "ShapeProxy::offset_from_points",
236 "points/transform",
237 points,
238 radius,
239 transform,
240 )
241 }
242
243 pub(crate) fn offset_from_points_for<I, P>(
244 operation: &'static str,
245 transformed_argument: &'static str,
246 points: I,
247 radius: f32,
248 transform: Transform,
249 ) -> Result<Self>
250 where
251 I: IntoIterator<Item = P>,
252 P: Into<Vec2>,
253 {
254 let (mut raw_points, count) = collect_shape_proxy_points(operation, points, radius)?;
255 if !transform.is_valid() {
256 return Err(Error::invalid_argument(
257 operation,
258 "transform",
259 "a finite rigid transform",
260 ));
261 }
262 for point in raw_points.iter_mut().take(count as usize) {
263 let transformed = transform.transform_point(Vec2::from_raw(*point));
264 if !transformed.is_valid() {
265 return Err(Error::invalid_argument(
266 operation,
267 transformed_argument,
268 "a transform whose proxy points remain finite",
269 ));
270 }
271 *point = transformed.into_raw();
272 }
273 Ok(Self {
274 raw: ffi::b2ShapeProxy {
275 points: raw_points,
276 count,
277 radius,
278 },
279 })
280 }
281
282 #[inline]
284 pub fn points(&self) -> &[Vec2] {
285 let count = self.count();
286 unsafe { core::slice::from_raw_parts(self.raw.points.as_ptr().cast::<Vec2>(), count) }
287 }
288
289 #[inline]
291 pub fn count(&self) -> usize {
292 self.raw.count.clamp(0, MAX_SHAPE_PROXY_POINTS as i32) as usize
293 }
294
295 #[inline]
297 pub fn radius(&self) -> f32 {
298 self.raw.radius
299 }
300
301 pub fn validate(&self) -> Result<()> {
303 if !(1..=MAX_SHAPE_PROXY_POINTS as i32).contains(&self.raw.count) {
304 return Err(Error::invalid_argument(
305 "ShapeProxy::validate",
306 "points",
307 "between 1 and Box2D's maximum shape-proxy point count",
308 ));
309 }
310 check_collision_non_negative_finite_scalar(
311 "ShapeProxy::validate",
312 "radius",
313 self.raw.radius,
314 )?;
315 for point in self.points().iter().copied() {
316 check_collision_vec2_valid("ShapeProxy::validate", "points", point)?;
317 }
318 Ok(())
319 }
320
321 #[inline]
322 pub(crate) fn into_raw(self) -> ffi::b2ShapeProxy {
323 self.raw
324 }
325
326 #[inline]
327 fn raw(self) -> ffi::b2ShapeProxy {
328 self.into_raw()
329 }
330}
331
332fn collect_shape_proxy_points<I, P>(
333 operation: &'static str,
334 points: I,
335 radius: f32,
336) -> Result<([ffi::b2Vec2; MAX_SHAPE_PROXY_POINTS], i32)>
337where
338 I: IntoIterator<Item = P>,
339 P: Into<Vec2>,
340{
341 check_collision_non_negative_finite_scalar(operation, "radius", radius)?;
342 let mut raw_points = [ffi::b2Vec2 { x: 0.0, y: 0.0 }; MAX_SHAPE_PROXY_POINTS];
343 let mut count = 0usize;
344
345 for point in points {
346 if count == MAX_SHAPE_PROXY_POINTS {
347 return Err(Error::invalid_argument(
348 operation,
349 "points",
350 "no more than Box2D's maximum shape-proxy point count",
351 ));
352 }
353 let point = point.into();
354 check_collision_vec2_valid(operation, "points", point)?;
355 raw_points[count] = point.into_raw();
356 count += 1;
357 }
358
359 if count == 0 {
360 return Err(Error::invalid_argument(
361 operation,
362 "points",
363 "at least one point",
364 ));
365 }
366
367 Ok((raw_points, count as i32))
368}
369
370impl fmt::Debug for ShapeProxy {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 f.debug_struct("ShapeProxy")
373 .field("points", &self.points())
374 .field("radius", &self.radius())
375 .finish()
376 }
377}
378
379#[doc(alias = "shape_cast_input")]
381#[derive(Copy, Clone, Debug)]
382pub struct ShapeCastInput {
383 pub(crate) proxy: ShapeProxy,
384 pub(crate) translation: Vec2,
385 pub(crate) max_fraction: f32,
386 pub(crate) can_encroach: bool,
387}
388
389impl ShapeCastInput {
390 #[inline]
392 pub fn new<T: Into<Vec2>>(proxy: ShapeProxy, translation: T) -> Result<Self> {
393 let input = Self {
394 proxy,
395 translation: translation.into(),
396 max_fraction: 1.0,
397 can_encroach: false,
398 };
399 input.validate()?;
400 Ok(input)
401 }
402
403 #[inline]
405 pub fn with_max_fraction(mut self, max_fraction: f32) -> Result<Self> {
406 check_collision_unit_interval_scalar(
407 "ShapeCastInput::with_max_fraction",
408 "max_fraction",
409 max_fraction,
410 )?;
411 self.max_fraction = max_fraction;
412 Ok(self)
413 }
414
415 #[inline]
417 pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
418 self.can_encroach = can_encroach;
419 self
420 }
421
422 #[inline]
423 pub const fn proxy(self) -> ShapeProxy {
424 self.proxy
425 }
426
427 #[inline]
428 pub const fn translation(self) -> Vec2 {
429 self.translation
430 }
431
432 #[inline]
433 pub const fn max_fraction(self) -> f32 {
434 self.max_fraction
435 }
436
437 #[inline]
438 pub const fn can_encroach(self) -> bool {
439 self.can_encroach
440 }
441
442 pub fn validate(&self) -> Result<()> {
444 self.proxy.validate()?;
445 check_collision_vec2_valid("ShapeCastInput::validate", "translation", self.translation)?;
446 check_collision_unit_interval_scalar(
447 "ShapeCastInput::validate",
448 "max_fraction",
449 self.max_fraction,
450 )
451 }
452
453 #[inline]
454 pub fn into_raw(self) -> ffi::b2ShapeCastInput {
455 ffi::b2ShapeCastInput {
456 proxy: self.proxy.into_raw(),
457 translation: self.translation.into_raw(),
458 maxFraction: self.max_fraction,
459 canEncroach: self.can_encroach,
460 }
461 }
462}
463
464#[doc(alias = "simplex_cache")]
466#[derive(Copy, Clone)]
467pub struct SimplexCache {
468 raw: ffi::b2SimplexCache,
469}
470
471impl Default for SimplexCache {
472 fn default() -> Self {
473 Self {
474 raw: ffi::b2SimplexCache {
475 count: 0,
476 indexA: [0; 3],
477 indexB: [0; 3],
478 },
479 }
480 }
481}
482
483impl SimplexCache {
484 #[inline]
486 pub fn new() -> Self {
487 Self::default()
488 }
489
490 #[inline]
492 pub fn clear(&mut self) {
493 *self = Self::default();
494 }
495
496 #[inline]
498 pub fn count(&self) -> usize {
499 self.raw.count.min(3) as usize
500 }
501
502 #[inline]
504 pub fn index_a(&self) -> &[u8] {
505 &self.raw.indexA[..self.count()]
506 }
507
508 #[inline]
510 pub fn index_b(&self) -> &[u8] {
511 &self.raw.indexB[..self.count()]
512 }
513
514 #[inline]
515 fn raw_mut(&mut self) -> *mut ffi::b2SimplexCache {
516 &mut self.raw
517 }
518
519 fn validate_for(
520 &self,
521 operation: &'static str,
522 proxy_a_count: usize,
523 proxy_b_count: usize,
524 ) -> Result<()> {
525 let count = usize::from(self.raw.count);
526 if count > 3 {
527 return Err(Error::invalid_argument(
528 operation,
529 "cache.count",
530 "a simplex point count in 0..=3",
531 ));
532 }
533 if self.raw.indexA[..count]
534 .iter()
535 .any(|index| usize::from(*index) >= proxy_a_count)
536 {
537 return Err(Error::invalid_argument(
538 operation,
539 "cache.index_a",
540 "indices within shape A's proxy points",
541 ));
542 }
543 if self.raw.indexB[..count]
544 .iter()
545 .any(|index| usize::from(*index) >= proxy_b_count)
546 {
547 return Err(Error::invalid_argument(
548 operation,
549 "cache.index_b",
550 "indices within shape B's proxy points",
551 ));
552 }
553 Ok(())
554 }
555
556 fn validate_native_for(
557 &self,
558 operation: &'static str,
559 proxy_a_count: usize,
560 proxy_b_count: usize,
561 ) -> Result<()> {
562 self.validate_for(operation, proxy_a_count, proxy_b_count)
563 .map_err(|_| Error::InvalidNativeOutput {
564 operation,
565 output: "simplex_cache",
566 constraint: "at most three in-range proxy point indices",
567 })
568 }
569}
570
571impl fmt::Debug for SimplexCache {
572 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573 f.debug_struct("SimplexCache")
574 .field("count", &self.count())
575 .field("index_a", &self.index_a())
576 .field("index_b", &self.index_b())
577 .finish()
578 }
579}
580
581fn commit_native_simplex_cache(
582 cache: Option<&mut SimplexCache>,
583 staged: SimplexCache,
584 operation: &'static str,
585 proxy_a_count: usize,
586 proxy_b_count: usize,
587) -> Result<()> {
588 staged.validate_native_for(operation, proxy_a_count, proxy_b_count)?;
589 if let Some(cache) = cache {
590 *cache = staged;
591 }
592 Ok(())
593}
594
595#[doc(alias = "local_manifold_point")]
600#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
601#[derive(Copy, Clone, Debug, Default, PartialEq)]
602pub struct LocalManifoldPoint {
603 pub point: Vec2,
605 pub separation: f32,
607 pub id: u16,
609}
610
611impl LocalManifoldPoint {
612 #[inline]
613 pub fn from_raw(raw: ffi::b2LocalManifoldPoint) -> Result<Self> {
614 let point = Self::from_raw_unvalidated(raw);
615 point.validate_for("LocalManifoldPoint::from_raw")?;
616 Ok(point)
617 }
618
619 #[inline]
620 const fn from_raw_unvalidated(raw: ffi::b2LocalManifoldPoint) -> Self {
621 Self {
622 point: Vec2::from_raw(raw.point),
623 separation: raw.separation,
624 id: raw.id,
625 }
626 }
627
628 pub fn validate(&self) -> Result<()> {
629 self.validate_for("LocalManifoldPoint::validate")
630 }
631
632 fn validate_for(&self, operation: &'static str) -> Result<()> {
633 check_collision_vec2_valid(operation, "point", self.point)?;
634 check_collision_finite_scalar(operation, "separation", self.separation)
635 }
636
637 #[inline]
638 pub const fn into_raw(self) -> ffi::b2LocalManifoldPoint {
639 ffi::b2LocalManifoldPoint {
640 point: self.point.into_raw(),
641 separation: self.separation,
642 id: self.id,
643 }
644 }
645}
646
647#[doc(alias = "local_manifold")]
655#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
656#[derive(Copy, Clone, Debug, Default, PartialEq)]
657pub struct LocalManifold {
658 pub normal: Vec2,
659 pub contact_points: [LocalManifoldPoint; MAX_LOCAL_MANIFOLD_POINTS],
660 pub point_count: i32,
661}
662
663impl LocalManifold {
664 #[inline]
666 pub fn points(&self) -> &[LocalManifoldPoint] {
667 &self.contact_points[..self.point_count()]
668 }
669
670 #[inline]
672 pub fn point_count(&self) -> usize {
673 self.point_count.clamp(0, MAX_LOCAL_MANIFOLD_POINTS as i32) as usize
674 }
675
676 #[inline]
678 pub fn is_empty(&self) -> bool {
679 self.point_count() == 0
680 }
681
682 #[inline]
683 pub fn from_raw(raw: ffi::b2LocalManifold) -> Result<Self> {
684 let manifold = Self::from_raw_unvalidated(raw);
685 manifold.validate_for("LocalManifold::from_raw")?;
686 Ok(manifold)
687 }
688
689 #[inline]
690 fn from_native(operation: &'static str, raw: ffi::b2LocalManifold) -> Result<Self> {
691 let mut manifold = Self::from_raw_unvalidated(raw);
692 if manifold.point_count > 0 && manifold.normal == Vec2::ZERO {
696 manifold.normal = Vec2::new(1.0, 0.0);
697 }
698 manifold
699 .validate_for(operation)
700 .map_err(|_| Error::InvalidNativeOutput {
701 operation,
702 output: "local_manifold",
703 constraint: "zero to two finite contact points and a unit normal when non-empty",
704 })?;
705 Ok(manifold)
706 }
707
708 #[inline]
709 fn from_raw_unvalidated(raw: ffi::b2LocalManifold) -> Self {
710 Self {
711 normal: Vec2::from_raw(raw.normal),
712 contact_points: raw.points.map(LocalManifoldPoint::from_raw_unvalidated),
713 point_count: raw.pointCount,
714 }
715 }
716
717 pub fn validate(&self) -> Result<()> {
718 self.validate_for("LocalManifold::validate")
719 }
720
721 fn validate_for(&self, operation: &'static str) -> Result<()> {
722 if !(0..=MAX_LOCAL_MANIFOLD_POINTS as i32).contains(&self.point_count) {
723 return Err(Error::invalid_argument(
724 operation,
725 "point_count",
726 "a contact point count in 0..=2",
727 ));
728 }
729 check_collision_vec2_valid(operation, "normal", self.normal)?;
730 if self.point_count > 0 && !collision_unit_vector_is_valid(self.normal) {
731 return Err(Error::invalid_argument(
732 operation,
733 "normal",
734 "a finite unit vector when the manifold is non-empty",
735 ));
736 }
737 for point in self.points() {
738 point.validate_for(operation)?;
739 }
740 Ok(())
741 }
742
743 #[inline]
744 pub fn into_raw(self) -> ffi::b2LocalManifold {
745 ffi::b2LocalManifold {
746 normal: self.normal.into_raw(),
747 points: self.contact_points.map(LocalManifoldPoint::into_raw),
748 pointCount: self.point_count,
749 }
750 }
751}
752
753#[doc(alias = "segment_distance_result")]
755#[derive(Copy, Clone, Debug)]
756pub struct SegmentDistanceResult {
757 pub closest1: Vec2,
758 pub closest2: Vec2,
759 pub fraction1: f32,
760 pub fraction2: f32,
761 pub distance_squared: f32,
762}
763
764impl SegmentDistanceResult {
765 #[inline]
766 pub fn from_raw(raw: ffi::b2SegmentDistanceResult) -> Result<Self> {
767 let result = Self {
768 closest1: Vec2::from_raw(raw.closest1),
769 closest2: Vec2::from_raw(raw.closest2),
770 fraction1: raw.fraction1,
771 fraction2: raw.fraction2,
772 distance_squared: raw.distanceSquared,
773 };
774 result.validate_for("SegmentDistanceResult::from_raw")?;
775 Ok(result)
776 }
777
778 fn from_native(operation: &'static str, raw: ffi::b2SegmentDistanceResult) -> Result<Self> {
779 Self::from_raw(raw).map_err(|_| Error::InvalidNativeOutput {
780 operation,
781 output: "segment_distance",
782 constraint: "finite points, unit-interval fractions, and a non-negative squared distance",
783 })
784 }
785
786 pub fn validate(&self) -> Result<()> {
787 self.validate_for("SegmentDistanceResult::validate")
788 }
789
790 fn validate_for(&self, operation: &'static str) -> Result<()> {
791 check_collision_vec2_valid(operation, "closest1", self.closest1)?;
792 check_collision_vec2_valid(operation, "closest2", self.closest2)?;
793 check_collision_unit_interval_scalar(operation, "fraction1", self.fraction1)?;
794 check_collision_unit_interval_scalar(operation, "fraction2", self.fraction2)?;
795 check_collision_non_negative_finite_scalar(
796 operation,
797 "distance_squared",
798 self.distance_squared,
799 )
800 }
801}
802
803#[doc(alias = "cast_output")]
805#[derive(Copy, Clone, Debug)]
806pub struct CastOutput {
807 pub normal: Vec2,
808 pub point: Vec2,
809 pub fraction: f32,
810 pub iterations: i32,
811 pub hit: bool,
812}
813
814impl CastOutput {
815 pub const MISS: Self = Self {
816 normal: Vec2::ZERO,
817 point: Vec2::ZERO,
818 fraction: 0.0,
819 iterations: 0,
820 hit: false,
821 };
822
823 #[inline]
824 pub fn from_raw(raw: ffi::b2CastOutput) -> Result<Self> {
825 let output = Self {
826 normal: Vec2::from_raw(raw.normal),
827 point: Vec2::from_raw(raw.point),
828 fraction: raw.fraction,
829 iterations: raw.iterations,
830 hit: raw.hit,
831 };
832 output.validate_for("CastOutput::from_raw")?;
833 Ok(output)
834 }
835
836 pub(crate) fn from_native(operation: &'static str, raw: ffi::b2CastOutput) -> Result<Self> {
837 let output = Self {
838 normal: Vec2::from_raw(raw.normal),
839 point: Vec2::from_raw(raw.point),
840 fraction: raw.fraction,
841 iterations: raw.iterations,
842 hit: raw.hit,
843 };
844 output
845 .validate_for(operation)
846 .map_err(|_| Error::InvalidNativeOutput {
847 operation,
848 output: "cast_output",
849 constraint: "finite hit data, a unit-interval fraction, and non-negative iterations",
850 })?;
851 Ok(output)
852 }
853
854 pub fn validate(&self) -> Result<()> {
855 self.validate_for("CastOutput::validate")
856 }
857
858 fn validate_for(&self, operation: &'static str) -> Result<()> {
859 check_collision_vec2_valid(operation, "normal", self.normal)?;
860 check_collision_vec2_valid(operation, "point", self.point)?;
861 check_collision_unit_interval_scalar(operation, "fraction", self.fraction)?;
862 check_collision_non_negative_int(operation, "iterations", self.iterations)?;
863 if self.hit && self.fraction > 0.0 && !collision_unit_vector_is_valid(self.normal) {
864 return Err(Error::invalid_argument(
865 operation,
866 "normal",
867 "a finite unit vector for a non-overlap hit",
868 ));
869 }
870 if self.hit
871 && self.fraction == 0.0
872 && self.normal != Vec2::ZERO
873 && !collision_unit_vector_is_valid(self.normal)
874 {
875 return Err(Error::invalid_argument(
876 operation,
877 "normal",
878 "a finite unit vector, or zero for an initial overlap",
879 ));
880 }
881 Ok(())
882 }
883}
884
885#[doc(alias = "distance_input")]
887#[derive(Copy, Clone, Debug)]
888pub struct DistanceInput {
889 pub(crate) proxy_a: ShapeProxy,
890 pub(crate) proxy_b: ShapeProxy,
891 pub(crate) transform_b_in_a: Transform,
893 pub(crate) use_radii: bool,
894}
895
896impl DistanceInput {
897 #[inline]
901 pub fn new(
902 proxy_a: ShapeProxy,
903 proxy_b: ShapeProxy,
904 transform_b_in_a: Transform,
905 ) -> Result<Self> {
906 let input = Self {
907 proxy_a,
908 proxy_b,
909 transform_b_in_a,
910 use_radii: false,
911 };
912 input.validate()?;
913 Ok(input)
914 }
915
916 #[inline]
918 pub fn with_radii(mut self, use_radii: bool) -> Self {
919 self.use_radii = use_radii;
920 self
921 }
922
923 #[inline]
924 pub const fn proxy_a(self) -> ShapeProxy {
925 self.proxy_a
926 }
927
928 #[inline]
929 pub const fn proxy_b(self) -> ShapeProxy {
930 self.proxy_b
931 }
932
933 #[inline]
934 pub const fn transform_b_in_a(self) -> Transform {
935 self.transform_b_in_a
936 }
937
938 #[inline]
939 pub const fn use_radii(self) -> bool {
940 self.use_radii
941 }
942
943 pub fn validate(&self) -> Result<()> {
945 self.proxy_a.validate()?;
946 self.proxy_b.validate()?;
947 check_collision_transform_valid("DistanceInput::validate", self.transform_b_in_a)?;
948 Ok(())
949 }
950
951 #[inline]
952 pub fn into_raw(self) -> ffi::b2DistanceInput {
953 ffi::b2DistanceInput {
954 proxyA: self.proxy_a.raw(),
955 proxyB: self.proxy_b.raw(),
956 transform: self.transform_b_in_a.into_raw(),
957 useRadii: self.use_radii,
958 }
959 }
960}
961
962#[doc(alias = "distance_output")]
964#[derive(Copy, Clone, Debug)]
965pub struct DistanceOutput {
966 pub point_a: Vec2,
967 pub point_b: Vec2,
968 pub normal: Vec2,
969 pub distance: f32,
970 pub iterations: i32,
971 pub simplex_count: i32,
972}
973
974impl DistanceOutput {
975 #[inline]
976 pub fn from_raw(raw: ffi::b2DistanceOutput) -> Result<Self> {
977 let output = Self {
978 point_a: Vec2::from_raw(raw.pointA),
979 point_b: Vec2::from_raw(raw.pointB),
980 normal: Vec2::from_raw(raw.normal),
981 distance: raw.distance,
982 iterations: raw.iterations,
983 simplex_count: raw.simplexCount,
984 };
985 output.validate_for("DistanceOutput::from_raw")?;
986 Ok(output)
987 }
988
989 fn from_native(operation: &'static str, raw: ffi::b2DistanceOutput) -> Result<Self> {
990 let output = Self {
991 point_a: Vec2::from_raw(raw.pointA),
992 point_b: Vec2::from_raw(raw.pointB),
993 normal: Vec2::from_raw(raw.normal),
994 distance: raw.distance,
995 iterations: raw.iterations,
996 simplex_count: raw.simplexCount,
997 };
998 output
999 .validate_for(operation)
1000 .map_err(|_| Error::InvalidNativeOutput {
1001 operation,
1002 output: "distance_output",
1003 constraint: "finite points and distance with valid normal and non-negative counters",
1004 })?;
1005 Ok(output)
1006 }
1007
1008 pub fn validate(&self) -> Result<()> {
1009 self.validate_for("DistanceOutput::validate")
1010 }
1011
1012 fn validate_for(&self, operation: &'static str) -> Result<()> {
1013 check_collision_vec2_valid(operation, "point_a", self.point_a)?;
1014 check_collision_vec2_valid(operation, "point_b", self.point_b)?;
1015 check_collision_vec2_valid(operation, "normal", self.normal)?;
1016 check_collision_non_negative_finite_scalar(operation, "distance", self.distance)?;
1017 check_collision_non_negative_int(operation, "iterations", self.iterations)?;
1018 check_collision_non_negative_int(operation, "simplex_count", self.simplex_count)?;
1019 if self.distance > 0.0 && !collision_unit_vector_is_valid(self.normal) {
1020 return Err(Error::invalid_argument(
1021 operation,
1022 "normal",
1023 "a finite unit vector when distance is positive",
1024 ));
1025 }
1026 if self.distance == 0.0
1027 && self.normal != Vec2::ZERO
1028 && !collision_unit_vector_is_valid(self.normal)
1029 {
1030 return Err(Error::invalid_argument(
1031 operation,
1032 "normal",
1033 "a finite unit vector, or zero when distance is zero",
1034 ));
1035 }
1036 Ok(())
1037 }
1038}
1039
1040#[doc(alias = "shape_cast_pair_input")]
1042#[derive(Copy, Clone, Debug)]
1043pub struct ShapeCastPairInput {
1044 pub(crate) proxy_a: ShapeProxy,
1045 pub(crate) proxy_b: ShapeProxy,
1046 pub(crate) transform_b_in_a: Transform,
1048 pub(crate) translation_b_in_a: Vec2,
1050 pub(crate) max_fraction: f32,
1051 pub(crate) can_encroach: bool,
1052}
1053
1054impl ShapeCastPairInput {
1055 #[inline]
1058 pub fn new<V: Into<Vec2>>(
1059 proxy_a: ShapeProxy,
1060 proxy_b: ShapeProxy,
1061 transform_b_in_a: Transform,
1062 translation_b_in_a: V,
1063 ) -> Result<Self> {
1064 let input = Self {
1065 proxy_a,
1066 proxy_b,
1067 transform_b_in_a,
1068 translation_b_in_a: translation_b_in_a.into(),
1069 max_fraction: 1.0,
1070 can_encroach: false,
1071 };
1072 input.validate()?;
1073 Ok(input)
1074 }
1075
1076 #[inline]
1078 pub fn with_max_fraction(mut self, max_fraction: f32) -> Result<Self> {
1079 check_collision_unit_interval_scalar(
1080 "ShapeCastPairInput::with_max_fraction",
1081 "max_fraction",
1082 max_fraction,
1083 )?;
1084 self.max_fraction = max_fraction;
1085 Ok(self)
1086 }
1087
1088 #[inline]
1090 pub fn with_can_encroach(mut self, can_encroach: bool) -> Self {
1091 self.can_encroach = can_encroach;
1092 self
1093 }
1094
1095 #[inline]
1096 pub const fn proxy_a(self) -> ShapeProxy {
1097 self.proxy_a
1098 }
1099
1100 #[inline]
1101 pub const fn proxy_b(self) -> ShapeProxy {
1102 self.proxy_b
1103 }
1104
1105 #[inline]
1106 pub const fn transform_b_in_a(self) -> Transform {
1107 self.transform_b_in_a
1108 }
1109
1110 #[inline]
1111 pub const fn translation_b_in_a(self) -> Vec2 {
1112 self.translation_b_in_a
1113 }
1114
1115 #[inline]
1116 pub const fn max_fraction(self) -> f32 {
1117 self.max_fraction
1118 }
1119
1120 #[inline]
1121 pub const fn can_encroach(self) -> bool {
1122 self.can_encroach
1123 }
1124
1125 pub fn validate(&self) -> Result<()> {
1127 self.proxy_a.validate()?;
1128 self.proxy_b.validate()?;
1129 check_collision_transform_valid("ShapeCastPairInput::validate", self.transform_b_in_a)?;
1130 check_collision_vec2_valid(
1131 "ShapeCastPairInput::validate",
1132 "translation_b_in_a",
1133 self.translation_b_in_a,
1134 )?;
1135 check_collision_unit_interval_scalar(
1136 "ShapeCastPairInput::validate",
1137 "max_fraction",
1138 self.max_fraction,
1139 )?;
1140 Ok(())
1141 }
1142
1143 #[inline]
1144 pub fn into_raw(self) -> ffi::b2ShapeCastPairInput {
1145 ffi::b2ShapeCastPairInput {
1146 proxyA: self.proxy_a.raw(),
1147 proxyB: self.proxy_b.raw(),
1148 transform: self.transform_b_in_a.into_raw(),
1149 translationB: self.translation_b_in_a.into_raw(),
1150 maxFraction: self.max_fraction,
1151 canEncroach: self.can_encroach,
1152 }
1153 }
1154}
1155
1156#[doc(alias = "sweep")]
1158#[derive(Copy, Clone, Debug)]
1159pub struct Sweep {
1160 pub(crate) local_center: Vec2,
1161 pub(crate) c1: Vec2,
1162 pub(crate) c2: Vec2,
1163 pub(crate) q1: Rot,
1164 pub(crate) q2: Rot,
1165}
1166
1167impl Sweep {
1168 #[inline]
1169 pub fn new<LC: Into<Vec2>, C1: Into<Vec2>, C2: Into<Vec2>>(
1170 local_center: LC,
1171 c1: C1,
1172 c2: C2,
1173 q1: Rot,
1174 q2: Rot,
1175 ) -> Result<Self> {
1176 let sweep = Self {
1177 local_center: local_center.into(),
1178 c1: c1.into(),
1179 c2: c2.into(),
1180 q1,
1181 q2,
1182 };
1183 sweep.validate()?;
1184 Ok(sweep)
1185 }
1186
1187 #[inline]
1188 pub fn from_raw(raw: ffi::b2Sweep) -> Result<Self> {
1190 let sweep = Self {
1191 local_center: Vec2::from_raw(raw.localCenter),
1192 c1: Vec2::from_raw(raw.c1),
1193 c2: Vec2::from_raw(raw.c2),
1194 q1: Rot::from_raw(raw.q1)?,
1195 q2: Rot::from_raw(raw.q2)?,
1196 };
1197 sweep.validate()?;
1198 Ok(sweep)
1199 }
1200
1201 #[inline]
1202 pub const fn local_center(self) -> Vec2 {
1203 self.local_center
1204 }
1205
1206 #[inline]
1207 pub const fn start_center(self) -> Vec2 {
1208 self.c1
1209 }
1210
1211 #[inline]
1212 pub const fn end_center(self) -> Vec2 {
1213 self.c2
1214 }
1215
1216 #[inline]
1217 pub const fn start_rotation(self) -> Rot {
1218 self.q1
1219 }
1220
1221 #[inline]
1222 pub const fn end_rotation(self) -> Rot {
1223 self.q2
1224 }
1225
1226 #[inline]
1227 pub fn into_raw(self) -> ffi::b2Sweep {
1228 ffi::b2Sweep {
1229 localCenter: self.local_center.into_raw(),
1230 c1: self.c1.into_raw(),
1231 c2: self.c2.into_raw(),
1232 q1: self.q1.into_raw(),
1233 q2: self.q2.into_raw(),
1234 }
1235 }
1236
1237 pub fn validate(&self) -> Result<()> {
1239 check_collision_vec2_valid("Sweep::validate", "local_center", self.local_center)?;
1240 check_collision_vec2_valid("Sweep::validate", "c1", self.c1)?;
1241 check_collision_vec2_valid("Sweep::validate", "c2", self.c2)?;
1242 check_collision_rot_valid("Sweep::validate", "q1", self.q1)?;
1243 check_collision_rot_valid("Sweep::validate", "q2", self.q2)?;
1244 Ok(())
1245 }
1246
1247 #[inline]
1252 pub fn transform_at(self, time: f32) -> Result<Transform> {
1253 self.validate()?;
1254 check_collision_unit_interval_scalar("Sweep::transform_at", "time", time)?;
1255 let _lease = transient_native_lease()?;
1256 let raw = self.into_raw();
1257 Transform::from_raw(unsafe { ffi::b2GetSweepTransform(&raw, time) }).map_err(|_| {
1258 Error::InvalidNativeOutput {
1259 operation: "Sweep::transform_at",
1260 output: "transform",
1261 constraint: "a finite rigid transform",
1262 }
1263 })
1264 }
1265}
1266
1267#[doc(alias = "toi_input")]
1269#[derive(Copy, Clone, Debug)]
1270pub struct ToiInput {
1271 pub(crate) proxy_a: ShapeProxy,
1272 pub(crate) proxy_b: ShapeProxy,
1273 pub(crate) sweep_a: Sweep,
1274 pub(crate) sweep_b: Sweep,
1275 pub(crate) max_fraction: f32,
1276}
1277
1278impl ToiInput {
1279 #[inline]
1281 pub fn new(
1282 proxy_a: ShapeProxy,
1283 proxy_b: ShapeProxy,
1284 sweep_a: Sweep,
1285 sweep_b: Sweep,
1286 ) -> Result<Self> {
1287 let input = Self {
1288 proxy_a,
1289 proxy_b,
1290 sweep_a,
1291 sweep_b,
1292 max_fraction: 1.0,
1293 };
1294 input.validate()?;
1295 Ok(input)
1296 }
1297
1298 #[inline]
1300 pub fn with_max_fraction(mut self, max_fraction: f32) -> Result<Self> {
1301 check_collision_unit_interval_scalar(
1302 "ToiInput::with_max_fraction",
1303 "max_fraction",
1304 max_fraction,
1305 )?;
1306 self.max_fraction = max_fraction;
1307 Ok(self)
1308 }
1309
1310 #[inline]
1311 pub const fn proxy_a(self) -> ShapeProxy {
1312 self.proxy_a
1313 }
1314
1315 #[inline]
1316 pub const fn proxy_b(self) -> ShapeProxy {
1317 self.proxy_b
1318 }
1319
1320 #[inline]
1321 pub const fn sweep_a(self) -> Sweep {
1322 self.sweep_a
1323 }
1324
1325 #[inline]
1326 pub const fn sweep_b(self) -> Sweep {
1327 self.sweep_b
1328 }
1329
1330 #[inline]
1331 pub const fn max_fraction(self) -> f32 {
1332 self.max_fraction
1333 }
1334
1335 pub fn validate(&self) -> Result<()> {
1337 self.proxy_a.validate()?;
1338 self.proxy_b.validate()?;
1339 self.sweep_a.validate()?;
1340 self.sweep_b.validate()?;
1341 check_collision_unit_interval_scalar(
1342 "ToiInput::validate",
1343 "max_fraction",
1344 self.max_fraction,
1345 )?;
1346 Ok(())
1347 }
1348
1349 #[inline]
1350 pub fn into_raw(self) -> ffi::b2TOIInput {
1351 ffi::b2TOIInput {
1352 proxyA: self.proxy_a.raw(),
1353 proxyB: self.proxy_b.raw(),
1354 sweepA: self.sweep_a.into_raw(),
1355 sweepB: self.sweep_b.into_raw(),
1356 maxFraction: self.max_fraction,
1357 }
1358 }
1359}
1360
1361#[doc(alias = "toi_state")]
1363#[repr(u32)]
1364#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1365pub enum ToiState {
1366 Unknown = ffi::b2TOIState_b2_toiStateUnknown,
1367 Failed = ffi::b2TOIState_b2_toiStateFailed,
1368 Overlapped = ffi::b2TOIState_b2_toiStateOverlapped,
1369 Hit = ffi::b2TOIState_b2_toiStateHit,
1370 Separated = ffi::b2TOIState_b2_toiStateSeparated,
1371}
1372
1373impl ToiState {
1374 #[inline]
1375 pub const fn from_raw(raw: ffi::b2TOIState) -> Option<Self> {
1376 match raw {
1377 ffi::b2TOIState_b2_toiStateUnknown => Some(Self::Unknown),
1378 ffi::b2TOIState_b2_toiStateFailed => Some(Self::Failed),
1379 ffi::b2TOIState_b2_toiStateOverlapped => Some(Self::Overlapped),
1380 ffi::b2TOIState_b2_toiStateHit => Some(Self::Hit),
1381 ffi::b2TOIState_b2_toiStateSeparated => Some(Self::Separated),
1382 _ => None,
1383 }
1384 }
1385}
1386
1387#[doc(alias = "toi_output")]
1389#[derive(Copy, Clone, Debug)]
1390pub struct ToiOutput {
1391 pub state: ToiState,
1392 pub point: Vec2,
1393 pub normal: Vec2,
1394 pub fraction: f32,
1395}
1396
1397impl ToiOutput {
1398 #[inline]
1399 pub fn from_raw(raw: ffi::b2TOIOutput) -> Result<Self> {
1400 let output = Self {
1401 state: ToiState::from_raw(raw.state).ok_or_else(|| {
1402 Error::invalid_argument("ToiOutput::from_raw", "state", "a known Box2D TOI state")
1403 })?,
1404 point: Vec2::from_raw(raw.point),
1405 normal: Vec2::from_raw(raw.normal),
1406 fraction: raw.fraction,
1407 };
1408 output.validate_for("ToiOutput::from_raw")?;
1409 Ok(output)
1410 }
1411
1412 fn from_native(operation: &'static str, raw: ffi::b2TOIOutput) -> Result<Self> {
1413 let state = ToiState::from_raw(raw.state).ok_or(Error::InvalidNativeOutput {
1414 operation,
1415 output: "toi_output.state",
1416 constraint: "a known Box2D TOI state",
1417 })?;
1418 let output = Self {
1419 state,
1420 point: Vec2::from_raw(raw.point),
1421 normal: Vec2::from_raw(raw.normal),
1422 fraction: raw.fraction,
1423 };
1424 output
1425 .validate_for(operation)
1426 .map_err(|_| Error::InvalidNativeOutput {
1427 operation,
1428 output: "toi_output",
1429 constraint: "finite hit data and a unit-interval fraction",
1430 })?;
1431 Ok(output)
1432 }
1433
1434 pub fn validate(&self) -> Result<()> {
1435 self.validate_for("ToiOutput::validate")
1436 }
1437
1438 fn validate_for(&self, operation: &'static str) -> Result<()> {
1439 check_collision_vec2_valid(operation, "point", self.point)?;
1440 check_collision_vec2_valid(operation, "normal", self.normal)?;
1441 check_collision_unit_interval_scalar(operation, "fraction", self.fraction)?;
1442 if self.state == ToiState::Hit && !collision_unit_vector_is_valid(self.normal) {
1443 return Err(Error::invalid_argument(
1444 operation,
1445 "normal",
1446 "a finite unit vector for a TOI hit",
1447 ));
1448 }
1449 Ok(())
1450 }
1451}
1452
1453pub fn segment_distance<P1, Q1, P2, Q2>(
1455 p1: P1,
1456 q1: Q1,
1457 p2: P2,
1458 q2: Q2,
1459) -> Result<SegmentDistanceResult>
1460where
1461 P1: Into<Vec2>,
1462 Q1: Into<Vec2>,
1463 P2: Into<Vec2>,
1464 Q2: Into<Vec2>,
1465{
1466 let p1 = p1.into();
1467 let q1 = q1.into();
1468 let p2 = p2.into();
1469 let q2 = q2.into();
1470 check_collision_vec2_valid("segment_distance", "p1", p1)?;
1471 check_collision_vec2_valid("segment_distance", "q1", q1)?;
1472 check_collision_vec2_valid("segment_distance", "p2", p2)?;
1473 check_collision_vec2_valid("segment_distance", "q2", q2)?;
1474 let _lease = transient_native_lease()?;
1475 SegmentDistanceResult::from_native("segment_distance", unsafe {
1476 ffi::b2SegmentDistance(p1.into_raw(), q1.into_raw(), p2.into_raw(), q2.into_raw())
1477 })
1478}
1479
1480pub fn shape_distance(input: DistanceInput, cache: &mut SimplexCache) -> Result<DistanceOutput> {
1482 input.validate()?;
1483 let proxy_a_count = input.proxy_a.count();
1484 let proxy_b_count = input.proxy_b.count();
1485 cache.validate_for("shape_distance", proxy_a_count, proxy_b_count)?;
1486 let raw_input = input.into_raw();
1487 let mut staged_cache = *cache;
1488 let _lease = transient_native_lease()?;
1489 let output = DistanceOutput::from_native("shape_distance", unsafe {
1490 ffi::b2ShapeDistance(&raw_input, staged_cache.raw_mut(), core::ptr::null_mut(), 0)
1491 })?;
1492 if output.simplex_count != 0 {
1493 return Err(Error::InvalidNativeOutput {
1494 operation: "shape_distance",
1495 output: "distance_output.simplex_count",
1496 constraint: "zero when no simplex output buffer was supplied",
1497 });
1498 }
1499 commit_native_simplex_cache(
1500 Some(cache),
1501 staged_cache,
1502 "shape_distance",
1503 proxy_a_count,
1504 proxy_b_count,
1505 )?;
1506 Ok(output)
1507}
1508
1509pub fn shape_cast(input: ShapeCastPairInput) -> Result<CastOutput> {
1513 input.validate()?;
1514 let raw_input = input.into_raw();
1515 let _lease = transient_native_lease()?;
1516 CastOutput::from_native("shape_cast", unsafe { ffi::b2ShapeCast(&raw_input) })
1517}
1518
1519pub fn time_of_impact(input: ToiInput) -> Result<ToiOutput> {
1521 input.validate()?;
1522 let raw_input = input.into_raw();
1523 let _lease = transient_native_lease()?;
1524 ToiOutput::from_native("time_of_impact", unsafe { ffi::b2TimeOfImpact(&raw_input) })
1525}
1526
1527#[doc(alias = "b2CollideCircles")]
1532pub fn collide_circles(
1533 circle_a: Circle,
1534 circle_b: Circle,
1535 transform_b_in_a: Transform,
1536) -> Result<LocalManifold> {
1537 circle_a.validate()?;
1538 circle_b.validate()?;
1539 check_collision_transform_valid("collide_circles", transform_b_in_a)?;
1540 let raw_a = circle_a.into_raw();
1541 let raw_b = circle_b.into_raw();
1542 let _lease = transient_native_lease()?;
1543 LocalManifold::from_native("collide_circles", unsafe {
1544 ffi::b2CollideCircles(&raw_a, &raw_b, transform_b_in_a.into_raw())
1545 })
1546}
1547
1548#[doc(alias = "b2CollideCapsuleAndCircle")]
1553pub fn collide_capsule_and_circle(
1554 capsule_a: Capsule,
1555 circle_b: Circle,
1556 transform_b_in_a: Transform,
1557) -> Result<LocalManifold> {
1558 capsule_a.validate()?;
1559 circle_b.validate()?;
1560 check_collision_transform_valid("collide_capsule_and_circle", transform_b_in_a)?;
1561 let raw_a = capsule_a.into_raw();
1562 let raw_b = circle_b.into_raw();
1563 let _lease = transient_native_lease()?;
1564 LocalManifold::from_native("collide_capsule_and_circle", unsafe {
1565 ffi::b2CollideCapsuleAndCircle(&raw_a, &raw_b, transform_b_in_a.into_raw())
1566 })
1567}
1568
1569#[doc(alias = "b2CollideSegmentAndCircle")]
1574pub fn collide_segment_and_circle(
1575 segment_a: Segment,
1576 circle_b: Circle,
1577 transform_b_in_a: Transform,
1578) -> Result<LocalManifold> {
1579 segment_a.validate()?;
1580 circle_b.validate()?;
1581 check_collision_transform_valid("collide_segment_and_circle", transform_b_in_a)?;
1582 let raw_a = segment_a.into_raw();
1583 let raw_b = circle_b.into_raw();
1584 let _lease = transient_native_lease()?;
1585 LocalManifold::from_native("collide_segment_and_circle", unsafe {
1586 ffi::b2CollideSegmentAndCircle(&raw_a, &raw_b, transform_b_in_a.into_raw())
1587 })
1588}
1589
1590#[doc(alias = "b2CollidePolygonAndCircle")]
1595pub fn collide_polygon_and_circle(
1596 polygon_a: Polygon,
1597 circle_b: Circle,
1598 transform_b_in_a: Transform,
1599) -> Result<LocalManifold> {
1600 polygon_a.validate()?;
1601 circle_b.validate()?;
1602 check_collision_transform_valid("collide_polygon_and_circle", transform_b_in_a)?;
1603 let raw_a = polygon_a.into_raw();
1604 let raw_b = circle_b.into_raw();
1605 let _lease = transient_native_lease()?;
1606 LocalManifold::from_native("collide_polygon_and_circle", unsafe {
1607 ffi::b2CollidePolygonAndCircle(&raw_a, &raw_b, transform_b_in_a.into_raw())
1608 })
1609}
1610
1611#[doc(alias = "b2CollideCapsules")]
1616pub fn collide_capsules(
1617 capsule_a: Capsule,
1618 capsule_b: Capsule,
1619 transform_b_in_a: Transform,
1620) -> Result<LocalManifold> {
1621 capsule_a.validate()?;
1622 capsule_b.validate()?;
1623 check_collision_transform_valid("collide_capsules", transform_b_in_a)?;
1624 let raw_a = capsule_a.into_raw();
1625 let raw_b = capsule_b.into_raw();
1626 let _lease = transient_native_lease()?;
1627 LocalManifold::from_native("collide_capsules", unsafe {
1628 ffi::b2CollideCapsules(&raw_a, &raw_b, transform_b_in_a.into_raw())
1629 })
1630}
1631
1632#[doc(alias = "b2CollideSegmentAndCapsule")]
1637pub fn collide_segment_and_capsule(
1638 segment_a: Segment,
1639 capsule_b: Capsule,
1640 transform_b_in_a: Transform,
1641) -> Result<LocalManifold> {
1642 segment_a.validate()?;
1643 capsule_b.validate()?;
1644 check_collision_transform_valid("collide_segment_and_capsule", transform_b_in_a)?;
1645 let raw_a = segment_a.into_raw();
1646 let raw_b = capsule_b.into_raw();
1647 let _lease = transient_native_lease()?;
1648 LocalManifold::from_native("collide_segment_and_capsule", unsafe {
1649 ffi::b2CollideSegmentAndCapsule(&raw_a, &raw_b, transform_b_in_a.into_raw())
1650 })
1651}
1652
1653#[doc(alias = "b2CollidePolygonAndCapsule")]
1658pub fn collide_polygon_and_capsule(
1659 polygon_a: Polygon,
1660 capsule_b: Capsule,
1661 transform_b_in_a: Transform,
1662) -> Result<LocalManifold> {
1663 polygon_a.validate()?;
1664 capsule_b.validate()?;
1665 check_collision_transform_valid("collide_polygon_and_capsule", transform_b_in_a)?;
1666 let raw_a = polygon_a.into_raw();
1667 let raw_b = capsule_b.into_raw();
1668 let _lease = transient_native_lease()?;
1669 LocalManifold::from_native("collide_polygon_and_capsule", unsafe {
1670 ffi::b2CollidePolygonAndCapsule(&raw_a, &raw_b, transform_b_in_a.into_raw())
1671 })
1672}
1673
1674#[doc(alias = "b2CollidePolygons")]
1679pub fn collide_polygons(
1680 polygon_a: Polygon,
1681 polygon_b: Polygon,
1682 transform_b_in_a: Transform,
1683) -> Result<LocalManifold> {
1684 polygon_a.validate()?;
1685 polygon_b.validate()?;
1686 check_collision_transform_valid("collide_polygons", transform_b_in_a)?;
1687 let raw_a = polygon_a.into_raw();
1688 let raw_b = polygon_b.into_raw();
1689 let _lease = transient_native_lease()?;
1690 LocalManifold::from_native("collide_polygons", unsafe {
1691 ffi::b2CollidePolygons(&raw_a, &raw_b, transform_b_in_a.into_raw())
1692 })
1693}
1694
1695#[doc(alias = "b2CollideSegmentAndPolygon")]
1700pub fn collide_segment_and_polygon(
1701 segment_a: Segment,
1702 polygon_b: Polygon,
1703 transform_b_in_a: Transform,
1704) -> Result<LocalManifold> {
1705 segment_a.validate()?;
1706 polygon_b.validate()?;
1707 check_collision_transform_valid("collide_segment_and_polygon", transform_b_in_a)?;
1708 let raw_a = segment_a.into_raw();
1709 let raw_b = polygon_b.into_raw();
1710 let _lease = transient_native_lease()?;
1711 LocalManifold::from_native("collide_segment_and_polygon", unsafe {
1712 ffi::b2CollideSegmentAndPolygon(&raw_a, &raw_b, transform_b_in_a.into_raw())
1713 })
1714}
1715
1716#[doc(alias = "b2CollideChainSegmentAndCircle")]
1721pub fn collide_chain_segment_and_circle(
1722 segment_a: ChainSegment,
1723 circle_b: Circle,
1724 transform_b_in_a: Transform,
1725) -> Result<LocalManifold> {
1726 segment_a.validate()?;
1727 circle_b.validate()?;
1728 check_collision_transform_valid("collide_chain_segment_and_circle", transform_b_in_a)?;
1729 let raw_a = segment_a.into_raw();
1730 let raw_b = circle_b.into_raw();
1731 let _lease = transient_native_lease()?;
1732 LocalManifold::from_native("collide_chain_segment_and_circle", unsafe {
1733 ffi::b2CollideChainSegmentAndCircle(&raw_a, &raw_b, transform_b_in_a.into_raw())
1734 })
1735}
1736
1737#[doc(alias = "b2CollideChainSegmentAndCapsule")]
1745pub fn collide_chain_segment_and_capsule(
1746 segment_a: ChainSegment,
1747 capsule_b: Capsule,
1748 transform_b_in_a: Transform,
1749 cache: Option<&mut SimplexCache>,
1750) -> Result<LocalManifold> {
1751 segment_a.validate()?;
1752 capsule_b.validate()?;
1753 check_collision_transform_valid("collide_chain_segment_and_capsule", transform_b_in_a)?;
1754 let raw_a = segment_a.into_raw();
1755 let raw_b = capsule_b.into_raw();
1756 let mut staged_cache = cache.as_deref().copied().unwrap_or_default();
1757 staged_cache.validate_for("collide_chain_segment_and_capsule", 2, 2)?;
1758 let _lease = transient_native_lease()?;
1759 let manifold = LocalManifold::from_native("collide_chain_segment_and_capsule", unsafe {
1760 ffi::b2CollideChainSegmentAndCapsule(
1761 &raw_a,
1762 &raw_b,
1763 transform_b_in_a.into_raw(),
1764 staged_cache.raw_mut(),
1765 )
1766 })?;
1767 commit_native_simplex_cache(
1768 cache,
1769 staged_cache,
1770 "collide_chain_segment_and_capsule",
1771 2,
1772 2,
1773 )?;
1774 Ok(manifold)
1775}
1776
1777#[doc(alias = "b2CollideChainSegmentAndPolygon")]
1785pub fn collide_chain_segment_and_polygon(
1786 segment_a: ChainSegment,
1787 polygon_b: Polygon,
1788 transform_b_in_a: Transform,
1789 cache: Option<&mut SimplexCache>,
1790) -> Result<LocalManifold> {
1791 segment_a.validate()?;
1792 polygon_b.validate()?;
1793 check_collision_transform_valid("collide_chain_segment_and_polygon", transform_b_in_a)?;
1794 let raw_a = segment_a.into_raw();
1795 let raw_b = polygon_b.into_raw();
1796 let proxy_b_count = usize::try_from(raw_b.count).map_err(|_| {
1797 Error::invalid_argument(
1798 "collide_chain_segment_and_polygon",
1799 "polygon_b",
1800 "a polygon with a representable point count",
1801 )
1802 })?;
1803 let mut staged_cache = cache.as_deref().copied().unwrap_or_default();
1804 staged_cache.validate_for("collide_chain_segment_and_polygon", 2, proxy_b_count)?;
1805 let _lease = transient_native_lease()?;
1806 let manifold = LocalManifold::from_native("collide_chain_segment_and_polygon", unsafe {
1807 ffi::b2CollideChainSegmentAndPolygon(
1808 &raw_a,
1809 &raw_b,
1810 transform_b_in_a.into_raw(),
1811 staged_cache.raw_mut(),
1812 )
1813 })?;
1814 commit_native_simplex_cache(
1815 cache,
1816 staged_cache,
1817 "collide_chain_segment_and_polygon",
1818 2,
1819 proxy_b_count,
1820 )?;
1821 Ok(manifold)
1822}
1823
1824impl Aabb {
1825 #[inline]
1827 pub fn is_valid(self) -> bool {
1828 let width = self.upper.x - self.lower.x;
1829 let height = self.upper.y - self.lower.y;
1830 width >= 0.0 && height >= 0.0 && self.lower.is_valid() && self.upper.is_valid()
1831 }
1832
1833 #[inline]
1835 pub fn validate(self) -> Result<()> {
1836 if self.is_valid() {
1837 Ok(())
1838 } else {
1839 Err(Error::invalid_argument(
1840 "Aabb::validate",
1841 "self",
1842 "finite ordered lower and upper bounds",
1843 ))
1844 }
1845 }
1846
1847 pub fn ray_cast<VO: Into<Vec2>, VT: Into<Vec2>>(
1851 self,
1852 origin: VO,
1853 translation: VT,
1854 ) -> Result<CastOutput> {
1855 let origin = origin.into();
1856 let translation = translation.into();
1857 self.validate()?;
1858 check_collision_vec2_valid("Aabb::ray_cast", "origin", origin)?;
1859 check_collision_vec2_valid("Aabb::ray_cast", "translation", translation)?;
1860 Ok(self.ray_cast_validated(origin, translation))
1861 }
1862
1863 fn ray_cast_validated(self, origin: Vec2, translation: Vec2) -> CastOutput {
1864 let mut axis_state = RayCastAxisState {
1865 tmin: 0.0,
1866 tmax: 1.0,
1867 normal: Vec2::ZERO,
1868 };
1869
1870 if !ray_cast_axis(
1871 RayCastAxisInput {
1872 origin: origin.x,
1873 translation: translation.x,
1874 lower: self.lower.x,
1875 upper: self.upper.x,
1876 enter_normal: Vec2::new(-1.0, 0.0),
1877 exit_normal: Vec2::new(1.0, 0.0),
1878 },
1879 &mut axis_state,
1880 ) {
1881 return CastOutput::MISS;
1882 }
1883
1884 if !ray_cast_axis(
1885 RayCastAxisInput {
1886 origin: origin.y,
1887 translation: translation.y,
1888 lower: self.lower.y,
1889 upper: self.upper.y,
1890 enter_normal: Vec2::new(0.0, -1.0),
1891 exit_normal: Vec2::new(0.0, 1.0),
1892 },
1893 &mut axis_state,
1894 ) {
1895 return CastOutput::MISS;
1896 }
1897
1898 if !(0.0..=1.0).contains(&axis_state.tmin) {
1899 return CastOutput::MISS;
1900 }
1901
1902 CastOutput {
1903 normal: axis_state.normal,
1904 point: Vec2::new(
1905 origin.x + axis_state.tmin * translation.x,
1906 origin.y + axis_state.tmin * translation.y,
1907 ),
1908 fraction: axis_state.tmin,
1909 iterations: 0,
1910 hit: true,
1911 }
1912 }
1913}
1914
1915#[cfg(test)]
1916mod tests {
1917 use super::*;
1918
1919 #[test]
1920 fn shape_proxy_owns_point_validation_and_storage_invariants() {
1921 let maximum =
1922 ShapeProxy::new((0..MAX_SHAPE_PROXY_POINTS).map(|_| [0.0_f32, 0.0]), 0.25).unwrap();
1923 assert_eq!(maximum.count(), MAX_SHAPE_PROXY_POINTS);
1924
1925 assert_eq!(
1926 ShapeProxy::new((0..=MAX_SHAPE_PROXY_POINTS).map(|_| [0.0_f32, 0.0]), 0.25,)
1927 .unwrap_err(),
1928 Error::invalid_argument(
1929 "ShapeProxy::new",
1930 "points",
1931 "no more than Box2D's maximum shape-proxy point count",
1932 )
1933 );
1934
1935 let raw = ShapeProxy::new([[1.0_f32, 2.0]], 0.25).unwrap().into_raw();
1936 assert_eq!(raw.count, 1);
1937 assert_eq!(raw.radius, 0.25);
1938 assert_eq!(raw.points[0].x, 1.0);
1939 assert_eq!(raw.points[0].y, 2.0);
1940 assert!(
1941 raw.points[1..]
1942 .iter()
1943 .all(|point| point.x == 0.0 && point.y == 0.0)
1944 );
1945
1946 assert_eq!(
1947 ShapeProxy::offset_from_points(
1948 [[f32::MAX, 0.0]],
1949 0.0,
1950 Transform::from_pos_angle([f32::MAX, 0.0], 0.0).unwrap(),
1951 )
1952 .unwrap_err(),
1953 Error::invalid_argument(
1954 "ShapeProxy::offset_from_points",
1955 "points/transform",
1956 "a transform whose proxy points remain finite",
1957 )
1958 );
1959 }
1960
1961 #[test]
1962 fn invalid_native_simplex_cache_is_not_published() {
1963 let original = SimplexCache {
1964 raw: ffi::b2SimplexCache {
1965 count: 1,
1966 indexA: [0, 0, 0],
1967 indexB: [0, 0, 0],
1968 },
1969 };
1970
1971 for staged in [
1972 SimplexCache {
1973 raw: ffi::b2SimplexCache {
1974 count: 4,
1975 indexA: [0, 0, 0],
1976 indexB: [0, 0, 0],
1977 },
1978 },
1979 SimplexCache {
1980 raw: ffi::b2SimplexCache {
1981 count: 1,
1982 indexA: [2, 0, 0],
1983 indexB: [0, 0, 0],
1984 },
1985 },
1986 ] {
1987 let mut target = original;
1988 assert_eq!(
1989 commit_native_simplex_cache(Some(&mut target), staged, "test_query", 2, 2),
1990 Err(Error::InvalidNativeOutput {
1991 operation: "test_query",
1992 output: "simplex_cache",
1993 constraint: "at most three in-range proxy point indices",
1994 })
1995 );
1996 assert_eq!(target.raw.count, original.raw.count);
1997 assert_eq!(target.raw.indexA, original.raw.indexA);
1998 assert_eq!(target.raw.indexB, original.raw.indexB);
1999 }
2000 }
2001
2002 #[test]
2003 fn aabb_validation_matches_upstream_finite_and_ordering_rules() {
2004 assert!(
2005 Aabb::new([0.0_f32, 0.0], [0.0_f32, 0.0])
2006 .unwrap()
2007 .is_valid()
2008 );
2009 assert!(
2010 Aabb::new([f32::MIN, f32::MIN], [f32::MAX, f32::MAX])
2011 .unwrap()
2012 .is_valid()
2013 );
2014
2015 for invalid in [
2016 Aabb::new([1.0_f32, 0.0], [0.0_f32, 1.0]),
2017 Aabb::new([0.0_f32, 1.0], [1.0_f32, 0.0]),
2018 Aabb::new([f32::NAN, 0.0], [1.0_f32, 1.0]),
2019 Aabb::new([0.0_f32, 0.0], [f32::INFINITY, 1.0]),
2020 Aabb::new([f32::NEG_INFINITY, 0.0], [1.0_f32, 1.0]),
2021 ] {
2022 assert!(invalid.is_err());
2023 }
2024 }
2025
2026 #[test]
2027 fn worldless_native_collision_calls_obey_the_callback_gate() {
2028 crate::Foundation::initialize_default().unwrap();
2029
2030 let proxy = ShapeProxy::new([[0.0_f32, 0.0]], 0.0).unwrap();
2031 let circle = Circle::new([0.0_f32, 0.0], 0.5).unwrap();
2032 let invalid_circle = Circle {
2033 center: Vec2::new(f32::NAN, 0.0),
2034 radius: 0.5,
2035 };
2036 let aabb = Aabb::new([-1.0_f32, -1.0], [1.0_f32, 1.0]).unwrap();
2037
2038 {
2039 let _callback_guard = crate::core::callback_state::CallbackGuard::enter();
2040 let proxy_input_was_materialized = std::cell::Cell::new(false);
2041
2042 assert_eq!(
2043 segment_distance(
2044 [0.0_f32, 0.0],
2045 [1.0_f32, 0.0],
2046 [0.0_f32, 1.0],
2047 [1.0_f32, 1.0],
2048 )
2049 .unwrap_err(),
2050 Error::InCallback
2051 );
2052 assert_eq!(
2053 collide_circles(circle, circle, Transform::IDENTITY).unwrap_err(),
2054 Error::InCallback
2055 );
2056 assert_eq!(
2057 collide_circles(invalid_circle, circle, Transform::IDENTITY).unwrap_err(),
2058 Error::invalid_argument(
2059 "Circle::validate",
2060 "circle",
2061 "finite center coordinates and a finite non-negative radius",
2062 )
2063 );
2064 let callback_proxy = ShapeProxy::new(
2065 core::iter::once_with(|| {
2066 proxy_input_was_materialized.set(true);
2067 [0.0_f32, 0.0]
2068 }),
2069 0.0,
2070 )
2071 .unwrap();
2072 assert_eq!(callback_proxy.points(), &[Vec2::ZERO]);
2073 assert!(proxy_input_was_materialized.get());
2074 assert!(aabb.is_valid());
2075 assert!(aabb.ray_cast([-2.0_f32, 0.0], [4.0_f32, 0.0]).unwrap().hit);
2076 assert_eq!(
2077 segment_distance(
2078 [f32::NAN, 0.0],
2079 [1.0_f32, 0.0],
2080 [0.0_f32, 1.0],
2081 [1.0_f32, 1.0],
2082 )
2083 .unwrap_err(),
2084 Error::invalid_argument("segment_distance", "p1", "a finite vector")
2085 );
2086 }
2087
2088 assert!(
2089 shape_distance(
2090 DistanceInput::new(proxy, proxy, Transform::IDENTITY).unwrap(),
2091 &mut SimplexCache::default(),
2092 )
2093 .is_ok()
2094 );
2095 }
2096
2097 #[test]
2098 fn sweep_transform_validation_precedes_foundation_activity() {
2099 let valid_sweep = Sweep::new(
2100 [0.0_f32, 0.0],
2101 [0.0_f32, 0.0],
2102 [1.0_f32, 0.0],
2103 Rot::IDENTITY,
2104 Rot::IDENTITY,
2105 )
2106 .unwrap();
2107 let invalid_sweep = Sweep {
2108 local_center: Vec2::new(f32::NAN, 0.0),
2109 c1: Vec2::ZERO,
2110 c2: Vec2::new(1.0, 0.0),
2111 q1: Rot::IDENTITY,
2112 q2: Rot::IDENTITY,
2113 };
2114 let _callback_guard = crate::core::callback_state::CallbackGuard::enter();
2115
2116 assert_eq!(
2117 invalid_sweep.transform_at(0.5).unwrap_err(),
2118 Error::invalid_argument("Sweep::validate", "local_center", "a finite vector",)
2119 );
2120 assert_eq!(
2121 valid_sweep.transform_at(f32::NAN).unwrap_err(),
2122 Error::invalid_argument("Sweep::transform_at", "time", "a finite value in 0.0..=1.0",)
2123 );
2124 assert_eq!(
2125 valid_sweep.transform_at(0.5).unwrap_err(),
2126 Error::InCallback
2127 );
2128 }
2129}