collections::HashMap,
fmt::{Debug, Display, Write},
io::Read,
- iter::{once, repeat},
+ iter::{once, repeat, repeat_n},
ops::{Index, IndexMut, Not, Range, RangeInclusive},
str::{from_utf8, FromStr, Utf8Error},
sync::{Arc, OnceLock, Weak},
/// only one or even (pathologically) none.
children: Vec<Category>,
- /// Display a label for the group itself?
- pub show_label: bool,
-
- show_label_in_corner: bool,
+ /// Position for the group's own label, if any.
+ pub show_label: Option<LabelPosition>,
}
#[derive(Clone)]
self.hide_all_labels = true;
self
}
- fn build(
- mut self,
- level: usize,
- top_index: usize,
- dimension_labels_in_corner: bool,
- ) -> Dimension {
+ fn build(mut self, level: usize, top_index: usize, label_position: LabelPosition) -> Dimension {
let mut leaves = Vec::with_capacity(self.len);
- self.root.assign_label_depth(dimension_labels_in_corner);
- let root = self
- .root
- .build(dimension_labels_in_corner, None, &mut leaves);
+ self.root.assign_label_depth(label_position);
+ let root = self.root.build(label_position, None, &mut leaves);
Dimension {
axis: self.axis,
level,
show_label: bool,
label_depth: usize,
extra_depth: usize,
- show_label_in_corner: bool,
+ label_position: Option<LabelPosition>,
}
impl GroupBuilder {
show_label: true,
label_depth: 0,
extra_depth: 0,
- show_label_in_corner: false,
+ label_position: None,
}
}
pub fn push<T>(&mut self, value: T)
fn len(&self) -> usize {
self.children.iter().map(|category| category.len()).sum()
}
- fn assign_label_depth(&mut self, dimension_labels_in_corner: bool) {
+ fn assign_label_depth(&mut self, label_position: LabelPosition) {
for child in self.children.iter_mut() {
- child.assign_label_depth(false);
+ child.assign_label_depth(label_position);
}
let depth = self
.children
}
child.set_label_depth(depth);
}
- self.show_label_in_corner = self.show_label && dimension_labels_in_corner;
- self.label_depth = if self.show_label && !self.show_label_in_corner {
- depth + 1
- } else {
- depth
- };
+ self.label_position = self.show_label.then_some(label_position);
+ self.label_depth = depth + (self.label_position == Some(LabelPosition::Nested)) as usize;
}
fn distribute_extra_depth(&mut self, extra_depth: usize) {
if self.children.is_empty() {
}
fn build(
self,
- dimension_labels_in_corner: bool,
+ label_position: LabelPosition,
parent: Option<Weak<Group>>,
leaves: &mut Vec<Arc<Leaf>>,
) -> Arc<Group> {
.children
.into_iter()
.enumerate()
- .map(|(group_index, c)| {
- c.build(
- dimension_labels_in_corner,
- weak.clone(),
- group_index,
- leaves,
- )
- })
+ .map(|(group_index, c)| c.build(label_position, weak.clone(), group_index, leaves))
.collect(),
- show_label: self.show_label,
- show_label_in_corner: self.show_label && dimension_labels_in_corner,
+ show_label: self.show_label.then_some(label_position),
})
}
}
}
fn build(
self,
- dimension_labels_in_corner: bool,
+ label_position: LabelPosition,
parent: Weak<Group>,
group_index: usize,
leaves: &mut Vec<Arc<Leaf>>,
) -> Category {
match self {
Self::Group(group) => {
- Category::Group(group.build(dimension_labels_in_corner, Some(parent), leaves))
+ Category::Group(group.build(label_position, Some(parent), leaves))
}
Self::Leaf {
name,
}
}
}
- fn assign_label_depth(&mut self, dimension_labels_in_corner: bool) {
+ fn assign_label_depth(&mut self, label_position: LabelPosition) {
match self {
- CategoryBuilder::Group(group) => group.assign_label_depth(dimension_labels_in_corner),
+ CategoryBuilder::Group(group) => group.assign_label_depth(label_position),
CategoryBuilder::Leaf { label_depth, .. } => {
*label_depth = 1;
}
}
}
+impl From<GroupBuilder> for CategoryBuilder {
+ fn from(value: GroupBuilder) -> Self {
+ Self::Group(Box::new(value))
+ }
+}
+
impl From<Value> for CategoryBuilder {
fn from(name: Value) -> Self {
Self::Leaf {
let mut axes = EnumMap::from_fn(|_key| Vec::with_capacity(self.dimensions.len()));
for (top_index, d) in self.dimensions.into_iter().enumerate() {
let axis = d.axis;
- let d = Arc::new(d.build(
- axes[axis].len(),
- top_index,
- axis == Axis3::Y && row_label_position == RowLabelPosition::Corner && !corner_text,
- ));
+ let label_position = if axis == Axis3::Y && !corner_text {
+ self.look.row_label_position
+ } else {
+ LabelPosition::Nested
+ };
+ let d = Arc::new(d.build(axes[axis].len(), top_index, label_position));
axes[d.axis].push(d.clone());
dimensions.push(d);
}
}
});
table.cells = self.cells;
+ table.current_layer = repeat_n(0, table.axes[Axis3::Z].dimensions.len()).collect();
table
}
}
fn show_label(&self) -> bool {
match self {
- Category::Group(group) => group.show_label,
+ Category::Group(group) => group.show_label.is_some(),
Category::Leaf(_) => true,
}
}
/// Whether to hide rows or columns whose cells are all empty.
pub omit_empty: bool,
- pub row_label_position: RowLabelPosition,
+ pub row_label_position: LabelPosition,
/// Ranges of column widths in the two heading regions, in 1/96" units.
pub heading_widths: EnumMap<HeadingRegion, RangeInclusive<usize>>,
Self {
name: None,
omit_empty: true,
- row_label_position: RowLabelPosition::default(),
+ row_label_position: LabelPosition::default(),
heading_widths: EnumMap::from_fn(|region| match region {
HeadingRegion::RowHeadings => 36..=72,
HeadingRegion::ColumnHeadings => 36..=120,
}
}
+/// Position for group labels.
#[derive(Copy, Clone, Debug, Default, Deserialize, PartialEq, Eq)]
-pub enum RowLabelPosition {
+pub enum LabelPosition {
+ /// Hierarachically enclosing the categories.
+ ///
+ /// For column labels, group labels appear above the categories. For row
+ /// labels, group labels appear to the left of the categories.
+ ///
+ /// ```text
+ /// +---------+----------+
+ /// | | columns |
+ /// +----+----+----+----+ +------+--+----------+
+ /// | | nested | | |a1|...data...|
+ /// | +----+----+----+ |nested|a2|...data...|
+ /// | | a1 | a2 | a3 | | |a3|...data...|
+ /// +----+----+----+----+ +------+--+----------+
+ /// | |data|data|data|
+ /// | | . | . | . |
+ /// |rows| . | . | . |
+ /// | | . | . | . |
+ /// +----+----+----+----+
+ /// ```
#[serde(rename = "nested")]
Nested,
+ /// In the corner (row labels only).
+ ///
+ /// ```text
+ /// +------+----------+
+ /// |corner| columns |
+ /// +------+----------+
+ /// | a1|...data...|
+ /// | a2|...data...|
+ /// | a3|...data...|
+ /// +------+----------+
+ /// ```
#[default]
#[serde(rename = "inCorner")]
Corner,