work
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 14 Jan 2025 02:54:12 +0000 (18:54 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 14 Jan 2025 02:54:12 +0000 (18:54 -0800)
rust/pspp/src/format.rs
rust/pspp/src/output/pivot/mod.rs
rust/pspp/src/output/pivot/output.rs
rust/pspp/src/output/render.rs
rust/pspp/src/output/table.rs

index a266953a7f48eb243139ef3b5120e59d84f257bb..0747cac03cda606e5fb0ab1e4644f2def72a3a7a 100644 (file)
@@ -781,7 +781,7 @@ pub struct Settings {
     pub leading_zero: bool,
 
     /// Custom currency styles.
-    pub ccs: EnumMap<CC, Option<NumberStyle>>,
+    pub ccs: EnumMap<CC, Option<Box<NumberStyle>>>,
 }
 
 #[derive(Copy, Clone, Enum)]
@@ -849,7 +849,7 @@ impl Settings {
                 });
                 &PCT.get(self)
             }
-            Type::CC(cc) => self.ccs[cc].as_ref().unwrap_or(&DEFAULT),
+            Type::CC(cc) => self.ccs[cc].as_deref().unwrap_or(&DEFAULT),
             Type::N
             | Type::Z
             | Type::P
index 606bb55177da4b88344be4c6a7bff810fff231f9..cc8a3b093bb10e94759070142e4eeca73949a92e 100644 (file)
@@ -80,7 +80,7 @@ use crate::{
 pub mod output;
 
 /// Areas of a pivot table for styling purposes.
-#[derive(Copy, Clone, Debug, Enum, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Default, Enum, PartialEq, Eq)]
 pub enum Area {
     Title,
     Caption,
@@ -93,6 +93,7 @@ pub enum Area {
 
     ColumnLabels,
     RowLabels,
+    #[default]
     Data,
 
     /// Layer indication.
@@ -351,7 +352,7 @@ impl Dimension {
 #[derive(Clone, Debug)]
 pub struct Group {
     parent: Option<Weak<Group>>,
-    name: Value,
+    name: Box<Value>,
     label_depth: usize,
     extra_depth: usize,
 
@@ -370,7 +371,7 @@ pub struct Group {
 #[derive(Clone, Debug)]
 pub struct Leaf {
     parent: Weak<Group>,
-    name: Value,
+    name: Box<Value>,
     label_depth: usize,
     extra_depth: usize,
 
@@ -871,7 +872,7 @@ pub struct PivotTable {
     pub current_layer: Vec<usize>,
 
     /// Column and row sizing and page breaks.
-    pub sizing: EnumMap<Axis2, Sizing>,
+    pub sizing: EnumMap<Axis2, Option<Box<Sizing>>>,
 
     /// Format settings.
     pub settings: FormatSettings,
@@ -889,10 +890,10 @@ pub struct PivotTable {
     pub datafile: Option<String>,
     pub date: Option<NaiveDateTime>,
     pub footnotes: Vec<Footnote>,
-    pub title: Option<Value>,
-    pub subtype: Option<Value>,
-    pub corner_text: Option<Value>,
-    pub caption: Option<Value>,
+    pub title: Option<Box<Value>>,
+    pub subtype: Option<Box<Value>>,
+    pub corner_text: Option<Box<Value>>,
+    pub caption: Option<Box<Value>>,
     pub notes: Option<String>,
     pub dimensions: Vec<Arc<Dimension>>,
     pub axes: EnumMap<Axis3, Axis>,
index 678350ec06d919d7019df5b69791a5dd08529ade..28e62c85bf55ed3f3165cd86abf0fcaf81b207ea 100644 (file)
@@ -182,7 +182,7 @@ impl PivotTable {
                     CellInner {
                         rotate: false,
                         area: Area::Data,
-                        value: value.cloned(),
+                        value: value.map(|value| Box::new(value.clone())),
                     },
                 );
             }
@@ -275,7 +275,7 @@ impl PivotTable {
                     f.display_marker(Some(self)),
                     f.display_content(Some(self))
                 );
-                let value = Some(Value::new_user_text(s));
+                let value = Some(Box::new(Value::new_user_text(s)));
                 footnotes.put(
                     Rect2::for_cell(Coord2::new(0, y)),
                     CellInner::new(Area::Footer, value),
@@ -480,7 +480,7 @@ fn compose_headings(
                             rotate: (rotate_inner_labels && is_inner_row)
                                 || (rotate_outer_labels && is_outer_row),
                             area,
-                            value: Some(c.name().clone()),
+                            value: Some(Box::new(c.name().clone())),
                         },
                     );
 
index f417dc17c1da0aaf9ec8d19766a2056364ba4508..dcc1da6acc73040be6fade1635b01b5c167901f0 100644 (file)
@@ -160,23 +160,23 @@ struct Page {
 
     /// "Cell positions".
     ///
-    /// cp[H] represents x positions within the table.
-    /// cp[H][0] = 0.
-    /// cp[H][1] = the width of the leftmost vertical rule.
-    /// cp[H][2] = cp[H][1] + the width of the leftmost column.
-    /// cp[H][3] = cp[H][2] + the width of the second-from-left vertical rule.
+    /// cp[X] represents x positions within the table.
+    /// cp[X][0] = 0.
+    /// cp[X][1] = the width of the leftmost vertical rule.
+    /// cp[X][2] = cp[X][1] + the width of the leftmost column.
+    /// cp[X][3] = cp[X][2] + the width of the second-from-left vertical rule.
     /// and so on:
-    /// cp[H][2 * n[H]] = x position of the rightmost vertical rule.
-    /// cp[H][2 * n[H] + 1] = total table width including all rules.
+    /// cp[X][2 * n[X]] = x position of the rightmost vertical rule.
+    /// cp[X][2 * n[X] + 1] = total table width including all rules.
     ///
-    /// Similarly, cp[V] represents y positions within the table.
-    /// cp[V][0] = 0.
-    /// cp[V][1] = the height of the topmost horizontal rule.
-    /// cp[V][2] = cp[V][1] + the height of the topmost row.
-    /// cp[V][3] = cp[V][2] + the height of the second-from-top horizontal rule.
+    /// Similarly, cp[Y] represents y positions within the table.
+    /// cp[Y][0] = 0.
+    /// cp[Y][1] = the height of the topmost horizontal rule.
+    /// cp[Y][2] = cp[Y][1] + the height of the topmost row.
+    /// cp[Y][3] = cp[Y][2] + the height of the second-from-top horizontal rule.
     /// and so on:
-    /// cp[V][2 * n[V]] = y position of the bottommost horizontal rule.
-    /// cp[V][2 * n[V] + 1] = total table height including all rules.
+    /// cp[Y][2 * n[Y]] = y position of the bottommost horizontal rule.
+    /// cp[Y][2 * n[Y] + 1] = total table height including all rules.
     ///
     /// Rules and columns can have width or height 0, in which case consecutive
     /// values in this array are equal.
@@ -196,10 +196,10 @@ struct Page {
     ///
     /// # Interpretation
     ///
-    /// overflow[H][0]: space trimmed off its left side.
-    /// overflow[H][1]: space trimmed off its right side.
-    /// overflow[V][0]: space trimmed off its top.
-    /// overflow[V][1]: space trimmed off its bottom.
+    /// overflow[X][0]: space trimmed off its left side.
+    /// overflow[X][1]: space trimmed off its right side.
+    /// overflow[Y][0]: space trimmed off its top.
+    /// overflow[Y][1]: space trimmed off its bottom.
     ///
     /// During rendering, this information is used to position the rendered
     /// portion of the cell within the available space.
@@ -225,9 +225,9 @@ struct Page {
     ///
     /// If this render_page is broken at the rule that separates "gh" from
     /// "ijk", then the page that contains the left side of the "abcdef" cell
-    /// will have overflow[H][1] of 10 + 30 = 40 for its portion of the cell,
+    /// will have overflow[X][1] of 10 + 30 = 40 for its portion of the cell,
     /// and the page that contains the right side of the cell will have
-    /// overflow[H][0] of 20 + 10 = 30.  The two resulting pages would look like
+    /// overflow[X][0] of 20 + 10 = 30.  The two resulting pages would look like
     /// this:
     ///
     /// ```text
@@ -256,13 +256,13 @@ struct Page {
     /// across multiple render_pages.  This member indicates when this has
     /// happened:
     ///
-    /// is_edge_cutoff[H][0] is true if pixels have been cut off the left side
+    /// is_edge_cutoff[X][0] is true if pixels have been cut off the left side
     /// of the leftmost column in this page, and false otherwise.
     ///
-    /// is_edge_cutoff[H][1] is true if pixels have been cut off the right side
+    /// is_edge_cutoff[X][1] is true if pixels have been cut off the right side
     /// of the rightmost column in this page, and false otherwise.
     ///
-    /// is_edge_cutoff[V][0] and is_edge_cutoff[V][1] are similar for the top
+    /// is_edge_cutoff[Y][0] and is_edge_cutoff[Y][1] are similar for the top
     /// and bottom of the table.
     ///
     /// The effect of is_edge_cutoff is to prevent rules along the edge in
@@ -539,6 +539,7 @@ impl Page {
         *self.cp[axis].last().unwrap()
     }
 
+    /// XXX This could return a 
     fn get_map(&self, a: Axis2, z: usize) -> Map {
         if z < self.h[a] {
             Map {
@@ -554,8 +555,107 @@ impl Page {
             }
         }
     }
+
+/*
+    fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
+        
+    }*/
+
+    /// Creates and returns a new [Page] whose contents are a subregion of
+    /// thispage's contents.  The new page includes cells `extent` (exclusive)
+    /// along `axis`, plus any headers on `axis`.
+    ///
+    /// If `pixel0` is nonzero, then it is a number of pixels to exclude from
+    /// the left or top (according to `axis`) of cell `extent.start`.
+    /// Similarly, `pixel1` is a number of pixels to exclude from the right or
+    /// bottom of cell `extent.end - 1`.  (`pixel0` and `pixel1` are used to
+    /// render cells that are too large to fit on a single page.)
+    ///
+    /// The whole of axis `!axis` is included.  (The caller may follow up with
+    /// another call to select on `!axis` to select on that axis as well.)
+    fn select(
+        self: &Arc<Self>,
+        axis: Axis2,
+        extent: Range<usize>,
+        pixel0: usize,
+        pixel1: usize,
+    ) -> Arc<Self> {
+        let z0 = extent.start;
+        let z1 = extent.end;
+
+        // If all of the page is selected, just make a copy.
+        if z0 == self.h[axis] && z1 == self.n[axis] && pixel0 == 0 && pixel1 == 0 {
+            return self.clone();
+        }
+
+        // Figure out `n`, `h`, `r` for the subpage.
+        let trim = [z0 - self.h[axis], self.n[axis] - z1];
+        let mut n = self.n;
+        n[axis] -= trim[0] + trim[1];
+        let mut h = self.h;
+        let mut r = self.r.clone();
+        r[axis].start += trim[0];
+        r[axis].end -= trim[1];
+
+        // An edge is cut off if it was cut off in `self` or if we're trimming
+        // pixels off that side of the page and there are no headers.
+        let mut is_edge_cutoff = self.is_edge_cutoff.clone();
+        is_edge_cutoff[axis][0] =
+            h[axis] == 0 && (pixel0 > 0 || (z0 == 0 && self.is_edge_cutoff[axis][0]));
+        is_edge_cutoff[axis][1] =
+            pixel1 > 0 || (z1 == self.n[axis] && self.is_edge_cutoff[axis][1]);
+
+        // Select widths from `self` into subpage.
+        let scp = self.cp[axis].as_slice();
+        let mut dcp = Vec::with_capacity(2 * n[axis] + 1);
+        dcp.push(0);
+        let mut total = 0;
+        for z in 0..=Self::rule_ofs(h[axis]) {
+            total += if z == 0 && is_edge_cutoff[axis][0] {
+                0
+            } else {
+                scp[z + 1] - scp[z]
+            };
+            dcp.push(total);
+        }
+        for z in Self::cell_ofs(z0)..=Self::cell_ofs(z1 - 1) {
+            total += scp[z + 1] - scp[z];
+            if z== Self::cell_ofs(z0) {
+                total -= pixel0;
+            }
+            if z == Self::cell_ofs(z1 - 1) {
+                total -= pixel1;
+            }
+            dcp.push(total);
+        }
+        let z = self.rule_ofs_r(axis, 0);
+        if !is_edge_cutoff[axis][1] {
+            total += scp[z + 1] - scp[z];
+        }
+        dcp.push(total);
+        debug_assert_eq!(dcp.len(), 2 * n[axis] + 1);
+
+        let mut cp = EnumMap::default();
+        cp[axis] = dcp;
+        cp[!axis] = self.cp[!axis].clone();
+
+        // Add new overflows.
+        let s = Selection;
+        if self.h[axis] == 0 || z0 > self.h[axis] || pixel0 > 0 {
+            let b = !axis;
+            let mut z = 0;
+            while z < self.n[b] {
+                let d = Coord2::for_axis((axis, z0), z);
+
+            }
+        }
+
+        todo!()
+    }
 }
 
+struct Selection;
+
 /// Maps a contiguous range of cells from a page to the underlying table along
 /// the horizontal or vertical dimension.
 struct Map {
index 4b3ba58dfea478edfe41e3b5a1555799a177453d..bf6a2f8f0c35ff128aba89c4d79ae6d4e4073886 100644 (file)
@@ -58,33 +58,20 @@ impl<'a> CellRef<'a> {
 
 #[derive(Clone)]
 pub enum Content {
-    Empty,
-    Value(Box<CellInner>),
+    Value(CellInner),
     Join(Arc<Cell>),
 }
 
 impl Content {
     pub fn inner(&self) -> &CellInner {
         match self {
-            Content::Empty => {
-                static INNER: CellInner = CellInner {
-                    rotate: false,
-                    area: Area::Title,
-                    value: None,
-                };
-                &INNER
-            }
             Content::Value(cell_inner) => &cell_inner,
             Content::Join(cell) => &cell.inner,
         }
     }
 
     pub fn is_empty(&self) -> bool {
-        if let Content::Empty = self {
-            true
-        } else {
-            false
-        }
+        self.inner().is_empty()
     }
 
     /// Returns the rectangle that this cell covers, only if the cell contains
@@ -130,6 +117,12 @@ impl Content {
     }
 }
 
+impl Default for Content {
+    fn default() -> Self {
+        Self::Value(CellInner::default())
+    }
+}
+
 #[derive(Clone)]
 pub struct Cell {
     inner: CellInner,
@@ -144,7 +137,7 @@ impl Cell {
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Default)]
 pub struct CellInner {
     /// Rotate cell contents 90 degrees?
     pub rotate: bool,
@@ -152,17 +145,21 @@ pub struct CellInner {
     /// The area that the cell belongs to.
     pub area: Area,
 
-    pub value: Option<Value>,
+    pub value: Option<Box<Value>>,
 }
 
 impl CellInner {
-    pub fn new(area: Area, value: Option<Value>) -> Self {
+    pub fn new(area: Area, value: Option<Box<Value>>) -> Self {
         Self {
             rotate: false,
             area,
             value,
         }
     }
+
+    pub fn is_empty(&self) -> bool {
+        self.value.is_none()
+    }
 }
 
 /// A table.
@@ -195,7 +192,7 @@ impl Table {
         Self {
             n,
             h: headers,
-            contents: vec![Content::Empty; n.y() * n.x()],
+            contents: vec![Content::default(); n.y() * n.x()],
             areas,
             borders,
             rules: enum_map! {
@@ -232,7 +229,7 @@ impl Table {
         use Axis2::*;
         if region[X].len() == 1 && region[Y].len() == 1 {
             let offset = self.offset(Coord2::new(region[X].start, region[Y].start));
-            self.contents[offset] = Content::Value(Box::new(inner));
+            self.contents[offset] = Content::Value(inner);
         } else {
             let cell = Arc::new(Cell::new(inner, region.clone()));
             for y in region[Y].clone() {