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 pub fn geom_curve(self) -> Self {
918 self.add_geom(GeomCurve::default())
919 }
920
921 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
922 self.add_geom_with(geom)
923 }
924
925 pub fn geom_violin(self) -> Self {
926 self.add_geom(GeomViolin::default())
927 }
928
929 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
930 self.add_geom_with(geom)
931 }
932
933 pub fn geom_dotplot(self) -> Self {
934 self.add_geom(GeomDotplot::default())
935 }
936
937 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
938 self.add_geom_with(geom)
939 }
940
941 pub fn geom_qq(self) -> Self {
942 self.add_geom(GeomQQ::default())
943 }
944
945 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
946 self.add_geom_with(geom)
947 }
948
949 pub fn geom_qq_line(self) -> Self {
950 self.add_geom(GeomQQLine::default())
951 }
952
953 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
954 self.add_geom_with(geom)
955 }
956
957 pub fn geom_bin2d(self) -> Self {
958 self.add_geom(GeomBin2d::default())
959 }
960
961 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
962 self.add_geom_with(geom)
963 }
964
965 pub fn geom_hex(self) -> Self {
966 self.add_geom(GeomHex::default())
967 }
968
969 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
970 self.add_geom_with(geom)
971 }
972
973 pub fn geom_count(self) -> Self {
974 self.add_geom(GeomCount::default())
975 }
976
977 pub fn geom_count_with(self, geom: GeomCount) -> Self {
978 self.add_geom_with(geom)
979 }
980
981 pub fn geom_contour(self) -> Self {
982 self.add_geom(GeomContour::default())
983 }
984
985 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
986 self.add_geom_with(geom)
987 }
988
989 pub fn geom_contour_filled(self) -> Self {
992 self.add_geom(GeomPolygon {
993 line_width: 0.0,
994 alpha: 1.0,
995 ..GeomPolygon::default()
996 })
997 .stat(crate::stat::contour_filled::StatContourFilled::default())
998 }
999
1000 pub fn geom_density2d(self) -> Self {
1001 self.add_geom(GeomDensity2d::default())
1002 }
1003
1004 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
1005 self.add_geom_with(geom)
1006 }
1007
1008 pub fn geom_candlestick(self) -> Self {
1011 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
1012 }
1013
1014 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
1015 self.add_geom_with(geom)
1016 }
1017
1018 pub fn geom_ohlc(self) -> Self {
1021 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
1022 }
1023
1024 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
1025 self.add_geom_with(geom)
1026 }
1027
1028 pub fn geom_calendar(self) -> Self {
1039 self.geom_calendar_with(
1040 GeomTile {
1041 color: (255, 255, 255),
1042 line_width: 1.5,
1043 ..Default::default()
1044 },
1045 false,
1046 )
1047 }
1048
1049 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
1052 use crate::stat::calendar::{
1053 day_number, month_boundaries, month_breaks, month_breaks_localized, weekday_labels,
1054 CalendarGrid, StatCalendar, MAX_CALENDAR_YEARS,
1055 };
1056 let grid = self
1057 .mapping
1058 .get_mapping(&crate::aes::Aesthetic::X)
1059 .and_then(|c| self.data.column(c))
1060 .and_then(|col| {
1061 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
1062 });
1063 let Some((grid, clipped)) = grid else {
1064 return self.add_geom_with(tile).stat(StatCalendar {
1066 grid: None,
1067 monday_first,
1068 });
1069 };
1070 if clipped {
1071 self.warnings.push(format!(
1072 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
1073 ));
1074 }
1075 let (xb, _) = month_breaks(&grid);
1076 let yb = if monday_first {
1078 vec![6.0, 4.0, 2.0]
1079 } else {
1080 vec![5.0, 3.0, 1.0]
1081 };
1082 let segs = month_boundaries(&grid);
1083 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
1084 segs.iter().map(|s| Value::Float(f(s))).collect()
1085 };
1086 let boundaries = vec![
1087 ("x".to_string(), col(|s| s.0)),
1088 ("y".to_string(), col(|s| s.1)),
1089 ("xend".to_string(), col(|s| s.2)),
1090 ("yend".to_string(), col(|s| s.3)),
1091 ];
1092 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
1093 let plot = self
1094 .add_geom_with(tile)
1095 .stat(StatCalendar {
1096 grid: Some(grid),
1097 monday_first,
1098 })
1099 .scale_x_continuous(
1100 ScaleContinuous::new()
1101 .with_breaks(xb)
1102 .with_localized_labels(move |l| month_breaks_localized(&grid, l).1)
1104 .with_expand(0.0, 0.0),
1105 )
1106 .scale_y_continuous(
1107 ScaleContinuous::new()
1108 .with_breaks(yb)
1109 .with_localized_labels(weekday_labels)
1110 .with_expand(0.0, 0.0),
1111 );
1112 if segs.is_empty() {
1113 return plot;
1114 }
1115 plot.add_geom_with(crate::geom::segment::GeomCalendarBorder)
1117 .layer_data(boundaries)
1118 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1119 .show_legend(false)
1120 }
1121
1122 pub fn geom_blank(self) -> Self {
1123 self.add_geom(GeomBlank)
1124 }
1125
1126 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
1129 let mut plot = self.add_geom(geom);
1130 if let Some(layer) = plot.layers.last_mut() {
1131 layer.explicit_style = true;
1132 }
1133 plot
1134 }
1135
1136 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
1137 let stat = geom.default_stat();
1138 let position = geom.default_position();
1139 let params = geom.default_params();
1140 self.layers.push(Layer {
1141 data: None,
1142 mapping: Aes::default(),
1143 geom: Box::new(geom),
1144 stat,
1145 position,
1146 params,
1147 show_legend: None,
1148 explicit_style: false,
1149 tooltip_template: None,
1150 });
1151 self
1152 }
1153
1154 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
1158 if let Some(layer) = self.layers.last_mut() {
1159 layer.stat = Box::new(stat);
1160 }
1161 self
1162 }
1163
1164 pub fn position(mut self, pos: impl Position + 'static) -> Self {
1166 if let Some(layer) = self.layers.last_mut() {
1167 layer.position = Box::new(pos);
1168 }
1169 self
1170 }
1171
1172 pub fn layer_data(mut self, data: impl GGData) -> Self {
1174 if let Some(layer) = self.layers.last_mut() {
1175 layer.data = Some(data.into_dataframe());
1176 }
1177 self
1178 }
1179
1180 pub fn layer_aes(mut self, mapping: Aes) -> Self {
1182 self.warnings.extend(mapping.warnings.iter().cloned());
1183 if let Some(layer) = self.layers.last_mut() {
1184 layer.mapping = mapping;
1185 }
1186 self
1187 }
1188
1189 pub fn tooltip(mut self, template: &str) -> Self {
1200 if let Some(layer) = self.layers.last_mut() {
1201 layer.tooltip_template = Some(template.to_string());
1202 }
1203 self
1204 }
1205
1206 pub fn show_legend(mut self, show: bool) -> Self {
1209 if let Some(layer) = self.layers.last_mut() {
1210 layer.show_legend = Some(show);
1211 }
1212 self
1213 }
1214
1215 pub fn decorative(mut self) -> Self {
1242 if let Some(layer) = self.layers.last_mut() {
1243 let geom = std::mem::replace(&mut layer.geom, Box::new(GeomBlank));
1244 layer.geom = Box::new(crate::geom::decorative::Decorative(geom));
1245 if layer.show_legend.is_none() {
1246 layer.show_legend = Some(false);
1247 }
1248 }
1249 self
1250 }
1251
1252 pub fn layer_svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1286 let (name, value) = (name.into(), value.into());
1287 let Some(idx) = self.layers.len().checked_sub(1) else {
1288 self.warnings.push(format!(
1289 "layer_svg_attr(\"{name}\") called before any layer was added — ignored"
1290 ));
1291 return self;
1292 };
1293 if let Some((n, v)) =
1294 crate::render::svg_backend::validate_layer_svg_attr(&name, &value, &mut self.warnings)
1295 {
1296 self.layer_svg_attrs
1297 .retain(|(i, k, _)| !(*i == idx && *k == n));
1298 self.layer_svg_attrs.push((idx, n, v));
1299 }
1300 self
1301 }
1302
1303 pub fn layer_svg_attrs<N: Into<String>, V: Into<String>>(
1305 self,
1306 attrs: impl IntoIterator<Item = (N, V)>,
1307 ) -> Self {
1308 attrs
1309 .into_iter()
1310 .fold(self, |p, (n, v)| p.layer_svg_attr(n, v))
1311 }
1312
1313 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
1316 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1317 self.scales.push(Box::new(s));
1318 self
1319 }
1320
1321 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
1322 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1323 self.scales.push(Box::new(s));
1324 self
1325 }
1326
1327 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> 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_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> 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_color(mut self, s: impl Scale + 'static) -> Self {
1340 self.scales.push(Box::new(s));
1341 self
1342 }
1343
1344 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
1345 self.scales.push(Box::new(s));
1346 self
1347 }
1348
1349 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1350 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
1351 self.scale_color(s)
1352 }
1353
1354 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1355 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
1356 self.scale_fill(s)
1357 }
1358
1359 pub fn scale_colour_identity(self) -> Self {
1362 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Color);
1363 self.scale_color(s)
1364 }
1365
1366 pub fn scale_color_identity(self) -> Self {
1368 self.scale_colour_identity()
1369 }
1370
1371 pub fn scale_fill_identity(self) -> Self {
1374 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Fill);
1375 self.scale_fill(s)
1376 }
1377
1378 pub fn scale_color_viridis(self) -> Self {
1379 use crate::scale::color::ScaleColorDiscrete;
1380 use crate::scale::palettes::PaletteName;
1381 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
1382 .with_named_palette(&PaletteName::Viridis);
1383 self.scale_color(s)
1384 }
1385
1386 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1387 use crate::scale::color::ScaleColorDiscrete;
1388 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
1389 self.scale_color(s)
1390 }
1391
1392 pub fn scale_color_gradient(
1393 self,
1394 low: crate::scale::color::RGBAColor,
1395 high: crate::scale::color::RGBAColor,
1396 ) -> Self {
1397 use crate::scale::color::ScaleColorContinuous;
1398 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
1399 self.scale_color(s)
1400 }
1401
1402 pub fn scale_fill_gradient(
1403 self,
1404 low: crate::scale::color::RGBAColor,
1405 high: crate::scale::color::RGBAColor,
1406 ) -> Self {
1407 use crate::scale::color::ScaleColorContinuous;
1408 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
1409 self.scale_fill(s)
1410 }
1411
1412 pub fn scale_color_gradient2(
1413 self,
1414 low: crate::scale::color::RGBAColor,
1415 mid: crate::scale::color::RGBAColor,
1416 high: crate::scale::color::RGBAColor,
1417 ) -> Self {
1418 use crate::scale::gradient::ScaleColorGradient2;
1419 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
1420 self.scale_color(s)
1421 }
1422
1423 pub fn scale_fill_gradient2(
1424 self,
1425 low: crate::scale::color::RGBAColor,
1426 mid: crate::scale::color::RGBAColor,
1427 high: crate::scale::color::RGBAColor,
1428 ) -> Self {
1429 use crate::scale::gradient::ScaleColorGradient2;
1430 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
1431 self.scale_fill(s)
1432 }
1433
1434 pub fn scale_fill_viridis(self) -> Self {
1435 use crate::scale::color::ScaleColorDiscrete;
1436 use crate::scale::palettes::PaletteName;
1437 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
1438 .with_named_palette(&PaletteName::Viridis);
1439 self.scale_fill(s)
1440 }
1441
1442 pub fn scale_color_viridis_c(self) -> Self {
1444 use crate::scale::gradient_n::ScaleColorGradientN;
1445 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
1446 self.scale_color(s)
1447 }
1448
1449 pub fn scale_fill_viridis_c(self) -> Self {
1451 use crate::scale::gradient_n::ScaleColorGradientN;
1452 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
1453 self.scale_fill(s)
1454 }
1455
1456 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1458 use crate::scale::gradient_n::ScaleColorGradientN;
1459 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
1460 self.scale_color(s)
1461 }
1462
1463 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1465 use crate::scale::gradient_n::ScaleColorGradientN;
1466 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
1467 self.scale_fill(s)
1468 }
1469
1470 pub fn scale_color_steps(
1473 self,
1474 low: crate::scale::color::RGBAColor,
1475 high: crate::scale::color::RGBAColor,
1476 n_bins: usize,
1477 ) -> Self {
1478 let s = crate::scale::steps::ScaleColorSteps::two(
1479 crate::aes::Aesthetic::Color,
1480 (low.r, low.g, low.b),
1481 (high.r, high.g, high.b),
1482 n_bins,
1483 );
1484 self.scale_color(s)
1485 }
1486
1487 pub fn scale_color_stepsn(
1489 self,
1490 stops: Vec<crate::scale::color::RGBAColor>,
1491 n_bins: usize,
1492 ) -> Self {
1493 let s = crate::scale::steps::ScaleColorSteps::new(
1494 crate::aes::Aesthetic::Color,
1495 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
1496 n_bins,
1497 );
1498 self.scale_color(s)
1499 }
1500
1501 pub fn scale_color_fermenter(
1503 self,
1504 name: crate::scale::palettes::PaletteName,
1505 n_bins: usize,
1506 ) -> Self {
1507 let stops = crate::scale::palettes::palette(&name)
1508 .iter()
1509 .map(|c| (c.r, c.g, c.b))
1510 .collect();
1511 let s =
1512 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
1513 self.scale_color(s)
1514 }
1515
1516 pub fn scale_fill_steps(
1518 self,
1519 low: crate::scale::color::RGBAColor,
1520 high: crate::scale::color::RGBAColor,
1521 n_bins: usize,
1522 ) -> Self {
1523 let s = crate::scale::steps::ScaleColorSteps::two(
1524 crate::aes::Aesthetic::Fill,
1525 (low.r, low.g, low.b),
1526 (high.r, high.g, high.b),
1527 n_bins,
1528 );
1529 self.scale_fill(s)
1530 }
1531
1532 pub fn scale_fill_binned(
1543 self,
1544 breaks: Vec<f64>,
1545 low: crate::scale::color::RGBAColor,
1546 high: crate::scale::color::RGBAColor,
1547 ) -> Self {
1548 let s = crate::scale::binned::ScaleFillBinned::two(
1549 crate::aes::Aesthetic::Fill,
1550 breaks,
1551 (low.r, low.g, low.b),
1552 (high.r, high.g, high.b),
1553 );
1554 self.scale_fill(s)
1555 }
1556
1557 pub fn scale_fill_binned_with(
1561 self,
1562 breaks: Vec<f64>,
1563 colors: Vec<crate::scale::color::RGBAColor>,
1564 ) -> Self {
1565 let s = crate::scale::binned::ScaleFillBinned::new(
1566 crate::aes::Aesthetic::Fill,
1567 breaks,
1568 colors.iter().map(|c| (c.r, c.g, c.b)).collect(),
1569 );
1570 self.scale_fill(s)
1571 }
1572
1573 pub fn scale_fill_fermenter(
1575 self,
1576 name: crate::scale::palettes::PaletteName,
1577 n_bins: usize,
1578 ) -> Self {
1579 let stops = crate::scale::palettes::palette(&name)
1580 .iter()
1581 .map(|c| (c.r, c.g, c.b))
1582 .collect();
1583 let s =
1584 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
1585 self.scale_fill(s)
1586 }
1587
1588 pub fn scale_fill_discrete_sorted(self) -> Self {
1593 use crate::scale::color::ScaleColorDiscrete;
1594 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
1595 }
1596
1597 pub fn scale_color_discrete_sorted(self) -> Self {
1600 use crate::scale::color::ScaleColorDiscrete;
1601 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
1602 }
1603
1604 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1605 use crate::scale::color::ScaleColorDiscrete;
1606 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
1607 self.scale_fill(s)
1608 }
1609
1610 pub fn scale_linetype_manual(
1611 self,
1612 values: Vec<(&str, crate::render::backend::Linetype)>,
1613 ) -> Self {
1614 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
1615 self.scale_color(s)
1616 }
1617
1618 pub fn scale_shape_manual(
1619 self,
1620 values: Vec<(&str, crate::render::backend::PointShape)>,
1621 ) -> Self {
1622 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
1623 self.scale_color(s)
1624 }
1625
1626 pub fn scale_color_grey(self) -> Self {
1627 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
1628 self.scale_color(s)
1629 }
1630
1631 pub fn scale_fill_grey(self) -> Self {
1632 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
1633 self.scale_fill(s)
1634 }
1635
1636 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1637 self.scale_color(s)
1638 }
1639
1640 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1641 self.scale_fill(s)
1642 }
1643
1644 pub fn scale_x_reverse(self) -> Self {
1645 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1646 }
1647
1648 pub fn scale_y_reverse(self) -> Self {
1649 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1650 }
1651
1652 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1653 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1654 self.scales.push(Box::new(s));
1655 self
1656 }
1657
1658 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1659 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1660 self.scales.push(Box::new(s));
1661 self
1662 }
1663
1664 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
1665 self.scales.push(Box::new(s));
1666 self
1667 }
1668
1669 pub fn scale_size_area(mut self, max_size: f64) -> Self {
1673 self.scales.push(Box::new(
1674 crate::scale::size::ScaleSizeContinuous::new().area(max_size),
1675 ));
1676 self
1677 }
1678
1679 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
1680 self.scales.push(Box::new(s));
1681 self
1682 }
1683
1684 pub fn scale_linewidth(mut self, s: crate::scale::linewidth::ScaleLinewidthContinuous) -> Self {
1688 self.scales.push(Box::new(s));
1689 self
1690 }
1691
1692 pub fn xlim(self, min: f64, max: f64) -> Self {
1693 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
1694 }
1695
1696 pub fn ylim(self, min: f64, max: f64) -> Self {
1697 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
1698 }
1699
1700 pub fn scale_x_log10(self) -> Self {
1701 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1702 }
1703
1704 pub fn scale_y_log10(self) -> Self {
1705 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1706 }
1707
1708 pub fn scale_x_sqrt(self) -> Self {
1709 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1710 }
1711
1712 pub fn scale_y_sqrt(self) -> Self {
1713 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1714 }
1715
1716 pub fn scale_x_log2(self) -> Self {
1717 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1718 }
1719
1720 pub fn scale_y_log2(self) -> Self {
1721 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1722 }
1723
1724 pub fn scale_x_ln(self) -> Self {
1725 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1726 }
1727
1728 pub fn scale_y_ln(self) -> Self {
1729 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1730 }
1731
1732 pub fn scale_x_logit(self) -> Self {
1734 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1735 }
1736
1737 pub fn scale_y_logit(self) -> Self {
1738 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1739 }
1740
1741 pub fn scale_x_probit(self) -> Self {
1743 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1744 }
1745
1746 pub fn scale_y_probit(self) -> Self {
1747 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1748 }
1749
1750 pub fn scale_x_pseudo_log(self) -> Self {
1752 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1753 }
1754
1755 pub fn scale_y_pseudo_log(self) -> Self {
1756 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1757 }
1758
1759 pub fn scale_x_reciprocal(self) -> Self {
1761 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1762 }
1763
1764 pub fn scale_y_reciprocal(self) -> Self {
1765 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1766 }
1767
1768 pub fn scale_x_exp(self) -> Self {
1770 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1771 }
1772
1773 pub fn scale_y_exp(self) -> Self {
1774 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1775 }
1776
1777 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
1779 self.scale_x_continuous(
1780 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1781 )
1782 }
1783
1784 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
1785 self.scale_y_continuous(
1786 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1787 )
1788 }
1789
1790 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
1793 self.facet = Facet::Wrap {
1794 var: var.to_string(),
1795 ncol,
1796 scales: FacetScales::Fixed,
1797 labeller: FacetLabeller::default(),
1798 };
1799 self
1800 }
1801
1802 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
1803 self.facet = Facet::Wrap {
1804 var: var.to_string(),
1805 ncol,
1806 scales,
1807 labeller: FacetLabeller::default(),
1808 };
1809 self
1810 }
1811
1812 pub fn facet_wrap_labeller(
1813 mut self,
1814 var: &str,
1815 ncol: Option<usize>,
1816 labeller: FacetLabeller,
1817 ) -> Self {
1818 self.facet = Facet::Wrap {
1819 var: var.to_string(),
1820 ncol,
1821 scales: FacetScales::Fixed,
1822 labeller,
1823 };
1824 self
1825 }
1826
1827 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
1828 self.facet = Facet::Grid {
1829 row_var: row.map(String::from),
1830 col_var: col.map(String::from),
1831 scales: FacetScales::Fixed,
1832 labeller: FacetLabeller::default(),
1833 space: FacetSpace::Fixed,
1834 };
1835 self
1836 }
1837
1838 pub fn facet_grid_free(
1839 mut self,
1840 row: Option<&str>,
1841 col: Option<&str>,
1842 scales: FacetScales,
1843 ) -> Self {
1844 self.facet = Facet::Grid {
1845 row_var: row.map(String::from),
1846 col_var: col.map(String::from),
1847 scales,
1848 labeller: FacetLabeller::default(),
1849 space: FacetSpace::Fixed,
1850 };
1851 self
1852 }
1853
1854 pub fn facet_grid_multi(
1858 mut self,
1859 row: Option<&str>,
1860 cols: &[&str],
1861 scales: FacetScales,
1862 space: FacetSpace,
1863 ) -> Self {
1864 let col_var = if cols.len() > 1 {
1865 let n = self.data.nrows();
1867 let mut combined = Vec::with_capacity(n);
1868 for i in 0..n {
1869 let parts: Vec<String> = cols
1870 .iter()
1871 .map(|c| {
1872 self.data
1873 .column(c)
1874 .and_then(|col| col.get(i))
1875 .map(|v| v.to_group_key())
1876 .unwrap_or_default()
1877 })
1878 .collect();
1879 combined.push(crate::data::Value::Str(parts.join(" . ")));
1880 }
1881 let name = "__facet_cols__".to_string();
1882 self.data.add_column(name.clone(), combined);
1883 Some(name)
1884 } else {
1885 cols.first().map(|s| s.to_string())
1886 };
1887 self.facet = Facet::Grid {
1888 row_var: row.map(String::from),
1889 col_var,
1890 scales,
1891 labeller: FacetLabeller::default(),
1892 space,
1893 };
1894 self
1895 }
1896
1897 pub fn facet_grid_space(
1900 mut self,
1901 row: Option<&str>,
1902 col: Option<&str>,
1903 scales: FacetScales,
1904 space: FacetSpace,
1905 ) -> Self {
1906 self.facet = Facet::Grid {
1907 row_var: row.map(String::from),
1908 col_var: col.map(String::from),
1909 scales,
1910 labeller: FacetLabeller::default(),
1911 space,
1912 };
1913 self
1914 }
1915
1916 pub fn facet_grid_labeller(
1917 mut self,
1918 row: Option<&str>,
1919 col: Option<&str>,
1920 labeller: FacetLabeller,
1921 ) -> Self {
1922 self.facet = Facet::Grid {
1923 row_var: row.map(String::from),
1924 col_var: col.map(String::from),
1925 scales: FacetScales::Fixed,
1926 labeller,
1927 space: FacetSpace::Fixed,
1928 };
1929 self
1930 }
1931
1932 pub fn coord_flip(mut self) -> Self {
1935 self.coord = Box::new(CoordFlip);
1936 self
1937 }
1938
1939 pub fn coord_fixed(mut self, ratio: f64) -> Self {
1940 self.coord = Box::new(CoordFixed::new(ratio));
1941 self
1942 }
1943
1944 #[cfg(feature = "sf")]
1948 pub fn coord_sf(mut self) -> Self {
1949 self.coord = Box::new(crate::coord::sf::CoordSf::new());
1950 self
1951 }
1952
1953 #[cfg(feature = "sf")]
1958 pub fn coord_sf_with(mut self, coord: crate::coord::sf::CoordSf) -> Self {
1959 self.coord = Box::new(coord);
1960 self
1961 }
1962
1963 pub fn svg_root_data(mut self, on: bool) -> Self {
1970 self.svg_root_data = on;
1971 self
1972 }
1973
1974 pub fn svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1984 self.root_attrs.push((name.into(), value.into()));
1985 self
1986 }
1987
1988 pub fn svg_attrs(mut self, attrs: impl IntoIterator<Item = (String, String)>) -> Self {
1990 self.root_attrs.extend(attrs);
1991 self
1992 }
1993
1994 pub fn svg_warnings_attr(mut self, on: bool) -> Self {
1999 self.svg_warnings_attr = on;
2000 self
2001 }
2002
2003 pub fn svg_formatted_values(mut self, on: bool) -> Self {
2012 self.svg_formatted_values = on;
2013 self
2014 }
2015
2016 pub fn svg_title(mut self, title: impl Into<String>) -> Self {
2026 self.svg_title = Some(title.into());
2027 self
2028 }
2029
2030 pub fn svg_desc(mut self, desc: impl Into<String>) -> Self {
2038 self.svg_desc = Some(desc.into());
2039 self
2040 }
2041
2042 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
2047 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
2048 self
2049 }
2050
2051 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
2053 self.coord_trans(None, Some(y))
2054 }
2055
2056 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
2058 self.coord_trans(Some(x), None)
2059 }
2060
2061 pub fn coord_cartesian_zoom(
2063 mut self,
2064 xlim: Option<(f64, f64)>,
2065 ylim: Option<(f64, f64)>,
2066 ) -> Self {
2067 let mut c = CoordCartesian::new();
2068 if let Some((min, max)) = xlim {
2069 c = c.xlim(min, max);
2070 }
2071 if let Some((min, max)) = ylim {
2072 c = c.ylim(min, max);
2073 }
2074 self.coord = Box::new(c);
2075 self
2076 }
2077
2078 pub fn coord_polar(mut self) -> Self {
2079 self.coord = Box::new(CoordPolar::new());
2080 self
2081 }
2082
2083 pub fn coord_radar(mut self) -> Self {
2087 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
2088 self
2089 }
2090
2091 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
2092 self.coord = Box::new(coord);
2093 self
2094 }
2095
2096 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
2097 self.coord = Box::new(coord);
2098 self
2099 }
2100
2101 pub fn theme(mut self, theme: Theme) -> Self {
2106 self.apply_preset(theme);
2107 self
2108 }
2109
2110 fn apply_preset(&mut self, theme: Theme) {
2111 let primary = self.theme.primary;
2112 self.theme = theme;
2113 if self.theme.primary.is_none() {
2114 self.theme.primary = primary;
2115 }
2116 }
2117
2118 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
2122 self.theme.axis_text_x.angle = degrees;
2123 self
2124 }
2125
2126 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
2128 self.theme.axis_text_y.angle = degrees;
2129 self
2130 }
2131
2132 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
2135 self.theme.axis_text_x_dodge = n.max(1);
2136 self
2137 }
2138
2139 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
2142 self.theme.aspect_ratio = Some(ratio);
2143 self
2144 }
2145
2146 pub fn panel_ontop(mut self) -> Self {
2148 self.theme.panel_ontop = true;
2149 self
2150 }
2151
2152 pub fn axis_minor_ticks(mut self) -> Self {
2154 self.theme.axis_minor_ticks = true;
2155 self
2156 }
2157
2158 pub fn axis_text_x_fit(mut self, fit: crate::theme::AxisLabelFit) -> Self {
2163 self.theme.axis_text_x_fit = fit;
2164 self
2165 }
2166
2167 pub fn axis_text_x_check_overlap(mut self, on: bool) -> Self {
2170 self.theme.axis_text_x_check_overlap = on;
2171 self
2172 }
2173
2174 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
2177 self.theme.title_position = pos;
2178 self
2179 }
2180
2181 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
2183 self.theme.tag_position = pos;
2184 self
2185 }
2186
2187 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
2189 self.theme.legend_direction = Some(dir);
2190 self
2191 }
2192
2193 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
2200 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
2201 self
2202 }
2203
2204 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
2206 self.theme.legend_position = pos;
2207 self
2208 }
2209
2210 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
2211 self.theme.primary = Some(color);
2212 self
2213 }
2214
2215 pub fn theme_minimal(mut self) -> Self {
2216 self.apply_preset(crate::theme::presets::theme_minimal());
2217 self
2218 }
2219
2220 pub fn theme_bw(mut self) -> Self {
2221 self.apply_preset(crate::theme::presets::theme_bw());
2222 self
2223 }
2224
2225 pub fn theme_gray(mut self) -> Self {
2226 self.apply_preset(crate::theme::presets::theme_gray());
2227 self
2228 }
2229
2230 pub fn theme_classic(mut self) -> Self {
2231 self.apply_preset(crate::theme::presets::theme_classic());
2232 self
2233 }
2234
2235 pub fn theme_pubr(mut self) -> Self {
2237 self.apply_preset(crate::theme::presets::theme_pubr());
2238 self
2239 }
2240
2241 pub fn theme_linedraw(mut self) -> Self {
2242 self.apply_preset(crate::theme::presets::theme_linedraw());
2243 self
2244 }
2245
2246 pub fn theme_light(mut self) -> Self {
2247 self.apply_preset(crate::theme::presets::theme_light());
2248 self
2249 }
2250
2251 pub fn theme_dark(mut self) -> Self {
2252 self.apply_preset(crate::theme::presets::theme_dark());
2253 self
2254 }
2255
2256 pub fn theme_void(mut self) -> Self {
2257 self.apply_preset(crate::theme::presets::theme_void());
2258 self
2259 }
2260
2261 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
2264 self.theme = self.theme.update(update);
2265 self
2266 }
2267
2268 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
2272 self.guide_legend = guide;
2273 self
2274 }
2275
2276 pub fn labs(mut self, labels: Labels) -> Self {
2279 if labels.title.is_some() {
2280 self.labels.title = labels.title;
2281 }
2282 if labels.subtitle.is_some() {
2283 self.labels.subtitle = labels.subtitle;
2284 }
2285 if labels.x.is_some() {
2286 self.labels.x = labels.x;
2287 }
2288 if labels.y.is_some() {
2289 self.labels.y = labels.y;
2290 }
2291 if labels.caption.is_some() {
2292 self.labels.caption = labels.caption;
2293 }
2294 if labels.tag.is_some() {
2295 self.labels.tag = labels.tag;
2296 }
2297 self
2298 }
2299
2300 pub fn title(mut self, title: &str) -> Self {
2301 self.labels.title = Some(title.to_string());
2302 self
2303 }
2304
2305 pub fn tag(mut self, tag: &str) -> Self {
2308 self.labels.tag = Some(tag.to_string());
2309 self
2310 }
2311
2312 pub fn subtitle(mut self, subtitle: &str) -> Self {
2313 self.labels.subtitle = Some(subtitle.to_string());
2314 self
2315 }
2316
2317 pub fn xlab(mut self, label: &str) -> Self {
2318 self.labels.x = Some(label.to_string());
2319 self
2320 }
2321
2322 pub fn ylab(mut self, label: &str) -> Self {
2323 self.labels.y = Some(label.to_string());
2324 self
2325 }
2326
2327 pub fn caption(mut self, caption: &str) -> Self {
2328 self.labels.caption = Some(caption.to_string());
2329 self
2330 }
2331
2332 pub fn annotate(mut self, annotation: Annotation) -> Self {
2336 self.annotations.push(annotation);
2337 self
2338 }
2339
2340 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
2342 self.annotate(Annotation::text(label, x, y))
2343 }
2344
2345 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
2347 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
2348 }
2349
2350 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
2352 self.annotate(Annotation::segment(x, y, xend, yend))
2353 }
2354
2355 pub fn locale(mut self, locale: crate::locale::Locale) -> Self {
2378 self.locale = Some(locale);
2379 self
2380 }
2381
2382 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
2386 PlotBuilder::build(self)
2387 }
2388
2389 pub fn build(self) -> crate::build::BuiltPlot {
2393 self.try_build().expect("plot build failed")
2394 }
2395
2396 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2400 pub fn save(self, path: &str) -> Result<(), GGError> {
2401 self.save_with_size(path, 800, 600)
2402 }
2403
2404 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2406 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
2407 let (built, layout) = self.prepare(w, h)?;
2408
2409 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
2411
2412 match ext.as_str() {
2413 "svg" => {
2414 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
2415 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2416 }
2417 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
2418 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
2419 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2420 }
2421 _ => {
2422 return Err(GGError::UnsupportedFormat(ext));
2423 }
2424 }
2425
2426 Ok(())
2427 }
2428
2429 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2435 pub fn render_svg(self) -> Result<String, GGError> {
2436 self.render_svg_with_size(800, 600)
2437 }
2438
2439 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2443 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2444 let (built, layout) = self.prepare(w, h)?;
2445 let mut buf = String::new();
2446 {
2447 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
2448 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2449 }
2450 Ok(buf)
2451 }
2452
2453 pub fn render_svg_native(self) -> Result<String, GGError> {
2457 self.render_svg_native_with_size(800, 600)
2458 }
2459
2460 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2468 Ok(self.render_native(w, h)?.0.finish())
2469 }
2470
2471 pub fn render_svg_native_with_warnings(
2476 self,
2477 w: u32,
2478 h: u32,
2479 ) -> Result<(String, Vec<String>), GGError> {
2480 let (backend, warnings, _) = self.render_native(w, h)?;
2481 Ok((backend.finish(), warnings))
2482 }
2483
2484 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
2496 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
2497 }
2498
2499 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
2503 let (backend, _, pa) = self.render_native(w, h)?;
2504 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
2505 }
2506
2507 fn render_native(
2511 self,
2512 w: u32,
2513 h: u32,
2514 ) -> Result<
2515 (
2516 crate::render::svg_backend::SvgBackend,
2517 Vec<String>,
2518 crate::render::Rect,
2519 ),
2520 GGError,
2521 > {
2522 let svg_root_data = self.svg_root_data;
2523 let root_attrs = self.root_attrs.clone();
2524 let svg_warnings_attr = self.svg_warnings_attr;
2525 let svg_formatted_values = self.svg_formatted_values;
2526 let svg_title = self.svg_title.clone();
2527 let svg_desc = self.svg_desc.clone();
2528 let (built, layout) = self.prepare(w, h)?;
2529 let pa = layout.plot_area.clone();
2530 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
2531 backend.set_formatted_values(svg_formatted_values);
2532 backend.set_svg_title(svg_title.clone());
2533 backend.set_svg_desc(svg_desc.clone());
2534 let existing_root_attrs = if svg_root_data {
2535 let attrs = crate::render::svg_backend::root_data_attrs(&built);
2536 backend.set_root_attrs(attrs.clone());
2537 let mut reserved = attrs;
2541 reserved.push(("data-plot".to_string(), String::new()));
2542 reserved
2543 } else {
2544 backend.without_plot_attr();
2545 Vec::new()
2546 };
2547 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2548 let mut warnings = built.warnings;
2549 warnings.extend(backend.take_warnings());
2550
2551 let mut reserved = existing_root_attrs;
2552 if svg_warnings_attr {
2553 reserved.push(("data-warnings".to_string(), String::new()));
2554 }
2555 if svg_title.is_some() || svg_desc.is_some() {
2560 reserved.push(("role".to_string(), String::new()));
2561 }
2562 if svg_title.is_some() {
2563 reserved.push(("aria-labelledby".to_string(), String::new()));
2564 }
2565 if svg_desc.is_some() {
2566 reserved.push(("aria-describedby".to_string(), String::new()));
2567 }
2568 let extra = crate::render::svg_backend::validate_root_attrs(
2569 &root_attrs,
2570 &reserved,
2571 "plot",
2572 &mut warnings,
2573 );
2574 backend.extend_root_attrs(extra);
2575 if svg_warnings_attr {
2576 backend.extend_root_attrs(vec![(
2577 "data-warnings".to_string(),
2578 crate::render::svg_backend::warnings_json(&warnings),
2579 )]);
2580 }
2581 Ok((backend, warnings, pa))
2582 }
2583
2584 #[cfg(feature = "canvas")]
2589 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
2590 let (built, layout) = self.prepare(w, h)?;
2591 let mut backend =
2592 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2593 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2594 let (ow, oh) = backend.dimensions();
2595 Ok((ow, oh, backend.into_rgba()))
2596 }
2597
2598 #[cfg(feature = "canvas")]
2602 pub fn render_rgba_area_with_size(
2603 self,
2604 w: u32,
2605 h: u32,
2606 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
2607 let (built, layout) = self.prepare(w, h)?;
2608 let pa = layout.plot_area.clone();
2609 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
2610 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2611 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
2612 }
2613
2614 #[cfg(feature = "canvas")]
2618 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2619 let (built, layout) = self.prepare(w, h)?;
2620 let mut backend =
2621 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2622 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2623 backend.into_png().map_err(GGError::Render)
2624 }
2625
2626 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2633 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
2634 self.render_png_with_size(800, 600)
2635 }
2636
2637 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2640 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2641 let (built, layout) = self.prepare(w, h)?;
2642
2643 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
2646 {
2647 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
2648 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2649 }
2650
2651 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
2652 GGError::Render(RenderError::BackendError(
2653 "PNG buffer size mismatch".to_string(),
2654 ))
2655 })?;
2656 let mut out = std::io::Cursor::new(Vec::new());
2657 img.write_to(&mut out, image::ImageOutputFormat::Png)
2658 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2659 Ok(out.into_inner())
2660 }
2661
2662 pub(crate) fn prepare(
2664 self,
2665 w: u32,
2666 h: u32,
2667 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
2668 let (mut built, meta) = self.build_for_render()?;
2669 let layout = Self::layout_built(&mut built, &meta, w, h);
2670 Ok((built, layout))
2671 }
2672
2673 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
2676 let plot = self;
2677
2678 let has_title = plot.labels.title.is_some();
2679 let has_subtitle = plot.labels.subtitle.is_some();
2680 let has_caption = plot.labels.caption.is_some();
2681 let has_legend = plot.has_legend_mapping();
2682 let x_label = plot.labels.x.clone();
2683 let y_label = plot.labels.y.clone();
2684
2685 let mut built = PlotBuilder::build(plot)?;
2686
2687 built.theme.resolve_inheritance();
2689
2690 if let Some(ref label) = x_label {
2692 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
2693 s.set_name(label);
2694 }
2695 }
2696 if let Some(ref label) = y_label {
2697 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
2698 s.set_name(label);
2699 }
2700 }
2701
2702 Ok((
2703 built,
2704 RenderMeta {
2705 has_title,
2706 has_subtitle,
2707 has_caption,
2708 has_legend,
2709 },
2710 ))
2711 }
2712
2713 pub(crate) fn layout_built(
2716 built: &mut crate::build::BuiltPlot,
2717 meta: &RenderMeta,
2718 w: u32,
2719 h: u32,
2720 ) -> PlotLayout {
2721 let RenderMeta {
2722 has_title,
2723 has_subtitle,
2724 has_caption,
2725 has_legend,
2726 } = *meta;
2727 let x_axis_top = built
2728 .scales
2729 .get(&crate::aes::Aesthetic::X)
2730 .map(|s| s.axis_position_opposite())
2731 .unwrap_or(false);
2732
2733 let flipped = built.coord.is_flipped();
2737 let h_aes = if flipped {
2738 crate::aes::Aesthetic::Y
2739 } else {
2740 crate::aes::Aesthetic::X
2741 };
2742 let v_aes = if flipped {
2743 crate::aes::Aesthetic::X
2744 } else {
2745 crate::aes::Aesthetic::Y
2746 };
2747
2748 let drawn_x_labels: Vec<(f64, String)> = built
2756 .scales
2757 .get(&h_aes)
2758 .map(|hs| {
2759 hs.breaks()
2760 .into_iter()
2761 .filter(|(_, label)| !label.is_empty())
2762 .collect()
2763 })
2764 .unwrap_or_default();
2765 let h_is_discrete = built
2766 .scales
2767 .get(&h_aes)
2768 .map(|hs| hs.is_discrete())
2769 .unwrap_or(false);
2770
2771 if built.theme.axis_text_x.visible
2784 && built.theme.axis_text_x.angle.abs() < 1.0
2785 && h_is_discrete
2786 && !drawn_x_labels.is_empty()
2787 {
2788 let font = built.theme.axis_text_x.size;
2789 let max_w = drawn_x_labels
2790 .iter()
2791 .map(|(_, l)| crate::render::text::text_width(l, font))
2792 .fold(0.0_f64, f64::max);
2793 let n = drawn_x_labels.len() as f64;
2794 let spacing = 0.9 * w as f64 / n; if max_w > spacing {
2796 let vertical = max_w * std::f64::consts::FRAC_1_SQRT_2 > spacing;
2800 built.theme.axis_text_x.angle = if vertical { 270.0 } else { -45.0 };
2801 }
2802 }
2803
2804 let x_label_height = if built.theme.axis_text_x.visible
2819 && built.theme.axis_text_x.angle.abs() > 10.0
2820 && !drawn_x_labels.is_empty()
2821 {
2822 let font = built.theme.axis_text_x.size;
2823 let angle = built.theme.axis_text_x.angle;
2824 let dir = if x_axis_top { -1.0 } else { 1.0 };
2825 let (rise, fall) = crate::render::text::rotated_label_rise_fall(
2826 drawn_x_labels.iter().map(|(_, l)| l.as_str()),
2827 font,
2828 angle,
2829 crate::render::text::rotated_anchor_frac(angle, dir),
2830 );
2831 Some(built.theme.legend_spacing / 2.0 + font / 2.0 + rise + fall)
2832 } else {
2833 None
2834 };
2835
2836 let y_label_width = if built.theme.axis_text_y.visible {
2840 built.scales.get(&v_aes).map(|vs| {
2841 let max_ch = vs
2842 .breaks()
2843 .iter()
2844 .map(|(_, l)| l.chars().count())
2845 .max()
2846 .unwrap_or(0);
2847 let measured = max_ch as f64 * built.theme.axis_text_y.size * 0.6 + 4.0;
2848 let default = built.theme.axis_text_y.size * 3.5 + 4.0;
2849 measured.max(default).min(0.35 * w as f64)
2850 })
2851 } else {
2852 None
2853 };
2854
2855 let mut extras = crate::render::layout::LayoutExtras::default();
2866 let can_reserve = built.facet.is_none()
2867 && built.theme.axis_text_x.visible
2868 && built.theme.axis_text_x_fit == crate::theme::AxisLabelFit::Reserve
2869 && !built.coord.is_polar()
2870 && built.coord.supports_axis_reserve();
2871 if can_reserve {
2872 if let Some(hs) = built.scales.get(&h_aes) {
2873 let breaks = hs.breaks();
2874 if let (Some(first), Some(last)) = (breaks.first(), breaks.last()) {
2875 let angle = built.theme.axis_text_x.angle;
2876 let anchor = if angle.abs() > 10.0 {
2877 let dir = if x_axis_top { -1.0 } else { 1.0 };
2878 crate::render::text::rotated_anchor_frac(angle, dir)
2879 } else {
2880 0.5
2881 };
2882 let size = built.theme.axis_text_x.size;
2883 const EDGE: f64 = 1.0;
2884 for _ in 0..2 {
2885 let layout = PlotLayout::compute_with_extras(
2886 w as f64,
2887 h as f64,
2888 &built.theme,
2889 has_title,
2890 has_subtitle,
2891 has_caption,
2892 has_legend,
2893 x_axis_top,
2894 y_label_width,
2895 x_label_height,
2896 extras,
2897 );
2898 let area = &layout.plot_area;
2899 let edge_y = area.y + area.height;
2900 let to_point = |pos: f64| if flipped { (0.0, pos) } else { (pos, 0.0) };
2901 let px_first = built.coord.transform(to_point(first.0), area).0;
2902 let px_last = built.coord.transform(to_point(last.0), area).0;
2903 let bbox_first = crate::render::text::label_bbox(
2904 &first.1, size, px_first, edge_y, angle, anchor,
2905 );
2906 let bbox_last = crate::render::text::label_bbox(
2907 &last.1, size, px_last, edge_y, angle, anchor,
2908 );
2909 let deficit_left = (EDGE - bbox_first[0]).max(0.0);
2910 let deficit_right = (bbox_last[2] - (w as f64 - EDGE)).max(0.0);
2911 if deficit_left <= 0.0 && deficit_right <= 0.0 {
2912 break;
2913 }
2914 let (p_first, p_last) = (first.0, last.0);
2923 if deficit_left > 0.0 && deficit_right > 0.0 {
2924 let (a, b, c, d) = (1.0 - p_first, p_first, 1.0 - p_last, p_last);
2929 let det = a * d - b * c;
2930 if det.abs() > 0.01 {
2931 extras.left +=
2932 ((deficit_left * d + deficit_right * b) / det).max(0.0);
2933 extras.right +=
2934 ((deficit_right * a + deficit_left * c) / det).max(0.0);
2935 } else {
2936 extras.left += deficit_left / (1.0 - p_first).max(0.05);
2940 extras.right += deficit_right / p_last.max(0.05);
2941 }
2942 } else if deficit_left > 0.0 {
2943 extras.left += deficit_left / (1.0 - p_first).max(0.05);
2944 } else if deficit_right > 0.0 {
2945 extras.right += deficit_right / p_last.max(0.05);
2946 }
2947 const MIN_PANEL_WIDTH: f64 = 50.0;
2962 let cap = (w as f64 - MIN_PANEL_WIDTH).max(0.0);
2963 if extras.left + extras.right > cap {
2964 let scale = if extras.left + extras.right > 0.0 {
2965 cap / (extras.left + extras.right)
2966 } else {
2967 0.0
2968 };
2969 extras.left *= scale;
2970 extras.right *= scale;
2971 }
2972 }
2973 }
2974 }
2975 }
2976
2977 if has_legend
2984 && !matches!(
2985 built.theme.legend_position,
2986 crate::theme::LegendPosition::None
2987 )
2988 {
2989 let legend_aes =
2990 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
2991 extras.legend_max_key_size = legend_aes
2995 .iter()
2996 .filter_map(|aes| {
2997 let scale = built.scales.get(aes)?;
2998 let breaks = scale.breaks();
2999 Some(crate::guide::legend::size_key_diameter(
3000 aes,
3001 &built.scales,
3002 scale,
3003 &breaks,
3004 ))
3005 })
3006 .fold(0.0_f64, f64::max);
3007 let has_discrete_guide = legend_aes.iter().any(|a| {
3008 built
3009 .scales
3010 .get(a)
3011 .map(|s| s.is_discrete())
3012 .unwrap_or(false)
3013 });
3014 if has_discrete_guide {
3015 let layout0 = PlotLayout::compute_with_extras(
3016 w as f64,
3017 h as f64,
3018 &built.theme,
3019 has_title,
3020 has_subtitle,
3021 has_caption,
3022 has_legend,
3023 x_axis_top,
3024 y_label_width,
3025 x_label_height,
3026 extras,
3027 );
3028 let (legend_x0, _, mut is_horizontal) = crate::guide::legend::legend_position(
3038 &built.theme,
3039 &layout0.plot_area,
3040 extras.legend_extra_rows,
3041 (0.0, 0.0),
3042 &layout0.total,
3043 );
3044 if let Some(dir) = built.theme.legend_direction {
3045 is_horizontal = matches!(dir, crate::theme::LegendDirection::Horizontal);
3046 }
3047 let avail_width =
3048 (layout0.plot_area.x + layout0.plot_area.width - legend_x0).max(0.0);
3049 let mut legend_warnings = Vec::new();
3050 let mut plans = crate::guide::legend::plan_all_legends(
3051 &built.scales,
3052 &built.guide_legend,
3053 &built.theme,
3054 &built.suppressed_aes,
3055 avail_width,
3056 layout0.plot_area.height,
3057 is_horizontal,
3058 &mut legend_warnings,
3059 );
3060 built.warnings.extend(legend_warnings);
3061
3062 if !plans.is_empty() {
3063 extras.legend_extra_rows = if is_horizontal {
3069 plans
3070 .values()
3071 .map(|p| p.rows.saturating_sub(1))
3072 .max()
3073 .unwrap_or(0)
3074 } else {
3075 plans.values().map(|p| p.rows.saturating_sub(1)).sum()
3076 };
3077 extras.legend_extra_width = plans
3084 .values()
3085 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3086 .fold(0.0_f64, f64::max);
3087
3088 if !is_horizontal {
3100 let final_layout = PlotLayout::compute_with_extras(
3101 w as f64,
3102 h as f64,
3103 &built.theme,
3104 has_title,
3105 has_subtitle,
3106 has_caption,
3107 has_legend,
3108 x_axis_top,
3109 y_label_width,
3110 x_label_height,
3111 extras,
3112 );
3113 let (legend_x_final, _, _) = crate::guide::legend::legend_position(
3119 &built.theme,
3120 &final_layout.plot_area,
3121 extras.legend_extra_rows,
3122 (0.0, 0.0),
3123 &final_layout.total,
3124 );
3125 let swatch_and_gap =
3126 built.theme.legend_key_width + built.theme.legend_spacing;
3127 let text_size = built.theme.legend_text.size;
3128 fn right_edge_of(
3134 legend_x: f64,
3135 swatch_and_gap: f64,
3136 text_size: f64,
3137 plan: &crate::guide::legend::LegendPlan,
3138 breaks: &[(f64, String)],
3139 ) -> f64 {
3140 plan.entries
3141 .iter()
3142 .filter_map(|e| breaks.get(e.break_idx).map(|(_, l)| (e, l)))
3143 .map(|(e, label)| {
3144 let text =
3145 e.label_override.as_deref().unwrap_or(label.as_str());
3146 legend_x
3147 + e.x_offset
3148 + swatch_and_gap
3149 + crate::render::text::text_width(text, text_size)
3150 })
3151 .fold(0.0_f64, f64::max)
3152 }
3153
3154 let mut updates: Vec<(
3155 crate::aes::Aesthetic,
3156 crate::guide::legend::LegendPlan,
3157 )> = Vec::new();
3158 let mut reduced_aes: Vec<crate::aes::Aesthetic> = Vec::new();
3165 for (aes, plan) in &plans {
3166 if plan.cols <= 1 {
3167 continue;
3168 }
3169 let Some(scale) = built.scales.get(aes) else {
3170 continue;
3171 };
3172 let breaks = scale.breaks();
3173 if right_edge_of(
3174 legend_x_final,
3175 swatch_and_gap,
3176 text_size,
3177 plan,
3178 &breaks,
3179 ) <= w as f64
3180 {
3181 continue; }
3183
3184 let n = breaks.len();
3187 let mut reduced: Vec<crate::guide::legend::LegendPlan> = Vec::new();
3188 for c in (1..plan.cols).rev() {
3189 let mut candidate_guide = built.guide_legend.clone();
3190 candidate_guide.ncol = Some(c);
3191 candidate_guide.nrow = None;
3192 let mut tmp_warnings = Vec::new();
3193 let candidate_guide =
3194 candidate_guide.validated(n, &mut tmp_warnings);
3195 if let Some(cand) = crate::guide::legend::plan_legend(
3196 &breaks,
3197 &candidate_guide,
3198 &built.theme,
3199 avail_width,
3200 layout0.plot_area.height,
3201 false,
3202 ) {
3203 reduced.push(cand);
3204 }
3205 }
3206 if let Some(fits) = reduced.iter().find(|c| {
3207 right_edge_of(legend_x_final, swatch_and_gap, text_size, c, &breaks)
3208 <= w as f64
3209 }) {
3210 extras.legend_extra_rows =
3211 extras.legend_extra_rows.max(fits.rows.saturating_sub(1));
3212 updates.push((aes.clone(), fits.clone()));
3213 reduced_aes.push(aes.clone());
3214 continue;
3215 }
3216
3217 let base = reduced.last().cloned().unwrap_or_else(|| plan.clone());
3222 extras.legend_extra_rows =
3223 extras.legend_extra_rows.max(base.rows.saturating_sub(1));
3224 let mut truncated = base.clone();
3225 for e in &mut truncated.entries {
3226 let Some((_, label)) = breaks.get(e.break_idx) else {
3227 continue;
3228 };
3229 let avail = (w as f64
3230 - (legend_x_final + e.x_offset + swatch_and_gap))
3231 .max(0.0);
3232 if let Some(short) =
3233 crate::render::text::truncate_to_width(label, avail, text_size)
3234 {
3235 e.label_override = Some(short);
3236 }
3237 }
3238
3239 let truncated_edge = right_edge_of(
3243 legend_x_final,
3244 swatch_and_gap,
3245 text_size,
3246 &truncated,
3247 &breaks,
3248 );
3249 if truncated_edge > w as f64 {
3250 built.warnings.push(format!(
3251 "legend: the {}-column grid still doesn't fit the {w}px chart (needs about {} px)",
3252 truncated.cols,
3253 truncated_edge.ceil() as i64
3254 ));
3255 }
3256 updates.push((aes.clone(), truncated));
3257 }
3258 for (aes, new_plan) in &updates {
3259 plans.insert(aes.clone(), new_plan.clone());
3260 }
3261
3262 if !reduced_aes.is_empty() {
3303 let original_reserve = extras.legend_extra_width;
3304 let tighter_reserve = plans
3305 .values()
3306 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3307 .fold(0.0_f64, f64::max);
3308 if tighter_reserve < original_reserve {
3309 let mut retry_extras = extras;
3310 retry_extras.legend_extra_width = tighter_reserve;
3311 let retry_layout = PlotLayout::compute_with_extras(
3312 w as f64,
3313 h as f64,
3314 &built.theme,
3315 has_title,
3316 has_subtitle,
3317 has_caption,
3318 has_legend,
3319 x_axis_top,
3320 y_label_width,
3321 x_label_height,
3322 retry_extras,
3323 );
3324 let (retry_x, _, _) = crate::guide::legend::legend_position(
3330 &built.theme,
3331 &retry_layout.plot_area,
3332 retry_extras.legend_extra_rows,
3333 (0.0, 0.0),
3334 &retry_layout.total,
3335 );
3336 let all_still_fit = plans.iter().all(|(aes, plan)| {
3337 let Some(scale) = built.scales.get(aes) else {
3338 return true;
3339 };
3340 let breaks = scale.breaks();
3341 right_edge_of(retry_x, swatch_and_gap, text_size, plan, &breaks)
3342 <= w as f64
3343 });
3344 if all_still_fit {
3345 extras.legend_extra_width = tighter_reserve;
3346 }
3347 }
3348 }
3349 }
3350 for (aes, plan) in &plans {
3351 if let Some(ov) = &plan.overflow {
3352 let n = built
3353 .scales
3354 .get(aes)
3355 .map(|s| s.breaks().len())
3356 .unwrap_or(ov.dropped);
3357 let shown = n.saturating_sub(ov.dropped);
3358 built.warnings.push(format!(
3359 "legend: {} of {n} entries don't fit the chart; the legend shows {shown} and \"+{} more\"",
3360 ov.dropped, ov.dropped
3361 ));
3362 }
3363 }
3364 }
3365 built.legend_plans = plans;
3371 }
3372 }
3373
3374 if has_legend
3385 && !matches!(
3386 built.theme.legend_position,
3387 crate::theme::LegendPosition::None
3388 )
3389 {
3390 let is_horizontal = match built.theme.legend_direction {
3391 Some(dir) => matches!(dir, crate::theme::LegendDirection::Horizontal),
3392 None => matches!(
3393 built.theme.legend_position,
3394 crate::theme::LegendPosition::Top | crate::theme::LegendPosition::Bottom
3395 ),
3396 };
3397 if is_horizontal {
3398 let legend_aes =
3399 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3400 let continuous_colourbar = legend_aes.iter().find_map(|aes| {
3401 if !matches!(
3402 aes,
3403 crate::aes::Aesthetic::Color | crate::aes::Aesthetic::Fill
3404 ) {
3405 return None;
3406 }
3407 let scale = built.scales.get(aes)?;
3408 if scale.is_discrete() {
3409 return None;
3410 }
3411 Some(scale)
3412 });
3413 if let Some(scale) = continuous_colourbar {
3414 let title = built
3415 .guide_legend
3416 .title
3417 .as_deref()
3418 .unwrap_or_else(|| scale.name());
3419 let bar_height = crate::guide::legend::continuous_horizontal_bar_height(
3420 &built.theme,
3421 !title.is_empty(),
3422 );
3423 let key_h = built
3424 .theme
3425 .legend_key_height
3426 .max(extras.legend_max_key_size);
3427 extras.continuous_horizontal_legend_extra_height =
3428 (bar_height - key_h).max(0.0);
3429 }
3430 }
3431 }
3432
3433 PlotLayout::compute_with_extras(
3434 w as f64,
3435 h as f64,
3436 &built.theme,
3437 has_title,
3438 has_subtitle,
3439 has_caption,
3440 has_legend,
3441 x_axis_top,
3442 y_label_width,
3443 x_label_height,
3444 extras,
3445 )
3446 }
3447
3448 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3451 fn render_into<DB>(
3452 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
3453 built: &crate::build::BuiltPlot,
3454 layout: &PlotLayout,
3455 ) -> Result<(), GGError>
3456 where
3457 DB: plotters::prelude::DrawingBackend,
3458 DB::ErrorType: 'static,
3459 {
3460 area.fill(&plotters::prelude::WHITE)
3461 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3462 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
3463 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
3464 area.present()
3465 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3466 Ok(())
3467 }
3468
3469 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3472 pub fn ggsave(
3473 self,
3474 path: &str,
3475 width_inches: f64,
3476 height_inches: f64,
3477 dpi: f64,
3478 ) -> Result<(), GGError> {
3479 let w = (width_inches * dpi) as u32;
3480 let h = (height_inches * dpi) as u32;
3481 self.save_with_size(path, w, h)
3482 }
3483
3484 fn has_legend_mapping(&self) -> bool {
3499 use crate::guide::legend::is_legend_aesthetic;
3504 let plot_level = self
3505 .mapping
3506 .mappings
3507 .iter()
3508 .any(|m| is_legend_aesthetic(&m.aesthetic));
3509 plot_level
3510 || self.layers.iter().any(|layer| {
3511 if layer.show_legend == Some(false) {
3512 return false;
3513 }
3514 let effective = if layer.geom.inherit_aes() {
3515 self.mapping.merge(&layer.mapping)
3516 } else {
3517 layer.mapping.clone()
3518 };
3519 effective
3520 .mappings
3521 .iter()
3522 .any(|m| is_legend_aesthetic(&m.aesthetic))
3523 })
3524 }
3525}
3526
3527#[derive(Clone, Copy, Debug)]
3529pub(crate) struct RenderMeta {
3530 pub(crate) has_title: bool,
3531 pub(crate) has_subtitle: bool,
3532 pub(crate) has_caption: bool,
3533 pub(crate) has_legend: bool,
3534}
3535
3536#[derive(Debug)]
3538#[non_exhaustive]
3539pub enum GGError {
3540 Render(RenderError),
3541 UnsupportedFormat(String),
3542 Io(std::io::Error),
3543 ValidationError(String),
3544}
3545
3546impl std::fmt::Display for GGError {
3547 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3548 match self {
3549 GGError::Render(e) => write!(f, "Render error: {e}"),
3550 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
3551 GGError::Io(e) => write!(f, "IO error: {e}"),
3552 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
3553 }
3554 }
3555}
3556
3557impl std::error::Error for GGError {}