refactoring some trip spawning stuff from importer, using it in the new agent spawner too

This commit is contained in:
Dustin Carlino 2020-06-01 17:37:54 -07:00
parent ac752fccfe
commit aceadef79f
4 changed files with 209 additions and 228 deletions

View File

@ -1,7 +1,7 @@
use crate::app::{App, ShowEverything}; use crate::app::{App, ShowEverything};
use crate::common::{CityPicker, CommonState}; use crate::common::{CityPicker, CommonState};
use crate::edit::EditMode; use crate::edit::EditMode;
use crate::game::{msg, State, Transition, WizardState}; use crate::game::{State, Transition, WizardState};
use crate::helpers::{nice_map_name, ID}; use crate::helpers::{nice_map_name, ID};
use crate::sandbox::gameplay::{GameplayMode, GameplayState}; use crate::sandbox::gameplay::{GameplayMode, GameplayState};
use crate::sandbox::SandboxControls; use crate::sandbox::SandboxControls;
@ -15,8 +15,10 @@ use geom::{Distance, Duration, Polygon};
use map_model::{ use map_model::{
IntersectionID, Map, PathConstraints, PathRequest, Position, NORMAL_LANE_THICKNESS, IntersectionID, Map, PathConstraints, PathRequest, Position, NORMAL_LANE_THICKNESS,
}; };
use rand_xorshift::XorShiftRng; use sim::{
use sim::{DontDrawAgents, SidewalkSpot, Sim, TripEndpoint, TripMode, TripSpawner, TripSpec}; DontDrawAgents, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip,
TripEndpoint, TripMode,
};
use std::collections::BTreeSet; use std::collections::BTreeSet;
// TODO Maybe remember what things were spawned, offer to replay this later // TODO Maybe remember what things were spawned, offer to replay this later
@ -304,27 +306,31 @@ impl State for AgentSpawner {
} }
if self.goal.is_some() && app.per_obj.left_click(ctx, "end here") { if self.goal.is_some() && app.per_obj.left_click(ctx, "end here") {
let mut rng = app.primary.current_flags.sim_flags.make_rng();
let map = &app.primary.map; let map = &app.primary.map;
let sim = &mut app.primary.sim; let mut scenario = Scenario::empty(map, "one-shot");
let mut spawner = sim.make_spawner(); scenario.people.push(PersonSpec {
let err = schedule_trip( id: PersonID(app.primary.sim.get_all_people().len()),
self.source.take().unwrap(), orig_id: None,
self.goal.take().unwrap().0, trips: vec![IndividTrip {
self.composite.dropdown_value("mode"), depart: app.primary.sim.time(),
trip: SpawnTrip::new(
self.source.take().unwrap(),
self.goal.take().unwrap().0,
self.composite.dropdown_value("mode"),
map,
),
}],
});
let mut rng = app.primary.current_flags.sim_flags.make_rng();
scenario.instantiate(
&mut app.primary.sim,
map, map,
sim,
&mut spawner,
&mut rng, &mut rng,
&mut Timer::new("spawn trip"),
); );
sim.flush_spawner(spawner, map, &mut Timer::new("spawn trip")); app.primary.sim.normal_step(map, SMALL_DT);
sim.normal_step(map, SMALL_DT);
app.recalculate_current_selection(ctx); app.recalculate_current_selection(ctx);
if let Some(e) = err { return Transition::Pop;
return Transition::Replace(msg("Spawning error", vec![e]));
} else {
return Transition::Pop;
}
} }
} }
} }
@ -398,17 +404,3 @@ fn pos(endpt: TripEndpoint, mode: TripMode, from: bool, map: &Map) -> Option<Pos
}, },
} }
} }
// Returns optional error message
fn schedule_trip(
from: TripEndpoint,
to: TripEndpoint,
mode: TripMode,
map: &Map,
sim: &mut Sim,
spawner: &mut TripSpawner,
rng: &mut XorShiftRng,
) -> Option<String> {
// TODO
None
}

View File

@ -3,223 +3,135 @@ use abstutil::{prettyprint_usize, MultiMap, Timer};
use geom::LonLat; use geom::LonLat;
use map_model::{BuildingID, IntersectionID, Map, PathConstraints, PathRequest, PathStep}; use map_model::{BuildingID, IntersectionID, Map, PathConstraints, PathRequest, PathStep};
use sim::{ use sim::{
DrivingGoal, IndividTrip, OffMapLocation, OrigPersonID, PersonID, PersonSpec, Scenario, IndividTrip, OffMapLocation, OrigPersonID, PersonID, PersonSpec, Scenario, SpawnTrip,
SidewalkSpot, SpawnTrip, TripMode, TripEndpoint, TripMode,
}; };
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Trip { struct Trip {
from: TripEndpt, from: TripEndpoint,
to: TripEndpt, to: TripEndpoint,
orig: OrigTrip, orig: OrigTrip,
} }
#[derive(Clone, Debug)] // TODO Saying this function exploded in complexity is like saying I have coffee occasionally.
enum TripEndpt { fn endpoints(
Building(BuildingID), from: &Endpoint,
Border(IntersectionID, OffMapLocation), to: &Endpoint,
} map: &Map,
osm_id_to_bldg: &HashMap<i64, BuildingID>,
impl Trip { (in_borders, out_borders): (
fn to_spawn_trip(&self, map: &Map) -> SpawnTrip { &Vec<(IntersectionID, LonLat)>,
match self.orig.mode { &Vec<(IntersectionID, LonLat)>,
TripMode::Drive => match self.from { ),
TripEndpt::Border(i, ref origin) => SpawnTrip::FromBorder { constraints: PathConstraints,
dr: map.get_i(i).some_outgoing_road(map), maybe_huge_map: Option<&(&Map, HashMap<i64, BuildingID>)>,
goal: self.to.driving_goal(PathConstraints::Car, map), ) -> Option<(TripEndpoint, TripEndpoint)> {
is_bike: false, let from_bldg = from
origin: Some(origin.clone()), .osm_building
}, .and_then(|id| osm_id_to_bldg.get(&id))
TripEndpt::Building(b) => { .cloned();
SpawnTrip::UsingParkedCar(b, self.to.driving_goal(PathConstraints::Car, map)) let to_bldg = to
} .osm_building
}, .and_then(|id| osm_id_to_bldg.get(&id))
TripMode::Bike => match self.from { .cloned();
TripEndpt::Building(b) => SpawnTrip::UsingBike( let border_endpt = match (from_bldg, to_bldg) {
SidewalkSpot::building(b, map), (Some(b1), Some(b2)) => {
self.to.driving_goal(PathConstraints::Bike, map), return Some((TripEndpoint::Bldg(b1), TripEndpoint::Bldg(b2)));
),
TripEndpt::Border(i, ref origin) => SpawnTrip::FromBorder {
dr: map.get_i(i).some_outgoing_road(map),
goal: self.to.driving_goal(PathConstraints::Bike, map),
is_bike: true,
origin: Some(origin.clone()),
},
},
TripMode::Walk => SpawnTrip::JustWalking(
self.from.start_sidewalk_spot(map),
self.to.end_sidewalk_spot(map),
),
TripMode::Transit => {
let start = self.from.start_sidewalk_spot(map);
let goal = self.to.end_sidewalk_spot(map);
if let Some((stop1, stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{
SpawnTrip::UsingTransit(start, goal, route, stop1, stop2)
} else {
//timer.warn(format!("{:?} not actually using transit, because pathfinding
// didn't find any useful route", trip));
SpawnTrip::JustWalking(start, goal)
}
}
} }
} (Some(_), None) => to,
} (None, Some(_)) => from,
(None, None) => {
// TODO Detect and handle pass-through trips
return None;
}
};
let usable_borders = if from_bldg.is_some() {
out_borders
} else {
in_borders
};
impl TripEndpt { // The trip begins or ends at a border.
// TODO Saying this function exploded in complexity is like saying I have coffee occasionally. // TODO It'd be nice to fix depart_at, trip_time, and trip_dist. Assume constant speed
fn new( // through the trip. But when I last tried this, the distance was way off. :\
from: &Endpoint,
to: &Endpoint, // If this isn't huge_seattle, use the large map to find the real path somebody might take,
map: &Map, // then try to match that to a border in the smaller map.
osm_id_to_bldg: &HashMap<i64, BuildingID>, let maybe_other_border = if let Some((huge_map, huge_osm_id_to_bldg)) = maybe_huge_map {
(in_borders, out_borders): ( let maybe_b1 = from
&Vec<(IntersectionID, LonLat)>,
&Vec<(IntersectionID, LonLat)>,
),
constraints: PathConstraints,
maybe_huge_map: Option<&(&Map, HashMap<i64, BuildingID>)>,
) -> Option<(TripEndpt, TripEndpt)> {
let from_bldg = from
.osm_building .osm_building
.and_then(|id| osm_id_to_bldg.get(&id)) .and_then(|id| huge_osm_id_to_bldg.get(&id))
.cloned(); .cloned();
let to_bldg = to let maybe_b2 = to
.osm_building .osm_building
.and_then(|id| osm_id_to_bldg.get(&id)) .and_then(|id| huge_osm_id_to_bldg.get(&id))
.cloned(); .cloned();
let border_endpt = match (from_bldg, to_bldg) { if let (Some(b1), Some(b2)) = (maybe_b1, maybe_b2) {
(Some(b1), Some(b2)) => { // TODO Super rough...
return Some((TripEndpt::Building(b1), TripEndpt::Building(b2))); let start = if constraints == PathConstraints::Pedestrian {
} Some(huge_map.get_b(b1).front_path.sidewalk)
(Some(_), None) => to, } else {
(None, Some(_)) => from, huge_map.get_b(b1).parking.as_ref().map(|p| p.driving_pos)
(None, None) => { };
// TODO Detect and handle pass-through trips let end = if constraints == PathConstraints::Pedestrian {
return None; Some(huge_map.get_b(b2).front_path.sidewalk)
} } else {
}; huge_map.get_b(b2).parking.as_ref().map(|p| p.driving_pos)
let usable_borders = if from_bldg.is_some() { };
out_borders if let Some(path) = start.and_then(|start| {
} else { end.and_then(|end| {
in_borders huge_map.pathfind(PathRequest {
}; start,
end,
// The trip begins or ends at a border. constraints,
// TODO It'd be nice to fix depart_at, trip_time, and trip_dist. Assume constant speed
// through the trip. But when I last tried this, the distance was way off. :\
// If this isn't huge_seattle, use the large map to find the real path somebody might take,
// then try to match that to a border in the smaller map.
let maybe_other_border = if let Some((huge_map, huge_osm_id_to_bldg)) = maybe_huge_map {
let maybe_b1 = from
.osm_building
.and_then(|id| huge_osm_id_to_bldg.get(&id))
.cloned();
let maybe_b2 = to
.osm_building
.and_then(|id| huge_osm_id_to_bldg.get(&id))
.cloned();
if let (Some(b1), Some(b2)) = (maybe_b1, maybe_b2) {
// TODO Super rough...
let start = if constraints == PathConstraints::Pedestrian {
Some(huge_map.get_b(b1).front_path.sidewalk)
} else {
huge_map.get_b(b1).parking.as_ref().map(|p| p.driving_pos)
};
let end = if constraints == PathConstraints::Pedestrian {
Some(huge_map.get_b(b2).front_path.sidewalk)
} else {
huge_map.get_b(b2).parking.as_ref().map(|p| p.driving_pos)
};
if let Some(path) = start.and_then(|start| {
end.and_then(|end| {
huge_map.pathfind(PathRequest {
start,
end,
constraints,
})
}) })
}) { })
// Do any of the usable borders match the path? }) {
// TODO Calculate this once // Do any of the usable borders match the path?
let mut node_id_to_border = HashMap::new(); // TODO Calculate this once
for (i, _) in usable_borders { let mut node_id_to_border = HashMap::new();
node_id_to_border.insert(map.get_i(*i).orig_id, *i); for (i, _) in usable_borders {
} node_id_to_border.insert(map.get_i(*i).orig_id, *i);
let mut found_border = None; }
for step in path.get_steps() { let mut found_border = None;
if let PathStep::Turn(t) = step { for step in path.get_steps() {
if let Some(i) = if let PathStep::Turn(t) = step {
node_id_to_border.get(&huge_map.get_i(t.parent).orig_id) if let Some(i) = node_id_to_border.get(&huge_map.get_i(t.parent).orig_id) {
{ found_border = Some(*i);
found_border = Some(*i); break;
break;
}
} }
} }
found_border
} else {
None
} }
found_border
} else { } else {
None None
} }
} else { } else {
None None
};
// Fallback to finding the nearest border with straight-line distance
let border_i = maybe_other_border.or_else(|| {
usable_borders
.iter()
.min_by_key(|(_, pt)| pt.fast_dist(border_endpt.pos))
.map(|(id, _)| *id)
})?;
let border = TripEndpt::Border(
border_i,
OffMapLocation {
gps: border_endpt.pos,
parcel_id: border_endpt.parcel_id,
},
);
if let Some(b) = from_bldg {
Some((TripEndpt::Building(b), border))
} else {
Some((border, TripEndpt::Building(to_bldg.unwrap())))
}
}
fn start_sidewalk_spot(&self, map: &Map) -> SidewalkSpot {
match self {
TripEndpt::Building(b) => SidewalkSpot::building(*b, map),
TripEndpt::Border(i, origin) => {
SidewalkSpot::start_at_border(*i, Some(origin.clone()), map).unwrap()
}
}
}
fn end_sidewalk_spot(&self, map: &Map) -> SidewalkSpot {
match self {
TripEndpt::Building(b) => SidewalkSpot::building(*b, map),
TripEndpt::Border(i, destination) => {
SidewalkSpot::end_at_border(*i, Some(destination.clone()), map).unwrap()
}
}
}
fn driving_goal(&self, constraints: PathConstraints, map: &Map) -> DrivingGoal {
match self {
TripEndpt::Building(b) => DrivingGoal::ParkNear(*b),
TripEndpt::Border(i, destination) => DrivingGoal::end_at_border(
map.get_i(*i).some_incoming_road(map),
constraints,
Some(destination.clone()),
map,
)
.unwrap(),
} }
} else {
None
};
// Fallback to finding the nearest border with straight-line distance
let border_i = maybe_other_border.or_else(|| {
usable_borders
.iter()
.min_by_key(|(_, pt)| pt.fast_dist(border_endpt.pos))
.map(|(id, _)| *id)
})?;
let border = TripEndpoint::Border(
border_i,
Some(OffMapLocation {
gps: border_endpt.pos,
parcel_id: border_endpt.parcel_id,
}),
);
if let Some(b) = from_bldg {
Some((TripEndpoint::Bldg(b), border))
} else {
Some((border, TripEndpoint::Bldg(to_bldg.unwrap())))
} }
} }
@ -286,7 +198,7 @@ fn clip_trips(map: &Map, popdat: &PopDat, huge_map: &Map, timer: &mut Timer) ->
let total_trips = popdat.trips.len(); let total_trips = popdat.trips.len();
let maybe_results: Vec<Option<Trip>> = let maybe_results: Vec<Option<Trip>> =
timer.parallelize("clip trips", popdat.trips.iter().collect(), |orig| { timer.parallelize("clip trips", popdat.trips.iter().collect(), |orig| {
let (from, to) = TripEndpt::new( let (from, to) = endpoints(
&orig.from, &orig.from,
&orig.to, &orig.to,
map, map,
@ -338,7 +250,7 @@ pub fn make_weekday_scenario(
for (trip, depart, person, seq) in for (trip, depart, person, seq) in
timer.parallelize("turn Soundcast trips into SpawnTrips", trips, |trip| { timer.parallelize("turn Soundcast trips into SpawnTrips", trips, |trip| {
( (
trip.to_spawn_trip(map), SpawnTrip::new(trip.from, trip.to, trip.orig.mode, map),
trip.orig.depart_at, trip.orig.depart_at,
trip.orig.person, trip.orig.person,
trip.orig.seq, trip.orig.seq,

View File

@ -471,6 +471,50 @@ impl SpawnTrip {
} }
} }
} }
pub fn new(from: TripEndpoint, to: TripEndpoint, mode: TripMode, map: &Map) -> SpawnTrip {
match mode {
TripMode::Drive => match from {
TripEndpoint::Bldg(b) => {
SpawnTrip::UsingParkedCar(b, to.driving_goal(PathConstraints::Car, map))
}
TripEndpoint::Border(i, ref origin) => SpawnTrip::FromBorder {
dr: map.get_i(i).some_outgoing_road(map),
goal: to.driving_goal(PathConstraints::Car, map),
is_bike: false,
origin: origin.clone(),
},
},
TripMode::Bike => match from {
TripEndpoint::Bldg(b) => SpawnTrip::UsingBike(
SidewalkSpot::building(b, map),
to.driving_goal(PathConstraints::Bike, map),
),
TripEndpoint::Border(i, ref origin) => SpawnTrip::FromBorder {
dr: map.get_i(i).some_outgoing_road(map),
goal: to.driving_goal(PathConstraints::Bike, map),
is_bike: true,
origin: origin.clone(),
},
},
TripMode::Walk => {
SpawnTrip::JustWalking(from.start_sidewalk_spot(map), to.end_sidewalk_spot(map))
}
TripMode::Transit => {
let start = from.start_sidewalk_spot(map);
let goal = to.end_sidewalk_spot(map);
if let Some((stop1, stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{
SpawnTrip::UsingTransit(start, goal, route, stop1, stop2)
} else {
//timer.warn(format!("{:?} not actually using transit, because pathfinding
// didn't find any useful route", trip));
SpawnTrip::JustWalking(start, goal)
}
}
}
}
} }
impl PersonSpec { impl PersonSpec {

View File

@ -1415,3 +1415,36 @@ pub enum PersonState {
Inside(BuildingID), Inside(BuildingID),
OffMap, OffMap,
} }
impl TripEndpoint {
pub(crate) fn start_sidewalk_spot(&self, map: &Map) -> SidewalkSpot {
match self {
TripEndpoint::Bldg(b) => SidewalkSpot::building(*b, map),
TripEndpoint::Border(i, origin) => {
SidewalkSpot::start_at_border(*i, origin.clone(), map).unwrap()
}
}
}
pub(crate) fn end_sidewalk_spot(&self, map: &Map) -> SidewalkSpot {
match self {
TripEndpoint::Bldg(b) => SidewalkSpot::building(*b, map),
TripEndpoint::Border(i, destination) => {
SidewalkSpot::end_at_border(*i, destination.clone(), map).unwrap()
}
}
}
pub(crate) fn driving_goal(&self, constraints: PathConstraints, map: &Map) -> DrivingGoal {
match self {
TripEndpoint::Bldg(b) => DrivingGoal::ParkNear(*b),
TripEndpoint::Border(i, destination) => DrivingGoal::end_at_border(
map.get_i(*i).some_incoming_road(map),
constraints,
destination.clone(),
map,
)
.unwrap(),
}
}
}