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) legend_bottom_share: Option<f64>,
113 pub(crate) default_aspect_ratio: Option<f64>,
116 pub(crate) locale: Option<crate::locale::Locale>,
118 pub(crate) svg_root_data: bool,
123 pub(crate) root_attrs: Vec<(String, String)>,
126 pub(crate) svg_warnings_attr: bool,
130 pub(crate) svg_formatted_values: bool,
134 pub(crate) layer_svg_attrs: Vec<(usize, String, String)>,
138 pub(crate) svg_title: Option<String>,
143 pub(crate) svg_desc: Option<String>,
148}
149
150impl GGPlot {
151 pub fn new(data: impl GGData) -> Self {
158 GGPlot {
159 data: data.into_dataframe(),
160 mapping: Aes::default(),
161 layers: Vec::new(),
162 scales: Vec::new(),
163 coord: Box::new(CoordCartesian::new()),
164 theme: Theme::default(),
165 labels: Labels::default(),
166 facet: Facet::default(),
167 annotations: Vec::new(),
168 guide_legend: crate::guide::config::GuideLegend::default(),
169 warnings: Vec::new(),
170 legend_bottom_share: None,
171 default_aspect_ratio: None,
172 locale: None,
173 svg_root_data: true,
174 root_attrs: Vec::new(),
175 svg_warnings_attr: false,
176 svg_formatted_values: false,
177 layer_svg_attrs: Vec::new(),
178 svg_title: None,
179 svg_desc: None,
180 }
181 }
182
183 pub fn aes(mut self, mapping: Aes) -> Self {
185 self.warnings.extend(mapping.warnings.iter().cloned());
186 self.mapping = mapping;
187 self
188 }
189
190 pub fn geom_point(self) -> Self {
193 self.add_geom(GeomPoint::default())
194 }
195
196 pub fn geom_point_with(self, geom: GeomPoint) -> Self {
197 self.add_geom_with(geom)
198 }
199
200 pub fn geom_line(self) -> Self {
201 self.add_geom(GeomLine::default())
202 }
203
204 pub fn geom_line_with(self, geom: GeomLine) -> Self {
205 self.add_geom_with(geom)
206 }
207
208 pub fn geom_bar(self) -> Self {
209 self.add_geom(GeomBar::default())
210 }
211
212 pub fn geom_bar_with(self, geom: GeomBar) -> Self {
213 self.add_geom_with(geom)
214 }
215
216 pub fn geom_histogram(self) -> Self {
217 self.add_geom(GeomHistogram::default())
218 }
219
220 pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self {
221 self.add_geom_with(geom)
222 }
223
224 pub fn geom_boxplot(self) -> Self {
225 self.add_geom(GeomBoxplot::default())
226 }
227
228 pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self {
229 self.add_geom_with(geom)
230 }
231
232 pub fn geom_smooth(self) -> Self {
233 self.add_geom(GeomSmooth::default())
234 }
235
236 pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self {
237 self.add_geom_with(geom)
238 }
239
240 pub fn geom_col(self) -> Self {
241 self.add_geom(GeomCol::default())
242 }
243
244 pub fn geom_col_with(self, geom: GeomCol) -> Self {
245 self.add_geom_with(geom)
246 }
247
248 pub fn geom_hline(self, yintercept: f64) -> Self {
252 self.add_refline(
253 GeomHline::new(yintercept),
254 false,
255 &[("yintercept", yintercept)],
256 )
257 }
258
259 pub fn geom_hline_with(self, geom: GeomHline) -> Self {
261 let y = geom.yintercept;
262 self.add_refline(geom, true, &[("yintercept", y)])
263 }
264
265 pub fn geom_hline_aes(self, mapping: Aes) -> Self {
289 self.add_geom(GeomHline::mapped()).layer_aes(mapping)
290 }
291
292 pub fn geom_hline_aes_with(self, geom: GeomHline, mapping: Aes) -> Self {
294 self.add_geom_with(geom).layer_aes(mapping)
295 }
296
297 pub fn geom_vline(self, xintercept: f64) -> Self {
300 self.add_refline(
301 GeomVline::new(xintercept),
302 false,
303 &[("xintercept", xintercept)],
304 )
305 }
306
307 pub fn geom_vline_with(self, geom: GeomVline) -> Self {
309 let x = geom.xintercept;
310 self.add_refline(geom, true, &[("xintercept", x)])
311 }
312
313 pub fn geom_vline_aes(self, mapping: Aes) -> Self {
316 self.add_geom(GeomVline::mapped()).layer_aes(mapping)
317 }
318
319 pub fn geom_vline_aes_with(self, geom: GeomVline, mapping: Aes) -> Self {
321 self.add_geom_with(geom).layer_aes(mapping)
322 }
323
324 pub fn geom_abline(self, slope: f64, intercept: f64) -> Self {
327 self.add_refline(
328 GeomAbline::new(slope, intercept),
329 false,
330 &[("slope", slope), ("intercept", intercept)],
331 )
332 }
333
334 pub fn geom_abline_with(self, geom: GeomAbline) -> Self {
336 let (b, a) = (geom.slope, geom.intercept);
337 self.add_refline(geom, true, &[("slope", b), ("intercept", a)])
338 }
339
340 pub fn geom_abline_aes(self, mapping: Aes) -> Self {
343 self.add_geom(GeomAbline::mapped()).layer_aes(mapping)
344 }
345
346 pub fn geom_abline_aes_with(self, geom: GeomAbline, mapping: Aes) -> Self {
348 self.add_geom_with(geom).layer_aes(mapping)
349 }
350
351 fn add_refline(
354 self,
355 geom: impl Geom + 'static,
356 explicit: bool,
357 values: &[(&str, f64)],
358 ) -> Self {
359 let mut data = DataFrame::new();
360 let mut mapping = Aes::new();
361 for (col, v) in values {
362 data.add_column(col.to_string(), vec![Value::Float(*v)]);
363 mapping = match *col {
364 "xintercept" => mapping.xintercept(col),
365 "yintercept" => mapping.yintercept(col),
366 "slope" => mapping.slope(col),
367 _ => mapping.intercept(col),
368 };
369 }
370 let plot = if explicit {
371 self.add_geom_with(geom)
372 } else {
373 self.add_geom(geom)
374 };
375 plot.layer_aes(mapping).layer_data(data)
376 }
377
378 pub fn geom_text(self) -> Self {
379 self.add_geom(GeomText::default())
380 }
381
382 pub fn geom_text_with(self, geom: GeomText) -> Self {
383 self.add_geom_with(geom)
384 }
385
386 #[cfg(feature = "ggpubr")]
391 pub fn stat_cor(self) -> Self {
392 self.geom_text().stat(crate::stat::cor::StatCor::default())
393 }
394
395 #[cfg(feature = "ggpubr")]
398 pub fn stat_cor_with(self, stat: crate::stat::cor::StatCor) -> Self {
399 self.geom_text().stat(stat)
400 }
401
402 #[cfg(feature = "ggpubr")]
407 pub fn stat_compare_means(self) -> Self {
408 self.geom_text()
409 .stat(crate::stat::compare_means::StatCompareMeans::default())
410 }
411
412 #[cfg(feature = "ggpubr")]
415 pub fn stat_compare_means_with(
416 self,
417 stat: crate::stat::compare_means::StatCompareMeans,
418 ) -> Self {
419 self.geom_text().stat(stat)
420 }
421
422 #[cfg(feature = "ggpubr")]
428 pub fn stat_compare_means_pairwise(self, comparisons: &[(&str, &str)]) -> Self {
429 self.stat_compare_means_pairwise_with(
430 crate::stat::compare_means::CompareMethod::Auto,
431 comparisons,
432 )
433 }
434
435 #[cfg(feature = "ggpubr")]
438 pub fn stat_compare_means_pairwise_with(
439 self,
440 method: crate::stat::compare_means::CompareMethod,
441 comparisons: &[(&str, &str)],
442 ) -> Self {
443 let col_for = |a: crate::aes::Aesthetic| {
445 self.mapping
446 .mappings
447 .iter()
448 .find(|m| m.aesthetic == a)
449 .map(|m| m.column.clone())
450 };
451 let (xcol, ycol) = match (
452 col_for(crate::aes::Aesthetic::X),
453 col_for(crate::aes::Aesthetic::Y),
454 ) {
455 (Some(x), Some(y)) => (x, y),
456 _ => return self,
457 };
458 let (xc, yc) = match (self.data.column(&xcol), self.data.column(&ycol)) {
459 (Some(x), Some(y)) => (x, y),
460 _ => return self,
461 };
462
463 let mut groups: Vec<(String, Vec<f64>)> = Vec::new();
465 let mut ymax = f64::NEG_INFINITY;
466 let mut ymin = f64::INFINITY;
467 for (xv, yv) in xc.iter().zip(yc.iter()) {
468 let y = match yv.as_f64() {
469 Some(y) if y.is_finite() => y,
470 _ => continue,
471 };
472 ymax = ymax.max(y);
473 ymin = ymin.min(y);
474 let key = xv.to_group_key();
475 if let Some(g) = groups.iter_mut().find(|(k, _)| *k == key) {
476 g.1.push(y);
477 } else {
478 groups.push((key, vec![y]));
479 }
480 }
481 if groups.len() < 2 {
482 return self;
483 }
484 let step = if ymax > ymin {
485 (ymax - ymin) * 0.12
486 } else {
487 1.0
488 };
489
490 let (mut xmin_v, mut xmax_v, mut y_v, mut label_v) =
492 (Vec::new(), Vec::new(), Vec::new(), Vec::new());
493 let mut placed = 0usize;
494 for (a, b) in comparisons {
495 let ga = groups.iter().find(|(k, _)| k == a).map(|g| &g.1);
496 let gb = groups.iter().find(|(k, _)| k == b).map(|g| &g.1);
497 let (ga, gb) = match (ga, gb) {
498 (Some(x), Some(y)) if x.len() >= 2 && y.len() >= 2 => (x, y),
499 _ => continue,
500 };
501 let p = match crate::stat::compare_means::pairwise_p(method, ga, gb) {
502 Some(p) => p,
503 None => continue,
504 };
505 xmin_v.push(Value::Str((*a).to_string()));
506 xmax_v.push(Value::Str((*b).to_string()));
507 y_v.push(Value::Float(ymax + step * (placed as f64 + 1.0)));
508 label_v.push(Value::Str(crate::stat::cor::format_p_value(p)));
509 placed += 1;
510 }
511 if xmin_v.is_empty() {
512 return self;
513 }
514
515 let data = vec![
516 ("xmin".to_string(), xmin_v),
517 ("xmax".to_string(), xmax_v),
518 ("y".to_string(), y_v),
519 ("label".to_string(), label_v),
520 ];
521 self.add_geom(crate::geom::bracket::GeomBracket::default())
522 .layer_data(data)
523 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
524 }
525
526 pub fn geom_bracket(self, xmin: &str, xmax: &str, y: f64, label: &str) -> Self {
531 self.geom_bracket_many(
532 crate::geom::bracket::GeomBracket::default(),
533 &[(xmin, xmax, y, label)],
534 )
535 }
536
537 pub fn geom_bracket_many(
541 self,
542 geom: crate::geom::bracket::GeomBracket,
543 brackets: &[(&str, &str, f64, &str)],
544 ) -> Self {
545 let xmin = brackets
546 .iter()
547 .map(|b| Value::Str(b.0.to_string()))
548 .collect::<Vec<_>>();
549 let xmax = brackets
550 .iter()
551 .map(|b| Value::Str(b.1.to_string()))
552 .collect::<Vec<_>>();
553 let y = brackets
554 .iter()
555 .map(|b| Value::Float(b.2))
556 .collect::<Vec<_>>();
557 let label = brackets
558 .iter()
559 .map(|b| Value::Str(b.3.to_string()))
560 .collect::<Vec<_>>();
561 let data = vec![
562 ("xmin".to_string(), xmin),
563 ("xmax".to_string(), xmax),
564 ("y".to_string(), y),
565 ("label".to_string(), label),
566 ];
567 self.add_geom(geom)
568 .layer_data(data)
569 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
570 }
571
572 pub fn geom_bracket_table(
581 mut self,
582 table: impl GGData,
583 spec: crate::geom::bracket::BracketTable,
584 ) -> Self {
585 let table = table.into_dataframe();
586 let col_for = |a: crate::aes::Aesthetic| {
587 self.mapping
588 .mappings
589 .iter()
590 .find(|m| m.aesthetic == a)
591 .map(|m| m.column.clone())
592 };
593 let x_levels: Vec<String> = col_for(crate::aes::Aesthetic::X)
596 .and_then(|c| self.data.column(&c))
597 .filter(|col| col.iter().any(|v| matches!(v, Value::Str(_))))
598 .map(|col| {
599 let mut seen = std::collections::HashSet::new();
600 col.iter()
601 .filter(|v| !v.is_na())
602 .map(|v| v.to_group_key())
603 .filter(|k| seen.insert(k.clone()))
604 .collect()
605 })
606 .unwrap_or_default();
607 let y_range = col_for(crate::aes::Aesthetic::Y)
608 .and_then(|c| self.data.column(&c))
609 .and_then(|col| {
610 let (lo, hi) = col
611 .iter()
612 .filter_map(|v| v.as_f64())
613 .filter(|v| v.is_finite())
614 .fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), v| {
615 (lo.min(v), hi.max(v))
616 });
617 (lo <= hi).then_some((lo, hi))
618 });
619 let mut warnings = Vec::new();
620 let resolved = spec.resolve(&table, &x_levels, y_range, &mut warnings);
621 self.warnings.extend(warnings);
622 match resolved {
623 Some(data) => self
624 .add_geom(crate::geom::bracket::GeomBracket::from(spec.geom))
625 .layer_data(data)
626 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label")),
627 None => self,
628 }
629 }
630
631 pub fn geom_text_repel(self) -> Self {
636 self.add_geom(crate::geom::repel::GeomTextRepel::default())
637 }
638
639 pub fn geom_text_repel_with(self, geom: crate::geom::repel::GeomTextRepel) -> Self {
642 self.add_geom_with(geom)
643 }
644
645 pub fn geom_label_repel(self) -> Self {
648 self.add_geom(crate::geom::repel::GeomLabelRepel::default())
649 }
650
651 pub fn geom_label_repel_with(self, geom: crate::geom::repel::GeomLabelRepel) -> Self {
653 self.add_geom_with(geom)
654 }
655
656 pub fn geom_label(self) -> Self {
657 self.add_geom(GeomLabel::default())
658 }
659
660 pub fn geom_label_with(self, geom: GeomLabel) -> Self {
661 self.add_geom_with(geom)
662 }
663
664 pub fn geom_area(self) -> Self {
665 self.add_geom(GeomArea::default())
666 }
667
668 pub fn geom_area_with(self, geom: GeomArea) -> Self {
669 self.add_geom_with(geom)
670 }
671
672 pub fn geom_ribbon(self) -> Self {
673 self.add_geom(GeomRibbon::default())
674 }
675
676 pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self {
677 self.add_geom_with(geom)
678 }
679
680 pub fn geom_errorbar(self) -> Self {
681 self.add_geom(GeomErrorbar::default())
682 }
683
684 pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self {
685 self.add_geom_with(geom)
686 }
687
688 pub fn geom_segment(self) -> Self {
689 self.add_geom(GeomSegment::default())
690 }
691
692 pub fn geom_segment_with(self, geom: GeomSegment) -> Self {
693 self.add_geom_with(geom)
694 }
695
696 pub fn geom_density(self) -> Self {
697 self.add_geom(GeomDensity::default())
698 }
699
700 pub fn geom_density_with(self, geom: GeomDensity) -> Self {
701 self.add_geom_with(geom)
702 }
703
704 pub fn geom_rug(self) -> Self {
705 self.add_geom(GeomRug::default())
706 }
707
708 pub fn geom_rug_with(self, geom: GeomRug) -> Self {
709 self.add_geom_with(geom)
710 }
711
712 pub fn geom_jitter(self) -> Self {
713 self.add_geom(GeomJitter::default())
714 }
715
716 pub fn geom_jitter_with(self, geom: GeomJitter) -> Self {
717 self.add_geom_with(geom)
718 }
719
720 pub fn geom_path(self) -> Self {
721 self.add_geom(GeomPath::default())
722 }
723
724 pub fn geom_path_with(self, geom: GeomPath) -> Self {
725 self.add_geom_with(geom)
726 }
727
728 pub fn stat_ellipse(self) -> Self {
730 self.geom_path()
731 .stat(crate::stat::ellipse::StatEllipse::default())
732 }
733
734 pub fn stat_ellipse_level(self, level: f64) -> Self {
736 self.geom_path()
737 .stat(crate::stat::ellipse::StatEllipse::new(level))
738 }
739
740 #[cfg(feature = "regression")]
743 pub fn stat_quantile(mut self, taus: &[f64]) -> Self {
744 for &tau in taus {
745 self = self
746 .geom_path()
747 .stat(crate::stat::quantile::StatQuantile::new(tau));
748 }
749 self
750 }
751
752 #[cfg(feature = "regression")]
754 pub fn geom_quantile(self) -> Self {
755 self.stat_quantile(&[0.25, 0.5, 0.75])
756 }
757
758 pub fn geom_step(self) -> Self {
759 self.add_geom(GeomStep::default())
760 }
761
762 pub fn geom_step_with(self, geom: GeomStep) -> Self {
763 self.add_geom_with(geom)
764 }
765
766 pub fn geom_freqpoly(self) -> Self {
767 self.add_geom(GeomFreqpoly::default())
768 }
769
770 pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self {
771 self.add_geom_with(geom)
772 }
773
774 pub fn geom_linerange(self) -> Self {
775 self.add_geom(GeomLinerange::default())
776 }
777
778 pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self {
779 self.add_geom_with(geom)
780 }
781
782 pub fn geom_pointrange(self) -> Self {
783 self.add_geom(GeomPointrange::default())
784 }
785
786 pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self {
787 self.add_geom_with(geom)
788 }
789
790 pub fn geom_crossbar(self) -> Self {
791 self.add_geom(GeomCrossbar::default())
792 }
793
794 pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self {
795 self.add_geom_with(geom)
796 }
797
798 pub fn geom_spoke(self) -> Self {
799 self.add_geom(GeomSpoke::default())
800 }
801
802 pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self {
803 self.add_geom_with(geom)
804 }
805
806 pub fn geom_rect(self) -> Self {
807 self.add_geom(GeomRect::default())
808 }
809
810 pub fn geom_rect_with(self, geom: GeomRect) -> Self {
811 self.add_geom_with(geom)
812 }
813
814 pub fn geom_tile(self) -> Self {
815 self.add_geom(GeomTile::default())
816 }
817
818 pub fn geom_tile_with(self, geom: GeomTile) -> Self {
819 self.add_geom_with(geom)
820 }
821
822 pub fn geom_raster(self) -> Self {
824 self.add_geom(crate::geom::raster::GeomRaster::default())
825 }
826
827 pub fn geom_raster_with(self, geom: crate::geom::raster::GeomRaster) -> Self {
828 self.add_geom_with(geom)
829 }
830
831 pub fn geom_polygon(self) -> Self {
832 self.add_geom(GeomPolygon::default())
833 }
834
835 pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self {
836 self.add_geom_with(geom)
837 }
838
839 #[cfg(feature = "sf")]
841 pub fn geom_sf(self) -> Self {
842 self.add_geom(crate::geom::sf::GeomSf::default())
843 }
844
845 #[cfg(feature = "sf")]
846 pub fn geom_sf_with(self, geom: crate::geom::sf::GeomSf) -> Self {
847 self.add_geom_with(geom)
848 }
849
850 #[cfg(feature = "sf")]
853 pub fn geom_sf_text(self) -> Self {
854 self.add_geom(crate::geom::sf_text::GeomSfText::default())
855 }
856
857 #[cfg(feature = "sf")]
858 pub fn geom_sf_text_with(self, geom: crate::geom::sf_text::GeomSfText) -> Self {
859 self.add_geom_with(geom)
860 }
861
862 #[cfg(feature = "sf")]
865 pub fn geom_sf_label(self) -> Self {
866 self.add_geom(crate::geom::sf_text::GeomSfLabel::default())
867 }
868
869 #[cfg(feature = "sf")]
870 pub fn geom_sf_label_with(self, geom: crate::geom::sf_text::GeomSfLabel) -> Self {
871 self.add_geom_with(geom)
872 }
873
874 #[cfg(feature = "sf")]
878 pub fn annotation_scale(self) -> Self {
879 self.add_geom(crate::geom::annotation_scale::GeomAnnotationScale::default())
880 }
881
882 #[cfg(feature = "sf")]
883 pub fn annotation_scale_with(
884 self,
885 geom: crate::geom::annotation_scale::GeomAnnotationScale,
886 ) -> Self {
887 self.add_geom_with(geom)
888 }
889
890 #[cfg(feature = "sf")]
893 pub fn annotation_north_arrow_with(
894 self,
895 geom: crate::geom::annotation_scale::GeomNorthArrow,
896 ) -> Self {
897 self.add_geom_with(geom)
898 }
899
900 #[cfg(feature = "sf")]
903 pub fn annotation_attribution(self, text: impl Into<String>) -> Self {
904 self.add_geom(crate::geom::annotation_scale::GeomAttribution::new(text))
905 }
906
907 #[cfg(feature = "sf")]
908 pub fn annotation_attribution_with(
909 self,
910 geom: crate::geom::annotation_scale::GeomAttribution,
911 ) -> Self {
912 self.add_geom_with(geom)
913 }
914
915 #[cfg(feature = "sf")]
923 pub fn bivariate_legend(
924 self,
925 geom: crate::geom::bivariate_legend::GeomBivariateLegend,
926 ) -> Self {
927 self.add_geom(geom)
928 }
929
930 #[cfg(feature = "sf")]
935 pub fn bivariate_legend_counts(
936 self,
937 geom: crate::geom::bivariate_legend::GeomBivariateLegendCounts,
938 ) -> Self {
939 self.add_geom(geom)
940 }
941
942 pub fn geom_curve(self) -> Self {
943 self.add_geom(GeomCurve::default())
944 }
945
946 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
947 self.add_geom_with(geom)
948 }
949
950 pub fn geom_violin(self) -> Self {
951 self.add_geom(GeomViolin::default())
952 }
953
954 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
955 self.add_geom_with(geom)
956 }
957
958 pub fn geom_dotplot(self) -> Self {
959 self.add_geom(GeomDotplot::default())
960 }
961
962 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
963 self.add_geom_with(geom)
964 }
965
966 pub fn geom_qq(self) -> Self {
967 self.add_geom(GeomQQ::default())
968 }
969
970 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
971 self.add_geom_with(geom)
972 }
973
974 pub fn geom_qq_line(self) -> Self {
975 self.add_geom(GeomQQLine::default())
976 }
977
978 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
979 self.add_geom_with(geom)
980 }
981
982 pub fn geom_bin2d(self) -> Self {
983 self.add_geom(GeomBin2d::default())
984 }
985
986 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
987 self.add_geom_with(geom)
988 }
989
990 pub fn geom_hex(self) -> Self {
991 self.add_geom(GeomHex::default())
992 }
993
994 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
995 self.add_geom_with(geom)
996 }
997
998 pub fn geom_count(self) -> Self {
999 self.add_geom(GeomCount::default())
1000 }
1001
1002 pub fn geom_count_with(self, geom: GeomCount) -> Self {
1003 self.add_geom_with(geom)
1004 }
1005
1006 pub fn geom_contour(self) -> Self {
1007 self.add_geom(GeomContour::default())
1008 }
1009
1010 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
1011 self.add_geom_with(geom)
1012 }
1013
1014 pub fn geom_contour_filled(self) -> Self {
1017 self.add_geom(GeomPolygon {
1018 line_width: 0.0,
1019 alpha: 1.0,
1020 ..GeomPolygon::default()
1021 })
1022 .stat(crate::stat::contour_filled::StatContourFilled::default())
1023 }
1024
1025 pub fn geom_density2d(self) -> Self {
1026 self.add_geom(GeomDensity2d::default())
1027 }
1028
1029 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
1030 self.add_geom_with(geom)
1031 }
1032
1033 pub fn geom_candlestick(self) -> Self {
1036 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
1037 }
1038
1039 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
1040 self.add_geom_with(geom)
1041 }
1042
1043 pub fn geom_ohlc(self) -> Self {
1046 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
1047 }
1048
1049 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
1050 self.add_geom_with(geom)
1051 }
1052
1053 pub fn geom_calendar(self) -> Self {
1064 self.geom_calendar_with(
1065 GeomTile {
1066 color: (255, 255, 255),
1067 line_width: 1.5,
1068 ..Default::default()
1069 },
1070 false,
1071 )
1072 }
1073
1074 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
1077 use crate::stat::calendar::{
1078 day_number, month_boundaries, month_breaks, month_breaks_localized, weekday_labels,
1079 CalendarGrid, StatCalendar, MAX_CALENDAR_YEARS,
1080 };
1081 let grid = self
1082 .mapping
1083 .get_mapping(&crate::aes::Aesthetic::X)
1084 .and_then(|c| self.data.column(c))
1085 .and_then(|col| {
1086 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
1087 });
1088 let Some((grid, clipped)) = grid else {
1089 return self.add_geom_with(tile).stat(StatCalendar {
1091 grid: None,
1092 monday_first,
1093 });
1094 };
1095 if clipped {
1096 self.warnings.push(format!(
1097 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
1098 ));
1099 }
1100 let (xb, _) = month_breaks(&grid);
1101 let yb = if monday_first {
1103 vec![6.0, 4.0, 2.0]
1104 } else {
1105 vec![5.0, 3.0, 1.0]
1106 };
1107 let segs = month_boundaries(&grid);
1108 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
1109 segs.iter().map(|s| Value::Float(f(s))).collect()
1110 };
1111 let boundaries = vec![
1112 ("x".to_string(), col(|s| s.0)),
1113 ("y".to_string(), col(|s| s.1)),
1114 ("xend".to_string(), col(|s| s.2)),
1115 ("yend".to_string(), col(|s| s.3)),
1116 ];
1117 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
1118 let plot = self
1119 .add_geom_with(tile)
1120 .stat(StatCalendar {
1121 grid: Some(grid),
1122 monday_first,
1123 })
1124 .scale_x_continuous(
1125 ScaleContinuous::new()
1126 .with_breaks(xb)
1127 .with_localized_labels(move |l| month_breaks_localized(&grid, l).1)
1129 .with_expand(0.0, 0.0),
1130 )
1131 .scale_y_continuous(
1132 ScaleContinuous::new()
1133 .with_breaks(yb)
1134 .with_localized_labels(weekday_labels)
1135 .with_expand(0.0, 0.0),
1136 );
1137 if segs.is_empty() {
1138 return plot;
1139 }
1140 plot.add_geom_with(crate::geom::segment::GeomCalendarBorder)
1142 .layer_data(boundaries)
1143 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1144 .show_legend(false)
1145 }
1146
1147 pub fn geom_blank(self) -> Self {
1148 self.add_geom(GeomBlank)
1149 }
1150
1151 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
1154 let mut plot = self.add_geom(geom);
1155 if let Some(layer) = plot.layers.last_mut() {
1156 layer.explicit_style = true;
1157 }
1158 plot
1159 }
1160
1161 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
1162 let stat = geom.default_stat();
1163 let position = geom.default_position();
1164 let params = geom.default_params();
1165 self.layers.push(Layer {
1166 data: None,
1167 mapping: Aes::default(),
1168 geom: Box::new(geom),
1169 stat,
1170 position,
1171 params,
1172 show_legend: None,
1173 explicit_style: false,
1174 tooltip_template: None,
1175 });
1176 self
1177 }
1178
1179 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
1183 if let Some(layer) = self.layers.last_mut() {
1184 layer.stat = Box::new(stat);
1185 }
1186 self
1187 }
1188
1189 pub fn position(mut self, pos: impl Position + 'static) -> Self {
1191 if let Some(layer) = self.layers.last_mut() {
1192 layer.position = Box::new(pos);
1193 }
1194 self
1195 }
1196
1197 pub fn layer_data(mut self, data: impl GGData) -> Self {
1199 if let Some(layer) = self.layers.last_mut() {
1200 layer.data = Some(data.into_dataframe());
1201 }
1202 self
1203 }
1204
1205 pub fn layer_aes(mut self, mapping: Aes) -> Self {
1207 self.warnings.extend(mapping.warnings.iter().cloned());
1208 if let Some(layer) = self.layers.last_mut() {
1209 layer.mapping = mapping;
1210 }
1211 self
1212 }
1213
1214 pub fn tooltip(mut self, template: &str) -> Self {
1225 if let Some(layer) = self.layers.last_mut() {
1226 layer.tooltip_template = Some(template.to_string());
1227 }
1228 self
1229 }
1230
1231 pub fn show_legend(mut self, show: bool) -> Self {
1234 if let Some(layer) = self.layers.last_mut() {
1235 layer.show_legend = Some(show);
1236 }
1237 self
1238 }
1239
1240 pub fn decorative(mut self) -> Self {
1267 if let Some(layer) = self.layers.last_mut() {
1268 let geom = std::mem::replace(&mut layer.geom, Box::new(GeomBlank));
1269 layer.geom = Box::new(crate::geom::decorative::Decorative(geom));
1270 if layer.show_legend.is_none() {
1271 layer.show_legend = Some(false);
1272 }
1273 }
1274 self
1275 }
1276
1277 pub fn layer_svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1311 let (name, value) = (name.into(), value.into());
1312 let Some(idx) = self.layers.len().checked_sub(1) else {
1313 self.warnings.push(format!(
1314 "layer_svg_attr(\"{name}\") called before any layer was added — ignored"
1315 ));
1316 return self;
1317 };
1318 if let Some((n, v)) =
1319 crate::render::svg_backend::validate_layer_svg_attr(&name, &value, &mut self.warnings)
1320 {
1321 self.layer_svg_attrs
1322 .retain(|(i, k, _)| !(*i == idx && *k == n));
1323 self.layer_svg_attrs.push((idx, n, v));
1324 }
1325 self
1326 }
1327
1328 pub fn layer_svg_attrs<N: Into<String>, V: Into<String>>(
1330 self,
1331 attrs: impl IntoIterator<Item = (N, V)>,
1332 ) -> Self {
1333 attrs
1334 .into_iter()
1335 .fold(self, |p, (n, v)| p.layer_svg_attr(n, v))
1336 }
1337
1338 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
1341 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1342 self.scales.push(Box::new(s));
1343 self
1344 }
1345
1346 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
1347 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1348 self.scales.push(Box::new(s));
1349 self
1350 }
1351
1352 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1353 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1354 self.scales.push(Box::new(s));
1355 self
1356 }
1357
1358 pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1359 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1360 self.scales.push(Box::new(s));
1361 self
1362 }
1363
1364 pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
1365 self.scales.push(Box::new(s));
1366 self
1367 }
1368
1369 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
1370 self.scales.push(Box::new(s));
1371 self
1372 }
1373
1374 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1375 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
1376 self.scale_color(s)
1377 }
1378
1379 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1380 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
1381 self.scale_fill(s)
1382 }
1383
1384 pub fn scale_colour_identity(self) -> Self {
1387 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Color);
1388 self.scale_color(s)
1389 }
1390
1391 pub fn scale_color_identity(self) -> Self {
1393 self.scale_colour_identity()
1394 }
1395
1396 pub fn scale_fill_identity(self) -> Self {
1399 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Fill);
1400 self.scale_fill(s)
1401 }
1402
1403 pub fn scale_color_viridis(self) -> Self {
1404 use crate::scale::color::ScaleColorDiscrete;
1405 use crate::scale::palettes::PaletteName;
1406 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
1407 .with_named_palette(&PaletteName::Viridis);
1408 self.scale_color(s)
1409 }
1410
1411 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1412 use crate::scale::color::ScaleColorDiscrete;
1413 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
1414 self.scale_color(s)
1415 }
1416
1417 pub fn scale_color_gradient(
1418 self,
1419 low: crate::scale::color::RGBAColor,
1420 high: crate::scale::color::RGBAColor,
1421 ) -> Self {
1422 use crate::scale::color::ScaleColorContinuous;
1423 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
1424 self.scale_color(s)
1425 }
1426
1427 pub fn scale_fill_gradient(
1428 self,
1429 low: crate::scale::color::RGBAColor,
1430 high: crate::scale::color::RGBAColor,
1431 ) -> Self {
1432 use crate::scale::color::ScaleColorContinuous;
1433 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
1434 self.scale_fill(s)
1435 }
1436
1437 pub fn scale_color_gradient2(
1438 self,
1439 low: crate::scale::color::RGBAColor,
1440 mid: crate::scale::color::RGBAColor,
1441 high: crate::scale::color::RGBAColor,
1442 ) -> Self {
1443 use crate::scale::gradient::ScaleColorGradient2;
1444 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
1445 self.scale_color(s)
1446 }
1447
1448 pub fn scale_fill_gradient2(
1449 self,
1450 low: crate::scale::color::RGBAColor,
1451 mid: crate::scale::color::RGBAColor,
1452 high: crate::scale::color::RGBAColor,
1453 ) -> Self {
1454 use crate::scale::gradient::ScaleColorGradient2;
1455 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
1456 self.scale_fill(s)
1457 }
1458
1459 pub fn scale_fill_viridis(self) -> Self {
1460 use crate::scale::color::ScaleColorDiscrete;
1461 use crate::scale::palettes::PaletteName;
1462 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
1463 .with_named_palette(&PaletteName::Viridis);
1464 self.scale_fill(s)
1465 }
1466
1467 pub fn scale_color_viridis_c(self) -> Self {
1469 use crate::scale::gradient_n::ScaleColorGradientN;
1470 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
1471 self.scale_color(s)
1472 }
1473
1474 pub fn scale_fill_viridis_c(self) -> Self {
1476 use crate::scale::gradient_n::ScaleColorGradientN;
1477 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
1478 self.scale_fill(s)
1479 }
1480
1481 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1483 use crate::scale::gradient_n::ScaleColorGradientN;
1484 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
1485 self.scale_color(s)
1486 }
1487
1488 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1490 use crate::scale::gradient_n::ScaleColorGradientN;
1491 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
1492 self.scale_fill(s)
1493 }
1494
1495 pub fn scale_color_steps(
1498 self,
1499 low: crate::scale::color::RGBAColor,
1500 high: crate::scale::color::RGBAColor,
1501 n_bins: usize,
1502 ) -> Self {
1503 let s = crate::scale::steps::ScaleColorSteps::two(
1504 crate::aes::Aesthetic::Color,
1505 (low.r, low.g, low.b),
1506 (high.r, high.g, high.b),
1507 n_bins,
1508 );
1509 self.scale_color(s)
1510 }
1511
1512 pub fn scale_color_stepsn(
1514 self,
1515 stops: Vec<crate::scale::color::RGBAColor>,
1516 n_bins: usize,
1517 ) -> Self {
1518 let s = crate::scale::steps::ScaleColorSteps::new(
1519 crate::aes::Aesthetic::Color,
1520 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
1521 n_bins,
1522 );
1523 self.scale_color(s)
1524 }
1525
1526 pub fn scale_color_fermenter(
1528 self,
1529 name: crate::scale::palettes::PaletteName,
1530 n_bins: usize,
1531 ) -> Self {
1532 let stops = crate::scale::palettes::palette(&name)
1533 .iter()
1534 .map(|c| (c.r, c.g, c.b))
1535 .collect();
1536 let s =
1537 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
1538 self.scale_color(s)
1539 }
1540
1541 pub fn scale_fill_steps(
1543 self,
1544 low: crate::scale::color::RGBAColor,
1545 high: crate::scale::color::RGBAColor,
1546 n_bins: usize,
1547 ) -> Self {
1548 let s = crate::scale::steps::ScaleColorSteps::two(
1549 crate::aes::Aesthetic::Fill,
1550 (low.r, low.g, low.b),
1551 (high.r, high.g, high.b),
1552 n_bins,
1553 );
1554 self.scale_fill(s)
1555 }
1556
1557 pub fn scale_fill_binned(
1568 self,
1569 breaks: Vec<f64>,
1570 low: crate::scale::color::RGBAColor,
1571 high: crate::scale::color::RGBAColor,
1572 ) -> Self {
1573 let s = crate::scale::binned::ScaleFillBinned::two(
1574 crate::aes::Aesthetic::Fill,
1575 breaks,
1576 (low.r, low.g, low.b),
1577 (high.r, high.g, high.b),
1578 );
1579 self.scale_fill(s)
1580 }
1581
1582 pub fn scale_fill_binned_with(
1586 self,
1587 breaks: Vec<f64>,
1588 colors: Vec<crate::scale::color::RGBAColor>,
1589 ) -> Self {
1590 let s = crate::scale::binned::ScaleFillBinned::new(
1591 crate::aes::Aesthetic::Fill,
1592 breaks,
1593 colors.iter().map(|c| (c.r, c.g, c.b)).collect(),
1594 );
1595 self.scale_fill(s)
1596 }
1597
1598 pub fn scale_fill_fermenter(
1600 self,
1601 name: crate::scale::palettes::PaletteName,
1602 n_bins: usize,
1603 ) -> Self {
1604 let stops = crate::scale::palettes::palette(&name)
1605 .iter()
1606 .map(|c| (c.r, c.g, c.b))
1607 .collect();
1608 let s =
1609 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
1610 self.scale_fill(s)
1611 }
1612
1613 pub fn scale_fill_discrete_sorted(self) -> Self {
1618 use crate::scale::color::ScaleColorDiscrete;
1619 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
1620 }
1621
1622 pub fn scale_color_discrete_sorted(self) -> Self {
1625 use crate::scale::color::ScaleColorDiscrete;
1626 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
1627 }
1628
1629 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1630 use crate::scale::color::ScaleColorDiscrete;
1631 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
1632 self.scale_fill(s)
1633 }
1634
1635 pub fn scale_linetype_manual(
1636 self,
1637 values: Vec<(&str, crate::render::backend::Linetype)>,
1638 ) -> Self {
1639 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
1640 self.scale_color(s)
1641 }
1642
1643 pub fn scale_shape_manual(
1644 self,
1645 values: Vec<(&str, crate::render::backend::PointShape)>,
1646 ) -> Self {
1647 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
1648 self.scale_color(s)
1649 }
1650
1651 pub fn scale_color_grey(self) -> Self {
1652 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
1653 self.scale_color(s)
1654 }
1655
1656 pub fn scale_fill_grey(self) -> Self {
1657 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
1658 self.scale_fill(s)
1659 }
1660
1661 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1662 self.scale_color(s)
1663 }
1664
1665 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1666 self.scale_fill(s)
1667 }
1668
1669 pub fn scale_x_reverse(self) -> Self {
1670 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1671 }
1672
1673 pub fn scale_y_reverse(self) -> Self {
1674 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1675 }
1676
1677 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1678 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1679 self.scales.push(Box::new(s));
1680 self
1681 }
1682
1683 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1684 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1685 self.scales.push(Box::new(s));
1686 self
1687 }
1688
1689 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
1690 self.scales.push(Box::new(s));
1691 self
1692 }
1693
1694 pub fn scale_size_area(mut self, max_size: f64) -> Self {
1698 self.scales.push(Box::new(
1699 crate::scale::size::ScaleSizeContinuous::new().area(max_size),
1700 ));
1701 self
1702 }
1703
1704 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
1705 self.scales.push(Box::new(s));
1706 self
1707 }
1708
1709 pub fn scale_linewidth(mut self, s: crate::scale::linewidth::ScaleLinewidthContinuous) -> Self {
1713 self.scales.push(Box::new(s));
1714 self
1715 }
1716
1717 pub fn xlim(self, min: f64, max: f64) -> Self {
1718 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
1719 }
1720
1721 pub fn ylim(self, min: f64, max: f64) -> Self {
1722 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
1723 }
1724
1725 pub fn scale_x_log10(self) -> Self {
1726 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1727 }
1728
1729 pub fn scale_y_log10(self) -> Self {
1730 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1731 }
1732
1733 pub fn scale_x_sqrt(self) -> Self {
1734 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1735 }
1736
1737 pub fn scale_y_sqrt(self) -> Self {
1738 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1739 }
1740
1741 pub fn scale_x_log2(self) -> Self {
1742 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1743 }
1744
1745 pub fn scale_y_log2(self) -> Self {
1746 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1747 }
1748
1749 pub fn scale_x_ln(self) -> Self {
1750 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1751 }
1752
1753 pub fn scale_y_ln(self) -> Self {
1754 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1755 }
1756
1757 pub fn scale_x_logit(self) -> Self {
1759 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1760 }
1761
1762 pub fn scale_y_logit(self) -> Self {
1763 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1764 }
1765
1766 pub fn scale_x_probit(self) -> Self {
1768 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1769 }
1770
1771 pub fn scale_y_probit(self) -> Self {
1772 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1773 }
1774
1775 pub fn scale_x_pseudo_log(self) -> Self {
1777 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1778 }
1779
1780 pub fn scale_y_pseudo_log(self) -> Self {
1781 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1782 }
1783
1784 pub fn scale_x_reciprocal(self) -> Self {
1786 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1787 }
1788
1789 pub fn scale_y_reciprocal(self) -> Self {
1790 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1791 }
1792
1793 pub fn scale_x_exp(self) -> Self {
1795 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1796 }
1797
1798 pub fn scale_y_exp(self) -> Self {
1799 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1800 }
1801
1802 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
1804 self.scale_x_continuous(
1805 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1806 )
1807 }
1808
1809 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
1810 self.scale_y_continuous(
1811 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1812 )
1813 }
1814
1815 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
1818 self.facet = Facet::Wrap {
1819 var: var.to_string(),
1820 ncol,
1821 scales: FacetScales::Fixed,
1822 labeller: FacetLabeller::default(),
1823 };
1824 self
1825 }
1826
1827 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
1828 self.facet = Facet::Wrap {
1829 var: var.to_string(),
1830 ncol,
1831 scales,
1832 labeller: FacetLabeller::default(),
1833 };
1834 self
1835 }
1836
1837 pub fn facet_wrap_labeller(
1838 mut self,
1839 var: &str,
1840 ncol: Option<usize>,
1841 labeller: FacetLabeller,
1842 ) -> Self {
1843 self.facet = Facet::Wrap {
1844 var: var.to_string(),
1845 ncol,
1846 scales: FacetScales::Fixed,
1847 labeller,
1848 };
1849 self
1850 }
1851
1852 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
1853 self.facet = Facet::Grid {
1854 row_var: row.map(String::from),
1855 col_var: col.map(String::from),
1856 scales: FacetScales::Fixed,
1857 labeller: FacetLabeller::default(),
1858 space: FacetSpace::Fixed,
1859 };
1860 self
1861 }
1862
1863 pub fn facet_grid_free(
1864 mut self,
1865 row: Option<&str>,
1866 col: Option<&str>,
1867 scales: FacetScales,
1868 ) -> Self {
1869 self.facet = Facet::Grid {
1870 row_var: row.map(String::from),
1871 col_var: col.map(String::from),
1872 scales,
1873 labeller: FacetLabeller::default(),
1874 space: FacetSpace::Fixed,
1875 };
1876 self
1877 }
1878
1879 pub fn facet_grid_multi(
1883 mut self,
1884 row: Option<&str>,
1885 cols: &[&str],
1886 scales: FacetScales,
1887 space: FacetSpace,
1888 ) -> Self {
1889 let col_var = if cols.len() > 1 {
1890 let n = self.data.nrows();
1892 let mut combined = Vec::with_capacity(n);
1893 for i in 0..n {
1894 let parts: Vec<String> = cols
1895 .iter()
1896 .map(|c| {
1897 self.data
1898 .column(c)
1899 .and_then(|col| col.get(i))
1900 .map(|v| v.to_group_key())
1901 .unwrap_or_default()
1902 })
1903 .collect();
1904 combined.push(crate::data::Value::Str(parts.join(" . ")));
1905 }
1906 let name = "__facet_cols__".to_string();
1907 self.data.add_column(name.clone(), combined);
1908 Some(name)
1909 } else {
1910 cols.first().map(|s| s.to_string())
1911 };
1912 self.facet = Facet::Grid {
1913 row_var: row.map(String::from),
1914 col_var,
1915 scales,
1916 labeller: FacetLabeller::default(),
1917 space,
1918 };
1919 self
1920 }
1921
1922 pub fn facet_grid_space(
1925 mut self,
1926 row: Option<&str>,
1927 col: Option<&str>,
1928 scales: FacetScales,
1929 space: FacetSpace,
1930 ) -> Self {
1931 self.facet = Facet::Grid {
1932 row_var: row.map(String::from),
1933 col_var: col.map(String::from),
1934 scales,
1935 labeller: FacetLabeller::default(),
1936 space,
1937 };
1938 self
1939 }
1940
1941 pub fn facet_grid_labeller(
1942 mut self,
1943 row: Option<&str>,
1944 col: Option<&str>,
1945 labeller: FacetLabeller,
1946 ) -> Self {
1947 self.facet = Facet::Grid {
1948 row_var: row.map(String::from),
1949 col_var: col.map(String::from),
1950 scales: FacetScales::Fixed,
1951 labeller,
1952 space: FacetSpace::Fixed,
1953 };
1954 self
1955 }
1956
1957 pub fn coord_flip(mut self) -> Self {
1960 self.coord = Box::new(CoordFlip);
1961 self
1962 }
1963
1964 pub fn coord_fixed(mut self, ratio: f64) -> Self {
1965 self.coord = Box::new(CoordFixed::new(ratio));
1966 self
1967 }
1968
1969 #[cfg(feature = "sf")]
1973 pub fn coord_sf(mut self) -> Self {
1974 self.coord = Box::new(crate::coord::sf::CoordSf::new());
1975 self
1976 }
1977
1978 #[cfg(feature = "sf")]
1983 pub fn coord_sf_with(mut self, coord: crate::coord::sf::CoordSf) -> Self {
1984 self.coord = Box::new(coord);
1985 self
1986 }
1987
1988 pub fn svg_root_data(mut self, on: bool) -> Self {
1995 self.svg_root_data = on;
1996 self
1997 }
1998
1999 pub fn svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
2009 self.root_attrs.push((name.into(), value.into()));
2010 self
2011 }
2012
2013 pub fn svg_attrs(mut self, attrs: impl IntoIterator<Item = (String, String)>) -> Self {
2015 self.root_attrs.extend(attrs);
2016 self
2017 }
2018
2019 pub fn svg_warnings_attr(mut self, on: bool) -> Self {
2024 self.svg_warnings_attr = on;
2025 self
2026 }
2027
2028 pub fn svg_formatted_values(mut self, on: bool) -> Self {
2037 self.svg_formatted_values = on;
2038 self
2039 }
2040
2041 pub fn svg_title(mut self, title: impl Into<String>) -> Self {
2051 self.svg_title = Some(title.into());
2052 self
2053 }
2054
2055 pub fn svg_desc(mut self, desc: impl Into<String>) -> Self {
2063 self.svg_desc = Some(desc.into());
2064 self
2065 }
2066
2067 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
2072 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
2073 self
2074 }
2075
2076 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
2078 self.coord_trans(None, Some(y))
2079 }
2080
2081 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
2083 self.coord_trans(Some(x), None)
2084 }
2085
2086 pub fn coord_cartesian_zoom(
2088 mut self,
2089 xlim: Option<(f64, f64)>,
2090 ylim: Option<(f64, f64)>,
2091 ) -> Self {
2092 let mut c = CoordCartesian::new();
2093 if let Some((min, max)) = xlim {
2094 c = c.xlim(min, max);
2095 }
2096 if let Some((min, max)) = ylim {
2097 c = c.ylim(min, max);
2098 }
2099 self.coord = Box::new(c);
2100 self
2101 }
2102
2103 pub fn coord_polar(mut self) -> Self {
2104 self.coord = Box::new(CoordPolar::new());
2105 self
2106 }
2107
2108 pub fn coord_radar(mut self) -> Self {
2112 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
2113 self
2114 }
2115
2116 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
2117 self.coord = Box::new(coord);
2118 self
2119 }
2120
2121 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
2122 self.coord = Box::new(coord);
2123 self
2124 }
2125
2126 pub fn theme(mut self, theme: Theme) -> Self {
2131 self.apply_preset(theme);
2132 self
2133 }
2134
2135 fn apply_preset(&mut self, theme: Theme) {
2136 let primary = self.theme.primary;
2137 self.theme = theme;
2138 if self.theme.primary.is_none() {
2139 self.theme.primary = primary;
2140 }
2141 }
2142
2143 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
2147 self.theme.axis_text_x.angle = degrees;
2148 self
2149 }
2150
2151 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
2153 self.theme.axis_text_y.angle = degrees;
2154 self
2155 }
2156
2157 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
2160 self.theme.axis_text_x_dodge = n.max(1);
2161 self
2162 }
2163
2164 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
2167 self.theme.aspect_ratio = Some(ratio);
2168 self
2169 }
2170
2171 pub fn panel_ontop(mut self) -> Self {
2173 self.theme.panel_ontop = true;
2174 self
2175 }
2176
2177 pub fn axis_minor_ticks(mut self) -> Self {
2179 self.theme.axis_minor_ticks = true;
2180 self
2181 }
2182
2183 pub fn axis_text_x_fit(mut self, fit: crate::theme::AxisLabelFit) -> Self {
2188 self.theme.axis_text_x_fit = fit;
2189 self
2190 }
2191
2192 pub fn axis_text_x_check_overlap(mut self, on: bool) -> Self {
2195 self.theme.axis_text_x_check_overlap = on;
2196 self
2197 }
2198
2199 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
2202 self.theme.title_position = pos;
2203 self
2204 }
2205
2206 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
2208 self.theme.tag_position = pos;
2209 self
2210 }
2211
2212 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
2214 self.theme.legend_direction = Some(dir);
2215 self
2216 }
2217
2218 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
2225 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
2226 self
2227 }
2228
2229 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
2231 self.theme.legend_position = pos;
2232 self
2233 }
2234
2235 pub fn legend_bottom_when_narrow(mut self, share: f64) -> Self {
2242 self.legend_bottom_share = Some(share);
2243 self
2244 }
2245
2246 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
2247 self.theme.primary = Some(color);
2248 self
2249 }
2250
2251 pub fn theme_minimal(mut self) -> Self {
2252 self.apply_preset(crate::theme::presets::theme_minimal());
2253 self
2254 }
2255
2256 pub fn theme_bw(mut self) -> Self {
2257 self.apply_preset(crate::theme::presets::theme_bw());
2258 self
2259 }
2260
2261 pub fn theme_gray(mut self) -> Self {
2262 self.apply_preset(crate::theme::presets::theme_gray());
2263 self
2264 }
2265
2266 pub fn theme_classic(mut self) -> Self {
2267 self.apply_preset(crate::theme::presets::theme_classic());
2268 self
2269 }
2270
2271 pub fn theme_pubr(mut self) -> Self {
2273 self.apply_preset(crate::theme::presets::theme_pubr());
2274 self
2275 }
2276
2277 pub fn theme_linedraw(mut self) -> Self {
2278 self.apply_preset(crate::theme::presets::theme_linedraw());
2279 self
2280 }
2281
2282 pub fn theme_light(mut self) -> Self {
2283 self.apply_preset(crate::theme::presets::theme_light());
2284 self
2285 }
2286
2287 pub fn theme_dark(mut self) -> Self {
2288 self.apply_preset(crate::theme::presets::theme_dark());
2289 self
2290 }
2291
2292 pub fn theme_void(mut self) -> Self {
2293 self.apply_preset(crate::theme::presets::theme_void());
2294 self
2295 }
2296
2297 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
2300 self.theme = self.theme.update(update);
2301 self
2302 }
2303
2304 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
2308 self.guide_legend = guide;
2309 self
2310 }
2311
2312 pub fn labs(mut self, labels: Labels) -> Self {
2315 if labels.title.is_some() {
2316 self.labels.title = labels.title;
2317 }
2318 if labels.subtitle.is_some() {
2319 self.labels.subtitle = labels.subtitle;
2320 }
2321 if labels.x.is_some() {
2322 self.labels.x = labels.x;
2323 }
2324 if labels.y.is_some() {
2325 self.labels.y = labels.y;
2326 }
2327 if labels.caption.is_some() {
2328 self.labels.caption = labels.caption;
2329 }
2330 if labels.tag.is_some() {
2331 self.labels.tag = labels.tag;
2332 }
2333 self
2334 }
2335
2336 pub fn title(mut self, title: &str) -> Self {
2337 self.labels.title = Some(title.to_string());
2338 self
2339 }
2340
2341 pub fn tag(mut self, tag: &str) -> Self {
2344 self.labels.tag = Some(tag.to_string());
2345 self
2346 }
2347
2348 pub fn subtitle(mut self, subtitle: &str) -> Self {
2349 self.labels.subtitle = Some(subtitle.to_string());
2350 self
2351 }
2352
2353 pub fn xlab(mut self, label: &str) -> Self {
2354 self.labels.x = Some(label.to_string());
2355 self
2356 }
2357
2358 pub fn ylab(mut self, label: &str) -> Self {
2359 self.labels.y = Some(label.to_string());
2360 self
2361 }
2362
2363 pub fn caption(mut self, caption: &str) -> Self {
2364 self.labels.caption = Some(caption.to_string());
2365 self
2366 }
2367
2368 pub fn annotate(mut self, annotation: Annotation) -> Self {
2372 self.annotations.push(annotation);
2373 self
2374 }
2375
2376 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
2378 self.annotate(Annotation::text(label, x, y))
2379 }
2380
2381 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
2383 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
2384 }
2385
2386 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
2388 self.annotate(Annotation::segment(x, y, xend, yend))
2389 }
2390
2391 pub fn locale(mut self, locale: crate::locale::Locale) -> Self {
2414 self.locale = Some(locale);
2415 self
2416 }
2417
2418 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
2422 PlotBuilder::build(self)
2423 }
2424
2425 pub fn build(self) -> crate::build::BuiltPlot {
2429 self.try_build().expect("plot build failed")
2430 }
2431
2432 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2436 pub fn save(self, path: &str) -> Result<(), GGError> {
2437 self.save_with_size(path, 800, 600)
2438 }
2439
2440 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2442 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
2443 let (built, layout) = self.prepare(w, h)?;
2444
2445 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
2447
2448 match ext.as_str() {
2449 "svg" => {
2450 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
2451 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2452 }
2453 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
2454 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
2455 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2456 }
2457 _ => {
2458 return Err(GGError::UnsupportedFormat(ext));
2459 }
2460 }
2461
2462 Ok(())
2463 }
2464
2465 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2471 pub fn render_svg(self) -> Result<String, GGError> {
2472 self.render_svg_with_size(800, 600)
2473 }
2474
2475 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2479 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2480 let (built, layout) = self.prepare(w, h)?;
2481 let mut buf = String::new();
2482 {
2483 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
2484 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2485 }
2486 Ok(buf)
2487 }
2488
2489 pub fn render_svg_native(self) -> Result<String, GGError> {
2493 self.render_svg_native_with_size(800, 600)
2494 }
2495
2496 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2504 Ok(self.render_native(w, h)?.0.finish())
2505 }
2506
2507 pub fn render_svg_native_with_warnings(
2512 self,
2513 w: u32,
2514 h: u32,
2515 ) -> Result<(String, Vec<String>), GGError> {
2516 let (backend, warnings, _) = self.render_native(w, h)?;
2517 Ok((backend.finish(), warnings))
2518 }
2519
2520 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
2532 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
2533 }
2534
2535 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
2539 let (backend, _, pa) = self.render_native(w, h)?;
2540 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
2541 }
2542
2543 fn render_native(
2547 self,
2548 w: u32,
2549 h: u32,
2550 ) -> Result<
2551 (
2552 crate::render::svg_backend::SvgBackend,
2553 Vec<String>,
2554 crate::render::Rect,
2555 ),
2556 GGError,
2557 > {
2558 let svg_root_data = self.svg_root_data;
2559 let root_attrs = self.root_attrs.clone();
2560 let svg_warnings_attr = self.svg_warnings_attr;
2561 let svg_formatted_values = self.svg_formatted_values;
2562 let svg_title = self.svg_title.clone();
2563 let svg_desc = self.svg_desc.clone();
2564 let (built, layout) = self.prepare(w, h)?;
2565 let pa = layout.plot_area.clone();
2566 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
2567 backend.set_formatted_values(svg_formatted_values);
2568 backend.set_svg_title(svg_title.clone());
2569 backend.set_svg_desc(svg_desc.clone());
2570 let existing_root_attrs = if svg_root_data {
2571 let attrs = crate::render::svg_backend::root_data_attrs(&built);
2572 backend.set_root_attrs(attrs.clone());
2573 let mut reserved = attrs;
2577 for name in ["data-plot", "data-viewport", "data-panels"] {
2580 reserved.push((name.to_string(), String::new()));
2581 }
2582 reserved
2583 } else {
2584 backend.without_plot_attr();
2585 Vec::new()
2586 };
2587 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2588 let mut warnings = built.warnings;
2589 warnings.extend(backend.take_warnings());
2590
2591 let mut reserved = existing_root_attrs;
2592 if svg_warnings_attr {
2593 reserved.push(("data-warnings".to_string(), String::new()));
2594 }
2595 if svg_title.is_some() || svg_desc.is_some() {
2600 reserved.push(("role".to_string(), String::new()));
2601 }
2602 if svg_title.is_some() {
2603 reserved.push(("aria-labelledby".to_string(), String::new()));
2604 }
2605 if svg_desc.is_some() {
2606 reserved.push(("aria-describedby".to_string(), String::new()));
2607 }
2608 let extra = crate::render::svg_backend::validate_root_attrs(
2609 &root_attrs,
2610 &reserved,
2611 "plot",
2612 &mut warnings,
2613 );
2614 backend.extend_root_attrs(extra);
2615 if svg_warnings_attr {
2616 backend.extend_root_attrs(vec![(
2617 "data-warnings".to_string(),
2618 crate::render::svg_backend::warnings_json(&warnings),
2619 )]);
2620 }
2621 Ok((backend, warnings, pa))
2622 }
2623
2624 #[cfg(feature = "canvas")]
2629 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
2630 let (built, layout) = self.prepare(w, h)?;
2631 let mut backend =
2632 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2633 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2634 let (ow, oh) = backend.dimensions();
2635 Ok((ow, oh, backend.into_rgba()))
2636 }
2637
2638 #[cfg(feature = "canvas")]
2642 pub fn render_rgba_area_with_size(
2643 self,
2644 w: u32,
2645 h: u32,
2646 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
2647 let (built, layout) = self.prepare(w, h)?;
2648 let pa = layout.plot_area.clone();
2649 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
2650 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2651 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
2652 }
2653
2654 #[cfg(feature = "canvas")]
2658 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2659 let (built, layout) = self.prepare(w, h)?;
2660 let mut backend =
2661 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2662 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2663 backend.into_png().map_err(GGError::Render)
2664 }
2665
2666 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2673 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
2674 self.render_png_with_size(800, 600)
2675 }
2676
2677 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2680 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2681 let (built, layout) = self.prepare(w, h)?;
2682
2683 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
2686 {
2687 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
2688 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2689 }
2690
2691 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
2692 GGError::Render(RenderError::BackendError(
2693 "PNG buffer size mismatch".to_string(),
2694 ))
2695 })?;
2696 let mut out = std::io::Cursor::new(Vec::new());
2697 img.write_to(&mut out, image::ImageOutputFormat::Png)
2698 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2699 Ok(out.into_inner())
2700 }
2701
2702 pub(crate) fn prepare(
2704 self,
2705 w: u32,
2706 h: u32,
2707 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
2708 let (mut built, meta) = self.build_for_render()?;
2709 let layout = Self::layout_built(&mut built, &meta, w, h);
2710 Ok((built, layout))
2711 }
2712
2713 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
2716 let plot = self;
2717
2718 let has_title = plot.labels.title.is_some();
2719 let has_subtitle = plot.labels.subtitle.is_some();
2720 let has_caption = plot.labels.caption.is_some();
2721 let has_legend = plot.has_legend_mapping();
2722 let legend_bottom_share = plot.legend_bottom_share;
2723 let x_label = plot.labels.x.clone();
2724 let y_label = plot.labels.y.clone();
2725
2726 let mut built = PlotBuilder::build(plot)?;
2727
2728 built.theme.resolve_inheritance();
2730
2731 if let Some(ref label) = x_label {
2733 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
2734 s.set_name(label);
2735 }
2736 }
2737 if let Some(ref label) = y_label {
2738 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
2739 s.set_name(label);
2740 }
2741 }
2742
2743 Ok((
2744 built,
2745 RenderMeta {
2746 has_title,
2747 has_subtitle,
2748 has_caption,
2749 has_legend,
2750 legend_bottom_share,
2751 },
2752 ))
2753 }
2754
2755 pub(crate) fn layout_built(
2758 built: &mut crate::build::BuiltPlot,
2759 meta: &RenderMeta,
2760 w: u32,
2761 h: u32,
2762 ) -> PlotLayout {
2763 let RenderMeta {
2764 has_title,
2765 has_subtitle,
2766 has_caption,
2767 has_legend,
2768 legend_bottom_share,
2769 } = *meta;
2770 let x_axis_top = built
2771 .scales
2772 .get(&crate::aes::Aesthetic::X)
2773 .map(|s| s.axis_position_opposite())
2774 .unwrap_or(false);
2775
2776 if let Some(share) = legend_bottom_share {
2779 if has_legend
2780 && matches!(
2781 built.theme.legend_position,
2782 crate::theme::LegendPosition::Right
2783 )
2784 {
2785 let panel_width = |pos| {
2786 let mut theme = built.theme.clone();
2787 theme.legend_position = pos;
2788 PlotLayout::compute_with_extras(
2789 w as f64,
2790 h as f64,
2791 &theme,
2792 has_title,
2793 has_subtitle,
2794 has_caption,
2795 has_legend,
2796 x_axis_top,
2797 None,
2798 None,
2799 crate::render::layout::LayoutExtras::default(),
2800 )
2801 .plot_area
2802 .width
2803 };
2804 let free = panel_width(crate::theme::LegendPosition::None);
2807 if panel_width(crate::theme::LegendPosition::Right) < (1.0 - share) * free {
2808 built.theme.legend_position = crate::theme::LegendPosition::Bottom;
2809 }
2810 }
2811 }
2812
2813 let flipped = built.coord.is_flipped();
2817 let h_aes = if flipped {
2818 crate::aes::Aesthetic::Y
2819 } else {
2820 crate::aes::Aesthetic::X
2821 };
2822 let v_aes = if flipped {
2823 crate::aes::Aesthetic::X
2824 } else {
2825 crate::aes::Aesthetic::Y
2826 };
2827
2828 let drawn_x_labels: Vec<(f64, String)> = built
2836 .scales
2837 .get(&h_aes)
2838 .map(|hs| {
2839 hs.breaks()
2840 .into_iter()
2841 .filter(|(_, label)| !label.is_empty())
2842 .collect()
2843 })
2844 .unwrap_or_default();
2845 let h_is_discrete = built
2846 .scales
2847 .get(&h_aes)
2848 .map(|hs| hs.is_discrete())
2849 .unwrap_or(false);
2850
2851 if built.theme.axis_text_x.visible
2864 && built.theme.axis_text_x.angle.abs() < 1.0
2865 && h_is_discrete
2866 && !drawn_x_labels.is_empty()
2867 {
2868 let font = built.theme.axis_text_x.size;
2869 let max_w = drawn_x_labels
2870 .iter()
2871 .map(|(_, l)| crate::render::text::text_width(l, font))
2872 .fold(0.0_f64, f64::max);
2873 let n = drawn_x_labels.len() as f64;
2874 let spacing = 0.9 * w as f64 / n; if max_w > spacing {
2876 let vertical = max_w * std::f64::consts::FRAC_1_SQRT_2 > spacing;
2880 built.theme.axis_text_x.angle = if vertical { 270.0 } else { -45.0 };
2881 }
2882 }
2883
2884 let x_label_height = if built.theme.axis_text_x.visible
2899 && built.theme.axis_text_x.angle.abs() > 10.0
2900 && !drawn_x_labels.is_empty()
2901 {
2902 let font = built.theme.axis_text_x.size;
2903 let angle = built.theme.axis_text_x.angle;
2904 let dir = if x_axis_top { -1.0 } else { 1.0 };
2905 let (rise, fall) = crate::render::text::rotated_label_rise_fall(
2906 drawn_x_labels.iter().map(|(_, l)| l.as_str()),
2907 font,
2908 angle,
2909 crate::render::text::rotated_anchor_frac(angle, dir),
2910 );
2911 Some(built.theme.legend_spacing / 2.0 + font / 2.0 + rise + fall)
2912 } else {
2913 None
2914 };
2915
2916 let y_label_width = if built.theme.axis_text_y.visible {
2920 built.scales.get(&v_aes).map(|vs| {
2921 let max_ch = vs
2922 .breaks()
2923 .iter()
2924 .map(|(_, l)| l.chars().count())
2925 .max()
2926 .unwrap_or(0);
2927 let measured = max_ch as f64 * built.theme.axis_text_y.size * 0.6 + 4.0;
2928 let default = built.theme.axis_text_y.size * 3.5 + 4.0;
2929 measured.max(default).min(0.35 * w as f64)
2930 })
2931 } else {
2932 None
2933 };
2934
2935 let mut extras = crate::render::layout::LayoutExtras::default();
2946 let can_reserve = built.facet.is_none()
2947 && built.theme.axis_text_x.visible
2948 && built.theme.axis_text_x_fit == crate::theme::AxisLabelFit::Reserve
2949 && !built.coord.is_polar()
2950 && built.coord.supports_axis_reserve();
2951 if can_reserve {
2952 if let Some(hs) = built.scales.get(&h_aes) {
2953 let breaks = hs.breaks();
2954 if let (Some(first), Some(last)) = (breaks.first(), breaks.last()) {
2955 let angle = built.theme.axis_text_x.angle;
2956 let anchor = if angle.abs() > 10.0 {
2957 let dir = if x_axis_top { -1.0 } else { 1.0 };
2958 crate::render::text::rotated_anchor_frac(angle, dir)
2959 } else {
2960 0.5
2961 };
2962 let size = built.theme.axis_text_x.size;
2963 const EDGE: f64 = 1.0;
2964 for _ in 0..2 {
2965 let layout = PlotLayout::compute_with_extras(
2966 w as f64,
2967 h as f64,
2968 &built.theme,
2969 has_title,
2970 has_subtitle,
2971 has_caption,
2972 has_legend,
2973 x_axis_top,
2974 y_label_width,
2975 x_label_height,
2976 extras,
2977 );
2978 let area = &layout.plot_area;
2979 let edge_y = area.y + area.height;
2980 let to_point = |pos: f64| if flipped { (0.0, pos) } else { (pos, 0.0) };
2981 let px_first = built.coord.transform(to_point(first.0), area).0;
2982 let px_last = built.coord.transform(to_point(last.0), area).0;
2983 let bbox_first = crate::render::text::label_bbox(
2984 &first.1, size, px_first, edge_y, angle, anchor,
2985 );
2986 let bbox_last = crate::render::text::label_bbox(
2987 &last.1, size, px_last, edge_y, angle, anchor,
2988 );
2989 let deficit_left = (EDGE - bbox_first[0]).max(0.0);
2990 let deficit_right = (bbox_last[2] - (w as f64 - EDGE)).max(0.0);
2991 if deficit_left <= 0.0 && deficit_right <= 0.0 {
2992 break;
2993 }
2994 let (p_first, p_last) = (first.0, last.0);
3003 if deficit_left > 0.0 && deficit_right > 0.0 {
3004 let (a, b, c, d) = (1.0 - p_first, p_first, 1.0 - p_last, p_last);
3009 let det = a * d - b * c;
3010 if det.abs() > 0.01 {
3011 extras.left +=
3012 ((deficit_left * d + deficit_right * b) / det).max(0.0);
3013 extras.right +=
3014 ((deficit_right * a + deficit_left * c) / det).max(0.0);
3015 } else {
3016 extras.left += deficit_left / (1.0 - p_first).max(0.05);
3020 extras.right += deficit_right / p_last.max(0.05);
3021 }
3022 } else if deficit_left > 0.0 {
3023 extras.left += deficit_left / (1.0 - p_first).max(0.05);
3024 } else if deficit_right > 0.0 {
3025 extras.right += deficit_right / p_last.max(0.05);
3026 }
3027 const MIN_PANEL_WIDTH: f64 = 50.0;
3042 let cap = (w as f64 - MIN_PANEL_WIDTH).max(0.0);
3043 if extras.left + extras.right > cap {
3044 let scale = if extras.left + extras.right > 0.0 {
3045 cap / (extras.left + extras.right)
3046 } else {
3047 0.0
3048 };
3049 extras.left *= scale;
3050 extras.right *= scale;
3051 }
3052 }
3053 }
3054 }
3055 }
3056
3057 if has_legend
3064 && !matches!(
3065 built.theme.legend_position,
3066 crate::theme::LegendPosition::None
3067 )
3068 {
3069 let legend_aes =
3070 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3071 extras.legend_max_key_size = legend_aes
3075 .iter()
3076 .filter_map(|aes| {
3077 let scale = built.scales.get(aes)?;
3078 let breaks = scale.breaks();
3079 Some(crate::guide::legend::size_key_diameter(
3080 aes,
3081 &built.scales,
3082 scale,
3083 &breaks,
3084 ))
3085 })
3086 .fold(0.0_f64, f64::max);
3087 let has_wrap_guide = legend_aes.iter().any(|a| {
3098 let is_discrete = built
3099 .scales
3100 .get(a)
3101 .map(|s| s.is_discrete())
3102 .unwrap_or(false);
3103 is_discrete || !crate::guide::legend::is_colorbar_aesthetic(a)
3104 });
3105 if has_wrap_guide {
3106 let layout0 = PlotLayout::compute_with_extras(
3107 w as f64,
3108 h as f64,
3109 &built.theme,
3110 has_title,
3111 has_subtitle,
3112 has_caption,
3113 has_legend,
3114 x_axis_top,
3115 y_label_width,
3116 x_label_height,
3117 extras,
3118 );
3119 let (legend_x0, _, mut is_horizontal) = crate::guide::legend::legend_position(
3129 &built.theme,
3130 &layout0.plot_area,
3131 extras.legend_extra_rows,
3132 (0.0, 0.0),
3133 &layout0.total,
3134 );
3135 if let Some(dir) = built.theme.legend_direction {
3136 is_horizontal = matches!(dir, crate::theme::LegendDirection::Horizontal);
3137 }
3138 let avail_width =
3139 (layout0.plot_area.x + layout0.plot_area.width - legend_x0).max(0.0);
3140 let mut legend_warnings = Vec::new();
3141 let mut plans = crate::guide::legend::plan_all_legends(
3142 &built.scales,
3143 &built.guide_legend,
3144 &built.theme,
3145 &built.suppressed_aes,
3146 avail_width,
3147 layout0.plot_area.height,
3148 is_horizontal,
3149 &mut legend_warnings,
3150 );
3151 built.warnings.extend(legend_warnings);
3152
3153 if !plans.is_empty() {
3154 extras.legend_extra_rows = if is_horizontal {
3160 plans
3161 .values()
3162 .map(|p| p.rows.saturating_sub(1))
3163 .max()
3164 .unwrap_or(0)
3165 } else {
3166 plans.values().map(|p| p.rows.saturating_sub(1)).sum()
3167 };
3168 extras.legend_extra_width = plans
3175 .values()
3176 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3177 .fold(0.0_f64, f64::max);
3178
3179 if !is_horizontal {
3191 let final_layout = PlotLayout::compute_with_extras(
3192 w as f64,
3193 h as f64,
3194 &built.theme,
3195 has_title,
3196 has_subtitle,
3197 has_caption,
3198 has_legend,
3199 x_axis_top,
3200 y_label_width,
3201 x_label_height,
3202 extras,
3203 );
3204 let (legend_x_final, _, _) = crate::guide::legend::legend_position(
3210 &built.theme,
3211 &final_layout.plot_area,
3212 extras.legend_extra_rows,
3213 (0.0, 0.0),
3214 &final_layout.total,
3215 );
3216 let swatch_and_gap =
3217 built.theme.legend_key_width + built.theme.legend_spacing;
3218 let text_size = built.theme.legend_text.size;
3219 fn right_edge_of(
3225 legend_x: f64,
3226 swatch_and_gap: f64,
3227 text_size: f64,
3228 plan: &crate::guide::legend::LegendPlan,
3229 breaks: &[(f64, String)],
3230 ) -> f64 {
3231 plan.entries
3232 .iter()
3233 .filter_map(|e| breaks.get(e.break_idx).map(|(_, l)| (e, l)))
3234 .map(|(e, label)| {
3235 let text =
3236 e.label_override.as_deref().unwrap_or(label.as_str());
3237 legend_x
3238 + e.x_offset
3239 + swatch_and_gap
3240 + crate::render::text::text_width(text, text_size)
3241 })
3242 .fold(0.0_f64, f64::max)
3243 }
3244
3245 let mut updates: Vec<(
3246 crate::aes::Aesthetic,
3247 crate::guide::legend::LegendPlan,
3248 )> = Vec::new();
3249 let mut reduced_aes: Vec<crate::aes::Aesthetic> = Vec::new();
3256 for (aes, plan) in &plans {
3257 if plan.cols <= 1 {
3258 continue;
3259 }
3260 let Some(scale) = built.scales.get(aes) else {
3261 continue;
3262 };
3263 let breaks = scale.breaks();
3264 if right_edge_of(
3265 legend_x_final,
3266 swatch_and_gap,
3267 text_size,
3268 plan,
3269 &breaks,
3270 ) <= w as f64
3271 {
3272 continue; }
3274
3275 let n = breaks.len();
3278 let mut reduced: Vec<crate::guide::legend::LegendPlan> = Vec::new();
3279 for c in (1..plan.cols).rev() {
3280 let mut candidate_guide = built.guide_legend.clone();
3281 candidate_guide.ncol = Some(c);
3282 candidate_guide.nrow = None;
3283 let mut tmp_warnings = Vec::new();
3284 let candidate_guide =
3285 candidate_guide.validated(n, &mut tmp_warnings);
3286 if let Some(cand) = crate::guide::legend::plan_legend(
3287 &breaks,
3288 &candidate_guide,
3289 &built.theme,
3290 avail_width,
3291 layout0.plot_area.height,
3292 false,
3293 ) {
3294 reduced.push(cand);
3295 }
3296 }
3297 if let Some(fits) = reduced.iter().find(|c| {
3298 right_edge_of(legend_x_final, swatch_and_gap, text_size, c, &breaks)
3299 <= w as f64
3300 }) {
3301 extras.legend_extra_rows =
3302 extras.legend_extra_rows.max(fits.rows.saturating_sub(1));
3303 updates.push((aes.clone(), fits.clone()));
3304 reduced_aes.push(aes.clone());
3305 continue;
3306 }
3307
3308 let base = reduced.last().cloned().unwrap_or_else(|| plan.clone());
3313 extras.legend_extra_rows =
3314 extras.legend_extra_rows.max(base.rows.saturating_sub(1));
3315 let mut truncated = base.clone();
3316 for e in &mut truncated.entries {
3317 let Some((_, label)) = breaks.get(e.break_idx) else {
3318 continue;
3319 };
3320 let avail = (w as f64
3321 - (legend_x_final + e.x_offset + swatch_and_gap))
3322 .max(0.0);
3323 if let Some(short) =
3324 crate::render::text::truncate_to_width(label, avail, text_size)
3325 {
3326 e.label_override = Some(short);
3327 }
3328 }
3329
3330 let truncated_edge = right_edge_of(
3334 legend_x_final,
3335 swatch_and_gap,
3336 text_size,
3337 &truncated,
3338 &breaks,
3339 );
3340 if truncated_edge > w as f64 {
3341 built.warnings.push(format!(
3342 "legend: the {}-column grid still doesn't fit the {w}px chart (needs about {} px)",
3343 truncated.cols,
3344 truncated_edge.ceil() as i64
3345 ));
3346 }
3347 updates.push((aes.clone(), truncated));
3348 }
3349 for (aes, new_plan) in &updates {
3350 plans.insert(aes.clone(), new_plan.clone());
3351 }
3352
3353 if !reduced_aes.is_empty() {
3394 let original_reserve = extras.legend_extra_width;
3395 let tighter_reserve = plans
3396 .values()
3397 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3398 .fold(0.0_f64, f64::max);
3399 if tighter_reserve < original_reserve {
3400 let mut retry_extras = extras;
3401 retry_extras.legend_extra_width = tighter_reserve;
3402 let retry_layout = PlotLayout::compute_with_extras(
3403 w as f64,
3404 h as f64,
3405 &built.theme,
3406 has_title,
3407 has_subtitle,
3408 has_caption,
3409 has_legend,
3410 x_axis_top,
3411 y_label_width,
3412 x_label_height,
3413 retry_extras,
3414 );
3415 let (retry_x, _, _) = crate::guide::legend::legend_position(
3421 &built.theme,
3422 &retry_layout.plot_area,
3423 retry_extras.legend_extra_rows,
3424 (0.0, 0.0),
3425 &retry_layout.total,
3426 );
3427 let all_still_fit = plans.iter().all(|(aes, plan)| {
3428 let Some(scale) = built.scales.get(aes) else {
3429 return true;
3430 };
3431 let breaks = scale.breaks();
3432 right_edge_of(retry_x, swatch_and_gap, text_size, plan, &breaks)
3433 <= w as f64
3434 });
3435 if all_still_fit {
3436 extras.legend_extra_width = tighter_reserve;
3437 }
3438 }
3439 }
3440 }
3441 for (aes, plan) in &plans {
3442 if let Some(ov) = &plan.overflow {
3443 let n = built
3444 .scales
3445 .get(aes)
3446 .map(|s| s.breaks().len())
3447 .unwrap_or(ov.dropped);
3448 let shown = n.saturating_sub(ov.dropped);
3449 built.warnings.push(format!(
3450 "legend: {} of {n} entries don't fit the chart; the legend shows {shown} and \"+{} more\"",
3451 ov.dropped, ov.dropped
3452 ));
3453 }
3454 }
3455 }
3456 built.legend_plans = plans;
3462 }
3463 }
3464
3465 if has_legend
3476 && !matches!(
3477 built.theme.legend_position,
3478 crate::theme::LegendPosition::None
3479 )
3480 {
3481 let is_horizontal = match built.theme.legend_direction {
3482 Some(dir) => matches!(dir, crate::theme::LegendDirection::Horizontal),
3483 None => matches!(
3484 built.theme.legend_position,
3485 crate::theme::LegendPosition::Top | crate::theme::LegendPosition::Bottom
3486 ),
3487 };
3488 if is_horizontal {
3489 let legend_aes =
3490 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3491 let continuous_colourbar = legend_aes.iter().find_map(|aes| {
3492 if !matches!(
3493 aes,
3494 crate::aes::Aesthetic::Color | crate::aes::Aesthetic::Fill
3495 ) {
3496 return None;
3497 }
3498 let scale = built.scales.get(aes)?;
3499 if scale.is_discrete() {
3500 return None;
3501 }
3502 Some(scale)
3503 });
3504 if let Some(scale) = continuous_colourbar {
3505 let title = built
3506 .guide_legend
3507 .title
3508 .as_deref()
3509 .unwrap_or_else(|| scale.name());
3510 let bar_height = crate::guide::legend::continuous_horizontal_bar_height(
3511 &built.theme,
3512 !title.is_empty(),
3513 );
3514 let key_h = built
3515 .theme
3516 .legend_key_height
3517 .max(extras.legend_max_key_size);
3518 extras.continuous_horizontal_legend_extra_height =
3519 (bar_height - key_h).max(0.0);
3520 }
3521 }
3522 }
3523
3524 PlotLayout::compute_with_extras(
3525 w as f64,
3526 h as f64,
3527 &built.theme,
3528 has_title,
3529 has_subtitle,
3530 has_caption,
3531 has_legend,
3532 x_axis_top,
3533 y_label_width,
3534 x_label_height,
3535 extras,
3536 )
3537 }
3538
3539 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3542 fn render_into<DB>(
3543 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
3544 built: &crate::build::BuiltPlot,
3545 layout: &PlotLayout,
3546 ) -> Result<(), GGError>
3547 where
3548 DB: plotters::prelude::DrawingBackend,
3549 DB::ErrorType: 'static,
3550 {
3551 area.fill(&plotters::prelude::WHITE)
3552 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3553 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
3554 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
3555 area.present()
3556 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3557 Ok(())
3558 }
3559
3560 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3563 pub fn ggsave(
3564 self,
3565 path: &str,
3566 width_inches: f64,
3567 height_inches: f64,
3568 dpi: f64,
3569 ) -> Result<(), GGError> {
3570 let w = (width_inches * dpi) as u32;
3571 let h = (height_inches * dpi) as u32;
3572 self.save_with_size(path, w, h)
3573 }
3574
3575 fn has_legend_mapping(&self) -> bool {
3590 use crate::guide::legend::is_legend_aesthetic;
3595 let plot_level = self
3596 .mapping
3597 .mappings
3598 .iter()
3599 .any(|m| is_legend_aesthetic(&m.aesthetic));
3600 plot_level
3601 || self.layers.iter().any(|layer| {
3602 if layer.show_legend == Some(false) {
3603 return false;
3604 }
3605 let effective = if layer.geom.inherit_aes() {
3606 self.mapping.merge(&layer.mapping)
3607 } else {
3608 layer.mapping.clone()
3609 };
3610 effective
3611 .mappings
3612 .iter()
3613 .any(|m| is_legend_aesthetic(&m.aesthetic))
3614 })
3615 }
3616}
3617
3618#[derive(Clone, Copy, Debug)]
3620pub(crate) struct RenderMeta {
3621 pub(crate) has_title: bool,
3622 pub(crate) has_subtitle: bool,
3623 pub(crate) has_caption: bool,
3624 pub(crate) has_legend: bool,
3625 pub(crate) legend_bottom_share: Option<f64>,
3626}
3627
3628#[derive(Debug)]
3630#[non_exhaustive]
3631pub enum GGError {
3632 Render(RenderError),
3633 UnsupportedFormat(String),
3634 Io(std::io::Error),
3635 ValidationError(String),
3636}
3637
3638impl std::fmt::Display for GGError {
3639 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3640 match self {
3641 GGError::Render(e) => write!(f, "Render error: {e}"),
3642 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
3643 GGError::Io(e) => write!(f, "IO error: {e}"),
3644 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
3645 }
3646 }
3647}
3648
3649impl std::error::Error for GGError {}