mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +03:00
show entire signal diagram when hovering
This commit is contained in:
parent
e05d4be34b
commit
ae6f8d5e16
@ -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?
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user