diff --git a/docs/design/biking.md b/docs/design/biking.md index 2bea061af1..2f71c82d18 100644 --- a/docs/design/biking.md +++ b/docs/design/biking.md @@ -42,7 +42,7 @@ minimal. Alternate idea for another branch: = spawn param to decide if a trip without an owned car will instead bike = walking state can own the 'parking/unparking' state. - make sure biking from border works, needs an extra bit i think - - need a new DrivingGoal, simpler than ParkNear. + = need a new DrivingGoal, simpler than ParkNear. - entirely new render code, but the same DrawCarInput (plus is_bike bit). that part shouldn't matter, right? - render peds doing bike prep differently diff --git a/sim/src/driving.rs b/sim/src/driving.rs index d7ca7d3e83..1932fa1bbe 100644 --- a/sim/src/driving.rs +++ b/sim/src/driving.rs @@ -73,6 +73,7 @@ impl Eq for Car {} pub enum Action { StartParking(ParkingSpot), WorkOnParking, + StartParkingBike, Continue(Acceleration, Vec), // TODO Get rid of this one VanishAtDeadEnd, @@ -557,8 +558,10 @@ impl DrivingSimState { } // Note that this populates the view BEFORE the step is applied. - // Returns cars that reached a parking spot this step, and also the cars that vanished at a - // border. + // Returns + // 1) cars that reached a parking spot this step + // 2) the cars that vanished at a border + // 3) the bikes that reached some ending and should start parking pub fn step( &mut self, view: &mut WorldView, @@ -571,7 +574,7 @@ impl DrivingSimState { transit_sim: &mut TransitSimState, rng: &mut XorShiftRng, current_agent: &mut Option, - ) -> Result<(Vec, Vec), Error> { + ) -> Result<(Vec, Vec, Vec<(CarID, LaneID, Distance)>), Error> { self.populate_view(view); // Could be concurrent, since this is deterministic -- EXCEPT for the rng, used to @@ -600,6 +603,7 @@ impl DrivingSimState { let mut finished_parking: Vec = Vec::new(); let mut vanished_at_border: Vec = Vec::new(); + let mut done_biking: Vec<(CarID, LaneID, Distance)> = Vec::new(); // Apply moves. Since lookahead behavior works, there are no conflicts to resolve, meaning // this could be applied concurrently! @@ -627,6 +631,14 @@ impl DrivingSimState { self.cars.get_mut(&id).unwrap().parking = Some(state); } } + Action::StartParkingBike => { + { + let c = self.cars.get(&id).unwrap(); + done_biking.push((*id, c.on.as_lane(), c.dist_along)); + } + self.cars.remove(&id); + self.routers.remove(&id); + } Action::Continue(accel, ref requests) => { let done = { let c = self.cars.get_mut(&id).unwrap(); @@ -697,7 +709,7 @@ impl DrivingSimState { } } - Ok((finished_parking, vanished_at_border)) + Ok((finished_parking, vanished_at_border, done_biking)) } // True if the car started, false if there wasn't currently room diff --git a/sim/src/sim.rs b/sim/src/sim.rs index b33787b5aa..48c24c4152 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -161,7 +161,7 @@ impl Sim { &mut self.trips_state, ); - let (newly_parked, at_border) = self.driving_state.step( + let (newly_parked, at_border, done_biking) = self.driving_state.step( &mut view, &mut events, self.time, @@ -187,6 +187,11 @@ impl Sim { for c in at_border { self.trips_state.car_reached_border(c, self.time); } + for (bike, lane, dist) in done_biking { + // TODO push an event, backtrace, etc + self.spawner + .bike_reached_end(self.time, bike, lane, dist, &mut self.trips_state); + } self.walking_state.populate_view(&mut view); let (reached_parking, ready_to_bike) = self.walking_state.step( diff --git a/sim/src/spawn.rs b/sim/src/spawn.rs index 8843d17126..a247527914 100644 --- a/sim/src/spawn.rs +++ b/sim/src/spawn.rs @@ -265,8 +265,8 @@ impl Spawner { } Command::Bike { trip, - vehicle, - goal, + ref vehicle, + ref goal, .. } => { if driving_sim.start_car_on_lane( @@ -282,7 +282,7 @@ impl Spawner { start: req.0, dist_along: req.1, router: match goal { - DrivingGoal::ParkNear(b) => { + DrivingGoal::ParkNear(_) => { Router::make_bike_router(path, req.3) } DrivingGoal::Border(_, _) => { @@ -559,8 +559,13 @@ impl Spawner { let bike_id = CarID(self.car_id_counter); self.car_id_counter += 1; + let first_spot = { + let b = map.get_b(start_bldg); + SidewalkSpot::bike_rack(b.front_path.sidewalk, b.front_path.dist_along_sidewalk) + }; + let mut legs = vec![ - TripLeg::Walk(SidewalkSpot::bike_rack(start_bldg, map)), + TripLeg::Walk(first_spot.clone()), TripLeg::Bike(Vehicle::generate_bike(bike_id, rng), goal.clone()), ]; if let DrivingGoal::ParkNear(b) = goal { @@ -571,7 +576,7 @@ impl Spawner { trips.new_trip(at, ped_id, legs), ped_id, SidewalkSpot::building(start_bldg, map), - SidewalkSpot::bike_rack(start_bldg, map), + first_spot, )); } @@ -693,6 +698,24 @@ impl Spawner { )); } + pub fn bike_reached_end( + &mut self, + at: Tick, + bike: CarID, + lane: LaneID, + dist: Distance, + trips: &mut TripManager, + ) { + let (trip, ped, walk_to) = trips.bike_reached_end(bike); + self.enqueue_command(Command::Walk( + at.next(), + trip, + ped, + SidewalkSpot::bike_rack(lane, dist), + walk_to, + )); + } + pub fn ped_ready_to_bike( &mut self, at: Tick, diff --git a/sim/src/trips.rs b/sim/src/trips.rs index 513dc768c1..c126ad93aa 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -87,10 +87,24 @@ impl TripManager { x => panic!("First trip leg {:?} doesn't match ped_ready_to_bike", x), }; let (vehicle, bike_to) = match trip.legs[0] { - TripLeg::Bike(vehicle, to) => (vehicle, to), + TripLeg::Bike(ref vehicle, ref to) => (vehicle, to), ref x => panic!("Next trip leg is {:?}, not biking", x), }; - (trip.id, vehicle, bike_to) + (trip.id, vehicle.clone(), bike_to.clone()) + } + + pub fn bike_reached_end(&mut self, bike: CarID) -> (TripID, PedestrianID, SidewalkSpot) { + let trip = &mut self.trips[self.active_trip_mode.remove(&AgentID::Car(bike)).unwrap().0]; + + match trip.legs.pop_front().unwrap() { + TripLeg::Bike { .. } => {} + x => panic!("First trip leg {:?} doesn't match bike_reached_end", x), + }; + let walk_to = match trip.legs[0] { + TripLeg::Walk(ref to) => to, + ref x => panic!("Next trip leg is {:?}, not walking", x), + }; + (trip.id, trip.ped, walk_to.clone()) } pub fn ped_reached_building_or_border(&mut self, ped: PedestrianID, now: Tick) { diff --git a/sim/src/walking.rs b/sim/src/walking.rs index 956286b622..6e01daf410 100644 --- a/sim/src/walking.rs +++ b/sim/src/walking.rs @@ -67,13 +67,11 @@ impl SidewalkSpot { } } - // These happen to be lined up with buildings right now - pub fn bike_rack(bldg: BuildingID, map: &Map) -> SidewalkSpot { - let front_path = &map.get_b(bldg).front_path; + pub fn bike_rack(sidewalk: LaneID, dist_along: Distance) -> SidewalkSpot { SidewalkSpot { connection: SidewalkPOI::BikeRack, - sidewalk: front_path.sidewalk, - dist_along: front_path.dist_along_sidewalk, + sidewalk, + dist_along, } } @@ -448,14 +446,23 @@ impl WalkingSimState { }); } Action::KeepPreparingBike => { - let p = self.peds.get_mut(&id).unwrap(); - if (now - p.bike_parking.unwrap().started_at).as_time() >= TIME_TO_PREPARE_BIKE - { - if p.bike_parking.unwrap().is_parking { + let state = self + .peds + .get(&id) + .unwrap() + .bike_parking + .as_ref() + .unwrap() + .clone(); + if (now - state.started_at).as_time() >= TIME_TO_PREPARE_BIKE { + if state.is_parking { // Now they'll start walking somewhere - p.bike_parking = None; + self.peds.get_mut(&id).unwrap().bike_parking = None; } else { - ready_to_bike.push((*id, p.on.as_lane(), p.dist_along)); + { + let p = self.peds.get(&id).unwrap(); + ready_to_bike.push((*id, p.on.as_lane(), p.dist_along)); + } self.peds.remove(&id); } }