From 3f2a7d6e68dc1583b2e4300906432a34843d2b13 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 14 Dec 2018 11:44:06 -0800 Subject: [PATCH] display when a traffic signal is in overtime --- docs/design/intersections.md | 5 ++ editor/src/init_colors.rs | 1 + editor/src/plugins/view/turn_cycler.rs | 77 ++++++++++++++++---------- ezgui/src/color.rs | 1 + ezgui/src/lib.rs | 2 + sim/src/intersections.rs | 12 ++++ sim/src/sim.rs | 4 ++ 7 files changed, 73 insertions(+), 29 deletions(-) diff --git a/docs/design/intersections.md b/docs/design/intersections.md index 471df77e98..3f47bccba0 100644 --- a/docs/design/intersections.md +++ b/docs/design/intersections.md @@ -157,3 +157,8 @@ Shelby and 23rd... - need to fix up IDs everywhere - ... rest of map construction proceeds + +## Live editing + +Traffic signal changes can happen live, because weird changes will just look +like brief blips of overtime. Same for stop signs, I think... diff --git a/editor/src/init_colors.rs b/editor/src/init_colors.rs index 6250b0cf4f..2bee7c0e07 100644 --- a/editor/src/init_colors.rs +++ b/editor/src/init_colors.rs @@ -98,6 +98,7 @@ pub fn default_colors() -> HashMap { m.insert("sidewalk corner".to_string(), Color::grey(0.7)); m.insert("sidewalk lines".to_string(), Color::grey(0.7)); m.insert("signal editor panel".to_string(), Color::BLACK.alpha(0.95)); + m.insert("signal overtime timer".to_string(), Color::PINK); m.insert( "something associated with something else".to_string(), Color::PURPLE, diff --git a/editor/src/plugins/view/turn_cycler.rs b/editor/src/plugins/view/turn_cycler.rs index d723496553..fb51781d6a 100644 --- a/editor/src/plugins/view/turn_cycler.rs +++ b/editor/src/plugins/view/turn_cycler.rs @@ -1,7 +1,7 @@ use crate::objects::{Ctx, ID}; use crate::plugins::{Plugin, PluginCtx}; use crate::render::{draw_signal_cycle, draw_stop_sign, stop_sign_rendering_hints, DrawTurn}; -use ezgui::{Color, GfxCtx}; +use ezgui::{Color, GfxCtx, Text}; use geom::{Polygon, Pt2D}; use map_model::{IntersectionID, LaneID, TurnType}; use piston::input::Key; @@ -34,12 +34,14 @@ impl Plugin for TurnCyclerState { self.state = State::ShowIntersection(id); if let Some(signal) = ctx.primary.map.maybe_get_traffic_signal(id) { - let (cycle, _) = - signal.current_cycle_and_remaining_time(ctx.primary.sim.time.as_time()); ctx.hints.suppress_intersection_icon = Some(id); - ctx.hints - .hide_crosswalks - .extend(cycle.get_absent_crosswalks(&ctx.primary.map)); + if !ctx.primary.sim.is_in_overtime(id) { + let (cycle, _) = + signal.current_cycle_and_remaining_time(ctx.primary.sim.time.as_time()); + ctx.hints + .hide_crosswalks + .extend(cycle.get_absent_crosswalks(&ctx.primary.map)); + } } else if let Some(sign) = ctx.primary.map.maybe_get_stop_sign(id) { stop_sign_rendering_hints(&mut ctx.hints, sign, &ctx.primary.map, ctx.cs); } @@ -100,37 +102,54 @@ impl Plugin for TurnCyclerState { } State::ShowIntersection(id) => { if let Some(signal) = ctx.map.maybe_get_traffic_signal(id) { - // TODO Cycle might be over-run; should depict that by asking sim layer. - 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, - &ctx.hints.hide_crosswalks, - ); - - // Draw a little timer box in the top-left corner of the screen. - { + if ctx.sim.is_in_overtime(id) { let old_ctx = g.fork_screenspace(); let width = 50.0; let height = 100.0; g.draw_polygon( - ctx.cs.get_def("timer foreground", Color::RED), + ctx.cs.get_def("signal overtime timer", Color::PINK), &Polygon::rectangle_topleft(Pt2D::new(10.0, 10.0), width, height), ); - g.draw_polygon( - ctx.cs.get_def("timer background", Color::BLACK), - &Polygon::rectangle_topleft( - Pt2D::new(10.0, 10.0), - width, - (time_left / cycle.duration).value_unsafe * height, - ), + // TODO We can't use draw_text_at, because canvas doesn't know about forked + // contexts. + ctx.canvas.draw_text_at_screenspace_topleft( + g, + Text::from_line("Overtime!".to_string()), + (10.0 + width / 2.0, 10.0 + height / 2.0), ); g.unfork(old_ctx); + } else { + 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, + &ctx.hints.hide_crosswalks, + ); + + // Draw a little timer box in the top-left corner of the screen. + { + let old_ctx = g.fork_screenspace(); + let width = 50.0; + let height = 100.0; + g.draw_polygon( + ctx.cs.get_def("timer foreground", Color::RED), + &Polygon::rectangle_topleft(Pt2D::new(10.0, 10.0), width, height), + ); + g.draw_polygon( + ctx.cs.get_def("timer background", Color::BLACK), + &Polygon::rectangle_topleft( + Pt2D::new(10.0, 10.0), + width, + (time_left / cycle.duration).value_unsafe * height, + ), + ); + g.unfork(old_ctx); + } } } else if let Some(sign) = ctx.map.maybe_get_stop_sign(id) { draw_stop_sign(sign, g, ctx.cs, ctx.map); diff --git a/ezgui/src/color.rs b/ezgui/src/color.rs index b72fa23641..34055cb440 100644 --- a/ezgui/src/color.rs +++ b/ezgui/src/color.rs @@ -26,6 +26,7 @@ impl Color { pub const CYAN: Color = Color([0.0, 1.0, 1.0, 1.0]); pub const YELLOW: Color = Color([1.0, 1.0, 0.0, 1.0]); pub const PURPLE: Color = Color([0.5, 0.0, 0.5, 1.0]); + pub const PINK: Color = Color([1.0, 0.41, 0.71, 1.0]); // TODO should assert stuff about the inputs diff --git a/ezgui/src/lib.rs b/ezgui/src/lib.rs index a4dc1f34dd..abe4c13f10 100644 --- a/ezgui/src/lib.rs +++ b/ezgui/src/lib.rs @@ -53,6 +53,8 @@ impl<'a> GfxCtx<'a> { } // Up to the caller to call unfork()! + // TODO Canvas doesn't understand this change, so things like text drawing that use + // map_to_screen will just be confusing. pub fn fork(&mut self, top_left: Pt2D, zoom: f64) -> graphics::Context { mem::replace( &mut self.ctx, diff --git a/sim/src/intersections.rs b/sim/src/intersections.rs index 760d1b307c..bf6e8e31d9 100644 --- a/sim/src/intersections.rs +++ b/sim/src/intersections.rs @@ -166,6 +166,14 @@ impl IntersectionSimState { IntersectionPolicy::Border => HashSet::new(), } } + + pub fn is_in_overtime(&self, id: IntersectionID) -> bool { + match self.intersections[id.0] { + IntersectionPolicy::StopSign(_) => unreachable!(), + IntersectionPolicy::TrafficSignal(ref p) => p.overtime, + IntersectionPolicy::Border => unreachable!(), + } + } } // Use an enum instead of traits so that serialization works. I couldn't figure out erased_serde. @@ -307,6 +315,7 @@ struct TrafficSignal { id: IntersectionID, accepted: BTreeSet, requests: BTreeSet, + overtime: bool, debug: bool, } @@ -316,6 +325,7 @@ impl TrafficSignal { id, accepted: BTreeSet::new(), requests: BTreeSet::new(), + overtime: false, debug: false, } } @@ -334,9 +344,11 @@ impl TrafficSignal { req.agent, req.turn ); } + self.overtime = true; return; } } + self.overtime = false; let priority_requests: BTreeSet = self .requests diff --git a/sim/src/sim.rs b/sim/src/sim.rs index 4991279dc6..558e69c426 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -415,6 +415,10 @@ impl Sim { self.intersection_state.get_accepted_agents(id) } + pub fn is_in_overtime(&self, id: IntersectionID) -> bool { + self.intersection_state.is_in_overtime(id) + } + pub fn get_stats(&self) -> &SimStats { &self.stats }