mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-27 15:03:20 +03:00
Remove UberTurnGroup and the prototype of editing a cluster of traffic
signals using uber turn groups. #555 This was an old half-baked experiment for handling a cluster of traffic signals. Since then, merging intersections (by manually tagging them in OSM for now, maybe automatically in the future) has proven better. Removing this old code in preparation for pathfinding v2.
This commit is contained in:
parent
4b1a206a43
commit
1e3708f9b4
@ -12,7 +12,6 @@ use widgetry::{
|
||||
|
||||
use crate::app::{App, ShowEverything, Transition};
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::ClusterTrafficSignalEditor;
|
||||
|
||||
pub struct UberTurnPicker {
|
||||
members: BTreeSet<IntersectionID>,
|
||||
@ -39,11 +38,6 @@ impl UberTurnPicker {
|
||||
.text("View uber-turns")
|
||||
.hotkey(Key::Enter)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_outline
|
||||
.text("Edit")
|
||||
.hotkey(Key::E)
|
||||
.build_def(ctx),
|
||||
ctx.style()
|
||||
.btn_outline
|
||||
.text("Detect all clusters")
|
||||
@ -70,20 +64,6 @@ impl SimpleState<App> for UberTurnPicker {
|
||||
}
|
||||
Transition::Replace(UberTurnViewer::new(ctx, app, self.members.clone(), 0, true))
|
||||
}
|
||||
"Edit" => {
|
||||
if self.members.len() < 2 {
|
||||
return Transition::Push(PopupMsg::new(
|
||||
ctx,
|
||||
"Error",
|
||||
vec!["Select at least two intersections"],
|
||||
));
|
||||
}
|
||||
Transition::Replace(ClusterTrafficSignalEditor::new(
|
||||
ctx,
|
||||
app,
|
||||
&IntersectionCluster::new(self.members.clone(), &app.primary.map).0,
|
||||
))
|
||||
}
|
||||
"Detect all clusters" => {
|
||||
self.members.clear();
|
||||
for ic in IntersectionCluster::find_all(&app.primary.map) {
|
||||
|
@ -1,101 +0,0 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use geom::ArrowCap;
|
||||
use map_gui::render::{DrawOptions, DrawUberTurnGroup, BIG_ARROW_THICKNESS};
|
||||
use map_model::{IntersectionCluster, IntersectionID};
|
||||
use widgetry::{
|
||||
DrawBaselayer, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::app::Transition;
|
||||
use crate::app::{App, ShowEverything};
|
||||
|
||||
pub struct ClusterTrafficSignalEditor {
|
||||
panel: Panel,
|
||||
|
||||
members: BTreeSet<IntersectionID>,
|
||||
groups: Vec<DrawUberTurnGroup>,
|
||||
group_selected: Option<usize>,
|
||||
}
|
||||
|
||||
impl ClusterTrafficSignalEditor {
|
||||
pub fn new(ctx: &mut EventCtx, app: &mut App, ic: &IntersectionCluster) -> Box<dyn State<App>> {
|
||||
app.primary.current_selection = None;
|
||||
Box::new(ClusterTrafficSignalEditor {
|
||||
panel: Panel::new(Widget::row(vec![ctx
|
||||
.style()
|
||||
.btn_solid_primary
|
||||
.text("Finish")
|
||||
.hotkey(Key::Escape)
|
||||
.build_def(ctx)]))
|
||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||
.build(ctx),
|
||||
groups: DrawUberTurnGroup::new(ic, &app.primary.map),
|
||||
group_selected: None,
|
||||
members: ic.members.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl State<App> for ClusterTrafficSignalEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
"Finish" => {
|
||||
return Transition::Pop;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ctx.canvas_movement();
|
||||
if ctx.redo_mouseover() {
|
||||
self.group_selected = None;
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
|
||||
for (idx, g) in self.groups.iter().enumerate() {
|
||||
if g.block.contains_pt(pt) {
|
||||
self.group_selected = Some(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw_baselayer(&self) -> DrawBaselayer {
|
||||
DrawBaselayer::Custom
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
{
|
||||
let mut opts = DrawOptions::new();
|
||||
opts.suppress_traffic_signal_details
|
||||
.extend(self.members.clone());
|
||||
app.draw(g, opts, &ShowEverything::new());
|
||||
}
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
for (idx, g) in self.groups.iter().enumerate() {
|
||||
if Some(idx) == self.group_selected {
|
||||
batch.push(app.cs.selected, g.block.clone());
|
||||
batch.push(
|
||||
app.cs.selected,
|
||||
g.group
|
||||
.geom
|
||||
.make_arrow(BIG_ARROW_THICKNESS, ArrowCap::Triangle),
|
||||
);
|
||||
} else {
|
||||
batch.push(app.cs.signal_turn_block_bg, g.block.clone());
|
||||
}
|
||||
let arrow_color = app.cs.signal_protected_turn;
|
||||
batch.push(arrow_color, g.arrow.clone());
|
||||
}
|
||||
batch.draw(g);
|
||||
|
||||
self.panel.draw(g);
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ use widgetry::{
|
||||
Key, Line, Menu, Outcome, Panel, State, Text, TextExt, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
pub use self::cluster_traffic_signals::ClusterTrafficSignalEditor;
|
||||
pub use self::lanes::LaneEditor;
|
||||
pub use self::routes::RouteEditor;
|
||||
pub use self::stop_signs::StopSignEditor;
|
||||
@ -26,7 +25,6 @@ use crate::debug::DebugMode;
|
||||
use crate::sandbox::{GameplayMode, SandboxMode, TimeWarpScreen};
|
||||
|
||||
mod bulk;
|
||||
mod cluster_traffic_signals;
|
||||
mod lanes;
|
||||
mod routes;
|
||||
mod select;
|
||||
|
@ -14,7 +14,7 @@ use crate::render::car::DrawCar;
|
||||
pub use crate::render::intersection::{calculate_corners, DrawIntersection};
|
||||
pub use crate::render::map::DrawMap;
|
||||
pub use crate::render::pedestrian::{DrawPedCrowd, DrawPedestrian};
|
||||
pub use crate::render::turn::{DrawMovement, DrawUberTurnGroup};
|
||||
pub use crate::render::turn::DrawMovement;
|
||||
use crate::{AppLike, ID};
|
||||
|
||||
mod agents;
|
||||
|
@ -1,10 +1,7 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use geom::{Angle, ArrowCap, Circle, Distance, PolyLine, Polygon};
|
||||
use map_model::{
|
||||
IntersectionCluster, IntersectionID, LaneID, Map, MovementID, TurnPriority, UberTurnGroup,
|
||||
SIDEWALK_THICKNESS,
|
||||
};
|
||||
use map_model::{IntersectionID, LaneID, Map, MovementID, TurnPriority, SIDEWALK_THICKNESS};
|
||||
use widgetry::{Color, GeomBatch, Prerender};
|
||||
|
||||
use crate::colors::ColorScheme;
|
||||
@ -181,68 +178,6 @@ impl DrawMovement {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawUberTurnGroup {
|
||||
pub group: UberTurnGroup,
|
||||
pub block: Polygon,
|
||||
pub arrow: Polygon,
|
||||
}
|
||||
|
||||
impl DrawUberTurnGroup {
|
||||
pub fn new(ic: &IntersectionCluster, map: &Map) -> Vec<DrawUberTurnGroup> {
|
||||
let mut offset_per_lane: HashMap<LaneID, usize> = HashMap::new();
|
||||
let mut draw = Vec::new();
|
||||
for group in ic.uber_turn_groups(map) {
|
||||
let offset = group
|
||||
.members
|
||||
.iter()
|
||||
.map(|ut| *offset_per_lane.entry(ut.entry()).or_insert(0))
|
||||
.max()
|
||||
.unwrap();
|
||||
let (pl, width) = group.src_center_and_width(map);
|
||||
let (block, arrow) = make_block_geom(offset as f64, pl, width, group.angle());
|
||||
let mut seen_lanes = HashSet::new();
|
||||
for ut in &group.members {
|
||||
if !seen_lanes.contains(&ut.entry()) {
|
||||
*offset_per_lane.get_mut(&ut.entry()).unwrap() = offset + 1;
|
||||
seen_lanes.insert(ut.entry());
|
||||
}
|
||||
}
|
||||
|
||||
draw.push(DrawUberTurnGroup {
|
||||
group,
|
||||
block,
|
||||
arrow,
|
||||
});
|
||||
}
|
||||
draw
|
||||
}
|
||||
}
|
||||
|
||||
// Produces (block, arrow)
|
||||
fn make_block_geom(offset: f64, pl: PolyLine, width: Distance, angle: Angle) -> (Polygon, Polygon) {
|
||||
let height = TURN_ICON_ARROW_LENGTH;
|
||||
// Always extend the pl first to handle short entry lanes
|
||||
let extension = PolyLine::must_new(vec![
|
||||
pl.last_pt(),
|
||||
pl.last_pt()
|
||||
.project_away(Distance::meters(500.0), pl.last_line().angle()),
|
||||
]);
|
||||
let pl = pl.must_extend(extension);
|
||||
let slice = pl.exact_slice(offset * height, (offset + 1.0) * height);
|
||||
let block = slice.make_polygons(width);
|
||||
|
||||
let arrow = {
|
||||
let center = slice.middle();
|
||||
PolyLine::must_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), ArrowCap::Triangle)
|
||||
};
|
||||
|
||||
(block, arrow)
|
||||
}
|
||||
|
||||
// Produces (circle, arrow)
|
||||
fn make_circle_geom(offset: f64, pl: PolyLine, angle: Angle) -> (Polygon, Polygon) {
|
||||
let height = 2.0 * TURN_ICON_ARROW_LENGTH;
|
||||
|
@ -54,7 +54,7 @@ pub use crate::objects::turn::{
|
||||
CompressedMovementID, Movement, MovementID, Turn, TurnID, TurnPriority, TurnType,
|
||||
};
|
||||
pub use crate::objects::zone::{AccessRestrictions, Zone};
|
||||
pub use crate::pathfind::uber_turns::{IntersectionCluster, UberTurn, UberTurnGroup};
|
||||
pub use crate::pathfind::uber_turns::{IntersectionCluster, UberTurn};
|
||||
use crate::pathfind::Pathfinder;
|
||||
pub use crate::pathfind::{Path, PathConstraints, PathRequest, PathStep, RoutingParams};
|
||||
pub use crate::traversable::{Position, Traversable, MAX_BIKE_SPEED, MAX_WALKING_SPEED};
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! Pathfinding without needing to build a separate contraction hierarchy.
|
||||
|
||||
// TODO Dijkstra's for vehicles currently ignores uber-turns!
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use petgraph::graphmap::DiGraphMap;
|
||||
|
@ -6,14 +6,12 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use petgraph::graphmap::UnGraphMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use abstutil::MultiMap;
|
||||
use geom::{Angle, Distance, PolyLine, Pt2D};
|
||||
use geom::{Distance, PolyLine};
|
||||
|
||||
use crate::{DirectedRoadID, Direction, IntersectionID, LaneID, Map, TurnID};
|
||||
use crate::{IntersectionID, LaneID, Map, TurnID};
|
||||
|
||||
/// This only applies to VehiclePathfinder; walking through these intersections is nothing special.
|
||||
// TODO I haven't seen any cases yet with "interior" intersections. Some stuff might break.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct IntersectionCluster {
|
||||
pub members: BTreeSet<IntersectionID>,
|
||||
pub uber_turns: Vec<UberTurn>,
|
||||
@ -256,98 +254,3 @@ impl UberTurn {
|
||||
pl
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UberTurnGroup {
|
||||
pub from: DirectedRoadID,
|
||||
pub to: DirectedRoadID,
|
||||
pub members: Vec<UberTurn>,
|
||||
pub geom: PolyLine,
|
||||
}
|
||||
|
||||
impl IntersectionCluster {
|
||||
pub fn uber_turn_groups(&self, map: &Map) -> Vec<UberTurnGroup> {
|
||||
// TODO LaneType should also be part of the grouping... the entry? exit? everything in
|
||||
// between? What about mixes?
|
||||
let mut groups: MultiMap<(DirectedRoadID, DirectedRoadID), usize> = MultiMap::new();
|
||||
for (idx, ut) in self.uber_turns.iter().enumerate() {
|
||||
groups.insert(
|
||||
(
|
||||
map.get_l(ut.entry()).get_directed_parent(map),
|
||||
map.get_l(ut.exit()).get_directed_parent(map),
|
||||
),
|
||||
idx,
|
||||
);
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
for ((from, to), member_indices) in groups.consume() {
|
||||
let mut members = Vec::new();
|
||||
let mut polylines = Vec::new();
|
||||
for idx in member_indices {
|
||||
polylines.push(self.uber_turns[idx].geom(map));
|
||||
members.push(self.uber_turns[idx].clone());
|
||||
}
|
||||
result.push(UberTurnGroup {
|
||||
from,
|
||||
to,
|
||||
members,
|
||||
geom: group_geom(polylines),
|
||||
});
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl UberTurnGroup {
|
||||
// TODO Share code with TurnGroup
|
||||
/// Polyline points FROM intersection
|
||||
pub fn src_center_and_width(&self, map: &Map) -> (PolyLine, Distance) {
|
||||
let r = map.get_r(self.from.id);
|
||||
|
||||
let mut leftmost = Distance::meters(99999.0);
|
||||
let mut rightmost = Distance::ZERO;
|
||||
let mut left = Distance::ZERO;
|
||||
|
||||
for (l, _, _) in r.lanes_ltr() {
|
||||
let right = left + map.get_l(l).width;
|
||||
|
||||
if self.members.iter().any(|ut| ut.entry() == l) {
|
||||
leftmost = leftmost.min(left);
|
||||
rightmost = rightmost.max(right);
|
||||
}
|
||||
|
||||
left = right;
|
||||
}
|
||||
|
||||
let mut pl = r
|
||||
.get_left_side(map)
|
||||
.must_shift_right((leftmost + rightmost) / 2.0);
|
||||
// Point towards the intersection
|
||||
if self.from.dir == Direction::Back {
|
||||
pl = pl.reversed();
|
||||
}
|
||||
// Flip direction, so we point away from the intersection
|
||||
(pl.reversed(), rightmost - leftmost)
|
||||
}
|
||||
|
||||
pub fn angle(&self) -> Angle {
|
||||
self.geom.first_pt().angle_to(self.geom.last_pt())
|
||||
}
|
||||
}
|
||||
|
||||
fn group_geom(mut polylines: Vec<PolyLine>) -> PolyLine {
|
||||
let num_pts = polylines[0].points().len();
|
||||
for pl in &polylines {
|
||||
if num_pts != pl.points().len() {
|
||||
return polylines.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
let mut pts = Vec::new();
|
||||
for idx in 0..num_pts {
|
||||
pts.push(Pt2D::center(
|
||||
&polylines.iter().map(|pl| pl.points()[idx]).collect(),
|
||||
));
|
||||
}
|
||||
PolyLine::must_new(pts)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user