mod constraint;
mod frame;
mod rows;
use alloc::vec::Vec;
use crate::physics::sim::math::Vec3;
pub(crate) use constraint::{JointSolver, Prepared};
pub(crate) use frame::{JointFrame, JointKind};
pub(crate) use rows::Push;
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub(crate) struct JointImpulses {
pub(crate) linear: Vec3,
pub(crate) angular: Vec3,
pub(crate) lower: f32,
pub(crate) upper: f32,
pub(crate) motor: f32,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Joint {
pub(crate) a: u32,
pub(crate) b: u32,
pub(crate) anchor_a: Vec3,
pub(crate) anchor_b: Vec3,
pub(crate) frame: JointFrame,
pub(crate) impulses: JointImpulses,
}
impl Joint {
pub(crate) fn other(&self, slot: u32) -> Option<u32> {
if self.a == slot {
Some(self.b)
} else if self.b == slot {
Some(self.a)
} else {
None
}
}
}
pub(crate) struct JointSet {
joints: Vec<Joint>,
}
impl JointSet {
pub(crate) fn with_capacity(capacity: usize) -> Self {
JointSet {
joints: Vec::with_capacity(capacity),
}
}
pub(crate) fn len(&self) -> usize {
self.joints.len()
}
pub(crate) fn as_slice(&self) -> &[Joint] {
&self.joints
}
pub(crate) fn as_mut_slice(&mut self) -> &mut [Joint] {
&mut self.joints
}
pub(crate) fn push(&mut self, joint: Joint) {
self.joints.push(joint);
}
pub(crate) fn remove_incident(&mut self, slot: u32) -> bool {
let before = self.joints.len();
self.joints
.retain(|joint| joint.a != slot && joint.b != slot);
self.joints.len() != before
}
pub(crate) fn reserved_bytes(&self) -> u64 {
(self.joints.capacity() * size_of::<Joint>()) as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::physics::JointSpec;
use crate::physics::sim::math::Quat;
fn joint(a: u32, b: u32) -> Joint {
Joint {
a,
b,
anchor_a: Vec3::ZERO,
anchor_b: Vec3::ZERO,
frame: JointFrame::new(JointSpec::Fixed, Quat::IDENTITY, Quat::IDENTITY),
impulses: JointImpulses::default(),
}
}
#[test]
fn a_joint_names_the_body_at_the_other_end() {
let j = joint(3, 7);
assert_eq!(j.other(3), Some(7));
assert_eq!(j.other(7), Some(3));
assert_eq!(j.other(4), None);
}
#[test]
fn removing_a_body_drops_only_the_joints_it_was_in() {
let mut set = JointSet::with_capacity(4);
for pair in [(0, 1), (1, 2), (2, 3)] {
set.push(joint(pair.0, pair.1));
}
assert!(set.remove_incident(1));
let left: Vec<(u32, u32)> = set.as_slice().iter().map(|j| (j.a, j.b)).collect();
assert_eq!(left, [(2, 3)]);
assert!(!set.remove_incident(9), "a body in no joint drops nothing");
assert_eq!(set.len(), 1);
assert!(set.reserved_bytes() > 0);
}
#[test]
fn removal_keeps_the_surviving_joints_in_the_order_they_were_added() {
let mut set = JointSet::with_capacity(8);
for pair in [(0, 1), (2, 3), (4, 5), (2, 6), (7, 8)] {
set.push(joint(pair.0, pair.1));
}
set.remove_incident(2);
let left: Vec<(u32, u32)> = set.as_slice().iter().map(|j| (j.a, j.b)).collect();
assert_eq!(left, [(0, 1), (4, 5), (7, 8)]);
set.as_mut_slice()[0].impulses.lower = 2.0;
assert_eq!(set.as_slice()[0].impulses.lower, 2.0);
}
}