Skip to main content

chart/
model.rs

1//! What a chart draws: layers over shared axes, each one [`Mark`] per row of
2//! its [`Data`], placed by the fields its [`Encoding`] names.
3
4use gpui::SharedString;
5
6use crate::data::Data;
7
8#[derive(Clone, Debug)]
9pub struct Chart {
10    /// Paint order. Their x scales are one scale, as are their y scales, and
11    /// a colour value is one colour across them.
12    pub layers: Vec<Layer>,
13    pub title: Option<SharedString>,
14}
15
16#[derive(Clone, Debug)]
17pub struct Layer {
18    pub data: Data,
19    pub mark: Mark,
20    pub encoding: Encoding,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24pub enum Mark {
25    Bar,
26    Line,
27    Area,
28    Point,
29    /// Pie and donut slices, sized by `theta`.
30    Arc,
31}
32
33#[derive(Clone, Debug, Default, PartialEq)]
34pub struct Encoding {
35    pub x: Option<Channel>,
36    pub y: Option<Channel>,
37    /// One series per value.
38    pub color: Option<Channel>,
39    pub theta: Option<Channel>,
40}
41
42#[derive(Clone, Debug, PartialEq)]
43pub struct Channel {
44    pub field: SharedString,
45    pub kind: Kind,
46    /// The axis's or legend's title. `None` shows none.
47    pub title: Option<SharedString>,
48    pub scale: Scale,
49    /// The order of a discrete field's values.
50    pub sort: Sort,
51}
52
53/// How a continuous field's values map to positions.
54#[derive(Clone, Copy, Debug, PartialEq)]
55pub struct Scale {
56    /// Whether a quantitative scale takes in zero. A stack of bars always
57    /// starts at zero.
58    pub zero: bool,
59    /// Fixed `[low, high]`, times in milliseconds since the epoch. Marks
60    /// outside it are cut off at the plot's edge.
61    pub domain: Option<[f64; 2]>,
62    /// Whether a scale with no fixed domain widens to the ticks either side.
63    pub nice: bool,
64}
65
66impl Default for Scale {
67    fn default() -> Self {
68        Self {
69            zero: true,
70            domain: None,
71            nice: true,
72        }
73    }
74}
75
76#[derive(Clone, Debug, Default, PartialEq)]
77pub enum Sort {
78    /// The order rows first show each value in.
79    #[default]
80    Data,
81    Ascending,
82    Descending,
83    /// These values first, in this order, then the rest in data order. A
84    /// number matches as its read-out text.
85    Explicit(Vec<SharedString>),
86}
87
88/// How a field's values are read, whatever column holds them.
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
90pub enum Kind {
91    Quantitative,
92    Temporal,
93    Ordinal,
94    Nominal,
95}
96
97impl Chart {
98    /// One layer.
99    pub fn new(mark: Mark, data: Data) -> Self {
100        Self {
101            layers: vec![Layer {
102                data,
103                mark,
104                encoding: Encoding::default(),
105            }],
106            title: None,
107        }
108    }
109
110    /// `other`'s layers over this chart's. Its title is dropped.
111    pub fn layer(mut self, other: Chart) -> Self {
112        self.layers.extend(other.layers);
113        self
114    }
115
116    fn last(&mut self) -> &mut Encoding {
117        &mut self
118            .layers
119            .last_mut()
120            .expect("a chart has a layer")
121            .encoding
122    }
123
124    pub fn bar(data: Data) -> Self {
125        Self::new(Mark::Bar, data)
126    }
127
128    pub fn line(data: Data) -> Self {
129        Self::new(Mark::Line, data)
130    }
131
132    pub fn area(data: Data) -> Self {
133        Self::new(Mark::Area, data)
134    }
135
136    pub fn point(data: Data) -> Self {
137        Self::new(Mark::Point, data)
138    }
139
140    pub fn arc(data: Data) -> Self {
141        Self::new(Mark::Arc, data)
142    }
143
144    /// Sets the last layer's x, as the other channels do theirs.
145    pub fn x(mut self, channel: Channel) -> Self {
146        self.last().x = Some(channel);
147        self
148    }
149
150    pub fn y(mut self, channel: Channel) -> Self {
151        self.last().y = Some(channel);
152        self
153    }
154
155    pub fn color(mut self, channel: Channel) -> Self {
156        self.last().color = Some(channel);
157        self
158    }
159
160    pub fn theta(mut self, channel: Channel) -> Self {
161        self.last().theta = Some(channel);
162        self
163    }
164
165    pub fn title(mut self, title: impl Into<SharedString>) -> Self {
166        self.title = Some(title.into());
167        self
168    }
169}
170
171impl Channel {
172    pub fn new(field: impl Into<SharedString>, kind: Kind) -> Self {
173        Self {
174            field: field.into(),
175            kind,
176            title: None,
177            scale: Scale::default(),
178            sort: Sort::default(),
179        }
180    }
181
182    pub fn quantitative(field: impl Into<SharedString>) -> Self {
183        Self::new(field, Kind::Quantitative)
184    }
185
186    pub fn temporal(field: impl Into<SharedString>) -> Self {
187        Self::new(field, Kind::Temporal)
188    }
189
190    pub fn ordinal(field: impl Into<SharedString>) -> Self {
191        Self::new(field, Kind::Ordinal)
192    }
193
194    pub fn nominal(field: impl Into<SharedString>) -> Self {
195        Self::new(field, Kind::Nominal)
196    }
197
198    pub fn title(mut self, title: impl Into<SharedString>) -> Self {
199        self.title = Some(title.into());
200        self
201    }
202
203    pub fn zero(mut self, zero: bool) -> Self {
204        self.scale.zero = zero;
205        self
206    }
207
208    pub fn domain(mut self, low: f64, high: f64) -> Self {
209        self.scale.domain = Some([low, high]);
210        self
211    }
212
213    pub fn nice(mut self, nice: bool) -> Self {
214        self.scale.nice = nice;
215        self
216    }
217
218    pub fn sort(mut self, sort: Sort) -> Self {
219        self.sort = sort;
220        self
221    }
222}