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 pub tooltip_template: Option<String>,
94}
95
96pub struct GGPlot {
98 pub(crate) data: DataFrame,
99 pub(crate) mapping: Aes,
100 pub(crate) layers: Vec<Layer>,
101 pub(crate) scales: Vec<Box<dyn Scale>>,
102 pub(crate) coord: Box<dyn Coord>,
103 pub(crate) theme: Theme,
104 pub(crate) labels: Labels,
105 pub(crate) facet: Facet,
106 pub(crate) annotations: Vec<Annotation>,
107 pub(crate) guide_legend: crate::guide::config::GuideLegend,
108 pub(crate) warnings: Vec<String>,
111 pub(crate) default_aspect_ratio: Option<f64>,
114 pub(crate) locale: Option<crate::locale::Locale>,
116 pub(crate) svg_root_data: bool,
121 pub(crate) root_attrs: Vec<(String, String)>,
124 pub(crate) svg_warnings_attr: bool,
128 pub(crate) svg_formatted_values: bool,
132 pub(crate) layer_svg_attrs: Vec<(usize, String, String)>,
136 pub(crate) svg_title: Option<String>,
141 pub(crate) svg_desc: Option<String>,
146}
147
148impl GGPlot {
149 pub fn new(data: impl GGData) -> Self {
156 GGPlot {
157 data: data.into_dataframe(),
158 mapping: Aes::default(),
159 layers: Vec::new(),
160 scales: Vec::new(),
161 coord: Box::new(CoordCartesian::new()),
162 theme: Theme::default(),
163 labels: Labels::default(),
164 facet: Facet::default(),
165 annotations: Vec::new(),
166 guide_legend: crate::guide::config::GuideLegend::default(),
167 warnings: Vec::new(),
168 default_aspect_ratio: None,
169 locale: None,
170 svg_root_data: true,
171 root_attrs: Vec::new(),
172 svg_warnings_attr: false,
173 svg_formatted_values: false,
174 layer_svg_attrs: Vec::new(),
175 svg_title: None,
176 svg_desc: None,
177 }
178 }
179
180 pub fn aes(mut self, mapping: Aes) -> Self {
182 self.warnings.extend(mapping.warnings.iter().cloned());
183 self.mapping = mapping;
184 self
185 }
186
187 pub fn geom_point(self) -> Self {
190 self.add_geom(GeomPoint::default())
191 }
192
193 pub fn geom_point_with(self, geom: GeomPoint) -> Self {
194 self.add_geom_with(geom)
195 }
196
197 pub fn geom_line(self) -> Self {
198 self.add_geom(GeomLine::default())
199 }
200
201 pub fn geom_line_with(self, geom: GeomLine) -> Self {
202 self.add_geom_with(geom)
203 }
204
205 pub fn geom_bar(self) -> Self {
206 self.add_geom(GeomBar::default())
207 }
208
209 pub fn geom_bar_with(self, geom: GeomBar) -> Self {
210 self.add_geom_with(geom)
211 }
212
213 pub fn geom_histogram(self) -> Self {
214 self.add_geom(GeomHistogram::default())
215 }
216
217 pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self {
218 self.add_geom_with(geom)
219 }
220
221 pub fn geom_boxplot(self) -> Self {
222 self.add_geom(GeomBoxplot::default())
223 }
224
225 pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self {
226 self.add_geom_with(geom)
227 }
228
229 pub fn geom_smooth(self) -> Self {
230 self.add_geom(GeomSmooth::default())
231 }
232
233 pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self {
234 self.add_geom_with(geom)
235 }
236
237 pub fn geom_col(self) -> Self {
238 self.add_geom(GeomCol::default())
239 }
240
241 pub fn geom_col_with(self, geom: GeomCol) -> Self {
242 self.add_geom_with(geom)
243 }
244
245 pub fn geom_hline(self, yintercept: f64) -> Self {
249 self.add_refline(
250 GeomHline::new(yintercept),
251 false,
252 &[("yintercept", yintercept)],
253 )
254 }
255
256 pub fn geom_hline_with(self, geom: GeomHline) -> Self {
258 let y = geom.yintercept;
259 self.add_refline(geom, true, &[("yintercept", y)])
260 }
261
262 pub fn geom_hline_aes(self, mapping: Aes) -> Self {
286 self.add_geom(GeomHline::mapped()).layer_aes(mapping)
287 }
288
289 pub fn geom_hline_aes_with(self, geom: GeomHline, mapping: Aes) -> Self {
291 self.add_geom_with(geom).layer_aes(mapping)
292 }
293
294 pub fn geom_vline(self, xintercept: f64) -> Self {
297 self.add_refline(
298 GeomVline::new(xintercept),
299 false,
300 &[("xintercept", xintercept)],
301 )
302 }
303
304 pub fn geom_vline_with(self, geom: GeomVline) -> Self {
306 let x = geom.xintercept;
307 self.add_refline(geom, true, &[("xintercept", x)])
308 }
309
310 pub fn geom_vline_aes(self, mapping: Aes) -> Self {
313 self.add_geom(GeomVline::mapped()).layer_aes(mapping)
314 }
315
316 pub fn geom_vline_aes_with(self, geom: GeomVline, mapping: Aes) -> Self {
318 self.add_geom_with(geom).layer_aes(mapping)
319 }
320
321 pub fn geom_abline(self, slope: f64, intercept: f64) -> Self {
324 self.add_refline(
325 GeomAbline::new(slope, intercept),
326 false,
327 &[("slope", slope), ("intercept", intercept)],
328 )
329 }
330
331 pub fn geom_abline_with(self, geom: GeomAbline) -> Self {
333 let (b, a) = (geom.slope, geom.intercept);
334 self.add_refline(geom, true, &[("slope", b), ("intercept", a)])
335 }
336
337 pub fn geom_abline_aes(self, mapping: Aes) -> Self {
340 self.add_geom(GeomAbline::mapped()).layer_aes(mapping)
341 }
342
343 pub fn geom_abline_aes_with(self, geom: GeomAbline, mapping: Aes) -> Self {
345 self.add_geom_with(geom).layer_aes(mapping)
346 }
347
348 fn add_refline(
351 self,
352 geom: impl Geom + 'static,
353 explicit: bool,
354 values: &[(&str, f64)],
355 ) -> Self {
356 let mut data = DataFrame::new();
357 let mut mapping = Aes::new();
358 for (col, v) in values {
359 data.add_column(col.to_string(), vec![Value::Float(*v)]);
360 mapping = match *col {
361 "xintercept" => mapping.xintercept(col),
362 "yintercept" => mapping.yintercept(col),
363 "slope" => mapping.slope(col),
364 _ => mapping.intercept(col),
365 };
366 }
367 let plot = if explicit {
368 self.add_geom_with(geom)
369 } else {
370 self.add_geom(geom)
371 };
372 plot.layer_aes(mapping).layer_data(data)
373 }
374
375 pub fn geom_text(self) -> Self {
376 self.add_geom(GeomText::default())
377 }
378
379 pub fn geom_text_with(self, geom: GeomText) -> Self {
380 self.add_geom_with(geom)
381 }
382
383 #[cfg(feature = "ggpubr")]
388 pub fn stat_cor(self) -> Self {
389 self.geom_text().stat(crate::stat::cor::StatCor::default())
390 }
391
392 #[cfg(feature = "ggpubr")]
395 pub fn stat_cor_with(self, stat: crate::stat::cor::StatCor) -> Self {
396 self.geom_text().stat(stat)
397 }
398
399 #[cfg(feature = "ggpubr")]
404 pub fn stat_compare_means(self) -> Self {
405 self.geom_text()
406 .stat(crate::stat::compare_means::StatCompareMeans::default())
407 }
408
409 #[cfg(feature = "ggpubr")]
412 pub fn stat_compare_means_with(
413 self,
414 stat: crate::stat::compare_means::StatCompareMeans,
415 ) -> Self {
416 self.geom_text().stat(stat)
417 }
418
419 #[cfg(feature = "ggpubr")]
425 pub fn stat_compare_means_pairwise(self, comparisons: &[(&str, &str)]) -> Self {
426 self.stat_compare_means_pairwise_with(
427 crate::stat::compare_means::CompareMethod::Auto,
428 comparisons,
429 )
430 }
431
432 #[cfg(feature = "ggpubr")]
435 pub fn stat_compare_means_pairwise_with(
436 self,
437 method: crate::stat::compare_means::CompareMethod,
438 comparisons: &[(&str, &str)],
439 ) -> Self {
440 let col_for = |a: crate::aes::Aesthetic| {
442 self.mapping
443 .mappings
444 .iter()
445 .find(|m| m.aesthetic == a)
446 .map(|m| m.column.clone())
447 };
448 let (xcol, ycol) = match (
449 col_for(crate::aes::Aesthetic::X),
450 col_for(crate::aes::Aesthetic::Y),
451 ) {
452 (Some(x), Some(y)) => (x, y),
453 _ => return self,
454 };
455 let (xc, yc) = match (self.data.column(&xcol), self.data.column(&ycol)) {
456 (Some(x), Some(y)) => (x, y),
457 _ => return self,
458 };
459
460 let mut groups: Vec<(String, Vec<f64>)> = Vec::new();
462 let mut ymax = f64::NEG_INFINITY;
463 let mut ymin = f64::INFINITY;
464 for (xv, yv) in xc.iter().zip(yc.iter()) {
465 let y = match yv.as_f64() {
466 Some(y) if y.is_finite() => y,
467 _ => continue,
468 };
469 ymax = ymax.max(y);
470 ymin = ymin.min(y);
471 let key = xv.to_group_key();
472 if let Some(g) = groups.iter_mut().find(|(k, _)| *k == key) {
473 g.1.push(y);
474 } else {
475 groups.push((key, vec![y]));
476 }
477 }
478 if groups.len() < 2 {
479 return self;
480 }
481 let step = if ymax > ymin {
482 (ymax - ymin) * 0.12
483 } else {
484 1.0
485 };
486
487 let (mut xmin_v, mut xmax_v, mut y_v, mut label_v) =
489 (Vec::new(), Vec::new(), Vec::new(), Vec::new());
490 let mut placed = 0usize;
491 for (a, b) in comparisons {
492 let ga = groups.iter().find(|(k, _)| k == a).map(|g| &g.1);
493 let gb = groups.iter().find(|(k, _)| k == b).map(|g| &g.1);
494 let (ga, gb) = match (ga, gb) {
495 (Some(x), Some(y)) if x.len() >= 2 && y.len() >= 2 => (x, y),
496 _ => continue,
497 };
498 let p = match crate::stat::compare_means::pairwise_p(method, ga, gb) {
499 Some(p) => p,
500 None => continue,
501 };
502 xmin_v.push(Value::Str((*a).to_string()));
503 xmax_v.push(Value::Str((*b).to_string()));
504 y_v.push(Value::Float(ymax + step * (placed as f64 + 1.0)));
505 label_v.push(Value::Str(crate::stat::cor::format_p_value(p)));
506 placed += 1;
507 }
508 if xmin_v.is_empty() {
509 return self;
510 }
511
512 let data = vec![
513 ("xmin".to_string(), xmin_v),
514 ("xmax".to_string(), xmax_v),
515 ("y".to_string(), y_v),
516 ("label".to_string(), label_v),
517 ];
518 self.add_geom(crate::geom::bracket::GeomBracket::default())
519 .layer_data(data)
520 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
521 }
522
523 pub fn geom_bracket(self, xmin: &str, xmax: &str, y: f64, label: &str) -> Self {
528 self.geom_bracket_many(
529 crate::geom::bracket::GeomBracket::default(),
530 &[(xmin, xmax, y, label)],
531 )
532 }
533
534 pub fn geom_bracket_many(
538 self,
539 geom: crate::geom::bracket::GeomBracket,
540 brackets: &[(&str, &str, f64, &str)],
541 ) -> Self {
542 let xmin = brackets
543 .iter()
544 .map(|b| Value::Str(b.0.to_string()))
545 .collect::<Vec<_>>();
546 let xmax = brackets
547 .iter()
548 .map(|b| Value::Str(b.1.to_string()))
549 .collect::<Vec<_>>();
550 let y = brackets
551 .iter()
552 .map(|b| Value::Float(b.2))
553 .collect::<Vec<_>>();
554 let label = brackets
555 .iter()
556 .map(|b| Value::Str(b.3.to_string()))
557 .collect::<Vec<_>>();
558 let data = vec![
559 ("xmin".to_string(), xmin),
560 ("xmax".to_string(), xmax),
561 ("y".to_string(), y),
562 ("label".to_string(), label),
563 ];
564 self.add_geom(geom)
565 .layer_data(data)
566 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
567 }
568
569 pub fn geom_bracket_table(
578 mut self,
579 table: impl GGData,
580 spec: crate::geom::bracket::BracketTable,
581 ) -> Self {
582 let table = table.into_dataframe();
583 let col_for = |a: crate::aes::Aesthetic| {
584 self.mapping
585 .mappings
586 .iter()
587 .find(|m| m.aesthetic == a)
588 .map(|m| m.column.clone())
589 };
590 let x_levels: Vec<String> = col_for(crate::aes::Aesthetic::X)
593 .and_then(|c| self.data.column(&c))
594 .filter(|col| col.iter().any(|v| matches!(v, Value::Str(_))))
595 .map(|col| {
596 let mut seen = std::collections::HashSet::new();
597 col.iter()
598 .filter(|v| !v.is_na())
599 .map(|v| v.to_group_key())
600 .filter(|k| seen.insert(k.clone()))
601 .collect()
602 })
603 .unwrap_or_default();
604 let y_range = col_for(crate::aes::Aesthetic::Y)
605 .and_then(|c| self.data.column(&c))
606 .and_then(|col| {
607 let (lo, hi) = col
608 .iter()
609 .filter_map(|v| v.as_f64())
610 .filter(|v| v.is_finite())
611 .fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), v| {
612 (lo.min(v), hi.max(v))
613 });
614 (lo <= hi).then_some((lo, hi))
615 });
616 let mut warnings = Vec::new();
617 let resolved = spec.resolve(&table, &x_levels, y_range, &mut warnings);
618 self.warnings.extend(warnings);
619 match resolved {
620 Some(data) => self
621 .add_geom(crate::geom::bracket::GeomBracket::from(spec.geom))
622 .layer_data(data)
623 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label")),
624 None => self,
625 }
626 }
627
628 pub fn geom_text_repel(self) -> Self {
633 self.add_geom(crate::geom::repel::GeomTextRepel::default())
634 }
635
636 pub fn geom_text_repel_with(self, geom: crate::geom::repel::GeomTextRepel) -> Self {
639 self.add_geom_with(geom)
640 }
641
642 pub fn geom_label_repel(self) -> Self {
645 self.add_geom(crate::geom::repel::GeomLabelRepel::default())
646 }
647
648 pub fn geom_label_repel_with(self, geom: crate::geom::repel::GeomLabelRepel) -> Self {
650 self.add_geom_with(geom)
651 }
652
653 pub fn geom_label(self) -> Self {
654 self.add_geom(GeomLabel::default())
655 }
656
657 pub fn geom_label_with(self, geom: GeomLabel) -> Self {
658 self.add_geom_with(geom)
659 }
660
661 pub fn geom_area(self) -> Self {
662 self.add_geom(GeomArea::default())
663 }
664
665 pub fn geom_area_with(self, geom: GeomArea) -> Self {
666 self.add_geom_with(geom)
667 }
668
669 pub fn geom_ribbon(self) -> Self {
670 self.add_geom(GeomRibbon::default())
671 }
672
673 pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self {
674 self.add_geom_with(geom)
675 }
676
677 pub fn geom_errorbar(self) -> Self {
678 self.add_geom(GeomErrorbar::default())
679 }
680
681 pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self {
682 self.add_geom_with(geom)
683 }
684
685 pub fn geom_segment(self) -> Self {
686 self.add_geom(GeomSegment::default())
687 }
688
689 pub fn geom_segment_with(self, geom: GeomSegment) -> Self {
690 self.add_geom_with(geom)
691 }
692
693 pub fn geom_density(self) -> Self {
694 self.add_geom(GeomDensity::default())
695 }
696
697 pub fn geom_density_with(self, geom: GeomDensity) -> Self {
698 self.add_geom_with(geom)
699 }
700
701 pub fn geom_rug(self) -> Self {
702 self.add_geom(GeomRug::default())
703 }
704
705 pub fn geom_rug_with(self, geom: GeomRug) -> Self {
706 self.add_geom_with(geom)
707 }
708
709 pub fn geom_jitter(self) -> Self {
710 self.add_geom(GeomJitter::default())
711 }
712
713 pub fn geom_jitter_with(self, geom: GeomJitter) -> Self {
714 self.add_geom_with(geom)
715 }
716
717 pub fn geom_path(self) -> Self {
718 self.add_geom(GeomPath::default())
719 }
720
721 pub fn geom_path_with(self, geom: GeomPath) -> Self {
722 self.add_geom_with(geom)
723 }
724
725 pub fn stat_ellipse(self) -> Self {
727 self.geom_path()
728 .stat(crate::stat::ellipse::StatEllipse::default())
729 }
730
731 pub fn stat_ellipse_level(self, level: f64) -> Self {
733 self.geom_path()
734 .stat(crate::stat::ellipse::StatEllipse::new(level))
735 }
736
737 #[cfg(feature = "regression")]
740 pub fn stat_quantile(mut self, taus: &[f64]) -> Self {
741 for &tau in taus {
742 self = self
743 .geom_path()
744 .stat(crate::stat::quantile::StatQuantile::new(tau));
745 }
746 self
747 }
748
749 #[cfg(feature = "regression")]
751 pub fn geom_quantile(self) -> Self {
752 self.stat_quantile(&[0.25, 0.5, 0.75])
753 }
754
755 pub fn geom_step(self) -> Self {
756 self.add_geom(GeomStep::default())
757 }
758
759 pub fn geom_step_with(self, geom: GeomStep) -> Self {
760 self.add_geom_with(geom)
761 }
762
763 pub fn geom_freqpoly(self) -> Self {
764 self.add_geom(GeomFreqpoly::default())
765 }
766
767 pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self {
768 self.add_geom_with(geom)
769 }
770
771 pub fn geom_linerange(self) -> Self {
772 self.add_geom(GeomLinerange::default())
773 }
774
775 pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self {
776 self.add_geom_with(geom)
777 }
778
779 pub fn geom_pointrange(self) -> Self {
780 self.add_geom(GeomPointrange::default())
781 }
782
783 pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self {
784 self.add_geom_with(geom)
785 }
786
787 pub fn geom_crossbar(self) -> Self {
788 self.add_geom(GeomCrossbar::default())
789 }
790
791 pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self {
792 self.add_geom_with(geom)
793 }
794
795 pub fn geom_spoke(self) -> Self {
796 self.add_geom(GeomSpoke::default())
797 }
798
799 pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self {
800 self.add_geom_with(geom)
801 }
802
803 pub fn geom_rect(self) -> Self {
804 self.add_geom(GeomRect::default())
805 }
806
807 pub fn geom_rect_with(self, geom: GeomRect) -> Self {
808 self.add_geom_with(geom)
809 }
810
811 pub fn geom_tile(self) -> Self {
812 self.add_geom(GeomTile::default())
813 }
814
815 pub fn geom_tile_with(self, geom: GeomTile) -> Self {
816 self.add_geom_with(geom)
817 }
818
819 pub fn geom_raster(self) -> Self {
821 self.add_geom(crate::geom::raster::GeomRaster::default())
822 }
823
824 pub fn geom_raster_with(self, geom: crate::geom::raster::GeomRaster) -> Self {
825 self.add_geom_with(geom)
826 }
827
828 pub fn geom_polygon(self) -> Self {
829 self.add_geom(GeomPolygon::default())
830 }
831
832 pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self {
833 self.add_geom_with(geom)
834 }
835
836 #[cfg(feature = "sf")]
838 pub fn geom_sf(self) -> Self {
839 self.add_geom(crate::geom::sf::GeomSf::default())
840 }
841
842 #[cfg(feature = "sf")]
843 pub fn geom_sf_with(self, geom: crate::geom::sf::GeomSf) -> Self {
844 self.add_geom_with(geom)
845 }
846
847 #[cfg(feature = "sf")]
850 pub fn geom_sf_text(self) -> Self {
851 self.add_geom(crate::geom::sf_text::GeomSfText::default())
852 }
853
854 #[cfg(feature = "sf")]
855 pub fn geom_sf_text_with(self, geom: crate::geom::sf_text::GeomSfText) -> Self {
856 self.add_geom_with(geom)
857 }
858
859 #[cfg(feature = "sf")]
862 pub fn geom_sf_label(self) -> Self {
863 self.add_geom(crate::geom::sf_text::GeomSfLabel::default())
864 }
865
866 #[cfg(feature = "sf")]
867 pub fn geom_sf_label_with(self, geom: crate::geom::sf_text::GeomSfLabel) -> Self {
868 self.add_geom_with(geom)
869 }
870
871 #[cfg(feature = "sf")]
875 pub fn annotation_scale(self) -> Self {
876 self.add_geom(crate::geom::annotation_scale::GeomAnnotationScale::default())
877 }
878
879 #[cfg(feature = "sf")]
880 pub fn annotation_scale_with(
881 self,
882 geom: crate::geom::annotation_scale::GeomAnnotationScale,
883 ) -> Self {
884 self.add_geom_with(geom)
885 }
886
887 #[cfg(feature = "sf")]
890 pub fn annotation_attribution(self, text: impl Into<String>) -> Self {
891 self.add_geom(crate::geom::annotation_scale::GeomAttribution::new(text))
892 }
893
894 #[cfg(feature = "sf")]
895 pub fn annotation_attribution_with(
896 self,
897 geom: crate::geom::annotation_scale::GeomAttribution,
898 ) -> Self {
899 self.add_geom_with(geom)
900 }
901
902 #[cfg(feature = "sf")]
910 pub fn bivariate_legend(
911 self,
912 geom: crate::geom::bivariate_legend::GeomBivariateLegend,
913 ) -> Self {
914 self.add_geom(geom)
915 }
916
917 #[cfg(feature = "sf")]
922 pub fn bivariate_legend_counts(
923 self,
924 geom: crate::geom::bivariate_legend::GeomBivariateLegendCounts,
925 ) -> Self {
926 self.add_geom(geom)
927 }
928
929 pub fn geom_curve(self) -> Self {
930 self.add_geom(GeomCurve::default())
931 }
932
933 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
934 self.add_geom_with(geom)
935 }
936
937 pub fn geom_violin(self) -> Self {
938 self.add_geom(GeomViolin::default())
939 }
940
941 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
942 self.add_geom_with(geom)
943 }
944
945 pub fn geom_dotplot(self) -> Self {
946 self.add_geom(GeomDotplot::default())
947 }
948
949 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
950 self.add_geom_with(geom)
951 }
952
953 pub fn geom_qq(self) -> Self {
954 self.add_geom(GeomQQ::default())
955 }
956
957 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
958 self.add_geom_with(geom)
959 }
960
961 pub fn geom_qq_line(self) -> Self {
962 self.add_geom(GeomQQLine::default())
963 }
964
965 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
966 self.add_geom_with(geom)
967 }
968
969 pub fn geom_bin2d(self) -> Self {
970 self.add_geom(GeomBin2d::default())
971 }
972
973 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
974 self.add_geom_with(geom)
975 }
976
977 pub fn geom_hex(self) -> Self {
978 self.add_geom(GeomHex::default())
979 }
980
981 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
982 self.add_geom_with(geom)
983 }
984
985 pub fn geom_count(self) -> Self {
986 self.add_geom(GeomCount::default())
987 }
988
989 pub fn geom_count_with(self, geom: GeomCount) -> Self {
990 self.add_geom_with(geom)
991 }
992
993 pub fn geom_contour(self) -> Self {
994 self.add_geom(GeomContour::default())
995 }
996
997 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
998 self.add_geom_with(geom)
999 }
1000
1001 pub fn geom_contour_filled(self) -> Self {
1004 self.add_geom(GeomPolygon {
1005 line_width: 0.0,
1006 alpha: 1.0,
1007 ..GeomPolygon::default()
1008 })
1009 .stat(crate::stat::contour_filled::StatContourFilled::default())
1010 }
1011
1012 pub fn geom_density2d(self) -> Self {
1013 self.add_geom(GeomDensity2d::default())
1014 }
1015
1016 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
1017 self.add_geom_with(geom)
1018 }
1019
1020 pub fn geom_candlestick(self) -> Self {
1023 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
1024 }
1025
1026 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
1027 self.add_geom_with(geom)
1028 }
1029
1030 pub fn geom_ohlc(self) -> Self {
1033 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
1034 }
1035
1036 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
1037 self.add_geom_with(geom)
1038 }
1039
1040 pub fn geom_calendar(self) -> Self {
1051 self.geom_calendar_with(
1052 GeomTile {
1053 color: (255, 255, 255),
1054 line_width: 1.5,
1055 ..Default::default()
1056 },
1057 false,
1058 )
1059 }
1060
1061 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
1064 use crate::stat::calendar::{
1065 day_number, month_boundaries, month_breaks, month_breaks_localized, weekday_labels,
1066 CalendarGrid, StatCalendar, MAX_CALENDAR_YEARS,
1067 };
1068 let grid = self
1069 .mapping
1070 .get_mapping(&crate::aes::Aesthetic::X)
1071 .and_then(|c| self.data.column(c))
1072 .and_then(|col| {
1073 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
1074 });
1075 let Some((grid, clipped)) = grid else {
1076 return self.add_geom_with(tile).stat(StatCalendar {
1078 grid: None,
1079 monday_first,
1080 });
1081 };
1082 if clipped {
1083 self.warnings.push(format!(
1084 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
1085 ));
1086 }
1087 let (xb, _) = month_breaks(&grid);
1088 let yb = if monday_first {
1090 vec![6.0, 4.0, 2.0]
1091 } else {
1092 vec![5.0, 3.0, 1.0]
1093 };
1094 let segs = month_boundaries(&grid);
1095 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
1096 segs.iter().map(|s| Value::Float(f(s))).collect()
1097 };
1098 let boundaries = vec![
1099 ("x".to_string(), col(|s| s.0)),
1100 ("y".to_string(), col(|s| s.1)),
1101 ("xend".to_string(), col(|s| s.2)),
1102 ("yend".to_string(), col(|s| s.3)),
1103 ];
1104 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
1105 let plot = self
1106 .add_geom_with(tile)
1107 .stat(StatCalendar {
1108 grid: Some(grid),
1109 monday_first,
1110 })
1111 .scale_x_continuous(
1112 ScaleContinuous::new()
1113 .with_breaks(xb)
1114 .with_localized_labels(move |l| month_breaks_localized(&grid, l).1)
1116 .with_expand(0.0, 0.0),
1117 )
1118 .scale_y_continuous(
1119 ScaleContinuous::new()
1120 .with_breaks(yb)
1121 .with_localized_labels(weekday_labels)
1122 .with_expand(0.0, 0.0),
1123 );
1124 if segs.is_empty() {
1125 return plot;
1126 }
1127 plot.add_geom_with(crate::geom::segment::GeomCalendarBorder)
1129 .layer_data(boundaries)
1130 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1131 .show_legend(false)
1132 }
1133
1134 pub fn geom_blank(self) -> Self {
1135 self.add_geom(GeomBlank)
1136 }
1137
1138 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
1141 let mut plot = self.add_geom(geom);
1142 if let Some(layer) = plot.layers.last_mut() {
1143 layer.explicit_style = true;
1144 }
1145 plot
1146 }
1147
1148 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
1149 let stat = geom.default_stat();
1150 let position = geom.default_position();
1151 let params = geom.default_params();
1152 self.layers.push(Layer {
1153 data: None,
1154 mapping: Aes::default(),
1155 geom: Box::new(geom),
1156 stat,
1157 position,
1158 params,
1159 show_legend: None,
1160 explicit_style: false,
1161 tooltip_template: None,
1162 });
1163 self
1164 }
1165
1166 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
1170 if let Some(layer) = self.layers.last_mut() {
1171 layer.stat = Box::new(stat);
1172 }
1173 self
1174 }
1175
1176 pub fn position(mut self, pos: impl Position + 'static) -> Self {
1178 if let Some(layer) = self.layers.last_mut() {
1179 layer.position = Box::new(pos);
1180 }
1181 self
1182 }
1183
1184 pub fn layer_data(mut self, data: impl GGData) -> Self {
1186 if let Some(layer) = self.layers.last_mut() {
1187 layer.data = Some(data.into_dataframe());
1188 }
1189 self
1190 }
1191
1192 pub fn layer_aes(mut self, mapping: Aes) -> Self {
1194 self.warnings.extend(mapping.warnings.iter().cloned());
1195 if let Some(layer) = self.layers.last_mut() {
1196 layer.mapping = mapping;
1197 }
1198 self
1199 }
1200
1201 pub fn tooltip(mut self, template: &str) -> Self {
1212 if let Some(layer) = self.layers.last_mut() {
1213 layer.tooltip_template = Some(template.to_string());
1214 }
1215 self
1216 }
1217
1218 pub fn show_legend(mut self, show: bool) -> Self {
1221 if let Some(layer) = self.layers.last_mut() {
1222 layer.show_legend = Some(show);
1223 }
1224 self
1225 }
1226
1227 pub fn decorative(mut self) -> Self {
1254 if let Some(layer) = self.layers.last_mut() {
1255 let geom = std::mem::replace(&mut layer.geom, Box::new(GeomBlank));
1256 layer.geom = Box::new(crate::geom::decorative::Decorative(geom));
1257 if layer.show_legend.is_none() {
1258 layer.show_legend = Some(false);
1259 }
1260 }
1261 self
1262 }
1263
1264 pub fn layer_svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1298 let (name, value) = (name.into(), value.into());
1299 let Some(idx) = self.layers.len().checked_sub(1) else {
1300 self.warnings.push(format!(
1301 "layer_svg_attr(\"{name}\") called before any layer was added — ignored"
1302 ));
1303 return self;
1304 };
1305 if let Some((n, v)) =
1306 crate::render::svg_backend::validate_layer_svg_attr(&name, &value, &mut self.warnings)
1307 {
1308 self.layer_svg_attrs
1309 .retain(|(i, k, _)| !(*i == idx && *k == n));
1310 self.layer_svg_attrs.push((idx, n, v));
1311 }
1312 self
1313 }
1314
1315 pub fn layer_svg_attrs<N: Into<String>, V: Into<String>>(
1317 self,
1318 attrs: impl IntoIterator<Item = (N, V)>,
1319 ) -> Self {
1320 attrs
1321 .into_iter()
1322 .fold(self, |p, (n, v)| p.layer_svg_attr(n, v))
1323 }
1324
1325 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
1328 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1329 self.scales.push(Box::new(s));
1330 self
1331 }
1332
1333 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
1334 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1335 self.scales.push(Box::new(s));
1336 self
1337 }
1338
1339 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1340 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1341 self.scales.push(Box::new(s));
1342 self
1343 }
1344
1345 pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1346 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1347 self.scales.push(Box::new(s));
1348 self
1349 }
1350
1351 pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
1352 self.scales.push(Box::new(s));
1353 self
1354 }
1355
1356 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
1357 self.scales.push(Box::new(s));
1358 self
1359 }
1360
1361 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1362 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
1363 self.scale_color(s)
1364 }
1365
1366 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1367 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
1368 self.scale_fill(s)
1369 }
1370
1371 pub fn scale_colour_identity(self) -> Self {
1374 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Color);
1375 self.scale_color(s)
1376 }
1377
1378 pub fn scale_color_identity(self) -> Self {
1380 self.scale_colour_identity()
1381 }
1382
1383 pub fn scale_fill_identity(self) -> Self {
1386 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Fill);
1387 self.scale_fill(s)
1388 }
1389
1390 pub fn scale_color_viridis(self) -> Self {
1391 use crate::scale::color::ScaleColorDiscrete;
1392 use crate::scale::palettes::PaletteName;
1393 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
1394 .with_named_palette(&PaletteName::Viridis);
1395 self.scale_color(s)
1396 }
1397
1398 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1399 use crate::scale::color::ScaleColorDiscrete;
1400 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
1401 self.scale_color(s)
1402 }
1403
1404 pub fn scale_color_gradient(
1405 self,
1406 low: crate::scale::color::RGBAColor,
1407 high: crate::scale::color::RGBAColor,
1408 ) -> Self {
1409 use crate::scale::color::ScaleColorContinuous;
1410 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
1411 self.scale_color(s)
1412 }
1413
1414 pub fn scale_fill_gradient(
1415 self,
1416 low: crate::scale::color::RGBAColor,
1417 high: crate::scale::color::RGBAColor,
1418 ) -> Self {
1419 use crate::scale::color::ScaleColorContinuous;
1420 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
1421 self.scale_fill(s)
1422 }
1423
1424 pub fn scale_color_gradient2(
1425 self,
1426 low: crate::scale::color::RGBAColor,
1427 mid: crate::scale::color::RGBAColor,
1428 high: crate::scale::color::RGBAColor,
1429 ) -> Self {
1430 use crate::scale::gradient::ScaleColorGradient2;
1431 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
1432 self.scale_color(s)
1433 }
1434
1435 pub fn scale_fill_gradient2(
1436 self,
1437 low: crate::scale::color::RGBAColor,
1438 mid: crate::scale::color::RGBAColor,
1439 high: crate::scale::color::RGBAColor,
1440 ) -> Self {
1441 use crate::scale::gradient::ScaleColorGradient2;
1442 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
1443 self.scale_fill(s)
1444 }
1445
1446 pub fn scale_fill_viridis(self) -> Self {
1447 use crate::scale::color::ScaleColorDiscrete;
1448 use crate::scale::palettes::PaletteName;
1449 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
1450 .with_named_palette(&PaletteName::Viridis);
1451 self.scale_fill(s)
1452 }
1453
1454 pub fn scale_color_viridis_c(self) -> Self {
1456 use crate::scale::gradient_n::ScaleColorGradientN;
1457 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
1458 self.scale_color(s)
1459 }
1460
1461 pub fn scale_fill_viridis_c(self) -> Self {
1463 use crate::scale::gradient_n::ScaleColorGradientN;
1464 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
1465 self.scale_fill(s)
1466 }
1467
1468 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1470 use crate::scale::gradient_n::ScaleColorGradientN;
1471 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
1472 self.scale_color(s)
1473 }
1474
1475 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1477 use crate::scale::gradient_n::ScaleColorGradientN;
1478 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
1479 self.scale_fill(s)
1480 }
1481
1482 pub fn scale_color_steps(
1485 self,
1486 low: crate::scale::color::RGBAColor,
1487 high: crate::scale::color::RGBAColor,
1488 n_bins: usize,
1489 ) -> Self {
1490 let s = crate::scale::steps::ScaleColorSteps::two(
1491 crate::aes::Aesthetic::Color,
1492 (low.r, low.g, low.b),
1493 (high.r, high.g, high.b),
1494 n_bins,
1495 );
1496 self.scale_color(s)
1497 }
1498
1499 pub fn scale_color_stepsn(
1501 self,
1502 stops: Vec<crate::scale::color::RGBAColor>,
1503 n_bins: usize,
1504 ) -> Self {
1505 let s = crate::scale::steps::ScaleColorSteps::new(
1506 crate::aes::Aesthetic::Color,
1507 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
1508 n_bins,
1509 );
1510 self.scale_color(s)
1511 }
1512
1513 pub fn scale_color_fermenter(
1515 self,
1516 name: crate::scale::palettes::PaletteName,
1517 n_bins: usize,
1518 ) -> Self {
1519 let stops = crate::scale::palettes::palette(&name)
1520 .iter()
1521 .map(|c| (c.r, c.g, c.b))
1522 .collect();
1523 let s =
1524 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
1525 self.scale_color(s)
1526 }
1527
1528 pub fn scale_fill_steps(
1530 self,
1531 low: crate::scale::color::RGBAColor,
1532 high: crate::scale::color::RGBAColor,
1533 n_bins: usize,
1534 ) -> Self {
1535 let s = crate::scale::steps::ScaleColorSteps::two(
1536 crate::aes::Aesthetic::Fill,
1537 (low.r, low.g, low.b),
1538 (high.r, high.g, high.b),
1539 n_bins,
1540 );
1541 self.scale_fill(s)
1542 }
1543
1544 pub fn scale_fill_binned(
1555 self,
1556 breaks: Vec<f64>,
1557 low: crate::scale::color::RGBAColor,
1558 high: crate::scale::color::RGBAColor,
1559 ) -> Self {
1560 let s = crate::scale::binned::ScaleFillBinned::two(
1561 crate::aes::Aesthetic::Fill,
1562 breaks,
1563 (low.r, low.g, low.b),
1564 (high.r, high.g, high.b),
1565 );
1566 self.scale_fill(s)
1567 }
1568
1569 pub fn scale_fill_binned_with(
1573 self,
1574 breaks: Vec<f64>,
1575 colors: Vec<crate::scale::color::RGBAColor>,
1576 ) -> Self {
1577 let s = crate::scale::binned::ScaleFillBinned::new(
1578 crate::aes::Aesthetic::Fill,
1579 breaks,
1580 colors.iter().map(|c| (c.r, c.g, c.b)).collect(),
1581 );
1582 self.scale_fill(s)
1583 }
1584
1585 pub fn scale_fill_fermenter(
1587 self,
1588 name: crate::scale::palettes::PaletteName,
1589 n_bins: usize,
1590 ) -> Self {
1591 let stops = crate::scale::palettes::palette(&name)
1592 .iter()
1593 .map(|c| (c.r, c.g, c.b))
1594 .collect();
1595 let s =
1596 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
1597 self.scale_fill(s)
1598 }
1599
1600 pub fn scale_fill_discrete_sorted(self) -> Self {
1605 use crate::scale::color::ScaleColorDiscrete;
1606 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
1607 }
1608
1609 pub fn scale_color_discrete_sorted(self) -> Self {
1612 use crate::scale::color::ScaleColorDiscrete;
1613 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
1614 }
1615
1616 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1617 use crate::scale::color::ScaleColorDiscrete;
1618 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
1619 self.scale_fill(s)
1620 }
1621
1622 pub fn scale_linetype_manual(
1623 self,
1624 values: Vec<(&str, crate::render::backend::Linetype)>,
1625 ) -> Self {
1626 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
1627 self.scale_color(s)
1628 }
1629
1630 pub fn scale_shape_manual(
1631 self,
1632 values: Vec<(&str, crate::render::backend::PointShape)>,
1633 ) -> Self {
1634 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
1635 self.scale_color(s)
1636 }
1637
1638 pub fn scale_color_grey(self) -> Self {
1639 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
1640 self.scale_color(s)
1641 }
1642
1643 pub fn scale_fill_grey(self) -> Self {
1644 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
1645 self.scale_fill(s)
1646 }
1647
1648 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1649 self.scale_color(s)
1650 }
1651
1652 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1653 self.scale_fill(s)
1654 }
1655
1656 pub fn scale_x_reverse(self) -> Self {
1657 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1658 }
1659
1660 pub fn scale_y_reverse(self) -> Self {
1661 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1662 }
1663
1664 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1665 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1666 self.scales.push(Box::new(s));
1667 self
1668 }
1669
1670 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1671 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1672 self.scales.push(Box::new(s));
1673 self
1674 }
1675
1676 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
1677 self.scales.push(Box::new(s));
1678 self
1679 }
1680
1681 pub fn scale_size_area(mut self, max_size: f64) -> Self {
1685 self.scales.push(Box::new(
1686 crate::scale::size::ScaleSizeContinuous::new().area(max_size),
1687 ));
1688 self
1689 }
1690
1691 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
1692 self.scales.push(Box::new(s));
1693 self
1694 }
1695
1696 pub fn scale_linewidth(mut self, s: crate::scale::linewidth::ScaleLinewidthContinuous) -> Self {
1700 self.scales.push(Box::new(s));
1701 self
1702 }
1703
1704 pub fn xlim(self, min: f64, max: f64) -> Self {
1705 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
1706 }
1707
1708 pub fn ylim(self, min: f64, max: f64) -> Self {
1709 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
1710 }
1711
1712 pub fn scale_x_log10(self) -> Self {
1713 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1714 }
1715
1716 pub fn scale_y_log10(self) -> Self {
1717 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1718 }
1719
1720 pub fn scale_x_sqrt(self) -> Self {
1721 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1722 }
1723
1724 pub fn scale_y_sqrt(self) -> Self {
1725 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1726 }
1727
1728 pub fn scale_x_log2(self) -> Self {
1729 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1730 }
1731
1732 pub fn scale_y_log2(self) -> Self {
1733 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1734 }
1735
1736 pub fn scale_x_ln(self) -> Self {
1737 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1738 }
1739
1740 pub fn scale_y_ln(self) -> Self {
1741 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1742 }
1743
1744 pub fn scale_x_logit(self) -> Self {
1746 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1747 }
1748
1749 pub fn scale_y_logit(self) -> Self {
1750 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1751 }
1752
1753 pub fn scale_x_probit(self) -> Self {
1755 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1756 }
1757
1758 pub fn scale_y_probit(self) -> Self {
1759 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1760 }
1761
1762 pub fn scale_x_pseudo_log(self) -> Self {
1764 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1765 }
1766
1767 pub fn scale_y_pseudo_log(self) -> Self {
1768 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1769 }
1770
1771 pub fn scale_x_reciprocal(self) -> Self {
1773 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1774 }
1775
1776 pub fn scale_y_reciprocal(self) -> Self {
1777 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1778 }
1779
1780 pub fn scale_x_exp(self) -> Self {
1782 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1783 }
1784
1785 pub fn scale_y_exp(self) -> Self {
1786 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1787 }
1788
1789 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
1791 self.scale_x_continuous(
1792 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1793 )
1794 }
1795
1796 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
1797 self.scale_y_continuous(
1798 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1799 )
1800 }
1801
1802 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
1805 self.facet = Facet::Wrap {
1806 var: var.to_string(),
1807 ncol,
1808 scales: FacetScales::Fixed,
1809 labeller: FacetLabeller::default(),
1810 };
1811 self
1812 }
1813
1814 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
1815 self.facet = Facet::Wrap {
1816 var: var.to_string(),
1817 ncol,
1818 scales,
1819 labeller: FacetLabeller::default(),
1820 };
1821 self
1822 }
1823
1824 pub fn facet_wrap_labeller(
1825 mut self,
1826 var: &str,
1827 ncol: Option<usize>,
1828 labeller: FacetLabeller,
1829 ) -> Self {
1830 self.facet = Facet::Wrap {
1831 var: var.to_string(),
1832 ncol,
1833 scales: FacetScales::Fixed,
1834 labeller,
1835 };
1836 self
1837 }
1838
1839 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
1840 self.facet = Facet::Grid {
1841 row_var: row.map(String::from),
1842 col_var: col.map(String::from),
1843 scales: FacetScales::Fixed,
1844 labeller: FacetLabeller::default(),
1845 space: FacetSpace::Fixed,
1846 };
1847 self
1848 }
1849
1850 pub fn facet_grid_free(
1851 mut self,
1852 row: Option<&str>,
1853 col: Option<&str>,
1854 scales: FacetScales,
1855 ) -> Self {
1856 self.facet = Facet::Grid {
1857 row_var: row.map(String::from),
1858 col_var: col.map(String::from),
1859 scales,
1860 labeller: FacetLabeller::default(),
1861 space: FacetSpace::Fixed,
1862 };
1863 self
1864 }
1865
1866 pub fn facet_grid_multi(
1870 mut self,
1871 row: Option<&str>,
1872 cols: &[&str],
1873 scales: FacetScales,
1874 space: FacetSpace,
1875 ) -> Self {
1876 let col_var = if cols.len() > 1 {
1877 let n = self.data.nrows();
1879 let mut combined = Vec::with_capacity(n);
1880 for i in 0..n {
1881 let parts: Vec<String> = cols
1882 .iter()
1883 .map(|c| {
1884 self.data
1885 .column(c)
1886 .and_then(|col| col.get(i))
1887 .map(|v| v.to_group_key())
1888 .unwrap_or_default()
1889 })
1890 .collect();
1891 combined.push(crate::data::Value::Str(parts.join(" . ")));
1892 }
1893 let name = "__facet_cols__".to_string();
1894 self.data.add_column(name.clone(), combined);
1895 Some(name)
1896 } else {
1897 cols.first().map(|s| s.to_string())
1898 };
1899 self.facet = Facet::Grid {
1900 row_var: row.map(String::from),
1901 col_var,
1902 scales,
1903 labeller: FacetLabeller::default(),
1904 space,
1905 };
1906 self
1907 }
1908
1909 pub fn facet_grid_space(
1912 mut self,
1913 row: Option<&str>,
1914 col: Option<&str>,
1915 scales: FacetScales,
1916 space: FacetSpace,
1917 ) -> Self {
1918 self.facet = Facet::Grid {
1919 row_var: row.map(String::from),
1920 col_var: col.map(String::from),
1921 scales,
1922 labeller: FacetLabeller::default(),
1923 space,
1924 };
1925 self
1926 }
1927
1928 pub fn facet_grid_labeller(
1929 mut self,
1930 row: Option<&str>,
1931 col: Option<&str>,
1932 labeller: FacetLabeller,
1933 ) -> Self {
1934 self.facet = Facet::Grid {
1935 row_var: row.map(String::from),
1936 col_var: col.map(String::from),
1937 scales: FacetScales::Fixed,
1938 labeller,
1939 space: FacetSpace::Fixed,
1940 };
1941 self
1942 }
1943
1944 pub fn coord_flip(mut self) -> Self {
1947 self.coord = Box::new(CoordFlip);
1948 self
1949 }
1950
1951 pub fn coord_fixed(mut self, ratio: f64) -> Self {
1952 self.coord = Box::new(CoordFixed::new(ratio));
1953 self
1954 }
1955
1956 #[cfg(feature = "sf")]
1960 pub fn coord_sf(mut self) -> Self {
1961 self.coord = Box::new(crate::coord::sf::CoordSf::new());
1962 self
1963 }
1964
1965 #[cfg(feature = "sf")]
1970 pub fn coord_sf_with(mut self, coord: crate::coord::sf::CoordSf) -> Self {
1971 self.coord = Box::new(coord);
1972 self
1973 }
1974
1975 pub fn svg_root_data(mut self, on: bool) -> Self {
1982 self.svg_root_data = on;
1983 self
1984 }
1985
1986 pub fn svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1996 self.root_attrs.push((name.into(), value.into()));
1997 self
1998 }
1999
2000 pub fn svg_attrs(mut self, attrs: impl IntoIterator<Item = (String, String)>) -> Self {
2002 self.root_attrs.extend(attrs);
2003 self
2004 }
2005
2006 pub fn svg_warnings_attr(mut self, on: bool) -> Self {
2011 self.svg_warnings_attr = on;
2012 self
2013 }
2014
2015 pub fn svg_formatted_values(mut self, on: bool) -> Self {
2024 self.svg_formatted_values = on;
2025 self
2026 }
2027
2028 pub fn svg_title(mut self, title: impl Into<String>) -> Self {
2038 self.svg_title = Some(title.into());
2039 self
2040 }
2041
2042 pub fn svg_desc(mut self, desc: impl Into<String>) -> Self {
2050 self.svg_desc = Some(desc.into());
2051 self
2052 }
2053
2054 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
2059 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
2060 self
2061 }
2062
2063 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
2065 self.coord_trans(None, Some(y))
2066 }
2067
2068 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
2070 self.coord_trans(Some(x), None)
2071 }
2072
2073 pub fn coord_cartesian_zoom(
2075 mut self,
2076 xlim: Option<(f64, f64)>,
2077 ylim: Option<(f64, f64)>,
2078 ) -> Self {
2079 let mut c = CoordCartesian::new();
2080 if let Some((min, max)) = xlim {
2081 c = c.xlim(min, max);
2082 }
2083 if let Some((min, max)) = ylim {
2084 c = c.ylim(min, max);
2085 }
2086 self.coord = Box::new(c);
2087 self
2088 }
2089
2090 pub fn coord_polar(mut self) -> Self {
2091 self.coord = Box::new(CoordPolar::new());
2092 self
2093 }
2094
2095 pub fn coord_radar(mut self) -> Self {
2099 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
2100 self
2101 }
2102
2103 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
2104 self.coord = Box::new(coord);
2105 self
2106 }
2107
2108 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
2109 self.coord = Box::new(coord);
2110 self
2111 }
2112
2113 pub fn theme(mut self, theme: Theme) -> Self {
2118 self.apply_preset(theme);
2119 self
2120 }
2121
2122 fn apply_preset(&mut self, theme: Theme) {
2123 let primary = self.theme.primary;
2124 self.theme = theme;
2125 if self.theme.primary.is_none() {
2126 self.theme.primary = primary;
2127 }
2128 }
2129
2130 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
2134 self.theme.axis_text_x.angle = degrees;
2135 self
2136 }
2137
2138 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
2140 self.theme.axis_text_y.angle = degrees;
2141 self
2142 }
2143
2144 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
2147 self.theme.axis_text_x_dodge = n.max(1);
2148 self
2149 }
2150
2151 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
2154 self.theme.aspect_ratio = Some(ratio);
2155 self
2156 }
2157
2158 pub fn panel_ontop(mut self) -> Self {
2160 self.theme.panel_ontop = true;
2161 self
2162 }
2163
2164 pub fn axis_minor_ticks(mut self) -> Self {
2166 self.theme.axis_minor_ticks = true;
2167 self
2168 }
2169
2170 pub fn axis_text_x_fit(mut self, fit: crate::theme::AxisLabelFit) -> Self {
2175 self.theme.axis_text_x_fit = fit;
2176 self
2177 }
2178
2179 pub fn axis_text_x_check_overlap(mut self, on: bool) -> Self {
2182 self.theme.axis_text_x_check_overlap = on;
2183 self
2184 }
2185
2186 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
2189 self.theme.title_position = pos;
2190 self
2191 }
2192
2193 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
2195 self.theme.tag_position = pos;
2196 self
2197 }
2198
2199 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
2201 self.theme.legend_direction = Some(dir);
2202 self
2203 }
2204
2205 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
2212 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
2213 self
2214 }
2215
2216 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
2218 self.theme.legend_position = pos;
2219 self
2220 }
2221
2222 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
2223 self.theme.primary = Some(color);
2224 self
2225 }
2226
2227 pub fn theme_minimal(mut self) -> Self {
2228 self.apply_preset(crate::theme::presets::theme_minimal());
2229 self
2230 }
2231
2232 pub fn theme_bw(mut self) -> Self {
2233 self.apply_preset(crate::theme::presets::theme_bw());
2234 self
2235 }
2236
2237 pub fn theme_gray(mut self) -> Self {
2238 self.apply_preset(crate::theme::presets::theme_gray());
2239 self
2240 }
2241
2242 pub fn theme_classic(mut self) -> Self {
2243 self.apply_preset(crate::theme::presets::theme_classic());
2244 self
2245 }
2246
2247 pub fn theme_pubr(mut self) -> Self {
2249 self.apply_preset(crate::theme::presets::theme_pubr());
2250 self
2251 }
2252
2253 pub fn theme_linedraw(mut self) -> Self {
2254 self.apply_preset(crate::theme::presets::theme_linedraw());
2255 self
2256 }
2257
2258 pub fn theme_light(mut self) -> Self {
2259 self.apply_preset(crate::theme::presets::theme_light());
2260 self
2261 }
2262
2263 pub fn theme_dark(mut self) -> Self {
2264 self.apply_preset(crate::theme::presets::theme_dark());
2265 self
2266 }
2267
2268 pub fn theme_void(mut self) -> Self {
2269 self.apply_preset(crate::theme::presets::theme_void());
2270 self
2271 }
2272
2273 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
2276 self.theme = self.theme.update(update);
2277 self
2278 }
2279
2280 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
2284 self.guide_legend = guide;
2285 self
2286 }
2287
2288 pub fn labs(mut self, labels: Labels) -> Self {
2291 if labels.title.is_some() {
2292 self.labels.title = labels.title;
2293 }
2294 if labels.subtitle.is_some() {
2295 self.labels.subtitle = labels.subtitle;
2296 }
2297 if labels.x.is_some() {
2298 self.labels.x = labels.x;
2299 }
2300 if labels.y.is_some() {
2301 self.labels.y = labels.y;
2302 }
2303 if labels.caption.is_some() {
2304 self.labels.caption = labels.caption;
2305 }
2306 if labels.tag.is_some() {
2307 self.labels.tag = labels.tag;
2308 }
2309 self
2310 }
2311
2312 pub fn title(mut self, title: &str) -> Self {
2313 self.labels.title = Some(title.to_string());
2314 self
2315 }
2316
2317 pub fn tag(mut self, tag: &str) -> Self {
2320 self.labels.tag = Some(tag.to_string());
2321 self
2322 }
2323
2324 pub fn subtitle(mut self, subtitle: &str) -> Self {
2325 self.labels.subtitle = Some(subtitle.to_string());
2326 self
2327 }
2328
2329 pub fn xlab(mut self, label: &str) -> Self {
2330 self.labels.x = Some(label.to_string());
2331 self
2332 }
2333
2334 pub fn ylab(mut self, label: &str) -> Self {
2335 self.labels.y = Some(label.to_string());
2336 self
2337 }
2338
2339 pub fn caption(mut self, caption: &str) -> Self {
2340 self.labels.caption = Some(caption.to_string());
2341 self
2342 }
2343
2344 pub fn annotate(mut self, annotation: Annotation) -> Self {
2348 self.annotations.push(annotation);
2349 self
2350 }
2351
2352 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
2354 self.annotate(Annotation::text(label, x, y))
2355 }
2356
2357 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
2359 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
2360 }
2361
2362 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
2364 self.annotate(Annotation::segment(x, y, xend, yend))
2365 }
2366
2367 pub fn locale(mut self, locale: crate::locale::Locale) -> Self {
2390 self.locale = Some(locale);
2391 self
2392 }
2393
2394 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
2398 PlotBuilder::build(self)
2399 }
2400
2401 pub fn build(self) -> crate::build::BuiltPlot {
2405 self.try_build().expect("plot build failed")
2406 }
2407
2408 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2412 pub fn save(self, path: &str) -> Result<(), GGError> {
2413 self.save_with_size(path, 800, 600)
2414 }
2415
2416 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2418 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
2419 let (built, layout) = self.prepare(w, h)?;
2420
2421 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
2423
2424 match ext.as_str() {
2425 "svg" => {
2426 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
2427 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2428 }
2429 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
2430 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
2431 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2432 }
2433 _ => {
2434 return Err(GGError::UnsupportedFormat(ext));
2435 }
2436 }
2437
2438 Ok(())
2439 }
2440
2441 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2447 pub fn render_svg(self) -> Result<String, GGError> {
2448 self.render_svg_with_size(800, 600)
2449 }
2450
2451 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2455 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2456 let (built, layout) = self.prepare(w, h)?;
2457 let mut buf = String::new();
2458 {
2459 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
2460 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2461 }
2462 Ok(buf)
2463 }
2464
2465 pub fn render_svg_native(self) -> Result<String, GGError> {
2469 self.render_svg_native_with_size(800, 600)
2470 }
2471
2472 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2480 Ok(self.render_native(w, h)?.0.finish())
2481 }
2482
2483 pub fn render_svg_native_with_warnings(
2488 self,
2489 w: u32,
2490 h: u32,
2491 ) -> Result<(String, Vec<String>), GGError> {
2492 let (backend, warnings, _) = self.render_native(w, h)?;
2493 Ok((backend.finish(), warnings))
2494 }
2495
2496 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
2508 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
2509 }
2510
2511 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
2515 let (backend, _, pa) = self.render_native(w, h)?;
2516 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
2517 }
2518
2519 fn render_native(
2523 self,
2524 w: u32,
2525 h: u32,
2526 ) -> Result<
2527 (
2528 crate::render::svg_backend::SvgBackend,
2529 Vec<String>,
2530 crate::render::Rect,
2531 ),
2532 GGError,
2533 > {
2534 let svg_root_data = self.svg_root_data;
2535 let root_attrs = self.root_attrs.clone();
2536 let svg_warnings_attr = self.svg_warnings_attr;
2537 let svg_formatted_values = self.svg_formatted_values;
2538 let svg_title = self.svg_title.clone();
2539 let svg_desc = self.svg_desc.clone();
2540 let (built, layout) = self.prepare(w, h)?;
2541 let pa = layout.plot_area.clone();
2542 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
2543 backend.set_formatted_values(svg_formatted_values);
2544 backend.set_svg_title(svg_title.clone());
2545 backend.set_svg_desc(svg_desc.clone());
2546 let existing_root_attrs = if svg_root_data {
2547 let attrs = crate::render::svg_backend::root_data_attrs(&built);
2548 backend.set_root_attrs(attrs.clone());
2549 let mut reserved = attrs;
2553 reserved.push(("data-plot".to_string(), String::new()));
2554 reserved
2555 } else {
2556 backend.without_plot_attr();
2557 Vec::new()
2558 };
2559 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2560 let mut warnings = built.warnings;
2561 warnings.extend(backend.take_warnings());
2562
2563 let mut reserved = existing_root_attrs;
2564 if svg_warnings_attr {
2565 reserved.push(("data-warnings".to_string(), String::new()));
2566 }
2567 if svg_title.is_some() || svg_desc.is_some() {
2572 reserved.push(("role".to_string(), String::new()));
2573 }
2574 if svg_title.is_some() {
2575 reserved.push(("aria-labelledby".to_string(), String::new()));
2576 }
2577 if svg_desc.is_some() {
2578 reserved.push(("aria-describedby".to_string(), String::new()));
2579 }
2580 let extra = crate::render::svg_backend::validate_root_attrs(
2581 &root_attrs,
2582 &reserved,
2583 "plot",
2584 &mut warnings,
2585 );
2586 backend.extend_root_attrs(extra);
2587 if svg_warnings_attr {
2588 backend.extend_root_attrs(vec![(
2589 "data-warnings".to_string(),
2590 crate::render::svg_backend::warnings_json(&warnings),
2591 )]);
2592 }
2593 Ok((backend, warnings, pa))
2594 }
2595
2596 #[cfg(feature = "canvas")]
2601 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
2602 let (built, layout) = self.prepare(w, h)?;
2603 let mut backend =
2604 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2605 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2606 let (ow, oh) = backend.dimensions();
2607 Ok((ow, oh, backend.into_rgba()))
2608 }
2609
2610 #[cfg(feature = "canvas")]
2614 pub fn render_rgba_area_with_size(
2615 self,
2616 w: u32,
2617 h: u32,
2618 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
2619 let (built, layout) = self.prepare(w, h)?;
2620 let pa = layout.plot_area.clone();
2621 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
2622 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2623 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
2624 }
2625
2626 #[cfg(feature = "canvas")]
2630 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2631 let (built, layout) = self.prepare(w, h)?;
2632 let mut backend =
2633 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2634 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2635 backend.into_png().map_err(GGError::Render)
2636 }
2637
2638 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2645 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
2646 self.render_png_with_size(800, 600)
2647 }
2648
2649 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2652 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2653 let (built, layout) = self.prepare(w, h)?;
2654
2655 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
2658 {
2659 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
2660 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2661 }
2662
2663 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
2664 GGError::Render(RenderError::BackendError(
2665 "PNG buffer size mismatch".to_string(),
2666 ))
2667 })?;
2668 let mut out = std::io::Cursor::new(Vec::new());
2669 img.write_to(&mut out, image::ImageOutputFormat::Png)
2670 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2671 Ok(out.into_inner())
2672 }
2673
2674 pub(crate) fn prepare(
2676 self,
2677 w: u32,
2678 h: u32,
2679 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
2680 let (mut built, meta) = self.build_for_render()?;
2681 let layout = Self::layout_built(&mut built, &meta, w, h);
2682 Ok((built, layout))
2683 }
2684
2685 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
2688 let plot = self;
2689
2690 let has_title = plot.labels.title.is_some();
2691 let has_subtitle = plot.labels.subtitle.is_some();
2692 let has_caption = plot.labels.caption.is_some();
2693 let has_legend = plot.has_legend_mapping();
2694 let x_label = plot.labels.x.clone();
2695 let y_label = plot.labels.y.clone();
2696
2697 let mut built = PlotBuilder::build(plot)?;
2698
2699 built.theme.resolve_inheritance();
2701
2702 if let Some(ref label) = x_label {
2704 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
2705 s.set_name(label);
2706 }
2707 }
2708 if let Some(ref label) = y_label {
2709 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
2710 s.set_name(label);
2711 }
2712 }
2713
2714 Ok((
2715 built,
2716 RenderMeta {
2717 has_title,
2718 has_subtitle,
2719 has_caption,
2720 has_legend,
2721 },
2722 ))
2723 }
2724
2725 pub(crate) fn layout_built(
2728 built: &mut crate::build::BuiltPlot,
2729 meta: &RenderMeta,
2730 w: u32,
2731 h: u32,
2732 ) -> PlotLayout {
2733 let RenderMeta {
2734 has_title,
2735 has_subtitle,
2736 has_caption,
2737 has_legend,
2738 } = *meta;
2739 let x_axis_top = built
2740 .scales
2741 .get(&crate::aes::Aesthetic::X)
2742 .map(|s| s.axis_position_opposite())
2743 .unwrap_or(false);
2744
2745 let flipped = built.coord.is_flipped();
2749 let h_aes = if flipped {
2750 crate::aes::Aesthetic::Y
2751 } else {
2752 crate::aes::Aesthetic::X
2753 };
2754 let v_aes = if flipped {
2755 crate::aes::Aesthetic::X
2756 } else {
2757 crate::aes::Aesthetic::Y
2758 };
2759
2760 let drawn_x_labels: Vec<(f64, String)> = built
2768 .scales
2769 .get(&h_aes)
2770 .map(|hs| {
2771 hs.breaks()
2772 .into_iter()
2773 .filter(|(_, label)| !label.is_empty())
2774 .collect()
2775 })
2776 .unwrap_or_default();
2777 let h_is_discrete = built
2778 .scales
2779 .get(&h_aes)
2780 .map(|hs| hs.is_discrete())
2781 .unwrap_or(false);
2782
2783 if built.theme.axis_text_x.visible
2796 && built.theme.axis_text_x.angle.abs() < 1.0
2797 && h_is_discrete
2798 && !drawn_x_labels.is_empty()
2799 {
2800 let font = built.theme.axis_text_x.size;
2801 let max_w = drawn_x_labels
2802 .iter()
2803 .map(|(_, l)| crate::render::text::text_width(l, font))
2804 .fold(0.0_f64, f64::max);
2805 let n = drawn_x_labels.len() as f64;
2806 let spacing = 0.9 * w as f64 / n; if max_w > spacing {
2808 let vertical = max_w * std::f64::consts::FRAC_1_SQRT_2 > spacing;
2812 built.theme.axis_text_x.angle = if vertical { 270.0 } else { -45.0 };
2813 }
2814 }
2815
2816 let x_label_height = if built.theme.axis_text_x.visible
2831 && built.theme.axis_text_x.angle.abs() > 10.0
2832 && !drawn_x_labels.is_empty()
2833 {
2834 let font = built.theme.axis_text_x.size;
2835 let angle = built.theme.axis_text_x.angle;
2836 let dir = if x_axis_top { -1.0 } else { 1.0 };
2837 let (rise, fall) = crate::render::text::rotated_label_rise_fall(
2838 drawn_x_labels.iter().map(|(_, l)| l.as_str()),
2839 font,
2840 angle,
2841 crate::render::text::rotated_anchor_frac(angle, dir),
2842 );
2843 Some(built.theme.legend_spacing / 2.0 + font / 2.0 + rise + fall)
2844 } else {
2845 None
2846 };
2847
2848 let y_label_width = if built.theme.axis_text_y.visible {
2852 built.scales.get(&v_aes).map(|vs| {
2853 let max_ch = vs
2854 .breaks()
2855 .iter()
2856 .map(|(_, l)| l.chars().count())
2857 .max()
2858 .unwrap_or(0);
2859 let measured = max_ch as f64 * built.theme.axis_text_y.size * 0.6 + 4.0;
2860 let default = built.theme.axis_text_y.size * 3.5 + 4.0;
2861 measured.max(default).min(0.35 * w as f64)
2862 })
2863 } else {
2864 None
2865 };
2866
2867 let mut extras = crate::render::layout::LayoutExtras::default();
2878 let can_reserve = built.facet.is_none()
2879 && built.theme.axis_text_x.visible
2880 && built.theme.axis_text_x_fit == crate::theme::AxisLabelFit::Reserve
2881 && !built.coord.is_polar()
2882 && built.coord.supports_axis_reserve();
2883 if can_reserve {
2884 if let Some(hs) = built.scales.get(&h_aes) {
2885 let breaks = hs.breaks();
2886 if let (Some(first), Some(last)) = (breaks.first(), breaks.last()) {
2887 let angle = built.theme.axis_text_x.angle;
2888 let anchor = if angle.abs() > 10.0 {
2889 let dir = if x_axis_top { -1.0 } else { 1.0 };
2890 crate::render::text::rotated_anchor_frac(angle, dir)
2891 } else {
2892 0.5
2893 };
2894 let size = built.theme.axis_text_x.size;
2895 const EDGE: f64 = 1.0;
2896 for _ in 0..2 {
2897 let layout = PlotLayout::compute_with_extras(
2898 w as f64,
2899 h as f64,
2900 &built.theme,
2901 has_title,
2902 has_subtitle,
2903 has_caption,
2904 has_legend,
2905 x_axis_top,
2906 y_label_width,
2907 x_label_height,
2908 extras,
2909 );
2910 let area = &layout.plot_area;
2911 let edge_y = area.y + area.height;
2912 let to_point = |pos: f64| if flipped { (0.0, pos) } else { (pos, 0.0) };
2913 let px_first = built.coord.transform(to_point(first.0), area).0;
2914 let px_last = built.coord.transform(to_point(last.0), area).0;
2915 let bbox_first = crate::render::text::label_bbox(
2916 &first.1, size, px_first, edge_y, angle, anchor,
2917 );
2918 let bbox_last = crate::render::text::label_bbox(
2919 &last.1, size, px_last, edge_y, angle, anchor,
2920 );
2921 let deficit_left = (EDGE - bbox_first[0]).max(0.0);
2922 let deficit_right = (bbox_last[2] - (w as f64 - EDGE)).max(0.0);
2923 if deficit_left <= 0.0 && deficit_right <= 0.0 {
2924 break;
2925 }
2926 let (p_first, p_last) = (first.0, last.0);
2935 if deficit_left > 0.0 && deficit_right > 0.0 {
2936 let (a, b, c, d) = (1.0 - p_first, p_first, 1.0 - p_last, p_last);
2941 let det = a * d - b * c;
2942 if det.abs() > 0.01 {
2943 extras.left +=
2944 ((deficit_left * d + deficit_right * b) / det).max(0.0);
2945 extras.right +=
2946 ((deficit_right * a + deficit_left * c) / det).max(0.0);
2947 } else {
2948 extras.left += deficit_left / (1.0 - p_first).max(0.05);
2952 extras.right += deficit_right / p_last.max(0.05);
2953 }
2954 } else if deficit_left > 0.0 {
2955 extras.left += deficit_left / (1.0 - p_first).max(0.05);
2956 } else if deficit_right > 0.0 {
2957 extras.right += deficit_right / p_last.max(0.05);
2958 }
2959 const MIN_PANEL_WIDTH: f64 = 50.0;
2974 let cap = (w as f64 - MIN_PANEL_WIDTH).max(0.0);
2975 if extras.left + extras.right > cap {
2976 let scale = if extras.left + extras.right > 0.0 {
2977 cap / (extras.left + extras.right)
2978 } else {
2979 0.0
2980 };
2981 extras.left *= scale;
2982 extras.right *= scale;
2983 }
2984 }
2985 }
2986 }
2987 }
2988
2989 if has_legend
2996 && !matches!(
2997 built.theme.legend_position,
2998 crate::theme::LegendPosition::None
2999 )
3000 {
3001 let legend_aes =
3002 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3003 extras.legend_max_key_size = legend_aes
3007 .iter()
3008 .filter_map(|aes| {
3009 let scale = built.scales.get(aes)?;
3010 let breaks = scale.breaks();
3011 Some(crate::guide::legend::size_key_diameter(
3012 aes,
3013 &built.scales,
3014 scale,
3015 &breaks,
3016 ))
3017 })
3018 .fold(0.0_f64, f64::max);
3019 let has_discrete_guide = legend_aes.iter().any(|a| {
3020 built
3021 .scales
3022 .get(a)
3023 .map(|s| s.is_discrete())
3024 .unwrap_or(false)
3025 });
3026 if has_discrete_guide {
3027 let layout0 = PlotLayout::compute_with_extras(
3028 w as f64,
3029 h as f64,
3030 &built.theme,
3031 has_title,
3032 has_subtitle,
3033 has_caption,
3034 has_legend,
3035 x_axis_top,
3036 y_label_width,
3037 x_label_height,
3038 extras,
3039 );
3040 let (legend_x0, _, mut is_horizontal) = crate::guide::legend::legend_position(
3050 &built.theme,
3051 &layout0.plot_area,
3052 extras.legend_extra_rows,
3053 (0.0, 0.0),
3054 &layout0.total,
3055 );
3056 if let Some(dir) = built.theme.legend_direction {
3057 is_horizontal = matches!(dir, crate::theme::LegendDirection::Horizontal);
3058 }
3059 let avail_width =
3060 (layout0.plot_area.x + layout0.plot_area.width - legend_x0).max(0.0);
3061 let mut legend_warnings = Vec::new();
3062 let mut plans = crate::guide::legend::plan_all_legends(
3063 &built.scales,
3064 &built.guide_legend,
3065 &built.theme,
3066 &built.suppressed_aes,
3067 avail_width,
3068 layout0.plot_area.height,
3069 is_horizontal,
3070 &mut legend_warnings,
3071 );
3072 built.warnings.extend(legend_warnings);
3073
3074 if !plans.is_empty() {
3075 extras.legend_extra_rows = if is_horizontal {
3081 plans
3082 .values()
3083 .map(|p| p.rows.saturating_sub(1))
3084 .max()
3085 .unwrap_or(0)
3086 } else {
3087 plans.values().map(|p| p.rows.saturating_sub(1)).sum()
3088 };
3089 extras.legend_extra_width = plans
3096 .values()
3097 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3098 .fold(0.0_f64, f64::max);
3099
3100 if !is_horizontal {
3112 let final_layout = PlotLayout::compute_with_extras(
3113 w as f64,
3114 h as f64,
3115 &built.theme,
3116 has_title,
3117 has_subtitle,
3118 has_caption,
3119 has_legend,
3120 x_axis_top,
3121 y_label_width,
3122 x_label_height,
3123 extras,
3124 );
3125 let (legend_x_final, _, _) = crate::guide::legend::legend_position(
3131 &built.theme,
3132 &final_layout.plot_area,
3133 extras.legend_extra_rows,
3134 (0.0, 0.0),
3135 &final_layout.total,
3136 );
3137 let swatch_and_gap =
3138 built.theme.legend_key_width + built.theme.legend_spacing;
3139 let text_size = built.theme.legend_text.size;
3140 fn right_edge_of(
3146 legend_x: f64,
3147 swatch_and_gap: f64,
3148 text_size: f64,
3149 plan: &crate::guide::legend::LegendPlan,
3150 breaks: &[(f64, String)],
3151 ) -> f64 {
3152 plan.entries
3153 .iter()
3154 .filter_map(|e| breaks.get(e.break_idx).map(|(_, l)| (e, l)))
3155 .map(|(e, label)| {
3156 let text =
3157 e.label_override.as_deref().unwrap_or(label.as_str());
3158 legend_x
3159 + e.x_offset
3160 + swatch_and_gap
3161 + crate::render::text::text_width(text, text_size)
3162 })
3163 .fold(0.0_f64, f64::max)
3164 }
3165
3166 let mut updates: Vec<(
3167 crate::aes::Aesthetic,
3168 crate::guide::legend::LegendPlan,
3169 )> = Vec::new();
3170 let mut reduced_aes: Vec<crate::aes::Aesthetic> = Vec::new();
3177 for (aes, plan) in &plans {
3178 if plan.cols <= 1 {
3179 continue;
3180 }
3181 let Some(scale) = built.scales.get(aes) else {
3182 continue;
3183 };
3184 let breaks = scale.breaks();
3185 if right_edge_of(
3186 legend_x_final,
3187 swatch_and_gap,
3188 text_size,
3189 plan,
3190 &breaks,
3191 ) <= w as f64
3192 {
3193 continue; }
3195
3196 let n = breaks.len();
3199 let mut reduced: Vec<crate::guide::legend::LegendPlan> = Vec::new();
3200 for c in (1..plan.cols).rev() {
3201 let mut candidate_guide = built.guide_legend.clone();
3202 candidate_guide.ncol = Some(c);
3203 candidate_guide.nrow = None;
3204 let mut tmp_warnings = Vec::new();
3205 let candidate_guide =
3206 candidate_guide.validated(n, &mut tmp_warnings);
3207 if let Some(cand) = crate::guide::legend::plan_legend(
3208 &breaks,
3209 &candidate_guide,
3210 &built.theme,
3211 avail_width,
3212 layout0.plot_area.height,
3213 false,
3214 ) {
3215 reduced.push(cand);
3216 }
3217 }
3218 if let Some(fits) = reduced.iter().find(|c| {
3219 right_edge_of(legend_x_final, swatch_and_gap, text_size, c, &breaks)
3220 <= w as f64
3221 }) {
3222 extras.legend_extra_rows =
3223 extras.legend_extra_rows.max(fits.rows.saturating_sub(1));
3224 updates.push((aes.clone(), fits.clone()));
3225 reduced_aes.push(aes.clone());
3226 continue;
3227 }
3228
3229 let base = reduced.last().cloned().unwrap_or_else(|| plan.clone());
3234 extras.legend_extra_rows =
3235 extras.legend_extra_rows.max(base.rows.saturating_sub(1));
3236 let mut truncated = base.clone();
3237 for e in &mut truncated.entries {
3238 let Some((_, label)) = breaks.get(e.break_idx) else {
3239 continue;
3240 };
3241 let avail = (w as f64
3242 - (legend_x_final + e.x_offset + swatch_and_gap))
3243 .max(0.0);
3244 if let Some(short) =
3245 crate::render::text::truncate_to_width(label, avail, text_size)
3246 {
3247 e.label_override = Some(short);
3248 }
3249 }
3250
3251 let truncated_edge = right_edge_of(
3255 legend_x_final,
3256 swatch_and_gap,
3257 text_size,
3258 &truncated,
3259 &breaks,
3260 );
3261 if truncated_edge > w as f64 {
3262 built.warnings.push(format!(
3263 "legend: the {}-column grid still doesn't fit the {w}px chart (needs about {} px)",
3264 truncated.cols,
3265 truncated_edge.ceil() as i64
3266 ));
3267 }
3268 updates.push((aes.clone(), truncated));
3269 }
3270 for (aes, new_plan) in &updates {
3271 plans.insert(aes.clone(), new_plan.clone());
3272 }
3273
3274 if !reduced_aes.is_empty() {
3315 let original_reserve = extras.legend_extra_width;
3316 let tighter_reserve = plans
3317 .values()
3318 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3319 .fold(0.0_f64, f64::max);
3320 if tighter_reserve < original_reserve {
3321 let mut retry_extras = extras;
3322 retry_extras.legend_extra_width = tighter_reserve;
3323 let retry_layout = PlotLayout::compute_with_extras(
3324 w as f64,
3325 h as f64,
3326 &built.theme,
3327 has_title,
3328 has_subtitle,
3329 has_caption,
3330 has_legend,
3331 x_axis_top,
3332 y_label_width,
3333 x_label_height,
3334 retry_extras,
3335 );
3336 let (retry_x, _, _) = crate::guide::legend::legend_position(
3342 &built.theme,
3343 &retry_layout.plot_area,
3344 retry_extras.legend_extra_rows,
3345 (0.0, 0.0),
3346 &retry_layout.total,
3347 );
3348 let all_still_fit = plans.iter().all(|(aes, plan)| {
3349 let Some(scale) = built.scales.get(aes) else {
3350 return true;
3351 };
3352 let breaks = scale.breaks();
3353 right_edge_of(retry_x, swatch_and_gap, text_size, plan, &breaks)
3354 <= w as f64
3355 });
3356 if all_still_fit {
3357 extras.legend_extra_width = tighter_reserve;
3358 }
3359 }
3360 }
3361 }
3362 for (aes, plan) in &plans {
3363 if let Some(ov) = &plan.overflow {
3364 let n = built
3365 .scales
3366 .get(aes)
3367 .map(|s| s.breaks().len())
3368 .unwrap_or(ov.dropped);
3369 let shown = n.saturating_sub(ov.dropped);
3370 built.warnings.push(format!(
3371 "legend: {} of {n} entries don't fit the chart; the legend shows {shown} and \"+{} more\"",
3372 ov.dropped, ov.dropped
3373 ));
3374 }
3375 }
3376 }
3377 built.legend_plans = plans;
3383 }
3384 }
3385
3386 if has_legend
3397 && !matches!(
3398 built.theme.legend_position,
3399 crate::theme::LegendPosition::None
3400 )
3401 {
3402 let is_horizontal = match built.theme.legend_direction {
3403 Some(dir) => matches!(dir, crate::theme::LegendDirection::Horizontal),
3404 None => matches!(
3405 built.theme.legend_position,
3406 crate::theme::LegendPosition::Top | crate::theme::LegendPosition::Bottom
3407 ),
3408 };
3409 if is_horizontal {
3410 let legend_aes =
3411 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3412 let continuous_colourbar = legend_aes.iter().find_map(|aes| {
3413 if !matches!(
3414 aes,
3415 crate::aes::Aesthetic::Color | crate::aes::Aesthetic::Fill
3416 ) {
3417 return None;
3418 }
3419 let scale = built.scales.get(aes)?;
3420 if scale.is_discrete() {
3421 return None;
3422 }
3423 Some(scale)
3424 });
3425 if let Some(scale) = continuous_colourbar {
3426 let title = built
3427 .guide_legend
3428 .title
3429 .as_deref()
3430 .unwrap_or_else(|| scale.name());
3431 let bar_height = crate::guide::legend::continuous_horizontal_bar_height(
3432 &built.theme,
3433 !title.is_empty(),
3434 );
3435 let key_h = built
3436 .theme
3437 .legend_key_height
3438 .max(extras.legend_max_key_size);
3439 extras.continuous_horizontal_legend_extra_height =
3440 (bar_height - key_h).max(0.0);
3441 }
3442 }
3443 }
3444
3445 PlotLayout::compute_with_extras(
3446 w as f64,
3447 h as f64,
3448 &built.theme,
3449 has_title,
3450 has_subtitle,
3451 has_caption,
3452 has_legend,
3453 x_axis_top,
3454 y_label_width,
3455 x_label_height,
3456 extras,
3457 )
3458 }
3459
3460 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3463 fn render_into<DB>(
3464 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
3465 built: &crate::build::BuiltPlot,
3466 layout: &PlotLayout,
3467 ) -> Result<(), GGError>
3468 where
3469 DB: plotters::prelude::DrawingBackend,
3470 DB::ErrorType: 'static,
3471 {
3472 area.fill(&plotters::prelude::WHITE)
3473 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3474 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
3475 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
3476 area.present()
3477 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3478 Ok(())
3479 }
3480
3481 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3484 pub fn ggsave(
3485 self,
3486 path: &str,
3487 width_inches: f64,
3488 height_inches: f64,
3489 dpi: f64,
3490 ) -> Result<(), GGError> {
3491 let w = (width_inches * dpi) as u32;
3492 let h = (height_inches * dpi) as u32;
3493 self.save_with_size(path, w, h)
3494 }
3495
3496 fn has_legend_mapping(&self) -> bool {
3511 use crate::guide::legend::is_legend_aesthetic;
3516 let plot_level = self
3517 .mapping
3518 .mappings
3519 .iter()
3520 .any(|m| is_legend_aesthetic(&m.aesthetic));
3521 plot_level
3522 || self.layers.iter().any(|layer| {
3523 if layer.show_legend == Some(false) {
3524 return false;
3525 }
3526 let effective = if layer.geom.inherit_aes() {
3527 self.mapping.merge(&layer.mapping)
3528 } else {
3529 layer.mapping.clone()
3530 };
3531 effective
3532 .mappings
3533 .iter()
3534 .any(|m| is_legend_aesthetic(&m.aesthetic))
3535 })
3536 }
3537}
3538
3539#[derive(Clone, Copy, Debug)]
3541pub(crate) struct RenderMeta {
3542 pub(crate) has_title: bool,
3543 pub(crate) has_subtitle: bool,
3544 pub(crate) has_caption: bool,
3545 pub(crate) has_legend: bool,
3546}
3547
3548#[derive(Debug)]
3550#[non_exhaustive]
3551pub enum GGError {
3552 Render(RenderError),
3553 UnsupportedFormat(String),
3554 Io(std::io::Error),
3555 ValidationError(String),
3556}
3557
3558impl std::fmt::Display for GGError {
3559 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3560 match self {
3561 GGError::Render(e) => write!(f, "Render error: {e}"),
3562 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
3563 GGError::Io(e) => write!(f, "IO error: {e}"),
3564 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
3565 }
3566 }
3567}
3568
3569impl std::error::Error for GGError {}