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::common::{CityPicker, CommonState};
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::sandbox::gameplay::{GameplayMode, GameplayState};
use crate::sandbox::SandboxControls;
@ -15,8 +15,10 @@ use geom::{Distance, Duration, Polygon};
use map_model::{
IntersectionID, Map, PathConstraints, PathRequest, Position, NORMAL_LANE_THICKNESS,
};
use rand_xorshift::XorShiftRng;
use sim::{DontDrawAgents, SidewalkSpot, Sim, TripEndpoint, TripMode, TripSpawner, TripSpec};
use sim::{
DontDrawAgents, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip,
TripEndpoint, TripMode,
};
use std::collections::BTreeSet;
// 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") {
let mut rng = app.primary.current_flags.sim_flags.make_rng();
let map = &app.primary.map;
let sim = &mut app.primary.sim;
let mut spawner = sim.make_spawner();
let err = schedule_trip(
self.source.take().unwrap(),
self.goal.take().unwrap().0,
self.composite.dropdown_value("mode"),
let mut scenario = Scenario::empty(map, "one-shot");
scenario.people.push(PersonSpec {
id: PersonID(app.primary.sim.get_all_people().len()),
orig_id: None,
trips: vec![IndividTrip {
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,
sim,
&mut spawner,
&mut rng,
&mut Timer::new("spawn trip"),
);
sim.flush_spawner(spawner, map, &mut Timer::new("spawn trip"));
sim.normal_step(map, SMALL_DT);
app.primary.sim.normal_step(map, SMALL_DT);
app.recalculate_current_selection(ctx);
if let Some(e) = err {
return Transition::Replace(msg("Spawning error", vec![e]));
} else {
return Transition::Pop;
}
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 map_model::{BuildingID, IntersectionID, Map, PathConstraints, PathRequest, PathStep};
use sim::{
DrivingGoal, IndividTrip, OffMapLocation, OrigPersonID, PersonID, PersonSpec, Scenario,
SidewalkSpot, SpawnTrip, TripMode,
IndividTrip, OffMapLocation, OrigPersonID, PersonID, PersonSpec, Scenario, SpawnTrip,
TripEndpoint, TripMode,
};
use std::collections::HashMap;
#[derive(Clone, Debug)]
struct Trip {
from: TripEndpt,
to: TripEndpt,
from: TripEndpoint,
to: TripEndpoint,
orig: OrigTrip,
}
#[derive(Clone, Debug)]
enum TripEndpt {
Building(BuildingID),
Border(IntersectionID, OffMapLocation),
}
impl Trip {
fn to_spawn_trip(&self, map: &Map) -> SpawnTrip {
match self.orig.mode {
TripMode::Drive => match self.from {
TripEndpt::Border(i, ref origin) => SpawnTrip::FromBorder {
dr: map.get_i(i).some_outgoing_road(map),
goal: self.to.driving_goal(PathConstraints::Car, map),
is_bike: false,
origin: Some(origin.clone()),
},
TripEndpt::Building(b) => {
SpawnTrip::UsingParkedCar(b, self.to.driving_goal(PathConstraints::Car, map))
}
},
TripMode::Bike => match self.from {
TripEndpt::Building(b) => SpawnTrip::UsingBike(
SidewalkSpot::building(b, map),
self.to.driving_goal(PathConstraints::Bike, map),
),
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)
}
}
// TODO Saying this function exploded in complexity is like saying I have coffee occasionally.
fn endpoints(
from: &Endpoint,
to: &Endpoint,
map: &Map,
osm_id_to_bldg: &HashMap<i64, BuildingID>,
(in_borders, out_borders): (
&Vec<(IntersectionID, LonLat)>,
&Vec<(IntersectionID, LonLat)>,
),
constraints: PathConstraints,
maybe_huge_map: Option<&(&Map, HashMap<i64, BuildingID>)>,
) -> Option<(TripEndpoint, TripEndpoint)> {
let from_bldg = from
.osm_building
.and_then(|id| osm_id_to_bldg.get(&id))
.cloned();
let to_bldg = to
.osm_building
.and_then(|id| osm_id_to_bldg.get(&id))
.cloned();
let border_endpt = match (from_bldg, to_bldg) {
(Some(b1), Some(b2)) => {
return Some((TripEndpoint::Bldg(b1), TripEndpoint::Bldg(b2)));
}
}
}
(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 {
// TODO Saying this function exploded in complexity is like saying I have coffee occasionally.
fn new(
from: &Endpoint,
to: &Endpoint,
map: &Map,
osm_id_to_bldg: &HashMap<i64, BuildingID>,
(in_borders, out_borders): (
&Vec<(IntersectionID, LonLat)>,
&Vec<(IntersectionID, LonLat)>,
),
constraints: PathConstraints,
maybe_huge_map: Option<&(&Map, HashMap<i64, BuildingID>)>,
) -> Option<(TripEndpt, TripEndpt)> {
let from_bldg = from
// The trip begins or ends at a border.
// 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| osm_id_to_bldg.get(&id))
.and_then(|id| huge_osm_id_to_bldg.get(&id))
.cloned();
let to_bldg = to
let maybe_b2 = to
.osm_building
.and_then(|id| osm_id_to_bldg.get(&id))
.and_then(|id| huge_osm_id_to_bldg.get(&id))
.cloned();
let border_endpt = match (from_bldg, to_bldg) {
(Some(b1), Some(b2)) => {
return Some((TripEndpt::Building(b1), TripEndpt::Building(b2)));
}
(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
};
// The trip begins or ends at a border.
// 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,
})
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
let mut node_id_to_border = HashMap::new();
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() {
if let PathStep::Turn(t) = step {
if let Some(i) =
node_id_to_border.get(&huge_map.get_i(t.parent).orig_id)
{
found_border = Some(*i);
break;
}
})
}) {
// Do any of the usable borders match the path?
// TODO Calculate this once
let mut node_id_to_border = HashMap::new();
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() {
if let PathStep::Turn(t) = step {
if let Some(i) = node_id_to_border.get(&huge_map.get_i(t.parent).orig_id) {
found_border = Some(*i);
break;
}
}
found_border
} else {
None
}
found_border
} else {
None
}
} 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 = 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 maybe_results: Vec<Option<Trip>> =
timer.parallelize("clip trips", popdat.trips.iter().collect(), |orig| {
let (from, to) = TripEndpt::new(
let (from, to) = endpoints(
&orig.from,
&orig.to,
map,
@ -338,7 +250,7 @@ pub fn make_weekday_scenario(
for (trip, depart, person, seq) in
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.person,
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 {

View File

@ -1415,3 +1415,36 @@ pub enum PersonState {
Inside(BuildingID),
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(),
}
}
}