1#[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2use plotters::prelude::IntoDrawingArea;
3
4use crate::aes::Aes;
5use crate::annotate::Annotation;
6use crate::build::PlotBuilder;
7use crate::coord::cartesian::CoordCartesian;
8use crate::coord::fixed::CoordFixed;
9use crate::coord::flip::CoordFlip;
10use crate::coord::polar::CoordPolar;
11use crate::coord::Coord;
12use crate::data::{DataFrame, GGData, Value};
13use crate::facet::{Facet, FacetLabeller, FacetScales, FacetSpace};
14use crate::geom::area::GeomArea;
15use crate::geom::bar::GeomBar;
16use crate::geom::bin2d::GeomBin2d;
17use crate::geom::blank::GeomBlank;
18use crate::geom::boxplot::GeomBoxplot;
19use crate::geom::col::GeomCol;
20use crate::geom::contour::GeomContour;
21use crate::geom::count::GeomCount;
22use crate::geom::crossbar::GeomCrossbar;
23use crate::geom::curve::GeomCurve;
24use crate::geom::density::GeomDensity;
25use crate::geom::density2d::GeomDensity2d;
26use crate::geom::dotplot::GeomDotplot;
27use crate::geom::errorbar::GeomErrorbar;
28use crate::geom::freqpoly::GeomFreqpoly;
29use crate::geom::hex::GeomHex;
30use crate::geom::histogram::GeomHistogram;
31use crate::geom::jitter::GeomJitter;
32use crate::geom::line::GeomLine;
33use crate::geom::linerange::GeomLinerange;
34use crate::geom::path::GeomPath;
35use crate::geom::point::GeomPoint;
36use crate::geom::pointrange::GeomPointrange;
37use crate::geom::polygon::GeomPolygon;
38use crate::geom::qq::{GeomQQ, GeomQQLine};
39use crate::geom::rect::GeomRect;
40use crate::geom::refline::{GeomAbline, GeomHline, GeomVline};
41use crate::geom::ribbon::GeomRibbon;
42use crate::geom::rug::GeomRug;
43use crate::geom::segment::GeomSegment;
44use crate::geom::smooth::GeomSmooth;
45use crate::geom::spoke::GeomSpoke;
46use crate::geom::step::GeomStep;
47use crate::geom::text::{GeomLabel, GeomText};
48use crate::geom::tile::GeomTile;
49use crate::geom::violin::GeomViolin;
50use crate::geom::{Geom, GeomParams};
51use crate::position::Position;
52use crate::render::layout::PlotLayout;
53#[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
54use crate::render::plotters_backend::PlottersAdapter;
55use crate::render::renderer::PlotRenderer;
56use crate::render::RenderError;
57use crate::scale::continuous::ScaleContinuous;
58use crate::scale::transform::ScaleTransform;
59use crate::scale::Scale;
60use crate::stat::Stat;
61use crate::theme::Theme;
62
63mod stat_layers;
66
67#[derive(Clone, Debug, Default)]
69pub struct Labels {
70 pub title: Option<String>,
71 pub subtitle: Option<String>,
72 pub x: Option<String>,
73 pub y: Option<String>,
74 pub caption: Option<String>,
75 pub tag: Option<String>,
77}
78
79pub struct Layer {
81 pub data: Option<DataFrame>,
82 pub mapping: Aes,
83 pub geom: Box<dyn Geom>,
84 pub stat: Box<dyn Stat>,
85 pub position: Box<dyn Position>,
86 pub params: GeomParams,
87 pub show_legend: Option<bool>,
88 pub explicit_style: bool,
91}
92
93pub struct GGPlot {
95 pub(crate) data: DataFrame,
96 pub(crate) mapping: Aes,
97 pub(crate) layers: Vec<Layer>,
98 pub(crate) scales: Vec<Box<dyn Scale>>,
99 pub(crate) coord: Box<dyn Coord>,
100 pub(crate) theme: Theme,
101 pub(crate) labels: Labels,
102 pub(crate) facet: Facet,
103 pub(crate) annotations: Vec<Annotation>,
104 pub(crate) guide_legend: crate::guide::config::GuideLegend,
105 pub(crate) warnings: Vec<String>,
108 pub(crate) default_aspect_ratio: Option<f64>,
111}
112
113impl GGPlot {
114 pub fn new(data: impl GGData) -> Self {
121 GGPlot {
122 data: data.into_dataframe(),
123 mapping: Aes::default(),
124 layers: Vec::new(),
125 scales: Vec::new(),
126 coord: Box::new(CoordCartesian::new()),
127 theme: Theme::default(),
128 labels: Labels::default(),
129 facet: Facet::default(),
130 annotations: Vec::new(),
131 guide_legend: crate::guide::config::GuideLegend::default(),
132 warnings: Vec::new(),
133 default_aspect_ratio: None,
134 }
135 }
136
137 pub fn aes(mut self, mapping: Aes) -> Self {
139 self.mapping = mapping;
140 self
141 }
142
143 pub fn geom_point(self) -> Self {
146 self.add_geom(GeomPoint::default())
147 }
148
149 pub fn geom_point_with(self, geom: GeomPoint) -> Self {
150 self.add_geom_with(geom)
151 }
152
153 pub fn geom_line(self) -> Self {
154 self.add_geom(GeomLine::default())
155 }
156
157 pub fn geom_line_with(self, geom: GeomLine) -> Self {
158 self.add_geom_with(geom)
159 }
160
161 pub fn geom_bar(self) -> Self {
162 self.add_geom(GeomBar::default())
163 }
164
165 pub fn geom_bar_with(self, geom: GeomBar) -> Self {
166 self.add_geom_with(geom)
167 }
168
169 pub fn geom_histogram(self) -> Self {
170 self.add_geom(GeomHistogram::default())
171 }
172
173 pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self {
174 self.add_geom_with(geom)
175 }
176
177 pub fn geom_boxplot(self) -> Self {
178 self.add_geom(GeomBoxplot::default())
179 }
180
181 pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self {
182 self.add_geom_with(geom)
183 }
184
185 pub fn geom_smooth(self) -> Self {
186 self.add_geom(GeomSmooth::default())
187 }
188
189 pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self {
190 self.add_geom_with(geom)
191 }
192
193 pub fn geom_col(self) -> Self {
194 self.add_geom(GeomCol::default())
195 }
196
197 pub fn geom_col_with(self, geom: GeomCol) -> Self {
198 self.add_geom_with(geom)
199 }
200
201 pub fn geom_hline(self, yintercept: f64) -> Self {
205 self.add_refline(
206 GeomHline::new(yintercept),
207 false,
208 &[("yintercept", yintercept)],
209 )
210 }
211
212 pub fn geom_hline_with(self, geom: GeomHline) -> Self {
214 let y = geom.yintercept;
215 self.add_refline(geom, true, &[("yintercept", y)])
216 }
217
218 pub fn geom_hline_aes(self, mapping: Aes) -> Self {
242 self.add_geom(GeomHline::mapped()).layer_aes(mapping)
243 }
244
245 pub fn geom_hline_aes_with(self, geom: GeomHline, mapping: Aes) -> Self {
247 self.add_geom_with(geom).layer_aes(mapping)
248 }
249
250 pub fn geom_vline(self, xintercept: f64) -> Self {
253 self.add_refline(
254 GeomVline::new(xintercept),
255 false,
256 &[("xintercept", xintercept)],
257 )
258 }
259
260 pub fn geom_vline_with(self, geom: GeomVline) -> Self {
262 let x = geom.xintercept;
263 self.add_refline(geom, true, &[("xintercept", x)])
264 }
265
266 pub fn geom_vline_aes(self, mapping: Aes) -> Self {
269 self.add_geom(GeomVline::mapped()).layer_aes(mapping)
270 }
271
272 pub fn geom_vline_aes_with(self, geom: GeomVline, mapping: Aes) -> Self {
274 self.add_geom_with(geom).layer_aes(mapping)
275 }
276
277 pub fn geom_abline(self, slope: f64, intercept: f64) -> Self {
280 self.add_refline(
281 GeomAbline::new(slope, intercept),
282 false,
283 &[("slope", slope), ("intercept", intercept)],
284 )
285 }
286
287 pub fn geom_abline_with(self, geom: GeomAbline) -> Self {
289 let (b, a) = (geom.slope, geom.intercept);
290 self.add_refline(geom, true, &[("slope", b), ("intercept", a)])
291 }
292
293 pub fn geom_abline_aes(self, mapping: Aes) -> Self {
296 self.add_geom(GeomAbline::mapped()).layer_aes(mapping)
297 }
298
299 pub fn geom_abline_aes_with(self, geom: GeomAbline, mapping: Aes) -> Self {
301 self.add_geom_with(geom).layer_aes(mapping)
302 }
303
304 fn add_refline(
307 self,
308 geom: impl Geom + 'static,
309 explicit: bool,
310 values: &[(&str, f64)],
311 ) -> Self {
312 let mut data = DataFrame::new();
313 let mut mapping = Aes::new();
314 for (col, v) in values {
315 data.add_column(col.to_string(), vec![Value::Float(*v)]);
316 mapping = match *col {
317 "xintercept" => mapping.xintercept(col),
318 "yintercept" => mapping.yintercept(col),
319 "slope" => mapping.slope(col),
320 _ => mapping.intercept(col),
321 };
322 }
323 let plot = if explicit {
324 self.add_geom_with(geom)
325 } else {
326 self.add_geom(geom)
327 };
328 plot.layer_aes(mapping).layer_data(data)
329 }
330
331 pub fn geom_text(self) -> Self {
332 self.add_geom(GeomText::default())
333 }
334
335 pub fn geom_text_with(self, geom: GeomText) -> Self {
336 self.add_geom_with(geom)
337 }
338
339 #[cfg(feature = "ggpubr")]
344 pub fn stat_cor(self) -> Self {
345 self.geom_text().stat(crate::stat::cor::StatCor::default())
346 }
347
348 #[cfg(feature = "ggpubr")]
351 pub fn stat_cor_with(self, stat: crate::stat::cor::StatCor) -> Self {
352 self.geom_text().stat(stat)
353 }
354
355 #[cfg(feature = "ggpubr")]
360 pub fn stat_compare_means(self) -> Self {
361 self.geom_text()
362 .stat(crate::stat::compare_means::StatCompareMeans::default())
363 }
364
365 #[cfg(feature = "ggpubr")]
368 pub fn stat_compare_means_with(
369 self,
370 stat: crate::stat::compare_means::StatCompareMeans,
371 ) -> Self {
372 self.geom_text().stat(stat)
373 }
374
375 #[cfg(feature = "ggpubr")]
381 pub fn stat_compare_means_pairwise(self, comparisons: &[(&str, &str)]) -> Self {
382 self.stat_compare_means_pairwise_with(
383 crate::stat::compare_means::CompareMethod::Auto,
384 comparisons,
385 )
386 }
387
388 #[cfg(feature = "ggpubr")]
391 pub fn stat_compare_means_pairwise_with(
392 self,
393 method: crate::stat::compare_means::CompareMethod,
394 comparisons: &[(&str, &str)],
395 ) -> Self {
396 let col_for = |a: crate::aes::Aesthetic| {
398 self.mapping
399 .mappings
400 .iter()
401 .find(|m| m.aesthetic == a)
402 .map(|m| m.column.clone())
403 };
404 let (xcol, ycol) = match (
405 col_for(crate::aes::Aesthetic::X),
406 col_for(crate::aes::Aesthetic::Y),
407 ) {
408 (Some(x), Some(y)) => (x, y),
409 _ => return self,
410 };
411 let (xc, yc) = match (self.data.column(&xcol), self.data.column(&ycol)) {
412 (Some(x), Some(y)) => (x, y),
413 _ => return self,
414 };
415
416 let mut groups: Vec<(String, Vec<f64>)> = Vec::new();
418 let mut ymax = f64::NEG_INFINITY;
419 let mut ymin = f64::INFINITY;
420 for (xv, yv) in xc.iter().zip(yc.iter()) {
421 let y = match yv.as_f64() {
422 Some(y) if y.is_finite() => y,
423 _ => continue,
424 };
425 ymax = ymax.max(y);
426 ymin = ymin.min(y);
427 let key = xv.to_group_key();
428 if let Some(g) = groups.iter_mut().find(|(k, _)| *k == key) {
429 g.1.push(y);
430 } else {
431 groups.push((key, vec![y]));
432 }
433 }
434 if groups.len() < 2 {
435 return self;
436 }
437 let step = if ymax > ymin {
438 (ymax - ymin) * 0.12
439 } else {
440 1.0
441 };
442
443 let (mut xmin_v, mut xmax_v, mut y_v, mut label_v) =
445 (Vec::new(), Vec::new(), Vec::new(), Vec::new());
446 let mut placed = 0usize;
447 for (a, b) in comparisons {
448 let ga = groups.iter().find(|(k, _)| k == a).map(|g| &g.1);
449 let gb = groups.iter().find(|(k, _)| k == b).map(|g| &g.1);
450 let (ga, gb) = match (ga, gb) {
451 (Some(x), Some(y)) if x.len() >= 2 && y.len() >= 2 => (x, y),
452 _ => continue,
453 };
454 let p = match crate::stat::compare_means::pairwise_p(method, ga, gb) {
455 Some(p) => p,
456 None => continue,
457 };
458 xmin_v.push(Value::Str((*a).to_string()));
459 xmax_v.push(Value::Str((*b).to_string()));
460 y_v.push(Value::Float(ymax + step * (placed as f64 + 1.0)));
461 label_v.push(Value::Str(crate::stat::cor::format_p_value(p)));
462 placed += 1;
463 }
464 if xmin_v.is_empty() {
465 return self;
466 }
467
468 let data = vec![
469 ("xmin".to_string(), xmin_v),
470 ("xmax".to_string(), xmax_v),
471 ("y".to_string(), y_v),
472 ("label".to_string(), label_v),
473 ];
474 self.add_geom(crate::geom::bracket::GeomBracket::default())
475 .layer_data(data)
476 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
477 }
478
479 pub fn geom_bracket(self, xmin: &str, xmax: &str, y: f64, label: &str) -> Self {
484 self.geom_bracket_many(
485 crate::geom::bracket::GeomBracket::default(),
486 &[(xmin, xmax, y, label)],
487 )
488 }
489
490 pub fn geom_bracket_many(
494 self,
495 geom: crate::geom::bracket::GeomBracket,
496 brackets: &[(&str, &str, f64, &str)],
497 ) -> Self {
498 let xmin = brackets
499 .iter()
500 .map(|b| Value::Str(b.0.to_string()))
501 .collect::<Vec<_>>();
502 let xmax = brackets
503 .iter()
504 .map(|b| Value::Str(b.1.to_string()))
505 .collect::<Vec<_>>();
506 let y = brackets
507 .iter()
508 .map(|b| Value::Float(b.2))
509 .collect::<Vec<_>>();
510 let label = brackets
511 .iter()
512 .map(|b| Value::Str(b.3.to_string()))
513 .collect::<Vec<_>>();
514 let data = vec![
515 ("xmin".to_string(), xmin),
516 ("xmax".to_string(), xmax),
517 ("y".to_string(), y),
518 ("label".to_string(), label),
519 ];
520 self.add_geom(geom)
521 .layer_data(data)
522 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
523 }
524
525 pub fn geom_bracket_table(
534 mut self,
535 table: impl GGData,
536 spec: crate::geom::bracket::BracketTable,
537 ) -> Self {
538 let table = table.into_dataframe();
539 let col_for = |a: crate::aes::Aesthetic| {
540 self.mapping
541 .mappings
542 .iter()
543 .find(|m| m.aesthetic == a)
544 .map(|m| m.column.clone())
545 };
546 let x_levels: Vec<String> = col_for(crate::aes::Aesthetic::X)
549 .and_then(|c| self.data.column(&c))
550 .filter(|col| col.iter().any(|v| matches!(v, Value::Str(_))))
551 .map(|col| {
552 let mut seen = std::collections::HashSet::new();
553 col.iter()
554 .filter(|v| !v.is_na())
555 .map(|v| v.to_group_key())
556 .filter(|k| seen.insert(k.clone()))
557 .collect()
558 })
559 .unwrap_or_default();
560 let y_range = col_for(crate::aes::Aesthetic::Y)
561 .and_then(|c| self.data.column(&c))
562 .and_then(|col| {
563 let (lo, hi) = col
564 .iter()
565 .filter_map(|v| v.as_f64())
566 .filter(|v| v.is_finite())
567 .fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), v| {
568 (lo.min(v), hi.max(v))
569 });
570 (lo <= hi).then_some((lo, hi))
571 });
572 let mut warnings = Vec::new();
573 let resolved = spec.resolve(&table, &x_levels, y_range, &mut warnings);
574 self.warnings.extend(warnings);
575 match resolved {
576 Some(data) => self
577 .add_geom(crate::geom::bracket::GeomBracket::from(spec.geom))
578 .layer_data(data)
579 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label")),
580 None => self,
581 }
582 }
583
584 pub fn geom_text_repel(self) -> Self {
589 self.add_geom(crate::geom::repel::GeomTextRepel::default())
590 }
591
592 pub fn geom_text_repel_with(self, geom: crate::geom::repel::GeomTextRepel) -> Self {
595 self.add_geom_with(geom)
596 }
597
598 pub fn geom_label_repel(self) -> Self {
601 self.add_geom(crate::geom::repel::GeomLabelRepel::default())
602 }
603
604 pub fn geom_label_repel_with(self, geom: crate::geom::repel::GeomLabelRepel) -> Self {
606 self.add_geom_with(geom)
607 }
608
609 pub fn geom_label(self) -> Self {
610 self.add_geom(GeomLabel::default())
611 }
612
613 pub fn geom_label_with(self, geom: GeomLabel) -> Self {
614 self.add_geom_with(geom)
615 }
616
617 pub fn geom_area(self) -> Self {
618 self.add_geom(GeomArea::default())
619 }
620
621 pub fn geom_area_with(self, geom: GeomArea) -> Self {
622 self.add_geom_with(geom)
623 }
624
625 pub fn geom_ribbon(self) -> Self {
626 self.add_geom(GeomRibbon::default())
627 }
628
629 pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self {
630 self.add_geom_with(geom)
631 }
632
633 pub fn geom_errorbar(self) -> Self {
634 self.add_geom(GeomErrorbar::default())
635 }
636
637 pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self {
638 self.add_geom_with(geom)
639 }
640
641 pub fn geom_segment(self) -> Self {
642 self.add_geom(GeomSegment::default())
643 }
644
645 pub fn geom_segment_with(self, geom: GeomSegment) -> Self {
646 self.add_geom_with(geom)
647 }
648
649 pub fn geom_density(self) -> Self {
650 self.add_geom(GeomDensity::default())
651 }
652
653 pub fn geom_density_with(self, geom: GeomDensity) -> Self {
654 self.add_geom_with(geom)
655 }
656
657 pub fn geom_rug(self) -> Self {
658 self.add_geom(GeomRug::default())
659 }
660
661 pub fn geom_rug_with(self, geom: GeomRug) -> Self {
662 self.add_geom_with(geom)
663 }
664
665 pub fn geom_jitter(self) -> Self {
666 self.add_geom(GeomJitter::default())
667 }
668
669 pub fn geom_jitter_with(self, geom: GeomJitter) -> Self {
670 self.add_geom_with(geom)
671 }
672
673 pub fn geom_path(self) -> Self {
674 self.add_geom(GeomPath::default())
675 }
676
677 pub fn geom_path_with(self, geom: GeomPath) -> Self {
678 self.add_geom_with(geom)
679 }
680
681 pub fn stat_ellipse(self) -> Self {
683 self.geom_path()
684 .stat(crate::stat::ellipse::StatEllipse::default())
685 }
686
687 pub fn stat_ellipse_level(self, level: f64) -> Self {
689 self.geom_path()
690 .stat(crate::stat::ellipse::StatEllipse::new(level))
691 }
692
693 #[cfg(feature = "regression")]
696 pub fn stat_quantile(mut self, taus: &[f64]) -> Self {
697 for &tau in taus {
698 self = self
699 .geom_path()
700 .stat(crate::stat::quantile::StatQuantile::new(tau));
701 }
702 self
703 }
704
705 #[cfg(feature = "regression")]
707 pub fn geom_quantile(self) -> Self {
708 self.stat_quantile(&[0.25, 0.5, 0.75])
709 }
710
711 pub fn geom_step(self) -> Self {
712 self.add_geom(GeomStep::default())
713 }
714
715 pub fn geom_step_with(self, geom: GeomStep) -> Self {
716 self.add_geom_with(geom)
717 }
718
719 pub fn geom_freqpoly(self) -> Self {
720 self.add_geom(GeomFreqpoly::default())
721 }
722
723 pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self {
724 self.add_geom_with(geom)
725 }
726
727 pub fn geom_linerange(self) -> Self {
728 self.add_geom(GeomLinerange::default())
729 }
730
731 pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self {
732 self.add_geom_with(geom)
733 }
734
735 pub fn geom_pointrange(self) -> Self {
736 self.add_geom(GeomPointrange::default())
737 }
738
739 pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self {
740 self.add_geom_with(geom)
741 }
742
743 pub fn geom_crossbar(self) -> Self {
744 self.add_geom(GeomCrossbar::default())
745 }
746
747 pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self {
748 self.add_geom_with(geom)
749 }
750
751 pub fn geom_spoke(self) -> Self {
752 self.add_geom(GeomSpoke::default())
753 }
754
755 pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self {
756 self.add_geom_with(geom)
757 }
758
759 pub fn geom_rect(self) -> Self {
760 self.add_geom(GeomRect::default())
761 }
762
763 pub fn geom_rect_with(self, geom: GeomRect) -> Self {
764 self.add_geom_with(geom)
765 }
766
767 pub fn geom_tile(self) -> Self {
768 self.add_geom(GeomTile::default())
769 }
770
771 pub fn geom_tile_with(self, geom: GeomTile) -> Self {
772 self.add_geom_with(geom)
773 }
774
775 pub fn geom_raster(self) -> Self {
777 self.add_geom(crate::geom::raster::GeomRaster::default())
778 }
779
780 pub fn geom_raster_with(self, geom: crate::geom::raster::GeomRaster) -> Self {
781 self.add_geom_with(geom)
782 }
783
784 pub fn geom_polygon(self) -> Self {
785 self.add_geom(GeomPolygon::default())
786 }
787
788 pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self {
789 self.add_geom_with(geom)
790 }
791
792 #[cfg(feature = "sf")]
794 pub fn geom_sf(self) -> Self {
795 self.add_geom(crate::geom::sf::GeomSf::default())
796 }
797
798 #[cfg(feature = "sf")]
799 pub fn geom_sf_with(self, geom: crate::geom::sf::GeomSf) -> Self {
800 self.add_geom_with(geom)
801 }
802
803 pub fn geom_curve(self) -> Self {
804 self.add_geom(GeomCurve::default())
805 }
806
807 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
808 self.add_geom_with(geom)
809 }
810
811 pub fn geom_violin(self) -> Self {
812 self.add_geom(GeomViolin::default())
813 }
814
815 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
816 self.add_geom_with(geom)
817 }
818
819 pub fn geom_dotplot(self) -> Self {
820 self.add_geom(GeomDotplot::default())
821 }
822
823 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
824 self.add_geom_with(geom)
825 }
826
827 pub fn geom_qq(self) -> Self {
828 self.add_geom(GeomQQ::default())
829 }
830
831 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
832 self.add_geom_with(geom)
833 }
834
835 pub fn geom_qq_line(self) -> Self {
836 self.add_geom(GeomQQLine::default())
837 }
838
839 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
840 self.add_geom_with(geom)
841 }
842
843 pub fn geom_bin2d(self) -> Self {
844 self.add_geom(GeomBin2d::default())
845 }
846
847 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
848 self.add_geom_with(geom)
849 }
850
851 pub fn geom_hex(self) -> Self {
852 self.add_geom(GeomHex::default())
853 }
854
855 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
856 self.add_geom_with(geom)
857 }
858
859 pub fn geom_count(self) -> Self {
860 self.add_geom(GeomCount::default())
861 }
862
863 pub fn geom_count_with(self, geom: GeomCount) -> Self {
864 self.add_geom_with(geom)
865 }
866
867 pub fn geom_contour(self) -> Self {
868 self.add_geom(GeomContour::default())
869 }
870
871 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
872 self.add_geom_with(geom)
873 }
874
875 pub fn geom_contour_filled(self) -> Self {
878 self.add_geom(GeomPolygon {
879 line_width: 0.0,
880 alpha: 1.0,
881 ..GeomPolygon::default()
882 })
883 .stat(crate::stat::contour_filled::StatContourFilled::default())
884 }
885
886 pub fn geom_density2d(self) -> Self {
887 self.add_geom(GeomDensity2d::default())
888 }
889
890 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
891 self.add_geom_with(geom)
892 }
893
894 pub fn geom_candlestick(self) -> Self {
897 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
898 }
899
900 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
901 self.add_geom_with(geom)
902 }
903
904 pub fn geom_ohlc(self) -> Self {
907 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
908 }
909
910 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
911 self.add_geom_with(geom)
912 }
913
914 pub fn geom_calendar(self) -> Self {
925 self.geom_calendar_with(
926 GeomTile {
927 color: (255, 255, 255),
928 line_width: 1.5,
929 ..Default::default()
930 },
931 false,
932 )
933 }
934
935 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
938 use crate::stat::calendar::{
939 day_number, month_boundaries, month_breaks, CalendarGrid, StatCalendar,
940 MAX_CALENDAR_YEARS,
941 };
942 let grid = self
943 .mapping
944 .get_mapping(&crate::aes::Aesthetic::X)
945 .and_then(|c| self.data.column(c))
946 .and_then(|col| {
947 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
948 });
949 let Some((grid, clipped)) = grid else {
950 return self.add_geom_with(tile).stat(StatCalendar {
952 grid: None,
953 monday_first,
954 });
955 };
956 if clipped {
957 self.warnings.push(format!(
958 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
959 ));
960 }
961 let (xb, xl) = month_breaks(&grid);
962 let (yb, yl) = if monday_first {
964 (vec![6.0, 4.0, 2.0], ["Mon", "Wed", "Fri"])
965 } else {
966 (vec![5.0, 3.0, 1.0], ["Mon", "Wed", "Fri"])
967 };
968 let segs = month_boundaries(&grid);
969 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
970 segs.iter().map(|s| Value::Float(f(s))).collect()
971 };
972 let boundaries = vec![
973 ("x".to_string(), col(|s| s.0)),
974 ("y".to_string(), col(|s| s.1)),
975 ("xend".to_string(), col(|s| s.2)),
976 ("yend".to_string(), col(|s| s.3)),
977 ];
978 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
979 let plot = self
980 .add_geom_with(tile)
981 .stat(StatCalendar {
982 grid: Some(grid),
983 monday_first,
984 })
985 .scale_x_continuous(
986 ScaleContinuous::new()
987 .with_breaks(xb)
988 .with_labels(xl)
989 .with_expand(0.0, 0.0),
990 )
991 .scale_y_continuous(
992 ScaleContinuous::new()
993 .with_breaks(yb)
994 .with_labels(yl.iter().map(|s| s.to_string()).collect())
995 .with_expand(0.0, 0.0),
996 );
997 if segs.is_empty() {
998 return plot;
999 }
1000 plot.geom_segment_with(GeomSegment {
1001 color: (120, 128, 140),
1002 width: 1.2,
1003 alpha: 1.0,
1004 })
1005 .layer_data(boundaries)
1006 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1007 .show_legend(false)
1008 }
1009
1010 pub fn geom_blank(self) -> Self {
1011 self.add_geom(GeomBlank)
1012 }
1013
1014 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
1017 let mut plot = self.add_geom(geom);
1018 if let Some(layer) = plot.layers.last_mut() {
1019 layer.explicit_style = true;
1020 }
1021 plot
1022 }
1023
1024 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
1025 let stat = geom.default_stat();
1026 let position = geom.default_position();
1027 let params = geom.default_params();
1028 self.layers.push(Layer {
1029 data: None,
1030 mapping: Aes::default(),
1031 geom: Box::new(geom),
1032 stat,
1033 position,
1034 params,
1035 show_legend: None,
1036 explicit_style: false,
1037 });
1038 self
1039 }
1040
1041 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
1045 if let Some(layer) = self.layers.last_mut() {
1046 layer.stat = Box::new(stat);
1047 }
1048 self
1049 }
1050
1051 pub fn position(mut self, pos: impl Position + 'static) -> Self {
1053 if let Some(layer) = self.layers.last_mut() {
1054 layer.position = Box::new(pos);
1055 }
1056 self
1057 }
1058
1059 pub fn layer_data(mut self, data: impl GGData) -> Self {
1061 if let Some(layer) = self.layers.last_mut() {
1062 layer.data = Some(data.into_dataframe());
1063 }
1064 self
1065 }
1066
1067 pub fn layer_aes(mut self, mapping: Aes) -> Self {
1069 if let Some(layer) = self.layers.last_mut() {
1070 layer.mapping = mapping;
1071 }
1072 self
1073 }
1074
1075 pub fn show_legend(mut self, show: bool) -> Self {
1078 if let Some(layer) = self.layers.last_mut() {
1079 layer.show_legend = Some(show);
1080 }
1081 self
1082 }
1083
1084 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
1087 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1088 self.scales.push(Box::new(s));
1089 self
1090 }
1091
1092 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
1093 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1094 self.scales.push(Box::new(s));
1095 self
1096 }
1097
1098 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1099 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1100 self.scales.push(Box::new(s));
1101 self
1102 }
1103
1104 pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1105 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1106 self.scales.push(Box::new(s));
1107 self
1108 }
1109
1110 pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
1111 self.scales.push(Box::new(s));
1112 self
1113 }
1114
1115 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
1116 self.scales.push(Box::new(s));
1117 self
1118 }
1119
1120 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1121 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
1122 self.scale_color(s)
1123 }
1124
1125 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1126 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
1127 self.scale_fill(s)
1128 }
1129
1130 pub fn scale_color_viridis(self) -> Self {
1131 use crate::scale::color::ScaleColorDiscrete;
1132 use crate::scale::palettes::PaletteName;
1133 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
1134 .with_named_palette(&PaletteName::Viridis);
1135 self.scale_color(s)
1136 }
1137
1138 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1139 use crate::scale::color::ScaleColorDiscrete;
1140 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
1141 self.scale_color(s)
1142 }
1143
1144 pub fn scale_color_gradient(
1145 self,
1146 low: crate::scale::color::RGBAColor,
1147 high: crate::scale::color::RGBAColor,
1148 ) -> Self {
1149 use crate::scale::color::ScaleColorContinuous;
1150 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
1151 self.scale_color(s)
1152 }
1153
1154 pub fn scale_fill_gradient(
1155 self,
1156 low: crate::scale::color::RGBAColor,
1157 high: crate::scale::color::RGBAColor,
1158 ) -> Self {
1159 use crate::scale::color::ScaleColorContinuous;
1160 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
1161 self.scale_fill(s)
1162 }
1163
1164 pub fn scale_color_gradient2(
1165 self,
1166 low: crate::scale::color::RGBAColor,
1167 mid: crate::scale::color::RGBAColor,
1168 high: crate::scale::color::RGBAColor,
1169 ) -> Self {
1170 use crate::scale::gradient::ScaleColorGradient2;
1171 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
1172 self.scale_color(s)
1173 }
1174
1175 pub fn scale_fill_gradient2(
1176 self,
1177 low: crate::scale::color::RGBAColor,
1178 mid: crate::scale::color::RGBAColor,
1179 high: crate::scale::color::RGBAColor,
1180 ) -> Self {
1181 use crate::scale::gradient::ScaleColorGradient2;
1182 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
1183 self.scale_fill(s)
1184 }
1185
1186 pub fn scale_fill_viridis(self) -> Self {
1187 use crate::scale::color::ScaleColorDiscrete;
1188 use crate::scale::palettes::PaletteName;
1189 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
1190 .with_named_palette(&PaletteName::Viridis);
1191 self.scale_fill(s)
1192 }
1193
1194 pub fn scale_color_viridis_c(self) -> Self {
1196 use crate::scale::gradient_n::ScaleColorGradientN;
1197 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
1198 self.scale_color(s)
1199 }
1200
1201 pub fn scale_fill_viridis_c(self) -> Self {
1203 use crate::scale::gradient_n::ScaleColorGradientN;
1204 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
1205 self.scale_fill(s)
1206 }
1207
1208 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1210 use crate::scale::gradient_n::ScaleColorGradientN;
1211 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
1212 self.scale_color(s)
1213 }
1214
1215 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1217 use crate::scale::gradient_n::ScaleColorGradientN;
1218 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
1219 self.scale_fill(s)
1220 }
1221
1222 pub fn scale_color_steps(
1225 self,
1226 low: crate::scale::color::RGBAColor,
1227 high: crate::scale::color::RGBAColor,
1228 n_bins: usize,
1229 ) -> Self {
1230 let s = crate::scale::steps::ScaleColorSteps::two(
1231 crate::aes::Aesthetic::Color,
1232 (low.r, low.g, low.b),
1233 (high.r, high.g, high.b),
1234 n_bins,
1235 );
1236 self.scale_color(s)
1237 }
1238
1239 pub fn scale_color_stepsn(
1241 self,
1242 stops: Vec<crate::scale::color::RGBAColor>,
1243 n_bins: usize,
1244 ) -> Self {
1245 let s = crate::scale::steps::ScaleColorSteps::new(
1246 crate::aes::Aesthetic::Color,
1247 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
1248 n_bins,
1249 );
1250 self.scale_color(s)
1251 }
1252
1253 pub fn scale_color_fermenter(
1255 self,
1256 name: crate::scale::palettes::PaletteName,
1257 n_bins: usize,
1258 ) -> Self {
1259 let stops = crate::scale::palettes::palette(&name)
1260 .iter()
1261 .map(|c| (c.r, c.g, c.b))
1262 .collect();
1263 let s =
1264 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
1265 self.scale_color(s)
1266 }
1267
1268 pub fn scale_fill_steps(
1270 self,
1271 low: crate::scale::color::RGBAColor,
1272 high: crate::scale::color::RGBAColor,
1273 n_bins: usize,
1274 ) -> Self {
1275 let s = crate::scale::steps::ScaleColorSteps::two(
1276 crate::aes::Aesthetic::Fill,
1277 (low.r, low.g, low.b),
1278 (high.r, high.g, high.b),
1279 n_bins,
1280 );
1281 self.scale_fill(s)
1282 }
1283
1284 pub fn scale_fill_fermenter(
1286 self,
1287 name: crate::scale::palettes::PaletteName,
1288 n_bins: usize,
1289 ) -> Self {
1290 let stops = crate::scale::palettes::palette(&name)
1291 .iter()
1292 .map(|c| (c.r, c.g, c.b))
1293 .collect();
1294 let s =
1295 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
1296 self.scale_fill(s)
1297 }
1298
1299 pub fn scale_fill_discrete_sorted(self) -> Self {
1304 use crate::scale::color::ScaleColorDiscrete;
1305 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
1306 }
1307
1308 pub fn scale_color_discrete_sorted(self) -> Self {
1311 use crate::scale::color::ScaleColorDiscrete;
1312 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
1313 }
1314
1315 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1316 use crate::scale::color::ScaleColorDiscrete;
1317 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
1318 self.scale_fill(s)
1319 }
1320
1321 pub fn scale_linetype_manual(
1322 self,
1323 values: Vec<(&str, crate::render::backend::Linetype)>,
1324 ) -> Self {
1325 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
1326 self.scale_color(s)
1327 }
1328
1329 pub fn scale_shape_manual(
1330 self,
1331 values: Vec<(&str, crate::render::backend::PointShape)>,
1332 ) -> Self {
1333 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
1334 self.scale_color(s)
1335 }
1336
1337 pub fn scale_color_grey(self) -> Self {
1338 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
1339 self.scale_color(s)
1340 }
1341
1342 pub fn scale_fill_grey(self) -> Self {
1343 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
1344 self.scale_fill(s)
1345 }
1346
1347 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1348 self.scale_color(s)
1349 }
1350
1351 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1352 self.scale_fill(s)
1353 }
1354
1355 pub fn scale_x_reverse(self) -> Self {
1356 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1357 }
1358
1359 pub fn scale_y_reverse(self) -> Self {
1360 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1361 }
1362
1363 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1364 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1365 self.scales.push(Box::new(s));
1366 self
1367 }
1368
1369 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1370 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1371 self.scales.push(Box::new(s));
1372 self
1373 }
1374
1375 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
1376 self.scales.push(Box::new(s));
1377 self
1378 }
1379
1380 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
1381 self.scales.push(Box::new(s));
1382 self
1383 }
1384
1385 pub fn xlim(self, min: f64, max: f64) -> Self {
1386 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
1387 }
1388
1389 pub fn ylim(self, min: f64, max: f64) -> Self {
1390 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
1391 }
1392
1393 pub fn scale_x_log10(self) -> Self {
1394 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1395 }
1396
1397 pub fn scale_y_log10(self) -> Self {
1398 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1399 }
1400
1401 pub fn scale_x_sqrt(self) -> Self {
1402 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1403 }
1404
1405 pub fn scale_y_sqrt(self) -> Self {
1406 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1407 }
1408
1409 pub fn scale_x_log2(self) -> Self {
1410 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1411 }
1412
1413 pub fn scale_y_log2(self) -> Self {
1414 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1415 }
1416
1417 pub fn scale_x_ln(self) -> Self {
1418 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1419 }
1420
1421 pub fn scale_y_ln(self) -> Self {
1422 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1423 }
1424
1425 pub fn scale_x_logit(self) -> Self {
1427 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1428 }
1429
1430 pub fn scale_y_logit(self) -> Self {
1431 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1432 }
1433
1434 pub fn scale_x_probit(self) -> Self {
1436 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1437 }
1438
1439 pub fn scale_y_probit(self) -> Self {
1440 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1441 }
1442
1443 pub fn scale_x_pseudo_log(self) -> Self {
1445 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1446 }
1447
1448 pub fn scale_y_pseudo_log(self) -> Self {
1449 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1450 }
1451
1452 pub fn scale_x_reciprocal(self) -> Self {
1454 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1455 }
1456
1457 pub fn scale_y_reciprocal(self) -> Self {
1458 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1459 }
1460
1461 pub fn scale_x_exp(self) -> Self {
1463 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1464 }
1465
1466 pub fn scale_y_exp(self) -> Self {
1467 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1468 }
1469
1470 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
1472 self.scale_x_continuous(
1473 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1474 )
1475 }
1476
1477 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
1478 self.scale_y_continuous(
1479 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1480 )
1481 }
1482
1483 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
1486 self.facet = Facet::Wrap {
1487 var: var.to_string(),
1488 ncol,
1489 scales: FacetScales::Fixed,
1490 labeller: FacetLabeller::default(),
1491 };
1492 self
1493 }
1494
1495 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
1496 self.facet = Facet::Wrap {
1497 var: var.to_string(),
1498 ncol,
1499 scales,
1500 labeller: FacetLabeller::default(),
1501 };
1502 self
1503 }
1504
1505 pub fn facet_wrap_labeller(
1506 mut self,
1507 var: &str,
1508 ncol: Option<usize>,
1509 labeller: FacetLabeller,
1510 ) -> Self {
1511 self.facet = Facet::Wrap {
1512 var: var.to_string(),
1513 ncol,
1514 scales: FacetScales::Fixed,
1515 labeller,
1516 };
1517 self
1518 }
1519
1520 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
1521 self.facet = Facet::Grid {
1522 row_var: row.map(String::from),
1523 col_var: col.map(String::from),
1524 scales: FacetScales::Fixed,
1525 labeller: FacetLabeller::default(),
1526 space: FacetSpace::Fixed,
1527 };
1528 self
1529 }
1530
1531 pub fn facet_grid_free(
1532 mut self,
1533 row: Option<&str>,
1534 col: Option<&str>,
1535 scales: FacetScales,
1536 ) -> Self {
1537 self.facet = Facet::Grid {
1538 row_var: row.map(String::from),
1539 col_var: col.map(String::from),
1540 scales,
1541 labeller: FacetLabeller::default(),
1542 space: FacetSpace::Fixed,
1543 };
1544 self
1545 }
1546
1547 pub fn facet_grid_multi(
1551 mut self,
1552 row: Option<&str>,
1553 cols: &[&str],
1554 scales: FacetScales,
1555 space: FacetSpace,
1556 ) -> Self {
1557 let col_var = if cols.len() > 1 {
1558 let n = self.data.nrows();
1560 let mut combined = Vec::with_capacity(n);
1561 for i in 0..n {
1562 let parts: Vec<String> = cols
1563 .iter()
1564 .map(|c| {
1565 self.data
1566 .column(c)
1567 .and_then(|col| col.get(i))
1568 .map(|v| v.to_group_key())
1569 .unwrap_or_default()
1570 })
1571 .collect();
1572 combined.push(crate::data::Value::Str(parts.join(" . ")));
1573 }
1574 let name = "__facet_cols__".to_string();
1575 self.data.add_column(name.clone(), combined);
1576 Some(name)
1577 } else {
1578 cols.first().map(|s| s.to_string())
1579 };
1580 self.facet = Facet::Grid {
1581 row_var: row.map(String::from),
1582 col_var,
1583 scales,
1584 labeller: FacetLabeller::default(),
1585 space,
1586 };
1587 self
1588 }
1589
1590 pub fn facet_grid_space(
1593 mut self,
1594 row: Option<&str>,
1595 col: Option<&str>,
1596 scales: FacetScales,
1597 space: FacetSpace,
1598 ) -> Self {
1599 self.facet = Facet::Grid {
1600 row_var: row.map(String::from),
1601 col_var: col.map(String::from),
1602 scales,
1603 labeller: FacetLabeller::default(),
1604 space,
1605 };
1606 self
1607 }
1608
1609 pub fn facet_grid_labeller(
1610 mut self,
1611 row: Option<&str>,
1612 col: Option<&str>,
1613 labeller: FacetLabeller,
1614 ) -> Self {
1615 self.facet = Facet::Grid {
1616 row_var: row.map(String::from),
1617 col_var: col.map(String::from),
1618 scales: FacetScales::Fixed,
1619 labeller,
1620 space: FacetSpace::Fixed,
1621 };
1622 self
1623 }
1624
1625 pub fn coord_flip(mut self) -> Self {
1628 self.coord = Box::new(CoordFlip);
1629 self
1630 }
1631
1632 pub fn coord_fixed(mut self, ratio: f64) -> Self {
1633 self.coord = Box::new(CoordFixed::new(ratio));
1634 self
1635 }
1636
1637 #[cfg(feature = "sf")]
1641 pub fn coord_sf(mut self) -> Self {
1642 self.coord = Box::new(crate::coord::sf::CoordSf::new());
1643 self
1644 }
1645
1646 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
1651 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
1652 self
1653 }
1654
1655 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
1657 self.coord_trans(None, Some(y))
1658 }
1659
1660 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
1662 self.coord_trans(Some(x), None)
1663 }
1664
1665 pub fn coord_cartesian_zoom(
1667 mut self,
1668 xlim: Option<(f64, f64)>,
1669 ylim: Option<(f64, f64)>,
1670 ) -> Self {
1671 let mut c = CoordCartesian::new();
1672 if let Some((min, max)) = xlim {
1673 c = c.xlim(min, max);
1674 }
1675 if let Some((min, max)) = ylim {
1676 c = c.ylim(min, max);
1677 }
1678 self.coord = Box::new(c);
1679 self
1680 }
1681
1682 pub fn coord_polar(mut self) -> Self {
1683 self.coord = Box::new(CoordPolar::new());
1684 self
1685 }
1686
1687 pub fn coord_radar(mut self) -> Self {
1691 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
1692 self
1693 }
1694
1695 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
1696 self.coord = Box::new(coord);
1697 self
1698 }
1699
1700 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
1701 self.coord = Box::new(coord);
1702 self
1703 }
1704
1705 pub fn theme(mut self, theme: Theme) -> Self {
1710 self.apply_preset(theme);
1711 self
1712 }
1713
1714 fn apply_preset(&mut self, theme: Theme) {
1715 let primary = self.theme.primary;
1716 self.theme = theme;
1717 if self.theme.primary.is_none() {
1718 self.theme.primary = primary;
1719 }
1720 }
1721
1722 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
1726 self.theme.axis_text_x.angle = degrees;
1727 self
1728 }
1729
1730 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
1732 self.theme.axis_text_y.angle = degrees;
1733 self
1734 }
1735
1736 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
1739 self.theme.axis_text_x_dodge = n.max(1);
1740 self
1741 }
1742
1743 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
1746 self.theme.aspect_ratio = Some(ratio);
1747 self
1748 }
1749
1750 pub fn panel_ontop(mut self) -> Self {
1752 self.theme.panel_ontop = true;
1753 self
1754 }
1755
1756 pub fn axis_minor_ticks(mut self) -> Self {
1758 self.theme.axis_minor_ticks = true;
1759 self
1760 }
1761
1762 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
1765 self.theme.title_position = pos;
1766 self
1767 }
1768
1769 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
1771 self.theme.tag_position = pos;
1772 self
1773 }
1774
1775 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
1777 self.theme.legend_direction = Some(dir);
1778 self
1779 }
1780
1781 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
1788 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
1789 self
1790 }
1791
1792 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
1794 self.theme.legend_position = pos;
1795 self
1796 }
1797
1798 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
1799 self.theme.primary = Some(color);
1800 self
1801 }
1802
1803 pub fn theme_minimal(mut self) -> Self {
1804 self.apply_preset(crate::theme::presets::theme_minimal());
1805 self
1806 }
1807
1808 pub fn theme_bw(mut self) -> Self {
1809 self.apply_preset(crate::theme::presets::theme_bw());
1810 self
1811 }
1812
1813 pub fn theme_gray(mut self) -> Self {
1814 self.apply_preset(crate::theme::presets::theme_gray());
1815 self
1816 }
1817
1818 pub fn theme_classic(mut self) -> Self {
1819 self.apply_preset(crate::theme::presets::theme_classic());
1820 self
1821 }
1822
1823 pub fn theme_pubr(mut self) -> Self {
1825 self.apply_preset(crate::theme::presets::theme_pubr());
1826 self
1827 }
1828
1829 pub fn theme_linedraw(mut self) -> Self {
1830 self.apply_preset(crate::theme::presets::theme_linedraw());
1831 self
1832 }
1833
1834 pub fn theme_light(mut self) -> Self {
1835 self.apply_preset(crate::theme::presets::theme_light());
1836 self
1837 }
1838
1839 pub fn theme_dark(mut self) -> Self {
1840 self.apply_preset(crate::theme::presets::theme_dark());
1841 self
1842 }
1843
1844 pub fn theme_void(mut self) -> Self {
1845 self.apply_preset(crate::theme::presets::theme_void());
1846 self
1847 }
1848
1849 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
1852 self.theme = self.theme.update(update);
1853 self
1854 }
1855
1856 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
1860 self.guide_legend = guide;
1861 self
1862 }
1863
1864 pub fn labs(mut self, labels: Labels) -> Self {
1867 if labels.title.is_some() {
1868 self.labels.title = labels.title;
1869 }
1870 if labels.subtitle.is_some() {
1871 self.labels.subtitle = labels.subtitle;
1872 }
1873 if labels.x.is_some() {
1874 self.labels.x = labels.x;
1875 }
1876 if labels.y.is_some() {
1877 self.labels.y = labels.y;
1878 }
1879 if labels.caption.is_some() {
1880 self.labels.caption = labels.caption;
1881 }
1882 if labels.tag.is_some() {
1883 self.labels.tag = labels.tag;
1884 }
1885 self
1886 }
1887
1888 pub fn title(mut self, title: &str) -> Self {
1889 self.labels.title = Some(title.to_string());
1890 self
1891 }
1892
1893 pub fn tag(mut self, tag: &str) -> Self {
1896 self.labels.tag = Some(tag.to_string());
1897 self
1898 }
1899
1900 pub fn subtitle(mut self, subtitle: &str) -> Self {
1901 self.labels.subtitle = Some(subtitle.to_string());
1902 self
1903 }
1904
1905 pub fn xlab(mut self, label: &str) -> Self {
1906 self.labels.x = Some(label.to_string());
1907 self
1908 }
1909
1910 pub fn ylab(mut self, label: &str) -> Self {
1911 self.labels.y = Some(label.to_string());
1912 self
1913 }
1914
1915 pub fn caption(mut self, caption: &str) -> Self {
1916 self.labels.caption = Some(caption.to_string());
1917 self
1918 }
1919
1920 pub fn annotate(mut self, annotation: Annotation) -> Self {
1924 self.annotations.push(annotation);
1925 self
1926 }
1927
1928 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
1930 self.annotate(Annotation::text(label, x, y))
1931 }
1932
1933 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
1935 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
1936 }
1937
1938 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
1940 self.annotate(Annotation::segment(x, y, xend, yend))
1941 }
1942
1943 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
1947 PlotBuilder::build(self)
1948 }
1949
1950 pub fn build(self) -> crate::build::BuiltPlot {
1954 self.try_build().expect("plot build failed")
1955 }
1956
1957 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
1961 pub fn save(self, path: &str) -> Result<(), GGError> {
1962 self.save_with_size(path, 800, 600)
1963 }
1964
1965 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
1967 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
1968 let (built, layout) = self.prepare(w, h)?;
1969
1970 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
1972
1973 match ext.as_str() {
1974 "svg" => {
1975 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
1976 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
1977 }
1978 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
1979 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
1980 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
1981 }
1982 _ => {
1983 return Err(GGError::UnsupportedFormat(ext));
1984 }
1985 }
1986
1987 Ok(())
1988 }
1989
1990 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
1996 pub fn render_svg(self) -> Result<String, GGError> {
1997 self.render_svg_with_size(800, 600)
1998 }
1999
2000 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2004 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2005 let (built, layout) = self.prepare(w, h)?;
2006 let mut buf = String::new();
2007 {
2008 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
2009 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2010 }
2011 Ok(buf)
2012 }
2013
2014 pub fn render_svg_native(self) -> Result<String, GGError> {
2018 self.render_svg_native_with_size(800, 600)
2019 }
2020
2021 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2029 Ok(self.render_native(w, h)?.0.finish())
2030 }
2031
2032 pub fn render_svg_native_with_warnings(
2037 self,
2038 w: u32,
2039 h: u32,
2040 ) -> Result<(String, Vec<String>), GGError> {
2041 let (backend, warnings, _) = self.render_native(w, h)?;
2042 Ok((backend.finish(), warnings))
2043 }
2044
2045 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
2057 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
2058 }
2059
2060 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
2064 let (backend, _, pa) = self.render_native(w, h)?;
2065 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
2066 }
2067
2068 fn render_native(
2072 self,
2073 w: u32,
2074 h: u32,
2075 ) -> Result<
2076 (
2077 crate::render::svg_backend::SvgBackend,
2078 Vec<String>,
2079 crate::render::Rect,
2080 ),
2081 GGError,
2082 > {
2083 let (built, layout) = self.prepare(w, h)?;
2084 let pa = layout.plot_area.clone();
2085 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
2086 backend.set_root_attrs(crate::render::svg_backend::root_data_attrs(&built));
2087 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2088 let mut warnings = built.warnings;
2089 warnings.extend(backend.take_warnings());
2090 Ok((backend, warnings, pa))
2091 }
2092
2093 #[cfg(feature = "canvas")]
2098 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
2099 let (built, layout) = self.prepare(w, h)?;
2100 let mut backend =
2101 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2102 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2103 let (ow, oh) = backend.dimensions();
2104 Ok((ow, oh, backend.into_rgba()))
2105 }
2106
2107 #[cfg(feature = "canvas")]
2111 pub fn render_rgba_area_with_size(
2112 self,
2113 w: u32,
2114 h: u32,
2115 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
2116 let (built, layout) = self.prepare(w, h)?;
2117 let pa = layout.plot_area.clone();
2118 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
2119 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2120 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
2121 }
2122
2123 #[cfg(feature = "canvas")]
2127 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2128 let (built, layout) = self.prepare(w, h)?;
2129 let mut backend =
2130 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2131 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2132 backend.into_png().map_err(GGError::Render)
2133 }
2134
2135 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2142 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
2143 self.render_png_with_size(800, 600)
2144 }
2145
2146 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2149 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2150 let (built, layout) = self.prepare(w, h)?;
2151
2152 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
2155 {
2156 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
2157 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2158 }
2159
2160 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
2161 GGError::Render(RenderError::BackendError(
2162 "PNG buffer size mismatch".to_string(),
2163 ))
2164 })?;
2165 let mut out = std::io::Cursor::new(Vec::new());
2166 img.write_to(&mut out, image::ImageOutputFormat::Png)
2167 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2168 Ok(out.into_inner())
2169 }
2170
2171 pub(crate) fn prepare(
2173 self,
2174 w: u32,
2175 h: u32,
2176 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
2177 let (mut built, meta) = self.build_for_render()?;
2178 let layout = Self::layout_built(&mut built, &meta, w, h);
2179 Ok((built, layout))
2180 }
2181
2182 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
2185 let plot = self;
2186
2187 let has_title = plot.labels.title.is_some();
2188 let has_subtitle = plot.labels.subtitle.is_some();
2189 let has_caption = plot.labels.caption.is_some();
2190 let has_legend = plot.has_legend_mapping();
2191 let x_label = plot.labels.x.clone();
2192 let y_label = plot.labels.y.clone();
2193
2194 let mut built = PlotBuilder::build(plot)?;
2195
2196 built.theme.resolve_inheritance();
2198
2199 if let Some(ref label) = x_label {
2201 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
2202 s.set_name(label);
2203 }
2204 }
2205 if let Some(ref label) = y_label {
2206 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
2207 s.set_name(label);
2208 }
2209 }
2210
2211 Ok((
2212 built,
2213 RenderMeta {
2214 has_title,
2215 has_subtitle,
2216 has_caption,
2217 has_legend,
2218 },
2219 ))
2220 }
2221
2222 pub(crate) fn layout_built(
2225 built: &mut crate::build::BuiltPlot,
2226 meta: &RenderMeta,
2227 w: u32,
2228 h: u32,
2229 ) -> PlotLayout {
2230 let RenderMeta {
2231 has_title,
2232 has_subtitle,
2233 has_caption,
2234 has_legend,
2235 } = *meta;
2236 let x_axis_top = built
2237 .scales
2238 .get(&crate::aes::Aesthetic::X)
2239 .map(|s| s.axis_position_opposite())
2240 .unwrap_or(false);
2241
2242 let flipped = built.coord.is_flipped();
2246 let h_aes = if flipped {
2247 crate::aes::Aesthetic::Y
2248 } else {
2249 crate::aes::Aesthetic::X
2250 };
2251 let v_aes = if flipped {
2252 crate::aes::Aesthetic::X
2253 } else {
2254 crate::aes::Aesthetic::Y
2255 };
2256
2257 let x_rot: Option<(f64, f64)> =
2261 if built.theme.axis_text_x.visible && built.theme.axis_text_x.angle.abs() < 1.0 {
2262 built.scales.get(&h_aes).and_then(|hs| {
2263 if !hs.is_discrete() {
2264 return None;
2265 }
2266 let breaks = hs.breaks();
2267 let n = breaks.len().max(1) as f64;
2268 let max_ch = breaks
2269 .iter()
2270 .map(|(_, l)| l.chars().count())
2271 .max()
2272 .unwrap_or(0) as f64;
2273 let font = built.theme.axis_text_x.size;
2274 let label_w = max_ch * font * 0.6; let spacing = 0.9 * w as f64 / n; if label_w <= spacing {
2277 return None; }
2279 let vertical = label_w * 0.707 > spacing;
2283 let angle = if vertical { 270.0 } else { 45.0 };
2284 let drop = if vertical { label_w } else { label_w * 0.707 };
2285 Some((angle, drop + font + 4.0))
2286 })
2287 } else {
2288 None
2289 };
2290 let x_label_height = x_rot.map(|(_, drop)| drop);
2291 if let Some((angle, _)) = x_rot {
2292 built.theme.axis_text_x.angle = angle;
2293 }
2294
2295 let y_label_width = if built.theme.axis_text_y.visible {
2299 built.scales.get(&v_aes).map(|vs| {
2300 let max_ch = vs
2301 .breaks()
2302 .iter()
2303 .map(|(_, l)| l.chars().count())
2304 .max()
2305 .unwrap_or(0);
2306 let measured = max_ch as f64 * built.theme.axis_text_y.size * 0.6 + 4.0;
2307 let default = built.theme.axis_text_y.size * 3.5 + 4.0;
2308 measured.max(default).min(0.35 * w as f64)
2309 })
2310 } else {
2311 None
2312 };
2313
2314 PlotLayout::compute_full(
2315 w as f64,
2316 h as f64,
2317 &built.theme,
2318 has_title,
2319 has_subtitle,
2320 has_caption,
2321 has_legend,
2322 x_axis_top,
2323 y_label_width,
2324 x_label_height,
2325 )
2326 }
2327
2328 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2331 fn render_into<DB>(
2332 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
2333 built: &crate::build::BuiltPlot,
2334 layout: &PlotLayout,
2335 ) -> Result<(), GGError>
2336 where
2337 DB: plotters::prelude::DrawingBackend,
2338 DB::ErrorType: 'static,
2339 {
2340 area.fill(&plotters::prelude::WHITE)
2341 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2342 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
2343 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
2344 area.present()
2345 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2346 Ok(())
2347 }
2348
2349 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2352 pub fn ggsave(
2353 self,
2354 path: &str,
2355 width_inches: f64,
2356 height_inches: f64,
2357 dpi: f64,
2358 ) -> Result<(), GGError> {
2359 let w = (width_inches * dpi) as u32;
2360 let h = (height_inches * dpi) as u32;
2361 self.save_with_size(path, w, h)
2362 }
2363
2364 fn has_legend_mapping(&self) -> bool {
2365 self.mapping.mappings.iter().any(|m| {
2366 matches!(
2367 m.aesthetic,
2368 crate::aes::Aesthetic::Color
2369 | crate::aes::Aesthetic::Fill
2370 | crate::aes::Aesthetic::Shape
2371 | crate::aes::Aesthetic::Linetype
2372 | crate::aes::Aesthetic::Size
2373 | crate::aes::Aesthetic::Alpha
2374 )
2375 })
2376 }
2377}
2378
2379#[derive(Clone, Copy, Debug)]
2381pub(crate) struct RenderMeta {
2382 pub(crate) has_title: bool,
2383 pub(crate) has_subtitle: bool,
2384 pub(crate) has_caption: bool,
2385 pub(crate) has_legend: bool,
2386}
2387
2388#[derive(Debug)]
2390pub enum GGError {
2391 Render(RenderError),
2392 UnsupportedFormat(String),
2393 Io(std::io::Error),
2394 ValidationError(String),
2395}
2396
2397impl std::fmt::Display for GGError {
2398 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2399 match self {
2400 GGError::Render(e) => write!(f, "Render error: {e}"),
2401 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
2402 GGError::Io(e) => write!(f, "IO error: {e}"),
2403 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
2404 }
2405 }
2406}
2407
2408impl std::error::Error for GGError {}