bcar 0.2.1

BCar is a Rust library with basic bicycle car computations.
Documentation
//! Parking slot type and related methods

use crate::geometry::Point;

/// Parking slot structure.
///
///
/// Examples
/// ========
///
/// ```
/// let ps = bcar::PSlot::new_phwl(bcar::Point::new(0.0, 0.0), 0.0, 2.2, 6.86);
/// let def_ps = bcar::PSlot::default();
/// assert!(ps.border[0].x - def_ps.border[0].x < 0.00001);
/// assert!(ps.border[0].y - def_ps.border[0].y < 0.00001);
/// assert!(ps.border[1].x - def_ps.border[1].x < 0.00001);
/// assert!(ps.border[1].y - def_ps.border[1].y < 0.00001);
/// assert!(ps.border[2].x - def_ps.border[2].x < 0.00001);
/// assert!(ps.border[2].y - def_ps.border[2].y < 0.00001);
/// assert!(ps.border[3].x - def_ps.border[3].x < 0.00001);
/// assert!(ps.border[3].y - def_ps.border[3].y < 0.00001);
/// ```
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct PSlot {
    pub border: [Point; 4],
}

impl PSlot {
    pub fn new(border: [Point; 4]) -> PSlot {
        PSlot {border}
    }

    /// Return new parking slot
    ///
    /// Arguments
    /// ---------
    ///
    /// - `p` -- Parking slot first point.
    /// - `h` -- Parking slot heading.
    /// - `w` -- Parking slot width.
    /// - `l` -- Parking slot length
    pub fn new_phwl(p: Point, h: f64, w: f64, l: f64) -> PSlot {
        let pi = std::f64::consts::PI;
        let p2 = Point::new(
            p.x + w*(h - pi/2.0).cos(),
            p.y + w*(h - pi/2.0).sin()
        );
        let p3 = Point::new(p2.x + l*h.cos(), p2.y + l*h.sin());
        let p4 = Point::new(p.x + l*h.cos(), p.y + l*h.sin());
        PSlot {border: [p, p2, p3, p4]}
    }

    /// Return default `PSlot`.
    ///
    ///
    /// Examples
    /// ========
    ///
    /// ```
    /// let ps = bcar::PSlot::default();
    /// let p1 = bcar::Point::default();
    /// let p2 = bcar::Point::new(0.0, -2.2);
    /// let p3 = bcar::Point::new(6.86, -2.2);
    /// let p4 = bcar::Point::new(6.86, 0.0);
    /// assert!(ps.border[0].x - p1.x < 0.00001);
    /// assert!(ps.border[0].y - p1.y < 0.00001);
    /// assert!(ps.border[1].x - p2.x < 0.00001);
    /// assert!(ps.border[1].y - p2.y < 0.00001);
    /// assert!(ps.border[2].x - p3.x < 0.00001);
    /// assert!(ps.border[2].y - p3.y < 0.00001);
    /// assert!(ps.border[3].x - p4.x < 0.00001);
    /// assert!(ps.border[3].y - p4.y < 0.00001);
    /// ```
    pub fn default() -> PSlot {
        PSlot::new_phwl(Point::default(), 0.0, 2.2, 6.86)
    }

    /// Return the heading of the parking slot.
    ///
    /// The heading is given by the direction from the first point to
    /// the last point.
    ///
    /// Examples
    /// ========
    ///
    /// ```
    /// let ps = bcar::PSlot::default();
    /// assert!(ps.heading() - 0.0 < 0.00001);
    /// ```
    pub fn heading(&self) -> f64 {
        (self.border[3].y - self.border[0].y)
        .atan2(self.border[3].x - self.border[0].x)
    }

    /// Return the length of the parking slot.
    ///
    /// Examples
    /// ========
    ///
    ///     let ps = bcar::PSlot::default();
    ///     assert!(ps.length() - 6.86 < 0.00001);
    pub fn length(&self) -> f64 {
        (
            (self.border[3].x - self.border[0].x).powi(2)
            + (self.border[3].y - self.border[0].y).powi(2)
        ).sqrt()
    }

    /// Return the width of the parking slot.
    ///
    /// Examples
    /// ========
    ///
    ///     let ps = bcar::PSlot::default();
    ///     assert!(ps.width() - 2.6 < 1e-10);
    pub fn width(&self) -> f64 {
        (
            (self.border[1].x - self.border[0].x).powi(2)
            + (self.border[1].y - self.border[0].y).powi(2)
        ).sqrt()
    }

    /// Return if the parking slot is parallel.
    ///
    /// If not, the parking slot is perpendicular.
    ///
    /// Examples
    /// ========
    ///
    /// ```
    /// let ps = bcar::PSlot::default();
    /// assert!(ps.parallel());
    /// ```
    pub fn parallel(&self) -> bool {
        let d1 = (
            (self.border[1].x - self.border[0].x).powi(2)
            + (self.border[1].y - self.border[0].y).powi(2)
        ).sqrt();
        let d2 = (
            (self.border[2].x - self.border[1].x).powi(2)
            + (self.border[2].y - self.border[1].y).powi(2)
        ).sqrt();
        d1 < d2
    }

    /// Return if the parking slot is on the right side.
    ///
    /// Right side of line given by the first and the last point.
    ///
    /// Examples
    /// ========
    ///
    /// ```
    /// let ps = bcar::PSlot::default();
    /// assert!(ps.right());
    /// ```
    pub fn right(&self) -> bool {
        (
            (self.border[1].x - self.border[0].x)
            * (self.border[3].y - self.border[0].y)
            - (self.border[1].y - self.border[0].y)
            * (self.border[3].x - self.border[0].x)
        ) > 0.0
    }

    /// Return if the `Point p` is inside the parking slot.
    ///
    /// See https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule.
    ///
    /// Examples
    /// ========
    ///
    ///     let ps = bcar::PSlot::default();
    ///     assert!(ps.inside(bcar::Point::new(0.1, -0.1)));
    ///     assert!(ps.inside(bcar::Point::new(0.1, -2.1)));
    ///     assert!(ps.inside(bcar::Point::new(6.85, -2.1)));
    ///     assert!(ps.inside(bcar::Point::new(6.85, -0.1)));
    pub fn inside(&self, p: Point) -> bool {
        let mut inside = false;
        let mut j = 3;

        for i in 0..4 {
            if
                ((self.border[i].y > p.y) != (self.border[j].y > p.y))
                && (
                    p.x < self.border[i].x
                    + (self.border[j].x - self.border[i].x)
                    * (p.y - self.border[i].y)
                    / (self.border[j].y - self.border[i].y)
                )
            {
                inside = !inside;
            }
            j = i;
        }

        inside
    }
}