use bullet_physics::dynamics::{MultiBody, MultiBodyConstraint, MultiBodyHandle, PhysicsWorld};
use bullet_physics::math::{Quaternion, Real, Vector3};
fn main() {
let mut world = PhysicsWorld::new();
world.set_gravity(Vector3::new(0.0, -9.81, 0.0));
let mut pendulum = MultiBody::new(1, 1.0, Vector3::ONE, true, false);
pendulum.setup_revolute(
0,
1.0,
Vector3::ONE,
None,
Quaternion::IDENTITY,
Vector3::Z,
Vector3::ZERO,
Vector3::new(0.0, -1.0, 0.0),
);
pendulum.finalize_multidof();
pendulum.set_joint_pos_multidof(0, &[bullet_physics::math::FRAC_PI_2]);
world.add_multibody(pendulum);
let mut slider = MultiBody::new(1, 1.0, Vector3::ONE, true, false);
slider.setup_prismatic(
0,
1.0,
Vector3::ONE,
None,
Quaternion::IDENTITY,
Vector3::X,
Vector3::ZERO,
Vector3::ZERO,
);
slider.finalize_multidof();
let slider_idx = world.add_multibody(slider);
let snapshot = world.multibodies().to_vec();
let motor = MultiBodyConstraint::joint_motor(slider_idx, 0, 0, 2.0, 100.0, &snapshot);
world.add_multibody_constraint(motor);
let mut a = MultiBody::new(0, 1.0, Vector3::ONE, false, false);
let mut b = MultiBody::new(0, 2.0, Vector3::splat(1.5), false, false);
a.finalize_multidof();
b.finalize_multidof();
b.set_base_vel(Vector3::new(0.0, 3.0, 0.0));
let ia = world.add_multibody(a);
let ib = world.add_multibody(b);
let snapshot = world.multibodies().to_vec();
let tether = MultiBodyConstraint::point2point(ia, None, ib, None, Vector3::ZERO, Vector3::ZERO, &snapshot);
world.add_multibody_constraint(tether);
let mut ga = MultiBody::new(1, 1.0, Vector3::ONE, true, false);
ga.setup_revolute(
0,
1.0,
Vector3::ONE,
None,
Quaternion::IDENTITY,
Vector3::Z,
Vector3::ZERO,
Vector3::new(1.0, 0.0, 0.0),
);
let mut gb = MultiBody::new(1, 1.0, Vector3::ONE, true, false);
gb.setup_revolute(
0,
1.0,
Vector3::ONE,
None,
Quaternion::IDENTITY,
Vector3::Z,
Vector3::ZERO,
Vector3::new(1.0, 0.0, 0.0),
);
ga.finalize_multidof();
gb.finalize_multidof();
let iga = world.add_multibody(ga);
let igb = world.add_multibody(gb);
let snapshot = world.multibodies().to_vec();
let gear = MultiBodyConstraint::gear(iga, 0, igb, 0, 2.0, &snapshot);
world.add_multibody_constraint(gear);
let gear_motor = MultiBodyConstraint::joint_motor(iga, 0, 0, 1.0, 100.0, &snapshot);
world.add_multibody_constraint(gear_motor);
let dt: Real = 1.0 / 60.0;
let mut bottom_speeds = Vec::new();
let mut prev_q: Real = 0.0;
let mut max_separation: Real = 0.0;
let mut slider_speed = 0.0;
let mut gear_speeds = (0.0, 0.0);
for step in 0..900 {
world.step_simulation(dt);
let pend = world.multibody(MultiBodyHandle::from_index(0));
let q = pend.joint_pos_multidof(0)[0];
let qd = pend.joint_vel_multidof(0)[0];
if prev_q.abs() < 0.1 && q.abs() < 0.1 && prev_q * q <= 0.0 {
bottom_speeds.push(qd.abs());
}
prev_q = q;
if step > 600 {
slider_speed = world.multibody(slider_idx).joint_vel_multidof(0)[0];
gear_speeds = (
world.multibody(iga).joint_vel_multidof(0)[0],
world.multibody(igb).joint_vel_multidof(0)[0],
);
}
let pa = world.multibody(ia).base_pos();
let pb = world.multibody(ib).base_pos();
max_separation = max_separation.max((pa - pb).length());
}
let first_bottom = bottom_speeds.first().copied().unwrap_or(0.0);
let slider_pos = world.multibody(slider_idx).joint_pos_multidof(0)[0];
println!(
"pendulum bottom speed = {first_bottom:.3} rad/s, slider x = {slider_pos:.2} (v = {slider_speed:.3}), \
tether max separation = {max_separation:.4} m, gear speeds = ({:.3}, {:.3}) rad/s",
gear_speeds.0, gear_speeds.1
);
assert!(first_bottom > 2.7 && first_bottom < 3.6, "pendulum bottom speed {first_bottom}");
assert!((slider_speed - 2.0).abs() < 0.1, "slider speed {slider_speed}");
assert!((slider_pos - 30.0).abs() < 1.0, "slider position {slider_pos}");
assert!(max_separation < 0.05, "tether separation {max_separation}");
assert!((gear_speeds.0 - 1.0).abs() < 0.05, "gear drive speed {}", gear_speeds.0);
assert!((gear_speeds.1 + 0.5).abs() < 0.05, "gear follower speed {}", gear_speeds.1);
println!("OK: Featherstone multibody pendulum, motor slider, tether and gear all behave.");
}