use super::items::MultiColItem;
use crate::layout::roundoff::exceeds_with_roundoff;
pub(super) fn balanced_buckets_height(items: &[MultiColItem], buckets: &[Vec<usize>]) -> f32 {
buckets
.iter()
.map(|bucket| bucket.iter().map(|&idx| items[idx].height).sum::<f32>())
.fold(0.0f32, f32::max)
}
pub(super) fn max_vertical_rl_item_height(items: &[MultiColItem]) -> f32 {
items.iter().map(|item| item.height).fold(0.0f32, f32::max)
}
pub(super) fn balance_columns(heights: &[f32], num_cols: usize) -> Vec<Vec<usize>> {
let n = heights.len();
if num_cols <= 1 || n == 0 {
return vec![(0..n).collect()];
}
let fits = |limit: f32| -> Option<usize> {
let mut cols_used = 1usize;
let mut col_h = 0.0f32;
for &h in heights {
if col_h > 0.0 && exceeds_with_roundoff(col_h + h, limit) {
cols_used += 1;
col_h = 0.0;
if cols_used > num_cols {
return None;
}
}
col_h += h;
}
Some(cols_used)
};
let total: f32 = heights.iter().sum();
let max_item = heights.iter().cloned().fold(0.0f32, f32::max);
let lo = max_item.max(total / num_cols as f32);
let hi = total.max(lo);
let limit = if fits(lo).is_some() {
lo
} else if lo.is_finite() && hi.is_finite() {
let mut infeasible_bits = lo.to_bits();
let mut feasible_bits = hi.to_bits();
while feasible_bits - infeasible_bits > 1 {
let mid_bits = infeasible_bits + (feasible_bits - infeasible_bits) / 2;
if fits(f32::from_bits(mid_bits)).is_some() {
feasible_bits = mid_bits;
} else {
infeasible_bits = mid_bits;
}
}
f32::from_bits(feasible_bits)
} else {
hi
};
let mut buckets: Vec<Vec<usize>> = vec![Vec::new(); num_cols];
let mut col = 0usize;
let mut col_h = 0.0f32;
for (idx, &h) in heights.iter().enumerate() {
if col + 1 < num_cols && col_h > 0.0 && exceeds_with_roundoff(col_h + h, limit) {
col += 1;
col_h = 0.0;
}
buckets[col].push(idx);
col_h += h;
}
buckets
}
pub(super) fn fill_columns(heights: &[f32], num_cols: usize, fill_h: f32) -> Vec<Vec<usize>> {
let n = heights.len();
if n == 0 || fill_h <= 0.0 {
return vec![(0..n).collect()];
}
let mut buckets: Vec<Vec<usize>> = (0..num_cols.max(1)).map(|_| Vec::new()).collect();
let mut col = 0usize;
let mut col_h = 0.0f32;
for (idx, &h) in heights.iter().enumerate() {
if col_h > 0.0 && exceeds_with_roundoff(col_h + h, fill_h) {
col += 1;
col_h = 0.0;
if col == buckets.len() {
buckets.push(Vec::new());
}
}
buckets[col].push(idx);
col_h += h;
}
buckets
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sequential_overflow_extends_past_the_specified_column_count() {
assert_eq!(
fill_columns(&[60.0, 60.0, 60.0], 2, 100.0),
vec![vec![0], vec![1], vec![2]]
);
}
}