use std::{borrow::Cow, hash::Hash};
use bevy::{platform::collections::HashMap, prelude::*};
#[derive(Debug, Clone, Reflect, Component)]
pub struct RdRobot<Id: RdId> {
pub links_by_name: HashMap<RdName, Id>,
pub joints_by_name: HashMap<RdName, Id>,
}
impl<Id: RdId> RdRobot<Id> {
pub fn insert_link(&mut self, name: RdName, id: Id) {
self.links_by_name.insert(name, id);
}
pub fn insert_joint(&mut self, name: RdName, id: Id) {
self.joints_by_name.insert(name, id);
}
pub fn remove_link(&mut self, name: &RdName) -> Option<Id> {
self.links_by_name.remove(name)
}
pub fn remove_joint(&mut self, name: &RdName) -> Option<Id> {
self.joints_by_name.remove(name)
}
pub fn get_link(&self, name: &RdName) -> Option<&Id> {
self.links_by_name.get(name)
}
pub fn get_joint(&self, name: &RdName) -> Option<&Id> {
self.joints_by_name.get(name)
}
}
impl<Id: RdId> Default for RdRobot<Id> {
fn default() -> Self {
Self {
links_by_name: HashMap::default(),
joints_by_name: HashMap::default(),
}
}
}
pub trait RdId: Clone + Reflect + Eq + Hash {}
impl<T: Clone + Reflect + Eq + Hash> RdId for T {}
#[derive(Debug, Clone, Reflect, Deref, DerefMut, PartialEq, Eq, Hash)]
pub struct RdName(pub Cow<'static, str>);
impl Default for RdName {
fn default() -> Self {
Self(Cow::from("unnamed"))
}
}
impl RdName {
pub fn new(name: &str) -> Self {
Self(Cow::from(name.to_string()))
}
}
impl From<Name> for RdName {
fn from(name: Name) -> Self {
Self(Cow::from(name.to_string()))
}
}
impl From<RdName> for Name {
fn from(value: RdName) -> Self {
Self::new(value.0.to_string())
}
}
pub type RdVector = Vec3;
pub type RdScaler = f32;
#[derive(Debug, Clone, Reflect, Component)]
pub struct RdLink<Id: RdId> {
pub colliders_by_name: HashMap<RdName, Id>,
}
impl<Id: RdId> RdLink<Id> {
pub fn insert_collider(&mut self, name: RdName, id: Id) {
self.colliders_by_name.insert(name.clone(), id.clone());
}
pub fn remove_collider(&mut self, name: &RdName) -> Option<Id> {
self.colliders_by_name.remove(name)
}
pub fn get_collider(&self, name: &RdName) -> Option<&Id> {
self.colliders_by_name.get(name)
}
}
impl<Id: RdId> Default for RdLink<Id> {
fn default() -> Self {
Self {
colliders_by_name: HashMap::default(),
}
}
}
#[derive(Debug, Clone, Reflect, Component)]
pub struct RdCollider {
pub density: RdScaler,
}
impl Default for RdCollider {
fn default() -> Self {
Self { density: 1.0 }
}
}
#[derive(Debug, Clone, Reflect, Component)]
pub struct RdJoint<Id: RdId> {
pub parent: Id,
pub child: Id,
pub kind: RdJointKind,
}
#[derive(Debug, Clone, Reflect, Component)]
pub enum RdJointKind {
Fixed(RdFixedJointData),
Revolute(RdRevoluteJointData),
Prismatic(RdPrismaticJointData),
Spherical(RdSphericalJointData),
}
#[derive(Debug, Clone, Reflect)]
pub struct RdFixedJointData {
pub local_anchor1: RdVector,
pub local_anchor2: RdVector,
}
#[derive(Debug, Clone, Reflect, Default)]
pub struct RdRevoluteJointData {
pub axis: RdVector,
pub local_anchor1: RdVector,
pub local_anchor2: RdVector,
pub motor: RdMotor,
pub limits: Option<RdJointLimits>,
}
#[derive(Debug, Clone, Reflect, Default)]
pub struct RdPrismaticJointData {
pub axis: RdVector,
pub local_anchor1: RdVector,
pub local_anchor2: RdVector,
pub motor: RdMotor,
pub limits: Option<RdJointLimits>,
}
#[derive(Debug, Clone, Reflect, Default)]
pub struct RdSphericalJointData {
pub local_anchor1: RdVector,
pub local_anchor2: RdVector,
pub motor_x: Option<RdMotor>,
pub motor_y: Option<RdMotor>,
pub motor_z: Option<RdMotor>,
pub limit_ang_x: Option<RdJointLimits>,
pub limit_ang_y: Option<RdJointLimits>,
pub limit_ang_z: Option<RdJointLimits>,
}
#[derive(Debug, Clone, Reflect, Default)]
pub struct RdMotor {
pub model: RdMotorModel,
pub velocity: Option<RdMotorVelocity>,
pub position: Option<RdMotorPosition>,
pub max_force: Option<RdScaler>,
}
#[derive(Debug, Clone, Reflect)]
#[reflect(Default)]
pub enum RdMotorModel {
SpringDamper {
frequency: f32,
damping_ratio: f32,
},
ForceBased {
stiffness: f32,
damping: f32,
},
AccelerationBased {
stiffness: f32,
damping: f32,
},
}
impl Default for RdMotorModel {
fn default() -> Self {
Self::AccelerationBased {
stiffness: 0.0,
damping: 0.1,
}
}
}
#[derive(Debug, Clone, Reflect)]
pub struct RdMotorVelocity {
pub target: RdScaler,
}
#[derive(Debug, Clone, Reflect)]
pub struct RdMotorPosition {
pub target: RdScaler,
}
#[derive(Debug, Clone, Reflect)]
pub struct RdJointLimits {
pub min: RdScaler,
pub max: RdScaler,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn change_robot_joint_motor_velocity() {
let mut app = App::new();
app.add_systems(Update, update_robot_joint_motor_velocity);
let mut commands = app.world_mut().commands();
let mut rd_robot = RdRobot::<Entity>::default();
let mut link_1_rd_link = RdLink::<Entity>::default();
let link_1_collider_name = RdName::new("link_1_collider");
let link_1_collider = commands
.spawn((
Name::from(link_1_collider_name.clone()),
RdCollider::default(),
))
.id();
link_1_rd_link.insert_collider(link_1_collider_name.clone(), link_1_collider);
let link_1_name = RdName::new("link_1");
let link_1 = commands
.spawn((Name::from(link_1_name.clone()), link_1_rd_link))
.id();
rd_robot.insert_link(link_1_name.clone(), link_1);
let mut link_2_rd_link = RdLink::<Entity>::default();
let link_2_collider_name = RdName::new("link_2_collider");
let link_2_collider = commands
.spawn((
Name::from(link_2_collider_name.clone()),
RdCollider::default(),
))
.id();
link_2_rd_link.insert_collider(link_2_collider_name.clone(), link_2_collider);
let link_2_name = RdName::new("link_2");
let link_2 = commands
.spawn((Name::from(link_2_name.clone()), link_2_rd_link))
.id();
rd_robot.insert_link(link_2_name.clone(), link_2);
let joint_1_rd_joint = RdJoint {
parent: link_1,
child: link_2,
kind: RdJointKind::Revolute(RdRevoluteJointData {
axis: Vec3::Y,
local_anchor1: Vec3::Y / 2.,
local_anchor2: Vec3::Y / 2.,
motor: RdMotor {
velocity: Some(RdMotorVelocity { target: 0.0 }),
..Default::default()
},
limits: None,
}),
};
let joint_1_name = RdName::new("joint_1");
let joint_1 = commands
.spawn((Name::from(joint_1_name.clone()), joint_1_rd_joint))
.id();
rd_robot.insert_joint(joint_1_name.clone(), joint_1);
let robot_name = RdName::new("robot");
commands.spawn((Name::from(robot_name), rd_robot));
pub fn update_robot_joint_motor_velocity(
robots: Query<&RdRobot<Entity>>,
mut joints: Query<&mut RdJoint<Entity>>,
) {
for robot in robots.iter() {
for mut joint in joints.iter_mut() {
if joint.parent == *robot.get_link(&RdName::new("link_1")).unwrap()
&& joint.child == *robot.get_link(&RdName::new("link_2")).unwrap()
{
if let RdJointKind::Revolute(ref mut data) = joint.kind {
data.motor.velocity = Some(RdMotorVelocity { target: 10.0 });
}
}
}
}
}
app.update();
let joint = app.world().get::<RdJoint<Entity>>(joint_1).unwrap();
if let RdJointKind::Revolute(data) = &joint.kind {
if let Some(RdMotorVelocity { target }) = data.motor.velocity {
assert_eq!(target, 10.0);
} else {
panic!("Motor velocity is not set!");
}
} else {
panic!("Joint kind is not Revolute!");
}
}
}