more fluidly glue together agents, trips, people

This commit is contained in:
Dustin Carlino 2020-03-24 22:47:06 -07:00
parent 76c0ecff43
commit dad4c9bef2
4 changed files with 212 additions and 81 deletions

View File

@ -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

View File

@ -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(

View File

@ -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),

View File

@ -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,
}
}
}