use std::ops::Range;
use std::sync::Arc;
-use enum_map::{enum_map, EnumMap};
+use enum_map::EnumMap;
use smallvec::SmallVec;
use super::pivot::{Axis2, BorderStyle, Coord2, Look, PivotTable, Rect2, Stroke};
/// Main region of cells to render.
r: Rect2,
+ /// Mappings from [Page] positions to those in the underlying [Table].
+ maps: EnumMap<Axis2, [Map; 2]>,
+
/// "Cell positions".
///
/// cp[X] represents x positions within the table.
is_edge_cutoff: EnumMap<Axis2, [bool; 2]>,
}
+/// Returns the width of `extent` along `axis`.
+fn axis_width(cp: &[usize], extent: Range<usize>) -> usize {
+ cp[extent.end] - cp[extent.start]
+}
+
+/// Returns the width of cells within `extent` along `axis`.
+fn joined_width(cp: &[usize], extent: Range<usize>) -> usize {
+ axis_width(cp, cell_ofs(extent.start)..cell_ofs(extent.end) - 1)
+}
+/// Returns the offset in [Self::cp] of the cell with index `cell_index`.
+/// That is, if `cell_index` is 0, then the offset is 1, that of the leftmost
+/// or topmost cell; if `cell_index` is 1, then the offset is 3, that of the
+/// next cell to the right (or below); and so on. */
+fn cell_ofs(cell_index: usize) -> usize {
+ cell_index * 2 + 1
+}
+
+/// Returns the offset in [Self::cp] of the rule with index `rule_index`.
+/// That is, if `rule_index` is 0, then the offset is that of the leftmost
+/// or topmost rule; if `rule_index` is 1, then the offset is that of the
+/// next rule to the right (or below); and so on.
+fn rule_ofs(rule_index: usize) -> usize {
+ rule_index * 2
+}
+
+/// Returns the width of cell `z` along `axis`.
+fn cell_width(cp: &[usize], z: usize) -> usize {
+ let ofs = cell_ofs(z);
+ axis_width(cp, ofs..ofs + 1)
+}
+
impl Page {
/// Creates and returns a new [Page] for rendering `table` with the given
/// `look` on `device`.
fn new(table: &Arc<Table>, device: &Arc<dyn Device>, min_width: usize, look: &Look) -> Self {
use Axis2::*;
+ let n = table.n.clone();
+
// Figure out rule widths.
let rules = EnumMap::from_fn(|axis| {
- let n = table.n[axis];
- (0..n)
+ (0..n[axis])
.map(|z| measure_rule(table, &**device, axis, z))
.collect::<Vec<_>>()
});
// Calculate minimum and maximum widths of cells that do not span
// multiple columns.
- let mut columns = [vec![0; table.n.x()], vec![0; table.n.x()]];
+ let mut columns = [vec![0; n.x()], vec![0; n.x()]];
for cell in table.cells().filter(|cell| cell.col_span() == 1) {
let mut w = device.measure_cell_width(cell.inner());
if device.params().px_size.is_some() {
// In pathological cases, spans can cause the minimum width of a column
// to exceed the maximum width. This bollixes our interpolation
// algorithm later, so fix it up.
- for i in 0..table.n.x() {
+ for i in 0..n.x() {
if columns[0][i].width > columns[1][i].width {
columns[1][i].width = columns[0][i].width;
}
.map(|row| row.iter().map(|row| row.width).sum::<usize>() + rule_widths)
.collect::<SmallVec<[usize; 2]>>();
- let mut page = if table_widths[1] <= device.params().size[X] {
+ let cp_x = if table_widths[1] <= device.params().size[X] {
// Fits even with maximum widths. Use them.
- Self::new_with_exact_widths(device, table, &columns[1], &rules[X])
+ Self::use_row_widths(&columns[1], &rules[X])
} else if table_widths[0] <= device.params().size[X] {
// Fits with minimum widths, so distribute the leftover space.
//Self::new_with_interpolated_widths()
- todo!()
+ Self::interpolate_row_widths(
+ &table,
+ device.params(),
+ &columns[0],
+ &columns[1],
+ table_widths[0],
+ table_widths[1],
+ &rules[X],
+ )
} else {
// Doesn't fit even with minimum widths. Assign minimums for now, and
// later we can break it horizontally into multiple pages.
- Self::new_with_exact_widths(device, table, &columns[0], &rules[X])
+ Self::use_row_widths(&columns[0], &rules[X])
};
// Calculate heights of cells that do not span multiple rows.
- let mut rows = vec![Row::default(); table.n[Y]];
+ let mut rows = vec![Row::default(); n[Y]];
for cell in table.cells().filter(|cell| cell.row_span() == 1) {
let rect = cell.rect();
- let w = page.joined_width(X, rect[X].clone());
+ let w = joined_width(&cp_x, rect[X].clone());
let h = device.measure_cell_height(cell.inner(), w);
let row = &mut rows[cell.coord.y()];
// Distribute heights of spanned rows.
for cell in table.cells().filter(|cell| cell.row_span() > 1) {
let rect = cell.rect();
- let w = page.joined_width(X, rect[X].clone());
+ let w = joined_width(&cp_x, rect[X].clone());
let h = device.measure_cell_height(cell.inner(), w);
distribute_spanned_width(
h,
}
// Decide final row heights.
- page.cp[Y] = Self::accumulate_row_widths(&rows, &rules[Y]);
+ let cp_y = Self::use_row_widths(&rows, &rules[Y]);
// Measure headers. If they are "too big", get rid of them.
- for axis in [X, Y] {
- let hw = page.headers_width(axis);
+ let mut h = table.h.clone();
+ for (axis, cp) in [(X, cp_x.as_slice()), (Y, cp_y.as_slice())] {
+ let header_width = axis_width(cp, 0..table.h[axis]);
+ let max_cell_width = (table.h[axis]..n[axis])
+ .map(|z| cell_width(cp, z))
+ .max()
+ .unwrap_or(0);
let threshold = device.params().size[axis];
- if hw * 2 >= threshold || hw + page.max_cell_width(axis) > threshold {
- page.h[axis] = 0;
- page.r[axis] = 0..page.n[axis];
+ if header_width * 2 >= threshold || header_width + max_cell_width > threshold {
+ h[axis] = 0;
}
}
- page
+ let r = Rect2::new(h[X]..n[X], h[Y]..n[Y]);
+ let maps = Self::new_mappings(h, &r);
+ Self {
+ device: device.clone(),
+ table: table.clone(),
+ n,
+ h,
+ r,
+ cp: Axis2::new_enum(cp_x, cp_y),
+ overflows: HashMap::new(),
+ is_edge_cutoff: EnumMap::default(),
+ maps,
+ }
}
- fn accumulate_row_widths(rows: &[Row], rules: &[usize]) -> Vec<usize> {
+ fn accumulate_vec(mut vec: Vec<usize>) -> Vec<usize> {
+ for i in 1..vec.len() {
+ vec[i] += vec[i - 1]
+ }
+ vec
+ }
+
+ fn use_row_widths(rows: &[Row], rules: &[usize]) -> Vec<usize> {
debug_assert_eq!(rows.len() + 1, rules.len());
- let n = 2 * (rows.len()) + 1;
- let mut cp = Vec::with_capacity(n);
- let mut total = 0;
- cp.push(total);
+ let mut cp = Vec::with_capacity(2 * (rows.len()) + 1);
+
+ cp.push(0);
for (rule, row) in rules.iter().zip(rows.iter()) {
- total += *rule;
- cp.push(total);
- total += row.width;
- cp.push(total);
+ cp.push(*rule);
+ cp.push(row.width);
}
- total += rules.last().unwrap();
- cp.push(total);
- debug_assert_eq!(cp.len(), n);
- cp
+ cp.push(*rules.last().unwrap());
+
+ Self::accumulate_vec(cp)
}
- fn new_with_exact_widths(
- device: &Arc<dyn Device>,
- table: &Arc<Table>,
- rows: &[Row],
+ fn interpolate_row_widths(
+ table: &Table,
+ params: &Params,
+ rows_min: &[Row],
+ rows_max: &[Row],
+ w_min: usize,
+ w_max: usize,
rules: &[usize],
- ) -> Self {
- use Axis2::*;
- Self {
- device: device.clone(),
- table: table.clone(),
- n: table.n,
- h: table.h,
- r: Rect2::new(table.h[X]..table.n[X], table.h[Y]..table.n[Y]),
- cp: enum_map! {
- X => Self::accumulate_row_widths(rows, rules),
- Y => Vec::new(),
- },
- overflows: HashMap::new(),
- is_edge_cutoff: EnumMap::default(),
- }
+ ) -> Vec<usize> {
+ let avail = params.size[Axis2::X] - w_min;
+ let wanted = w_max - w_min;
+ let mut w = wanted / 2;
+ let rows_mid = rows_min
+ .iter()
+ .zip(rows_max.iter())
+ .map(|(min, max)| {
+ w += avail * (max.width - min.width);
+ let extra = w / wanted;
+ w -= extra * wanted;
+ Row {
+ width: min.width + extra,
+ unspanned: 0,
+ }
+ })
+ .collect::<Vec<_>>();
+ return Self::use_row_widths(&rows_mid, rules);
}
/// Returns the width of `extent` along `axis`.
fn axis_width(&self, axis: Axis2, extent: Range<usize>) -> usize {
- self.cp[axis][extent.end] - self.cp[axis][extent.start]
+ axis_width(&self.cp[axis], extent)
}
/// Returns the width of cells within `extent` along `axis`.
fn joined_width(&self, axis: Axis2, extent: Range<usize>) -> usize {
- self.axis_width(
- axis,
- Self::cell_ofs(extent.start)..Self::cell_ofs(extent.end) - 1,
+ joined_width(
+ &self.cp[axis],
+ cell_ofs(extent.start)..cell_ofs(extent.end) - 1,
)
}
/// Returns the width of the headers along `axis`.
fn headers_width(&self, axis: Axis2) -> usize {
- self.axis_width(axis, Self::rule_ofs(0)..Self::cell_ofs(self.h[axis]))
+ self.axis_width(axis, rule_ofs(0)..cell_ofs(self.h[axis]))
}
/// Returns the width of rule `z` along `axis`.
fn rule_width(&self, axis: Axis2, z: usize) -> usize {
- self.axis_width(axis, Self::rule_ofs(z + 1)..Self::rule_ofs(z + 1))
+ self.axis_width(axis, rule_ofs(z + 1)..rule_ofs(z + 1))
}
/// Returns the width of rule `z` along `axis`, counting in reverse order.
self.axis_width(axis, ofs..ofs + 1)
}
- /// Returns the offset in [Self::cp] of the cell with index `cell_index`.
- /// That is, if `cell_index` is 0, then the offset is 1, that of the leftmost
- /// or topmost cell; if `cell_index` is 1, then the offset is 3, that of the
- /// next cell to the right (or below); and so on. */
- fn cell_ofs(cell_index: usize) -> usize {
- cell_index * 2 + 1
- }
-
- /// Returns the offset in [Self::cp] of the rule with index `rule_index`.
- /// That is, if `rule_index` is 0, then the offset is that of the leftmost
- /// or topmost rule; if `rule_index` is 1, then the offset is that of the
- /// next rule to the right (or below); and so on.
- fn rule_ofs(rule_index: usize) -> usize {
- rule_index * 2
- }
-
/// Returns the offset in [Self::cp] of the rule with
/// index `rule_index_r`, which counts from the right side (or bottom) of the page
/// left (or up), according to `axis`, respectively. That is,
/// Returns the width of cell `z` along `axis`.
fn cell_width(&self, axis: Axis2, z: usize) -> usize {
- let ofs = Self::cell_ofs(z);
+ let ofs = cell_ofs(z);
self.axis_width(axis, ofs..ofs + 1)
}
*self.cp[axis].last().unwrap()
}
- /// XXX This could return a
+ /// XXX This could return a
fn get_map(&self, a: Axis2, z: usize) -> Map {
if z < self.h[a] {
Map {
}
}
-/*
+ fn new_mappings(h: Coord2, r: &Rect2) -> EnumMap<Axis2, [Map; 2]> {
+ EnumMap::from_fn(|axis| {
+ [
+ Map {
+ p0: 0,
+ t0: 0,
+ n: h[axis],
+ },
+ Map {
+ p0: h[axis],
+ t0: r[axis].start,
+ n: r[axis].len(),
+ },
+ ]
+ })
+ }
+
+ /*
fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
-
+
}*/
/// Creates and returns a new [Page] whose contents are a subregion of
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]) {
+ for z in 0..=rule_ofs(h[axis]) {
total += if z == 0 && is_edge_cutoff[axis][0] {
0
} else {
};
dcp.push(total);
}
- for z in Self::cell_ofs(z0)..=Self::cell_ofs(z1 - 1) {
+ for z in cell_ofs(z0)..=cell_ofs(z1 - 1) {
total += scp[z + 1] - scp[z];
- if z== Self::cell_ofs(z0) {
+ if z == cell_ofs(z0) {
total -= pixel0;
}
- if z == Self::cell_ofs(z1 - 1) {
+ if z == cell_ofs(z1 - 1) {
total -= pixel1;
}
dcp.push(total);
let mut z = 0;
while z < self.n[b] {
let d = Coord2::for_axis((axis, z0), z);
-
}
}
/// Maps a contiguous range of cells from a page to the underlying table along
/// the horizontal or vertical dimension.
+#[derive(Copy, Clone)]
struct Map {
/// First ordinate in the page.
p0: usize,
// Width of header not including its rightmost rule.
let mut size = self
.page
- .axis_width(self.axis, 0..Page::rule_ofs(self.page.h[self.axis]));
+ .axis_width(self.axis, 0..rule_ofs(self.page.h[self.axis]));
// If we have a pixel offset and there is no header, then we omit
// the leftmost rule of the body. Otherwise the rendering is deceptive