Skip to main content

ggplot_rs/position/
dodge2.rs

1use crate::data::DataFrame;
2
3use super::dodge::{dedup_order, dodge_rows, group_keys};
4use super::{Position, PositionParams};
5
6/// Like position_dodge but preserves total width and adds padding between
7/// groups (ggplot2's `position_dodge2(width, padding, reverse)`). Elements
8/// are centred exactly as [`PositionDodge`](struct@super::dodge::PositionDodge)
9/// places them; on a continuous axis `xmin`/`xmax` shrink to
10/// `(1 − padding)` of each group's share.
11#[derive(Clone, Debug, PartialEq)]
12pub struct PositionDodge2 {
13    pub padding: f64,
14    /// Total dodge width; `None` = the layer default (0.9).
15    pub width: Option<f64>,
16    /// Reverse the group order.
17    pub reverse: bool,
18    /// `preserve = "single"` (see
19    /// [`PositionDodge::preserve_single`](super::dodge::PositionDodge::preserve_single)).
20    pub preserve_single: bool,
21}
22
23impl PositionDodge2 {
24    pub fn new(padding: f64) -> Self {
25        PositionDodge2 {
26            padding,
27            ..Default::default()
28        }
29    }
30
31    /// Set the total dodge width.
32    pub fn with_width(mut self, width: f64) -> Self {
33        self.width = Some(width);
34        self
35    }
36
37    /// Reverse the order of the dodged groups (`reverse = TRUE`).
38    pub fn with_reverse(mut self, reverse: bool) -> Self {
39        self.reverse = reverse;
40        self
41    }
42
43    /// `preserve = "single"`: elements keep one group's width in a slot with
44    /// fewer groups.
45    pub fn with_preserve_single(mut self) -> Self {
46        self.preserve_single = true;
47        self
48    }
49}
50
51impl Default for PositionDodge2 {
52    fn default() -> Self {
53        PositionDodge2 {
54            padding: 0.1,
55            width: None,
56            reverse: false,
57            preserve_single: false,
58        }
59    }
60}
61
62impl Position for PositionDodge2 {
63    fn compute(&self, data: &mut DataFrame, params: &PositionParams) {
64        dodge_rows(
65            data,
66            self.width.unwrap_or(params.width),
67            Some(self.padding),
68            self.reverse,
69            self.preserve_single,
70            params.group_order.as_deref(),
71        );
72    }
73
74    fn name(&self) -> &str {
75        "dodge2"
76    }
77
78    /// Same per-panel rule as `PositionDodge` (#153): `dodge_rows` is
79    /// shared between the two.
80    fn per_panel(&self) -> bool {
81        true
82    }
83
84    /// Same whole-layer group ranking as `PositionDodge` (review #167
85    /// finding 2): `dodge_rows` is shared between the two.
86    fn group_order(&self, data: &DataFrame) -> Option<Vec<String>> {
87        group_keys(data).map(|keys| dedup_order(&keys))
88    }
89}