OK actually got it complete now? rust
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 30 Jan 2025 01:20:16 +0000 (17:20 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 30 Jan 2025 01:20:16 +0000 (17:20 -0800)
rust/pspp/src/output/pivot/mod.rs
rust/pspp/src/output/render.rs

index 8d219e99f0f02f036c8f32b47887547116b80637..d6f81083054eaf00f813bb94060ef0e8ffe08bc9 100644 (file)
@@ -688,6 +688,29 @@ pub struct BorderStyle {
     pub color: Color,
 }
 
+impl BorderStyle {
+    pub const fn none() -> Self {
+        Self {
+            stroke: Stroke::None,
+            color: Color::BLACK,
+        }
+    }
+
+    pub fn is_none(&self) -> bool {
+        self.stroke.is_none()
+    }
+
+    /// Returns a border style that "combines" the two arguments, that is, that
+    /// gives a reasonable choice for a rule for different reasons should have
+    /// both styles.
+    pub fn combine(self, other: BorderStyle) -> Self {
+        Self {
+            stroke: self.stroke.combine(other.stroke),
+            color: self.color,
+        }
+    }
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Enum)]
 pub enum Stroke {
     None,
@@ -699,10 +722,14 @@ pub enum Stroke {
 }
 
 impl Stroke {
+    pub fn is_none(&self) -> bool {
+        self == &Self::None
+    }
+
     /// Returns a stroke that "combines" the two arguments, that is, that gives
     /// a reasonable stroke choice for a rule for different reasons should have
     /// both styles.
-    fn combine(self, other: Stroke) -> Self {
+    pub fn combine(self, other: Stroke) -> Self {
         self.max(other)
     }
 }
@@ -751,6 +778,13 @@ impl Coord2 {
         coord
     }
 
+    pub fn from_fn<F>(f: F) -> Self
+    where
+        F: FnMut(Axis2) -> usize,
+    {
+        Self(EnumMap::from_fn(f))
+    }
+
     pub fn x(&self) -> usize {
         self.0[Axis2::X]
     }
@@ -814,7 +848,7 @@ impl Rect2 {
     {
         Self(EnumMap::from_fn(f))
     }
-    pub fn offset(self, offset: Coord2) -> Rect2 {
+    pub fn translate(self, offset: Coord2) -> Rect2 {
         Self::from_fn(|axis| self[axis].start + offset[axis]..self[axis].end + offset[axis])
     }
 }
index 7eed58f1307497908bb7b537814721b2ccbd9f30..6f78a28136241eeede29862a2bbec7abdf78038e 100644 (file)
@@ -71,16 +71,6 @@ pub struct Params {
     can_scale: bool,
 }
 
-/*
-pub struct DeviceCell {
-    /// Rotate cell contents 90 degrees?
-    rotate: bool,
-
-    /// Value to render.
-    value: &ValueInner,
-
-
-}*/
 pub trait Device {
     fn params(&self) -> &Params;
 
@@ -633,21 +623,28 @@ impl Page {
         })
     }
 
-    fn get_map(&self, axis: Axis2, ordinate: usize) -> &Map {
-        if ordinate < self.h[axis] {
+    fn get_map(&self, axis: Axis2, z: usize) -> &Map {
+        if z < self.h[axis] {
             &self.maps[axis][0]
         } else {
             &self.maps[axis][1]
         }
     }
 
+    fn map_z(&self, axis: Axis2, z: usize) -> usize {
+        z + self.get_map(axis, z).ofs
+    }
+
+    fn map_coord(&self, coord: Coord2) -> Coord2 {
+        Coord2::from_fn(|a| self.map_z(a, coord[a]))
+    }
+
     fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
         let maps = EnumMap::from_fn(|axis| self.get_map(axis, coord[axis]));
-        let coord = Coord2(coord.0.map(|axis, ordinate| ordinate + maps[axis].ofs));
-        let cell = self.table.get(coord);
+        let cell = self.table.get(self.map_coord(coord));
         RenderCell {
             rect: Rect2(cell.rect().0.map(|axis, Range { start, end }| {
-                let m = &maps[axis];
+                let m = maps[axis];
                 max(m.p0, start - m.ofs)..min(m.p0 + m.n, end - m.ofs)
             })),
             content: cell.content,
@@ -852,6 +849,62 @@ impl Page {
         }*/
     }
 
+    fn draw_rule(&self, ofs: Coord2, draw: &mut dyn Draw, coord: Coord2) {
+        const NO_BORDER: BorderStyle = BorderStyle::none();
+        let styles = EnumMap::from_fn(|a: Axis2| {
+            let b = !a;
+            if !is_rule(coord[a])
+                || (self.is_edge_cutoff[a][0] && coord[a] == 0)
+                || (self.is_edge_cutoff[a][1] && coord[a] == self.n[a] * 2)
+            {
+                [NO_BORDER, NO_BORDER]
+            } else if is_rule(coord[b]) {
+                let first = if coord[b] > 0 {
+                    let mut e = coord;
+                    e[b] -= 1;
+                    self.get_rule(a, e)
+                } else {
+                    NO_BORDER
+                };
+
+                let second = if coord[b] / 2 < self.n[b] {
+                    self.get_rule(a, coord)
+                } else {
+                    NO_BORDER
+                };
+
+                [first, second]
+            } else {
+                let rule = self.get_rule(a, coord);
+                [rule, rule]
+            }
+        });
+
+        if !styles
+            .values()
+            .all(|border| border.iter().all(BorderStyle::is_none))
+        {
+            let bb =
+                Rect2::from_fn(|a| self.cp[a][coord[a]]..self.cp[a][coord[a] + 1]).translate(ofs);
+            draw.draw_line(bb, styles);
+        }
+    }
+
+    fn get_rule(&self, a: Axis2, coord: Coord2) -> BorderStyle {
+        let coord = Coord2::from_fn(|a| coord[a] / 2);
+        let coord = self.map_coord(coord);
+
+        let border = self.table.get_rule(a, coord);
+        if self.h[a] > 0 && coord[a] == self.h[a] {
+            let border2 = self
+                .table
+                .get_rule(a, Coord2::for_axis((a, self.h[a]), coord[!a]));
+            border.combine(border2)
+        } else {
+            border
+        }
+    }
+
     fn extra_height(&self, bb: &Rect2, inner: &CellInner) -> usize {
         use Axis2::*;
         let height = self.device.measure_cell_height(inner, bb[X].len());
@@ -880,7 +933,7 @@ impl Page {
         let mut bb = Rect2::from_fn(|a| {
             self.cp[a][cell.rect[a].start * 2 + 1]..self.cp[a][cell.rect[a].end * 2]
         })
-        .offset(ofs);
+        .translate(ofs);
         let spill = EnumMap::from_fn(|a| {
             [
                 self.rule_width(a, cell.rect[a].start) / 2,