overhaul one of the oldest things ever, the almighty turn cycler

This commit is contained in:
Dustin Carlino 2020-03-08 11:27:00 -07:00
parent 6e8414c088
commit da6abc019b
7 changed files with 222 additions and 100 deletions

View File

@ -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));

View File

@ -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<Transition> {
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<IntersectionID> {
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),
}
}

View File

@ -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);

View File

@ -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());
}

View File

@ -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();

View File

@ -91,7 +91,7 @@ pub fn dashed_lines(
// DrawOptions.
#[derive(Clone)]
pub struct DrawOptions {
pub suppress_traffic_signal_details: Option<IntersectionID>,
pub suppress_traffic_signal_details: Vec<IntersectionID>,
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,
}

View File

@ -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 {