use crate::BinaryImage;
pub fn erode_interior(image: &BinaryImage, depth: usize) -> BinaryImage {
let w = image.width;
let h = image.height;
let n = w * h;
let mut bin: Vec<bool> = (0..n).map(|i| image.get_pixel(i % w, i / w)).collect();
let mut keep = vec![false; n];
for _ in 0..depth {
let mut cur = vec![false; n];
let mut any = false;
for i in 0..n {
if bin[i] && is_4boundary(&bin, i, w, h) {
cur[i] = true;
keep[i] = true;
any = true;
}
}
if !any { break; }
for i in 0..n {
if cur[i] { bin[i] = false; }
}
}
let mut out = BinaryImage::new_w_h(w, h);
for i in 0..n {
if keep[i] { out.set_pixel(i % w, i / w, true); }
}
out
}
#[inline]
fn is_4boundary(bin: &[bool], i: usize, w: usize, h: usize) -> bool {
let x = i % w;
let y = i / w;
(x == 0 || !bin[i - 1]) ||
(x == w - 1 || !bin[i + 1]) ||
(y == 0 || !bin[i - w]) ||
(y == h - 1 || !bin[i + w])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn peel1_solid_3x3_removes_center() {
let image = BinaryImage::from_string(
"***\n\
***\n\
***\n");
let result = erode_interior(&image, 1);
assert_eq!(result.to_string(),
"***\n\
*-*\n\
***\n");
}
#[test]
fn peel2_solid_5x5_removes_center_only() {
let image = BinaryImage::from_string(
"*****\n\
*****\n\
*****\n\
*****\n\
*****\n");
let result = erode_interior(&image, 2);
assert_eq!(result.to_string(),
"*****\n\
*****\n\
**-**\n\
*****\n\
*****\n");
}
#[test]
fn peel_thin_stroke_unchanged() {
let image = BinaryImage::from_string("*****\n");
assert_eq!(erode_interior(&image, 1).to_string(), "*****\n");
assert_eq!(erode_interior(&image, 3).to_string(), "*****\n");
}
#[test]
fn peel_thin_stroke_2px_unchanged() {
let image = BinaryImage::from_string(
"*****\n\
*****\n");
assert_eq!(erode_interior(&image, 1).to_string(),
"*****\n\
*****\n");
}
#[test]
fn peel0_returns_original() {
let image = BinaryImage::from_string(
"***\n\
***\n\
***\n");
let result = erode_interior(&image, 0);
assert_eq!(result.to_string(),
"---\n\
---\n\
---\n");
}
#[test]
fn peel_more_than_depth_returns_full_shell() {
let image = BinaryImage::from_string(
"***\n\
***\n\
***\n");
let result = erode_interior(&image, 5);
assert_eq!(result.to_string(),
"***\n\
***\n\
***\n");
}
#[test]
fn peel2_fill_star_matches_stroke_shell() {
let fill = BinaryImage::from_string(concat!(
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
"---------------**---------------\n",
"---------------**---------------\n",
"--------------****--------------\n",
"--------------****--------------\n",
"-------------*****--------------\n",
"-------------******-------------\n",
"-------------******-------------\n",
"------------********------------\n",
"---**************************---\n",
"----************************----\n",
"-----**********************-----\n",
"------********************------\n",
"-------******************-------\n",
"---------**************---------\n",
"----------************----------\n",
"----------************----------\n",
"---------**************---------\n",
"---------**************---------\n",
"---------**************---------\n",
"---------******--******---------\n",
"---------*****----*****---------\n",
"--------****--------****--------\n",
"--------**------------**--------\n",
"--------*--------------*--------\n",
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
));
let result = erode_interior(&fill, 2);
assert_eq!(result.to_string(), concat!(
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
"---------------**---------------\n",
"---------------**---------------\n",
"--------------****--------------\n",
"--------------****--------------\n",
"-------------**-**--------------\n",
"-------------**--**-------------\n",
"-------------**--**-------------\n",
"------------**----**------------\n",
"---**********------**********---\n",
"----********--------********----\n",
"-----**------------------**-----\n",
"------***--------------***------\n",
"-------***------------***-------\n",
"---------**----------**---------\n",
"----------**--------**----------\n",
"----------**--------**----------\n",
"---------**----------**---------\n",
"---------**----**----**---------\n",
"---------**---****---**---------\n",
"---------**-***--***-**---------\n",
"---------*****----*****---------\n",
"--------****--------****--------\n",
"--------**------------**--------\n",
"--------*--------------*--------\n",
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
"--------------------------------\n",
));
}
}