]> pintos-os.org Git - pspp/commitdiff
collect facetlevels
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 18 May 2026 19:10:47 +0000 (12:10 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 18 May 2026 19:10:47 +0000 (12:10 -0700)
rust/pspp/src/spv/read/graph.rs

index 20b6bbd3f90624fb4ee6409ed6486a0fc9bb13ab..62b0f4e829d225bdc853b397a14e7535d0eaedbb 100644 (file)
@@ -711,9 +711,10 @@ impl Visualization {
             rect.0[Axis2::Y].1 -= font_height;
         }
 
+        let facet_levels = graph.facet_levels();
         let faceting = graph
             .faceting()
-            .map(|f| f.decode(&series))
+            .map(|f| f.decode(&series, &facet_levels))
             .unwrap_or_default();
 
         let x_facets = FacetIterator::new(&faceting[Axis2::X]);
@@ -805,11 +806,11 @@ impl Visualization {
             }
 
             let label = zip(&y_facet, &faceting[Axis2::Y])
-                .map(|(value, series)| {
+                .map(|(value, facet)| {
                     if let Some(label) = &value.label {
                         label.clone()
                     } else {
-                        series.formatter.display(&value.value).to_string()
+                        facet.series.formatter.display(&value.value).to_string()
                     }
                 })
                 .join(", ");
@@ -922,8 +923,8 @@ impl Visualization {
 
                     let mut grouped_data = BTreeMap::<_, Vec<_>>::new();
                     'values: for i in 0.. {
-                        for (index, variable) in faceting[Axis2::X].iter().enumerate() {
-                            let Some(value) = variable.values.get(i) else {
+                        for (index, facet) in faceting[Axis2::X].iter().enumerate() {
+                            let Some(value) = facet.series.values.get(i) else {
                                 break 'values;
                             };
                             let expected = x_facet[index];
@@ -931,8 +932,8 @@ impl Visualization {
                                 continue 'values;
                             }
                         }
-                        for (index, variable) in faceting[Axis2::Y].iter().enumerate() {
-                            let Some(value) = variable.values.get(i) else {
+                        for (index, facet) in faceting[Axis2::Y].iter().enumerate() {
+                            let Some(value) = facet.series.values.get(i) else {
                                 break 'values;
                             };
                             let expected = y_facet[index];
@@ -2094,13 +2095,23 @@ impl VariableReference {
     fn decode_faceting<'a>(
         &self,
         series: &'a BTreeMap<&str, Series>,
-        faceting: &mut EnumMap<Axis2, Vec<&'a Series>>,
-        index: usize,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        axis_index: usize,
+        variable_index: &mut usize,
     ) {
-        let axis = if index % 2 == 0 { Axis2::X } else { Axis2::Y };
+        let axis = if axis_index % 2 == 0 {
+            Axis2::X
+        } else {
+            Axis2::Y
+        };
+        *variable_index += 1;
         let series = series.get(self.reference.as_str()).unwrap(); // XXX
         assert!(series.categorical); // XXX
-        faceting[axis].push(series);
+        faceting[axis].push(Facet {
+            series,
+            facet_level: facet_levels.get(&*variable_index).map(|v| &**v),
+        });
     }
 }
 
@@ -2943,6 +2954,15 @@ impl Graph {
     fn faceting(&self) -> Option<&Faceting> {
         self.children.iter().find_map(|child| child.faceting())
     }
+
+    fn facet_levels(&self) -> HashMap<usize, &FacetLevel> {
+        self.children
+            .iter()
+            .flat_map(|child| child.facet_layout())
+            .flat_map(|facet_layout| facet_layout.facet_levels())
+            .map(|facet_level| (facet_level.level, facet_level))
+            .collect()
+    }
 }
 
 impl Locations for Graph {
@@ -2987,6 +3007,13 @@ impl GraphChild {
         }
     }
 
+    fn facet_layout(&self) -> Option<&FacetLayout> {
+        match self {
+            Self::FacetLayout(facet_layout) => Some(facet_layout),
+            _ => None,
+        }
+    }
+
     fn location(&self) -> Option<&Location> {
         match self {
             Self::Location(location) => Some(location),
@@ -3382,6 +3409,12 @@ struct Faceting {
     children: Vec<FacetingChild>,
 }
 
+#[derive(Clone, Debug)]
+struct Facet<'a> {
+    series: &'a Series,
+    facet_level: Option<&'a FacetLevel>,
+}
+
 impl Faceting {
     /// Returns the faceting for each axis, as variable names from innermost to
     /// outermost.
@@ -3391,10 +3424,15 @@ impl Faceting {
     ///
     /// This could decode layering into Z-axis faceting but the corpus doesn't
     /// contain any examples of layering.
-    fn decode<'a>(&self, series: &'a BTreeMap<&str, Series>) -> EnumMap<Axis2, Vec<&'a Series>> {
+    fn decode<'a>(
+        &self,
+        series: &'a BTreeMap<&str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+    ) -> EnumMap<Axis2, Vec<Facet<'a>>> {
         let mut faceting = EnumMap::default();
+        let mut variable_index = 0;
         for child in &self.children {
-            child.decode(series, &mut faceting);
+            child.decode(series, facet_levels, &mut faceting, &mut variable_index);
         }
         faceting
     }
@@ -3412,11 +3450,13 @@ impl FacetingChild {
     fn decode<'a>(
         &self,
         series: &'a BTreeMap<&str, Series>,
-        faceting: &mut EnumMap<Axis2, Vec<&'a Series>>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        variable_index: &mut usize,
     ) {
         match self {
             FacetingChild::Cross(cross) | FacetingChild::Dot(cross) => {
-                cross.decode(series, faceting, 0)
+                cross.decode(series, facet_levels, faceting, 0, variable_index)
             }
             FacetingChild::Layer(_layer) => { // XXX warn
             }
@@ -3435,11 +3475,13 @@ impl Cross {
     fn decode<'a>(
         &self,
         series: &'a BTreeMap<&str, Series>,
-        faceting: &mut EnumMap<Axis2, Vec<&'a Series>>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
         start_index: usize,
+        variable_index: &mut usize,
     ) {
         for (child, index) in self.children.iter().zip(start_index..) {
-            child.decode(series, faceting, index);
+            child.decode(series, facet_levels, faceting, index, variable_index);
         }
     }
 }
@@ -3461,16 +3503,28 @@ impl CrossChild {
     fn decode<'a>(
         &self,
         series: &'a BTreeMap<&str, Series>,
-        faceting: &mut EnumMap<Axis2, Vec<&'a Series>>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
         index: usize,
+        variable_index: &mut usize,
     ) {
         match self {
             CrossChild::Unity => (),
-            CrossChild::Nest(nest) => nest.decode(series, faceting, index),
+            CrossChild::Nest(nest) => {
+                nest.decode(series, facet_levels, faceting, index, variable_index)
+            }
             CrossChild::VariableReference(variable_reference) => {
-                variable_reference.decode_faceting(series, faceting, index);
+                variable_reference.decode_faceting(
+                    series,
+                    facet_levels,
+                    faceting,
+                    index,
+                    variable_index,
+                );
+            }
+            CrossChild::Cross(cross) => {
+                cross.decode(series, facet_levels, faceting, index, variable_index)
             }
-            CrossChild::Cross(cross) => cross.decode(series, faceting, index),
         }
     }
 }
@@ -3486,11 +3540,19 @@ impl Nest {
     fn decode<'a>(
         &self,
         series: &'a BTreeMap<&'a str, Series>,
-        faceting: &mut EnumMap<Axis2, Vec<&'a Series>>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
         index: usize,
+        variable_index: &mut usize,
     ) {
         for variable_reference in &self.variable_references {
-            variable_reference.decode_faceting(series, faceting, index);
+            variable_reference.decode_faceting(
+                series,
+                facet_levels,
+                faceting,
+                index,
+                variable_index,
+            );
         }
     }
 }
@@ -3515,6 +3577,12 @@ struct FacetLayout {
     children: Vec<FacetLayoutChild>,
 }
 
+impl FacetLayout {
+    fn facet_levels(&self) -> impl Iterator<Item = &FacetLevel> {
+        self.children.iter().flat_map(|child| child.facet_level())
+    }
+}
+
 #[derive(Deserialize, Debug)]
 #[serde(rename_all = "camelCase")]
 enum FacetLayoutChild {
@@ -3524,6 +3592,15 @@ enum FacetLayoutChild {
     Other,
 }
 
+impl FacetLayoutChild {
+    fn facet_level(&self) -> Option<&FacetLevel> {
+        match self {
+            FacetLayoutChild::FacetLevel(facet_level) => Some(facet_level),
+            _ => None,
+        }
+    }
+}
+
 #[derive(Deserialize, Debug)]
 #[serde(rename_all = "camelCase")]
 struct SetCellProperties {
@@ -4685,22 +4762,22 @@ fn diaconis_rule(iqr: f64, n: f64) -> Option<usize> {
 
 #[derive(Clone, Debug)]
 struct FacetIterator<'a> {
-    series: &'a [&'a Series],
+    facets: &'a [Facet<'a>],
     next: SmallVec<[usize; 2]>,
     remaining: usize,
 }
 
 impl<'a> FacetIterator<'a> {
-    fn new(series: &'a [&'a Series]) -> Self {
+    fn new(facets: &'a [Facet<'a>]) -> Self {
         Self {
-            series,
-            next: smallvec![0; series.len()],
-            remaining: if series.is_empty() {
+            facets,
+            next: smallvec![0; facets.len()],
+            remaining: if facets.is_empty() {
                 1
             } else {
-                series
+                facets
                     .into_iter()
-                    .map(|series| series.unique_values.len())
+                    .map(|facet| facet.series.unique_values.len())
                     .product()
             },
         }
@@ -4718,12 +4795,12 @@ impl<'a> Iterator for FacetIterator<'a> {
                 .next
                 .iter()
                 .copied()
-                .zip(self.series)
-                .map(|(index, series)| &series.unique_values[index])
+                .zip(self.facets)
+                .map(|(index, facet)| &facet.series.unique_values[index])
                 .collect();
-            for (index, series) in self.next.iter_mut().zip(self.series) {
+            for (index, facet) in self.next.iter_mut().zip(self.facets) {
                 *index += 1;
-                if *index < series.unique_values.len() {
+                if *index < facet.series.unique_values.len() {
                     break;
                 }
                 *index = 0;