widgetry/mapspace/
unzoomed.rs1use std::cell::RefCell;
2
3use geom::{Circle, Distance, PolyLine, Pt2D};
4
5use crate::{Color, Drawable, GeomBatch, GfxCtx};
6
7pub struct DrawUnzoomedShapes {
12 shapes: Vec<Shape>,
13 per_zoom: RefCell<[Option<Drawable>; 11]>,
14}
15
16enum Shape {
17 Line {
18 polyline: PolyLine,
19 width: Distance,
20 color: Color,
21 },
22 Circle {
23 center: Pt2D,
24 radius: Distance,
25 color: Color,
26 },
27}
28
29impl Shape {
30 fn render(&self, batch: &mut GeomBatch, thickness: f64) {
31 match self {
32 Shape::Line {
33 polyline,
34 width,
35 color,
36 } => {
37 batch.push(*color, polyline.make_polygons(thickness * *width));
38 }
39 Shape::Circle {
40 center,
41 radius,
42 color,
43 } => {
44 batch.push(
48 *color,
49 Circle::new(*center, thickness * *radius).to_polygon(),
50 );
51 }
52 }
53 }
54}
55
56pub struct DrawUnzoomedShapesBuilder {
57 shapes: Vec<Shape>,
58}
59
60impl DrawUnzoomedShapes {
61 pub fn empty() -> Self {
62 Self {
63 shapes: Vec::new(),
64 per_zoom: Default::default(),
65 }
66 }
67
68 pub fn builder() -> DrawUnzoomedShapesBuilder {
69 DrawUnzoomedShapesBuilder { shapes: Vec::new() }
70 }
71
72 pub fn draw(&self, g: &mut GfxCtx) {
73 let (zoom, idx) = discretize_zoom(g.canvas.cam_zoom);
74 let value = &mut self.per_zoom.borrow_mut()[idx];
75 if value.is_none() {
76 let max = 5.0;
79 let thickness = 1.0 + (max - 1.0) * (1.0 - zoom);
81
82 let mut batch = GeomBatch::new();
83 for shape in &self.shapes {
84 shape.render(&mut batch, thickness);
85 }
86 *value = Some(g.upload(batch));
87 }
88 g.redraw(value.as_ref().unwrap());
89 }
90}
91
92impl DrawUnzoomedShapesBuilder {
93 pub fn add_line(&mut self, polyline: PolyLine, width: Distance, color: Color) {
94 self.shapes.push(Shape::Line {
95 polyline,
96 width,
97 color,
98 });
99 }
100
101 pub fn add_circle(&mut self, center: Pt2D, radius: Distance, color: Color) {
102 self.shapes.push(Shape::Circle {
103 center,
104 radius,
105 color,
106 });
107 }
108
109 pub fn build(self) -> DrawUnzoomedShapes {
110 DrawUnzoomedShapes {
111 shapes: self.shapes,
112 per_zoom: Default::default(),
113 }
114 }
115}
116
117fn discretize_zoom(zoom: f64) -> (f64, usize) {
122 if zoom >= 1.0 {
123 return (1.0, 10);
124 }
125 let rounded = (zoom * 10.0).round();
126 let idx = rounded as usize;
127 (rounded / 10.0, idx)
128}
129
130pub struct DrawCustomUnzoomedShapes {
136 shapes: Vec<Box<dyn Fn(&mut GeomBatch, f64)>>,
137 per_zoom: RefCell<PerZoom>,
138}
139
140pub struct DrawCustomUnzoomedShapesBuilder {
141 shapes: Vec<Box<dyn Fn(&mut GeomBatch, f64)>>,
142}
143
144impl DrawCustomUnzoomedShapes {
145 pub fn empty() -> Self {
146 Self {
147 shapes: Vec::new(),
148 per_zoom: RefCell::new(PerZoom::new(1.0, 0.1)),
149 }
150 }
151
152 pub fn builder() -> DrawCustomUnzoomedShapesBuilder {
153 DrawCustomUnzoomedShapesBuilder { shapes: Vec::new() }
154 }
155
156 pub fn maybe_draw(&self, g: &mut GfxCtx) -> bool {
158 let mut per_zoom = self.per_zoom.borrow_mut();
159
160 if g.canvas.cam_zoom >= per_zoom.min_zoom_for_detail {
161 return false;
162 }
163
164 let (zoom, idx) = per_zoom.discretize_zoom(g.canvas.cam_zoom);
165 let value = &mut per_zoom.draw_per_zoom[idx];
166 if value.is_none() {
167 let thickness = 1.0 / zoom;
168
169 let mut batch = GeomBatch::new();
170 for shape in &self.shapes {
171 (shape)(&mut batch, thickness);
172 }
173 *value = Some(g.upload(batch));
174 }
175 g.redraw(value.as_ref().unwrap());
176
177 true
178 }
179}
180
181impl DrawCustomUnzoomedShapesBuilder {
182 pub fn add_custom(&mut self, f: Box<dyn Fn(&mut GeomBatch, f64)>) {
183 self.shapes.push(f);
184 }
185
186 pub fn build(self, per_zoom: PerZoom) -> DrawCustomUnzoomedShapes {
187 DrawCustomUnzoomedShapes {
188 shapes: self.shapes,
189 per_zoom: RefCell::new(per_zoom),
190 }
191 }
192}
193
194pub struct PerZoom {
197 pub draw_per_zoom: Vec<Option<Drawable>>,
200 step_size: f64,
201 min_zoom_for_detail: f64,
202}
203
204impl PerZoom {
205 pub fn new(min_zoom_for_detail: f64, step_size: f64) -> Self {
206 let num_buckets = (min_zoom_for_detail / step_size) as usize;
207 Self {
208 draw_per_zoom: std::iter::repeat_with(|| None).take(num_buckets).collect(),
209 step_size,
210 min_zoom_for_detail,
211 }
212 }
213
214 pub fn discretize_zoom(&self, zoom: f64) -> (f64, usize) {
217 let bucket = (zoom / self.step_size).floor() as usize;
218 let rounded = (bucket.max(1) as f64) * self.step_size;
221 (rounded, bucket)
222 }
223}