use log::{info};
use std::fmt;
use std::cmp::Ordering;
use nalgebra::{Matrix, Dynamic, VecStorage, U1, Vector, RowVector};
use std::fmt::Formatter;
type DMatrixf32 = Matrix<f32, Dynamic, Dynamic, VecStorage<f32, Dynamic, Dynamic>>;
type DColVectorf32 = Vector<f32, Dynamic, VecStorage<f32, Dynamic, U1>>;
type DRowVectorf32 = RowVector<f32, Dynamic, VecStorage<f32, U1, Dynamic>>;
#[derive(Debug, Copy, Clone)]
pub struct Point {
pub x: f32,
pub y: f32,
pub z: f32,
pub t: f32,
}
impl Point {
pub fn new(x: f32, y: f32, z: f32, t: f32) -> Point {
Point { x, y, z, t }
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Point(x: {}, y: {}, z: {}, t: {})", self.x, self.y, self.z, self.t)
}
}
#[derive(Debug, Clone)]
pub struct Polynomial {
coeffs: Vec<f32>,
}
impl Polynomial {
pub fn new(coeffs: &Vec<f32>) -> Polynomial {
if coeffs.is_empty() {
panic!("Cannot initialize Polynomial with empty coefficients!");
}
let coeffs = coeffs.clone();
Polynomial { coeffs }
}
pub fn eval(&self, t: f32) -> f32 {
let mut val = self.coeffs[0];
let mut tt = t;
for idx in 1..self.coeffs.len() {
val = val + tt * self.coeffs[idx];
tt *= t;
}
val
}
pub fn derivative(&self) -> Polynomial {
if self.coeffs.len() == 1 {
return Polynomial::new(&vec![0.]);
}
let mut newcoeffs = Vec::new();
for idx in 1..self.coeffs.len() {
newcoeffs.push(self.coeffs[idx] * idx as f32);
}
Polynomial::new(&newcoeffs)
}
}
impl fmt::Display for Polynomial {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut s = String::from("f(t) = ");
for idx in 0..self.coeffs.len() {
let coeff: String = match self.coeffs[idx].partial_cmp(&0.0)
.expect("Found a coefficient that is NaN!") {
Ordering::Less => format!("- {}", -self.coeffs[idx]),
Ordering::Greater => format!("+ {}", self.coeffs[idx]),
Ordering::Equal => String::new(),
};
if coeff.len() == 0 {
continue;
}
s.push_str(&coeff);
if idx > 0 {
let x = if idx > 1 {format!("t^{} ", idx)} else {String::from("t ")};
s.push_str(&x);
} else {
s.push(' ');
}
}
write!(f, "{}", s.trim())
}
}
#[derive(Debug, PartialEq)]
pub enum TrajectoryType {
Velocity,
Acceleration,
Jerk,
Snap,
}
fn num_coeffs(traj_type: &TrajectoryType) -> usize {
match traj_type {
TrajectoryType::Velocity => 2,
TrajectoryType::Acceleration => 4,
TrajectoryType::Jerk => 6,
TrajectoryType::Snap => 8,
}
}
#[derive(Debug)]
pub struct TrajectoryGenerator {
traj_type: TrajectoryType,
x_polys: Vec<Polynomial>,
y_polys: Vec<Polynomial>,
z_polys: Vec<Polynomial>,
points: Vec<Point>,
}
impl TrajectoryGenerator {
pub fn new(traj_type: TrajectoryType) -> TrajectoryGenerator {
TrajectoryGenerator {
traj_type,
x_polys: Vec::new(),
y_polys: Vec::new(),
z_polys: Vec::new(),
points: Vec::new(),
}
}
fn coeffs_at_time(&self, eqs: Vec<Polynomial>, t: f32) -> DMatrixf32 {
let numcoeffs = num_coeffs(&self.traj_type);
let mut retval = DMatrixf32::zeros(eqs.len(), numcoeffs);
for ii in 0..eqs.len() {
let mut row = DRowVectorf32::zeros(numcoeffs);
let eq = &eqs[ii];
let offset = numcoeffs - eq.coeffs.len();
for jj in 0..eq.coeffs.len() {
row[jj+offset] = eq.coeffs[jj] * t.powi(jj as i32);
}
retval.set_row(ii, &row);
}
retval
}
pub fn generate(&mut self, points: &Vec<Point>) {
self.x_polys.clear();
self.y_polys.clear();
self.z_polys.clear();
self.points.clear();
self.points = points.clone();
if points.len() < 2 {
panic!("At least 2 points are required to generate a trajectory");
}
for idx in 0..points.len()-1 {
if points[idx+1].t <= points[idx].t {
panic!("Time at index {} is less than or equal to time at index {}", idx+1, idx);
}
}
info!("Generating trajectory using {} waypoints", points.len());
let numcoeffs = num_coeffs(&self.traj_type);
let mut eqs: Vec<Polynomial> = Vec::new();
let poscoeffs:Vec<f32> = vec![1.0; numcoeffs];
let poly = Polynomial::new(&poscoeffs);
eqs.push(poly);
while eqs.len() < numcoeffs - 1 {
eqs.push(eqs.last().unwrap().derivative());
}
let n= numcoeffs * (points.len() - 1);
let mut a = DMatrixf32::zeros(n, n);
let mut bx = DColVectorf32::zeros(n);
let mut by = DColVectorf32::zeros(n);
let mut bz = DColVectorf32::zeros(n);
let mut rowidx: usize = 0;
let numeqs = (numcoeffs - 2) / 2;
let coeffs = self.coeffs_at_time(Vec::from(&eqs[1..1+numeqs]), points[0].t);
let mut m = a.slice_mut((rowidx, 0), (numeqs, numcoeffs));
for (idx, row) in coeffs.row_iter().enumerate() {
let rowvec = DRowVectorf32::from(row);
m.set_row(idx, &rowvec);
}
rowidx += numeqs;
let coeffs = self.coeffs_at_time(Vec::from(&eqs[1..1+numeqs]), points.last().unwrap().t);
let mut m = a.slice_mut((rowidx, n - numcoeffs), (numeqs, numcoeffs));
for (idx, row) in coeffs.row_iter().enumerate() {
let rowvec = DRowVectorf32::from(row);
m.set_row(idx, &rowvec);
}
rowidx += numeqs;
for idx in 0..points.len()-1 {
let startp = points[idx];
let endp = points[idx+1];
let startt = points[idx].t;
let endt = points[idx+1].t;
let col = idx * numcoeffs;
let coeffs = self.coeffs_at_time(Vec::from(&eqs[0..1]), startt);
let rowvec = DRowVectorf32::from(coeffs.row(0));
a.slice_mut((rowidx, col), (1, numcoeffs)).set_row(0, &rowvec);
bx[rowidx] = startp.x;
by[rowidx] = startp.y;
bz[rowidx] = startp.z;
rowidx += 1;
let coeffs = self.coeffs_at_time(Vec::from(&eqs[0..1]), endt);
let rowvec = DRowVectorf32::from(coeffs.row(0));
a.slice_mut((rowidx, col), (1, numcoeffs)).set_row(0, &rowvec);
bx[rowidx] = endp.x;
by[rowidx] = endp.y;
bz[rowidx] = endp.z;
rowidx += 1;
}
for idx in 0..points.len()-2 {
let endt = points[idx+1].t;
let mut col = idx * numcoeffs;
let mut coeffs = self.coeffs_at_time(Vec::from(&eqs[1..numcoeffs-1]), endt);
let mut m = a.slice_mut((rowidx, col), (numcoeffs-2, numcoeffs));
for (ii, row) in coeffs.row_iter().enumerate() {
let rowvec = DRowVectorf32::from(row);
m.set_row(ii, &rowvec);
}
col += numcoeffs;
coeffs.neg_mut();
let mut m = a.slice_mut((rowidx, col), (numcoeffs-2, numcoeffs));
for (ii, row) in coeffs.row_iter().enumerate() {
let rowvec = DRowVectorf32::from(row);
m.set_row(ii, &rowvec);
}
rowidx += numeqs;
}
let ainv = a.try_inverse()
.expect("Failed to invert A matrix!");
let ansx = &ainv * bx;
let ansy = &ainv * by;
let ansz = &ainv * bz;
for idx in 0..points.len()-1 {
let offset = idx * numcoeffs;
let mut arrx: Vec<f32> = Vec::new();
let mut arry: Vec<f32> = Vec::new();
let mut arrz: Vec<f32> = Vec::new();
for ii in offset..offset+numcoeffs {
arrx.push(ansx[(ii, 0)]);
arry.push(ansy[(ii, 0)]);
arrz.push(ansz[(ii, 0)]);
}
self.x_polys.push(Polynomial::new(&Vec::from(arrx)));
self.y_polys.push(Polynomial::new(&Vec::from(arry)));
self.z_polys.push(Polynomial::new(&Vec::from(arrz)));
}
info!("Finished generating trajectory using {} waypoints", points.len());
}
fn poly_index(&self, t: f32) -> Option<usize> {
if self.points.is_empty() {
None
} else {
if t < self.points[0].t {
Some(0)
} else if t > self.points.last().unwrap().t {
Some(self.x_polys.len() - 1)
} else {
let mut idx= 0;
for ii in 0..self.points.len()-1 {
let startp = self.points[ii];
let endp = self.points[ii+1];
if t >= startp.t && t <= endp.t {
idx = ii;
break;
}
}
Some(idx)
}
}
}
pub fn get_value(&self, t: f32, derivative: u8) -> Option<Point> {
let idx = self.poly_index(t)
.expect("A trajectory must be generated first");
let mut t = t;
if t < self.points.first().unwrap().t {
t = self.points.first().unwrap().t;
} else if t > self.points.last().unwrap().t {
t = self.points.last().unwrap().t;
}
let mut x_poly = self.x_polys[idx].clone();
let mut y_poly = self.y_polys[idx].clone();
let mut z_poly = self.z_polys[idx].clone();
let mut derivative = derivative;
loop {
if derivative == 0 {
break;
}
x_poly = x_poly.derivative();
y_poly = y_poly.derivative();
z_poly = z_poly.derivative();
derivative -= 1;
}
Some(Point{
x: x_poly.eval(t),
y: y_poly.eval(t),
z: z_poly.eval(t),
t
})
}
pub fn get_position(&self, t: f32) -> Option<Point> {
return self.get_value(t, 0);
}
pub fn get_velocity(&self, t: f32) -> Option<Point> {
return self.get_value(t, 1);
}
pub fn get_acceleration(&self, t: f32) -> Option<Point> {
return self.get_value(t, 2);
}
pub fn get_jerk(&self, t: f32) -> Option<Point> {
return self.get_value(t, 3);
}
pub fn get_values(&self, start: f32, end: f32, step: f32, derivative: u8) -> Vec<Point> {
if end <= start {
panic!("End must not be before start");
}
if step <= 0. {
panic!("Step must be non-zero");
}
let mut values: Vec<Point> = Vec::new();
let mut t = start;
loop {
values.push(self.get_value(t, derivative).unwrap());
t += step;
if t > end {
break;
}
}
values
}
pub fn get_positions(&self, start: f32, end: f32, step: f32) -> Vec<Point> {
return self.get_values(start, end, step, 0);
}
pub fn get_velocities(&self, start: f32, end: f32, step: f32) -> Vec<Point> {
return self.get_values(start, end, step, 1);
}
pub fn get_accelerations(&self, start: f32, end: f32, step: f32) -> Vec<Point> {
return self.get_values(start, end, step, 2);
}
pub fn get_jerks(&self, start: f32, end: f32, step: f32) -> Vec<Point> {
return self.get_values(start, end, step, 3);
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert::close;
#[test]
fn point_new() {
let point = Point::new(5.2, -6.75, 0.23, 2.32);
assert_eq!(5.2, point.x);
assert_eq!(-6.75, point.y);
assert_eq!(0.23, point.z);
assert_eq!(2.32, point.t);
}
#[test]
fn point_fmt() {
let point = Point::new(5.2, -6.75, 0.23, 2.32);
assert_eq!("Point(x: 5.2, y: -6.75, z: 0.23, t: 2.32)", format!("{}", point))
}
#[test]
fn coeffs_for_traj_type() {
assert_eq!(2, num_coeffs(&TrajectoryType::Velocity));
assert_eq!(4, num_coeffs(&TrajectoryType::Acceleration));
assert_eq!(6, num_coeffs(&TrajectoryType::Jerk));
assert_eq!(8, num_coeffs(&TrajectoryType::Snap));
}
#[test]
fn poly_new() {
let poly = Polynomial::new(&vec![7.0f32, -2.0f32, 3.0f32]);
assert_eq!(3, poly.coeffs.len());
assert_eq!(7.0, poly.coeffs[0]);
assert_eq!(-2.0, poly.coeffs[1]);
assert_eq!(3.0, poly.coeffs[2]);
}
#[test]
#[should_panic]
fn poly_new_invalid() {
Polynomial::new(&Vec::new());
}
#[test]
fn poly_eval() {
let poly = Polynomial::new(&vec![7., -2., 3., -4.]);
assert_eq!(7.0, poly.eval(0.0));
assert_eq!(4.0, poly.eval(1.0));
assert_eq!(-1380.0261, poly.eval(7.26));
}
#[test]
fn poly_derivative() {
let poly = Polynomial::new(&vec![7.0f32, -2.0f32, 3.0f32]);
let poly = poly.derivative();
assert_eq!(2, poly.coeffs.len());
assert_eq!(-2.0, poly.coeffs[0]);
assert_eq!(6.0, poly.coeffs[1]);
let poly = poly.derivative();
assert_eq!(1, poly.coeffs.len());
assert_eq!(6.0, poly.coeffs[0]);
let poly = poly.derivative();
assert_eq!(1, poly.coeffs.len());
assert_eq!(0.0, poly.coeffs[0]);
}
#[test]
fn poly_print() {
let poly = Polynomial::new(&vec![7.0f32, -2.0f32, 3.0f32]);
assert_eq!(poly.to_string(), "f(t) = + 7 - 2t + 3t^2");
let poly = poly.derivative();
assert_eq!(poly.to_string(), "f(t) = - 2 + 6t");
let poly = Polynomial::new(&vec![7.0f32, 0.0f32, 3.0f32]);
assert_eq!(poly.to_string(), "f(t) = + 7 + 3t^2");
let poly = Polynomial::new(&vec![0.0f32, -2.1f32, 3.9f32]);
assert_eq!(poly.to_string(), "f(t) = - 2.1t + 3.9t^2");
let poly = Polynomial::new(&vec![0.0f32, 0.0f32, 0.0f32]);
assert_eq!(poly.to_string(), "f(t) =");
}
fn assert_poly_close(expected: Vec<f32>, actual: &Polynomial, delta: f32) {
assert_eq!(expected.len(), actual.coeffs.len());
for idx in 0..expected.len() {
close(expected[idx], actual.coeffs[idx], delta);
}
}
#[test]
fn traj_gen_new() {
let traj_gen = TrajectoryGenerator::new(TrajectoryType::Jerk);
assert_eq!(TrajectoryType::Jerk, traj_gen.traj_type);
assert_eq!(0, traj_gen.x_polys.len());
assert_eq!(0, traj_gen.y_polys.len());
assert_eq!(0, traj_gen.z_polys.len());
assert_eq!(0, traj_gen.points.len());
}
#[test]
#[should_panic]
fn traj_generate_empty_inputs() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Jerk);
let points: Vec<Point> = Vec::new();
traj_gen.generate(&points);
}
#[test]
#[should_panic]
fn traj_generate_single_point() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Jerk);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.)];
traj_gen.generate(&points);
}
#[test]
fn traj_generate_two_points_velocity() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Velocity);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.)];
traj_gen.generate(&points);
assert_eq!(1, traj_gen.x_polys.len());
assert_eq!(1, traj_gen.y_polys.len());
assert_eq!(1, traj_gen.z_polys.len());
let delta = 1e-9;
assert_poly_close(vec![1., 1.], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![2., 1.], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![3., 1.], &traj_gen.z_polys[0], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
}
#[test]
fn traj_generate_two_points_acceleration() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Acceleration);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.), Point::new(2., 3., 4., 1.)];
traj_gen.generate(&points);
assert_eq!(1, traj_gen.x_polys.len());
assert_eq!(1, traj_gen.y_polys.len());
assert_eq!(1, traj_gen.z_polys.len());
let delta = 1e-9;
assert_poly_close(vec![1., 0., 3., -2.], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![2., 0., 3., -2.], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![3., 0., 3., -2.], &traj_gen.z_polys[0], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
}
#[test]
fn traj_generate_two_points_jerk() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Jerk);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.), Point::new(2., 3., 4., 1.)];
traj_gen.generate(&points);
assert_eq!(1, traj_gen.x_polys.len());
assert_eq!(1, traj_gen.y_polys.len());
assert_eq!(1, traj_gen.z_polys.len());
let delta = 1e-4;
assert_poly_close(vec![1., 0., 0., 10., -15., 6.], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![2., 0., 0., 10., -15., 6.], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![3., 0., 0., 10., -15., 6.], &traj_gen.z_polys[0], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
}
#[test]
fn traj_generate_two_points_snap() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.), Point::new(2., 3., 4., 1.)];
traj_gen.generate(&points);
assert_eq!(1, traj_gen.x_polys.len());
assert_eq!(1, traj_gen.y_polys.len());
assert_eq!(1, traj_gen.z_polys.len());
let delta = 1e-2;
assert_poly_close(vec![1., 0., 0., 0., 35., -84., 70., -20.], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![2., 0., 0., 0., 35., -84., 70., -20.], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![3., 0., 0., 0., 35., -84., 70., -20.], &traj_gen.z_polys[0], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
}
#[test]
fn traj_generate_multi_points_velocity() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Velocity);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
assert_eq!(2, traj_gen.x_polys.len());
assert_eq!(2, traj_gen.y_polys.len());
assert_eq!(2, traj_gen.z_polys.len());
let delta = 1e-9;
assert_poly_close(vec![1., 1.], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![1., 1.], &traj_gen.x_polys[1], delta);
assert_poly_close(vec![2., 1.], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![2., 1.], &traj_gen.y_polys[1], delta);
assert_poly_close(vec![3., 1.], &traj_gen.z_polys[0], delta);
assert_poly_close(vec![3., 1.], &traj_gen.z_polys[1], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
close(2., traj_gen.x_polys[1].eval(1.), delta);
close(3., traj_gen.y_polys[1].eval(1.), delta);
close(4., traj_gen.z_polys[1].eval(1.), delta);
close(3., traj_gen.x_polys[1].eval(2.), delta);
close(4., traj_gen.y_polys[1].eval(2.), delta);
close(5., traj_gen.z_polys[1].eval(2.), delta);
}
#[test]
fn traj_generate_multi_points_acceleration() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Acceleration);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
assert_eq!(2, traj_gen.x_polys.len());
assert_eq!(2, traj_gen.y_polys.len());
assert_eq!(2, traj_gen.z_polys.len());
let delta = 1e-4;
assert_poly_close(vec![1., 0., 1.5, -0.5], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![1., 0., 1.5, -0.5], &traj_gen.x_polys[1], delta);
assert_poly_close(vec![2., 0., 1.5, -0.5], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![2., 0., 1.5, -0.5], &traj_gen.y_polys[1], delta);
assert_poly_close(vec![3., 0., 1.5, -0.5], &traj_gen.z_polys[0], delta);
assert_poly_close(vec![3., 0., 1.5, -0.5], &traj_gen.z_polys[1], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
close(2., traj_gen.x_polys[1].eval(1.), delta);
close(3., traj_gen.y_polys[1].eval(1.), delta);
close(4., traj_gen.z_polys[1].eval(1.), delta);
close(3., traj_gen.x_polys[1].eval(2.), delta);
close(4., traj_gen.y_polys[1].eval(2.), delta);
close(5., traj_gen.z_polys[1].eval(2.), delta);
}
#[test]
fn traj_generate_multi_points_jerk() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Jerk);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
assert_eq!(2, traj_gen.x_polys.len());
assert_eq!(2, traj_gen.y_polys.len());
assert_eq!(2, traj_gen.z_polys.len());
let delta = 1e-4;
assert_poly_close(vec![1., 0., 0., 2.5, -1.875, 0.375], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![1., 0., 0., 2.5, -1.875, 0.375], &traj_gen.x_polys[1], delta);
assert_poly_close(vec![2., 0., 0., 2.5, -1.875, 0.375], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![2., 0., 0., 2.5, -1.875, 0.375], &traj_gen.y_polys[1], delta);
assert_poly_close(vec![3., 0., 0., 2.5, -1.875, 0.375], &traj_gen.z_polys[0], delta);
assert_poly_close(vec![3., 0., 0., 2.5, -1.875, 0.375], &traj_gen.z_polys[1], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
close(2., traj_gen.x_polys[1].eval(1.), delta);
close(3., traj_gen.y_polys[1].eval(1.), delta);
close(4., traj_gen.z_polys[1].eval(1.), delta);
close(3., traj_gen.x_polys[1].eval(2.), delta);
close(4., traj_gen.y_polys[1].eval(2.), delta);
close(5., traj_gen.z_polys[1].eval(2.), delta);
}
#[test]
fn traj_generate_multi_points_snap() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
assert_eq!(2, traj_gen.x_polys.len());
assert_eq!(2, traj_gen.y_polys.len());
assert_eq!(2, traj_gen.z_polys.len());
let delta = 1e-2;
assert_poly_close(vec![1., 0., 0., 0., 4.375, -5.25, 2.1875, -0.3125], &traj_gen.x_polys[0], delta);
assert_poly_close(vec![1., 0., 0., 0., 4.375, -5.25, 2.1875, -0.3125], &traj_gen.x_polys[1], delta);
assert_poly_close(vec![2., 0., 0., 0., 4.375, -5.25, 2.1875, -0.3125], &traj_gen.y_polys[0], delta);
assert_poly_close(vec![2., 0., 0., 0., 4.375, -5.25, 2.1875, -0.3125], &traj_gen.y_polys[1], delta);
assert_poly_close(vec![3., 0., 0., 0., 4.375, -5.25, 2.1875, -0.3125], &traj_gen.z_polys[0], delta);
assert_poly_close(vec![3., 0., 0., 0., 4.375, -5.25, 2.1875, -0.3125], &traj_gen.z_polys[1], delta);
close(1., traj_gen.x_polys[0].eval(0.), delta);
close(2., traj_gen.y_polys[0].eval(0.), delta);
close(3., traj_gen.z_polys[0].eval(0.), delta);
close(2., traj_gen.x_polys[0].eval(1.), delta);
close(3., traj_gen.y_polys[0].eval(1.), delta);
close(4., traj_gen.z_polys[0].eval(1.), delta);
close(2., traj_gen.x_polys[1].eval(1.), delta);
close(3., traj_gen.y_polys[1].eval(1.), delta);
close(4., traj_gen.z_polys[1].eval(1.), delta);
close(3., traj_gen.x_polys[1].eval(2.), delta);
close(4., traj_gen.y_polys[1].eval(2.), delta);
close(5., traj_gen.z_polys[1].eval(2.), delta);
}
#[test]
#[should_panic]
fn traj_poly_index_invalid() {
let traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
traj_gen.poly_index(1.23).expect("This should fail");
}
#[test]
fn traj_poly_index() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
assert_eq!(0, traj_gen.poly_index(-1.23).unwrap());
assert_eq!(1, traj_gen.poly_index(5.23).unwrap());
assert_eq!(0, traj_gen.poly_index(0.5).unwrap());
assert_eq!(1, traj_gen.poly_index(1.5).unwrap());
assert_eq!(0, traj_gen.poly_index(0.).unwrap());
assert_eq!(0, traj_gen.poly_index(1.).unwrap());
assert_eq!(1, traj_gen.poly_index(2.).unwrap());
}
#[test]
fn traj_get_value() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
let delta = 1e-3;
close(1., traj_gen.get_value(0., 0).unwrap().x, delta);
close(2., traj_gen.get_value(0., 0).unwrap().y, delta);
close(3., traj_gen.get_value(0., 0).unwrap().z, delta);
close(2., traj_gen.get_value(1., 0).unwrap().x, delta);
close(3., traj_gen.get_value(1., 0).unwrap().y, delta);
close(4., traj_gen.get_value(1., 0).unwrap().z, delta);
close(3., traj_gen.get_value(2., 0).unwrap().x, delta);
close(4., traj_gen.get_value(2., 0).unwrap().y, delta);
close(5., traj_gen.get_value(2., 0).unwrap().z, delta);
close(2.187, traj_gen.get_value(1., 1).unwrap().x, delta);
close(2.187, traj_gen.get_value(1., 1).unwrap().y, delta);
close(2.187, traj_gen.get_value(1., 1).unwrap().z, delta);
close(0., traj_gen.get_value(1., 2).unwrap().x, delta);
close(0., traj_gen.get_value(1., 2).unwrap().y, delta);
close(0., traj_gen.get_value(1., 2).unwrap().z, delta);
}
#[test]
fn traj_convenience() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
let delta = 1e-2;
close(3., traj_gen.get_position(1.).unwrap().y, delta);
close(0., traj_gen.get_velocity(0.).unwrap().y, delta);
close(0., traj_gen.get_acceleration(0.).unwrap().z, delta);
close(0., traj_gen.get_jerk(0.).unwrap().x, delta);
close(0., traj_gen.get_velocity(2.).unwrap().y, delta);
close(0., traj_gen.get_acceleration(2.).unwrap().z, delta);
close(0., traj_gen.get_jerk(2.).unwrap().x, delta);
}
#[test]
fn traj_get_values() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
let points = traj_gen.get_values(0., 2., 0.2, 0);
assert_eq!(10, points.len());
let points = traj_gen.get_values(-1., 3., 0.2, 0);
assert_eq!(20, points.len());
}
#[test]
#[should_panic]
fn traj_get_values_bad_range() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
traj_gen.get_values(2., 1., 0.2, 0);
}
#[test]
#[should_panic]
fn traj_get_values_bad_step() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
traj_gen.get_values(2., 1., -0.2, 0);
}
#[test]
fn traj_convenience_sample() {
let mut traj_gen = TrajectoryGenerator::new(TrajectoryType::Snap);
let points: Vec<Point> = vec![Point::new(1., 2., 3., 0.),
Point::new(2., 3., 4., 1.),
Point::new(3., 4., 5., 2.)];
traj_gen.generate(&points);
assert_eq!(10, traj_gen.get_positions(0., 2., 0.2).len());
assert_eq!(10, traj_gen.get_velocities(0., 2., 0.2).len());
assert_eq!(10, traj_gen.get_accelerations(0., 2., 0.2).len());
assert_eq!(10, traj_gen.get_jerks(0., 2., 0.2).len());
}
}