stop right BEFORE intersections

This commit is contained in:
Dustin Carlino 2018-08-07 13:22:24 -07:00
parent 0a0740c1bf
commit d0744083fc
10 changed files with 86 additions and 53 deletions

View File

@ -12,11 +12,40 @@ mod gps;
mod line; mod line;
mod polyline; mod polyline;
mod pt; mod pt;
mod util;
pub use angle::Angle; pub use angle::Angle;
pub use bounds::Bounds; pub use bounds::Bounds;
use dimensioned::si;
pub use gps::LonLat; pub use gps::LonLat;
pub use line::Line; pub use line::Line;
pub use polyline::PolyLine; pub use polyline::PolyLine;
pub use pt::{HashablePt2D, Pt2D}; pub use pt::{HashablePt2D, Pt2D};
use std::marker;
pub(crate) const EPSILON_DIST: si::Meter<f64> = si::Meter {
value_unsafe: 0.00001,
_marker: marker::PhantomData,
};
// NOT segment. Fails for parallel lines.
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
pub(crate) fn line_intersection(l1: &Line, l2: &Line) -> Option<Pt2D> {
let x1 = l1.pt1().x();
let y1 = l1.pt1().y();
let x2 = l1.pt2().x();
let y2 = l1.pt2().y();
let x3 = l2.pt1().x();
let y3 = l2.pt1().y();
let x4 = l2.pt2().x();
let y4 = l2.pt2().y();
let numer_x = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
let numer_y = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
let denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if denom == 0.0 {
None
} else {
Some(Pt2D::new(numer_x / denom, numer_y / denom))
}
}

View File

@ -1,5 +1,5 @@
use dimensioned::si; use dimensioned::si;
use {util, Angle, Pt2D}; use {line_intersection, Angle, Pt2D, EPSILON_DIST};
// Segment, technically // Segment, technically
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -33,7 +33,7 @@ impl Line {
if !self.intersects(other) { if !self.intersects(other) {
None None
} else { } else {
util::line_intersection(self, other) line_intersection(self, other)
} }
} }
@ -63,7 +63,7 @@ impl Line {
pub fn dist_along(&self, dist: si::Meter<f64>) -> Pt2D { pub fn dist_along(&self, dist: si::Meter<f64>) -> Pt2D {
let len = self.length(); let len = self.length();
if dist > len + util::EPSILON_METERS { if dist > len + EPSILON_DIST {
panic!("cant do {} along a line of length {}", dist, len); panic!("cant do {} along a line of length {}", dist, len);
} }
@ -98,9 +98,9 @@ impl Line {
pub fn contains_pt(&self, pt: Pt2D) -> bool { pub fn contains_pt(&self, pt: Pt2D) -> bool {
let dist = Line(self.0, pt).length() + Line(pt, self.1).length() - self.length(); let dist = Line(self.0, pt).length() + Line(pt, self.1).length() - self.length();
if dist < 0.0 * si::M { if dist < 0.0 * si::M {
-1.0 * dist < util::EPSILON_METERS -1.0 * dist < EPSILON_DIST
} else { } else {
dist < util::EPSILON_METERS dist < EPSILON_DIST
} }
} }
} }

View File

@ -2,7 +2,7 @@ use dimensioned::si;
use graphics::math::Vec2d; use graphics::math::Vec2d;
use std::f64; use std::f64;
use std::fmt; use std::fmt;
use {util, Angle, Line, Pt2D}; use {line_intersection, Angle, Line, Pt2D, EPSILON_DIST};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PolyLine { pub struct PolyLine {
@ -59,7 +59,7 @@ impl PolyLine {
let l = Line::new(pair[0], pair[1]); let l = Line::new(pair[0], pair[1]);
let length = l.length(); let length = l.length();
let epsilon = if idx == self.pts.len() - 2 { let epsilon = if idx == self.pts.len() - 2 {
util::EPSILON_METERS EPSILON_DIST
} else { } else {
0.0 * si::M 0.0 * si::M
}; };
@ -115,7 +115,7 @@ impl PolyLine {
let l2 = Line::new(pt2_raw, pt3_raw).shift(width); let l2 = Line::new(pt2_raw, pt3_raw).shift(width);
// When the lines are perfectly parallel, it means pt2_shift_1st == pt2_shift_2nd and the // When the lines are perfectly parallel, it means pt2_shift_1st == pt2_shift_2nd and the
// original geometry is redundant. // original geometry is redundant.
let pt2_shift = util::line_intersection(&l1, &l2).unwrap_or(l1.pt2()); let pt2_shift = line_intersection(&l1, &l2).unwrap_or(l1.pt2());
if pt3_idx == 2 { if pt3_idx == 2 {
result.push(l1.pt1()); result.push(l1.pt1());
@ -248,8 +248,8 @@ impl fmt::Display for PolyLine {
#[test] #[test]
fn shift_polyline_equivalence() { fn shift_polyline_equivalence() {
use line_intersection;
use rand; use rand;
use util::line_intersection;
let scale = 1000.0; let scale = 1000.0;
let pt1 = Pt2D::new(rand::random::<f64>() * scale, rand::random::<f64>() * scale); let pt1 = Pt2D::new(rand::random::<f64>() * scale, rand::random::<f64>() * scale);

View File

@ -1,31 +0,0 @@
use dimensioned::si;
use std::marker;
use {Line, Pt2D};
pub(crate) const EPSILON_METERS: si::Meter<f64> = si::Meter {
value_unsafe: 0.00001,
_marker: marker::PhantomData,
};
// NOT segment. Fails for parallel lines.
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
pub(crate) fn line_intersection(l1: &Line, l2: &Line) -> Option<Pt2D> {
let x1 = l1.pt1().x();
let y1 = l1.pt1().y();
let x2 = l1.pt2().x();
let y2 = l1.pt2().y();
let x3 = l2.pt1().x();
let y3 = l2.pt1().y();
let x4 = l2.pt2().x();
let y4 = l2.pt2().y();
let numer_x = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
let numer_y = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
let denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if denom == 0.0 {
None
} else {
Some(Pt2D::new(numer_x / denom, numer_y / denom))
}
}

View File

@ -38,12 +38,10 @@ fn main() {
sim.seed_pedestrians(&map, 100); sim.seed_pedestrians(&map, 100);
sim.start_many_parked_cars(&map, 100); sim.start_many_parked_cars(&map, 100);
let mut counter = 0;
let mut benchmark = sim.start_benchmark(); let mut benchmark = sim.start_benchmark();
loop { loop {
counter += 1;
sim.step(&map, &control_map); sim.step(&map, &control_map);
if counter % 1000 == 0 { if sim.time.is_multiple_of_minute() {
let speed = sim.measure_speed(&mut benchmark); let speed = sim.measure_speed(&mut benchmark);
println!("{0}, speed = {1:.2}x", sim.summary(), speed); println!("{0}, speed = {1:.2}x", sim.summary(), speed);
} }

View File

@ -11,6 +11,7 @@ dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa
ezgui = { path = "../ezgui" } ezgui = { path = "../ezgui" }
geom = { path = "../geom" } geom = { path = "../geom" }
map_model = { path = "../map_model" } map_model = { path = "../map_model" }
more-asserts = "0.2.1"
multimap = "0.4.0" multimap = "0.4.0"
ordered-float = "0.5.0" ordered-float = "0.5.0"
piston2d-graphics = "*" piston2d-graphics = "*"

View File

@ -162,7 +162,12 @@ impl Car {
loop { loop {
let leftover_dist = self.dist_along - self.on.length(map); let leftover_dist = self.dist_along - self.on.length(map);
if leftover_dist < 0.0 * si::M { // == 0.0 is important! If no floating point imprecision happens, cars will stop RIGHT
// at the end of a lane, with exactly 0 leftover distance. We don't want to bump them
// into the turn and illegally enter the intersection in that case. The alternative
// from AORTA, IIRC, is to make cars stop anywhere in a small buffer at the end of the
// lane.
if leftover_dist <= 0.0 * si::M {
break; break;
} }
let next_on = match self.on { let next_on = match self.on {
@ -178,7 +183,15 @@ impl Car {
self.waiting_for = None; self.waiting_for = None;
self.on = next_on; self.on = next_on;
if let On::Turn(t) = self.on { if let On::Turn(t) = self.on {
intersections.on_enter(Request::for_car(self.id, t))?; // TODO easier way to attach more debug info?
intersections
.on_enter(Request::for_car(self.id, t))
.map_err(|e| {
InvariantViolated(format!(
"{}. new speed {}, leftover dist {}",
e, self.speed, leftover_dist
))
})?;
} }
self.dist_along = leftover_dist; self.dist_along = leftover_dist;
} }
@ -213,7 +226,7 @@ impl SimQueue {
) -> Result<(), InvariantViolated> { ) -> Result<(), InvariantViolated> {
let old_queue = self.cars_queue.clone(); let old_queue = self.cars_queue.clone();
assert!(ids.len() <= self.capacity); assert_le!(ids.len(), self.capacity);
self.cars_queue.clear(); self.cars_queue.clear();
self.cars_queue.extend(ids); self.cars_queue.extend(ids);
// Sort descending. // Sort descending.

View File

@ -298,6 +298,7 @@ impl TrafficSignal {
continue; continue;
} }
// How long will it take the agent to cross the turn? // How long will it take the agent to cross the turn?
// TODO assuming they accelerate!
let crossing_time = turn.length() / speeds[&agent]; let crossing_time = turn.length() / speeds[&agent];
// TODO account for TIMESTEP // TODO account for TIMESTEP

View File

@ -1,8 +1,15 @@
use dimensioned::si; use dimensioned::si;
use models::FOLLOWING_DISTANCE; use models::FOLLOWING_DISTANCE;
use std;
use {Acceleration, Distance, Speed, Time, TIMESTEP}; use {Acceleration, Distance, Speed, Time, TIMESTEP};
pub const EPSILON_SPEED: Speed = si::MeterPerSecond {
value_unsafe: 0.00000001,
_marker: std::marker::PhantomData,
};
// TODO unit test all of this // TODO unit test all of this
// TODO handle floating point issues uniformly here
pub struct Vehicle { pub struct Vehicle {
// > 0 // > 0
@ -36,7 +43,11 @@ impl Vehicle {
// TODO this needs unit tests and some careful checking // TODO this needs unit tests and some careful checking
pub fn accel_to_stop_in_dist(&self, speed: Speed, dist: Distance) -> Acceleration { pub fn accel_to_stop_in_dist(&self, speed: Speed, dist: Distance) -> Acceleration {
assert!(dist > 0.0 * si::M); assert_ge!(dist, 0.0 * si::M);
// Don't NaN out
if dist == 0.0 * si::M {
return 0.0 * si::MPS2;
}
// d = (v_1)(t) + (1/2)(a)(t^2) // d = (v_1)(t) + (1/2)(a)(t^2)
// 0 = (v_1) + (a)(t) // 0 = (v_1) + (a)(t)
@ -60,7 +71,7 @@ impl Vehicle {
// Assume we accelerate as much as possible this tick (restricted only by the speed limit), // Assume we accelerate as much as possible this tick (restricted only by the speed limit),
// then stop as fast as possible. // then stop as fast as possible.
pub fn max_lookahead_dist(&self, current_speed: Speed, speed_limit: Speed) -> Distance { pub fn max_lookahead_dist(&self, current_speed: Speed, speed_limit: Speed) -> Distance {
assert!(current_speed <= speed_limit); assert_le!(current_speed, speed_limit);
let max_next_accel = min_accel(self.max_accel, (speed_limit - current_speed) / TIMESTEP); let max_next_accel = min_accel(self.max_accel, (speed_limit - current_speed) / TIMESTEP);
let max_next_dist = dist_at_constant_accel(max_next_accel, TIMESTEP, current_speed); let max_next_dist = dist_at_constant_accel(max_next_accel, TIMESTEP, current_speed);
let max_next_speed = current_speed + max_next_accel * TIMESTEP; let max_next_speed = current_speed + max_next_accel * TIMESTEP;
@ -69,7 +80,7 @@ impl Vehicle {
// TODO share with max_lookahead_dist // TODO share with max_lookahead_dist
fn max_next_dist(&self, current_speed: Speed, speed_limit: Speed) -> Distance { fn max_next_dist(&self, current_speed: Speed, speed_limit: Speed) -> Distance {
assert!(current_speed <= speed_limit); assert_le!(current_speed, speed_limit);
let max_next_accel = min_accel(self.max_accel, (speed_limit - current_speed) / TIMESTEP); let max_next_accel = min_accel(self.max_accel, (speed_limit - current_speed) / TIMESTEP);
dist_at_constant_accel(max_next_accel, TIMESTEP, current_speed) dist_at_constant_accel(max_next_accel, TIMESTEP, current_speed)
} }
@ -154,9 +165,13 @@ pub fn results_of_accel_for_one_tick(
min_time(TIMESTEP, -1.0 * initial_speed / accel) min_time(TIMESTEP, -1.0 * initial_speed / accel)
}; };
let dist = (initial_speed * actual_time) + (0.5 * accel * (actual_time * actual_time)); let dist = (initial_speed * actual_time) + (0.5 * accel * (actual_time * actual_time));
assert!(dist >= 0.0 * si::M); assert_ge!(dist, 0.0 * si::M);
let new_speed = initial_speed + (accel * actual_time); let mut new_speed = initial_speed + (accel * actual_time);
assert!(new_speed >= 0.0 * si::MPS); // Handle some floating point imprecision
if new_speed < 0.0 * si::MPS && new_speed >= -1.0 * EPSILON_SPEED {
new_speed = 0.0 * si::MPS;
}
assert_ge!(new_speed, 0.0 * si::MPS);
(dist, new_speed) (dist, new_speed)
} }

View File

@ -9,6 +9,8 @@ extern crate ezgui;
extern crate geom; extern crate geom;
extern crate graphics; extern crate graphics;
extern crate map_model; extern crate map_model;
#[macro_use]
extern crate more_asserts;
extern crate multimap; extern crate multimap;
extern crate ordered_float; extern crate ordered_float;
#[macro_use] #[macro_use]
@ -86,6 +88,11 @@ impl Tick {
pub fn increment(&mut self) { pub fn increment(&mut self) {
self.0 += 1; self.0 += 1;
} }
// TODO er, little weird
pub fn is_multiple_of_minute(&self) -> bool {
self.0 % 600 == 0
}
} }
impl std::ops::Sub for Tick { impl std::ops::Sub for Tick {