bevy_xpbd_2d/constraints/joints/
revolute.rs1use crate::prelude::*;
4use bevy::{
5 ecs::entity::{EntityMapper, MapEntities},
6 prelude::*,
7};
8
9#[derive(Component, Clone, Copy, Debug, PartialEq)]
13#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
14pub struct RevoluteJoint {
15 pub entity1: Entity,
17 pub entity2: Entity,
19 pub local_anchor1: Vector,
21 pub local_anchor2: Vector,
23 #[cfg(feature = "2d")]
27 pub(crate) aligned_axis: Vector3,
28 #[cfg(feature = "3d")]
30 pub aligned_axis: Vector,
31 pub angle_limit: Option<AngleLimit>,
33 pub damping_linear: Scalar,
35 pub damping_angular: Scalar,
37 pub position_lagrange: Scalar,
39 pub align_lagrange: Scalar,
41 pub angle_limit_lagrange: Scalar,
43 pub compliance: Scalar,
45 pub force: Vector,
47 pub align_torque: Torque,
49 pub angle_limit_torque: Torque,
51}
52
53impl XpbdConstraint<2> for RevoluteJoint {
54 fn entities(&self) -> [Entity; 2] {
55 [self.entity1, self.entity2]
56 }
57
58 fn clear_lagrange_multipliers(&mut self) {
59 self.position_lagrange = 0.0;
60 self.align_lagrange = 0.0;
61 self.angle_limit_lagrange = 0.0;
62 }
63
64 fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) {
65 let [body1, body2] = bodies;
66 let compliance = self.compliance;
67
68 let dq = self.get_delta_q(&body1.rotation, &body2.rotation);
70 let mut lagrange = self.align_lagrange;
71 self.align_torque = self.align_orientation(body1, body2, dq, &mut lagrange, compliance, dt);
72 self.align_lagrange = lagrange;
73
74 let mut lagrange = self.position_lagrange;
76 self.force = self.align_position(
77 body1,
78 body2,
79 self.local_anchor1,
80 self.local_anchor2,
81 &mut lagrange,
82 compliance,
83 dt,
84 );
85 self.position_lagrange = lagrange;
86
87 self.angle_limit_torque = self.apply_angle_limits(body1, body2, dt);
89 }
90}
91
92impl Joint for RevoluteJoint {
93 fn new(entity1: Entity, entity2: Entity) -> Self {
94 Self {
95 entity1,
96 entity2,
97 local_anchor1: Vector::ZERO,
98 local_anchor2: Vector::ZERO,
99 aligned_axis: Vector3::Z,
100 angle_limit: None,
101 damping_linear: 1.0,
102 damping_angular: 1.0,
103 position_lagrange: 0.0,
104 align_lagrange: 0.0,
105 angle_limit_lagrange: 0.0,
106 compliance: 0.0,
107 force: Vector::ZERO,
108 #[cfg(feature = "2d")]
109 align_torque: 0.0,
110 #[cfg(feature = "3d")]
111 align_torque: Vector::ZERO,
112 #[cfg(feature = "2d")]
113 angle_limit_torque: 0.0,
114 #[cfg(feature = "3d")]
115 angle_limit_torque: Vector::ZERO,
116 }
117 }
118
119 fn with_compliance(self, compliance: Scalar) -> Self {
120 Self { compliance, ..self }
121 }
122
123 fn with_local_anchor_1(self, anchor: Vector) -> Self {
124 Self {
125 local_anchor1: anchor,
126 ..self
127 }
128 }
129
130 fn with_local_anchor_2(self, anchor: Vector) -> Self {
131 Self {
132 local_anchor2: anchor,
133 ..self
134 }
135 }
136
137 fn with_linear_velocity_damping(self, damping: Scalar) -> Self {
138 Self {
139 damping_linear: damping,
140 ..self
141 }
142 }
143
144 fn with_angular_velocity_damping(self, damping: Scalar) -> Self {
145 Self {
146 damping_angular: damping,
147 ..self
148 }
149 }
150
151 fn local_anchor_1(&self) -> Vector {
152 self.local_anchor1
153 }
154
155 fn local_anchor_2(&self) -> Vector {
156 self.local_anchor2
157 }
158
159 fn damping_linear(&self) -> Scalar {
160 self.damping_linear
161 }
162
163 fn damping_angular(&self) -> Scalar {
164 self.damping_angular
165 }
166}
167
168impl RevoluteJoint {
169 #[cfg(feature = "3d")]
171 pub fn with_aligned_axis(self, axis: Vector) -> Self {
172 Self {
173 aligned_axis: axis,
174 ..self
175 }
176 }
177
178 pub fn with_angle_limits(self, min: Scalar, max: Scalar) -> Self {
180 Self {
181 angle_limit: Some(AngleLimit::new(min, max)),
182 ..self
183 }
184 }
185
186 fn get_delta_q(&self, rot1: &Rotation, rot2: &Rotation) -> Vector3 {
187 let a1 = rot1.rotate_vec3(self.aligned_axis);
188 let a2 = rot2.rotate_vec3(self.aligned_axis);
189 a1.cross(a2)
190 }
191
192 #[allow(clippy::too_many_arguments)]
194 fn apply_angle_limits(
195 &mut self,
196 body1: &mut RigidBodyQueryItem,
197 body2: &mut RigidBodyQueryItem,
198 dt: Scalar,
199 ) -> Torque {
200 if let Some(angle_limit) = self.angle_limit {
201 let limit_axis = Vector3::new(
202 self.aligned_axis.z,
203 self.aligned_axis.x,
204 self.aligned_axis.y,
205 );
206 let a1 = body1.rotation.rotate_vec3(limit_axis);
207 let a2 = body2.rotation.rotate_vec3(limit_axis);
208 let n = a1.cross(a2).normalize();
209
210 if let Some(dq) = angle_limit.compute_correction(n, a1, a2, PI) {
211 let mut lagrange = self.angle_limit_lagrange;
212 let torque =
213 self.align_orientation(body1, body2, dq, &mut lagrange, self.compliance, dt);
214 self.angle_limit_lagrange = lagrange;
215 return torque;
216 }
217 }
218 Torque::ZERO
219 }
220}
221
222impl PositionConstraint for RevoluteJoint {}
223
224impl AngularConstraint for RevoluteJoint {}
225
226impl MapEntities for RevoluteJoint {
227 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
228 self.entity1 = entity_mapper.map_entity(self.entity1);
229 self.entity2 = entity_mapper.map_entity(self.entity2);
230 }
231}