avian3d/dynamics/solver/joints/
prismatic.rs1use crate::{dynamics::solver::xpbd::*, prelude::*};
4use bevy::{
5 ecs::{
6 entity::{EntityMapper, MapEntities},
7 reflect::ReflectMapEntities,
8 },
9 prelude::*,
10};
11
12#[derive(Component, Clone, Copy, Debug, PartialEq, Reflect)]
16#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
18#[reflect(Debug, Component, MapEntities, PartialEq)]
19pub struct PrismaticJoint {
20 pub entity1: Entity,
22 pub entity2: Entity,
24 pub local_anchor1: Vector,
26 pub local_anchor2: Vector,
28 pub free_axis: Vector,
30 pub free_axis_limits: Option<DistanceLimit>,
32 pub damping_linear: Scalar,
34 pub damping_angular: Scalar,
36 pub position_lagrange: Scalar,
38 pub align_lagrange: Scalar,
40 pub compliance: Scalar,
42 pub force: Vector,
44 pub align_torque: Torque,
46}
47
48impl XpbdConstraint<2> for PrismaticJoint {
49 fn entities(&self) -> [Entity; 2] {
50 [self.entity1, self.entity2]
51 }
52
53 fn clear_lagrange_multipliers(&mut self) {
54 self.position_lagrange = 0.0;
55 self.align_lagrange = 0.0;
56 }
57
58 fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
59 let [body1, body2] = bodies;
60 let compliance = self.compliance;
61
62 let difference = self.get_rotation_difference(&body1.rotation, &body2.rotation);
64 let mut lagrange = self.align_lagrange;
65 self.align_torque =
66 self.align_orientation(body1, body2, difference, &mut lagrange, compliance, dt);
67 self.align_lagrange = lagrange;
68
69 self.force = self.constrain_positions(body1, body2, dt);
71 }
72}
73
74impl Joint for PrismaticJoint {
75 fn new(entity1: Entity, entity2: Entity) -> Self {
76 Self {
77 entity1,
78 entity2,
79 local_anchor1: Vector::ZERO,
80 local_anchor2: Vector::ZERO,
81 free_axis: Vector::X,
82 free_axis_limits: None,
83 damping_linear: 1.0,
84 damping_angular: 1.0,
85 position_lagrange: 0.0,
86 align_lagrange: 0.0,
87 compliance: 0.0,
88 force: Vector::ZERO,
89 #[cfg(feature = "2d")]
90 align_torque: 0.0,
91 #[cfg(feature = "3d")]
92 align_torque: Vector::ZERO,
93 }
94 }
95
96 fn with_compliance(self, compliance: Scalar) -> Self {
97 Self { compliance, ..self }
98 }
99
100 fn with_local_anchor_1(self, anchor: Vector) -> Self {
101 Self {
102 local_anchor1: anchor,
103 ..self
104 }
105 }
106
107 fn with_local_anchor_2(self, anchor: Vector) -> Self {
108 Self {
109 local_anchor2: anchor,
110 ..self
111 }
112 }
113
114 fn with_linear_velocity_damping(self, damping: Scalar) -> Self {
115 Self {
116 damping_linear: damping,
117 ..self
118 }
119 }
120
121 fn with_angular_velocity_damping(self, damping: Scalar) -> Self {
122 Self {
123 damping_angular: damping,
124 ..self
125 }
126 }
127
128 fn local_anchor_1(&self) -> Vector {
129 self.local_anchor1
130 }
131
132 fn local_anchor_2(&self) -> Vector {
133 self.local_anchor2
134 }
135
136 fn damping_linear(&self) -> Scalar {
137 self.damping_linear
138 }
139
140 fn damping_angular(&self) -> Scalar {
141 self.damping_angular
142 }
143}
144
145impl PrismaticJoint {
146 fn constrain_positions(
150 &mut self,
151 body1: &mut RigidBodyQueryItem,
152 body2: &mut RigidBodyQueryItem,
153 dt: Scalar,
154 ) -> Vector {
155 let world_r1 = *body1.rotation * self.local_anchor1;
156 let world_r2 = *body2.rotation * self.local_anchor2;
157
158 let mut delta_x = Vector::ZERO;
159
160 let axis1 = *body1.rotation * self.free_axis;
161 if let Some(limits) = self.free_axis_limits {
162 delta_x += limits.compute_correction_along_axis(
163 body1.current_position() + world_r1,
164 body2.current_position() + world_r2,
165 axis1,
166 );
167 }
168
169 let zero_distance_limit = DistanceLimit::ZERO;
170
171 #[cfg(feature = "2d")]
172 {
173 let axis2 = Vector::new(axis1.y, -axis1.x);
174 delta_x += zero_distance_limit.compute_correction_along_axis(
175 body1.current_position() + world_r1,
176 body2.current_position() + world_r2,
177 axis2,
178 );
179 }
180 #[cfg(feature = "3d")]
181 {
182 let axis2 = axis1.any_orthogonal_vector();
183 let axis3 = axis1.cross(axis2);
184
185 delta_x += zero_distance_limit.compute_correction_along_axis(
186 body1.current_position() + world_r1,
187 body2.current_position() + world_r2,
188 axis2,
189 );
190 delta_x += zero_distance_limit.compute_correction_along_axis(
191 body1.current_position() + world_r1,
192 body2.current_position() + world_r2,
193 axis3,
194 );
195 }
196
197 let magnitude = delta_x.length();
198
199 if magnitude <= Scalar::EPSILON {
200 return Vector::ZERO;
201 }
202
203 let dir = delta_x / magnitude;
204
205 let w1 = PositionConstraint::compute_generalized_inverse_mass(self, body1, world_r1, dir);
207 let w2 = PositionConstraint::compute_generalized_inverse_mass(self, body2, world_r2, dir);
208
209 let delta_lagrange = self.compute_lagrange_update(
211 self.position_lagrange,
212 magnitude,
213 &[w1, w2],
214 self.compliance,
215 dt,
216 );
217 self.position_lagrange += delta_lagrange;
218
219 self.apply_positional_lagrange_update(
221 body1,
222 body2,
223 delta_lagrange,
224 dir,
225 world_r1,
226 world_r2,
227 );
228
229 self.compute_force(self.position_lagrange, dir, dt)
231 }
232
233 pub fn with_free_axis(self, axis: Vector) -> Self {
235 Self {
236 free_axis: axis,
237 ..self
238 }
239 }
240
241 pub fn with_limits(self, min: Scalar, max: Scalar) -> Self {
243 Self {
244 free_axis_limits: Some(DistanceLimit::new(min, max)),
245 ..self
246 }
247 }
248
249 #[cfg(feature = "2d")]
250 fn get_rotation_difference(&self, rot1: &Rotation, rot2: &Rotation) -> Scalar {
251 rot1.angle_between(*rot2)
252 }
253
254 #[cfg(feature = "3d")]
255 fn get_rotation_difference(&self, rot1: &Rotation, rot2: &Rotation) -> Vector {
256 -2.0 * (rot1.0 * rot2.inverse().0).xyz()
259 }
260}
261
262impl PositionConstraint for PrismaticJoint {}
263
264impl AngularConstraint for PrismaticJoint {}
265
266impl MapEntities for PrismaticJoint {
267 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
268 self.entity1 = entity_mapper.get_mapped(self.entity1);
269 self.entity2 = entity_mapper.get_mapped(self.entity2);
270 }
271}