1#[cfg(any(feature = "line", feature = "bar"))]
69use crate::chart::traits::ChartBuilder;
70#[allow(unused_imports)]
71use crate::data::MultiSeries;
72#[cfg(any(feature = "line", feature = "bar"))]
73use crate::data::{Point2D, StaticDataSeries};
74#[cfg(any(feature = "line", feature = "bar"))]
75use crate::error::ChartResult;
76#[cfg(any(feature = "line", feature = "bar"))]
77use embedded_graphics::prelude::*;
78#[cfg(any(feature = "line", feature = "bar"))]
79use heapless::String;
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ChartPreset {
84 Professional,
86 Embedded,
88 Vibrant,
90 Pastel,
92 Dark,
94}
95
96pub struct Chart;
98
99impl Chart {
100 #[cfg(feature = "line")]
102 pub fn line<C>() -> FluentLineChartBuilder<C>
103 where
104 C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565> + 'static,
105 {
106 FluentLineChartBuilder::new()
107 }
108
109 #[cfg(feature = "bar")]
111 pub fn bar<C>() -> FluentBarChartBuilder<C>
112 where
113 C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565> + 'static,
114 {
115 FluentBarChartBuilder::new()
116 }
117}
118
119#[cfg(feature = "line")]
121pub struct FluentLineChartBuilder<C: PixelColor> {
122 data: Option<StaticDataSeries<Point2D, 256>>,
123 multi_data: Option<MultiSeries<Point2D, 8, 256>>,
124 color: Option<C>,
125 title: Option<String<64>>,
126 preset: Option<ChartPreset>,
127 line_width: Option<u32>,
128 show_markers: bool,
129 marker_size: Option<u32>,
130}
131
132#[cfg(feature = "line")]
133impl<C: PixelColor + 'static> FluentLineChartBuilder<C>
134where
135 C: From<embedded_graphics::pixelcolor::Rgb565>,
136{
137 fn new() -> Self {
138 Self {
139 data: None,
140 multi_data: None,
141 color: None,
142 title: None,
143 preset: None,
144 line_width: None,
145 show_markers: false,
146 marker_size: None,
147 }
148 }
149
150 pub fn data_from_tuples(mut self, tuples: &[(f32, f32)]) -> Self {
152 let series =
153 StaticDataSeries::from_tuples(tuples).unwrap_or_else(|_| StaticDataSeries::new());
154 self.data = Some(series);
155 self
156 }
157
158 pub fn series(mut self, label: &str, tuples: &[(f32, f32)]) -> Self {
160 if self.multi_data.is_none() {
161 self.multi_data = Some(MultiSeries::new());
162 }
163
164 if let Some(ref mut multi_data) = self.multi_data {
165 let mut series = StaticDataSeries::with_label(label);
166 for &(x, y) in tuples {
167 if series.push(Point2D::new(x, y)).is_err() {
168 break; }
170 }
171 let _ = multi_data.add_series(series);
172 }
173 self
174 }
175
176 pub fn color(mut self, color: C) -> Self {
178 self.color = Some(color);
179 self
180 }
181
182 pub fn title(mut self, title: &str) -> Self {
184 if let Ok(title_string) = String::try_from(title) {
185 self.title = Some(title_string);
186 }
187 self
188 }
189
190 pub fn preset(mut self, preset: ChartPreset) -> Self {
192 self.preset = Some(preset);
193 self
194 }
195
196 pub fn line_width(mut self, width: u32) -> Self {
198 self.line_width = Some(width);
199 self
200 }
201
202 pub fn with_markers(mut self) -> Self {
204 self.show_markers = true;
205 self
206 }
207
208 pub fn marker_size(mut self, size: u32) -> Self {
210 self.marker_size = Some(size);
211 self.show_markers = true;
212 self
213 }
214
215 pub fn build(self) -> ChartResult<crate::chart::LineChart<C>> {
217 let mut builder = crate::chart::LineChart::builder();
218
219 if let Some(preset) = self.preset {
221 builder = self.apply_preset_to_line_builder(builder, preset);
222 }
223
224 if let Some(color) = self.color {
226 builder = builder.line_color(color);
227 }
228
229 if let Some(width) = self.line_width {
230 builder = builder.line_width(width);
231 }
232
233 if self.show_markers {
234 let marker_style = crate::chart::MarkerStyle {
235 shape: crate::chart::MarkerShape::Circle,
236 size: self.marker_size.unwrap_or(4),
237 color: self
238 .color
239 .unwrap_or_else(|| C::from(embedded_graphics::pixelcolor::Rgb565::BLUE)),
240 visible: true,
241 };
242 builder = builder.with_markers(marker_style);
243 }
244
245 builder.build()
246 }
247
248 fn apply_preset_to_line_builder(
249 &self,
250 mut builder: crate::chart::LineChartBuilder<C>,
251 preset: ChartPreset,
252 ) -> crate::chart::LineChartBuilder<C> {
253 match preset {
254 ChartPreset::Professional => {
255 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
256 70 >> 3,
257 130 >> 2,
258 180 >> 3,
259 ));
260 builder = builder.line_color(color).line_width(2);
261 }
262 ChartPreset::Embedded => {
263 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(0, 31, 0)); builder = builder.line_color(color).line_width(1);
265 }
266 ChartPreset::Vibrant => {
267 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
268 236 >> 3,
269 72 >> 2,
270 153 >> 3,
271 ));
272 builder = builder.line_color(color).line_width(3);
273 }
274 ChartPreset::Pastel => {
275 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
276 147 >> 3,
277 197 >> 2,
278 253 >> 3,
279 ));
280 builder = builder.line_color(color).line_width(2);
281 }
282 ChartPreset::Dark => {
283 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
284 200 >> 3,
285 200 >> 2,
286 200 >> 3,
287 ));
288 builder = builder.line_color(color).line_width(1);
289 }
290 }
291 builder
292 }
293}
294
295#[cfg(feature = "bar")]
297pub struct FluentBarChartBuilder<C: PixelColor> {
298 data: Option<StaticDataSeries<Point2D, 256>>,
299 color: Option<C>,
300 title: Option<String<64>>,
301 preset: Option<ChartPreset>,
302 bar_width: Option<u32>,
303}
304
305#[cfg(feature = "bar")]
306impl<C: PixelColor + 'static> FluentBarChartBuilder<C>
307where
308 C: From<embedded_graphics::pixelcolor::Rgb565>,
309{
310 fn new() -> Self {
311 Self {
312 data: None,
313 color: None,
314 title: None,
315 preset: None,
316 bar_width: None,
317 }
318 }
319
320 pub fn data_from_tuples(mut self, tuples: &[(f32, f32)]) -> Self {
322 let series =
323 StaticDataSeries::from_tuples(tuples).unwrap_or_else(|_| StaticDataSeries::new());
324 self.data = Some(series);
325 self
326 }
327
328 pub fn color(mut self, color: C) -> Self {
330 self.color = Some(color);
331 self
332 }
333
334 pub fn title(mut self, title: &str) -> Self {
336 if let Ok(title_string) = String::try_from(title) {
337 self.title = Some(title_string);
338 }
339 self
340 }
341
342 pub fn preset(mut self, preset: ChartPreset) -> Self {
344 self.preset = Some(preset);
345 self
346 }
347
348 pub fn bar_width(mut self, width: u32) -> Self {
350 self.bar_width = Some(width);
351 self
352 }
353
354 pub fn build(self) -> ChartResult<crate::chart::BarChart<C>> {
356 let mut builder = crate::chart::BarChart::builder();
357
358 if let Some(preset) = self.preset {
360 builder = self.apply_preset_to_bar_builder(builder, preset);
361 }
362
363 if let Some(color) = self.color {
365 builder = builder.colors(&[color]);
366 }
367
368 if let Some(width) = self.bar_width {
369 builder = builder.bar_width(crate::chart::bar::BarWidth::Fixed(width));
370 }
371
372 builder.build()
373 }
374
375 fn apply_preset_to_bar_builder(
376 &self,
377 mut builder: crate::chart::BarChartBuilder<C>,
378 preset: ChartPreset,
379 ) -> crate::chart::BarChartBuilder<C> {
380 match preset {
381 ChartPreset::Professional => {
382 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
383 59 >> 3,
384 130 >> 2,
385 246 >> 3,
386 ));
387 builder = builder.colors(&[color]);
388 }
389 ChartPreset::Embedded => {
390 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(0, 31, 0));
391 builder = builder.colors(&[color]);
392 }
393 ChartPreset::Vibrant => {
394 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
395 245 >> 3,
396 101 >> 2,
397 101 >> 3,
398 ));
399 builder = builder.colors(&[color]);
400 }
401 ChartPreset::Pastel => {
402 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
403 167 >> 3,
404 243 >> 2,
405 208 >> 3,
406 ));
407 builder = builder.colors(&[color]);
408 }
409 ChartPreset::Dark => {
410 let color = C::from(embedded_graphics::pixelcolor::Rgb565::new(
411 150 >> 3,
412 150 >> 2,
413 150 >> 3,
414 ));
415 builder = builder.colors(&[color]);
416 }
417 }
418 builder
419 }
420}
421
422pub mod quick {
426 #[cfg(any(feature = "line", feature = "bar"))]
427 use super::*;
428
429 #[cfg(feature = "line")]
431 pub fn line_chart<C>(data: &[(f32, f32)]) -> ChartResult<crate::chart::LineChart<C>>
432 where
433 C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565> + 'static,
434 {
435 Chart::line().data_from_tuples(data).build()
436 }
437
438 #[cfg(feature = "line")]
440 pub fn professional_line_chart<C>(
441 data: &[(f32, f32)],
442 ) -> ChartResult<crate::chart::LineChart<C>>
443 where
444 C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565> + 'static,
445 {
446 Chart::line()
447 .data_from_tuples(data)
448 .preset(ChartPreset::Professional)
449 .with_markers()
450 .build()
451 }
452
453 #[cfg(feature = "bar")]
455 pub fn bar_chart<C>(data: &[(f32, f32)]) -> ChartResult<crate::chart::BarChart<C>>
456 where
457 C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565> + 'static,
458 {
459 Chart::bar().data_from_tuples(data).build()
460 }
461
462 #[cfg(feature = "line")]
464 pub fn embedded_line_chart<C>(data: &[(f32, f32)]) -> ChartResult<crate::chart::LineChart<C>>
465 where
466 C: PixelColor + From<embedded_graphics::pixelcolor::Rgb565> + 'static,
467 {
468 Chart::line()
469 .data_from_tuples(data)
470 .preset(ChartPreset::Embedded)
471 .build()
472 }
473}
474
475#[cfg(test)]
476mod tests {
477 #[cfg(feature = "line")]
478 use super::*;
479 #[cfg(feature = "line")]
480 use embedded_graphics::pixelcolor::Rgb565;
481
482 #[test]
483 #[cfg(feature = "line")]
484 fn test_fluent_line_chart() {
485 let data = [(0.0, 10.0), (1.0, 20.0), (2.0, 15.0)];
486 let chart = Chart::line::<Rgb565>()
487 .data_from_tuples(&data)
488 .color(Rgb565::BLUE)
489 .title("Test Chart")
490 .with_markers()
491 .build();
492
493 assert!(chart.is_ok());
494 }
495
496 #[test]
497 #[cfg(feature = "line")]
498 fn test_quick_line_chart() {
499 let data = [(0.0, 10.0), (1.0, 20.0), (2.0, 15.0)];
500 let chart = quick::line_chart::<Rgb565>(&data);
501 assert!(chart.is_ok());
502 }
503
504 #[test]
505 #[cfg(feature = "line")]
506 fn test_professional_preset() {
507 let data = [(0.0, 10.0), (1.0, 20.0)];
508 let chart = Chart::line::<Rgb565>()
509 .data_from_tuples(&data)
510 .preset(ChartPreset::Professional)
511 .build();
512
513 assert!(chart.is_ok());
514 }
515
516 #[test]
517 #[cfg(feature = "line")]
518 fn test_multi_series() {
519 let chart = Chart::line::<Rgb565>()
520 .series("Series 1", &[(0.0, 10.0), (1.0, 20.0)])
521 .series("Series 2", &[(0.0, 15.0), (1.0, 25.0)])
522 .preset(ChartPreset::Vibrant)
523 .build();
524
525 assert!(chart.is_ok());
526 }
527}