gpui_liveplot/
transform.rs1use crate::geom::{Point, ScreenPoint, ScreenRect};
3use crate::view::{Range, Viewport};
4
5const MIN_SPAN: f64 = 1e-12;
6
7#[derive(Debug, Clone)]
9pub(crate) struct Transform {
10 viewport: Viewport,
11 screen: ScreenRect,
12 x_axis: Range,
13 y_axis: Range,
14}
15
16impl Transform {
17 pub(crate) fn new(viewport: Viewport, screen: ScreenRect) -> Option<Self> {
19 if !screen.is_valid() {
20 return None;
21 }
22 let x_axis = map_range(viewport.x.with_min_span(MIN_SPAN))?;
23 let y_axis = map_range(viewport.y.with_min_span(MIN_SPAN))?;
24 Some(Self {
25 viewport,
26 screen,
27 x_axis,
28 y_axis,
29 })
30 }
31
32 pub(crate) fn viewport(&self) -> Viewport {
34 self.viewport
35 }
36
37 pub(crate) fn screen(&self) -> ScreenRect {
39 self.screen
40 }
41
42 pub(crate) fn data_to_screen(&self, point: Point) -> Option<ScreenPoint> {
44 if !point.x.is_finite() || !point.y.is_finite() {
45 return None;
46 }
47 let x_norm = (point.x - self.x_axis.min) / self.x_axis.span();
48 let y_norm = (point.y - self.y_axis.min) / self.y_axis.span();
49 let sx = self.screen.min.x as f64 + x_norm * self.screen.width() as f64;
50 let sy = self.screen.max.y as f64 - y_norm * self.screen.height() as f64;
51 Some(ScreenPoint::new(sx as f32, sy as f32))
52 }
53
54 pub(crate) fn screen_to_data(&self, point: ScreenPoint) -> Option<Point> {
56 let x_norm = (point.x as f64 - self.screen.min.x as f64) / self.screen.width() as f64;
57 let y_norm = (self.screen.max.y as f64 - point.y as f64) / self.screen.height() as f64;
58 let x_axis = self.x_axis.min + x_norm * self.x_axis.span();
59 let y_axis = self.y_axis.min + y_norm * self.y_axis.span();
60 Some(Point::new(x_axis, y_axis))
61 }
62}
63
64fn map_range(range: Range) -> Option<Range> {
65 if !range.is_finite() {
66 return None;
67 }
68 Some(range)
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn linear_roundtrip() {
77 let viewport = Viewport::new(Range::new(0.0, 10.0), Range::new(0.0, 10.0));
78 let screen = ScreenRect::new(ScreenPoint::new(0.0, 0.0), ScreenPoint::new(100.0, 100.0));
79 let transform = Transform::new(viewport, screen).expect("valid transform");
80 let point = Point::new(5.0, 7.5);
81 let screen_point = transform.data_to_screen(point).unwrap();
82 let roundtrip = transform.screen_to_data(screen_point).unwrap();
83 assert!((roundtrip.x - point.x).abs() < 1e-9);
84 assert!((roundtrip.y - point.y).abs() < 1e-9);
85 }
86}