1use std::collections::BTreeSet;
2
3use serde::{Deserialize, Serialize};
4
5use geom::{Distance, Line};
6
7use crate::{EditCmd, IntersectionID, Map, PathConstraints, RoadID};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
12pub enum FilterType {
13 NoEntry,
14 WalkCycleOnly,
15 BusGate,
16 SchoolStreet,
17}
18
19#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
21pub struct RoadFilter {
22 pub dist: Distance,
23 pub filter_type: FilterType,
24}
25
26impl RoadFilter {
27 pub fn new(dist: Distance, filter_type: FilterType) -> Self {
28 Self { dist, filter_type }
29 }
30}
31
32#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
39pub struct DiagonalFilter {
40 pub i: IntersectionID,
41 pub r1: RoadID,
42 pub r2: RoadID,
43 pub filter_type: FilterType,
44
45 pub group1: BTreeSet<RoadID>,
46 pub group2: BTreeSet<RoadID>,
47}
48
49impl DiagonalFilter {
50 pub fn cycle_through_alternatives(
52 map: &Map,
53 i: IntersectionID,
54 filter_type: FilterType,
55 ) -> Vec<EditCmd> {
56 let mut roads = map.get_i(i).roads.clone();
57 roads.retain(|r| PathConstraints::Car.can_use_road(map.get_r(*r), map));
60
61 let mut commands = Vec::new();
62
63 if roads.len() == 4 {
64 let alt1 = DiagonalFilter::new(map, i, roads[0], roads[1], filter_type);
66 let alt2 = DiagonalFilter::new(map, i, roads[1], roads[2], filter_type);
67
68 match map.get_i(i).modal_filter {
69 Some(ref prev) => {
70 if alt1.approx_eq(prev) {
71 commands.push(map.edit_intersection_cmd(i, |new| {
72 new.modal_filter = Some(alt2);
73 }));
74 } else if alt2.approx_eq(prev) {
75 commands.push(map.edit_intersection_cmd(i, |new| {
76 new.modal_filter = None;
77 }));
78 } else {
79 unreachable!()
80 }
81 }
82 None => {
83 commands.push(map.edit_intersection_cmd(i, |new| {
84 new.modal_filter = Some(alt1);
85 }));
86 }
87 }
88 } else if roads.len() > 1 {
89 roads.retain(|r| {
94 let road = map.get_r(*r);
95 road.oneway_for_driving().is_none() && !road.is_deadend_for_driving(map)
96 });
97
98 if roads.is_empty() {
101 return commands;
102 }
103
104 let mut add_filter_to = None;
105 if let Some(idx) = roads
106 .iter()
107 .position(|r| map.get_r(*r).modal_filter.is_some())
108 {
109 commands.push(map.edit_road_cmd(roads[idx], |new| {
110 new.modal_filter = None;
111 }));
112 if idx != roads.len() - 1 {
113 add_filter_to = Some(roads[idx + 1]);
114 }
115 } else {
116 add_filter_to = Some(roads[0]);
117 }
118 if let Some(r) = add_filter_to {
119 let road = map.get_r(r);
120 let dist = if i == road.src_i {
121 Distance::ZERO
122 } else {
123 road.length()
124 };
125 commands.push(map.edit_road_cmd(r, |new| {
126 new.modal_filter = Some(RoadFilter::new(dist, filter_type));
127 }));
128 }
129 }
130 commands
131 }
132
133 fn new(
134 map: &Map,
135 i: IntersectionID,
136 r1: RoadID,
137 r2: RoadID,
138 filter_type: FilterType,
139 ) -> DiagonalFilter {
140 let mut roads = map.get_i(i).roads.clone();
141 while roads[0] != r1 {
143 roads.rotate_right(1);
144 }
145
146 let mut group1 = BTreeSet::new();
147 group1.insert(roads.remove(0));
148 loop {
149 let next = roads.remove(0);
150 group1.insert(next);
151 if next == r2 {
152 break;
153 }
154 }
155 assert_eq!(group1.len(), 2);
157 assert_eq!(roads.len(), 2);
158
159 DiagonalFilter {
160 r1,
161 r2,
162 i,
163 filter_type,
164 group1,
165 group2: roads.into_iter().collect(),
166 }
167 }
168
169 pub fn geometry(&self, map: &Map) -> Line {
171 let r1 = map.get_r(self.r1);
172 let r2 = map.get_r(self.r2);
173
174 let pl1 = r1.center_pts.maybe_reverse(r1.src_i == self.i);
176 let pl2 = r2.center_pts.maybe_reverse(r2.src_i == self.i);
177
178 let pt1 = pl1.must_shift_right(r1.get_half_width()).last_pt();
181 let pt2 = pl2.must_shift_left(r2.get_half_width()).last_pt();
182 match Line::new(pt1, pt2) {
183 Ok(line) => line,
184 Err(_) => Line::must_new(
187 pt1,
188 pt1.project_away(r1.get_half_width(), pt1.angle_to(pt2)),
189 ),
190 }
191 }
192
193 pub fn allows_turn(&self, from: RoadID, to: RoadID) -> bool {
194 self.group1.contains(&from) == self.group1.contains(&to)
195 }
196
197 pub fn avoid_movements_between_roads(&self) -> Vec<(RoadID, RoadID)> {
198 let mut pairs = Vec::new();
199 for from in &self.group1 {
200 for to in &self.group2 {
201 pairs.push((*from, *to));
202 pairs.push((*to, *from));
203 }
204 }
205 pairs
206 }
207
208 fn approx_eq(&self, other: &DiagonalFilter) -> bool {
209 (self.r1, self.r2, self.i, &self.group1, &self.group2)
215 == (other.r1, other.r2, other.i, &other.group1, &other.group2)
216 }
217}