1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::collections::BTreeSet;
use map_gui::tools::{CityPicker, DrawRoadLabels};
use map_gui::ID;
use widgetry::{
Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Line, Outcome, Panel, State,
TextExt, VerticalAlignment, Widget,
};
use super::{Neighborhood, Viewer};
use crate::app::{App, Transition};
use crate::common::intersections_from_roads;
pub struct BrowseNeighborhoods {
panel: Panel,
draw_neighborhoods: Drawable,
labels: DrawRoadLabels,
}
impl BrowseNeighborhoods {
pub fn new_state(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
let panel = Panel::new_builder(Widget::col(vec![
Widget::row(vec![
Line("LTN tool").small_heading().into_widget(ctx),
map_gui::tools::change_map_btn(ctx, app)
.centered_vert()
.align_right(),
]),
"Click a neighborhood".text_widget(ctx),
]))
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
.build(ctx);
let draw_neighborhoods = calculate_neighborhoods(app).upload(ctx);
Box::new(BrowseNeighborhoods {
panel,
draw_neighborhoods,
labels: DrawRoadLabels::only_major_roads(),
})
}
}
impl State<App> for BrowseNeighborhoods {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
ctx.canvas_movement();
if ctx.redo_mouseover() {
app.primary.current_selection =
match app.mouseover_unzoomed_roads_and_intersections(ctx) {
x @ Some(ID::Road(_)) => x,
Some(ID::Lane(l)) => Some(ID::Road(l.road)),
_ => None,
};
}
if let Some(ID::Road(r)) = app.primary.current_selection {
if Neighborhood::is_interior_road(r, &app.primary.map) && ctx.normal_left_click() {
return Transition::Replace(Viewer::start_from_road(ctx, app, r));
}
}
if let Outcome::Clicked(x) = self.panel.event(ctx) {
match x.as_ref() {
"change map" => {
return Transition::Push(CityPicker::new_state(
ctx,
app,
Box::new(|ctx, app| {
Transition::Replace(BrowseNeighborhoods::new_state(ctx, app))
}),
));
}
_ => unreachable!(),
}
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
self.panel.draw(g);
g.redraw(&self.draw_neighborhoods);
self.labels.draw(g, app);
}
}
fn calculate_neighborhoods(app: &App) -> GeomBatch {
let map = &app.primary.map;
let mut unvisited = BTreeSet::new();
let mut batch = GeomBatch::new();
let colors = [
Color::BLUE,
Color::ORANGE,
Color::PURPLE,
Color::RED,
Color::GREEN,
Color::CYAN,
];
let mut num_neighborhoods = 0;
for r in map.all_roads() {
if Neighborhood::is_interior_road(r.id, map) {
unvisited.insert(r.id);
}
}
while !unvisited.is_empty() {
let start = *unvisited.iter().next().unwrap();
let neighborhood = Neighborhood::from_road(map, start);
let color = colors[num_neighborhoods % colors.len()];
num_neighborhoods += 1;
for i in intersections_from_roads(&neighborhood.interior, map) {
batch.push(color, map.get_i(i).polygon.clone());
}
for r in neighborhood.interior {
batch.push(color, map.get_r(r).get_thick_polygon());
unvisited.remove(&r);
}
}
batch
}