123 lines
3.5 KiB
Rust
123 lines
3.5 KiB
Rust
use rand::prelude::ThreadRng;
|
|
use rand::Rng;
|
|
use serde_derive::{Deserialize, Serialize};
|
|
use std::fmt;
|
|
use std::ops::{Add, AddAssign, Div, Sub, SubAssign};
|
|
|
|
///
|
|
/// a struct for an RGB color
|
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)]
|
|
pub struct PixelColor {
|
|
pub(crate) red: u8,
|
|
pub(crate) green: u8,
|
|
pub(crate) blue: u8,
|
|
}
|
|
|
|
impl fmt::Display for PixelColor {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "[{},{},{}]", self.red, self.green, self.blue)
|
|
}
|
|
}
|
|
|
|
impl Div<u8> for PixelColor {
|
|
type Output = PixelColor;
|
|
|
|
fn div(self, rhs: u8) -> Self::Output {
|
|
PixelColor {
|
|
red: self.red / rhs,
|
|
green: self.green / rhs,
|
|
blue: self.blue / rhs,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Add for PixelColor {
|
|
type Output = Self;
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
PixelColor {
|
|
red: self.red.saturating_add(rhs.red),
|
|
green: self.green.saturating_add(rhs.green),
|
|
blue: self.blue.saturating_add(rhs.blue),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Sub<u8> for PixelColor {
|
|
type Output = Self;
|
|
|
|
fn sub(self, rhs: u8) -> Self::Output {
|
|
PixelColor {
|
|
red: self.red.saturating_sub(rhs),
|
|
green: self.green.saturating_sub(rhs),
|
|
blue: self.blue.saturating_sub(rhs),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SubAssign for PixelColor {
|
|
fn sub_assign(&mut self, rhs: Self) {
|
|
self.red = self.red.saturating_sub(rhs.red);
|
|
self.green = self.red.saturating_sub(rhs.green);
|
|
self.blue = self.red.saturating_sub(rhs.blue);
|
|
}
|
|
}
|
|
|
|
impl AddAssign for PixelColor {
|
|
fn add_assign(&mut self, rhs: Self) {
|
|
self.red = self.red.saturating_add(rhs.red);
|
|
self.green = self.red.saturating_add(rhs.green);
|
|
self.blue = self.red.saturating_add(rhs.blue);
|
|
}
|
|
}
|
|
|
|
impl PixelColor {
|
|
/// apply random delta to pixel and return computed value
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `pixel`: pixel to apply delta
|
|
/// * `stability`: amplitude of delta between 0 and `stability`
|
|
///
|
|
/// returns: PixelColor
|
|
///
|
|
pub fn random_delta(&self, stability: usize, rng: &mut ThreadRng) -> PixelColor {
|
|
let minus_interval = -(stability as i8);
|
|
let max_interval = stability as i8;
|
|
let red_delta = rng.gen_range(minus_interval..max_interval) as i8;
|
|
let green_delta = rng.gen_range(minus_interval..max_interval) as i8;
|
|
let blue_delta = rng.gen_range(minus_interval..max_interval) as i8;
|
|
self.delta((red_delta, green_delta, blue_delta))
|
|
}
|
|
|
|
pub fn delta(&self, delta: (i8, i8, i8)) -> PixelColor {
|
|
let (red, green, blue) = delta;
|
|
|
|
let red = self.red as i16 + red as i16;
|
|
let green = self.green as i16 + green as i16;
|
|
let blue = self.blue as i16 + blue as i16;
|
|
|
|
let bound = |color: i16| {
|
|
if color < 0 {
|
|
0
|
|
} else if color > 255 {
|
|
255
|
|
} else {
|
|
color as u8
|
|
}
|
|
};
|
|
|
|
PixelColor {
|
|
red: bound(red),
|
|
green: bound(green),
|
|
blue: bound(blue),
|
|
}
|
|
}
|
|
|
|
pub fn is_closer(&self, pixel: PixelColor, distance: PixelColor) -> bool {
|
|
(self.red as i16 - pixel.red as i16).abs() <= distance.red as i16
|
|
&& (self.green as i16 - pixel.green as i16).abs() <= distance.green as i16
|
|
&& (self.blue as i16 - pixel.blue as i16).abs() <= distance.blue as i16
|
|
}
|
|
}
|