use cgmath::{BaseFloat, Basis2, EuclideanSpace, Euler, Quaternion, Rad, Rotation, Rotation2,
Vector3, VectorSpace, Zero};
use BodyPose;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "eders", derive(Serialize, Deserialize))]
pub struct Velocity<L, A> {
linear: L,
angular: A,
}
impl<L, A> Default for Velocity<L, A>
where
L: Zero,
A: Clone + Zero,
{
fn default() -> Self {
Self::new(L::zero(), A::zero())
}
}
impl<L, A> Velocity<L, A>
where
L: Zero,
A: Clone + Zero,
{
pub fn new(linear: L, angular: A) -> Self {
Self { linear, angular }
}
pub fn from_linear(linear: L) -> Self {
Self::new(linear, A::zero())
}
pub fn set_linear(&mut self, linear: L) {
self.linear = linear;
}
pub fn linear(&self) -> &L {
&self.linear
}
pub fn set_angular(&mut self, angular: A) {
self.angular = angular;
}
pub fn angular(&self) -> &A {
&self.angular
}
pub fn apply<P, R>(&self, pose: &BodyPose<P, R>, dt: L::Scalar) -> BodyPose<P, R>
where
P: EuclideanSpace<Scalar = L::Scalar, Diff = L>,
L: VectorSpace,
L::Scalar: BaseFloat,
R: ApplyAngular<L::Scalar, A> + Rotation<P>,
{
BodyPose::new(
self.apply_linear(pose.position(), dt),
self.apply_angular(pose.rotation(), dt),
)
}
pub fn apply_linear<P>(&self, linear: &P, dt: L::Scalar) -> P
where
P: EuclideanSpace<Scalar = L::Scalar, Diff = L>,
L::Scalar: BaseFloat,
L: VectorSpace,
{
*linear + self.linear * dt
}
pub fn apply_angular<R>(&self, rotation: &R, dt: L::Scalar) -> R
where
R: ApplyAngular<L::Scalar, A>,
L: VectorSpace,
L::Scalar: BaseFloat,
{
rotation.apply(&self.angular, dt)
}
}
pub trait ApplyAngular<S, A> {
fn apply(&self, velocity: &A, dt: S) -> Self;
}
impl<S> ApplyAngular<S, S> for S
where
S: BaseFloat,
{
fn apply(&self, velocity: &S, dt: S) -> Self {
*self + *velocity * dt
}
}
impl<S> ApplyAngular<S, S> for Basis2<S>
where
S: BaseFloat,
{
fn apply(&self, velocity: &S, dt: S) -> Self {
*self * Basis2::from_angle(Rad(*velocity * dt))
}
}
impl<S> ApplyAngular<S, Vector3<S>> for Quaternion<S>
where
S: BaseFloat,
{
fn apply(&self, velocity: &Vector3<S>, dt: S) -> Self {
self * Quaternion::from(Euler {
x: Rad(velocity.x * dt),
y: Rad(velocity.y * dt),
z: Rad(velocity.z * dt),
})
}
}
#[cfg(test)]
mod tests_f32 {
use cgmath::{Basis2, Point2, Point3, Rad, Rotation2, Rotation3, Transform, Vector2};
use super::*;
use physics::prelude2d::BodyPose2;
use physics::prelude3d::BodyPose3;
#[test]
fn test_velocity_linear() {
let velocity = Velocity::new(Vector2::new(1., 1.), 0.);
let pose = Point2::<f32>::new(0., 0.);
let pose = velocity.apply_linear(&pose, 0.1);
assert_eq!(Point2::new(0.1, 0.1), pose);
}
#[test]
fn test_velocity_2d_angular() {
let velocity = Velocity::new(Vector2::new(1., 1.), 1.);
let orientation = Basis2::<f32>::from_angle(Rad(0.));
let orientation = velocity.apply_angular(&orientation, 0.1);
assert_eq!(Basis2::from_angle(Rad(0.1)), orientation);
}
#[test]
fn test_velocity_3d_angular() {
let velocity = Velocity::new(Vector3::new(1., 1., 1.), Vector3::new(0., 1., 0.));
let orientation = Quaternion::<f32>::from_angle_y(Rad(0.));
let orientation = velocity.apply_angular(&orientation, 0.1);
assert_eq!(Quaternion::from_angle_y(Rad(0.1)), orientation);
}
#[test]
fn test_velocity_full_2d() {
let velocity = Velocity::new(Vector2::new(1., 1.), 1.);
let pose = BodyPose2::<f32>::one();
let pose = velocity.apply(&pose, 0.1);
assert_eq!(Point2::new(0.1, 0.1), *pose.position());
assert_eq!(Basis2::from_angle(Rad(0.1)), *pose.rotation());
}
#[test]
fn test_velocity_full_3d() {
let velocity = Velocity::new(Vector3::new(1., 1., 1.), Vector3::new(0., 1., 0.));
let pose = BodyPose3::<f32>::one();
let pose = velocity.apply(&pose, 0.1);
assert_eq!(Point3::new(0.1, 0.1, 0.1), *pose.position());
assert_eq!(Quaternion::from_angle_y(Rad(0.1)), *pose.rotation());
}
#[test]
fn test_apply_angular_basis2() {
let orientation = Basis2::<f32>::from_angle(Rad(0.));
let velocity = 0.5;
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
assert_ulps_eq!(Basis2::from_angle(Rad(0.2)), orientation);
}
#[test]
fn test_apply_angular_real() {
let orientation: f32 = 0.;
let velocity = 0.5;
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
assert_eq!(0.2, orientation);
}
#[test]
fn test_apply_angular_quat() {
let orientation = Quaternion::<f32>::from_angle_x(Rad(0.));
let velocity = Vector3::new(0.5, 0., 0.);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
assert_ulps_eq!(Quaternion::from_angle_x(Rad(0.2)), orientation);
}
}
#[cfg(test)]
mod tests_f64 {
use cgmath::{Basis2, Point2, Point3, Rad, Rotation2, Rotation3, Transform, Vector2};
use super::*;
use physics::prelude2d::BodyPose2;
use physics::prelude3d::BodyPose3;
#[test]
fn test_velocity_linear() {
let velocity = Velocity::new(Vector2::new(1., 1.), 0.);
let pose = Point2::<f64>::new(0., 0.);
let pose = velocity.apply_linear(&pose, 0.1);
assert_eq!(Point2::new(0.1, 0.1), pose);
}
#[test]
fn test_velocity_2d_angular() {
let velocity = Velocity::new(Vector2::new(1., 1.), 1.);
let orientation = Basis2::<f64>::from_angle(Rad(0.));
let orientation = velocity.apply_angular(&orientation, 0.1);
assert_eq!(Basis2::from_angle(Rad(0.1)), orientation);
}
#[test]
fn test_velocity_3d_angular() {
let velocity = Velocity::new(Vector3::new(1., 1., 1.), Vector3::new(0., 1., 0.));
let orientation = Quaternion::<f64>::from_angle_y(Rad(0.));
let orientation = velocity.apply_angular(&orientation, 0.1);
assert_eq!(Quaternion::from_angle_y(Rad(0.1)), orientation);
}
#[test]
fn test_velocity_full_2d() {
let velocity = Velocity::new(Vector2::new(1., 1.), 1.);
let pose = BodyPose2::<f64>::one();
let pose = velocity.apply(&pose, 0.1);
assert_eq!(Point2::new(0.1, 0.1), *pose.position());
assert_eq!(Basis2::from_angle(Rad(0.1)), *pose.rotation());
}
#[test]
fn test_velocity_full_3d() {
let velocity = Velocity::new(Vector3::new(1., 1., 1.), Vector3::new(0., 1., 0.));
let pose = BodyPose3::<f64>::one();
let pose = velocity.apply(&pose, 0.1);
assert_eq!(Point3::new(0.1, 0.1, 0.1), *pose.position());
assert_eq!(Quaternion::from_angle_y(Rad(0.1)), *pose.rotation());
}
#[test]
fn test_apply_angular_basis2() {
let orientation = Basis2::<f64>::from_angle(Rad(0.));
let velocity = 0.5;
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
assert_ulps_eq!(Basis2::from_angle(Rad(0.2)), orientation);
}
#[test]
fn test_apply_angular_real() {
let orientation: f64 = 0.;
let velocity = 0.5;
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
assert_eq!(0.2, orientation);
}
#[test]
fn test_apply_angular_quat() {
let orientation = Quaternion::<f64>::from_angle_x(Rad(0.));
let velocity = Vector3::new(0.5, 0., 0.);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
let orientation = orientation.apply(&velocity, 0.1);
assert_ulps_eq!(Quaternion::from_angle_x(Rad(0.2)), orientation);
}
}