parry2d/query/contact_manifolds/
contact_manifolds_capsule_capsule.rs1use crate::math::{Pose, Real, Vector};
2use crate::query::{ContactManifold, TrackedContact};
3#[cfg(feature = "dim2")]
4use crate::shape::SegmentPointLocation;
5use crate::shape::{Capsule, PackedFeatureId, Shape};
6#[cfg(feature = "dim2")]
7use approx::AbsDiffEq;
8
9pub fn contact_manifold_capsule_capsule_shapes<ManifoldData, ContactData>(
11 pos12: &Pose,
12 shape1: &dyn Shape,
13 shape2: &dyn Shape,
14 prediction: Real,
15 manifold: &mut ContactManifold<ManifoldData, ContactData>,
16) where
17 ContactData: Default + Copy,
18{
19 if let (Some(capsule1), Some(capsule2)) = (shape1.as_capsule(), shape2.as_capsule()) {
20 contact_manifold_capsule_capsule(pos12, capsule1, capsule2, prediction, manifold);
21 }
22}
23
24#[cfg(feature = "dim2")]
26pub fn contact_manifold_capsule_capsule<'a, ManifoldData, ContactData>(
27 pos12: &Pose,
28 capsule1: &'a Capsule,
29 capsule2: &'a Capsule,
30 prediction: Real,
31 manifold: &mut ContactManifold<ManifoldData, ContactData>,
32) where
33 ContactData: Default + Copy,
34{
35 let seg1 = capsule1.segment;
36 let seg2_1 = capsule2.segment.transformed(pos12);
37 let (loc1, loc2) = crate::query::details::closest_points_segment_segment_with_locations_nD(
38 (&seg1.a, &seg1.b),
39 (&seg2_1.a, &seg2_1.b),
40 );
41
42 let old_manifold_points = manifold.points.clone();
45 manifold.clear();
46
47 let fid1 = if let SegmentPointLocation::OnVertex(v1) = loc1 {
48 v1 * 2
49 } else {
50 1
51 };
52 let fid2 = if let SegmentPointLocation::OnVertex(v2) = loc2 {
53 v2 * 2
54 } else {
55 1
56 };
57
58 let bcoords1 = loc1.barycentric_coordinates();
59 let bcoords2 = loc2.barycentric_coordinates();
60 let local_p1 = seg1.a * bcoords1[0] + seg1.b * bcoords1[1];
61 let local_p2_1 = seg2_1.a * bcoords2[0] + seg2_1.b * bcoords2[1];
62
63 let local_n1 = (local_p2_1 - local_p1).try_normalize().unwrap_or(Vector::Y);
64 let dist = (local_p2_1 - local_p1).dot(local_n1);
65
66 if dist <= prediction + capsule1.radius + capsule2.radius {
67 let local_n2 = pos12.rotation.inverse() * -local_n1;
68 let local_p2 = pos12.inverse_transform_point(local_p2_1);
69 let contact = TrackedContact::new(
70 local_p1,
71 local_p2,
72 PackedFeatureId::face(fid1),
73 PackedFeatureId::face(fid2),
74 dist,
75 );
76 manifold.points.push(contact);
77
78 manifold.local_n1 = local_n1;
79 manifold.local_n2 = local_n2;
80 } else {
81 return;
83 }
84
85 if let (Some(dir1), Some(dir2)) = (seg1.direction(), seg2_1.direction()) {
86 if dir1.dot(dir2).abs() >= crate::utils::COS_FRAC_PI_8
87 && dir1.dot(local_n1).abs() < crate::utils::SIN_FRAC_PI_8
88 {
89 if let Some((clip_a, clip_b)) = crate::query::details::clip_segment_segment_with_normal(
92 (seg1.a, seg1.b),
93 (seg2_1.a, seg2_1.b),
94 local_n1,
95 ) {
96 let contact =
97 if (clip_a.0 - local_p1).length_squared() > Real::default_epsilon() * 100.0 {
98 TrackedContact::new(
100 clip_a.0,
101 pos12.inverse_transform_point(clip_a.1),
102 PackedFeatureId::face(clip_a.2 as u32),
103 PackedFeatureId::face(clip_a.3 as u32),
104 (clip_a.1 - clip_a.0).dot(local_n1),
105 )
106 } else {
107 TrackedContact::new(
109 clip_b.0,
110 pos12.inverse_transform_point(clip_b.1),
111 PackedFeatureId::face(clip_b.2 as u32),
112 PackedFeatureId::face(clip_b.3 as u32),
113 (clip_b.1 - clip_b.0).dot(local_n1),
114 )
115 };
116
117 manifold.points.push(contact);
118 }
119 }
120 }
121
122 for point in &mut manifold.points {
123 point.local_p1 += manifold.local_n1 * capsule1.radius;
124 point.local_p2 += manifold.local_n2 * capsule2.radius;
125 point.dist -= capsule1.radius + capsule2.radius;
126 }
127
128 manifold.match_contacts(&old_manifold_points);
129}
130
131#[cfg(feature = "dim3")]
133pub fn contact_manifold_capsule_capsule<'a, ManifoldData, ContactData>(
134 pos12: &Pose,
135 capsule1: &'a Capsule,
136 capsule2: &'a Capsule,
137 prediction: Real,
138 manifold: &mut ContactManifold<ManifoldData, ContactData>,
139) where
140 ContactData: Default + Copy,
141{
142 let seg1 = capsule1.segment;
143 let seg2_1 = capsule2.segment.transformed(pos12);
144 let (loc1, loc2) =
145 crate::query::closest_points::closest_points_segment_segment_with_locations_nD(
146 (&seg1.a, &seg1.b),
147 (&seg2_1.a, &seg2_1.b),
148 );
149
150 let bcoords1 = loc1.barycentric_coordinates();
151 let bcoords2 = loc2.barycentric_coordinates();
152 let local_p1 = seg1.a * bcoords1[0] + seg1.b * bcoords1[1];
153 let local_p2_1 = seg2_1.a * bcoords2[0] + seg2_1.b * bcoords2[1];
154
155 let local_n1 = (local_p2_1 - local_p1).try_normalize().unwrap_or(Vector::Y);
156 let dist = (local_p2_1 - local_p1).dot(local_n1) - capsule1.radius - capsule2.radius;
157
158 if dist <= prediction {
159 let local_n2 = pos12.rotation.inverse() * -local_n1;
160 let fid = PackedFeatureId::face(0);
161 let contact = TrackedContact::new(
162 local_p1 + local_n1 * capsule1.radius,
163 pos12.inverse_transform_point(local_p2_1) + local_n2 * capsule2.radius,
164 fid,
165 fid,
166 dist,
167 );
168
169 if !manifold.points.is_empty() {
170 manifold.points[0].copy_geometry_from(contact);
171 } else {
172 manifold.points.push(contact);
173 }
174
175 manifold.local_n1 = local_n1;
176 manifold.local_n2 = local_n2;
177 } else {
178 manifold.clear();
179 }
180}