improve turn block rendering

This commit is contained in:
Dustin Carlino 2019-11-21 16:04:53 -08:00
parent aa1d7faa00
commit 55eead8ec7
4 changed files with 118 additions and 62 deletions

View File

@ -6,7 +6,7 @@ use crate::render::{
}; };
use crate::ui::{ShowEverything, UI}; use crate::ui::{ShowEverything, UI};
use ezgui::{hotkey, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ModalMenu, Text}; use ezgui::{hotkey, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ModalMenu, Text};
use geom::{Distance, Duration}; use geom::Duration;
use map_model::{ use map_model::{
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroup, TurnID, TurnPriority, TurnType, ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroup, TurnID, TurnPriority, TurnType,
}; };
@ -219,40 +219,48 @@ impl State for TrafficSignalEditor {
for g in &self.groups { for g in &self.groups {
if Some(g.group.clone()) == self.group_selected { if Some(g.group.clone()) == self.group_selected {
batch.push(Color::RED, g.block.clone()); batch.push(ui.cs.get_def("solid selected", Color::RED), g.block.clone());
batch.extend( // Overwrite the original thing
ui.cs.get_def("selected turn", Color::RED), batch.push(
g.group.geom(&ui.primary.map).dashed_polygons( ui.cs.get("solid selected"),
BIG_ARROW_THICKNESS, g.group
Distance::meters(1.0), .geom(&ui.primary.map)
Distance::meters(0.5), .make_arrow(BIG_ARROW_THICKNESS)
), .unwrap(),
); );
} else { } else {
let color = match phase.get_priority_group(&g.group) { batch.push(
TurnPriority::Protected => ui.cs.get("turn protected by traffic signal"), ui.cs.get_def("turn block background", Color::grey(0.6)),
TurnPriority::Yield => ui g.block.clone(),
.cs );
.get("turn that can yield by traffic signal")
.alpha(1.0),
TurnPriority::Banned => {
ui.cs.get_def("turn not in current phase", Color::BLACK)
}
};
batch.push(color, g.block.clone());
} }
let arrow_color = match phase.get_priority_group(&g.group) {
TurnPriority::Protected => ui.cs.get("turn protected by traffic signal"),
TurnPriority::Yield => ui
.cs
.get("turn that can yield by traffic signal")
.alpha(1.0),
TurnPriority::Banned => ui.cs.get_def("turn not in current phase", Color::BLACK),
};
batch.push(arrow_color, g.arrow.clone());
} }
batch.draw(g); batch.draw(g);
self.diagram.draw(g, &ctx); self.diagram.draw(g, &ctx);
self.menu.draw(g); self.menu.draw(g);
// TODO groups... if let Some(ref group) = self.group_selected {
/*if let Some(t) = self.icon_selected { CommonState::draw_custom_osd(
CommonState::draw_osd(g, ui, &Some(ID::Turn(t))); g,
} else {*/ Text::from(Line(format!(
CommonState::draw_osd(g, ui, &None); "Turn from {} to {}",
//} ui.primary.map.get_r(group.from).get_name(),
ui.primary.map.get_r(group.to).get_name()
))),
);
} else {
CommonState::draw_osd(g, ui, &None);
}
} }
} }

View File

@ -37,8 +37,6 @@ const EXTRA_SHAPE_POINT_RADIUS: Distance = Distance::const_meters(10.0);
pub const BIG_ARROW_THICKNESS: Distance = Distance::const_meters(0.5); pub const BIG_ARROW_THICKNESS: Distance = Distance::const_meters(0.5);
const TURN_ICON_ARROW_THICKNESS: Distance = Distance::const_meters(0.15);
const TURN_ICON_ARROW_LENGTH: Distance = Distance::const_meters(2.0);
pub const CROSSWALK_LINE_THICKNESS: Distance = Distance::const_meters(0.25); pub const CROSSWALK_LINE_THICKNESS: Distance = Distance::const_meters(0.25);
pub const OUTLINE_THICKNESS: Distance = Distance::const_meters(0.5); pub const OUTLINE_THICKNESS: Distance = Distance::const_meters(0.5);

View File

@ -1,20 +1,19 @@
use crate::render::{BIG_ARROW_THICKNESS, TURN_ICON_ARROW_LENGTH}; use crate::render::BIG_ARROW_THICKNESS;
use ezgui::{Color, GeomBatch, GfxCtx}; use ezgui::{Color, GeomBatch, GfxCtx};
use geom::{Distance, Line, Polygon}; use geom::{Distance, Line, PolyLine, Polygon};
use map_model::{IntersectionID, Map, RoadID, Turn, TurnGroup, LANE_THICKNESS}; use map_model::{IntersectionID, LaneID, Map, Turn, TurnGroup};
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
const TURN_ICON_ARROW_LENGTH: Distance = Distance::const_meters(2.0);
pub struct DrawTurn {} pub struct DrawTurn {}
impl DrawTurn { impl DrawTurn {
pub fn full_geom(t: &Turn, batch: &mut GeomBatch, color: Color) {
batch.push(color, t.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap());
}
pub fn draw_full(t: &Turn, g: &mut GfxCtx, color: Color) { pub fn draw_full(t: &Turn, g: &mut GfxCtx, color: Color) {
let mut batch = GeomBatch::new(); g.draw_polygon(
DrawTurn::full_geom(t, &mut batch, color); color,
batch.draw(g); &t.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
);
} }
// TODO make a polyline.dashed or something // TODO make a polyline.dashed or something
@ -45,41 +44,56 @@ impl DrawTurn {
} }
} }
// TODO Don't store these in DrawMap; just generate when we hop into the traffic signal editor.
// Simplifies apply_map_edits!
pub struct DrawTurnGroup { pub struct DrawTurnGroup {
pub group: TurnGroup, pub group: TurnGroup,
pub block: Polygon, pub block: Polygon,
pub arrow: Polygon,
} }
impl DrawTurnGroup { impl DrawTurnGroup {
pub fn for_i(i: IntersectionID, map: &Map) -> Vec<DrawTurnGroup> { pub fn for_i(i: IntersectionID, map: &Map) -> Vec<DrawTurnGroup> {
// TODO Sort by angle here if we want some consistency // TODO Sort by angle here if we want some consistency
// TODO Handle short roads // TODO Handle short roads
let mut offset_per_road: HashMap<RoadID, f64> = HashMap::new(); let mut offset_per_lane: HashMap<LaneID, usize> = HashMap::new();
let mut draw = Vec::new(); let mut draw = Vec::new();
for group in TurnGroup::for_i(i, map) { for group in TurnGroup::for_i(i, map) {
let offset = offset_per_road.entry(group.from).or_insert(0.5); let offset = group
.members
.iter()
.map(|t| *offset_per_lane.entry(t.src).or_insert(0))
.max()
.unwrap() as f64;
let (pl, width) = group.src_center_and_width(map);
let slice = pl.exact_slice(
offset * TURN_ICON_ARROW_LENGTH,
(offset + 1.0) * TURN_ICON_ARROW_LENGTH,
);
let block = slice.make_polygons(width);
// TODO center it properly let arrow = {
let pl = { let angle = group.angle(map);
let r = map.get_r(group.from); let center = slice.middle();
if r.dst_i == i { PolyLine::new(vec![
r.center_pts.reversed() center.project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle.opposite()),
} else { center.project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle),
r.center_pts.clone() ])
} .make_arrow(Distance::meters(0.5))
.unwrap()
}; };
// TODO Not number of turns, number of source lanes
let block = pl
.exact_slice(
*offset * TURN_ICON_ARROW_LENGTH,
(*offset + 1.0) * TURN_ICON_ARROW_LENGTH,
)
.make_polygons(LANE_THICKNESS * (group.members.len() as f64));
draw.push(DrawTurnGroup { group, block });
*offset += 1.0; let mut seen_lanes = HashSet::new();
for t in &group.members {
if !seen_lanes.contains(&t.src) {
*offset_per_lane.get_mut(&t.src).unwrap() += 1;
seen_lanes.insert(t.src);
}
}
draw.push(DrawTurnGroup {
group,
block,
arrow,
});
} }
draw draw
} }

View File

@ -1,6 +1,6 @@
use crate::{IntersectionID, LaneID, Map, RoadID}; use crate::{IntersectionID, LaneID, Map, RoadID, LANE_THICKNESS};
use abstutil::MultiMap; use abstutil::MultiMap;
use geom::{Angle, PolyLine, Pt2D}; use geom::{Angle, Distance, PolyLine, Pt2D};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt; use std::fmt;
@ -164,4 +164,40 @@ impl TurnGroup {
} }
PolyLine::new(pts) PolyLine::new(pts)
} }
pub fn angle(&self, map: &Map) -> Angle {
let t = *self.members.iter().next().unwrap();
map.get_t(t).angle()
}
// Polyline points FROM intersection
pub fn src_center_and_width(&self, map: &Map) -> (PolyLine, Distance) {
let r = map.get_r(self.from);
let dir = r.dir_and_offset(self.members.iter().next().unwrap().src).0;
let pl = if dir {
r.center_pts.clone()
} else {
r.center_pts.reversed()
};
let mut offsets: Vec<usize> = self
.members
.iter()
.map(|t| r.dir_and_offset(t.src).1)
.collect();
offsets.sort();
offsets.dedup();
let offset = if offsets.len() % 2 == 0 {
// Middle of two lanes
(offsets[offsets.len() / 2] as f64) - 0.5
} else {
offsets[offsets.len() / 2] as f64
};
let pl = pl
.shift_right(LANE_THICKNESS * (0.5 + offset))
.unwrap()
.reversed();
let width = LANE_THICKNESS * ((*offsets.last().unwrap() - offsets[0] + 1) as f64);
(pl, width)
}
} }