diff --git a/game/src/sandbox/gameplay/freeform.rs b/game/src/sandbox/gameplay/freeform.rs index fa1d1af58b..347bde7d79 100644 --- a/game/src/sandbox/gameplay/freeform.rs +++ b/game/src/sandbox/gameplay/freeform.rs @@ -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 Option { - // TODO - None -} diff --git a/importer/src/soundcast/trips.rs b/importer/src/soundcast/trips.rs index 02f9e48647..816878989e 100644 --- a/importer/src/soundcast/trips.rs +++ b/importer/src/soundcast/trips.rs @@ -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, + (in_borders, out_borders): ( + &Vec<(IntersectionID, LonLat)>, + &Vec<(IntersectionID, LonLat)>, + ), + constraints: PathConstraints, + maybe_huge_map: Option<&(&Map, HashMap)>, +) -> 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, - (in_borders, out_borders): ( - &Vec<(IntersectionID, LonLat)>, - &Vec<(IntersectionID, LonLat)>, - ), - constraints: PathConstraints, - maybe_huge_map: Option<&(&Map, HashMap)>, - ) -> 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> = 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, diff --git a/sim/src/make/scenario.rs b/sim/src/make/scenario.rs index 77f0ef162c..16a13b67e0 100644 --- a/sim/src/make/scenario.rs +++ b/sim/src/make/scenario.rs @@ -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 { diff --git a/sim/src/trips.rs b/sim/src/trips.rs index a2f5692201..afddea1f79 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -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(), + } + } +}