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, LineStyle, Linetype};
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
15pub enum StepDirection {
17 Hv,
19 Vh,
21}
22
23pub struct GeomStep {
25 pub color: (u8, u8, u8),
26 pub width: f64,
27 pub alpha: f64,
28 pub direction: StepDirection,
29}
30
31impl Default for GeomStep {
32 fn default() -> Self {
33 GeomStep {
34 color: (0, 0, 0),
35 width: 1.5,
36 alpha: 1.0,
37 direction: StepDirection::Hv,
38 }
39 }
40}
41
42impl Geom for GeomStep {
43 fn draw(
44 &self,
45 data: &DataFrame,
46 coord: &dyn Coord,
47 scales: &ScaleSet,
48 _theme: &Theme,
49 backend: &mut dyn DrawBackend,
50 ) -> Result<(), RenderError> {
51 let x_col = data
52 .column("x")
53 .ok_or(RenderError::MissingAesthetic("x".into()))?;
54 let y_col = data
55 .column("y")
56 .ok_or(RenderError::MissingAesthetic("y".into()))?;
57 let color_col = data.column("color");
58
59 let plot_area = backend.plot_area();
60 let x_scale = scales.get(&Aesthetic::X);
61 let y_scale = scales.get(&Aesthetic::Y);
62
63 let mut sorted: Vec<usize> = (0..data.nrows()).collect();
65 sorted.sort_by(|&a, &b| {
66 let xa = x_col[a].as_f64().unwrap_or(0.0);
67 let xb = x_col[b].as_f64().unwrap_or(0.0);
68 xa.partial_cmp(&xb).unwrap_or(std::cmp::Ordering::Equal)
69 });
70
71 let raw: Vec<(f64, f64)> = sorted
73 .iter()
74 .map(|&i| {
75 let nx = x_scale.map(|s| s.map(&x_col[i])).unwrap_or(0.0);
76 let ny = y_scale.map(|s| s.map(&y_col[i])).unwrap_or(0.0);
77 (nx, ny)
78 })
79 .collect();
80
81 let mut step_points: Vec<(f64, f64)> = Vec::new();
83 for (j, &(nx, ny)) in raw.iter().enumerate() {
84 if j > 0 {
85 let (prev_nx, prev_ny) = raw[j - 1];
86 match self.direction {
87 StepDirection::Hv => step_points.push((nx, prev_ny)),
88 StepDirection::Vh => step_points.push((prev_nx, ny)),
89 }
90 }
91 step_points.push((nx, ny));
92 }
93
94 let points: Vec<(f64, f64)> = step_points
95 .iter()
96 .map(|&(nx, ny)| coord.transform((nx, ny), &plot_area))
97 .collect();
98
99 let line_color = color_col
100 .and_then(|cc| {
101 if cc.is_empty() {
102 None
103 } else {
104 scales.map_color(&Aesthetic::Color, &cc[0])
105 }
106 })
107 .unwrap_or(self.color);
108
109 if points.len() >= 2 {
110 backend.draw_line(
111 &points,
112 &LineStyle {
113 color: line_color,
114 alpha: self.alpha,
115 width: self.width,
116 linetype: Linetype::Solid,
117 },
118 )?;
119 }
120
121 Ok(())
122 }
123
124 fn required_aes(&self) -> Vec<Aesthetic> {
125 vec![Aesthetic::X, Aesthetic::Y]
126 }
127
128 fn default_stat(&self) -> Box<dyn Stat> {
129 Box::new(StatIdentity)
130 }
131
132 fn default_position(&self) -> Box<dyn Position> {
133 Box::new(PositionIdentity)
134 }
135
136 fn default_params(&self) -> GeomParams {
137 GeomParams::default()
138 }
139
140 fn name(&self) -> &str {
141 "step"
142 }
143
144 fn set_series_color(&mut self, color: (u8, u8, u8)) {
145 self.color = color;
146 }
147}