parry3d/query/contact_manifolds/
contact_manifolds_capsule_capsule.rs1use crate::math::{Isometry, Real, Vector};
2use crate::query::{ContactManifold, TrackedContact};
3#[cfg(feature = "dim2")]
4use crate::shape::SegmentPointLocation;
5use crate::shape::{Capsule, PackedFeatureId, Shape};
6use approx::AbsDiffEq;
7use na::Unit;
8
9pub fn contact_manifold_capsule_capsule_shapes<ManifoldData, ContactData>(
11 pos12: &Isometry<Real>,
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: &Isometry<Real>,
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.coords * bcoords1[1];
61 let local_p2_1 = seg2_1.a * bcoords2[0] + seg2_1.b.coords * bcoords2[1];
62
63 let local_n1 =
64 Unit::try_new(local_p2_1 - local_p1, Real::default_epsilon()).unwrap_or(Vector::y_axis());
65 let dist = (local_p2_1 - local_p1).dot(&local_n1);
66
67 if dist <= prediction + capsule1.radius + capsule2.radius {
68 let local_n2 = pos12.inverse_transform_unit_vector(&-local_n1);
69 let local_p2 = pos12.inverse_transform_point(&local_p2_1);
70 let contact = TrackedContact::new(
71 local_p1,
72 local_p2,
73 PackedFeatureId::face(fid1),
74 PackedFeatureId::face(fid2),
75 dist,
76 );
77 manifold.points.push(contact);
78
79 manifold.local_n1 = *local_n1;
80 manifold.local_n2 = *local_n2;
81 } else {
82 return;
84 }
85
86 if let (Some(dir1), Some(dir2)) = (seg1.direction(), seg2_1.direction()) {
87 if dir1.dot(&dir2).abs() >= crate::utils::COS_FRAC_PI_8
88 && dir1.dot(&local_n1).abs() < crate::utils::SIN_FRAC_PI_8
89 {
90 if let Some((clip_a, clip_b)) = crate::query::details::clip_segment_segment_with_normal(
93 (seg1.a, seg1.b),
94 (seg2_1.a, seg2_1.b),
95 *local_n1,
96 ) {
97 let contact =
98 if (clip_a.0 - local_p1).norm_squared() > Real::default_epsilon() * 100.0 {
99 TrackedContact::new(
101 clip_a.0,
102 pos12.inverse_transform_point(&clip_a.1),
103 PackedFeatureId::face(clip_a.2 as u32),
104 PackedFeatureId::face(clip_a.3 as u32),
105 (clip_a.1 - clip_a.0).dot(&local_n1),
106 )
107 } else {
108 TrackedContact::new(
110 clip_b.0,
111 pos12.inverse_transform_point(&clip_b.1),
112 PackedFeatureId::face(clip_b.2 as u32),
113 PackedFeatureId::face(clip_b.3 as u32),
114 (clip_b.1 - clip_b.0).dot(&local_n1),
115 )
116 };
117
118 manifold.points.push(contact);
119 }
120 }
121 }
122
123 for point in &mut manifold.points {
124 point.local_p1 += manifold.local_n1 * capsule1.radius;
125 point.local_p2 += manifold.local_n2 * capsule2.radius;
126 point.dist -= capsule1.radius + capsule2.radius;
127 }
128
129 manifold.match_contacts(&old_manifold_points);
130}
131
132#[cfg(feature = "dim3")]
134pub fn contact_manifold_capsule_capsule<'a, ManifoldData, ContactData>(
135 pos12: &Isometry<Real>,
136 capsule1: &'a Capsule,
137 capsule2: &'a Capsule,
138 prediction: Real,
139 manifold: &mut ContactManifold<ManifoldData, ContactData>,
140) where
141 ContactData: Default + Copy,
142{
143 let seg1 = capsule1.segment;
144 let seg2_1 = capsule2.segment.transformed(pos12);
145 let (loc1, loc2) =
146 crate::query::closest_points::closest_points_segment_segment_with_locations_nD(
147 (&seg1.a, &seg1.b),
148 (&seg2_1.a, &seg2_1.b),
149 );
150
151 let bcoords1 = loc1.barycentric_coordinates();
152 let bcoords2 = loc2.barycentric_coordinates();
153 let local_p1 = seg1.a * bcoords1[0] + seg1.b.coords * bcoords1[1];
154 let local_p2_1 = seg2_1.a * bcoords2[0] + seg2_1.b.coords * bcoords2[1];
155
156 let local_n1 =
157 Unit::try_new(local_p2_1 - local_p1, Real::default_epsilon()).unwrap_or(Vector::y_axis());
158 let dist = (local_p2_1 - local_p1).dot(&local_n1) - capsule1.radius - capsule2.radius;
159
160 if dist <= prediction {
161 let local_n2 = pos12.inverse_transform_unit_vector(&-local_n1);
162 let fid = PackedFeatureId::face(0);
163 let contact = TrackedContact::new(
164 local_p1 + *local_n1 * capsule1.radius,
165 pos12.inverse_transform_point(&local_p2_1) + *local_n2 * capsule2.radius,
166 fid,
167 fid,
168 dist,
169 );
170
171 if !manifold.points.is_empty() {
172 manifold.points[0].copy_geometry_from(contact);
173 } else {
174 manifold.points.push(contact);
175 }
176
177 manifold.local_n1 = *local_n1;
178 manifold.local_n2 = *local_n2;
179 } else {
180 manifold.clear();
181 }
182}