Skip to main content

scene_management_dso/
graphics_utils.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::display_metrics::DisplayMetrics;
6use crate::lib::{Position, Size};
7
8/// [`ScreenCoordinates`] represents a point on the screen. It can be created from pixels, pips, or
9/// millimeters and the coordinate vales can be retrieved in any of those units.
10#[derive(Clone, Copy, Debug)]
11pub struct ScreenCoordinates {
12    x_pixels: f32,
13    y_pixels: f32,
14    display_metrics: DisplayMetrics,
15}
16
17impl ScreenCoordinates {
18    /// Create a [`ScreenCoordinates`] from the given pixel coordinates
19    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    /// Create a [`ScreenCoordinates`] from the given pip coordinates
24    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    /// Create a [`ScreenCoordinates`] from the given millimeter coordinates
33    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    /// This takes a Position struct and maps it to ScreenCoordinates
42    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    /// Retrieve the x and y positions as pixel values
47    pub fn pixels(&self) -> (f32, f32) {
48        (self.x_pixels, self.y_pixels)
49    }
50
51    /// Retrieve the x and y positions as pip values
52    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    /// Retrieve the x and y positions as millimeter values
60    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    /// Retrieve the x and y positions as a [`Position`]
66    pub fn position(&self) -> Position {
67        Position { x: self.x_pixels, y: self.y_pixels }
68    }
69}
70
71/// [`ScreenSize`] represents a size on the screen. It can be created from pixels, pips, or
72/// millimeters and can be retrieved in any of those units.
73#[derive(Clone, Copy, Debug)]
74pub struct ScreenSize {
75    width_pixels: f32,
76    height_pixels: f32,
77    display_metrics: DisplayMetrics,
78}
79
80impl ScreenSize {
81    /// Create a [`ScreenSize`] from the given pixel coordinates
82    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    /// Create a [`ScreenSize`] from the given pip coordinates
87    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    /// Create a [`ScreenSize`] from the given millimeter coordinates
96    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    /// This takes a Position struct and maps it to ScreenSize
105    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    /// Retrieve the width and height as pixel values
110    pub fn pixels(&self) -> (f32, f32) {
111        (self.width_pixels, self.height_pixels)
112    }
113
114    /// Retrieve the width and height as pip values
115    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    /// Retrieve the width and height as millimeter values
123    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    /// Retrieve the width and height as a [`Size`]
132    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}