scaled rainbow

This commit is contained in:
mehbark 2024-12-10 14:22:41 -05:00
parent 6d22aa0065
commit 8155efe2e8

View file

@ -1,4 +1,7 @@
use std::{collections::HashMap, env};
use std::{
collections::{HashMap, HashSet},
env,
};
use hsl::HSL;
use image::{GenericImage, GenericImageView, Pixel, Rgb, Rgba};
@ -11,56 +14,78 @@ fn main() {
let in_path = args.next().expect("i need a PATH. now!");
let start_x = args
.next()
.and_then(|n| n.parse::<i32>().ok())
.and_then(|n| n.parse::<u32>().ok())
.expect("start x");
let start_y = args
.next()
.and_then(|n| n.parse::<i32>().ok())
.and_then(|n| n.parse::<u32>().ok())
.expect("start y");
let out_path = args.next().expect("out path");
let mut img = image::open(in_path).expect("i wanna open an image");
// let's just hsl-rotate floodfill first
let mut covered = HashMap::new();
covered.insert((start_x, start_y), HSL::from_rgb(&[255, 0, 0]));
// get length of path, find covered
// jk we can't precompute covered
let mut covered: HashSet<(u32, u32)> = HashSet::new();
let mut front = HashSet::new();
front.insert((start_x, start_y));
let mut step: u32 = 0;
// TODO: hold front and covered. duh
// scale based on longest path (ez)
loop {
let mut front = HashMap::new();
for ((x, y), hsl) in &covered {
for (dx, dy) in (-1..=1).cartesian_product(-1..=1) {
let (x, y) = (x + dx, y + dy);
let Ok(x_idx) = x.try_into() else {
break;
};
let Ok(y_idx) = y.try_into() else {
break;
};
if img.in_bounds(x_idx, y_idx)
&& !is_dark(img.get_pixel(x_idx, y_idx).to_rgb())
&& !covered.contains_key(&(x, y))
let mut new_front = HashSet::new();
for (x, y) in &front {
for (x, y) in adjacent(*x, *y) {
if img.in_bounds(x, y)
&& !is_dark(img.get_pixel(x, y).to_rgb())
&& !covered.contains(&(x, y))
{
let mut color = *hsl;
color.h += 1.;
if color.h >= 360. {
color.h = 0.;
}
let (r, g, b) = color.to_rgb();
img.put_pixel(x_idx, y_idx, Rgba([r, g, b, 255]));
front.insert((x, y), color);
new_front.insert((x, y));
}
}
}
if front.is_empty() {
if new_front.is_empty() {
break;
}
covered.extend(front);
covered.extend(new_front.iter());
front = new_front;
step += 1;
}
// color :D
let change_per_step = 360. / f64::from(step).max(1.);
let mut covered: HashSet<(u32, u32)> = HashSet::new();
let mut front = HashMap::new();
front.insert((start_x, start_y), HSL::from_rgb(&[255, 0, 0]));
loop {
let mut new_front = HashMap::new();
for ((x, y), hsl) in &front {
for (x, y) in adjacent(*x, *y) {
if img.in_bounds(x, y)
&& !is_dark(img.get_pixel(x, y).to_rgb())
&& !covered.contains(&(x, y))
{
let mut color = *hsl;
color.h += change_per_step;
if color.h >= 360. {
color.h = 0.;
}
let (r, g, b) = color.to_rgb();
new_front.insert((x, y), color);
img.put_pixel(x, y, Rgba([r, g, b, 255]));
}
}
}
if new_front.is_empty() {
break;
}
covered.extend(new_front.keys());
front = new_front;
}
img.save(out_path).expect("tried to save :(");
@ -70,3 +95,15 @@ fn is_dark(pixel: Rgb<u8>) -> bool {
let Rgb([r, g, b]) = pixel;
r < 127 && g < 127 && b < 127
}
fn adjacent(x: u32, y: u32) -> impl Iterator<Item = (u32, u32)> {
(-1..=1)
.cartesian_product(-1..=1)
.filter_map(move |(dx, dy): (i32, i32)| {
if (dx == -1 && x == 0) || (dy == -1 && y == 0) {
None
} else {
Some(((x as i32 + dx) as u32, (y as i32 + dy) as u32))
}
})
}