hiding crosswalks way more sanely

This commit is contained in:
Dustin Carlino 2018-12-04 10:31:47 -08:00
parent d4006d3e93
commit 2c7abcbc3a
6 changed files with 89 additions and 76 deletions

View File

@ -4,6 +4,7 @@ use geom::Pt2D;
use map_model::{AreaID, BuildingID, BusStopID, IntersectionID, LaneID, Map, ParcelID, TurnID};
use render::{DrawMap, ExtraShapeID};
use sim::{AgentID, CarID, GetDrawAgents, PedestrianID, Sim, TripID};
use std::collections::HashSet;
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug, PartialOrd, Ord)]
pub enum ID {
@ -103,6 +104,7 @@ pub struct RenderingHints {
// Miscellaneous cases where a plugin needs to control rendering.
pub suppress_traffic_signal_icon: Option<IntersectionID>,
pub hide_crosswalks: HashSet<TurnID>,
}
// For plugins and rendering. Not sure what module this should live in, here seems fine.
@ -113,8 +115,6 @@ pub struct Ctx<'a> {
pub canvas: &'a Canvas,
pub sim: &'a Sim,
pub hints: &'a RenderingHints,
// TODO This one's a slight hack
pub current_selection: Option<ID>,
}
// TODO not the right module for this, totally temp

View File

@ -1,5 +1,3 @@
// TODO how to edit cycle time?
use ezgui::{Color, GfxCtx};
use geom::{Bounds, Polygon, Pt2D};
use map_model::{IntersectionID, TurnPriority};
@ -7,6 +5,7 @@ use objects::{Ctx, ID};
use piston::input::Key;
use plugins::{Plugin, PluginCtx};
use render::draw_signal_cycle;
use std::collections::HashSet;
#[derive(PartialEq)]
pub enum TrafficSignalEditor {
@ -26,13 +25,12 @@ impl TrafficSignalEditor {
impl Plugin for TrafficSignalEditor {
fn event(&mut self, ctx: PluginCtx) -> bool {
let input = ctx.input;
let map = &mut ctx.primary.map;
let selected = ctx.primary.current_selection;
if *self == TrafficSignalEditor::Inactive {
match selected {
Some(ID::Intersection(id)) => {
if map.maybe_get_traffic_signal(id).is_some()
if ctx.primary.map.maybe_get_traffic_signal(id).is_some()
&& input.key_pressed(Key::E, &format!("edit traffic signal for {}", id))
{
*self = TrafficSignalEditor::Active {
@ -54,10 +52,14 @@ impl Plugin for TrafficSignalEditor {
new_state = Some(TrafficSignalEditor::Inactive);
} else {
ctx.hints.suppress_traffic_signal_icon = Some(*i);
ctx.hints.hide_crosswalks.extend(
ctx.primary.map.get_traffic_signal(*i).cycles[*current_cycle]
.get_absent_crosswalks(ctx.primary.map.get_turns_in_intersection(*i)),
);
// Change cycles
{
let cycles = &map.get_traffic_signal(*i).cycles;
let cycles = &ctx.primary.map.get_traffic_signal(*i).cycles;
if let Some(n) = input.number_chosen(
cycles.len(),
&format!(
@ -74,7 +76,7 @@ impl Plugin for TrafficSignalEditor {
// Change turns
if let Some(ID::Turn(id)) = selected {
if id.parent == *i {
let mut signal = map.get_traffic_signal(*i).clone();
let mut signal = ctx.primary.map.get_traffic_signal(*i).clone();
{
let cycle = &mut signal.cycles[*current_cycle];
if cycle.get_priority(id) == TurnPriority::Priority {
@ -84,7 +86,7 @@ impl Plugin for TrafficSignalEditor {
) {
cycle.remove(id);
}
} else if cycle.could_be_priority_turn(id, map) {
} else if cycle.could_be_priority_turn(id, &ctx.primary.map) {
if input.key_pressed(
Key::Space,
"add this turn to this cycle as priority",
@ -100,7 +102,7 @@ impl Plugin for TrafficSignalEditor {
}
}
map.edit_traffic_signal(signal);
ctx.primary.map.edit_traffic_signal(signal);
}
}
}
@ -116,20 +118,18 @@ impl Plugin for TrafficSignalEditor {
}
}
fn draw(&self, g: &mut GfxCtx, mut ctx: Ctx) {
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
if let TrafficSignalEditor::Active { i, current_cycle } = self {
let cycles = &ctx.map.get_traffic_signal(*i).cycles;
draw_signal_cycle(
&cycles[*current_cycle],
*i,
if ctx.current_selection == Some(ID::Intersection(*i)) {
ctx.cs.get("selected", Color::BLUE)
} else {
ctx.cs.get("unchanged intersection", Color::grey(0.6))
},
g,
&mut ctx,
ctx.cs,
ctx.map,
ctx.draw_map,
&ctx.hints.hide_crosswalks,
);
// Draw all of the cycles off to the side
@ -148,14 +148,9 @@ impl Plugin for TrafficSignalEditor {
)
};
let panel_bg_color = ctx.cs.get("signal editor panel", Color::BLACK.alpha(0.95));
let panel_selected_color = ctx.cs.get(
"current cycle in signal editor panel",
Color::BLUE.alpha(0.95),
);
let old_ctx = g.fork_screenspace();
g.draw_polygon(
panel_bg_color,
ctx.cs.get("signal editor panel", Color::BLACK.alpha(0.95)),
&Polygon::rectangle_topleft(
Pt2D::new(10.0, 10.0),
width * zoom,
@ -165,7 +160,10 @@ impl Plugin for TrafficSignalEditor {
// 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(
panel_selected_color,
ctx.cs.get(
"current cycle in signal editor panel",
Color::BLUE.alpha(0.95),
),
&Polygon::rectangle_topleft(
Pt2D::new(
10.0,
@ -185,16 +183,17 @@ impl Plugin for TrafficSignalEditor {
),
zoom,
);
let mut hide_crosswalks = HashSet::new();
hide_crosswalks
.extend(cycle.get_absent_crosswalks(ctx.map.get_turns_in_intersection(*i)));
draw_signal_cycle(
cycle,
&cycle,
*i,
if idx == *current_cycle {
panel_selected_color
} else {
panel_bg_color
},
g,
&mut ctx,
ctx.cs,
ctx.map,
ctx.draw_map,
&hide_crosswalks,
);
}

View File

@ -27,7 +27,15 @@ impl Plugin for TurnCyclerState {
Some(ID::Lane(id)) => id,
Some(ID::Intersection(id)) => {
*self = TurnCyclerState::Intersection(id);
ctx.hints.suppress_traffic_signal_icon = Some(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_traffic_signal_icon = Some(id);
ctx.hints.hide_crosswalks.extend(
cycle.get_absent_crosswalks(ctx.primary.map.get_turns_in_intersection(id)),
);
}
return false;
}
_ => {
@ -64,7 +72,7 @@ impl Plugin for TurnCyclerState {
}
}
fn draw(&self, g: &mut GfxCtx, mut ctx: Ctx) {
fn draw(&self, g: &mut GfxCtx, ctx: Ctx) {
match self {
TurnCyclerState::Inactive => {}
TurnCyclerState::Active(l, current_turn_index) => {
@ -96,13 +104,15 @@ impl Plugin for TurnCyclerState {
let (cycle, time_left) =
signal.current_cycle_and_remaining_time(ctx.sim.time.as_time());
// TODO Ew... there's got to be a way to prevent drawing the intersection instead
let hide_crosswalk = if ctx.current_selection == Some(ID::Intersection(*id)) {
ctx.cs.get("selected", Color::BLUE)
} else {
ctx.cs.get("unchanged intersection", Color::grey(0.6))
};
draw_signal_cycle(cycle, *id, hide_crosswalk, g, &mut ctx);
draw_signal_cycle(
cycle,
*id,
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.
{

View File

@ -1,11 +1,16 @@
use colors::ColorScheme;
use dimensioned::si;
use ezgui::{Color, GfxCtx};
use geom::{Angle, Bounds, Circle, Polygon, Pt2D};
use map_model::{
Cycle, Intersection, IntersectionID, IntersectionType, Map, Turn, TurnType, LANE_THICKNESS,
Cycle, Intersection, IntersectionID, IntersectionType, Map, Turn, TurnID, TurnType,
LANE_THICKNESS,
};
use objects::{Ctx, ID};
use render::{DrawCrosswalk, RenderOptions, Renderable, BIG_ARROW_THICKNESS, BIG_ARROW_TIP_LENGTH};
use render::{
DrawCrosswalk, DrawMap, RenderOptions, Renderable, BIG_ARROW_THICKNESS, BIG_ARROW_TIP_LENGTH,
};
use std::collections::HashSet;
#[derive(Debug)]
pub struct DrawIntersection {
@ -98,7 +103,11 @@ impl Renderable for DrawIntersection {
g.draw_polygon(color, &self.polygon);
for crosswalk in &self.crosswalks {
crosswalk.draw(g, ctx.cs.get("crosswalk", Color::WHITE));
if !ctx.hints.hide_crosswalks.contains(&crosswalk.id1)
&& !ctx.hints.hide_crosswalks.contains(&crosswalk.id2)
{
crosswalk.draw(g, ctx.cs.get("crosswalk", Color::WHITE));
}
}
for corner in &self.sidewalk_corners {
@ -162,49 +171,34 @@ fn calculate_corners(i: IntersectionID, map: &Map) -> Vec<Polygon> {
corners
}
// TODO Taking &mut Ctx is a hack to let this be called multiple times in one plugin's draw().
// Should really pass down individual pieces of the Ctx, or even better, make it Copy (and rethink
// the mutable ColorScheme pattern).
pub fn draw_signal_cycle(
cycle: &Cycle,
id: IntersectionID,
hide_crosswalk: Color,
g: &mut GfxCtx,
ctx: &mut Ctx,
cs: &mut ColorScheme,
map: &Map,
draw_map: &DrawMap,
hide_crosswalks: &HashSet<TurnID>,
) {
let priority_color = ctx
.cs
.get("turns protected by traffic signal right now", Color::GREEN);
let yield_color = ctx.cs.get(
let priority_color = cs.get("turns protected by traffic signal right now", Color::GREEN);
let yield_color = cs.get(
"turns allowed with yielding by traffic signal right now",
Color::rgba(255, 105, 180, 0.8),
);
// First over-draw the crosswalks.
// TODO Should this use the color_for system? Really want to show/hide...
for crosswalk in &ctx.draw_map.get_i(id).crosswalks {
let color = if cycle.priority_turns.contains(&crosswalk.id1)
|| cycle.priority_turns.contains(&crosswalk.id2)
{
ctx.cs.get("crosswalk", Color::WHITE)
} else if cycle.yield_turns.contains(&crosswalk.id1)
|| cycle.yield_turns.contains(&crosswalk.id2)
{
panic!("Crosswalks shouldn't ever yield")
} else {
hide_crosswalk
};
crosswalk.draw(g, color);
for crosswalk in &draw_map.get_i(id).crosswalks {
if !hide_crosswalks.contains(&crosswalk.id1) && !hide_crosswalks.contains(&crosswalk.id2) {
crosswalk.draw(g, cs.get("crosswalk", Color::WHITE));
}
}
for t in &cycle.priority_turns {
let turn = ctx.map.get_t(*t);
let turn = map.get_t(*t);
if !turn.between_sidewalks() {
draw_full(turn, g, priority_color);
}
}
for t in &cycle.yield_turns {
let turn = ctx.map.get_t(*t);
let turn = map.get_t(*t);
if !turn.between_sidewalks() {
for poly in turn
.geom

View File

@ -20,6 +20,7 @@ use render::{DrawMap, RenderOptions};
use sim;
use sim::{GetDrawAgents, Sim, SimFlags, Tick};
use std::cell::RefCell;
use std::collections::HashSet;
use std::panic;
use std::process;
@ -97,7 +98,6 @@ impl GUI<RenderingHints> for UI {
canvas: &self.canvas,
sim: &self.primary.sim,
hints: &hints,
current_selection: self.primary.current_selection,
},
);
}
@ -117,7 +117,6 @@ impl GUI<RenderingHints> for UI {
canvas: &self.canvas,
sim: &self.primary.sim,
hints: &hints,
current_selection: self.primary.current_selection,
},
);
}
@ -132,7 +131,6 @@ impl GUI<RenderingHints> for UI {
canvas: &self.canvas,
sim: &self.primary.sim,
hints: &hints,
current_selection: self.primary.current_selection,
},
);
} else {
@ -148,7 +146,6 @@ impl GUI<RenderingHints> for UI {
canvas: &self.canvas,
sim: &self.primary.sim,
hints: &hints,
current_selection: self.primary.current_selection,
},
);
}
@ -341,6 +338,7 @@ impl UI {
mode: EventLoopMode::InputOnly,
osd: Text::new(),
suppress_traffic_signal_icon: None,
hide_crosswalks: HashSet::new(),
};
// First update the camera and handle zoom
@ -434,7 +432,6 @@ impl UI {
canvas: &self.canvas,
sim: &self.primary.sim,
hints,
current_selection: self.primary.current_selection,
};
if let Some(p) = self.get_active_plugin() {
return p.color_for(id, ctx);

View File

@ -2,7 +2,7 @@ use abstutil::Error;
use dimensioned::si;
use std;
use std::collections::BTreeSet;
use {IntersectionID, Map, RoadID, TurnID, TurnPriority, TurnType};
use {IntersectionID, Map, RoadID, Turn, TurnID, TurnPriority, TurnType};
const CYCLE_DURATION: si::Second<f64> = si::Second {
value_unsafe: 15.0,
@ -122,6 +122,19 @@ impl Cycle {
);
}
}
pub fn get_absent_crosswalks(&self, turns: Vec<&Turn>) -> Vec<TurnID> {
let mut result = Vec::new();
for t in turns.into_iter() {
if t.between_sidewalks()
&& !self.priority_turns.contains(&t.id)
&& !self.yield_turns.contains(&t.id)
{
result.push(t.id);
}
}
result
}
}
fn greedy_assignment(map: &Map, intersection: IntersectionID) -> ControlTrafficSignal {