scaled rainbow
This commit is contained in:
parent
6d22aa0065
commit
8155efe2e8
1 changed files with 70 additions and 33 deletions
91
src/main.rs
91
src/main.rs
|
@ -1,4 +1,7 @@
|
||||||
use std::{collections::HashMap, env};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
env,
|
||||||
|
};
|
||||||
|
|
||||||
use hsl::HSL;
|
use hsl::HSL;
|
||||||
use image::{GenericImage, GenericImageView, Pixel, Rgb, Rgba};
|
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 in_path = args.next().expect("i need a PATH. now!");
|
||||||
let start_x = args
|
let start_x = args
|
||||||
.next()
|
.next()
|
||||||
.and_then(|n| n.parse::<i32>().ok())
|
.and_then(|n| n.parse::<u32>().ok())
|
||||||
.expect("start x");
|
.expect("start x");
|
||||||
let start_y = args
|
let start_y = args
|
||||||
.next()
|
.next()
|
||||||
.and_then(|n| n.parse::<i32>().ok())
|
.and_then(|n| n.parse::<u32>().ok())
|
||||||
.expect("start y");
|
.expect("start y");
|
||||||
let out_path = args.next().expect("out path");
|
let out_path = args.next().expect("out path");
|
||||||
|
|
||||||
let mut img = image::open(in_path).expect("i wanna open an image");
|
let mut img = image::open(in_path).expect("i wanna open an image");
|
||||||
|
|
||||||
// let's just hsl-rotate floodfill first
|
// get length of path, find covered
|
||||||
let mut covered = HashMap::new();
|
// jk we can't precompute covered
|
||||||
covered.insert((start_x, start_y), HSL::from_rgb(&[255, 0, 0]));
|
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 {
|
loop {
|
||||||
let mut front = HashMap::new();
|
let mut new_front = HashSet::new();
|
||||||
for ((x, y), hsl) in &covered {
|
for (x, y) in &front {
|
||||||
for (dx, dy) in (-1..=1).cartesian_product(-1..=1) {
|
for (x, y) in adjacent(*x, *y) {
|
||||||
let (x, y) = (x + dx, y + dy);
|
if img.in_bounds(x, y)
|
||||||
let Ok(x_idx) = x.try_into() else {
|
&& !is_dark(img.get_pixel(x, y).to_rgb())
|
||||||
break;
|
&& !covered.contains(&(x, y))
|
||||||
};
|
{
|
||||||
let Ok(y_idx) = y.try_into() else {
|
new_front.insert((x, y));
|
||||||
break;
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if img.in_bounds(x_idx, y_idx)
|
if new_front.is_empty() {
|
||||||
&& !is_dark(img.get_pixel(x_idx, y_idx).to_rgb())
|
break;
|
||||||
&& !covered.contains_key(&(x, y))
|
}
|
||||||
|
|
||||||
|
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;
|
let mut color = *hsl;
|
||||||
color.h += 1.;
|
color.h += change_per_step;
|
||||||
if color.h >= 360. {
|
if color.h >= 360. {
|
||||||
color.h = 0.;
|
color.h = 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (r, g, b) = color.to_rgb();
|
let (r, g, b) = color.to_rgb();
|
||||||
|
new_front.insert((x, y), color);
|
||||||
img.put_pixel(x_idx, y_idx, Rgba([r, g, b, 255]));
|
img.put_pixel(x, y, Rgba([r, g, b, 255]));
|
||||||
front.insert((x, y), color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if front.is_empty() {
|
if new_front.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
covered.extend(front);
|
covered.extend(new_front.keys());
|
||||||
|
front = new_front;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.save(out_path).expect("tried to save :(");
|
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;
|
let Rgb([r, g, b]) = pixel;
|
||||||
r < 127 && g < 127 && b < 127
|
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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue