Skip to main content

ggplot_rs/
plot.rs

1use plotters::prelude::IntoDrawingArea;
2
3use crate::aes::Aes;
4use crate::annotate::Annotation;
5use crate::build::PlotBuilder;
6use crate::coord::cartesian::CoordCartesian;
7use crate::coord::fixed::CoordFixed;
8use crate::coord::flip::CoordFlip;
9use crate::coord::polar::CoordPolar;
10use crate::coord::Coord;
11use crate::data::{DataFrame, GGData};
12use crate::facet::{Facet, FacetLabeller, FacetScales};
13use crate::geom::area::GeomArea;
14use crate::geom::bar::GeomBar;
15use crate::geom::bin2d::GeomBin2d;
16use crate::geom::blank::GeomBlank;
17use crate::geom::boxplot::GeomBoxplot;
18use crate::geom::col::GeomCol;
19use crate::geom::contour::GeomContour;
20use crate::geom::count::GeomCount;
21use crate::geom::crossbar::GeomCrossbar;
22use crate::geom::curve::GeomCurve;
23use crate::geom::density::GeomDensity;
24use crate::geom::density2d::GeomDensity2d;
25use crate::geom::dotplot::GeomDotplot;
26use crate::geom::errorbar::GeomErrorbar;
27use crate::geom::freqpoly::GeomFreqpoly;
28use crate::geom::hex::GeomHex;
29use crate::geom::histogram::GeomHistogram;
30use crate::geom::jitter::GeomJitter;
31use crate::geom::line::GeomLine;
32use crate::geom::linerange::GeomLinerange;
33use crate::geom::path::GeomPath;
34use crate::geom::point::GeomPoint;
35use crate::geom::pointrange::GeomPointrange;
36use crate::geom::polygon::GeomPolygon;
37use crate::geom::qq::{GeomQQ, GeomQQLine};
38use crate::geom::rect::GeomRect;
39use crate::geom::refline::{GeomAbline, GeomHline, GeomVline};
40use crate::geom::ribbon::GeomRibbon;
41use crate::geom::rug::GeomRug;
42use crate::geom::segment::GeomSegment;
43use crate::geom::smooth::GeomSmooth;
44use crate::geom::spoke::GeomSpoke;
45use crate::geom::step::GeomStep;
46use crate::geom::text::{GeomLabel, GeomText};
47use crate::geom::tile::GeomTile;
48use crate::geom::violin::GeomViolin;
49use crate::geom::{Geom, GeomParams};
50use crate::position::Position;
51use crate::render::layout::PlotLayout;
52use crate::render::plotters_backend::PlottersAdapter;
53use crate::render::renderer::PlotRenderer;
54use crate::render::RenderError;
55use crate::scale::continuous::ScaleContinuous;
56use crate::scale::transform::ScaleTransform;
57use crate::scale::Scale;
58use crate::stat::Stat;
59use crate::theme::Theme;
60
61/// Labels for the plot.
62#[derive(Clone, Debug, Default)]
63pub struct Labels {
64    pub title: Option<String>,
65    pub subtitle: Option<String>,
66    pub x: Option<String>,
67    pub y: Option<String>,
68    pub caption: Option<String>,
69}
70
71/// A single layer in the plot.
72pub struct Layer {
73    pub data: Option<DataFrame>,
74    pub mapping: Aes,
75    pub geom: Box<dyn Geom>,
76    pub stat: Box<dyn Stat>,
77    pub position: Box<dyn Position>,
78    pub params: GeomParams,
79    pub show_legend: Option<bool>,
80}
81
82/// The top-level plot specification — builder pattern.
83pub struct GGPlot {
84    pub(crate) data: DataFrame,
85    pub(crate) mapping: Aes,
86    pub(crate) layers: Vec<Layer>,
87    pub(crate) scales: Vec<Box<dyn Scale>>,
88    pub(crate) coord: Box<dyn Coord>,
89    pub(crate) theme: Theme,
90    pub(crate) labels: Labels,
91    pub(crate) facet: Facet,
92    pub(crate) annotations: Vec<Annotation>,
93    pub(crate) guide_legend: crate::guide::config::GuideLegend,
94}
95
96impl GGPlot {
97    /// Create a new plot with the given data source.
98    pub fn new(data: impl GGData) -> Self {
99        GGPlot {
100            data: data.into_dataframe(),
101            mapping: Aes::default(),
102            layers: Vec::new(),
103            scales: Vec::new(),
104            coord: Box::new(CoordCartesian::new()),
105            theme: Theme::default(),
106            labels: Labels::default(),
107            facet: Facet::default(),
108            annotations: Vec::new(),
109            guide_legend: crate::guide::config::GuideLegend::default(),
110        }
111    }
112
113    /// Set the plot-level aesthetic mapping.
114    pub fn aes(mut self, mapping: Aes) -> Self {
115        self.mapping = mapping;
116        self
117    }
118
119    // ─── Geom shortcuts ──────────────────────────────────────────
120
121    pub fn geom_point(self) -> Self {
122        self.add_geom(GeomPoint::default())
123    }
124
125    pub fn geom_point_with(self, geom: GeomPoint) -> Self {
126        self.add_geom(geom)
127    }
128
129    pub fn geom_line(self) -> Self {
130        self.add_geom(GeomLine::default())
131    }
132
133    pub fn geom_line_with(self, geom: GeomLine) -> Self {
134        self.add_geom(geom)
135    }
136
137    pub fn geom_bar(self) -> Self {
138        self.add_geom(GeomBar::default())
139    }
140
141    pub fn geom_bar_with(self, geom: GeomBar) -> Self {
142        self.add_geom(geom)
143    }
144
145    pub fn geom_histogram(self) -> Self {
146        self.add_geom(GeomHistogram::default())
147    }
148
149    pub fn geom_histogram_with(self, geom: GeomHistogram) -> Self {
150        self.add_geom(geom)
151    }
152
153    pub fn geom_boxplot(self) -> Self {
154        self.add_geom(GeomBoxplot::default())
155    }
156
157    pub fn geom_boxplot_with(self, geom: GeomBoxplot) -> Self {
158        self.add_geom(geom)
159    }
160
161    pub fn geom_smooth(self) -> Self {
162        self.add_geom(GeomSmooth::default())
163    }
164
165    pub fn geom_smooth_with(self, geom: GeomSmooth) -> Self {
166        self.add_geom(geom)
167    }
168
169    pub fn geom_col(self) -> Self {
170        self.add_geom(GeomCol::default())
171    }
172
173    pub fn geom_col_with(self, geom: GeomCol) -> Self {
174        self.add_geom(geom)
175    }
176
177    pub fn geom_hline(self, yintercept: f64) -> Self {
178        self.add_geom(GeomHline::new(yintercept))
179    }
180
181    /// Add a horizontal reference line with custom styling (color/linetype/width).
182    pub fn geom_hline_with(self, geom: GeomHline) -> Self {
183        self.add_geom(geom)
184    }
185
186    pub fn geom_vline(self, xintercept: f64) -> Self {
187        self.add_geom(GeomVline::new(xintercept))
188    }
189
190    /// Add a vertical reference line with custom styling (color/linetype/width).
191    pub fn geom_vline_with(self, geom: GeomVline) -> Self {
192        self.add_geom(geom)
193    }
194
195    pub fn geom_abline(self, slope: f64, intercept: f64) -> Self {
196        self.add_geom(GeomAbline::new(slope, intercept))
197    }
198
199    /// Add a slope/intercept reference line with custom styling.
200    pub fn geom_abline_with(self, geom: GeomAbline) -> Self {
201        self.add_geom(geom)
202    }
203
204    pub fn geom_text(self) -> Self {
205        self.add_geom(GeomText::default())
206    }
207
208    pub fn geom_text_with(self, geom: GeomText) -> Self {
209        self.add_geom(geom)
210    }
211
212    pub fn geom_label(self) -> Self {
213        self.add_geom(GeomLabel::default())
214    }
215
216    pub fn geom_label_with(self, geom: GeomLabel) -> Self {
217        self.add_geom(geom)
218    }
219
220    pub fn geom_area(self) -> Self {
221        self.add_geom(GeomArea::default())
222    }
223
224    pub fn geom_area_with(self, geom: GeomArea) -> Self {
225        self.add_geom(geom)
226    }
227
228    pub fn geom_ribbon(self) -> Self {
229        self.add_geom(GeomRibbon::default())
230    }
231
232    pub fn geom_ribbon_with(self, geom: GeomRibbon) -> Self {
233        self.add_geom(geom)
234    }
235
236    pub fn geom_errorbar(self) -> Self {
237        self.add_geom(GeomErrorbar::default())
238    }
239
240    pub fn geom_errorbar_with(self, geom: GeomErrorbar) -> Self {
241        self.add_geom(geom)
242    }
243
244    pub fn geom_segment(self) -> Self {
245        self.add_geom(GeomSegment::default())
246    }
247
248    pub fn geom_segment_with(self, geom: GeomSegment) -> Self {
249        self.add_geom(geom)
250    }
251
252    pub fn geom_density(self) -> Self {
253        self.add_geom(GeomDensity::default())
254    }
255
256    pub fn geom_density_with(self, geom: GeomDensity) -> Self {
257        self.add_geom(geom)
258    }
259
260    pub fn geom_rug(self) -> Self {
261        self.add_geom(GeomRug::default())
262    }
263
264    pub fn geom_rug_with(self, geom: GeomRug) -> Self {
265        self.add_geom(geom)
266    }
267
268    pub fn geom_jitter(self) -> Self {
269        self.add_geom(GeomJitter::default())
270    }
271
272    pub fn geom_jitter_with(self, geom: GeomJitter) -> Self {
273        self.add_geom(geom)
274    }
275
276    pub fn geom_path(self) -> Self {
277        self.add_geom(GeomPath::default())
278    }
279
280    pub fn geom_path_with(self, geom: GeomPath) -> Self {
281        self.add_geom(geom)
282    }
283
284    pub fn geom_step(self) -> Self {
285        self.add_geom(GeomStep::default())
286    }
287
288    pub fn geom_step_with(self, geom: GeomStep) -> Self {
289        self.add_geom(geom)
290    }
291
292    pub fn geom_freqpoly(self) -> Self {
293        self.add_geom(GeomFreqpoly::default())
294    }
295
296    pub fn geom_freqpoly_with(self, geom: GeomFreqpoly) -> Self {
297        self.add_geom(geom)
298    }
299
300    pub fn geom_linerange(self) -> Self {
301        self.add_geom(GeomLinerange::default())
302    }
303
304    pub fn geom_linerange_with(self, geom: GeomLinerange) -> Self {
305        self.add_geom(geom)
306    }
307
308    pub fn geom_pointrange(self) -> Self {
309        self.add_geom(GeomPointrange::default())
310    }
311
312    pub fn geom_pointrange_with(self, geom: GeomPointrange) -> Self {
313        self.add_geom(geom)
314    }
315
316    pub fn geom_crossbar(self) -> Self {
317        self.add_geom(GeomCrossbar::default())
318    }
319
320    pub fn geom_crossbar_with(self, geom: GeomCrossbar) -> Self {
321        self.add_geom(geom)
322    }
323
324    pub fn geom_spoke(self) -> Self {
325        self.add_geom(GeomSpoke::default())
326    }
327
328    pub fn geom_spoke_with(self, geom: GeomSpoke) -> Self {
329        self.add_geom(geom)
330    }
331
332    pub fn geom_rect(self) -> Self {
333        self.add_geom(GeomRect::default())
334    }
335
336    pub fn geom_rect_with(self, geom: GeomRect) -> Self {
337        self.add_geom(geom)
338    }
339
340    pub fn geom_tile(self) -> Self {
341        self.add_geom(GeomTile::default())
342    }
343
344    pub fn geom_tile_with(self, geom: GeomTile) -> Self {
345        self.add_geom(geom)
346    }
347
348    pub fn geom_polygon(self) -> Self {
349        self.add_geom(GeomPolygon::default())
350    }
351
352    pub fn geom_polygon_with(self, geom: GeomPolygon) -> Self {
353        self.add_geom(geom)
354    }
355
356    pub fn geom_curve(self) -> Self {
357        self.add_geom(GeomCurve::default())
358    }
359
360    pub fn geom_curve_with(self, geom: GeomCurve) -> Self {
361        self.add_geom(geom)
362    }
363
364    pub fn geom_violin(self) -> Self {
365        self.add_geom(GeomViolin::default())
366    }
367
368    pub fn geom_violin_with(self, geom: GeomViolin) -> Self {
369        self.add_geom(geom)
370    }
371
372    pub fn geom_dotplot(self) -> Self {
373        self.add_geom(GeomDotplot::default())
374    }
375
376    pub fn geom_dotplot_with(self, geom: GeomDotplot) -> Self {
377        self.add_geom(geom)
378    }
379
380    pub fn geom_qq(self) -> Self {
381        self.add_geom(GeomQQ::default())
382    }
383
384    pub fn geom_qq_with(self, geom: GeomQQ) -> Self {
385        self.add_geom(geom)
386    }
387
388    pub fn geom_qq_line(self) -> Self {
389        self.add_geom(GeomQQLine::default())
390    }
391
392    pub fn geom_qq_line_with(self, geom: GeomQQLine) -> Self {
393        self.add_geom(geom)
394    }
395
396    pub fn geom_bin2d(self) -> Self {
397        self.add_geom(GeomBin2d::default())
398    }
399
400    pub fn geom_bin2d_with(self, geom: GeomBin2d) -> Self {
401        self.add_geom(geom)
402    }
403
404    pub fn geom_hex(self) -> Self {
405        self.add_geom(GeomHex::default())
406    }
407
408    pub fn geom_hex_with(self, geom: GeomHex) -> Self {
409        self.add_geom(geom)
410    }
411
412    pub fn geom_count(self) -> Self {
413        self.add_geom(GeomCount::default())
414    }
415
416    pub fn geom_count_with(self, geom: GeomCount) -> Self {
417        self.add_geom(geom)
418    }
419
420    pub fn geom_contour(self) -> Self {
421        self.add_geom(GeomContour::default())
422    }
423
424    pub fn geom_contour_with(self, geom: GeomContour) -> Self {
425        self.add_geom(geom)
426    }
427
428    pub fn geom_density2d(self) -> Self {
429        self.add_geom(GeomDensity2d::default())
430    }
431
432    pub fn geom_density2d_with(self, geom: GeomDensity2d) -> Self {
433        self.add_geom(geom)
434    }
435
436    pub fn geom_blank(self) -> Self {
437        self.add_geom(GeomBlank)
438    }
439
440    fn add_geom(mut self, geom: impl Geom + 'static) -> Self {
441        let stat = geom.default_stat();
442        let position = geom.default_position();
443        let params = geom.default_params();
444        self.layers.push(Layer {
445            data: None,
446            mapping: Aes::default(),
447            geom: Box::new(geom),
448            stat,
449            position,
450            params,
451            show_legend: None,
452        });
453        self
454    }
455
456    // ─── Layer-level overrides ──────────────────────────────────
457
458    /// Override the stat for the most recently added layer.
459    pub fn stat(mut self, stat: impl Stat + 'static) -> Self {
460        if let Some(layer) = self.layers.last_mut() {
461            layer.stat = Box::new(stat);
462        }
463        self
464    }
465
466    /// Override the position for the most recently added layer.
467    pub fn position(mut self, pos: impl Position + 'static) -> Self {
468        if let Some(layer) = self.layers.last_mut() {
469            layer.position = Box::new(pos);
470        }
471        self
472    }
473
474    /// Override the data for the most recently added layer.
475    pub fn layer_data(mut self, data: impl GGData) -> Self {
476        if let Some(layer) = self.layers.last_mut() {
477            layer.data = Some(data.into_dataframe());
478        }
479        self
480    }
481
482    /// Override the aesthetic mapping for the most recently added layer.
483    pub fn layer_aes(mut self, mapping: Aes) -> Self {
484        if let Some(layer) = self.layers.last_mut() {
485            layer.mapping = mapping;
486        }
487        self
488    }
489
490    /// Control whether the most recently added layer contributes to the legend.
491    /// `true` = always show, `false` = always hide, default (None) = auto.
492    pub fn show_legend(mut self, show: bool) -> Self {
493        if let Some(layer) = self.layers.last_mut() {
494            layer.show_legend = Some(show);
495        }
496        self
497    }
498
499    // ─── Scales ──────────────────────────────────────────────────
500
501    pub fn scale_x_continuous(mut self, s: ScaleContinuous) -> Self {
502        let s = s.for_aesthetic(crate::aes::Aesthetic::X);
503        self.scales.push(Box::new(s));
504        self
505    }
506
507    pub fn scale_y_continuous(mut self, s: ScaleContinuous) -> Self {
508        let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
509        self.scales.push(Box::new(s));
510        self
511    }
512
513    pub fn scale_x_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
514        let s = s.for_aesthetic(crate::aes::Aesthetic::X);
515        self.scales.push(Box::new(s));
516        self
517    }
518
519    pub fn scale_y_discrete(mut self, s: crate::scale::discrete::ScaleDiscrete) -> Self {
520        let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
521        self.scales.push(Box::new(s));
522        self
523    }
524
525    pub fn scale_color(mut self, s: impl Scale + 'static) -> Self {
526        self.scales.push(Box::new(s));
527        self
528    }
529
530    pub fn scale_fill(mut self, s: impl Scale + 'static) -> Self {
531        self.scales.push(Box::new(s));
532        self
533    }
534
535    pub fn scale_color_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
536        let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Color, values);
537        self.scale_color(s)
538    }
539
540    pub fn scale_fill_manual(self, values: Vec<(&str, crate::scale::color::RGBAColor)>) -> Self {
541        let s = crate::scale::manual::ScaleManual::new(crate::aes::Aesthetic::Fill, values);
542        self.scale_fill(s)
543    }
544
545    pub fn scale_color_viridis(self) -> Self {
546        use crate::scale::color::ScaleColorDiscrete;
547        use crate::scale::palettes::PaletteName;
548        let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color)
549            .with_named_palette(&PaletteName::Viridis);
550        self.scale_color(s)
551    }
552
553    pub fn scale_color_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
554        use crate::scale::color::ScaleColorDiscrete;
555        let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Color).with_named_palette(&name);
556        self.scale_color(s)
557    }
558
559    pub fn scale_color_gradient(
560        self,
561        low: crate::scale::color::RGBAColor,
562        high: crate::scale::color::RGBAColor,
563    ) -> Self {
564        use crate::scale::color::ScaleColorContinuous;
565        let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Color).with_colors(low, high);
566        self.scale_color(s)
567    }
568
569    pub fn scale_fill_gradient(
570        self,
571        low: crate::scale::color::RGBAColor,
572        high: crate::scale::color::RGBAColor,
573    ) -> Self {
574        use crate::scale::color::ScaleColorContinuous;
575        let s = ScaleColorContinuous::new(crate::aes::Aesthetic::Fill).with_colors(low, high);
576        self.scale_fill(s)
577    }
578
579    pub fn scale_color_gradient2(
580        self,
581        low: crate::scale::color::RGBAColor,
582        mid: crate::scale::color::RGBAColor,
583        high: crate::scale::color::RGBAColor,
584    ) -> Self {
585        use crate::scale::gradient::ScaleColorGradient2;
586        let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Color).with_colors(low, mid, high);
587        self.scale_color(s)
588    }
589
590    pub fn scale_fill_gradient2(
591        self,
592        low: crate::scale::color::RGBAColor,
593        mid: crate::scale::color::RGBAColor,
594        high: crate::scale::color::RGBAColor,
595    ) -> Self {
596        use crate::scale::gradient::ScaleColorGradient2;
597        let s = ScaleColorGradient2::new(crate::aes::Aesthetic::Fill).with_colors(low, mid, high);
598        self.scale_fill(s)
599    }
600
601    pub fn scale_fill_viridis(self) -> Self {
602        use crate::scale::color::ScaleColorDiscrete;
603        use crate::scale::palettes::PaletteName;
604        let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill)
605            .with_named_palette(&PaletteName::Viridis);
606        self.scale_fill(s)
607    }
608
609    /// Continuous viridis color scale (for numeric data).
610    pub fn scale_color_viridis_c(self) -> Self {
611        use crate::scale::gradient_n::ScaleColorGradientN;
612        let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Color);
613        self.scale_color(s)
614    }
615
616    /// Continuous viridis fill scale (for numeric data).
617    pub fn scale_fill_viridis_c(self) -> Self {
618        use crate::scale::gradient_n::ScaleColorGradientN;
619        let s = ScaleColorGradientN::viridis(crate::aes::Aesthetic::Fill);
620        self.scale_fill(s)
621    }
622
623    /// N-stop continuous color gradient.
624    pub fn scale_color_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
625        use crate::scale::gradient_n::ScaleColorGradientN;
626        let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Color, stops);
627        self.scale_color(s)
628    }
629
630    /// N-stop continuous fill gradient.
631    pub fn scale_fill_gradientn(self, stops: Vec<(f64, crate::scale::color::RGBAColor)>) -> Self {
632        use crate::scale::gradient_n::ScaleColorGradientN;
633        let s = ScaleColorGradientN::new(crate::aes::Aesthetic::Fill, stops);
634        self.scale_fill(s)
635    }
636
637    pub fn scale_fill_brewer(self, name: crate::scale::palettes::PaletteName) -> Self {
638        use crate::scale::color::ScaleColorDiscrete;
639        let s = ScaleColorDiscrete::new(crate::aes::Aesthetic::Fill).with_named_palette(&name);
640        self.scale_fill(s)
641    }
642
643    pub fn scale_linetype_manual(
644        self,
645        values: Vec<(&str, crate::render::backend::Linetype)>,
646    ) -> Self {
647        let s = crate::scale::linetype_manual::ScaleLinetypeManual::new(values);
648        self.scale_color(s)
649    }
650
651    pub fn scale_shape_manual(
652        self,
653        values: Vec<(&str, crate::render::backend::PointShape)>,
654    ) -> Self {
655        let s = crate::scale::shape_manual::ScaleShapeManual::new(values);
656        self.scale_color(s)
657    }
658
659    pub fn scale_color_grey(self) -> Self {
660        let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Color);
661        self.scale_color(s)
662    }
663
664    pub fn scale_fill_grey(self) -> Self {
665        let s = crate::scale::grey::ScaleColorGrey::new(crate::aes::Aesthetic::Fill);
666        self.scale_fill(s)
667    }
668
669    pub fn scale_color_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
670        self.scale_color(s)
671    }
672
673    pub fn scale_fill_grey_with(self, s: crate::scale::grey::ScaleColorGrey) -> Self {
674        self.scale_fill(s)
675    }
676
677    pub fn scale_x_reverse(self) -> Self {
678        self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
679    }
680
681    pub fn scale_y_reverse(self) -> Self {
682        self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Reverse))
683    }
684
685    pub fn scale_x_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
686        let s = s.for_aesthetic(crate::aes::Aesthetic::X);
687        self.scales.push(Box::new(s));
688        self
689    }
690
691    pub fn scale_y_datetime(mut self, s: crate::scale::datetime::ScaleDateTime) -> Self {
692        let s = s.for_aesthetic(crate::aes::Aesthetic::Y);
693        self.scales.push(Box::new(s));
694        self
695    }
696
697    pub fn scale_size(mut self, s: crate::scale::size::ScaleSizeContinuous) -> Self {
698        self.scales.push(Box::new(s));
699        self
700    }
701
702    pub fn scale_alpha(mut self, s: crate::scale::alpha::ScaleAlphaContinuous) -> Self {
703        self.scales.push(Box::new(s));
704        self
705    }
706
707    pub fn xlim(self, min: f64, max: f64) -> Self {
708        self.scale_x_continuous(ScaleContinuous::new().with_limits(min, max))
709    }
710
711    pub fn ylim(self, min: f64, max: f64) -> Self {
712        self.scale_y_continuous(ScaleContinuous::new().with_limits(min, max))
713    }
714
715    pub fn scale_x_log10(self) -> Self {
716        self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
717    }
718
719    pub fn scale_y_log10(self) -> Self {
720        self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log10))
721    }
722
723    pub fn scale_x_sqrt(self) -> Self {
724        self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
725    }
726
727    pub fn scale_y_sqrt(self) -> Self {
728        self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Sqrt))
729    }
730
731    pub fn scale_x_log2(self) -> Self {
732        self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
733    }
734
735    pub fn scale_y_log2(self) -> Self {
736        self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Log2))
737    }
738
739    pub fn scale_x_ln(self) -> Self {
740        self.scale_x_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
741    }
742
743    pub fn scale_y_ln(self) -> Self {
744        self.scale_y_continuous(ScaleContinuous::new().with_transform(ScaleTransform::Ln))
745    }
746
747    // ─── Faceting ─────────────────────────────────────────────────
748
749    pub fn facet_wrap(mut self, var: &str, ncol: Option<usize>) -> Self {
750        self.facet = Facet::Wrap {
751            var: var.to_string(),
752            ncol,
753            scales: FacetScales::Fixed,
754            labeller: FacetLabeller::default(),
755        };
756        self
757    }
758
759    pub fn facet_wrap_free(mut self, var: &str, ncol: Option<usize>, scales: FacetScales) -> Self {
760        self.facet = Facet::Wrap {
761            var: var.to_string(),
762            ncol,
763            scales,
764            labeller: FacetLabeller::default(),
765        };
766        self
767    }
768
769    pub fn facet_wrap_labeller(
770        mut self,
771        var: &str,
772        ncol: Option<usize>,
773        labeller: FacetLabeller,
774    ) -> Self {
775        self.facet = Facet::Wrap {
776            var: var.to_string(),
777            ncol,
778            scales: FacetScales::Fixed,
779            labeller,
780        };
781        self
782    }
783
784    pub fn facet_grid(mut self, row: Option<&str>, col: Option<&str>) -> Self {
785        self.facet = Facet::Grid {
786            row_var: row.map(String::from),
787            col_var: col.map(String::from),
788            scales: FacetScales::Fixed,
789            labeller: FacetLabeller::default(),
790        };
791        self
792    }
793
794    pub fn facet_grid_free(
795        mut self,
796        row: Option<&str>,
797        col: Option<&str>,
798        scales: FacetScales,
799    ) -> Self {
800        self.facet = Facet::Grid {
801            row_var: row.map(String::from),
802            col_var: col.map(String::from),
803            scales,
804            labeller: FacetLabeller::default(),
805        };
806        self
807    }
808
809    pub fn facet_grid_labeller(
810        mut self,
811        row: Option<&str>,
812        col: Option<&str>,
813        labeller: FacetLabeller,
814    ) -> Self {
815        self.facet = Facet::Grid {
816            row_var: row.map(String::from),
817            col_var: col.map(String::from),
818            scales: FacetScales::Fixed,
819            labeller,
820        };
821        self
822    }
823
824    // ─── Coordinates ─────────────────────────────────────────────
825
826    pub fn coord_flip(mut self) -> Self {
827        self.coord = Box::new(CoordFlip);
828        self
829    }
830
831    pub fn coord_fixed(mut self, ratio: f64) -> Self {
832        self.coord = Box::new(CoordFixed::new(ratio));
833        self
834    }
835
836    /// Zoom into a region without filtering data (unlike xlim/ylim which filter).
837    pub fn coord_cartesian_zoom(
838        mut self,
839        xlim: Option<(f64, f64)>,
840        ylim: Option<(f64, f64)>,
841    ) -> Self {
842        let mut c = CoordCartesian::new();
843        if let Some((min, max)) = xlim {
844            c = c.xlim(min, max);
845        }
846        if let Some((min, max)) = ylim {
847            c = c.ylim(min, max);
848        }
849        self.coord = Box::new(c);
850        self
851    }
852
853    pub fn coord_polar(mut self) -> Self {
854        self.coord = Box::new(CoordPolar::new());
855        self
856    }
857
858    pub fn coord_polar_with(mut self, coord: CoordPolar) -> Self {
859        self.coord = Box::new(coord);
860        self
861    }
862
863    // ─── Theme ───────────────────────────────────────────────────
864
865    pub fn theme(mut self, theme: Theme) -> Self {
866        self.theme = theme;
867        self
868    }
869
870    /// Set the brand/primary color used as the default for single-series geoms
871    /// that have no color/fill aesthetic mapped. Composes with any theme — one
872    /// render process can serve different tenants' brands at render time.
873    pub fn primary_color(mut self, color: (u8, u8, u8)) -> Self {
874        self.theme.primary = Some(color);
875        self
876    }
877
878    pub fn theme_minimal(mut self) -> Self {
879        self.theme = crate::theme::presets::theme_minimal();
880        self
881    }
882
883    pub fn theme_bw(mut self) -> Self {
884        self.theme = crate::theme::presets::theme_bw();
885        self
886    }
887
888    pub fn theme_gray(mut self) -> Self {
889        self.theme = crate::theme::presets::theme_gray();
890        self
891    }
892
893    pub fn theme_classic(mut self) -> Self {
894        self.theme = crate::theme::presets::theme_classic();
895        self
896    }
897
898    pub fn theme_linedraw(mut self) -> Self {
899        self.theme = crate::theme::presets::theme_linedraw();
900        self
901    }
902
903    pub fn theme_light(mut self) -> Self {
904        self.theme = crate::theme::presets::theme_light();
905        self
906    }
907
908    pub fn theme_dark(mut self) -> Self {
909        self.theme = crate::theme::presets::theme_dark();
910        self
911    }
912
913    pub fn theme_void(mut self) -> Self {
914        self.theme = crate::theme::presets::theme_void();
915        self
916    }
917
918    /// Apply incremental theme modifications on top of the current theme.
919    /// Like R's `+ theme(axis.text.x = element_text(...))`.
920    pub fn theme_update(mut self, update: crate::theme::ThemeUpdate) -> Self {
921        self.theme = self.theme.update(update);
922        self
923    }
924
925    // ─── Guides ──────────────────────────────────────────────────
926
927    /// Configure legend guide (title, ncol, reverse).
928    pub fn guides(mut self, guide: crate::guide::config::GuideLegend) -> Self {
929        self.guide_legend = guide;
930        self
931    }
932
933    // ─── Labels ──────────────────────────────────────────────────
934
935    pub fn labs(mut self, labels: Labels) -> Self {
936        if labels.title.is_some() {
937            self.labels.title = labels.title;
938        }
939        if labels.subtitle.is_some() {
940            self.labels.subtitle = labels.subtitle;
941        }
942        if labels.x.is_some() {
943            self.labels.x = labels.x;
944        }
945        if labels.y.is_some() {
946            self.labels.y = labels.y;
947        }
948        if labels.caption.is_some() {
949            self.labels.caption = labels.caption;
950        }
951        self
952    }
953
954    pub fn title(mut self, title: &str) -> Self {
955        self.labels.title = Some(title.to_string());
956        self
957    }
958
959    pub fn subtitle(mut self, subtitle: &str) -> Self {
960        self.labels.subtitle = Some(subtitle.to_string());
961        self
962    }
963
964    pub fn xlab(mut self, label: &str) -> Self {
965        self.labels.x = Some(label.to_string());
966        self
967    }
968
969    pub fn ylab(mut self, label: &str) -> Self {
970        self.labels.y = Some(label.to_string());
971        self
972    }
973
974    pub fn caption(mut self, caption: &str) -> Self {
975        self.labels.caption = Some(caption.to_string());
976        self
977    }
978
979    // ─── Annotations ──────────────────────────────────────────────
980
981    /// Add an annotation to the plot.
982    pub fn annotate(mut self, annotation: Annotation) -> Self {
983        self.annotations.push(annotation);
984        self
985    }
986
987    /// Add a text annotation at data coordinates.
988    pub fn annotate_text(self, label: &str, x: f64, y: f64) -> Self {
989        self.annotate(Annotation::text(label, x, y))
990    }
991
992    /// Add a rectangle annotation at data coordinates.
993    pub fn annotate_rect(self, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
994        self.annotate(Annotation::rect(xmin, xmax, ymin, ymax))
995    }
996
997    /// Add a segment annotation between data coordinates.
998    pub fn annotate_segment(self, x: f64, y: f64, xend: f64, yend: f64) -> Self {
999        self.annotate(Annotation::segment(x, y, xend, yend))
1000    }
1001
1002    // ─── Build and Render ────────────────────────────────────────
1003
1004    /// Build the plot without rendering, returning errors on validation failure.
1005    pub fn try_build(self) -> Result<crate::build::BuiltPlot, GGError> {
1006        PlotBuilder::build(self)
1007    }
1008
1009    /// Build the plot without rendering (analogous to R's ggplot_build()).
1010    /// Returns the fully computed BuiltPlot with layer data ready for inspection.
1011    /// Panics on validation errors — use `try_build()` for error handling.
1012    pub fn build(self) -> crate::build::BuiltPlot {
1013        self.try_build().expect("plot build failed")
1014    }
1015
1016    /// Build and save the plot to a file. Format determined by extension.
1017    pub fn save(self, path: &str) -> Result<(), GGError> {
1018        self.save_with_size(path, 800, 600)
1019    }
1020
1021    /// Build and save with custom dimensions.
1022    pub fn save_with_size(self, path: &str, w: u32, h: u32) -> Result<(), GGError> {
1023        let (built, layout) = self.prepare(w, h)?;
1024
1025        // Determine backend from file extension
1026        let ext = path.rsplit('.').next().unwrap_or("svg").to_lowercase();
1027
1028        match ext.as_str() {
1029            "svg" => {
1030                let backend = plotters::prelude::SVGBackend::new(path, (w, h));
1031                Self::render_into(backend.into_drawing_area(), &built, &layout)?;
1032            }
1033            "png" | "bmp" | "gif" | "jpeg" | "jpg" | "tiff" => {
1034                let backend = plotters::prelude::BitMapBackend::new(path, (w, h));
1035                Self::render_into(backend.into_drawing_area(), &built, &layout)?;
1036            }
1037            _ => {
1038                return Err(GGError::UnsupportedFormat(ext));
1039            }
1040        }
1041
1042        Ok(())
1043    }
1044
1045    /// Render the plot to an in-memory SVG document (default 800x600).
1046    ///
1047    /// Unlike [`save`](Self::save), this writes nothing to disk — handy for
1048    /// serving charts from a web/MCP service.
1049    pub fn render_svg(self) -> Result<String, GGError> {
1050        self.render_svg_with_size(800, 600)
1051    }
1052
1053    /// Render the plot to an in-memory SVG document with custom dimensions.
1054    pub fn render_svg_with_size(self, w: u32, h: u32) -> Result<String, GGError> {
1055        let (built, layout) = self.prepare(w, h)?;
1056        let mut buf = String::new();
1057        {
1058            let backend = plotters::prelude::SVGBackend::with_string(&mut buf, (w, h));
1059            Self::render_into(backend.into_drawing_area(), &built, &layout)?;
1060        }
1061        Ok(buf)
1062    }
1063
1064    /// Render the plot to in-memory PNG bytes (default 800x600).
1065    ///
1066    /// Returns a fully-encoded PNG, ready to write to an HTTP response or
1067    /// embed as a data URI — no temp files involved.
1068    pub fn render_png(self) -> Result<Vec<u8>, GGError> {
1069        self.render_png_with_size(800, 600)
1070    }
1071
1072    /// Render the plot to in-memory PNG bytes with custom dimensions.
1073    pub fn render_png_with_size(self, w: u32, h: u32) -> Result<Vec<u8>, GGError> {
1074        let (built, layout) = self.prepare(w, h)?;
1075
1076        // plotters' BitMapBackend draws into a raw RGB buffer; we then encode
1077        // that buffer to PNG via the `image` crate.
1078        let mut rgb = vec![0u8; (w as usize) * (h as usize) * 3];
1079        {
1080            let backend = plotters::prelude::BitMapBackend::with_buffer(&mut rgb, (w, h));
1081            Self::render_into(backend.into_drawing_area(), &built, &layout)?;
1082        }
1083
1084        let img = image::RgbImage::from_raw(w, h, rgb).ok_or_else(|| {
1085            GGError::Render(RenderError::BackendError(
1086                "PNG buffer size mismatch".to_string(),
1087            ))
1088        })?;
1089        let mut out = std::io::Cursor::new(Vec::new());
1090        img.write_to(&mut out, image::ImageOutputFormat::Png)
1091            .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
1092        Ok(out.into_inner())
1093    }
1094
1095    /// Shared pipeline: build the plot, apply label overrides, compute layout.
1096    fn prepare(self, w: u32, h: u32) -> Result<(crate::build::BuiltPlot, PlotLayout), GGError> {
1097        let plot = self;
1098
1099        let has_title = plot.labels.title.is_some();
1100        let has_subtitle = plot.labels.subtitle.is_some();
1101        let has_caption = plot.labels.caption.is_some();
1102        let has_legend = plot.has_legend_mapping();
1103        let x_label = plot.labels.x.clone();
1104        let y_label = plot.labels.y.clone();
1105
1106        let mut built = PlotBuilder::build(plot)?;
1107
1108        // Apply user label overrides to scales
1109        if let Some(ref label) = x_label {
1110            if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::X) {
1111                s.set_name(label);
1112            }
1113        }
1114        if let Some(ref label) = y_label {
1115            if let Some(s) = built.scales.get_mut(&crate::aes::Aesthetic::Y) {
1116                s.set_name(label);
1117            }
1118        }
1119
1120        let layout = PlotLayout::compute_full(
1121            w as f64,
1122            h as f64,
1123            &built.theme,
1124            has_title,
1125            has_subtitle,
1126            has_caption,
1127            has_legend,
1128        );
1129
1130        Ok((built, layout))
1131    }
1132
1133    /// Fill the background, render the built plot, and flush — for any backend.
1134    fn render_into<DB>(
1135        area: plotters::drawing::DrawingArea<DB, plotters::coord::Shift>,
1136        built: &crate::build::BuiltPlot,
1137        layout: &PlotLayout,
1138    ) -> Result<(), GGError>
1139    where
1140        DB: plotters::prelude::DrawingBackend,
1141        DB::ErrorType: 'static,
1142    {
1143        area.fill(&plotters::prelude::WHITE)
1144            .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
1145        let mut adapter = PlottersAdapter::new(&area, layout.plot_area.clone());
1146        PlotRenderer::render(built, &mut adapter).map_err(GGError::Render)?;
1147        area.present()
1148            .map_err(|e| GGError::Render(RenderError::BackendError(format!("{:?}", e))))?;
1149        Ok(())
1150    }
1151
1152    /// Save with physical dimensions (inches) and DPI.
1153    pub fn ggsave(
1154        self,
1155        path: &str,
1156        width_inches: f64,
1157        height_inches: f64,
1158        dpi: f64,
1159    ) -> Result<(), GGError> {
1160        let w = (width_inches * dpi) as u32;
1161        let h = (height_inches * dpi) as u32;
1162        self.save_with_size(path, w, h)
1163    }
1164
1165    fn has_legend_mapping(&self) -> bool {
1166        self.mapping.mappings.iter().any(|m| {
1167            matches!(
1168                m.aesthetic,
1169                crate::aes::Aesthetic::Color
1170                    | crate::aes::Aesthetic::Fill
1171                    | crate::aes::Aesthetic::Shape
1172                    | crate::aes::Aesthetic::Linetype
1173                    | crate::aes::Aesthetic::Size
1174                    | crate::aes::Aesthetic::Alpha
1175            )
1176        })
1177    }
1178}
1179
1180/// Top-level error type.
1181#[derive(Debug)]
1182pub enum GGError {
1183    Render(RenderError),
1184    UnsupportedFormat(String),
1185    Io(std::io::Error),
1186    ValidationError(String),
1187}
1188
1189impl std::fmt::Display for GGError {
1190    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1191        match self {
1192            GGError::Render(e) => write!(f, "Render error: {e}"),
1193            GGError::UnsupportedFormat(ext) => write!(f, "Unsupported output format: {ext}"),
1194            GGError::Io(e) => write!(f, "IO error: {e}"),
1195            GGError::ValidationError(msg) => write!(f, "Validation error: {msg}"),
1196        }
1197    }
1198}
1199
1200impl std::error::Error for GGError {}