Skip to main content

ggplot_rs/geom/
area.rs

1use crate::aes::Aesthetic;
2use crate::coord::Coord;
3use crate::data::{DataFrame, Value};
4use crate::position::identity::PositionIdentity;
5use crate::position::Position;
6use crate::render::backend::{DrawBackend, LineStyle, Linetype, RectStyle};
7use crate::render::RenderError;
8use crate::scale::ScaleSet;
9use crate::stat::identity::StatIdentity;
10use crate::stat::Stat;
11use crate::theme::Theme;
12
13use super::{Geom, GeomParams};
14
15/// Area geometry — filled polygon from line to x-axis baseline.
16pub struct GeomArea {
17    pub fill: (u8, u8, u8),
18    pub color: (u8, u8, u8),
19    pub alpha: f64,
20    pub line_width: f64,
21}
22
23impl Default for GeomArea {
24    fn default() -> Self {
25        GeomArea {
26            fill: (97, 156, 255),
27            color: (50, 50, 50),
28            alpha: 0.4,
29            line_width: 1.0,
30        }
31    }
32}
33
34impl Geom for GeomArea {
35    fn draw(
36        &self,
37        data: &DataFrame,
38        coord: &dyn Coord,
39        scales: &ScaleSet,
40        _theme: &Theme,
41        backend: &mut dyn DrawBackend,
42    ) -> Result<(), RenderError> {
43        let x_col = data
44            .column("x")
45            .ok_or(RenderError::MissingAesthetic("x".into()))?;
46        let y_col = data
47            .column("y")
48            .ok_or(RenderError::MissingAesthetic("y".into()))?;
49
50        let plot_area = backend.plot_area();
51        let x_scale = scales.get(&Aesthetic::X);
52        let y_scale = scales.get(&Aesthetic::Y);
53
54        let base_ny = y_scale.map(|s| s.map(&Value::Float(0.0))).unwrap_or(0.0);
55
56        let mut upper: Vec<(f64, f64)> = Vec::new();
57        let mut lower: Vec<(f64, f64)> = Vec::new();
58
59        for i in 0..data.nrows() {
60            let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
61            let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
62            upper.push(coord.transform((nx, ny), &plot_area));
63            lower.push(coord.transform((nx, base_ny), &plot_area));
64        }
65
66        // Build polygon: upper left-to-right, then lower right-to-left
67        let mut polygon = upper.clone();
68        lower.reverse();
69        polygon.extend(lower);
70
71        if polygon.len() >= 3 {
72            backend.draw_polygon(
73                &polygon,
74                &RectStyle {
75                    fill: Some(self.fill),
76                    stroke: None,
77                    stroke_width: 0.0,
78                    alpha: self.alpha,
79                    clip: true,
80                },
81            )?;
82        }
83
84        // Draw the top line
85        if upper.len() >= 2 {
86            backend.draw_line(
87                &upper,
88                &LineStyle {
89                    color: self.color,
90                    alpha: 1.0,
91                    width: self.line_width,
92                    linetype: Linetype::Solid,
93                },
94            )?;
95        }
96
97        Ok(())
98    }
99
100    fn required_aes(&self) -> Vec<Aesthetic> {
101        vec![Aesthetic::X, Aesthetic::Y]
102    }
103
104    fn default_stat(&self) -> Box<dyn Stat> {
105        Box::new(StatIdentity)
106    }
107    fn default_position(&self) -> Box<dyn Position> {
108        Box::new(PositionIdentity)
109    }
110    fn default_params(&self) -> GeomParams {
111        GeomParams::default()
112    }
113    fn name(&self) -> &str {
114        "area"
115    }
116
117    fn set_series_color(&mut self, color: (u8, u8, u8)) {
118        self.fill = color;
119    }
120}