mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
properly merging nearby pedestrians by intervals of dist_along
This commit is contained in:
parent
d0d7bb79e3
commit
9d58ee12a4
@ -1,9 +1,9 @@
|
||||
use crate::helpers::{ColorScheme, ID};
|
||||
use crate::render::{DrawCtx, DrawOptions, Renderable};
|
||||
use crate::render::{DrawCtx, DrawOptions, Renderable, OUTLINE_THICKNESS};
|
||||
use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Prerender, Text};
|
||||
use geom::{Circle, Distance, PolyLine, Polygon, Pt2D};
|
||||
use map_model::{Map, Traversable, LANE_THICKNESS};
|
||||
use sim::{DrawPedestrianInput, PedestrianID};
|
||||
use geom::{Circle, Distance, PolyLine, Polygon};
|
||||
use map_model::{Map, LANE_THICKNESS};
|
||||
use sim::{DrawPedCrowdInput, DrawPedestrianInput, PedestrianID};
|
||||
|
||||
pub struct DrawPedestrian {
|
||||
pub id: PedestrianID,
|
||||
@ -110,61 +110,6 @@ impl DrawPedestrian {
|
||||
draw_default: prerender.upload(draw_default),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aggregate_peds(
|
||||
mut peds: Vec<DrawPedestrianInput>,
|
||||
) -> (Vec<DrawPedestrianInput>, Vec<PedCrowd>) {
|
||||
if peds.is_empty() {
|
||||
return (Vec::new(), Vec::new());
|
||||
}
|
||||
|
||||
// This'd be much cheaper to do by distance along the traversable, but it's a bit
|
||||
// memory-expensive to plumb that out for this experiment. It also doesn't handle front
|
||||
// paths as well.
|
||||
// TODO But average position will wind up off the sidewalk. :P
|
||||
let radius = LANE_THICKNESS / 4.0;
|
||||
|
||||
let mut loners: Vec<PedestrianID> = Vec::new();
|
||||
let mut crowds: Vec<PedCrowd> = Vec::new();
|
||||
let mut remaining: Vec<(PedestrianID, Pt2D)> = peds.iter().map(|p| (p.id, p.pos)).collect();
|
||||
// We know everyone's on the same traversable.
|
||||
let on = peds[0].on;
|
||||
|
||||
// TODO Incorrect and inefficient clustering
|
||||
while !remaining.is_empty() {
|
||||
let mut keep: Vec<(PedestrianID, Pt2D)> = Vec::new();
|
||||
let mut combine: Vec<(PedestrianID, Pt2D)> = Vec::new();
|
||||
let (base_id, base_pos) = remaining.pop().unwrap();
|
||||
|
||||
for (id, pos) in &remaining {
|
||||
if base_pos.dist_to(*pos) <= 2.0 * radius {
|
||||
combine.push((*id, *pos));
|
||||
} else {
|
||||
keep.push((*id, *pos));
|
||||
}
|
||||
}
|
||||
if combine.is_empty() {
|
||||
loners.push(base_id);
|
||||
} else {
|
||||
let mut positions = vec![base_pos];
|
||||
let mut members = vec![base_id];
|
||||
peds.retain(|p| p.id != base_id);
|
||||
for (id, pos) in combine {
|
||||
positions.push(pos);
|
||||
members.push(id);
|
||||
remaining.retain(|(rem_id, _)| *rem_id != id);
|
||||
peds.retain(|p| p.id != id);
|
||||
}
|
||||
crowds.push(PedCrowd {
|
||||
members,
|
||||
avg_pos: Pt2D::center(&positions),
|
||||
on,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(peds, crowds)
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderable for DrawPedestrian {
|
||||
@ -190,15 +135,10 @@ impl Renderable for DrawPedestrian {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PedCrowd {
|
||||
avg_pos: Pt2D,
|
||||
members: Vec<PedestrianID>,
|
||||
on: Traversable,
|
||||
}
|
||||
|
||||
pub struct DrawPedCrowd {
|
||||
members: Vec<PedestrianID>,
|
||||
body_circle: Circle,
|
||||
blob: Polygon,
|
||||
blob_pl: PolyLine,
|
||||
zorder: isize,
|
||||
|
||||
draw_default: Drawable,
|
||||
@ -206,14 +146,26 @@ pub struct DrawPedCrowd {
|
||||
}
|
||||
|
||||
impl DrawPedCrowd {
|
||||
pub fn new(input: PedCrowd, map: &Map, prerender: &Prerender, _: &ColorScheme) -> DrawPedCrowd {
|
||||
let radius = LANE_THICKNESS / 4.0;
|
||||
let body_circle = Circle::new(input.avg_pos, radius);
|
||||
let mut batch = GeomBatch::new();
|
||||
batch.push(Color::GREEN, body_circle.to_polygon());
|
||||
pub fn new(
|
||||
input: DrawPedCrowdInput,
|
||||
map: &Map,
|
||||
prerender: &Prerender,
|
||||
cs: &ColorScheme,
|
||||
) -> DrawPedCrowd {
|
||||
// TODO front path
|
||||
let pl_slice = input.on.exact_slice(input.low, input.high, map);
|
||||
let pl_shifted = if input.contraflow {
|
||||
pl_slice.shift_left(LANE_THICKNESS / 4.0).unwrap()
|
||||
} else {
|
||||
pl_slice.shift_right(LANE_THICKNESS / 4.0).unwrap()
|
||||
};
|
||||
let blob = pl_shifted.make_polygons(LANE_THICKNESS / 2.0);
|
||||
let draw_default = prerender.upload_borrowed(vec![(cs.get("pedestrian"), &blob)]);
|
||||
|
||||
let mut label = Text::with_bg_color(None);
|
||||
label.add_styled_line(
|
||||
format!("{}", input.members.len()),
|
||||
// Ideally "pedestrian head", but it looks really faded...
|
||||
Some(Color::BLACK),
|
||||
None,
|
||||
Some(15),
|
||||
@ -221,9 +173,10 @@ impl DrawPedCrowd {
|
||||
|
||||
DrawPedCrowd {
|
||||
members: input.members,
|
||||
body_circle,
|
||||
blob_pl: pl_shifted,
|
||||
blob,
|
||||
zorder: input.on.get_zorder(map),
|
||||
draw_default: prerender.upload(batch),
|
||||
draw_default,
|
||||
label,
|
||||
}
|
||||
}
|
||||
@ -237,16 +190,17 @@ impl Renderable for DrawPedCrowd {
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, opts: &DrawOptions, _: &DrawCtx) {
|
||||
if let Some(color) = opts.color(self.get_id()) {
|
||||
g.draw_circle(color, &self.body_circle);
|
||||
g.draw_polygon(color, &self.blob);
|
||||
} else {
|
||||
g.redraw(&self.draw_default);
|
||||
}
|
||||
g.draw_text_at_mapspace(&self.label, self.body_circle.center);
|
||||
g.draw_text_at_mapspace(&self.label, self.blob.center());
|
||||
}
|
||||
|
||||
fn get_outline(&self, _: &Map) -> Polygon {
|
||||
// TODO thin ring
|
||||
self.body_circle.to_polygon()
|
||||
self.blob_pl
|
||||
.to_thick_boundary(LANE_THICKNESS / 2.0, OUTLINE_THICKNESS)
|
||||
.unwrap_or_else(|| self.blob.clone())
|
||||
}
|
||||
|
||||
fn get_zorder(&self) -> isize {
|
||||
|
@ -6,7 +6,9 @@ use abstutil::MultiMap;
|
||||
use ezgui::{hotkey, EventCtx, GfxCtx, ItemSlider, Key, Text};
|
||||
use geom::Duration;
|
||||
use map_model::{Map, Traversable};
|
||||
use sim::{CarID, DrawCarInput, DrawPedestrianInput, GetDrawAgents, PedestrianID};
|
||||
use sim::{
|
||||
CarID, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, GetDrawAgents, PedestrianID,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct InactiveTimeTravel {
|
||||
@ -151,14 +153,22 @@ impl GetDrawAgents for TimeTraveler {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_draw_peds(&self, on: Traversable, _map: &Map) -> Vec<DrawPedestrianInput> {
|
||||
// TODO This cheats and doesn't handle crowds. :\
|
||||
fn get_draw_peds(
|
||||
&self,
|
||||
on: Traversable,
|
||||
_map: &Map,
|
||||
) -> (Vec<DrawPedestrianInput>, Vec<DrawPedCrowdInput>) {
|
||||
let state = self.get_current_state();
|
||||
state
|
||||
.peds_per_traversable
|
||||
.get(on)
|
||||
.iter()
|
||||
.map(|id| state.peds[id].clone())
|
||||
.collect()
|
||||
(
|
||||
state
|
||||
.peds_per_traversable
|
||||
.get(on)
|
||||
.iter()
|
||||
.map(|id| state.peds[id].clone())
|
||||
.collect(),
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_all_draw_cars(&self, _map: &Map) -> Vec<DrawCarInput> {
|
||||
|
@ -346,8 +346,7 @@ impl UI {
|
||||
for c in source.get_draw_cars(*on, map).into_iter() {
|
||||
list.push(draw_vehicle(c, map, prerender, &self.cs));
|
||||
}
|
||||
let (loners, crowds) =
|
||||
DrawPedestrian::aggregate_peds(source.get_draw_peds(*on, map));
|
||||
let (loners, crowds) = source.get_draw_peds(*on, map);
|
||||
for p in loners {
|
||||
list.push(Box::new(DrawPedestrian::new(
|
||||
p, step_count, map, prerender, &self.cs,
|
||||
|
@ -192,6 +192,7 @@ impl GUI for UI {
|
||||
true
|
||||
} else if menu.action("reset hints") {
|
||||
self.hints.hints.clear();
|
||||
self.hints.parking_overrides.clear();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -23,7 +23,7 @@ pub(crate) use self::transit::TransitSimState;
|
||||
pub use self::trips::{FinishedTrips, TripEnd, TripMode, TripStart, TripStatus};
|
||||
pub(crate) use self::trips::{TripLeg, TripManager};
|
||||
pub use crate::render::{
|
||||
CarStatus, DrawCarInput, DrawPedestrianInput, GetDrawAgents, UnzoomedAgent,
|
||||
CarStatus, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, GetDrawAgents, UnzoomedAgent,
|
||||
};
|
||||
use abstutil::Cloneable;
|
||||
use geom::{Distance, Duration, Pt2D, Speed};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
AgentID, Command, CreatePedestrian, DistanceInterval, DrawPedestrianInput,
|
||||
AgentID, Command, CreatePedestrian, DistanceInterval, DrawPedCrowdInput, DrawPedestrianInput,
|
||||
IntersectionSimState, ParkingSimState, PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot,
|
||||
TimeInterval, TransitSimState, TripID, TripManager, TripPositions, UnzoomedAgent,
|
||||
};
|
||||
@ -86,19 +86,6 @@ impl WalkingSimState {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_draw_peds(
|
||||
&self,
|
||||
time: Duration,
|
||||
on: Traversable,
|
||||
map: &Map,
|
||||
) -> Vec<DrawPedestrianInput> {
|
||||
self.peds_per_traversable
|
||||
.get(on)
|
||||
.iter()
|
||||
.map(|id| self.peds[id].get_draw_ped(time, map))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn update_ped(
|
||||
&mut self,
|
||||
id: PedestrianID,
|
||||
@ -306,6 +293,94 @@ impl WalkingSimState {
|
||||
.insert(ped.trip, ped.get_draw_ped(trip_positions.time, map).pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_draw_peds_on(
|
||||
&self,
|
||||
time: Duration,
|
||||
on: Traversable,
|
||||
map: &Map,
|
||||
) -> (Vec<DrawPedestrianInput>, Vec<DrawPedCrowdInput>) {
|
||||
// Classify into direction-based groups or by building front path.
|
||||
let mut forwards: Vec<(PedestrianID, Distance)> = Vec::new();
|
||||
let mut backwards: Vec<(PedestrianID, Distance)> = Vec::new();
|
||||
let mut front_path: MultiMap<BuildingID, (PedestrianID, Distance)> = MultiMap::new();
|
||||
|
||||
let mut loners: Vec<DrawPedestrianInput> = Vec::new();
|
||||
|
||||
for id in self.peds_per_traversable.get(on) {
|
||||
let ped = &self.peds[id];
|
||||
let dist = ped.get_dist_along(time, map);
|
||||
|
||||
match ped.state {
|
||||
PedState::Crossing(ref dist_int, _) => {
|
||||
if dist_int.start < dist_int.end {
|
||||
forwards.push((*id, dist));
|
||||
} else {
|
||||
backwards.push((*id, dist));
|
||||
}
|
||||
}
|
||||
PedState::WaitingToTurn(dist) => {
|
||||
if dist == Distance::ZERO {
|
||||
backwards.push((*id, dist));
|
||||
} else {
|
||||
forwards.push((*id, dist));
|
||||
}
|
||||
}
|
||||
PedState::LeavingBuilding(b, _) | PedState::EnteringBuilding(b, _) => {
|
||||
// TODO Group on front paths too.
|
||||
if false {
|
||||
// TODO Distance along the front path
|
||||
front_path.insert(b, (*id, dist));
|
||||
} else {
|
||||
loners.push(ped.get_draw_ped(time, map));
|
||||
}
|
||||
}
|
||||
PedState::StartingToBike(_, _, _)
|
||||
| PedState::FinishingBiking(_, _, _)
|
||||
| PedState::WaitingForBus => {
|
||||
// The backwards half of the sidewalk is closer to the road.
|
||||
backwards.push((*id, dist));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut crowds: Vec<DrawPedCrowdInput> = Vec::new();
|
||||
let on_len = on.length(map);
|
||||
|
||||
// For each group, sort by distance along. Attempt to bundle into intervals.
|
||||
for (idx, mut group) in vec![forwards, backwards]
|
||||
.into_iter()
|
||||
.chain(
|
||||
front_path
|
||||
.consume()
|
||||
.values()
|
||||
.map(|set| set.into_iter().cloned().collect::<Vec<_>>()),
|
||||
)
|
||||
.enumerate()
|
||||
{
|
||||
if group.is_empty() {
|
||||
continue;
|
||||
}
|
||||
group.sort_by_key(|(_, dist)| *dist);
|
||||
let (individs, these_crowds) = find_crowds(group, on);
|
||||
for id in individs {
|
||||
loners.push(self.peds[&id].get_draw_ped(time, map));
|
||||
}
|
||||
for mut crowd in these_crowds {
|
||||
crowd.contraflow = idx == 1;
|
||||
// Clamp the distance intervals.
|
||||
if crowd.low < Distance::ZERO {
|
||||
crowd.low = Distance::ZERO;
|
||||
}
|
||||
if crowd.high > on_len {
|
||||
crowd.high = on_len;
|
||||
}
|
||||
crowds.push(crowd);
|
||||
}
|
||||
}
|
||||
|
||||
(loners, crowds)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq)]
|
||||
@ -498,3 +573,50 @@ impl PedState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The crowds returned here may have low/high values extending up to radius past the real geometry.
|
||||
fn find_crowds(
|
||||
input: Vec<(PedestrianID, Distance)>,
|
||||
on: Traversable,
|
||||
) -> (Vec<PedestrianID>, Vec<DrawPedCrowdInput>) {
|
||||
let mut loners = Vec::new();
|
||||
let mut crowds = Vec::new();
|
||||
let radius = LANE_THICKNESS / 4.0;
|
||||
|
||||
let mut current_crowd = DrawPedCrowdInput {
|
||||
low: input[0].1 - radius,
|
||||
high: input[0].1 + radius,
|
||||
contraflow: false,
|
||||
members: vec![input[0].0],
|
||||
on,
|
||||
};
|
||||
for (id, dist) in input.into_iter().skip(1) {
|
||||
// If the pedestrian circles would overlap at all,
|
||||
if dist - radius <= current_crowd.high {
|
||||
current_crowd.members.push(id);
|
||||
current_crowd.high = dist + radius;
|
||||
} else {
|
||||
if current_crowd.members.len() == 1 {
|
||||
loners.push(current_crowd.members[0]);
|
||||
} else {
|
||||
crowds.push(current_crowd);
|
||||
}
|
||||
// Reset current_crowd
|
||||
current_crowd = DrawPedCrowdInput {
|
||||
low: dist - radius,
|
||||
high: dist + radius,
|
||||
contraflow: false,
|
||||
members: vec![id],
|
||||
on,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Handle the last bit
|
||||
if current_crowd.members.len() == 1 {
|
||||
loners.push(current_crowd.members[0]);
|
||||
} else {
|
||||
crowds.push(current_crowd);
|
||||
}
|
||||
|
||||
(loners, crowds)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{CarID, PedestrianID};
|
||||
use geom::{Angle, Duration, PolyLine, Pt2D};
|
||||
use geom::{Angle, Distance, Duration, PolyLine, Pt2D};
|
||||
use map_model::{Map, Traversable, TurnID};
|
||||
|
||||
// Intermediate structures so that sim and editor crates don't have a cyclic dependency.
|
||||
@ -13,6 +13,14 @@ pub struct DrawPedestrianInput {
|
||||
pub on: Traversable,
|
||||
}
|
||||
|
||||
pub struct DrawPedCrowdInput {
|
||||
pub low: Distance,
|
||||
pub high: Distance,
|
||||
pub contraflow: bool,
|
||||
pub members: Vec<PedestrianID>,
|
||||
pub on: Traversable,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DrawCarInput {
|
||||
pub id: CarID,
|
||||
@ -49,7 +57,11 @@ pub trait GetDrawAgents {
|
||||
fn get_draw_car(&self, id: CarID, map: &Map) -> Option<DrawCarInput>;
|
||||
fn get_draw_ped(&self, id: PedestrianID, map: &Map) -> Option<DrawPedestrianInput>;
|
||||
fn get_draw_cars(&self, on: Traversable, map: &Map) -> Vec<DrawCarInput>;
|
||||
fn get_draw_peds(&self, on: Traversable, map: &Map) -> Vec<DrawPedestrianInput>;
|
||||
fn get_draw_peds(
|
||||
&self,
|
||||
on: Traversable,
|
||||
map: &Map,
|
||||
) -> (Vec<DrawPedestrianInput>, Vec<DrawPedCrowdInput>);
|
||||
fn get_all_draw_cars(&self, map: &Map) -> Vec<DrawCarInput>;
|
||||
fn get_all_draw_peds(&self, map: &Map) -> Vec<DrawPedestrianInput>;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
AgentID, CarID, Command, CreateCar, DrawCarInput, DrawPedestrianInput, DrivingGoal,
|
||||
DrivingSimState, Event, FinishedTrips, GetDrawAgents, IntersectionSimState, ParkedCar,
|
||||
ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler, TransitSimState, TripID,
|
||||
TripLeg, TripManager, TripPositions, TripSpawner, TripSpec, TripStatus, UnzoomedAgent,
|
||||
AgentID, CarID, Command, CreateCar, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput,
|
||||
DrivingGoal, DrivingSimState, Event, FinishedTrips, GetDrawAgents, IntersectionSimState,
|
||||
ParkedCar, ParkingSimState, ParkingSpot, PedestrianID, Router, Scheduler, TransitSimState,
|
||||
TripID, TripLeg, TripManager, TripPositions, TripSpawner, TripSpec, TripStatus, UnzoomedAgent,
|
||||
VehicleSpec, VehicleType, WalkingSimState, BUS_LENGTH,
|
||||
};
|
||||
use abstutil::{elapsed_seconds, Timer};
|
||||
@ -287,8 +287,12 @@ impl GetDrawAgents for Sim {
|
||||
.get_draw_cars_on(self.time, on, map, &self.transit)
|
||||
}
|
||||
|
||||
fn get_draw_peds(&self, on: Traversable, map: &Map) -> Vec<DrawPedestrianInput> {
|
||||
self.walking.get_draw_peds(self.time, on, map)
|
||||
fn get_draw_peds(
|
||||
&self,
|
||||
on: Traversable,
|
||||
map: &Map,
|
||||
) -> (Vec<DrawPedestrianInput>, Vec<DrawPedCrowdInput>) {
|
||||
self.walking.get_draw_peds_on(self.time, on, map)
|
||||
}
|
||||
|
||||
fn get_all_draw_cars(&self, map: &Map) -> Vec<DrawCarInput> {
|
||||
|
Loading…
Reference in New Issue
Block a user