1use crate::body::get_body_transform;
12use crate::id::JointId;
13use crate::joint::{
14 get_joint_full_id, get_joint_sim_check_type, get_joint_sim_check_type_ref, get_joint_sim_ref,
15 JointSim, JointType,
16};
17use crate::math_functions::WorldTransform;
18use crate::math_functions::{
19 add, cross_sv, dot, left_perp, max_float, min_float, mul_sv, rotate_vector, sub, sub_pos,
20 to_relative_transform, transform_point, Vec2, VEC2_ZERO,
21};
22use crate::solver_set::AWAKE_SET;
23use crate::world::World;
24
25mod solve;
26
27pub use solve::*;
28
29pub fn prismatic_joint_enable_spring(world: &mut World, joint_id: JointId, enable_spring: bool) {
31 crate::recording::record_op(world, |rec, _| {
32 crate::recording::write_joint_bool(
33 rec,
34 crate::recording::OP_PRISMATIC_ENABLE_SPRING,
35 joint_id,
36 enable_spring,
37 )
38 });
39 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
40 let prismatic = joint.prismatic_mut();
41 if enable_spring != prismatic.enable_spring {
42 prismatic.enable_spring = enable_spring;
43 prismatic.spring_impulse = 0.0;
44 }
45}
46
47pub fn prismatic_joint_is_spring_enabled(world: &World, joint_id: JointId) -> bool {
49 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
50 joint.prismatic().enable_spring
51}
52
53pub fn prismatic_joint_set_spring_hertz(world: &mut World, joint_id: JointId, hertz: f32) {
55 crate::recording::record_op(world, |rec, _| {
56 crate::recording::write_joint_f32(
57 rec,
58 crate::recording::OP_PRISMATIC_SET_SPRING_HERTZ,
59 joint_id,
60 hertz,
61 )
62 });
63 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
64 joint.prismatic_mut().hertz = hertz;
65}
66
67pub fn prismatic_joint_get_spring_hertz(world: &World, joint_id: JointId) -> f32 {
69 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
70 joint.prismatic().hertz
71}
72
73pub fn prismatic_joint_set_spring_damping_ratio(
75 world: &mut World,
76 joint_id: JointId,
77 damping_ratio: f32,
78) {
79 crate::recording::record_op(world, |rec, _| {
80 crate::recording::write_joint_f32(
81 rec,
82 crate::recording::OP_PRISMATIC_SET_SPRING_DAMPING_RATIO,
83 joint_id,
84 damping_ratio,
85 )
86 });
87 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
88 joint.prismatic_mut().damping_ratio = damping_ratio;
89}
90
91pub fn prismatic_joint_get_spring_damping_ratio(world: &World, joint_id: JointId) -> f32 {
93 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
94 joint.prismatic().damping_ratio
95}
96
97pub fn prismatic_joint_set_target_translation(
99 world: &mut World,
100 joint_id: JointId,
101 translation: f32,
102) {
103 crate::recording::record_op(world, |rec, _| {
104 crate::recording::write_joint_f32(
105 rec,
106 crate::recording::OP_PRISMATIC_SET_TARGET_TRANSLATION,
107 joint_id,
108 translation,
109 )
110 });
111 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
112 joint.prismatic_mut().target_translation = translation;
113}
114
115pub fn prismatic_joint_get_target_translation(world: &World, joint_id: JointId) -> f32 {
117 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
118 joint.prismatic().target_translation
119}
120
121pub fn prismatic_joint_enable_limit(world: &mut World, joint_id: JointId, enable_limit: bool) {
123 crate::recording::record_op(world, |rec, _| {
124 crate::recording::write_joint_bool(
125 rec,
126 crate::recording::OP_PRISMATIC_ENABLE_LIMIT,
127 joint_id,
128 enable_limit,
129 )
130 });
131 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
132 let prismatic = joint.prismatic_mut();
133 if enable_limit != prismatic.enable_limit {
134 prismatic.enable_limit = enable_limit;
135 prismatic.lower_impulse = 0.0;
136 prismatic.upper_impulse = 0.0;
137 }
138}
139
140pub fn prismatic_joint_is_limit_enabled(world: &World, joint_id: JointId) -> bool {
142 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
143 joint.prismatic().enable_limit
144}
145
146pub fn prismatic_joint_get_lower_limit(world: &World, joint_id: JointId) -> f32 {
148 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
149 joint.prismatic().lower_translation
150}
151
152pub fn prismatic_joint_get_upper_limit(world: &World, joint_id: JointId) -> f32 {
154 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
155 joint.prismatic().upper_translation
156}
157
158pub fn prismatic_joint_set_limits(world: &mut World, joint_id: JointId, lower: f32, upper: f32) {
160 crate::recording::record_op(world, |rec, _| {
161 crate::recording::write_joint_f32_pair(
162 rec,
163 crate::recording::OP_PRISMATIC_SET_LIMITS,
164 joint_id,
165 lower,
166 upper,
167 )
168 });
169 debug_assert!(lower <= upper);
170
171 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
172 let prismatic = joint.prismatic_mut();
173 if lower != prismatic.lower_translation || upper != prismatic.upper_translation {
174 prismatic.lower_translation = min_float(lower, upper);
175 prismatic.upper_translation = max_float(lower, upper);
176 prismatic.lower_impulse = 0.0;
177 prismatic.upper_impulse = 0.0;
178 }
179}
180
181pub fn prismatic_joint_enable_motor(world: &mut World, joint_id: JointId, enable_motor: bool) {
183 crate::recording::record_op(world, |rec, _| {
184 crate::recording::write_joint_bool(
185 rec,
186 crate::recording::OP_PRISMATIC_ENABLE_MOTOR,
187 joint_id,
188 enable_motor,
189 )
190 });
191 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
192 let prismatic = joint.prismatic_mut();
193 if enable_motor != prismatic.enable_motor {
194 prismatic.enable_motor = enable_motor;
195 prismatic.motor_impulse = 0.0;
196 }
197}
198
199pub fn prismatic_joint_is_motor_enabled(world: &World, joint_id: JointId) -> bool {
201 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
202 joint.prismatic().enable_motor
203}
204
205pub fn prismatic_joint_set_motor_speed(world: &mut World, joint_id: JointId, motor_speed: f32) {
207 crate::recording::record_op(world, |rec, _| {
208 crate::recording::write_joint_f32(
209 rec,
210 crate::recording::OP_PRISMATIC_SET_MOTOR_SPEED,
211 joint_id,
212 motor_speed,
213 )
214 });
215 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
216 joint.prismatic_mut().motor_speed = motor_speed;
217}
218
219pub fn prismatic_joint_get_motor_speed(world: &World, joint_id: JointId) -> f32 {
221 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
222 joint.prismatic().motor_speed
223}
224
225pub fn prismatic_joint_get_motor_force(world: &World, joint_id: JointId) -> f32 {
227 let base = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
228 world.inv_h * base.prismatic().motor_impulse
229}
230
231pub fn prismatic_joint_set_max_motor_force(world: &mut World, joint_id: JointId, force: f32) {
233 crate::recording::record_op(world, |rec, _| {
234 crate::recording::write_joint_f32(
235 rec,
236 crate::recording::OP_PRISMATIC_SET_MAX_MOTOR_FORCE,
237 joint_id,
238 force,
239 )
240 });
241 let joint = get_joint_sim_check_type(world, joint_id, JointType::Prismatic);
242 joint.prismatic_mut().max_motor_force = force;
243}
244
245pub fn prismatic_joint_get_max_motor_force(world: &World, joint_id: JointId) -> f32 {
247 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
248 joint.prismatic().max_motor_force
249}
250
251pub fn prismatic_joint_get_translation(world: &World, joint_id: JointId) -> f32 {
253 let joint_sim = get_joint_sim_check_type_ref(world, joint_id, JointType::Prismatic);
254
255 let wxf_a = get_body_transform(world, joint_sim.body_id_a);
258 let transform_a = to_relative_transform(wxf_a, wxf_a.p);
259 let transform_b =
260 to_relative_transform(get_body_transform(world, joint_sim.body_id_b), wxf_a.p);
261
262 let local_axis_a = rotate_vector(joint_sim.local_frame_a.q, Vec2 { x: 1.0, y: 0.0 });
263 let axis_a = rotate_vector(transform_a.q, local_axis_a);
264 let p_a = transform_point(transform_a, joint_sim.local_frame_a.p);
265 let p_b = transform_point(transform_b, joint_sim.local_frame_b.p);
266 let d = sub(p_b, p_a);
267 dot(d, axis_a)
268}
269
270pub fn prismatic_joint_get_speed(world: &World, joint_id: JointId) -> f32 {
272 let joint_index = get_joint_full_id(world, joint_id);
273 debug_assert!(world.joints[joint_index as usize].type_ == JointType::Prismatic);
274 let base = get_joint_sim_ref(world, joint_index);
275 debug_assert!(base.joint_type() == JointType::Prismatic);
276
277 let body_a = &world.bodies[base.body_id_a as usize];
278 let body_b = &world.bodies[base.body_id_b as usize];
279 let body_sim_a =
280 &world.solver_sets[body_a.set_index as usize].body_sims[body_a.local_index as usize];
281 let body_sim_b =
282 &world.solver_sets[body_b.set_index as usize].body_sims[body_b.local_index as usize];
283 let body_state_a = if body_a.set_index == AWAKE_SET {
284 Some(&world.solver_sets[AWAKE_SET as usize].body_states[body_a.local_index as usize])
285 } else {
286 None
287 };
288 let body_state_b = if body_b.set_index == AWAKE_SET {
289 Some(&world.solver_sets[AWAKE_SET as usize].body_states[body_b.local_index as usize])
290 } else {
291 None
292 };
293
294 let q_a = body_sim_a.transform.q;
295 let q_b = body_sim_b.transform.q;
296
297 let local_axis_a = rotate_vector(base.local_frame_a.q, Vec2 { x: 1.0, y: 0.0 });
298 let axis_a = rotate_vector(q_a, local_axis_a);
299 let r_a = rotate_vector(q_a, sub(base.local_frame_a.p, body_sim_a.local_center));
300 let r_b = rotate_vector(q_b, sub(base.local_frame_b.p, body_sim_b.local_center));
301
302 let dc = sub_pos(body_sim_b.center, body_sim_a.center);
305 let d = add(dc, sub(r_b, r_a));
306
307 let v_a = body_state_a.map_or(VEC2_ZERO, |s| s.linear_velocity);
308 let v_b = body_state_b.map_or(VEC2_ZERO, |s| s.linear_velocity);
309 let w_a = body_state_a.map_or(0.0, |s| s.angular_velocity);
310 let w_b = body_state_b.map_or(0.0, |s| s.angular_velocity);
311
312 let v_rel = sub(add(v_b, cross_sv(w_b, r_b)), add(v_a, cross_sv(w_a, r_a)));
313 dot(d, cross_sv(w_a, axis_a)) + dot(axis_a, v_rel)
314}
315
316pub fn get_prismatic_joint_force(world: &World, base: &JointSim) -> Vec2 {
318 let q_a = get_body_transform(world, base.body_id_a).q;
319
320 let joint = base.prismatic();
321
322 let local_axis_a = rotate_vector(base.local_frame_a.q, Vec2 { x: 1.0, y: 0.0 });
323 let axis_a = rotate_vector(q_a, local_axis_a);
324 let perp_a = left_perp(axis_a);
325
326 let inv_h = world.inv_h;
327 let perp_force = inv_h * joint.impulse.x;
328 let axial_force = inv_h * (joint.motor_impulse + joint.lower_impulse - joint.upper_impulse);
329
330 add(mul_sv(perp_force, perp_a), mul_sv(axial_force, axis_a))
331}
332
333pub fn get_prismatic_joint_torque(world: &World, base: &JointSim) -> f32 {
335 world.inv_h * base.prismatic().impulse.y
336}
337
338pub fn draw_prismatic_joint(
386 draw: &mut dyn crate::debug_draw::DebugDraw,
387 base: &JointSim,
388 transform_a: WorldTransform,
389 transform_b: WorldTransform,
390 draw_scale: f32,
391) {
392 use crate::debug_draw::HexColor;
393 use crate::math_functions::{
394 left_perp, mul_sv, neg, offset_pos, offset_world_transform, rotate_vector, Vec2,
395 };
396
397 debug_assert!(base.joint_type() == JointType::Prismatic);
398
399 let joint = base.prismatic();
400
401 let frame_a = offset_world_transform(transform_a, base.local_frame_a);
402 let frame_b = offset_world_transform(transform_b, base.local_frame_b);
403 let axis_a = rotate_vector(frame_a.q, Vec2 { x: 1.0, y: 0.0 });
404
405 draw.draw_line(frame_a.p, frame_b.p, HexColor::DIM_GRAY);
406
407 if joint.enable_limit {
408 let b = 0.25 * draw_scale;
409 let lower = offset_pos(frame_a.p, mul_sv(joint.lower_translation, axis_a));
410 let upper = offset_pos(frame_a.p, mul_sv(joint.upper_translation, axis_a));
411 let perp = left_perp(axis_a);
412 draw.draw_line(lower, upper, HexColor::GRAY);
413 draw.draw_line(
414 offset_pos(lower, mul_sv(-b, perp)),
415 offset_pos(lower, mul_sv(b, perp)),
416 HexColor::GREEN,
417 );
418 draw.draw_line(
419 offset_pos(upper, mul_sv(-b, perp)),
420 offset_pos(upper, mul_sv(b, perp)),
421 HexColor::RED,
422 );
423 } else {
424 draw.draw_line(
425 offset_pos(frame_a.p, neg(axis_a)),
426 offset_pos(frame_a.p, axis_a),
427 HexColor::GRAY,
428 );
429 }
430
431 if joint.enable_spring {
432 let p = offset_pos(frame_a.p, mul_sv(joint.target_translation, axis_a));
433 draw.draw_point(p, 8.0, HexColor::VIOLET);
434 }
435
436 draw.draw_point(frame_a.p, 5.0, HexColor::GRAY);
437 draw.draw_point(frame_b.p, 5.0, HexColor::BLUE);
438}