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::density_ridges::GeomDensityRidges;
27use crate::geom::dotplot::GeomDotplot;
28use crate::geom::errorbar::GeomErrorbar;
29use crate::geom::freqpoly::GeomFreqpoly;
30use crate::geom::hex::GeomHex;
31use crate::geom::histogram::GeomHistogram;
32use crate::geom::jitter::GeomJitter;
33use crate::geom::line::GeomLine;
34use crate::geom::linerange::GeomLinerange;
35use crate::geom::path::GeomPath;
36use crate::geom::point::GeomPoint;
37use crate::geom::pointrange::GeomPointrange;
38use crate::geom::polygon::GeomPolygon;
39use crate::geom::qq::{GeomQQ, GeomQQLine};
40use crate::geom::rect::GeomRect;
41use crate::geom::refline::{GeomAbline, GeomHline, GeomVline};
42use crate::geom::ribbon::GeomRibbon;
43use crate::geom::rug::GeomRug;
44use crate::geom::segment::GeomSegment;
45use crate::geom::smooth::GeomSmooth;
46use crate::geom::spoke::GeomSpoke;
47use crate::geom::step::GeomStep;
48use crate::geom::text::{GeomLabel, GeomText};
49use crate::geom::tile::GeomTile;
50use crate::geom::violin::GeomViolin;
51use crate::geom::{Geom, GeomParams};
52use crate::position::Position;
53use crate::render::layout::PlotLayout;
54#[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
55use crate::render::plotters_backend::PlottersAdapter;
56use crate::render::renderer::PlotRenderer;
57use crate::render::RenderError;
58use crate::scale::continuous::ScaleContinuous;
59use crate::scale::transform::ScaleTransform;
60use crate::scale::Scale;
61use crate::stat::Stat;
62use crate::theme::Theme;
63
64mod stat_layers;
67
68#[derive(Clone, Debug, Default)]
70pub struct Labels {
71 pub title: Option<String>,
72 pub subtitle: Option<String>,
73 pub x: Option<String>,
74 pub y: Option<String>,
75 pub caption: Option<String>,
76 pub tag: Option<String>,
78}
79
80pub struct Layer {
82 pub data: Option<DataFrame>,
83 pub mapping: Aes,
84 pub geom: Box<dyn Geom>,
85 pub stat: Box<dyn Stat>,
86 pub position: Box<dyn Position>,
87 pub params: GeomParams,
88 pub show_legend: Option<bool>,
89 pub explicit_style: bool,
92 pub tooltip_template: Option<String>,
95 pub key_glyph: Option<crate::guide::key::KeyGlyph>,
97 pub show_legend_aes: Vec<(crate::aes::Aesthetic, bool)>,
99}
100
101pub struct GGPlot {
103 pub(crate) data: DataFrame,
104 pub(crate) mapping: Aes,
105 pub(crate) layers: Vec<Layer>,
106 pub(crate) scales: Vec<Box<dyn Scale>>,
107 pub(crate) coord: Box<dyn Coord>,
108 pub(crate) theme: Theme,
109 pub(crate) labels: Labels,
110 pub(crate) facet: Facet,
111 pub(crate) annotations: Vec<Annotation>,
112 pub(crate) guide_legend: crate::guide::config::GuideLegend,
113 pub(crate) warnings: Vec<String>,
116 pub(crate) legend_bottom_share: Option<f64>,
118 pub(crate) legend_auto: Option<LegendAuto>,
120 pub(crate) default_aspect_ratio: Option<f64>,
123 pub(crate) hide_position_guides: bool,
126 pub(crate) hide_y_guides: bool,
128 pub(crate) locale: Option<crate::locale::Locale>,
130 pub(crate) svg_root_data: bool,
135 pub(crate) root_attrs: Vec<(String, String)>,
138 pub(crate) svg_warnings_attr: bool,
142 pub(crate) svg_formatted_values: bool,
146 pub(crate) layer_svg_attrs: Vec<(usize, String, String)>,
150 pub(crate) svg_title: Option<String>,
155 pub(crate) svg_desc: Option<String>,
160}
161
162impl GGPlot {
163 pub fn qplot(data: impl GGData, x: &str, y: Option<&str>) -> Self {
172 let plot = GGPlot::new(data);
173 match y {
174 Some(y) => plot.aes(Aes::new().x(x).y(y)).geom_point(),
175 None => {
176 let text = plot
177 .data
178 .column(x)
179 .and_then(|c| c.iter().find(|v| !v.is_na()))
180 .is_some_and(|v| matches!(v, Value::Str(_) | Value::Bool(_)));
181 let plot = plot.aes(Aes::new().x(x));
182 if text {
183 plot.geom_bar()
184 } else {
185 plot.geom_histogram()
186 }
187 }
188 }
189 }
190
191 pub fn new(data: impl GGData) -> Self {
192 GGPlot {
193 data: data.into_dataframe(),
194 mapping: Aes::default(),
195 layers: Vec::new(),
196 scales: Vec::new(),
197 coord: Box::new(CoordCartesian::new()),
198 theme: Theme::default(),
199 labels: Labels::default(),
200 facet: Facet::default(),
201 annotations: Vec::new(),
202 guide_legend: crate::guide::config::GuideLegend::default(),
203 warnings: Vec::new(),
204 legend_bottom_share: None,
205 legend_auto: None,
206 default_aspect_ratio: None,
207 hide_position_guides: false,
208 hide_y_guides: false,
209 locale: None,
210 svg_root_data: true,
211 root_attrs: Vec::new(),
212 svg_warnings_attr: false,
213 svg_formatted_values: false,
214 layer_svg_attrs: Vec::new(),
215 svg_title: None,
216 svg_desc: None,
217 }
218 }
219
220 pub fn aes(mut self, mapping: Aes) -> Self {
222 self.warnings.extend(mapping.warnings.iter().cloned());
223 self.mapping = mapping;
224 self
225 }
226
227 pub fn geom_point(self) -> Self {
230 self.add_geom(GeomPoint::default())
231 }
232
233 pub fn geom_point_with(self, geom: GeomPoint) -> Self {
234 self.add_geom_with(geom)
235 }
236
237 pub fn geom_line(self) -> Self {
238 self.add_geom(GeomLine::default())
239 }
240
241 pub fn geom_line_with(self, geom: GeomLine) -> Self {
242 self.add_geom_with(geom)
243 }
244
245 pub fn geom_bar(self) -> Self {
246 self.add_geom(GeomBar::default())
247 }
248
249 pub fn geom_bar_with(self, geom: GeomBar) -> Self {
250 self.add_geom_with(geom)
251 }
252
253 pub fn geom_histogram(self) -> Self {
254 self.add_geom(GeomHistogram::default())
255 }
256
257 pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self {
258 self.add_geom_with(geom)
259 }
260
261 pub fn geom_boxplot(self) -> Self {
262 self.add_geom(GeomBoxplot::default())
263 }
264
265 pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self {
266 self.add_geom_with(geom)
267 }
268
269 pub fn geom_smooth(self) -> Self {
270 self.add_geom(GeomSmooth::default())
271 }
272
273 pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self {
274 self.add_geom_with(geom)
275 }
276
277 pub fn geom_col(self) -> Self {
278 self.add_geom(GeomCol::default())
279 }
280
281 pub fn geom_col_with(self, geom: GeomCol) -> Self {
282 self.add_geom_with(geom)
283 }
284
285 pub fn geom_hline(self, yintercept: f64) -> Self {
289 self.add_refline(
290 GeomHline::new(yintercept),
291 false,
292 &[("yintercept", yintercept)],
293 )
294 }
295
296 pub fn geom_hline_with(self, geom: GeomHline) -> Self {
298 let y = geom.yintercept;
299 self.add_refline(geom, true, &[("yintercept", y)])
300 }
301
302 pub fn geom_hline_aes(self, mapping: Aes) -> Self {
326 self.add_geom(GeomHline::mapped()).layer_aes(mapping)
327 }
328
329 pub fn geom_hline_aes_with(self, geom: GeomHline, mapping: Aes) -> Self {
331 self.add_geom_with(geom).layer_aes(mapping)
332 }
333
334 pub fn geom_vline(self, xintercept: f64) -> Self {
337 self.add_refline(
338 GeomVline::new(xintercept),
339 false,
340 &[("xintercept", xintercept)],
341 )
342 }
343
344 pub fn geom_vline_with(self, geom: GeomVline) -> Self {
346 let x = geom.xintercept;
347 self.add_refline(geom, true, &[("xintercept", x)])
348 }
349
350 pub fn geom_vline_aes(self, mapping: Aes) -> Self {
353 self.add_geom(GeomVline::mapped()).layer_aes(mapping)
354 }
355
356 pub fn geom_vline_aes_with(self, geom: GeomVline, mapping: Aes) -> Self {
358 self.add_geom_with(geom).layer_aes(mapping)
359 }
360
361 pub fn geom_abline(self, slope: f64, intercept: f64) -> Self {
364 self.add_refline(
365 GeomAbline::new(slope, intercept),
366 false,
367 &[("slope", slope), ("intercept", intercept)],
368 )
369 }
370
371 pub fn geom_abline_with(self, geom: GeomAbline) -> Self {
373 let (b, a) = (geom.slope, geom.intercept);
374 self.add_refline(geom, true, &[("slope", b), ("intercept", a)])
375 }
376
377 pub fn geom_abline_aes(self, mapping: Aes) -> Self {
380 self.add_geom(GeomAbline::mapped()).layer_aes(mapping)
381 }
382
383 pub fn geom_abline_aes_with(self, geom: GeomAbline, mapping: Aes) -> Self {
385 self.add_geom_with(geom).layer_aes(mapping)
386 }
387
388 fn add_refline(
391 self,
392 geom: impl Geom + 'static,
393 explicit: bool,
394 values: &[(&str, f64)],
395 ) -> Self {
396 let mut data = DataFrame::new();
397 let mut mapping = Aes::new();
398 for (col, v) in values {
399 data.add_column(col.to_string(), vec![Value::Float(*v)]);
400 mapping = match *col {
401 "xintercept" => mapping.xintercept(col),
402 "yintercept" => mapping.yintercept(col),
403 "slope" => mapping.slope(col),
404 _ => mapping.intercept(col),
405 };
406 }
407 let plot = if explicit {
408 self.add_geom_with(geom)
409 } else {
410 self.add_geom(geom)
411 };
412 plot.layer_aes(mapping).layer_data(data)
413 }
414
415 pub fn geom_text(self) -> Self {
416 self.add_geom(GeomText::default())
417 }
418
419 pub fn geom_text_with(self, geom: GeomText) -> Self {
420 self.add_geom_with(geom)
421 }
422
423 #[cfg(feature = "ggpubr")]
428 pub fn stat_cor(self) -> Self {
429 self.geom_text().stat(crate::stat::cor::StatCor::default())
430 }
431
432 #[cfg(feature = "ggpubr")]
435 pub fn stat_cor_with(self, stat: crate::stat::cor::StatCor) -> Self {
436 self.geom_text().stat(stat)
437 }
438
439 #[cfg(feature = "ggpubr")]
444 pub fn stat_compare_means(self) -> Self {
445 self.geom_text()
446 .stat(crate::stat::compare_means::StatCompareMeans::default())
447 }
448
449 #[cfg(feature = "ggpubr")]
452 pub fn stat_compare_means_with(
453 self,
454 stat: crate::stat::compare_means::StatCompareMeans,
455 ) -> Self {
456 self.geom_text().stat(stat)
457 }
458
459 #[cfg(feature = "ggpubr")]
465 pub fn stat_compare_means_pairwise(self, comparisons: &[(&str, &str)]) -> Self {
466 self.stat_compare_means_pairwise_with(
467 crate::stat::compare_means::CompareMethod::Auto,
468 comparisons,
469 )
470 }
471
472 #[cfg(feature = "ggpubr")]
475 pub fn stat_compare_means_pairwise_with(
476 self,
477 method: crate::stat::compare_means::CompareMethod,
478 comparisons: &[(&str, &str)],
479 ) -> Self {
480 let col_for = |a: crate::aes::Aesthetic| {
482 self.mapping
483 .mappings
484 .iter()
485 .find(|m| m.aesthetic == a)
486 .map(|m| m.column.clone())
487 };
488 let (xcol, ycol) = match (
489 col_for(crate::aes::Aesthetic::X),
490 col_for(crate::aes::Aesthetic::Y),
491 ) {
492 (Some(x), Some(y)) => (x, y),
493 _ => return self,
494 };
495 let (xc, yc) = match (self.data.column(&xcol), self.data.column(&ycol)) {
496 (Some(x), Some(y)) => (x, y),
497 _ => return self,
498 };
499
500 let mut groups: Vec<(String, Vec<f64>)> = Vec::new();
502 let mut ymax = f64::NEG_INFINITY;
503 let mut ymin = f64::INFINITY;
504 for (xv, yv) in xc.iter().zip(yc.iter()) {
505 let y = match yv.as_f64() {
506 Some(y) if y.is_finite() => y,
507 _ => continue,
508 };
509 ymax = ymax.max(y);
510 ymin = ymin.min(y);
511 let key = xv.to_group_key();
512 if let Some(g) = groups.iter_mut().find(|(k, _)| *k == key) {
513 g.1.push(y);
514 } else {
515 groups.push((key, vec![y]));
516 }
517 }
518 if groups.len() < 2 {
519 return self;
520 }
521 let step = if ymax > ymin {
522 (ymax - ymin) * 0.12
523 } else {
524 1.0
525 };
526
527 let (mut xmin_v, mut xmax_v, mut y_v, mut label_v) =
529 (Vec::new(), Vec::new(), Vec::new(), Vec::new());
530 let mut placed = 0usize;
531 for (a, b) in comparisons {
532 let ga = groups.iter().find(|(k, _)| k == a).map(|g| &g.1);
533 let gb = groups.iter().find(|(k, _)| k == b).map(|g| &g.1);
534 let (ga, gb) = match (ga, gb) {
535 (Some(x), Some(y)) if x.len() >= 2 && y.len() >= 2 => (x, y),
536 _ => continue,
537 };
538 let p = match crate::stat::compare_means::pairwise_p(method, ga, gb) {
539 Some(p) => p,
540 None => continue,
541 };
542 xmin_v.push(Value::Str((*a).to_string()));
543 xmax_v.push(Value::Str((*b).to_string()));
544 y_v.push(Value::Float(ymax + step * (placed as f64 + 1.0)));
545 label_v.push(Value::Str(crate::stat::cor::format_p_value(p)));
546 placed += 1;
547 }
548 if xmin_v.is_empty() {
549 return self;
550 }
551
552 let data = vec![
553 ("xmin".to_string(), xmin_v),
554 ("xmax".to_string(), xmax_v),
555 ("y".to_string(), y_v),
556 ("label".to_string(), label_v),
557 ];
558 self.add_geom(crate::geom::bracket::GeomBracket::default())
559 .layer_data(data)
560 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
561 }
562
563 pub fn geom_bracket(self, xmin: &str, xmax: &str, y: f64, label: &str) -> Self {
568 self.geom_bracket_many(
569 crate::geom::bracket::GeomBracket::default(),
570 &[(xmin, xmax, y, label)],
571 )
572 }
573
574 pub fn geom_bracket_many(
578 self,
579 geom: crate::geom::bracket::GeomBracket,
580 brackets: &[(&str, &str, f64, &str)],
581 ) -> Self {
582 let xmin = brackets
583 .iter()
584 .map(|b| Value::Str(b.0.to_string()))
585 .collect::<Vec<_>>();
586 let xmax = brackets
587 .iter()
588 .map(|b| Value::Str(b.1.to_string()))
589 .collect::<Vec<_>>();
590 let y = brackets
591 .iter()
592 .map(|b| Value::Float(b.2))
593 .collect::<Vec<_>>();
594 let label = brackets
595 .iter()
596 .map(|b| Value::Str(b.3.to_string()))
597 .collect::<Vec<_>>();
598 let data = vec![
599 ("xmin".to_string(), xmin),
600 ("xmax".to_string(), xmax),
601 ("y".to_string(), y),
602 ("label".to_string(), label),
603 ];
604 self.add_geom(geom)
605 .layer_data(data)
606 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
607 }
608
609 pub fn geom_bracket_table(
618 mut self,
619 table: impl GGData,
620 spec: crate::geom::bracket::BracketTable,
621 ) -> Self {
622 let table = table.into_dataframe();
623 let col_for = |a: crate::aes::Aesthetic| {
624 self.mapping
625 .mappings
626 .iter()
627 .find(|m| m.aesthetic == a)
628 .map(|m| m.column.clone())
629 };
630 let x_levels: Vec<String> = col_for(crate::aes::Aesthetic::X)
633 .and_then(|c| self.data.column(&c))
634 .filter(|col| col.iter().any(|v| matches!(v, Value::Str(_))))
635 .map(|col| {
636 let mut seen = std::collections::HashSet::new();
637 col.iter()
638 .filter(|v| !v.is_na())
639 .map(|v| v.to_group_key())
640 .filter(|k| seen.insert(k.clone()))
641 .collect()
642 })
643 .unwrap_or_default();
644 let y_range = col_for(crate::aes::Aesthetic::Y)
645 .and_then(|c| self.data.column(&c))
646 .and_then(|col| {
647 let (lo, hi) = col
648 .iter()
649 .filter_map(|v| v.as_f64())
650 .filter(|v| v.is_finite())
651 .fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), v| {
652 (lo.min(v), hi.max(v))
653 });
654 (lo <= hi).then_some((lo, hi))
655 });
656 let mut warnings = Vec::new();
657 let resolved = spec.resolve(&table, &x_levels, y_range, &mut warnings);
658 self.warnings.extend(warnings);
659 match resolved {
660 Some(data) => self
661 .add_geom(crate::geom::bracket::GeomBracket::from(spec.geom))
662 .layer_data(data)
663 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label")),
664 None => self,
665 }
666 }
667
668 pub fn geom_text_repel(self) -> Self {
673 self.add_geom(crate::geom::repel::GeomTextRepel::default())
674 }
675
676 pub fn geom_text_repel_with(self, geom: crate::geom::repel::GeomTextRepel) -> Self {
679 self.add_geom_with(geom)
680 }
681
682 pub fn geom_label_repel(self) -> Self {
685 self.add_geom(crate::geom::repel::GeomLabelRepel::default())
686 }
687
688 pub fn geom_label_repel_with(self, geom: crate::geom::repel::GeomLabelRepel) -> Self {
690 self.add_geom_with(geom)
691 }
692
693 pub fn geom_label(self) -> Self {
694 self.add_geom(GeomLabel::default())
695 }
696
697 pub fn geom_label_with(self, geom: GeomLabel) -> Self {
698 self.add_geom_with(geom)
699 }
700
701 pub fn geom_area(self) -> Self {
702 self.add_geom(GeomArea::default())
703 }
704
705 pub fn geom_area_with(self, geom: GeomArea) -> Self {
706 self.add_geom_with(geom)
707 }
708
709 pub fn geom_ribbon(self) -> Self {
710 self.add_geom(GeomRibbon::default())
711 }
712
713 pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self {
714 self.add_geom_with(geom)
715 }
716
717 pub fn geom_errorbar(self) -> Self {
718 self.add_geom(GeomErrorbar::default())
719 }
720
721 pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self {
722 self.add_geom_with(geom)
723 }
724
725 pub fn geom_segment(self) -> Self {
726 self.add_geom(GeomSegment::default())
727 }
728
729 pub fn geom_segment_with(self, geom: GeomSegment) -> Self {
730 self.add_geom_with(geom)
731 }
732
733 pub fn geom_density(self) -> Self {
734 self.add_geom(GeomDensity::default())
735 }
736
737 pub fn geom_density_with(self, geom: GeomDensity) -> Self {
738 self.add_geom_with(geom)
739 }
740
741 pub fn geom_rug(self) -> Self {
742 self.add_geom(GeomRug::default())
743 }
744
745 pub fn geom_rug_with(self, geom: GeomRug) -> Self {
746 self.add_geom_with(geom)
747 }
748
749 pub fn geom_jitter(self) -> Self {
750 self.add_geom(GeomJitter::default())
751 }
752
753 pub fn geom_jitter_with(self, geom: GeomJitter) -> Self {
754 self.add_geom_with(geom)
755 }
756
757 pub fn geom_path(self) -> Self {
758 self.add_geom(GeomPath::default())
759 }
760
761 pub fn geom_path_with(self, geom: GeomPath) -> Self {
762 self.add_geom_with(geom)
763 }
764
765 pub fn geom_function(self, fun: impl Fn(f64) -> f64 + Send + Sync + 'static) -> Self {
769 self.geom_line()
770 .stat(crate::stat::function::StatFunction::new(fun))
771 }
772
773 pub fn geom_function_xlim(
775 self,
776 fun: impl Fn(f64) -> f64 + Send + Sync + 'static,
777 lo: f64,
778 hi: f64,
779 ) -> Self {
780 let range = vec![("x".to_string(), vec![Value::Float(lo), Value::Float(hi)])];
781 self.geom_line()
782 .layer_data(range)
783 .layer_aes(Aes::new().x("x"))
784 .stat(crate::stat::function::StatFunction::new(fun).with_xlim(lo, hi))
785 }
786
787 pub fn stat_connect(self, connection: crate::stat::connect::Connection) -> Self {
790 self.geom_path()
791 .stat(crate::stat::connect::StatConnect::new(connection))
792 }
793
794 pub fn stat_unique(self) -> Self {
797 self.geom_point().stat(crate::stat::unique::StatUnique)
798 }
799
800 pub fn stat_manual(
803 self,
804 fun: impl Fn(&crate::data::DataFrame) -> crate::data::DataFrame + Send + Sync + 'static,
805 ) -> Self {
806 self.geom_point()
807 .stat(crate::stat::manual::StatManual::new(fun))
808 }
809
810 pub fn stat_summary_hex(self, fun: crate::stat::summary::SummaryFun) -> Self {
813 self.geom_hex()
814 .stat(crate::stat::binhex::StatSummaryHex::new(fun))
815 }
816
817 #[cfg(feature = "sf")]
821 pub fn stat_sf_coordinates(self) -> Self {
822 self.geom_point()
823 .stat(crate::stat::sf_coordinates::StatSfCoordinates::default())
824 }
825
826 pub fn stat_ellipse(self) -> Self {
828 self.geom_path()
829 .stat(crate::stat::ellipse::StatEllipse::default())
830 }
831
832 pub fn stat_ellipse_level(self, level: f64) -> Self {
834 self.geom_path()
835 .stat(crate::stat::ellipse::StatEllipse::new(level))
836 }
837
838 #[cfg(feature = "regression")]
841 pub fn stat_quantile(mut self, taus: &[f64]) -> Self {
842 for &tau in taus {
843 self = self
844 .geom_path()
845 .stat(crate::stat::quantile::StatQuantile::new(tau));
846 }
847 self
848 }
849
850 #[cfg(feature = "regression")]
852 pub fn geom_quantile(self) -> Self {
853 self.stat_quantile(&[0.25, 0.5, 0.75])
854 }
855
856 pub fn geom_step(self) -> Self {
857 self.add_geom(GeomStep::default())
858 }
859
860 pub fn geom_step_with(self, geom: GeomStep) -> Self {
861 self.add_geom_with(geom)
862 }
863
864 pub fn geom_freqpoly(self) -> Self {
865 self.add_geom(GeomFreqpoly::default())
866 }
867
868 pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self {
869 self.add_geom_with(geom)
870 }
871
872 pub fn geom_linerange(self) -> Self {
873 self.add_geom(GeomLinerange::default())
874 }
875
876 pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self {
877 self.add_geom_with(geom)
878 }
879
880 pub fn geom_pointrange(self) -> Self {
881 self.add_geom(GeomPointrange::default())
882 }
883
884 pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self {
885 self.add_geom_with(geom)
886 }
887
888 pub fn geom_crossbar(self) -> Self {
889 self.add_geom(GeomCrossbar::default())
890 }
891
892 pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self {
893 self.add_geom_with(geom)
894 }
895
896 pub fn geom_spoke(self) -> Self {
897 self.add_geom(GeomSpoke::default())
898 }
899
900 pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self {
901 self.add_geom_with(geom)
902 }
903
904 pub fn geom_rect(self) -> Self {
905 self.add_geom(GeomRect::default())
906 }
907
908 pub fn geom_rect_with(self, geom: GeomRect) -> Self {
909 self.add_geom_with(geom)
910 }
911
912 pub fn geom_tile(self) -> Self {
913 self.add_geom(GeomTile::default())
914 }
915
916 pub fn geom_tile_with(self, geom: GeomTile) -> Self {
917 self.add_geom_with(geom)
918 }
919
920 pub fn geom_raster(self) -> Self {
922 self.add_geom(crate::geom::raster::GeomRaster::default())
923 }
924
925 pub fn geom_raster_with(self, geom: crate::geom::raster::GeomRaster) -> Self {
926 self.add_geom_with(geom)
927 }
928
929 pub fn geom_polygon(self) -> Self {
930 self.add_geom(GeomPolygon::default())
931 }
932
933 pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self {
934 self.add_geom_with(geom)
935 }
936
937 #[cfg(feature = "sf")]
939 pub fn geom_sf(self) -> Self {
940 self.add_geom(crate::geom::sf::GeomSf::default())
941 }
942
943 #[cfg(feature = "sf")]
944 pub fn geom_sf_with(self, geom: crate::geom::sf::GeomSf) -> Self {
945 self.add_geom_with(geom)
946 }
947
948 #[cfg(feature = "sf")]
951 pub fn geom_sf_text(self) -> Self {
952 self.add_geom(crate::geom::sf_text::GeomSfText::default())
953 }
954
955 #[cfg(feature = "sf")]
956 pub fn geom_sf_text_with(self, geom: crate::geom::sf_text::GeomSfText) -> Self {
957 self.add_geom_with(geom)
958 }
959
960 #[cfg(feature = "sf")]
963 pub fn geom_sf_label(self) -> Self {
964 self.add_geom(crate::geom::sf_text::GeomSfLabel::default())
965 }
966
967 #[cfg(feature = "sf")]
968 pub fn geom_sf_label_with(self, geom: crate::geom::sf_text::GeomSfLabel) -> Self {
969 self.add_geom_with(geom)
970 }
971
972 #[cfg(feature = "sf")]
976 pub fn annotation_scale(self) -> Self {
977 self.add_geom(crate::geom::annotation_scale::GeomAnnotationScale::default())
978 }
979
980 #[cfg(feature = "sf")]
981 pub fn annotation_scale_with(
982 self,
983 geom: crate::geom::annotation_scale::GeomAnnotationScale,
984 ) -> Self {
985 self.add_geom_with(geom)
986 }
987
988 #[cfg(feature = "sf")]
991 pub fn annotation_north_arrow_with(
992 self,
993 geom: crate::geom::annotation_scale::GeomNorthArrow,
994 ) -> Self {
995 self.add_geom_with(geom)
996 }
997
998 #[cfg(feature = "sf")]
1001 pub fn annotation_attribution(self, text: impl Into<String>) -> Self {
1002 self.add_geom(crate::geom::annotation_scale::GeomAttribution::new(text))
1003 }
1004
1005 #[cfg(feature = "tiles")]
1013 pub fn annotation_map_tile(
1014 self,
1015 source: crate::geom::map_tile::TileSource,
1016 fetcher: impl crate::geom::map_tile::TileFetcher + 'static,
1017 ) -> Self {
1018 self.annotation_map_tile_with(crate::geom::map_tile::GeomMapTile::new(source, fetcher))
1019 }
1020
1021 #[cfg(feature = "tiles")]
1024 pub fn annotation_map_tile_with(self, geom: crate::geom::map_tile::GeomMapTile) -> Self {
1025 let attribution = geom.source.attribution.clone();
1026 let mut plot = self.add_geom_with(geom);
1027 if let Some(layer) = plot.layers.pop() {
1029 plot.layers.insert(0, layer);
1030 for (idx, ..) in plot.layer_svg_attrs.iter_mut() {
1031 *idx += 1;
1032 }
1033 }
1034 if attribution.is_empty() {
1035 plot
1036 } else {
1037 plot.annotation_attribution(attribution)
1038 }
1039 }
1040
1041 #[cfg(feature = "sf")]
1042 pub fn annotation_attribution_with(
1043 self,
1044 geom: crate::geom::annotation_scale::GeomAttribution,
1045 ) -> Self {
1046 self.add_geom_with(geom)
1047 }
1048
1049 #[cfg(feature = "sf")]
1057 pub fn bivariate_legend(
1058 self,
1059 geom: crate::geom::bivariate_legend::GeomBivariateLegend,
1060 ) -> Self {
1061 self.add_geom(geom)
1062 }
1063
1064 #[cfg(feature = "sf")]
1069 pub fn bivariate_legend_counts(
1070 self,
1071 geom: crate::geom::bivariate_legend::GeomBivariateLegendCounts,
1072 ) -> Self {
1073 self.add_geom(geom)
1074 }
1075
1076 pub fn geom_curve(self) -> Self {
1077 self.add_geom(GeomCurve::default())
1078 }
1079
1080 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
1081 self.add_geom_with(geom)
1082 }
1083
1084 pub fn geom_violin(self) -> Self {
1085 self.add_geom(GeomViolin::default())
1086 }
1087
1088 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
1089 self.add_geom_with(geom)
1090 }
1091
1092 pub fn geom_density_ridges(self) -> Self {
1096 self.add_geom(GeomDensityRidges::default())
1097 }
1098
1099 pub fn geom_density_ridges_with(self, geom: GeomDensityRidges) -> Self {
1100 self.add_geom_with(geom)
1101 }
1102
1103 pub fn geom_dotplot(self) -> Self {
1104 self.add_geom(GeomDotplot::default())
1105 }
1106
1107 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
1108 self.add_geom_with(geom)
1109 }
1110
1111 pub fn geom_qq(self) -> Self {
1112 self.add_geom(GeomQQ::default())
1113 }
1114
1115 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
1116 self.add_geom_with(geom)
1117 }
1118
1119 pub fn geom_qq_line(self) -> Self {
1120 self.add_geom(GeomQQLine::default())
1121 }
1122
1123 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
1124 self.add_geom_with(geom)
1125 }
1126
1127 pub fn geom_bin2d(self) -> Self {
1128 self.add_geom(GeomBin2d::default())
1129 }
1130
1131 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
1132 self.add_geom_with(geom)
1133 }
1134
1135 pub fn geom_hex(self) -> Self {
1136 self.add_geom(GeomHex::default())
1137 }
1138
1139 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
1140 self.add_geom_with(geom)
1141 }
1142
1143 pub fn geom_count(self) -> Self {
1144 self.add_geom(GeomCount::default())
1145 }
1146
1147 pub fn geom_count_with(self, geom: GeomCount) -> Self {
1148 self.add_geom_with(geom)
1149 }
1150
1151 pub fn geom_contour(self) -> Self {
1152 self.add_geom(GeomContour::default())
1153 }
1154
1155 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
1156 self.add_geom_with(geom)
1157 }
1158
1159 pub fn geom_contour_filled(self) -> Self {
1162 self.add_geom(GeomPolygon {
1163 line_width: 0.0,
1164 alpha: 1.0,
1165 ..GeomPolygon::default()
1166 })
1167 .stat(crate::stat::contour_filled::StatContourFilled::default())
1168 }
1169
1170 pub fn geom_density2d(self) -> Self {
1171 self.add_geom(GeomDensity2d::default())
1172 }
1173
1174 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
1175 self.add_geom_with(geom)
1176 }
1177
1178 pub fn geom_waterfall(self) -> Self {
1184 self.default_styled(|p| {
1185 p.geom_waterfall_with(crate::geom::waterfall::GeomWaterfall::default())
1186 })
1187 }
1188
1189 pub fn geom_waterfall_with(self, geom: crate::geom::waterfall::GeomWaterfall) -> Self {
1196 use crate::scale::color::RGBAColor;
1197 use crate::stat::waterfall::{WATERFALL_DECREASE, WATERFALL_INCREASE, WATERFALL_TOTAL};
1198 let rgba = |(r, g, b): (u8, u8, u8)| RGBAColor::new(r, g, b);
1199 let mut values = vec![
1200 (WATERFALL_INCREASE, rgba(geom.up)),
1201 (WATERFALL_DECREASE, rgba(geom.down)),
1202 ];
1203 if geom.has_totals() {
1204 values.push((WATERFALL_TOTAL, rgba(geom.total)));
1205 }
1206 let fill_mapped = self
1207 .mapping
1208 .get_mapping(&crate::aes::Aesthetic::Fill)
1209 .is_some();
1210 let has_fill_scale = self
1211 .scales
1212 .iter()
1213 .any(|s| s.aesthetic() == crate::aes::Aesthetic::Fill);
1214 let mut plot = self.add_geom_with(geom);
1215 if !fill_mapped {
1216 plot = plot.layer_aes(Aes::new().after_stat_fill("direction"));
1217 if !has_fill_scale {
1218 plot = plot.scale_fill_manual(values);
1219 }
1220 }
1221 plot
1222 }
1223
1224 pub fn geom_pareto(self) -> Self {
1228 self.add_pareto(crate::geom::pareto::GeomPareto::default(), false)
1229 }
1230
1231 pub fn geom_pareto_with(self, geom: crate::geom::pareto::GeomPareto) -> Self {
1237 self.add_pareto(geom, true)
1238 }
1239
1240 fn add_pareto(self, geom: crate::geom::pareto::GeomPareto, explicit: bool) -> Self {
1241 let has_y_scale = self
1242 .scales
1243 .iter()
1244 .any(|s| s.aesthetic() == crate::aes::Aesthetic::Y);
1245 let plot = if explicit {
1246 self.add_geom_with(geom)
1247 } else {
1248 self.add_geom(geom)
1249 };
1250 if has_y_scale {
1251 plot
1252 } else {
1253 plot.scale_y_continuous(ScaleContinuous::new().with_sec_axis(
1254 crate::scale::sec_axis::SecAxis::percent_of_max().with_name("Cumulative %"),
1255 ))
1256 }
1257 }
1258
1259 pub fn geom_sankey(self) -> Self {
1266 self.add_sankey(crate::geom::sankey::GeomSankey::default(), false)
1267 }
1268
1269 pub fn geom_sankey_with(self, geom: crate::geom::sankey::GeomSankey) -> Self {
1271 self.add_sankey(geom, true)
1272 }
1273
1274 fn add_sankey(self, geom: crate::geom::sankey::GeomSankey, explicit: bool) -> Self {
1275 let mut plot = if explicit {
1276 self.add_geom_with(geom)
1277 } else {
1278 self.add_geom(geom)
1279 };
1280 plot.hide_position_guides = true;
1281 plot.layer_aes(Aes::new().after_stat_fill("node"))
1282 .show_legend(false)
1283 }
1284
1285 pub fn geom_chord(self) -> Self {
1292 self.add_chord(crate::geom::chord::GeomChord::default(), false)
1293 }
1294
1295 pub fn geom_chord_with(self, geom: crate::geom::chord::GeomChord) -> Self {
1297 self.add_chord(geom, true)
1298 }
1299
1300 fn add_chord(self, geom: crate::geom::chord::GeomChord, explicit: bool) -> Self {
1301 let (ex, ey) = geom.extents();
1302 let mut plot = if explicit {
1303 self.add_geom_with(geom)
1304 } else {
1305 self.add_geom(geom)
1306 };
1307 plot.hide_position_guides = true;
1308 plot.default_aspect_ratio = Some(ey / ex);
1309 plot.layer_aes(Aes::new().after_stat_fill("node"))
1310 .show_legend(false)
1311 }
1312
1313 pub fn geom_arcdiagram(self) -> Self {
1318 self.add_arcdiagram(crate::geom::arcdiagram::GeomArcdiagram::default(), false)
1319 }
1320
1321 pub fn geom_arcdiagram_with(self, geom: crate::geom::arcdiagram::GeomArcdiagram) -> Self {
1324 self.add_arcdiagram(geom, true)
1325 }
1326
1327 fn add_arcdiagram(self, geom: crate::geom::arcdiagram::GeomArcdiagram, explicit: bool) -> Self {
1328 let mut plot = if explicit {
1329 self.add_geom_with(geom)
1330 } else {
1331 self.add_geom(geom)
1332 };
1333 plot.hide_position_guides = true;
1334 plot.layer_aes(Aes::new().after_stat_fill("node"))
1335 .show_legend(false)
1336 }
1337
1338 pub fn geom_treemap(self) -> Self {
1346 self.add_treemap(crate::geom::treemap::GeomTreemap::default(), false)
1347 }
1348
1349 pub fn geom_treemap_with(self, geom: crate::geom::treemap::GeomTreemap) -> Self {
1351 self.add_treemap(geom, true)
1352 }
1353
1354 fn add_treemap(self, geom: crate::geom::treemap::GeomTreemap, explicit: bool) -> Self {
1355 use crate::aes::Aesthetic;
1356 let aspect = geom.aspect;
1357 let has = |plot: &Self, aes: Aesthetic| plot.scales.iter().any(|s| s.aesthetic() == aes);
1358 let (has_x, has_y) = (has(&self, Aesthetic::X), has(&self, Aesthetic::Y));
1359 let fill_by = match (
1360 self.mapping.get_mapping(&Aesthetic::Fill),
1361 self.mapping.get_mapping(&Aesthetic::Subgroup),
1362 ) {
1363 (None, Some(sub)) => Some(sub.to_string()),
1364 _ => None,
1365 };
1366 let mut plot = if explicit {
1367 self.add_geom_with(geom)
1368 } else {
1369 self.add_geom(geom)
1370 };
1371 plot.hide_position_guides = true;
1372 plot.default_aspect_ratio = Some(1.0 / aspect.max(1e-6));
1373 if let Some(sub) = fill_by {
1374 plot = plot.layer_aes(Aes::new().fill(&sub));
1375 }
1376 if !has_x {
1377 plot = plot.scale_x_continuous(ScaleContinuous::new().with_expand(0.0, 0.0));
1378 }
1379 if !has_y {
1380 plot = plot.scale_y_continuous(ScaleContinuous::new().with_expand(0.0, 0.0));
1381 }
1382 plot
1383 }
1384
1385 pub fn geom_sunburst<S: Into<String>>(self, levels: impl IntoIterator<Item = S>) -> Self {
1392 let geom = crate::geom::partition::GeomPartition::sunburst(levels);
1393 let depth = geom.levels.len().max(1) as f64;
1395 let inner = geom.hole / (geom.hole + depth);
1396 let mut plot = self.default_styled(|p| p.geom_partition_with(geom));
1397 plot.coord = Box::new(CoordPolar::new().inner_radius(inner));
1398 plot
1399 }
1400
1401 pub fn geom_icicle<S: Into<String>>(self, levels: impl IntoIterator<Item = S>) -> Self {
1405 self.default_styled(|p| {
1406 p.geom_partition_with(crate::geom::partition::GeomPartition::icicle(levels))
1407 })
1408 }
1409
1410 pub fn geom_partition_with(self, geom: crate::geom::partition::GeomPartition) -> Self {
1414 use crate::aes::Aesthetic;
1415 let has = |plot: &Self, aes: Aesthetic| plot.scales.iter().any(|s| s.aesthetic() == aes);
1416 let (has_x, has_y) = (has(&self, Aesthetic::X), has(&self, Aesthetic::Y));
1417 let fill_mapped = self.mapping.get_mapping(&Aesthetic::Fill).is_some();
1418 let mut plot = self.add_geom_with(geom);
1419 plot.hide_position_guides = true;
1420 if !fill_mapped {
1421 plot = plot
1422 .layer_aes(Aes::new().after_stat_fill("root"))
1423 .show_legend(false);
1424 }
1425 if !has_x {
1426 plot = plot.scale_x_continuous(ScaleContinuous::new().with_expand(0.0, 0.0));
1427 }
1428 if !has_y {
1429 plot = plot.scale_y_continuous(ScaleContinuous::new().with_expand(0.0, 0.0));
1430 }
1431 plot
1432 }
1433
1434 pub fn geom_funnel(self) -> Self {
1442 self.default_styled(|p| p.geom_funnel_with(crate::geom::funnel::GeomFunnel::default()))
1443 }
1444
1445 pub fn geom_pyramid(self) -> Self {
1447 self.default_styled(|p| p.geom_funnel_with(crate::geom::funnel::GeomFunnel::pyramid()))
1448 }
1449
1450 pub fn geom_funnel_with(self, geom: crate::geom::funnel::GeomFunnel) -> Self {
1452 let fill_mapped = self
1453 .mapping
1454 .get_mapping(&crate::aes::Aesthetic::Fill)
1455 .is_some();
1456 let mut plot = self.add_geom_with(geom);
1457 plot.hide_position_guides = true;
1458 if !fill_mapped {
1459 plot = plot
1460 .layer_aes(Aes::new().after_stat_fill("stage"))
1461 .show_legend(false);
1462 }
1463 plot
1464 }
1465
1466 pub fn geom_stream(self) -> Self {
1472 self.geom_area().position(
1473 crate::position::stack::position_stack()
1474 .with_offset(crate::position::stack::StackOffset::Wiggle),
1475 )
1476 }
1477
1478 pub fn geom_lollipop(self) -> Self {
1481 self.add_geom(crate::geom::lollipop::GeomLollipop::default())
1482 }
1483
1484 pub fn geom_lollipop_with(self, geom: crate::geom::lollipop::GeomLollipop) -> Self {
1485 self.add_geom_with(geom)
1486 }
1487
1488 pub fn geom_dumbbell(self) -> Self {
1492 self.add_geom(crate::geom::lollipop::GeomDumbbell::default())
1493 }
1494
1495 pub fn geom_dumbbell_with(self, geom: crate::geom::lollipop::GeomDumbbell) -> Self {
1496 self.add_geom_with(geom)
1497 }
1498
1499 pub fn geom_bullet(self, geom: crate::geom::bullet::GeomBullet) -> Self {
1505 self.add_geom_with(geom).coord_flip()
1506 }
1507
1508 pub fn geom_bullet_vertical(self, geom: crate::geom::bullet::GeomBullet) -> Self {
1510 self.add_geom_with(geom)
1511 }
1512
1513 pub fn geom_waffle(self, rows: usize) -> Self {
1517 self.default_styled(|p| p.geom_waffle_with(crate::geom::waffle::GeomWaffle::waffle(rows)))
1518 }
1519
1520 pub fn geom_parliament(self, rows: usize) -> Self {
1524 self.default_styled(|p| {
1525 p.geom_waffle_with(crate::geom::waffle::GeomWaffle::parliament(rows))
1526 })
1527 }
1528
1529 pub fn geom_waffle_with(self, geom: crate::geom::waffle::GeomWaffle) -> Self {
1531 let mut plot = self.add_geom_with(geom);
1532 plot.hide_position_guides = true;
1533 plot
1534 }
1535
1536 pub fn geom_packed_bubble(self) -> Self {
1541 self.default_styled(|p| {
1542 p.geom_packed_bubble_with(crate::geom::pack::GeomPackedBubble::default())
1543 })
1544 }
1545
1546 pub fn geom_packed_bubble_with(self, geom: crate::geom::pack::GeomPackedBubble) -> Self {
1548 use crate::aes::Aesthetic;
1549 let fill_by = match (
1550 self.mapping.get_mapping(&Aesthetic::Fill),
1551 self.mapping.get_mapping(&Aesthetic::Subgroup),
1552 ) {
1553 (None, Some(sub)) => Some(sub.to_string()),
1554 _ => None,
1555 };
1556 let mut plot = self.add_geom_with(geom);
1557 plot.hide_position_guides = true;
1558 plot.default_aspect_ratio = Some(1.0);
1559 if let Some(sub) = fill_by {
1560 plot = plot.layer_aes(Aes::new().fill(&sub));
1561 }
1562 plot
1563 }
1564
1565 pub fn geom_network(self) -> Self {
1571 self.add_network(crate::geom::network::GeomNetwork::default(), false)
1572 }
1573
1574 pub fn geom_network_with(self, geom: crate::geom::network::GeomNetwork) -> Self {
1576 self.add_network(geom, true)
1577 }
1578
1579 fn add_network(self, geom: crate::geom::network::GeomNetwork, explicit: bool) -> Self {
1580 let by_component = geom.color_components;
1581 let mut plot = if explicit {
1582 self.add_geom_with(geom)
1583 } else {
1584 self.add_geom(geom)
1585 };
1586 plot.hide_position_guides = true;
1587 plot.default_aspect_ratio = Some(1.0);
1588 if by_component {
1589 plot = plot
1590 .layer_aes(Aes::new().after_stat_fill("component"))
1591 .show_legend(false);
1592 }
1593 plot
1594 }
1595
1596 pub fn geom_tree(self) -> Self {
1603 self.default_styled(|p| p.geom_tree_with(crate::geom::tree::GeomTree::default()))
1604 }
1605
1606 pub fn geom_dendrogram(self) -> Self {
1610 self.default_styled(|p| p.geom_tree_with(crate::geom::tree::GeomTree::dendrogram()))
1611 }
1612
1613 pub fn geom_tree_with(self, geom: crate::geom::tree::GeomTree) -> Self {
1615 let radial = geom.orientation == crate::stat::tree::TreeOrientation::Radial;
1616 let mut plot = self.add_geom_with(geom);
1617 plot.hide_position_guides = true;
1618 if radial {
1619 plot.default_aspect_ratio = Some(1.0);
1620 }
1621 plot
1622 }
1623
1624 pub fn geom_venn(self) -> Self {
1630 self.default_styled(|p| p.geom_venn_with(crate::geom::venn::GeomVenn::default()))
1631 }
1632
1633 pub fn geom_venn_with(self, geom: crate::geom::venn::GeomVenn) -> Self {
1635 let mut plot = self.add_geom_with(geom);
1636 plot.hide_position_guides = true;
1637 plot.default_aspect_ratio = Some(1.0);
1638 plot.layer_aes(Aes::new().after_stat_fill("set"))
1639 .show_legend(false)
1640 }
1641
1642 pub fn geom_wordcloud(self) -> Self {
1647 self.default_styled(|p| {
1648 p.geom_wordcloud_with(crate::geom::wordcloud::GeomWordcloud::default())
1649 })
1650 }
1651
1652 pub fn geom_wordcloud_with(self, geom: crate::geom::wordcloud::GeomWordcloud) -> Self {
1655 let mut plot = self.add_geom_with(geom);
1656 plot.hide_position_guides = true;
1657 plot
1658 }
1659
1660 pub fn geom_variwide(self) -> Self {
1665 self.default_styled(|p| {
1666 p.geom_marimekko_with(crate::geom::marimekko::GeomMarimekko::variwide())
1667 })
1668 }
1669
1670 pub fn geom_marimekko(self) -> Self {
1674 self.default_styled(|p| {
1675 p.geom_marimekko_with(crate::geom::marimekko::GeomMarimekko::marimekko())
1676 })
1677 }
1678
1679 pub fn geom_marimekko_with(self, geom: crate::geom::marimekko::GeomMarimekko) -> Self {
1681 use crate::aes::Aesthetic;
1682 let has = |plot: &Self, aes: Aesthetic| plot.scales.iter().any(|s| s.aesthetic() == aes);
1683 let (has_x, has_y) = (has(&self, Aesthetic::X), has(&self, Aesthetic::Y));
1684 let axis = self.mapping.get_mapping(&Aesthetic::X).and_then(|xc| {
1686 let x = self.data.column(xc)?;
1687 let w = self
1688 .mapping
1689 .get_mapping(&Aesthetic::Weight)
1690 .and_then(|wc| self.data.column(wc));
1691 let totals = crate::stat::marimekko::column_totals(x, w);
1692 let widths: Vec<f64> = totals.iter().map(|t| t.1).collect();
1693 let spans = crate::stat::marimekko::column_spans(&widths, geom.gap);
1694 Some((
1695 spans
1696 .iter()
1697 .map(|s| (s.0 + s.1) / 2.0)
1698 .collect::<Vec<f64>>(),
1699 totals.into_iter().map(|t| t.0).collect::<Vec<String>>(),
1700 ))
1701 });
1702 let mosaic = geom.mosaic;
1703 let mut plot = self.add_geom_with(geom);
1704 if !has_x {
1705 let mut sx = ScaleContinuous::new().with_expand(0.0, 0.0);
1706 if let Some((breaks, labels)) = axis {
1707 sx = sx.with_breaks(breaks).with_labels(labels);
1708 }
1709 plot = plot.scale_x_continuous(sx);
1710 }
1711 if mosaic && !has_y {
1712 plot = plot.scale_y_continuous(
1713 ScaleContinuous::new()
1714 .with_expand(0.0, 0.0)
1715 .with_label_formatter(|v| format!("{}%", (v * 100.0).round())),
1716 );
1717 }
1718 plot
1719 }
1720
1721 pub fn geom_timeline(self) -> Self {
1726 let mut plot = self.add_geom(crate::geom::timeline::GeomTimeline::default());
1727 plot.hide_y_guides = true;
1728 plot
1729 }
1730
1731 pub fn geom_timeline_with(self, geom: crate::geom::timeline::GeomTimeline) -> Self {
1733 let mut plot = self.add_geom_with(geom);
1734 plot.hide_y_guides = true;
1735 plot
1736 }
1737
1738 pub fn geom_xrange(self) -> Self {
1745 self.add_xrange(crate::geom::xrange::GeomXrange::default(), false)
1746 }
1747
1748 pub fn geom_xrange_with(self, geom: crate::geom::xrange::GeomXrange) -> Self {
1750 self.add_xrange(geom, true)
1751 }
1752
1753 fn add_xrange(self, geom: crate::geom::xrange::GeomXrange, explicit: bool) -> Self {
1754 use crate::aes::Aesthetic;
1755 let has_y = self.scales.iter().any(|s| s.aesthetic() == Aesthetic::Y);
1756 let levels: Option<Vec<String>> = self
1758 .mapping
1759 .get_mapping(&Aesthetic::Y)
1760 .and_then(|c| self.data.column(c))
1761 .map(|col| {
1762 let mut seen: indexmap::IndexSet<String> = indexmap::IndexSet::new();
1763 for v in col.iter().filter(|v| !v.is_na()) {
1764 seen.insert(v.key_str().into_owned());
1765 }
1766 seen.into_iter().rev().collect()
1767 });
1768 let mut plot = if explicit {
1769 self.add_geom_with(geom)
1770 } else {
1771 self.add_geom(geom)
1772 };
1773 if let (false, Some(levels)) = (has_y, levels) {
1774 let refs: Vec<&str> = levels.iter().map(String::as_str).collect();
1775 plot = plot
1776 .scale_y_discrete(crate::scale::discrete::ScaleDiscrete::new().with_limits(refs));
1777 }
1778 plot
1779 }
1780
1781 pub fn geom_tilemap(self, shape: crate::geom::tilemap::TileShape) -> Self {
1786 self.default_styled(|p| {
1787 p.geom_tilemap_with(crate::geom::tilemap::GeomTilemap {
1788 shape,
1789 ..Default::default()
1790 })
1791 })
1792 }
1793
1794 pub fn geom_tilemap_with(self, geom: crate::geom::tilemap::GeomTilemap) -> Self {
1796 let mut plot = self.add_geom_with(geom);
1797 plot.hide_position_guides = true;
1798 plot
1799 }
1800
1801 pub fn geom_windbarb(self, geom: crate::geom::windbarb::GeomWindbarb) -> Self {
1806 self.add_geom(geom)
1807 }
1808
1809 pub fn geom_variable_pie(self) -> Self {
1813 let mut plot = self.add_geom(crate::geom::variablepie::GeomVariablePie::default());
1814 plot.hide_position_guides = true;
1815 plot.default_aspect_ratio = Some(1.0);
1816 plot
1817 }
1818
1819 pub fn geom_variable_pie_with(self, geom: crate::geom::variablepie::GeomVariablePie) -> Self {
1822 let mut plot = self.add_geom_with(geom);
1823 plot.hide_position_guides = true;
1824 plot.default_aspect_ratio = Some(1.0);
1825 plot
1826 }
1827
1828 pub fn geom_hlc(self) -> Self {
1831 self.add_geom(crate::geom::candlestick::GeomOhlc::hlc())
1832 }
1833
1834 pub fn geom_heikin_ashi(self) -> Self {
1838 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
1839 .stat(crate::stat::heikin_ashi::StatHeikinAshi)
1840 }
1841
1842 pub fn geom_renko(self, box_size: f64) -> Self {
1846 self.add_geom(crate::geom::renko::GeomRenko::renko(box_size))
1847 .xlab("Brick")
1848 }
1849
1850 pub fn geom_point_figure(self, box_size: f64, reversal: usize) -> Self {
1854 self.add_geom(crate::geom::renko::GeomRenko::point_figure(
1855 box_size, reversal,
1856 ))
1857 .xlab("Column")
1858 }
1859
1860 pub fn geom_flags(self) -> Self {
1865 self.add_geom(crate::geom::flags::GeomFlags::default())
1866 }
1867
1868 pub fn geom_flags_with(self, geom: crate::geom::flags::GeomFlags) -> Self {
1869 self.add_geom_with(geom)
1870 }
1871
1872 #[cfg(feature = "sf")]
1877 pub fn geom_flow(self, geom: crate::geom::flow::GeomFlow) -> Self {
1878 self.add_geom(geom)
1879 }
1880
1881 pub fn geom_candlestick(self) -> Self {
1884 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
1885 }
1886
1887 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
1888 self.add_geom_with(geom)
1889 }
1890
1891 pub fn geom_ohlc(self) -> Self {
1894 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
1895 }
1896
1897 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
1898 self.add_geom_with(geom)
1899 }
1900
1901 pub fn geom_calendar(self) -> Self {
1912 self.geom_calendar_with(
1913 GeomTile {
1914 color: (255, 255, 255),
1915 line_width: 1.5,
1916 ..Default::default()
1917 },
1918 false,
1919 )
1920 }
1921
1922 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
1925 use crate::stat::calendar::{
1926 day_number, month_boundaries, month_breaks, month_breaks_localized, weekday_labels,
1927 CalendarGrid, StatCalendar, MAX_CALENDAR_YEARS,
1928 };
1929 let grid = self
1930 .mapping
1931 .get_mapping(&crate::aes::Aesthetic::X)
1932 .and_then(|c| self.data.column(c))
1933 .and_then(|col| {
1934 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
1935 });
1936 let Some((grid, clipped)) = grid else {
1937 return self.add_geom_with(tile).stat(StatCalendar {
1939 grid: None,
1940 monday_first,
1941 });
1942 };
1943 if clipped {
1944 self.warnings.push(format!(
1945 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
1946 ));
1947 }
1948 let (xb, _) = month_breaks(&grid);
1949 let yb = if monday_first {
1951 vec![6.0, 4.0, 2.0]
1952 } else {
1953 vec![5.0, 3.0, 1.0]
1954 };
1955 let segs = month_boundaries(&grid);
1956 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
1957 segs.iter().map(|s| Value::Float(f(s))).collect()
1958 };
1959 let boundaries = vec![
1960 ("x".to_string(), col(|s| s.0)),
1961 ("y".to_string(), col(|s| s.1)),
1962 ("xend".to_string(), col(|s| s.2)),
1963 ("yend".to_string(), col(|s| s.3)),
1964 ];
1965 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
1966 let plot = self
1967 .add_geom_with(tile)
1968 .stat(StatCalendar {
1969 grid: Some(grid),
1970 monday_first,
1971 })
1972 .scale_x_continuous(
1973 ScaleContinuous::new()
1974 .with_breaks(xb)
1975 .with_localized_labels(move |l| month_breaks_localized(&grid, l).1)
1977 .with_expand(0.0, 0.0),
1978 )
1979 .scale_y_continuous(
1980 ScaleContinuous::new()
1981 .with_breaks(yb)
1982 .with_localized_labels(weekday_labels)
1983 .with_expand(0.0, 0.0),
1984 );
1985 if segs.is_empty() {
1986 return plot;
1987 }
1988 plot.add_geom_with(crate::geom::segment::GeomCalendarBorder)
1990 .layer_data(boundaries)
1991 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1992 .show_legend(false)
1993 }
1994
1995 pub fn geom_blank(self) -> Self {
1996 self.add_geom(GeomBlank)
1997 }
1998
1999 fn default_styled(self, build: impl FnOnce(Self) -> Self) -> Self {
2006 let i = self.layers.len();
2007 let mut plot = build(self);
2008 if let Some(layer) = plot.layers.get_mut(i) {
2009 layer.explicit_style = false;
2010 }
2011 plot
2012 }
2013
2014 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
2015 let mut plot = self.add_geom(geom);
2016 if let Some(layer) = plot.layers.last_mut() {
2017 layer.explicit_style = true;
2018 }
2019 plot
2020 }
2021
2022 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
2023 let stat = geom.default_stat();
2024 let position = geom.default_position();
2025 let params = geom.default_params();
2026 self.layers.push(Layer {
2027 data: None,
2028 mapping: Aes::default(),
2029 geom: Box::new(geom),
2030 stat,
2031 position,
2032 params,
2033 show_legend: None,
2034 explicit_style: false,
2035 tooltip_template: None,
2036 key_glyph: None,
2037 show_legend_aes: Vec::new(),
2038 });
2039 self
2040 }
2041
2042 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
2046 if let Some(layer) = self.layers.last_mut() {
2047 layer.stat = Box::new(stat);
2048 }
2049 self
2050 }
2051
2052 pub fn position(mut self, pos: impl Position + 'static) -> Self {
2054 if let Some(layer) = self.layers.last_mut() {
2055 layer.position = Box::new(pos);
2056 }
2057 self
2058 }
2059
2060 pub fn layer_data(mut self, data: impl GGData) -> Self {
2062 if let Some(layer) = self.layers.last_mut() {
2063 layer.data = Some(data.into_dataframe());
2064 }
2065 self
2066 }
2067
2068 pub fn layer_aes(mut self, mapping: Aes) -> Self {
2070 self.warnings.extend(mapping.warnings.iter().cloned());
2071 if let Some(layer) = self.layers.last_mut() {
2072 layer.mapping = mapping;
2073 }
2074 self
2075 }
2076
2077 pub fn tooltip(mut self, template: &str) -> Self {
2088 if let Some(layer) = self.layers.last_mut() {
2089 layer.tooltip_template = Some(template.to_string());
2090 }
2091 self
2092 }
2093
2094 pub fn show_legend(mut self, show: bool) -> Self {
2097 if let Some(layer) = self.layers.last_mut() {
2098 layer.show_legend = Some(show);
2099 }
2100 self
2101 }
2102
2103 pub fn show_legend_for(mut self, aes: crate::aes::Aesthetic, show: bool) -> Self {
2106 if let Some(layer) = self.layers.last_mut() {
2107 layer.show_legend_aes.retain(|(a, _)| *a != aes);
2108 layer.show_legend_aes.push((aes, show));
2109 }
2110 self
2111 }
2112
2113 pub fn key_glyph(mut self, glyph: crate::guide::key::KeyGlyph) -> Self {
2116 if let Some(layer) = self.layers.last_mut() {
2117 layer.key_glyph = Some(glyph);
2118 }
2119 self
2120 }
2121
2122 pub fn decorative(mut self) -> Self {
2149 if let Some(layer) = self.layers.last_mut() {
2150 let geom = std::mem::replace(&mut layer.geom, Box::new(GeomBlank));
2151 layer.geom = Box::new(crate::geom::decorative::Decorative(geom));
2152 if layer.show_legend.is_none() {
2153 layer.show_legend = Some(false);
2154 }
2155 }
2156 self
2157 }
2158
2159 pub fn layer_svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
2193 let (name, value) = (name.into(), value.into());
2194 let Some(idx) = self.layers.len().checked_sub(1) else {
2195 self.warnings.push(format!(
2196 "layer_svg_attr(\"{name}\") called before any layer was added — ignored"
2197 ));
2198 return self;
2199 };
2200 if let Some((n, v)) =
2201 crate::render::svg_backend::validate_layer_svg_attr(&name, &value, &mut self.warnings)
2202 {
2203 self.layer_svg_attrs
2204 .retain(|(i, k, _)| !(*i == idx && *k == n));
2205 self.layer_svg_attrs.push((idx, n, v));
2206 }
2207 self
2208 }
2209
2210 pub fn layer_svg_attrs<N: Into<String>, V: Into<String>>(
2212 self,
2213 attrs: impl IntoIterator<Item = (N, V)>,
2214 ) -> Self {
2215 attrs
2216 .into_iter()
2217 .fold(self, |p, (n, v)| p.layer_svg_attr(n, v))
2218 }
2219
2220 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
2223 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
2224 self.scales.push(Box::new(s));
2225 self
2226 }
2227
2228 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
2229 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
2230 self.scales.push(Box::new(s));
2231 self
2232 }
2233
2234 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
2235 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
2236 self.scales.push(Box::new(s));
2237 self
2238 }
2239
2240 pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
2241 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
2242 self.scales.push(Box::new(s));
2243 self
2244 }
2245
2246 pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
2247 self.scales.push(Box::new(s));
2248 self
2249 }
2250
2251 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
2252 self.scales.push(Box::new(s));
2253 self
2254 }
2255
2256 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
2257 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
2258 self.scale_color(s)
2259 }
2260
2261 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
2262 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
2263 self.scale_fill(s)
2264 }
2265
2266 pub fn scale_colour_identity(self) -> Self {
2269 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Color);
2270 self.scale_color(s)
2271 }
2272
2273 pub fn scale_color_identity(self) -> Self {
2275 self.scale_colour_identity()
2276 }
2277
2278 pub fn scale_fill_identity(self) -> Self {
2281 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Fill);
2282 self.scale_fill(s)
2283 }
2284
2285 pub fn scale_color_viridis(self) -> Self {
2286 use crate::scale::color::ScaleColorDiscrete;
2287 use crate::scale::palettes::PaletteName;
2288 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
2289 .with_named_palette(&PaletteName::Viridis);
2290 self.scale_color(s)
2291 }
2292
2293 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
2294 use crate::scale::color::ScaleColorDiscrete;
2295 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
2296 self.scale_color(s)
2297 }
2298
2299 pub fn scale_color_gradient(
2300 self,
2301 low: crate::scale::color::RGBAColor,
2302 high: crate::scale::color::RGBAColor,
2303 ) -> Self {
2304 use crate::scale::color::ScaleColorContinuous;
2305 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
2306 self.scale_color(s)
2307 }
2308
2309 pub fn scale_fill_gradient(
2310 self,
2311 low: crate::scale::color::RGBAColor,
2312 high: crate::scale::color::RGBAColor,
2313 ) -> Self {
2314 use crate::scale::color::ScaleColorContinuous;
2315 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
2316 self.scale_fill(s)
2317 }
2318
2319 pub fn scale_color_gradient2(
2320 self,
2321 low: crate::scale::color::RGBAColor,
2322 mid: crate::scale::color::RGBAColor,
2323 high: crate::scale::color::RGBAColor,
2324 ) -> Self {
2325 use crate::scale::gradient::ScaleColorGradient2;
2326 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
2327 self.scale_color(s)
2328 }
2329
2330 pub fn scale_fill_gradient2(
2331 self,
2332 low: crate::scale::color::RGBAColor,
2333 mid: crate::scale::color::RGBAColor,
2334 high: crate::scale::color::RGBAColor,
2335 ) -> Self {
2336 use crate::scale::gradient::ScaleColorGradient2;
2337 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
2338 self.scale_fill(s)
2339 }
2340
2341 pub fn scale_fill_viridis(self) -> Self {
2342 use crate::scale::color::ScaleColorDiscrete;
2343 use crate::scale::palettes::PaletteName;
2344 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
2345 .with_named_palette(&PaletteName::Viridis);
2346 self.scale_fill(s)
2347 }
2348
2349 pub fn scale_color_viridis_c(self) -> Self {
2351 use crate::scale::gradient_n::ScaleColorGradientN;
2352 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
2353 self.scale_color(s)
2354 }
2355
2356 pub fn scale_fill_viridis_c(self) -> Self {
2358 use crate::scale::gradient_n::ScaleColorGradientN;
2359 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
2360 self.scale_fill(s)
2361 }
2362
2363 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
2365 use crate::scale::gradient_n::ScaleColorGradientN;
2366 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
2367 self.scale_color(s)
2368 }
2369
2370 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
2372 use crate::scale::gradient_n::ScaleColorGradientN;
2373 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
2374 self.scale_fill(s)
2375 }
2376
2377 pub fn scale_color_steps(
2380 self,
2381 low: crate::scale::color::RGBAColor,
2382 high: crate::scale::color::RGBAColor,
2383 n_bins: usize,
2384 ) -> Self {
2385 let s = crate::scale::steps::ScaleColorSteps::two(
2386 crate::aes::Aesthetic::Color,
2387 (low.r, low.g, low.b),
2388 (high.r, high.g, high.b),
2389 n_bins,
2390 );
2391 self.scale_color(s)
2392 }
2393
2394 pub fn scale_color_stepsn(
2396 self,
2397 stops: Vec<crate::scale::color::RGBAColor>,
2398 n_bins: usize,
2399 ) -> Self {
2400 let s = crate::scale::steps::ScaleColorSteps::new(
2401 crate::aes::Aesthetic::Color,
2402 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
2403 n_bins,
2404 );
2405 self.scale_color(s)
2406 }
2407
2408 pub fn scale_color_fermenter(
2410 self,
2411 name: crate::scale::palettes::PaletteName,
2412 n_bins: usize,
2413 ) -> Self {
2414 let stops = crate::scale::palettes::palette(&name)
2415 .iter()
2416 .map(|c| (c.r, c.g, c.b))
2417 .collect();
2418 let s =
2419 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
2420 self.scale_color(s)
2421 }
2422
2423 pub fn scale_fill_steps(
2425 self,
2426 low: crate::scale::color::RGBAColor,
2427 high: crate::scale::color::RGBAColor,
2428 n_bins: usize,
2429 ) -> Self {
2430 let s = crate::scale::steps::ScaleColorSteps::two(
2431 crate::aes::Aesthetic::Fill,
2432 (low.r, low.g, low.b),
2433 (high.r, high.g, high.b),
2434 n_bins,
2435 );
2436 self.scale_fill(s)
2437 }
2438
2439 pub fn scale_fill_binned(
2450 self,
2451 breaks: Vec<f64>,
2452 low: crate::scale::color::RGBAColor,
2453 high: crate::scale::color::RGBAColor,
2454 ) -> Self {
2455 let s = crate::scale::binned::ScaleFillBinned::two(
2456 crate::aes::Aesthetic::Fill,
2457 breaks,
2458 (low.r, low.g, low.b),
2459 (high.r, high.g, high.b),
2460 );
2461 self.scale_fill(s)
2462 }
2463
2464 pub fn scale_fill_binned_with(
2468 self,
2469 breaks: Vec<f64>,
2470 colors: Vec<crate::scale::color::RGBAColor>,
2471 ) -> Self {
2472 let s = crate::scale::binned::ScaleFillBinned::new(
2473 crate::aes::Aesthetic::Fill,
2474 breaks,
2475 colors.iter().map(|c| (c.r, c.g, c.b)).collect(),
2476 );
2477 self.scale_fill(s)
2478 }
2479
2480 pub fn scale_fill_fermenter(
2482 self,
2483 name: crate::scale::palettes::PaletteName,
2484 n_bins: usize,
2485 ) -> Self {
2486 let stops = crate::scale::palettes::palette(&name)
2487 .iter()
2488 .map(|c| (c.r, c.g, c.b))
2489 .collect();
2490 let s =
2491 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
2492 self.scale_fill(s)
2493 }
2494
2495 pub fn scale_fill_discrete_sorted(self) -> Self {
2500 use crate::scale::color::ScaleColorDiscrete;
2501 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
2502 }
2503
2504 pub fn scale_color_discrete_sorted(self) -> Self {
2507 use crate::scale::color::ScaleColorDiscrete;
2508 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
2509 }
2510
2511 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
2512 use crate::scale::color::ScaleColorDiscrete;
2513 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
2514 self.scale_fill(s)
2515 }
2516
2517 pub fn scale_linetype_manual(
2518 self,
2519 values: Vec<(&str, crate::render::backend::Linetype)>,
2520 ) -> Self {
2521 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
2522 self.scale_color(s)
2523 }
2524
2525 pub fn scale_shape_manual(
2526 self,
2527 values: Vec<(&str, crate::render::backend::PointShape)>,
2528 ) -> Self {
2529 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
2530 self.scale_color(s)
2531 }
2532
2533 pub fn scale_color_grey(self) -> Self {
2534 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
2535 self.scale_color(s)
2536 }
2537
2538 pub fn scale_fill_grey(self) -> Self {
2539 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
2540 self.scale_fill(s)
2541 }
2542
2543 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
2544 self.scale_color(s)
2545 }
2546
2547 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
2548 self.scale_fill(s)
2549 }
2550
2551 pub fn scale_x_reverse(self) -> Self {
2552 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
2553 }
2554
2555 pub fn scale_y_reverse(self) -> Self {
2556 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
2557 }
2558
2559 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
2560 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
2561 self.scales.push(Box::new(s));
2562 self
2563 }
2564
2565 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
2566 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
2567 self.scales.push(Box::new(s));
2568 self
2569 }
2570
2571 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
2572 self.scales.push(Box::new(s));
2573 self
2574 }
2575
2576 pub fn scale_size_area(mut self, max_size: f64) -> Self {
2580 self.scales.push(Box::new(
2581 crate::scale::size::ScaleSizeContinuous::new().area(max_size),
2582 ));
2583 self
2584 }
2585
2586 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
2587 self.scales.push(Box::new(s));
2588 self
2589 }
2590
2591 pub fn scale_linewidth(mut self, s: crate::scale::linewidth::ScaleLinewidthContinuous) -> Self {
2595 self.scales.push(Box::new(s));
2596 self
2597 }
2598
2599 pub fn xlim(self, min: f64, max: f64) -> Self {
2600 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
2601 }
2602
2603 pub fn ylim(self, min: f64, max: f64) -> Self {
2604 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
2605 }
2606
2607 pub fn scale_x_log10(self) -> Self {
2608 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
2609 }
2610
2611 pub fn scale_y_log10(self) -> Self {
2612 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
2613 }
2614
2615 pub fn scale_x_sqrt(self) -> Self {
2616 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
2617 }
2618
2619 pub fn scale_y_sqrt(self) -> Self {
2620 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
2621 }
2622
2623 pub fn scale_x_log2(self) -> Self {
2624 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
2625 }
2626
2627 pub fn scale_y_log2(self) -> Self {
2628 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
2629 }
2630
2631 pub fn scale_x_ln(self) -> Self {
2632 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
2633 }
2634
2635 pub fn scale_y_ln(self) -> Self {
2636 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
2637 }
2638
2639 pub fn scale_x_logit(self) -> Self {
2641 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
2642 }
2643
2644 pub fn scale_y_logit(self) -> Self {
2645 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
2646 }
2647
2648 pub fn scale_x_probit(self) -> Self {
2650 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
2651 }
2652
2653 pub fn scale_y_probit(self) -> Self {
2654 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
2655 }
2656
2657 pub fn scale_x_pseudo_log(self) -> Self {
2659 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
2660 }
2661
2662 pub fn scale_y_pseudo_log(self) -> Self {
2663 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
2664 }
2665
2666 pub fn scale_x_reciprocal(self) -> Self {
2668 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
2669 }
2670
2671 pub fn scale_y_reciprocal(self) -> Self {
2672 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
2673 }
2674
2675 pub fn scale_x_exp(self) -> Self {
2677 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
2678 }
2679
2680 pub fn scale_y_exp(self) -> Self {
2681 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
2682 }
2683
2684 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
2686 self.scale_x_continuous(
2687 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
2688 )
2689 }
2690
2691 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
2692 self.scale_y_continuous(
2693 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
2694 )
2695 }
2696
2697 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
2700 self.facet = Facet::Wrap {
2701 var: var.to_string(),
2702 ncol,
2703 scales: FacetScales::Fixed,
2704 labeller: FacetLabeller::default(),
2705 levels: None,
2706 };
2707 self
2708 }
2709
2710 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
2711 self.facet = Facet::Wrap {
2712 var: var.to_string(),
2713 ncol,
2714 scales,
2715 labeller: FacetLabeller::default(),
2716 levels: None,
2717 };
2718 self
2719 }
2720
2721 pub fn facet_wrap_labeller(
2722 mut self,
2723 var: &str,
2724 ncol: Option<usize>,
2725 labeller: FacetLabeller,
2726 ) -> Self {
2727 self.facet = Facet::Wrap {
2728 var: var.to_string(),
2729 ncol,
2730 scales: FacetScales::Fixed,
2731 labeller,
2732 levels: None,
2733 };
2734 self
2735 }
2736
2737 pub fn facet_levels<S: AsRef<str>>(mut self, levels: &[S]) -> Self {
2744 if let Facet::Wrap { levels: l, .. } = &mut self.facet {
2745 let mut seen = Vec::<String>::new();
2746 for s in levels {
2747 if !seen.iter().any(|x| x == s.as_ref()) {
2748 seen.push(s.as_ref().to_string());
2749 }
2750 }
2751 *l = Some(seen);
2752 }
2753 self
2754 }
2755
2756 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
2757 self.facet = Facet::Grid {
2758 row_var: row.map(String::from),
2759 col_var: col.map(String::from),
2760 scales: FacetScales::Fixed,
2761 labeller: FacetLabeller::default(),
2762 space: FacetSpace::Fixed,
2763 };
2764 self
2765 }
2766
2767 pub fn facet_grid_free(
2768 mut self,
2769 row: Option<&str>,
2770 col: Option<&str>,
2771 scales: FacetScales,
2772 ) -> Self {
2773 self.facet = Facet::Grid {
2774 row_var: row.map(String::from),
2775 col_var: col.map(String::from),
2776 scales,
2777 labeller: FacetLabeller::default(),
2778 space: FacetSpace::Fixed,
2779 };
2780 self
2781 }
2782
2783 pub fn facet_grid_multi(
2787 mut self,
2788 row: Option<&str>,
2789 cols: &[&str],
2790 scales: FacetScales,
2791 space: FacetSpace,
2792 ) -> Self {
2793 let col_var = if cols.len() > 1 {
2794 let n = self.data.nrows();
2796 let mut combined = Vec::with_capacity(n);
2797 for i in 0..n {
2798 let parts: Vec<String> = cols
2799 .iter()
2800 .map(|c| {
2801 self.data
2802 .column(c)
2803 .and_then(|col| col.get(i))
2804 .map(|v| v.to_group_key())
2805 .unwrap_or_default()
2806 })
2807 .collect();
2808 combined.push(crate::data::Value::Str(parts.join(" . ")));
2809 }
2810 let name = "__facet_cols__".to_string();
2811 self.data.add_column(name.clone(), combined);
2812 Some(name)
2813 } else {
2814 cols.first().map(|s| s.to_string())
2815 };
2816 self.facet = Facet::Grid {
2817 row_var: row.map(String::from),
2818 col_var,
2819 scales,
2820 labeller: FacetLabeller::default(),
2821 space,
2822 };
2823 self
2824 }
2825
2826 pub fn facet_grid_space(
2829 mut self,
2830 row: Option<&str>,
2831 col: Option<&str>,
2832 scales: FacetScales,
2833 space: FacetSpace,
2834 ) -> Self {
2835 self.facet = Facet::Grid {
2836 row_var: row.map(String::from),
2837 col_var: col.map(String::from),
2838 scales,
2839 labeller: FacetLabeller::default(),
2840 space,
2841 };
2842 self
2843 }
2844
2845 pub fn facet_grid_labeller(
2846 mut self,
2847 row: Option<&str>,
2848 col: Option<&str>,
2849 labeller: FacetLabeller,
2850 ) -> Self {
2851 self.facet = Facet::Grid {
2852 row_var: row.map(String::from),
2853 col_var: col.map(String::from),
2854 scales: FacetScales::Fixed,
2855 labeller,
2856 space: FacetSpace::Fixed,
2857 };
2858 self
2859 }
2860
2861 pub fn coord_flip(mut self) -> Self {
2864 self.coord = Box::new(CoordFlip);
2865 self
2866 }
2867
2868 pub fn coord_fixed(mut self, ratio: f64) -> Self {
2869 self.coord = Box::new(CoordFixed::new(ratio));
2870 self
2871 }
2872
2873 #[cfg(feature = "sf")]
2877 pub fn coord_sf(mut self) -> Self {
2878 self.coord = Box::new(crate::coord::sf::CoordSf::new());
2879 self
2880 }
2881
2882 #[cfg(feature = "sf")]
2887 pub fn coord_sf_with(mut self, coord: crate::coord::sf::CoordSf) -> Self {
2888 self.coord = Box::new(coord);
2889 self
2890 }
2891
2892 pub fn svg_root_data(mut self, on: bool) -> Self {
2899 self.svg_root_data = on;
2900 self
2901 }
2902
2903 pub fn svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
2913 self.root_attrs.push((name.into(), value.into()));
2914 self
2915 }
2916
2917 pub fn svg_attrs(mut self, attrs: impl IntoIterator<Item = (String, String)>) -> Self {
2919 self.root_attrs.extend(attrs);
2920 self
2921 }
2922
2923 pub fn svg_warnings_attr(mut self, on: bool) -> Self {
2928 self.svg_warnings_attr = on;
2929 self
2930 }
2931
2932 pub fn svg_formatted_values(mut self, on: bool) -> Self {
2941 self.svg_formatted_values = on;
2942 self
2943 }
2944
2945 pub fn svg_title(mut self, title: impl Into<String>) -> Self {
2955 self.svg_title = Some(title.into());
2956 self
2957 }
2958
2959 pub fn svg_desc(mut self, desc: impl Into<String>) -> Self {
2967 self.svg_desc = Some(desc.into());
2968 self
2969 }
2970
2971 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
2976 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
2977 self
2978 }
2979
2980 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
2982 self.coord_trans(None, Some(y))
2983 }
2984
2985 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
2987 self.coord_trans(Some(x), None)
2988 }
2989
2990 pub fn coord_cartesian_zoom(
2992 mut self,
2993 xlim: Option<(f64, f64)>,
2994 ylim: Option<(f64, f64)>,
2995 ) -> Self {
2996 let mut c = CoordCartesian::new();
2997 if let Some((min, max)) = xlim {
2998 c = c.xlim(min, max);
2999 }
3000 if let Some((min, max)) = ylim {
3001 c = c.ylim(min, max);
3002 }
3003 self.coord = Box::new(c);
3004 self
3005 }
3006
3007 pub fn coord_polar(mut self) -> Self {
3008 self.coord = Box::new(CoordPolar::new());
3009 self
3010 }
3011
3012 pub fn coord_radar(mut self) -> Self {
3016 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
3017 self
3018 }
3019
3020 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
3021 self.coord = Box::new(coord);
3022 self
3023 }
3024
3025 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
3026 self.coord = Box::new(coord);
3027 self
3028 }
3029
3030 pub fn theme(mut self, theme: Theme) -> Self {
3035 self.apply_preset(theme);
3036 self
3037 }
3038
3039 fn apply_preset(&mut self, theme: Theme) {
3040 let primary = self.theme.primary;
3041 self.theme = theme;
3042 if self.theme.primary.is_none() {
3043 self.theme.primary = primary;
3044 }
3045 }
3046
3047 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
3051 self.theme.axis_text_x.angle = degrees;
3052 self
3053 }
3054
3055 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
3057 self.theme.axis_text_y.angle = degrees;
3058 self
3059 }
3060
3061 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
3064 self.theme.axis_text_x_dodge = n.max(1);
3065 self
3066 }
3067
3068 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
3071 self.theme.aspect_ratio = Some(ratio);
3072 self
3073 }
3074
3075 pub fn panel_ontop(mut self) -> Self {
3077 self.theme.panel_ontop = true;
3078 self
3079 }
3080
3081 pub fn axis_minor_ticks(mut self) -> Self {
3083 self.theme.axis_minor_ticks = true;
3084 self
3085 }
3086
3087 pub fn axis_text_x_fit(mut self, fit: crate::theme::AxisLabelFit) -> Self {
3092 self.theme.axis_text_x_fit = fit;
3093 self
3094 }
3095
3096 pub fn axis_text_x_check_overlap(mut self, on: bool) -> Self {
3099 self.theme.axis_text_x_check_overlap = on;
3100 self
3101 }
3102
3103 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
3106 self.theme.title_position = pos;
3107 self
3108 }
3109
3110 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
3112 self.theme.tag_position = pos;
3113 self
3114 }
3115
3116 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
3118 self.theme.legend_direction = Some(dir);
3119 self
3120 }
3121
3122 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
3129 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
3130 self
3131 }
3132
3133 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
3135 self.theme.legend_position = pos;
3136 self
3137 }
3138
3139 pub fn legend_bottom_when_narrow(mut self, share: f64) -> Self {
3146 self.legend_bottom_share = Some(share);
3147 self
3148 }
3149
3150 pub fn legend_inside_auto(self) -> Self {
3161 self.legend_inside_auto_with(&crate::theme::LegendCorner::ALL, None)
3162 }
3163
3164 pub fn legend_inside_auto_with(
3169 mut self,
3170 corners: &[crate::theme::LegendCorner],
3171 fallback: Option<crate::theme::LegendPosition>,
3172 ) -> Self {
3173 use crate::theme::LegendPosition as P;
3174 let mut c = [None; 4];
3175 for (slot, corner) in c.iter_mut().zip(corners) {
3176 *slot = Some(*corner);
3177 }
3178 let fallback = match fallback {
3179 Some(P::Top) => Some(LegendSide::Top),
3180 Some(P::Right) => Some(LegendSide::Right),
3181 Some(P::Bottom) => Some(LegendSide::Bottom),
3182 Some(P::Left) => Some(LegendSide::Left),
3183 _ => None,
3184 };
3185 self.legend_auto = Some(LegendAuto {
3186 corners: c,
3187 fallback,
3188 });
3189 self
3190 }
3191
3192 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
3193 self.theme.primary = Some(color);
3194 self
3195 }
3196
3197 pub fn theme_minimal(mut self) -> Self {
3198 self.apply_preset(crate::theme::presets::theme_minimal());
3199 self
3200 }
3201
3202 pub fn theme_bw(mut self) -> Self {
3203 self.apply_preset(crate::theme::presets::theme_bw());
3204 self
3205 }
3206
3207 pub fn theme_gray(mut self) -> Self {
3208 self.apply_preset(crate::theme::presets::theme_gray());
3209 self
3210 }
3211
3212 pub fn theme_classic(mut self) -> Self {
3213 self.apply_preset(crate::theme::presets::theme_classic());
3214 self
3215 }
3216
3217 pub fn theme_pubr(mut self) -> Self {
3219 self.apply_preset(crate::theme::presets::theme_pubr());
3220 self
3221 }
3222
3223 pub fn theme_linedraw(mut self) -> Self {
3224 self.apply_preset(crate::theme::presets::theme_linedraw());
3225 self
3226 }
3227
3228 pub fn theme_light(mut self) -> Self {
3229 self.apply_preset(crate::theme::presets::theme_light());
3230 self
3231 }
3232
3233 pub fn theme_dark(mut self) -> Self {
3234 self.apply_preset(crate::theme::presets::theme_dark());
3235 self
3236 }
3237
3238 pub fn theme_void(mut self) -> Self {
3239 self.apply_preset(crate::theme::presets::theme_void());
3240 self
3241 }
3242
3243 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
3246 self.theme = self.theme.update(update);
3247 self
3248 }
3249
3250 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
3254 self.guide_legend = guide;
3255 self
3256 }
3257
3258 pub fn labs(mut self, labels: Labels) -> Self {
3261 if labels.title.is_some() {
3262 self.labels.title = labels.title;
3263 }
3264 if labels.subtitle.is_some() {
3265 self.labels.subtitle = labels.subtitle;
3266 }
3267 if labels.x.is_some() {
3268 self.labels.x = labels.x;
3269 }
3270 if labels.y.is_some() {
3271 self.labels.y = labels.y;
3272 }
3273 if labels.caption.is_some() {
3274 self.labels.caption = labels.caption;
3275 }
3276 if labels.tag.is_some() {
3277 self.labels.tag = labels.tag;
3278 }
3279 self
3280 }
3281
3282 pub fn title(mut self, title: &str) -> Self {
3283 self.labels.title = Some(title.to_string());
3284 self
3285 }
3286
3287 pub fn tag(mut self, tag: &str) -> Self {
3290 self.labels.tag = Some(tag.to_string());
3291 self
3292 }
3293
3294 pub fn subtitle(mut self, subtitle: &str) -> Self {
3295 self.labels.subtitle = Some(subtitle.to_string());
3296 self
3297 }
3298
3299 pub fn xlab(mut self, label: &str) -> Self {
3300 self.labels.x = Some(label.to_string());
3301 self
3302 }
3303
3304 pub fn ylab(mut self, label: &str) -> Self {
3305 self.labels.y = Some(label.to_string());
3306 self
3307 }
3308
3309 pub fn caption(mut self, caption: &str) -> Self {
3310 self.labels.caption = Some(caption.to_string());
3311 self
3312 }
3313
3314 pub fn annotate(mut self, annotation: Annotation) -> Self {
3318 self.annotations.push(annotation);
3319 self
3320 }
3321
3322 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
3324 self.annotate(Annotation::text(label, x, y))
3325 }
3326
3327 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
3329 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
3330 }
3331
3332 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
3334 self.annotate(Annotation::segment(x, y, xend, yend))
3335 }
3336
3337 pub fn annotation_logticks(self, sides: &str) -> Self {
3340 self.annotate(Annotation::logticks(sides))
3341 }
3342
3343 pub fn annotation_raster(
3346 self,
3347 image: Vec<u8>,
3348 xmin: f64,
3349 xmax: f64,
3350 ymin: f64,
3351 ymax: f64,
3352 ) -> Self {
3353 self.annotate(Annotation::raster(image, xmin, xmax, ymin, ymax))
3354 }
3355
3356 #[cfg(feature = "png")]
3359 pub fn annotation_raster_rgba(
3360 self,
3361 (width, height): (u32, u32),
3362 rgba: &[u8],
3363 (xmin, xmax, ymin, ymax): (f64, f64, f64, f64),
3364 ) -> Self {
3365 use image::ImageEncoder;
3366 let mut png = Vec::new();
3367 let ok = image::codecs::png::PngEncoder::new(&mut png)
3368 .write_image(rgba, width, height, image::ColorType::Rgba8)
3369 .is_ok();
3370 if !ok {
3371 return self;
3372 }
3373 self.annotation_raster(png, xmin, xmax, ymin, ymax)
3374 }
3375
3376 pub fn annotation_custom(
3380 self,
3381 inset: GGPlot,
3382 xmin: f64,
3383 xmax: f64,
3384 ymin: f64,
3385 ymax: f64,
3386 ) -> Self {
3387 self.annotate(Annotation::inset(inset, xmin, xmax, ymin, ymax))
3388 }
3389
3390 pub fn locale(mut self, locale: crate::locale::Locale) -> Self {
3413 self.locale = Some(locale);
3414 self
3415 }
3416
3417 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
3421 PlotBuilder::build(self)
3422 }
3423
3424 pub fn build(self) -> crate::build::BuiltPlot {
3428 self.try_build().expect("plot build failed")
3429 }
3430
3431 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3435 pub fn save(self, path: &str) -> Result<(), GGError> {
3436 self.save_with_size(path, 800, 600)
3437 }
3438
3439 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3441 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
3442 let (built, layout) = self.prepare(w, h)?;
3443
3444 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
3446
3447 match ext.as_str() {
3448 "svg" => {
3449 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
3450 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
3451 }
3452 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
3453 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
3454 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
3455 }
3456 _ => {
3457 return Err(GGError::UnsupportedFormat(ext));
3458 }
3459 }
3460
3461 Ok(())
3462 }
3463
3464 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3470 pub fn render_svg(self) -> Result<String, GGError> {
3471 self.render_svg_with_size(800, 600)
3472 }
3473
3474 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3478 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
3479 let (built, layout) = self.prepare(w, h)?;
3480 let mut buf = String::new();
3481 {
3482 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
3483 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
3484 }
3485 Ok(buf)
3486 }
3487
3488 pub fn render_svg_native(self) -> Result<String, GGError> {
3492 self.render_svg_native_with_size(800, 600)
3493 }
3494
3495 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
3503 Ok(self.render_native(w, h)?.0.finish())
3504 }
3505
3506 pub fn render_svg_native_with_warnings(
3511 self,
3512 w: u32,
3513 h: u32,
3514 ) -> Result<(String, Vec<String>), GGError> {
3515 let (backend, warnings, _) = self.render_native(w, h)?;
3516 Ok((backend.finish(), warnings))
3517 }
3518
3519 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
3531 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
3532 }
3533
3534 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
3538 let (backend, _, pa) = self.render_native(w, h)?;
3539 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
3540 }
3541
3542 fn render_native(
3546 self,
3547 w: u32,
3548 h: u32,
3549 ) -> Result<
3550 (
3551 crate::render::svg_backend::SvgBackend,
3552 Vec<String>,
3553 crate::render::Rect,
3554 ),
3555 GGError,
3556 > {
3557 let svg_root_data = self.svg_root_data;
3558 let root_attrs = self.root_attrs.clone();
3559 let svg_warnings_attr = self.svg_warnings_attr;
3560 let svg_formatted_values = self.svg_formatted_values;
3561 let svg_title = self.svg_title.clone();
3562 let svg_desc = self.svg_desc.clone();
3563 let (built, layout) = self.prepare(w, h)?;
3564 let pa = layout.plot_area.clone();
3565 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
3566 backend.set_formatted_values(svg_formatted_values);
3567 backend.set_svg_title(svg_title.clone());
3568 backend.set_svg_desc(svg_desc.clone());
3569 let existing_root_attrs = if svg_root_data {
3570 let attrs = crate::render::svg_backend::root_data_attrs(&built);
3571 backend.set_root_attrs(attrs.clone());
3572 let mut reserved = attrs;
3576 for name in ["data-plot", "data-viewport", "data-panels"] {
3579 reserved.push((name.to_string(), String::new()));
3580 }
3581 reserved
3582 } else {
3583 backend.without_plot_attr();
3584 Vec::new()
3585 };
3586 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
3587 let mut warnings = built.warnings;
3588 warnings.extend(backend.take_warnings());
3589
3590 let mut reserved = existing_root_attrs;
3591 if svg_warnings_attr {
3592 reserved.push(("data-warnings".to_string(), String::new()));
3593 }
3594 if svg_title.is_some() || svg_desc.is_some() {
3599 reserved.push(("role".to_string(), String::new()));
3600 }
3601 if svg_title.is_some() {
3602 reserved.push(("aria-labelledby".to_string(), String::new()));
3603 }
3604 if svg_desc.is_some() {
3605 reserved.push(("aria-describedby".to_string(), String::new()));
3606 }
3607 let extra = crate::render::svg_backend::validate_root_attrs(
3608 &root_attrs,
3609 &reserved,
3610 "plot",
3611 &mut warnings,
3612 );
3613 backend.extend_root_attrs(extra);
3614 if svg_warnings_attr {
3615 backend.extend_root_attrs(vec![(
3616 "data-warnings".to_string(),
3617 crate::render::svg_backend::warnings_json(&warnings),
3618 )]);
3619 }
3620 Ok((backend, warnings, pa))
3621 }
3622
3623 #[cfg(feature = "canvas")]
3628 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
3629 let (built, layout) = self.prepare(w, h)?;
3630 let mut backend =
3631 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
3632 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
3633 let (ow, oh) = backend.dimensions();
3634 Ok((ow, oh, backend.into_rgba()))
3635 }
3636
3637 #[cfg(feature = "canvas")]
3641 pub fn render_rgba_area_with_size(
3642 self,
3643 w: u32,
3644 h: u32,
3645 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
3646 let (built, layout) = self.prepare(w, h)?;
3647 let pa = layout.plot_area.clone();
3648 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
3649 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
3650 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
3651 }
3652
3653 #[cfg(feature = "canvas")]
3657 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
3658 let (built, layout) = self.prepare(w, h)?;
3659 let mut backend =
3660 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
3661 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
3662 backend.into_png().map_err(GGError::Render)
3663 }
3664
3665 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3672 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
3673 self.render_png_with_size(800, 600)
3674 }
3675
3676 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3679 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
3680 let (built, layout) = self.prepare(w, h)?;
3681
3682 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
3685 {
3686 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
3687 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
3688 }
3689
3690 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
3691 GGError::Render(RenderError::BackendError(
3692 "PNG buffer size mismatch".to_string(),
3693 ))
3694 })?;
3695 let mut out = std::io::Cursor::new(Vec::new());
3696 img.write_to(&mut out, image::ImageOutputFormat::Png)
3697 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3698 Ok(out.into_inner())
3699 }
3700
3701 pub(crate) fn prepare(
3703 self,
3704 w: u32,
3705 h: u32,
3706 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
3707 let (mut built, meta) = self.build_for_render()?;
3708 let layout = Self::layout_built(&mut built, &meta, w, h);
3709 Ok((built, layout))
3710 }
3711
3712 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
3715 let plot = self;
3716
3717 let has_title = plot.labels.title.is_some();
3718 let has_subtitle = plot.labels.subtitle.is_some();
3719 let has_caption = plot.labels.caption.is_some();
3720 let has_legend = plot.has_legend_mapping();
3721 let legend_bottom_share = plot.legend_bottom_share;
3722 let legend_auto = plot.legend_auto;
3723 let x_label = plot.labels.x.clone();
3724 let y_label = plot.labels.y.clone();
3725
3726 let mut built = PlotBuilder::build(plot)?;
3727
3728 built.theme.resolve_inheritance();
3730
3731 if let Some(ref label) = x_label {
3733 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
3734 s.set_name(label);
3735 }
3736 }
3737 if let Some(ref label) = y_label {
3738 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
3739 s.set_name(label);
3740 }
3741 }
3742
3743 Ok((
3744 built,
3745 RenderMeta {
3746 has_title,
3747 has_subtitle,
3748 has_caption,
3749 has_legend,
3750 legend_bottom_share,
3751 legend_auto,
3752 },
3753 ))
3754 }
3755
3756 pub(crate) fn layout_built(
3759 built: &mut crate::build::BuiltPlot,
3760 meta: &RenderMeta,
3761 w: u32,
3762 h: u32,
3763 ) -> PlotLayout {
3764 match meta.legend_auto {
3765 Some(auto) if meta.has_legend && built.panels.is_empty() => {
3766 Self::layout_legend_auto(built, meta, auto, w, h)
3767 }
3768 _ => Self::layout_built_inner(built, meta, w, h),
3769 }
3770 }
3771
3772 fn layout_legend_auto(
3775 built: &mut crate::build::BuiltPlot,
3776 meta: &RenderMeta,
3777 auto: LegendAuto,
3778 w: u32,
3779 h: u32,
3780 ) -> PlotLayout {
3781 use crate::render::probe::Probe;
3782 use crate::theme::LegendPosition as P;
3783 let total = crate::render::Rect {
3784 x: 0.0,
3785 y: 0.0,
3786 width: w as f64,
3787 height: h as f64,
3788 };
3789 let (theme0, n_warnings) = (built.theme.clone(), built.warnings.len());
3792 let reset = |built: &mut crate::build::BuiltPlot, pos: P| {
3793 built.theme = theme0.clone();
3794 built.theme.legend_position = pos;
3795 built.warnings.truncate(n_warnings);
3796 };
3797
3798 reset(built, P::Inside(1.0, 0.0));
3799 let layout = Self::layout_built_inner(built, meta, w, h);
3800 let pa = layout.plot_area.clone();
3801 let mut marks = Probe::new(pa.clone(), total.clone());
3802 for layer in built.layers.iter().filter(|l| l.data.nrows() > 0) {
3803 let _ = layer.geom.draw(
3804 &layer.data,
3805 built.coord.as_ref(),
3806 &built.scales,
3807 &built.theme,
3808 &mut marks,
3809 );
3810 }
3811 for corner in auto.corners.iter().flatten() {
3812 built.theme.legend_position = corner.inside();
3813 let mut legend = Probe::new(pa.clone(), total.clone());
3814 let drawn = crate::guide::legend::draw_legend_planned(
3815 &built.scales,
3816 &built.theme,
3817 &pa,
3818 pa.width,
3819 &mut legend,
3820 &built.guide_legend,
3821 &built.suppressed_aes,
3822 &built.legend_plans,
3823 );
3824 let Some((x0, y0, x1, y1)) = legend.extent().filter(|_| drawn.is_ok()) else {
3825 continue;
3826 };
3827 const PAD: f64 = 2.0;
3828 if !marks.hits((x0 - PAD, y0 - PAD, x1 + PAD, y1 + PAD)) {
3829 built.legend_at = Some(format!("inside-{}", corner.name()));
3830 return layout;
3831 }
3832 }
3833
3834 let sides = match auto.fallback {
3837 Some(side) => vec![side],
3838 None => vec![LegendSide::Top, LegendSide::Right],
3839 };
3840 let data_area = |built: &crate::build::BuiltPlot, layout: &PlotLayout| {
3841 let r = built
3842 .coord
3843 .data_rect(&layout.plot_area)
3844 .unwrap_or_else(|| layout.plot_area.clone());
3845 r.width.max(0.0) * r.height.max(0.0)
3846 };
3847 let mut best = (sides[0], f64::MIN);
3848 for &side in &sides {
3849 reset(built, side.position());
3850 let trial = Self::layout_built_inner(built, meta, w, h);
3851 let area = data_area(built, &trial);
3852 if area > best.1 {
3853 best = (side, area);
3854 }
3855 }
3856 reset(built, best.0.position());
3857 let layout = Self::layout_built_inner(built, meta, w, h);
3858 built.legend_at = Some(
3859 match built.theme.legend_position {
3860 P::Top => "top",
3861 P::Right => "right",
3862 P::Bottom => "bottom",
3863 _ => "left",
3864 }
3865 .to_string(),
3866 );
3867 layout
3868 }
3869
3870 fn layout_built_inner(
3871 built: &mut crate::build::BuiltPlot,
3872 meta: &RenderMeta,
3873 w: u32,
3874 h: u32,
3875 ) -> PlotLayout {
3876 let RenderMeta {
3877 has_title,
3878 has_subtitle,
3879 has_caption,
3880 has_legend,
3881 legend_bottom_share,
3882 legend_auto: _,
3883 } = *meta;
3884 let x_axis_top = built
3885 .scales
3886 .get(&crate::aes::Aesthetic::X)
3887 .map(|s| s.axis_position_opposite())
3888 .unwrap_or(false);
3889
3890 if let Some(share) = legend_bottom_share {
3893 if has_legend
3894 && matches!(
3895 built.theme.legend_position,
3896 crate::theme::LegendPosition::Right
3897 )
3898 {
3899 let panel_width = |pos| {
3900 let mut theme = built.theme.clone();
3901 theme.legend_position = pos;
3902 PlotLayout::compute_with_extras(
3903 w as f64,
3904 h as f64,
3905 &theme,
3906 has_title,
3907 has_subtitle,
3908 has_caption,
3909 has_legend,
3910 x_axis_top,
3911 None,
3912 None,
3913 crate::render::layout::LayoutExtras::default(),
3914 )
3915 .plot_area
3916 .width
3917 };
3918 let free = panel_width(crate::theme::LegendPosition::None);
3921 if panel_width(crate::theme::LegendPosition::Right) < (1.0 - share) * free {
3922 built.theme.legend_position = crate::theme::LegendPosition::Bottom;
3923 }
3924 }
3925 }
3926
3927 let flipped = built.coord.is_flipped();
3931 let h_aes = if flipped {
3932 crate::aes::Aesthetic::Y
3933 } else {
3934 crate::aes::Aesthetic::X
3935 };
3936 let v_aes = if flipped {
3937 crate::aes::Aesthetic::X
3938 } else {
3939 crate::aes::Aesthetic::Y
3940 };
3941
3942 let drawn_x_labels: Vec<(f64, String)> = built
3950 .scales
3951 .get(&h_aes)
3952 .map(|hs| {
3953 hs.breaks()
3954 .into_iter()
3955 .filter(|(_, label)| !label.is_empty())
3956 .collect()
3957 })
3958 .unwrap_or_default();
3959 let h_is_discrete = built
3960 .scales
3961 .get(&h_aes)
3962 .map(|hs| hs.is_discrete())
3963 .unwrap_or(false);
3964
3965 if built.theme.axis_text_x.visible
3978 && built.theme.axis_text_x.angle.abs() < 1.0
3979 && h_is_discrete
3980 && !drawn_x_labels.is_empty()
3981 {
3982 let font = built.theme.axis_text_x.size;
3983 let max_w = drawn_x_labels
3984 .iter()
3985 .map(|(_, l)| crate::render::text::text_width(l, font))
3986 .fold(0.0_f64, f64::max);
3987 let n = drawn_x_labels.len() as f64;
3988 let spacing = 0.9 * w as f64 / n; if max_w > spacing {
3990 let vertical = max_w * std::f64::consts::FRAC_1_SQRT_2 > spacing;
3994 built.theme.axis_text_x.angle = if vertical { 270.0 } else { -45.0 };
3995 }
3996 }
3997
3998 let x_label_height = if built.theme.axis_text_x.visible
4013 && built.theme.axis_text_x.angle.abs() > 10.0
4014 && !drawn_x_labels.is_empty()
4015 {
4016 let font = built.theme.axis_text_x.size;
4017 let angle = built.theme.axis_text_x.angle;
4018 let dir = if x_axis_top { -1.0 } else { 1.0 };
4019 let (rise, fall) = crate::render::text::rotated_label_rise_fall(
4020 drawn_x_labels.iter().map(|(_, l)| l.as_str()),
4021 font,
4022 angle,
4023 crate::render::text::rotated_anchor_frac(angle, dir),
4024 );
4025 Some(built.theme.legend_spacing / 2.0 + font / 2.0 + rise + fall)
4026 } else {
4027 None
4028 };
4029
4030 let y_label_width = if built.theme.axis_text_y.visible {
4034 built.scales.get(&v_aes).map(|vs| {
4035 crate::guide::axis::y_label_reserve(&built.theme, &vs.breaks()).min(0.35 * w as f64)
4036 })
4037 } else {
4038 None
4039 };
4040
4041 let mut extras = crate::render::layout::LayoutExtras::default();
4052 if let (Some(vs), Some(sec)) = (
4056 built.scales.get(&crate::aes::Aesthetic::Y),
4057 built.scales.sec_axis(&crate::aes::Aesthetic::Y),
4058 ) {
4059 let theme = &built.theme;
4060 let max_ch = match sec.own_ticks(vs) {
4061 Some(t) => t.iter().map(|(_, l)| l.chars().count()).max(),
4062 None => vs.breaks().iter().map(|(_, l)| l.chars().count() + 2).max(),
4063 }
4064 .unwrap_or(0);
4065 let text = if theme.axis_text_y.visible {
4066 (max_ch as f64 * theme.axis_text_y.size * 0.6).max(theme.axis_text_y.size * 3.5)
4067 } else {
4068 0.0
4069 };
4070 let title = if !sec.name.is_empty() && theme.axis_title_y.visible {
4071 theme.axis_title_y.size * 1.5
4072 } else {
4073 0.0
4074 };
4075 extras.right += theme.axis_ticks_length + theme.legend_spacing + text + title;
4076 }
4077 let can_reserve = built.facet.is_none()
4078 && built.theme.axis_text_x.visible
4079 && built.theme.axis_text_x_fit == crate::theme::AxisLabelFit::Reserve
4080 && !built.coord.is_polar()
4081 && built.coord.supports_axis_reserve();
4082 if can_reserve {
4083 if let Some(hs) = built.scales.get(&h_aes) {
4084 let breaks = hs.breaks();
4085 if let (Some(first), Some(last)) = (breaks.first(), breaks.last()) {
4086 let angle = built.theme.axis_text_x.angle;
4087 let anchor = if angle.abs() > 10.0 {
4088 let dir = if x_axis_top { -1.0 } else { 1.0 };
4089 crate::render::text::rotated_anchor_frac(angle, dir)
4090 } else {
4091 0.5
4092 };
4093 let size = built.theme.axis_text_x.size;
4094 const EDGE: f64 = 1.0;
4095 for _ in 0..2 {
4096 let layout = PlotLayout::compute_with_extras(
4097 w as f64,
4098 h as f64,
4099 &built.theme,
4100 has_title,
4101 has_subtitle,
4102 has_caption,
4103 has_legend,
4104 x_axis_top,
4105 y_label_width,
4106 x_label_height,
4107 extras,
4108 );
4109 let area = &layout.plot_area;
4110 let edge_y = area.y + area.height;
4111 let to_point = |pos: f64| if flipped { (0.0, pos) } else { (pos, 0.0) };
4112 let px_first = built.coord.transform(to_point(first.0), area).0;
4113 let px_last = built.coord.transform(to_point(last.0), area).0;
4114 let bbox_first = crate::render::text::label_bbox(
4115 &first.1, size, px_first, edge_y, angle, anchor,
4116 );
4117 let bbox_last = crate::render::text::label_bbox(
4118 &last.1, size, px_last, edge_y, angle, anchor,
4119 );
4120 let deficit_left = (EDGE - bbox_first[0]).max(0.0);
4121 let deficit_right = (bbox_last[2] - (w as f64 - EDGE)).max(0.0);
4122 if deficit_left <= 0.0 && deficit_right <= 0.0 {
4123 break;
4124 }
4125 let (p_first, p_last) = (first.0, last.0);
4134 if deficit_left > 0.0 && deficit_right > 0.0 {
4135 let (a, b, c, d) = (1.0 - p_first, p_first, 1.0 - p_last, p_last);
4140 let det = a * d - b * c;
4141 if det.abs() > 0.01 {
4142 extras.left +=
4143 ((deficit_left * d + deficit_right * b) / det).max(0.0);
4144 extras.right +=
4145 ((deficit_right * a + deficit_left * c) / det).max(0.0);
4146 } else {
4147 extras.left += deficit_left / (1.0 - p_first).max(0.05);
4151 extras.right += deficit_right / p_last.max(0.05);
4152 }
4153 } else if deficit_left > 0.0 {
4154 extras.left += deficit_left / (1.0 - p_first).max(0.05);
4155 } else if deficit_right > 0.0 {
4156 extras.right += deficit_right / p_last.max(0.05);
4157 }
4158 const MIN_PANEL_WIDTH: f64 = 50.0;
4173 let cap = (w as f64 - MIN_PANEL_WIDTH).max(0.0);
4174 if extras.left + extras.right > cap {
4175 let scale = if extras.left + extras.right > 0.0 {
4176 cap / (extras.left + extras.right)
4177 } else {
4178 0.0
4179 };
4180 extras.left *= scale;
4181 extras.right *= scale;
4182 }
4183 }
4184 }
4185 }
4186 }
4187
4188 if has_legend
4195 && !matches!(
4196 built.theme.legend_position,
4197 crate::theme::LegendPosition::None
4198 )
4199 {
4200 let legend_aes =
4201 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
4202 extras.legend_max_key_size = legend_aes
4206 .iter()
4207 .filter_map(|aes| {
4208 let scale = built.scales.get(aes)?;
4209 let breaks = scale.breaks();
4210 Some(crate::guide::legend::size_key_diameter(
4211 aes,
4212 &built.scales,
4213 scale,
4214 &breaks,
4215 ))
4216 })
4217 .fold(0.0_f64, f64::max);
4218 let has_wrap_guide = legend_aes.iter().any(|a| {
4229 let is_discrete = built
4230 .scales
4231 .get(a)
4232 .map(|s| s.is_discrete())
4233 .unwrap_or(false);
4234 is_discrete || !crate::guide::legend::is_colorbar_aesthetic(a)
4235 });
4236 if has_wrap_guide {
4237 let layout0 = PlotLayout::compute_with_extras(
4238 w as f64,
4239 h as f64,
4240 &built.theme,
4241 has_title,
4242 has_subtitle,
4243 has_caption,
4244 has_legend,
4245 x_axis_top,
4246 y_label_width,
4247 x_label_height,
4248 extras,
4249 );
4250 let (legend_x0, _, mut is_horizontal) = crate::guide::legend::legend_position(
4260 &built.theme,
4261 &layout0.plot_area,
4262 extras.legend_extra_rows,
4263 (0.0, 0.0),
4264 0.0,
4265 &layout0.total,
4266 );
4267 if let Some(dir) = built.theme.legend_direction {
4268 is_horizontal = matches!(dir, crate::theme::LegendDirection::Horizontal);
4269 }
4270 let avail_width =
4271 (layout0.plot_area.x + layout0.plot_area.width - legend_x0).max(0.0);
4272 let mut legend_warnings = Vec::new();
4273 let mut plans = crate::guide::legend::plan_all_legends(
4274 &built.scales,
4275 &built.guide_legend,
4276 &built.theme,
4277 &built.suppressed_aes,
4278 avail_width,
4279 layout0.plot_area.height,
4280 is_horizontal,
4281 &mut legend_warnings,
4282 );
4283 built.warnings.extend(legend_warnings);
4284
4285 if !plans.is_empty() {
4286 extras.legend_extra_rows = if is_horizontal {
4292 plans
4293 .values()
4294 .map(|p| p.rows.saturating_sub(1))
4295 .max()
4296 .unwrap_or(0)
4297 } else {
4298 plans.values().map(|p| p.rows.saturating_sub(1)).sum()
4299 };
4300 extras.legend_extra_width = plans
4307 .values()
4308 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
4309 .fold(0.0_f64, f64::max);
4310
4311 if !is_horizontal {
4323 let final_layout = PlotLayout::compute_with_extras(
4324 w as f64,
4325 h as f64,
4326 &built.theme,
4327 has_title,
4328 has_subtitle,
4329 has_caption,
4330 has_legend,
4331 x_axis_top,
4332 y_label_width,
4333 x_label_height,
4334 extras,
4335 );
4336 let (legend_x_final, _, _) = crate::guide::legend::legend_position(
4342 &built.theme,
4343 &final_layout.plot_area,
4344 extras.legend_extra_rows,
4345 (0.0, 0.0),
4346 0.0,
4347 &final_layout.total,
4348 );
4349 let swatch_and_gap =
4350 built.theme.legend_key_width + built.theme.legend_spacing;
4351 let text_size = built.theme.legend_text.size;
4352 fn right_edge_of(
4358 legend_x: f64,
4359 swatch_and_gap: f64,
4360 text_size: f64,
4361 plan: &crate::guide::legend::LegendPlan,
4362 breaks: &[(f64, String)],
4363 ) -> f64 {
4364 plan.entries
4365 .iter()
4366 .filter_map(|e| breaks.get(e.break_idx).map(|(_, l)| (e, l)))
4367 .map(|(e, label)| {
4368 let text =
4369 e.label_override.as_deref().unwrap_or(label.as_str());
4370 legend_x
4371 + e.x_offset
4372 + swatch_and_gap
4373 + crate::render::text::text_width(text, text_size)
4374 })
4375 .fold(0.0_f64, f64::max)
4376 }
4377
4378 let mut updates: Vec<(
4379 crate::aes::Aesthetic,
4380 crate::guide::legend::LegendPlan,
4381 )> = Vec::new();
4382 let mut reduced_aes: Vec<crate::aes::Aesthetic> = Vec::new();
4389 for (aes, plan) in &plans {
4390 if plan.cols <= 1 {
4391 continue;
4392 }
4393 let Some(scale) = built.scales.get(aes) else {
4394 continue;
4395 };
4396 let breaks = scale.breaks();
4397 if right_edge_of(
4398 legend_x_final,
4399 swatch_and_gap,
4400 text_size,
4401 plan,
4402 &breaks,
4403 ) <= w as f64
4404 {
4405 continue; }
4407
4408 let n = breaks.len();
4411 let mut reduced: Vec<crate::guide::legend::LegendPlan> = Vec::new();
4412 for c in (1..plan.cols).rev() {
4413 let mut candidate_guide = built.guide_legend.clone();
4414 candidate_guide.ncol = Some(c);
4415 candidate_guide.nrow = None;
4416 let mut tmp_warnings = Vec::new();
4417 let candidate_guide =
4418 candidate_guide.validated(n, &mut tmp_warnings);
4419 if let Some(cand) = crate::guide::legend::plan_legend(
4420 &breaks,
4421 &candidate_guide,
4422 &built.theme,
4423 avail_width,
4424 layout0.plot_area.height,
4425 false,
4426 ) {
4427 reduced.push(cand);
4428 }
4429 }
4430 if let Some(fits) = reduced.iter().find(|c| {
4431 right_edge_of(legend_x_final, swatch_and_gap, text_size, c, &breaks)
4432 <= w as f64
4433 }) {
4434 extras.legend_extra_rows =
4435 extras.legend_extra_rows.max(fits.rows.saturating_sub(1));
4436 updates.push((aes.clone(), fits.clone()));
4437 reduced_aes.push(aes.clone());
4438 continue;
4439 }
4440
4441 let base = reduced.last().cloned().unwrap_or_else(|| plan.clone());
4446 extras.legend_extra_rows =
4447 extras.legend_extra_rows.max(base.rows.saturating_sub(1));
4448 let mut truncated = base.clone();
4449 for e in &mut truncated.entries {
4450 let Some((_, label)) = breaks.get(e.break_idx) else {
4451 continue;
4452 };
4453 let avail = (w as f64
4454 - (legend_x_final + e.x_offset + swatch_and_gap))
4455 .max(0.0);
4456 if let Some(short) =
4457 crate::render::text::truncate_to_width(label, avail, text_size)
4458 {
4459 e.label_override = Some(short);
4460 }
4461 }
4462
4463 let truncated_edge = right_edge_of(
4467 legend_x_final,
4468 swatch_and_gap,
4469 text_size,
4470 &truncated,
4471 &breaks,
4472 );
4473 if truncated_edge > w as f64 {
4474 built.warnings.push(format!(
4475 "legend: the {}-column grid still doesn't fit the {w}px chart (needs about {} px)",
4476 truncated.cols,
4477 truncated_edge.ceil() as i64
4478 ));
4479 }
4480 updates.push((aes.clone(), truncated));
4481 }
4482 for (aes, new_plan) in &updates {
4483 plans.insert(aes.clone(), new_plan.clone());
4484 }
4485
4486 if !reduced_aes.is_empty() {
4527 let original_reserve = extras.legend_extra_width;
4528 let tighter_reserve = plans
4529 .values()
4530 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
4531 .fold(0.0_f64, f64::max);
4532 if tighter_reserve < original_reserve {
4533 let mut retry_extras = extras;
4534 retry_extras.legend_extra_width = tighter_reserve;
4535 let retry_layout = PlotLayout::compute_with_extras(
4536 w as f64,
4537 h as f64,
4538 &built.theme,
4539 has_title,
4540 has_subtitle,
4541 has_caption,
4542 has_legend,
4543 x_axis_top,
4544 y_label_width,
4545 x_label_height,
4546 retry_extras,
4547 );
4548 let (retry_x, _, _) = crate::guide::legend::legend_position(
4554 &built.theme,
4555 &retry_layout.plot_area,
4556 retry_extras.legend_extra_rows,
4557 (0.0, 0.0),
4558 0.0,
4559 &retry_layout.total,
4560 );
4561 let all_still_fit = plans.iter().all(|(aes, plan)| {
4562 let Some(scale) = built.scales.get(aes) else {
4563 return true;
4564 };
4565 let breaks = scale.breaks();
4566 right_edge_of(retry_x, swatch_and_gap, text_size, plan, &breaks)
4567 <= w as f64
4568 });
4569 if all_still_fit {
4570 extras.legend_extra_width = tighter_reserve;
4571 }
4572 }
4573 }
4574 }
4575 for (aes, plan) in &plans {
4576 if let Some(ov) = &plan.overflow {
4577 let n = built
4578 .scales
4579 .get(aes)
4580 .map(|s| s.breaks().len())
4581 .unwrap_or(ov.dropped);
4582 let shown = n.saturating_sub(ov.dropped);
4583 built.warnings.push(format!(
4584 "legend: {} of {n} entries don't fit the chart; the legend shows {shown} and \"+{} more\"",
4585 ov.dropped, ov.dropped
4586 ));
4587 }
4588 }
4589 }
4590 built.legend_plans = plans;
4596 }
4597 }
4598
4599 if has_legend
4610 && !matches!(
4611 built.theme.legend_position,
4612 crate::theme::LegendPosition::None
4613 )
4614 {
4615 let is_horizontal = match built.theme.legend_direction {
4616 Some(dir) => matches!(dir, crate::theme::LegendDirection::Horizontal),
4617 None => matches!(
4618 built.theme.legend_position,
4619 crate::theme::LegendPosition::Top | crate::theme::LegendPosition::Bottom
4620 ),
4621 };
4622 if is_horizontal {
4623 let legend_aes =
4624 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
4625 let continuous_colourbar = legend_aes.iter().find_map(|aes| {
4626 if !matches!(
4627 aes,
4628 crate::aes::Aesthetic::Color | crate::aes::Aesthetic::Fill
4629 ) {
4630 return None;
4631 }
4632 let scale = built.scales.get(aes)?;
4633 if scale.is_discrete() {
4634 return None;
4635 }
4636 Some(scale)
4637 });
4638 if let Some(scale) = continuous_colourbar {
4639 let title = built
4640 .guide_legend
4641 .title
4642 .as_deref()
4643 .unwrap_or_else(|| scale.name());
4644 let bar_height = crate::guide::legend::continuous_horizontal_bar_height(
4645 &built.theme,
4646 !title.is_empty(),
4647 );
4648 let key_h = built
4649 .theme
4650 .legend_key_height
4651 .max(extras.legend_max_key_size);
4652 extras.continuous_horizontal_legend_extra_height =
4653 (bar_height - key_h).max(0.0);
4654 }
4655 }
4656 }
4657
4658 PlotLayout::compute_with_extras(
4659 w as f64,
4660 h as f64,
4661 &built.theme,
4662 has_title,
4663 has_subtitle,
4664 has_caption,
4665 has_legend,
4666 x_axis_top,
4667 y_label_width,
4668 x_label_height,
4669 extras,
4670 )
4671 }
4672
4673 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
4676 fn render_into<DB>(
4677 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
4678 built: &crate::build::BuiltPlot,
4679 layout: &PlotLayout,
4680 ) -> Result<(), GGError>
4681 where
4682 DB: plotters::prelude::DrawingBackend,
4683 DB::ErrorType: 'static,
4684 {
4685 area.fill(&plotters::prelude::WHITE)
4686 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
4687 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
4688 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
4689 area.present()
4690 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
4691 Ok(())
4692 }
4693
4694 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
4697 pub fn ggsave(
4698 self,
4699 path: &str,
4700 width_inches: f64,
4701 height_inches: f64,
4702 dpi: f64,
4703 ) -> Result<(), GGError> {
4704 let w = (width_inches * dpi) as u32;
4705 let h = (height_inches * dpi) as u32;
4706 self.save_with_size(path, w, h)
4707 }
4708
4709 fn has_legend_mapping(&self) -> bool {
4724 use crate::guide::legend::is_legend_aesthetic;
4729 let plot_level = self
4730 .mapping
4731 .mappings
4732 .iter()
4733 .any(|m| is_legend_aesthetic(&m.aesthetic));
4734 plot_level
4735 || self.layers.iter().any(|layer| {
4736 if layer.show_legend == Some(false) {
4737 return false;
4738 }
4739 let effective = if layer.geom.inherit_aes() {
4740 self.mapping.merge(&layer.mapping)
4741 } else {
4742 layer.mapping.clone()
4743 };
4744 effective
4745 .mappings
4746 .iter()
4747 .any(|m| is_legend_aesthetic(&m.aesthetic))
4748 })
4749 }
4750}
4751
4752#[derive(Clone, Copy, Debug)]
4754pub(crate) struct RenderMeta {
4755 pub(crate) has_title: bool,
4756 pub(crate) has_subtitle: bool,
4757 pub(crate) has_caption: bool,
4758 pub(crate) has_legend: bool,
4759 pub(crate) legend_bottom_share: Option<f64>,
4760 pub(crate) legend_auto: Option<LegendAuto>,
4761}
4762
4763#[derive(Clone, Copy, Debug)]
4765pub(crate) struct LegendAuto {
4766 corners: [Option<crate::theme::LegendCorner>; 4],
4767 fallback: Option<LegendSide>,
4768}
4769
4770#[derive(Clone, Copy, Debug)]
4772enum LegendSide {
4773 Top,
4774 Right,
4775 Bottom,
4776 Left,
4777}
4778
4779impl LegendSide {
4780 fn position(self) -> crate::theme::LegendPosition {
4781 use crate::theme::LegendPosition as P;
4782 match self {
4783 LegendSide::Top => P::Top,
4784 LegendSide::Right => P::Right,
4785 LegendSide::Bottom => P::Bottom,
4786 LegendSide::Left => P::Left,
4787 }
4788 }
4789}
4790
4791#[derive(Debug)]
4793#[non_exhaustive]
4794pub enum GGError {
4795 Render(RenderError),
4796 UnsupportedFormat(String),
4797 Io(std::io::Error),
4798 ValidationError(String),
4799}
4800
4801impl std::fmt::Display for GGError {
4802 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4803 match self {
4804 GGError::Render(e) => write!(f, "Render error: {e}"),
4805 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
4806 GGError::Io(e) => write!(f, "IO error: {e}"),
4807 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
4808 }
4809 }
4810}
4811
4812impl std::error::Error for GGError {}