1use na::{DVector, RealField, Unit};
2use std::ops::Range;
3
4use crate::joint::{unit_constraint, JointConstraint};
5use crate::math::{AngularVector, Point, Vector, DIM, SPATIAL_DIM};
6use crate::object::{BodyHandle, BodyPartHandle, BodySet};
7use crate::solver::helper;
8use crate::solver::{
9 GenericNonlinearConstraint, IntegrationParameters, LinearConstraints,
10 NonlinearConstraintGenerator,
11};
12
13pub struct PrismaticConstraint<N: RealField + Copy, Handle: BodyHandle> {
15 b1: BodyPartHandle<Handle>,
16 b2: BodyPartHandle<Handle>,
17 anchor1: Point<N>,
18 anchor2: Point<N>,
19 axis1: Unit<Vector<N>>,
20 lin_impulses: Vector<N>,
21 ang_impulses: AngularVector<N>,
22 break_torque_squared: N,
23 break_force_squared: N,
24 broken: bool,
25 limit_impulse: N,
26 bilateral_ground_rng: Range<usize>,
27 bilateral_rng: Range<usize>,
28
29 min_offset: Option<N>,
30 max_offset: Option<N>,
31}
32
33impl<N: RealField + Copy, Handle: BodyHandle> PrismaticConstraint<N, Handle> {
34 pub fn new(
38 b1: BodyPartHandle<Handle>,
39 b2: BodyPartHandle<Handle>,
40 anchor1: Point<N>,
41 axis1: Unit<Vector<N>>,
42 anchor2: Point<N>,
43 ) -> Self {
44 let min_offset = None;
45 let max_offset = None;
46
47 PrismaticConstraint {
48 b1,
49 b2,
50 anchor1,
51 anchor2,
52 axis1,
53 lin_impulses: Vector::zeros(),
54 ang_impulses: AngularVector::zeros(),
55 break_force_squared: N::max_value(),
56 break_torque_squared: N::max_value(),
57 broken: false,
58 limit_impulse: N::zero(),
59 bilateral_ground_rng: 0..0,
60 bilateral_rng: 0..0,
61 min_offset,
62 max_offset,
63 }
64 }
65
66 pub fn set_break_force(&mut self, break_force: N) {
68 self.break_force_squared = break_force * break_force;
69 }
70
71 pub fn set_break_torque(&mut self, break_torque: N) {
73 self.break_torque_squared = break_torque * break_torque;
74 }
75
76 pub fn min_offset(&self) -> Option<N> {
78 self.min_offset
79 }
80
81 pub fn max_offset(&self) -> Option<N> {
83 self.max_offset
84 }
85
86 pub fn disable_min_offset(&mut self) {
88 self.min_offset = None;
89 }
90
91 pub fn disable_max_offset(&mut self) {
93 self.max_offset = None;
94 }
95
96 pub fn enable_min_offset(&mut self, limit: N) {
98 self.min_offset = Some(limit);
99 self.assert_limits();
100 }
101
102 pub fn enable_max_offset(&mut self, limit: N) {
104 self.max_offset = Some(limit);
105 self.assert_limits();
106 }
107
108 fn assert_limits(&self) {
109 if let (Some(min_offset), Some(max_offset)) = (self.min_offset, self.max_offset) {
110 assert!(
111 min_offset <= max_offset,
112 "RevoluteJoint constraint limits: the min angle must be larger than (or equal to) the max angle.");
113 }
114 }
115}
116
117impl<N: RealField + Copy, Handle: BodyHandle> JointConstraint<N, Handle>
118 for PrismaticConstraint<N, Handle>
119{
120 fn is_broken(&self) -> bool {
121 self.broken
122 }
123
124 fn num_velocity_constraints(&self) -> usize {
125 (SPATIAL_DIM - 1) + 2
126 }
127
128 fn anchors(&self) -> (BodyPartHandle<Handle>, BodyPartHandle<Handle>) {
129 (self.b1, self.b2)
130 }
131
132 fn velocity_constraints(
133 &mut self,
134 _: &IntegrationParameters<N>,
135 bodies: &dyn BodySet<N, Handle = Handle>,
136 ext_vels: &DVector<N>,
137 ground_j_id: &mut usize,
138 j_id: &mut usize,
139 jacobians: &mut [N],
140 constraints: &mut LinearConstraints<N, usize>,
141 ) {
142 let body1 = try_ret!(bodies.get(self.b1.0));
143 let body2 = try_ret!(bodies.get(self.b2.0));
144 let part1 = try_ret!(body1.part(self.b1.1));
145 let part2 = try_ret!(body2.part(self.b2.1));
146
147 let pos1 = body1.position_at_material_point(part1, &self.anchor1);
153 let pos2 = body2.position_at_material_point(part2, &self.anchor2);
154
155 let anchor1 = Point::from(pos1.translation.vector);
156 let anchor2 = Point::from(pos2.translation.vector);
157
158 let assembly_id1 = body1.companion_id();
159 let assembly_id2 = body2.companion_id();
160
161 let first_bilateral_ground = constraints.bilateral_ground.len();
162 let first_bilateral = constraints.bilateral.len();
163
164 let axis = pos1 * self.axis1;
165
166 helper::restrict_relative_linear_velocity_to_axis(
167 body1,
168 part1,
169 self.b1,
170 body2,
171 part2,
172 self.b2,
173 assembly_id1,
174 assembly_id2,
175 &anchor1,
176 &anchor2,
177 &axis,
178 ext_vels,
179 self.lin_impulses.as_slice(),
180 0,
181 ground_j_id,
182 j_id,
183 jacobians,
184 constraints,
185 );
186
187 helper::cancel_relative_angular_velocity(
188 body1,
189 part1,
190 self.b1,
191 body2,
192 part2,
193 self.b2,
194 assembly_id1,
195 assembly_id2,
196 &anchor1,
197 &anchor2,
198 ext_vels,
199 &self.ang_impulses,
200 DIM - 1,
201 ground_j_id,
202 j_id,
203 jacobians,
204 constraints,
205 );
206
207 unit_constraint::build_linear_limits_velocity_constraint(
213 body1,
214 part1,
215 self.b1,
216 body2,
217 part2,
218 self.b2,
219 assembly_id1,
220 assembly_id2,
221 &anchor1,
222 &anchor2,
223 &axis,
224 self.min_offset,
225 self.max_offset,
226 ext_vels,
227 self.limit_impulse,
228 SPATIAL_DIM - 1,
229 ground_j_id,
230 j_id,
231 jacobians,
232 constraints,
233 );
234
235 self.bilateral_ground_rng = first_bilateral_ground..constraints.bilateral_ground.len();
236 self.bilateral_rng = first_bilateral..constraints.bilateral.len();
237 }
238
239 fn cache_impulses(&mut self, constraints: &LinearConstraints<N, usize>, inv_dt: N) {
240 for c in &constraints.bilateral_ground[self.bilateral_ground_rng.clone()] {
241 if c.impulse_id < DIM - 1 {
242 self.lin_impulses[c.impulse_id] = c.impulse;
243 } else if c.impulse_id < SPATIAL_DIM - 1 {
244 self.ang_impulses[c.impulse_id + 1 - DIM] = c.impulse;
245 } else {
246 self.limit_impulse = c.impulse
247 }
248 }
249
250 for c in &constraints.bilateral[self.bilateral_rng.clone()] {
251 if c.impulse_id < DIM - 1 {
252 self.lin_impulses[c.impulse_id] = c.impulse;
253 } else if c.impulse_id < SPATIAL_DIM - 1 {
254 self.ang_impulses[c.impulse_id + 1 - DIM] = c.impulse;
255 } else {
256 self.limit_impulse = c.impulse
257 }
258 }
259
260 let inv_dt2 = inv_dt * inv_dt;
261
262 if self.lin_impulses.norm_squared() * inv_dt2 > self.break_force_squared
263 || self.ang_impulses.norm_squared() * inv_dt2 > self.break_torque_squared
264 {
265 self.broken = true;
266 }
267 }
268}
269
270impl<N: RealField + Copy, Handle: BodyHandle> NonlinearConstraintGenerator<N, Handle>
271 for PrismaticConstraint<N, Handle>
272{
273 fn num_position_constraints(&self, bodies: &dyn BodySet<N, Handle = Handle>) -> usize {
274 if self.is_active(bodies) {
276 if self.min_offset.is_some() || self.max_offset.is_some() {
277 3
278 } else {
279 2
280 }
281 } else {
282 0
283 }
284 }
285
286 fn position_constraint(
287 &self,
288 parameters: &IntegrationParameters<N>,
289 i: usize,
290 bodies: &mut dyn BodySet<N, Handle = Handle>,
291 jacobians: &mut [N],
292 ) -> Option<GenericNonlinearConstraint<N, Handle>> {
293 let body1 = bodies.get(self.b1.0)?;
294 let body2 = bodies.get(self.b2.0)?;
295 let part1 = body1.part(self.b1.1)?;
296 let part2 = body2.part(self.b2.1)?;
297
298 let pos1 = body1.position_at_material_point(part1, &self.anchor1);
299 let pos2 = body2.position_at_material_point(part2, &self.anchor2);
300
301 let anchor1 = Point::from(pos1.translation.vector);
302 let anchor2 = Point::from(pos2.translation.vector);
303
304 if i == 0 {
305 return helper::cancel_relative_rotation(
306 parameters,
307 body1,
308 part1,
309 self.b1,
310 body2,
311 part2,
312 self.b2,
313 &anchor1,
314 &anchor2,
315 &pos1.rotation,
316 &pos2.rotation,
317 jacobians,
318 );
319 } else if i == 1 {
320 let axis = pos1 * self.axis1;
321
322 return helper::project_anchor_to_axis(
323 parameters, body1, part1, self.b1, body2, part2, self.b2, &anchor1, &anchor2,
324 &axis, jacobians,
325 );
326 } else if i == 2 {
327 let axis = pos1 * self.axis1;
328
329 return unit_constraint::build_linear_limits_position_constraint(
330 parameters,
331 body1,
332 part1,
333 self.b1,
334 body2,
335 part2,
336 self.b2,
337 &anchor1,
338 &anchor2,
339 &axis,
340 self.min_offset,
341 self.max_offset,
342 jacobians,
343 );
344 }
345
346 None
347 }
348}