mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
refactoring some trip spawning stuff from importer, using it in the new agent spawner too
This commit is contained in:
parent
ac752fccfe
commit
aceadef79f
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user