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/point_constraint.svg")]
31#[derive(Component, Clone, Debug, PartialEq, Reflect)]
32#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
34#[reflect(Component, Debug, MapEntities, PartialEq)]
35pub struct FixedJoint {
36 pub body1: Entity,
38 pub body2: Entity,
40 pub frame1: JointFrame,
43 pub frame2: JointFrame,
46 pub point_compliance: Scalar,
48 pub angle_compliance: Scalar,
50}
51
52impl EntityConstraint<2> for FixedJoint {
53 fn entities(&self) -> [Entity; 2] {
54 [self.body1, self.body2]
55 }
56}
57
58impl FixedJoint {
59 #[inline]
61 pub const fn new(body1: Entity, body2: Entity) -> Self {
62 Self {
63 body1,
64 body2,
65 frame1: JointFrame::IDENTITY,
66 frame2: JointFrame::IDENTITY,
67 point_compliance: 0.0,
68 angle_compliance: 0.0,
69 }
70 }
71
72 #[inline]
74 pub fn with_local_frame1(mut self, frame: impl Into<Isometry>) -> Self {
75 self.frame1 = JointFrame::local(frame);
76 self
77 }
78
79 #[inline]
81 pub fn with_local_frame2(mut self, frame: impl Into<Isometry>) -> Self {
82 self.frame2 = JointFrame::local(frame);
83 self
84 }
85
86 #[inline]
90 pub const fn with_anchor(mut self, anchor: Vector) -> Self {
91 self.frame1.anchor = JointAnchor::FromGlobal(anchor);
92 self.frame2.anchor = JointAnchor::FromGlobal(anchor);
93 self
94 }
95
96 #[inline]
100 pub const fn with_local_anchor1(mut self, anchor: Vector) -> Self {
101 self.frame1.anchor = JointAnchor::Local(anchor);
102 self
103 }
104
105 #[inline]
109 pub const fn with_local_anchor2(mut self, anchor: Vector) -> Self {
110 self.frame2.anchor = JointAnchor::Local(anchor);
111 self
112 }
113
114 #[inline]
118 pub fn with_basis(mut self, basis: impl Into<Rot>) -> Self {
119 let basis = basis.into();
120 self.frame1.basis = JointBasis::FromGlobal(basis);
121 self.frame2.basis = JointBasis::FromGlobal(basis);
122 self
123 }
124
125 #[inline]
129 pub fn with_local_basis1(mut self, basis: impl Into<Rot>) -> Self {
130 self.frame1.basis = JointBasis::Local(basis.into());
131 self
132 }
133
134 #[inline]
138 pub fn with_local_basis2(mut self, basis: impl Into<Rot>) -> Self {
139 self.frame2.basis = JointBasis::Local(basis.into());
140 self
141 }
142
143 #[inline]
150 pub fn local_frame1(&self) -> Option<Isometry> {
151 self.frame1.get_local_isometry()
152 }
153
154 #[inline]
161 pub fn local_frame2(&self) -> Option<Isometry> {
162 self.frame2.get_local_isometry()
163 }
164
165 #[inline]
170 pub const fn local_anchor1(&self) -> Option<Vector> {
171 match self.frame1.anchor {
172 JointAnchor::Local(anchor) => Some(anchor),
173 _ => None,
174 }
175 }
176
177 #[inline]
182 pub const fn local_anchor2(&self) -> Option<Vector> {
183 match self.frame2.anchor {
184 JointAnchor::Local(anchor) => Some(anchor),
185 _ => None,
186 }
187 }
188
189 #[inline]
194 pub fn local_basis1(&self) -> Option<Rot> {
195 match self.frame1.basis {
196 JointBasis::Local(basis) => Some(basis),
197 _ => None,
198 }
199 }
200
201 #[inline]
206 pub fn local_basis2(&self) -> Option<Rot> {
207 match self.frame2.basis {
208 JointBasis::Local(basis) => Some(basis),
209 _ => None,
210 }
211 }
212
213 #[inline]
215 #[deprecated(
216 since = "0.4.0",
217 note = "Use `with_point_compliance` and `with_angle_compliance` instead."
218 )]
219 pub const fn with_compliance(mut self, compliance: Scalar) -> Self {
220 self.point_compliance = compliance;
221 self.angle_compliance = compliance;
222 self
223 }
224
225 #[inline]
227 pub const fn with_point_compliance(mut self, compliance: Scalar) -> Self {
228 self.point_compliance = compliance;
229 self
230 }
231
232 #[inline]
234 pub const fn with_angle_compliance(mut self, compliance: Scalar) -> Self {
235 self.angle_compliance = compliance;
236 self
237 }
238}
239
240impl MapEntities for FixedJoint {
241 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
242 self.body1 = entity_mapper.get_mapped(self.body1);
243 self.body2 = entity_mapper.get_mapped(self.body2);
244 }
245}
246
247pub(super) fn plugin(app: &mut App) {
248 app.add_systems(
249 PhysicsSchedule,
250 update_local_frames.in_set(JointSystems::PrepareLocalFrames),
251 );
252}
253
254fn update_local_frames(
255 mut joints: Query<&mut FixedJoint, Changed<FixedJoint>>,
256 bodies: Query<(&Position, &Rotation)>,
257) {
258 for mut joint in &mut joints {
259 if matches!(joint.frame1.anchor, JointAnchor::Local(_))
260 && matches!(joint.frame2.anchor, JointAnchor::Local(_))
261 && matches!(joint.frame1.basis, JointBasis::Local(_))
262 && matches!(joint.frame2.basis, JointBasis::Local(_))
263 {
264 continue;
265 }
266
267 let Ok([(pos1, rot1), (pos2, rot2)]) = bodies.get_many(joint.entities()) else {
268 continue;
269 };
270
271 let [frame1, frame2] =
273 JointFrame::compute_local(joint.frame1, joint.frame2, pos1.0, pos2.0, rot1, rot2);
274 joint.frame1 = frame1;
275 joint.frame2 = frame2;
276 }
277}
278
279#[cfg(feature = "debug-plugin")]
280impl DebugRenderConstraint<2> for FixedJoint {
281 type Context = ();
282
283 fn debug_render(
284 &self,
285 positions: [Vector; 2],
286 rotations: [Rotation; 2],
287 _context: &mut Self::Context,
288 gizmos: &mut Gizmos<PhysicsGizmos>,
289 config: &PhysicsGizmos,
290 ) {
291 let [pos1, pos2] = positions;
292 let [rot1, rot2] = rotations;
293
294 let Some(local_anchor1) = self.local_anchor1() else {
295 return;
296 };
297 let Some(local_anchor2) = self.local_anchor2() else {
298 return;
299 };
300
301 let anchor1 = pos1 + rot1 * local_anchor1;
302 let anchor2 = pos2 + rot2 * local_anchor2;
303
304 if let Some(anchor_color) = config.joint_anchor_color {
305 gizmos.draw_line(pos1, anchor1, anchor_color);
306 gizmos.draw_line(pos2, anchor2, anchor_color);
307 }
308
309 if let Some(color) = config.joint_separation_color {
310 gizmos.draw_line(anchor1, anchor2, color);
311 }
312 }
313}