1use crate::display_metrics::DisplayMetrics;
6use crate::lib::{Position, Size};
7
8#[derive(Clone, Copy, Debug)]
11pub struct ScreenCoordinates {
12 x_pixels: f32,
13 y_pixels: f32,
14 display_metrics: DisplayMetrics,
15}
16
17impl ScreenCoordinates {
18 pub fn from_pixels(x: f32, y: f32, display_metrics: DisplayMetrics) -> Self {
20 Self { x_pixels: x, y_pixels: y, display_metrics }
21 }
22
23 pub fn from_pips(x: f32, y: f32, display_metrics: DisplayMetrics) -> Self {
25 Self {
26 x_pixels: x * display_metrics.pixels_per_pip(),
27 y_pixels: y * display_metrics.pixels_per_pip(),
28 display_metrics,
29 }
30 }
31
32 pub fn from_mm(x: f32, y: f32, display_metrics: DisplayMetrics) -> Self {
34 Self {
35 x_pixels: x * display_metrics.pixels_per_pip() * display_metrics.mm_per_pip(),
36 y_pixels: y * display_metrics.pixels_per_pip() * display_metrics.mm_per_pip(),
37 display_metrics,
38 }
39 }
40
41 pub fn from_position(position: &Position, display_metrics: DisplayMetrics) -> Self {
43 Self { x_pixels: position.x, y_pixels: position.y, display_metrics }
44 }
45
46 pub fn pixels(&self) -> (f32, f32) {
48 (self.x_pixels, self.y_pixels)
49 }
50
51 pub fn pips(&self) -> (f32, f32) {
53 (
54 self.x_pixels / self.display_metrics.pixels_per_pip(),
55 self.y_pixels / self.display_metrics.pixels_per_pip(),
56 )
57 }
58
59 pub fn mm(&self) -> (f32, f32) {
61 let (x_pips, y_pips) = self.pips();
62 (x_pips / self.display_metrics.mm_per_pip(), y_pips / self.display_metrics.mm_per_pip())
63 }
64
65 pub fn position(&self) -> Position {
67 Position { x: self.x_pixels, y: self.y_pixels }
68 }
69}
70
71#[derive(Clone, Copy, Debug)]
74pub struct ScreenSize {
75 width_pixels: f32,
76 height_pixels: f32,
77 display_metrics: DisplayMetrics,
78}
79
80impl ScreenSize {
81 pub fn from_pixels(width: f32, height: f32, display_metrics: DisplayMetrics) -> Self {
83 Self { width_pixels: width, height_pixels: height, display_metrics }
84 }
85
86 pub fn from_pips(width: f32, height: f32, display_metrics: DisplayMetrics) -> Self {
88 Self {
89 width_pixels: width * display_metrics.pixels_per_pip(),
90 height_pixels: height * display_metrics.pixels_per_pip(),
91 display_metrics,
92 }
93 }
94
95 pub fn from_mm(width: f32, height: f32, display_metrics: DisplayMetrics) -> Self {
97 Self {
98 width_pixels: width * display_metrics.pixels_per_pip() * display_metrics.mm_per_pip(),
99 height_pixels: height * display_metrics.pixels_per_pip() * display_metrics.mm_per_pip(),
100 display_metrics,
101 }
102 }
103
104 pub fn from_size(size: &Size, display_metrics: DisplayMetrics) -> Self {
106 Self { width_pixels: size.width, height_pixels: size.height, display_metrics }
107 }
108
109 pub fn pixels(&self) -> (f32, f32) {
111 (self.width_pixels, self.height_pixels)
112 }
113
114 pub fn pips(&self) -> (f32, f32) {
116 (
117 self.width_pixels / self.display_metrics.pixels_per_pip(),
118 self.height_pixels / self.display_metrics.pixels_per_pip(),
119 )
120 }
121
122 pub fn mm(&self) -> (f32, f32) {
124 let (width_pips, height_pips) = self.pips();
125 (
126 width_pips / self.display_metrics.mm_per_pip(),
127 height_pips / self.display_metrics.mm_per_pip(),
128 )
129 }
130
131 pub fn size(&self) -> Size {
133 Size { width: self.width_pixels, height: self.height_pixels }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[inline]
142 fn assert_close_enough(left: (f32, f32), right: (f32, f32)) {
143 const EPSILON: f32 = 0.0001;
144 assert!(left.0 > right.0 - EPSILON, "Left: {:?} Right: {:?}", left, right);
145 assert!(left.0 < right.0 + EPSILON, "Left: {:?} Right: {:?}", left, right);
146 assert!(left.1 > right.1 - EPSILON, "Left: {:?} Right: {:?}", left, right);
147 assert!(left.1 < right.1 + EPSILON, "Left: {:?} Right: {:?}", left, right);
148 }
149
150 #[inline]
151 fn assert_position_close_enough(left: Position, right: Position) {
152 assert_close_enough((left.x, left.y), (right.x, right.y));
153 }
154
155 #[fuchsia::test]
156 fn test_pixel_constructor() {
157 let display_metrics =
158 DisplayMetrics::new(Size { width: 1000.0, height: 500.0 }, Some(10.0), None, None);
159 let coords = ScreenCoordinates::from_pixels(100.0, 100.0, display_metrics);
160
161 assert_close_enough(coords.pixels(), (100.0, 100.0));
162 assert_close_enough(coords.pips(), (52.2449, 52.2449));
163 assert_close_enough(coords.mm(), (272.9529, 272.9529));
164 assert_position_close_enough(coords.position(), Position { x: 100.0, y: 100.0 });
165 }
166
167 #[fuchsia::test]
168 fn test_pip_constructor() {
169 let display_metrics =
170 DisplayMetrics::new(Size { width: 1000.0, height: 500.0 }, Some(10.0), None, None);
171 let coords = ScreenCoordinates::from_pips(52.2449, 52.2449, display_metrics);
172
173 assert_close_enough(coords.pixels(), (100.0, 100.0));
174 assert_close_enough(coords.pips(), (52.2449, 52.2449));
175 assert_close_enough(coords.mm(), (272.9529, 272.9529));
176 assert_position_close_enough(coords.position(), Position { x: 100.0, y: 100.0 });
177 }
178
179 #[fuchsia::test]
180 fn test_mm_constructor() {
181 let display_metrics =
182 DisplayMetrics::new(Size { width: 1000.0, height: 500.0 }, Some(10.0), None, None);
183 let coords = ScreenCoordinates::from_mm(272.9529, 272.9529, display_metrics);
184
185 assert_close_enough(coords.pixels(), (100.0, 100.0));
186 assert_close_enough(coords.pips(), (52.2449, 52.2449));
187 assert_close_enough(coords.mm(), (272.9529, 272.9529));
188 assert_position_close_enough(coords.position(), Position { x: 100.0, y: 100.0 });
189 }
190
191 #[fuchsia::test]
192 fn test_position_constructor() {
193 let display_metrics =
194 DisplayMetrics::new(Size { width: 1000.0, height: 500.0 }, Some(10.0), None, None);
195 let coords =
196 ScreenCoordinates::from_position(&Position { x: 100.0, y: 100.0 }, display_metrics);
197
198 assert_close_enough(coords.pixels(), (100.0, 100.0));
199 assert_close_enough(coords.pips(), (52.2449, 52.2449));
200 assert_close_enough(coords.mm(), (272.9529, 272.9529));
201 assert_position_close_enough(coords.position(), Position { x: 100.0, y: 100.0 });
202 }
203}