mirror of
https://github.com/a-b-street/abstreet.git
synced 2025-01-03 20:14:53 +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 ezgui::{hotkey, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ModalMenu, Text};
|
||||
use geom::{Distance, Duration};
|
||||
use geom::Duration;
|
||||
use map_model::{
|
||||
ControlTrafficSignal, EditCmd, IntersectionID, Phase, TurnGroup, TurnID, TurnPriority, TurnType,
|
||||
};
|
||||
@ -219,40 +219,48 @@ impl State for TrafficSignalEditor {
|
||||
|
||||
for g in &self.groups {
|
||||
if Some(g.group.clone()) == self.group_selected {
|
||||
batch.push(Color::RED, g.block.clone());
|
||||
batch.extend(
|
||||
ui.cs.get_def("selected turn", Color::RED),
|
||||
g.group.geom(&ui.primary.map).dashed_polygons(
|
||||
BIG_ARROW_THICKNESS,
|
||||
Distance::meters(1.0),
|
||||
Distance::meters(0.5),
|
||||
),
|
||||
batch.push(ui.cs.get_def("solid selected", Color::RED), g.block.clone());
|
||||
// Overwrite the original thing
|
||||
batch.push(
|
||||
ui.cs.get("solid selected"),
|
||||
g.group
|
||||
.geom(&ui.primary.map)
|
||||
.make_arrow(BIG_ARROW_THICKNESS)
|
||||
.unwrap(),
|
||||
);
|
||||
} else {
|
||||
let 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(color, g.block.clone());
|
||||
batch.push(
|
||||
ui.cs.get_def("turn block background", Color::grey(0.6)),
|
||||
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);
|
||||
|
||||
self.diagram.draw(g, &ctx);
|
||||
|
||||
self.menu.draw(g);
|
||||
// TODO groups...
|
||||
/*if let Some(t) = self.icon_selected {
|
||||
CommonState::draw_osd(g, ui, &Some(ID::Turn(t)));
|
||||
} else {*/
|
||||
CommonState::draw_osd(g, ui, &None);
|
||||
//}
|
||||
if let Some(ref group) = self.group_selected {
|
||||
CommonState::draw_custom_osd(
|
||||
g,
|
||||
Text::from(Line(format!(
|
||||
"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);
|
||||
|
||||
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 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 geom::{Distance, Line, Polygon};
|
||||
use map_model::{IntersectionID, Map, RoadID, Turn, TurnGroup, LANE_THICKNESS};
|
||||
use std::collections::HashMap;
|
||||
use geom::{Distance, Line, PolyLine, Polygon};
|
||||
use map_model::{IntersectionID, LaneID, Map, Turn, TurnGroup};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
const TURN_ICON_ARROW_LENGTH: Distance = Distance::const_meters(2.0);
|
||||
|
||||
pub struct 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) {
|
||||
let mut batch = GeomBatch::new();
|
||||
DrawTurn::full_geom(t, &mut batch, color);
|
||||
batch.draw(g);
|
||||
g.draw_polygon(
|
||||
color,
|
||||
&t.geom.make_arrow(BIG_ARROW_THICKNESS * 2.0).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
// 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 group: TurnGroup,
|
||||
pub block: Polygon,
|
||||
pub arrow: Polygon,
|
||||
}
|
||||
|
||||
impl DrawTurnGroup {
|
||||
pub fn for_i(i: IntersectionID, map: &Map) -> Vec<DrawTurnGroup> {
|
||||
// TODO Sort by angle here if we want some consistency
|
||||
// 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();
|
||||
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 pl = {
|
||||
let r = map.get_r(group.from);
|
||||
if r.dst_i == i {
|
||||
r.center_pts.reversed()
|
||||
} else {
|
||||
r.center_pts.clone()
|
||||
}
|
||||
let arrow = {
|
||||
let angle = group.angle(map);
|
||||
let center = slice.middle();
|
||||
PolyLine::new(vec![
|
||||
center.project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle.opposite()),
|
||||
center.project_away(TURN_ICON_ARROW_LENGTH / 2.0, angle),
|
||||
])
|
||||
.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
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{IntersectionID, LaneID, Map, RoadID};
|
||||
use crate::{IntersectionID, LaneID, Map, RoadID, LANE_THICKNESS};
|
||||
use abstutil::MultiMap;
|
||||
use geom::{Angle, PolyLine, Pt2D};
|
||||
use geom::{Angle, Distance, PolyLine, Pt2D};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
@ -164,4 +164,40 @@ impl TurnGroup {
|
||||
}
|
||||
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