ggplot_rs/position/
dodge.rs1use crate::data::{DataFrame, Value};
2
3use super::{Position, PositionParams};
4
5pub struct PositionDodge;
7
8impl Position for PositionDodge {
9 fn compute(&self, data: &mut DataFrame, params: &PositionParams) {
10 let x_col = match data.column("x") {
11 Some(c) => c.to_vec(),
12 None => return,
13 };
14
15 let group_col = data
17 .column("fill")
18 .or_else(|| data.column("color"))
19 .or_else(|| data.column("group"));
20
21 let group_keys: Vec<String> = match group_col {
22 Some(col) => col.iter().map(|v| v.to_group_key()).collect(),
23 None => return, };
25
26 let mut unique_groups: Vec<String> = Vec::new();
28 for g in &group_keys {
29 if !unique_groups.contains(g) {
30 unique_groups.push(g.clone());
31 }
32 }
33
34 let n_groups = unique_groups.len() as f64;
35 if n_groups <= 1.0 {
36 return;
37 }
38
39 let width = params.width;
40 let group_width = width / n_groups;
41
42 let mut new_x = x_col.clone();
43 for (i, (x, group)) in x_col.iter().zip(group_keys.iter()).enumerate() {
44 let group_idx = unique_groups.iter().position(|g| g == group).unwrap() as f64;
45 let offset = (group_idx - (n_groups - 1.0) / 2.0) * group_width;
46
47 if let Some(x_val) = x.as_f64() {
48 new_x[i] = Value::Float(x_val + offset);
49 }
50 }
51
52 if let Some(col) = data.column_mut("x") {
53 *col = new_x;
54 }
55 }
56
57 fn name(&self) -> &str {
58 "dodge"
59 }
60}