bcar 0.2.1

BCar is a Rust library with basic bicycle car computations.
Documentation
//! Compute entries to the parking slot

use crate::bcar::BCar;
use crate::geometry::Point;
use crate::geometry::Pose;
use crate::geometry::PoseRange;
use crate::geometry;
use crate::properties::Motion;
use crate::properties::Size;
use crate::pslot::PSlot;

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct EntryPlanner {
    car_size: Size,
    slot: PSlot,
}

impl EntryPlanner {
    pub fn new(slot: PSlot, car_size: Size) -> EntryPlanner {
        EntryPlanner {slot, car_size}
    }

    /// Return if the `car` is inside the `slot`.
    ///
    /// Examples
    /// ========
    ///
    ///     let c = bcar::BCar::default();
    ///     let s = bcar::PSlot::default();
    ///     let p = bcar::EntryPlanner::new(s, c.size);
    ///     let mut c = c;
    ///     c.pose.x = 1.1;
    ///     c.pose.y = -1.1;
    ///     let p = bcar::EntryPlanner::new(s, c.size);
    ///     assert!(p.parked(c));
    ///
    pub fn parked(&self, car: BCar) -> bool {
        return self.slot.inside(car.lf())
            && self.slot.inside(car.lr())
            && self.slot.inside(car.rr())
            && self.slot.inside(car.rf())
    }

    /// Return if the front of the `car` is outside, so able to leave the slot.
    ///
    /// Use this method when going out of the parking slot and want to check if
    /// the car is finally out.
    ///
    /// Examples
    /// ========
    ///
    ///     let c = bcar::BCar::default();
    ///     let s = bcar::PSlot::default();
    ///     let p = bcar::EntryPlanner::new(s, c.size);
    ///     let mut c = c;
    ///     c.pose.x = 1.1;
    ///     c.pose.y = -1.1;
    ///     let p = bcar::EntryPlanner::new(s, c.size);
    ///     assert!(!p.unparked(c));
    ///
    pub fn unparked(&self, car: BCar) -> bool {
        return !self.slot.inside(car.lf())
            && !self.slot.inside(car.rf())
    }

    /// Return if car `c` collide with the parking slot `self.slot`.
    ///
    /// Check each frame line of the car with each frame line of the slot.
    ///
    pub fn collide(&self, c: BCar) -> Option<Point> {
        let b0 = self.slot.border[0];
        let b1 = self.slot.border[1];
        let b2 = self.slot.border[2];
        let b3 = self.slot.border[3];
        let c0 = c.lf();
        let c1 = c.lr();
        let c2 = c.rr();
        let c3 = c.rf();
        match geometry::intersect_line_line(b0, b1, c0, c1) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b0, b1, c1, c2) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b0, b1, c2, c3) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b0, b1, c3, c0) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b1, b2, c0, c1) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b1, b2, c1, c2) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b1, b2, c2, c3) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b1, b2, c3, c0) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b2, b3, c0, c1) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b2, b3, c1, c2) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b2, b3, c2, c3) {
            Some(i) => return Some(i),
            None => {},
        }
        match geometry::intersect_line_line(b2, b3, c3, c0) {
            Some(i) => return Some(i),
            None => {},
        }
        None
    }

    /// Drive the car `c` out ouf `self.slot` and return `True` if success.
    pub fn drive_out_of_slot(&self, c: &mut BCar) -> (u8, Vec<BCar>) {
        // some assertions to make it easier for now
        assert!(self.car_size.length < self.slot.length());
        assert!(self.slot.right());
        assert!(self.slot.parallel());
        assert!(self.car_size.width < self.car_size.length);
        assert!(self.car_size.width < self.slot.width());

        let mut out: Vec<BCar> = Vec::new();
        out.push(*c);
        let mut cusp = 0;
        loop {
            if self.unparked(*c) {
                //print!(" {:?}", cusp);
                //eprintln!(" {:?}", cusp);
                return (cusp, out);
            }
            c.next();
            if let Some(_i) = self.collide(*c) {
                c.motion.speed *= -1.0;
                c.next();
                c.motion.steer *= -1.0;
                cusp += 1;
                if cusp > 10 {
                    //print!(" {:?}", -1);
                    //eprintln!(" {:?}", -1);
                    return (cusp, out);
                }
            }
            out.push(*c);
        }
    }


    /// Drive the car `c` in the `self.slot`.
    pub fn drive_in_the_slot(&self, c: &mut BCar) -> (u8, Vec<BCar>) {
        // some assertions to make it easier for now
        assert!(self.car_size.length < self.slot.length());
        assert!(self.slot.right());
        assert!(self.slot.parallel());
        assert!(self.car_size.width < self.car_size.length);
        assert!(self.car_size.width < self.slot.width());

        let mut out: Vec<BCar> = Vec::new();
        out.push(*c);
        let mut cusp = 0;
        loop {
            if self.parked(*c) {
                return (cusp, out);
            }
            c.next();
            if let Some(_i) = self.collide(*c) {
                c.motion.speed *= -1.0;
                c.next();
                c.motion.steer *= -1.0;
                cusp += 1;
                if cusp > 10 {
                    return (cusp, out);
                }
            }
            out.push(*c);
        }
    }

    /// Return goal of left rear in parking slot.
    ///
    /// Use `1e-3` offset to avoid init collision.
    ///
    pub fn guess_goal_by_reversed(&self) -> BCar {
        // some assertions to make it easier for now
        assert!(self.car_size.length < self.slot.length());
        assert!(self.slot.right());
        assert!(self.slot.parallel());
        assert!(self.car_size.width < self.car_size.length);
        assert!(self.car_size.width < self.slot.width());

        let pi = std::f64::consts::PI;
        let cdist = 1e-3 // move from the slot frame
            + self.car_size.length
            - self.car_size.distance_to_front;
        let cw2 = 1e-3 // move from the slot frame
            + self.car_size.width/2.0;
        BCar::new(
            Pose::new(
                self.slot.border[0].x
                + cdist * self.slot.heading().cos()
                - cw2 * (self.slot.heading() + pi/2.0).cos()
                ,
                self.slot.border[0].y
                + cdist * self.slot.heading().sin()
                - cw2 * (self.slot.heading() + pi/2.0).sin()
                ,
                self.slot.heading()
                ,
            ),
            self.car_size,
            Motion::new(1e-3, self.car_size.max_steer()),
        )
    }

    /// Use `1e-3` offset to avoid init collision.
    ///
    pub fn find_goals_by_bfs(&self) -> Vec<BCar> {
        // some assertions to make it easier for now
        assert!(self.car_size.length < self.slot.length());
        assert!(self.slot.right());
        assert!(self.slot.parallel());
        assert!(self.car_size.width < self.car_size.length);
        assert!(self.car_size.width < self.slot.width());

        // entries starts
        let mut queue: Vec<BCar> = Vec::new();
        let pi = std::f64::consts::PI;
        let cdist = -1e-3 // move from the slot frame
            + self.slot.length()
            - self.car_size.distance_to_front;
        let cw2 = 1e-3 // move from the slot frame
            + self.car_size.width/2.0;
        let mut c = BCar::new(
            Pose::new(
                self.slot.border[0].x
                + cdist * self.slot.heading().cos()
                + cw2 * (self.slot.heading() + pi/2.0).cos()
                ,
                self.slot.border[0].y
                + cdist * self.slot.heading().sin()
                + cw2 * (self.slot.heading() + pi/2.0).sin()
                ,
                self.slot.heading()
                ,
            ),
            self.car_size,
            Motion::new(-1e-3, self.car_size.max_steer()),
        );
        let max_to_slot = match geometry::intersect_circle_line(
            self.slot.border[3], c.size.length,
            self.slot.border[1], self.slot.border[2],
        ) {
            Some((i1, i2)) => {
                geometry::angle_between_closer(
                    c.rr(), self.slot.border[3], i1, i2
                )
            },
            None => panic!("there must be intersection"),
        };
        let mut a_to_slot = 0.0;
        let d_to_slot = 1e-4;
        while a_to_slot < max_to_slot {
            a_to_slot += d_to_slot;
            c.pose.rotate(self.slot.border[3], d_to_slot);
            let ccl_rr = geometry::edist(c.ccl(), c.rr());
            let ccl_p1 = geometry::edist(c.ccl(), self.slot.border[0]);
            if ccl_rr < ccl_p1 { continue; } // passes the slot
            queue.push(c);
        }

        let mut goals: Vec<(u8, Vec<BCar>)> = Vec::new();
        while let Some(mut c) = queue.pop() {
            goals.push(self.drive_in_the_slot(&mut c));
        }

        // Get goals with minimum cusp.
        let mut min = goals[0].0;
        for g in &goals {
            if g.0 < min { min = g.0; }
        }
        let mut min_goals: Vec<(u8, Vec<BCar>)> = Vec::new();
        for g in goals {
            if g.0 == min { min_goals.push(g); }
        }
        // Get entries with minimum cusp.
        let mut outs: Vec<(u8, Vec<BCar>)> = Vec::new();
        for g in &min_goals {
            let mut c = g.1[g.1.len() - 1];
            c.motion.speed *= -1.0;
            outs.push(self.drive_out_of_slot(&mut c));
        }
        let mut min = outs[0].0;
        for o in &outs {
            if o.0 < min { min = o.0; }
        }
        let mut min_outs: Vec<(u8, Vec<BCar>)> = Vec::new();
        for o in outs {
            if o.0 == min { min_outs.push(o); }
        }
        let mut goals: Vec<BCar> = Vec::new();
        for o in &min_outs {
            goals.push(o.1[0]);
        }
        goals
    }

    pub fn find_entries_by_bfs(&self) -> PoseRange {
        let goals = self.find_goals_by_bfs();
        let mut entries: Vec<BCar> = Vec::new();
        for g in goals {
            let mut c = g;
            let out = self.drive_out_of_slot(&mut c);
            entries.push(out.1[out.1.len() - 1]);
        }
        PoseRange::from_poses(entries[0].pose, entries[entries.len() - 1].pose)
    }
}