use re_sdk_types::archetypes::Transform3D;
use re_sdk_types::external::glam;
use urdf_rs::{Joint, JointType};
use super::quat_from_rpy;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Joint type '{0:?}' is not supported for transform computation")]
UnsupportedJointType(JointType),
}
pub fn compute_transform3d(joint: &Joint, value: f64, clamp: bool) -> Result<Transform3D, Error> {
let result = internal::compute_joint_transform(joint, value, clamp)?;
if let Some(warning) = &result.warning {
re_log::warn!("{}", warning);
}
Ok(Transform3D::update_fields()
.with_translation(result.translation.to_array())
.with_quaternion(result.quaternion.to_array())
.with_parent_frame(result.parent_frame)
.with_child_frame(result.child_frame))
}
pub mod internal {
use super::{Error, Joint, JointType, glam, quat_from_rpy};
pub struct JointTransform {
pub quaternion: glam::Quat,
pub translation: glam::Vec3,
pub parent_frame: String,
pub child_frame: String,
pub warning: Option<String>,
}
pub fn compute_joint_transform(
joint: &Joint,
value: f64,
clamp: bool,
) -> Result<JointTransform, Error> {
let Joint {
name,
joint_type,
origin,
parent,
child,
axis,
limit,
calibration: _,
dynamics: _,
mimic: _,
safety_controller: _,
} = joint;
let urdf_rs::Pose {
xyz: origin_xyz,
rpy: origin_rpy,
} = origin;
let parent_frame = parent.link.clone();
let child_frame = child.link.clone();
let origin_quat = quat_from_rpy(origin_rpy);
let origin_translation = glam::Vec3::new(
origin_xyz[0] as f32,
origin_xyz[1] as f32,
origin_xyz[2] as f32,
);
match joint_type {
JointType::Revolute | JointType::Continuous => {
let mut warning = None;
let mut value = value;
if clamp
&& *joint_type == JointType::Revolute
&& !(limit.lower <= value && value <= limit.upper)
{
warning = Some(format!(
"Joint '{}' angle {:.4} rad is outside limits [{:.4}, {:.4}] rad. Clamping.",
name, value, limit.lower, limit.upper
));
value = value.clamp(limit.lower, limit.upper);
}
let axis_vec =
glam::Vec3::new(axis.xyz[0] as f32, axis.xyz[1] as f32, axis.xyz[2] as f32);
let quat_dynamic = glam::Quat::from_axis_angle(axis_vec, value as f32);
let combined_quat = origin_quat * quat_dynamic;
Ok(JointTransform {
quaternion: combined_quat,
translation: origin_translation,
parent_frame,
child_frame,
warning,
})
}
JointType::Prismatic => {
let mut warning = None;
let mut value = value;
if clamp && !(limit.lower <= value && value <= limit.upper) {
warning = Some(format!(
"Joint '{}' distance {:.4} m is outside limits [{:.4}, {:.4}] m. Clamping.",
name, value, limit.lower, limit.upper
));
value = value.clamp(limit.lower, limit.upper);
}
let translation = glam::Vec3::new(
(origin_xyz[0] + axis.xyz[0] * value) as f32,
(origin_xyz[1] + axis.xyz[1] * value) as f32,
(origin_xyz[2] + axis.xyz[2] * value) as f32,
);
Ok(JointTransform {
quaternion: origin_quat,
translation,
parent_frame,
child_frame,
warning,
})
}
JointType::Fixed => Ok(JointTransform {
quaternion: origin_quat,
translation: origin_translation,
parent_frame,
child_frame,
warning: None,
}),
JointType::Floating | JointType::Planar | JointType::Spherical => {
Err(Error::UnsupportedJointType(joint_type.clone()))
}
}
}
}