mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
stop right BEFORE intersections
This commit is contained in:
parent
0a0740c1bf
commit
d0744083fc
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 = "*"
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user