crop.rs
3.6 kB · rust · 118 lines
1use crate::two::Cell2d;2use mrlycore::tensor::Tensor;34/// Crops a frame sequence to the centred square bounding every cell ever alive.5pub fn crop(grids: &[Cell2d]) -> Vec<Cell2d> {6 if grids.is_empty() {7 return Vec::new();8 }9 let shape = &grids[0].types().shape;10 let (h, w) = (shape[0], shape[1]);11 let mut any = vec![false; h * w];12 let mut found = false;13 for grid in grids {14 for (i, &v) in grid.types().bytes().iter().enumerate() {15 if v == 1 {16 any[i] = true;17 found = true;18 }19 }20 }21 if !found {22 return grids.to_vec();23 }24 let (mut rmin, mut rmax, mut cmin, mut cmax) = (h, 0usize, w, 0usize);25 for r in 0..h {26 for c in 0..w {27 if any[r * w + c] {28 rmin = rmin.min(r);29 rmax = rmax.max(r);30 cmin = cmin.min(c);31 cmax = cmax.max(c);32 }33 }34 }35 let bh = rmax - rmin + 1;36 let bw = cmax - cmin + 1;37 if bh == h && bw == w {38 return grids.to_vec();39 }40 let side = bh.max(bw);41 let pad_top = (side - bh) / 2;42 let pad_left = (side - bw) / 2;43 grids44 .iter()45 .map(|grid| {46 let src = grid.types();47 let mut out = Tensor::new(vec![side, side]);48 for r in 0..bh {49 for c in 0..bw {50 let v = src.get(&[rmin + r, cmin + c]);51 if v != 0 {52 out.set(&[pad_top + r, pad_left + c], v);53 }54 }55 }56 Cell2d::new(out)57 })58 .collect()59}6061/// Tiles every frame n by n to reach at least min_canvas a side, unchanged when already there.62pub fn tessellate(grids: &[Cell2d], min_canvas: usize) -> Vec<Cell2d> {63 if grids.is_empty() || min_canvas == 0 {64 return grids.to_vec();65 }66 let shape = &grids[0].types().shape;67 let side = shape[0].max(shape[1]);68 if side == 0 || side >= min_canvas {69 return grids.to_vec();70 }71 let n = min_canvas.div_ceil(side);72 grids.iter().map(|grid| grid.clone().tile(n, n)).collect()73}7475#[cfg(test)]76mod tests {77 use super::*;78 #[test]79 fn crops_to_bounding_square() {80 let mut t = Tensor::new(vec![5, 5]);81 t.set(&[2, 3], 1);82 let cropped = crop(&[Cell2d::new(t)]);83 assert_eq!(cropped[0].types().shape, vec![1, 1]);84 assert_eq!(cropped[0].types().get(&[0, 0]), 1);85 }86 #[test]87 fn empty_is_returned_unchanged() {88 let t = Tensor::new(vec![4, 4]);89 let cropped = crop(&[Cell2d::new(t)]);90 assert_eq!(cropped[0].types().shape, vec![4, 4]);91 }92 #[test]93 fn full_grid_is_unchanged() {94 let mut t = Tensor::new(vec![3, 3]);95 t.set(&[0, 0], 1);96 t.set(&[2, 2], 1);97 let cropped = crop(&[Cell2d::new(t)]);98 assert_eq!(cropped[0].types().shape, vec![3, 3]);99 }100 #[test]101 fn tessellate_tiles_small_runs_up_to_the_canvas() {102 let mut t = Tensor::new(vec![5, 5]);103 t.set(&[1, 2], 1);104 let grids = vec![Cell2d::new(t)];105 let tiled = tessellate(&grids, 12);106 assert_eq!(tiled[0].types().shape, vec![15, 15]);107 assert_eq!(tiled[0].types().get(&[1, 2]), 1);108 assert_eq!(tiled[0].types().get(&[6, 7]), 1);109 assert_eq!(tiled[0].types().sum(), 9);110 }111 #[test]112 fn tessellate_leaves_big_runs_alone() {113 let grids = vec![Cell2d::new(Tensor::new(vec![5, 5]))];114 assert_eq!(tessellate(&grids, 5)[0].types().shape, vec![5, 5]);115 assert_eq!(tessellate(&grids, 0)[0].types().shape, vec![5, 5]);116 assert!(tessellate(&[], 12).is_empty());117 }118}