1use super::*;
11use crate::body::{make_body_id, wake_body};
12use crate::core::NULL_INDEX;
13use crate::id::{BodyId, JointId};
14use crate::math_functions::{
15 abs_float, dot, is_valid_float, is_valid_transform, left_perp, length, relative_angle,
16 rotate_vector, sub, to_relative_transform, transform_point, Transform, Vec2,
17};
18use crate::world::World;
19
20pub fn joint_is_valid(world: &World, id: JointId) -> bool {
23 let joint_index = id.index1 - 1;
24 if joint_index < 0 || world.joints.len() as i32 <= joint_index {
25 return false;
26 }
27
28 let joint = &world.joints[joint_index as usize];
29 if joint.joint_id == NULL_INDEX {
30 return false;
32 }
33
34 debug_assert!(joint.joint_id == joint_index);
35
36 id.generation == joint.generation
37}
38
39pub fn joint_get_type(world: &World, joint_id: JointId) -> JointType {
41 let joint_index = get_joint_full_id(world, joint_id);
42 world.joints[joint_index as usize].type_
43}
44
45pub fn joint_get_body_a(world: &World, joint_id: JointId) -> BodyId {
47 let joint_index = get_joint_full_id(world, joint_id);
48 make_body_id(world, world.joints[joint_index as usize].edges[0].body_id)
49}
50
51pub fn joint_get_body_b(world: &World, joint_id: JointId) -> BodyId {
53 let joint_index = get_joint_full_id(world, joint_id);
54 make_body_id(world, world.joints[joint_index as usize].edges[1].body_id)
55}
56
57pub fn joint_set_local_frame_a(world: &mut World, joint_id: JointId, local_frame: Transform) {
59 crate::recording::record_op(world, |rec, _| {
60 crate::recording::write_joint_xf(
61 rec,
62 crate::recording::OP_JOINT_SET_LOCAL_FRAME_A,
63 joint_id,
64 local_frame,
65 )
66 });
67 debug_assert!(is_valid_transform(local_frame));
68
69 let joint_index = get_joint_full_id(world, joint_id);
70 let joint_sim = get_joint_sim(world, joint_index);
71 joint_sim.local_frame_a = local_frame;
72}
73
74pub fn joint_get_local_frame_a(world: &World, joint_id: JointId) -> Transform {
76 let joint_index = get_joint_full_id(world, joint_id);
77 get_joint_sim_ref(world, joint_index).local_frame_a
78}
79
80pub fn joint_set_local_frame_b(world: &mut World, joint_id: JointId, local_frame: Transform) {
82 crate::recording::record_op(world, |rec, _| {
83 crate::recording::write_joint_xf(
84 rec,
85 crate::recording::OP_JOINT_SET_LOCAL_FRAME_B,
86 joint_id,
87 local_frame,
88 )
89 });
90 debug_assert!(is_valid_transform(local_frame));
91
92 let joint_index = get_joint_full_id(world, joint_id);
93 let joint_sim = get_joint_sim(world, joint_index);
94 joint_sim.local_frame_b = local_frame;
95}
96
97pub fn joint_get_local_frame_b(world: &World, joint_id: JointId) -> Transform {
99 let joint_index = get_joint_full_id(world, joint_id);
100 get_joint_sim_ref(world, joint_index).local_frame_b
101}
102
103pub fn joint_set_collide_connected(world: &mut World, joint_id: JointId, should_collide: bool) {
105 crate::recording::record_op(world, |rec, _| {
106 crate::recording::write_joint_bool(
107 rec,
108 crate::recording::OP_JOINT_SET_COLLIDE_CONNECTED,
109 joint_id,
110 should_collide,
111 )
112 });
113 let joint_index = get_joint_full_id(world, joint_id);
114 if world.joints[joint_index as usize].collide_connected == should_collide {
115 return;
116 }
117
118 world.joints[joint_index as usize].collide_connected = should_collide;
119
120 let body_id_a = world.joints[joint_index as usize].edges[0].body_id;
121 let body_id_b = world.joints[joint_index as usize].edges[1].body_id;
122
123 if should_collide {
124 let shape_count_a = world.bodies[body_id_a as usize].shape_count;
127 let shape_count_b = world.bodies[body_id_b as usize].shape_count;
128
129 let mut shape_id = if shape_count_a < shape_count_b {
130 world.bodies[body_id_a as usize].head_shape_id
131 } else {
132 world.bodies[body_id_b as usize].head_shape_id
133 };
134 while shape_id != NULL_INDEX {
135 let proxy_key = world.shapes[shape_id as usize].proxy_key;
136 if proxy_key != NULL_INDEX {
137 world.broad_phase.buffer_move(proxy_key);
138 }
139
140 shape_id = world.shapes[shape_id as usize].next_shape_id;
141 }
142 } else {
143 destroy_contacts_between_bodies(world, body_id_a, body_id_b);
144 }
145}
146
147pub fn joint_get_collide_connected(world: &World, joint_id: JointId) -> bool {
149 let joint_index = get_joint_full_id(world, joint_id);
150 world.joints[joint_index as usize].collide_connected
151}
152
153pub fn joint_set_user_data(world: &mut World, joint_id: JointId, user_data: u64) {
155 let joint_index = get_joint_full_id(world, joint_id);
156 world.joints[joint_index as usize].user_data = user_data;
157}
158
159pub fn joint_get_user_data(world: &World, joint_id: JointId) -> u64 {
161 let joint_index = get_joint_full_id(world, joint_id);
162 world.joints[joint_index as usize].user_data
163}
164
165pub fn joint_wake_bodies(world: &mut World, joint_id: JointId) {
167 crate::recording::record_op(world, |rec, _| {
168 crate::recording::write_joint_marker(rec, crate::recording::OP_JOINT_WAKE_BODIES, joint_id)
169 });
170 let joint_index = get_joint_full_id(world, joint_id);
171 let body_id_a = world.joints[joint_index as usize].edges[0].body_id;
172 let body_id_b = world.joints[joint_index as usize].edges[1].body_id;
173
174 wake_body(world, body_id_a);
175 wake_body(world, body_id_b);
176}
177
178pub fn joint_get_constraint_force(world: &World, joint_id: JointId) -> Vec2 {
180 let joint_index = get_joint_full_id(world, joint_id);
181 get_joint_constraint_force(world, joint_index)
182}
183
184pub fn joint_get_constraint_torque(world: &World, joint_id: JointId) -> f32 {
186 let joint_index = get_joint_full_id(world, joint_id);
187 get_joint_constraint_torque(world, joint_index)
188}
189
190pub fn joint_get_linear_separation(world: &World, joint_id: JointId) -> f32 {
192 let joint_index = get_joint_full_id(world, joint_id);
193 let base = get_joint_sim_ref(world, joint_index);
194 let joint = &world.joints[joint_index as usize];
195
196 let wxf_a = crate::body::get_body_transform(world, joint.edges[0].body_id);
199 let xf_a = to_relative_transform(wxf_a, wxf_a.p);
200 let xf_b = to_relative_transform(
201 crate::body::get_body_transform(world, joint.edges[1].body_id),
202 wxf_a.p,
203 );
204
205 let p_a = transform_point(xf_a, base.local_frame_a.p);
206 let p_b = transform_point(xf_b, base.local_frame_b.p);
207 let dp = sub(p_b, p_a);
208
209 match &base.payload {
210 JointPayload::Distance(distance_joint) => {
211 let length_ = length(dp);
212 if distance_joint.enable_spring {
213 if distance_joint.enable_limit {
214 if length_ < distance_joint.min_length {
215 return distance_joint.min_length - length_;
216 }
217
218 if length_ > distance_joint.max_length {
219 return length_ - distance_joint.max_length;
220 }
221
222 return 0.0;
223 }
224
225 return 0.0;
226 }
227
228 abs_float(length_ - distance_joint.length)
229 }
230
231 JointPayload::Motor(_) => 0.0,
232
233 JointPayload::Filter => 0.0,
234
235 JointPayload::Prismatic(prismatic_joint) => {
236 let axis_a = rotate_vector(xf_a.q, Vec2 { x: 1.0, y: 0.0 });
237 let perp_a = left_perp(axis_a);
238 let perpendicular_separation = abs_float(dot(perp_a, dp));
239 let mut limit_separation = 0.0;
240
241 if prismatic_joint.enable_limit {
242 let translation = dot(axis_a, dp);
243 if translation < prismatic_joint.lower_translation {
244 limit_separation = prismatic_joint.lower_translation - translation;
245 }
246
247 if prismatic_joint.upper_translation < translation {
248 limit_separation = translation - prismatic_joint.upper_translation;
249 }
250 }
251
252 (perpendicular_separation * perpendicular_separation
253 + limit_separation * limit_separation)
254 .sqrt()
255 }
256
257 JointPayload::Revolute(_) => length(dp),
258
259 JointPayload::Weld(weld_joint) => {
260 if weld_joint.linear_hertz == 0.0 {
261 return length(dp);
262 }
263
264 0.0
265 }
266
267 JointPayload::Wheel(wheel_joint) => {
268 let axis_a = rotate_vector(xf_a.q, Vec2 { x: 1.0, y: 0.0 });
269 let perp_a = left_perp(axis_a);
270 let perpendicular_separation = abs_float(dot(perp_a, dp));
271 let mut limit_separation = 0.0;
272
273 if wheel_joint.enable_limit {
274 let translation = dot(axis_a, dp);
275 if translation < wheel_joint.lower_translation {
276 limit_separation = wheel_joint.lower_translation - translation;
277 }
278
279 if wheel_joint.upper_translation < translation {
280 limit_separation = translation - wheel_joint.upper_translation;
281 }
282 }
283
284 (perpendicular_separation * perpendicular_separation
285 + limit_separation * limit_separation)
286 .sqrt()
287 }
288 }
289}
290
291pub fn joint_get_angular_separation(world: &World, joint_id: JointId) -> f32 {
293 let joint_index = get_joint_full_id(world, joint_id);
294 let base = get_joint_sim_ref(world, joint_index);
295 let joint = &world.joints[joint_index as usize];
296
297 let q_a = crate::body::get_body_transform(world, joint.edges[0].body_id).q;
298 let q_b = crate::body::get_body_transform(world, joint.edges[1].body_id).q;
299 let relative_angle_ = relative_angle(q_a, q_b);
300
301 match &base.payload {
302 JointPayload::Distance(_) => 0.0,
303
304 JointPayload::Motor(_) => 0.0,
305
306 JointPayload::Filter => 0.0,
307
308 JointPayload::Prismatic(_) => relative_angle_,
309
310 JointPayload::Revolute(revolute_joint) => {
311 if revolute_joint.enable_limit {
312 let angle = relative_angle_;
313 if angle < revolute_joint.lower_angle {
314 return revolute_joint.lower_angle - angle;
315 }
316
317 if revolute_joint.upper_angle < angle {
318 return angle - revolute_joint.upper_angle;
319 }
320 }
321
322 0.0
323 }
324
325 JointPayload::Weld(weld_joint) => {
326 if weld_joint.angular_hertz == 0.0 {
327 return relative_angle_;
328 }
329
330 0.0
331 }
332
333 JointPayload::Wheel(_) => 0.0,
334 }
335}
336
337pub fn joint_set_constraint_tuning(
339 world: &mut World,
340 joint_id: JointId,
341 hertz: f32,
342 damping_ratio: f32,
343) {
344 crate::recording::record_op(world, |rec, _| {
345 crate::recording::write_joint_f32_pair(
346 rec,
347 crate::recording::OP_JOINT_SET_CONSTRAINT_TUNING,
348 joint_id,
349 hertz,
350 damping_ratio,
351 )
352 });
353 debug_assert!(is_valid_float(hertz) && hertz >= 0.0);
354 debug_assert!(is_valid_float(damping_ratio) && damping_ratio >= 0.0);
355
356 let joint_index = get_joint_full_id(world, joint_id);
357 let base = get_joint_sim(world, joint_index);
358 base.constraint_hertz = hertz;
359 base.constraint_damping_ratio = damping_ratio;
360}
361
362pub fn joint_get_constraint_tuning(world: &World, joint_id: JointId) -> (f32, f32) {
364 let joint_index = get_joint_full_id(world, joint_id);
365 let base = get_joint_sim_ref(world, joint_index);
366 (base.constraint_hertz, base.constraint_damping_ratio)
367}
368
369pub fn joint_set_force_threshold(world: &mut World, joint_id: JointId, threshold: f32) {
371 crate::recording::record_op(world, |rec, _| {
372 crate::recording::write_joint_f32(
373 rec,
374 crate::recording::OP_JOINT_SET_FORCE_THRESHOLD,
375 joint_id,
376 threshold,
377 )
378 });
379 debug_assert!(is_valid_float(threshold) && threshold >= 0.0);
380
381 let joint_index = get_joint_full_id(world, joint_id);
382 let base = get_joint_sim(world, joint_index);
383 base.force_threshold = threshold;
384}
385
386pub fn joint_get_force_threshold(world: &World, joint_id: JointId) -> f32 {
388 let joint_index = get_joint_full_id(world, joint_id);
389 get_joint_sim_ref(world, joint_index).force_threshold
390}
391
392pub fn joint_set_torque_threshold(world: &mut World, joint_id: JointId, threshold: f32) {
394 crate::recording::record_op(world, |rec, _| {
395 crate::recording::write_joint_f32(
396 rec,
397 crate::recording::OP_JOINT_SET_TORQUE_THRESHOLD,
398 joint_id,
399 threshold,
400 )
401 });
402 debug_assert!(is_valid_float(threshold) && threshold >= 0.0);
403
404 let joint_index = get_joint_full_id(world, joint_id);
405 let base = get_joint_sim(world, joint_index);
406 base.torque_threshold = threshold;
407}
408
409pub fn joint_get_torque_threshold(world: &World, joint_id: JointId) -> f32 {
411 let joint_index = get_joint_full_id(world, joint_id);
412 get_joint_sim_ref(world, joint_index).torque_threshold
413}