ggplot_rs/position/
fill.rs1use std::borrow::Cow;
2use std::collections::HashMap;
3
4use crate::data::{DataFrame, Value};
5
6use super::{Position, PositionParams};
7
8pub struct PositionFill;
10
11impl Position for PositionFill {
12 fn compute(&self, data: &mut DataFrame, _params: &PositionParams) {
13 let x_col = match data.column("x") {
14 Some(c) => c.to_vec(),
15 None => return,
16 };
17 let y_col = match data.column("y") {
18 Some(c) => c.to_vec(),
19 None => return,
20 };
21 super::preserve_raw_y(data, &y_col);
22
23 let mut x_totals: HashMap<Cow<'_, str>, f64> = HashMap::new();
25 for (x, y) in x_col.iter().zip(y_col.iter()) {
26 *x_totals.entry(x.key_str()).or_insert(0.0) += y.as_f64().unwrap_or(0.0);
27 }
28
29 let mut x_cumsum: HashMap<Cow<'_, str>, f64> = HashMap::new();
31 let mut new_y = Vec::with_capacity(y_col.len());
32 let mut ymin_vals = Vec::with_capacity(y_col.len());
33
34 for (x, y) in x_col.iter().zip(y_col.iter()) {
35 let x_key = x.key_str();
36 let y_val = y.as_f64().unwrap_or(0.0);
37
38 let total = x_totals.get(&x_key).copied().unwrap_or(1.0);
39 let total = if total.abs() < f64::EPSILON {
40 1.0
41 } else {
42 total
43 };
44
45 let consumed = x_cumsum.get(&x_key).copied().unwrap_or(0.0);
47
48 let norm_y = y_val / total;
49 new_y.push(Value::Float(1.0 - consumed));
50 ymin_vals.push(Value::Float(1.0 - consumed - norm_y));
51
52 *x_cumsum.entry(x_key).or_insert(0.0) += norm_y;
53 }
54
55 if let Some(col) = data.column_mut("y") {
56 *col = new_y;
57 }
58 if !data.has_column("ymin") {
59 data.add_column("ymin".to_string(), ymin_vals);
60 } else if let Some(col) = data.column_mut("ymin") {
61 *col = ymin_vals;
62 }
63 }
64
65 fn name(&self) -> &str {
66 "fill"
67 }
68}