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_attribution(self, text: impl Into<String>) -> Self {
894 self.add_geom(crate::geom::annotation_scale::GeomAttribution::new(text))
895 }
896
897 #[cfg(feature = "sf")]
898 pub fn annotation_attribution_with(
899 self,
900 geom: crate::geom::annotation_scale::GeomAttribution,
901 ) -> Self {
902 self.add_geom_with(geom)
903 }
904
905 #[cfg(feature = "sf")]
913 pub fn bivariate_legend(
914 self,
915 geom: crate::geom::bivariate_legend::GeomBivariateLegend,
916 ) -> Self {
917 self.add_geom(geom)
918 }
919
920 #[cfg(feature = "sf")]
925 pub fn bivariate_legend_counts(
926 self,
927 geom: crate::geom::bivariate_legend::GeomBivariateLegendCounts,
928 ) -> Self {
929 self.add_geom(geom)
930 }
931
932 pub fn geom_curve(self) -> Self {
933 self.add_geom(GeomCurve::default())
934 }
935
936 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
937 self.add_geom_with(geom)
938 }
939
940 pub fn geom_violin(self) -> Self {
941 self.add_geom(GeomViolin::default())
942 }
943
944 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
945 self.add_geom_with(geom)
946 }
947
948 pub fn geom_dotplot(self) -> Self {
949 self.add_geom(GeomDotplot::default())
950 }
951
952 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
953 self.add_geom_with(geom)
954 }
955
956 pub fn geom_qq(self) -> Self {
957 self.add_geom(GeomQQ::default())
958 }
959
960 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
961 self.add_geom_with(geom)
962 }
963
964 pub fn geom_qq_line(self) -> Self {
965 self.add_geom(GeomQQLine::default())
966 }
967
968 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
969 self.add_geom_with(geom)
970 }
971
972 pub fn geom_bin2d(self) -> Self {
973 self.add_geom(GeomBin2d::default())
974 }
975
976 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
977 self.add_geom_with(geom)
978 }
979
980 pub fn geom_hex(self) -> Self {
981 self.add_geom(GeomHex::default())
982 }
983
984 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
985 self.add_geom_with(geom)
986 }
987
988 pub fn geom_count(self) -> Self {
989 self.add_geom(GeomCount::default())
990 }
991
992 pub fn geom_count_with(self, geom: GeomCount) -> Self {
993 self.add_geom_with(geom)
994 }
995
996 pub fn geom_contour(self) -> Self {
997 self.add_geom(GeomContour::default())
998 }
999
1000 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
1001 self.add_geom_with(geom)
1002 }
1003
1004 pub fn geom_contour_filled(self) -> Self {
1007 self.add_geom(GeomPolygon {
1008 line_width: 0.0,
1009 alpha: 1.0,
1010 ..GeomPolygon::default()
1011 })
1012 .stat(crate::stat::contour_filled::StatContourFilled::default())
1013 }
1014
1015 pub fn geom_density2d(self) -> Self {
1016 self.add_geom(GeomDensity2d::default())
1017 }
1018
1019 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
1020 self.add_geom_with(geom)
1021 }
1022
1023 pub fn geom_candlestick(self) -> Self {
1026 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
1027 }
1028
1029 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
1030 self.add_geom_with(geom)
1031 }
1032
1033 pub fn geom_ohlc(self) -> Self {
1036 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
1037 }
1038
1039 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
1040 self.add_geom_with(geom)
1041 }
1042
1043 pub fn geom_calendar(self) -> Self {
1054 self.geom_calendar_with(
1055 GeomTile {
1056 color: (255, 255, 255),
1057 line_width: 1.5,
1058 ..Default::default()
1059 },
1060 false,
1061 )
1062 }
1063
1064 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
1067 use crate::stat::calendar::{
1068 day_number, month_boundaries, month_breaks, month_breaks_localized, weekday_labels,
1069 CalendarGrid, StatCalendar, MAX_CALENDAR_YEARS,
1070 };
1071 let grid = self
1072 .mapping
1073 .get_mapping(&crate::aes::Aesthetic::X)
1074 .and_then(|c| self.data.column(c))
1075 .and_then(|col| {
1076 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
1077 });
1078 let Some((grid, clipped)) = grid else {
1079 return self.add_geom_with(tile).stat(StatCalendar {
1081 grid: None,
1082 monday_first,
1083 });
1084 };
1085 if clipped {
1086 self.warnings.push(format!(
1087 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
1088 ));
1089 }
1090 let (xb, _) = month_breaks(&grid);
1091 let yb = if monday_first {
1093 vec![6.0, 4.0, 2.0]
1094 } else {
1095 vec![5.0, 3.0, 1.0]
1096 };
1097 let segs = month_boundaries(&grid);
1098 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
1099 segs.iter().map(|s| Value::Float(f(s))).collect()
1100 };
1101 let boundaries = vec![
1102 ("x".to_string(), col(|s| s.0)),
1103 ("y".to_string(), col(|s| s.1)),
1104 ("xend".to_string(), col(|s| s.2)),
1105 ("yend".to_string(), col(|s| s.3)),
1106 ];
1107 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
1108 let plot = self
1109 .add_geom_with(tile)
1110 .stat(StatCalendar {
1111 grid: Some(grid),
1112 monday_first,
1113 })
1114 .scale_x_continuous(
1115 ScaleContinuous::new()
1116 .with_breaks(xb)
1117 .with_localized_labels(move |l| month_breaks_localized(&grid, l).1)
1119 .with_expand(0.0, 0.0),
1120 )
1121 .scale_y_continuous(
1122 ScaleContinuous::new()
1123 .with_breaks(yb)
1124 .with_localized_labels(weekday_labels)
1125 .with_expand(0.0, 0.0),
1126 );
1127 if segs.is_empty() {
1128 return plot;
1129 }
1130 plot.add_geom_with(crate::geom::segment::GeomCalendarBorder)
1132 .layer_data(boundaries)
1133 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1134 .show_legend(false)
1135 }
1136
1137 pub fn geom_blank(self) -> Self {
1138 self.add_geom(GeomBlank)
1139 }
1140
1141 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
1144 let mut plot = self.add_geom(geom);
1145 if let Some(layer) = plot.layers.last_mut() {
1146 layer.explicit_style = true;
1147 }
1148 plot
1149 }
1150
1151 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
1152 let stat = geom.default_stat();
1153 let position = geom.default_position();
1154 let params = geom.default_params();
1155 self.layers.push(Layer {
1156 data: None,
1157 mapping: Aes::default(),
1158 geom: Box::new(geom),
1159 stat,
1160 position,
1161 params,
1162 show_legend: None,
1163 explicit_style: false,
1164 tooltip_template: None,
1165 });
1166 self
1167 }
1168
1169 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
1173 if let Some(layer) = self.layers.last_mut() {
1174 layer.stat = Box::new(stat);
1175 }
1176 self
1177 }
1178
1179 pub fn position(mut self, pos: impl Position + 'static) -> Self {
1181 if let Some(layer) = self.layers.last_mut() {
1182 layer.position = Box::new(pos);
1183 }
1184 self
1185 }
1186
1187 pub fn layer_data(mut self, data: impl GGData) -> Self {
1189 if let Some(layer) = self.layers.last_mut() {
1190 layer.data = Some(data.into_dataframe());
1191 }
1192 self
1193 }
1194
1195 pub fn layer_aes(mut self, mapping: Aes) -> Self {
1197 self.warnings.extend(mapping.warnings.iter().cloned());
1198 if let Some(layer) = self.layers.last_mut() {
1199 layer.mapping = mapping;
1200 }
1201 self
1202 }
1203
1204 pub fn tooltip(mut self, template: &str) -> Self {
1215 if let Some(layer) = self.layers.last_mut() {
1216 layer.tooltip_template = Some(template.to_string());
1217 }
1218 self
1219 }
1220
1221 pub fn show_legend(mut self, show: bool) -> Self {
1224 if let Some(layer) = self.layers.last_mut() {
1225 layer.show_legend = Some(show);
1226 }
1227 self
1228 }
1229
1230 pub fn decorative(mut self) -> Self {
1257 if let Some(layer) = self.layers.last_mut() {
1258 let geom = std::mem::replace(&mut layer.geom, Box::new(GeomBlank));
1259 layer.geom = Box::new(crate::geom::decorative::Decorative(geom));
1260 if layer.show_legend.is_none() {
1261 layer.show_legend = Some(false);
1262 }
1263 }
1264 self
1265 }
1266
1267 pub fn layer_svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1301 let (name, value) = (name.into(), value.into());
1302 let Some(idx) = self.layers.len().checked_sub(1) else {
1303 self.warnings.push(format!(
1304 "layer_svg_attr(\"{name}\") called before any layer was added — ignored"
1305 ));
1306 return self;
1307 };
1308 if let Some((n, v)) =
1309 crate::render::svg_backend::validate_layer_svg_attr(&name, &value, &mut self.warnings)
1310 {
1311 self.layer_svg_attrs
1312 .retain(|(i, k, _)| !(*i == idx && *k == n));
1313 self.layer_svg_attrs.push((idx, n, v));
1314 }
1315 self
1316 }
1317
1318 pub fn layer_svg_attrs<N: Into<String>, V: Into<String>>(
1320 self,
1321 attrs: impl IntoIterator<Item = (N, V)>,
1322 ) -> Self {
1323 attrs
1324 .into_iter()
1325 .fold(self, |p, (n, v)| p.layer_svg_attr(n, v))
1326 }
1327
1328 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
1331 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1332 self.scales.push(Box::new(s));
1333 self
1334 }
1335
1336 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
1337 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1338 self.scales.push(Box::new(s));
1339 self
1340 }
1341
1342 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1343 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1344 self.scales.push(Box::new(s));
1345 self
1346 }
1347
1348 pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1349 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1350 self.scales.push(Box::new(s));
1351 self
1352 }
1353
1354 pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
1355 self.scales.push(Box::new(s));
1356 self
1357 }
1358
1359 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
1360 self.scales.push(Box::new(s));
1361 self
1362 }
1363
1364 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1365 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
1366 self.scale_color(s)
1367 }
1368
1369 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1370 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
1371 self.scale_fill(s)
1372 }
1373
1374 pub fn scale_colour_identity(self) -> Self {
1377 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Color);
1378 self.scale_color(s)
1379 }
1380
1381 pub fn scale_color_identity(self) -> Self {
1383 self.scale_colour_identity()
1384 }
1385
1386 pub fn scale_fill_identity(self) -> Self {
1389 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Fill);
1390 self.scale_fill(s)
1391 }
1392
1393 pub fn scale_color_viridis(self) -> Self {
1394 use crate::scale::color::ScaleColorDiscrete;
1395 use crate::scale::palettes::PaletteName;
1396 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
1397 .with_named_palette(&PaletteName::Viridis);
1398 self.scale_color(s)
1399 }
1400
1401 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1402 use crate::scale::color::ScaleColorDiscrete;
1403 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
1404 self.scale_color(s)
1405 }
1406
1407 pub fn scale_color_gradient(
1408 self,
1409 low: crate::scale::color::RGBAColor,
1410 high: crate::scale::color::RGBAColor,
1411 ) -> Self {
1412 use crate::scale::color::ScaleColorContinuous;
1413 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
1414 self.scale_color(s)
1415 }
1416
1417 pub fn scale_fill_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::Fill).with_colors(low, high);
1424 self.scale_fill(s)
1425 }
1426
1427 pub fn scale_color_gradient2(
1428 self,
1429 low: crate::scale::color::RGBAColor,
1430 mid: crate::scale::color::RGBAColor,
1431 high: crate::scale::color::RGBAColor,
1432 ) -> Self {
1433 use crate::scale::gradient::ScaleColorGradient2;
1434 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
1435 self.scale_color(s)
1436 }
1437
1438 pub fn scale_fill_gradient2(
1439 self,
1440 low: crate::scale::color::RGBAColor,
1441 mid: crate::scale::color::RGBAColor,
1442 high: crate::scale::color::RGBAColor,
1443 ) -> Self {
1444 use crate::scale::gradient::ScaleColorGradient2;
1445 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
1446 self.scale_fill(s)
1447 }
1448
1449 pub fn scale_fill_viridis(self) -> Self {
1450 use crate::scale::color::ScaleColorDiscrete;
1451 use crate::scale::palettes::PaletteName;
1452 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
1453 .with_named_palette(&PaletteName::Viridis);
1454 self.scale_fill(s)
1455 }
1456
1457 pub fn scale_color_viridis_c(self) -> Self {
1459 use crate::scale::gradient_n::ScaleColorGradientN;
1460 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
1461 self.scale_color(s)
1462 }
1463
1464 pub fn scale_fill_viridis_c(self) -> Self {
1466 use crate::scale::gradient_n::ScaleColorGradientN;
1467 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
1468 self.scale_fill(s)
1469 }
1470
1471 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1473 use crate::scale::gradient_n::ScaleColorGradientN;
1474 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
1475 self.scale_color(s)
1476 }
1477
1478 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1480 use crate::scale::gradient_n::ScaleColorGradientN;
1481 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
1482 self.scale_fill(s)
1483 }
1484
1485 pub fn scale_color_steps(
1488 self,
1489 low: crate::scale::color::RGBAColor,
1490 high: crate::scale::color::RGBAColor,
1491 n_bins: usize,
1492 ) -> Self {
1493 let s = crate::scale::steps::ScaleColorSteps::two(
1494 crate::aes::Aesthetic::Color,
1495 (low.r, low.g, low.b),
1496 (high.r, high.g, high.b),
1497 n_bins,
1498 );
1499 self.scale_color(s)
1500 }
1501
1502 pub fn scale_color_stepsn(
1504 self,
1505 stops: Vec<crate::scale::color::RGBAColor>,
1506 n_bins: usize,
1507 ) -> Self {
1508 let s = crate::scale::steps::ScaleColorSteps::new(
1509 crate::aes::Aesthetic::Color,
1510 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
1511 n_bins,
1512 );
1513 self.scale_color(s)
1514 }
1515
1516 pub fn scale_color_fermenter(
1518 self,
1519 name: crate::scale::palettes::PaletteName,
1520 n_bins: usize,
1521 ) -> Self {
1522 let stops = crate::scale::palettes::palette(&name)
1523 .iter()
1524 .map(|c| (c.r, c.g, c.b))
1525 .collect();
1526 let s =
1527 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
1528 self.scale_color(s)
1529 }
1530
1531 pub fn scale_fill_steps(
1533 self,
1534 low: crate::scale::color::RGBAColor,
1535 high: crate::scale::color::RGBAColor,
1536 n_bins: usize,
1537 ) -> Self {
1538 let s = crate::scale::steps::ScaleColorSteps::two(
1539 crate::aes::Aesthetic::Fill,
1540 (low.r, low.g, low.b),
1541 (high.r, high.g, high.b),
1542 n_bins,
1543 );
1544 self.scale_fill(s)
1545 }
1546
1547 pub fn scale_fill_binned(
1558 self,
1559 breaks: Vec<f64>,
1560 low: crate::scale::color::RGBAColor,
1561 high: crate::scale::color::RGBAColor,
1562 ) -> Self {
1563 let s = crate::scale::binned::ScaleFillBinned::two(
1564 crate::aes::Aesthetic::Fill,
1565 breaks,
1566 (low.r, low.g, low.b),
1567 (high.r, high.g, high.b),
1568 );
1569 self.scale_fill(s)
1570 }
1571
1572 pub fn scale_fill_binned_with(
1576 self,
1577 breaks: Vec<f64>,
1578 colors: Vec<crate::scale::color::RGBAColor>,
1579 ) -> Self {
1580 let s = crate::scale::binned::ScaleFillBinned::new(
1581 crate::aes::Aesthetic::Fill,
1582 breaks,
1583 colors.iter().map(|c| (c.r, c.g, c.b)).collect(),
1584 );
1585 self.scale_fill(s)
1586 }
1587
1588 pub fn scale_fill_fermenter(
1590 self,
1591 name: crate::scale::palettes::PaletteName,
1592 n_bins: usize,
1593 ) -> Self {
1594 let stops = crate::scale::palettes::palette(&name)
1595 .iter()
1596 .map(|c| (c.r, c.g, c.b))
1597 .collect();
1598 let s =
1599 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
1600 self.scale_fill(s)
1601 }
1602
1603 pub fn scale_fill_discrete_sorted(self) -> Self {
1608 use crate::scale::color::ScaleColorDiscrete;
1609 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
1610 }
1611
1612 pub fn scale_color_discrete_sorted(self) -> Self {
1615 use crate::scale::color::ScaleColorDiscrete;
1616 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
1617 }
1618
1619 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1620 use crate::scale::color::ScaleColorDiscrete;
1621 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
1622 self.scale_fill(s)
1623 }
1624
1625 pub fn scale_linetype_manual(
1626 self,
1627 values: Vec<(&str, crate::render::backend::Linetype)>,
1628 ) -> Self {
1629 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
1630 self.scale_color(s)
1631 }
1632
1633 pub fn scale_shape_manual(
1634 self,
1635 values: Vec<(&str, crate::render::backend::PointShape)>,
1636 ) -> Self {
1637 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
1638 self.scale_color(s)
1639 }
1640
1641 pub fn scale_color_grey(self) -> Self {
1642 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
1643 self.scale_color(s)
1644 }
1645
1646 pub fn scale_fill_grey(self) -> Self {
1647 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
1648 self.scale_fill(s)
1649 }
1650
1651 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1652 self.scale_color(s)
1653 }
1654
1655 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1656 self.scale_fill(s)
1657 }
1658
1659 pub fn scale_x_reverse(self) -> Self {
1660 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1661 }
1662
1663 pub fn scale_y_reverse(self) -> Self {
1664 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1665 }
1666
1667 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1668 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1669 self.scales.push(Box::new(s));
1670 self
1671 }
1672
1673 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1674 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1675 self.scales.push(Box::new(s));
1676 self
1677 }
1678
1679 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
1680 self.scales.push(Box::new(s));
1681 self
1682 }
1683
1684 pub fn scale_size_area(mut self, max_size: f64) -> Self {
1688 self.scales.push(Box::new(
1689 crate::scale::size::ScaleSizeContinuous::new().area(max_size),
1690 ));
1691 self
1692 }
1693
1694 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
1695 self.scales.push(Box::new(s));
1696 self
1697 }
1698
1699 pub fn scale_linewidth(mut self, s: crate::scale::linewidth::ScaleLinewidthContinuous) -> Self {
1703 self.scales.push(Box::new(s));
1704 self
1705 }
1706
1707 pub fn xlim(self, min: f64, max: f64) -> Self {
1708 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
1709 }
1710
1711 pub fn ylim(self, min: f64, max: f64) -> Self {
1712 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
1713 }
1714
1715 pub fn scale_x_log10(self) -> Self {
1716 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1717 }
1718
1719 pub fn scale_y_log10(self) -> Self {
1720 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1721 }
1722
1723 pub fn scale_x_sqrt(self) -> Self {
1724 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1725 }
1726
1727 pub fn scale_y_sqrt(self) -> Self {
1728 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1729 }
1730
1731 pub fn scale_x_log2(self) -> Self {
1732 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1733 }
1734
1735 pub fn scale_y_log2(self) -> Self {
1736 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1737 }
1738
1739 pub fn scale_x_ln(self) -> Self {
1740 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1741 }
1742
1743 pub fn scale_y_ln(self) -> Self {
1744 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1745 }
1746
1747 pub fn scale_x_logit(self) -> Self {
1749 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1750 }
1751
1752 pub fn scale_y_logit(self) -> Self {
1753 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1754 }
1755
1756 pub fn scale_x_probit(self) -> Self {
1758 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1759 }
1760
1761 pub fn scale_y_probit(self) -> Self {
1762 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1763 }
1764
1765 pub fn scale_x_pseudo_log(self) -> Self {
1767 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1768 }
1769
1770 pub fn scale_y_pseudo_log(self) -> Self {
1771 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1772 }
1773
1774 pub fn scale_x_reciprocal(self) -> Self {
1776 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1777 }
1778
1779 pub fn scale_y_reciprocal(self) -> Self {
1780 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1781 }
1782
1783 pub fn scale_x_exp(self) -> Self {
1785 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1786 }
1787
1788 pub fn scale_y_exp(self) -> Self {
1789 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1790 }
1791
1792 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
1794 self.scale_x_continuous(
1795 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1796 )
1797 }
1798
1799 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
1800 self.scale_y_continuous(
1801 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1802 )
1803 }
1804
1805 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
1808 self.facet = Facet::Wrap {
1809 var: var.to_string(),
1810 ncol,
1811 scales: FacetScales::Fixed,
1812 labeller: FacetLabeller::default(),
1813 };
1814 self
1815 }
1816
1817 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
1818 self.facet = Facet::Wrap {
1819 var: var.to_string(),
1820 ncol,
1821 scales,
1822 labeller: FacetLabeller::default(),
1823 };
1824 self
1825 }
1826
1827 pub fn facet_wrap_labeller(
1828 mut self,
1829 var: &str,
1830 ncol: Option<usize>,
1831 labeller: FacetLabeller,
1832 ) -> Self {
1833 self.facet = Facet::Wrap {
1834 var: var.to_string(),
1835 ncol,
1836 scales: FacetScales::Fixed,
1837 labeller,
1838 };
1839 self
1840 }
1841
1842 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
1843 self.facet = Facet::Grid {
1844 row_var: row.map(String::from),
1845 col_var: col.map(String::from),
1846 scales: FacetScales::Fixed,
1847 labeller: FacetLabeller::default(),
1848 space: FacetSpace::Fixed,
1849 };
1850 self
1851 }
1852
1853 pub fn facet_grid_free(
1854 mut self,
1855 row: Option<&str>,
1856 col: Option<&str>,
1857 scales: FacetScales,
1858 ) -> Self {
1859 self.facet = Facet::Grid {
1860 row_var: row.map(String::from),
1861 col_var: col.map(String::from),
1862 scales,
1863 labeller: FacetLabeller::default(),
1864 space: FacetSpace::Fixed,
1865 };
1866 self
1867 }
1868
1869 pub fn facet_grid_multi(
1873 mut self,
1874 row: Option<&str>,
1875 cols: &[&str],
1876 scales: FacetScales,
1877 space: FacetSpace,
1878 ) -> Self {
1879 let col_var = if cols.len() > 1 {
1880 let n = self.data.nrows();
1882 let mut combined = Vec::with_capacity(n);
1883 for i in 0..n {
1884 let parts: Vec<String> = cols
1885 .iter()
1886 .map(|c| {
1887 self.data
1888 .column(c)
1889 .and_then(|col| col.get(i))
1890 .map(|v| v.to_group_key())
1891 .unwrap_or_default()
1892 })
1893 .collect();
1894 combined.push(crate::data::Value::Str(parts.join(" . ")));
1895 }
1896 let name = "__facet_cols__".to_string();
1897 self.data.add_column(name.clone(), combined);
1898 Some(name)
1899 } else {
1900 cols.first().map(|s| s.to_string())
1901 };
1902 self.facet = Facet::Grid {
1903 row_var: row.map(String::from),
1904 col_var,
1905 scales,
1906 labeller: FacetLabeller::default(),
1907 space,
1908 };
1909 self
1910 }
1911
1912 pub fn facet_grid_space(
1915 mut self,
1916 row: Option<&str>,
1917 col: Option<&str>,
1918 scales: FacetScales,
1919 space: FacetSpace,
1920 ) -> Self {
1921 self.facet = Facet::Grid {
1922 row_var: row.map(String::from),
1923 col_var: col.map(String::from),
1924 scales,
1925 labeller: FacetLabeller::default(),
1926 space,
1927 };
1928 self
1929 }
1930
1931 pub fn facet_grid_labeller(
1932 mut self,
1933 row: Option<&str>,
1934 col: Option<&str>,
1935 labeller: FacetLabeller,
1936 ) -> Self {
1937 self.facet = Facet::Grid {
1938 row_var: row.map(String::from),
1939 col_var: col.map(String::from),
1940 scales: FacetScales::Fixed,
1941 labeller,
1942 space: FacetSpace::Fixed,
1943 };
1944 self
1945 }
1946
1947 pub fn coord_flip(mut self) -> Self {
1950 self.coord = Box::new(CoordFlip);
1951 self
1952 }
1953
1954 pub fn coord_fixed(mut self, ratio: f64) -> Self {
1955 self.coord = Box::new(CoordFixed::new(ratio));
1956 self
1957 }
1958
1959 #[cfg(feature = "sf")]
1963 pub fn coord_sf(mut self) -> Self {
1964 self.coord = Box::new(crate::coord::sf::CoordSf::new());
1965 self
1966 }
1967
1968 #[cfg(feature = "sf")]
1973 pub fn coord_sf_with(mut self, coord: crate::coord::sf::CoordSf) -> Self {
1974 self.coord = Box::new(coord);
1975 self
1976 }
1977
1978 pub fn svg_root_data(mut self, on: bool) -> Self {
1985 self.svg_root_data = on;
1986 self
1987 }
1988
1989 pub fn svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1999 self.root_attrs.push((name.into(), value.into()));
2000 self
2001 }
2002
2003 pub fn svg_attrs(mut self, attrs: impl IntoIterator<Item = (String, String)>) -> Self {
2005 self.root_attrs.extend(attrs);
2006 self
2007 }
2008
2009 pub fn svg_warnings_attr(mut self, on: bool) -> Self {
2014 self.svg_warnings_attr = on;
2015 self
2016 }
2017
2018 pub fn svg_formatted_values(mut self, on: bool) -> Self {
2027 self.svg_formatted_values = on;
2028 self
2029 }
2030
2031 pub fn svg_title(mut self, title: impl Into<String>) -> Self {
2041 self.svg_title = Some(title.into());
2042 self
2043 }
2044
2045 pub fn svg_desc(mut self, desc: impl Into<String>) -> Self {
2053 self.svg_desc = Some(desc.into());
2054 self
2055 }
2056
2057 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
2062 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
2063 self
2064 }
2065
2066 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
2068 self.coord_trans(None, Some(y))
2069 }
2070
2071 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
2073 self.coord_trans(Some(x), None)
2074 }
2075
2076 pub fn coord_cartesian_zoom(
2078 mut self,
2079 xlim: Option<(f64, f64)>,
2080 ylim: Option<(f64, f64)>,
2081 ) -> Self {
2082 let mut c = CoordCartesian::new();
2083 if let Some((min, max)) = xlim {
2084 c = c.xlim(min, max);
2085 }
2086 if let Some((min, max)) = ylim {
2087 c = c.ylim(min, max);
2088 }
2089 self.coord = Box::new(c);
2090 self
2091 }
2092
2093 pub fn coord_polar(mut self) -> Self {
2094 self.coord = Box::new(CoordPolar::new());
2095 self
2096 }
2097
2098 pub fn coord_radar(mut self) -> Self {
2102 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
2103 self
2104 }
2105
2106 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
2107 self.coord = Box::new(coord);
2108 self
2109 }
2110
2111 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
2112 self.coord = Box::new(coord);
2113 self
2114 }
2115
2116 pub fn theme(mut self, theme: Theme) -> Self {
2121 self.apply_preset(theme);
2122 self
2123 }
2124
2125 fn apply_preset(&mut self, theme: Theme) {
2126 let primary = self.theme.primary;
2127 self.theme = theme;
2128 if self.theme.primary.is_none() {
2129 self.theme.primary = primary;
2130 }
2131 }
2132
2133 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
2137 self.theme.axis_text_x.angle = degrees;
2138 self
2139 }
2140
2141 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
2143 self.theme.axis_text_y.angle = degrees;
2144 self
2145 }
2146
2147 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
2150 self.theme.axis_text_x_dodge = n.max(1);
2151 self
2152 }
2153
2154 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
2157 self.theme.aspect_ratio = Some(ratio);
2158 self
2159 }
2160
2161 pub fn panel_ontop(mut self) -> Self {
2163 self.theme.panel_ontop = true;
2164 self
2165 }
2166
2167 pub fn axis_minor_ticks(mut self) -> Self {
2169 self.theme.axis_minor_ticks = true;
2170 self
2171 }
2172
2173 pub fn axis_text_x_fit(mut self, fit: crate::theme::AxisLabelFit) -> Self {
2178 self.theme.axis_text_x_fit = fit;
2179 self
2180 }
2181
2182 pub fn axis_text_x_check_overlap(mut self, on: bool) -> Self {
2185 self.theme.axis_text_x_check_overlap = on;
2186 self
2187 }
2188
2189 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
2192 self.theme.title_position = pos;
2193 self
2194 }
2195
2196 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
2198 self.theme.tag_position = pos;
2199 self
2200 }
2201
2202 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
2204 self.theme.legend_direction = Some(dir);
2205 self
2206 }
2207
2208 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
2215 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
2216 self
2217 }
2218
2219 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
2221 self.theme.legend_position = pos;
2222 self
2223 }
2224
2225 pub fn legend_bottom_when_narrow(mut self, share: f64) -> Self {
2232 self.legend_bottom_share = Some(share);
2233 self
2234 }
2235
2236 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
2237 self.theme.primary = Some(color);
2238 self
2239 }
2240
2241 pub fn theme_minimal(mut self) -> Self {
2242 self.apply_preset(crate::theme::presets::theme_minimal());
2243 self
2244 }
2245
2246 pub fn theme_bw(mut self) -> Self {
2247 self.apply_preset(crate::theme::presets::theme_bw());
2248 self
2249 }
2250
2251 pub fn theme_gray(mut self) -> Self {
2252 self.apply_preset(crate::theme::presets::theme_gray());
2253 self
2254 }
2255
2256 pub fn theme_classic(mut self) -> Self {
2257 self.apply_preset(crate::theme::presets::theme_classic());
2258 self
2259 }
2260
2261 pub fn theme_pubr(mut self) -> Self {
2263 self.apply_preset(crate::theme::presets::theme_pubr());
2264 self
2265 }
2266
2267 pub fn theme_linedraw(mut self) -> Self {
2268 self.apply_preset(crate::theme::presets::theme_linedraw());
2269 self
2270 }
2271
2272 pub fn theme_light(mut self) -> Self {
2273 self.apply_preset(crate::theme::presets::theme_light());
2274 self
2275 }
2276
2277 pub fn theme_dark(mut self) -> Self {
2278 self.apply_preset(crate::theme::presets::theme_dark());
2279 self
2280 }
2281
2282 pub fn theme_void(mut self) -> Self {
2283 self.apply_preset(crate::theme::presets::theme_void());
2284 self
2285 }
2286
2287 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
2290 self.theme = self.theme.update(update);
2291 self
2292 }
2293
2294 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
2298 self.guide_legend = guide;
2299 self
2300 }
2301
2302 pub fn labs(mut self, labels: Labels) -> Self {
2305 if labels.title.is_some() {
2306 self.labels.title = labels.title;
2307 }
2308 if labels.subtitle.is_some() {
2309 self.labels.subtitle = labels.subtitle;
2310 }
2311 if labels.x.is_some() {
2312 self.labels.x = labels.x;
2313 }
2314 if labels.y.is_some() {
2315 self.labels.y = labels.y;
2316 }
2317 if labels.caption.is_some() {
2318 self.labels.caption = labels.caption;
2319 }
2320 if labels.tag.is_some() {
2321 self.labels.tag = labels.tag;
2322 }
2323 self
2324 }
2325
2326 pub fn title(mut self, title: &str) -> Self {
2327 self.labels.title = Some(title.to_string());
2328 self
2329 }
2330
2331 pub fn tag(mut self, tag: &str) -> Self {
2334 self.labels.tag = Some(tag.to_string());
2335 self
2336 }
2337
2338 pub fn subtitle(mut self, subtitle: &str) -> Self {
2339 self.labels.subtitle = Some(subtitle.to_string());
2340 self
2341 }
2342
2343 pub fn xlab(mut self, label: &str) -> Self {
2344 self.labels.x = Some(label.to_string());
2345 self
2346 }
2347
2348 pub fn ylab(mut self, label: &str) -> Self {
2349 self.labels.y = Some(label.to_string());
2350 self
2351 }
2352
2353 pub fn caption(mut self, caption: &str) -> Self {
2354 self.labels.caption = Some(caption.to_string());
2355 self
2356 }
2357
2358 pub fn annotate(mut self, annotation: Annotation) -> Self {
2362 self.annotations.push(annotation);
2363 self
2364 }
2365
2366 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
2368 self.annotate(Annotation::text(label, x, y))
2369 }
2370
2371 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
2373 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
2374 }
2375
2376 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
2378 self.annotate(Annotation::segment(x, y, xend, yend))
2379 }
2380
2381 pub fn locale(mut self, locale: crate::locale::Locale) -> Self {
2404 self.locale = Some(locale);
2405 self
2406 }
2407
2408 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
2412 PlotBuilder::build(self)
2413 }
2414
2415 pub fn build(self) -> crate::build::BuiltPlot {
2419 self.try_build().expect("plot build failed")
2420 }
2421
2422 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2426 pub fn save(self, path: &str) -> Result<(), GGError> {
2427 self.save_with_size(path, 800, 600)
2428 }
2429
2430 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2432 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
2433 let (built, layout) = self.prepare(w, h)?;
2434
2435 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
2437
2438 match ext.as_str() {
2439 "svg" => {
2440 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
2441 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2442 }
2443 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
2444 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
2445 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2446 }
2447 _ => {
2448 return Err(GGError::UnsupportedFormat(ext));
2449 }
2450 }
2451
2452 Ok(())
2453 }
2454
2455 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2461 pub fn render_svg(self) -> Result<String, GGError> {
2462 self.render_svg_with_size(800, 600)
2463 }
2464
2465 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2469 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2470 let (built, layout) = self.prepare(w, h)?;
2471 let mut buf = String::new();
2472 {
2473 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
2474 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2475 }
2476 Ok(buf)
2477 }
2478
2479 pub fn render_svg_native(self) -> Result<String, GGError> {
2483 self.render_svg_native_with_size(800, 600)
2484 }
2485
2486 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2494 Ok(self.render_native(w, h)?.0.finish())
2495 }
2496
2497 pub fn render_svg_native_with_warnings(
2502 self,
2503 w: u32,
2504 h: u32,
2505 ) -> Result<(String, Vec<String>), GGError> {
2506 let (backend, warnings, _) = self.render_native(w, h)?;
2507 Ok((backend.finish(), warnings))
2508 }
2509
2510 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
2522 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
2523 }
2524
2525 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
2529 let (backend, _, pa) = self.render_native(w, h)?;
2530 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
2531 }
2532
2533 fn render_native(
2537 self,
2538 w: u32,
2539 h: u32,
2540 ) -> Result<
2541 (
2542 crate::render::svg_backend::SvgBackend,
2543 Vec<String>,
2544 crate::render::Rect,
2545 ),
2546 GGError,
2547 > {
2548 let svg_root_data = self.svg_root_data;
2549 let root_attrs = self.root_attrs.clone();
2550 let svg_warnings_attr = self.svg_warnings_attr;
2551 let svg_formatted_values = self.svg_formatted_values;
2552 let svg_title = self.svg_title.clone();
2553 let svg_desc = self.svg_desc.clone();
2554 let (built, layout) = self.prepare(w, h)?;
2555 let pa = layout.plot_area.clone();
2556 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
2557 backend.set_formatted_values(svg_formatted_values);
2558 backend.set_svg_title(svg_title.clone());
2559 backend.set_svg_desc(svg_desc.clone());
2560 let existing_root_attrs = if svg_root_data {
2561 let attrs = crate::render::svg_backend::root_data_attrs(&built);
2562 backend.set_root_attrs(attrs.clone());
2563 let mut reserved = attrs;
2567 reserved.push(("data-plot".to_string(), String::new()));
2568 reserved
2569 } else {
2570 backend.without_plot_attr();
2571 Vec::new()
2572 };
2573 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2574 let mut warnings = built.warnings;
2575 warnings.extend(backend.take_warnings());
2576
2577 let mut reserved = existing_root_attrs;
2578 if svg_warnings_attr {
2579 reserved.push(("data-warnings".to_string(), String::new()));
2580 }
2581 if svg_title.is_some() || svg_desc.is_some() {
2586 reserved.push(("role".to_string(), String::new()));
2587 }
2588 if svg_title.is_some() {
2589 reserved.push(("aria-labelledby".to_string(), String::new()));
2590 }
2591 if svg_desc.is_some() {
2592 reserved.push(("aria-describedby".to_string(), String::new()));
2593 }
2594 let extra = crate::render::svg_backend::validate_root_attrs(
2595 &root_attrs,
2596 &reserved,
2597 "plot",
2598 &mut warnings,
2599 );
2600 backend.extend_root_attrs(extra);
2601 if svg_warnings_attr {
2602 backend.extend_root_attrs(vec![(
2603 "data-warnings".to_string(),
2604 crate::render::svg_backend::warnings_json(&warnings),
2605 )]);
2606 }
2607 Ok((backend, warnings, pa))
2608 }
2609
2610 #[cfg(feature = "canvas")]
2615 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
2616 let (built, layout) = self.prepare(w, h)?;
2617 let mut backend =
2618 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2619 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2620 let (ow, oh) = backend.dimensions();
2621 Ok((ow, oh, backend.into_rgba()))
2622 }
2623
2624 #[cfg(feature = "canvas")]
2628 pub fn render_rgba_area_with_size(
2629 self,
2630 w: u32,
2631 h: u32,
2632 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
2633 let (built, layout) = self.prepare(w, h)?;
2634 let pa = layout.plot_area.clone();
2635 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
2636 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2637 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
2638 }
2639
2640 #[cfg(feature = "canvas")]
2644 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2645 let (built, layout) = self.prepare(w, h)?;
2646 let mut backend =
2647 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2648 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2649 backend.into_png().map_err(GGError::Render)
2650 }
2651
2652 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2659 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
2660 self.render_png_with_size(800, 600)
2661 }
2662
2663 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2666 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2667 let (built, layout) = self.prepare(w, h)?;
2668
2669 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
2672 {
2673 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
2674 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2675 }
2676
2677 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
2678 GGError::Render(RenderError::BackendError(
2679 "PNG buffer size mismatch".to_string(),
2680 ))
2681 })?;
2682 let mut out = std::io::Cursor::new(Vec::new());
2683 img.write_to(&mut out, image::ImageOutputFormat::Png)
2684 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2685 Ok(out.into_inner())
2686 }
2687
2688 pub(crate) fn prepare(
2690 self,
2691 w: u32,
2692 h: u32,
2693 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
2694 let (mut built, meta) = self.build_for_render()?;
2695 let layout = Self::layout_built(&mut built, &meta, w, h);
2696 Ok((built, layout))
2697 }
2698
2699 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
2702 let plot = self;
2703
2704 let has_title = plot.labels.title.is_some();
2705 let has_subtitle = plot.labels.subtitle.is_some();
2706 let has_caption = plot.labels.caption.is_some();
2707 let has_legend = plot.has_legend_mapping();
2708 let legend_bottom_share = plot.legend_bottom_share;
2709 let x_label = plot.labels.x.clone();
2710 let y_label = plot.labels.y.clone();
2711
2712 let mut built = PlotBuilder::build(plot)?;
2713
2714 built.theme.resolve_inheritance();
2716
2717 if let Some(ref label) = x_label {
2719 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
2720 s.set_name(label);
2721 }
2722 }
2723 if let Some(ref label) = y_label {
2724 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
2725 s.set_name(label);
2726 }
2727 }
2728
2729 Ok((
2730 built,
2731 RenderMeta {
2732 has_title,
2733 has_subtitle,
2734 has_caption,
2735 has_legend,
2736 legend_bottom_share,
2737 },
2738 ))
2739 }
2740
2741 pub(crate) fn layout_built(
2744 built: &mut crate::build::BuiltPlot,
2745 meta: &RenderMeta,
2746 w: u32,
2747 h: u32,
2748 ) -> PlotLayout {
2749 let RenderMeta {
2750 has_title,
2751 has_subtitle,
2752 has_caption,
2753 has_legend,
2754 legend_bottom_share,
2755 } = *meta;
2756 let x_axis_top = built
2757 .scales
2758 .get(&crate::aes::Aesthetic::X)
2759 .map(|s| s.axis_position_opposite())
2760 .unwrap_or(false);
2761
2762 if let Some(share) = legend_bottom_share {
2765 if has_legend
2766 && matches!(
2767 built.theme.legend_position,
2768 crate::theme::LegendPosition::Right
2769 )
2770 {
2771 let panel_width = |pos| {
2772 let mut theme = built.theme.clone();
2773 theme.legend_position = pos;
2774 PlotLayout::compute_with_extras(
2775 w as f64,
2776 h as f64,
2777 &theme,
2778 has_title,
2779 has_subtitle,
2780 has_caption,
2781 has_legend,
2782 x_axis_top,
2783 None,
2784 None,
2785 crate::render::layout::LayoutExtras::default(),
2786 )
2787 .plot_area
2788 .width
2789 };
2790 let free = panel_width(crate::theme::LegendPosition::None);
2793 if panel_width(crate::theme::LegendPosition::Right) < (1.0 - share) * free {
2794 built.theme.legend_position = crate::theme::LegendPosition::Bottom;
2795 }
2796 }
2797 }
2798
2799 let flipped = built.coord.is_flipped();
2803 let h_aes = if flipped {
2804 crate::aes::Aesthetic::Y
2805 } else {
2806 crate::aes::Aesthetic::X
2807 };
2808 let v_aes = if flipped {
2809 crate::aes::Aesthetic::X
2810 } else {
2811 crate::aes::Aesthetic::Y
2812 };
2813
2814 let drawn_x_labels: Vec<(f64, String)> = built
2822 .scales
2823 .get(&h_aes)
2824 .map(|hs| {
2825 hs.breaks()
2826 .into_iter()
2827 .filter(|(_, label)| !label.is_empty())
2828 .collect()
2829 })
2830 .unwrap_or_default();
2831 let h_is_discrete = built
2832 .scales
2833 .get(&h_aes)
2834 .map(|hs| hs.is_discrete())
2835 .unwrap_or(false);
2836
2837 if built.theme.axis_text_x.visible
2850 && built.theme.axis_text_x.angle.abs() < 1.0
2851 && h_is_discrete
2852 && !drawn_x_labels.is_empty()
2853 {
2854 let font = built.theme.axis_text_x.size;
2855 let max_w = drawn_x_labels
2856 .iter()
2857 .map(|(_, l)| crate::render::text::text_width(l, font))
2858 .fold(0.0_f64, f64::max);
2859 let n = drawn_x_labels.len() as f64;
2860 let spacing = 0.9 * w as f64 / n; if max_w > spacing {
2862 let vertical = max_w * std::f64::consts::FRAC_1_SQRT_2 > spacing;
2866 built.theme.axis_text_x.angle = if vertical { 270.0 } else { -45.0 };
2867 }
2868 }
2869
2870 let x_label_height = if built.theme.axis_text_x.visible
2885 && built.theme.axis_text_x.angle.abs() > 10.0
2886 && !drawn_x_labels.is_empty()
2887 {
2888 let font = built.theme.axis_text_x.size;
2889 let angle = built.theme.axis_text_x.angle;
2890 let dir = if x_axis_top { -1.0 } else { 1.0 };
2891 let (rise, fall) = crate::render::text::rotated_label_rise_fall(
2892 drawn_x_labels.iter().map(|(_, l)| l.as_str()),
2893 font,
2894 angle,
2895 crate::render::text::rotated_anchor_frac(angle, dir),
2896 );
2897 Some(built.theme.legend_spacing / 2.0 + font / 2.0 + rise + fall)
2898 } else {
2899 None
2900 };
2901
2902 let y_label_width = if built.theme.axis_text_y.visible {
2906 built.scales.get(&v_aes).map(|vs| {
2907 let max_ch = vs
2908 .breaks()
2909 .iter()
2910 .map(|(_, l)| l.chars().count())
2911 .max()
2912 .unwrap_or(0);
2913 let measured = max_ch as f64 * built.theme.axis_text_y.size * 0.6 + 4.0;
2914 let default = built.theme.axis_text_y.size * 3.5 + 4.0;
2915 measured.max(default).min(0.35 * w as f64)
2916 })
2917 } else {
2918 None
2919 };
2920
2921 let mut extras = crate::render::layout::LayoutExtras::default();
2932 let can_reserve = built.facet.is_none()
2933 && built.theme.axis_text_x.visible
2934 && built.theme.axis_text_x_fit == crate::theme::AxisLabelFit::Reserve
2935 && !built.coord.is_polar()
2936 && built.coord.supports_axis_reserve();
2937 if can_reserve {
2938 if let Some(hs) = built.scales.get(&h_aes) {
2939 let breaks = hs.breaks();
2940 if let (Some(first), Some(last)) = (breaks.first(), breaks.last()) {
2941 let angle = built.theme.axis_text_x.angle;
2942 let anchor = if angle.abs() > 10.0 {
2943 let dir = if x_axis_top { -1.0 } else { 1.0 };
2944 crate::render::text::rotated_anchor_frac(angle, dir)
2945 } else {
2946 0.5
2947 };
2948 let size = built.theme.axis_text_x.size;
2949 const EDGE: f64 = 1.0;
2950 for _ in 0..2 {
2951 let layout = PlotLayout::compute_with_extras(
2952 w as f64,
2953 h as f64,
2954 &built.theme,
2955 has_title,
2956 has_subtitle,
2957 has_caption,
2958 has_legend,
2959 x_axis_top,
2960 y_label_width,
2961 x_label_height,
2962 extras,
2963 );
2964 let area = &layout.plot_area;
2965 let edge_y = area.y + area.height;
2966 let to_point = |pos: f64| if flipped { (0.0, pos) } else { (pos, 0.0) };
2967 let px_first = built.coord.transform(to_point(first.0), area).0;
2968 let px_last = built.coord.transform(to_point(last.0), area).0;
2969 let bbox_first = crate::render::text::label_bbox(
2970 &first.1, size, px_first, edge_y, angle, anchor,
2971 );
2972 let bbox_last = crate::render::text::label_bbox(
2973 &last.1, size, px_last, edge_y, angle, anchor,
2974 );
2975 let deficit_left = (EDGE - bbox_first[0]).max(0.0);
2976 let deficit_right = (bbox_last[2] - (w as f64 - EDGE)).max(0.0);
2977 if deficit_left <= 0.0 && deficit_right <= 0.0 {
2978 break;
2979 }
2980 let (p_first, p_last) = (first.0, last.0);
2989 if deficit_left > 0.0 && deficit_right > 0.0 {
2990 let (a, b, c, d) = (1.0 - p_first, p_first, 1.0 - p_last, p_last);
2995 let det = a * d - b * c;
2996 if det.abs() > 0.01 {
2997 extras.left +=
2998 ((deficit_left * d + deficit_right * b) / det).max(0.0);
2999 extras.right +=
3000 ((deficit_right * a + deficit_left * c) / det).max(0.0);
3001 } else {
3002 extras.left += deficit_left / (1.0 - p_first).max(0.05);
3006 extras.right += deficit_right / p_last.max(0.05);
3007 }
3008 } else if deficit_left > 0.0 {
3009 extras.left += deficit_left / (1.0 - p_first).max(0.05);
3010 } else if deficit_right > 0.0 {
3011 extras.right += deficit_right / p_last.max(0.05);
3012 }
3013 const MIN_PANEL_WIDTH: f64 = 50.0;
3028 let cap = (w as f64 - MIN_PANEL_WIDTH).max(0.0);
3029 if extras.left + extras.right > cap {
3030 let scale = if extras.left + extras.right > 0.0 {
3031 cap / (extras.left + extras.right)
3032 } else {
3033 0.0
3034 };
3035 extras.left *= scale;
3036 extras.right *= scale;
3037 }
3038 }
3039 }
3040 }
3041 }
3042
3043 if has_legend
3050 && !matches!(
3051 built.theme.legend_position,
3052 crate::theme::LegendPosition::None
3053 )
3054 {
3055 let legend_aes =
3056 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3057 extras.legend_max_key_size = legend_aes
3061 .iter()
3062 .filter_map(|aes| {
3063 let scale = built.scales.get(aes)?;
3064 let breaks = scale.breaks();
3065 Some(crate::guide::legend::size_key_diameter(
3066 aes,
3067 &built.scales,
3068 scale,
3069 &breaks,
3070 ))
3071 })
3072 .fold(0.0_f64, f64::max);
3073 let has_wrap_guide = legend_aes.iter().any(|a| {
3084 let is_discrete = built
3085 .scales
3086 .get(a)
3087 .map(|s| s.is_discrete())
3088 .unwrap_or(false);
3089 is_discrete || !crate::guide::legend::is_colorbar_aesthetic(a)
3090 });
3091 if has_wrap_guide {
3092 let layout0 = PlotLayout::compute_with_extras(
3093 w as f64,
3094 h as f64,
3095 &built.theme,
3096 has_title,
3097 has_subtitle,
3098 has_caption,
3099 has_legend,
3100 x_axis_top,
3101 y_label_width,
3102 x_label_height,
3103 extras,
3104 );
3105 let (legend_x0, _, mut is_horizontal) = crate::guide::legend::legend_position(
3115 &built.theme,
3116 &layout0.plot_area,
3117 extras.legend_extra_rows,
3118 (0.0, 0.0),
3119 &layout0.total,
3120 );
3121 if let Some(dir) = built.theme.legend_direction {
3122 is_horizontal = matches!(dir, crate::theme::LegendDirection::Horizontal);
3123 }
3124 let avail_width =
3125 (layout0.plot_area.x + layout0.plot_area.width - legend_x0).max(0.0);
3126 let mut legend_warnings = Vec::new();
3127 let mut plans = crate::guide::legend::plan_all_legends(
3128 &built.scales,
3129 &built.guide_legend,
3130 &built.theme,
3131 &built.suppressed_aes,
3132 avail_width,
3133 layout0.plot_area.height,
3134 is_horizontal,
3135 &mut legend_warnings,
3136 );
3137 built.warnings.extend(legend_warnings);
3138
3139 if !plans.is_empty() {
3140 extras.legend_extra_rows = if is_horizontal {
3146 plans
3147 .values()
3148 .map(|p| p.rows.saturating_sub(1))
3149 .max()
3150 .unwrap_or(0)
3151 } else {
3152 plans.values().map(|p| p.rows.saturating_sub(1)).sum()
3153 };
3154 extras.legend_extra_width = plans
3161 .values()
3162 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3163 .fold(0.0_f64, f64::max);
3164
3165 if !is_horizontal {
3177 let final_layout = PlotLayout::compute_with_extras(
3178 w as f64,
3179 h as f64,
3180 &built.theme,
3181 has_title,
3182 has_subtitle,
3183 has_caption,
3184 has_legend,
3185 x_axis_top,
3186 y_label_width,
3187 x_label_height,
3188 extras,
3189 );
3190 let (legend_x_final, _, _) = crate::guide::legend::legend_position(
3196 &built.theme,
3197 &final_layout.plot_area,
3198 extras.legend_extra_rows,
3199 (0.0, 0.0),
3200 &final_layout.total,
3201 );
3202 let swatch_and_gap =
3203 built.theme.legend_key_width + built.theme.legend_spacing;
3204 let text_size = built.theme.legend_text.size;
3205 fn right_edge_of(
3211 legend_x: f64,
3212 swatch_and_gap: f64,
3213 text_size: f64,
3214 plan: &crate::guide::legend::LegendPlan,
3215 breaks: &[(f64, String)],
3216 ) -> f64 {
3217 plan.entries
3218 .iter()
3219 .filter_map(|e| breaks.get(e.break_idx).map(|(_, l)| (e, l)))
3220 .map(|(e, label)| {
3221 let text =
3222 e.label_override.as_deref().unwrap_or(label.as_str());
3223 legend_x
3224 + e.x_offset
3225 + swatch_and_gap
3226 + crate::render::text::text_width(text, text_size)
3227 })
3228 .fold(0.0_f64, f64::max)
3229 }
3230
3231 let mut updates: Vec<(
3232 crate::aes::Aesthetic,
3233 crate::guide::legend::LegendPlan,
3234 )> = Vec::new();
3235 let mut reduced_aes: Vec<crate::aes::Aesthetic> = Vec::new();
3242 for (aes, plan) in &plans {
3243 if plan.cols <= 1 {
3244 continue;
3245 }
3246 let Some(scale) = built.scales.get(aes) else {
3247 continue;
3248 };
3249 let breaks = scale.breaks();
3250 if right_edge_of(
3251 legend_x_final,
3252 swatch_and_gap,
3253 text_size,
3254 plan,
3255 &breaks,
3256 ) <= w as f64
3257 {
3258 continue; }
3260
3261 let n = breaks.len();
3264 let mut reduced: Vec<crate::guide::legend::LegendPlan> = Vec::new();
3265 for c in (1..plan.cols).rev() {
3266 let mut candidate_guide = built.guide_legend.clone();
3267 candidate_guide.ncol = Some(c);
3268 candidate_guide.nrow = None;
3269 let mut tmp_warnings = Vec::new();
3270 let candidate_guide =
3271 candidate_guide.validated(n, &mut tmp_warnings);
3272 if let Some(cand) = crate::guide::legend::plan_legend(
3273 &breaks,
3274 &candidate_guide,
3275 &built.theme,
3276 avail_width,
3277 layout0.plot_area.height,
3278 false,
3279 ) {
3280 reduced.push(cand);
3281 }
3282 }
3283 if let Some(fits) = reduced.iter().find(|c| {
3284 right_edge_of(legend_x_final, swatch_and_gap, text_size, c, &breaks)
3285 <= w as f64
3286 }) {
3287 extras.legend_extra_rows =
3288 extras.legend_extra_rows.max(fits.rows.saturating_sub(1));
3289 updates.push((aes.clone(), fits.clone()));
3290 reduced_aes.push(aes.clone());
3291 continue;
3292 }
3293
3294 let base = reduced.last().cloned().unwrap_or_else(|| plan.clone());
3299 extras.legend_extra_rows =
3300 extras.legend_extra_rows.max(base.rows.saturating_sub(1));
3301 let mut truncated = base.clone();
3302 for e in &mut truncated.entries {
3303 let Some((_, label)) = breaks.get(e.break_idx) else {
3304 continue;
3305 };
3306 let avail = (w as f64
3307 - (legend_x_final + e.x_offset + swatch_and_gap))
3308 .max(0.0);
3309 if let Some(short) =
3310 crate::render::text::truncate_to_width(label, avail, text_size)
3311 {
3312 e.label_override = Some(short);
3313 }
3314 }
3315
3316 let truncated_edge = right_edge_of(
3320 legend_x_final,
3321 swatch_and_gap,
3322 text_size,
3323 &truncated,
3324 &breaks,
3325 );
3326 if truncated_edge > w as f64 {
3327 built.warnings.push(format!(
3328 "legend: the {}-column grid still doesn't fit the {w}px chart (needs about {} px)",
3329 truncated.cols,
3330 truncated_edge.ceil() as i64
3331 ));
3332 }
3333 updates.push((aes.clone(), truncated));
3334 }
3335 for (aes, new_plan) in &updates {
3336 plans.insert(aes.clone(), new_plan.clone());
3337 }
3338
3339 if !reduced_aes.is_empty() {
3380 let original_reserve = extras.legend_extra_width;
3381 let tighter_reserve = plans
3382 .values()
3383 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3384 .fold(0.0_f64, f64::max);
3385 if tighter_reserve < original_reserve {
3386 let mut retry_extras = extras;
3387 retry_extras.legend_extra_width = tighter_reserve;
3388 let retry_layout = PlotLayout::compute_with_extras(
3389 w as f64,
3390 h as f64,
3391 &built.theme,
3392 has_title,
3393 has_subtitle,
3394 has_caption,
3395 has_legend,
3396 x_axis_top,
3397 y_label_width,
3398 x_label_height,
3399 retry_extras,
3400 );
3401 let (retry_x, _, _) = crate::guide::legend::legend_position(
3407 &built.theme,
3408 &retry_layout.plot_area,
3409 retry_extras.legend_extra_rows,
3410 (0.0, 0.0),
3411 &retry_layout.total,
3412 );
3413 let all_still_fit = plans.iter().all(|(aes, plan)| {
3414 let Some(scale) = built.scales.get(aes) else {
3415 return true;
3416 };
3417 let breaks = scale.breaks();
3418 right_edge_of(retry_x, swatch_and_gap, text_size, plan, &breaks)
3419 <= w as f64
3420 });
3421 if all_still_fit {
3422 extras.legend_extra_width = tighter_reserve;
3423 }
3424 }
3425 }
3426 }
3427 for (aes, plan) in &plans {
3428 if let Some(ov) = &plan.overflow {
3429 let n = built
3430 .scales
3431 .get(aes)
3432 .map(|s| s.breaks().len())
3433 .unwrap_or(ov.dropped);
3434 let shown = n.saturating_sub(ov.dropped);
3435 built.warnings.push(format!(
3436 "legend: {} of {n} entries don't fit the chart; the legend shows {shown} and \"+{} more\"",
3437 ov.dropped, ov.dropped
3438 ));
3439 }
3440 }
3441 }
3442 built.legend_plans = plans;
3448 }
3449 }
3450
3451 if has_legend
3462 && !matches!(
3463 built.theme.legend_position,
3464 crate::theme::LegendPosition::None
3465 )
3466 {
3467 let is_horizontal = match built.theme.legend_direction {
3468 Some(dir) => matches!(dir, crate::theme::LegendDirection::Horizontal),
3469 None => matches!(
3470 built.theme.legend_position,
3471 crate::theme::LegendPosition::Top | crate::theme::LegendPosition::Bottom
3472 ),
3473 };
3474 if is_horizontal {
3475 let legend_aes =
3476 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
3477 let continuous_colourbar = legend_aes.iter().find_map(|aes| {
3478 if !matches!(
3479 aes,
3480 crate::aes::Aesthetic::Color | crate::aes::Aesthetic::Fill
3481 ) {
3482 return None;
3483 }
3484 let scale = built.scales.get(aes)?;
3485 if scale.is_discrete() {
3486 return None;
3487 }
3488 Some(scale)
3489 });
3490 if let Some(scale) = continuous_colourbar {
3491 let title = built
3492 .guide_legend
3493 .title
3494 .as_deref()
3495 .unwrap_or_else(|| scale.name());
3496 let bar_height = crate::guide::legend::continuous_horizontal_bar_height(
3497 &built.theme,
3498 !title.is_empty(),
3499 );
3500 let key_h = built
3501 .theme
3502 .legend_key_height
3503 .max(extras.legend_max_key_size);
3504 extras.continuous_horizontal_legend_extra_height =
3505 (bar_height - key_h).max(0.0);
3506 }
3507 }
3508 }
3509
3510 PlotLayout::compute_with_extras(
3511 w as f64,
3512 h as f64,
3513 &built.theme,
3514 has_title,
3515 has_subtitle,
3516 has_caption,
3517 has_legend,
3518 x_axis_top,
3519 y_label_width,
3520 x_label_height,
3521 extras,
3522 )
3523 }
3524
3525 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3528 fn render_into<DB>(
3529 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
3530 built: &crate::build::BuiltPlot,
3531 layout: &PlotLayout,
3532 ) -> Result<(), GGError>
3533 where
3534 DB: plotters::prelude::DrawingBackend,
3535 DB::ErrorType: 'static,
3536 {
3537 area.fill(&plotters::prelude::WHITE)
3538 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3539 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
3540 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
3541 area.present()
3542 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3543 Ok(())
3544 }
3545
3546 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3549 pub fn ggsave(
3550 self,
3551 path: &str,
3552 width_inches: f64,
3553 height_inches: f64,
3554 dpi: f64,
3555 ) -> Result<(), GGError> {
3556 let w = (width_inches * dpi) as u32;
3557 let h = (height_inches * dpi) as u32;
3558 self.save_with_size(path, w, h)
3559 }
3560
3561 fn has_legend_mapping(&self) -> bool {
3576 use crate::guide::legend::is_legend_aesthetic;
3581 let plot_level = self
3582 .mapping
3583 .mappings
3584 .iter()
3585 .any(|m| is_legend_aesthetic(&m.aesthetic));
3586 plot_level
3587 || self.layers.iter().any(|layer| {
3588 if layer.show_legend == Some(false) {
3589 return false;
3590 }
3591 let effective = if layer.geom.inherit_aes() {
3592 self.mapping.merge(&layer.mapping)
3593 } else {
3594 layer.mapping.clone()
3595 };
3596 effective
3597 .mappings
3598 .iter()
3599 .any(|m| is_legend_aesthetic(&m.aesthetic))
3600 })
3601 }
3602}
3603
3604#[derive(Clone, Copy, Debug)]
3606pub(crate) struct RenderMeta {
3607 pub(crate) has_title: bool,
3608 pub(crate) has_subtitle: bool,
3609 pub(crate) has_caption: bool,
3610 pub(crate) has_legend: bool,
3611 pub(crate) legend_bottom_share: Option<f64>,
3612}
3613
3614#[derive(Debug)]
3616#[non_exhaustive]
3617pub enum GGError {
3618 Render(RenderError),
3619 UnsupportedFormat(String),
3620 Io(std::io::Error),
3621 ValidationError(String),
3622}
3623
3624impl std::fmt::Display for GGError {
3625 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3626 match self {
3627 GGError::Render(e) => write!(f, "Render error: {e}"),
3628 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
3629 GGError::Io(e) => write!(f, "IO error: {e}"),
3630 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
3631 }
3632 }
3633}
3634
3635impl std::error::Error for GGError {}