use super::joint_type::*;
use super::range::*;
use super::velocity::*;
use crate::errors::*;
use na::{Isometry3, RealField, Translation3, UnitQuaternion};
use nalgebra as na;
use simba::scalar::SubsetOf;
use std::cell::RefCell;
use std::fmt::{self, Display};
#[derive(Debug, Clone)]
pub struct Joint<T: RealField> {
pub name: String,
pub joint_type: JointType<T>,
position: T,
velocity: T,
pub limits: Option<Range<T>>,
origin: Isometry3<T>,
world_transform_cache: RefCell<Option<Isometry3<T>>>,
world_velocity_cache: RefCell<Option<Velocity<T>>>,
}
impl<T> Joint<T>
where
T: RealField + SubsetOf<f64>,
{
pub fn new(name: &str, joint_type: JointType<T>) -> Joint<T> {
Joint {
name: name.to_string(),
joint_type,
position: T::zero(),
velocity: T::zero(),
limits: None,
origin: Isometry3::identity(),
world_transform_cache: RefCell::new(None),
world_velocity_cache: RefCell::new(None),
}
}
pub fn set_joint_position(&mut self, position: T) -> Result<(), Error> {
if !self.is_movable() {
return Err(Error::SetToFixedError {
joint_name: self.name.to_string(),
});
}
if let Some(ref range) = self.limits {
if !range.is_valid(position.clone()) {
return Err(Error::OutOfLimitError {
joint_name: self.name.to_string(),
position: na::try_convert(position).unwrap_or_default(),
max_limit: na::try_convert(range.max.clone()).unwrap_or_default(),
min_limit: na::try_convert(range.min.clone()).unwrap_or_default(),
});
}
}
self.position = position;
self.clear_caches();
Ok(())
}
pub fn set_joint_position_clamped(&mut self, position: T) {
if !self.is_movable() {
return;
}
let position_clamped = if let Some(ref range) = self.limits {
range.clamp(position)
} else {
position
};
self.set_joint_position_unchecked(position_clamped);
}
pub fn set_joint_position_unchecked(&mut self, position: T) {
self.position = position;
self.clear_caches();
}
#[inline]
pub fn joint_position(&self) -> Option<T> {
match self.joint_type {
JointType::Fixed => None,
_ => Some(self.position.clone()),
}
}
#[inline]
pub fn origin(&self) -> &Isometry3<T> {
&self.origin
}
#[inline]
pub fn set_origin(&mut self, origin: Isometry3<T>) {
self.origin = origin;
self.clear_caches();
}
pub fn set_joint_velocity(&mut self, velocity: T) -> Result<(), Error> {
if let JointType::Fixed = self.joint_type {
return Err(Error::SetToFixedError {
joint_name: self.name.to_string(),
});
}
self.velocity = velocity;
self.world_velocity_cache.replace(None);
Ok(())
}
#[inline]
pub fn joint_velocity(&self) -> Option<T> {
match self.joint_type {
JointType::Fixed => None,
_ => Some(self.velocity.clone()),
}
}
pub fn local_transform(&self) -> Isometry3<T> {
let joint_transform = match &self.joint_type {
JointType::Fixed => Isometry3::identity(),
JointType::Rotational { axis } => Isometry3::from_parts(
Translation3::new(T::zero(), T::zero(), T::zero()),
UnitQuaternion::from_axis_angle(axis, self.position.clone()),
),
JointType::Linear { axis } => Isometry3::from_parts(
Translation3::from(axis.clone().into_inner() * self.position.clone()),
UnitQuaternion::identity(),
),
};
self.origin.clone() * joint_transform
}
#[inline]
pub(crate) fn set_world_transform(&self, world_transform: Isometry3<T>) {
self.world_transform_cache.replace(Some(world_transform));
}
#[inline]
pub(crate) fn set_world_velocity(&self, world_velocity: Velocity<T>) {
self.world_velocity_cache.replace(Some(world_velocity));
}
#[inline]
pub fn world_transform(&self) -> Option<Isometry3<T>> {
self.world_transform_cache.borrow().clone()
}
#[inline]
pub fn world_velocity(&self) -> Option<Velocity<T>> {
self.world_velocity_cache.borrow().clone()
}
#[inline]
pub fn is_movable(&self) -> bool {
!matches!(self.joint_type, JointType::Fixed)
}
#[inline]
pub fn clear_caches(&self) {
self.world_transform_cache.replace(None);
self.world_velocity_cache.replace(None);
}
}
impl<T: RealField> Display for Joint<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.name, self.joint_type)
}
}