#![cfg_attr(feature = "path-traits", doc = "```")]
#![cfg_attr(feature = "path-traits", doc = "use clothoid::ClothoidArc;")]
#![cfg_attr(feature = "path-traits", doc = "use clothoid::optimizer::Pose;")]
#![cfg_attr(
feature = "path-traits",
doc = "use path_traits::{Path, Heading, Curved};"
)]
#![cfg_attr(feature = "path-traits", doc = "")]
#![cfg_attr(feature = "path-traits", doc = "let arc = ClothoidArc {")]
#![cfg_attr(feature = "path-traits", doc = " start: Pose::new(0.0, 0.0, 0.0),")]
#![cfg_attr(feature = "path-traits", doc = " ks: 0.0,")]
#![cfg_attr(feature = "path-traits", doc = " ke: 1.0,")]
#![cfg_attr(feature = "path-traits", doc = " length: 5.0,")]
#![cfg_attr(feature = "path-traits", doc = " n_steps: 256,")]
#![cfg_attr(feature = "path-traits", doc = "};")]
#![cfg_attr(feature = "path-traits", doc = "")]
#![cfg_attr(feature = "path-traits", doc = "let pt = arc.sample_at(2.5).unwrap();")]
#![cfg_attr(
feature = "path-traits",
doc = "let heading = arc.heading_at(2.5).unwrap();"
)]
#![cfg_attr(
feature = "path-traits",
doc = "let curvature = arc.curvature_at(2.5).unwrap();"
)]
#![cfg_attr(feature = "path-traits", doc = "```")]
#![cfg_attr(feature = "path-traits", doc = "```")]
#![cfg_attr(feature = "path-traits", doc = "use clothoid::Clothoid;")]
#![cfg_attr(feature = "path-traits", doc = "use path_traits::Path;")]
#![cfg_attr(feature = "path-traits", doc = "")]
#![cfg_attr(feature = "path-traits", doc = "let c = Clothoid::new(2.0);")]
#![cfg_attr(feature = "path-traits", doc = "let arc = c.into_arc(4.0);")]
#![cfg_attr(
feature = "path-traits",
doc = "assert!((arc.length() - 4.0).abs() < 1e-10);"
)]
#![cfg_attr(feature = "path-traits", doc = "```")]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(any(feature = "nelder-mead", feature = "cma-es"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "nelder-mead", feature = "cma-es"))))]
pub mod fit;
pub mod optimizer;
#[cfg(any(feature = "nelder-mead", feature = "cma-es"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "nelder-mead", feature = "cma-es"))))]
pub use fit::{DefaultPlanner, Planner};
pub use optimizer::{PlanObjective, SymmetryMode};
#[cfg(feature = "cma-es")]
#[cfg_attr(docsrs, doc(cfg(feature = "cma-es")))]
pub use optimizer::CmaEs;
#[cfg(feature = "nelder-mead")]
#[cfg_attr(docsrs, doc(cfg(feature = "nelder-mead")))]
pub use optimizer::NelderMead;
#[cfg(any(feature = "nelder-mead", feature = "cma-es"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "nelder-mead", feature = "cma-es"))))]
pub use optimizer::Optimizer;
#[cfg(feature = "path-traits")]
#[cfg_attr(docsrs, doc(cfg(feature = "path-traits")))]
pub mod path_traits_impls;
#[cfg(feature = "path-traits")]
#[cfg_attr(docsrs, doc(cfg(feature = "path-traits")))]
pub use path_traits_impls::{ArcSegment, ClothoidArc, ClothoidPath, LinearSegment, Vec2};
#[allow(dead_code)]
const PI_SQRT: f64 =
1.772_453_850_905_516_027_298_167_483_341_145_182_797_549_456_122_387_128_213_807_789_8_f64;
#[allow(dead_code)]
const INV_PI_SQRT: f64 =
0.564_189_583_547_756_286_948_079_451_560_772_585_844_050_629_328_998_856_844_085_721_7_f64;
#[derive(Debug, Default, Copy, Clone, PartialOrd, PartialEq)]
pub struct Point2 {
pub x: f64,
pub y: f64,
}
pub struct Clothoid {
a: f64,
}
impl Clothoid {
#[must_use]
pub fn new(a: f64) -> Self {
Self { a }
}
#[inline]
#[must_use]
pub fn direction_angle(&self, arc_length: f64) -> f64 {
0.5 * (arc_length * arc_length) / (self.a * self.a)
}
#[inline]
#[allow(dead_code)]
fn calculate(&self, t: f64) -> Point2 {
#[cfg(feature = "fresnel")]
{
self.calculate_fresnl(t)
}
#[cfg(not(feature = "fresnel"))]
{
self.calculate_approx(t)
}
}
#[cfg(feature = "fresnel")]
#[cfg_attr(docsrs, doc(cfg(feature = "fresnel")))]
fn calculate_fresnl(&self, t: f64) -> Point2 {
let (s, c) = fresnel::fresnl(t * INV_PI_SQRT);
Point2 {
x: self.a * PI_SQRT * s,
y: self.a * PI_SQRT * c,
}
}
#[allow(dead_code)]
fn calculate_approx(&self, t: f64) -> Point2 {
let fsc = FresnelSinCos::compute(t * INV_PI_SQRT);
Point2 {
x: self.a * PI_SQRT * fsc.sin,
y: self.a * PI_SQRT * fsc.cos,
}
}
#[cfg(feature = "path-traits")]
#[cfg_attr(docsrs, doc(cfg(feature = "path-traits")))]
#[must_use]
pub fn into_arc(self, length: f64) -> ClothoidArc {
ClothoidArc {
start: crate::optimizer::Pose::new(0.0, 0.0, 0.0),
ks: 0.0,
ke: length / (self.a * self.a),
length,
n_steps: 256,
}
}
}
#[allow(dead_code)]
struct AuxFg {
pub f: f64,
pub g: f64,
}
impl AuxFg {
#[allow(dead_code)]
pub fn compute(x: f64) -> Self {
let x2 = x * x;
let x3 = x * x * x;
let f = (1. + 0.926 * x) / (2. + 1.792 * x + 3.104 * x2);
let g = 1. / (2. + 4.142 * x + 3.492 * x2 + 6.670 * x3);
Self { f, g }
}
}
#[allow(dead_code)]
struct FresnelSinCos {
pub cos: f64,
pub sin: f64,
}
impl FresnelSinCos {
#[allow(dead_code)]
pub fn compute(x: f64) -> Self {
let aux = AuxFg::compute(x);
let (sin, cos) = (x * x * std::f64::consts::FRAC_PI_2).sin_cos();
Self {
cos: 0.5 + aux.f * sin - aux.g * cos,
sin: 0.5 - aux.f * cos - aux.g * sin,
}
}
}
#[cfg(test)]
mod tests {
#![allow(
clippy::float_cmp,
clippy::cast_precision_loss,
clippy::unreadable_literal
)]
use super::*;
use assert_float_eq::*;
#[test]
fn it_works() {
let clothoid = Clothoid::new(1.);
let alpha = clothoid.direction_angle(0.);
assert_eq!(alpha, 0.);
}
#[test]
fn calculate() {
let clothoid = Clothoid::new(8.);
let pt = clothoid.calculate(0.);
assert_f64_near!(pt.x, 0.);
assert_f64_near!(pt.y, 0.);
let pt = clothoid.calculate_approx(std::f64::consts::PI);
assert!((pt.x - 6.77).abs() < 0.01);
assert!((pt.y - 4.59).abs() < 0.01);
}
#[test]
fn clothoid_state_defaults() {
use crate::optimizer::ClothoidState;
let state = ClothoidState::default();
assert_eq!(state.x, 0.0);
assert_eq!(state.y, 0.0);
assert_eq!(state.theta, 0.0);
}
#[test]
#[cfg(feature = "fresnel")]
fn calculate_fresnl_works() {
let clothoid = Clothoid::new(8.);
let pt = clothoid.calculate_fresnl(0.);
assert_f64_near!(pt.x, 0.);
assert_f64_near!(pt.y, 0.);
let pt = clothoid.calculate_fresnl(std::f64::consts::PI);
assert_f64_near!(pt.x, 6.7669799976205);
assert_f64_near!(pt.y, 4.615663254508842);
}
#[test]
fn calculate_approx_works() {
let clothoid = Clothoid::new(8.);
let pt = clothoid.calculate_approx(0.);
assert_f64_near!(pt.x, 0.);
assert_f64_near!(pt.y, 0.);
let pt = clothoid.calculate_approx(std::f64::consts::PI);
assert_f64_near!(pt.x, 6.777113091819308);
assert_f64_near!(pt.y, 4.588251163366395);
}
#[test]
fn pochhammer() {
fn p(a: f64, n: usize) -> f64 {
if n == 0 {
return 1.;
}
let mut product = 1.;
for i in 0..n {
product *= a + (i as f64);
}
product
}
assert_eq!(p(0.5, 0), 1.);
assert_eq!(p(0.5, 1), 0.5);
assert_eq!(p(0.5, 2), 0.75);
assert_eq!(p(0.5, 3), 1.875);
assert_eq!(p(0.5, 7), 1055.7421875);
}
}