1use crate::aes::{mapping::apply_after_stat, mapping::resolve_mappings, Aes, Aesthetic};
2use crate::annotate::Annotation;
3use crate::coord::Coord;
4use crate::data::DataFrame;
5use crate::facet::{Facet, FacetScales, Panel};
6use crate::geom::Geom;
7use crate::plot::{GGError, GGPlot, Labels, Layer};
8use crate::position::PositionParams;
9use crate::scale::ScaleSet;
10use crate::theme::Theme;
11
12pub struct BuiltLayer {
14 pub data: DataFrame,
15 pub geom: Box<dyn Geom>,
16 pub show_legend: Option<bool>,
17}
18
19pub struct BuiltPlot {
21 pub layers: Vec<BuiltLayer>,
22 pub scales: ScaleSet,
23 pub coord: Box<dyn Coord>,
24 pub theme: Theme,
25 pub labels: Labels,
26 pub facet: Facet,
27 pub panels: Vec<Panel>,
28 pub panels_data: Vec<Vec<DataFrame>>,
30 pub annotations: Vec<Annotation>,
31 pub guide_legend: crate::guide::config::GuideLegend,
32 pub suppressed_aes: std::collections::HashSet<Aesthetic>,
34 pub panel_scales: Vec<ScaleSet>,
36}
37
38pub struct PlotBuilder;
40
41impl PlotBuilder {
42 pub fn build(plot: GGPlot) -> Result<BuiltPlot, GGError> {
43 let GGPlot {
44 data: plot_data,
45 mapping: plot_mapping,
46 layers,
47 scales: user_scales,
48 mut coord,
49 theme,
50 labels,
51 facet,
52 annotations,
53 guide_legend,
54 } = plot;
55
56 let mut scale_set = ScaleSet::new();
57
58 for s in user_scales {
60 scale_set.add(s);
61 }
62
63 let mut built_layers = Vec::new();
64
65 let facet_vars = Self::facet_vars(&facet);
68
69 for layer in layers {
70 let built = Self::build_layer(
71 layer,
72 &plot_data,
73 &plot_mapping,
74 &mut scale_set,
75 theme.primary,
76 &facet_vars,
77 )?;
78 built_layers.push(built);
79 }
80
81 for bl in &built_layers {
83 scale_set.train_layer(&bl.data);
84 }
85
86 if let Some((min, max)) = coord.zoom_x() {
88 scale_set.set_limits(&Aesthetic::X, min, max);
89 }
90 if let Some((min, max)) = coord.zoom_y() {
91 scale_set.set_limits(&Aesthetic::Y, min, max);
92 }
93
94 let axis_span = |aes: &Aesthetic| {
98 scale_set.get(aes).and_then(|s| {
99 s.domain().map(|(min, max)| crate::coord::AxisSpan {
100 min,
101 max,
102 pmin: s.map(&crate::data::Value::Float(min)),
103 pmax: s.map(&crate::data::Value::Float(max)),
104 })
105 })
106 };
107 let x_span = axis_span(&Aesthetic::X);
108 let y_span = axis_span(&Aesthetic::Y);
109 coord.set_domains(x_span, y_span);
110
111 let (panels, panels_data) = Self::compute_facets(&facet, &built_layers, &plot_data);
113
114 let suppressed_aes = Self::compute_suppressed_aes(&built_layers);
116
117 let facet_scales_mode = match &facet {
119 Facet::Wrap { scales, .. } => scales.clone(),
120 Facet::Grid { scales, .. } => scales.clone(),
121 Facet::None => FacetScales::Fixed,
122 };
123 let panel_scales = Self::compute_panel_scales(&facet_scales_mode, &panels_data, &scale_set);
124
125 Ok(BuiltPlot {
126 layers: built_layers,
127 scales: scale_set,
128 coord,
129 theme,
130 labels,
131 facet,
132 panels,
133 panels_data,
134 annotations,
135 guide_legend,
136 suppressed_aes,
137 panel_scales,
138 })
139 }
140
141 fn facet_vars(facet: &Facet) -> Vec<String> {
143 match facet {
144 Facet::None => vec![],
145 Facet::Wrap { var, .. } => vec![var.clone()],
146 Facet::Grid {
147 row_var, col_var, ..
148 } => row_var.iter().chain(col_var.iter()).cloned().collect(),
149 }
150 }
151
152 fn compute_facets(
153 facet: &Facet,
154 built_layers: &[BuiltLayer],
155 _plot_data: &DataFrame,
156 ) -> (Vec<Panel>, Vec<Vec<DataFrame>>) {
157 match facet {
158 Facet::None => (vec![], vec![]),
159 Facet::Wrap {
160 var,
161 ncol,
162 labeller,
163 ..
164 } => {
165 let mut levels: Vec<String> = Vec::new();
167 for bl in built_layers {
168 if let Some(col) = bl.data.column(var) {
169 for v in col {
170 let key = v.to_group_key();
171 if !levels.contains(&key) {
172 levels.push(key);
173 }
174 }
175 }
176 }
177
178 let panels: Vec<Panel> = levels
180 .iter()
181 .enumerate()
182 .map(|(i, value)| {
183 let ncols =
184 ncol.unwrap_or_else(|| (levels.len() as f64).sqrt().ceil() as usize);
185 let formatted = labeller.format(var, value);
186 Panel {
187 row: i / ncols.max(1),
188 col: i % ncols.max(1),
189 label: formatted.clone(),
190 row_label: None,
191 col_label: Some(formatted),
192 rect: crate::render::Rect {
193 x: 0.0,
194 y: 0.0,
195 width: 0.0,
196 height: 0.0,
197 },
198 }
199 })
200 .collect();
201
202 let panels_data: Vec<Vec<DataFrame>> = levels
204 .iter()
205 .map(|level| {
206 built_layers
207 .iter()
208 .map(|bl| Self::filter_data_by_var(&bl.data, var, level))
209 .collect()
210 })
211 .collect();
212
213 (panels, panels_data)
214 }
215 Facet::Grid {
216 row_var,
217 col_var,
218 labeller,
219 ..
220 } => {
221 let mut row_levels: Vec<String> = Vec::new();
222 let mut col_levels: Vec<String> = Vec::new();
223
224 for bl in built_layers {
225 if let Some(rv) = row_var {
226 if let Some(col) = bl.data.column(rv) {
227 for v in col {
228 let key = v.to_group_key();
229 if !row_levels.contains(&key) {
230 row_levels.push(key);
231 }
232 }
233 }
234 }
235 if let Some(cv) = col_var {
236 if let Some(col) = bl.data.column(cv) {
237 for v in col {
238 let key = v.to_group_key();
239 if !col_levels.contains(&key) {
240 col_levels.push(key);
241 }
242 }
243 }
244 }
245 }
246
247 if row_levels.is_empty() {
248 row_levels.push("".to_string());
249 }
250 if col_levels.is_empty() {
251 col_levels.push("".to_string());
252 }
253
254 let mut panels = Vec::new();
255 let mut panels_data = Vec::new();
256
257 for (ri, rl) in row_levels.iter().enumerate() {
258 for (ci, cl) in col_levels.iter().enumerate() {
259 let row_fmt = if rl.is_empty() {
260 None
261 } else {
262 let rv = row_var.as_deref().unwrap_or("");
263 Some(labeller.format(rv, rl))
264 };
265 let col_fmt = if cl.is_empty() {
266 None
267 } else {
268 let cv = col_var.as_deref().unwrap_or("");
269 Some(labeller.format(cv, cl))
270 };
271 let label = match (&row_fmt, &col_fmt) {
272 (Some(r), Some(c)) => format!("{r} | {c}"),
273 (Some(r), None) => r.clone(),
274 (None, Some(c)) => c.clone(),
275 (None, None) => String::new(),
276 };
277 panels.push(Panel {
278 row: ri,
279 col: ci,
280 label,
281 row_label: row_fmt,
282 col_label: col_fmt,
283 rect: crate::render::Rect {
284 x: 0.0,
285 y: 0.0,
286 width: 0.0,
287 height: 0.0,
288 },
289 });
290
291 let layer_data: Vec<DataFrame> = built_layers
292 .iter()
293 .map(|bl| {
294 let mut data = bl.data.clone();
295 if let Some(rv) = row_var {
296 if !rl.is_empty() {
297 data = Self::filter_data_by_var(&data, rv, rl);
298 }
299 }
300 if let Some(cv) = col_var {
301 if !cl.is_empty() {
302 data = Self::filter_data_by_var(&data, cv, cl);
303 }
304 }
305 data
306 })
307 .collect();
308 panels_data.push(layer_data);
309 }
310 }
311
312 (panels, panels_data)
313 }
314 }
315 }
316
317 fn filter_data_by_var(data: &DataFrame, var: &str, level: &str) -> DataFrame {
318 if let Some(col) = data.column(var) {
319 let indices: Vec<usize> = col
320 .iter()
321 .enumerate()
322 .filter(|(_, v)| v.to_group_key() == level)
323 .map(|(i, _)| i)
324 .collect();
325
326 let mut result = DataFrame::new();
327 for col_name in data.column_names() {
328 if let Some(src) = data.column(col_name) {
329 let vals: Vec<_> = indices.iter().map(|&i| src[i].clone()).collect();
330 result.add_column(col_name.to_string(), vals);
331 }
332 }
333 result
334 } else {
335 data.clone()
336 }
337 }
338
339 fn build_layer(
340 layer: Layer,
341 plot_data: &DataFrame,
342 plot_mapping: &Aes,
343 scale_set: &mut ScaleSet,
344 primary: Option<(u8, u8, u8)>,
345 facet_vars: &[String],
346 ) -> Result<BuiltLayer, GGError> {
347 let Layer {
348 data: layer_data,
349 mapping: layer_mapping,
350 mut geom,
351 stat,
352 position,
353 params: _,
354 show_legend,
355 } = layer;
356
357 let source_data = layer_data.unwrap_or_else(|| plot_data.clone());
359
360 let merged_mapping = plot_mapping.merge(&layer_mapping);
362
363 if let Some(color) = primary {
366 let has_color = merged_mapping.get_mapping(&Aesthetic::Color).is_some();
367 let has_fill = merged_mapping.get_mapping(&Aesthetic::Fill).is_some();
368 if !has_color && !has_fill {
369 geom.set_series_color(color);
370 }
371 }
372
373 let mut working_data = resolve_mappings(&source_data, &merged_mapping);
375
376 let pre_stat_columns: Vec<String> = working_data
381 .column_names()
382 .iter()
383 .map(|s| s.to_string())
384 .collect();
385
386 for m in &merged_mapping.mappings {
388 scale_set.ensure_scale(&m.aesthetic, &working_data);
389 }
390
391 for scale in scale_set.iter() {
393 let col_name = scale.aesthetic().col_name().to_string();
394 if let Some(col) = working_data.column(&col_name) {
395 let transformed: Vec<_> = col.iter().map(|v| scale.transform(v)).collect();
396 let any_changed = transformed.iter().zip(col.iter()).any(|(t, o)| {
397 match (t.as_f64(), o.as_f64()) {
398 (Some(a), Some(b)) => (a - b).abs() > f64::EPSILON,
399 _ => false,
400 }
401 });
402 if any_changed {
403 if let Some(col_mut) = working_data.column_mut(&col_name) {
404 *col_mut = transformed;
405 }
406 }
407 }
408 }
409
410 Self::filter_oob_data(&mut working_data, scale_set);
412
413 let mut group_cols = Self::detect_group_columns(&working_data);
418 for fv in facet_vars {
419 if working_data.has_column(fv) && !group_cols.contains(fv) {
420 group_cols.push(fv.clone());
421 }
422 }
423
424 working_data = if !group_cols.is_empty() {
425 let groups =
426 working_data.group_by(&group_cols.iter().map(|s| s.as_str()).collect::<Vec<_>>());
427 let mut result = DataFrame::new();
428 for group in groups {
429 let mut computed = stat.compute_group(&group, scale_set);
430 let n = computed.nrows();
431 if n > 0 {
432 for fv in facet_vars {
433 if !computed.has_column(fv) {
434 if let Some(val) = group.column(fv).and_then(|c| c.first()).cloned() {
435 computed.add_column(fv.clone(), vec![val; n]);
436 }
437 }
438 }
439 }
440 result.vstack(&computed);
441 }
442 result
443 } else {
444 stat.compute_group(&working_data, scale_set)
445 };
446
447 apply_after_stat(&mut working_data, &merged_mapping);
449
450 for aes in &geom.required_aes() {
456 let col_name = aes.col_name();
457 let supplied = pre_stat_columns.iter().any(|c| c == col_name);
458 if !supplied && !working_data.has_column(col_name) {
459 return Err(GGError::ValidationError(format!(
460 "geom_{} requires aesthetic '{}' but it was not provided",
461 geom.name(),
462 col_name
463 )));
464 }
465 }
466
467 let stat_aes = [
469 ("x", Aesthetic::X),
470 ("y", Aesthetic::Y),
471 ("xmin", Aesthetic::X),
472 ("xmax", Aesthetic::X),
473 ("ymin", Aesthetic::Y),
474 ("ymax", Aesthetic::Y),
475 ];
476 for (col, aes) in &stat_aes {
477 if working_data.has_column(col) {
478 scale_set.ensure_scale(aes, &working_data);
479 }
480 }
481
482 let y_is_user_mapped = merged_mapping.get_mapping(&Aesthetic::Y).is_some();
484 if !y_is_user_mapped && working_data.has_column("y") {
485 if let Some(y_scale) = scale_set.get_mut(&Aesthetic::Y) {
486 y_scale.train(&[crate::data::Value::Float(0.0)]);
487 }
488 }
489
490 let params = PositionParams::default();
492 position.compute(&mut working_data, ¶ms);
493
494 scale_set.train_layer(&working_data);
496
497 for (col, aes) in &stat_aes {
501 if let Some(values) = working_data.column(col) {
502 if let Some(scale) = scale_set.get_mut(aes) {
503 scale.train(values);
504 }
505 }
506 }
507
508 Ok(BuiltLayer {
509 data: working_data,
510 geom,
511 show_legend,
512 })
513 }
514
515 fn filter_oob_data(data: &mut DataFrame, scale_set: &ScaleSet) {
517 let x_limits = scale_set.get(&Aesthetic::X).and_then(|s| s.filter_limits());
518 let y_limits = scale_set.get(&Aesthetic::Y).and_then(|s| s.filter_limits());
519
520 if x_limits.is_none() && y_limits.is_none() {
521 return;
522 }
523
524 let nrows = data.nrows();
525 let mut keep = vec![true; nrows];
526
527 if let Some((min, max)) = x_limits {
528 if let Some(col) = data.column("x") {
529 for (i, v) in col.iter().enumerate() {
530 if let Some(f) = v.as_f64() {
531 if f < min || f > max {
532 keep[i] = false;
533 }
534 }
535 }
536 }
537 }
538
539 if let Some((min, max)) = y_limits {
540 if let Some(col) = data.column("y") {
541 for (i, v) in col.iter().enumerate() {
542 if let Some(f) = v.as_f64() {
543 if f < min || f > max {
544 keep[i] = false;
545 }
546 }
547 }
548 }
549 }
550
551 if keep.iter().all(|&k| k) {
553 return;
554 }
555
556 let indices: Vec<usize> = keep
557 .iter()
558 .enumerate()
559 .filter(|(_, &k)| k)
560 .map(|(i, _)| i)
561 .collect();
562
563 let mut result = DataFrame::new();
564 for col_name in data.column_names() {
565 if let Some(src) = data.column(col_name) {
566 let vals: Vec<_> = indices.iter().map(|&i| src[i].clone()).collect();
567 result.add_column(col_name.to_string(), vals);
568 }
569 }
570 *data = result;
571 }
572
573 fn compute_panel_scales(
576 facet_scales: &FacetScales,
577 panels_data: &[Vec<DataFrame>],
578 base_scales: &ScaleSet,
579 ) -> Vec<ScaleSet> {
580 if matches!(facet_scales, FacetScales::Fixed) || panels_data.is_empty() {
581 return vec![];
582 }
583
584 let free_x = matches!(facet_scales, FacetScales::FreeX | FacetScales::Free);
585 let free_y = matches!(facet_scales, FacetScales::FreeY | FacetScales::Free);
586
587 panels_data
588 .iter()
589 .map(|panel_layers| {
590 let mut panel_set = base_scales.clone();
591
592 if free_x {
594 if let Some(s) = panel_set.get_mut(&Aesthetic::X) {
595 s.reset_training();
596 }
597 }
598 if free_y {
599 if let Some(s) = panel_set.get_mut(&Aesthetic::Y) {
600 s.reset_training();
601 }
602 }
603
604 for layer_data in panel_layers {
606 panel_set.train_layer(layer_data);
607 }
608
609 panel_set
610 })
611 .collect()
612 }
613
614 fn compute_suppressed_aes(built_layers: &[BuiltLayer]) -> std::collections::HashSet<Aesthetic> {
618 use std::collections::HashSet;
619 let legend_aes = [
620 Aesthetic::Color,
621 Aesthetic::Fill,
622 Aesthetic::Shape,
623 Aesthetic::Linetype,
624 Aesthetic::Size,
625 Aesthetic::Alpha,
626 ];
627 let mut suppressed = HashSet::new();
628 for aes in &legend_aes {
629 let col_name = aes.col_name();
630 let mut any_has = false;
631 let mut all_hidden = true;
632 for bl in built_layers {
633 if bl.data.has_column(col_name) {
634 any_has = true;
635 match bl.show_legend {
636 Some(false) => {} _ => {
638 all_hidden = false;
639 break;
640 }
641 }
642 }
643 }
644 if any_has && all_hidden {
645 suppressed.insert(aes.clone());
646 }
647 }
648 suppressed
649 }
650
651 fn detect_group_columns(data: &DataFrame) -> Vec<String> {
654 let candidates = ["group", "color", "fill", "x"];
655 let mut group_cols = Vec::new();
656 for &col in &candidates {
657 if data.has_column(col) {
658 if let Some(values) = data.column(col) {
659 let is_discrete = values
660 .iter()
661 .any(|v| matches!(v, crate::data::Value::Str(_)));
662 if is_discrete {
663 group_cols.push(col.to_string());
664 }
665 }
666 }
667 }
668 group_cols
669 }
670}