1use crate::{
2 dynamics::joints::{EntityConstraint, JointSystems},
3 prelude::*,
4};
5use bevy::{
6 ecs::{
7 entity::{EntityMapper, MapEntities},
8 reflect::ReflectMapEntities,
9 },
10 prelude::*,
11};
12
13#[doc = include_str!("./images/prismatic_joint.svg")]
22#[derive(Component, Clone, Debug, PartialEq, Reflect)]
23#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
25#[reflect(Component, Debug, MapEntities, PartialEq)]
26#[doc(alias = "SliderJoint")]
27pub struct PrismaticJoint {
28 pub body1: Entity,
30 pub body2: Entity,
32 pub frame1: JointFrame,
35 pub frame2: JointFrame,
38 pub slider_axis: Vector,
42 pub limits: Option<DistanceLimit>,
44 pub align_compliance: Scalar,
46 pub angle_compliance: Scalar,
48 pub limit_compliance: Scalar,
50}
51
52impl EntityConstraint<2> for PrismaticJoint {
53 fn entities(&self) -> [Entity; 2] {
54 [self.body1, self.body2]
55 }
56}
57
58impl PrismaticJoint {
59 pub const DEFAULT_SLIDER_AXIS: Vector = Vector::X;
61
62 #[inline]
64 pub const fn new(body1: Entity, body2: Entity) -> Self {
65 Self {
66 body1,
67 body2,
68 frame1: JointFrame::IDENTITY,
69 frame2: JointFrame::IDENTITY,
70 slider_axis: Self::DEFAULT_SLIDER_AXIS,
71 limits: None,
72 align_compliance: 0.0,
73 angle_compliance: 0.0,
74 limit_compliance: 0.0,
75 }
76 }
77
78 #[inline]
82 pub const fn with_slider_axis(mut self, axis: Vector) -> Self {
83 self.slider_axis = axis;
84 self
85 }
86
87 #[inline]
93 #[deprecated(since = "0.4.0", note = "Use `with_slider_axis` instead.")]
94 pub const fn with_free_axis(self, axis: Vector) -> Self {
95 self.with_slider_axis(axis)
96 }
97
98 #[inline]
100 pub fn with_local_frame1(mut self, frame: impl Into<Isometry>) -> Self {
101 self.frame1 = JointFrame::local(frame);
102 self
103 }
104
105 #[inline]
107 pub fn with_local_frame2(mut self, frame: impl Into<Isometry>) -> Self {
108 self.frame2 = JointFrame::local(frame);
109 self
110 }
111
112 #[inline]
116 pub const fn with_anchor(mut self, anchor: Vector) -> Self {
117 self.frame1.anchor = JointAnchor::FromGlobal(anchor);
118 self.frame2.anchor = JointAnchor::FromGlobal(anchor);
119 self
120 }
121
122 #[inline]
126 pub const fn with_local_anchor1(mut self, anchor: Vector) -> Self {
127 self.frame1.anchor = JointAnchor::Local(anchor);
128 self
129 }
130
131 #[inline]
135 pub const fn with_local_anchor2(mut self, anchor: Vector) -> Self {
136 self.frame2.anchor = JointAnchor::Local(anchor);
137 self
138 }
139
140 #[inline]
144 pub fn with_basis(mut self, basis: impl Into<Rot>) -> Self {
145 let basis = basis.into();
146 self.frame1.basis = JointBasis::FromGlobal(basis);
147 self.frame2.basis = JointBasis::FromGlobal(basis);
148 self
149 }
150
151 #[inline]
155 pub fn with_local_basis1(mut self, basis: impl Into<Rot>) -> Self {
156 self.frame1.basis = JointBasis::Local(basis.into());
157 self
158 }
159
160 #[inline]
164 pub fn with_local_basis2(mut self, basis: impl Into<Rot>) -> Self {
165 self.frame2.basis = JointBasis::Local(basis.into());
166 self
167 }
168
169 #[inline]
176 pub fn local_frame1(&self) -> Option<Isometry> {
177 self.frame1.get_local_isometry()
178 }
179
180 #[inline]
187 pub fn local_frame2(&self) -> Option<Isometry> {
188 self.frame2.get_local_isometry()
189 }
190
191 #[inline]
196 pub const fn local_anchor1(&self) -> Option<Vector> {
197 match self.frame1.anchor {
198 JointAnchor::Local(anchor) => Some(anchor),
199 _ => None,
200 }
201 }
202
203 #[inline]
208 pub const fn local_anchor2(&self) -> Option<Vector> {
209 match self.frame2.anchor {
210 JointAnchor::Local(anchor) => Some(anchor),
211 _ => None,
212 }
213 }
214
215 #[inline]
220 pub fn local_basis1(&self) -> Option<Rot> {
221 match self.frame1.basis {
222 JointBasis::Local(basis) => Some(basis),
223 _ => None,
224 }
225 }
226
227 #[inline]
232 pub fn local_basis2(&self) -> Option<Rot> {
233 match self.frame2.basis {
234 JointBasis::Local(basis) => Some(basis),
235 _ => None,
236 }
237 }
238
239 #[inline]
247 pub fn local_slider_axis1(&self) -> Option<Vector> {
248 match self.frame1.basis {
249 JointBasis::Local(basis) => Some(basis * self.slider_axis),
250 _ => None,
251 }
252 }
253
254 #[inline]
262 pub fn local_slider_axis2(&self) -> Option<Vector> {
263 match self.frame2.basis {
264 JointBasis::Local(basis) => Some(basis * self.slider_axis),
265 _ => None,
266 }
267 }
268
269 #[inline]
271 pub const fn with_limits(mut self, min: Scalar, max: Scalar) -> Self {
272 self.limits = Some(DistanceLimit::new(min, max));
273 self
274 }
275
276 #[inline]
278 #[deprecated(
279 since = "0.4.0",
280 note = "Use `with_align_compliance`, `with_limit_compliance`, and `with_angle_compliance` instead."
281 )]
282 pub const fn with_compliance(mut self, compliance: Scalar) -> Self {
283 self.align_compliance = compliance;
284 self.angle_compliance = compliance;
285 self.limit_compliance = compliance;
286 self
287 }
288
289 #[inline]
291 pub const fn with_align_compliance(mut self, compliance: Scalar) -> Self {
292 self.align_compliance = compliance;
293 self
294 }
295
296 #[inline]
298 pub const fn with_angle_compliance(mut self, compliance: Scalar) -> Self {
299 self.angle_compliance = compliance;
300 self
301 }
302
303 #[inline]
305 pub const fn with_limit_compliance(mut self, compliance: Scalar) -> Self {
306 self.limit_compliance = compliance;
307 self
308 }
309}
310
311impl MapEntities for PrismaticJoint {
312 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
313 self.body1 = entity_mapper.get_mapped(self.body1);
314 self.body2 = entity_mapper.get_mapped(self.body2);
315 }
316}
317
318pub(super) fn plugin(app: &mut App) {
319 app.add_systems(
320 PhysicsSchedule,
321 update_local_frames.in_set(JointSystems::PrepareLocalFrames),
322 );
323}
324
325fn update_local_frames(
326 mut joints: Query<&mut PrismaticJoint, Changed<PrismaticJoint>>,
327 bodies: Query<(&Position, &Rotation)>,
328) {
329 for mut joint in &mut joints {
330 if matches!(joint.frame1.anchor, JointAnchor::Local(_))
331 && matches!(joint.frame2.anchor, JointAnchor::Local(_))
332 && matches!(joint.frame1.basis, JointBasis::Local(_))
333 && matches!(joint.frame2.basis, JointBasis::Local(_))
334 {
335 continue;
336 }
337
338 let Ok([(pos1, rot1), (pos2, rot2)]) = bodies.get_many(joint.entities()) else {
339 continue;
340 };
341
342 let [frame1, frame2] =
343 JointFrame::compute_local(joint.frame1, joint.frame2, pos1.0, pos2.0, rot1, rot2);
344 joint.frame1 = frame1;
345 joint.frame2 = frame2;
346 }
347}
348
349#[cfg(feature = "debug-plugin")]
350impl DebugRenderConstraint<2> for PrismaticJoint {
351 type Context = ();
352
353 fn debug_render(
354 &self,
355 positions: [Vector; 2],
356 rotations: [Rotation; 2],
357 _context: &mut Self::Context,
358 gizmos: &mut Gizmos<PhysicsGizmos>,
359 config: &PhysicsGizmos,
360 ) {
361 let [pos1, pos2] = positions;
362 let [rot1, rot2] = rotations;
363
364 let Some(local_anchor1) = self.local_anchor1() else {
365 return;
366 };
367 let Some(local_anchor2) = self.local_anchor2() else {
368 return;
369 };
370
371 let anchor1 = pos1 + rot1 * local_anchor1;
372 let anchor2 = pos2 + rot2 * local_anchor2;
373
374 if let Some(anchor_color) = config.joint_anchor_color {
375 gizmos.draw_line(pos1, anchor1, anchor_color);
376 gizmos.draw_line(pos2, anchor2, anchor_color);
377 }
378
379 if let Some(color) = config.joint_separation_color {
380 gizmos.draw_line(anchor1, anchor2, color);
381 }
382 }
383}