use boxddd::{
BodyDef, BodyId, BodyType, DistanceJointDef, Error, FilterJointDef, JointId, JointType,
MotorJointDef, ParallelJointDef, PrismaticJointDef, RevoluteJointDef, ShapeDef, Sphere,
SphericalJointDef, WeldJointDef, WheelJointDef, World, WorldDef,
};
fn body_pair() -> (World, BodyId, BodyId) {
let mut world = World::new(WorldDef::default()).unwrap();
let a = world.create_body(
BodyDef::builder()
.body_type(BodyType::Dynamic)
.position([-0.5, 0.0, 0.0])
.build(),
);
let b = world.create_body(
BodyDef::builder()
.body_type(BodyType::Dynamic)
.position([0.5, 0.0, 0.0])
.build(),
);
(world, a, b)
}
fn contains_joint(joints: &[JointId], joint: JointId) -> bool {
joints.iter().any(|candidate| *candidate == joint)
}
#[test]
fn creates_every_joint_family_and_enumerates_body_joints() {
let (mut world, a, b) = body_pair();
let joints = [
(
world.create_parallel_joint(ParallelJointDef::new(a, b)),
JointType::Parallel,
),
(
world.create_distance_joint(DistanceJointDef::new(a, b).length(1.0)),
JointType::Distance,
),
(
world.create_filter_joint(FilterJointDef::new(a, b)),
JointType::Filter,
),
(
world.create_motor_joint(MotorJointDef::new(a, b)),
JointType::Motor,
),
(
world.create_prismatic_joint(PrismaticJointDef::new(a, b)),
JointType::Prismatic,
),
(
world.create_revolute_joint(RevoluteJointDef::new(a, b)),
JointType::Revolute,
),
(
world.create_spherical_joint(SphericalJointDef::new(a, b)),
JointType::Spherical,
),
(
world.create_weld_joint(WeldJointDef::new(a, b)),
JointType::Weld,
),
(
world.create_wheel_joint(WheelJointDef::new(a, b)),
JointType::Wheel,
),
];
for (joint, expected_type) in joints {
assert!(joint.is_valid());
assert_eq!(world.try_joint_type(joint).unwrap(), expected_type);
assert_eq!(world.try_joint_body_a(joint).unwrap(), a);
assert_eq!(world.try_joint_body_b(joint).unwrap(), b);
}
let body_a_joints = world.try_body_joints(a).unwrap();
let body_b_joints = world.try_body_joints(b).unwrap();
for (joint, _) in joints {
assert!(contains_joint(&body_a_joints, joint));
assert!(contains_joint(&body_b_joints, joint));
}
let removed = joints[0].0;
world.try_destroy_joint(removed, true).unwrap();
assert_eq!(
world.try_joint_type(removed).unwrap_err(),
Error::InvalidJointId
);
assert!(!contains_joint(&world.try_body_joints(a).unwrap(), removed));
}
#[test]
fn create_joint_rejects_invalid_stale_and_wrong_world_bodies() {
let (mut world, a, b) = body_pair();
let stale = world.create_body(BodyDef::builder().body_type(BodyType::Dynamic).build());
world.destroy_body(stale);
assert_eq!(
world
.try_create_distance_joint(DistanceJointDef::new(stale, b).length(1.0))
.unwrap_err(),
Error::InvalidBodyId
);
assert_eq!(
world
.try_create_distance_joint(DistanceJointDef::new(a, a).length(1.0))
.unwrap_err(),
Error::InvalidArgument
);
let mut other_world = World::new(WorldDef::default()).unwrap();
let other_body =
other_world.create_body(BodyDef::builder().body_type(BodyType::Dynamic).build());
assert_eq!(
world
.try_create_distance_joint(DistanceJointDef::new(a, other_body).length(1.0))
.unwrap_err(),
Error::InvalidArgument
);
}
#[test]
fn world_rejects_foreign_joint_handles() {
let (mut world, _, _) = body_pair();
let (mut other_world, a, b) = body_pair();
let foreign_joint = other_world.create_distance_joint(DistanceJointDef::new(a, b).length(1.0));
assert_eq!(
world.try_joint_type(foreign_joint).unwrap_err(),
Error::InvalidJointId
);
assert_eq!(
world.try_destroy_joint(foreign_joint, true).unwrap_err(),
Error::InvalidJointId
);
assert!(foreign_joint.is_valid());
assert_eq!(
other_world.try_joint_type(foreign_joint).unwrap(),
JointType::Distance
);
}
#[test]
fn distance_joint_keeps_a_simple_scene_within_a_coarse_bound() {
let mut world = World::new(WorldDef::builder().gravity([0.0, 0.0, 0.0]).build()).unwrap();
let anchor = world.create_body(BodyDef::builder().position([0.0, 0.0, 0.0]).build());
let body = world.create_body(
BodyDef::builder()
.body_type(BodyType::Dynamic)
.position([1.0, 0.0, 0.0])
.build(),
);
world.create_sphere_shape(
body,
&ShapeDef::builder().density(1.0).build(),
&Sphere::new([0.0, 0.0, 0.0], 0.25),
);
let joint = world.create_distance_joint(DistanceJointDef::new(anchor, body).length(1.0));
world
.try_apply_force_to_center(body, [250.0, 0.0, 0.0], true)
.unwrap();
for _ in 0..120 {
world.step(1.0 / 60.0, 4);
}
let length = world.try_distance_joint_current_length(joint).unwrap();
assert!(length < 1.5, "distance joint stretched too far: {length}");
}