mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
Extend the parking thought bubbles to handle people climbing steep hills.
(And some clippy fixes)
This commit is contained in:
parent
184593094e
commit
da704b4546
4
data/system/assets/tools/uphill.svg
Normal file
4
data/system/assets/tools/uphill.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="34" height="29" viewBox="0 0 34 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M18.2747 28.9371C13.0191 28.9127 6.98232 28.8746 4.85956 28.8526L1 28.8125L8.99035 23.1594C29.2549 8.82266 33.426 5.89413 33.4262 6.00287C33.4264 6.05255 33.5553 10.976 33.7128 16.9438C33.8703 22.9116 33.9993 28.0656 33.9995 28.3972L34 29L30.9152 28.9908C29.2186 28.9857 23.5304 28.9616 18.2748 28.9371L18.2747 28.9371Z" fill="black"/>
|
||||||
|
<path d="M28 0L16.5619 1.58208L23.6511 10.6967L28 0ZM1.61394 21.7894C11.3131 14.2456 17.5281 9.4117 21.5104 6.31431L20.2825 4.73561C16.3002 7.833 10.0852 12.6669 0.386059 20.2106L1.61394 21.7894Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 660 B |
@ -131,7 +131,7 @@ fn prebake(
|
|||||||
}
|
}
|
||||||
PrebakeSummary {
|
PrebakeSummary {
|
||||||
map: scenario.map_name.describe(),
|
map: scenario.map_name.describe(),
|
||||||
scenario: scenario.scenario_name.clone(),
|
scenario: scenario.scenario_name,
|
||||||
finished_trips,
|
finished_trips,
|
||||||
cancelled_trips,
|
cancelled_trips,
|
||||||
total_trip_duration_seconds,
|
total_trip_duration_seconds,
|
||||||
|
@ -306,6 +306,7 @@ pub fn draw_occupants(details: &mut Details, app: &App, id: BuildingID, focus: O
|
|||||||
pos,
|
pos,
|
||||||
facing: Angle::degrees(90.0),
|
facing: Angle::degrees(90.0),
|
||||||
waiting_for_turn: None,
|
waiting_for_turn: None,
|
||||||
|
intent: None,
|
||||||
preparing_bike: false,
|
preparing_bike: false,
|
||||||
// Both hands and feet!
|
// Both hands and feet!
|
||||||
waiting_for_bus: true,
|
waiting_for_bus: true,
|
||||||
|
@ -125,16 +125,14 @@ impl Distance {
|
|||||||
Distance::feet(10.0 * (ft / 10.0).ceil())
|
Distance::feet(10.0 * (ft / 10.0).ceil())
|
||||||
} else if miles < 0.1 {
|
} else if miles < 0.1 {
|
||||||
Distance::feet(100.0 * (ft / 100.0).ceil())
|
Distance::feet(100.0 * (ft / 100.0).ceil())
|
||||||
|
} else if miles <= 1.0 {
|
||||||
|
Distance::miles((miles * 10.0).ceil() / 10.0)
|
||||||
|
} else if miles <= 10.0 {
|
||||||
|
Distance::miles(miles.ceil())
|
||||||
|
} else if miles <= 100.0 {
|
||||||
|
Distance::miles(10.0 * (miles / 10.0).ceil())
|
||||||
} else {
|
} else {
|
||||||
if miles <= 1.0 {
|
self
|
||||||
Distance::miles((miles * 10.0).ceil() / 10.0)
|
|
||||||
} else if miles <= 10.0 {
|
|
||||||
Distance::miles(miles.ceil())
|
|
||||||
} else if miles <= 100.0 {
|
|
||||||
Distance::miles(10.0 * (miles / 10.0).ceil())
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use geom::{ArrowCap, Circle, Distance, Line, PolyLine, Polygon, Pt2D};
|
use geom::{ArrowCap, Circle, Distance, Line, PolyLine, Polygon, Pt2D};
|
||||||
use map_model::{Map, SIDEWALK_THICKNESS};
|
use map_model::{Map, SIDEWALK_THICKNESS};
|
||||||
use sim::{CarID, DrawCarInput, Sim};
|
use sim::{CarID, DrawCarInput, Intent, Sim};
|
||||||
use widgetry::{Drawable, GeomBatch, GfxCtx, Prerender};
|
use widgetry::{Drawable, GeomBatch, GfxCtx, Prerender};
|
||||||
|
|
||||||
use crate::colors::ColorScheme;
|
use crate::colors::ColorScheme;
|
||||||
@ -99,6 +99,25 @@ impl DrawBike {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.intent == Some(Intent::SteepUphill) {
|
||||||
|
let bubble_z = -0.0001;
|
||||||
|
let mut bubble_batch =
|
||||||
|
GeomBatch::load_svg(prerender, "system/assets/map/thought_bubble.svg")
|
||||||
|
.scale(0.05)
|
||||||
|
.centered_on(input.body.middle())
|
||||||
|
.translate(2.0, -3.5)
|
||||||
|
.set_z_offset(bubble_z);
|
||||||
|
bubble_batch.append(
|
||||||
|
GeomBatch::load_svg(prerender, "system/assets/tools/uphill.svg")
|
||||||
|
.scale(0.05)
|
||||||
|
.centered_on(input.body.middle())
|
||||||
|
.translate(2.2, -4.2)
|
||||||
|
.set_z_offset(bubble_z),
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_default.append(bubble_batch);
|
||||||
|
}
|
||||||
|
|
||||||
let zorder = input
|
let zorder = input
|
||||||
.partly_on
|
.partly_on
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use geom::{Angle, ArrowCap, Distance, PolyLine, Polygon, Pt2D, Ring};
|
use geom::{Angle, ArrowCap, Distance, PolyLine, Polygon, Pt2D, Ring};
|
||||||
use map_model::{Map, TurnType};
|
use map_model::{Map, TurnType};
|
||||||
use sim::{CarID, CarStatus, DrawCarInput, Sim, VehicleType};
|
use sim::{CarID, CarStatus, DrawCarInput, Intent, Sim, VehicleType};
|
||||||
use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
|
use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
|
||||||
|
|
||||||
use crate::colors::ColorScheme;
|
use crate::colors::ColorScheme;
|
||||||
@ -90,7 +90,7 @@ impl DrawCar {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.show_parking_intent {
|
if input.intent == Some(Intent::Parking) {
|
||||||
// draw intent bubble
|
// draw intent bubble
|
||||||
let bubble_z = -0.0001;
|
let bubble_z = -0.0001;
|
||||||
let mut bubble_batch =
|
let mut bubble_batch =
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use geom::{ArrowCap, Circle, Distance, PolyLine, Polygon, Pt2D};
|
use geom::{ArrowCap, Circle, Distance, PolyLine, Polygon, Pt2D};
|
||||||
use map_model::{DrivingSide, Map, SIDEWALK_THICKNESS};
|
use map_model::{DrivingSide, Map, SIDEWALK_THICKNESS};
|
||||||
use sim::{DrawPedCrowdInput, DrawPedestrianInput, PedCrowdLocation, PedestrianID, Sim};
|
use sim::{DrawPedCrowdInput, DrawPedestrianInput, Intent, PedCrowdLocation, PedestrianID, Sim};
|
||||||
use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
|
use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
|
||||||
|
|
||||||
use crate::colors::ColorScheme;
|
use crate::colors::ColorScheme;
|
||||||
@ -43,6 +43,25 @@ impl DrawPedestrian {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.intent == Some(Intent::SteepUphill) {
|
||||||
|
let bubble_z = -0.0001;
|
||||||
|
let mut bubble_batch =
|
||||||
|
GeomBatch::load_svg(prerender, "system/assets/map/thought_bubble.svg")
|
||||||
|
.scale(0.05)
|
||||||
|
.centered_on(input.pos)
|
||||||
|
.translate(2.0, -3.5)
|
||||||
|
.set_z_offset(bubble_z);
|
||||||
|
bubble_batch.append(
|
||||||
|
GeomBatch::load_svg(prerender, "system/assets/tools/uphill.svg")
|
||||||
|
.scale(0.05)
|
||||||
|
.centered_on(input.pos)
|
||||||
|
.translate(2.2, -4.2)
|
||||||
|
.set_z_offset(bubble_z),
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_default.append(bubble_batch);
|
||||||
|
}
|
||||||
|
|
||||||
DrawPedestrian {
|
DrawPedestrian {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
body_circle,
|
body_circle,
|
||||||
|
@ -260,7 +260,7 @@ pub fn vehicle_cost(
|
|||||||
PathConstraints::Pedestrian => unreachable!(),
|
PathConstraints::Pedestrian => unreachable!(),
|
||||||
};
|
};
|
||||||
let t1 = map.get_r(dr.id).center_pts.length()
|
let t1 = map.get_r(dr.id).center_pts.length()
|
||||||
/ Traversable::max_speed_along_road(dr, max_speed, constraints, map);
|
/ Traversable::max_speed_along_road(dr, max_speed, constraints, map).0;
|
||||||
let t2 =
|
let t2 =
|
||||||
mvmnt_length / Traversable::max_speed_along_movement(mvmnt, max_speed, constraints, map);
|
mvmnt_length / Traversable::max_speed_along_movement(mvmnt, max_speed, constraints, map);
|
||||||
|
|
||||||
|
@ -202,6 +202,18 @@ impl Traversable {
|
|||||||
constraints: PathConstraints,
|
constraints: PathConstraints,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
) -> Speed {
|
) -> Speed {
|
||||||
|
self.max_speed_and_incline_along(max_speed_on_flat_ground, constraints, map)
|
||||||
|
.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The single definitive place to determine how fast somebody could go along a single road or
|
||||||
|
/// turn. This should be used for pathfinding and simulation. Returns (speed, percent incline).
|
||||||
|
pub fn max_speed_and_incline_along(
|
||||||
|
&self,
|
||||||
|
max_speed_on_flat_ground: Option<Speed>,
|
||||||
|
constraints: PathConstraints,
|
||||||
|
map: &Map,
|
||||||
|
) -> (Speed, f64) {
|
||||||
match self {
|
match self {
|
||||||
Traversable::Lane(l) => Traversable::max_speed_along_road(
|
Traversable::Lane(l) => Traversable::max_speed_along_road(
|
||||||
map.get_l(*l).get_directed_parent(),
|
map.get_l(*l).get_directed_parent(),
|
||||||
@ -209,23 +221,26 @@ impl Traversable {
|
|||||||
constraints,
|
constraints,
|
||||||
map,
|
map,
|
||||||
),
|
),
|
||||||
Traversable::Turn(t) => Traversable::max_speed_along_movement(
|
Traversable::Turn(t) => (
|
||||||
t.to_movement(map),
|
Traversable::max_speed_along_movement(
|
||||||
max_speed_on_flat_ground,
|
t.to_movement(map),
|
||||||
constraints,
|
max_speed_on_flat_ground,
|
||||||
map,
|
constraints,
|
||||||
|
map,
|
||||||
|
),
|
||||||
|
0.0,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The single definitive place to determine how fast somebody could go along a single road.
|
/// The single definitive place to determine how fast somebody could go along a single road.
|
||||||
/// This should be used for pathfinding and simulation.
|
/// This should be used for pathfinding and simulation. Returns (speed, percent incline).
|
||||||
pub fn max_speed_along_road(
|
pub(crate) fn max_speed_along_road(
|
||||||
dr: DirectedRoadID,
|
dr: DirectedRoadID,
|
||||||
max_speed_on_flat_ground: Option<Speed>,
|
max_speed_on_flat_ground: Option<Speed>,
|
||||||
constraints: PathConstraints,
|
constraints: PathConstraints,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
) -> Speed {
|
) -> (Speed, f64) {
|
||||||
let road = map.get_r(dr.id);
|
let road = map.get_r(dr.id);
|
||||||
let percent_incline = if dr.dir == Direction::Fwd {
|
let percent_incline = if dr.dir == Direction::Fwd {
|
||||||
road.percent_incline
|
road.percent_incline
|
||||||
@ -245,16 +260,17 @@ impl Traversable {
|
|||||||
road.speed_limit
|
road.speed_limit
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(s) = max_speed_on_flat_ground {
|
let speed = if let Some(s) = max_speed_on_flat_ground {
|
||||||
base.min(s)
|
base.min(s)
|
||||||
} else {
|
} else {
|
||||||
base
|
base
|
||||||
}
|
};
|
||||||
|
(speed, percent_incline)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The single definitive place to determine how fast somebody could go along a single
|
/// The single definitive place to determine how fast somebody could go along a single
|
||||||
/// movement. This should be used for pathfinding and simulation.
|
/// movement. This should be used for pathfinding and simulation. Ignores elevation.
|
||||||
pub fn max_speed_along_movement(
|
pub(crate) fn max_speed_along_movement(
|
||||||
mvmnt: MovementID,
|
mvmnt: MovementID,
|
||||||
max_speed_on_flat_ground: Option<Speed>,
|
max_speed_on_flat_ground: Option<Speed>,
|
||||||
_: PathConstraints,
|
_: PathConstraints,
|
||||||
|
@ -29,7 +29,7 @@ use map_model::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::render::{
|
pub use crate::render::{
|
||||||
CarStatus, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, PedCrowdLocation,
|
CarStatus, DrawCarInput, DrawPedCrowdInput, DrawPedestrianInput, Intent, PedCrowdLocation,
|
||||||
UnzoomedAgent,
|
UnzoomedAgent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ use geom::{Distance, Duration, PolyLine, Time, EPSILON_DIST};
|
|||||||
use map_model::{Direction, LaneID, Map, Traversable};
|
use map_model::{Direction, LaneID, Map, Traversable};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CarID, CarStatus, DistanceInterval, DrawCarInput, ParkingSpot, PersonID, Router, TimeInterval,
|
CarID, CarStatus, DistanceInterval, DrawCarInput, Intent, ParkingSpot, PersonID, Router,
|
||||||
TransitSimState, TripID, Vehicle, VehicleType,
|
TimeInterval, TransitSimState, TripID, Vehicle, VehicleType,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a single vehicle. Note "car" is a misnomer; it could also be a bus or bike.
|
/// Represents a single vehicle. Note "car" is a misnomer; it could also be a bus or bike.
|
||||||
@ -51,13 +51,17 @@ impl Car {
|
|||||||
start_time: Time,
|
start_time: Time,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
) -> CarState {
|
) -> CarState {
|
||||||
let speed = self.router.head().max_speed_along(
|
let (speed, percent_incline) = self.router.head().max_speed_and_incline_along(
|
||||||
self.vehicle.max_speed,
|
self.vehicle.max_speed,
|
||||||
self.vehicle.vehicle_type.to_constraints(),
|
self.vehicle.vehicle_type.to_constraints(),
|
||||||
map,
|
map,
|
||||||
);
|
);
|
||||||
let dt = (dist_int.end - dist_int.start) / speed;
|
let dt = (dist_int.end - dist_int.start) / speed;
|
||||||
CarState::Crossing(TimeInterval::new(start_time, start_time + dt), dist_int)
|
CarState::Crossing {
|
||||||
|
time_int: TimeInterval::new(start_time, start_time + dt),
|
||||||
|
dist_int,
|
||||||
|
steep_uphill: percent_incline >= 0.08,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_draw_car(
|
pub fn get_draw_car(
|
||||||
@ -262,17 +266,23 @@ impl Car {
|
|||||||
status: match self.state {
|
status: match self.state {
|
||||||
CarState::Queued { .. } => CarStatus::Moving,
|
CarState::Queued { .. } => CarStatus::Moving,
|
||||||
CarState::WaitingToAdvance { .. } => CarStatus::Moving,
|
CarState::WaitingToAdvance { .. } => CarStatus::Moving,
|
||||||
CarState::Crossing(_, _) => CarStatus::Moving,
|
CarState::Crossing { .. } => CarStatus::Moving,
|
||||||
CarState::ChangingLanes { .. } => CarStatus::Moving,
|
CarState::ChangingLanes { .. } => CarStatus::Moving,
|
||||||
CarState::Unparking { .. } => CarStatus::Moving,
|
CarState::Unparking { .. } => CarStatus::Moving,
|
||||||
CarState::Parking(_, _, _) => CarStatus::Moving,
|
CarState::Parking(_, _, _) => CarStatus::Moving,
|
||||||
// Changing color for idling buses is helpful
|
// Changing color for idling buses is helpful
|
||||||
CarState::IdlingAtStop(_, _) => CarStatus::Parked,
|
CarState::IdlingAtStop(_, _) => CarStatus::Parked,
|
||||||
},
|
},
|
||||||
show_parking_intent: matches!(
|
intent: if self.is_parking() || matches!(self.state, CarState::Unparking { .. }) {
|
||||||
(self.is_parking(), &self.state),
|
Some(Intent::Parking)
|
||||||
(true, _) | (_, CarState::Unparking { .. })
|
} else {
|
||||||
),
|
match self.state {
|
||||||
|
CarState::Crossing { steep_uphill, .. } if steep_uphill => {
|
||||||
|
Some(Intent::SteepUphill)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
on: self.router.head(),
|
on: self.router.head(),
|
||||||
partly_on,
|
partly_on,
|
||||||
label: if self.vehicle.vehicle_type == VehicleType::Bus
|
label: if self.vehicle.vehicle_type == VehicleType::Bus
|
||||||
@ -303,7 +313,11 @@ impl Car {
|
|||||||
/// state machine encoded here.
|
/// state machine encoded here.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub(crate) enum CarState {
|
pub(crate) enum CarState {
|
||||||
Crossing(TimeInterval, DistanceInterval),
|
Crossing {
|
||||||
|
time_int: TimeInterval,
|
||||||
|
dist_int: DistanceInterval,
|
||||||
|
steep_uphill: bool,
|
||||||
|
},
|
||||||
ChangingLanes {
|
ChangingLanes {
|
||||||
from: LaneID,
|
from: LaneID,
|
||||||
to: LaneID,
|
to: LaneID,
|
||||||
@ -334,7 +348,7 @@ pub(crate) enum CarState {
|
|||||||
impl CarState {
|
impl CarState {
|
||||||
pub fn get_end_time(&self) -> Time {
|
pub fn get_end_time(&self) -> Time {
|
||||||
match self {
|
match self {
|
||||||
CarState::Crossing(ref time_int, _) => time_int.end,
|
CarState::Crossing { ref time_int, .. } => time_int.end,
|
||||||
CarState::Queued { .. } => unreachable!(),
|
CarState::Queued { .. } => unreachable!(),
|
||||||
CarState::WaitingToAdvance { .. } => unreachable!(),
|
CarState::WaitingToAdvance { .. } => unreachable!(),
|
||||||
// Note this state lasts for lc_time, NOT for new_time.
|
// Note this state lasts for lc_time, NOT for new_time.
|
||||||
|
@ -368,7 +368,7 @@ impl DrivingSimState {
|
|||||||
transit: &mut TransitSimState,
|
transit: &mut TransitSimState,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match car.state {
|
match car.state {
|
||||||
CarState::Crossing(_, _) => {
|
CarState::Crossing { .. } => {
|
||||||
car.state = CarState::Queued {
|
car.state = CarState::Queued {
|
||||||
blocked_since: now,
|
blocked_since: now,
|
||||||
want_to_change_lanes: None,
|
want_to_change_lanes: None,
|
||||||
@ -554,7 +554,11 @@ impl DrivingSimState {
|
|||||||
} => {
|
} => {
|
||||||
// The car is already in the target queue. Just set them in the crossing state; we
|
// The car is already in the target queue. Just set them in the crossing state; we
|
||||||
// already calculated the intervals for it.
|
// already calculated the intervals for it.
|
||||||
car.state = CarState::Crossing(new_time, new_dist);
|
car.state = CarState::Crossing {
|
||||||
|
time_int: new_time,
|
||||||
|
dist_int: new_dist,
|
||||||
|
steep_uphill: false,
|
||||||
|
};
|
||||||
ctx.scheduler
|
ctx.scheduler
|
||||||
.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
||||||
|
|
||||||
@ -595,7 +599,7 @@ impl DrivingSimState {
|
|||||||
let our_dist = dists[idx].front;
|
let our_dist = dists[idx].front;
|
||||||
|
|
||||||
match car.state {
|
match car.state {
|
||||||
CarState::Crossing(_, _)
|
CarState::Crossing { .. }
|
||||||
| CarState::Unparking { .. }
|
| CarState::Unparking { .. }
|
||||||
| CarState::WaitingToAdvance { .. }
|
| CarState::WaitingToAdvance { .. }
|
||||||
| CarState::ChangingLanes { .. } => unreachable!(),
|
| CarState::ChangingLanes { .. } => unreachable!(),
|
||||||
@ -709,7 +713,7 @@ impl DrivingSimState {
|
|||||||
/*
|
/*
|
||||||
// If this car wasn't blocked at all, when would it reach its goal?
|
// If this car wasn't blocked at all, when would it reach its goal?
|
||||||
let ideal_end_time = match car.crossing_state(our_dist, now, map) {
|
let ideal_end_time = match car.crossing_state(our_dist, now, map) {
|
||||||
CarState::Crossing(time_int, _) => time_int.end,
|
CarState::Crossing { time_int, .. } => time_int.end,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if ideal_end_time == now {
|
if ideal_end_time == now {
|
||||||
@ -876,7 +880,7 @@ impl DrivingSimState {
|
|||||||
Command::UpdateCar(follower_id),
|
Command::UpdateCar(follower_id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
CarState::Crossing(_, _) => {
|
CarState::Crossing { .. } => {
|
||||||
// If the follower was still Crossing, they might not've been blocked by the
|
// If the follower was still Crossing, they might not've been blocked by the
|
||||||
// leader yet. But recalculating their Crossing state isn't necessarily a no-op
|
// leader yet. But recalculating their Crossing state isn't necessarily a no-op
|
||||||
// -- this could prevent them from suddenly warping past a blockage.
|
// -- this could prevent them from suddenly warping past a blockage.
|
||||||
@ -901,7 +905,9 @@ impl DrivingSimState {
|
|||||||
now,
|
now,
|
||||||
ctx.map,
|
ctx.map,
|
||||||
) {
|
) {
|
||||||
CarState::Crossing(time, dist) => (time, dist),
|
CarState::Crossing {
|
||||||
|
time_int, dist_int, ..
|
||||||
|
} => (time_int, dist_int),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
assert!(new_time.end >= lc_time.end);
|
assert!(new_time.end >= lc_time.end);
|
||||||
@ -1051,7 +1057,7 @@ impl DrivingSimState {
|
|||||||
CarState::WaitingToAdvance { .. } => unreachable!(),
|
CarState::WaitingToAdvance { .. } => unreachable!(),
|
||||||
// They weren't blocked. Note that there's no way the Crossing state could
|
// They weren't blocked. Note that there's no way the Crossing state could
|
||||||
// jump forwards here; the leader vanished from the end of the traversable.
|
// jump forwards here; the leader vanished from the end of the traversable.
|
||||||
CarState::Crossing(_, _)
|
CarState::Crossing { .. }
|
||||||
| CarState::ChangingLanes { .. }
|
| CarState::ChangingLanes { .. }
|
||||||
| CarState::Unparking { .. }
|
| CarState::Unparking { .. }
|
||||||
| CarState::Parking(_, _, _)
|
| CarState::Parking(_, _, _)
|
||||||
@ -1151,7 +1157,9 @@ impl DrivingSimState {
|
|||||||
now,
|
now,
|
||||||
ctx.map,
|
ctx.map,
|
||||||
) {
|
) {
|
||||||
CarState::Crossing(time, dist) => (time, dist),
|
CarState::Crossing {
|
||||||
|
time_int, dist_int, ..
|
||||||
|
} => (time_int, dist_int),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1366,7 +1374,7 @@ impl DrivingSimState {
|
|||||||
id: cause,
|
id: cause,
|
||||||
waiting_for_turn: None,
|
waiting_for_turn: None,
|
||||||
status: CarStatus::Parked,
|
status: CarStatus::Parked,
|
||||||
show_parking_intent: false,
|
intent: None,
|
||||||
on,
|
on,
|
||||||
partly_on: Vec::new(),
|
partly_on: Vec::new(),
|
||||||
label: Some("block".to_string()),
|
label: Some("block".to_string()),
|
||||||
@ -1383,7 +1391,7 @@ impl DrivingSimState {
|
|||||||
id: cause,
|
id: cause,
|
||||||
waiting_for_turn: None,
|
waiting_for_turn: None,
|
||||||
status: CarStatus::Parked,
|
status: CarStatus::Parked,
|
||||||
show_parking_intent: false,
|
intent: None,
|
||||||
on,
|
on,
|
||||||
partly_on: Vec::new(),
|
partly_on: Vec::new(),
|
||||||
label: Some("block".to_string()),
|
label: Some("block".to_string()),
|
||||||
|
@ -370,7 +370,7 @@ impl ParkingSim for NormalParkingSimState {
|
|||||||
id: p.vehicle.id,
|
id: p.vehicle.id,
|
||||||
waiting_for_turn: None,
|
waiting_for_turn: None,
|
||||||
status: CarStatus::Parked,
|
status: CarStatus::Parked,
|
||||||
show_parking_intent: false,
|
intent: None,
|
||||||
on: Traversable::Lane(lane),
|
on: Traversable::Lane(lane),
|
||||||
partly_on: Vec::new(),
|
partly_on: Vec::new(),
|
||||||
label: None,
|
label: None,
|
||||||
@ -392,7 +392,7 @@ impl ParkingSim for NormalParkingSimState {
|
|||||||
id: p.vehicle.id,
|
id: p.vehicle.id,
|
||||||
waiting_for_turn: None,
|
waiting_for_turn: None,
|
||||||
status: CarStatus::Parked,
|
status: CarStatus::Parked,
|
||||||
show_parking_intent: false,
|
intent: None,
|
||||||
// Just used for z-order
|
// Just used for z-order
|
||||||
on: Traversable::Lane(pl.driving_pos.lane()),
|
on: Traversable::Lane(pl.driving_pos.lane()),
|
||||||
partly_on: Vec::new(),
|
partly_on: Vec::new(),
|
||||||
|
@ -228,7 +228,11 @@ impl Queue {
|
|||||||
}
|
}
|
||||||
self.geom_len
|
self.geom_len
|
||||||
}
|
}
|
||||||
CarState::Crossing(ref time_int, ref dist_int) => {
|
CarState::Crossing {
|
||||||
|
ref time_int,
|
||||||
|
ref dist_int,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// TODO Why percent_clamp_end? We process car updates in any order, so we might
|
// TODO Why percent_clamp_end? We process car updates in any order, so we might
|
||||||
// calculate this before moving this car from Crossing to another state.
|
// calculate this before moving this car from Crossing to another state.
|
||||||
dist_int.lerp(time_int.percent_clamp_end(now)).min(bound)
|
dist_int.lerp(time_int.percent_clamp_end(now)).min(bound)
|
||||||
@ -575,7 +579,11 @@ fn dump_cars(dists: &[QueueEntry], cars: &FixedMap<CarID, Car>, id: Traversable,
|
|||||||
println!("- {:?} @ {}..{}", entry.member, entry.front, entry.back);
|
println!("- {:?} @ {}..{}", entry.member, entry.front, entry.back);
|
||||||
match entry.member {
|
match entry.member {
|
||||||
Queued::Vehicle(id) => match cars[&id].state {
|
Queued::Vehicle(id) => match cars[&id].state {
|
||||||
CarState::Crossing(ref time_int, ref dist_int) => {
|
CarState::Crossing {
|
||||||
|
ref time_int,
|
||||||
|
ref dist_int,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
println!(
|
println!(
|
||||||
" Going {} .. {} during {} .. {}",
|
" Going {} .. {} during {} .. {}",
|
||||||
dist_int.start, dist_int.end, time_int.start, time_int.end
|
dist_int.start, dist_int.end, time_int.start, time_int.end
|
||||||
|
@ -12,9 +12,9 @@ use map_model::{
|
|||||||
use crate::sim::Ctx;
|
use crate::sim::Ctx;
|
||||||
use crate::{
|
use crate::{
|
||||||
AgentID, AgentProperties, Command, CommutersVehiclesCounts, CreatePedestrian, DistanceInterval,
|
AgentID, AgentProperties, Command, CommutersVehiclesCounts, CreatePedestrian, DistanceInterval,
|
||||||
DrawPedCrowdInput, DrawPedestrianInput, Event, IntersectionSimState, ParkedCar, ParkingSpot,
|
DrawPedCrowdInput, DrawPedestrianInput, Event, Intent, IntersectionSimState, ParkedCar,
|
||||||
PedCrowdLocation, PedestrianID, PersonID, Scheduler, SidewalkPOI, SidewalkSpot, TimeInterval,
|
ParkingSpot, PedCrowdLocation, PedestrianID, PersonID, Scheduler, SidewalkPOI, SidewalkSpot,
|
||||||
TransitSimState, TripID, TripManager, UnzoomedAgent,
|
TimeInterval, TransitSimState, TripID, TripManager, UnzoomedAgent,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TIME_TO_START_BIKING: Duration = Duration::const_seconds(30.0);
|
const TIME_TO_START_BIKING: Duration = Duration::const_seconds(30.0);
|
||||||
@ -60,13 +60,14 @@ impl WalkingSimState {
|
|||||||
let mut ped = Pedestrian {
|
let mut ped = Pedestrian {
|
||||||
id: params.id,
|
id: params.id,
|
||||||
// Temporary bogus thing
|
// Temporary bogus thing
|
||||||
state: PedState::Crossing(
|
state: PedState::Crossing {
|
||||||
DistanceInterval::new_walking(Distance::ZERO, Distance::meters(1.0)),
|
dist_int: DistanceInterval::new_walking(Distance::ZERO, Distance::meters(1.0)),
|
||||||
TimeInterval::new(
|
time_int: TimeInterval::new(
|
||||||
Time::START_OF_DAY,
|
Time::START_OF_DAY,
|
||||||
Time::START_OF_DAY + Duration::seconds(1.0),
|
Time::START_OF_DAY + Duration::seconds(1.0),
|
||||||
),
|
),
|
||||||
),
|
steep_uphill: false,
|
||||||
|
},
|
||||||
speed: params.speed,
|
speed: params.speed,
|
||||||
total_blocked_time: Duration::ZERO,
|
total_blocked_time: Duration::ZERO,
|
||||||
started_at: now,
|
started_at: now,
|
||||||
@ -129,7 +130,7 @@ impl WalkingSimState {
|
|||||||
) {
|
) {
|
||||||
let mut ped = self.peds.get_mut(&id).unwrap();
|
let mut ped = self.peds.get_mut(&id).unwrap();
|
||||||
match ped.state {
|
match ped.state {
|
||||||
PedState::Crossing(ref dist_int, _) => {
|
PedState::Crossing { ref dist_int, .. } => {
|
||||||
if ped.path.is_last_step() {
|
if ped.path.is_last_step() {
|
||||||
match ped.goal.connection {
|
match ped.goal.connection {
|
||||||
SidewalkPOI::ParkingSpot(spot) => {
|
SidewalkPOI::ParkingSpot(spot) => {
|
||||||
@ -371,9 +372,11 @@ impl WalkingSimState {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
let current_state_dist = match p.state {
|
let current_state_dist = match p.state {
|
||||||
PedState::Crossing(ref dist_int, ref time_int) => {
|
PedState::Crossing {
|
||||||
time_int.percent(now) * dist_int.length()
|
ref dist_int,
|
||||||
}
|
ref time_int,
|
||||||
|
..
|
||||||
|
} => time_int.percent(now) * dist_int.length(),
|
||||||
// We're at the beginning of our trip and are only walking along a driveway or biking
|
// We're at the beginning of our trip and are only walking along a driveway or biking
|
||||||
// connection
|
// connection
|
||||||
PedState::LeavingBuilding(_, _)
|
PedState::LeavingBuilding(_, _)
|
||||||
@ -448,7 +451,7 @@ impl WalkingSimState {
|
|||||||
let dist = ped.get_dist_along(now, map);
|
let dist = ped.get_dist_along(now, map);
|
||||||
|
|
||||||
match ped.state {
|
match ped.state {
|
||||||
PedState::Crossing(ref dist_int, _) => {
|
PedState::Crossing { ref dist_int, .. } => {
|
||||||
if dist_int.start < dist_int.end {
|
if dist_int.start < dist_int.end {
|
||||||
forwards.push((*id, dist));
|
forwards.push((*id, dist));
|
||||||
} else {
|
} else {
|
||||||
@ -628,18 +631,26 @@ impl Pedestrian {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let dist_int = DistanceInterval::new_walking(start_dist, end_dist);
|
let dist_int = DistanceInterval::new_walking(start_dist, end_dist);
|
||||||
let speed = self.path.current_step().as_traversable().max_speed_along(
|
let (speed, percent_incline) = self
|
||||||
Some(self.speed),
|
.path
|
||||||
PathConstraints::Pedestrian,
|
.current_step()
|
||||||
map,
|
.as_traversable()
|
||||||
);
|
.max_speed_and_incline_along(Some(self.speed), PathConstraints::Pedestrian, map);
|
||||||
let time_int = TimeInterval::new(start_time, start_time + dist_int.length() / speed);
|
let time_int = TimeInterval::new(start_time, start_time + dist_int.length() / speed);
|
||||||
PedState::Crossing(dist_int, time_int)
|
PedState::Crossing {
|
||||||
|
dist_int,
|
||||||
|
time_int,
|
||||||
|
steep_uphill: percent_incline >= 0.08,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dist_along(&self, now: Time, map: &Map) -> Distance {
|
fn get_dist_along(&self, now: Time, map: &Map) -> Distance {
|
||||||
match self.state {
|
match self.state {
|
||||||
PedState::Crossing(ref dist_int, ref time_int) => dist_int.lerp(time_int.percent(now)),
|
PedState::Crossing {
|
||||||
|
ref dist_int,
|
||||||
|
ref time_int,
|
||||||
|
..
|
||||||
|
} => dist_int.lerp(time_int.percent(now)),
|
||||||
PedState::WaitingToTurn(dist, _) => dist,
|
PedState::WaitingToTurn(dist, _) => dist,
|
||||||
PedState::LeavingBuilding(b, _) | PedState::EnteringBuilding(b, _) => {
|
PedState::LeavingBuilding(b, _) | PedState::EnteringBuilding(b, _) => {
|
||||||
map.get_b(b).sidewalk_pos.dist_along()
|
map.get_b(b).sidewalk_pos.dist_along()
|
||||||
@ -661,8 +672,13 @@ impl Pedestrian {
|
|||||||
} else {
|
} else {
|
||||||
270.0
|
270.0
|
||||||
};
|
};
|
||||||
|
let mut intent = None;
|
||||||
let (pos, facing) = match self.state {
|
let (pos, facing) = match self.state {
|
||||||
PedState::Crossing(ref dist_int, ref time_int) => {
|
PedState::Crossing {
|
||||||
|
ref dist_int,
|
||||||
|
ref time_int,
|
||||||
|
steep_uphill,
|
||||||
|
} => {
|
||||||
let percent = if now > time_int.end {
|
let percent = if now > time_int.end {
|
||||||
1.0
|
1.0
|
||||||
} else {
|
} else {
|
||||||
@ -677,6 +693,9 @@ impl Pedestrian {
|
|||||||
} else {
|
} else {
|
||||||
orig_angle.opposite()
|
orig_angle.opposite()
|
||||||
};
|
};
|
||||||
|
if steep_uphill {
|
||||||
|
intent = Some(Intent::SteepUphill);
|
||||||
|
}
|
||||||
(
|
(
|
||||||
pos.project_away(SIDEWALK_THICKNESS / 4.0, facing.rotate_degs(angle_offset)),
|
pos.project_away(SIDEWALK_THICKNESS / 4.0, facing.rotate_degs(angle_offset)),
|
||||||
facing,
|
facing,
|
||||||
@ -758,6 +777,7 @@ impl Pedestrian {
|
|||||||
PedState::WaitingToTurn(_, _) => Some(self.path.next_step().as_turn()),
|
PedState::WaitingToTurn(_, _) => Some(self.path.next_step().as_turn()),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
intent,
|
||||||
preparing_bike: matches!(
|
preparing_bike: matches!(
|
||||||
self.state,
|
self.state,
|
||||||
PedState::StartingToBike(_, _, _) | PedState::FinishingBiking(_, _, _)
|
PedState::StartingToBike(_, _, _) | PedState::FinishingBiking(_, _, _)
|
||||||
@ -817,7 +837,11 @@ impl Pedestrian {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
enum PedState {
|
enum PedState {
|
||||||
Crossing(DistanceInterval, TimeInterval),
|
Crossing {
|
||||||
|
dist_int: DistanceInterval,
|
||||||
|
time_int: TimeInterval,
|
||||||
|
steep_uphill: bool,
|
||||||
|
},
|
||||||
/// The Distance is either 0 or the current traversable's length. The Time is blocked_since.
|
/// The Distance is either 0 or the current traversable's length. The Time is blocked_since.
|
||||||
WaitingToTurn(Distance, Time),
|
WaitingToTurn(Distance, Time),
|
||||||
LeavingBuilding(BuildingID, TimeInterval),
|
LeavingBuilding(BuildingID, TimeInterval),
|
||||||
@ -832,7 +856,7 @@ enum PedState {
|
|||||||
impl PedState {
|
impl PedState {
|
||||||
fn get_end_time(&self) -> Time {
|
fn get_end_time(&self) -> Time {
|
||||||
match self {
|
match self {
|
||||||
PedState::Crossing(_, ref time_int) => time_int.end,
|
PedState::Crossing { ref time_int, .. } => time_int.end,
|
||||||
PedState::WaitingToTurn(_, _) => unreachable!(),
|
PedState::WaitingToTurn(_, _) => unreachable!(),
|
||||||
PedState::LeavingBuilding(_, ref time_int) => time_int.end,
|
PedState::LeavingBuilding(_, ref time_int) => time_int.end,
|
||||||
PedState::EnteringBuilding(_, ref time_int) => time_int.end,
|
PedState::EnteringBuilding(_, ref time_int) => time_int.end,
|
||||||
|
@ -11,6 +11,7 @@ pub struct DrawPedestrianInput {
|
|||||||
pub pos: Pt2D,
|
pub pos: Pt2D,
|
||||||
pub facing: Angle,
|
pub facing: Angle,
|
||||||
pub waiting_for_turn: Option<TurnID>,
|
pub waiting_for_turn: Option<TurnID>,
|
||||||
|
pub intent: Option<Intent>,
|
||||||
pub preparing_bike: bool,
|
pub preparing_bike: bool,
|
||||||
pub waiting_for_bus: bool,
|
pub waiting_for_bus: bool,
|
||||||
pub on: Traversable,
|
pub on: Traversable,
|
||||||
@ -37,7 +38,7 @@ pub struct DrawCarInput {
|
|||||||
pub id: CarID,
|
pub id: CarID,
|
||||||
pub waiting_for_turn: Option<TurnID>,
|
pub waiting_for_turn: Option<TurnID>,
|
||||||
pub status: CarStatus,
|
pub status: CarStatus,
|
||||||
pub show_parking_intent: bool,
|
pub intent: Option<Intent>,
|
||||||
/// Front of the car
|
/// Front of the car
|
||||||
pub on: Traversable,
|
pub on: Traversable,
|
||||||
/// Possibly the rest
|
/// Possibly the rest
|
||||||
@ -56,6 +57,13 @@ pub enum CarStatus {
|
|||||||
Parked,
|
Parked,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows an agent's current inner intention or thoughts.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum Intent {
|
||||||
|
Parking,
|
||||||
|
SteepUphill,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct UnzoomedAgent {
|
pub struct UnzoomedAgent {
|
||||||
pub id: AgentID,
|
pub id: AgentID,
|
||||||
pub pos: Pt2D,
|
pub pos: Pt2D,
|
||||||
|
Loading…
Reference in New Issue
Block a user