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 polyline;
mod pt;
mod util;
pub use angle::Angle;
pub use bounds::Bounds;
use dimensioned::si;
pub use gps::LonLat;
pub use line::Line;
pub use polyline::PolyLine;
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 {util, Angle, Pt2D};
use {line_intersection, Angle, Pt2D, EPSILON_DIST};
// Segment, technically
#[derive(Serialize, Deserialize, Debug)]
@ -33,7 +33,7 @@ impl Line {
if !self.intersects(other) {
None
} 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 {
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);
}
@ -98,9 +98,9 @@ impl Line {
pub fn contains_pt(&self, pt: Pt2D) -> bool {
let dist = Line(self.0, pt).length() + Line(pt, self.1).length() - self.length();
if dist < 0.0 * si::M {
-1.0 * dist < util::EPSILON_METERS
-1.0 * dist < EPSILON_DIST
} else {
dist < util::EPSILON_METERS
dist < EPSILON_DIST
}
}
}

View File

@ -2,7 +2,7 @@ use dimensioned::si;
use graphics::math::Vec2d;
use std::f64;
use std::fmt;
use {util, Angle, Line, Pt2D};
use {line_intersection, Angle, Line, Pt2D, EPSILON_DIST};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PolyLine {
@ -59,7 +59,7 @@ impl PolyLine {
let l = Line::new(pair[0], pair[1]);
let length = l.length();
let epsilon = if idx == self.pts.len() - 2 {
util::EPSILON_METERS
EPSILON_DIST
} else {
0.0 * si::M
};
@ -115,7 +115,7 @@ impl PolyLine {
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
// 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 {
result.push(l1.pt1());
@ -248,8 +248,8 @@ impl fmt::Display for PolyLine {
#[test]
fn shift_polyline_equivalence() {
use line_intersection;
use rand;
use util::line_intersection;
let scale = 1000.0;
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.start_many_parked_cars(&map, 100);
let mut counter = 0;
let mut benchmark = sim.start_benchmark();
loop {
counter += 1;
sim.step(&map, &control_map);
if counter % 1000 == 0 {
if sim.time.is_multiple_of_minute() {
let speed = sim.measure_speed(&mut benchmark);
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" }
geom = { path = "../geom" }
map_model = { path = "../map_model" }
more-asserts = "0.2.1"
multimap = "0.4.0"
ordered-float = "0.5.0"
piston2d-graphics = "*"

View File

@ -162,7 +162,12 @@ impl Car {
loop {
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;
}
let next_on = match self.on {
@ -178,7 +183,15 @@ impl Car {
self.waiting_for = None;
self.on = next_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;
}
@ -213,7 +226,7 @@ impl SimQueue {
) -> Result<(), InvariantViolated> {
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.extend(ids);
// Sort descending.

View File

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

View File

@ -1,8 +1,15 @@
use dimensioned::si;
use models::FOLLOWING_DISTANCE;
use std;
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 handle floating point issues uniformly here
pub struct Vehicle {
// > 0
@ -36,7 +43,11 @@ impl Vehicle {
// TODO this needs unit tests and some careful checking
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)
// 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),
// then stop as fast as possible.
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_dist = dist_at_constant_accel(max_next_accel, TIMESTEP, current_speed);
let max_next_speed = current_speed + max_next_accel * TIMESTEP;
@ -69,7 +80,7 @@ impl Vehicle {
// TODO share with max_lookahead_dist
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);
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)
};
let dist = (initial_speed * actual_time) + (0.5 * accel * (actual_time * actual_time));
assert!(dist >= 0.0 * si::M);
let new_speed = initial_speed + (accel * actual_time);
assert!(new_speed >= 0.0 * si::MPS);
assert_ge!(dist, 0.0 * si::M);
let mut new_speed = initial_speed + (accel * actual_time);
// 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)
}

View File

@ -9,6 +9,8 @@ extern crate ezgui;
extern crate geom;
extern crate graphics;
extern crate map_model;
#[macro_use]
extern crate more_asserts;
extern crate multimap;
extern crate ordered_float;
#[macro_use]
@ -86,6 +88,11 @@ impl Tick {
pub fn increment(&mut self) {
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 {