Skip to main content

densa_chart/
plot.rs

1use densa_core::Rect;
2
3use crate::Axis;
4
5/// Where a chart draws and what it shows: an area of the canvas, with `x` running left to
6/// right across it and `y` bottom to top.
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Plot {
9    area: Rect,
10    x: Axis,
11    y: Axis,
12}
13
14impl Plot {
15    /// A plot of `x` and `y` drawn in `area`.
16    pub fn new(area: Rect, x: Axis, y: Axis) -> Self {
17        Self { area, x, y }
18    }
19
20    /// The area drawn in.
21    pub fn area(&self) -> Rect {
22        self.area
23    }
24
25    /// The horizontal axis.
26    pub fn x(&self) -> Axis {
27        self.x
28    }
29
30    /// The vertical axis.
31    pub fn y(&self) -> Axis {
32        self.y
33    }
34
35    /// The plot with another horizontal axis.
36    #[must_use]
37    pub fn with_x(self, x: Axis) -> Self {
38        Self { x, ..self }
39    }
40
41    /// The plot with another vertical axis.
42    #[must_use]
43    pub fn with_y(self, y: Axis) -> Self {
44        Self { y, ..self }
45    }
46
47    /// The canvas `x` of `value`.
48    pub fn x_px(&self, value: f64) -> f32 {
49        // Lossless enough: pixels.
50        self.x_at(value) as f32
51    }
52
53    /// The canvas `y` of `value`.
54    pub fn y_px(&self, value: f64) -> f32 {
55        // Lossless enough: pixels.
56        self.y_at(value) as f32
57    }
58
59    /// The value at canvas `x`.
60    pub fn x_value(&self, px: f32) -> f64 {
61        let width = f64::from(self.area.width());
62        self.x
63            .value_at((f64::from(px) - f64::from(self.area.x0)) / width)
64    }
65
66    /// The value at canvas `y`.
67    pub fn y_value(&self, px: f32) -> f64 {
68        let height = f64::from(self.area.height());
69        self.y
70            .value_at((f64::from(self.area.y1) - f64::from(px)) / height)
71    }
72
73    /// The canvas `x` of `value`, unrounded.
74    pub(crate) fn x_at(&self, value: f64) -> f64 {
75        f64::from(self.area.x0) + self.x.fraction(value) * f64::from(self.area.width())
76    }
77
78    /// The canvas `y` of `value`, unrounded.
79    pub(crate) fn y_at(&self, value: f64) -> f64 {
80        f64::from(self.area.y1) - self.y.fraction(value) * f64::from(self.area.height())
81    }
82
83    /// How many pixels `distance` along the horizontal axis takes.
84    pub(crate) fn x_length(&self, distance: f64) -> f64 {
85        distance / self.x.span() * f64::from(self.area.width())
86    }
87
88    /// `y` held within a pixel of the area, so far-off values stay finite and small.
89    pub(crate) fn clamp_y(&self, y: f64) -> f64 {
90        let (top, bottom) = (f64::from(self.area.y0), f64::from(self.area.y1));
91        y.clamp(top - 1.0, bottom + 1.0)
92    }
93}