mirror of
https://github.com/a-b-street/abstreet.git
synced 2025-01-05 13:05:06 +03:00
improve turn block rendering
This commit is contained in:
parent
aa1d7faa00
commit
55eead8ec7
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user