show entire signal diagram when hovering

This commit is contained in:
Dustin Carlino 2018-12-23 09:36:49 -08:00
parent e05d4be34b
commit ae6f8d5e16
5 changed files with 112 additions and 106 deletions

View File

@ -1,9 +1,8 @@
use crate::objects::{Ctx, ID};
use crate::plugins::{Plugin, PluginCtx};
use crate::render::{draw_signal_cycle, DrawTurn};
use crate::render::{draw_signal_cycle, draw_signal_diagram, DrawTurn};
use dimensioned::si;
use ezgui::{Color, GfxCtx, Key, ScreenPt, Text, Wizard, WrappedWizard, TOP_MENU_HEIGHT};
use geom::{Bounds, Polygon, Pt2D};
use ezgui::{Color, GfxCtx, Key, Wizard, WrappedWizard};
use map_model::{ControlTrafficSignal, Cycle, IntersectionID, Map, TurnID, TurnPriority, TurnType};
// TODO Warn if there are empty cycles or if some turn is completely absent from the signal.
@ -190,10 +189,12 @@ impl Plugin for TrafficSignalEditor {
}
ctx.primary.map.edit_traffic_signal(signal);
} else if input.modal_action("add a new empty cycle") {
signal.cycles.insert(self.current_cycle, Cycle::new(self.i));
signal
.cycles
.insert(self.current_cycle, Cycle::new(self.i, signal.cycles.len()));
ctx.primary.map.edit_traffic_signal(signal);
} else if has_sidewalks && input.modal_action("add a new pedestrian scramble cycle") {
let mut cycle = Cycle::new(self.i);
let mut cycle = Cycle::new(self.i, signal.cycles.len());
for t in ctx.primary.map.get_turns_in_intersection(self.i) {
// edit_turn adds the other_crosswalk_id and asserts no duplicates.
if t.turn_type == TurnType::SharedSidewalkCorner
@ -212,80 +213,9 @@ impl Plugin for TrafficSignalEditor {
fn draw(&self, g: &mut GfxCtx, ctx: &Ctx) {
let cycles = &ctx.map.get_traffic_signal(self.i).cycles;
draw_signal_cycle(
&cycles[self.current_cycle],
g,
ctx.cs,
ctx.map,
ctx.draw_map,
);
draw_signal_cycle(&cycles[self.current_cycle], g, ctx);
// Draw all of the cycles off to the side
let padding = 5.0;
let zoom = 10.0;
let (top_left, width, height) = {
let mut b = Bounds::new();
for pt in &ctx.map.get_i(self.i).polygon {
b.update(*pt);
}
(
Pt2D::new(b.min_x, b.min_y),
b.max_x - b.min_x,
// Vertically pad
b.max_y - b.min_y,
)
};
let old_ctx = g.fork_screenspace();
g.draw_polygon(
ctx.cs
.get_def("signal editor panel", Color::BLACK.alpha(0.95)),
&Polygon::rectangle_topleft(
Pt2D::new(10.0, TOP_MENU_HEIGHT + 10.0),
1.0 * width * zoom,
(padding + height) * (cycles.len() as f64) * zoom,
),
);
// TODO Padding and offsets all a bit off. Abstractions are a bit awkward. Want to
// center a map-space thing inside a screen-space box.
g.draw_polygon(
ctx.cs.get_def(
"current cycle in signal editor panel",
Color::BLUE.alpha(0.95),
),
&Polygon::rectangle_topleft(
Pt2D::new(
10.0,
10.0 + TOP_MENU_HEIGHT
+ (padding + height) * (self.current_cycle as f64) * zoom,
),
width * zoom,
(padding + height) * zoom,
),
);
for (idx, cycle) in cycles.iter().enumerate() {
g.fork(
// TODO Apply the offset here too?
Pt2D::new(
top_left.x(),
top_left.y() - height * (idx as f64) - padding * ((idx as f64) + 1.0),
),
zoom,
);
draw_signal_cycle(&cycle, g, ctx.cs, ctx.map, ctx.draw_map);
ctx.canvas.draw_text_at_screenspace_topleft(
g,
Text::from_line(format!("Cycle {}: {}", idx + 1, cycle.duration)),
ScreenPt::new(
10.0 + (width * zoom),
10.0 + TOP_MENU_HEIGHT + (padding + height) * (idx as f64) * zoom,
),
);
}
g.unfork(old_ctx);
draw_signal_diagram(self.i, self.current_cycle, g, ctx);
if let Some(id) = self.icon_selected {
// TODO What should we do for currently banned turns?

View File

@ -1,13 +1,14 @@
use crate::objects::{Ctx, ID};
use crate::plugins::{Plugin, PluginCtx};
use crate::render::DrawTurn;
use crate::render::{draw_signal_diagram, DrawTurn};
use ezgui::{Color, GfxCtx, Key};
use map_model::{LaneID, TurnType};
use map_model::{IntersectionID, LaneID, TurnType};
pub enum TurnCyclerState {
Inactive,
ShowLane(LaneID),
CycleTurns(LaneID, usize),
ShowIntersection(IntersectionID),
}
impl TurnCyclerState {
@ -41,6 +42,9 @@ impl Plugin for TurnCyclerState {
ctx.hints.suppress_traffic_signal_details = Some(ctx.primary.map.get_l(id).dst_i);
}
Some(ID::Intersection(id)) => {
*self = TurnCyclerState::ShowIntersection(id);
}
_ => {
*self = TurnCyclerState::Inactive;
}
@ -62,6 +66,14 @@ impl Plugin for TurnCyclerState {
DrawTurn::draw_full(t, g, color_turn_type(t.turn_type, ctx));
}
}
TurnCyclerState::ShowIntersection(i) => {
// TODO depict overtime, time left, etc
let (cycle, _) = ctx
.map
.get_traffic_signal(*i)
.current_cycle_and_remaining_time(ctx.sim.time.as_time());
draw_signal_diagram(*i, cycle.idx, g, ctx);
}
}
}
}

View File

@ -1,9 +1,6 @@
use crate::colors::ColorScheme;
use crate::objects::{Ctx, ID};
use crate::render::{
DrawCrosswalk, DrawMap, DrawTurn, RenderOptions, Renderable, MIN_ZOOM_FOR_MARKINGS,
};
use ezgui::{Color, GfxCtx, Text};
use crate::render::{DrawCrosswalk, DrawTurn, RenderOptions, Renderable, MIN_ZOOM_FOR_MARKINGS};
use ezgui::{Color, GfxCtx, ScreenPt, Text, TOP_MENU_HEIGHT};
use geom::{Bounds, Polygon, Pt2D};
use map_model::{
Cycle, Intersection, IntersectionID, IntersectionType, Map, TurnPriority, TurnType,
@ -55,7 +52,7 @@ impl DrawIntersection {
let (cycle, time_left) =
signal.current_cycle_and_remaining_time(ctx.sim.time.as_time());
draw_signal_cycle(cycle, g, ctx.cs, ctx.map, ctx.draw_map);
draw_signal_cycle(cycle, g, ctx);
// Draw a little timer box in the middle of the intersection.
g.draw_polygon(
@ -155,34 +152,99 @@ fn calculate_corners(i: IntersectionID, map: &Map) -> Vec<Polygon> {
corners
}
pub fn draw_signal_cycle(
cycle: &Cycle,
g: &mut GfxCtx,
cs: &ColorScheme,
map: &Map,
draw_map: &DrawMap,
) {
let priority_color = cs.get_def("turns protected by traffic signal right now", Color::GREEN);
let yield_color = cs.get_def(
pub fn draw_signal_cycle(cycle: &Cycle, g: &mut GfxCtx, ctx: &Ctx) {
let priority_color = ctx
.cs
.get_def("turns protected by traffic signal right now", Color::GREEN);
let yield_color = ctx.cs.get_def(
"turns allowed with yielding by traffic signal right now",
Color::rgba(255, 105, 180, 0.8),
);
for crosswalk in &draw_map.get_i(cycle.parent).crosswalks {
for crosswalk in &ctx.draw_map.get_i(cycle.parent).crosswalks {
if cycle.get_priority(crosswalk.id1) == TurnPriority::Priority {
crosswalk.draw(g, cs.get("crosswalk"));
crosswalk.draw(g, ctx.cs.get("crosswalk"));
}
}
for t in &cycle.priority_turns {
let turn = map.get_t(*t);
let turn = ctx.map.get_t(*t);
if !turn.between_sidewalks() {
DrawTurn::draw_full(turn, g, priority_color);
}
}
for t in &cycle.yield_turns {
let turn = map.get_t(*t);
let turn = ctx.map.get_t(*t);
if !turn.between_sidewalks() {
DrawTurn::draw_dashed(turn, g, yield_color);
}
}
}
pub fn draw_signal_diagram(i: IntersectionID, current_cycle: usize, g: &mut GfxCtx, ctx: &Ctx) {
// Draw all of the cycles off to the side
let padding = 5.0;
let zoom = 10.0;
let (top_left, width, height) = {
let mut b = Bounds::new();
for pt in &ctx.map.get_i(i).polygon {
b.update(*pt);
}
(
Pt2D::new(b.min_x, b.min_y),
b.max_x - b.min_x,
// Vertically pad
b.max_y - b.min_y,
)
};
let cycles = &ctx.map.get_traffic_signal(i).cycles;
let old_ctx = g.fork_screenspace();
g.draw_polygon(
ctx.cs
.get_def("signal editor panel", Color::BLACK.alpha(0.95)),
&Polygon::rectangle_topleft(
Pt2D::new(10.0, TOP_MENU_HEIGHT + 10.0),
1.0 * width * zoom,
(padding + height) * (cycles.len() as f64) * zoom,
),
);
// TODO Padding and offsets all a bit off. Abstractions are a bit awkward. Want to
// center a map-space thing inside a screen-space box.
g.draw_polygon(
ctx.cs.get_def(
"current cycle in signal editor panel",
Color::BLUE.alpha(0.95),
),
&Polygon::rectangle_topleft(
Pt2D::new(
10.0,
10.0 + TOP_MENU_HEIGHT + (padding + height) * (current_cycle as f64) * zoom,
),
width * zoom,
(padding + height) * zoom,
),
);
for (idx, cycle) in cycles.iter().enumerate() {
g.fork(
// TODO Apply the offset here too?
Pt2D::new(
top_left.x(),
top_left.y() - height * (idx as f64) - padding * ((idx as f64) + 1.0),
),
zoom,
);
draw_signal_cycle(&cycle, g, ctx);
ctx.canvas.draw_text_at_screenspace_topleft(
g,
Text::from_line(format!("Cycle {}: {}", idx + 1, cycle.duration)),
ScreenPt::new(
10.0 + (width * zoom),
10.0 + TOP_MENU_HEIGHT + (padding + height) * (idx as f64) * zoom,
),
);
}
g.unfork(old_ctx);
}

View File

@ -16,7 +16,7 @@ pub use crate::render::area::DrawArea;
use crate::render::bike::DrawBike;
use crate::render::car::DrawCar;
pub use crate::render::extra_shape::ExtraShapeID;
pub use crate::render::intersection::draw_signal_cycle;
pub use crate::render::intersection::{draw_signal_cycle, draw_signal_diagram};
pub use crate::render::lane::DrawLane;
pub use crate::render::map::DrawMap;
pub use crate::render::pedestrian::DrawPedestrian;

View File

@ -103,7 +103,7 @@ impl ControlTrafficSignal {
.iter()
.map(|t| t.id)
.collect();
let mut current_cycle = Cycle::new(intersection);
let mut current_cycle = Cycle::new(intersection, cycles.len());
loop {
let add_turn = remaining_turns
.iter()
@ -115,8 +115,8 @@ impl ControlTrafficSignal {
.insert(remaining_turns.remove(idx));
}
None => {
cycles.push(current_cycle.clone());
current_cycle.priority_turns.clear();
cycles.push(current_cycle);
current_cycle = Cycle::new(intersection, cycles.len());
if remaining_turns.is_empty() {
break;
}
@ -305,15 +305,17 @@ impl ControlTrafficSignal {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Cycle {
pub parent: IntersectionID,
pub idx: usize,
pub priority_turns: BTreeSet<TurnID>,
pub yield_turns: BTreeSet<TurnID>,
pub duration: si::Second<f64>,
}
impl Cycle {
pub fn new(parent: IntersectionID) -> Cycle {
pub fn new(parent: IntersectionID, idx: usize) -> Cycle {
Cycle {
parent,
idx,
priority_turns: BTreeSet::new(),
yield_turns: BTreeSet::new(),
duration: CYCLE_DURATION,
@ -409,8 +411,8 @@ fn make_cycles(
) -> Vec<Cycle> {
let mut cycles: Vec<Cycle> = Vec::new();
for specs in cycle_specs.into_iter() {
let mut cycle = Cycle::new(i);
for (idx, specs) in cycle_specs.into_iter().enumerate() {
let mut cycle = Cycle::new(i, idx);
for (roads, turn_type, protected) in specs.into_iter() {
for turn in map.get_turns_in_intersection(i) {