mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 17:37:22 +03:00
starting to model pedestrians. spawn and draw them.
This commit is contained in:
parent
6f00507740
commit
e5de791fa7
@ -28,6 +28,6 @@
|
||||
|
||||
## pedestrians
|
||||
|
||||
- pedestrians with different speeds, moving bidirectionally on everything
|
||||
- pathfinding and stepping
|
||||
- make them start and end at buildings
|
||||
- trim the sidewalk path to the edge of a building
|
||||
|
@ -208,6 +208,15 @@ but the fact that sidewalks are oriented is actually convenient, it makes it cle
|
||||
what if we just add a bit and make turns bidirectional? still express them in the directional way?
|
||||
if we're looking at turns from a road that's a sidewalk, bake in some extra logic?
|
||||
|
||||
## Pedestrian modeling
|
||||
|
||||
- Is it useful to distinguish CarID and PedestrianID? What about when an agent has a multi-modal trip? Probably become AgentID later.
|
||||
|
||||
- Worth mentioning that I'm assuming pedestrians don't queue or collide. In
|
||||
most reasonable sidewalk cases, this is true. Don't need to model more
|
||||
detailed movement. As a consequence of this, crosswalk turns never conflict.
|
||||
Assume people can weave.
|
||||
|
||||
## Stop signs
|
||||
|
||||
How to depict stop signs? Each driving lane has a priority... asap go or full
|
||||
|
@ -240,6 +240,12 @@
|
||||
0.29857337,
|
||||
1.0
|
||||
],
|
||||
"Pedestrian": [
|
||||
0.2,
|
||||
0.2,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"TrafficSignalBox": [
|
||||
0.0,
|
||||
0.0,
|
||||
|
@ -57,6 +57,8 @@ pub enum Colors {
|
||||
StuckCar,
|
||||
ParkedCar,
|
||||
|
||||
Pedestrian,
|
||||
|
||||
TrafficSignalBox,
|
||||
TrafficSignalGreen,
|
||||
TrafficSignalYellow,
|
||||
|
@ -10,7 +10,7 @@ use map_model;
|
||||
use map_model::{BuildingID, IntersectionID, Map, RoadID, TurnID};
|
||||
use piston::input::{Button, Key, ReleaseEvent};
|
||||
use render;
|
||||
use sim::{CarID, Sim};
|
||||
use sim::{CarID, PedestrianID, Sim};
|
||||
use std::collections::HashSet;
|
||||
|
||||
// TODO only used for mouseover, which happens in order anyway...
|
||||
@ -21,6 +21,7 @@ pub enum ID {
|
||||
Turn(TurnID),
|
||||
Building(BuildingID),
|
||||
Car(CarID),
|
||||
Pedestrian(PedestrianID),
|
||||
//Parcel(ParcelID),
|
||||
}
|
||||
|
||||
@ -34,6 +35,7 @@ pub enum SelectionState {
|
||||
SelectedBuilding(BuildingID),
|
||||
SelectedTurn(TurnID),
|
||||
SelectedCar(CarID),
|
||||
SelectedPedestrian(PedestrianID),
|
||||
}
|
||||
|
||||
impl SelectionState {
|
||||
@ -51,6 +53,7 @@ impl SelectionState {
|
||||
Some(ID::Building(id)) => SelectionState::SelectedBuilding(id),
|
||||
Some(ID::Turn(id)) => SelectionState::SelectedTurn(id),
|
||||
Some(ID::Car(id)) => SelectionState::SelectedCar(id),
|
||||
Some(ID::Pedestrian(id)) => SelectionState::SelectedPedestrian(id),
|
||||
None => SelectionState::Empty,
|
||||
}
|
||||
}
|
||||
@ -154,6 +157,9 @@ impl SelectionState {
|
||||
SelectionState::SelectedCar(id) => {
|
||||
canvas.draw_mouse_tooltip(g, &sim.car_tooltip(id));
|
||||
}
|
||||
SelectionState::SelectedPedestrian(id) => {
|
||||
canvas.draw_mouse_tooltip(g, &sim.ped_tooltip(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ use plugins::traffic_signal_editor::TrafficSignalEditor;
|
||||
use plugins::turn_colors::TurnColors;
|
||||
use plugins::warp::WarpState;
|
||||
use render;
|
||||
use sim::{CarID, CarState};
|
||||
use sim::{CarID, CarState, PedestrianID};
|
||||
use std::collections::HashMap;
|
||||
use std::process;
|
||||
|
||||
@ -171,6 +171,11 @@ impl UI {
|
||||
return Some(ID::Car(c.id));
|
||||
}
|
||||
}
|
||||
for p in &self.sim_ctrl.sim.get_draw_peds_on_road(r.id, &self.map) {
|
||||
if p.contains_pt(x, y) {
|
||||
return Some(ID::Pedestrian(p.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_icons.is_enabled() {
|
||||
@ -191,6 +196,11 @@ impl UI {
|
||||
return Some(ID::Car(c.id));
|
||||
}
|
||||
}
|
||||
for p in &self.sim_ctrl.sim.get_draw_peds_on_turn(*t, &self.map) {
|
||||
if p.contains_pt(x, y) {
|
||||
return Some(ID::Pedestrian(p.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i.contains_pt(x, y) {
|
||||
@ -206,6 +216,11 @@ impl UI {
|
||||
return Some(ID::Car(c.id));
|
||||
}
|
||||
}
|
||||
for p in &self.sim_ctrl.sim.get_draw_peds_on_road(r.id, &self.map) {
|
||||
if p.contains_pt(x, y) {
|
||||
return Some(ID::Pedestrian(p.id));
|
||||
}
|
||||
}
|
||||
|
||||
if r.road_contains_pt(x, y) {
|
||||
return Some(ID::Road(r.id));
|
||||
@ -322,6 +337,10 @@ impl UI {
|
||||
CarState::Parked => self.cs.get(Colors::ParkedCar),
|
||||
}
|
||||
}
|
||||
|
||||
fn color_ped(&self, _id: PedestrianID) -> Color {
|
||||
self.cs.get(Colors::Pedestrian)
|
||||
}
|
||||
}
|
||||
|
||||
impl gui::GUI for UI {
|
||||
@ -416,8 +435,12 @@ impl gui::GUI for UI {
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
if self.sim_ctrl.sim.total_cars() == 0 {
|
||||
if input.unimportant_key_pressed(Key::S, "Seed the map with 50% parked cars") {
|
||||
if input.unimportant_key_pressed(
|
||||
Key::S,
|
||||
"Seed the map with 50% parked cars and some pedestrians",
|
||||
) {
|
||||
self.sim_ctrl.sim.seed_parked_cars(0.5);
|
||||
self.sim_ctrl.sim.seed_pedestrians(&self.map, 1000);
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
} else {
|
||||
@ -442,11 +465,9 @@ impl gui::GUI for UI {
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
if self.map.get_r(id).lane_type == map_model::LaneType::Driving {
|
||||
if input.key_pressed(Key::A, "Press A to start a parked car on this road") {
|
||||
self.sim_ctrl.sim.start_parked_car(&self.map, id);
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.key_pressed(Key::A, "Press A to start something on this road") {
|
||||
self.sim_ctrl.sim.start_agent(&self.map, id);
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
SelectionState::SelectedIntersection(id) => {
|
||||
@ -538,6 +559,9 @@ impl gui::GUI for UI {
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_turn(t.id, &self.map) {
|
||||
c.draw(g, self.color_car(c.id));
|
||||
}
|
||||
for p in &self.sim_ctrl.sim.get_draw_peds_on_turn(t.id, &self.map) {
|
||||
p.draw(g, self.color_ped(p.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,6 +569,9 @@ impl gui::GUI for UI {
|
||||
for c in &self.sim_ctrl.sim.get_draw_cars_on_road(r.id, &self.map) {
|
||||
c.draw(g, self.color_car(c.id));
|
||||
}
|
||||
for p in &self.sim_ctrl.sim.get_draw_peds_on_road(r.id, &self.map) {
|
||||
p.draw(g, self.color_ped(p.id));
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_buildings.is_enabled() {
|
||||
|
@ -254,3 +254,5 @@ fn shift_short_polyline_equivalence() {
|
||||
Some(PolyLine::new(vec![l.pt1(), l.pt2()]))
|
||||
);
|
||||
}
|
||||
|
||||
// TODO test that shifting lines and polylines is a reversible operation
|
||||
|
31
sim/src/draw_ped.rs
Normal file
31
sim/src/draw_ped.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use ezgui::GfxCtx;
|
||||
use geom::Pt2D;
|
||||
use graphics;
|
||||
use map_model::geometry;
|
||||
use PedestrianID;
|
||||
|
||||
const RADIUS: f64 = 1.0;
|
||||
|
||||
// TODO should this live in editor/render?
|
||||
// TODO show turns waited for
|
||||
pub struct DrawPedestrian {
|
||||
pub id: PedestrianID,
|
||||
circle: [f64; 4],
|
||||
}
|
||||
|
||||
impl DrawPedestrian {
|
||||
pub(crate) fn new(id: PedestrianID, pos: Pt2D) -> DrawPedestrian {
|
||||
DrawPedestrian {
|
||||
id,
|
||||
circle: geometry::circle(pos.x(), pos.y(), RADIUS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, color: graphics::types::Color) {
|
||||
g.draw_ellipse(color, self.circle);
|
||||
}
|
||||
|
||||
pub fn contains_pt(&self, x: f64, y: f64) -> bool {
|
||||
geometry::point_in_circle(x, y, [self.circle[0], self.circle[1]], RADIUS)
|
||||
}
|
||||
}
|
@ -12,57 +12,13 @@ use rand::Rng;
|
||||
use std;
|
||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||
use std::f64;
|
||||
use {CarID, Tick, SPEED_LIMIT};
|
||||
use {CarID, On, Tick, SPEED_LIMIT};
|
||||
|
||||
const FOLLOWING_DISTANCE: si::Meter<f64> = si::Meter {
|
||||
value_unsafe: 8.0,
|
||||
_marker: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
// TODO this name isn't quite right :)
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) enum On {
|
||||
Road(RoadID),
|
||||
Turn(TurnID),
|
||||
}
|
||||
|
||||
impl On {
|
||||
pub(crate) fn as_road(&self) -> RoadID {
|
||||
match self {
|
||||
&On::Road(id) => id,
|
||||
&On::Turn(_) => panic!("not a road"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_turn(&self) -> TurnID {
|
||||
match self {
|
||||
&On::Turn(id) => id,
|
||||
&On::Road(_) => panic!("not a turn"),
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_turn(&self) -> Option<TurnID> {
|
||||
match self {
|
||||
&On::Turn(id) => Some(id),
|
||||
&On::Road(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn length(&self, map: &Map) -> si::Meter<f64> {
|
||||
match self {
|
||||
&On::Road(id) => map.get_r(id).length(),
|
||||
&On::Turn(id) => map.get_t(id).length(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dist_along(&self, dist: si::Meter<f64>, map: &Map) -> (Pt2D, Angle) {
|
||||
match self {
|
||||
&On::Road(id) => map.get_r(id).dist_along(dist),
|
||||
&On::Turn(id) => map.get_t(id).dist_along(dist),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This represents an actively driving car, not a parked one
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub(crate) struct Car {
|
||||
|
@ -15,12 +15,16 @@ extern crate serde;
|
||||
extern crate serde_derive;
|
||||
|
||||
mod draw_car;
|
||||
mod draw_ped;
|
||||
mod driving;
|
||||
mod intersections;
|
||||
mod parking;
|
||||
mod sim;
|
||||
mod walking;
|
||||
|
||||
use dimensioned::si;
|
||||
use geom::{Angle, Pt2D};
|
||||
use map_model::{Map, RoadID, TurnID};
|
||||
pub use sim::{Benchmark, CarState, Sim};
|
||||
use std::fmt;
|
||||
|
||||
@ -33,6 +37,15 @@ impl fmt::Display for CarID {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct PedestrianID(pub usize);
|
||||
|
||||
impl fmt::Display for PedestrianID {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PedestrianID({0})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub const TIMESTEP: si::Second<f64> = si::Second {
|
||||
value_unsafe: 0.1,
|
||||
_marker: std::marker::PhantomData,
|
||||
@ -73,3 +86,47 @@ impl std::fmt::Display for Tick {
|
||||
write!(f, "{0:.1}s", (self.0 as f64) * TIMESTEP.value_unsafe)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this name isn't quite right :)
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) enum On {
|
||||
Road(RoadID),
|
||||
Turn(TurnID),
|
||||
}
|
||||
|
||||
impl On {
|
||||
pub(crate) fn as_road(&self) -> RoadID {
|
||||
match self {
|
||||
&On::Road(id) => id,
|
||||
&On::Turn(_) => panic!("not a road"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_turn(&self) -> TurnID {
|
||||
match self {
|
||||
&On::Turn(id) => id,
|
||||
&On::Road(_) => panic!("not a turn"),
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_turn(&self) -> Option<TurnID> {
|
||||
match self {
|
||||
&On::Turn(id) => Some(id),
|
||||
&On::Road(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn length(&self, map: &Map) -> si::Meter<f64> {
|
||||
match self {
|
||||
&On::Road(id) => map.get_r(id).length(),
|
||||
&On::Turn(id) => map.get_t(id).length(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dist_along(&self, dist: si::Meter<f64>, map: &Map) -> (Pt2D, Angle) {
|
||||
match self {
|
||||
&On::Road(id) => map.get_r(id).dist_along(dist),
|
||||
&On::Turn(id) => map.get_t(id).dist_along(dist),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,15 @@
|
||||
use control::ControlMap;
|
||||
use dimensioned::si;
|
||||
use draw_car::DrawCar;
|
||||
use draw_ped::DrawPedestrian;
|
||||
use driving::DrivingSimState;
|
||||
use map_model::{LaneType, Map, RoadID, TurnID};
|
||||
use parking::ParkingSimState;
|
||||
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
||||
use std::f64;
|
||||
use std::time::{Duration, Instant};
|
||||
use {CarID, Tick};
|
||||
use walking::WalkingSimState;
|
||||
use {CarID, PedestrianID, Tick};
|
||||
|
||||
pub enum CarState {
|
||||
Moving,
|
||||
@ -25,11 +27,12 @@ pub struct Sim {
|
||||
#[derivative(PartialEq = "ignore")]
|
||||
rng: XorShiftRng,
|
||||
pub time: Tick,
|
||||
id_counter: usize,
|
||||
car_id_counter: usize,
|
||||
debug: Option<CarID>,
|
||||
|
||||
driving_state: DrivingSimState,
|
||||
parking_state: ParkingSimState,
|
||||
walking_state: WalkingSimState,
|
||||
}
|
||||
|
||||
impl Sim {
|
||||
@ -43,19 +46,20 @@ impl Sim {
|
||||
rng,
|
||||
driving_state: DrivingSimState::new(map),
|
||||
parking_state: ParkingSimState::new(map),
|
||||
walking_state: WalkingSimState::new(),
|
||||
time: Tick::zero(),
|
||||
id_counter: 0,
|
||||
car_id_counter: 0,
|
||||
debug: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_cars(&self) -> usize {
|
||||
self.id_counter
|
||||
self.car_id_counter
|
||||
}
|
||||
|
||||
pub fn seed_parked_cars(&mut self, percent: f64) {
|
||||
self.parking_state
|
||||
.seed_random_cars(&mut self.rng, percent, &mut self.id_counter)
|
||||
.seed_random_cars(&mut self.rng, percent, &mut self.car_id_counter)
|
||||
}
|
||||
|
||||
pub fn start_many_parked_cars(&mut self, map: &Map, num_cars: usize) {
|
||||
@ -77,18 +81,19 @@ impl Sim {
|
||||
let n = num_cars.min(driving_lanes.len());
|
||||
let mut actual = 0;
|
||||
for i in 0..n {
|
||||
if self.start_parked_car(map, driving_lanes[i]) {
|
||||
if self.start_agent(map, driving_lanes[i]) {
|
||||
actual += 1;
|
||||
}
|
||||
}
|
||||
println!("Started {} parked cars of requested {}", actual, n);
|
||||
}
|
||||
|
||||
pub fn start_parked_car(&mut self, map: &Map, id: RoadID) -> bool {
|
||||
pub fn start_agent(&mut self, map: &Map, id: RoadID) -> bool {
|
||||
let (driving_lane, parking_lane) = match map.get_r(id).lane_type {
|
||||
LaneType::Sidewalk => {
|
||||
println!("{} is a sidewalk, can't start a parked car here", id);
|
||||
return false;
|
||||
self.walking_state.seed_pedestrian(id);
|
||||
println!("Spawned a pedestrian at {}", id);
|
||||
return true;
|
||||
}
|
||||
LaneType::Driving => {
|
||||
if let Some(parking) = map.find_parking_lane(id) {
|
||||
@ -125,11 +130,17 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seed_pedestrians(&mut self, map: &Map, num: usize) {
|
||||
self.walking_state.seed_pedestrians(&mut self.rng, map, num);
|
||||
println!("Spawned {} pedestrians", num);
|
||||
}
|
||||
|
||||
pub fn step(&mut self, map: &Map, control_map: &ControlMap) {
|
||||
self.time.increment();
|
||||
|
||||
// TODO Vanish action should become Park
|
||||
self.driving_state.step(self.time, map, control_map);
|
||||
self.walking_state.step(self.time, map, control_map);
|
||||
}
|
||||
|
||||
pub fn get_car_state(&self, c: CarID) -> CarState {
|
||||
@ -144,6 +155,7 @@ impl Sim {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO maybe just DrawAgent instead? should caller care?
|
||||
pub fn get_draw_cars_on_road(&self, r: RoadID, map: &Map) -> Vec<DrawCar> {
|
||||
match map.get_r(r).lane_type {
|
||||
LaneType::Driving => {
|
||||
@ -158,8 +170,16 @@ impl Sim {
|
||||
self.driving_state.turns[t.0].get_draw_cars(self.time, &self.driving_state, map)
|
||||
}
|
||||
|
||||
pub fn get_draw_peds_on_road(&self, r: RoadID, map: &Map) -> Vec<DrawPedestrian> {
|
||||
self.walking_state.get_draw_peds_on_road(map.get_r(r))
|
||||
}
|
||||
|
||||
pub fn get_draw_peds_on_turn(&self, t: TurnID, map: &Map) -> Vec<DrawPedestrian> {
|
||||
self.walking_state.get_draw_peds_on_turn(map.get_t(t))
|
||||
}
|
||||
|
||||
pub fn summary(&self) -> String {
|
||||
// TODO also report parking state
|
||||
// TODO also report parking state and walking state
|
||||
let waiting = self.driving_state
|
||||
.cars
|
||||
.values()
|
||||
@ -173,6 +193,10 @@ impl Sim {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ped_tooltip(&self, p: PedestrianID) -> Vec<String> {
|
||||
vec![format!("Hello to {}", p)]
|
||||
}
|
||||
|
||||
pub fn car_tooltip(&self, car: CarID) -> Vec<String> {
|
||||
if let Some(driving) = self.driving_state.cars.get(&car) {
|
||||
driving.tooltip_lines()
|
||||
|
116
sim/src/walking.rs
Normal file
116
sim/src/walking.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use control::ControlMap;
|
||||
use dimensioned::si;
|
||||
use draw_ped::DrawPedestrian;
|
||||
use map_model::{LaneType, Map, Road, RoadID, Turn, TurnID};
|
||||
use multimap::MultiMap;
|
||||
use rand::Rng;
|
||||
use std;
|
||||
use std::collections::VecDeque;
|
||||
use {On, PedestrianID, Tick};
|
||||
|
||||
// TODO tune these!
|
||||
// TODO make it vary, after we can easily serialize these
|
||||
const SPEED: si::MeterPerSecond<f64> = si::MeterPerSecond {
|
||||
value_unsafe: 0.9,
|
||||
_marker: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Pedestrian {
|
||||
id: PedestrianID,
|
||||
|
||||
on: On,
|
||||
// TODO si::Meter<f64> after serde support lands
|
||||
// TODO or since Tick is deliberately not f64, have a better type for Meters.
|
||||
dist_along: f64,
|
||||
// Traveling along the road/turn in its original direction or not?
|
||||
contraflow: bool,
|
||||
|
||||
// Head is the next road
|
||||
path: VecDeque<RoadID>,
|
||||
}
|
||||
|
||||
// TODO this is used for verifying sim state determinism, so it should actually check everything.
|
||||
// the f64 prevents this from being derived.
|
||||
impl PartialEq for Pedestrian {
|
||||
fn eq(&self, other: &Pedestrian) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
impl Eq for Pedestrian {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Derivative, PartialEq, Eq)]
|
||||
pub(crate) struct WalkingSimState {
|
||||
// Trying a different style than driving for storing things
|
||||
peds_per_sidewalk: MultiMap<RoadID, Pedestrian>,
|
||||
peds_per_turn: MultiMap<TurnID, Pedestrian>,
|
||||
|
||||
id_counter: usize,
|
||||
}
|
||||
|
||||
impl WalkingSimState {
|
||||
pub fn new() -> WalkingSimState {
|
||||
WalkingSimState {
|
||||
peds_per_sidewalk: MultiMap::new(),
|
||||
peds_per_turn: MultiMap::new(),
|
||||
id_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, time: Tick, map: &Map, control_map: &ControlMap) {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
pub fn get_draw_peds_on_road(&self, r: &Road) -> Vec<DrawPedestrian> {
|
||||
let mut result = Vec::new();
|
||||
for p in self.peds_per_sidewalk.get_vec(&r.id).unwrap_or(&Vec::new()) {
|
||||
result.push(DrawPedestrian::new(
|
||||
p.id,
|
||||
r.dist_along(p.dist_along * si::M).0,
|
||||
));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn get_draw_peds_on_turn(&self, t: &Turn) -> Vec<DrawPedestrian> {
|
||||
let mut result = Vec::new();
|
||||
for p in self.peds_per_turn.get_vec(&t.id).unwrap_or(&Vec::new()) {
|
||||
result.push(DrawPedestrian::new(
|
||||
p.id,
|
||||
t.dist_along(p.dist_along * si::M).0,
|
||||
));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn seed_pedestrians<R: Rng + ?Sized>(&mut self, rng: &mut R, map: &Map, num_peds: usize) {
|
||||
let mut sidewalks: Vec<RoadID> = Vec::new();
|
||||
for r in map.all_roads() {
|
||||
if r.lane_type == LaneType::Sidewalk {
|
||||
sidewalks.push(r.id);
|
||||
}
|
||||
}
|
||||
|
||||
for _i in 0..num_peds {
|
||||
self.seed_pedestrian(*rng.choose(&sidewalks).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seed_pedestrian(&mut self, r: RoadID) {
|
||||
let id = PedestrianID(self.id_counter);
|
||||
self.id_counter += 1;
|
||||
self.peds_per_sidewalk.insert(
|
||||
r,
|
||||
Pedestrian {
|
||||
id,
|
||||
on: On::Road(r),
|
||||
// TODO start next to a building path, or at least some random position
|
||||
dist_along: 0.0,
|
||||
// TODO should be based on first step
|
||||
contraflow: true,
|
||||
// TODO compute a path
|
||||
path: VecDeque::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user