mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +03:00
more fluidly glue together agents, trips, people
This commit is contained in:
parent
76c0ecff43
commit
dad4c9bef2
@ -1,17 +1,26 @@
|
||||
use crate::app::App;
|
||||
use crate::helpers::ID;
|
||||
use crate::info::trip::trip_details;
|
||||
use crate::info::{make_table, TripDetails};
|
||||
use crate::info::{make_table, make_tabs, person, InfoTab, TripDetails};
|
||||
use crate::render::Renderable;
|
||||
use ezgui::{Color, EventCtx, GeomBatch, Line, Text, Widget};
|
||||
use sim::{AgentID, CarID, PedestrianID, VehicleType};
|
||||
use sim::{AgentID, CarID, PedestrianID, PersonID, VehicleType};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Tab {
|
||||
Person(PersonID),
|
||||
}
|
||||
|
||||
pub fn car_info(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
id: CarID,
|
||||
tab: InfoTab,
|
||||
header_btns: Widget,
|
||||
action_btns: Vec<Widget>,
|
||||
batch: &mut GeomBatch,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
) -> (Vec<Widget>, Option<TripDetails>) {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -24,33 +33,57 @@ pub fn car_info(
|
||||
Line(format!("{} #{}", label, id.0)).roboto_bold().draw(ctx),
|
||||
header_btns,
|
||||
]));
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (kv, extra) = app.primary.sim.car_properties(id, &app.primary.map);
|
||||
rows.extend(make_table(ctx, kv));
|
||||
if !extra.is_empty() {
|
||||
let mut txt = Text::from(Line(""));
|
||||
for line in extra {
|
||||
txt.add(Line(line));
|
||||
rows.push(make_tabs(ctx, hyperlinks, ID::Car(id), tab.clone(), {
|
||||
let mut tabs = vec![("Info", InfoTab::Nil)];
|
||||
if let Some(p) = app
|
||||
.primary
|
||||
.sim
|
||||
.agent_to_trip(AgentID::Car(id))
|
||||
.and_then(|t| app.primary.sim.trip_to_person(t))
|
||||
{
|
||||
tabs.push(("Schedule", InfoTab::Agent(Tab::Person(p))));
|
||||
}
|
||||
rows.push(txt.draw(ctx));
|
||||
}
|
||||
tabs
|
||||
}));
|
||||
|
||||
let trip = if id.1 == VehicleType::Bus {
|
||||
None
|
||||
} else {
|
||||
app.primary.sim.agent_to_trip(AgentID::Car(id))
|
||||
};
|
||||
let details = trip.map(|t| {
|
||||
let (more, details) = trip_details(
|
||||
ctx,
|
||||
app,
|
||||
t,
|
||||
app.primary.sim.progress_along_path(AgentID::Car(id)),
|
||||
);
|
||||
rows.push(more);
|
||||
details
|
||||
});
|
||||
let mut details: Option<TripDetails> = None;
|
||||
|
||||
match tab {
|
||||
InfoTab::Nil => {
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (kv, extra) = app.primary.sim.car_properties(id, &app.primary.map);
|
||||
rows.extend(make_table(ctx, kv));
|
||||
if !extra.is_empty() {
|
||||
let mut txt = Text::from(Line(""));
|
||||
for line in extra {
|
||||
txt.add(Line(line));
|
||||
}
|
||||
rows.push(txt.draw(ctx));
|
||||
}
|
||||
|
||||
let trip = if id.1 == VehicleType::Bus {
|
||||
None
|
||||
} else {
|
||||
app.primary.sim.agent_to_trip(AgentID::Car(id))
|
||||
};
|
||||
details = trip.map(|t| {
|
||||
let (more, details) = trip_details(
|
||||
ctx,
|
||||
app,
|
||||
t,
|
||||
app.primary.sim.progress_along_path(AgentID::Car(id)),
|
||||
);
|
||||
rows.push(more);
|
||||
details
|
||||
});
|
||||
}
|
||||
InfoTab::Agent(Tab::Person(p)) => {
|
||||
rows.extend(person::info(ctx, app, p, None, Vec::new(), hyperlinks));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
if let Some(b) = app.primary.sim.get_owner_of_car(id) {
|
||||
// TODO Mention this, with a warp tool
|
||||
@ -68,8 +101,10 @@ pub fn ped_info(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
id: PedestrianID,
|
||||
tab: InfoTab,
|
||||
header_btns: Widget,
|
||||
action_btns: Vec<Widget>,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
) -> (Vec<Widget>, Option<TripDetails>) {
|
||||
let mut rows = vec![];
|
||||
|
||||
@ -79,30 +114,62 @@ pub fn ped_info(
|
||||
.draw(ctx),
|
||||
header_btns,
|
||||
]));
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (kv, extra) = app.primary.sim.ped_properties(id, &app.primary.map);
|
||||
rows.extend(make_table(ctx, kv));
|
||||
if !extra.is_empty() {
|
||||
let mut txt = Text::from(Line(""));
|
||||
for line in extra {
|
||||
txt.add(Line(line));
|
||||
let trip = app
|
||||
.primary
|
||||
.sim
|
||||
.agent_to_trip(AgentID::Pedestrian(id))
|
||||
.unwrap();
|
||||
|
||||
rows.push(make_tabs(
|
||||
ctx,
|
||||
hyperlinks,
|
||||
ID::Pedestrian(id),
|
||||
tab.clone(),
|
||||
vec![
|
||||
("Info", InfoTab::Nil),
|
||||
(
|
||||
"Schedule",
|
||||
InfoTab::Agent(Tab::Person(app.primary.sim.trip_to_person(trip).unwrap())),
|
||||
),
|
||||
],
|
||||
));
|
||||
|
||||
let mut details: Option<TripDetails> = None;
|
||||
|
||||
match tab {
|
||||
InfoTab::Nil => {
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (kv, extra) = app.primary.sim.ped_properties(id, &app.primary.map);
|
||||
rows.extend(make_table(ctx, kv));
|
||||
if !extra.is_empty() {
|
||||
let mut txt = Text::from(Line(""));
|
||||
for line in extra {
|
||||
txt.add(Line(line));
|
||||
}
|
||||
rows.push(txt.draw(ctx));
|
||||
}
|
||||
|
||||
let (more, trip_details) = trip_details(
|
||||
ctx,
|
||||
app,
|
||||
app.primary
|
||||
.sim
|
||||
.agent_to_trip(AgentID::Pedestrian(id))
|
||||
.unwrap(),
|
||||
app.primary.sim.progress_along_path(AgentID::Pedestrian(id)),
|
||||
);
|
||||
rows.push(more);
|
||||
details = Some(trip_details);
|
||||
}
|
||||
rows.push(txt.draw(ctx));
|
||||
InfoTab::Agent(Tab::Person(p)) => {
|
||||
rows.extend(person::info(ctx, app, p, None, Vec::new(), hyperlinks));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let (more, details) = trip_details(
|
||||
ctx,
|
||||
app,
|
||||
app.primary
|
||||
.sim
|
||||
.agent_to_trip(AgentID::Pedestrian(id))
|
||||
.unwrap(),
|
||||
app.primary.sim.progress_along_path(AgentID::Pedestrian(id)),
|
||||
);
|
||||
rows.push(more);
|
||||
|
||||
(rows, Some(details))
|
||||
(rows, details)
|
||||
}
|
||||
|
||||
// TODO Embedded panel is perfect here
|
||||
|
@ -19,7 +19,7 @@ use ezgui::{
|
||||
Line, Outcome, Plot, PlotOptions, Series, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
use geom::{Circle, Distance, Time};
|
||||
use sim::{AgentID, Analytics, PersonID, TripID, TripMode, TripResult, VehicleType};
|
||||
use sim::{AgentID, Analytics, TripID, TripMode, TripResult, VehicleType};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
pub struct InfoPanel {
|
||||
@ -43,6 +43,8 @@ pub enum InfoTab {
|
||||
Bldg(building::Tab),
|
||||
Lane(lane::Tab),
|
||||
Intersection(intersection::Tab),
|
||||
Agent(agents::Tab),
|
||||
Trip(trip::Tab),
|
||||
}
|
||||
|
||||
pub struct TripDetails {
|
||||
@ -179,8 +181,25 @@ impl InfoPanel {
|
||||
),
|
||||
None,
|
||||
),
|
||||
ID::Car(id) => agents::car_info(ctx, app, id, header_btns, action_btns, &mut batch),
|
||||
ID::Pedestrian(id) => agents::ped_info(ctx, app, id, header_btns, action_btns),
|
||||
ID::Car(id) => agents::car_info(
|
||||
ctx,
|
||||
app,
|
||||
id,
|
||||
tab.clone(),
|
||||
header_btns,
|
||||
action_btns,
|
||||
&mut batch,
|
||||
&mut hyperlinks,
|
||||
),
|
||||
ID::Pedestrian(id) => agents::ped_info(
|
||||
ctx,
|
||||
app,
|
||||
id,
|
||||
tab.clone(),
|
||||
header_btns,
|
||||
action_btns,
|
||||
&mut hyperlinks,
|
||||
),
|
||||
ID::PedCrowd(members) => (
|
||||
agents::crowd_info(ctx, app, members, header_btns, action_btns),
|
||||
None,
|
||||
@ -191,7 +210,30 @@ impl InfoPanel {
|
||||
debug::extra_shape(ctx, app, id, header_btns, action_btns),
|
||||
None,
|
||||
),
|
||||
ID::Trip(id) => trip::info(ctx, app, id, action_btns),
|
||||
ID::Trip(id) => match app.primary.sim.trip_to_agent(id).ok() {
|
||||
Some(AgentID::Car(c)) => agents::car_info(
|
||||
ctx,
|
||||
app,
|
||||
c,
|
||||
tab.clone(),
|
||||
header_btns,
|
||||
Vec::new(),
|
||||
&mut batch,
|
||||
&mut hyperlinks,
|
||||
),
|
||||
Some(AgentID::Pedestrian(p)) => agents::ped_info(
|
||||
ctx,
|
||||
app,
|
||||
p,
|
||||
tab.clone(),
|
||||
header_btns,
|
||||
Vec::new(),
|
||||
&mut hyperlinks,
|
||||
),
|
||||
None => {
|
||||
trip::inactive_info(ctx, app, id, tab.clone(), action_btns, &mut hyperlinks)
|
||||
}
|
||||
},
|
||||
ID::Person(id) => (
|
||||
person::info(
|
||||
ctx,
|
||||
@ -299,7 +341,7 @@ impl InfoPanel {
|
||||
))),
|
||||
);
|
||||
}
|
||||
TripResult::TripDoesntExist => unreachable!(),
|
||||
TripResult::TripNotStarted | TripResult::TripDoesntExist => unreachable!(),
|
||||
// Just wait a moment for trip_transition to kick in...
|
||||
TripResult::ModeChange => {}
|
||||
}
|
||||
@ -370,16 +412,6 @@ impl InfoPanel {
|
||||
&mut app.primary,
|
||||
))),
|
||||
)
|
||||
} else if let Some(idx) = strip_prefix_usize(&action, "examine Person #") {
|
||||
*self = InfoPanel::new(
|
||||
ID::Person(PersonID(idx)),
|
||||
InfoTab::Nil,
|
||||
ctx,
|
||||
app,
|
||||
Vec::new(),
|
||||
maybe_speed,
|
||||
);
|
||||
return (false, None);
|
||||
} else if action == "Info" {
|
||||
// Genericish
|
||||
*self = InfoPanel::new(
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::app::App;
|
||||
use crate::colors;
|
||||
use crate::helpers::ID;
|
||||
use crate::info::{make_table, TripDetails};
|
||||
use crate::info::{make_table, make_tabs, person, InfoTab, TripDetails};
|
||||
use crate::render::dashed_lines;
|
||||
use ezgui::{
|
||||
hotkey, Btn, Color, EventCtx, GeomBatch, Key, Line, Plot, PlotOptions, RewriteColor, Series,
|
||||
@ -9,30 +9,62 @@ use ezgui::{
|
||||
};
|
||||
use geom::{Angle, Distance, Duration, Polygon, Pt2D, Time};
|
||||
use map_model::{Map, Path, PathStep};
|
||||
use sim::{TripEnd, TripID, TripPhaseType, TripStart};
|
||||
use sim::{PersonID, TripEnd, TripID, TripPhaseType, TripStart};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn info(
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Tab {
|
||||
Person(PersonID),
|
||||
}
|
||||
|
||||
pub fn inactive_info(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
id: TripID,
|
||||
tab: InfoTab,
|
||||
action_btns: Vec<Widget>,
|
||||
hyperlinks: &mut HashMap<String, (ID, InfoTab)>,
|
||||
) -> (Vec<Widget>, Option<TripDetails>) {
|
||||
let mut rows = vec![];
|
||||
|
||||
rows.push(Widget::row(vec![
|
||||
Line(format!("Trip #{}", id.0)).roboto_bold().draw(ctx),
|
||||
// No jump-to-object button; this is probably a finished trip.
|
||||
Btn::text_fg("X")
|
||||
.build(ctx, "close info", hotkey(Key::Escape))
|
||||
.align_right(),
|
||||
]));
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (more, details) = trip_details(ctx, app, id, None);
|
||||
rows.push(more);
|
||||
rows.push(make_tabs(
|
||||
ctx,
|
||||
hyperlinks,
|
||||
ID::Trip(id),
|
||||
tab.clone(),
|
||||
vec![
|
||||
("Info", InfoTab::Nil),
|
||||
(
|
||||
"Schedule",
|
||||
InfoTab::Trip(Tab::Person(app.primary.sim.trip_to_person(id).unwrap())),
|
||||
),
|
||||
],
|
||||
));
|
||||
|
||||
(rows, Some(details))
|
||||
let mut details: Option<TripDetails> = None;
|
||||
|
||||
match tab {
|
||||
InfoTab::Nil => {
|
||||
rows.extend(action_btns);
|
||||
|
||||
let (more, trip_details) = trip_details(ctx, app, id, None);
|
||||
rows.push(more);
|
||||
details = Some(trip_details);
|
||||
}
|
||||
InfoTab::Trip(Tab::Person(p)) => {
|
||||
rows.extend(person::info(ctx, app, p, None, Vec::new(), hyperlinks));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
(rows, details)
|
||||
}
|
||||
|
||||
pub fn trip_details(
|
||||
@ -324,13 +356,6 @@ pub fn trip_details(
|
||||
let mut col = vec![Widget::row(timeline).evenly_spaced().margin_above(25)];
|
||||
col.extend(make_table(ctx, kv));
|
||||
col.extend(elevation);
|
||||
if let Some(p) = app.primary.sim.trip_to_person(trip) {
|
||||
col.push(
|
||||
Btn::text_bg1(format!("Trip by Person #{}", p.0))
|
||||
.build(ctx, format!("examine Person #{}", p.0), None)
|
||||
.margin(5),
|
||||
);
|
||||
}
|
||||
|
||||
(
|
||||
Widget::col(col),
|
||||
|
@ -534,12 +534,17 @@ impl TripManager {
|
||||
return TripResult::TripDone;
|
||||
}
|
||||
|
||||
match &trip.legs[0] {
|
||||
TripLeg::Walk(id, _, _) => TripResult::Ok(AgentID::Pedestrian(*id)),
|
||||
TripLeg::Drive(vehicle, _) => TripResult::Ok(AgentID::Car(vehicle.id)),
|
||||
let a = match &trip.legs[0] {
|
||||
TripLeg::Walk(id, _, _) => AgentID::Pedestrian(*id),
|
||||
TripLeg::Drive(vehicle, _) => AgentID::Car(vehicle.id),
|
||||
// TODO Should be the bus, but apparently transit sim tracks differently?
|
||||
TripLeg::RideBus(ped, _, _) => TripResult::Ok(AgentID::Pedestrian(*ped)),
|
||||
TripLeg::ServeBusRoute(id, _) => TripResult::Ok(AgentID::Car(*id)),
|
||||
TripLeg::RideBus(ped, _, _) => AgentID::Pedestrian(*ped),
|
||||
TripLeg::ServeBusRoute(id, _) => AgentID::Car(*id),
|
||||
};
|
||||
if self.active_trip_mode.get(&a) == Some(&id) {
|
||||
TripResult::Ok(a)
|
||||
} else {
|
||||
TripResult::TripNotStarted
|
||||
}
|
||||
}
|
||||
|
||||
@ -840,6 +845,7 @@ pub enum TripResult<T> {
|
||||
ModeChange,
|
||||
TripDone,
|
||||
TripDoesntExist,
|
||||
TripNotStarted,
|
||||
}
|
||||
|
||||
impl<T> TripResult<T> {
|
||||
@ -856,6 +862,7 @@ impl<T> TripResult<T> {
|
||||
TripResult::ModeChange => TripResult::ModeChange,
|
||||
TripResult::TripDone => TripResult::TripDone,
|
||||
TripResult::TripDoesntExist => TripResult::TripDoesntExist,
|
||||
TripResult::TripNotStarted => TripResult::TripNotStarted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user