1use crate::common::Size;
2use crate::options::*;
3use crate::templates::ScriptTemplate;
4use serde::Serialize;
5use serde_json::json;
6use uuid::Uuid;
7
8pub trait AxisInfo{
9 const AXIS_TYPE: AxisType;
10}
11pub struct ValueAxis;
12pub struct CategoryAxis;
13
14impl AxisInfo for ValueAxis {
15 const AXIS_TYPE: AxisType = AxisType::Value;
16}
17
18impl AxisInfo for CategoryAxis {
19 const AXIS_TYPE: AxisType = AxisType::Category;
20}
21
22pub trait AxisKindMarker {
23 type AxisType : AxisInfo;
24}
25
26impl AxisKindMarker for u128 {
27 type AxisType = ValueAxis;
28}
29impl AxisKindMarker for i32 {
30 type AxisType = ValueAxis;
31}
32
33impl AxisKindMarker for u32 {
34 type AxisType = ValueAxis;
35}
36
37impl AxisKindMarker for i64 {
38 type AxisType = ValueAxis;
39}
40
41impl AxisKindMarker for u64 {
42 type AxisType = ValueAxis;
43}
44
45impl AxisKindMarker for i16 {
46 type AxisType = ValueAxis;
47}
48impl AxisKindMarker for u16 {
49 type AxisType = ValueAxis;
50}
51
52impl AxisKindMarker for i8 {
53 type AxisType = ValueAxis;
54}
55
56impl AxisKindMarker for u8 {
57 type AxisType = ValueAxis;
58}
59
60impl AxisKindMarker for f32 {
61 type AxisType = ValueAxis;
62}
63
64impl AxisKindMarker for f64 {
65 type AxisType = ValueAxis;
66}
67
68impl AxisKindMarker for usize{
69 type AxisType = ValueAxis;
70}
71impl AxisKindMarker for isize {
72 type AxisType = ValueAxis;
73}
74
75impl AxisKindMarker for String {
76 type AxisType = CategoryAxis;
77}
78
79impl<'a> AxisKindMarker for &'a str {
80 type AxisType = CategoryAxis;
81}
82
83
84pub trait RegressionChartBuilder<X, Y>: ChartBuilder<X, Y>
86where X: AxisKindMarker<AxisType=ValueAxis> + Serialize,
87 Y: AxisKindMarker<AxisType=ValueAxis> + Serialize,
88
89{
90
91 fn add_linear_regression_dataset(self, data_source_index: usize) -> usize {
92 self.add_regression_dataset(data_source_index, RegressionMethod::Linear, None)
93 }
94
95 fn add_polynomial_regression_dataset(self, data_source_index: usize, order: u8) -> usize {
96 self.add_regression_dataset(data_source_index, RegressionMethod::Polynomial, Some(order))
97 }
98
99 fn add_exponential_regression_dataset(self, data_source_index: usize) -> usize {
100 self.add_regression_dataset(data_source_index, RegressionMethod::Exponential, None)
101 }
102
103 fn add_regression_dataset(mut self, data_source_index: usize, method: RegressionMethod, order: Option<u8>) -> usize {
104 let index = self.options().dataset.as_mut().unwrap().len();
105 let regression_config = RegressionConfig {
106 method: method.clone(),
107 order,
108 extra: None,
109 };
110 let dataset = DatasetTransform::regression(regression_config);
111 self.options().dataset.as_mut().unwrap().push(DatasetComponent::tr(dataset, data_source_index));
112 index
113 }
114
115
116 fn add_regression_series<TData: Into<DatasetComponent<X,Y>>>(mut self, series_label: &str, data: TData,
118 method: RegressionMethod, order: Option<u8>) -> Self {
119 if self.options().dataset.is_none() {
121 self.options().dataset = Some(Vec::new());
122 }
123
124 let datasets = self.options().dataset.as_mut().unwrap();
126 let source_index = datasets.len();
127 datasets.push(data.into());
128
129
130 let transform_index = datasets.len();
132 let mut regression_config = RegressionConfig {
133 method: method.clone(),
134 order: None,
135 extra: None,
136 };
137
138 if method == RegressionMethod::Polynomial {
140 regression_config.order = order;
141 }
142
143 datasets.push(
144 DatasetComponent::tr(
145 DatasetTransform::regression(regression_config),
146 source_index
147 )
148 );
149
150 self.options().series.as_mut().unwrap().push(Series {
152 r#type: Some(SeriesType::Scatter),
153 name: Some(format!("{} (data)", series_label)),
154 smooth: None,
155 area_style: None,
156 data: SeriesDataSource::DatasetIndex(source_index),
157 symbol: Some(DataPointSymbol::Circle),
158 symbol_size: Some(8),
159 extra: None,
160 });
161
162 self.options().series.as_mut().unwrap().push(Series {
164 r#type: Some(SeriesType::Line),
165 name: Some(format!("{} (regression)", series_label)),
166 smooth: Some(true),
167 area_style: None,
168 data: SeriesDataSource::DatasetIndex(transform_index),
169 symbol: Some(DataPointSymbol::None),
170 symbol_size: None,
171 extra: None
172 });
173
174 self
175 }
176
177 fn add_linear_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData) -> Self {
179 self.add_regression_series(series_label, data, RegressionMethod::Linear, None)
180 }
181
182 fn add_polynomial_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData, order: u8) -> Self {
184 self.add_regression_series(series_label, data, RegressionMethod::Polynomial, Some(order))
185 }
186
187 fn add_exponential_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData) -> Self
189 {
190 self.add_regression_series(series_label, data, RegressionMethod::Exponential, None)
191 }
192
193 fn add_logarithmic_regression_series<TData:Into<DatasetComponent<X,Y>>>(self, series_label: &str, data: TData) -> Self {
195 self.add_regression_series(series_label, data, RegressionMethod::Logarithmic, None)
196 }
197}
198
199
200pub trait ChartBuilder<X, Y>: Sized
201where X: AxisKindMarker+Serialize, Y: AxisKindMarker+Serialize
202{
203
204 fn options(&mut self) -> &mut EChartsOption<X,Y>;
205
206 fn title_str(mut self, title: &str) -> Self {
208 let t = self.options().title.get_or_insert_default();
209 t.left = Some(Position::Keyword(PositionKeyword::Center));
210 t.text = Some(title.to_string());
211 self
212 }
213
214 fn subtitle_str(mut self, subtitle: &str) -> Self {
215 self.options().title.get_or_insert_default().sub_text = Some(subtitle.to_string());
216 self
217 }
218
219 fn title(mut self, title: Title) -> Self {
220 self.options().title = Some(title);
221 self
222 }
223
224
225 fn x_axis_label(mut self, x: &str) -> Self {
226 self.options().x_axis.name = Some(x.to_string());
227 self
228 }
229
230 fn y_axis_label(mut self, y: &str) -> Self {
231 self.options().y_axis.name = Some(y.to_string());
232 self
233 }
234
235 fn add_dataset<TData:Into<DatasetComponent<X,Y>>>(mut self, data: TData) -> usize {
237 let index = self.options().dataset.as_mut().unwrap().len();
238 self.options().dataset.as_mut().unwrap().push(data.into());
239 index
240 }
241
242 fn add_dataset_visualisation(mut self, series_label:&str, series_type: SeriesType, dataset_index: usize) -> Self {
245 let datasets = &self.options().dataset;
246 if let Some(datasets) = datasets {
247 if let Some(dataset) = datasets.get(dataset_index){
248 match dataset {
249 DatasetComponent::Source(_) | DatasetComponent::Transform(_) => {
250 self.options().series.as_mut().unwrap().push(Series {
251 r#type: Some(series_type),
252 name: Some(format!("{}", series_label)),
253 smooth: Some(true),
254 area_style: None,
255 symbol: None,
256 symbol_size: None,
257 data: SeriesDataSource::DatasetIndex(dataset_index),
258 extra: None
259 });
260 }
261 DatasetComponent::LabelledSource(_) => {
262 self.options().series.as_mut().unwrap().push(Series {
263 r#type: Some(series_type),
264 name: Some(format!("{}", series_label)),
265 smooth: Some(true),
266 area_style: None,
267 symbol: None,
268 symbol_size: None,
269 data: SeriesDataSource::DatasetIndex(dataset_index),
270 extra: Some(json!(
271 {"encode": {"tooltip": [2,1], "x": 0, "y": 1 }}
272 ))
273 });
274 }
275 }
276
277 }
278 }
279 self
280 }
281
282
283 fn add_series<TData:Into<SeriesDataSource<X,Y>>>(mut self,series_type: SeriesType, series_label:&str, data: TData) -> Self {
284 self.options().series.as_mut().unwrap().push(
285 Series::new(series_label,series_type,data.into())
286 );
287 self
288 }
289
290
291
292 fn build(self, width: Size, height: Size) -> ScriptTemplate<X,Y>;
293}
294
295
296
297impl<X, Y> EChartsOption<X,Y>
298where X: AxisKindMarker, Y: AxisKindMarker {
299
300 pub fn new() -> Self {
301 Self {
302 title: None,
303 tooltip: Some(Tooltip {
304 show: true,
305 show_delay: None,
306 hide_delay: None,
307 trigger: Some(TooltipTrigger::Item),
308 formatter: None,
309 axis_pointer: Some(AxisPointer {
310 r#type: Some(AxisPointerType::Cross),
311 snap: Some(false),
312 animation: None,
313 axis: None,
314 })
315 }),
316 legend: None, grid: None, extra: None, dataset: None,
317 x_axis: Axis::default(),
318 y_axis: Axis::default(),
319 series: Some(Vec::new()),
320 }
321 }
322}
323
324
325impl<X, Y> ChartBuilder<X, Y> for EChartsOption<X, Y>
326where X: AxisKindMarker+Serialize , Y: AxisKindMarker+Serialize {
327 fn options(&mut self) -> &mut EChartsOption<X,Y> {
328 self
329 }
330
331 fn build(self, width: Size, height: Size) -> ScriptTemplate<X,Y>{
332 ScriptTemplate::new(Uuid::new_v4().to_string(), width, height, self)
333 }
334}
335
336
337impl <X, Y> RegressionChartBuilder<X, Y> for EChartsOption<X, Y>
338where X: AxisKindMarker<AxisType = ValueAxis> + Serialize,
339 Y: AxisKindMarker<AxisType = ValueAxis> + Serialize{}