Skip to content

Instantly share code, notes, and snippets.

@brenapp
Created June 30, 2022 23:57
Show Gist options
  • Save brenapp/0603d2c5b97c2ef26d49e25f4e25c515 to your computer and use it in GitHub Desktop.
Save brenapp/0603d2c5b97c2ef26d49e25f4e25c515 to your computer and use it in GitHub Desktop.
const DIV: u32 = 8;
pub fn tesselate(
image: &DynamicImage,
sites: &Vec<(u32, u32)>,
) -> image::ImageBuffer<Rgb<u8>, Vec<u8>> {
let (width, height) = image.dimensions();
// Create a mapping between a voronoi region and color
let mut site_colors: HashMap<(u32, u32), Rgb<u8>> = HashMap::new();
for (x, y) in sites {
let pixel = image.get_pixel(*x, *y);
site_colors.insert((*x, *y), pixel.to_rgb());
}
// Location sensitive hash
#[inline]
fn hash((x, y): (u32, u32)) -> (u32, u32) {
let bucket_x = x / DIV;
let bucket_y = y / DIV;
(bucket_x, bucket_y)
}
#[inline]
fn closest((x, y): (u32, u32), points: &Vec<(u32, u32)>) -> (u32, u32) {
let mut min_dist = u64::MAX;
let mut min_site = (0u32, 0u32);
for site in points {
let d = dist((x, y), *site);
if d <= min_dist {
min_dist = d;
min_site = *site;
}
}
min_site
}
let mut map: HashMap<(i64, i64), Vec<(u32, u32)>> = HashMap::new();
for (x, y) in sites {
let (hx, hy) = hash((*x, *y));
let hx: i64 = hx.into();
let hy: i64 = hy.into();
// Put this candidate in all of the neighboring buckets. this gets pretty close to the way
// there of the exact
let hashes = [
(hx + 0, hy + 0),
(hx + 0, hy + 1),
(hx + 0, hy - 1),
(hx + 1, hy + 0),
(hx + 1, hy + 1),
(hx + 1, hy - 1),
(hx - 1, hy + 0),
(hx - 1, hy + 1),
(hx - 1, hy - 1),
];
for h in hashes {
if map.contains_key(&h) {
map.get_mut(&h).unwrap().push((*x, *y));
} else {
map.insert(h, vec![(*x, *y)]);
}
}
}
// For each pixel, find the closet site, and color appropriately
let mut buffer = image::ImageBuffer::new(width, height);
for (x, y, pixel) in buffer.enumerate_pixels_mut() {
// first check for hash candidates
let (hx, hy) = hash((x, y));
let hx: i64 = hx.into();
let hy: i64 = hy.into();
let candidates = if let Some(can) = map.get(&(hx, hy)) {
if can.len() > 0 {
can
} else {
sites
}
} else {
sites
};
// let candidates = sites;
let site = closest((x, y), candidates);
let color = site_colors.get(&site).unwrap();
*pixel = *color;
}
buffer
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment