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::step::StepDirection;
14use super::{Geom, GeomParams};
15
16pub(crate) enum BandShape<'a> {
18 Linear,
20 Step(&'a StepDirection),
22}
23
24pub(crate) fn stepped(raw: &[(f64, f64)], direction: &StepDirection) -> Vec<(f64, f64)> {
26 let mut out = Vec::with_capacity(raw.len() * 2);
27 for (j, &(x, y)) in raw.iter().enumerate() {
28 if j > 0 {
29 let (px, py) = raw[j - 1];
30 match direction {
31 StepDirection::Hv => out.push((x, py)),
32 StepDirection::Vh => out.push((px, y)),
33 StepDirection::Mid => {
34 let m = 0.5 * (px + x);
35 out.push((m, py));
36 out.push((m, y));
37 }
38 }
39 }
40 out.push((x, y));
41 }
42 out
43}
44
45pub(crate) fn draw_bands(
49 data: &DataFrame,
50 coord: &dyn Coord,
51 scales: &ScaleSet,
52 backend: &mut dyn DrawBackend,
53 fill: (u8, u8, u8),
54 alpha: f64,
55 shape: BandShape<'_>,
56) -> Result<(), RenderError> {
57 let x_col = data
58 .column("x")
59 .ok_or(RenderError::MissingAesthetic("x".into()))?;
60 let ymin_col = data
61 .column("ymin")
62 .ok_or(RenderError::MissingAesthetic("ymin".into()))?;
63 let ymax_col = data
64 .column("ymax")
65 .ok_or(RenderError::MissingAesthetic("ymax".into()))?;
66 let plot_area = backend.plot_area();
67 let y_scale = scales.get(&Aesthetic::Y);
68 let xm = super::support::XMapper::new(data, scales);
69
70 for rows in super::support::row_groups(data) {
71 let mut pts: Vec<(f64, f64, f64)> = rows
72 .iter()
73 .map(|&i| {
74 (
75 xm.map(&x_col[i], i),
76 y_scale.map(|s| s.map(&ymin_col[i])).unwrap_or(0.0),
77 y_scale.map(|s| s.map(&ymax_col[i])).unwrap_or(0.0),
78 )
79 })
80 .filter(|(x, a, b)| x.is_finite() && a.is_finite() && b.is_finite())
81 .collect();
82 if pts.len() < 2 {
83 continue;
84 }
85 pts.sort_by(|a, b| a.0.total_cmp(&b.0));
86 let upper: Vec<(f64, f64)> = pts.iter().map(|p| (p.0, p.2)).collect();
87 let lower: Vec<(f64, f64)> = pts.iter().map(|p| (p.0, p.1)).collect();
88 let (upper, mut lower) = match shape {
89 BandShape::Linear => (upper, lower),
90 BandShape::Step(dir) => (stepped(&upper, dir), stepped(&lower, dir)),
91 };
92 lower.reverse();
93 let polygon: Vec<(f64, f64)> = upper
94 .into_iter()
95 .chain(lower)
96 .map(|p| coord.transform(p, &plot_area))
97 .collect();
98
99 let first = rows[0];
100 let band_fill = data
101 .column("fill")
102 .and_then(|c| scales.map_color(&Aesthetic::Fill, &c[first]))
103 .unwrap_or(fill);
104 let series = super::series_key(data, first);
105 super::set_mark(backend, series.clone(), None, series, None);
106 super::set_mark_formatted(backend, scales, None, &Aesthetic::Y, None);
107 backend.draw_polygon(
108 &polygon,
109 &RectStyle {
110 fill: Some(band_fill),
111 stroke: None,
112 stroke_width: 0.0,
113 alpha,
114 clip: true,
115 },
116 )?;
117 }
118 super::clear_mark(backend);
119 Ok(())
120}
121
122pub struct GeomRibbon {
124 pub fill: (u8, u8, u8),
125 pub alpha: f64,
126}
127
128impl Default for GeomRibbon {
129 fn default() -> Self {
130 GeomRibbon {
131 fill: (97, 156, 255),
132 alpha: 0.3,
133 }
134 }
135}
136
137impl Geom for GeomRibbon {
138 fn draw(
139 &self,
140 data: &DataFrame,
141 coord: &dyn Coord,
142 scales: &ScaleSet,
143 _theme: &Theme,
144 backend: &mut dyn DrawBackend,
145 ) -> Result<(), RenderError> {
146 draw_bands(
147 data,
148 coord,
149 scales,
150 backend,
151 self.fill,
152 self.alpha,
153 BandShape::Linear,
154 )
155 }
156
157 fn required_aes(&self) -> Vec<Aesthetic> {
158 vec![Aesthetic::X, Aesthetic::Ymin, Aesthetic::Ymax]
159 }
160
161 fn default_stat(&self) -> Box<dyn Stat> {
162 Box::new(StatIdentity)
163 }
164 fn default_position(&self) -> Box<dyn Position> {
165 Box::new(PositionIdentity)
166 }
167 fn default_params(&self) -> GeomParams {
168 GeomParams::default()
169 }
170 fn name(&self) -> &str {
171 "ribbon"
172 }
173
174 fn set_series_color(&mut self, color: (u8, u8, u8)) {
175 self.fill = color;
176 }
177}
178
179pub struct GeomStepribbon {
184 pub fill: (u8, u8, u8),
185 pub alpha: f64,
186 pub direction: StepDirection,
187}
188
189impl Default for GeomStepribbon {
190 fn default() -> Self {
191 GeomStepribbon {
192 fill: (97, 156, 255),
193 alpha: 0.3,
194 direction: StepDirection::Hv,
195 }
196 }
197}
198
199impl GeomStepribbon {
200 pub fn new(direction: StepDirection) -> Self {
202 GeomStepribbon {
203 direction,
204 ..Default::default()
205 }
206 }
207}
208
209impl Geom for GeomStepribbon {
210 fn draw(
211 &self,
212 data: &DataFrame,
213 coord: &dyn Coord,
214 scales: &ScaleSet,
215 _theme: &Theme,
216 backend: &mut dyn DrawBackend,
217 ) -> Result<(), RenderError> {
218 draw_bands(
219 data,
220 coord,
221 scales,
222 backend,
223 self.fill,
224 self.alpha,
225 BandShape::Step(&self.direction),
226 )
227 }
228
229 fn required_aes(&self) -> Vec<Aesthetic> {
230 vec![Aesthetic::X, Aesthetic::Ymin, Aesthetic::Ymax]
231 }
232
233 fn default_stat(&self) -> Box<dyn Stat> {
234 Box::new(StatIdentity)
235 }
236 fn default_position(&self) -> Box<dyn Position> {
237 Box::new(PositionIdentity)
238 }
239 fn default_params(&self) -> GeomParams {
240 GeomParams::default()
241 }
242 fn name(&self) -> &str {
243 "stepribbon"
244 }
245
246 fn allows_infinite(&self) -> bool {
247 true
248 }
249
250 fn set_series_color(&mut self, color: (u8, u8, u8)) {
251 self.fill = color;
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn step_corners() {
261 let raw = [(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)];
262 assert_eq!(
263 stepped(&raw, &StepDirection::Hv),
264 vec![(0.0, 1.0), (1.0, 1.0), (1.0, 2.0), (2.0, 2.0), (2.0, 3.0)]
265 );
266 assert_eq!(
267 stepped(&raw, &StepDirection::Vh),
268 vec![(0.0, 1.0), (0.0, 2.0), (1.0, 2.0), (1.0, 3.0), (2.0, 3.0)]
269 );
270 assert_eq!(
271 stepped(&raw[..2], &StepDirection::Mid),
272 vec![(0.0, 1.0), (0.5, 1.0), (0.5, 2.0), (1.0, 2.0)]
273 );
274 }
275}