1use std::cell::Cell;
2
3use geom::{Bounds, Polygon, Pt2D, Tessellation};
4
5use crate::assets::Assets;
6use crate::backend::{GfxCtxInnards, PrerenderInnards};
7use crate::{
8 Canvas, Color, Drawable, EventCtx, GeomBatch, Key, ScreenDims, ScreenPt, ScreenRectangle,
9 Style, Text,
10};
11
12pub(crate) const MAPSPACE_Z: f32 = 1.0;
18pub(crate) const SCREENSPACE_Z: f32 = 0.0;
19pub(crate) const MENU_Z: f32 = -1.0;
20pub(crate) const TOOLTIP_Z: f32 = -2.0;
21
22#[derive(Debug)]
23pub struct Uniforms {
24 pub transform: [f32; 3],
26 pub window: [f32; 3],
28}
29
30impl Uniforms {
31 pub fn new(canvas: &Canvas) -> Uniforms {
32 Uniforms {
33 transform: [
34 canvas.cam_x as f32,
35 canvas.cam_y as f32,
36 canvas.cam_zoom as f32,
37 ],
38 window: [
39 canvas.window_width as f32,
40 canvas.window_height as f32,
41 MAPSPACE_Z,
42 ],
43 }
44 }
45}
46
47pub struct GfxCtx<'a> {
48 pub(crate) inner: GfxCtxInnards<'a>,
49 uniforms: Uniforms,
50
51 screencap_mode: bool,
52 pub(crate) naming_hint: Option<String>,
53
54 pub canvas: &'a Canvas,
56 pub prerender: &'a Prerender,
57 style: &'a Style,
58
59 pub(crate) num_draw_calls: usize,
60 pub(crate) num_forks: usize,
61}
62
63impl<'a> GfxCtx<'a> {
64 pub(crate) fn new(
65 prerender: &'a Prerender,
66 canvas: &'a Canvas,
67 style: &'a Style,
68 screencap_mode: bool,
69 ) -> GfxCtx<'a> {
70 let uniforms = Uniforms::new(canvas);
71 GfxCtx {
72 inner: prerender.inner.draw_new_frame(),
73 uniforms,
74 canvas,
75 style,
76 prerender,
77 num_draw_calls: 0,
78 num_forks: 0,
79 screencap_mode,
80 naming_hint: None,
81 }
82 }
83
84 pub fn fork(
88 &mut self,
89 top_left_map: Pt2D,
90 top_left_screen: ScreenPt,
91 zoom: f64,
92 z: Option<f32>,
93 ) {
94 let cam_x = (top_left_map.x() * zoom) - top_left_screen.x;
96 let cam_y = (top_left_map.y() * zoom) - top_left_screen.y;
97
98 self.uniforms.transform = [cam_x as f32, cam_y as f32, zoom as f32];
99 self.uniforms.window = [
100 self.canvas.window_width as f32,
101 self.canvas.window_height as f32,
102 z.unwrap_or(SCREENSPACE_Z),
103 ];
104 self.num_forks += 1;
105 }
106
107 pub fn fork_screenspace(&mut self) {
108 self.uniforms.transform = [0.0, 0.0, 1.0];
109 self.uniforms.window = [
110 self.canvas.window_width as f32,
111 self.canvas.window_height as f32,
112 SCREENSPACE_Z,
113 ];
114 self.num_forks += 1;
115 }
116
117 pub fn unfork(&mut self) {
118 self.uniforms = Uniforms::new(self.canvas);
119 self.num_forks += 1;
120
121 }
123
124 pub fn clear(&mut self, color: Color) {
125 self.inner.clear(color);
126 }
127
128 pub fn draw_polygon<T: Into<Tessellation>>(&mut self, color: Color, poly: T) {
131 GeomBatch::from(vec![(color, poly)]).draw(self);
132 }
133
134 pub fn redraw(&mut self, obj: &Drawable) {
135 self.inner
136 .redraw(obj, &self.uniforms, &self.prerender.inner);
137 self.num_draw_calls += 1;
138
139 }
141
142 pub fn redraw_at(&mut self, top_left: ScreenPt, obj: &Drawable) {
143 self.fork(Pt2D::new(0.0, 0.0), top_left, 1.0, None);
144 self.redraw(obj);
145 self.unfork();
146 }
147
148 pub fn enable_clipping(&mut self, rect: ScreenRectangle) {
150 let scale_factor = self.prerender.get_scale_factor();
151 self.inner.enable_clipping(rect, scale_factor, self.canvas);
152 }
153
154 pub fn disable_clipping(&mut self) {
155 let scale_factor = self.prerender.get_scale_factor();
156 self.inner.disable_clipping(scale_factor, self.canvas);
157 }
158
159 pub fn draw_mouse_tooltip(&mut self, txt: Text) {
163 self.draw_tooltip_at(
164 txt,
165 ScreenPt::new(self.canvas.cursor.x, self.canvas.cursor.y + 20.0),
166 )
167 }
168
169 pub fn draw_tooltip_at(&mut self, txt: Text, center: ScreenPt) {
171 if txt.is_empty() {
172 return;
173 }
174
175 let pad = 5.0;
177 let txt = txt.default_fg(self.style.text_tooltip_color);
178 let txt_batch = txt.render(self);
179 let raw_dims = txt_batch.get_dims();
180 let dims = ScreenDims::new(raw_dims.width + 2.0 * pad, raw_dims.height + 2.0 * pad);
181
182 let pt = dims.top_left_for_corner(center, self.canvas);
184 let mut batch = GeomBatch::new();
185 batch.push(
187 Color::BLACK,
188 Polygon::rectangle(dims.width, dims.height).translate(pt.x, pt.y),
189 );
190 batch.append(txt_batch.translate(pt.x + pad, pt.y + pad));
191
192 self.uniforms.transform = [0.0, 0.0, 1.0];
194 self.uniforms.window = [
195 self.canvas.window_width as f32,
196 self.canvas.window_height as f32,
197 TOOLTIP_Z,
198 ];
199 self.num_forks += 1;
200 let clip = self
202 .inner
203 .take_clip(self.prerender.get_scale_factor(), self.canvas);
204 batch.draw(self);
205 self.unfork();
206 self.inner.restore_clip(clip);
207 }
208
209 pub fn get_screen_bounds(&self) -> Bounds {
210 self.canvas.get_screen_bounds()
211 }
212
213 pub fn screen_to_map(&self, pt: ScreenPt) -> Pt2D {
214 self.canvas.screen_to_map(pt)
215 }
216
217 pub fn get_cursor_in_map_space(&self) -> Option<Pt2D> {
218 self.canvas.get_cursor_in_map_space()
219 }
220
221 pub(crate) fn get_num_uploads(&self) -> usize {
222 self.prerender.num_uploads.get()
223 }
224
225 pub fn is_screencap(&self) -> bool {
226 self.screencap_mode
227 }
228
229 pub fn set_screencap_naming_hint(&mut self, hint: String) {
230 assert!(self.screencap_mode);
231 assert!(self.naming_hint.is_none());
232 self.naming_hint = Some(hint);
233 }
234
235 pub fn upload(&mut self, batch: GeomBatch) -> Drawable {
236 self.prerender.upload(batch)
237 }
238
239 pub fn default_line_height(&self) -> f64 {
241 *self.prerender.assets.default_line_height.borrow()
242 }
243
244 pub fn style(&self) -> &Style {
245 self.style
246 }
247
248 pub fn is_key_down(&self, key: Key) -> bool {
249 self.canvas.keys_held.contains(&key)
250 }
251}
252
253pub struct Prerender {
256 pub(crate) inner: PrerenderInnards,
257 pub(crate) assets: Assets,
258 pub(crate) num_uploads: Cell<usize>,
259 pub(crate) scale_factor: Cell<f64>,
260}
261
262impl Prerender {
263 pub fn upload(&self, batch: GeomBatch) -> Drawable {
264 self.actually_upload(true, batch)
265 }
266
267 pub(crate) fn upload_temporary(&self, batch: GeomBatch) -> Drawable {
268 self.actually_upload(false, batch)
269 }
270
271 pub fn get_total_bytes_uploaded(&self) -> usize {
272 self.inner.total_bytes_uploaded.get()
273 }
274
275 fn actually_upload(&self, permanent: bool, batch: GeomBatch) -> Drawable {
276 self.num_uploads.set(self.num_uploads.get() + 1);
277 self.inner.actually_upload(permanent, batch)
278
279 }
281
282 pub(crate) fn request_redraw(&self) {
283 self.inner.request_redraw()
284 }
285
286 pub fn get_scale_factor(&self) -> f64 {
287 self.scale_factor.get()
288 }
289
290 pub(crate) fn window_size(&self) -> ScreenDims {
291 self.inner.window_size(self.get_scale_factor())
292 }
293
294 pub(crate) fn window_resized(&self, new_size: ScreenDims) {
295 self.inner.window_resized(new_size, self.get_scale_factor())
296 }
297
298 pub fn assets_base_url(&self) -> Option<&str> {
299 self.assets.base_url()
300 }
301
302 pub fn assets_are_gzipped(&self) -> bool {
303 self.assets.are_gzipped()
304 }
305}
306
307impl std::convert::AsRef<Prerender> for GfxCtx<'_> {
308 fn as_ref(&self) -> &Prerender {
309 self.prerender
310 }
311}
312
313impl std::convert::AsRef<Prerender> for EventCtx<'_> {
314 fn as_ref(&self) -> &Prerender {
315 self.prerender
316 }
317}
318
319impl std::convert::AsRef<Prerender> for Prerender {
320 fn as_ref(&self) -> &Prerender {
321 self
322 }
323}