add border stop/start to route info panel. move the route drawing there, stop being a weird layer.

This commit is contained in:
Dustin Carlino 2020-07-26 13:13:48 -07:00
parent 4c86f568d0
commit a4e09ca91f
5 changed files with 104 additions and 170 deletions

View File

@ -239,6 +239,16 @@ impl<'a> ColorNetwork<'a> {
}
}
pub fn add_l(&mut self, l: LaneID, color: Color) {
self.unzoomed
.push(color, self.map.get_parent(l).get_thick_polygon(self.map));
let lane = self.map.get_l(l);
self.zoomed.push(
color.alpha(0.4),
lane.lane_center_pts.make_polygons(lane.width),
);
}
pub fn add_r(&mut self, r: RoadID, color: Color) {
self.unzoomed
.push(color, self.map.get_r(r).get_thick_polygon(self.map));

View File

@ -1,10 +1,11 @@
use crate::app::App;
use crate::common::ColorNetwork;
use crate::helpers::ID;
use crate::info::{header_btns, make_tabs, Details, Tab};
use abstutil::{prettyprint_usize, Counter};
use ezgui::{Btn, Color, EventCtx, Line, RewriteColor, Text, TextExt, Widget};
use geom::{Circle, Distance, Time};
use map_model::{BusRouteID, BusStopID, PathConstraints};
use map_model::{BusRouteID, BusStopID, PathConstraints, PathStep};
use sim::{AgentID, CarID};
pub fn stop(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusStopID) -> Vec<Widget> {
@ -21,7 +22,7 @@ pub fn stop(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusStopID)
let all_arrivals = &sim.get_analytics().bus_arrivals;
for r in app.primary.map.get_routes_serving_stop(id) {
let buses = app.primary.sim.status_of_buses(r.id);
let buses = app.primary.sim.status_of_buses(r.id, &app.primary.map);
if buses.is_empty() {
rows.push(format!("Route {}: no buses running", r.short_name).draw_text(ctx));
} else {
@ -164,7 +165,8 @@ fn bus_header(
}
pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteID) -> Vec<Widget> {
let route = app.primary.map.get_br(id);
let map = &app.primary.map;
let route = map.get_br(id);
let mut rows = vec![];
rows.push(Widget::row(vec![
@ -179,7 +181,8 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
.draw(ctx),
);
let buses = app.primary.sim.status_of_buses(id);
let buses = app.primary.sim.status_of_buses(id, map);
let mut bus_locations = Vec::new();
if buses.is_empty() {
if route.route_type == PathConstraints::Bus {
rows.push("No buses running".draw_text(ctx));
@ -187,11 +190,12 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
rows.push("No trains running".draw_text(ctx));
}
} else {
for (bus, _, _) in buses {
for (bus, _, _, pt) in buses {
rows.push(Btn::text_fg(bus.to_string()).build_def(ctx, None));
details
.hyperlinks
.insert(bus.to_string(), Tab::BusStatus(bus));
bus_locations.push(pt);
}
}
@ -228,8 +232,21 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
);
rows.push(format!("{} stops", route.stops.len()).draw_text(ctx));
if let Some(l) = route.start_border {
let i = map.get_i(map.get_l(l).src_i);
let name = format!("Starts at {}", i.name(map));
rows.push(Widget::row(vec![
Btn::svg(
"system/assets/timeline/goal_pos.svg",
RewriteColor::Change(Color::WHITE, app.cs.hovering),
)
.build(ctx, &name, None),
name.clone().draw_text(ctx),
]));
details.warpers.insert(name, ID::Intersection(i.id));
}
for (idx, bs) in route.stops.iter().enumerate() {
let bs = app.primary.map.get_bs(*bs);
let bs = map.get_bs(*bs);
let name = format!("Stop {}: {}", idx + 1, bs.name);
rows.push(Widget::row(vec![
Btn::svg(
@ -250,6 +267,61 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
]));
details.warpers.insert(name, ID::BusStop(bs.id));
}
if let Some(l) = route.end_border {
let i = map.get_i(map.get_l(l).dst_i);
let name = format!("Ends at {}", i.name(map));
rows.push(Widget::row(vec![
Btn::svg(
"system/assets/timeline/goal_pos.svg",
RewriteColor::Change(Color::WHITE, app.cs.hovering),
)
.build(ctx, &name, None),
name.clone().draw_text(ctx),
]));
details.warpers.insert(name, ID::Intersection(i.id));
}
// Draw the route, label stops, and show location of buses
{
let mut colorer = ColorNetwork::new(app);
for req in route.all_steps(map) {
for step in map.pathfind(req).unwrap().get_steps() {
if let PathStep::Lane(l) = step {
colorer.add_l(*l, app.cs.unzoomed_bus);
}
}
}
details.unzoomed.append(colorer.unzoomed);
details.zoomed.append(colorer.zoomed);
for pt in bus_locations {
details.unzoomed.push(
Color::BLUE,
Circle::new(pt, Distance::meters(20.0)).to_polygon(),
);
details.zoomed.push(
Color::BLUE.alpha(0.5),
Circle::new(pt, Distance::meters(5.0)).to_polygon(),
);
}
for bs in &route.stops {
let bs = map.get_bs(*bs);
details.unzoomed.append(
Text::from(Line(&bs.name))
.with_bg()
.render_to_batch(ctx.prerender)
.centered_on(bs.sidewalk_pos.pt(map)),
);
details.zoomed.append(
Text::from(Line(&bs.name))
.with_bg()
.render_to_batch(ctx.prerender)
.scale(0.1)
.centered_on(bs.sidewalk_pos.pt(map)),
);
}
}
rows
}

View File

@ -2,11 +2,10 @@ use crate::app::App;
use crate::common::ColorDiscrete;
use crate::layer::{Layer, LayerOutcome};
use ezgui::{
hotkey, Btn, Checkbox, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
HorizontalAlignment, Key, Line, Outcome, Text, TextExt, VerticalAlignment, Widget,
hotkey, Btn, Checkbox, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key,
Outcome, TextExt, VerticalAlignment, Widget,
};
use geom::{Circle, Distance, Pt2D, Time};
use map_model::{BusRouteID, PathConstraints, PathStep};
use map_model::{PathConstraints, PathStep};
pub struct TransitNetwork {
composite: Composite,
@ -149,132 +148,3 @@ impl TransitNetwork {
}
}
}
// TODO This maybe shouldn't be a layer
pub struct ShowTransitRoute {
time: Time,
route: BusRouteID,
labels: Vec<(Text, Pt2D)>,
bus_locations: Vec<Pt2D>,
composite: Composite,
unzoomed: Drawable,
zoomed: Drawable,
}
impl Layer for ShowTransitRoute {
fn name(&self) -> Option<&'static str> {
None
}
fn event(
&mut self,
ctx: &mut EventCtx,
app: &mut App,
minimap: &Composite,
) -> Option<LayerOutcome> {
if app.primary.sim.time() != self.time {
*self = ShowTransitRoute::new(ctx, app, self.route);
}
Layer::simple_event(ctx, minimap, &mut self.composite)
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
g.redraw(&self.unzoomed);
} else {
g.redraw(&self.zoomed);
}
self.composite.draw(g);
// TODO Do this once
let mut screen_batch = GeomBatch::new();
for (label, pt) in &self.labels {
screen_batch.append(
label
.clone()
.render_g(g)
.centered_on(g.canvas.map_to_screen(*pt).to_pt()),
);
}
let draw = g.upload(screen_batch);
g.fork_screenspace();
g.redraw(&draw);
g.unfork();
let mut batch = GeomBatch::new();
let radius = Distance::meters(20.0) / g.canvas.cam_zoom;
for pt in &self.bus_locations {
batch.push(Color::BLUE, Circle::new(*pt, radius).to_polygon());
}
batch.draw(g);
}
fn draw_minimap(&self, g: &mut GfxCtx) {
g.redraw(&self.unzoomed);
}
}
impl ShowTransitRoute {
pub fn new(ctx: &mut EventCtx, app: &App, id: BusRouteID) -> ShowTransitRoute {
let map = &app.primary.map;
let route = app.primary.map.get_br(id);
let mut bus_locations = Vec::new();
for (_, pt) in app.primary.sim.location_of_buses(id, map) {
bus_locations.push(pt);
}
let mut categories = vec![("route", app.cs.unzoomed_bus)];
if route.start_border.is_some() {
categories.push(("start", Color::RED));
}
if route.end_border.is_some() {
categories.push(("end", Color::GREEN));
}
let mut colorer = ColorDiscrete::new(app, categories);
if let Some(l) = route.start_border {
colorer.add_i(map.get_l(l).src_i, "start");
}
if let Some(l) = route.end_border {
colorer.add_i(map.get_l(l).dst_i, "end");
}
for req in route.all_steps(map) {
for step in map.pathfind(req).unwrap().get_steps() {
if let PathStep::Lane(l) = step {
colorer.add_l(*l, "route");
}
}
}
let mut labels = Vec::new();
for bs in &route.stops {
let bs = map.get_bs(*bs);
labels.push((
Text::from(Line(&bs.name)).with_bg(),
bs.sidewalk_pos.pt(map),
));
}
let (unzoomed, zoomed, legend) = colorer.build(ctx);
ShowTransitRoute {
time: app.primary.sim.time(),
route: id,
labels,
unzoomed,
zoomed,
composite: Composite::new(Widget::col(vec![
Widget::row(vec![
Widget::draw_svg(ctx, "system/assets/tools/layers.svg"),
Line(&route.full_name).draw(ctx),
Btn::plaintext("X")
.build(ctx, "close", hotkey(Key::Escape))
.align_right(),
]),
format!("{} buses", bus_locations.len()).draw_text(ctx),
legend,
]))
.aligned(HorizontalAlignment::Right, VerticalAlignment::Center)
.build(ctx),
bus_locations,
}
}
}

View File

@ -25,7 +25,7 @@ use ezgui::{
pub use gameplay::{spawn_agents_around, GameplayMode, TutorialPointer, TutorialState};
use geom::{Polygon, Time};
use map_model::MapEdits;
use sim::{AgentType, VehicleType};
use sim::AgentType;
pub use speed::TimeWarpScreen;
pub use speed::{SpeedControls, TimePanel};
@ -423,12 +423,6 @@ impl ContextualActions for Actions {
actions.push((Key::E, "edit lane".to_string()));
}
}
ID::Car(c) => {
if c.1 == VehicleType::Bus || c.1 == VehicleType::Train {
// TODO Hide the button if the layer is open
actions.push((Key::R, "show route".to_string()));
}
}
ID::Building(_) => {
if app.opts.dev {
actions.push((Key::I, "explore isochrone from here".to_string()));
@ -470,15 +464,6 @@ impl ContextualActions for Actions {
Box::new(EditMode::new(ctx, app, self.gameplay.clone())),
Box::new(LaneEditor::new(ctx, app, l, self.gameplay.clone())),
),
(ID::Car(c), "show route") => {
*close_panel = false;
app.layer = Some(Box::new(crate::layer::transit::ShowTransitRoute::new(
ctx,
app,
app.primary.sim.bus_route_id(c).unwrap(),
)));
Transition::Keep
}
(ID::Building(b), "explore isochrone from here") => {
Transition::Push(IsochroneViewer::new(ctx, app, b))
}

View File

@ -1069,23 +1069,20 @@ impl Sim {
self.intersections.get_blocked_by(a)
}
pub fn location_of_buses(&self, route: BusRouteID, map: &Map) -> Vec<(CarID, Pt2D)> {
let mut results = Vec::new();
for (car, _) in self.transit.buses_for_route(route) {
// TODO This is a slow, indirect method!
results.push((
car,
self.canonical_pt_for_agent(AgentID::Car(car), map).unwrap(),
));
}
results
}
// (bus, stop index it's coming from, percent to next stop)
pub fn status_of_buses(&self, route: BusRouteID) -> Vec<(CarID, Option<usize>, f64)> {
// (bus, stop index it's coming from, percent to next stop, location)
pub fn status_of_buses(
&self,
route: BusRouteID,
map: &Map,
) -> Vec<(CarID, Option<usize>, f64, Pt2D)> {
let mut results = Vec::new();
for (bus, stop_idx) in self.transit.buses_for_route(route) {
results.push((bus, stop_idx, self.driving.percent_along_route(bus)));
results.push((
bus,
stop_idx,
self.driving.percent_along_route(bus),
self.canonical_pt_for_agent(AgentID::Car(bus), map).unwrap(),
));
}
results
}