ltn/render/
filters.rs

1use std::collections::BTreeMap;
2
3use geom::Angle;
4use map_model::{FilterType, Map};
5use widgetry::mapspace::{DrawCustomUnzoomedShapes, PerZoom};
6use widgetry::{EventCtx, GeomBatch, RewriteColor};
7
8use crate::render;
9
10pub fn render_modal_filters(ctx: &EventCtx, map: &Map) -> render::Toggle3Zoomed {
11    let mut batch = GeomBatch::new();
12    let mut low_zoom = DrawCustomUnzoomedShapes::builder();
13
14    let mut icons = BTreeMap::new();
15    for ft in [
16        FilterType::NoEntry,
17        FilterType::WalkCycleOnly,
18        FilterType::BusGate,
19        FilterType::SchoolStreet,
20    ] {
21        icons.insert(ft, GeomBatch::load_svg(ctx, render::filter_svg_path(ft)));
22    }
23
24    for (road, filter) in map.all_roads_with_modal_filter() {
25        let icon = &icons[&filter.filter_type];
26        let rewrite_color = if map.get_edits().is_filter_modified(road.id, filter) {
27            RewriteColor::NoOp
28        } else {
29            RewriteColor::ChangeAlpha(0.7)
30        };
31
32        if let Ok((pt, road_angle)) = road.center_pts.dist_along(filter.dist) {
33            let angle = if filter.filter_type == FilterType::NoEntry {
34                road_angle.rotate_degs(90.0)
35            } else {
36                Angle::ZERO
37            };
38
39            batch.append(
40                icon.clone()
41                    .scale_to_fit_width(road.get_width().inner_meters())
42                    .centered_on(pt)
43                    .rotate(angle)
44                    .color(rewrite_color),
45            );
46
47            // TODO Memory intensive
48            let icon = icon.clone();
49            // TODO They can shrink a bit past their map size
50            low_zoom.add_custom(Box::new(move |batch, thickness| {
51                batch.append(
52                    icon.clone()
53                        .scale_to_fit_width(30.0 * thickness)
54                        .centered_on(pt)
55                        .rotate(angle)
56                        .color(rewrite_color),
57                );
58            }));
59        }
60    }
61
62    for i in map.all_intersections() {
63        if let Some(ref filter) = i.modal_filter {
64            let icon = &icons[&filter.filter_type];
65            let rewrite_color = if map.get_edits().is_diagonal_filter_modified(i.id, filter) {
66                RewriteColor::NoOp
67            } else {
68                RewriteColor::ChangeAlpha(0.7)
69            };
70
71            let line = filter.geometry(map);
72            let angle = if filter.filter_type == FilterType::NoEntry {
73                line.angle()
74            } else {
75                Angle::ZERO
76            };
77            let pt = line.middle().unwrap();
78
79            batch.append(
80                icon.clone()
81                    .scale_to_fit_width(line.length().inner_meters())
82                    .centered_on(pt)
83                    .rotate(angle)
84                    .color(rewrite_color),
85            );
86
87            let icon = icon.clone();
88            low_zoom.add_custom(Box::new(move |batch, thickness| {
89                // TODO Why is this magic value different than the one above?
90                batch.append(
91                    icon.clone()
92                        .scale(0.4 * thickness)
93                        .centered_on(pt)
94                        .rotate(angle)
95                        .color(rewrite_color),
96                );
97            }));
98        }
99    }
100
101    let min_zoom_for_detail = 5.0;
102    let step_size = 0.1;
103    // TODO Ideally we get rid of Toggle3Zoomed and make DrawCustomUnzoomedShapes handle this
104    // medium-zoom case.
105    render::Toggle3Zoomed::new(
106        batch.build(ctx),
107        low_zoom.build(PerZoom::new(min_zoom_for_detail, step_size)),
108    )
109}