defer choosing a starting lane for borders in scenarios, to respond to lane edits

This commit is contained in:
Dustin Carlino 2020-04-19 10:51:23 -07:00
parent 6d9695fb59
commit d80513235d
8 changed files with 177 additions and 207 deletions

View File

@ -219,15 +219,15 @@ d02d0d103f7b00672a5f1145c5169d8c data/system/fonts/Overpass-Bold.ttf
cc45f42cb24cad1cfdbf5ed7a0cb86d4 data/system/synthetic_maps/signal_double.json cc45f42cb24cad1cfdbf5ed7a0cb86d4 data/system/synthetic_maps/signal_double.json
8b949cc34d9a27ace0bd8ecde55a9520 data/system/synthetic_maps/signal_single.json 8b949cc34d9a27ace0bd8ecde55a9520 data/system/synthetic_maps/signal_single.json
1cd7be125e1d992613ed3a41e8b25b6a data/system/synthetic_maps/signal_fan_in.json 1cd7be125e1d992613ed3a41e8b25b6a data/system/synthetic_maps/signal_fan_in.json
dc7d7c8b8dec9c2d0375df3ed0c02d87 data/system/scenarios/ballard/weekday.bin 1d447747aa2e493310abe5e103a1f693 data/system/scenarios/ballard/weekday.bin
ec3d36603a91a1b61a63823404ad16d6 data/system/scenarios/intl_district/weekday.bin a72c52c5493d3d7eeaec296ffc7025a2 data/system/scenarios/intl_district/weekday.bin
34f366fc48867dc4d923fc638e804af0 data/system/scenarios/23rd/weekday.bin 86603a31ee66b91ebb4a66e1146a3b17 data/system/scenarios/23rd/weekday.bin
bca018492766b536680a6aae9592d397 data/system/scenarios/downtown/weekday.bin 17a1f28e4418937ff17504e357206b8c data/system/scenarios/downtown/weekday.bin
354a0d31dd19b31fe4599233a2057c41 data/system/scenarios/huge_seattle/weekday.bin afb0ab6073cd6439ccaa41addd3de68b data/system/scenarios/huge_seattle/weekday.bin
60712719b44a47cc6accf97d069fac92 data/system/scenarios/caphill/weekday.bin 248f2874b28dfda11cb052e53ef51414 data/system/scenarios/caphill/weekday.bin
8e38d4e4a93beb7080029dcb76d47563 data/system/scenarios/montlake/weekday.bin bf0bef6566fb918db5c9ad3e9054731a data/system/scenarios/montlake/weekday.bin
ad5d2c7a2c73ef5bf4a09ff11845c166 data/system/prebaked_results/signal_single/tutorial lvl1.bin 3e4614071e27205513a5cc9b7fc11c90 data/system/prebaked_results/signal_single/tutorial lvl1.bin
81081122aa46e8651d8f07bb606bc77a data/system/prebaked_results/signal_single/tutorial lvl2.bin eb732e116af8c64cd879e9acb1b2eb1f data/system/prebaked_results/signal_single/tutorial lvl2.bin
e15dd2d01fccadb1f16c4ed9f732b377 data/system/prebaked_results/montlake/car vs bike contention.bin 9577e253b4fa810edef68f3dd8860ff0 data/system/prebaked_results/montlake/car vs bike contention.bin
1f1314220db40c778c18cd03e9af5d18 data/system/prebaked_results/montlake/weekday.bin de9631bca160dea190cc23e5ac0b7139 data/system/prebaked_results/montlake/weekday.bin
ba713f7f6ad0c0201f1447d77957ce75 data/system/prebaked_results/montlake/car vs bus contention.bin b4a1a7f94385434dce3fb71b82e403e3 data/system/prebaked_results/montlake/car vs bus contention.bin

View File

@ -280,6 +280,14 @@ fn describe(person: &PersonSpec, trip: &IndividTrip, home: OD) -> String {
start.lane(), start.lane(),
driving_goal(goal) driving_goal(goal)
), ),
SpawnTrip::FromBorder { i, goal, is_bike } => format!(
"{} at {}: {} appears at {}, goes to {}",
person.id,
trip.depart,
if *is_bike { "bike" } else { "car" },
i,
driving_goal(goal)
),
SpawnTrip::MaybeUsingParkedCar(start_bldg, goal) => format!( SpawnTrip::MaybeUsingParkedCar(start_bldg, goal) => format!(
"{} at {}: try to drive from {} to {}", "{} at {}: try to drive from {} to {}",
person.id, person.id,
@ -332,6 +340,7 @@ fn other_endpt(trip: &IndividTrip, home: OD, map: &Map) -> ID {
ID::Intersection(map.get_l(start.lane()).src_i), ID::Intersection(map.get_l(start.lane()).src_i),
driving_goal(goal), driving_goal(goal),
), ),
SpawnTrip::FromBorder { i, goal, .. } => (ID::Intersection(*i), driving_goal(goal)),
SpawnTrip::MaybeUsingParkedCar(start_bldg, goal) => { SpawnTrip::MaybeUsingParkedCar(start_bldg, goal) => {
(ID::Building(*start_bldg), driving_goal(goal)) (ID::Building(*start_bldg), driving_goal(goal))
} }
@ -445,6 +454,9 @@ impl DotMap {
SpawnTrip::CarAppearing { start, goal, .. } => { SpawnTrip::CarAppearing { start, goal, .. } => {
(start.pt(map), goal.pt(map)) (start.pt(map), goal.pt(map))
} }
SpawnTrip::FromBorder { i, goal, .. } => {
(map.get_i(*i).polygon.center(), goal.pt(map))
}
SpawnTrip::MaybeUsingParkedCar(b, goal) => { SpawnTrip::MaybeUsingParkedCar(b, goal) => {
(map.get_b(*b).polygon.center(), goal.pt(map)) (map.get_b(*b).polygon.center(), goal.pt(map))
} }

View File

@ -347,7 +347,7 @@ fn schedule_trip(
}; };
match src { match src {
Source::Drive(from) => { Source::Drive(from) => {
if let Some(start_pos) = TripSpec::spawn_car_at(*from, map) { if let Some(start_pos) = TripSpec::spawn_vehicle_at(*from, false, map) {
spawner.schedule_trip( spawner.schedule_trip(
sim.random_person(true), sim.random_person(true),
sim.time(), sim.time(),

View File

@ -68,12 +68,18 @@ impl Intersection {
.collect() .collect()
} }
// Strict for bikes. If there are bike lanes, not allowed to use other lanes.
pub fn get_outgoing_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> { pub fn get_outgoing_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> {
self.outgoing_lanes let mut choices: Vec<LaneID> = self
.outgoing_lanes
.iter() .iter()
.filter(|l| constraints.can_use(map.get_l(**l), map)) .filter(|l| constraints.can_use(map.get_l(**l), map))
.cloned() .cloned()
.collect() .collect();
if constraints == PathConstraints::Bike {
choices.retain(|l| map.get_l(*l).is_biking());
}
choices
} }
pub fn get_zorder(&self, map: &Map) -> isize { pub fn get_zorder(&self, map: &Map) -> isize {

View File

@ -2,10 +2,8 @@ use crate::psrc::{Endpoint, Mode, Parcel, Purpose};
use crate::PopDat; use crate::PopDat;
use abstutil::{prettyprint_usize, MultiMap, Timer}; use abstutil::{prettyprint_usize, MultiMap, Timer};
use geom::{Distance, Duration, LonLat, Polygon, Pt2D, Time}; use geom::{Distance, Duration, LonLat, Polygon, Pt2D, Time};
use map_model::{BuildingID, IntersectionID, Map, PathConstraints, Position}; use map_model::{BuildingID, IntersectionID, Map, PathConstraints};
use sim::{ use sim::{DrivingGoal, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip};
DrivingGoal, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip, TripSpec,
};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -37,72 +35,45 @@ impl Trip {
self.depart_at + self.trip_time self.depart_at + self.trip_time
} }
pub fn to_spawn_trip(&self, map: &Map) -> Option<SpawnTrip> { fn to_spawn_trip(&self, map: &Map) -> SpawnTrip {
match self.mode { match self.mode {
Mode::Drive => match self.from { Mode::Drive => match self.from {
TripEndpt::Border(i, _) => { TripEndpt::Border(i, _) => SpawnTrip::FromBorder {
if let Some(start) = TripSpec::spawn_car_at( i,
Position::new(
map.get_i(i).get_outgoing_lanes(map, PathConstraints::Car)[0],
Distance::ZERO,
),
map,
) {
Some(SpawnTrip::CarAppearing {
start,
goal: self.to.driving_goal(PathConstraints::Car, map), goal: self.to.driving_goal(PathConstraints::Car, map),
is_bike: false, is_bike: false,
}) },
} else { TripEndpt::Building(b) => SpawnTrip::MaybeUsingParkedCar(
// TODO need to be able to emit warnings from parallelize
//timer.warn(format!("No room for car to appear at {:?}", self.from));
None
}
}
TripEndpt::Building(b) => Some(SpawnTrip::MaybeUsingParkedCar(
b, b,
self.to.driving_goal(PathConstraints::Car, map), self.to.driving_goal(PathConstraints::Car, map),
)), ),
}, },
Mode::Bike => match self.from { Mode::Bike => match self.from {
TripEndpt::Building(b) => Some(SpawnTrip::UsingBike( TripEndpt::Building(b) => SpawnTrip::UsingBike(
SidewalkSpot::building(b, map), SidewalkSpot::building(b, map),
self.to.driving_goal(PathConstraints::Bike, map), self.to.driving_goal(PathConstraints::Bike, map),
)),
TripEndpt::Border(i, _) => {
if let Some(start) = TripSpec::spawn_car_at(
Position::new(
map.get_i(i).get_outgoing_lanes(map, PathConstraints::Bike)[0],
Distance::ZERO,
), ),
map, TripEndpt::Border(i, _) => SpawnTrip::FromBorder {
) { i,
Some(SpawnTrip::CarAppearing {
start,
goal: self.to.driving_goal(PathConstraints::Bike, map), goal: self.to.driving_goal(PathConstraints::Bike, map),
is_bike: true, is_bike: true,
})
} else {
//timer.warn(format!("No room for bike to appear at {:?}", self.from));
None
}
}
}, },
Mode::Walk => Some(SpawnTrip::JustWalking( },
Mode::Walk => SpawnTrip::JustWalking(
self.from.start_sidewalk_spot(map), self.from.start_sidewalk_spot(map),
self.to.end_sidewalk_spot(map), self.to.end_sidewalk_spot(map),
)), ),
Mode::Transit => { Mode::Transit => {
let start = self.from.start_sidewalk_spot(map); let start = self.from.start_sidewalk_spot(map);
let goal = self.to.end_sidewalk_spot(map); let goal = self.to.end_sidewalk_spot(map);
if let Some((stop1, stop2, route)) = if let Some((stop1, stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos) map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{ {
Some(SpawnTrip::UsingTransit(start, goal, route, stop1, stop2)) SpawnTrip::UsingTransit(start, goal, route, stop1, stop2)
} else { } else {
//timer.warn(format!("{:?} not actually using transit, because pathfinding //timer.warn(format!("{:?} not actually using transit, because pathfinding
// didn't find any useful route", trip)); // didn't find any useful route", trip));
Some(SpawnTrip::JustWalking(start, goal)) SpawnTrip::JustWalking(start, goal)
} }
} }
} }
@ -289,13 +260,15 @@ pub fn trips_to_scenario(map: &Map, timer: &mut Timer) -> Scenario {
// person -> (trip seq, index into individ_trips) // person -> (trip seq, index into individ_trips)
let mut trips_per_person: MultiMap<(usize, usize), ((usize, bool, usize), usize)> = let mut trips_per_person: MultiMap<(usize, usize), ((usize, bool, usize), usize)> =
MultiMap::new(); MultiMap::new();
for (trip, depart, person, seq) in timer for (trip, depart, person, seq) in
.parallelize("turn PSRC trips into SpawnTrips", trips, |trip| { timer.parallelize("turn PSRC trips into SpawnTrips", trips, |trip| {
trip.to_spawn_trip(map) (
.map(|spawn| (spawn, trip.depart_at, trip.person, trip.seq)) trip.to_spawn_trip(map),
trip.depart_at,
trip.person,
trip.seq,
)
}) })
.into_iter()
.flatten()
{ {
let idx = individ_trips.len(); let idx = individ_trips.len();
individ_trips.push(Some(IndividTrip { depart, trip })); individ_trips.push(Some(IndividTrip { depart, trip }));
@ -324,7 +297,7 @@ pub fn trips_to_scenario(map: &Map, timer: &mut Timer) -> Scenario {
let mut has_car = false; let mut has_car = false;
for trip in &trips { for trip in &trips {
match trip.trip { match trip.trip {
SpawnTrip::CarAppearing { is_bike, .. } => { SpawnTrip::FromBorder { is_bike, .. } => {
if !is_bike { if !is_bike {
has_car = true; has_car = true;
} }

View File

@ -1,12 +1,7 @@
use crate::{ use crate::{DrivingGoal, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip};
DrivingGoal, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip, BIKE_LENGTH,
MAX_CAR_LENGTH,
};
use abstutil::Timer; use abstutil::Timer;
use geom::{Duration, Time}; use geom::{Duration, Time};
use map_model::{ use map_model::{BuildingID, DirectedRoadID, FullNeighborhoodInfo, Map, PathConstraints};
BuildingID, DirectedRoadID, FullNeighborhoodInfo, LaneID, Map, PathConstraints, Position,
};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::Rng; use rand::Rng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@ -80,8 +75,24 @@ impl ScenarioGenerator {
for s in &self.border_spawn_over_time { for s in &self.border_spawn_over_time {
timer.next(); timer.next();
s.spawn_peds(rng, &mut scenario, &neighborhoods, map, timer); s.spawn_peds(rng, &mut scenario, &neighborhoods, map, timer);
s.spawn_cars(rng, &mut scenario, &neighborhoods, map, timer); s.spawn_vehicles(
s.spawn_bikes(rng, &mut scenario, &neighborhoods, map, timer); s.num_cars,
PathConstraints::Car,
rng,
&mut scenario,
&neighborhoods,
map,
timer,
);
s.spawn_vehicles(
s.num_bikes,
PathConstraints::Bike,
rng,
&mut scenario,
&neighborhoods,
map,
timer,
);
} }
timer.stop(format!("Generating scenario {}", self.scenario_name)); timer.stop(format!("Generating scenario {}", self.scenario_name));
@ -327,97 +338,34 @@ impl BorderSpawnOverTime {
} }
} }
fn spawn_cars( fn spawn_vehicles(
&self, &self,
num: usize,
constraints: PathConstraints,
rng: &mut XorShiftRng, rng: &mut XorShiftRng,
scenario: &mut Scenario, scenario: &mut Scenario,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>, neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map, map: &Map,
timer: &mut Timer, timer: &mut Timer,
) { ) {
if self.num_cars == 0 { for _ in 0..num {
return;
}
let lanes = pick_starting_lanes(
self.start_from_border.lanes(PathConstraints::Car, map),
false,
map,
);
if lanes.is_empty() {
timer.warn(format!(
"Can't start {} cars at border for {}",
self.num_cars, self.start_from_border
));
return;
};
for _ in 0..self.num_cars {
let depart = rand_time(rng, self.start_time, self.stop_time); let depart = rand_time(rng, self.start_time, self.stop_time);
if let Some(goal) = if let Some(goal) =
self.goal self.goal
.pick_driving_goal(PathConstraints::Car, map, &neighborhoods, rng, timer) .pick_driving_goal(constraints, map, &neighborhoods, rng, timer)
{ {
let id = PersonID(scenario.people.len()); let id = PersonID(scenario.people.len());
scenario.people.push(PersonSpec { scenario.people.push(PersonSpec {
id, id,
trips: vec![IndividTrip { trips: vec![IndividTrip {
depart, depart,
trip: SpawnTrip::CarAppearing { trip: SpawnTrip::FromBorder {
// Safe because pick_starting_lanes checks for this i: self.start_from_border.src_i(map),
start: Position::new(*lanes.choose(rng).unwrap(), MAX_CAR_LENGTH),
goal, goal,
is_bike: false, is_bike: constraints == PathConstraints::Bike,
}, },
}], }],
has_car: true, has_car: constraints == PathConstraints::Car,
car_initially_parked_at: None,
});
}
}
}
fn spawn_bikes(
&self,
rng: &mut XorShiftRng,
scenario: &mut Scenario,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
timer: &mut Timer,
) {
if self.num_bikes == 0 {
return;
}
let lanes = pick_starting_lanes(
self.start_from_border.lanes(PathConstraints::Bike, map),
true,
map,
);
if lanes.is_empty() {
timer.warn(format!(
"Can't start {} bikes at border for {}",
self.num_bikes, self.start_from_border
));
return;
};
for _ in 0..self.num_bikes {
let depart = rand_time(rng, self.start_time, self.stop_time);
if let Some(goal) =
self.goal
.pick_driving_goal(PathConstraints::Bike, map, &neighborhoods, rng, timer)
{
let id = PersonID(scenario.people.len());
scenario.people.push(PersonSpec {
id,
trips: vec![IndividTrip {
depart,
trip: SpawnTrip::CarAppearing {
start: Position::new(*lanes.choose(rng).unwrap(), BIKE_LENGTH),
goal,
is_bike: true,
},
}],
has_car: false,
car_initially_parked_at: None, car_initially_parked_at: None,
}); });
} }
@ -487,22 +435,3 @@ fn rand_time(rng: &mut XorShiftRng, low: Time, high: Time) -> Time {
assert!(high > low); assert!(high > low);
Time::START_OF_DAY + Duration::seconds(rng.gen_range(low.inner_seconds(), high.inner_seconds())) Time::START_OF_DAY + Duration::seconds(rng.gen_range(low.inner_seconds(), high.inner_seconds()))
} }
fn pick_starting_lanes(mut lanes: Vec<LaneID>, is_bike: bool, map: &Map) -> Vec<LaneID> {
let min_len = if is_bike { BIKE_LENGTH } else { MAX_CAR_LENGTH };
lanes.retain(|l| map.get_l(*l).length() > min_len);
if is_bike {
// If there's a choice between bike lanes and otherwise, always use the bike lanes.
let bike_lanes = lanes
.iter()
.filter(|l| map.get_l(**l).is_biking())
.cloned()
.collect::<Vec<LaneID>>();
if !bike_lanes.is_empty() {
lanes = bike_lanes;
}
}
lanes
}

View File

@ -4,7 +4,9 @@ use crate::{
}; };
use abstutil::{MultiMap, Timer}; use abstutil::{MultiMap, Timer};
use geom::{Distance, Duration, Speed, Time}; use geom::{Distance, Duration, Speed, Time};
use map_model::{BuildingID, BusRouteID, BusStopID, IntersectionID, Map, Position, RoadID}; use map_model::{
BuildingID, BusRouteID, BusStopID, IntersectionID, Map, PathConstraints, Position, RoadID,
};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::Rng; use rand::Rng;
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@ -39,11 +41,16 @@ pub struct IndividTrip {
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub enum SpawnTrip { pub enum SpawnTrip {
// Only for interactive / debug trips
CarAppearing { CarAppearing {
// TODO Replace start with building|border
start: Position, start: Position,
goal: DrivingGoal, goal: DrivingGoal,
// For bikes starting at a border, use CarAppearing. UsingBike implies a walk->bike trip. is_bike: bool,
},
FromBorder {
i: IntersectionID,
goal: DrivingGoal,
// For bikes starting at a border, use FromBorder. UsingBike implies a walk->bike trip.
is_bike: bool, is_bike: bool,
}, },
MaybeUsingParkedCar(BuildingID, DrivingGoal), MaybeUsingParkedCar(BuildingID, DrivingGoal),
@ -90,9 +97,14 @@ impl Scenario {
// TODO Or spawner? // TODO Or spawner?
sim.new_person(p.id, p.has_car); sim.new_person(p.id, p.has_car);
for t in &p.trips { for t in &p.trips {
// The RNG call is stable over edits. // The RNG call might change over edits for picking the spawning lane from a border
let spec = t.trip.clone().to_trip_spec(rng); // with multiple choices for a vehicle type.
let mut tmp_rng = abstutil::fork_rng(rng);
if let Some(spec) = t.trip.clone().to_trip_spec(&mut tmp_rng, map) {
spawner.schedule_trip(p.id, t.depart, spec, map, sim); spawner.schedule_trip(p.id, t.depart, spec, map, sim);
} else {
timer.warn(format!("Couldn't turn {:?} into a trip", t.trip));
}
} }
} }
@ -294,14 +306,14 @@ fn find_spot_near_building(
} }
impl SpawnTrip { impl SpawnTrip {
fn to_trip_spec(self, rng: &mut XorShiftRng) -> TripSpec { fn to_trip_spec(self, rng: &mut XorShiftRng, map: &Map) -> Option<TripSpec> {
match self { match self {
SpawnTrip::CarAppearing { SpawnTrip::CarAppearing {
start, start,
goal, goal,
is_bike, is_bike,
.. ..
} => TripSpec::CarAppearing { } => Some(TripSpec::CarAppearing {
start_pos: start, start_pos: start,
goal, goal,
vehicle_spec: if is_bike { vehicle_spec: if is_bike {
@ -310,37 +322,67 @@ impl SpawnTrip {
Scenario::rand_car(rng) Scenario::rand_car(rng)
}, },
ped_speed: Scenario::rand_ped_speed(rng), ped_speed: Scenario::rand_ped_speed(rng),
}),
SpawnTrip::FromBorder {
i, goal, is_bike, ..
} => Some(TripSpec::CarAppearing {
start_pos: {
let l = *map
.get_i(i)
.get_outgoing_lanes(
map,
if is_bike {
PathConstraints::Bike
} else {
PathConstraints::Car
}, },
SpawnTrip::MaybeUsingParkedCar(start_bldg, goal) => TripSpec::MaybeUsingParkedCar { )
.choose(rng)?;
TripSpec::spawn_vehicle_at(Position::new(l, Distance::ZERO), is_bike, map)?
},
goal,
vehicle_spec: if is_bike {
Scenario::rand_bike(rng)
} else {
Scenario::rand_car(rng)
},
ped_speed: Scenario::rand_ped_speed(rng),
}),
SpawnTrip::MaybeUsingParkedCar(start_bldg, goal) => {
Some(TripSpec::MaybeUsingParkedCar {
start_bldg, start_bldg,
goal, goal,
ped_speed: Scenario::rand_ped_speed(rng), ped_speed: Scenario::rand_ped_speed(rng),
}, })
SpawnTrip::UsingBike(start, goal) => TripSpec::UsingBike { }
SpawnTrip::UsingBike(start, goal) => Some(TripSpec::UsingBike {
start, start,
goal, goal,
vehicle: Scenario::rand_bike(rng), vehicle: Scenario::rand_bike(rng),
ped_speed: Scenario::rand_ped_speed(rng), ped_speed: Scenario::rand_ped_speed(rng),
}, }),
SpawnTrip::JustWalking(start, goal) => TripSpec::JustWalking { SpawnTrip::JustWalking(start, goal) => Some(TripSpec::JustWalking {
start, start,
goal, goal,
ped_speed: Scenario::rand_ped_speed(rng), ped_speed: Scenario::rand_ped_speed(rng),
}, }),
SpawnTrip::UsingTransit(start, goal, route, stop1, stop2) => TripSpec::UsingTransit { SpawnTrip::UsingTransit(start, goal, route, stop1, stop2) => {
Some(TripSpec::UsingTransit {
start, start,
goal, goal,
route, route,
stop1, stop1,
stop2, stop2,
ped_speed: Scenario::rand_ped_speed(rng), ped_speed: Scenario::rand_ped_speed(rng),
}, })
}
} }
} }
pub fn start_from_bldg(&self) -> Option<BuildingID> { pub fn start_from_bldg(&self) -> Option<BuildingID> {
match self { match self {
SpawnTrip::CarAppearing { .. } => None, SpawnTrip::CarAppearing { .. } => None,
SpawnTrip::FromBorder { .. } => None,
SpawnTrip::MaybeUsingParkedCar(b, _) => Some(*b), SpawnTrip::MaybeUsingParkedCar(b, _) => Some(*b),
SpawnTrip::UsingBike(ref spot, _) SpawnTrip::UsingBike(ref spot, _)
| SpawnTrip::JustWalking(ref spot, _) | SpawnTrip::JustWalking(ref spot, _)
@ -353,8 +395,8 @@ impl SpawnTrip {
pub fn start_from_border(&self) -> Option<IntersectionID> { pub fn start_from_border(&self) -> Option<IntersectionID> {
match self { match self {
// TODO CarAppearing might be from a border
SpawnTrip::CarAppearing { .. } => None, SpawnTrip::CarAppearing { .. } => None,
SpawnTrip::FromBorder { i, .. } => Some(*i),
SpawnTrip::MaybeUsingParkedCar(_, _) => None, SpawnTrip::MaybeUsingParkedCar(_, _) => None,
SpawnTrip::UsingBike(ref spot, _) SpawnTrip::UsingBike(ref spot, _)
| SpawnTrip::JustWalking(ref spot, _) | SpawnTrip::JustWalking(ref spot, _)
@ -368,6 +410,7 @@ impl SpawnTrip {
pub fn end_at_bldg(&self) -> Option<BuildingID> { pub fn end_at_bldg(&self) -> Option<BuildingID> {
match self { match self {
SpawnTrip::CarAppearing { ref goal, .. } SpawnTrip::CarAppearing { ref goal, .. }
| SpawnTrip::FromBorder { ref goal, .. }
| SpawnTrip::MaybeUsingParkedCar(_, ref goal) | SpawnTrip::MaybeUsingParkedCar(_, ref goal)
| SpawnTrip::UsingBike(_, ref goal) => match goal { | SpawnTrip::UsingBike(_, ref goal) => match goal {
DrivingGoal::ParkNear(b) => Some(*b), DrivingGoal::ParkNear(b) => Some(*b),
@ -385,6 +428,7 @@ impl SpawnTrip {
pub fn end_at_border(&self) -> Option<IntersectionID> { pub fn end_at_border(&self) -> Option<IntersectionID> {
match self { match self {
SpawnTrip::CarAppearing { ref goal, .. } SpawnTrip::CarAppearing { ref goal, .. }
| SpawnTrip::FromBorder { ref goal, .. }
| SpawnTrip::MaybeUsingParkedCar(_, ref goal) | SpawnTrip::MaybeUsingParkedCar(_, ref goal)
| SpawnTrip::UsingBike(_, ref goal) => match goal { | SpawnTrip::UsingBike(_, ref goal) => match goal {
DrivingGoal::ParkNear(_) => None, DrivingGoal::ParkNear(_) => None,

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
CarID, Command, CreateCar, CreatePedestrian, DrivingGoal, ParkingSimState, ParkingSpot, CarID, Command, CreateCar, CreatePedestrian, DrivingGoal, ParkingSimState, ParkingSpot,
PedestrianID, PersonID, Scheduler, SidewalkPOI, SidewalkSpot, Sim, TripEndpoint, TripLeg, PedestrianID, PersonID, Scheduler, SidewalkPOI, SidewalkSpot, Sim, TripEndpoint, TripLeg,
TripManager, VehicleSpec, VehicleType, MAX_CAR_LENGTH, TripManager, VehicleSpec, VehicleType, BIKE_LENGTH, MAX_CAR_LENGTH,
}; };
use abstutil::Timer; use abstutil::Timer;
use geom::{Speed, Time, EPSILON_DIST}; use geom::{Speed, Time, EPSILON_DIST};
@ -138,13 +138,15 @@ impl TripSpawner {
} => { } => {
if start_pos.dist_along() < vehicle_spec.length { if start_pos.dist_along() < vehicle_spec.length {
panic!( panic!(
"Can't spawn a car at {}; too close to the start", "Can't spawn a {:?} at {}; too close to the start",
vehicle_spec.vehicle_type,
start_pos.dist_along() start_pos.dist_along()
); );
} }
if start_pos.dist_along() >= map.get_l(start_pos.lane()).length() { if start_pos.dist_along() >= map.get_l(start_pos.lane()).length() {
panic!( panic!(
"Can't spawn a car at {}; {} isn't that long", "Can't spawn a {:?} at {}; {} isn't that long",
vehicle_spec.vehicle_type,
start_pos.dist_along(), start_pos.dist_along(),
start_pos.lane() start_pos.lane()
); );
@ -154,7 +156,10 @@ impl TripSpawner {
if start_pos.lane() == *end_lane if start_pos.lane() == *end_lane
&& start_pos.dist_along() == map.get_l(*end_lane).length() && start_pos.dist_along() == map.get_l(*end_lane).length()
{ {
panic!("Can't start a car at the edge of a border already"); panic!(
"Can't start a {:?} at the edge of a border already",
vehicle_spec.vehicle_type
);
} }
} }
DrivingGoal::ParkNear(_) => {} DrivingGoal::ParkNear(_) => {}
@ -534,16 +539,17 @@ impl TripSpawner {
impl TripSpec { impl TripSpec {
// If possible, fixes problems that schedule_trip would hit. // If possible, fixes problems that schedule_trip would hit.
pub fn spawn_car_at(pos: Position, map: &Map) -> Option<Position> { pub fn spawn_vehicle_at(pos: Position, is_bike: bool, map: &Map) -> Option<Position> {
let len = map.get_l(pos.lane()).length(); let lane_len = map.get_l(pos.lane()).length();
let vehicle_len = if is_bike { BIKE_LENGTH } else { MAX_CAR_LENGTH };
// There's no hope. // There's no hope.
if len <= MAX_CAR_LENGTH { if lane_len <= vehicle_len {
return None; return None;
} }
if pos.dist_along() < MAX_CAR_LENGTH { if pos.dist_along() < vehicle_len {
Some(Position::new(pos.lane(), MAX_CAR_LENGTH)) Some(Position::new(pos.lane(), vehicle_len))
} else if pos.dist_along() == len { } else if pos.dist_along() == lane_len {
Some(Position::new(pos.lane(), pos.dist_along() - EPSILON_DIST)) Some(Position::new(pos.lane(), pos.dist_along() - EPSILON_DIST))
} else { } else {
Some(pos) Some(pos)