1 use crate::Canvas;
2 use geom::{trim_f64, Polygon, Pt2D};
3 use serde::{Deserialize, Serialize};
4 
5 /// ScreenPt is in units of logical pixels, as opposed to physical pixels.
6 #[derive(Debug, Clone, Copy, PartialEq)]
7 pub struct ScreenPt {
8     pub x: f64,
9     pub y: f64,
10 }
11 
12 impl ScreenPt {
new(x: f64, y: f64) -> ScreenPt13     pub fn new(x: f64, y: f64) -> ScreenPt {
14         ScreenPt { x, y }
15     }
16 
17     // The geom layer operates in map-space, but currently reusing lots of geom abstractions for
18     // screen-space.
to_pt(self) -> Pt2D19     pub fn to_pt(self) -> Pt2D {
20         Pt2D::new(self.x, self.y)
21     }
22 }
23 
24 impl From<winit::dpi::LogicalPosition<f64>> for ScreenPt {
from(lp: winit::dpi::LogicalPosition<f64>) -> ScreenPt25     fn from(lp: winit::dpi::LogicalPosition<f64>) -> ScreenPt {
26         ScreenPt { x: lp.x, y: lp.y }
27     }
28 }
29 
30 /// ScreenRectangle is in units of logical pixels, as opposed to physical pixels.
31 #[derive(Clone, Debug)]
32 pub struct ScreenRectangle {
33     pub x1: f64,
34     pub y1: f64,
35     pub x2: f64,
36     pub y2: f64,
37 }
38 
39 impl ScreenRectangle {
top_left(top_left: ScreenPt, dims: ScreenDims) -> ScreenRectangle40     pub fn top_left(top_left: ScreenPt, dims: ScreenDims) -> ScreenRectangle {
41         ScreenRectangle {
42             x1: top_left.x,
43             y1: top_left.y,
44             x2: top_left.x + dims.width,
45             y2: top_left.y + dims.height,
46         }
47     }
48 
placeholder() -> ScreenRectangle49     pub fn placeholder() -> ScreenRectangle {
50         ScreenRectangle {
51             x1: 0.0,
52             y1: 0.0,
53             x2: 0.0,
54             y2: 0.0,
55         }
56     }
57 
contains(&self, pt: ScreenPt) -> bool58     pub fn contains(&self, pt: ScreenPt) -> bool {
59         pt.x >= self.x1 && pt.x <= self.x2 && pt.y >= self.y1 && pt.y <= self.y2
60     }
61 
pt_to_percent(&self, pt: ScreenPt) -> Option<(f64, f64)>62     pub fn pt_to_percent(&self, pt: ScreenPt) -> Option<(f64, f64)> {
63         if self.contains(pt) {
64             Some((
65                 (pt.x - self.x1) / self.width(),
66                 (pt.y - self.y1) / self.height(),
67             ))
68         } else {
69             None
70         }
71     }
percent_to_pt(&self, x: f64, y: f64) -> ScreenPt72     pub fn percent_to_pt(&self, x: f64, y: f64) -> ScreenPt {
73         ScreenPt::new(self.x1 + x * self.width(), self.y1 + y * self.height())
74     }
75 
76     // TODO Remove these in favor of dims()
width(&self) -> f6477     pub fn width(&self) -> f64 {
78         self.x2 - self.x1
79     }
80 
height(&self) -> f6481     pub fn height(&self) -> f64 {
82         self.y2 - self.y1
83     }
84 
dims(&self) -> ScreenDims85     pub fn dims(&self) -> ScreenDims {
86         ScreenDims::new(self.x2 - self.x1, self.y2 - self.y1)
87     }
88 
center(&self) -> ScreenPt89     pub fn center(&self) -> ScreenPt {
90         ScreenPt::new((self.x1 + self.x2) / 2.0, (self.y1 + self.y2) / 2.0)
91     }
92 
to_polygon(&self) -> Polygon93     pub fn to_polygon(&self) -> Polygon {
94         Polygon::rectangle(self.width(), self.height()).translate(self.x1, self.y1)
95     }
96 }
97 
98 /// ScreenDims is in units of logical pixels, as opposed to physical pixels.
99 #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
100 pub struct ScreenDims {
101     pub width: f64,
102     pub height: f64,
103 }
104 
105 impl ScreenDims {
new(width: f64, height: f64) -> ScreenDims106     pub fn new(width: f64, height: f64) -> ScreenDims {
107         ScreenDims {
108             width: trim_f64(width),
109             height: trim_f64(height),
110         }
111     }
112 
top_left_for_corner(&self, corner: ScreenPt, canvas: &Canvas) -> ScreenPt113     pub fn top_left_for_corner(&self, corner: ScreenPt, canvas: &Canvas) -> ScreenPt {
114         // TODO Ideally also avoid covered canvas areas
115         if corner.x + self.width < canvas.window_width {
116             // corner.x is the left corner
117             if corner.y + self.height < canvas.window_height {
118                 // corner.y is the top corner
119                 corner
120             } else {
121                 // corner.y is the bottom corner
122                 ScreenPt::new(corner.x, corner.y - self.height)
123             }
124         } else {
125             // corner.x is the right corner
126             if corner.y + self.height < canvas.window_height {
127                 // corner.y is the top corner
128                 ScreenPt::new(corner.x - self.width, corner.y)
129             } else {
130                 // corner.y is the bottom corner
131                 ScreenPt::new(corner.x - self.width, corner.y - self.height)
132             }
133         }
134     }
135 
scaled(&self, factor: f64) -> ScreenDims136     pub fn scaled(&self, factor: f64) -> ScreenDims {
137         ScreenDims::new(self.width * factor, self.height * factor)
138     }
139 }
140 
141 impl From<winit::dpi::LogicalSize<f64>> for ScreenDims {
from(size: winit::dpi::LogicalSize<f64>) -> ScreenDims142     fn from(size: winit::dpi::LogicalSize<f64>) -> ScreenDims {
143         ScreenDims {
144             width: size.width,
145             height: size.height,
146         }
147     }
148 }
149 
150 impl From<ScreenDims> for winit::dpi::LogicalSize<f64> {
from(dims: ScreenDims) -> winit::dpi::LogicalSize<f64>151     fn from(dims: ScreenDims) -> winit::dpi::LogicalSize<f64> {
152         winit::dpi::LogicalSize::new(dims.width, dims.height)
153     }
154 }
155