1#[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2use plotters::prelude::IntoDrawingArea;
3
4use crate::aes::Aes;
5use crate::annotate::Annotation;
6use crate::build::PlotBuilder;
7use crate::coord::cartesian::CoordCartesian;
8use crate::coord::fixed::CoordFixed;
9use crate::coord::flip::CoordFlip;
10use crate::coord::polar::CoordPolar;
11use crate::coord::Coord;
12use crate::data::{DataFrame, GGData, Value};
13use crate::facet::{Facet, FacetLabeller, FacetScales, FacetSpace};
14use crate::geom::area::GeomArea;
15use crate::geom::bar::GeomBar;
16use crate::geom::bin2d::GeomBin2d;
17use crate::geom::blank::GeomBlank;
18use crate::geom::boxplot::GeomBoxplot;
19use crate::geom::col::GeomCol;
20use crate::geom::contour::GeomContour;
21use crate::geom::count::GeomCount;
22use crate::geom::crossbar::GeomCrossbar;
23use crate::geom::curve::GeomCurve;
24use crate::geom::density::GeomDensity;
25use crate::geom::density2d::GeomDensity2d;
26use crate::geom::dotplot::GeomDotplot;
27use crate::geom::errorbar::GeomErrorbar;
28use crate::geom::freqpoly::GeomFreqpoly;
29use crate::geom::hex::GeomHex;
30use crate::geom::histogram::GeomHistogram;
31use crate::geom::jitter::GeomJitter;
32use crate::geom::line::GeomLine;
33use crate::geom::linerange::GeomLinerange;
34use crate::geom::path::GeomPath;
35use crate::geom::point::GeomPoint;
36use crate::geom::pointrange::GeomPointrange;
37use crate::geom::polygon::GeomPolygon;
38use crate::geom::qq::{GeomQQ, GeomQQLine};
39use crate::geom::rect::GeomRect;
40use crate::geom::refline::{GeomAbline, GeomHline, GeomVline};
41use crate::geom::ribbon::GeomRibbon;
42use crate::geom::rug::GeomRug;
43use crate::geom::segment::GeomSegment;
44use crate::geom::smooth::GeomSmooth;
45use crate::geom::spoke::GeomSpoke;
46use crate::geom::step::GeomStep;
47use crate::geom::text::{GeomLabel, GeomText};
48use crate::geom::tile::GeomTile;
49use crate::geom::violin::GeomViolin;
50use crate::geom::{Geom, GeomParams};
51use crate::position::Position;
52use crate::render::layout::PlotLayout;
53#[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
54use crate::render::plotters_backend::PlottersAdapter;
55use crate::render::renderer::PlotRenderer;
56use crate::render::RenderError;
57use crate::scale::continuous::ScaleContinuous;
58use crate::scale::transform::ScaleTransform;
59use crate::scale::Scale;
60use crate::stat::Stat;
61use crate::theme::Theme;
62
63mod stat_layers;
66
67#[derive(Clone, Debug, Default)]
69pub struct Labels {
70 pub title: Option<String>,
71 pub subtitle: Option<String>,
72 pub x: Option<String>,
73 pub y: Option<String>,
74 pub caption: Option<String>,
75 pub tag: Option<String>,
77}
78
79pub struct Layer {
81 pub data: Option<DataFrame>,
82 pub mapping: Aes,
83 pub geom: Box<dyn Geom>,
84 pub stat: Box<dyn Stat>,
85 pub position: Box<dyn Position>,
86 pub params: GeomParams,
87 pub show_legend: Option<bool>,
88 pub explicit_style: bool,
91 pub tooltip_template: Option<String>,
94}
95
96pub struct GGPlot {
98 pub(crate) data: DataFrame,
99 pub(crate) mapping: Aes,
100 pub(crate) layers: Vec<Layer>,
101 pub(crate) scales: Vec<Box<dyn Scale>>,
102 pub(crate) coord: Box<dyn Coord>,
103 pub(crate) theme: Theme,
104 pub(crate) labels: Labels,
105 pub(crate) facet: Facet,
106 pub(crate) annotations: Vec<Annotation>,
107 pub(crate) guide_legend: crate::guide::config::GuideLegend,
108 pub(crate) warnings: Vec<String>,
111 pub(crate) default_aspect_ratio: Option<f64>,
114 pub(crate) locale: Option<crate::locale::Locale>,
116 pub(crate) svg_root_data: bool,
121 pub(crate) root_attrs: Vec<(String, String)>,
124 pub(crate) svg_warnings_attr: bool,
128 pub(crate) svg_formatted_values: bool,
132}
133
134impl GGPlot {
135 pub fn new(data: impl GGData) -> Self {
142 GGPlot {
143 data: data.into_dataframe(),
144 mapping: Aes::default(),
145 layers: Vec::new(),
146 scales: Vec::new(),
147 coord: Box::new(CoordCartesian::new()),
148 theme: Theme::default(),
149 labels: Labels::default(),
150 facet: Facet::default(),
151 annotations: Vec::new(),
152 guide_legend: crate::guide::config::GuideLegend::default(),
153 warnings: Vec::new(),
154 default_aspect_ratio: None,
155 locale: None,
156 svg_root_data: true,
157 root_attrs: Vec::new(),
158 svg_warnings_attr: false,
159 svg_formatted_values: false,
160 }
161 }
162
163 pub fn aes(mut self, mapping: Aes) -> Self {
165 self.warnings.extend(mapping.warnings.iter().cloned());
166 self.mapping = mapping;
167 self
168 }
169
170 pub fn geom_point(self) -> Self {
173 self.add_geom(GeomPoint::default())
174 }
175
176 pub fn geom_point_with(self, geom: GeomPoint) -> Self {
177 self.add_geom_with(geom)
178 }
179
180 pub fn geom_line(self) -> Self {
181 self.add_geom(GeomLine::default())
182 }
183
184 pub fn geom_line_with(self, geom: GeomLine) -> Self {
185 self.add_geom_with(geom)
186 }
187
188 pub fn geom_bar(self) -> Self {
189 self.add_geom(GeomBar::default())
190 }
191
192 pub fn geom_bar_with(self, geom: GeomBar) -> Self {
193 self.add_geom_with(geom)
194 }
195
196 pub fn geom_histogram(self) -> Self {
197 self.add_geom(GeomHistogram::default())
198 }
199
200 pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self {
201 self.add_geom_with(geom)
202 }
203
204 pub fn geom_boxplot(self) -> Self {
205 self.add_geom(GeomBoxplot::default())
206 }
207
208 pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self {
209 self.add_geom_with(geom)
210 }
211
212 pub fn geom_smooth(self) -> Self {
213 self.add_geom(GeomSmooth::default())
214 }
215
216 pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self {
217 self.add_geom_with(geom)
218 }
219
220 pub fn geom_col(self) -> Self {
221 self.add_geom(GeomCol::default())
222 }
223
224 pub fn geom_col_with(self, geom: GeomCol) -> Self {
225 self.add_geom_with(geom)
226 }
227
228 pub fn geom_hline(self, yintercept: f64) -> Self {
232 self.add_refline(
233 GeomHline::new(yintercept),
234 false,
235 &[("yintercept", yintercept)],
236 )
237 }
238
239 pub fn geom_hline_with(self, geom: GeomHline) -> Self {
241 let y = geom.yintercept;
242 self.add_refline(geom, true, &[("yintercept", y)])
243 }
244
245 pub fn geom_hline_aes(self, mapping: Aes) -> Self {
269 self.add_geom(GeomHline::mapped()).layer_aes(mapping)
270 }
271
272 pub fn geom_hline_aes_with(self, geom: GeomHline, mapping: Aes) -> Self {
274 self.add_geom_with(geom).layer_aes(mapping)
275 }
276
277 pub fn geom_vline(self, xintercept: f64) -> Self {
280 self.add_refline(
281 GeomVline::new(xintercept),
282 false,
283 &[("xintercept", xintercept)],
284 )
285 }
286
287 pub fn geom_vline_with(self, geom: GeomVline) -> Self {
289 let x = geom.xintercept;
290 self.add_refline(geom, true, &[("xintercept", x)])
291 }
292
293 pub fn geom_vline_aes(self, mapping: Aes) -> Self {
296 self.add_geom(GeomVline::mapped()).layer_aes(mapping)
297 }
298
299 pub fn geom_vline_aes_with(self, geom: GeomVline, mapping: Aes) -> Self {
301 self.add_geom_with(geom).layer_aes(mapping)
302 }
303
304 pub fn geom_abline(self, slope: f64, intercept: f64) -> Self {
307 self.add_refline(
308 GeomAbline::new(slope, intercept),
309 false,
310 &[("slope", slope), ("intercept", intercept)],
311 )
312 }
313
314 pub fn geom_abline_with(self, geom: GeomAbline) -> Self {
316 let (b, a) = (geom.slope, geom.intercept);
317 self.add_refline(geom, true, &[("slope", b), ("intercept", a)])
318 }
319
320 pub fn geom_abline_aes(self, mapping: Aes) -> Self {
323 self.add_geom(GeomAbline::mapped()).layer_aes(mapping)
324 }
325
326 pub fn geom_abline_aes_with(self, geom: GeomAbline, mapping: Aes) -> Self {
328 self.add_geom_with(geom).layer_aes(mapping)
329 }
330
331 fn add_refline(
334 self,
335 geom: impl Geom + 'static,
336 explicit: bool,
337 values: &[(&str, f64)],
338 ) -> Self {
339 let mut data = DataFrame::new();
340 let mut mapping = Aes::new();
341 for (col, v) in values {
342 data.add_column(col.to_string(), vec![Value::Float(*v)]);
343 mapping = match *col {
344 "xintercept" => mapping.xintercept(col),
345 "yintercept" => mapping.yintercept(col),
346 "slope" => mapping.slope(col),
347 _ => mapping.intercept(col),
348 };
349 }
350 let plot = if explicit {
351 self.add_geom_with(geom)
352 } else {
353 self.add_geom(geom)
354 };
355 plot.layer_aes(mapping).layer_data(data)
356 }
357
358 pub fn geom_text(self) -> Self {
359 self.add_geom(GeomText::default())
360 }
361
362 pub fn geom_text_with(self, geom: GeomText) -> Self {
363 self.add_geom_with(geom)
364 }
365
366 #[cfg(feature = "ggpubr")]
371 pub fn stat_cor(self) -> Self {
372 self.geom_text().stat(crate::stat::cor::StatCor::default())
373 }
374
375 #[cfg(feature = "ggpubr")]
378 pub fn stat_cor_with(self, stat: crate::stat::cor::StatCor) -> Self {
379 self.geom_text().stat(stat)
380 }
381
382 #[cfg(feature = "ggpubr")]
387 pub fn stat_compare_means(self) -> Self {
388 self.geom_text()
389 .stat(crate::stat::compare_means::StatCompareMeans::default())
390 }
391
392 #[cfg(feature = "ggpubr")]
395 pub fn stat_compare_means_with(
396 self,
397 stat: crate::stat::compare_means::StatCompareMeans,
398 ) -> Self {
399 self.geom_text().stat(stat)
400 }
401
402 #[cfg(feature = "ggpubr")]
408 pub fn stat_compare_means_pairwise(self, comparisons: &[(&str, &str)]) -> Self {
409 self.stat_compare_means_pairwise_with(
410 crate::stat::compare_means::CompareMethod::Auto,
411 comparisons,
412 )
413 }
414
415 #[cfg(feature = "ggpubr")]
418 pub fn stat_compare_means_pairwise_with(
419 self,
420 method: crate::stat::compare_means::CompareMethod,
421 comparisons: &[(&str, &str)],
422 ) -> Self {
423 let col_for = |a: crate::aes::Aesthetic| {
425 self.mapping
426 .mappings
427 .iter()
428 .find(|m| m.aesthetic == a)
429 .map(|m| m.column.clone())
430 };
431 let (xcol, ycol) = match (
432 col_for(crate::aes::Aesthetic::X),
433 col_for(crate::aes::Aesthetic::Y),
434 ) {
435 (Some(x), Some(y)) => (x, y),
436 _ => return self,
437 };
438 let (xc, yc) = match (self.data.column(&xcol), self.data.column(&ycol)) {
439 (Some(x), Some(y)) => (x, y),
440 _ => return self,
441 };
442
443 let mut groups: Vec<(String, Vec<f64>)> = Vec::new();
445 let mut ymax = f64::NEG_INFINITY;
446 let mut ymin = f64::INFINITY;
447 for (xv, yv) in xc.iter().zip(yc.iter()) {
448 let y = match yv.as_f64() {
449 Some(y) if y.is_finite() => y,
450 _ => continue,
451 };
452 ymax = ymax.max(y);
453 ymin = ymin.min(y);
454 let key = xv.to_group_key();
455 if let Some(g) = groups.iter_mut().find(|(k, _)| *k == key) {
456 g.1.push(y);
457 } else {
458 groups.push((key, vec![y]));
459 }
460 }
461 if groups.len() < 2 {
462 return self;
463 }
464 let step = if ymax > ymin {
465 (ymax - ymin) * 0.12
466 } else {
467 1.0
468 };
469
470 let (mut xmin_v, mut xmax_v, mut y_v, mut label_v) =
472 (Vec::new(), Vec::new(), Vec::new(), Vec::new());
473 let mut placed = 0usize;
474 for (a, b) in comparisons {
475 let ga = groups.iter().find(|(k, _)| k == a).map(|g| &g.1);
476 let gb = groups.iter().find(|(k, _)| k == b).map(|g| &g.1);
477 let (ga, gb) = match (ga, gb) {
478 (Some(x), Some(y)) if x.len() >= 2 && y.len() >= 2 => (x, y),
479 _ => continue,
480 };
481 let p = match crate::stat::compare_means::pairwise_p(method, ga, gb) {
482 Some(p) => p,
483 None => continue,
484 };
485 xmin_v.push(Value::Str((*a).to_string()));
486 xmax_v.push(Value::Str((*b).to_string()));
487 y_v.push(Value::Float(ymax + step * (placed as f64 + 1.0)));
488 label_v.push(Value::Str(crate::stat::cor::format_p_value(p)));
489 placed += 1;
490 }
491 if xmin_v.is_empty() {
492 return self;
493 }
494
495 let data = vec![
496 ("xmin".to_string(), xmin_v),
497 ("xmax".to_string(), xmax_v),
498 ("y".to_string(), y_v),
499 ("label".to_string(), label_v),
500 ];
501 self.add_geom(crate::geom::bracket::GeomBracket::default())
502 .layer_data(data)
503 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
504 }
505
506 pub fn geom_bracket(self, xmin: &str, xmax: &str, y: f64, label: &str) -> Self {
511 self.geom_bracket_many(
512 crate::geom::bracket::GeomBracket::default(),
513 &[(xmin, xmax, y, label)],
514 )
515 }
516
517 pub fn geom_bracket_many(
521 self,
522 geom: crate::geom::bracket::GeomBracket,
523 brackets: &[(&str, &str, f64, &str)],
524 ) -> Self {
525 let xmin = brackets
526 .iter()
527 .map(|b| Value::Str(b.0.to_string()))
528 .collect::<Vec<_>>();
529 let xmax = brackets
530 .iter()
531 .map(|b| Value::Str(b.1.to_string()))
532 .collect::<Vec<_>>();
533 let y = brackets
534 .iter()
535 .map(|b| Value::Float(b.2))
536 .collect::<Vec<_>>();
537 let label = brackets
538 .iter()
539 .map(|b| Value::Str(b.3.to_string()))
540 .collect::<Vec<_>>();
541 let data = vec![
542 ("xmin".to_string(), xmin),
543 ("xmax".to_string(), xmax),
544 ("y".to_string(), y),
545 ("label".to_string(), label),
546 ];
547 self.add_geom(geom)
548 .layer_data(data)
549 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label"))
550 }
551
552 pub fn geom_bracket_table(
561 mut self,
562 table: impl GGData,
563 spec: crate::geom::bracket::BracketTable,
564 ) -> Self {
565 let table = table.into_dataframe();
566 let col_for = |a: crate::aes::Aesthetic| {
567 self.mapping
568 .mappings
569 .iter()
570 .find(|m| m.aesthetic == a)
571 .map(|m| m.column.clone())
572 };
573 let x_levels: Vec<String> = col_for(crate::aes::Aesthetic::X)
576 .and_then(|c| self.data.column(&c))
577 .filter(|col| col.iter().any(|v| matches!(v, Value::Str(_))))
578 .map(|col| {
579 let mut seen = std::collections::HashSet::new();
580 col.iter()
581 .filter(|v| !v.is_na())
582 .map(|v| v.to_group_key())
583 .filter(|k| seen.insert(k.clone()))
584 .collect()
585 })
586 .unwrap_or_default();
587 let y_range = col_for(crate::aes::Aesthetic::Y)
588 .and_then(|c| self.data.column(&c))
589 .and_then(|col| {
590 let (lo, hi) = col
591 .iter()
592 .filter_map(|v| v.as_f64())
593 .filter(|v| v.is_finite())
594 .fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), v| {
595 (lo.min(v), hi.max(v))
596 });
597 (lo <= hi).then_some((lo, hi))
598 });
599 let mut warnings = Vec::new();
600 let resolved = spec.resolve(&table, &x_levels, y_range, &mut warnings);
601 self.warnings.extend(warnings);
602 match resolved {
603 Some(data) => self
604 .add_geom(crate::geom::bracket::GeomBracket::from(spec.geom))
605 .layer_data(data)
606 .layer_aes(Aes::new().xmin("xmin").xmax("xmax").y("y").label("label")),
607 None => self,
608 }
609 }
610
611 pub fn geom_text_repel(self) -> Self {
616 self.add_geom(crate::geom::repel::GeomTextRepel::default())
617 }
618
619 pub fn geom_text_repel_with(self, geom: crate::geom::repel::GeomTextRepel) -> Self {
622 self.add_geom_with(geom)
623 }
624
625 pub fn geom_label_repel(self) -> Self {
628 self.add_geom(crate::geom::repel::GeomLabelRepel::default())
629 }
630
631 pub fn geom_label_repel_with(self, geom: crate::geom::repel::GeomLabelRepel) -> Self {
633 self.add_geom_with(geom)
634 }
635
636 pub fn geom_label(self) -> Self {
637 self.add_geom(GeomLabel::default())
638 }
639
640 pub fn geom_label_with(self, geom: GeomLabel) -> Self {
641 self.add_geom_with(geom)
642 }
643
644 pub fn geom_area(self) -> Self {
645 self.add_geom(GeomArea::default())
646 }
647
648 pub fn geom_area_with(self, geom: GeomArea) -> Self {
649 self.add_geom_with(geom)
650 }
651
652 pub fn geom_ribbon(self) -> Self {
653 self.add_geom(GeomRibbon::default())
654 }
655
656 pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self {
657 self.add_geom_with(geom)
658 }
659
660 pub fn geom_errorbar(self) -> Self {
661 self.add_geom(GeomErrorbar::default())
662 }
663
664 pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self {
665 self.add_geom_with(geom)
666 }
667
668 pub fn geom_segment(self) -> Self {
669 self.add_geom(GeomSegment::default())
670 }
671
672 pub fn geom_segment_with(self, geom: GeomSegment) -> Self {
673 self.add_geom_with(geom)
674 }
675
676 pub fn geom_density(self) -> Self {
677 self.add_geom(GeomDensity::default())
678 }
679
680 pub fn geom_density_with(self, geom: GeomDensity) -> Self {
681 self.add_geom_with(geom)
682 }
683
684 pub fn geom_rug(self) -> Self {
685 self.add_geom(GeomRug::default())
686 }
687
688 pub fn geom_rug_with(self, geom: GeomRug) -> Self {
689 self.add_geom_with(geom)
690 }
691
692 pub fn geom_jitter(self) -> Self {
693 self.add_geom(GeomJitter::default())
694 }
695
696 pub fn geom_jitter_with(self, geom: GeomJitter) -> Self {
697 self.add_geom_with(geom)
698 }
699
700 pub fn geom_path(self) -> Self {
701 self.add_geom(GeomPath::default())
702 }
703
704 pub fn geom_path_with(self, geom: GeomPath) -> Self {
705 self.add_geom_with(geom)
706 }
707
708 pub fn stat_ellipse(self) -> Self {
710 self.geom_path()
711 .stat(crate::stat::ellipse::StatEllipse::default())
712 }
713
714 pub fn stat_ellipse_level(self, level: f64) -> Self {
716 self.geom_path()
717 .stat(crate::stat::ellipse::StatEllipse::new(level))
718 }
719
720 #[cfg(feature = "regression")]
723 pub fn stat_quantile(mut self, taus: &[f64]) -> Self {
724 for &tau in taus {
725 self = self
726 .geom_path()
727 .stat(crate::stat::quantile::StatQuantile::new(tau));
728 }
729 self
730 }
731
732 #[cfg(feature = "regression")]
734 pub fn geom_quantile(self) -> Self {
735 self.stat_quantile(&[0.25, 0.5, 0.75])
736 }
737
738 pub fn geom_step(self) -> Self {
739 self.add_geom(GeomStep::default())
740 }
741
742 pub fn geom_step_with(self, geom: GeomStep) -> Self {
743 self.add_geom_with(geom)
744 }
745
746 pub fn geom_freqpoly(self) -> Self {
747 self.add_geom(GeomFreqpoly::default())
748 }
749
750 pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self {
751 self.add_geom_with(geom)
752 }
753
754 pub fn geom_linerange(self) -> Self {
755 self.add_geom(GeomLinerange::default())
756 }
757
758 pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self {
759 self.add_geom_with(geom)
760 }
761
762 pub fn geom_pointrange(self) -> Self {
763 self.add_geom(GeomPointrange::default())
764 }
765
766 pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self {
767 self.add_geom_with(geom)
768 }
769
770 pub fn geom_crossbar(self) -> Self {
771 self.add_geom(GeomCrossbar::default())
772 }
773
774 pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self {
775 self.add_geom_with(geom)
776 }
777
778 pub fn geom_spoke(self) -> Self {
779 self.add_geom(GeomSpoke::default())
780 }
781
782 pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self {
783 self.add_geom_with(geom)
784 }
785
786 pub fn geom_rect(self) -> Self {
787 self.add_geom(GeomRect::default())
788 }
789
790 pub fn geom_rect_with(self, geom: GeomRect) -> Self {
791 self.add_geom_with(geom)
792 }
793
794 pub fn geom_tile(self) -> Self {
795 self.add_geom(GeomTile::default())
796 }
797
798 pub fn geom_tile_with(self, geom: GeomTile) -> Self {
799 self.add_geom_with(geom)
800 }
801
802 pub fn geom_raster(self) -> Self {
804 self.add_geom(crate::geom::raster::GeomRaster::default())
805 }
806
807 pub fn geom_raster_with(self, geom: crate::geom::raster::GeomRaster) -> Self {
808 self.add_geom_with(geom)
809 }
810
811 pub fn geom_polygon(self) -> Self {
812 self.add_geom(GeomPolygon::default())
813 }
814
815 pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self {
816 self.add_geom_with(geom)
817 }
818
819 #[cfg(feature = "sf")]
821 pub fn geom_sf(self) -> Self {
822 self.add_geom(crate::geom::sf::GeomSf::default())
823 }
824
825 #[cfg(feature = "sf")]
826 pub fn geom_sf_with(self, geom: crate::geom::sf::GeomSf) -> Self {
827 self.add_geom_with(geom)
828 }
829
830 #[cfg(feature = "sf")]
833 pub fn geom_sf_text(self) -> Self {
834 self.add_geom(crate::geom::sf_text::GeomSfText::default())
835 }
836
837 #[cfg(feature = "sf")]
838 pub fn geom_sf_text_with(self, geom: crate::geom::sf_text::GeomSfText) -> Self {
839 self.add_geom_with(geom)
840 }
841
842 #[cfg(feature = "sf")]
845 pub fn geom_sf_label(self) -> Self {
846 self.add_geom(crate::geom::sf_text::GeomSfLabel::default())
847 }
848
849 #[cfg(feature = "sf")]
850 pub fn geom_sf_label_with(self, geom: crate::geom::sf_text::GeomSfLabel) -> Self {
851 self.add_geom_with(geom)
852 }
853
854 #[cfg(feature = "sf")]
858 pub fn annotation_scale(self) -> Self {
859 self.add_geom(crate::geom::annotation_scale::GeomAnnotationScale::default())
860 }
861
862 #[cfg(feature = "sf")]
863 pub fn annotation_scale_with(
864 self,
865 geom: crate::geom::annotation_scale::GeomAnnotationScale,
866 ) -> Self {
867 self.add_geom_with(geom)
868 }
869
870 #[cfg(feature = "sf")]
873 pub fn annotation_attribution(self, text: impl Into<String>) -> Self {
874 self.add_geom(crate::geom::annotation_scale::GeomAttribution::new(text))
875 }
876
877 #[cfg(feature = "sf")]
878 pub fn annotation_attribution_with(
879 self,
880 geom: crate::geom::annotation_scale::GeomAttribution,
881 ) -> Self {
882 self.add_geom_with(geom)
883 }
884
885 #[cfg(feature = "sf")]
893 pub fn bivariate_legend(
894 self,
895 geom: crate::geom::bivariate_legend::GeomBivariateLegend,
896 ) -> Self {
897 self.add_geom(geom)
898 }
899
900 pub fn geom_curve(self) -> Self {
901 self.add_geom(GeomCurve::default())
902 }
903
904 pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
905 self.add_geom_with(geom)
906 }
907
908 pub fn geom_violin(self) -> Self {
909 self.add_geom(GeomViolin::default())
910 }
911
912 pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
913 self.add_geom_with(geom)
914 }
915
916 pub fn geom_dotplot(self) -> Self {
917 self.add_geom(GeomDotplot::default())
918 }
919
920 pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
921 self.add_geom_with(geom)
922 }
923
924 pub fn geom_qq(self) -> Self {
925 self.add_geom(GeomQQ::default())
926 }
927
928 pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
929 self.add_geom_with(geom)
930 }
931
932 pub fn geom_qq_line(self) -> Self {
933 self.add_geom(GeomQQLine::default())
934 }
935
936 pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
937 self.add_geom_with(geom)
938 }
939
940 pub fn geom_bin2d(self) -> Self {
941 self.add_geom(GeomBin2d::default())
942 }
943
944 pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
945 self.add_geom_with(geom)
946 }
947
948 pub fn geom_hex(self) -> Self {
949 self.add_geom(GeomHex::default())
950 }
951
952 pub fn geom_hex_with(self, geom: GeomHex) -> Self {
953 self.add_geom_with(geom)
954 }
955
956 pub fn geom_count(self) -> Self {
957 self.add_geom(GeomCount::default())
958 }
959
960 pub fn geom_count_with(self, geom: GeomCount) -> Self {
961 self.add_geom_with(geom)
962 }
963
964 pub fn geom_contour(self) -> Self {
965 self.add_geom(GeomContour::default())
966 }
967
968 pub fn geom_contour_with(self, geom: GeomContour) -> Self {
969 self.add_geom_with(geom)
970 }
971
972 pub fn geom_contour_filled(self) -> Self {
975 self.add_geom(GeomPolygon {
976 line_width: 0.0,
977 alpha: 1.0,
978 ..GeomPolygon::default()
979 })
980 .stat(crate::stat::contour_filled::StatContourFilled::default())
981 }
982
983 pub fn geom_density2d(self) -> Self {
984 self.add_geom(GeomDensity2d::default())
985 }
986
987 pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
988 self.add_geom_with(geom)
989 }
990
991 pub fn geom_candlestick(self) -> Self {
994 self.add_geom(crate::geom::candlestick::GeomCandlestick::default())
995 }
996
997 pub fn geom_candlestick_with(self, geom: crate::geom::candlestick::GeomCandlestick) -> Self {
998 self.add_geom_with(geom)
999 }
1000
1001 pub fn geom_ohlc(self) -> Self {
1004 self.add_geom(crate::geom::candlestick::GeomOhlc::default())
1005 }
1006
1007 pub fn geom_ohlc_with(self, geom: crate::geom::candlestick::GeomOhlc) -> Self {
1008 self.add_geom_with(geom)
1009 }
1010
1011 pub fn geom_calendar(self) -> Self {
1022 self.geom_calendar_with(
1023 GeomTile {
1024 color: (255, 255, 255),
1025 line_width: 1.5,
1026 ..Default::default()
1027 },
1028 false,
1029 )
1030 }
1031
1032 pub fn geom_calendar_with(mut self, tile: GeomTile, monday_first: bool) -> Self {
1035 use crate::stat::calendar::{
1036 day_number, month_boundaries, month_breaks, month_breaks_localized, weekday_labels,
1037 CalendarGrid, StatCalendar, MAX_CALENDAR_YEARS,
1038 };
1039 let grid = self
1040 .mapping
1041 .get_mapping(&crate::aes::Aesthetic::X)
1042 .and_then(|c| self.data.column(c))
1043 .and_then(|col| {
1044 CalendarGrid::from_days(col.iter().filter_map(day_number), monday_first)
1045 });
1046 let Some((grid, clipped)) = grid else {
1047 return self.add_geom_with(tile).stat(StatCalendar {
1049 grid: None,
1050 monday_first,
1051 });
1052 };
1053 if clipped {
1054 self.warnings.push(format!(
1055 "geom_calendar: dates more than {MAX_CALENDAR_YEARS} years before the newest were dropped"
1056 ));
1057 }
1058 let (xb, _) = month_breaks(&grid);
1059 let yb = if monday_first {
1061 vec![6.0, 4.0, 2.0]
1062 } else {
1063 vec![5.0, 3.0, 1.0]
1064 };
1065 let segs = month_boundaries(&grid);
1066 let col = |f: fn(&(f64, f64, f64, f64)) -> f64| -> Vec<Value> {
1067 segs.iter().map(|s| Value::Float(f(s))).collect()
1068 };
1069 let boundaries = vec![
1070 ("x".to_string(), col(|s| s.0)),
1071 ("y".to_string(), col(|s| s.1)),
1072 ("xend".to_string(), col(|s| s.2)),
1073 ("yend".to_string(), col(|s| s.3)),
1074 ];
1075 self.default_aspect_ratio = Some(7.0 / grid.n_weeks().max(1) as f64);
1076 let plot = self
1077 .add_geom_with(tile)
1078 .stat(StatCalendar {
1079 grid: Some(grid),
1080 monday_first,
1081 })
1082 .scale_x_continuous(
1083 ScaleContinuous::new()
1084 .with_breaks(xb)
1085 .with_localized_labels(move |l| month_breaks_localized(&grid, l).1)
1087 .with_expand(0.0, 0.0),
1088 )
1089 .scale_y_continuous(
1090 ScaleContinuous::new()
1091 .with_breaks(yb)
1092 .with_localized_labels(weekday_labels)
1093 .with_expand(0.0, 0.0),
1094 );
1095 if segs.is_empty() {
1096 return plot;
1097 }
1098 plot.add_geom_with(crate::geom::segment::GeomCalendarBorder)
1100 .layer_data(boundaries)
1101 .layer_aes(Aes::new().x("x").y("y").xend("xend").yend("yend"))
1102 .show_legend(false)
1103 }
1104
1105 pub fn geom_blank(self) -> Self {
1106 self.add_geom(GeomBlank)
1107 }
1108
1109 fn add_geom_with(self, geom: impl Geom + 'static) -> Self {
1112 let mut plot = self.add_geom(geom);
1113 if let Some(layer) = plot.layers.last_mut() {
1114 layer.explicit_style = true;
1115 }
1116 plot
1117 }
1118
1119 fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
1120 let stat = geom.default_stat();
1121 let position = geom.default_position();
1122 let params = geom.default_params();
1123 self.layers.push(Layer {
1124 data: None,
1125 mapping: Aes::default(),
1126 geom: Box::new(geom),
1127 stat,
1128 position,
1129 params,
1130 show_legend: None,
1131 explicit_style: false,
1132 tooltip_template: None,
1133 });
1134 self
1135 }
1136
1137 pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
1141 if let Some(layer) = self.layers.last_mut() {
1142 layer.stat = Box::new(stat);
1143 }
1144 self
1145 }
1146
1147 pub fn position(mut self, pos: impl Position + 'static) -> Self {
1149 if let Some(layer) = self.layers.last_mut() {
1150 layer.position = Box::new(pos);
1151 }
1152 self
1153 }
1154
1155 pub fn layer_data(mut self, data: impl GGData) -> Self {
1157 if let Some(layer) = self.layers.last_mut() {
1158 layer.data = Some(data.into_dataframe());
1159 }
1160 self
1161 }
1162
1163 pub fn layer_aes(mut self, mapping: Aes) -> Self {
1165 self.warnings.extend(mapping.warnings.iter().cloned());
1166 if let Some(layer) = self.layers.last_mut() {
1167 layer.mapping = mapping;
1168 }
1169 self
1170 }
1171
1172 pub fn tooltip(mut self, template: &str) -> Self {
1183 if let Some(layer) = self.layers.last_mut() {
1184 layer.tooltip_template = Some(template.to_string());
1185 }
1186 self
1187 }
1188
1189 pub fn show_legend(mut self, show: bool) -> Self {
1192 if let Some(layer) = self.layers.last_mut() {
1193 layer.show_legend = Some(show);
1194 }
1195 self
1196 }
1197
1198 pub fn decorative(mut self) -> Self {
1225 if let Some(layer) = self.layers.last_mut() {
1226 let geom = std::mem::replace(&mut layer.geom, Box::new(GeomBlank));
1227 layer.geom = Box::new(crate::geom::decorative::Decorative(geom));
1228 if layer.show_legend.is_none() {
1229 layer.show_legend = Some(false);
1230 }
1231 }
1232 self
1233 }
1234
1235 pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
1238 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1239 self.scales.push(Box::new(s));
1240 self
1241 }
1242
1243 pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
1244 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1245 self.scales.push(Box::new(s));
1246 self
1247 }
1248
1249 pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1250 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1251 self.scales.push(Box::new(s));
1252 self
1253 }
1254
1255 pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
1256 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1257 self.scales.push(Box::new(s));
1258 self
1259 }
1260
1261 pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
1262 self.scales.push(Box::new(s));
1263 self
1264 }
1265
1266 pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
1267 self.scales.push(Box::new(s));
1268 self
1269 }
1270
1271 pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1272 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
1273 self.scale_color(s)
1274 }
1275
1276 pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
1277 let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
1278 self.scale_fill(s)
1279 }
1280
1281 pub fn scale_colour_identity(self) -> Self {
1284 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Color);
1285 self.scale_color(s)
1286 }
1287
1288 pub fn scale_color_identity(self) -> Self {
1290 self.scale_colour_identity()
1291 }
1292
1293 pub fn scale_fill_identity(self) -> Self {
1296 let s = crate::scale::identity::ScaleColorIdentity::new(crate::aes::Aesthetic::Fill);
1297 self.scale_fill(s)
1298 }
1299
1300 pub fn scale_color_viridis(self) -> Self {
1301 use crate::scale::color::ScaleColorDiscrete;
1302 use crate::scale::palettes::PaletteName;
1303 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
1304 .with_named_palette(&PaletteName::Viridis);
1305 self.scale_color(s)
1306 }
1307
1308 pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1309 use crate::scale::color::ScaleColorDiscrete;
1310 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
1311 self.scale_color(s)
1312 }
1313
1314 pub fn scale_color_gradient(
1315 self,
1316 low: crate::scale::color::RGBAColor,
1317 high: crate::scale::color::RGBAColor,
1318 ) -> Self {
1319 use crate::scale::color::ScaleColorContinuous;
1320 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
1321 self.scale_color(s)
1322 }
1323
1324 pub fn scale_fill_gradient(
1325 self,
1326 low: crate::scale::color::RGBAColor,
1327 high: crate::scale::color::RGBAColor,
1328 ) -> Self {
1329 use crate::scale::color::ScaleColorContinuous;
1330 let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
1331 self.scale_fill(s)
1332 }
1333
1334 pub fn scale_color_gradient2(
1335 self,
1336 low: crate::scale::color::RGBAColor,
1337 mid: crate::scale::color::RGBAColor,
1338 high: crate::scale::color::RGBAColor,
1339 ) -> Self {
1340 use crate::scale::gradient::ScaleColorGradient2;
1341 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
1342 self.scale_color(s)
1343 }
1344
1345 pub fn scale_fill_gradient2(
1346 self,
1347 low: crate::scale::color::RGBAColor,
1348 mid: crate::scale::color::RGBAColor,
1349 high: crate::scale::color::RGBAColor,
1350 ) -> Self {
1351 use crate::scale::gradient::ScaleColorGradient2;
1352 let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
1353 self.scale_fill(s)
1354 }
1355
1356 pub fn scale_fill_viridis(self) -> Self {
1357 use crate::scale::color::ScaleColorDiscrete;
1358 use crate::scale::palettes::PaletteName;
1359 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
1360 .with_named_palette(&PaletteName::Viridis);
1361 self.scale_fill(s)
1362 }
1363
1364 pub fn scale_color_viridis_c(self) -> Self {
1366 use crate::scale::gradient_n::ScaleColorGradientN;
1367 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
1368 self.scale_color(s)
1369 }
1370
1371 pub fn scale_fill_viridis_c(self) -> Self {
1373 use crate::scale::gradient_n::ScaleColorGradientN;
1374 let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
1375 self.scale_fill(s)
1376 }
1377
1378 pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1380 use crate::scale::gradient_n::ScaleColorGradientN;
1381 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
1382 self.scale_color(s)
1383 }
1384
1385 pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
1387 use crate::scale::gradient_n::ScaleColorGradientN;
1388 let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
1389 self.scale_fill(s)
1390 }
1391
1392 pub fn scale_color_steps(
1395 self,
1396 low: crate::scale::color::RGBAColor,
1397 high: crate::scale::color::RGBAColor,
1398 n_bins: usize,
1399 ) -> Self {
1400 let s = crate::scale::steps::ScaleColorSteps::two(
1401 crate::aes::Aesthetic::Color,
1402 (low.r, low.g, low.b),
1403 (high.r, high.g, high.b),
1404 n_bins,
1405 );
1406 self.scale_color(s)
1407 }
1408
1409 pub fn scale_color_stepsn(
1411 self,
1412 stops: Vec<crate::scale::color::RGBAColor>,
1413 n_bins: usize,
1414 ) -> Self {
1415 let s = crate::scale::steps::ScaleColorSteps::new(
1416 crate::aes::Aesthetic::Color,
1417 stops.iter().map(|c| (c.r, c.g, c.b)).collect(),
1418 n_bins,
1419 );
1420 self.scale_color(s)
1421 }
1422
1423 pub fn scale_color_fermenter(
1425 self,
1426 name: crate::scale::palettes::PaletteName,
1427 n_bins: usize,
1428 ) -> Self {
1429 let stops = crate::scale::palettes::palette(&name)
1430 .iter()
1431 .map(|c| (c.r, c.g, c.b))
1432 .collect();
1433 let s =
1434 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Color, stops, n_bins);
1435 self.scale_color(s)
1436 }
1437
1438 pub fn scale_fill_steps(
1440 self,
1441 low: crate::scale::color::RGBAColor,
1442 high: crate::scale::color::RGBAColor,
1443 n_bins: usize,
1444 ) -> Self {
1445 let s = crate::scale::steps::ScaleColorSteps::two(
1446 crate::aes::Aesthetic::Fill,
1447 (low.r, low.g, low.b),
1448 (high.r, high.g, high.b),
1449 n_bins,
1450 );
1451 self.scale_fill(s)
1452 }
1453
1454 pub fn scale_fill_binned(
1465 self,
1466 breaks: Vec<f64>,
1467 low: crate::scale::color::RGBAColor,
1468 high: crate::scale::color::RGBAColor,
1469 ) -> Self {
1470 let s = crate::scale::binned::ScaleFillBinned::two(
1471 crate::aes::Aesthetic::Fill,
1472 breaks,
1473 (low.r, low.g, low.b),
1474 (high.r, high.g, high.b),
1475 );
1476 self.scale_fill(s)
1477 }
1478
1479 pub fn scale_fill_binned_with(
1483 self,
1484 breaks: Vec<f64>,
1485 colors: Vec<crate::scale::color::RGBAColor>,
1486 ) -> Self {
1487 let s = crate::scale::binned::ScaleFillBinned::new(
1488 crate::aes::Aesthetic::Fill,
1489 breaks,
1490 colors.iter().map(|c| (c.r, c.g, c.b)).collect(),
1491 );
1492 self.scale_fill(s)
1493 }
1494
1495 pub fn scale_fill_fermenter(
1497 self,
1498 name: crate::scale::palettes::PaletteName,
1499 n_bins: usize,
1500 ) -> Self {
1501 let stops = crate::scale::palettes::palette(&name)
1502 .iter()
1503 .map(|c| (c.r, c.g, c.b))
1504 .collect();
1505 let s =
1506 crate::scale::steps::ScaleColorSteps::new(crate::aes::Aesthetic::Fill, stops, n_bins);
1507 self.scale_fill(s)
1508 }
1509
1510 pub fn scale_fill_discrete_sorted(self) -> Self {
1515 use crate::scale::color::ScaleColorDiscrete;
1516 self.scale_fill(ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).sorted())
1517 }
1518
1519 pub fn scale_color_discrete_sorted(self) -> Self {
1522 use crate::scale::color::ScaleColorDiscrete;
1523 self.scale_color(ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).sorted())
1524 }
1525
1526 pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
1527 use crate::scale::color::ScaleColorDiscrete;
1528 let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
1529 self.scale_fill(s)
1530 }
1531
1532 pub fn scale_linetype_manual(
1533 self,
1534 values: Vec<(&str, crate::render::backend::Linetype)>,
1535 ) -> Self {
1536 let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
1537 self.scale_color(s)
1538 }
1539
1540 pub fn scale_shape_manual(
1541 self,
1542 values: Vec<(&str, crate::render::backend::PointShape)>,
1543 ) -> Self {
1544 let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
1545 self.scale_color(s)
1546 }
1547
1548 pub fn scale_color_grey(self) -> Self {
1549 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
1550 self.scale_color(s)
1551 }
1552
1553 pub fn scale_fill_grey(self) -> Self {
1554 let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
1555 self.scale_fill(s)
1556 }
1557
1558 pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1559 self.scale_color(s)
1560 }
1561
1562 pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
1563 self.scale_fill(s)
1564 }
1565
1566 pub fn scale_x_reverse(self) -> Self {
1567 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1568 }
1569
1570 pub fn scale_y_reverse(self) -> Self {
1571 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
1572 }
1573
1574 pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1575 let s = s.for_aesthetic(crate::aes::Aesthetic::X);
1576 self.scales.push(Box::new(s));
1577 self
1578 }
1579
1580 pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
1581 let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
1582 self.scales.push(Box::new(s));
1583 self
1584 }
1585
1586 pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
1587 self.scales.push(Box::new(s));
1588 self
1589 }
1590
1591 pub fn scale_size_area(mut self, max_size: f64) -> Self {
1595 self.scales.push(Box::new(
1596 crate::scale::size::ScaleSizeContinuous::new().area(max_size),
1597 ));
1598 self
1599 }
1600
1601 pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
1602 self.scales.push(Box::new(s));
1603 self
1604 }
1605
1606 pub fn scale_linewidth(mut self, s: crate::scale::linewidth::ScaleLinewidthContinuous) -> Self {
1610 self.scales.push(Box::new(s));
1611 self
1612 }
1613
1614 pub fn xlim(self, min: f64, max: f64) -> Self {
1615 self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
1616 }
1617
1618 pub fn ylim(self, min: f64, max: f64) -> Self {
1619 self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
1620 }
1621
1622 pub fn scale_x_log10(self) -> Self {
1623 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1624 }
1625
1626 pub fn scale_y_log10(self) -> Self {
1627 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
1628 }
1629
1630 pub fn scale_x_sqrt(self) -> Self {
1631 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1632 }
1633
1634 pub fn scale_y_sqrt(self) -> Self {
1635 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
1636 }
1637
1638 pub fn scale_x_log2(self) -> Self {
1639 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1640 }
1641
1642 pub fn scale_y_log2(self) -> Self {
1643 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
1644 }
1645
1646 pub fn scale_x_ln(self) -> Self {
1647 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1648 }
1649
1650 pub fn scale_y_ln(self) -> Self {
1651 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
1652 }
1653
1654 pub fn scale_x_logit(self) -> Self {
1656 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1657 }
1658
1659 pub fn scale_y_logit(self) -> Self {
1660 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Logit))
1661 }
1662
1663 pub fn scale_x_probit(self) -> Self {
1665 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1666 }
1667
1668 pub fn scale_y_probit(self) -> Self {
1669 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Probit))
1670 }
1671
1672 pub fn scale_x_pseudo_log(self) -> Self {
1674 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1675 }
1676
1677 pub fn scale_y_pseudo_log(self) -> Self {
1678 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::PseudoLog))
1679 }
1680
1681 pub fn scale_x_reciprocal(self) -> Self {
1683 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1684 }
1685
1686 pub fn scale_y_reciprocal(self) -> Self {
1687 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reciprocal))
1688 }
1689
1690 pub fn scale_x_exp(self) -> Self {
1692 self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1693 }
1694
1695 pub fn scale_y_exp(self) -> Self {
1696 self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Exp))
1697 }
1698
1699 pub fn scale_x_boxcox(self, lambda: f64) -> Self {
1701 self.scale_x_continuous(
1702 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1703 )
1704 }
1705
1706 pub fn scale_y_boxcox(self, lambda: f64) -> Self {
1707 self.scale_y_continuous(
1708 ScaleContinuous::new().with_transform(ScaleTransform::BoxCox(lambda)),
1709 )
1710 }
1711
1712 pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
1715 self.facet = Facet::Wrap {
1716 var: var.to_string(),
1717 ncol,
1718 scales: FacetScales::Fixed,
1719 labeller: FacetLabeller::default(),
1720 };
1721 self
1722 }
1723
1724 pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
1725 self.facet = Facet::Wrap {
1726 var: var.to_string(),
1727 ncol,
1728 scales,
1729 labeller: FacetLabeller::default(),
1730 };
1731 self
1732 }
1733
1734 pub fn facet_wrap_labeller(
1735 mut self,
1736 var: &str,
1737 ncol: Option<usize>,
1738 labeller: FacetLabeller,
1739 ) -> Self {
1740 self.facet = Facet::Wrap {
1741 var: var.to_string(),
1742 ncol,
1743 scales: FacetScales::Fixed,
1744 labeller,
1745 };
1746 self
1747 }
1748
1749 pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
1750 self.facet = Facet::Grid {
1751 row_var: row.map(String::from),
1752 col_var: col.map(String::from),
1753 scales: FacetScales::Fixed,
1754 labeller: FacetLabeller::default(),
1755 space: FacetSpace::Fixed,
1756 };
1757 self
1758 }
1759
1760 pub fn facet_grid_free(
1761 mut self,
1762 row: Option<&str>,
1763 col: Option<&str>,
1764 scales: FacetScales,
1765 ) -> Self {
1766 self.facet = Facet::Grid {
1767 row_var: row.map(String::from),
1768 col_var: col.map(String::from),
1769 scales,
1770 labeller: FacetLabeller::default(),
1771 space: FacetSpace::Fixed,
1772 };
1773 self
1774 }
1775
1776 pub fn facet_grid_multi(
1780 mut self,
1781 row: Option<&str>,
1782 cols: &[&str],
1783 scales: FacetScales,
1784 space: FacetSpace,
1785 ) -> Self {
1786 let col_var = if cols.len() > 1 {
1787 let n = self.data.nrows();
1789 let mut combined = Vec::with_capacity(n);
1790 for i in 0..n {
1791 let parts: Vec<String> = cols
1792 .iter()
1793 .map(|c| {
1794 self.data
1795 .column(c)
1796 .and_then(|col| col.get(i))
1797 .map(|v| v.to_group_key())
1798 .unwrap_or_default()
1799 })
1800 .collect();
1801 combined.push(crate::data::Value::Str(parts.join(" . ")));
1802 }
1803 let name = "__facet_cols__".to_string();
1804 self.data.add_column(name.clone(), combined);
1805 Some(name)
1806 } else {
1807 cols.first().map(|s| s.to_string())
1808 };
1809 self.facet = Facet::Grid {
1810 row_var: row.map(String::from),
1811 col_var,
1812 scales,
1813 labeller: FacetLabeller::default(),
1814 space,
1815 };
1816 self
1817 }
1818
1819 pub fn facet_grid_space(
1822 mut self,
1823 row: Option<&str>,
1824 col: Option<&str>,
1825 scales: FacetScales,
1826 space: FacetSpace,
1827 ) -> Self {
1828 self.facet = Facet::Grid {
1829 row_var: row.map(String::from),
1830 col_var: col.map(String::from),
1831 scales,
1832 labeller: FacetLabeller::default(),
1833 space,
1834 };
1835 self
1836 }
1837
1838 pub fn facet_grid_labeller(
1839 mut self,
1840 row: Option<&str>,
1841 col: Option<&str>,
1842 labeller: FacetLabeller,
1843 ) -> Self {
1844 self.facet = Facet::Grid {
1845 row_var: row.map(String::from),
1846 col_var: col.map(String::from),
1847 scales: FacetScales::Fixed,
1848 labeller,
1849 space: FacetSpace::Fixed,
1850 };
1851 self
1852 }
1853
1854 pub fn coord_flip(mut self) -> Self {
1857 self.coord = Box::new(CoordFlip);
1858 self
1859 }
1860
1861 pub fn coord_fixed(mut self, ratio: f64) -> Self {
1862 self.coord = Box::new(CoordFixed::new(ratio));
1863 self
1864 }
1865
1866 #[cfg(feature = "sf")]
1870 pub fn coord_sf(mut self) -> Self {
1871 self.coord = Box::new(crate::coord::sf::CoordSf::new());
1872 self
1873 }
1874
1875 #[cfg(feature = "sf")]
1880 pub fn coord_sf_with(mut self, coord: crate::coord::sf::CoordSf) -> Self {
1881 self.coord = Box::new(coord);
1882 self
1883 }
1884
1885 pub fn svg_root_data(mut self, on: bool) -> Self {
1892 self.svg_root_data = on;
1893 self
1894 }
1895
1896 pub fn svg_attr(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1906 self.root_attrs.push((name.into(), value.into()));
1907 self
1908 }
1909
1910 pub fn svg_attrs(mut self, attrs: impl IntoIterator<Item = (String, String)>) -> Self {
1912 self.root_attrs.extend(attrs);
1913 self
1914 }
1915
1916 pub fn svg_warnings_attr(mut self, on: bool) -> Self {
1921 self.svg_warnings_attr = on;
1922 self
1923 }
1924
1925 pub fn svg_formatted_values(mut self, on: bool) -> Self {
1934 self.svg_formatted_values = on;
1935 self
1936 }
1937
1938 pub fn coord_trans(mut self, x: Option<ScaleTransform>, y: Option<ScaleTransform>) -> Self {
1943 self.coord = Box::new(crate::coord::trans::CoordTrans::new(x, y));
1944 self
1945 }
1946
1947 pub fn coord_trans_y(self, y: ScaleTransform) -> Self {
1949 self.coord_trans(None, Some(y))
1950 }
1951
1952 pub fn coord_trans_x(self, x: ScaleTransform) -> Self {
1954 self.coord_trans(Some(x), None)
1955 }
1956
1957 pub fn coord_cartesian_zoom(
1959 mut self,
1960 xlim: Option<(f64, f64)>,
1961 ylim: Option<(f64, f64)>,
1962 ) -> Self {
1963 let mut c = CoordCartesian::new();
1964 if let Some((min, max)) = xlim {
1965 c = c.xlim(min, max);
1966 }
1967 if let Some((min, max)) = ylim {
1968 c = c.ylim(min, max);
1969 }
1970 self.coord = Box::new(c);
1971 self
1972 }
1973
1974 pub fn coord_polar(mut self) -> Self {
1975 self.coord = Box::new(CoordPolar::new());
1976 self
1977 }
1978
1979 pub fn coord_radar(mut self) -> Self {
1983 self.coord = Box::new(crate::coord::radar::CoordRadar::new());
1984 self
1985 }
1986
1987 pub fn coord_radar_with(mut self, coord: crate::coord::radar::CoordRadar) -> Self {
1988 self.coord = Box::new(coord);
1989 self
1990 }
1991
1992 pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
1993 self.coord = Box::new(coord);
1994 self
1995 }
1996
1997 pub fn theme(mut self, theme: Theme) -> Self {
2002 self.apply_preset(theme);
2003 self
2004 }
2005
2006 fn apply_preset(&mut self, theme: Theme) {
2007 let primary = self.theme.primary;
2008 self.theme = theme;
2009 if self.theme.primary.is_none() {
2010 self.theme.primary = primary;
2011 }
2012 }
2013
2014 pub fn axis_text_x_angle(mut self, degrees: f64) -> Self {
2018 self.theme.axis_text_x.angle = degrees;
2019 self
2020 }
2021
2022 pub fn axis_text_y_angle(mut self, degrees: f64) -> Self {
2024 self.theme.axis_text_y.angle = degrees;
2025 self
2026 }
2027
2028 pub fn axis_text_x_dodge(mut self, n: usize) -> Self {
2031 self.theme.axis_text_x_dodge = n.max(1);
2032 self
2033 }
2034
2035 pub fn aspect_ratio(mut self, ratio: f64) -> Self {
2038 self.theme.aspect_ratio = Some(ratio);
2039 self
2040 }
2041
2042 pub fn panel_ontop(mut self) -> Self {
2044 self.theme.panel_ontop = true;
2045 self
2046 }
2047
2048 pub fn axis_minor_ticks(mut self) -> Self {
2050 self.theme.axis_minor_ticks = true;
2051 self
2052 }
2053
2054 pub fn axis_text_x_fit(mut self, fit: crate::theme::AxisLabelFit) -> Self {
2059 self.theme.axis_text_x_fit = fit;
2060 self
2061 }
2062
2063 pub fn axis_text_x_check_overlap(mut self, on: bool) -> Self {
2066 self.theme.axis_text_x_check_overlap = on;
2067 self
2068 }
2069
2070 pub fn title_position(mut self, pos: crate::theme::TitlePosition) -> Self {
2073 self.theme.title_position = pos;
2074 self
2075 }
2076
2077 pub fn tag_position(mut self, pos: crate::theme::TagPosition) -> Self {
2079 self.theme.tag_position = pos;
2080 self
2081 }
2082
2083 pub fn legend_direction(mut self, dir: crate::theme::LegendDirection) -> Self {
2085 self.theme.legend_direction = Some(dir);
2086 self
2087 }
2088
2089 pub fn legend_position_inside(mut self, x: f64, y: f64) -> Self {
2096 self.theme.legend_position = crate::theme::LegendPosition::Inside(x, y);
2097 self
2098 }
2099
2100 pub fn legend_position(mut self, pos: crate::theme::LegendPosition) -> Self {
2102 self.theme.legend_position = pos;
2103 self
2104 }
2105
2106 pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
2107 self.theme.primary = Some(color);
2108 self
2109 }
2110
2111 pub fn theme_minimal(mut self) -> Self {
2112 self.apply_preset(crate::theme::presets::theme_minimal());
2113 self
2114 }
2115
2116 pub fn theme_bw(mut self) -> Self {
2117 self.apply_preset(crate::theme::presets::theme_bw());
2118 self
2119 }
2120
2121 pub fn theme_gray(mut self) -> Self {
2122 self.apply_preset(crate::theme::presets::theme_gray());
2123 self
2124 }
2125
2126 pub fn theme_classic(mut self) -> Self {
2127 self.apply_preset(crate::theme::presets::theme_classic());
2128 self
2129 }
2130
2131 pub fn theme_pubr(mut self) -> Self {
2133 self.apply_preset(crate::theme::presets::theme_pubr());
2134 self
2135 }
2136
2137 pub fn theme_linedraw(mut self) -> Self {
2138 self.apply_preset(crate::theme::presets::theme_linedraw());
2139 self
2140 }
2141
2142 pub fn theme_light(mut self) -> Self {
2143 self.apply_preset(crate::theme::presets::theme_light());
2144 self
2145 }
2146
2147 pub fn theme_dark(mut self) -> Self {
2148 self.apply_preset(crate::theme::presets::theme_dark());
2149 self
2150 }
2151
2152 pub fn theme_void(mut self) -> Self {
2153 self.apply_preset(crate::theme::presets::theme_void());
2154 self
2155 }
2156
2157 pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
2160 self.theme = self.theme.update(update);
2161 self
2162 }
2163
2164 pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
2168 self.guide_legend = guide;
2169 self
2170 }
2171
2172 pub fn labs(mut self, labels: Labels) -> Self {
2175 if labels.title.is_some() {
2176 self.labels.title = labels.title;
2177 }
2178 if labels.subtitle.is_some() {
2179 self.labels.subtitle = labels.subtitle;
2180 }
2181 if labels.x.is_some() {
2182 self.labels.x = labels.x;
2183 }
2184 if labels.y.is_some() {
2185 self.labels.y = labels.y;
2186 }
2187 if labels.caption.is_some() {
2188 self.labels.caption = labels.caption;
2189 }
2190 if labels.tag.is_some() {
2191 self.labels.tag = labels.tag;
2192 }
2193 self
2194 }
2195
2196 pub fn title(mut self, title: &str) -> Self {
2197 self.labels.title = Some(title.to_string());
2198 self
2199 }
2200
2201 pub fn tag(mut self, tag: &str) -> Self {
2204 self.labels.tag = Some(tag.to_string());
2205 self
2206 }
2207
2208 pub fn subtitle(mut self, subtitle: &str) -> Self {
2209 self.labels.subtitle = Some(subtitle.to_string());
2210 self
2211 }
2212
2213 pub fn xlab(mut self, label: &str) -> Self {
2214 self.labels.x = Some(label.to_string());
2215 self
2216 }
2217
2218 pub fn ylab(mut self, label: &str) -> Self {
2219 self.labels.y = Some(label.to_string());
2220 self
2221 }
2222
2223 pub fn caption(mut self, caption: &str) -> Self {
2224 self.labels.caption = Some(caption.to_string());
2225 self
2226 }
2227
2228 pub fn annotate(mut self, annotation: Annotation) -> Self {
2232 self.annotations.push(annotation);
2233 self
2234 }
2235
2236 pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
2238 self.annotate(Annotation::text(label, x, y))
2239 }
2240
2241 pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
2243 self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
2244 }
2245
2246 pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
2248 self.annotate(Annotation::segment(x, y, xend, yend))
2249 }
2250
2251 pub fn locale(mut self, locale: crate::locale::Locale) -> Self {
2274 self.locale = Some(locale);
2275 self
2276 }
2277
2278 pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
2282 PlotBuilder::build(self)
2283 }
2284
2285 pub fn build(self) -> crate::build::BuiltPlot {
2289 self.try_build().expect("plot build failed")
2290 }
2291
2292 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2296 pub fn save(self, path: &str) -> Result<(), GGError> {
2297 self.save_with_size(path, 800, 600)
2298 }
2299
2300 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2302 pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
2303 let (built, layout) = self.prepare(w, h)?;
2304
2305 let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
2307
2308 match ext.as_str() {
2309 "svg" => {
2310 let backend = plotters::prelude::SVGBackend::new(path, (w, h));
2311 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2312 }
2313 "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
2314 let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
2315 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2316 }
2317 _ => {
2318 return Err(GGError::UnsupportedFormat(ext));
2319 }
2320 }
2321
2322 Ok(())
2323 }
2324
2325 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2331 pub fn render_svg(self) -> Result<String, GGError> {
2332 self.render_svg_with_size(800, 600)
2333 }
2334
2335 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2339 pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2340 let (built, layout) = self.prepare(w, h)?;
2341 let mut buf = String::new();
2342 {
2343 let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
2344 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2345 }
2346 Ok(buf)
2347 }
2348
2349 pub fn render_svg_native(self) -> Result<String, GGError> {
2353 self.render_svg_native_with_size(800, 600)
2354 }
2355
2356 pub fn render_svg_native_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
2364 Ok(self.render_native(w, h)?.0.finish())
2365 }
2366
2367 pub fn render_svg_native_with_warnings(
2372 self,
2373 w: u32,
2374 h: u32,
2375 ) -> Result<(String, Vec<String>), GGError> {
2376 let (backend, warnings, _) = self.render_native(w, h)?;
2377 Ok((backend.finish(), warnings))
2378 }
2379
2380 pub fn render_svg_native_at(self, x: f64, y: f64, w: u32, h: u32) -> Result<String, GGError> {
2392 Ok(self.render_native(w, h)?.0.finish_fragment(x, y))
2393 }
2394
2395 pub fn render_svg_area_with_size(self, w: u32, h: u32) -> Result<(String, [f64; 4]), GGError> {
2399 let (backend, _, pa) = self.render_native(w, h)?;
2400 Ok((backend.finish(), [pa.x, pa.y, pa.width, pa.height]))
2401 }
2402
2403 fn render_native(
2407 self,
2408 w: u32,
2409 h: u32,
2410 ) -> Result<
2411 (
2412 crate::render::svg_backend::SvgBackend,
2413 Vec<String>,
2414 crate::render::Rect,
2415 ),
2416 GGError,
2417 > {
2418 let svg_root_data = self.svg_root_data;
2419 let root_attrs = self.root_attrs.clone();
2420 let svg_warnings_attr = self.svg_warnings_attr;
2421 let svg_formatted_values = self.svg_formatted_values;
2422 let (built, layout) = self.prepare(w, h)?;
2423 let pa = layout.plot_area.clone();
2424 let mut backend = crate::render::svg_backend::SvgBackend::new(w, h, pa.clone());
2425 backend.set_formatted_values(svg_formatted_values);
2426 let existing_root_attrs = if svg_root_data {
2427 let attrs = crate::render::svg_backend::root_data_attrs(&built);
2428 backend.set_root_attrs(attrs.clone());
2429 let mut reserved = attrs;
2433 reserved.push(("data-plot".to_string(), String::new()));
2434 reserved
2435 } else {
2436 backend.without_plot_attr();
2437 Vec::new()
2438 };
2439 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2440 let mut warnings = built.warnings;
2441 warnings.extend(backend.take_warnings());
2442
2443 let mut reserved = existing_root_attrs;
2444 if svg_warnings_attr {
2445 reserved.push(("data-warnings".to_string(), String::new()));
2446 }
2447 let extra = crate::render::svg_backend::validate_root_attrs(
2448 &root_attrs,
2449 &reserved,
2450 "plot",
2451 &mut warnings,
2452 );
2453 backend.extend_root_attrs(extra);
2454 if svg_warnings_attr {
2455 backend.extend_root_attrs(vec![(
2456 "data-warnings".to_string(),
2457 crate::render::svg_backend::warnings_json(&warnings),
2458 )]);
2459 }
2460 Ok((backend, warnings, pa))
2461 }
2462
2463 #[cfg(feature = "canvas")]
2468 pub fn render_rgba_with_size(self, w: u32, h: u32) -> Result<(u32, u32, Vec<u8>), GGError> {
2469 let (built, layout) = self.prepare(w, h)?;
2470 let mut backend =
2471 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2472 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2473 let (ow, oh) = backend.dimensions();
2474 Ok((ow, oh, backend.into_rgba()))
2475 }
2476
2477 #[cfg(feature = "canvas")]
2481 pub fn render_rgba_area_with_size(
2482 self,
2483 w: u32,
2484 h: u32,
2485 ) -> Result<(Vec<u8>, [f64; 4]), GGError> {
2486 let (built, layout) = self.prepare(w, h)?;
2487 let pa = layout.plot_area.clone();
2488 let mut backend = crate::render::pixel_backend::PixelBackend::new(w, h, pa.clone());
2489 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2490 Ok((backend.into_rgba(), [pa.x, pa.y, pa.width, pa.height]))
2491 }
2492
2493 #[cfg(feature = "canvas")]
2497 pub fn render_png_raster_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2498 let (built, layout) = self.prepare(w, h)?;
2499 let mut backend =
2500 crate::render::pixel_backend::PixelBackend::new(w, h, layout.plot_area.clone());
2501 PlotRenderer::render(&built, &mut backend).map_err(GGError::Render)?;
2502 backend.into_png().map_err(GGError::Render)
2503 }
2504
2505 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2512 pub fn render_png(self) -> Result<Vec<u8>, GGError> {
2513 self.render_png_with_size(800, 600)
2514 }
2515
2516 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
2519 pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
2520 let (built, layout) = self.prepare(w, h)?;
2521
2522 let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
2525 {
2526 let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
2527 Self::render_into(backend.into_drawing_area(), &built, &layout)?;
2528 }
2529
2530 let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
2531 GGError::Render(RenderError::BackendError(
2532 "PNG buffer size mismatch".to_string(),
2533 ))
2534 })?;
2535 let mut out = std::io::Cursor::new(Vec::new());
2536 img.write_to(&mut out, image::ImageOutputFormat::Png)
2537 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
2538 Ok(out.into_inner())
2539 }
2540
2541 pub(crate) fn prepare(
2543 self,
2544 w: u32,
2545 h: u32,
2546 ) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
2547 let (mut built, meta) = self.build_for_render()?;
2548 let layout = Self::layout_built(&mut built, &meta, w, h);
2549 Ok((built, layout))
2550 }
2551
2552 pub(crate) fn build_for_render(self) -> Result<(crate::build::BuiltPlot, RenderMeta), GGError> {
2555 let plot = self;
2556
2557 let has_title = plot.labels.title.is_some();
2558 let has_subtitle = plot.labels.subtitle.is_some();
2559 let has_caption = plot.labels.caption.is_some();
2560 let has_legend = plot.has_legend_mapping();
2561 let x_label = plot.labels.x.clone();
2562 let y_label = plot.labels.y.clone();
2563
2564 let mut built = PlotBuilder::build(plot)?;
2565
2566 built.theme.resolve_inheritance();
2568
2569 if let Some(ref label) = x_label {
2571 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
2572 s.set_name(label);
2573 }
2574 }
2575 if let Some(ref label) = y_label {
2576 if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
2577 s.set_name(label);
2578 }
2579 }
2580
2581 Ok((
2582 built,
2583 RenderMeta {
2584 has_title,
2585 has_subtitle,
2586 has_caption,
2587 has_legend,
2588 },
2589 ))
2590 }
2591
2592 pub(crate) fn layout_built(
2595 built: &mut crate::build::BuiltPlot,
2596 meta: &RenderMeta,
2597 w: u32,
2598 h: u32,
2599 ) -> PlotLayout {
2600 let RenderMeta {
2601 has_title,
2602 has_subtitle,
2603 has_caption,
2604 has_legend,
2605 } = *meta;
2606 let x_axis_top = built
2607 .scales
2608 .get(&crate::aes::Aesthetic::X)
2609 .map(|s| s.axis_position_opposite())
2610 .unwrap_or(false);
2611
2612 let flipped = built.coord.is_flipped();
2616 let h_aes = if flipped {
2617 crate::aes::Aesthetic::Y
2618 } else {
2619 crate::aes::Aesthetic::X
2620 };
2621 let v_aes = if flipped {
2622 crate::aes::Aesthetic::X
2623 } else {
2624 crate::aes::Aesthetic::Y
2625 };
2626
2627 let drawn_x_labels: Vec<(f64, String)> = built
2635 .scales
2636 .get(&h_aes)
2637 .map(|hs| {
2638 hs.breaks()
2639 .into_iter()
2640 .filter(|(_, label)| !label.is_empty())
2641 .collect()
2642 })
2643 .unwrap_or_default();
2644 let h_is_discrete = built
2645 .scales
2646 .get(&h_aes)
2647 .map(|hs| hs.is_discrete())
2648 .unwrap_or(false);
2649
2650 if built.theme.axis_text_x.visible
2663 && built.theme.axis_text_x.angle.abs() < 1.0
2664 && h_is_discrete
2665 && !drawn_x_labels.is_empty()
2666 {
2667 let font = built.theme.axis_text_x.size;
2668 let max_w = drawn_x_labels
2669 .iter()
2670 .map(|(_, l)| crate::render::text::text_width(l, font))
2671 .fold(0.0_f64, f64::max);
2672 let n = drawn_x_labels.len() as f64;
2673 let spacing = 0.9 * w as f64 / n; if max_w > spacing {
2675 let vertical = max_w * std::f64::consts::FRAC_1_SQRT_2 > spacing;
2679 built.theme.axis_text_x.angle = if vertical { 270.0 } else { -45.0 };
2680 }
2681 }
2682
2683 let x_label_height = if built.theme.axis_text_x.visible
2698 && built.theme.axis_text_x.angle.abs() > 10.0
2699 && !drawn_x_labels.is_empty()
2700 {
2701 let font = built.theme.axis_text_x.size;
2702 let angle = built.theme.axis_text_x.angle;
2703 let dir = if x_axis_top { -1.0 } else { 1.0 };
2704 let (rise, fall) = crate::render::text::rotated_label_rise_fall(
2705 drawn_x_labels.iter().map(|(_, l)| l.as_str()),
2706 font,
2707 angle,
2708 crate::render::text::rotated_anchor_frac(angle, dir),
2709 );
2710 Some(built.theme.legend_spacing / 2.0 + font / 2.0 + rise + fall)
2711 } else {
2712 None
2713 };
2714
2715 let y_label_width = if built.theme.axis_text_y.visible {
2719 built.scales.get(&v_aes).map(|vs| {
2720 let max_ch = vs
2721 .breaks()
2722 .iter()
2723 .map(|(_, l)| l.chars().count())
2724 .max()
2725 .unwrap_or(0);
2726 let measured = max_ch as f64 * built.theme.axis_text_y.size * 0.6 + 4.0;
2727 let default = built.theme.axis_text_y.size * 3.5 + 4.0;
2728 measured.max(default).min(0.35 * w as f64)
2729 })
2730 } else {
2731 None
2732 };
2733
2734 let mut extras = crate::render::layout::LayoutExtras::default();
2745 let can_reserve = built.facet.is_none()
2746 && built.theme.axis_text_x.visible
2747 && built.theme.axis_text_x_fit == crate::theme::AxisLabelFit::Reserve
2748 && !built.coord.is_polar()
2749 && built.coord.supports_axis_reserve();
2750 if can_reserve {
2751 if let Some(hs) = built.scales.get(&h_aes) {
2752 let breaks = hs.breaks();
2753 if let (Some(first), Some(last)) = (breaks.first(), breaks.last()) {
2754 let angle = built.theme.axis_text_x.angle;
2755 let anchor = if angle.abs() > 10.0 {
2756 let dir = if x_axis_top { -1.0 } else { 1.0 };
2757 crate::render::text::rotated_anchor_frac(angle, dir)
2758 } else {
2759 0.5
2760 };
2761 let size = built.theme.axis_text_x.size;
2762 const EDGE: f64 = 1.0;
2763 for _ in 0..2 {
2764 let layout = PlotLayout::compute_with_extras(
2765 w as f64,
2766 h as f64,
2767 &built.theme,
2768 has_title,
2769 has_subtitle,
2770 has_caption,
2771 has_legend,
2772 x_axis_top,
2773 y_label_width,
2774 x_label_height,
2775 extras,
2776 );
2777 let area = &layout.plot_area;
2778 let edge_y = area.y + area.height;
2779 let to_point = |pos: f64| if flipped { (0.0, pos) } else { (pos, 0.0) };
2780 let px_first = built.coord.transform(to_point(first.0), area).0;
2781 let px_last = built.coord.transform(to_point(last.0), area).0;
2782 let bbox_first = crate::render::text::label_bbox(
2783 &first.1, size, px_first, edge_y, angle, anchor,
2784 );
2785 let bbox_last = crate::render::text::label_bbox(
2786 &last.1, size, px_last, edge_y, angle, anchor,
2787 );
2788 let deficit_left = (EDGE - bbox_first[0]).max(0.0);
2789 let deficit_right = (bbox_last[2] - (w as f64 - EDGE)).max(0.0);
2790 if deficit_left <= 0.0 && deficit_right <= 0.0 {
2791 break;
2792 }
2793 let (p_first, p_last) = (first.0, last.0);
2802 if deficit_left > 0.0 && deficit_right > 0.0 {
2803 let (a, b, c, d) = (1.0 - p_first, p_first, 1.0 - p_last, p_last);
2808 let det = a * d - b * c;
2809 if det.abs() > 0.01 {
2810 extras.left +=
2811 ((deficit_left * d + deficit_right * b) / det).max(0.0);
2812 extras.right +=
2813 ((deficit_right * a + deficit_left * c) / det).max(0.0);
2814 } else {
2815 extras.left += deficit_left / (1.0 - p_first).max(0.05);
2819 extras.right += deficit_right / p_last.max(0.05);
2820 }
2821 } else if deficit_left > 0.0 {
2822 extras.left += deficit_left / (1.0 - p_first).max(0.05);
2823 } else if deficit_right > 0.0 {
2824 extras.right += deficit_right / p_last.max(0.05);
2825 }
2826 const MIN_PANEL_WIDTH: f64 = 50.0;
2841 let cap = (w as f64 - MIN_PANEL_WIDTH).max(0.0);
2842 if extras.left + extras.right > cap {
2843 let scale = if extras.left + extras.right > 0.0 {
2844 cap / (extras.left + extras.right)
2845 } else {
2846 0.0
2847 };
2848 extras.left *= scale;
2849 extras.right *= scale;
2850 }
2851 }
2852 }
2853 }
2854 }
2855
2856 if has_legend
2863 && !matches!(
2864 built.theme.legend_position,
2865 crate::theme::LegendPosition::None
2866 )
2867 {
2868 let legend_aes =
2869 crate::guide::legend::legend_scales_for(&built.scales, &built.suppressed_aes);
2870 let has_discrete_guide = legend_aes.iter().any(|a| {
2871 built
2872 .scales
2873 .get(a)
2874 .map(|s| s.is_discrete())
2875 .unwrap_or(false)
2876 });
2877 if has_discrete_guide {
2878 let layout0 = PlotLayout::compute_with_extras(
2879 w as f64,
2880 h as f64,
2881 &built.theme,
2882 has_title,
2883 has_subtitle,
2884 has_caption,
2885 has_legend,
2886 x_axis_top,
2887 y_label_width,
2888 x_label_height,
2889 extras,
2890 );
2891 let (legend_x0, _, mut is_horizontal) =
2892 crate::guide::legend::legend_position(&built.theme, &layout0.plot_area);
2893 if let Some(dir) = built.theme.legend_direction {
2894 is_horizontal = matches!(dir, crate::theme::LegendDirection::Horizontal);
2895 }
2896 let avail_width =
2897 (layout0.plot_area.x + layout0.plot_area.width - legend_x0).max(0.0);
2898 let mut legend_warnings = Vec::new();
2899 let mut plans = crate::guide::legend::plan_all_legends(
2900 &built.scales,
2901 &built.guide_legend,
2902 &built.theme,
2903 &built.suppressed_aes,
2904 avail_width,
2905 layout0.plot_area.height,
2906 is_horizontal,
2907 &mut legend_warnings,
2908 );
2909 built.warnings.extend(legend_warnings);
2910
2911 if !plans.is_empty() {
2912 extras.legend_extra_rows = if is_horizontal {
2918 plans
2919 .values()
2920 .map(|p| p.rows.saturating_sub(1))
2921 .max()
2922 .unwrap_or(0)
2923 } else {
2924 plans.values().map(|p| p.rows.saturating_sub(1)).sum()
2925 };
2926 extras.legend_extra_width = plans
2933 .values()
2934 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
2935 .fold(0.0_f64, f64::max);
2936
2937 if !is_horizontal {
2949 let final_layout = PlotLayout::compute_with_extras(
2950 w as f64,
2951 h as f64,
2952 &built.theme,
2953 has_title,
2954 has_subtitle,
2955 has_caption,
2956 has_legend,
2957 x_axis_top,
2958 y_label_width,
2959 x_label_height,
2960 extras,
2961 );
2962 let (legend_x_final, _, _) = crate::guide::legend::legend_position(
2963 &built.theme,
2964 &final_layout.plot_area,
2965 );
2966 let swatch_and_gap =
2967 built.theme.legend_key_width + built.theme.legend_spacing;
2968 let text_size = built.theme.legend_text.size;
2969 fn right_edge_of(
2975 legend_x: f64,
2976 swatch_and_gap: f64,
2977 text_size: f64,
2978 plan: &crate::guide::legend::LegendPlan,
2979 breaks: &[(f64, String)],
2980 ) -> f64 {
2981 plan.entries
2982 .iter()
2983 .filter_map(|e| breaks.get(e.break_idx).map(|(_, l)| (e, l)))
2984 .map(|(e, label)| {
2985 let text =
2986 e.label_override.as_deref().unwrap_or(label.as_str());
2987 legend_x
2988 + e.x_offset
2989 + swatch_and_gap
2990 + crate::render::text::text_width(text, text_size)
2991 })
2992 .fold(0.0_f64, f64::max)
2993 }
2994
2995 let mut updates: Vec<(
2996 crate::aes::Aesthetic,
2997 crate::guide::legend::LegendPlan,
2998 )> = Vec::new();
2999 let mut reduced_aes: Vec<crate::aes::Aesthetic> = Vec::new();
3006 for (aes, plan) in &plans {
3007 if plan.cols <= 1 {
3008 continue;
3009 }
3010 let Some(scale) = built.scales.get(aes) else {
3011 continue;
3012 };
3013 let breaks = scale.breaks();
3014 if right_edge_of(
3015 legend_x_final,
3016 swatch_and_gap,
3017 text_size,
3018 plan,
3019 &breaks,
3020 ) <= w as f64
3021 {
3022 continue; }
3024
3025 let n = breaks.len();
3028 let mut reduced: Vec<crate::guide::legend::LegendPlan> = Vec::new();
3029 for c in (1..plan.cols).rev() {
3030 let mut candidate_guide = built.guide_legend.clone();
3031 candidate_guide.ncol = Some(c);
3032 candidate_guide.nrow = None;
3033 let mut tmp_warnings = Vec::new();
3034 let candidate_guide =
3035 candidate_guide.validated(n, &mut tmp_warnings);
3036 if let Some(cand) = crate::guide::legend::plan_legend(
3037 &breaks,
3038 &candidate_guide,
3039 &built.theme,
3040 avail_width,
3041 layout0.plot_area.height,
3042 false,
3043 ) {
3044 reduced.push(cand);
3045 }
3046 }
3047 if let Some(fits) = reduced.iter().find(|c| {
3048 right_edge_of(legend_x_final, swatch_and_gap, text_size, c, &breaks)
3049 <= w as f64
3050 }) {
3051 extras.legend_extra_rows =
3052 extras.legend_extra_rows.max(fits.rows.saturating_sub(1));
3053 updates.push((aes.clone(), fits.clone()));
3054 reduced_aes.push(aes.clone());
3055 continue;
3056 }
3057
3058 let base = reduced.last().cloned().unwrap_or_else(|| plan.clone());
3063 extras.legend_extra_rows =
3064 extras.legend_extra_rows.max(base.rows.saturating_sub(1));
3065 let mut truncated = base.clone();
3066 for e in &mut truncated.entries {
3067 let Some((_, label)) = breaks.get(e.break_idx) else {
3068 continue;
3069 };
3070 let avail = (w as f64
3071 - (legend_x_final + e.x_offset + swatch_and_gap))
3072 .max(0.0);
3073 if let Some(short) =
3074 crate::render::text::truncate_to_width(label, avail, text_size)
3075 {
3076 e.label_override = Some(short);
3077 }
3078 }
3079
3080 let truncated_edge = right_edge_of(
3084 legend_x_final,
3085 swatch_and_gap,
3086 text_size,
3087 &truncated,
3088 &breaks,
3089 );
3090 if truncated_edge > w as f64 {
3091 built.warnings.push(format!(
3092 "legend: the {}-column grid still doesn't fit the {w}px chart (needs about {} px)",
3093 truncated.cols,
3094 truncated_edge.ceil() as i64
3095 ));
3096 }
3097 updates.push((aes.clone(), truncated));
3098 }
3099 for (aes, new_plan) in &updates {
3100 plans.insert(aes.clone(), new_plan.clone());
3101 }
3102
3103 if !reduced_aes.is_empty() {
3144 let original_reserve = extras.legend_extra_width;
3145 let tighter_reserve = plans
3146 .values()
3147 .map(|p| p.cols.saturating_sub(1) as f64 * p.col_pitch)
3148 .fold(0.0_f64, f64::max);
3149 if tighter_reserve < original_reserve {
3150 let mut retry_extras = extras;
3151 retry_extras.legend_extra_width = tighter_reserve;
3152 let retry_layout = PlotLayout::compute_with_extras(
3153 w as f64,
3154 h as f64,
3155 &built.theme,
3156 has_title,
3157 has_subtitle,
3158 has_caption,
3159 has_legend,
3160 x_axis_top,
3161 y_label_width,
3162 x_label_height,
3163 retry_extras,
3164 );
3165 let (retry_x, _, _) = crate::guide::legend::legend_position(
3166 &built.theme,
3167 &retry_layout.plot_area,
3168 );
3169 let all_still_fit = plans.iter().all(|(aes, plan)| {
3170 let Some(scale) = built.scales.get(aes) else {
3171 return true;
3172 };
3173 let breaks = scale.breaks();
3174 right_edge_of(retry_x, swatch_and_gap, text_size, plan, &breaks)
3175 <= w as f64
3176 });
3177 if all_still_fit {
3178 extras.legend_extra_width = tighter_reserve;
3179 }
3180 }
3181 }
3182 }
3183 for (aes, plan) in &plans {
3184 if let Some(ov) = &plan.overflow {
3185 let n = built
3186 .scales
3187 .get(aes)
3188 .map(|s| s.breaks().len())
3189 .unwrap_or(ov.dropped);
3190 let shown = n.saturating_sub(ov.dropped);
3191 built.warnings.push(format!(
3192 "legend: {} of {n} entries don't fit the chart; the legend shows {shown} and \"+{} more\"",
3193 ov.dropped, ov.dropped
3194 ));
3195 }
3196 }
3197 }
3198 built.legend_plans = plans;
3204 }
3205 }
3206
3207 PlotLayout::compute_with_extras(
3208 w as f64,
3209 h as f64,
3210 &built.theme,
3211 has_title,
3212 has_subtitle,
3213 has_caption,
3214 has_legend,
3215 x_axis_top,
3216 y_label_width,
3217 x_label_height,
3218 extras,
3219 )
3220 }
3221
3222 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3225 fn render_into<DB>(
3226 area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
3227 built: &crate::build::BuiltPlot,
3228 layout: &PlotLayout,
3229 ) -> Result<(), GGError>
3230 where
3231 DB: plotters::prelude::DrawingBackend,
3232 DB::ErrorType: 'static,
3233 {
3234 area.fill(&plotters::prelude::WHITE)
3235 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3236 let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
3237 PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
3238 area.present()
3239 .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
3240 Ok(())
3241 }
3242
3243 #[cfg(all(feature = "plotters", not(target_arch = "wasm32")))]
3246 pub fn ggsave(
3247 self,
3248 path: &str,
3249 width_inches: f64,
3250 height_inches: f64,
3251 dpi: f64,
3252 ) -> Result<(), GGError> {
3253 let w = (width_inches * dpi) as u32;
3254 let h = (height_inches * dpi) as u32;
3255 self.save_with_size(path, w, h)
3256 }
3257
3258 fn has_legend_mapping(&self) -> bool {
3273 fn is_legend_aes(a: &crate::aes::Aesthetic) -> bool {
3274 matches!(
3275 a,
3276 crate::aes::Aesthetic::Color
3277 | crate::aes::Aesthetic::Fill
3278 | crate::aes::Aesthetic::Shape
3279 | crate::aes::Aesthetic::Linetype
3280 | crate::aes::Aesthetic::Size
3281 | crate::aes::Aesthetic::Alpha
3282 )
3283 }
3284 let plot_level = self
3285 .mapping
3286 .mappings
3287 .iter()
3288 .any(|m| is_legend_aes(&m.aesthetic));
3289 plot_level
3290 || self.layers.iter().any(|layer| {
3291 if layer.show_legend == Some(false) {
3292 return false;
3293 }
3294 let effective = if layer.geom.inherit_aes() {
3295 self.mapping.merge(&layer.mapping)
3296 } else {
3297 layer.mapping.clone()
3298 };
3299 effective
3300 .mappings
3301 .iter()
3302 .any(|m| is_legend_aes(&m.aesthetic))
3303 })
3304 }
3305}
3306
3307#[derive(Clone, Copy, Debug)]
3309pub(crate) struct RenderMeta {
3310 pub(crate) has_title: bool,
3311 pub(crate) has_subtitle: bool,
3312 pub(crate) has_caption: bool,
3313 pub(crate) has_legend: bool,
3314}
3315
3316#[derive(Debug)]
3318pub enum GGError {
3319 Render(RenderError),
3320 UnsupportedFormat(String),
3321 Io(std::io::Error),
3322 ValidationError(String),
3323}
3324
3325impl std::fmt::Display for GGError {
3326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3327 match self {
3328 GGError::Render(e) => write!(f, "Render error: {e}"),
3329 GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
3330 GGError::Io(e) => write!(f, "IO error: {e}"),
3331 GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
3332 }
3333 }
3334}
3335
3336impl std::error::Error for GGError {}