#[derive(Clone, Copy, Debug)]
pub(super) struct Run {
pub row: u32,
pub c0: u32,
pub c1: u32,
}
impl Run {
#[inline]
pub(super) fn len(&self) -> usize {
(self.c1 - self.c0 + 1) as usize
}
}
pub(super) struct RunRegions {
pub runs: Vec<Run>,
pub region_of_run: Vec<u32>,
pub n_regions: usize,
}
impl RunRegions {
pub(super) fn group_by_region(&self) -> (Vec<u32>, Vec<u32>) {
let mut counts = vec![0u32; self.n_regions + 1];
for ® in &self.region_of_run {
counts[reg as usize + 1] += 1;
}
for k in 1..counts.len() {
counts[k] += counts[k - 1];
}
let offsets = counts.clone();
let mut cursor = counts;
let mut order = vec![0u32; self.runs.len()];
for (i, ®) in self.region_of_run.iter().enumerate() {
order[cursor[reg as usize] as usize] = i as u32;
cursor[reg as usize] += 1;
}
(offsets, order)
}
}
pub(super) fn sweep_runs(
w: usize,
h: usize,
mut lit: impl FnMut(usize, usize) -> bool,
) -> RunRegions {
let mut parents: Vec<u32> = Vec::new();
let mut runs: Vec<Run> = Vec::new();
let mut prev: Vec<(u32, u32, u32)> = Vec::new();
let mut cur: Vec<(u32, u32, u32)> = Vec::new();
for r in 0..h {
cur.clear();
let mut start: Option<usize> = None;
for c in 0..w {
if lit(r, c) {
if start.is_none() {
start = Some(c);
}
} else if let Some(s) = start.take() {
let label = runs.len() as u32;
parents.push(label);
runs.push(Run {
row: r as u32,
c0: s as u32,
c1: (c - 1) as u32,
});
cur.push((s as u32, (c - 1) as u32, label));
}
}
if let Some(s) = start.take() {
let label = runs.len() as u32;
parents.push(label);
runs.push(Run {
row: r as u32,
c0: s as u32,
c1: (w - 1) as u32,
});
cur.push((s as u32, (w - 1) as u32, label));
}
let (mut i, mut j) = (0usize, 0usize);
while i < cur.len() && j < prev.len() {
let (cs, ce, cl) = cur[i];
let (ps, pe, pl) = prev[j];
if ce + 1 < ps {
i += 1; } else if pe + 1 < cs {
j += 1; } else {
union(&mut parents, cl, pl);
if ce < pe {
i += 1;
} else {
j += 1;
}
}
}
std::mem::swap(&mut prev, &mut cur);
}
let mut region_of_run = vec![0u32; runs.len()];
let mut id_of_root = vec![u32::MAX; parents.len()];
let mut n_regions = 0usize;
for (i, reg) in region_of_run.iter_mut().enumerate() {
let root = find(&mut parents, i as u32) as usize;
if id_of_root[root] == u32::MAX {
id_of_root[root] = n_regions as u32;
n_regions += 1;
}
*reg = id_of_root[root];
}
RunRegions {
runs,
region_of_run,
n_regions,
}
}
fn find(parents: &mut [u32], mut x: u32) -> u32 {
while parents[x as usize] != x {
let p = parents[x as usize];
parents[x as usize] = parents[p as usize]; x = parents[x as usize];
}
x
}
fn union(parents: &mut [u32], a: u32, b: u32) {
let ra = find(parents, a);
let rb = find(parents, b);
if ra != rb {
parents[ra as usize] = rb;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn regions_of(mask: &[&[u8]]) -> RunRegions {
let h = mask.len();
let w = mask[0].len();
sweep_runs(w, h, |r, c| mask[r][c] != 0)
}
#[test]
fn test_sweep_runs_basic() {
let rr = regions_of(&[
&[1, 1, 0, 0, 1],
&[0, 1, 0, 1, 0], &[0, 0, 0, 0, 0],
&[1, 0, 0, 0, 1],
]);
assert_eq!(rr.n_regions, 4);
let (offsets, order) = rr.group_by_region();
let first = rr.region_of_run[0] as usize;
assert_eq!((offsets[first + 1] - offsets[first]), 2);
let total: usize = order.iter().map(|&i| rr.runs[i as usize].len()).sum();
assert_eq!(total, 7);
}
#[test]
fn test_sweep_runs_full_row_and_empty() {
let rr = regions_of(&[&[1, 1, 1], &[1, 1, 1]]);
assert_eq!(rr.n_regions, 1);
assert_eq!(rr.runs.len(), 2);
let rr = regions_of(&[&[0, 0], &[0, 0]]);
assert_eq!(rr.n_regions, 0);
assert!(rr.runs.is_empty());
}
}