Skip to content

Commit fcb1b4d

Browse files
committed
simplify flipping
1 parent 35e7eb0 commit fcb1b4d

3 files changed

Lines changed: 29 additions & 52 deletions

File tree

src/execute/layer.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use crate::plot::aesthetic::{is_positional_aesthetic, AestheticContext};
77
use crate::plot::layer::is_transposed;
8-
use crate::plot::layer::orientation::{flip_mappings, resolve_orientation};
8+
use crate::plot::layer::orientation::{flip_positional_aesthetics, resolve_orientation};
99
use crate::plot::{
1010
AestheticValue, DefaultAestheticValue, Layer, ParameterValue, Scale, Schema, SqlTypeNames,
1111
StatResult,
@@ -356,7 +356,7 @@ pub fn apply_layer_transforms<F>(
356356
where
357357
F: Fn(&str) -> Result<DataFrame>,
358358
{
359-
use crate::plot::layer::orientation::{flip_mappings, flip_remappings};
359+
use crate::plot::layer::orientation::flip_positional_aesthetics;
360360

361361
// Clone order_by early to avoid borrow conflicts
362362
let order_by = layer.order_by.clone();
@@ -433,7 +433,7 @@ where
433433
// We flip them to aligned orientation so they're uniform with defaults.
434434
// At the end, we flip everything back together.
435435
if needs_flip {
436-
flip_remappings(layer);
436+
flip_positional_aesthetics(&mut layer.remappings.aesthetics);
437437
}
438438

439439
// Apply literal default remappings from geom defaults (e.g., y2 => 0.0 for bar baseline).
@@ -580,10 +580,10 @@ where
580580
// later in mod.rs after apply_remappings_post_query creates the columns,
581581
// so that Phase 4.5 can flip those columns along with everything else.
582582
if needs_flip {
583-
flip_mappings(layer);
583+
flip_positional_aesthetics(&mut layer.mappings.aesthetics);
584584

585585
// Normalize mapping column names to match their aesthetic keys.
586-
// After flip_mappings, pos1 might point to __ggsql_aes_pos2__ (and vice versa).
586+
// After flipping, pos1 might point to __ggsql_aes_pos2__ (and vice versa).
587587
// We update the column names so pos1 → __ggsql_aes_pos1__, etc.
588588
// The DataFrame columns will be renamed correspondingly in mod.rs.
589589
normalize_mapping_column_names(layer);
@@ -601,11 +601,11 @@ where
601601

602602
/// Normalize mapping column names to match their aesthetic keys after flip-back.
603603
///
604-
/// After `flip_mappings`, the mapping values (column names) may not match the keys.
604+
/// After flipping positional aesthetics, the mapping values (column names) may not match the keys.
605605
/// For example, pos1 might point to `__ggsql_aes_pos2__`.
606606
/// This function updates the column names so pos1 → `__ggsql_aes_pos1__`, etc.
607607
///
608-
/// This should be called after flip_mappings during flip-back.
608+
/// This should be called after flipping during flip-back.
609609
/// The DataFrame columns should be renamed correspondingly using `flip_dataframe_columns`.
610610
fn normalize_mapping_column_names(layer: &mut Layer) {
611611
// Collect the aesthetics to update (to avoid borrowing issues)
@@ -710,7 +710,7 @@ pub fn resolve_orientations(
710710
ParameterValue::String(orientation.to_string()),
711711
);
712712
if is_transposed(layer) {
713-
flip_mappings(layer);
713+
flip_positional_aesthetics(&mut layer.mappings.aesthetics);
714714
// Also flip column names in type_info to match the flipped mappings
715715
if layer_idx < layer_type_info.len() {
716716
for (name, _, _) in &mut layer_type_info[layer_idx] {

src/execute/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,7 +1165,9 @@ pub fn prepare_data_with_reader<R: Reader>(query: &str, reader: &R) -> Result<Pr
11651165
// (which uses remapping keys to create mapping entries).
11661166
// Phase 4.5 will then flip the DataFrame columns to match.
11671167
if is_transposed(l) {
1168-
crate::plot::layer::orientation::flip_remappings(l);
1168+
crate::plot::layer::orientation::flip_positional_aesthetics(
1169+
&mut l.remappings.aesthetics,
1170+
);
11691171
}
11701172

11711173
// Update layer mappings for all layers (even if data shared)

src/plot/layer/orientation.rs

Lines changed: 18 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
use super::geom::GeomType;
2727
use super::Layer;
2828
use crate::plot::scale::ScaleTypeKind;
29-
use crate::plot::{Mappings, Scale};
29+
use crate::plot::{AestheticValue, Mappings, Scale};
3030

3131
/// Orientation value for aligned/vertical orientation.
3232
pub const ALIGNED: &str = "aligned";
@@ -215,7 +215,7 @@ fn is_discrete_scale(scale: &Scale) -> bool {
215215
})
216216
}
217217

218-
/// Swap positional aesthetic pairs in layer mappings.
218+
/// Swap positional aesthetic pairs in an aesthetics map.
219219
///
220220
/// Swaps the following pairs:
221221
/// - pos1 ↔ pos2
@@ -224,52 +224,27 @@ fn is_discrete_scale(scale: &Scale) -> bool {
224224
/// - pos1end ↔ pos2end
225225
/// - pos1offset ↔ pos2offset
226226
///
227-
/// This is called before stat transforms for Secondary orientation layers,
228-
/// so stats always see "standard" orientation. After stat transforms,
229-
/// this is called again to flip back to the correct output positions.
230-
pub fn flip_mappings(layer: &mut Layer) {
231-
let pairs = [
227+
/// Used for both mappings and remappings when handling transposed orientation.
228+
pub fn flip_positional_aesthetics(
229+
aesthetics: &mut std::collections::HashMap<String, AestheticValue>,
230+
) {
231+
const PAIRS: [(&str, &str); 5] = [
232232
("pos1", "pos2"),
233233
("pos1min", "pos2min"),
234234
("pos1max", "pos2max"),
235235
("pos1end", "pos2end"),
236236
("pos1offset", "pos2offset"),
237237
];
238238

239-
for (a, b) in pairs {
240-
let val_a = layer.mappings.aesthetics.remove(a);
241-
let val_b = layer.mappings.aesthetics.remove(b);
239+
for (a, b) in PAIRS {
240+
let val_a = aesthetics.remove(a);
241+
let val_b = aesthetics.remove(b);
242242

243243
if let Some(v) = val_a {
244-
layer.mappings.aesthetics.insert(b.to_string(), v);
244+
aesthetics.insert(b.to_string(), v);
245245
}
246246
if let Some(v) = val_b {
247-
layer.mappings.aesthetics.insert(a.to_string(), v);
248-
}
249-
}
250-
}
251-
252-
/// Swap positional aesthetic pairs in remappings.
253-
///
254-
/// Same as flip_mappings but for remappings (stat output mappings).
255-
pub fn flip_remappings(layer: &mut Layer) {
256-
let pairs = [
257-
("pos1", "pos2"),
258-
("pos1min", "pos2min"),
259-
("pos1max", "pos2max"),
260-
("pos1end", "pos2end"),
261-
("pos1offset", "pos2offset"),
262-
];
263-
264-
for (a, b) in pairs {
265-
let val_a = layer.remappings.aesthetics.remove(a);
266-
let val_b = layer.remappings.aesthetics.remove(b);
267-
268-
if let Some(v) = val_a {
269-
layer.remappings.aesthetics.insert(b.to_string(), v);
270-
}
271-
if let Some(v) = val_b {
272-
layer.remappings.aesthetics.insert(a.to_string(), v);
247+
aesthetics.insert(a.to_string(), v);
273248
}
274249
}
275250
}
@@ -411,7 +386,7 @@ mod tests {
411386
}
412387

413388
#[test]
414-
fn test_flip_mappings() {
389+
fn test_flip_positional_aesthetics() {
415390
let mut layer = Layer::new(Geom::bar());
416391
layer.mappings.insert(
417392
"pos1".to_string(),
@@ -426,7 +401,7 @@ mod tests {
426401
AestheticValue::standard_column("x2".to_string()),
427402
);
428403

429-
flip_mappings(&mut layer);
404+
flip_positional_aesthetics(&mut layer.mappings.aesthetics);
430405

431406
// pos1 ↔ pos2
432407
assert_eq!(
@@ -446,23 +421,23 @@ mod tests {
446421
}
447422

448423
#[test]
449-
fn test_flip_mappings_empty() {
424+
fn test_flip_positional_aesthetics_empty() {
450425
let mut layer = Layer::new(Geom::point());
451426
// No crash with empty mappings
452-
flip_mappings(&mut layer);
427+
flip_positional_aesthetics(&mut layer.mappings.aesthetics);
453428
assert!(layer.mappings.aesthetics.is_empty());
454429
}
455430

456431
#[test]
457-
fn test_flip_mappings_partial() {
432+
fn test_flip_positional_aesthetics_partial() {
458433
let mut layer = Layer::new(Geom::bar());
459434
// Only pos1 mapped
460435
layer.mappings.insert(
461436
"pos1".to_string(),
462437
AestheticValue::standard_column("x".to_string()),
463438
);
464439

465-
flip_mappings(&mut layer);
440+
flip_positional_aesthetics(&mut layer.mappings.aesthetics);
466441

467442
// pos1 moves to pos2
468443
assert!(layer.mappings.get("pos1").is_none());

0 commit comments

Comments
 (0)