Skip to main content

ggplot_rs/geom/
ribbon.rs

1use crate::aes::Aesthetic;
2use crate::coord::Coord;
3use crate::data::DataFrame;
4use crate::position::identity::PositionIdentity;
5use crate::position::Position;
6use crate::render::backend::{DrawBackend, 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/// Ribbon geometry — filled band between ymin and ymax.
16pub struct GeomRibbon {
17    pub fill: (u8, u8, u8),
18    pub alpha: f64,
19}
20
21impl Default for GeomRibbon {
22    fn default() -> Self {
23        GeomRibbon {
24            fill: (97, 156, 255),
25            alpha: 0.3,
26        }
27    }
28}
29
30impl Geom for GeomRibbon {
31    fn draw(
32        &self,
33        data: &DataFrame,
34        coord: &dyn Coord,
35        scales: &ScaleSet,
36        _theme: &Theme,
37        backend: &mut dyn DrawBackend,
38    ) -> Result<(), RenderError> {
39        let x_col = data
40            .column("x")
41            .ok_or(RenderError::MissingAesthetic("x".into()))?;
42        let ymin_col = data
43            .column("ymin")
44            .ok_or(RenderError::MissingAesthetic("ymin".into()))?;
45        let ymax_col = data
46            .column("ymax")
47            .ok_or(RenderError::MissingAesthetic("ymax".into()))?;
48
49        let plot_area = backend.plot_area();
50        let x_scale = scales.get(&Aesthetic::X);
51        let y_scale = scales.get(&Aesthetic::Y);
52
53        let mut upper: Vec<(f64, f64)> = Vec::new();
54        let mut lower: Vec<(f64, f64)> = Vec::new();
55
56        for i in 0..data.nrows() {
57            let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
58            let ny_max = y_scale.map(|s| s.map(&ymax_col[i])).unwrap_or(0.0);
59            let ny_min = y_scale.map(|s| s.map(&ymin_col[i])).unwrap_or(0.0);
60            upper.push(coord.transform((nx, ny_max), &plot_area));
61            lower.push(coord.transform((nx, ny_min), &plot_area));
62        }
63
64        let mut polygon = upper;
65        lower.reverse();
66        polygon.extend(lower);
67
68        if polygon.len() >= 3 {
69            backend.draw_polygon(
70                &polygon,
71                &RectStyle {
72                    fill: Some(self.fill),
73                    stroke: None,
74                    stroke_width: 0.0,
75                    alpha: self.alpha,
76                    clip: true,
77                },
78            )?;
79        }
80
81        Ok(())
82    }
83
84    fn required_aes(&self) -> Vec<Aesthetic> {
85        vec![Aesthetic::X, Aesthetic::Ymin, Aesthetic::Ymax]
86    }
87
88    fn default_stat(&self) -> Box<dyn Stat> {
89        Box::new(StatIdentity)
90    }
91    fn default_position(&self) -> Box<dyn Position> {
92        Box::new(PositionIdentity)
93    }
94    fn default_params(&self) -> GeomParams {
95        GeomParams::default()
96    }
97    fn name(&self) -> &str {
98        "ribbon"
99    }
100
101    fn set_series_color(&mut self, color: (u8, u8, u8)) {
102        self.fill = color;
103    }
104}