1use crate::core::NULL_INDEX;
9use crate::math_functions::{Mat22, Transform, Vec2, MAT22_ZERO, TRANSFORM_IDENTITY, VEC2_ZERO};
10use crate::solver::Softness;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub enum JointType {
15 #[default]
16 Distance,
17 Filter,
18 Motor,
19 Prismatic,
20 Revolute,
21 Weld,
22 Wheel,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct JointEdge {
29 pub body_id: i32,
30 pub prev_key: i32,
31 pub next_key: i32,
32}
33
34impl Default for JointEdge {
35 fn default() -> Self {
36 JointEdge {
37 body_id: NULL_INDEX,
38 prev_key: NULL_INDEX,
39 next_key: NULL_INDEX,
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct Joint {
47 pub user_data: u64,
48
49 pub set_index: i32,
51
52 pub color_index: i32,
55
56 pub local_index: i32,
58
59 pub edges: [JointEdge; 2],
60
61 pub joint_id: i32,
62 pub island_id: i32,
63
64 pub island_index: i32,
67
68 pub draw_scale: f32,
69
70 pub type_: JointType,
71
72 pub generation: u16,
74
75 pub collide_connected: bool,
76}
77
78impl Default for Joint {
79 fn default() -> Self {
80 Joint {
81 user_data: 0,
82 set_index: NULL_INDEX,
83 color_index: NULL_INDEX,
84 local_index: NULL_INDEX,
85 edges: [JointEdge::default(); 2],
86 joint_id: NULL_INDEX,
87 island_id: NULL_INDEX,
88 island_index: NULL_INDEX,
89 draw_scale: 1.0,
90 type_: JointType::Distance,
91 generation: 0,
92 collide_connected: false,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Default)]
99pub struct DistanceJoint {
100 pub length: f32,
101 pub hertz: f32,
102 pub damping_ratio: f32,
103 pub lower_spring_force: f32,
104 pub upper_spring_force: f32,
105 pub min_length: f32,
106 pub max_length: f32,
107
108 pub max_motor_force: f32,
109 pub motor_speed: f32,
110
111 pub impulse: f32,
112 pub lower_impulse: f32,
113 pub upper_impulse: f32,
114 pub motor_impulse: f32,
115
116 pub index_a: i32,
117 pub index_b: i32,
118 pub anchor_a: Vec2,
119 pub anchor_b: Vec2,
120 pub delta_center: Vec2,
121 pub distance_softness: Softness,
122 pub axial_mass: f32,
123
124 pub enable_spring: bool,
125 pub enable_limit: bool,
126 pub enable_motor: bool,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq)]
131pub struct MotorJoint {
132 pub linear_velocity: Vec2,
133 pub max_velocity_force: f32,
134 pub angular_velocity: f32,
135 pub max_velocity_torque: f32,
136 pub linear_hertz: f32,
137 pub linear_damping_ratio: f32,
138 pub max_spring_force: f32,
139 pub angular_hertz: f32,
140 pub angular_damping_ratio: f32,
141 pub max_spring_torque: f32,
142
143 pub linear_velocity_impulse: Vec2,
144 pub angular_velocity_impulse: f32,
145 pub linear_spring_impulse: Vec2,
146 pub angular_spring_impulse: f32,
147
148 pub linear_spring: Softness,
149 pub angular_spring: Softness,
150
151 pub index_a: i32,
152 pub index_b: i32,
153 pub frame_a: Transform,
154 pub frame_b: Transform,
155 pub delta_center: Vec2,
156 pub linear_mass: Mat22,
157 pub angular_mass: f32,
158}
159
160impl Default for MotorJoint {
161 fn default() -> Self {
162 MotorJoint {
163 linear_velocity: VEC2_ZERO,
164 max_velocity_force: 0.0,
165 angular_velocity: 0.0,
166 max_velocity_torque: 0.0,
167 linear_hertz: 0.0,
168 linear_damping_ratio: 0.0,
169 max_spring_force: 0.0,
170 angular_hertz: 0.0,
171 angular_damping_ratio: 0.0,
172 max_spring_torque: 0.0,
173 linear_velocity_impulse: VEC2_ZERO,
174 angular_velocity_impulse: 0.0,
175 linear_spring_impulse: VEC2_ZERO,
176 angular_spring_impulse: 0.0,
177 linear_spring: Softness::default(),
178 angular_spring: Softness::default(),
179 index_a: NULL_INDEX,
180 index_b: NULL_INDEX,
181 frame_a: TRANSFORM_IDENTITY,
182 frame_b: TRANSFORM_IDENTITY,
183 delta_center: VEC2_ZERO,
184 linear_mass: MAT22_ZERO,
185 angular_mass: 0.0,
186 }
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq)]
192pub struct PrismaticJoint {
193 pub impulse: Vec2,
194 pub spring_impulse: f32,
195 pub motor_impulse: f32,
196 pub lower_impulse: f32,
197 pub upper_impulse: f32,
198 pub hertz: f32,
199 pub damping_ratio: f32,
200 pub target_translation: f32,
201 pub max_motor_force: f32,
202 pub motor_speed: f32,
203 pub lower_translation: f32,
204 pub upper_translation: f32,
205
206 pub index_a: i32,
207 pub index_b: i32,
208 pub frame_a: Transform,
209 pub frame_b: Transform,
210 pub delta_center: Vec2,
211 pub spring_softness: Softness,
212
213 pub enable_spring: bool,
214 pub enable_limit: bool,
215 pub enable_motor: bool,
216}
217
218impl Default for PrismaticJoint {
219 fn default() -> Self {
220 PrismaticJoint {
221 impulse: VEC2_ZERO,
222 spring_impulse: 0.0,
223 motor_impulse: 0.0,
224 lower_impulse: 0.0,
225 upper_impulse: 0.0,
226 hertz: 0.0,
227 damping_ratio: 0.0,
228 target_translation: 0.0,
229 max_motor_force: 0.0,
230 motor_speed: 0.0,
231 lower_translation: 0.0,
232 upper_translation: 0.0,
233 index_a: NULL_INDEX,
234 index_b: NULL_INDEX,
235 frame_a: TRANSFORM_IDENTITY,
236 frame_b: TRANSFORM_IDENTITY,
237 delta_center: VEC2_ZERO,
238 spring_softness: Softness::default(),
239 enable_spring: false,
240 enable_limit: false,
241 enable_motor: false,
242 }
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq)]
248pub struct RevoluteJoint {
249 pub linear_impulse: Vec2,
250 pub spring_impulse: f32,
251 pub motor_impulse: f32,
252 pub lower_impulse: f32,
253 pub upper_impulse: f32,
254 pub hertz: f32,
255 pub damping_ratio: f32,
256 pub target_angle: f32,
257 pub max_motor_torque: f32,
258 pub motor_speed: f32,
259 pub lower_angle: f32,
260 pub upper_angle: f32,
261
262 pub index_a: i32,
263 pub index_b: i32,
264 pub frame_a: Transform,
265 pub frame_b: Transform,
266 pub delta_center: Vec2,
267 pub axial_mass: f32,
268 pub spring_softness: Softness,
269
270 pub enable_spring: bool,
271 pub enable_motor: bool,
272 pub enable_limit: bool,
273}
274
275impl Default for RevoluteJoint {
276 fn default() -> Self {
277 RevoluteJoint {
278 linear_impulse: VEC2_ZERO,
279 spring_impulse: 0.0,
280 motor_impulse: 0.0,
281 lower_impulse: 0.0,
282 upper_impulse: 0.0,
283 hertz: 0.0,
284 damping_ratio: 0.0,
285 target_angle: 0.0,
286 max_motor_torque: 0.0,
287 motor_speed: 0.0,
288 lower_angle: 0.0,
289 upper_angle: 0.0,
290 index_a: NULL_INDEX,
291 index_b: NULL_INDEX,
292 frame_a: TRANSFORM_IDENTITY,
293 frame_b: TRANSFORM_IDENTITY,
294 delta_center: VEC2_ZERO,
295 axial_mass: 0.0,
296 spring_softness: Softness::default(),
297 enable_spring: false,
298 enable_motor: false,
299 enable_limit: false,
300 }
301 }
302}
303
304#[derive(Debug, Clone, Copy, PartialEq)]
306pub struct WeldJoint {
307 pub linear_hertz: f32,
308 pub linear_damping_ratio: f32,
309 pub angular_hertz: f32,
310 pub angular_damping_ratio: f32,
311
312 pub linear_spring: Softness,
313 pub angular_spring: Softness,
314 pub linear_impulse: Vec2,
315 pub angular_impulse: f32,
316
317 pub index_a: i32,
318 pub index_b: i32,
319 pub frame_a: Transform,
320 pub frame_b: Transform,
321 pub delta_center: Vec2,
322 pub axial_mass: f32,
323}
324
325impl Default for WeldJoint {
326 fn default() -> Self {
327 WeldJoint {
328 linear_hertz: 0.0,
329 linear_damping_ratio: 0.0,
330 angular_hertz: 0.0,
331 angular_damping_ratio: 0.0,
332 linear_spring: Softness::default(),
333 angular_spring: Softness::default(),
334 linear_impulse: VEC2_ZERO,
335 angular_impulse: 0.0,
336 index_a: NULL_INDEX,
337 index_b: NULL_INDEX,
338 frame_a: TRANSFORM_IDENTITY,
339 frame_b: TRANSFORM_IDENTITY,
340 delta_center: VEC2_ZERO,
341 axial_mass: 0.0,
342 }
343 }
344}
345
346#[derive(Debug, Clone, Copy, PartialEq)]
348pub struct WheelJoint {
349 pub perp_impulse: f32,
350 pub motor_impulse: f32,
351 pub spring_impulse: f32,
352 pub lower_impulse: f32,
353 pub upper_impulse: f32,
354 pub max_motor_torque: f32,
355 pub motor_speed: f32,
356 pub lower_translation: f32,
357 pub upper_translation: f32,
358 pub hertz: f32,
359 pub damping_ratio: f32,
360
361 pub index_a: i32,
362 pub index_b: i32,
363 pub frame_a: Transform,
364 pub frame_b: Transform,
365 pub delta_center: Vec2,
366 pub perp_mass: f32,
367 pub motor_mass: f32,
368 pub axial_mass: f32,
369 pub spring_softness: Softness,
370
371 pub enable_spring: bool,
372 pub enable_motor: bool,
373 pub enable_limit: bool,
374}
375
376impl Default for WheelJoint {
377 fn default() -> Self {
378 WheelJoint {
379 perp_impulse: 0.0,
380 motor_impulse: 0.0,
381 spring_impulse: 0.0,
382 lower_impulse: 0.0,
383 upper_impulse: 0.0,
384 max_motor_torque: 0.0,
385 motor_speed: 0.0,
386 lower_translation: 0.0,
387 upper_translation: 0.0,
388 hertz: 0.0,
389 damping_ratio: 0.0,
390 index_a: NULL_INDEX,
391 index_b: NULL_INDEX,
392 frame_a: TRANSFORM_IDENTITY,
393 frame_b: TRANSFORM_IDENTITY,
394 delta_center: VEC2_ZERO,
395 perp_mass: 0.0,
396 motor_mass: 0.0,
397 axial_mass: 0.0,
398 spring_softness: Softness::default(),
399 enable_spring: false,
400 enable_motor: false,
401 enable_limit: false,
402 }
403 }
404}
405
406#[derive(Debug, Clone, Copy, PartialEq)]
410pub enum JointPayload {
411 Distance(DistanceJoint),
412 Filter,
413 Motor(MotorJoint),
414 Prismatic(PrismaticJoint),
415 Revolute(RevoluteJoint),
416 Weld(WeldJoint),
417 Wheel(WheelJoint),
418}
419
420impl JointPayload {
421 pub fn joint_type(&self) -> JointType {
423 match self {
424 JointPayload::Distance(_) => JointType::Distance,
425 JointPayload::Filter => JointType::Filter,
426 JointPayload::Motor(_) => JointType::Motor,
427 JointPayload::Prismatic(_) => JointType::Prismatic,
428 JointPayload::Revolute(_) => JointType::Revolute,
429 JointPayload::Weld(_) => JointType::Weld,
430 JointPayload::Wheel(_) => JointType::Wheel,
431 }
432 }
433}
434
435#[derive(Debug, Clone, Copy, PartialEq)]
437pub struct JointSim {
438 pub joint_id: i32,
439
440 pub body_id_a: i32,
441 pub body_id_b: i32,
442
443 pub local_frame_a: Transform,
444 pub local_frame_b: Transform,
445
446 pub inv_mass_a: f32,
447 pub inv_mass_b: f32,
448 pub inv_i_a: f32,
449 pub inv_i_b: f32,
450
451 pub constraint_hertz: f32,
452 pub constraint_damping_ratio: f32,
453
454 pub constraint_softness: Softness,
455
456 pub force_threshold: f32,
457 pub torque_threshold: f32,
458
459 pub payload: JointPayload,
461}
462
463impl JointSim {
464 pub fn joint_type(&self) -> JointType {
466 self.payload.joint_type()
467 }
468
469 pub fn distance(&self) -> &DistanceJoint {
471 match &self.payload {
472 JointPayload::Distance(joint) => joint,
473 _ => unreachable!("joint payload is not a distance joint"),
474 }
475 }
476
477 pub fn distance_mut(&mut self) -> &mut DistanceJoint {
478 match &mut self.payload {
479 JointPayload::Distance(joint) => joint,
480 _ => unreachable!("joint payload is not a distance joint"),
481 }
482 }
483
484 pub fn motor(&self) -> &MotorJoint {
486 match &self.payload {
487 JointPayload::Motor(joint) => joint,
488 _ => unreachable!("joint payload is not a motor joint"),
489 }
490 }
491
492 pub fn motor_mut(&mut self) -> &mut MotorJoint {
493 match &mut self.payload {
494 JointPayload::Motor(joint) => joint,
495 _ => unreachable!("joint payload is not a motor joint"),
496 }
497 }
498
499 pub fn prismatic(&self) -> &PrismaticJoint {
501 match &self.payload {
502 JointPayload::Prismatic(joint) => joint,
503 _ => unreachable!("joint payload is not a prismatic joint"),
504 }
505 }
506
507 pub fn prismatic_mut(&mut self) -> &mut PrismaticJoint {
508 match &mut self.payload {
509 JointPayload::Prismatic(joint) => joint,
510 _ => unreachable!("joint payload is not a prismatic joint"),
511 }
512 }
513
514 pub fn revolute(&self) -> &RevoluteJoint {
516 match &self.payload {
517 JointPayload::Revolute(joint) => joint,
518 _ => unreachable!("joint payload is not a revolute joint"),
519 }
520 }
521
522 pub fn revolute_mut(&mut self) -> &mut RevoluteJoint {
523 match &mut self.payload {
524 JointPayload::Revolute(joint) => joint,
525 _ => unreachable!("joint payload is not a revolute joint"),
526 }
527 }
528
529 pub fn weld(&self) -> &WeldJoint {
531 match &self.payload {
532 JointPayload::Weld(joint) => joint,
533 _ => unreachable!("joint payload is not a weld joint"),
534 }
535 }
536
537 pub fn weld_mut(&mut self) -> &mut WeldJoint {
538 match &mut self.payload {
539 JointPayload::Weld(joint) => joint,
540 _ => unreachable!("joint payload is not a weld joint"),
541 }
542 }
543
544 pub fn wheel(&self) -> &WheelJoint {
546 match &self.payload {
547 JointPayload::Wheel(joint) => joint,
548 _ => unreachable!("joint payload is not a wheel joint"),
549 }
550 }
551
552 pub fn wheel_mut(&mut self) -> &mut WheelJoint {
553 match &mut self.payload {
554 JointPayload::Wheel(joint) => joint,
555 _ => unreachable!("joint payload is not a wheel joint"),
556 }
557 }
558}
559
560mod api;
561mod draw;
562mod lifecycle;
563mod plumbing;
564mod solve;
565
566pub use api::*;
567pub use draw::*;
568pub use lifecycle::*;
569pub use plumbing::*;
570pub use solve::*;
571
572#[cfg(test)]
573mod tests {
574 use super::*;
575 use crate::body::{create_body, destroy_body, get_body_full_id};
576 use crate::broad_phase::update_broad_phase_pairs;
577 use crate::constraint_graph::OVERFLOW_INDEX;
578 use crate::core::NULL_INDEX;
579 use crate::geometry::make_box;
580 use crate::shape::create_polygon_shape;
581 use crate::solver_set::AWAKE_SET;
582 use crate::types::{
583 default_body_def, default_distance_joint_def, default_revolute_joint_def,
584 default_shape_def, default_world_def, BodyType,
585 };
586 use crate::world::World;
587
588 #[test]
592 fn create_and_destroy_joints() {
593 let mut world = World::new(&default_world_def());
594
595 let mut body_def = default_body_def();
596 body_def.type_ = BodyType::Dynamic;
597 let body_a = create_body(&mut world, &body_def);
598 let body_b = create_body(&mut world, &body_def);
599 let a_index = get_body_full_id(&world, body_a);
600 let b_index = get_body_full_id(&world, body_b);
601
602 let box_poly = make_box(0.5, 0.5);
603 let shape_def = default_shape_def();
604 let _sa = create_polygon_shape(&mut world, body_a, &shape_def, &box_poly);
605 let _sb = create_polygon_shape(&mut world, body_b, &shape_def, &box_poly);
606
607 update_broad_phase_pairs(&mut world);
608 assert_eq!(world.contact_id_pool.id_count(), 1);
609
610 assert_ne!(
612 world.bodies[a_index as usize].island_id,
613 world.bodies[b_index as usize].island_id
614 );
615
616 let mut revolute_def = default_revolute_joint_def();
619 revolute_def.base.body_id_a = body_a;
620 revolute_def.base.body_id_b = body_b;
621 let revolute_id = create_revolute_joint(&mut world, &revolute_def);
622
623 assert_eq!(world.contact_id_pool.id_count(), 0);
624 assert_eq!(world.joint_id_pool.id_count(), 1);
625 assert_eq!(world.bodies[a_index as usize].joint_count, 1);
626 assert_eq!(world.bodies[b_index as usize].joint_count, 1);
627 assert_eq!(
628 world.bodies[a_index as usize].island_id,
629 world.bodies[b_index as usize].island_id
630 );
631
632 let raw_revolute = get_joint_full_id(&world, revolute_id);
633 {
634 let joint = &world.joints[raw_revolute as usize];
635 assert_eq!(joint.set_index, AWAKE_SET);
636 assert!(joint.color_index != NULL_INDEX);
637 assert!(joint.island_id != NULL_INDEX);
638 assert_eq!(joint.type_, JointType::Revolute);
639 }
640 assert_eq!(joint_get_type(&world, revolute_id), JointType::Revolute);
641 assert!(!joint_get_collide_connected(&world, revolute_id));
642
643 update_broad_phase_pairs(&mut world);
645 assert_eq!(world.contact_id_pool.id_count(), 0);
646
647 let ground = create_body(&mut world, &default_body_def());
650 let mut distance_def = default_distance_joint_def();
651 distance_def.base.body_id_a = ground;
652 distance_def.base.body_id_b = body_a;
653 distance_def.length = 2.0;
654 let distance_id = create_distance_joint(&mut world, &distance_def);
655
656 assert_eq!(world.joint_id_pool.id_count(), 2);
657 let raw_distance = get_joint_full_id(&world, distance_id);
658 {
659 let joint = &world.joints[raw_distance as usize];
660 assert_eq!(joint.set_index, AWAKE_SET);
661 assert!(joint.color_index != NULL_INDEX && joint.color_index <= OVERFLOW_INDEX);
662 }
663 assert_eq!(
664 crate::distance_joint::distance_joint_get_length(&world, distance_id),
665 2.0
666 );
667 assert_eq!(joint_get_body_a(&world, distance_id), ground);
668 assert_eq!(joint_get_body_b(&world, distance_id), body_a);
669
670 joint_set_collide_connected(&mut world, revolute_id, true);
673 assert!(joint_get_collide_connected(&world, revolute_id));
674 update_broad_phase_pairs(&mut world);
675 assert_eq!(world.contact_id_pool.id_count(), 1);
676
677 destroy_joint(&mut world, revolute_id, true);
679 assert_eq!(world.joint_id_pool.id_count(), 1);
680 assert_eq!(world.bodies[a_index as usize].joint_count, 1); assert_eq!(world.bodies[b_index as usize].joint_count, 0);
682 assert_eq!(world.contact_id_pool.id_count(), 1);
683
684 destroy_body(&mut world, body_a);
686 assert_eq!(world.joint_id_pool.id_count(), 0);
687 assert_eq!(world.contact_id_pool.id_count(), 0);
688
689 world.validate_solver_sets();
690 }
691}