1use na::{DVector, RealField, Unit};
2use std::ops::Range;
3
4use crate::joint::JointConstraint;
5use crate::math::{AngularVector, Point};
6use crate::object::{BodyHandle, BodyPartHandle, BodySet};
7use crate::solver::helper;
8use crate::solver::{
9 GenericNonlinearConstraint, IntegrationParameters, LinearConstraints,
10 NonlinearConstraintGenerator,
11};
12
13pub struct PlanarConstraint<N: RealField + Copy, Handle: BodyHandle> {
17 b1: BodyPartHandle<Handle>,
18 b2: BodyPartHandle<Handle>,
19 anchor1: Point<N>,
20 anchor2: Point<N>,
21 axis1: Unit<AngularVector<N>>,
22 axis2: Unit<AngularVector<N>>,
23 lin_impulse: N,
24 ang_impulses: [N; 2],
25 break_torque_squared: N,
26 break_force_squared: N,
27 broken: bool,
28 bilateral_ground_rng: Range<usize>,
29 bilateral_rng: Range<usize>,
30}
31
32impl<N: RealField + Copy, Handle: BodyHandle> PlanarConstraint<N, Handle> {
33 pub fn new(
37 b1: BodyPartHandle<Handle>,
38 b2: BodyPartHandle<Handle>,
39 anchor1: Point<N>,
40 axis1: Unit<AngularVector<N>>,
41 anchor2: Point<N>,
42 axis2: Unit<AngularVector<N>>,
43 ) -> Self {
44 PlanarConstraint {
45 b1,
46 b2,
47 anchor1,
48 anchor2,
49 axis1,
50 axis2,
51 lin_impulse: N::zero(),
52 ang_impulses: [N::zero(), N::zero()],
53 break_force_squared: N::max_value(),
54 break_torque_squared: N::max_value(),
55 broken: false,
56 bilateral_ground_rng: 0..0,
57 bilateral_rng: 0..0,
58 }
59 }
60
61 pub fn set_break_force(&mut self, break_force: N) {
63 self.break_force_squared = break_force * break_force;
64 }
65
66 pub fn set_break_torque(&mut self, break_torque: N) {
68 self.break_torque_squared = break_torque * break_torque;
69 }
70}
71
72impl<N: RealField + Copy, Handle: BodyHandle> JointConstraint<N, Handle> for PlanarConstraint<N, Handle> {
73 fn is_broken(&self) -> bool {
74 self.broken
75 }
76
77 fn num_velocity_constraints(&self) -> usize {
78 3
79 }
80
81 fn anchors(&self) -> (BodyPartHandle<Handle>, BodyPartHandle<Handle>) {
82 (self.b1, self.b2)
83 }
84
85 fn velocity_constraints(
86 &mut self,
87 _: &IntegrationParameters<N>,
88 bodies: &dyn BodySet<N, Handle = Handle>,
89 ext_vels: &DVector<N>,
90 ground_j_id: &mut usize,
91 j_id: &mut usize,
92 jacobians: &mut [N],
93 constraints: &mut LinearConstraints<N, usize>,
94 ) {
95 let body1 = try_ret!(bodies.get(self.b1.0));
96 let body2 = try_ret!(bodies.get(self.b2.0));
97 let part1 = try_ret!(body1.part(self.b1.1));
98 let part2 = try_ret!(body2.part(self.b2.1));
99
100 let pos1 = body1.position_at_material_point(part1, &self.anchor1);
106 let pos2 = body2.position_at_material_point(part2, &self.anchor2);
107
108 let anchor1 = Point::from(pos1.translation.vector);
109 let anchor2 = Point::from(pos2.translation.vector);
110
111 let assembly_id1 = body1.companion_id();
112 let assembly_id2 = body2.companion_id();
113
114 let first_bilateral_ground = constraints.bilateral_ground.len();
115 let first_bilateral = constraints.bilateral.len();
116
117 let axis1 = pos1 * self.axis1;
118
119 helper::cancel_relative_linear_velocity_wrt_axis(
120 body1,
121 part1,
122 self.b1,
123 body2,
124 part2,
125 self.b2,
126 assembly_id1,
127 assembly_id2,
128 &anchor1,
129 &anchor2,
130 &axis1,
131 ext_vels,
132 self.lin_impulse,
133 0,
134 ground_j_id,
135 j_id,
136 jacobians,
137 constraints,
138 );
139
140 helper::restrict_relative_angular_velocity_to_axis(
141 body1,
142 part1,
143 self.b1,
144 body2,
145 part2,
146 self.b2,
147 assembly_id1,
148 assembly_id2,
149 &axis1,
150 &anchor1,
151 &anchor2,
152 ext_vels,
153 &self.ang_impulses[..],
154 1,
155 ground_j_id,
156 j_id,
157 jacobians,
158 constraints,
159 );
160
161 self.bilateral_ground_rng = first_bilateral_ground..constraints.bilateral_ground.len();
168 self.bilateral_rng = first_bilateral..constraints.bilateral.len();
169 }
170
171 fn cache_impulses(&mut self, constraints: &LinearConstraints<N, usize>, inv_dt: N) {
172 for c in &constraints.bilateral_ground[self.bilateral_ground_rng.clone()] {
173 if c.impulse_id == 0 {
174 self.lin_impulse = c.impulse
175 } else {
176 self.ang_impulses[c.impulse_id - 1] = c.impulse;
177 }
178 }
179
180 for c in &constraints.bilateral[self.bilateral_rng.clone()] {
181 if c.impulse_id == 0 {
182 self.lin_impulse = c.impulse
183 } else {
184 self.ang_impulses[c.impulse_id - 1] = c.impulse;
185 }
186 }
187
188 let inv_dt2 = inv_dt * inv_dt;
189
190 if self.lin_impulse * self.lin_impulse * inv_dt2 > self.break_force_squared
191 || self.ang_impulses[0] * self.ang_impulses[0] * inv_dt2
192 + self.ang_impulses[1] * self.ang_impulses[1] * inv_dt2
193 > self.break_torque_squared
194 {
195 self.broken = true;
196 }
197 }
198}
199
200impl<N: RealField + Copy, Handle: BodyHandle> NonlinearConstraintGenerator<N, Handle>
201 for PlanarConstraint<N, Handle>
202{
203 fn num_position_constraints(&self, bodies: &dyn BodySet<N, Handle = Handle>) -> usize {
204 if self.is_active(bodies) {
206 2
207 } else {
208 0
209 }
210 }
211
212 fn position_constraint(
213 &self,
214 parameters: &IntegrationParameters<N>,
215 i: usize,
216 bodies: &mut dyn BodySet<N, Handle = Handle>,
217 jacobians: &mut [N],
218 ) -> Option<GenericNonlinearConstraint<N, Handle>> {
219 let body1 = bodies.get(self.b1.0)?;
220 let body2 = bodies.get(self.b2.0)?;
221 let part1 = body1.part(self.b1.1)?;
222 let part2 = body2.part(self.b2.1)?;
223
224 let pos1 = body1.position_at_material_point(part1, &self.anchor1);
225 let pos2 = body2.position_at_material_point(part2, &self.anchor2);
226
227 let anchor1 = Point::from(pos1.translation.vector);
228 let anchor2 = Point::from(pos2.translation.vector);
229
230 let axis1 = pos1 * self.axis1;
231
232 if i == 0 {
233 return helper::cancel_relative_translation_wrt_axis(
234 parameters, body1, part1, self.b1, body2, part2, self.b2, &anchor1, &anchor2,
235 &axis1, jacobians,
236 );
237 }
238
239 if i == 1 {
240 let axis2 = pos2 * self.axis2;
241
242 return helper::align_axis(
243 parameters, body1, part1, self.b1, body2, part2, self.b2, &anchor1, &anchor2,
244 &axis1, &axis2, jacobians,
245 );
246 }
247
248 None
249 }
250}