mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
removing many external dependencies on Tick. moving time parsing to
Duration.
This commit is contained in:
parent
d5181f6bf8
commit
42c7c21246
@ -1,6 +1,6 @@
|
||||
use crate::objects::DrawCtx;
|
||||
use crate::plugins::{
|
||||
choose_intersection, choose_neighborhood, choose_origin_destination, input_tick,
|
||||
choose_intersection, choose_neighborhood, choose_origin_destination, input_time,
|
||||
input_weighted_usize, load_scenario, BlockingPlugin, PluginCtx,
|
||||
};
|
||||
use abstutil::Timer;
|
||||
@ -134,9 +134,9 @@ fn edit_scenario(map: &Map, scenario: &mut Scenario, mut wizard: WrappedWizard)
|
||||
x if x == spawn => {
|
||||
scenario.spawn_over_time.push(SpawnOverTime {
|
||||
num_agents: wizard.input_usize("Spawn how many agents?")?,
|
||||
start_tick: input_tick(&mut wizard, "Start spawning when?")?,
|
||||
// TODO input interval, or otherwise enforce stop_tick > start_tick
|
||||
stop_tick: input_tick(&mut wizard, "Stop spawning when?")?,
|
||||
start_time: input_time(&mut wizard, "Start spawning when?")?,
|
||||
// TODO input interval, or otherwise enforce stop_time > start_time
|
||||
stop_time: input_time(&mut wizard, "Stop spawning when?")?,
|
||||
start_from_neighborhood: choose_neighborhood(
|
||||
map,
|
||||
&mut wizard,
|
||||
@ -155,9 +155,9 @@ fn edit_scenario(map: &Map, scenario: &mut Scenario, mut wizard: WrappedWizard)
|
||||
num_peds: wizard.input_usize("Spawn how many pedestrians?")?,
|
||||
num_cars: wizard.input_usize("Spawn how many cars?")?,
|
||||
num_bikes: wizard.input_usize("Spawn how many bikes?")?,
|
||||
start_tick: input_tick(&mut wizard, "Start spawning when?")?,
|
||||
// TODO input interval, or otherwise enforce stop_tick > start_tick
|
||||
stop_tick: input_tick(&mut wizard, "Stop spawning when?")?,
|
||||
start_time: input_time(&mut wizard, "Start spawning when?")?,
|
||||
// TODO input interval, or otherwise enforce stop_time > start_time
|
||||
stop_time: input_time(&mut wizard, "Stop spawning when?")?,
|
||||
// TODO validate it's a border!
|
||||
start_from_border: choose_intersection(
|
||||
&mut wizard,
|
||||
|
@ -6,13 +6,14 @@ pub mod view;
|
||||
use crate::colors::ColorScheme;
|
||||
use crate::objects::{DrawCtx, RenderingHints, ID};
|
||||
use crate::state::{PerMapUI, PluginsPerMap};
|
||||
use ::sim::{ABTest, OriginDestination, Scenario, Tick};
|
||||
use ::sim::{ABTest, OriginDestination, Scenario};
|
||||
use abstutil;
|
||||
use abstutil::WeightedUsizeChoice;
|
||||
use downcast::{
|
||||
downcast, downcast_methods, downcast_methods_core, downcast_methods_std, impl_downcast, Any,
|
||||
};
|
||||
use ezgui::{Canvas, Color, GfxCtx, Prerender, UserInput, WrappedWizard};
|
||||
use geom::Duration;
|
||||
use map_model::{IntersectionID, Map, Neighborhood, NeighborhoodBuilder};
|
||||
|
||||
// TODO Split into two types, but then State needs two possible types in its exclusive blocking
|
||||
@ -143,8 +144,8 @@ pub fn load_ab_test(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Optio
|
||||
.map(|(_, t)| t)
|
||||
}
|
||||
|
||||
pub fn input_tick(wizard: &mut WrappedWizard, query: &str) -> Option<Tick> {
|
||||
wizard.input_something(query, None, Box::new(|line| Tick::parse(&line)))
|
||||
pub fn input_time(wizard: &mut WrappedWizard, query: &str) -> Option<Duration> {
|
||||
wizard.input_something(query, None, Box::new(|line| Duration::parse(&line)))
|
||||
}
|
||||
|
||||
pub fn input_weighted_usize(
|
||||
|
@ -2,17 +2,18 @@ use crate::plugins::{AmbientPluginWithPrimaryPlugins, PluginCtx};
|
||||
use crate::state::PluginsPerMap;
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::EventLoopMode;
|
||||
use sim::{Benchmark, Event, Sim, Tick, TIMESTEP};
|
||||
use geom::Duration;
|
||||
use sim::{Benchmark, Event, Sim, TIMESTEP};
|
||||
use std::mem;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
const ADJUST_SPEED: f64 = 0.1;
|
||||
|
||||
pub struct SimControls {
|
||||
desired_speed: f64, // sim seconds per real second
|
||||
state: State,
|
||||
// Optional because the 0th tick actually happens, and callers comparing wouldn't see that.
|
||||
primary_events: Option<(Tick, Vec<Event>)>,
|
||||
// Optional because Duration::ZERO actually happens, and callers comparing wouldn't see that.
|
||||
primary_events: Option<(Duration, Vec<Event>)>,
|
||||
}
|
||||
|
||||
enum State {
|
||||
@ -43,11 +44,11 @@ impl SimControls {
|
||||
|
||||
pub fn get_new_primary_events(
|
||||
&self,
|
||||
last_seen_tick: Option<Tick>,
|
||||
) -> Option<(Tick, &Vec<Event>)> {
|
||||
let (tick, events) = self.primary_events.as_ref()?;
|
||||
if last_seen_tick.is_none() || last_seen_tick != Some(*tick) {
|
||||
Some((*tick, events))
|
||||
last_seen_time: Option<Duration>,
|
||||
) -> Option<(Duration, &Vec<Event>)> {
|
||||
let (time, events) = self.primary_events.as_ref()?;
|
||||
if last_seen_time.is_none() || last_seen_time != Some(*time) {
|
||||
Some((*time, events))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -146,9 +147,9 @@ impl AmbientPluginWithPrimaryPlugins for SimControls {
|
||||
if ctx.input.action_chosen("run/pause sim") {
|
||||
self.run_sim(&mut ctx.primary.sim);
|
||||
} else if ctx.input.action_chosen("run one step of sim") {
|
||||
let tick = ctx.primary.sim.time;
|
||||
let time = ctx.primary.sim.time();
|
||||
let events = ctx.primary.sim.step(&ctx.primary.map);
|
||||
self.primary_events = Some((tick, events));
|
||||
self.primary_events = Some((time, events));
|
||||
|
||||
*ctx.recalculate_current_selection = true;
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
@ -174,9 +175,9 @@ impl AmbientPluginWithPrimaryPlugins for SimControls {
|
||||
// the speed says we should.
|
||||
let dt_s = elapsed_seconds(*last_step);
|
||||
if dt_s >= TIMESTEP.inner_seconds() / self.desired_speed {
|
||||
let tick = ctx.primary.sim.time;
|
||||
let time = ctx.primary.sim.time();
|
||||
let events = ctx.primary.sim.step(&ctx.primary.map);
|
||||
self.primary_events = Some((tick, events));
|
||||
self.primary_events = Some((time, events));
|
||||
|
||||
*ctx.recalculate_current_selection = true;
|
||||
if let Some((s, _)) = ctx.secondary {
|
||||
@ -185,7 +186,7 @@ impl AmbientPluginWithPrimaryPlugins for SimControls {
|
||||
*last_step = Instant::now();
|
||||
}
|
||||
|
||||
if benchmark.has_real_time_passed(Duration::from_secs(1)) {
|
||||
if benchmark.has_real_time_passed(std::time::Duration::from_secs(1)) {
|
||||
// I think the benchmark should naturally account for the delay of the
|
||||
// secondary sim.
|
||||
*speed = format!("{0:.2}x", ctx.primary.sim.measure_speed(benchmark));
|
||||
|
@ -162,7 +162,7 @@ fn spawn_car(sim: &mut new_des_model::Sim, rng: &mut XorShiftRng, map: &Map, sta
|
||||
let last_lane = path.last().unwrap().as_lane();
|
||||
let vehicle = rand_vehicle(rng);
|
||||
let start_dist = rand_dist(rng, vehicle.length, map.get_l(start_lane).length());
|
||||
let spawn_time = Duration::seconds(0.2) * rng.gen_range(0, 5) as f64;
|
||||
let spawn_time = Duration::seconds(0.2) * f64::from(rng.gen_range(0, 5));
|
||||
|
||||
sim.schedule_trip(
|
||||
spawn_time,
|
||||
@ -252,7 +252,7 @@ fn random_ped_near(
|
||||
map: &Map,
|
||||
rng: &mut XorShiftRng,
|
||||
) {
|
||||
let spawn_time = Duration::seconds(0.2) * rng.gen_range(0, 5) as f64;
|
||||
let spawn_time = Duration::seconds(0.2) * f64::from(rng.gen_range(0, 5));
|
||||
let end_near = random_path(start_near, rng, map).last().unwrap().as_lane();
|
||||
let (spot1, spot2) = match (
|
||||
random_bldg_near(start_near, map, rng),
|
||||
|
@ -265,18 +265,16 @@ impl Pedestrian {
|
||||
}
|
||||
PedState::LeavingBuilding(b, ref time_int) => {
|
||||
let front_path = &map.get_b(b).front_path;
|
||||
let pt = front_path
|
||||
front_path
|
||||
.line
|
||||
.dist_along(time_int.percent(time) * front_path.line.length());
|
||||
pt
|
||||
.dist_along(time_int.percent(time) * front_path.line.length())
|
||||
}
|
||||
PedState::EnteringBuilding(b, ref time_int) => {
|
||||
let front_path = &map.get_b(b).front_path;
|
||||
let pt = front_path
|
||||
front_path
|
||||
.line
|
||||
.reverse()
|
||||
.dist_along(time_int.percent(time) * front_path.line.length());
|
||||
pt
|
||||
.dist_along(time_int.percent(time) * front_path.line.length())
|
||||
}
|
||||
PedState::StartingToBike(_, ref line, ref time_int) => {
|
||||
line.percent_along(time_int.percent(time))
|
||||
|
@ -161,7 +161,7 @@ impl TripManager {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let driving_pos = match spot.connection {
|
||||
SidewalkPOI::BikeRack(ref p) => p.clone(),
|
||||
SidewalkPOI::BikeRack(p) => p,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
|
@ -3,8 +3,9 @@ use crate::plugins::view::legend::Legend;
|
||||
use crate::state::{DefaultUIState, Flags, PerMapUI, UIState};
|
||||
use abstutil::Timer;
|
||||
use ezgui::{EventCtx, GfxCtx, LogScroller, Prerender, Text};
|
||||
use geom::Duration;
|
||||
use map_model::Traversable;
|
||||
use sim::{Event, Tick};
|
||||
use sim::Event;
|
||||
|
||||
pub struct TutorialState {
|
||||
main: DefaultUIState,
|
||||
@ -14,7 +15,7 @@ pub struct TutorialState {
|
||||
enum State {
|
||||
GiveInstructions(LogScroller),
|
||||
Play {
|
||||
last_tick_observed: Option<Tick>,
|
||||
last_time_observed: Option<Duration>,
|
||||
spawned_from_south: usize,
|
||||
spawned_from_north: usize,
|
||||
},
|
||||
@ -60,25 +61,25 @@ impl UIState for TutorialState {
|
||||
self.main.sim_controls.run_sim(&mut self.main.primary.sim);
|
||||
self.main.legend = Some(Legend::start(ctx.input, ctx.canvas));
|
||||
self.state = State::Play {
|
||||
last_tick_observed: None,
|
||||
last_time_observed: None,
|
||||
spawned_from_north: 0,
|
||||
spawned_from_south: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
State::Play {
|
||||
ref mut last_tick_observed,
|
||||
ref mut last_time_observed,
|
||||
ref mut spawned_from_north,
|
||||
ref mut spawned_from_south,
|
||||
} => {
|
||||
self.main.event(ctx, hints, recalculate_current_selection);
|
||||
|
||||
if let Some((tick, events)) = self
|
||||
if let Some((time, events)) = self
|
||||
.main
|
||||
.sim_controls
|
||||
.get_new_primary_events(*last_tick_observed)
|
||||
.get_new_primary_events(*last_time_observed)
|
||||
{
|
||||
*last_tick_observed = Some(tick);
|
||||
*last_time_observed = Some(time);
|
||||
for ev in events {
|
||||
if let Event::AgentEntersTraversable(_, Traversable::Lane(lane)) = ev {
|
||||
if *lane == self.main.primary.map.driving_lane("north entrance").id {
|
||||
@ -126,7 +127,7 @@ impl UIState for TutorialState {
|
||||
}
|
||||
|
||||
fn setup_scenario(primary: &mut PerMapUI) {
|
||||
use sim::{BorderSpawnOverTime, OriginDestination, Scenario, Tick};
|
||||
use sim::{BorderSpawnOverTime, OriginDestination, Scenario};
|
||||
let map = &primary.map;
|
||||
|
||||
fn border_spawn(primary: &PerMapUI, from: &str, to: &str) -> BorderSpawnOverTime {
|
||||
@ -136,8 +137,8 @@ fn setup_scenario(primary: &mut PerMapUI) {
|
||||
num_cars: SPAWN_CARS_PER_BORDER,
|
||||
num_bikes: 0,
|
||||
percent_use_transit: 0.0,
|
||||
start_tick: Tick::zero(),
|
||||
stop_tick: Tick::from_minutes(10),
|
||||
start_time: Duration::ZERO,
|
||||
stop_time: Duration::minutes(10),
|
||||
start_from_border: primary.map.intersection(from).id,
|
||||
goal: OriginDestination::Border(primary.map.intersection(to).id),
|
||||
}
|
||||
|
@ -9,5 +9,6 @@ aabb-quadtree = "0.1.0"
|
||||
abstutil = { path = "../abstutil" }
|
||||
geo = "0.11.0"
|
||||
ordered-float = "1.0.1"
|
||||
regex = "1.0.6"
|
||||
serde = "1.0.87"
|
||||
serde_derive = "1.0.87"
|
||||
|
@ -26,3 +26,5 @@ pub const EPSILON_DIST: Distance = Distance::const_meters(0.01);
|
||||
pub(crate) fn trim_f64(x: f64) -> f64 {
|
||||
(x * 10_000.0).round() / 10_000.0
|
||||
}
|
||||
|
||||
impl abstutil::Cloneable for Duration {}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{trim_f64, EPSILON_DIST};
|
||||
use regex::Regex;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{cmp, f64, fmt, ops};
|
||||
|
||||
@ -175,6 +176,10 @@ impl Duration {
|
||||
Duration(trim_f64(value))
|
||||
}
|
||||
|
||||
pub fn minutes(mins: usize) -> Duration {
|
||||
Duration::seconds((mins as f64) * 60.0)
|
||||
}
|
||||
|
||||
pub const fn const_seconds(value: f64) -> Duration {
|
||||
Duration(value)
|
||||
}
|
||||
@ -195,11 +200,83 @@ impl Duration {
|
||||
pub fn is_multiple_of(self, other: Duration) -> bool {
|
||||
self.inner_seconds() % other.inner_seconds() == 0.0
|
||||
}
|
||||
|
||||
// TODO Why have these two forms? Consolidate
|
||||
pub fn parse(string: &str) -> Option<Duration> {
|
||||
let parts: Vec<&str> = string.split(':').collect();
|
||||
if parts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut seconds: f64 = 0.0;
|
||||
if parts.last().unwrap().contains('.') {
|
||||
let last_parts: Vec<&str> = parts.last().unwrap().split('.').collect();
|
||||
if last_parts.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
seconds += last_parts[1].parse::<f64>().ok()? / 10.0;
|
||||
seconds += last_parts[0].parse::<f64>().ok()?;
|
||||
} else {
|
||||
seconds += parts.last().unwrap().parse::<f64>().ok()?;
|
||||
}
|
||||
|
||||
match parts.len() {
|
||||
1 => Some(Duration::seconds(seconds)),
|
||||
2 => {
|
||||
seconds += 60.0 * parts[0].parse::<f64>().ok()?;
|
||||
Some(Duration(seconds))
|
||||
}
|
||||
3 => {
|
||||
seconds += 60.0 * parts[1].parse::<f64>().ok()?;
|
||||
seconds += 3600.0 * parts[0].parse::<f64>().ok()?;
|
||||
Some(Duration(seconds))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Unused right now.
|
||||
pub fn parse_filename(string: &str) -> Option<Duration> {
|
||||
// TODO lazy_static! {
|
||||
let regex = Regex::new(r"(\d+)h(\d+)m(\d+)\.(\d+)s").unwrap();
|
||||
|
||||
let caps = regex.captures(string)?;
|
||||
let hours = 3600.0 * caps[1].parse::<f64>().ok()?;
|
||||
let minutes = 60.0 * caps[2].parse::<f64>().ok()?;
|
||||
let seconds = caps[3].parse::<f64>().ok()?;
|
||||
let ms = caps[4].parse::<f64>().ok()? / 10.0;
|
||||
|
||||
Some(Duration::seconds(hours + minutes + seconds + ms))
|
||||
}
|
||||
|
||||
fn get_parts(self) -> (f64, f64, f64, f64) {
|
||||
let hours = self.inner_seconds() / 3600.0;
|
||||
let mut remainder = self.inner_seconds() % 3600.0;
|
||||
let minutes = remainder / 60.0;
|
||||
remainder %= 60.0;
|
||||
let seconds = remainder.floor();
|
||||
remainder -= seconds;
|
||||
|
||||
(hours, minutes, seconds, remainder)
|
||||
}
|
||||
|
||||
pub fn as_filename(self) -> String {
|
||||
let (hours, minutes, seconds, remainder) = self.get_parts();
|
||||
format!(
|
||||
"{0:02}h{1:02}m{2:02}.{3}s",
|
||||
hours, minutes, seconds, remainder
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Duration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}s", self.0)
|
||||
impl std::fmt::Display for Duration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let (hours, minutes, seconds, remainder) = self.get_parts();
|
||||
write!(
|
||||
f,
|
||||
"{0:02}:{1:02}:{2:02}.{3}",
|
||||
hours, minutes, seconds, remainder
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ pretty_assertions = "0.5.1"
|
||||
rand = { version = "0.6.5", features = ["serde1"] }
|
||||
rand_xorshift = "0.1.1"
|
||||
rayon = "1.0"
|
||||
regex = "1.0.6"
|
||||
serde = "1.0.87"
|
||||
serde_derive = "1.0.87"
|
||||
structopt = "0.2"
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
Sim, SpawnOverTime, Tick,
|
||||
};
|
||||
use abstutil::{Timer, WeightedUsizeChoice};
|
||||
use geom::Duration;
|
||||
use map_model::{
|
||||
BuildingID, BusRoute, BusRouteID, BusStopID, IntersectionID, LaneID, LaneType, Map, Position,
|
||||
RoadID,
|
||||
@ -106,8 +107,8 @@ impl Sim {
|
||||
}],
|
||||
spawn_over_time: vec![SpawnOverTime {
|
||||
num_agents: 100,
|
||||
start_tick: Tick::zero(),
|
||||
stop_tick: Tick::from_seconds(5),
|
||||
start_time: Duration::ZERO,
|
||||
stop_time: Duration::seconds(5.0),
|
||||
start_from_neighborhood: "_everywhere_".to_string(),
|
||||
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
|
||||
percent_biking: 0.5,
|
||||
@ -122,8 +123,8 @@ impl Sim {
|
||||
num_peds: 10,
|
||||
num_cars: 10,
|
||||
num_bikes: 10,
|
||||
start_tick: Tick::zero(),
|
||||
stop_tick: Tick::from_seconds(5),
|
||||
start_time: Duration::ZERO,
|
||||
stop_time: Duration::seconds(5.0),
|
||||
start_from_border: i.id,
|
||||
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
|
||||
percent_use_transit: 0.5,
|
||||
@ -133,8 +134,8 @@ impl Sim {
|
||||
for i in map.all_outgoing_borders() {
|
||||
s.spawn_over_time.push(SpawnOverTime {
|
||||
num_agents: 10,
|
||||
start_tick: Tick::zero(),
|
||||
stop_tick: Tick::from_seconds(5),
|
||||
start_time: Duration::ZERO,
|
||||
stop_time: Duration::seconds(5.0),
|
||||
start_from_neighborhood: "_everywhere_".to_string(),
|
||||
goal: OriginDestination::Border(i.id),
|
||||
percent_biking: 0.5,
|
||||
|
@ -4,7 +4,7 @@ use crate::walking::SidewalkSpot;
|
||||
use crate::{CarID, Sim, Tick};
|
||||
use abstutil;
|
||||
use abstutil::{Timer, WeightedUsizeChoice};
|
||||
use geom::Distance;
|
||||
use geom::{Distance, Duration};
|
||||
use map_model::{FullNeighborhoodInfo, IntersectionID, LaneType, Map, Pathfinder, Position};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::Rng;
|
||||
@ -29,8 +29,8 @@ pub struct Scenario {
|
||||
pub struct SpawnOverTime {
|
||||
pub num_agents: usize,
|
||||
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
|
||||
pub start_tick: Tick,
|
||||
pub stop_tick: Tick,
|
||||
pub start_time: Duration,
|
||||
pub stop_time: Duration,
|
||||
pub start_from_neighborhood: String,
|
||||
pub goal: OriginDestination,
|
||||
pub percent_biking: f64,
|
||||
@ -43,8 +43,8 @@ pub struct BorderSpawnOverTime {
|
||||
pub num_cars: usize,
|
||||
pub num_bikes: usize,
|
||||
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
|
||||
pub start_tick: Tick,
|
||||
pub stop_tick: Tick,
|
||||
pub start_time: Duration,
|
||||
pub stop_time: Duration,
|
||||
// TODO A serialized Scenario won't last well as the map changes...
|
||||
pub start_from_border: IntersectionID,
|
||||
pub goal: OriginDestination,
|
||||
@ -98,7 +98,7 @@ impl Scenario {
|
||||
timer.start_iter("SpawnOverTime each agent", s.num_agents);
|
||||
for _ in 0..s.num_agents {
|
||||
timer.next();
|
||||
let spawn_time = Tick::uniform(s.start_tick, s.stop_tick, &mut sim.rng);
|
||||
let spawn_time = Tick::uniform(s.start_time, s.stop_time, &mut sim.rng);
|
||||
// Note that it's fine for agents to start/end at the same building. Later we might
|
||||
// want a better assignment of people per household, or workers per office building.
|
||||
let from_bldg = *neighborhoods[&s.start_from_neighborhood]
|
||||
@ -195,7 +195,7 @@ impl Scenario {
|
||||
timer.next();
|
||||
if let Some(start) = SidewalkSpot::start_at_border(s.start_from_border, map) {
|
||||
for _ in 0..s.num_peds {
|
||||
let spawn_time = Tick::uniform(s.start_tick, s.stop_tick, &mut sim.rng);
|
||||
let spawn_time = Tick::uniform(s.start_time, s.stop_time, &mut sim.rng);
|
||||
if let Some(goal) =
|
||||
s.goal
|
||||
.pick_walking_goal(map, &neighborhoods, &mut sim.rng, timer)
|
||||
@ -249,7 +249,7 @@ impl Scenario {
|
||||
));
|
||||
} else {
|
||||
for _ in 0..s.num_cars {
|
||||
let spawn_time = Tick::uniform(s.start_tick, s.stop_tick, &mut sim.rng);
|
||||
let spawn_time = Tick::uniform(s.start_time, s.stop_time, &mut sim.rng);
|
||||
if let Some(goal) =
|
||||
s.goal
|
||||
.pick_driving_goal(map, &neighborhoods, &mut sim.rng, timer)
|
||||
@ -283,7 +283,7 @@ impl Scenario {
|
||||
}
|
||||
if !starting_biking_lanes.is_empty() {
|
||||
for _ in 0..s.num_bikes {
|
||||
let spawn_time = Tick::uniform(s.start_tick, s.stop_tick, &mut sim.rng);
|
||||
let spawn_time = Tick::uniform(s.start_time, s.stop_time, &mut sim.rng);
|
||||
if let Some(goal) =
|
||||
s.goal
|
||||
.pick_biking_goal(map, &neighborhoods, &mut sim.rng, timer)
|
||||
|
@ -1,8 +1,6 @@
|
||||
use geom::Duration;
|
||||
use lazy_static::lazy_static;
|
||||
use rand::Rng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use regex::Regex;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
pub const TIMESTEP: Duration = Duration::const_seconds(0.1);
|
||||
@ -32,54 +30,6 @@ impl Tick {
|
||||
Tick(t)
|
||||
}
|
||||
|
||||
// TODO Why have these two forms? Consolidate
|
||||
pub fn parse(string: &str) -> Option<Tick> {
|
||||
let parts: Vec<&str> = string.split(':').collect();
|
||||
if parts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut ticks: u32 = 0;
|
||||
if parts.last().unwrap().contains('.') {
|
||||
let last_parts: Vec<&str> = parts.last().unwrap().split('.').collect();
|
||||
if last_parts.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
ticks += u32::from_str_radix(last_parts[1], 10).ok()?;
|
||||
ticks += 10 * u32::from_str_radix(last_parts[0], 10).ok()?;
|
||||
} else {
|
||||
ticks += 10 * u32::from_str_radix(parts.last().unwrap(), 10).ok()?;
|
||||
}
|
||||
|
||||
match parts.len() {
|
||||
1 => Some(Tick(ticks)),
|
||||
2 => {
|
||||
ticks += 60 * 10 * u32::from_str_radix(parts[0], 10).ok()?;
|
||||
Some(Tick(ticks))
|
||||
}
|
||||
3 => {
|
||||
ticks += 60 * 10 * u32::from_str_radix(parts[1], 10).ok()?;
|
||||
ticks += 60 * 60 * 10 * u32::from_str_radix(parts[0], 10).ok()?;
|
||||
Some(Tick(ticks))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Unused right now.
|
||||
pub fn parse_filename(string: &str) -> Option<Tick> {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"(\d+)h(\d+)m(\d+)\.(\d+)s").unwrap();
|
||||
}
|
||||
let caps = RE.captures(string)?;
|
||||
let hours = 60 * 60 * 10 * u32::from_str_radix(&caps[1], 10).ok()?;
|
||||
let minutes = 60 * 10 * u32::from_str_radix(&caps[2], 10).ok()?;
|
||||
let seconds = 10 * u32::from_str_radix(&caps[3], 10).ok()?;
|
||||
let ms = u32::from_str_radix(&caps[4], 10).ok()?;
|
||||
|
||||
Some(Tick(hours + minutes + seconds + ms))
|
||||
}
|
||||
|
||||
// TODO as_duration?
|
||||
pub fn as_time(self) -> Duration {
|
||||
TIMESTEP * f64::from(self.0)
|
||||
@ -122,9 +72,9 @@ impl Tick {
|
||||
}
|
||||
|
||||
// TODO options for sampling normal distribution
|
||||
pub fn uniform(start: Tick, stop: Tick, rng: &mut XorShiftRng) -> Tick {
|
||||
pub fn uniform(start: Duration, stop: Duration, rng: &mut XorShiftRng) -> Tick {
|
||||
assert!(start < stop);
|
||||
Tick(rng.gen_range(start.0, stop.0))
|
||||
Tick(rng.gen_range((start / TIMESTEP) as u32, (stop / TIMESTEP) as u32))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
use abstutil;
|
||||
use abstutil::{Error, Profiler};
|
||||
use derivative::Derivative;
|
||||
use geom::{Distance, Pt2D};
|
||||
use geom::{Distance, Duration, Pt2D};
|
||||
use map_model::{
|
||||
BuildingID, IntersectionID, LaneID, LaneType, Map, Path, Trace, Traversable, Turn,
|
||||
};
|
||||
@ -493,4 +493,9 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Confusing to clash with the GetDrawAgents definition?
|
||||
pub fn time(&self) -> Duration {
|
||||
self.time.as_time()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::runner::TestRunner;
|
||||
use geom::{Line, PolyLine, Pt2D};
|
||||
use geom::{Duration, Line, PolyLine, Pt2D};
|
||||
//use rand;
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
@ -87,6 +87,21 @@ pub fn run(t: &mut TestRunner) {
|
||||
|
||||
pl.get_slice_ending_at(pt);
|
||||
});
|
||||
|
||||
t.run_fast("time_parsing", |_| {
|
||||
assert_eq!(Duration::parse("2.3"), Some(Duration::seconds(2.3)));
|
||||
assert_eq!(Duration::parse("02.3"), Some(Duration::seconds(2.3)));
|
||||
assert_eq!(Duration::parse("00:00:02.3"), Some(Duration::seconds(2.3)));
|
||||
|
||||
assert_eq!(
|
||||
Duration::parse("00:02:03.5"),
|
||||
Some(Duration::seconds(123.5))
|
||||
);
|
||||
assert_eq!(
|
||||
Duration::parse("01:02:03.5"),
|
||||
Some(Duration::seconds(3723.5))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO test that shifting lines and polylines is a reversible operation
|
||||
|
@ -1,7 +1,6 @@
|
||||
mod geom;
|
||||
mod map_conversion;
|
||||
mod parking;
|
||||
mod physics;
|
||||
mod runner;
|
||||
mod sim_completion;
|
||||
mod sim_determinism;
|
||||
@ -15,7 +14,6 @@ fn main() {
|
||||
geom::run(t.suite("geom"));
|
||||
map_conversion::run(t.suite("map_conversion"));
|
||||
parking::run(t.suite("parking"));
|
||||
physics::run(t.suite("physics"));
|
||||
sim_completion::run(t.suite("sim_completion"));
|
||||
sim_determinism::run(t.suite("sim_determinism"));
|
||||
transit::run(t.suite("transit"));
|
||||
|
@ -1,108 +0,0 @@
|
||||
use crate::runner::TestRunner;
|
||||
use geom::{Acceleration, Distance, Speed, EPSILON_DIST};
|
||||
use sim::kinematics::{results_of_accel_for_one_tick, Vehicle};
|
||||
use sim::{Tick, TIMESTEP};
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
pub fn run(t: &mut TestRunner) {
|
||||
// TODO table driven test style?
|
||||
/*t.run_fast("accel_to_stop_in_dist/easy", |_| {
|
||||
let v = Vehicle {
|
||||
id: CarID(0),
|
||||
debug: true,
|
||||
vehicle_type: VehicleType::Car,
|
||||
length: geom::Distance(3.0),
|
||||
max_accel: Acceleration::meters_per_second_squared(2.7),
|
||||
max_deaccel: Acceleration::meters_per_second_squared(-2.7),
|
||||
max_speed: None,
|
||||
};
|
||||
test_accel_to_stop_in_dist(v, Distance::meters(23.554161711896512), Speed::meters_per_second(8.5817572532688));
|
||||
});
|
||||
|
||||
t.run_fast("accel_to_stop_in_dist/hard", |_| {
|
||||
let v = Vehicle {
|
||||
id: CarID(0),
|
||||
debug: true,
|
||||
vehicle_type: VehicleType::Car,
|
||||
length: geom::Distance(3.0),
|
||||
max_accel: Acceleration::meters_per_second_squared(2.7),
|
||||
max_deaccel: Acceleration::meters_per_second_squared(-2.7),
|
||||
max_speed: None,
|
||||
};
|
||||
test_accel_to_stop_in_dist(v, Distance::meters(4.543071997281501), Speed::meters_per_second(0.003911613164279909));
|
||||
});
|
||||
|
||||
t.run_fast("accel_to_stop_in_dist/bike", |_| {
|
||||
let v = Vehicle {
|
||||
id: CarID(1481),
|
||||
debug: true,
|
||||
vehicle_type: VehicleType::Bike,
|
||||
max_accel: Acceleration::meters_per_second_squared(0.2515536204703175),
|
||||
max_deaccel: Acceleration::meters_per_second_squared(-0.23358239419143578),
|
||||
length: Distance::meters(1.9474688967345983),
|
||||
max_speed: Some(Speed::meters_per_second(4.10644207854944)),
|
||||
};
|
||||
test_accel_to_stop_in_dist(v, Distance::meters(19.34189455075048), Speed::meters_per_second(1.6099431710100307));
|
||||
});*/
|
||||
|
||||
t.run_fast("time_parsing", |_| {
|
||||
assert_eq!(Tick::parse("2.3"), Some(Tick::testonly_from_raw(23)));
|
||||
assert_eq!(Tick::parse("02.3"), Some(Tick::testonly_from_raw(23)));
|
||||
assert_eq!(Tick::parse("00:00:02.3"), Some(Tick::testonly_from_raw(23)));
|
||||
|
||||
assert_eq!(
|
||||
Tick::parse("00:02:03.5"),
|
||||
Some(Tick::testonly_from_raw(35 + 1200))
|
||||
);
|
||||
assert_eq!(
|
||||
Tick::parse("01:02:03.5"),
|
||||
Some(Tick::testonly_from_raw(35 + 1200 + 36000))
|
||||
);
|
||||
});
|
||||
|
||||
t.run_fast("min_accel_doesnt_round_to_zero", |_| {
|
||||
// Copied from kinematics.rs, for bikes.
|
||||
let min_accel = Acceleration::meters_per_second_squared(1.1);
|
||||
let speed = min_accel * TIMESTEP;
|
||||
assert!(!speed.is_zero(TIMESTEP));
|
||||
});
|
||||
}
|
||||
|
||||
// TODO Make sure speed never exceeds the vehicle's cap
|
||||
#[allow(dead_code)]
|
||||
fn test_accel_to_stop_in_dist(vehicle: Vehicle, orig_dist_left: Distance, orig_speed: Speed) {
|
||||
// Can we successfully stop in a certain distance from some initial conditions?
|
||||
let mut speed = orig_speed;
|
||||
let mut dist_left = orig_dist_left;
|
||||
|
||||
for step in 0..200 {
|
||||
let desired_accel = vehicle.accel_to_stop_in_dist(speed, dist_left).unwrap();
|
||||
let accel = vehicle.clamp_accel(desired_accel);
|
||||
println!(
|
||||
"Step {}: speed {}, dist_left {}, want accel {} but doing {}",
|
||||
step, speed, dist_left, desired_accel, accel
|
||||
);
|
||||
|
||||
let (dist_covered, new_speed) = results_of_accel_for_one_tick(speed, accel);
|
||||
speed = new_speed;
|
||||
dist_left -= dist_covered;
|
||||
|
||||
if dist_left < -EPSILON_DIST {
|
||||
println!(" Result: speed {}, dist_left {}", speed, dist_left);
|
||||
panic!("We overshot too much!");
|
||||
}
|
||||
if dist_left <= EPSILON_DIST {
|
||||
println!(" Result: speed {}, dist_left {}", speed, dist_left);
|
||||
if !speed.is_zero(TIMESTEP) {
|
||||
panic!("Finished, but going too fast");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
println!(" Result: speed {}, dist_left {}", speed, dist_left);
|
||||
panic!(
|
||||
"Didn't finish in 20s; only covered {} of {}",
|
||||
orig_dist_left - dist_left,
|
||||
orig_dist_left
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user