mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +03:00
Fix blackholed buildings with --infinite_parking. Before, they were just
skipped outright. Now they redirect to a reasonably close building that isn't blackholed. #329 (For context, https://dabreegster.github.io/abstreet/map/index.html#connectivity explains "blackhole") I'm increasingly convinced I made the wrong decision to split the normal/infinite parking implementation. It's low-priority, but I'll try consolidating them later.
This commit is contained in:
parent
ec114e51e1
commit
fd460a2f43
@ -308,13 +308,15 @@ impl Static {
|
||||
vec![
|
||||
("driving blackhole", Color::RED),
|
||||
("biking blackhole", Color::GREEN),
|
||||
("driving + biking blackhole", Color::BLUE),
|
||||
],
|
||||
);
|
||||
for l in app.primary.map.all_lanes() {
|
||||
if l.driving_blackhole {
|
||||
if l.driving_blackhole && l.biking_blackhole {
|
||||
colorer.add_l(l.id, "driving + biking blackhole");
|
||||
} else if l.driving_blackhole {
|
||||
colorer.add_l(l.id, "driving blackhole");
|
||||
}
|
||||
if l.biking_blackhole {
|
||||
} else if l.biking_blackhole {
|
||||
colorer.add_l(l.id, "biking blackhole");
|
||||
}
|
||||
}
|
||||
|
@ -656,6 +656,9 @@ impl ParkingLane {
|
||||
// disables the simulation of parking entirely, making driving trips just go directly between
|
||||
// buildings. Useful for maps without good parking data (which is currently all of them) and
|
||||
// experiments where parking contention skews results and just gets in the way.
|
||||
//
|
||||
// TODO Reconsider this split implementation. There's lots of copied code. We can maybe just use
|
||||
// NormalParkingSimState with an 'infinite: bool' and rethinking num_spots_per_offstreet.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct InfiniteParkingSimState {
|
||||
#[serde(
|
||||
@ -676,7 +679,9 @@ pub struct InfiniteParkingSimState {
|
||||
deserialize_with = "deserialize_multimap"
|
||||
)]
|
||||
driving_to_offstreet: MultiMap<LaneID, (BuildingID, Distance)>,
|
||||
blackholed_buildings: BTreeSet<BuildingID>,
|
||||
// For an entry b1 -> b2, b1 is blackholed, so instead go park at b2 and walk the rest of the
|
||||
// way.
|
||||
blackholed_building_redirects: BTreeMap<BuildingID, BuildingID>,
|
||||
|
||||
events: Vec<Event>,
|
||||
}
|
||||
@ -689,10 +694,11 @@ impl InfiniteParkingSimState {
|
||||
reserved_spots: BTreeSet::new(),
|
||||
|
||||
driving_to_offstreet: MultiMap::new(),
|
||||
blackholed_buildings: BTreeSet::new(),
|
||||
blackholed_building_redirects: BTreeMap::new(),
|
||||
|
||||
events: Vec::new(),
|
||||
};
|
||||
let mut blackholes = Vec::new();
|
||||
for b in map.all_buildings() {
|
||||
if let Some((pos, _)) = b.driving_connection(map) {
|
||||
if !map.get_l(pos.lane()).driving_blackhole {
|
||||
@ -701,13 +707,40 @@ impl InfiniteParkingSimState {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sim.blackholed_buildings.insert(b.id);
|
||||
blackholes.push(b.id);
|
||||
}
|
||||
|
||||
// For every blackholed building, find a nearby building that isn't blackholed
|
||||
for b in blackholes {
|
||||
// TODO This is a simple DFS. Could sort by distance.
|
||||
let mut queue = vec![map.find_driving_lane_near_building(b)];
|
||||
let mut seen = BTreeSet::new();
|
||||
loop {
|
||||
let current = queue.pop().unwrap();
|
||||
if seen.contains(¤t) {
|
||||
continue;
|
||||
}
|
||||
seen.insert(current);
|
||||
if let Some((redirect, _)) = sim.driving_to_offstreet.get(current).iter().next() {
|
||||
sim.blackholed_building_redirects.insert(b, *redirect);
|
||||
break;
|
||||
}
|
||||
for turn in map.get_turns_for(current, PathConstraints::Car) {
|
||||
queue.push(turn.id.dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sim
|
||||
}
|
||||
|
||||
fn get_free_bldg_spot(&self, b: BuildingID) -> ParkingSpot {
|
||||
assert!(!self.blackholed_buildings.contains(&b));
|
||||
if let Some(redirect) = self.blackholed_building_redirects.get(&b) {
|
||||
// This won't recurse endlessly; the redirect is not a key in
|
||||
// blackholed_building_redirects.
|
||||
return self.get_free_bldg_spot(*redirect);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
let spot = ParkingSpot::Offstreet(b, i);
|
||||
@ -724,7 +757,7 @@ impl ParkingSim for InfiniteParkingSimState {
|
||||
// Can live edits possibly affect anything?
|
||||
let new = InfiniteParkingSimState::new(map);
|
||||
self.driving_to_offstreet = new.driving_to_offstreet;
|
||||
self.blackholed_buildings = new.blackholed_buildings;
|
||||
self.blackholed_building_redirects = new.blackholed_building_redirects;
|
||||
|
||||
Vec::new()
|
||||
}
|
||||
@ -734,12 +767,8 @@ impl ParkingSim for InfiniteParkingSimState {
|
||||
}
|
||||
|
||||
fn get_free_offstreet_spots(&self, b: BuildingID) -> Vec<ParkingSpot> {
|
||||
if self.blackholed_buildings.contains(&b) {
|
||||
Vec::new()
|
||||
} else {
|
||||
// Just returns the next free spot
|
||||
vec![self.get_free_bldg_spot(b)]
|
||||
}
|
||||
// Just returns the next free spot
|
||||
vec![self.get_free_bldg_spot(b)]
|
||||
}
|
||||
|
||||
fn get_free_lot_spots(&self, _: ParkingLotID) -> Vec<ParkingSpot> {
|
||||
@ -865,14 +894,58 @@ impl ParkingSim for InfiniteParkingSimState {
|
||||
|
||||
fn path_to_free_parking_spot(
|
||||
&self,
|
||||
_: LaneID,
|
||||
_: &Vehicle,
|
||||
_: BuildingID,
|
||||
_: &Map,
|
||||
start: LaneID,
|
||||
vehicle: &Vehicle,
|
||||
target: BuildingID,
|
||||
map: &Map,
|
||||
) -> Option<(Vec<PathStep>, ParkingSpot, Position)> {
|
||||
// The original building we're aiming for will always have room, unless it's located on a
|
||||
// blackholed lane. In that case, there's usually a nearby building on the last connected
|
||||
// lane. If not, then just give up for now.
|
||||
// TODO This impl is copied from NormalParkingSimState. Instead, we already know the
|
||||
// redirect... could just path to it.
|
||||
let mut backrefs: HashMap<LaneID, TurnID> = HashMap::new();
|
||||
// Don't travel far.
|
||||
// This is a max-heap, so negate all distances. Tie breaker is lane ID, arbitrary but
|
||||
// deterministic.
|
||||
let mut queue: BinaryHeap<(Distance, LaneID)> = BinaryHeap::new();
|
||||
queue.push((Distance::ZERO, start));
|
||||
|
||||
while !queue.is_empty() {
|
||||
let (dist_so_far, current) = queue.pop().unwrap();
|
||||
// If the current lane has a spot open, we wouldn't be asking. This can happen if a spot
|
||||
// opens up on the 'start' lane, but behind the car.
|
||||
if current != start {
|
||||
// Pick the closest to the start of the lane, since that's closest to where we came
|
||||
// from
|
||||
if let Some((spot, pos)) = self
|
||||
.get_all_free_spots(Position::start(current), vehicle, target, map)
|
||||
.into_iter()
|
||||
.min_by_key(|(_, pos)| pos.dist_along())
|
||||
{
|
||||
let mut steps = vec![PathStep::Lane(current)];
|
||||
let mut current = current;
|
||||
loop {
|
||||
if current == start {
|
||||
// Don't include PathStep::Lane(start)
|
||||
steps.pop();
|
||||
steps.reverse();
|
||||
return Some((steps, spot, pos));
|
||||
}
|
||||
let turn = backrefs[¤t];
|
||||
steps.push(PathStep::Turn(turn));
|
||||
steps.push(PathStep::Lane(turn.src));
|
||||
current = turn.src;
|
||||
}
|
||||
}
|
||||
}
|
||||
for turn in map.get_turns_for(current, PathConstraints::Car) {
|
||||
if !backrefs.contains_key(&turn.id.dst) {
|
||||
let dist_this_step = turn.geom.length() + map.get_l(current).length();
|
||||
backrefs.insert(turn.id.dst, turn.id);
|
||||
// Remember, keep things negative
|
||||
queue.push((dist_so_far - dist_this_step, turn.id.dst));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user