Skip to main content

ggplot_rs/facet/
mod.rs

1pub mod grid;
2pub mod wrap;
3
4use crate::render::Rect;
5
6/// Whether facet panels share axes.
7#[derive(Clone, Debug)]
8pub enum FacetScales {
9    Fixed,
10    FreeX,
11    FreeY,
12    Free,
13}
14
15/// Whether facet panels are sized proportionally to their data range
16/// (R's `space =`). `Fixed` = equal-size panels (default).
17#[derive(Clone, Debug, Default, PartialEq)]
18pub enum FacetSpace {
19    #[default]
20    Fixed,
21    FreeX,
22    FreeY,
23    Free,
24}
25
26impl FacetSpace {
27    pub fn free_x(&self) -> bool {
28        matches!(self, FacetSpace::FreeX | FacetSpace::Free)
29    }
30    pub fn free_y(&self) -> bool {
31        matches!(self, FacetSpace::FreeY | FacetSpace::Free)
32    }
33}
34
35/// How facet strip labels are formatted.
36#[derive(Clone, Default)]
37pub enum FacetLabeller {
38    /// Show just the value (default).
39    #[default]
40    Value,
41    /// Show "var: value".
42    Both,
43    /// Custom formatting function: fn(variable_name, value) -> label.
44    Custom(fn(&str, &str) -> String),
45}
46
47impl FacetLabeller {
48    pub fn format(&self, var: &str, value: &str) -> String {
49        match self {
50            FacetLabeller::Value => value.to_string(),
51            FacetLabeller::Both => format!("{var}: {value}"),
52            FacetLabeller::Custom(f) => f(var, value),
53        }
54    }
55}
56
57/// A single panel in a faceted layout.
58#[derive(Clone, Debug)]
59pub struct Panel {
60    pub row: usize,
61    pub col: usize,
62    pub label: String,
63    pub row_label: Option<String>,
64    pub col_label: Option<String>,
65    pub rect: Rect,
66}
67
68/// Facet specification.
69#[derive(Clone, Default)]
70pub enum Facet {
71    #[default]
72    None,
73    Wrap {
74        var: String,
75        ncol: Option<usize>,
76        scales: FacetScales,
77        labeller: FacetLabeller,
78    },
79    Grid {
80        row_var: Option<String>,
81        col_var: Option<String>,
82        scales: FacetScales,
83        labeller: FacetLabeller,
84        #[doc = "Proportional panel sizing (R's `space =`)."]
85        space: FacetSpace,
86    },
87}
88
89impl Facet {
90    pub fn is_none(&self) -> bool {
91        matches!(self, Facet::None)
92    }
93
94    pub fn labeller(&self) -> &FacetLabeller {
95        match self {
96            Facet::None => &FacetLabeller::Value,
97            Facet::Wrap { labeller, .. } => labeller,
98            Facet::Grid { labeller, .. } => labeller,
99        }
100    }
101}