kenshii 0.2.0

Disk usage analyzer written in Rust
use ratatui::layout::Rect as CellRect;

#[derive(Clone, Copy, Debug)]
struct FRect {
    x: f64,
    y: f64,
    w: f64,
    h: f64,
}

fn worst_ratio(row_areas: &[f64], length: f64) -> f64 {
    if length <= 0.0 {
        return f64::INFINITY;
    }
    let sum: f64 = row_areas.iter().sum();
    if sum <= 0.0 {
        return f64::INFINITY;
    }
    let thickness = sum / length;
    if thickness <= 0.0 {
        return f64::INFINITY;
    }
    row_areas
        .iter()
        .map(|&a| {
            let side = a / thickness;
            if side <= 0.0 {
                f64::INFINITY
            } else if thickness > side {
                thickness / side
            } else {
                side / thickness
            }
        })
        .fold(0.0_f64, f64::max)
}

fn layout_row(row: &[(usize, f64)], rect: FRect, out: &mut [FRect]) -> FRect {
    let row_sum: f64 = row.iter().map(|(_, a)| *a).sum();

    if rect.w <= rect.h {
        let thickness = if rect.w > 0.0 {
            (row_sum / rect.w).min(rect.h)
        } else {
            0.0
        };
        let mut x = rect.x;
        for &(idx, area) in row {
            let item_w = if thickness > 0.0 {
                area / thickness
            } else {
                0.0
            };
            out[idx] = FRect {
                x,
                y: rect.y,
                w: item_w,
                h: thickness,
            };
            x += item_w;
        }
        FRect {
            x: rect.x,
            y: rect.y + thickness,
            w: rect.w,
            h: (rect.h - thickness).max(0.0),
        }
    } else {
        let thickness = if rect.h > 0.0 {
            (row_sum / rect.h).min(rect.w)
        } else {
            0.0
        };
        let mut y = rect.y;
        for &(idx, area) in row {
            let item_h = if thickness > 0.0 {
                area / thickness
            } else {
                0.0
            };
            out[idx] = FRect {
                x: rect.x,
                y,
                w: thickness,
                h: item_h,
            };
            y += item_h;
        }
        FRect {
            x: rect.x + thickness,
            y: rect.y,
            w: (rect.w - thickness).max(0.0),
            h: rect.h,
        }
    }
}

fn squarify_rec(items: &[(usize, f64)], rect: FRect, out: &mut [FRect]) {
    if items.is_empty() {
        return;
    }
    if items.len() == 1 || rect.w <= 0.0 || rect.h <= 0.0 {
        for &(idx, _) in items {
            out[idx] = rect;
        }
        return;
    }

    let length = rect.w.min(rect.h);

    let mut i = 1;
    let mut best = worst_ratio(&[items[0].1], length);
    while i < items.len() {
        let row_areas: Vec<f64> = items[..=i].iter().map(|(_, a)| *a).collect();
        let candidate = worst_ratio(&row_areas, length);
        if candidate > best {
            break;
        }
        best = candidate;
        i += 1;
    }

    let row = &items[..i];
    let rest = &items[i..];
    let remainder = layout_row(row, rect, out);
    squarify_rec(rest, remainder, out);
}

pub fn layout(sizes: &[u64], area: CellRect) -> Vec<CellRect> {
    let n = sizes.len();
    if n == 0 || area.width == 0 || area.height == 0 {
        return vec![CellRect::new(0, 0, 0, 0); n];
    }

    let total_size: u64 = sizes.iter().sum();
    let floor = if total_size > 0 {
        (total_size as f64 * 0.001).max(1.0)
    } else {
        1.0
    };
    let weights: Vec<f64> = sizes.iter().map(|&s| (s as f64).max(floor)).collect();
    let total_weight: f64 = weights.iter().sum();

    let cell_area = area.width as f64 * area.height as f64;
    let scale = if total_weight > 0.0 {
        cell_area / total_weight
    } else {
        0.0
    };
    let scaled: Vec<f64> = weights.iter().map(|w| w * scale).collect();

    let items: Vec<(usize, f64)> = scaled.iter().copied().enumerate().collect();
    let mut out = vec![
        FRect {
            x: 0.0,
            y: 0.0,
            w: 0.0,
            h: 0.0
        };
        n
    ];
    let root = FRect {
        x: area.x as f64,
        y: area.y as f64,
        w: area.width as f64,
        h: area.height as f64,
    };
    squarify_rec(&items, root, &mut out);

    let min_x = area.x as f64;
    let min_y = area.y as f64;
    let max_x = (area.x + area.width) as f64;
    let max_y = (area.y + area.height) as f64;

    out.iter()
        .map(|r| {
            let x0 = r.x.round().clamp(min_x, max_x);
            let y0 = r.y.round().clamp(min_y, max_y);
            let x1 = (r.x + r.w).round().clamp(min_x, max_x);
            let y1 = (r.y + r.h).round().clamp(min_y, max_y);
            CellRect {
                x: x0 as u16,
                y: y0 as u16,
                width: (x1 - x0).max(0.0) as u16,
                height: (y1 - y0).max(0.0) as u16,
            }
        })
        .collect()
}