Я не могу найти библиотеку, которая реализует какой-то итератор RGB, который выполняет итерацию от красного (255-0-0) к желтому (255-255-0) и т. д. Возможно ли это реализовать?
Его структура должна реализовывать Iterator<Item=(u8,u8,u8)>
и, возможно, содержать max_iters
для изменения ее длины.
Ища функции/методы в библиотеках, я не нашел их реализации.
(255-0-0)
, (f64,f64,f64)
- обычно f64
находится от 0.0
до 1.0
. Только цвета на основе uint8_t
от 0
до 255
.
ой, я имел в виду (u8, u8, u8)
Я сделал реализацию:
pub struct RainbowIterator {
max: usize,
current: usize,
}
impl RainbowIterator {
pub fn new(max: usize) -> Self {
Self { max, current: 0 }
}
}
impl Iterator for RainbowIterator {
type Item = (u8, u8, u8);
fn next(&mut self) -> Option<Self::Item> {
let mut ret = (255, 255, 255);
if self.current >= self.max {
return None;
}
let progress: f64 = self.current as f64 / self.max as f64;
let subprogress = progress % (1.0 / 6.0);
let phase = (progress * 6.0) as u8;
if phase == 0 {
ret.2 *= 0;
ret.1 = (255.0 * progress) as u8;
} else if phase == 1 {
ret.2 *= 0;
ret.0 = 255 - (255.0 * progress) as u8;
} else if phase == 2 {
ret.0 *= 0;
ret.2 = (255.0 * progress) as u8;
} else if phase == 3 {
ret.0 *= 0;
ret.1 = 255 - (255.0 * progress) as u8;
} else if phase == 4 {
ret.1 *= 0;
ret.0 = (255.0 * progress) as u8;
} else {
ret.1 *= 0;
ret.2 = 255 - (255.0 * progress) as u8;
}
self.current += 1;
return Some(ret);
}
}
Вы можете использовать совпадение вместо if else...
Эти компоненты RGB представляют собой отдельные циклические треугольные волны с разными смещениями, ограниченными от 0 до 255. Небольшая арифметика дает нам относительно компактную формулу: используйте модуль для получения циклов, абсолютное значение дает нам треугольную волну, .min()
и .max()
фиксируют значение.
struct Rainbow(isize);
impl Rainbow {
fn eval(&self, offset: isize) -> u8 {
let x = 255 * 2 - ((self.0 + offset * 255) % (255 * 6) - 255 * 3).abs();
x.min(255).max(0) as u8
}
}
impl Iterator for Rainbow {
type Item = (u8, u8, u8);
fn next(&mut self) -> Option<Self::Item> {
let rgb = (self.eval(3), self.eval(1), self.eval(5));
self.0 += 1;
Some(rgb)
}
}
fn main() {
for (r, g, b) in Rainbow(0).take(1024) {
println!("({r}, {g}, {b})");
}
}
Я принимаю это как ответ, так как я неправильно набрал (u8, u8, u8) как (f64, f64, f64), но я бы принял ответ кмдреко, если бы я на самом деле имел в виду (f64, f64, f64)
Идея состоит в том, чтобы просто увеличить значение «оттенка» и использовать функцию для сопоставления HSV (или аналогичного цветового пространства) с RGB. Функция преобразования адаптирована из здесь. Затем вы можете реализовать итератор следующим образом:
fn hsv_to_rgb(h: f64, s: f64, v: f64) -> (f64, f64, f64) {
let i = (h * 6.0).floor();
let f = h * 6.0 - i;
let p = v * (1.0 - s);
let q = v * (1.0 - f * s);
let t = v * (1.0 - (1.0 - f) * s);
match (i as u32) % 6 {
0 => (v, t, p),
1 => (q, v, p),
2 => (p, v, t),
3 => (p, q, v),
4 => (t, p, v),
_ => (v, p, q),
}
}
fn rainbow_wheel(steps: usize) -> impl Iterator<Item = (f64, f64, f64)> {
(0..steps).map(move |i| hsv_to_rgb(i as f64 / (steps - 1) as f64, 1.0, 1.0))
}
Я думаю, что было бы довольно просто перебрать HSV (или подобное цветовое пространство); просто увеличьте значение оттенка и используйте функцию
hsv_to_rgb
для сопоставления с цветовым пространством RGB.