mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +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::objects::DrawCtx;
|
||||||
use crate::plugins::{
|
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,
|
input_weighted_usize, load_scenario, BlockingPlugin, PluginCtx,
|
||||||
};
|
};
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
@ -134,9 +134,9 @@ fn edit_scenario(map: &Map, scenario: &mut Scenario, mut wizard: WrappedWizard)
|
|||||||
x if x == spawn => {
|
x if x == spawn => {
|
||||||
scenario.spawn_over_time.push(SpawnOverTime {
|
scenario.spawn_over_time.push(SpawnOverTime {
|
||||||
num_agents: wizard.input_usize("Spawn how many agents?")?,
|
num_agents: wizard.input_usize("Spawn how many agents?")?,
|
||||||
start_tick: input_tick(&mut wizard, "Start spawning when?")?,
|
start_time: input_time(&mut wizard, "Start spawning when?")?,
|
||||||
// TODO input interval, or otherwise enforce stop_tick > start_tick
|
// TODO input interval, or otherwise enforce stop_time > start_time
|
||||||
stop_tick: input_tick(&mut wizard, "Stop spawning when?")?,
|
stop_time: input_time(&mut wizard, "Stop spawning when?")?,
|
||||||
start_from_neighborhood: choose_neighborhood(
|
start_from_neighborhood: choose_neighborhood(
|
||||||
map,
|
map,
|
||||||
&mut wizard,
|
&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_peds: wizard.input_usize("Spawn how many pedestrians?")?,
|
||||||
num_cars: wizard.input_usize("Spawn how many cars?")?,
|
num_cars: wizard.input_usize("Spawn how many cars?")?,
|
||||||
num_bikes: wizard.input_usize("Spawn how many bikes?")?,
|
num_bikes: wizard.input_usize("Spawn how many bikes?")?,
|
||||||
start_tick: input_tick(&mut wizard, "Start spawning when?")?,
|
start_time: input_time(&mut wizard, "Start spawning when?")?,
|
||||||
// TODO input interval, or otherwise enforce stop_tick > start_tick
|
// TODO input interval, or otherwise enforce stop_time > start_time
|
||||||
stop_tick: input_tick(&mut wizard, "Stop spawning when?")?,
|
stop_time: input_time(&mut wizard, "Stop spawning when?")?,
|
||||||
// TODO validate it's a border!
|
// TODO validate it's a border!
|
||||||
start_from_border: choose_intersection(
|
start_from_border: choose_intersection(
|
||||||
&mut wizard,
|
&mut wizard,
|
||||||
|
@ -6,13 +6,14 @@ pub mod view;
|
|||||||
use crate::colors::ColorScheme;
|
use crate::colors::ColorScheme;
|
||||||
use crate::objects::{DrawCtx, RenderingHints, ID};
|
use crate::objects::{DrawCtx, RenderingHints, ID};
|
||||||
use crate::state::{PerMapUI, PluginsPerMap};
|
use crate::state::{PerMapUI, PluginsPerMap};
|
||||||
use ::sim::{ABTest, OriginDestination, Scenario, Tick};
|
use ::sim::{ABTest, OriginDestination, Scenario};
|
||||||
use abstutil;
|
use abstutil;
|
||||||
use abstutil::WeightedUsizeChoice;
|
use abstutil::WeightedUsizeChoice;
|
||||||
use downcast::{
|
use downcast::{
|
||||||
downcast, downcast_methods, downcast_methods_core, downcast_methods_std, impl_downcast, Any,
|
downcast, downcast_methods, downcast_methods_core, downcast_methods_std, impl_downcast, Any,
|
||||||
};
|
};
|
||||||
use ezgui::{Canvas, Color, GfxCtx, Prerender, UserInput, WrappedWizard};
|
use ezgui::{Canvas, Color, GfxCtx, Prerender, UserInput, WrappedWizard};
|
||||||
|
use geom::Duration;
|
||||||
use map_model::{IntersectionID, Map, Neighborhood, NeighborhoodBuilder};
|
use map_model::{IntersectionID, Map, Neighborhood, NeighborhoodBuilder};
|
||||||
|
|
||||||
// TODO Split into two types, but then State needs two possible types in its exclusive blocking
|
// 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)
|
.map(|(_, t)| t)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_tick(wizard: &mut WrappedWizard, query: &str) -> Option<Tick> {
|
pub fn input_time(wizard: &mut WrappedWizard, query: &str) -> Option<Duration> {
|
||||||
wizard.input_something(query, None, Box::new(|line| Tick::parse(&line)))
|
wizard.input_something(query, None, Box::new(|line| Duration::parse(&line)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_weighted_usize(
|
pub fn input_weighted_usize(
|
||||||
|
@ -2,17 +2,18 @@ use crate::plugins::{AmbientPluginWithPrimaryPlugins, PluginCtx};
|
|||||||
use crate::state::PluginsPerMap;
|
use crate::state::PluginsPerMap;
|
||||||
use abstutil::elapsed_seconds;
|
use abstutil::elapsed_seconds;
|
||||||
use ezgui::EventLoopMode;
|
use ezgui::EventLoopMode;
|
||||||
use sim::{Benchmark, Event, Sim, Tick, TIMESTEP};
|
use geom::Duration;
|
||||||
|
use sim::{Benchmark, Event, Sim, TIMESTEP};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Instant;
|
||||||
|
|
||||||
const ADJUST_SPEED: f64 = 0.1;
|
const ADJUST_SPEED: f64 = 0.1;
|
||||||
|
|
||||||
pub struct SimControls {
|
pub struct SimControls {
|
||||||
desired_speed: f64, // sim seconds per real second
|
desired_speed: f64, // sim seconds per real second
|
||||||
state: State,
|
state: State,
|
||||||
// Optional because the 0th tick actually happens, and callers comparing wouldn't see that.
|
// Optional because Duration::ZERO actually happens, and callers comparing wouldn't see that.
|
||||||
primary_events: Option<(Tick, Vec<Event>)>,
|
primary_events: Option<(Duration, Vec<Event>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
@ -43,11 +44,11 @@ impl SimControls {
|
|||||||
|
|
||||||
pub fn get_new_primary_events(
|
pub fn get_new_primary_events(
|
||||||
&self,
|
&self,
|
||||||
last_seen_tick: Option<Tick>,
|
last_seen_time: Option<Duration>,
|
||||||
) -> Option<(Tick, &Vec<Event>)> {
|
) -> Option<(Duration, &Vec<Event>)> {
|
||||||
let (tick, events) = self.primary_events.as_ref()?;
|
let (time, events) = self.primary_events.as_ref()?;
|
||||||
if last_seen_tick.is_none() || last_seen_tick != Some(*tick) {
|
if last_seen_time.is_none() || last_seen_time != Some(*time) {
|
||||||
Some((*tick, events))
|
Some((*time, events))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -146,9 +147,9 @@ impl AmbientPluginWithPrimaryPlugins for SimControls {
|
|||||||
if ctx.input.action_chosen("run/pause sim") {
|
if ctx.input.action_chosen("run/pause sim") {
|
||||||
self.run_sim(&mut ctx.primary.sim);
|
self.run_sim(&mut ctx.primary.sim);
|
||||||
} else if ctx.input.action_chosen("run one step of 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);
|
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;
|
*ctx.recalculate_current_selection = true;
|
||||||
if let Some((s, _)) = ctx.secondary {
|
if let Some((s, _)) = ctx.secondary {
|
||||||
@ -174,9 +175,9 @@ impl AmbientPluginWithPrimaryPlugins for SimControls {
|
|||||||
// the speed says we should.
|
// the speed says we should.
|
||||||
let dt_s = elapsed_seconds(*last_step);
|
let dt_s = elapsed_seconds(*last_step);
|
||||||
if dt_s >= TIMESTEP.inner_seconds() / self.desired_speed {
|
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);
|
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;
|
*ctx.recalculate_current_selection = true;
|
||||||
if let Some((s, _)) = ctx.secondary {
|
if let Some((s, _)) = ctx.secondary {
|
||||||
@ -185,7 +186,7 @@ impl AmbientPluginWithPrimaryPlugins for SimControls {
|
|||||||
*last_step = Instant::now();
|
*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
|
// I think the benchmark should naturally account for the delay of the
|
||||||
// secondary sim.
|
// secondary sim.
|
||||||
*speed = format!("{0:.2}x", ctx.primary.sim.measure_speed(benchmark));
|
*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 last_lane = path.last().unwrap().as_lane();
|
||||||
let vehicle = rand_vehicle(rng);
|
let vehicle = rand_vehicle(rng);
|
||||||
let start_dist = rand_dist(rng, vehicle.length, map.get_l(start_lane).length());
|
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(
|
sim.schedule_trip(
|
||||||
spawn_time,
|
spawn_time,
|
||||||
@ -252,7 +252,7 @@ fn random_ped_near(
|
|||||||
map: &Map,
|
map: &Map,
|
||||||
rng: &mut XorShiftRng,
|
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 end_near = random_path(start_near, rng, map).last().unwrap().as_lane();
|
||||||
let (spot1, spot2) = match (
|
let (spot1, spot2) = match (
|
||||||
random_bldg_near(start_near, map, rng),
|
random_bldg_near(start_near, map, rng),
|
||||||
|
@ -265,18 +265,16 @@ impl Pedestrian {
|
|||||||
}
|
}
|
||||||
PedState::LeavingBuilding(b, ref time_int) => {
|
PedState::LeavingBuilding(b, ref time_int) => {
|
||||||
let front_path = &map.get_b(b).front_path;
|
let front_path = &map.get_b(b).front_path;
|
||||||
let pt = front_path
|
front_path
|
||||||
.line
|
.line
|
||||||
.dist_along(time_int.percent(time) * front_path.line.length());
|
.dist_along(time_int.percent(time) * front_path.line.length())
|
||||||
pt
|
|
||||||
}
|
}
|
||||||
PedState::EnteringBuilding(b, ref time_int) => {
|
PedState::EnteringBuilding(b, ref time_int) => {
|
||||||
let front_path = &map.get_b(b).front_path;
|
let front_path = &map.get_b(b).front_path;
|
||||||
let pt = front_path
|
front_path
|
||||||
.line
|
.line
|
||||||
.reverse()
|
.reverse()
|
||||||
.dist_along(time_int.percent(time) * front_path.line.length());
|
.dist_along(time_int.percent(time) * front_path.line.length())
|
||||||
pt
|
|
||||||
}
|
}
|
||||||
PedState::StartingToBike(_, ref line, ref time_int) => {
|
PedState::StartingToBike(_, ref line, ref time_int) => {
|
||||||
line.percent_along(time_int.percent(time))
|
line.percent_along(time_int.percent(time))
|
||||||
|
@ -161,7 +161,7 @@ impl TripManager {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let driving_pos = match spot.connection {
|
let driving_pos = match spot.connection {
|
||||||
SidewalkPOI::BikeRack(ref p) => p.clone(),
|
SidewalkPOI::BikeRack(p) => p,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@ use crate::plugins::view::legend::Legend;
|
|||||||
use crate::state::{DefaultUIState, Flags, PerMapUI, UIState};
|
use crate::state::{DefaultUIState, Flags, PerMapUI, UIState};
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use ezgui::{EventCtx, GfxCtx, LogScroller, Prerender, Text};
|
use ezgui::{EventCtx, GfxCtx, LogScroller, Prerender, Text};
|
||||||
|
use geom::Duration;
|
||||||
use map_model::Traversable;
|
use map_model::Traversable;
|
||||||
use sim::{Event, Tick};
|
use sim::Event;
|
||||||
|
|
||||||
pub struct TutorialState {
|
pub struct TutorialState {
|
||||||
main: DefaultUIState,
|
main: DefaultUIState,
|
||||||
@ -14,7 +15,7 @@ pub struct TutorialState {
|
|||||||
enum State {
|
enum State {
|
||||||
GiveInstructions(LogScroller),
|
GiveInstructions(LogScroller),
|
||||||
Play {
|
Play {
|
||||||
last_tick_observed: Option<Tick>,
|
last_time_observed: Option<Duration>,
|
||||||
spawned_from_south: usize,
|
spawned_from_south: usize,
|
||||||
spawned_from_north: 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.sim_controls.run_sim(&mut self.main.primary.sim);
|
||||||
self.main.legend = Some(Legend::start(ctx.input, ctx.canvas));
|
self.main.legend = Some(Legend::start(ctx.input, ctx.canvas));
|
||||||
self.state = State::Play {
|
self.state = State::Play {
|
||||||
last_tick_observed: None,
|
last_time_observed: None,
|
||||||
spawned_from_north: 0,
|
spawned_from_north: 0,
|
||||||
spawned_from_south: 0,
|
spawned_from_south: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Play {
|
State::Play {
|
||||||
ref mut last_tick_observed,
|
ref mut last_time_observed,
|
||||||
ref mut spawned_from_north,
|
ref mut spawned_from_north,
|
||||||
ref mut spawned_from_south,
|
ref mut spawned_from_south,
|
||||||
} => {
|
} => {
|
||||||
self.main.event(ctx, hints, recalculate_current_selection);
|
self.main.event(ctx, hints, recalculate_current_selection);
|
||||||
|
|
||||||
if let Some((tick, events)) = self
|
if let Some((time, events)) = self
|
||||||
.main
|
.main
|
||||||
.sim_controls
|
.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 {
|
for ev in events {
|
||||||
if let Event::AgentEntersTraversable(_, Traversable::Lane(lane)) = ev {
|
if let Event::AgentEntersTraversable(_, Traversable::Lane(lane)) = ev {
|
||||||
if *lane == self.main.primary.map.driving_lane("north entrance").id {
|
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) {
|
fn setup_scenario(primary: &mut PerMapUI) {
|
||||||
use sim::{BorderSpawnOverTime, OriginDestination, Scenario, Tick};
|
use sim::{BorderSpawnOverTime, OriginDestination, Scenario};
|
||||||
let map = &primary.map;
|
let map = &primary.map;
|
||||||
|
|
||||||
fn border_spawn(primary: &PerMapUI, from: &str, to: &str) -> BorderSpawnOverTime {
|
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_cars: SPAWN_CARS_PER_BORDER,
|
||||||
num_bikes: 0,
|
num_bikes: 0,
|
||||||
percent_use_transit: 0.0,
|
percent_use_transit: 0.0,
|
||||||
start_tick: Tick::zero(),
|
start_time: Duration::ZERO,
|
||||||
stop_tick: Tick::from_minutes(10),
|
stop_time: Duration::minutes(10),
|
||||||
start_from_border: primary.map.intersection(from).id,
|
start_from_border: primary.map.intersection(from).id,
|
||||||
goal: OriginDestination::Border(primary.map.intersection(to).id),
|
goal: OriginDestination::Border(primary.map.intersection(to).id),
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,6 @@ aabb-quadtree = "0.1.0"
|
|||||||
abstutil = { path = "../abstutil" }
|
abstutil = { path = "../abstutil" }
|
||||||
geo = "0.11.0"
|
geo = "0.11.0"
|
||||||
ordered-float = "1.0.1"
|
ordered-float = "1.0.1"
|
||||||
|
regex = "1.0.6"
|
||||||
serde = "1.0.87"
|
serde = "1.0.87"
|
||||||
serde_derive = "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 {
|
pub(crate) fn trim_f64(x: f64) -> f64 {
|
||||||
(x * 10_000.0).round() / 10_000.0
|
(x * 10_000.0).round() / 10_000.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl abstutil::Cloneable for Duration {}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{trim_f64, EPSILON_DIST};
|
use crate::{trim_f64, EPSILON_DIST};
|
||||||
|
use regex::Regex;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{cmp, f64, fmt, ops};
|
use std::{cmp, f64, fmt, ops};
|
||||||
|
|
||||||
@ -175,6 +176,10 @@ impl Duration {
|
|||||||
Duration(trim_f64(value))
|
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 {
|
pub const fn const_seconds(value: f64) -> Duration {
|
||||||
Duration(value)
|
Duration(value)
|
||||||
}
|
}
|
||||||
@ -195,11 +200,83 @@ impl Duration {
|
|||||||
pub fn is_multiple_of(self, other: Duration) -> bool {
|
pub fn is_multiple_of(self, other: Duration) -> bool {
|
||||||
self.inner_seconds() % other.inner_seconds() == 0.0
|
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 {
|
impl std::fmt::Display for Duration {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(f, "{}s", self.0)
|
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 = { version = "0.6.5", features = ["serde1"] }
|
||||||
rand_xorshift = "0.1.1"
|
rand_xorshift = "0.1.1"
|
||||||
rayon = "1.0"
|
rayon = "1.0"
|
||||||
regex = "1.0.6"
|
|
||||||
serde = "1.0.87"
|
serde = "1.0.87"
|
||||||
serde_derive = "1.0.87"
|
serde_derive = "1.0.87"
|
||||||
structopt = "0.2"
|
structopt = "0.2"
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
Sim, SpawnOverTime, Tick,
|
Sim, SpawnOverTime, Tick,
|
||||||
};
|
};
|
||||||
use abstutil::{Timer, WeightedUsizeChoice};
|
use abstutil::{Timer, WeightedUsizeChoice};
|
||||||
|
use geom::Duration;
|
||||||
use map_model::{
|
use map_model::{
|
||||||
BuildingID, BusRoute, BusRouteID, BusStopID, IntersectionID, LaneID, LaneType, Map, Position,
|
BuildingID, BusRoute, BusRouteID, BusStopID, IntersectionID, LaneID, LaneType, Map, Position,
|
||||||
RoadID,
|
RoadID,
|
||||||
@ -106,8 +107,8 @@ impl Sim {
|
|||||||
}],
|
}],
|
||||||
spawn_over_time: vec![SpawnOverTime {
|
spawn_over_time: vec![SpawnOverTime {
|
||||||
num_agents: 100,
|
num_agents: 100,
|
||||||
start_tick: Tick::zero(),
|
start_time: Duration::ZERO,
|
||||||
stop_tick: Tick::from_seconds(5),
|
stop_time: Duration::seconds(5.0),
|
||||||
start_from_neighborhood: "_everywhere_".to_string(),
|
start_from_neighborhood: "_everywhere_".to_string(),
|
||||||
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
|
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
|
||||||
percent_biking: 0.5,
|
percent_biking: 0.5,
|
||||||
@ -122,8 +123,8 @@ impl Sim {
|
|||||||
num_peds: 10,
|
num_peds: 10,
|
||||||
num_cars: 10,
|
num_cars: 10,
|
||||||
num_bikes: 10,
|
num_bikes: 10,
|
||||||
start_tick: Tick::zero(),
|
start_time: Duration::ZERO,
|
||||||
stop_tick: Tick::from_seconds(5),
|
stop_time: Duration::seconds(5.0),
|
||||||
start_from_border: i.id,
|
start_from_border: i.id,
|
||||||
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
|
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
|
||||||
percent_use_transit: 0.5,
|
percent_use_transit: 0.5,
|
||||||
@ -133,8 +134,8 @@ impl Sim {
|
|||||||
for i in map.all_outgoing_borders() {
|
for i in map.all_outgoing_borders() {
|
||||||
s.spawn_over_time.push(SpawnOverTime {
|
s.spawn_over_time.push(SpawnOverTime {
|
||||||
num_agents: 10,
|
num_agents: 10,
|
||||||
start_tick: Tick::zero(),
|
start_time: Duration::ZERO,
|
||||||
stop_tick: Tick::from_seconds(5),
|
stop_time: Duration::seconds(5.0),
|
||||||
start_from_neighborhood: "_everywhere_".to_string(),
|
start_from_neighborhood: "_everywhere_".to_string(),
|
||||||
goal: OriginDestination::Border(i.id),
|
goal: OriginDestination::Border(i.id),
|
||||||
percent_biking: 0.5,
|
percent_biking: 0.5,
|
||||||
|
@ -4,7 +4,7 @@ use crate::walking::SidewalkSpot;
|
|||||||
use crate::{CarID, Sim, Tick};
|
use crate::{CarID, Sim, Tick};
|
||||||
use abstutil;
|
use abstutil;
|
||||||
use abstutil::{Timer, WeightedUsizeChoice};
|
use abstutil::{Timer, WeightedUsizeChoice};
|
||||||
use geom::Distance;
|
use geom::{Distance, Duration};
|
||||||
use map_model::{FullNeighborhoodInfo, IntersectionID, LaneType, Map, Pathfinder, Position};
|
use map_model::{FullNeighborhoodInfo, IntersectionID, LaneType, Map, Pathfinder, Position};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -29,8 +29,8 @@ pub struct Scenario {
|
|||||||
pub struct SpawnOverTime {
|
pub struct SpawnOverTime {
|
||||||
pub num_agents: usize,
|
pub num_agents: usize,
|
||||||
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
|
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
|
||||||
pub start_tick: Tick,
|
pub start_time: Duration,
|
||||||
pub stop_tick: Tick,
|
pub stop_time: Duration,
|
||||||
pub start_from_neighborhood: String,
|
pub start_from_neighborhood: String,
|
||||||
pub goal: OriginDestination,
|
pub goal: OriginDestination,
|
||||||
pub percent_biking: f64,
|
pub percent_biking: f64,
|
||||||
@ -43,8 +43,8 @@ pub struct BorderSpawnOverTime {
|
|||||||
pub num_cars: usize,
|
pub num_cars: usize,
|
||||||
pub num_bikes: usize,
|
pub num_bikes: usize,
|
||||||
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
|
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
|
||||||
pub start_tick: Tick,
|
pub start_time: Duration,
|
||||||
pub stop_tick: Tick,
|
pub stop_time: Duration,
|
||||||
// TODO A serialized Scenario won't last well as the map changes...
|
// TODO A serialized Scenario won't last well as the map changes...
|
||||||
pub start_from_border: IntersectionID,
|
pub start_from_border: IntersectionID,
|
||||||
pub goal: OriginDestination,
|
pub goal: OriginDestination,
|
||||||
@ -98,7 +98,7 @@ impl Scenario {
|
|||||||
timer.start_iter("SpawnOverTime each agent", s.num_agents);
|
timer.start_iter("SpawnOverTime each agent", s.num_agents);
|
||||||
for _ in 0..s.num_agents {
|
for _ in 0..s.num_agents {
|
||||||
timer.next();
|
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
|
// 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.
|
// want a better assignment of people per household, or workers per office building.
|
||||||
let from_bldg = *neighborhoods[&s.start_from_neighborhood]
|
let from_bldg = *neighborhoods[&s.start_from_neighborhood]
|
||||||
@ -195,7 +195,7 @@ impl Scenario {
|
|||||||
timer.next();
|
timer.next();
|
||||||
if let Some(start) = SidewalkSpot::start_at_border(s.start_from_border, map) {
|
if let Some(start) = SidewalkSpot::start_at_border(s.start_from_border, map) {
|
||||||
for _ in 0..s.num_peds {
|
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) =
|
if let Some(goal) =
|
||||||
s.goal
|
s.goal
|
||||||
.pick_walking_goal(map, &neighborhoods, &mut sim.rng, timer)
|
.pick_walking_goal(map, &neighborhoods, &mut sim.rng, timer)
|
||||||
@ -249,7 +249,7 @@ impl Scenario {
|
|||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
for _ in 0..s.num_cars {
|
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) =
|
if let Some(goal) =
|
||||||
s.goal
|
s.goal
|
||||||
.pick_driving_goal(map, &neighborhoods, &mut sim.rng, timer)
|
.pick_driving_goal(map, &neighborhoods, &mut sim.rng, timer)
|
||||||
@ -283,7 +283,7 @@ impl Scenario {
|
|||||||
}
|
}
|
||||||
if !starting_biking_lanes.is_empty() {
|
if !starting_biking_lanes.is_empty() {
|
||||||
for _ in 0..s.num_bikes {
|
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) =
|
if let Some(goal) =
|
||||||
s.goal
|
s.goal
|
||||||
.pick_biking_goal(map, &neighborhoods, &mut sim.rng, timer)
|
.pick_biking_goal(map, &neighborhoods, &mut sim.rng, timer)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use geom::Duration;
|
use geom::Duration;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
use regex::Regex;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const TIMESTEP: Duration = Duration::const_seconds(0.1);
|
pub const TIMESTEP: Duration = Duration::const_seconds(0.1);
|
||||||
@ -32,54 +30,6 @@ impl Tick {
|
|||||||
Tick(t)
|
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?
|
// TODO as_duration?
|
||||||
pub fn as_time(self) -> Duration {
|
pub fn as_time(self) -> Duration {
|
||||||
TIMESTEP * f64::from(self.0)
|
TIMESTEP * f64::from(self.0)
|
||||||
@ -122,9 +72,9 @@ impl Tick {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO options for sampling normal distribution
|
// 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);
|
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;
|
||||||
use abstutil::{Error, Profiler};
|
use abstutil::{Error, Profiler};
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use geom::{Distance, Pt2D};
|
use geom::{Distance, Duration, Pt2D};
|
||||||
use map_model::{
|
use map_model::{
|
||||||
BuildingID, IntersectionID, LaneID, LaneType, Map, Path, Trace, Traversable, Turn,
|
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 crate::runner::TestRunner;
|
||||||
use geom::{Line, PolyLine, Pt2D};
|
use geom::{Duration, Line, PolyLine, Pt2D};
|
||||||
//use rand;
|
//use rand;
|
||||||
|
|
||||||
#[allow(clippy::unreadable_literal)]
|
#[allow(clippy::unreadable_literal)]
|
||||||
@ -87,6 +87,21 @@ pub fn run(t: &mut TestRunner) {
|
|||||||
|
|
||||||
pl.get_slice_ending_at(pt);
|
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
|
// TODO test that shifting lines and polylines is a reversible operation
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
mod geom;
|
mod geom;
|
||||||
mod map_conversion;
|
mod map_conversion;
|
||||||
mod parking;
|
mod parking;
|
||||||
mod physics;
|
|
||||||
mod runner;
|
mod runner;
|
||||||
mod sim_completion;
|
mod sim_completion;
|
||||||
mod sim_determinism;
|
mod sim_determinism;
|
||||||
@ -15,7 +14,6 @@ fn main() {
|
|||||||
geom::run(t.suite("geom"));
|
geom::run(t.suite("geom"));
|
||||||
map_conversion::run(t.suite("map_conversion"));
|
map_conversion::run(t.suite("map_conversion"));
|
||||||
parking::run(t.suite("parking"));
|
parking::run(t.suite("parking"));
|
||||||
physics::run(t.suite("physics"));
|
|
||||||
sim_completion::run(t.suite("sim_completion"));
|
sim_completion::run(t.suite("sim_completion"));
|
||||||
sim_determinism::run(t.suite("sim_determinism"));
|
sim_determinism::run(t.suite("sim_determinism"));
|
||||||
transit::run(t.suite("transit"));
|
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