nphysics3d/joint/
planar_constraint.rs

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
13/// A constraint that removes one relative translational degree of freedom, and all but one rotational degrees of freedom.
14///
15/// This ensures a body moves only on a plane wrt. its parent.
16pub 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    /// Create a new planar constraint which ensures the two provided axii always coincide.
34    ///
35    /// All anchros and axii are expressed in their corresponding body part local coordinate frame.
36    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    /// The maximum force this joint can absorb before breaking.
62    pub fn set_break_force(&mut self, break_force: N) {
63        self.break_force_squared = break_force * break_force;
64    }
65
66    /// The maximum torque this joint can absorb before breaking.
67    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        /*
101         *
102         * Joint constraints.
103         *
104         */
105        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        /*
162         *
163         * Limit constraints.
164         *
165         */
166
167        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        // FIXME: calling this at each iteration of the non-linear resolution is costly.
205        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}