feat: basic conversion
This commit is contained in:
@@ -3,7 +3,7 @@ mod self_operations;
|
|||||||
|
|
||||||
pub type BaseNumber = i16;
|
pub type BaseNumber = i16;
|
||||||
|
|
||||||
#[derive(Eq, Clone)]
|
#[derive(Eq, Clone, Debug)]
|
||||||
pub struct RangedInt<const LOW: BaseNumber, const HIGH: BaseNumber>(BaseNumber);
|
pub struct RangedInt<const LOW: BaseNumber, const HIGH: BaseNumber>(BaseNumber);
|
||||||
|
|
||||||
impl<const LOW: BaseNumber, const HIGH: BaseNumber> RangedInt<{ LOW }, { HIGH }> {
|
impl<const LOW: BaseNumber, const HIGH: BaseNumber> RangedInt<{ LOW }, { HIGH }> {
|
||||||
@@ -13,4 +13,8 @@ impl<const LOW: BaseNumber, const HIGH: BaseNumber> RangedInt<{ LOW }, { HIGH }>
|
|||||||
pub fn new(number: BaseNumber) -> Self {
|
pub fn new(number: BaseNumber) -> Self {
|
||||||
Self(number.min(Self::HIGH).max(Self::LOW))
|
Self(number.min(Self::HIGH).max(Self::LOW))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_f32(&self) -> f32 {
|
||||||
|
self.0 as f32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ use std::{
|
|||||||
ops::{Add, Div, Mul, Sub},
|
ops::{Add, Div, Mul, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
impl<const LOW: BaseNumber, const HIGH: BaseNumber> Into<f32> for RangedInt<{ LOW }, { HIGH }> {
|
||||||
|
fn into(self) -> f32 {
|
||||||
|
self.0 as f32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<const LOW: BaseNumber, const HIGH: BaseNumber> PartialEq for RangedInt<{ LOW }, { HIGH }> {
|
impl<const LOW: BaseNumber, const HIGH: BaseNumber> PartialEq for RangedInt<{ LOW }, { HIGH }> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.0 == other.0
|
self.0 == other.0
|
||||||
|
|||||||
226
colorizer/src/formats/colors.rs
Normal file
226
colorizer/src/formats/colors.rs
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
use std::convert::From;
|
||||||
|
|
||||||
|
use crate::core::ranged::{BaseNumber, RangedInt};
|
||||||
|
|
||||||
|
pub type ColorIntensity = RangedInt<0, 255>;
|
||||||
|
pub type ColorHue = RangedInt<0, 360>;
|
||||||
|
pub type Percentage = RangedInt<0, 100>;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RGB(ColorIntensity, ColorIntensity, ColorIntensity);
|
||||||
|
pub struct HSL(ColorHue, Percentage, Percentage);
|
||||||
|
// pub struct HSV(ColorHue, Percentage, Percentage);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Color(RGB);
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
pub fn format(&self) -> String {
|
||||||
|
format!("{:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Color {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Self) -> bool {
|
||||||
|
self.0 != other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RGB {
|
||||||
|
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||||
|
Self(
|
||||||
|
ColorIntensity::new(r as BaseNumber),
|
||||||
|
ColorIntensity::new(g as BaseNumber),
|
||||||
|
ColorIntensity::new(b as BaseNumber),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for RGB {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0 && self.1 == other.1 && self.2 == other.2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Self) -> bool {
|
||||||
|
self.0 != other.0 || self.1 != other.1 || self.2 != other.2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HSL {
|
||||||
|
pub fn new(h: u16, s: u8, l: u8) -> Self {
|
||||||
|
Self(
|
||||||
|
ColorHue::new(h as i16),
|
||||||
|
Percentage::new(s as i16),
|
||||||
|
Percentage::new(l as i16),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RGB> for Color {
|
||||||
|
fn from(color: RGB) -> Self {
|
||||||
|
Self(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HSL> for Color {
|
||||||
|
fn from(color: HSL) -> Self {
|
||||||
|
Color(RGB::from(color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min_of_float_vec(vector: Vec<f32>) -> Option<f32> {
|
||||||
|
let mut min: Option<f32> = None;
|
||||||
|
|
||||||
|
for element in vector.iter() {
|
||||||
|
if let Some(value) = min {
|
||||||
|
if element < &value {
|
||||||
|
min = Some(*element)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
min = Some(*element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_of_float_vec(vector: Vec<f32>) -> Option<f32> {
|
||||||
|
let mut max: Option<f32> = None;
|
||||||
|
|
||||||
|
for element in vector.iter() {
|
||||||
|
if let Some(value) = max {
|
||||||
|
if element > &value {
|
||||||
|
max = Some(*element)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
max = Some(*element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RGB> for HSL {
|
||||||
|
fn from(value: RGB) -> Self {
|
||||||
|
let r = value.0.to_f32() / 255.0;
|
||||||
|
let g = value.1.to_f32() / 255.0;
|
||||||
|
let b = value.2.to_f32() / 255.0;
|
||||||
|
|
||||||
|
let min: f32 = min_of_float_vec(vec![r, g, b]).unwrap();
|
||||||
|
let max: f32 = max_of_float_vec(vec![r, g, b]).unwrap();
|
||||||
|
|
||||||
|
// Luminance
|
||||||
|
let l = ((min + max) / 2.0).round();
|
||||||
|
|
||||||
|
// Saturation
|
||||||
|
let s: f32;
|
||||||
|
if r == g && g == b {
|
||||||
|
s = 0.0;
|
||||||
|
} else {
|
||||||
|
if l <= 0.5 {
|
||||||
|
s = (max - min) / (max + min);
|
||||||
|
} else {
|
||||||
|
s = (max - min) / (2.0 - max - min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hue
|
||||||
|
let h: f32;
|
||||||
|
if max == r {
|
||||||
|
h = (g - b) / (max - min);
|
||||||
|
} else if max == g {
|
||||||
|
h = 2.0 + (b - r) / (max - min);
|
||||||
|
} else {
|
||||||
|
h = 4.0 + (r - g) / (max - min);
|
||||||
|
}
|
||||||
|
|
||||||
|
HSL::new(
|
||||||
|
(h * 60.0).round() as u16,
|
||||||
|
(s * 100.0).round() as u8,
|
||||||
|
(l * 100.0).round() as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HSL> for RGB {
|
||||||
|
fn from(color: HSL) -> Self {
|
||||||
|
// No saturation
|
||||||
|
if color.1 == 0 {
|
||||||
|
let shade = color.2 * 255 / 100;
|
||||||
|
let intensity = ColorIntensity::new(shade);
|
||||||
|
return Self(intensity.clone(), intensity.clone(), intensity.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_1: f32;
|
||||||
|
|
||||||
|
if color.2 < 50 {
|
||||||
|
// Low lum
|
||||||
|
temp_1 = (color.2.to_f32() / 100.0) * (color.1.to_f32() / 100.0 + 1.0);
|
||||||
|
} else {
|
||||||
|
// High lum
|
||||||
|
temp_1 = (color.2.to_f32() / 100.0 + color.1.to_f32())
|
||||||
|
- (color.2.to_f32() * color.1.to_f32());
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_2: f32 = (color.2.to_f32() / 100.0) * 2.0 - temp_1;
|
||||||
|
|
||||||
|
let hue = color.0.to_f32() / 360.0;
|
||||||
|
|
||||||
|
let mut temp_r = hue + 0.333;
|
||||||
|
let temp_g = hue;
|
||||||
|
let temp_b = hue - 0.333;
|
||||||
|
|
||||||
|
// Normalize values
|
||||||
|
if temp_r > 1.0 {
|
||||||
|
temp_r = temp_r - 1.0;
|
||||||
|
}
|
||||||
|
if temp_b < 0.0 {
|
||||||
|
temp_r = temp_r + 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calc Red
|
||||||
|
let red: f32;
|
||||||
|
if temp_r * 6.0 < 1.0 {
|
||||||
|
red = temp_2 + (temp_1 - temp_2) * 6.0 * temp_r;
|
||||||
|
} else if temp_r * 2.0 < 1.0 {
|
||||||
|
red = temp_1;
|
||||||
|
} else if temp_r * 3.0 < 2.0 {
|
||||||
|
red = temp_2 + (temp_1 - temp_2) * (0.666 - temp_r) * 6.0;
|
||||||
|
} else {
|
||||||
|
red = temp_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calc Green
|
||||||
|
let green: f32;
|
||||||
|
if temp_g * 6.0 < 1.0 {
|
||||||
|
green = temp_2 + (temp_1 - temp_2) * 6.0 * temp_g;
|
||||||
|
} else if temp_g * 2.0 < 1.0 {
|
||||||
|
green = temp_1;
|
||||||
|
} else if temp_g * 3.0 < 2.0 {
|
||||||
|
green = temp_2 + (temp_1 - temp_2) * (0.666 - temp_g) * 6.0;
|
||||||
|
} else {
|
||||||
|
green = temp_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calc blue
|
||||||
|
let blue: f32;
|
||||||
|
if temp_b * 6.0 < 1.0 {
|
||||||
|
blue = temp_2 + (temp_1 - temp_2) * 6.0 * temp_b;
|
||||||
|
} else if temp_b * 2.0 < 1.0 {
|
||||||
|
blue = temp_1;
|
||||||
|
} else if temp_b * 3.0 < 2.0 {
|
||||||
|
blue = temp_2 + (temp_1 - temp_2) * (0.666 - temp_b) * 6.0;
|
||||||
|
} else {
|
||||||
|
blue = temp_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::new(
|
||||||
|
(red * 255.0).round() as u8,
|
||||||
|
(green * 255.0).round() as u8,
|
||||||
|
(blue * 255.0).round() as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
colorizer/src/formats/mod.rs
Normal file
5
colorizer/src/formats/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod colors;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "./test/colors.test.rs"]
|
||||||
|
mod test;
|
||||||
26
colorizer/src/formats/test/colors.test.rs
Normal file
26
colorizer/src/formats/test/colors.test.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use crate::formats::colors::{Color, HSL, RGB};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_conversion() {
|
||||||
|
let hsl_color = Color::from(HSL::new(193, 67, 28));
|
||||||
|
let rgb_color = Color::from(RGB::from(HSL::new(193, 67, 28)));
|
||||||
|
assert_eq!(hsl_color, rgb_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hsl_variants() {
|
||||||
|
let red_hsl = Color::from(HSL::new(0, 100, 50));
|
||||||
|
let red_rgb = Color::from(RGB::new(255, 0, 0));
|
||||||
|
assert_eq!(red_hsl, red_rgb);
|
||||||
|
|
||||||
|
let green_hsl = Color::from(HSL::new(120, 100, 50));
|
||||||
|
let green_rgb = Color::from(RGB::new(0, 255, 0));
|
||||||
|
assert_eq!(green_hsl, green_rgb);
|
||||||
|
|
||||||
|
let blue_hsl = Color::from(HSL::new(240, 100, 50));
|
||||||
|
let blue_rgb = Color::from(RGB::new(0, 0, 255));
|
||||||
|
assert_eq!(blue_hsl, blue_rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,10 @@ use clipboard::ClipboardContext;
|
|||||||
use clipboard::ClipboardProvider;
|
use clipboard::ClipboardProvider;
|
||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
mod transmuter;
|
mod formats;
|
||||||
|
use formats::colors::{Color, RGB};
|
||||||
|
|
||||||
#[cfg(test)]
|
use crate::formats::colors::HSL;
|
||||||
#[path = "./test/transmuter.test.rs"]
|
|
||||||
mod test;
|
|
||||||
|
|
||||||
fn example() {
|
fn example() {
|
||||||
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
|
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
|
||||||
@@ -17,4 +16,9 @@ fn example() {
|
|||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
example();
|
example();
|
||||||
|
|
||||||
|
let hsl_color = Color::from(HSL::new(193, 67, 28));
|
||||||
|
let rgb_color = Color::from(RGB::from(HSL::new(193, 67, 28)));
|
||||||
|
println!("HSL Color: {}", hsl_color.format());
|
||||||
|
println!("RGB Color: {}", rgb_color.format());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
#[test]
|
|
||||||
fn test_success() {
|
|
||||||
let my_hello = "Hello world!";
|
|
||||||
assert_eq!(my_hello, "Hello world!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
use std::convert::From;
|
|
||||||
|
|
||||||
use crate::core::ranged::{BaseNumber, RangedInt};
|
|
||||||
|
|
||||||
type ColorIntensity = RangedInt<0, 255>;
|
|
||||||
struct RGB(ColorIntensity, ColorIntensity, ColorIntensity);
|
|
||||||
struct HSL(RangedInt<0, 360>, RangedInt<0, 100>, RangedInt<0, 100>);
|
|
||||||
struct HSV(RangedInt<0, 360>, RangedInt<0, 100>, RangedInt<0, 100>);
|
|
||||||
|
|
||||||
pub struct Color(RGB);
|
|
||||||
|
|
||||||
impl Color {}
|
|
||||||
|
|
||||||
impl RGB {
|
|
||||||
fn new(r: u8, g: u8, b: u8) -> Self {
|
|
||||||
Self(
|
|
||||||
ColorIntensity::new(r as BaseNumber),
|
|
||||||
ColorIntensity::new(g as BaseNumber),
|
|
||||||
ColorIntensity::new(b as BaseNumber),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RGB> for Color {
|
|
||||||
fn from(color: RGB) -> Self {
|
|
||||||
Self(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HSL> for Color {
|
|
||||||
fn from(color: HSL) -> Self {
|
|
||||||
Color(RGB::from(color))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HSL> for RGB {
|
|
||||||
fn from(color: HSL) -> Self {
|
|
||||||
// No saturation
|
|
||||||
if color.1 == 0 {
|
|
||||||
let shade = color.2 / 100 * 255;
|
|
||||||
let intensity = ColorIntensity::new(shade);
|
|
||||||
return Self(intensity.clone(), intensity.clone(), intensity.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut temp_1: i16 = 0;
|
|
||||||
|
|
||||||
if color.2 < 50 {
|
|
||||||
// Low lum
|
|
||||||
temp_1 = color.2.clone() * (color.1 + 100);
|
|
||||||
} else {
|
|
||||||
// High lum
|
|
||||||
temp_1 = color.2.clone() + color.1.clone() - color.2.clone() * color.1.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let temp_2: i16 = color.2 * 2 - temp_1;
|
|
||||||
let hue = (color.0.clone() / 360) * 100;
|
|
||||||
|
|
||||||
Self::new(0, 0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
mod colors;
|
|
||||||
mod parsers;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
Reference in New Issue
Block a user