From da6abc019b2dca7aab8e06ac604dc0213975aebf Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sun, 8 Mar 2020 11:27:00 -0700 Subject: [PATCH] overhaul one of the oldest things ever, the almighty turn cycler --- game/src/common/mod.rs | 9 - game/src/common/turn_cycler.rs | 282 +++++++++++++++++++++++-------- game/src/debug/mod.rs | 3 +- game/src/edit/traffic_signals.rs | 2 +- game/src/render/intersection.rs | 2 +- game/src/render/mod.rs | 4 +- game/src/sandbox/mod.rs | 20 +-- 7 files changed, 222 insertions(+), 100 deletions(-) diff --git a/game/src/common/mod.rs b/game/src/common/mod.rs index d6051a2969..25c3df8d10 100644 --- a/game/src/common/mod.rs +++ b/game/src/common/mod.rs @@ -18,7 +18,6 @@ pub use self::warp::Warping; use crate::app::App; use crate::game::Transition; use crate::helpers::{list_names, ID}; -use crate::render::DrawOptions; use crate::sandbox::SpeedControls; use ezgui::{ lctrl, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ScreenDims, ScreenPt, ScreenRectangle, @@ -280,14 +279,6 @@ impl CommonState { )); } - pub fn draw_options(&self, app: &App) -> DrawOptions { - let mut opts = DrawOptions::new(); - opts.suppress_traffic_signal_details = self - .turn_cycler - .suppress_traffic_signal_details(&app.primary.map); - opts - } - // Meant to be used for launching from other states pub fn launch_info_panel(&mut self, id: ID, ctx: &mut EventCtx, app: &mut App) { self.info_panel = Some(info::InfoPanel::new(id, ctx, app, Vec::new(), None)); diff --git a/game/src/common/turn_cycler.rs b/game/src/common/turn_cycler.rs index 9806d92c8f..b331e27104 100644 --- a/game/src/common/turn_cycler.rs +++ b/game/src/common/turn_cycler.rs @@ -1,41 +1,37 @@ use crate::app::{App, ShowEverything}; +use crate::colors; +use crate::common::ColorLegend; use crate::game::{DrawBaselayer, State, Transition}; use crate::helpers::ID; +use crate::managed::WrappedComposite; use crate::render::{dashed_lines, draw_signal_phase, make_signal_diagram, DrawOptions, DrawTurn}; -use ezgui::{hotkey, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Outcome}; -use geom::{Distance, Time}; -use map_model::{IntersectionID, LaneID, Map, TurnType}; +use ezgui::{ + hotkey, Button, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, + Key, Line, ManagedWidget, Outcome, Text, VerticalAlignment, +}; +use geom::{Distance, Polygon, Time}; +use map_model::{IntersectionID, LaneID, TurnType}; use sim::{AgentID, DontDrawAgents}; // TODO Misnomer. Kind of just handles temporary hovering things now. pub enum TurnCyclerState { Inactive, - ShowLane(LaneID), ShowRoute(AgentID, Time, Drawable), - CycleTurns(LaneID, usize), } impl TurnCyclerState { pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Option { match app.primary.current_selection { Some(ID::Lane(id)) if !app.primary.map.get_turns_from_lane(id).is_empty() => { - if let TurnCyclerState::CycleTurns(current, idx) = self { - if *current != id { - *self = TurnCyclerState::ShowLane(id); - } else if app - .per_obj - .action(ctx, Key::Z, "cycle through this lane's turns") - { - *self = TurnCyclerState::CycleTurns(id, *idx + 1); - } - } else { - *self = TurnCyclerState::ShowLane(id); - if app - .per_obj - .action(ctx, Key::Z, "cycle through this lane's turns") - { - *self = TurnCyclerState::CycleTurns(id, 0); - } + if app + .per_obj + .action(ctx, Key::Z, "explore turns from this lane") + { + return Some(Transition::Push(Box::new(TurnExplorer { + l: id, + idx: 0, + composite: TurnExplorer::make_panel(ctx, app, id, 0), + }))); } } Some(ID::Intersection(i)) => { @@ -92,59 +88,14 @@ impl TurnCyclerState { None } - pub fn draw(&self, g: &mut GfxCtx, app: &App) { + pub fn draw(&self, g: &mut GfxCtx, _: &App) { match self { TurnCyclerState::Inactive => {} - TurnCyclerState::ShowLane(l) => { - for turn in &app.primary.map.get_turns_from_lane(*l) { - DrawTurn::draw_full(turn, g, color_turn_type(turn.turn_type, app).alpha(0.5)); - } - } - TurnCyclerState::CycleTurns(l, idx) => { - let turns = app.primary.map.get_turns_from_lane(*l); - let current = turns[*idx % turns.len()]; - DrawTurn::draw_full(current, g, color_turn_type(current.turn_type, app)); - - let mut batch = GeomBatch::new(); - for t in app.primary.map.get_turns_in_intersection(current.id.parent) { - if current.conflicts_with(t) { - DrawTurn::draw_dashed( - t, - &mut batch, - app.cs.get_def("conflicting turn", Color::RED.alpha(0.8)), - ); - } - } - batch.draw(g); - } TurnCyclerState::ShowRoute(_, _, ref d) => { g.redraw(d); } } } - - pub fn suppress_traffic_signal_details(&self, map: &Map) -> Option { - match self { - TurnCyclerState::ShowLane(l) | TurnCyclerState::CycleTurns(l, _) => { - Some(map.get_l(*l).dst_i) - } - TurnCyclerState::ShowRoute(_, _, _) | TurnCyclerState::Inactive => None, - } - } -} - -fn color_turn_type(t: TurnType, app: &App) -> Color { - match t { - TurnType::SharedSidewalkCorner => { - app.cs.get_def("shared sidewalk corner turn", Color::BLACK) - } - TurnType::Crosswalk => app.cs.get_def("crosswalk turn", Color::WHITE), - TurnType::Straight => app.cs.get_def("straight turn", Color::BLUE), - TurnType::LaneChangeLeft => app.cs.get_def("change lanes left turn", Color::CYAN), - TurnType::LaneChangeRight => app.cs.get_def("change lanes right turn", Color::PURPLE), - TurnType::Right => app.cs.get_def("right turn", Color::GREEN), - TurnType::Left => app.cs.get_def("left turn", Color::RED), - } } struct ShowTrafficSignal { @@ -189,7 +140,7 @@ impl State for ShowTrafficSignal { fn draw(&self, g: &mut GfxCtx, app: &App) { let mut opts = DrawOptions::new(); - opts.suppress_traffic_signal_details = Some(self.i); + opts.suppress_traffic_signal_details.push(self.i); app.draw(g, opts, &DontDrawAgents {}, &ShowEverything::new()); let mut batch = GeomBatch::new(); draw_signal_phase( @@ -217,3 +168,196 @@ impl ShowTrafficSignal { } } } + +struct TurnExplorer { + l: LaneID, + // 0 means all turns, otherwise one particular turn + idx: usize, + composite: Composite, +} + +impl State for TurnExplorer { + fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition { + ctx.canvas_movement(); + + match self.composite.event(ctx) { + Some(Outcome::Clicked(x)) => match x.as_ref() { + "X" => { + return Transition::Pop; + } + "previous turn" => { + if self.idx != 0 { + self.idx -= 1; + self.composite = TurnExplorer::make_panel(ctx, app, self.l, self.idx); + } + } + "next turn" => { + if self.idx != app.primary.map.get_turns_from_lane(self.l).len() { + self.idx += 1; + self.composite = TurnExplorer::make_panel(ctx, app, self.l, self.idx); + } + } + _ => unreachable!(), + }, + None => {} + } + + Transition::Keep + } + + fn draw_baselayer(&self) -> DrawBaselayer { + DrawBaselayer::Custom + } + + fn draw(&self, g: &mut GfxCtx, app: &App) { + let mut opts = DrawOptions::new(); + { + let l = app.primary.map.get_l(self.l); + opts.suppress_traffic_signal_details.push(l.src_i); + opts.suppress_traffic_signal_details.push(l.dst_i); + } + app.draw(g, opts, &DontDrawAgents {}, &ShowEverything::new()); + + if self.idx == 0 { + for turn in &app.primary.map.get_turns_from_lane(self.l) { + DrawTurn::draw_full(turn, g, color_turn_type(turn.turn_type, app).alpha(0.5)); + } + } else { + let current = &app.primary.map.get_turns_from_lane(self.l)[self.idx - 1]; + DrawTurn::draw_full(current, g, app.cs.get_def("current turn", Color::GREEN)); + + let mut batch = GeomBatch::new(); + for t in app.primary.map.get_turns_in_intersection(current.id.parent) { + if current.conflicts_with(t) { + DrawTurn::draw_dashed( + t, + &mut batch, + app.cs.get_def("conflicting turn", Color::RED.alpha(0.8)), + ); + } + } + batch.draw(g); + } + + self.composite.draw(g); + } +} + +impl TurnExplorer { + fn make_panel(ctx: &mut EventCtx, app: &App, l: LaneID, idx: usize) -> Composite { + let num_turns = app.primary.map.get_turns_from_lane(l).len(); + + let mut col = vec![ManagedWidget::row(vec![ + ManagedWidget::draw_text( + ctx, + Text::from( + Line(format!( + "Turns from {}", + app.primary.map.get_parent(l).get_name() + )) + .size(26), + ), + ) + .margin(5), + ManagedWidget::draw_batch( + ctx, + GeomBatch::from(vec![(Color::WHITE, Polygon::rectangle(2.0, 50.0))]), + ) + .margin(5), + ManagedWidget::draw_text( + ctx, + Text::from(Line(format!("{}/{}", idx, num_turns)).size(20)), + ) + .margin(5) + .centered_vert(), + if idx == 0 { + Button::inactive_button(ctx, "<") + } else { + WrappedComposite::nice_text_button( + ctx, + Text::from(Line("<")), + hotkey(Key::LeftArrow), + "previous turn", + ) + } + .margin(5), + if idx == num_turns { + Button::inactive_button(ctx, ">") + } else { + WrappedComposite::nice_text_button( + ctx, + Text::from(Line(">")), + hotkey(Key::RightArrow), + "next turn", + ) + } + .margin(5), + WrappedComposite::text_button(ctx, "X", hotkey(Key::Escape)), + ])]; + if idx == 0 { + if app.primary.map.get_l(l).is_sidewalk() { + col.push(ColorLegend::row( + ctx, + app.cs.get("crosswalk turn"), + "crosswalk", + )); + col.push(ColorLegend::row( + ctx, + app.cs.get("shared sidewalk corner turn"), + "sidewalk connection", + )); + } else { + col.push(ColorLegend::row( + ctx, + app.cs.get("straight turn"), + "straight", + )); + col.push(ColorLegend::row( + ctx, + app.cs.get("right turn"), + "right turn", + )); + col.push(ColorLegend::row(ctx, app.cs.get("left turn"), "left turn")); + col.push(ColorLegend::row( + ctx, + app.cs.get("change lanes left turn"), + "straight, but lane-change left", + )); + col.push(ColorLegend::row( + ctx, + app.cs.get("change lanes right turn"), + "straight, but lane-change right", + )); + } + } else { + col.push(ColorLegend::row( + ctx, + app.cs.get("current turn"), + "current turn", + )); + col.push(ColorLegend::row( + ctx, + app.cs.get("conflicting turn"), + "conflicting turn", + )); + } + + Composite::new(ManagedWidget::col(col).bg(colors::PANEL_BG)) + .aligned(HorizontalAlignment::Center, VerticalAlignment::Top) + .build(ctx) + } +} + +fn color_turn_type(t: TurnType, app: &App) -> Color { + match t { + TurnType::SharedSidewalkCorner => { + app.cs.get_def("shared sidewalk corner turn", Color::BLACK) + } + TurnType::Crosswalk => app.cs.get_def("crosswalk turn", Color::WHITE), + TurnType::Straight => app.cs.get_def("straight turn", Color::BLUE), + TurnType::LaneChangeLeft => app.cs.get_def("change lanes left turn", Color::CYAN), + TurnType::LaneChangeRight => app.cs.get_def("change lanes right turn", Color::PURPLE), + TurnType::Right => app.cs.get_def("right turn", Color::GREEN), + TurnType::Left => app.cs.get_def("left turn", Color::RED), + } +} diff --git a/game/src/debug/mod.rs b/game/src/debug/mod.rs index 31ba444098..5228ebaad0 100644 --- a/game/src/debug/mod.rs +++ b/game/src/debug/mod.rs @@ -9,6 +9,7 @@ use crate::common::{tool_panel, CommonState}; use crate::game::{msg, DrawBaselayer, State, Transition, WizardState}; use crate::helpers::ID; use crate::managed::{WrappedComposite, WrappedOutcome}; +use crate::render::DrawOptions; use ezgui::{ hotkey, lctrl, Color, Composite, Drawable, EventCtx, EventLoopMode, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, ManagedWidget, Outcome, Text, VerticalAlignment, Wizard, @@ -328,7 +329,7 @@ impl State for DebugMode { } fn draw(&self, g: &mut GfxCtx, app: &App) { - let mut opts = self.common.draw_options(app); + let mut opts = DrawOptions::new(); opts.label_buildings = self.layers.show_labels; opts.label_roads = self.layers.show_labels; app.draw(g, opts, &app.primary.sim, self); diff --git a/game/src/edit/traffic_signals.rs b/game/src/edit/traffic_signals.rs index d8b81c85aa..9a5d08a76b 100644 --- a/game/src/edit/traffic_signals.rs +++ b/game/src/edit/traffic_signals.rs @@ -230,7 +230,7 @@ impl State for TrafficSignalEditor { fn draw(&self, g: &mut GfxCtx, app: &App) { { let mut opts = DrawOptions::new(); - opts.suppress_traffic_signal_details = Some(self.i); + opts.suppress_traffic_signal_details.push(self.i); app.draw(g, opts, &app.primary.sim, &ShowEverything::new()); } diff --git a/game/src/render/intersection.rs b/game/src/render/intersection.rs index beda97207e..b4bdf71ae7 100644 --- a/game/src/render/intersection.rs +++ b/game/src/render/intersection.rs @@ -124,7 +124,7 @@ impl Renderable for DrawIntersection { g.redraw(&self.draw_default); if self.intersection_type == IntersectionType::TrafficSignal - && opts.suppress_traffic_signal_details != Some(self.id) + && !opts.suppress_traffic_signal_details.contains(&self.id) { let signal = app.primary.map.get_traffic_signal(self.id); let mut maybe_redraw = self.draw_traffic_signal.borrow_mut(); diff --git a/game/src/render/mod.rs b/game/src/render/mod.rs index 1319dede6d..68b3adb210 100644 --- a/game/src/render/mod.rs +++ b/game/src/render/mod.rs @@ -91,7 +91,7 @@ pub fn dashed_lines( // DrawOptions. #[derive(Clone)] pub struct DrawOptions { - pub suppress_traffic_signal_details: Option, + pub suppress_traffic_signal_details: Vec, pub label_buildings: bool, pub label_roads: bool, } @@ -99,7 +99,7 @@ pub struct DrawOptions { impl DrawOptions { pub fn new() -> DrawOptions { DrawOptions { - suppress_traffic_signal_details: None, + suppress_traffic_signal_details: Vec::new(), label_buildings: false, label_roads: false, } diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs index 854d2a5eda..8960ad39f5 100644 --- a/game/src/sandbox/mod.rs +++ b/game/src/sandbox/mod.rs @@ -2,7 +2,7 @@ mod dashboards; mod gameplay; mod speed; -use crate::app::{App, ShowEverything}; +use crate::app::App; use crate::colors; use crate::common::{tool_panel, CommonState, Minimap, Overlays, ShowBusRoute}; use crate::debug::DebugMode; @@ -10,11 +10,11 @@ use crate::edit::{ apply_map_edits, can_edit_lane, save_edits_as, EditMode, LaneEditor, StopSignEditor, TrafficSignalEditor, }; -use crate::game::{DrawBaselayer, State, Transition, WizardState}; +use crate::game::{State, Transition, WizardState}; use crate::helpers::{cmp_duration_shorter, ID}; use crate::managed::{WrappedComposite, WrappedOutcome}; use crate::pregame::main_menu; -use crate::render::{AgentColorScheme, DrawOptions}; +use crate::render::AgentColorScheme; pub use crate::sandbox::gameplay::{TutorialPointer, TutorialState}; use ezgui::{ hotkey, lctrl, Choice, Color, Composite, EventCtx, EventLoopMode, GeomBatch, GfxCtx, @@ -266,21 +266,7 @@ impl State for SandboxMode { } } - fn draw_baselayer(&self) -> DrawBaselayer { - DrawBaselayer::Custom - } - fn draw(&self, g: &mut GfxCtx, app: &App) { - app.draw( - g, - self.controls - .common - .as_ref() - .map(|c| c.draw_options(app)) - .unwrap_or_else(DrawOptions::new), - &app.primary.sim, - &ShowEverything::new(), - ); app.overlay.draw(g); if let Some(ref c) = self.controls.common {