linear_sim/constraint/
contact.rs1use derive_more::From;
2use either::Either;
3
4#[cfg(feature = "derive_serdes")]
5use serde::{Deserialize, Serialize};
6
7use crate::collision::Proximity;
8use crate::object;
9use crate::math::*;
10
11use super::{Constraint, Impulses, PostImpulses};
12
13pub const CONTACT_DISTANCE : f64 = 0.005; pub const RESTING_VELOCITY : f64 = (1.0/120.0) * CONTACT_DISTANCE;
24
25#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
27#[derive(Clone, Debug, PartialEq, From)]
28pub struct Contact {
29 pub normal : Unit3 <f64>,
30 pub distance : f64
31}
32
33impl Contact {
34 #[inline]
35 pub const fn new (normal : Unit3 <f64>, distance : f64) -> Self {
36 Contact { normal, distance }
37 }
38
39 pub(crate) fn constrain_velocity_with_restitution (&self,
40 mut object_a : Either <&object::Static, &mut object::Dynamic>,
41 object_b : &mut object::Dynamic,
42 pseudovelocity_a : Option <Vector3 <f64>>,
43 pseudovelocity_b : Vector3 <f64>,
44 restitution : Normalized <f64>
46 ) -> Option <Impulses> {
47 let velocity_effective_a = match &object_a {
48 Either::Left (_) => {
49 debug_assert!(pseudovelocity_a.is_none());
50 Vector3::zero()
51 }
52 Either::Right (object_a) => object_a.derivatives.velocity +
53 pseudovelocity_a.unwrap_or (Vector3::zero())
54 };
55 let velocity_effective_b = object_b.derivatives.velocity + pseudovelocity_b;
56 let velocity_effective_relative = velocity_effective_a - velocity_effective_b;
57 let velocity_normal_component = velocity_effective_relative.dot (*self.normal);
58 let mut impulses = None;
59 let mut mass_relative_a = 0.0;
60 let mut mass_relative_b = 0.0;
61 if velocity_normal_component < 0.0 {
62 let velocity_normal = velocity_normal_component * *self.normal;
63 let impulse_normal = velocity_normal + *restitution * velocity_normal;
65 let impulse_normal_magnitude2 = impulse_normal.magnitude_squared();
66 if impulse_normal_magnitude2 < RESTING_VELOCITY * RESTING_VELOCITY {
67 return None
68 }
69 let (impulse_normal_a, impulse_normal_b) = match &mut object_a {
70 Either::Right (object_a) => {
71 let mass_combined = object_a.mass + object_b.mass;
72 mass_relative_a = object_a.mass.mass() * mass_combined.mass_reciprocal();
73 mass_relative_b = object_b.mass.mass() * mass_combined.mass_reciprocal();
74 let impulse_normal_a = -mass_relative_b * impulse_normal;
75 let impulse_normal_b = mass_relative_a * impulse_normal;
76 object_a.derivatives.velocity += impulse_normal_a;
77 object_b.derivatives.velocity += impulse_normal_b;
78 (impulse_normal_a, impulse_normal_b)
79 }
80 Either::Left (_) => {
81 object_b.derivatives.velocity += impulse_normal;
82 (Vector3::zero(), impulse_normal)
83 }
84 };
85 let (impulse_tangent_a, impulse_tangent_b) = {
87 let velocity_tangent = velocity_effective_relative - velocity_normal;
88 let velocity_tangent_magnitude2 = velocity_tangent.magnitude_squared();
89 if velocity_tangent_magnitude2 > 0.0 {
90 let friction_a = match &object_a {
91 Either::Left (x) => x.material.friction,
92 Either::Right (x) => x.material.friction
93 };
94 let friction_coefficient = friction_a * object_b.material.friction;
95 let impulse_tangent_potential_magnitude2 =
96 *friction_coefficient * impulse_normal_magnitude2;
97 let impulse_tangent = if impulse_tangent_potential_magnitude2 <
98 velocity_tangent_magnitude2
99 {
100 (impulse_tangent_potential_magnitude2.sqrt()
101 / velocity_tangent_magnitude2.sqrt()) * velocity_tangent
102 } else {
103 velocity_tangent
104 };
105 match object_a {
106 Either::Right (object_a) => {
107 let impulse_tangent_a = -mass_relative_b * impulse_tangent;
108 let impulse_tangent_b = mass_relative_a * impulse_tangent;
109 object_a.derivatives.velocity += impulse_tangent_a;
110 object_b.derivatives.velocity += impulse_tangent_b;
111 (impulse_tangent_a, impulse_tangent_b)
112 }
113 Either::Left (_) => {
114 object_b.derivatives.velocity += impulse_tangent;
115 (Vector3::zero(), impulse_tangent)
116 }
117 }
118 } else {
119 (Vector3::zero(), Vector3::zero())
120 }
121 };
122 impulses = Some (Impulses {
123 impulse_normal_a, impulse_normal_b,
124 impulse_tangent_a, impulse_tangent_b
125 });
126 }
127 impulses
128 }
129}
130
131impl Constraint for Contact {
132 fn constrain_velocity (&self,
134 object_a : Either <&object::Static, &mut object::Dynamic>,
135 object_b : &mut object::Dynamic,
136 pseudovelocity_a : Option <Vector3 <f64>>,
137 pseudovelocity_b : Vector3 <f64>
138 ) -> Option <Impulses> {
139 self.constrain_velocity_with_restitution (
140 object_a, object_b, pseudovelocity_a, pseudovelocity_b, Normalized::zero())
141 }
142
143 fn constrain_position (&mut self,
146 t_reverse : f64,
147 mut object_a : Either <&object::Static, &mut object::Dynamic>,
148 object_b : &mut object::Dynamic,
149 pseudovelocity_a : Option <&mut Vector3 <f64>>,
150 pseudovelocity_b : &mut Vector3 <f64>
151 ) -> Option <PostImpulses> {
152 let t_forward = -t_reverse;
153 let mut impulses = None;
154 let mut proximity = match &object_a {
155 Either::Left (object_a) => Proximity::query (*object_a, object_b),
156 Either::Right (object_a) => Proximity::query (*object_a, object_b)
157 };
158 log::trace!(distance=proximity.distance; "post impulse contact @ T==1.0");
159 if cfg!(debug_assertions) {
160 approx::assert_abs_diff_eq!(proximity.normal.cross (proximity.half_axis),
161 Vector3::zero());
162 }
163 let normal = proximity.normal;
164 if proximity.distance < 0.0 {
165 let a_to_b = -proximity.distance * *proximity.normal;
166 let pseudo_impulse = (a_to_b + 0.5 * CONTACT_DISTANCE * *proximity.normal)
167 / t_forward;
168 let (pseudo_impulse_a, pseudo_impulse_b) = match &mut object_a {
169 Either::Left (object_a) => {
170 object::lerp (object_b, *pseudovelocity_b, t_reverse);
171 let pseudo_impulse_b = -pseudo_impulse;
172 *pseudovelocity_b += pseudo_impulse_b;
173 object::lerp (object_b, *pseudovelocity_b, t_forward);
174 proximity = Proximity::query (*object_a, object_b);
175 (Vector3::zero(), pseudo_impulse_b)
176 }
177 Either::Right (object_a) => {
178 let pseudovelocity_a = pseudovelocity_a.unwrap();
179 object::lerp (*object_a, *pseudovelocity_a, t_reverse);
180 object::lerp (object_b, *pseudovelocity_b, t_reverse);
181 let mass_combined = object_a.mass + object_b.mass;
182 let mass_relative_a = object_a.mass.mass() * mass_combined.mass_reciprocal();
183 let mass_relative_b = object_b.mass.mass() * mass_combined.mass_reciprocal();
184 let pseudo_impulse_a = mass_relative_b * pseudo_impulse;
185 let pseudo_impulse_b = -mass_relative_a * pseudo_impulse;
186 *pseudovelocity_a += pseudo_impulse_a;
187 *pseudovelocity_b += pseudo_impulse_b;
188 object::lerp (*object_a, *pseudovelocity_a, t_forward);
189 object::lerp (object_b, *pseudovelocity_b, t_forward);
190 proximity = Proximity::query (*object_a, object_b);
191 (pseudo_impulse_a, pseudo_impulse_b)
192 }
193 };
194 log::trace!(distance=proximity.distance; "position corrected distance @ T==1.0");
195 debug_assert!(proximity.distance >= 0.0);
196 if cfg!(debug_assertions) {
197 approx::assert_relative_eq!(*proximity.normal, *normal, epsilon=0.000_000_1);
198 approx::assert_abs_diff_eq!(
199 proximity.distance, 0.5 * CONTACT_DISTANCE, epsilon=0.000_000_001);
200 }
201 impulses = Some (PostImpulses { pseudo_impulse_a, pseudo_impulse_b });
202 }
203 *self = Contact { normal: proximity.normal, distance: proximity.distance };
204 impulses
205 }
206}
207
208impl Eq for Contact { }
209#[expect(clippy::non_canonical_partial_ord_impl)]
210impl PartialOrd for Contact {
211 fn partial_cmp (&self, _rhs : &Self) -> Option <std::cmp::Ordering> {
214 unreachable!()
216 }
217}
218impl Ord for Contact {
219 fn cmp (&self, _other : &Self) -> std::cmp::Ordering {
220 unreachable!()
222 }
223}
224
225impl TryFrom <Proximity> for Contact {
226 type Error = ();
227 fn try_from (Proximity { distance, normal, .. } : Proximity)
228 -> Result <Self, Self::Error>
229 {
230 if distance >= 0.0 && distance < CONTACT_DISTANCE {
231 Ok (Contact { normal, distance })
232 } else {
233 Err (())
234 }
235 }
236}