1#[cfg(feature = "serde")]
2pub mod config;
3pub mod elements;
4pub mod presets;
5
6pub use elements::{ElementGeom, ElementLine, ElementRect, ElementText};
7
8#[derive(Clone, Debug)]
10pub enum LegendPosition {
11 Right,
12 Left,
13 Top,
14 Bottom,
15 None,
16 Inside(f64, f64),
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum LegendCorner {
25 TopLeft,
26 TopRight,
27 BottomLeft,
28 BottomRight,
29}
30
31impl LegendCorner {
32 pub const ALL: [LegendCorner; 4] = [
34 LegendCorner::BottomRight,
35 LegendCorner::BottomLeft,
36 LegendCorner::TopRight,
37 LegendCorner::TopLeft,
38 ];
39
40 pub fn inside(self) -> LegendPosition {
42 match self {
43 LegendCorner::TopLeft => LegendPosition::Inside(0.0, 1.0),
44 LegendCorner::TopRight => LegendPosition::Inside(1.0, 1.0),
45 LegendCorner::BottomLeft => LegendPosition::Inside(0.0, 0.0),
46 LegendCorner::BottomRight => LegendPosition::Inside(1.0, 0.0),
47 }
48 }
49
50 pub fn name(self) -> &'static str {
52 match self {
53 LegendCorner::TopLeft => "top-left",
54 LegendCorner::TopRight => "top-right",
55 LegendCorner::BottomLeft => "bottom-left",
56 LegendCorner::BottomRight => "bottom-right",
57 }
58 }
59}
60
61#[derive(Clone, Copy, Debug, Default, PartialEq)]
64pub enum TitlePosition {
65 #[default]
66 Panel,
67 Plot,
68}
69
70#[derive(Clone, Copy, Debug, Default, PartialEq)]
72pub enum TagPosition {
73 #[default]
74 TopLeft,
75 TopRight,
76 BottomLeft,
77 BottomRight,
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
82pub enum LegendDirection {
83 Vertical,
84 Horizontal,
85}
86
87#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
91pub enum AxisLabelFit {
92 Clip,
95 Shift,
100 #[default]
106 Reserve,
107}
108
109#[derive(Clone, Debug)]
111pub struct Margin {
112 pub top: f64,
113 pub right: f64,
114 pub bottom: f64,
115 pub left: f64,
116}
117
118impl Default for Margin {
119 fn default() -> Self {
120 Margin {
121 top: 10.0,
122 right: 10.0,
123 bottom: 10.0,
124 left: 10.0,
125 }
126 }
127}
128
129#[derive(Clone, Debug)]
131pub struct Theme {
132 pub text: ElementText,
134 pub title: ElementText,
135 pub axis_text_x: ElementText,
136 pub axis_text_y: ElementText,
137 pub axis_title_x: ElementText,
138 pub axis_title_y: ElementText,
139 pub axis_line: ElementLine,
140 pub axis_ticks: ElementLine,
141 pub panel_background: ElementRect,
142 pub panel_grid_major: ElementLine,
143 pub panel_grid_minor: ElementLine,
144 pub plot_background: ElementRect,
145 pub legend_position: LegendPosition,
146 pub plot_margin: Margin,
147
148 pub subtitle: ElementText,
150 pub caption: ElementText,
151 pub legend_title: ElementText,
152 pub legend_text: ElementText,
153 pub strip_text: ElementText,
154
155 pub axis_line_x: Option<ElementLine>,
157 pub axis_line_y: Option<ElementLine>,
158 pub axis_ticks_x: Option<ElementLine>,
159 pub axis_ticks_y: Option<ElementLine>,
160 pub panel_grid_major_x: Option<ElementLine>,
161 pub panel_grid_major_y: Option<ElementLine>,
162 pub panel_grid_minor_x: Option<ElementLine>,
163 pub panel_grid_minor_y: Option<ElementLine>,
164
165 pub panel_border: ElementLine,
167 pub legend_background: ElementRect,
168 pub legend_key: ElementRect,
169 pub strip_background: ElementRect,
170
171 pub axis_ticks_length: f64,
173 pub axis_text_x_dodge: usize,
176 pub legend_key_width: f64,
177 pub legend_key_height: f64,
178 pub legend_spacing: f64,
179 pub legend_margin: Margin,
180 pub panel_spacing: f64,
181 pub panel_spacing_x: Option<f64>,
182 pub panel_spacing_y: Option<f64>,
183
184 pub primary: Option<(u8, u8, u8)>,
189
190 pub aspect_ratio: Option<f64>,
193 pub panel_ontop: bool,
195 pub axis_minor_ticks: bool,
197 pub title_position: TitlePosition,
200 pub tag_position: TagPosition,
202 pub legend_direction: Option<LegendDirection>,
204 pub calendar_month_border: ElementLine,
206 pub legend_bar_border: Option<ElementLine>,
210 pub geom: ElementGeom,
213
214 pub axis_text_x_fit: AxisLabelFit,
217 pub axis_text_x_check_overlap: bool,
220}
221
222impl Theme {
223 pub fn hide_y_guides(&mut self) {
227 self.axis_text_y.visible = false;
228 self.axis_title_y.visible = false;
229 let hidden = |base: &ElementLine| ElementLine {
230 visible: false,
231 ..base.clone()
232 };
233 self.axis_line_y = Some(hidden(self.axis_line_y.as_ref().unwrap_or(&self.axis_line)));
234 self.axis_ticks_y = Some(hidden(
235 self.axis_ticks_y.as_ref().unwrap_or(&self.axis_ticks),
236 ));
237 self.panel_grid_major_y = Some(hidden(
238 self.panel_grid_major_y
239 .as_ref()
240 .unwrap_or(&self.panel_grid_major),
241 ));
242 self.panel_grid_minor_y = Some(hidden(
243 self.panel_grid_minor_y
244 .as_ref()
245 .unwrap_or(&self.panel_grid_minor),
246 ));
247 }
248
249 pub fn hide_position_guides(&mut self) {
253 for t in [
254 &mut self.axis_text_x,
255 &mut self.axis_text_y,
256 &mut self.axis_title_x,
257 &mut self.axis_title_y,
258 ] {
259 t.visible = false;
260 }
261 for l in [
262 &mut self.axis_line,
263 &mut self.axis_ticks,
264 &mut self.panel_grid_major,
265 &mut self.panel_grid_minor,
266 ] {
267 l.visible = false;
268 }
269 for l in [
270 &mut self.axis_line_x,
271 &mut self.axis_line_y,
272 &mut self.axis_ticks_x,
273 &mut self.axis_ticks_y,
274 &mut self.panel_grid_major_x,
275 &mut self.panel_grid_major_y,
276 &mut self.panel_grid_minor_x,
277 &mut self.panel_grid_minor_y,
278 ]
279 .into_iter()
280 .flatten()
281 {
282 l.visible = false;
283 }
284 }
285
286 pub fn primary_or(&self, fallback: (u8, u8, u8)) -> (u8, u8, u8) {
289 self.primary.unwrap_or(fallback)
290 }
291
292 pub fn with_primary(mut self, color: (u8, u8, u8)) -> Self {
294 self.primary = Some(color);
295 self
296 }
297
298 pub fn set_geom(mut self, el: ElementGeom) -> Self {
300 self.geom = el;
301 self
302 }
303
304 pub fn with_ink_paper(mut self, ink: (u8, u8, u8), paper: (u8, u8, u8)) -> Self {
307 self.geom.ink = Some(ink);
308 self.geom.paper = Some(paper);
309 self
310 }
311
312 pub fn resolve_inheritance(&mut self) {
318 let root = self.text.clone();
319 let def = ElementText::default();
320 let children: [&mut ElementText; 10] = [
321 &mut self.title,
322 &mut self.subtitle,
323 &mut self.caption,
324 &mut self.axis_text_x,
325 &mut self.axis_text_y,
326 &mut self.axis_title_x,
327 &mut self.axis_title_y,
328 &mut self.legend_title,
329 &mut self.legend_text,
330 &mut self.strip_text,
331 ];
332 for el in children {
333 if el.family == def.family {
334 el.family = root.family.clone();
335 }
336 if el.color == def.color {
337 el.color = root.color;
338 }
339 }
340 }
341
342 pub fn get_axis_line_x(&self) -> &ElementLine {
345 self.axis_line_x.as_ref().unwrap_or(&self.axis_line)
346 }
347
348 pub fn get_axis_line_y(&self) -> &ElementLine {
349 self.axis_line_y.as_ref().unwrap_or(&self.axis_line)
350 }
351
352 pub fn get_axis_ticks_x(&self) -> &ElementLine {
353 self.axis_ticks_x.as_ref().unwrap_or(&self.axis_ticks)
354 }
355
356 pub fn get_axis_ticks_y(&self) -> &ElementLine {
357 self.axis_ticks_y.as_ref().unwrap_or(&self.axis_ticks)
358 }
359
360 pub fn get_panel_grid_major_x(&self) -> &ElementLine {
361 self.panel_grid_major_x
362 .as_ref()
363 .unwrap_or(&self.panel_grid_major)
364 }
365
366 pub fn get_panel_grid_major_y(&self) -> &ElementLine {
367 self.panel_grid_major_y
368 .as_ref()
369 .unwrap_or(&self.panel_grid_major)
370 }
371
372 pub fn get_panel_grid_minor_x(&self) -> &ElementLine {
373 self.panel_grid_minor_x
374 .as_ref()
375 .unwrap_or(&self.panel_grid_minor)
376 }
377
378 pub fn get_panel_grid_minor_y(&self) -> &ElementLine {
379 self.panel_grid_minor_y
380 .as_ref()
381 .unwrap_or(&self.panel_grid_minor)
382 }
383
384 pub fn get_panel_spacing_x(&self) -> f64 {
385 self.panel_spacing_x.unwrap_or(self.panel_spacing)
386 }
387
388 pub fn get_panel_spacing_y(&self) -> f64 {
389 self.panel_spacing_y.unwrap_or(self.panel_spacing)
390 }
391
392 pub fn set_axis_text_x(mut self, el: ElementText) -> Self {
395 self.axis_text_x = el;
396 self
397 }
398
399 pub fn set_axis_text_y(mut self, el: ElementText) -> Self {
400 self.axis_text_y = el;
401 self
402 }
403
404 pub fn set_axis_title_x(mut self, el: ElementText) -> Self {
405 self.axis_title_x = el;
406 self
407 }
408
409 pub fn set_axis_title_y(mut self, el: ElementText) -> Self {
410 self.axis_title_y = el;
411 self
412 }
413
414 pub fn set_axis_line(mut self, el: ElementLine) -> Self {
415 self.axis_line = el;
416 self
417 }
418
419 pub fn set_axis_ticks(mut self, el: ElementLine) -> Self {
420 self.axis_ticks = el;
421 self
422 }
423
424 pub fn set_panel_background(mut self, el: ElementRect) -> Self {
425 self.panel_background = el;
426 self
427 }
428
429 pub fn set_panel_grid_major(mut self, el: ElementLine) -> Self {
430 self.panel_grid_major = el;
431 self
432 }
433
434 pub fn set_panel_grid_minor(mut self, el: ElementLine) -> Self {
435 self.panel_grid_minor = el;
436 self
437 }
438
439 pub fn set_plot_background(mut self, el: ElementRect) -> Self {
440 self.plot_background = el;
441 self
442 }
443
444 pub fn set_legend_position(mut self, pos: LegendPosition) -> Self {
445 self.legend_position = pos;
446 self
447 }
448
449 pub fn set_plot_margin(mut self, margin: Margin) -> Self {
450 self.plot_margin = margin;
451 self
452 }
453
454 pub fn set_title(mut self, el: ElementText) -> Self {
455 self.title = el;
456 self
457 }
458
459 pub fn set_text(mut self, el: ElementText) -> Self {
460 self.text = el;
461 self
462 }
463
464 pub fn set_subtitle(mut self, el: ElementText) -> Self {
467 self.subtitle = el;
468 self
469 }
470
471 pub fn set_caption(mut self, el: ElementText) -> Self {
472 self.caption = el;
473 self
474 }
475
476 pub fn set_legend_title(mut self, el: ElementText) -> Self {
477 self.legend_title = el;
478 self
479 }
480
481 pub fn set_legend_text(mut self, el: ElementText) -> Self {
482 self.legend_text = el;
483 self
484 }
485
486 pub fn set_strip_text(mut self, el: ElementText) -> Self {
487 self.strip_text = el;
488 self
489 }
490
491 pub fn set_axis_line_x(mut self, el: Option<ElementLine>) -> Self {
492 self.axis_line_x = el;
493 self
494 }
495
496 pub fn set_axis_line_y(mut self, el: Option<ElementLine>) -> Self {
497 self.axis_line_y = el;
498 self
499 }
500
501 pub fn set_axis_ticks_x(mut self, el: Option<ElementLine>) -> Self {
502 self.axis_ticks_x = el;
503 self
504 }
505
506 pub fn set_axis_ticks_y(mut self, el: Option<ElementLine>) -> Self {
507 self.axis_ticks_y = el;
508 self
509 }
510
511 pub fn set_panel_grid_major_x(mut self, el: Option<ElementLine>) -> Self {
512 self.panel_grid_major_x = el;
513 self
514 }
515
516 pub fn set_panel_grid_major_y(mut self, el: Option<ElementLine>) -> Self {
517 self.panel_grid_major_y = el;
518 self
519 }
520
521 pub fn set_panel_grid_minor_x(mut self, el: Option<ElementLine>) -> Self {
522 self.panel_grid_minor_x = el;
523 self
524 }
525
526 pub fn set_panel_grid_minor_y(mut self, el: Option<ElementLine>) -> Self {
527 self.panel_grid_minor_y = el;
528 self
529 }
530
531 pub fn set_panel_border(mut self, el: ElementLine) -> Self {
532 self.panel_border = el;
533 self
534 }
535
536 pub fn legend_bar_border_line(&self) -> ElementLine {
539 self.legend_bar_border.clone().unwrap_or(ElementLine {
540 color: self.legend_key.color.unwrap_or((50, 50, 50)),
541 width: 0.5,
542 visible: true,
543 linetype: crate::render::backend::Linetype::Solid,
544 })
545 }
546
547 pub fn set_calendar_month_border(mut self, el: ElementLine) -> Self {
548 self.calendar_month_border = el;
549 self
550 }
551
552 pub fn set_legend_bar_border(mut self, el: Option<ElementLine>) -> Self {
553 self.legend_bar_border = el;
554 self
555 }
556
557 pub fn set_legend_background(mut self, el: ElementRect) -> Self {
558 self.legend_background = el;
559 self
560 }
561
562 pub fn set_legend_key(mut self, el: ElementRect) -> Self {
563 self.legend_key = el;
564 self
565 }
566
567 pub fn set_strip_background(mut self, el: ElementRect) -> Self {
568 self.strip_background = el;
569 self
570 }
571
572 pub fn set_axis_ticks_length(mut self, val: f64) -> Self {
573 self.axis_ticks_length = val;
574 self
575 }
576
577 pub fn set_legend_key_width(mut self, val: f64) -> Self {
578 self.legend_key_width = val;
579 self
580 }
581
582 pub fn set_legend_key_height(mut self, val: f64) -> Self {
583 self.legend_key_height = val;
584 self
585 }
586
587 pub fn set_legend_spacing(mut self, val: f64) -> Self {
588 self.legend_spacing = val;
589 self
590 }
591
592 pub fn set_legend_margin(mut self, margin: Margin) -> Self {
593 self.legend_margin = margin;
594 self
595 }
596
597 pub fn set_panel_spacing(mut self, val: f64) -> Self {
598 self.panel_spacing = val;
599 self
600 }
601
602 pub fn set_panel_spacing_x(mut self, val: Option<f64>) -> Self {
603 self.panel_spacing_x = val;
604 self
605 }
606
607 pub fn set_panel_spacing_y(mut self, val: Option<f64>) -> Self {
608 self.panel_spacing_y = val;
609 self
610 }
611
612 pub fn update(mut self, upd: ThemeUpdate) -> Self {
615 if let Some(v) = upd.text {
616 self.text = v;
617 }
618 if let Some(v) = upd.title {
619 self.title = v;
620 }
621 if let Some(v) = upd.subtitle {
622 self.subtitle = v;
623 }
624 if let Some(v) = upd.caption {
625 self.caption = v;
626 }
627 if let Some(v) = upd.axis_text_x {
628 self.axis_text_x = v;
629 }
630 if let Some(v) = upd.axis_text_y {
631 self.axis_text_y = v;
632 }
633 if let Some(v) = upd.axis_title_x {
634 self.axis_title_x = v;
635 }
636 if let Some(v) = upd.axis_title_y {
637 self.axis_title_y = v;
638 }
639 if let Some(v) = upd.axis_line {
640 self.axis_line = v;
641 }
642 if let Some(v) = upd.axis_ticks {
643 self.axis_ticks = v;
644 }
645 if let Some(v) = upd.panel_background {
646 self.panel_background = v;
647 }
648 if let Some(v) = upd.panel_grid_major {
649 self.panel_grid_major = v;
650 }
651 if let Some(v) = upd.panel_grid_minor {
652 self.panel_grid_minor = v;
653 }
654 if let Some(v) = upd.panel_border {
655 self.panel_border = v;
656 }
657 if let Some(v) = upd.calendar_month_border {
658 self.calendar_month_border = v;
659 }
660 if let Some(v) = upd.legend_bar_border {
661 self.legend_bar_border = Some(v);
662 }
663 if let Some(v) = upd.plot_background {
664 self.plot_background = v;
665 }
666 if let Some(v) = upd.legend_position {
667 self.legend_position = v;
668 }
669 if let Some(v) = upd.legend_title {
670 self.legend_title = v;
671 }
672 if let Some(v) = upd.legend_text {
673 self.legend_text = v;
674 }
675 if let Some(v) = upd.legend_background {
676 self.legend_background = v;
677 }
678 if let Some(v) = upd.strip_text {
679 self.strip_text = v;
680 }
681 if let Some(v) = upd.strip_background {
682 self.strip_background = v;
683 }
684 if let Some(v) = upd.plot_margin {
685 self.plot_margin = v;
686 }
687 if let Some(v) = upd.geom {
688 self.geom = v;
689 }
690 self
691 }
692}
693
694#[derive(Clone, Debug, Default)]
697pub struct ThemeUpdate {
698 pub text: Option<ElementText>,
699 pub title: Option<ElementText>,
700 pub subtitle: Option<ElementText>,
701 pub caption: Option<ElementText>,
702 pub axis_text_x: Option<ElementText>,
703 pub axis_text_y: Option<ElementText>,
704 pub axis_title_x: Option<ElementText>,
705 pub axis_title_y: Option<ElementText>,
706 pub axis_line: Option<ElementLine>,
707 pub axis_ticks: Option<ElementLine>,
708 pub panel_background: Option<ElementRect>,
709 pub panel_grid_major: Option<ElementLine>,
710 pub panel_grid_minor: Option<ElementLine>,
711 pub panel_border: Option<ElementLine>,
712 pub calendar_month_border: Option<ElementLine>,
713 pub legend_bar_border: Option<ElementLine>,
714 pub plot_background: Option<ElementRect>,
715 pub legend_position: Option<LegendPosition>,
716 pub legend_title: Option<ElementText>,
717 pub legend_text: Option<ElementText>,
718 pub legend_background: Option<ElementRect>,
719 pub strip_text: Option<ElementText>,
720 pub strip_background: Option<ElementRect>,
721 pub plot_margin: Option<Margin>,
722 pub geom: Option<ElementGeom>,
724}
725
726impl Default for Theme {
727 fn default() -> Self {
728 presets::theme_gray()
729 }
730}