map_gui/tools/
draw_overlapping_paths.rs1use std::collections::BTreeMap;
2
3use geom::{Distance, Pt2D, Ring};
4use map_model::{CommonEndpoint, Direction, PathStepV2, PathV2, RoadID};
5use widgetry::mapspace::{ToggleZoomed, ToggleZoomedBuilder};
6use widgetry::Color;
7
8use crate::AppLike;
9
10pub fn draw_overlapping_paths(
11 app: &dyn AppLike,
12 paths: Vec<(PathV2, Color)>,
13) -> ToggleZoomedBuilder {
14 let mut colors_per_road: BTreeMap<RoadID, Vec<(Color, Option<DistanceInterval>)>> =
17 BTreeMap::new();
18 let mut colors_per_movement: Vec<(RoadID, RoadID, Color)> = Vec::new();
19 for (path, color) in paths {
20 for (idx, step) in path.get_steps().iter().enumerate() {
21 match step {
22 PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
23 let road_len = app.map().get_r(dr.road).length();
24 let interval = if idx == 0 {
26 if dr.dir == Direction::Fwd {
27 Some(DistanceInterval {
28 start: path.get_req().start.dist_along(),
29 end: road_len,
30 })
31 } else {
32 Some(DistanceInterval {
33 start: Distance::ZERO,
34 end: road_len - path.get_req().start.dist_along(),
39 })
40 }
41 } else if idx == path.get_steps().len() - 1 {
42 if dr.dir == Direction::Fwd {
43 Some(DistanceInterval {
44 start: Distance::ZERO,
45 end: path.get_req().end.dist_along(),
46 })
47 } else {
48 Some(DistanceInterval {
49 start: road_len - path.get_req().end.dist_along(),
51 end: road_len,
52 })
53 }
54 } else {
55 None
56 };
57 colors_per_road
58 .entry(dr.road)
59 .or_insert_with(Vec::new)
60 .push((color, interval));
61 }
62 PathStepV2::Movement(m) => {
63 colors_per_movement.push((m.from.road, m.to.road, color));
64 }
65 PathStepV2::ContraflowMovement(m) => {
66 colors_per_movement.push((m.to.road, m.from.road, color));
67 }
68 }
69 }
70 }
71
72 let mut pieces: BTreeMap<(RoadID, String), (Pt2D, Pt2D, Pt2D, Pt2D)> = BTreeMap::new();
76 let mut draw = ToggleZoomed::builder();
78 for (road, colors) in colors_per_road {
79 let road = app.map().get_r(road);
80 let width_per_piece = road.get_width() / (colors.len() as f64);
81 for (idx, (color, interval)) in colors.into_iter().enumerate() {
82 let center_line = if let Some(interval) = interval {
84 road.center_pts
85 .maybe_exact_slice(interval.start, interval.end)
86 } else {
87 Ok(road.center_pts.clone())
88 };
89 if let Ok(pl) = center_line.and_then(|pl| {
90 pl.shift_from_center(road.get_width(), (0.5 + (idx as f64)) * width_per_piece)
91 }) {
92 let polygon = pl.make_polygons(width_per_piece);
93 draw.unzoomed.push(color.alpha(0.8), polygon.clone());
94 draw.zoomed.push(color.alpha(0.5), polygon);
95
96 if let Some(corners) = pl.get_four_corners_of_thickened(width_per_piece) {
98 pieces.insert((road.id, color.as_hex()), corners);
99 }
100 }
101 }
102 }
103
104 for (from, to, color) in colors_per_movement {
106 if let Some(from_corners) = pieces.get(&(from, color.as_hex())) {
107 if let Some(to_corners) = pieces.get(&(to, color.as_hex())) {
108 let from_road = app.map().get_r(from);
109 let to_road = app.map().get_r(to);
110 if let CommonEndpoint::One(i) = from_road.common_endpoint(to_road) {
111 let (from_left, from_right) = if from_road.src_i == i {
112 (from_corners.0, from_corners.1)
113 } else {
114 (from_corners.2, from_corners.3)
115 };
116 let (to_left, to_right) = if to_road.src_i == i {
117 (to_corners.0, to_corners.1)
118 } else {
119 (to_corners.2, to_corners.3)
120 };
121 if let Ok(ring) =
123 Ring::new(vec![from_left, from_right, to_right, to_left, from_left])
124 {
125 let polygon = ring.into_polygon();
126 draw.unzoomed.push(color.alpha(0.8), polygon.clone());
127 draw.zoomed.push(color.alpha(0.5), polygon);
128 }
129 }
130 }
131 }
132 }
133
134 draw
135}
136
137struct DistanceInterval {
138 start: Distance,
139 end: Distance,
140}