mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 22:13:27 +03:00
move intersection demand layer to info panel
This commit is contained in:
parent
977d78775d
commit
2120035d26
@ -88,7 +88,7 @@ impl State for RouteSelect {
|
||||
let roads = if let Some(roads) = pathfind(&app.primary.map, i1, i2) {
|
||||
for r in &roads {
|
||||
batch.push(
|
||||
Color::RED,
|
||||
Color::RED.alpha(0.5),
|
||||
app.primary
|
||||
.map
|
||||
.get_r(*r)
|
||||
@ -219,7 +219,7 @@ impl BulkEdit {
|
||||
.align_right(),
|
||||
])
|
||||
.margin_below(5),
|
||||
Btn::text_fg("Quit").build_def(ctx, None),
|
||||
Btn::text_fg("Quit").build_def(ctx, hotkey(Key::Escape)),
|
||||
])
|
||||
.bg(app.cs.panel_bg)
|
||||
.padding(10),
|
||||
@ -374,7 +374,7 @@ impl State for PaintSelect {
|
||||
let mut batch = GeomBatch::new();
|
||||
for r in &self.roads {
|
||||
batch.push(
|
||||
Color::BLUE,
|
||||
Color::BLUE.alpha(0.5),
|
||||
app.primary
|
||||
.map
|
||||
.get_r(*r)
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::app::App;
|
||||
use crate::info::{header_btns, make_tabs, throughput, DataOptions, Details, Tab};
|
||||
use abstutil::prettyprint_usize;
|
||||
use ezgui::{EventCtx, Line, LinePlot, PlotOptions, Series, Text, Widget};
|
||||
use ezgui::{
|
||||
Color, EventCtx, GeomBatch, Line, LinePlot, PlotOptions, RewriteColor, Series, Text, Widget,
|
||||
};
|
||||
use geom::{Angle, ArrowCap, Distance, PolyLine};
|
||||
use geom::{Duration, Statistic, Time};
|
||||
use map_model::{IntersectionID, IntersectionType};
|
||||
use sim::Analytics;
|
||||
@ -96,6 +99,71 @@ pub fn delay(
|
||||
rows
|
||||
}
|
||||
|
||||
pub fn current_demand(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
details: &mut Details,
|
||||
id: IntersectionID,
|
||||
) -> Vec<Widget> {
|
||||
let mut rows = header(ctx, app, details, id, Tab::IntersectionDemand(id));
|
||||
|
||||
let mut total_demand = 0;
|
||||
let mut demand_per_group: Vec<(&PolyLine, usize)> = Vec::new();
|
||||
for g in app.primary.map.get_traffic_signal(id).turn_groups.values() {
|
||||
let demand = app
|
||||
.primary
|
||||
.sim
|
||||
.get_analytics()
|
||||
.demand
|
||||
.get(&g.id)
|
||||
.cloned()
|
||||
.unwrap_or(0);
|
||||
if demand > 0 {
|
||||
total_demand += demand;
|
||||
demand_per_group.push((&g.geom, demand));
|
||||
}
|
||||
}
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
let polygon = app.primary.map.get_i(id).polygon.clone();
|
||||
let bounds = polygon.get_bounds();
|
||||
// Pick a zoom so that we fit a fixed width in pixels
|
||||
let zoom = 300.0 / bounds.width();
|
||||
batch.push(app.cs.normal_intersection, polygon);
|
||||
|
||||
for (pl, demand) in demand_per_group {
|
||||
let percent = (demand as f64) / (total_demand as f64);
|
||||
batch.push(
|
||||
Color::RED,
|
||||
pl.make_arrow(percent * Distance::meters(3.0), ArrowCap::Triangle)
|
||||
.unwrap(),
|
||||
);
|
||||
batch.add_transformed(
|
||||
Text::from(Line(prettyprint_usize(demand))).render_ctx(ctx),
|
||||
pl.middle(),
|
||||
0.15,
|
||||
Angle::ZERO,
|
||||
RewriteColor::NoOp,
|
||||
);
|
||||
}
|
||||
let mut transformed_batch = GeomBatch::new();
|
||||
for (color, poly) in batch.consume() {
|
||||
transformed_batch.fancy_push(
|
||||
color,
|
||||
poly.translate(-bounds.min_x, -bounds.min_y).scale(zoom),
|
||||
);
|
||||
}
|
||||
|
||||
let mut txt = Text::from(Line(format!(
|
||||
"How many active trips will cross this intersection?"
|
||||
)));
|
||||
txt.add(Line(format!("Total: {}", prettyprint_usize(total_demand))).secondary());
|
||||
rows.push(txt.draw(ctx));
|
||||
rows.push(Widget::draw_batch(ctx, transformed_batch));
|
||||
|
||||
rows
|
||||
}
|
||||
|
||||
fn delay_plot(ctx: &EventCtx, app: &App, i: IntersectionID, opts: &DataOptions) -> Widget {
|
||||
let get_data = |a: &Analytics, t: Time| {
|
||||
let mut series: Vec<(Statistic, Vec<(Time, Duration)>)> = Statistic::all()
|
||||
@ -173,6 +241,7 @@ fn header(
|
||||
];
|
||||
if i.is_traffic_signal() {
|
||||
tabs.push(("Delay", Tab::IntersectionDelay(id, DataOptions::new(app))));
|
||||
tabs.push(("Current demand", Tab::IntersectionDemand(id)));
|
||||
}
|
||||
tabs
|
||||
}));
|
||||
|
@ -67,6 +67,7 @@ pub enum Tab {
|
||||
IntersectionInfo(IntersectionID),
|
||||
IntersectionTraffic(IntersectionID, DataOptions),
|
||||
IntersectionDelay(IntersectionID, DataOptions),
|
||||
IntersectionDemand(IntersectionID),
|
||||
|
||||
LaneInfo(LaneID),
|
||||
LaneDebug(LaneID),
|
||||
@ -134,7 +135,8 @@ impl Tab {
|
||||
Tab::Area(a) => Some(ID::Area(a)),
|
||||
Tab::IntersectionInfo(i)
|
||||
| Tab::IntersectionTraffic(i, _)
|
||||
| Tab::IntersectionDelay(i, _) => Some(ID::Intersection(i)),
|
||||
| Tab::IntersectionDelay(i, _)
|
||||
| Tab::IntersectionDemand(i) => Some(ID::Intersection(i)),
|
||||
Tab::LaneInfo(l) | Tab::LaneDebug(l) | Tab::LaneTraffic(l, _) => Some(ID::Lane(l)),
|
||||
}
|
||||
}
|
||||
@ -209,6 +211,10 @@ impl InfoPanel {
|
||||
Tab::IntersectionDelay(i, ref opts) => {
|
||||
(intersection::delay(ctx, app, &mut details, i, opts), false)
|
||||
}
|
||||
Tab::IntersectionDemand(i) => (
|
||||
intersection::current_demand(ctx, app, &mut details, i),
|
||||
false,
|
||||
),
|
||||
Tab::LaneInfo(l) => (lane::info(ctx, app, &mut details, l), true),
|
||||
Tab::LaneDebug(l) => (lane::debug(ctx, app, &mut details, l), false),
|
||||
Tab::LaneTraffic(l, ref opts) => {
|
||||
|
@ -28,9 +28,9 @@ pub trait Layer {
|
||||
fn draw_minimap(&self, g: &mut GfxCtx);
|
||||
}
|
||||
|
||||
// TODO Just return a bool for closed? Less readable...
|
||||
pub enum LayerOutcome {
|
||||
Close,
|
||||
Transition(Transition),
|
||||
}
|
||||
|
||||
pub struct PickLayer {
|
||||
@ -50,10 +50,6 @@ impl PickLayer {
|
||||
app.layer = None;
|
||||
return None;
|
||||
}
|
||||
Some(LayerOutcome::Transition(t)) => {
|
||||
app.layer = Some(layer);
|
||||
return Some(t);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
app.layer = Some(layer);
|
||||
|
@ -1,16 +1,13 @@
|
||||
use crate::app::App;
|
||||
use crate::common::{ColorLegend, Colorer, Warping};
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::ID;
|
||||
use crate::common::{ColorLegend, Colorer};
|
||||
use crate::layer::{Layer, LayerOutcome};
|
||||
use abstutil::{prettyprint_usize, Counter};
|
||||
use abstutil::Counter;
|
||||
use ezgui::{
|
||||
hotkey, Btn, Checkbox, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, Outcome, RewriteColor, Text, TextExt, VerticalAlignment,
|
||||
Widget,
|
||||
hotkey, Btn, Checkbox, Color, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key,
|
||||
Outcome, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
use geom::{Angle, ArrowCap, Distance, Duration, PolyLine, Time};
|
||||
use map_model::{IntersectionID, Traversable};
|
||||
use geom::{Duration, Time};
|
||||
use map_model::Traversable;
|
||||
|
||||
pub struct Dynamic {
|
||||
time: Time,
|
||||
@ -465,113 +462,3 @@ impl Throughput {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Does this make sense as a layer? Should it be an info panel tab?
|
||||
pub struct IntersectionDemand {
|
||||
time: Time,
|
||||
i: IntersectionID,
|
||||
draw: Drawable,
|
||||
composite: Composite,
|
||||
}
|
||||
|
||||
impl Layer for IntersectionDemand {
|
||||
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 = IntersectionDemand::new(ctx, app, self.i);
|
||||
}
|
||||
|
||||
self.composite.align_above(ctx, minimap);
|
||||
match self.composite.event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"intersection demand" => {
|
||||
let id = ID::Intersection(self.i);
|
||||
return Some(LayerOutcome::Transition(Transition::Push(Warping::new(
|
||||
ctx,
|
||||
id.canonical_point(&app.primary).unwrap(),
|
||||
Some(10.0),
|
||||
Some(id.clone()),
|
||||
&mut app.primary,
|
||||
))));
|
||||
}
|
||||
"X" => {
|
||||
return Some(LayerOutcome::Close);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||
self.composite.draw(g);
|
||||
g.redraw(&self.draw);
|
||||
}
|
||||
fn draw_minimap(&self, _: &mut GfxCtx) {}
|
||||
}
|
||||
|
||||
impl IntersectionDemand {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, i: IntersectionID) -> IntersectionDemand {
|
||||
let mut batch = GeomBatch::new();
|
||||
|
||||
let mut total_demand = 0;
|
||||
let mut demand_per_group: Vec<(&PolyLine, usize)> = Vec::new();
|
||||
for g in app.primary.map.get_traffic_signal(i).turn_groups.values() {
|
||||
let demand = app
|
||||
.primary
|
||||
.sim
|
||||
.get_analytics()
|
||||
.demand
|
||||
.get(&g.id)
|
||||
.cloned()
|
||||
.unwrap_or(0);
|
||||
if demand > 0 {
|
||||
total_demand += demand;
|
||||
demand_per_group.push((&g.geom, demand));
|
||||
}
|
||||
}
|
||||
|
||||
for (pl, demand) in demand_per_group {
|
||||
let percent = (demand as f64) / (total_demand as f64);
|
||||
batch.push(
|
||||
Color::RED,
|
||||
pl.make_arrow(percent * Distance::meters(5.0), ArrowCap::Triangle)
|
||||
.unwrap(),
|
||||
);
|
||||
batch.add_transformed(
|
||||
Text::from(Line(prettyprint_usize(demand))).render_ctx(ctx),
|
||||
pl.middle(),
|
||||
0.08,
|
||||
Angle::ZERO,
|
||||
RewriteColor::NoOp,
|
||||
);
|
||||
}
|
||||
|
||||
let col = vec![
|
||||
Widget::row(vec![
|
||||
"intersection demand".draw_text(ctx),
|
||||
Btn::svg_def("../data/system/assets/tools/location.svg")
|
||||
.build(ctx, "intersection demand", None)
|
||||
.margin(5),
|
||||
Btn::text_fg("X").build_def(ctx, None).align_right(),
|
||||
]),
|
||||
ColorLegend::row(ctx, Color::RED, "current demand"),
|
||||
];
|
||||
|
||||
IntersectionDemand {
|
||||
time: app.primary.sim.time(),
|
||||
i,
|
||||
draw: batch.upload(ctx),
|
||||
composite: Composite::new(Widget::col(col).bg(app.cs.panel_bg))
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Center)
|
||||
.build(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -383,7 +383,6 @@ impl ContextualActions for Actions {
|
||||
ID::Intersection(i) => {
|
||||
if app.primary.map.get_i(i).is_traffic_signal() {
|
||||
actions.push((Key::F, "explore traffic signal details".to_string()));
|
||||
actions.push((Key::C, "show current demand".to_string()));
|
||||
actions.push((Key::E, "edit traffic signal".to_string()));
|
||||
}
|
||||
if app.primary.map.get_i(i).is_stop_sign() {
|
||||
@ -423,12 +422,6 @@ impl ContextualActions for Actions {
|
||||
(ID::Intersection(i), "explore traffic signal details") => {
|
||||
Transition::Push(ShowTrafficSignal::new(ctx, app, i))
|
||||
}
|
||||
(ID::Intersection(i), "show current demand") => {
|
||||
app.layer = Some(Box::new(crate::layer::traffic::IntersectionDemand::new(
|
||||
ctx, app, i,
|
||||
)));
|
||||
Transition::Keep
|
||||
}
|
||||
(ID::Intersection(i), "edit traffic signal") => Transition::PushTwice(
|
||||
Box::new(EditMode::new(ctx, app, self.gameplay.clone())),
|
||||
Box::new(TrafficSignalEditor::new(i, ctx, app)),
|
||||
|
Loading…
Reference in New Issue
Block a user