分拆根文件中的功能函数。
This commit is contained in:
parent
86ecb5a258
commit
4a9dfbb664
190
color-module/src/analysis.rs
Normal file
190
color-module/src/analysis.rs
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use palette::{
|
||||||
|
cam16::{Cam16Jch, Parameters},
|
||||||
|
FromColor, Hsl, IntoColor, Oklch, Srgb,
|
||||||
|
};
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
color_differ::{self, ColorDifference},
|
||||||
|
errors, reversing,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn differ_in_rgb(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::rgb::RGBDifference, errors::ColorError> {
|
||||||
|
let srgb = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let other_srgb = Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
Ok(srgb.difference(&other_srgb))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn relative_differ_in_rgb(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::rgb::RGBDifference, errors::ColorError> {
|
||||||
|
let srgb = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let other_srgb = Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
Ok(srgb.difference_relative(&other_srgb))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn differ_in_hsl(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::hsl::HSLDifference, errors::ColorError> {
|
||||||
|
let hsl = Hsl::from_color(
|
||||||
|
Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
let other_hsl = Hsl::from_color(
|
||||||
|
Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
Ok(hsl.difference(&other_hsl))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn relative_differ_in_hsl(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::hsl::HSLDifference, errors::ColorError> {
|
||||||
|
let hsl = Hsl::from_color(
|
||||||
|
Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
let other_hsl = Hsl::from_color(
|
||||||
|
Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
Ok(hsl.difference_relative(&other_hsl))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn differ_in_hct(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::cam16jch::HctDiffference, errors::ColorError> {
|
||||||
|
let hct = Cam16Jch::from_xyz(
|
||||||
|
Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>()
|
||||||
|
.into_color(),
|
||||||
|
Parameters::default_static_wp(40.0),
|
||||||
|
);
|
||||||
|
let other_hct = Cam16Jch::from_xyz(
|
||||||
|
Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>()
|
||||||
|
.into_color(),
|
||||||
|
Parameters::default_static_wp(40.0),
|
||||||
|
);
|
||||||
|
Ok(hct.difference(&other_hct))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn relative_differ_in_hct(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::cam16jch::HctDiffference, errors::ColorError> {
|
||||||
|
let hct = Cam16Jch::from_xyz(
|
||||||
|
Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>()
|
||||||
|
.into_color(),
|
||||||
|
Parameters::default_static_wp(40.0),
|
||||||
|
);
|
||||||
|
let other_hct = Cam16Jch::from_xyz(
|
||||||
|
Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>()
|
||||||
|
.into_color(),
|
||||||
|
Parameters::default_static_wp(40.0),
|
||||||
|
);
|
||||||
|
Ok(hct.difference_relative(&other_hct))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn differ_in_oklch(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::oklch::OklchDifference, errors::ColorError> {
|
||||||
|
let oklch = Oklch::from_color(
|
||||||
|
Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
let other_oklch = Oklch::from_color(
|
||||||
|
Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
Ok(oklch.difference(&other_oklch))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn relative_differ_in_oklch(
|
||||||
|
color: &str,
|
||||||
|
other: &str,
|
||||||
|
) -> Result<color_differ::oklch::OklchDifference, errors::ColorError> {
|
||||||
|
let oklch = Oklch::from_color(
|
||||||
|
Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
let other_oklch = Oklch::from_color(
|
||||||
|
Srgb::from_str(other)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
||||||
|
.into_format::<f32>(),
|
||||||
|
);
|
||||||
|
Ok(oklch.difference_relative(&other_oklch))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn tint_scale(
|
||||||
|
basic_color: &str,
|
||||||
|
mixed_color: &str,
|
||||||
|
) -> Result<reversing::MixReversing, errors::ColorError> {
|
||||||
|
let basic_color = Srgb::from_str(basic_color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(basic_color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let mixed_color = Srgb::from_str(mixed_color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(mixed_color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
Ok(reversing::MixReversing::from_tint_rgb(
|
||||||
|
basic_color,
|
||||||
|
mixed_color,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn shade_scale(
|
||||||
|
basic_color: &str,
|
||||||
|
mixed_color: &str,
|
||||||
|
) -> Result<reversing::MixReversing, errors::ColorError> {
|
||||||
|
let basic_color = Srgb::from_str(basic_color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(basic_color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let mixed_color = Srgb::from_str(mixed_color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(mixed_color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
Ok(reversing::MixReversing::from_shade_rgb(
|
||||||
|
basic_color,
|
||||||
|
mixed_color,
|
||||||
|
))
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
use std::sync::LazyLock;
|
use std::{str::FromStr, sync::LazyLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{Display, EnumIter, EnumString};
|
use strum::{Display, EnumIter, EnumString, IntoEnumIterator};
|
||||||
|
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ColorDescription {
|
pub struct ColorDescription {
|
||||||
|
@ -87,3 +88,45 @@ impl Category {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn color_categories() -> Result<JsValue, String> {
|
||||||
|
let categories = Category::iter()
|
||||||
|
.map(|variant| {
|
||||||
|
serde_json::json!({
|
||||||
|
"label": variant.label(),
|
||||||
|
"value": variant.to_string(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
serde_wasm_bindgen::to_value(&categories).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn search_color_cards(tag: String, category: Option<String>) -> Result<JsValue, String> {
|
||||||
|
let selected_category = category.and_then(|c| Category::from_str(&c).ok());
|
||||||
|
let all_cards = &*COLOR_CARDS;
|
||||||
|
let mut cards = all_cards
|
||||||
|
.iter()
|
||||||
|
.filter(|card| card.tags.contains(&tag))
|
||||||
|
.filter(|card| {
|
||||||
|
if let Some(category) = &selected_category {
|
||||||
|
let card_category = Category::from_oklch(&card.oklch);
|
||||||
|
card_category == *category
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
cards.sort_by(|a, b| {
|
||||||
|
a.oklch[2]
|
||||||
|
.partial_cmp(&b.oklch[2])
|
||||||
|
.or_else(|| a.oklch[1].partial_cmp(&b.oklch[1]))
|
||||||
|
.or_else(|| a.oklch[0].partial_cmp(&b.oklch[0]))
|
||||||
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
|
});
|
||||||
|
|
||||||
|
serde_wasm_bindgen::to_value(&cards).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
80
color-module/src/color_shifting.rs
Normal file
80
color-module/src/color_shifting.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use palette::{Darken, FromColor, Lighten, Mix, Oklch, Srgb};
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
use crate::errors;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn lighten(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let lightened_color = oklch.lighten(percent);
|
||||||
|
let srgb = Srgb::from_color(lightened_color);
|
||||||
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn lighten_absolute(color: &str, value: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let lightened_color = oklch.lighten_fixed(value);
|
||||||
|
let srgb = Srgb::from_color(lightened_color);
|
||||||
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn darken(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let darkened_color = oklch.darken(percent);
|
||||||
|
let srgb = Srgb::from_color(darkened_color);
|
||||||
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn darken_absolute(color: &str, value: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let darkened_color = oklch.darken_fixed(value);
|
||||||
|
let srgb = Srgb::from_color(darkened_color);
|
||||||
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn mix(color1: &str, color2: &str, percent: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let srgb1 = Srgb::from_str(color1)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color1.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let srgb2 = Srgb::from_str(color2)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color2.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let mixed_color = srgb1.mix(srgb2, percent);
|
||||||
|
Ok(format!("{:x}", mixed_color.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn tint(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let tinted_color = origin_color.mix(Srgb::new(1.0, 1.0, 1.0), percent);
|
||||||
|
Ok(format!("{:x}", tinted_color.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn shade(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let shaded_color = origin_color.mix(Srgb::new(0.0, 0.0, 0.0), percent);
|
||||||
|
Ok(format!("{:x}", shaded_color.into_format::<u8>()))
|
||||||
|
}
|
|
@ -1,24 +1,24 @@
|
||||||
use std::{str::FromStr, sync::Arc};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use color_card::Category;
|
|
||||||
use color_differ::ColorDifference;
|
|
||||||
use palette::{
|
use palette::{
|
||||||
cam16::{Cam16Jch, Parameters},
|
cam16::{Cam16Jch, Parameters},
|
||||||
color_difference::Wcag21RelativeContrast,
|
color_difference::Wcag21RelativeContrast,
|
||||||
color_theory::*,
|
|
||||||
convert::FromColorUnclamped,
|
convert::FromColorUnclamped,
|
||||||
Darken, FromColor, Hsl, IntoColor, IsWithinBounds, Lab, Lighten, Mix, Oklch, ShiftHue, Srgb,
|
FromColor, Hsl, IntoColor, IsWithinBounds, Lab, Oklch, Srgb,
|
||||||
};
|
};
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
mod analysis;
|
||||||
mod color_card;
|
mod color_card;
|
||||||
mod color_differ;
|
mod color_differ;
|
||||||
|
mod color_shifting;
|
||||||
mod convert;
|
mod convert;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod palettes;
|
mod palettes;
|
||||||
mod reversing;
|
mod reversing;
|
||||||
mod schemes;
|
mod schemes;
|
||||||
|
mod series;
|
||||||
|
mod theory;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn represent_rgb(color: &str) -> Result<Box<[u8]>, errors::ColorError> {
|
pub fn represent_rgb(color: &str) -> Result<Box<[u8]>, errors::ColorError> {
|
||||||
|
@ -125,91 +125,6 @@ pub fn hct_to_hex(hue: f32, chroma: f32, tone: f32) -> Result<String, errors::Co
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn shift_hue(color: &str, degree: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let shifted_color = oklch.shift_hue(degree);
|
|
||||||
let srgb = Srgb::from_color(shifted_color);
|
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn lighten(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let lightened_color = oklch.lighten(percent);
|
|
||||||
let srgb = Srgb::from_color(lightened_color);
|
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn lighten_absolute(color: &str, value: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let lightened_color = oklch.lighten_fixed(value);
|
|
||||||
let srgb = Srgb::from_color(lightened_color);
|
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn darken(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let darkened_color = oklch.darken(percent);
|
|
||||||
let srgb = Srgb::from_color(darkened_color);
|
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn darken_absolute(color: &str, value: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let darkened_color = oklch.darken_fixed(value);
|
|
||||||
let srgb = Srgb::from_color(darkened_color);
|
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn mix(color1: &str, color2: &str, percent: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let srgb1 = Srgb::from_str(color1)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color1.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let srgb2 = Srgb::from_str(color2)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color2.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let mixed_color = srgb1.mix(srgb2, percent);
|
|
||||||
Ok(format!("{:x}", mixed_color.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn tint(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let tinted_color = origin_color.mix(Srgb::new(1.0, 1.0, 1.0), percent);
|
|
||||||
Ok(format!("{:x}", tinted_color.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn shade(color: &str, percent: f32) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let shaded_color = origin_color.mix(Srgb::new(0.0, 0.0, 0.0), percent);
|
|
||||||
Ok(format!("{:x}", shaded_color.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn wacg_relative_contrast(fg_color: &str, bg_color: &str) -> Result<f32, errors::ColorError> {
|
pub fn wacg_relative_contrast(fg_color: &str, bg_color: &str) -> Result<f32, errors::ColorError> {
|
||||||
let fg_srgb = Srgb::from_str(fg_color)
|
let fg_srgb = Srgb::from_str(fg_color)
|
||||||
|
@ -220,389 +135,3 @@ pub fn wacg_relative_contrast(fg_color: &str, bg_color: &str) -> Result<f32, err
|
||||||
.into_format::<f32>();
|
.into_format::<f32>();
|
||||||
Ok(fg_srgb.relative_contrast(bg_srgb))
|
Ok(fg_srgb.relative_contrast(bg_srgb))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn analogous_30(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let (color_n30, color_p30) = oklch.analogous();
|
|
||||||
let srgb_n30 = Srgb::from_color(color_n30);
|
|
||||||
let srgb_p30 = Srgb::from_color(color_p30);
|
|
||||||
Ok(vec![
|
|
||||||
format!("{:x}", srgb_n30.into_format::<u8>()),
|
|
||||||
format!("{:x}", srgb_p30.into_format::<u8>()),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn analogous_60(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let (color_n60, color_p60) = oklch.analogous_secondary();
|
|
||||||
let srgb_n60 = Srgb::from_color(color_n60);
|
|
||||||
let srgb_p60 = Srgb::from_color(color_p60);
|
|
||||||
Ok(vec![
|
|
||||||
format!("{:x}", srgb_n60.into_format::<u8>()),
|
|
||||||
format!("{:x}", srgb_p60.into_format::<u8>()),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn complementary(color: &str) -> Result<String, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let complementary_color = oklch.complementary();
|
|
||||||
let srgb = Srgb::from_color(complementary_color);
|
|
||||||
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn split_complementary(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let (color_p150, color_p210) = oklch.split_complementary();
|
|
||||||
let srgb_p150 = Srgb::from_color(color_p150);
|
|
||||||
let srgb_p210 = Srgb::from_color(color_p210);
|
|
||||||
Ok(vec![
|
|
||||||
format!("{:x}", srgb_p150.into_format::<u8>()),
|
|
||||||
format!("{:x}", srgb_p210.into_format::<u8>()),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn tetradic(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let (color_p90, color_p180, color_p270) = oklch.tetradic();
|
|
||||||
let srgb_p90 = Srgb::from_color(color_p90);
|
|
||||||
let srgb_p180 = Srgb::from_color(color_p180);
|
|
||||||
let srgb_p270 = Srgb::from_color(color_p270);
|
|
||||||
Ok(vec![
|
|
||||||
format!("{:x}", srgb_p90.into_format::<u8>()),
|
|
||||||
format!("{:x}", srgb_p180.into_format::<u8>()),
|
|
||||||
format!("{:x}", srgb_p270.into_format::<u8>()),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn triadic(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Oklch::from_color(origin_color);
|
|
||||||
let (color_p120, color_p240) = oklch.triadic();
|
|
||||||
let srgb_p120 = Srgb::from_color(color_p120);
|
|
||||||
let srgb_p240 = Srgb::from_color(color_p240);
|
|
||||||
Ok(vec![
|
|
||||||
format!("{:x}", srgb_p120.into_format::<u8>()),
|
|
||||||
format!("{:x}", srgb_p240.into_format::<u8>()),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn series(
|
|
||||||
color: &str,
|
|
||||||
expand_amount: i16,
|
|
||||||
step: f32,
|
|
||||||
) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let oklch = Arc::new(Oklch::from_color(origin_color.clone()));
|
|
||||||
|
|
||||||
let mut color_series = Vec::new();
|
|
||||||
for s in (1..=expand_amount).rev() {
|
|
||||||
let darkened_color = Arc::clone(&oklch).darken(s as f32 * step);
|
|
||||||
let srgb = Srgb::from_color(darkened_color);
|
|
||||||
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
|
||||||
}
|
|
||||||
color_series.push(format!("{:x}", origin_color.into_format::<u8>()));
|
|
||||||
for s in 1..=expand_amount {
|
|
||||||
let lightened_color = Arc::clone(&oklch).lighten(s as f32 * step);
|
|
||||||
let srgb = Srgb::from_color(lightened_color);
|
|
||||||
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
|
||||||
}
|
|
||||||
Ok(color_series)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn tonal_lighten_series(
|
|
||||||
color: &str,
|
|
||||||
expand_amount: i16,
|
|
||||||
step: f32,
|
|
||||||
) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let hct = Cam16Jch::from_xyz(
|
|
||||||
origin_color.into_color(),
|
|
||||||
Parameters::default_static_wp(40.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut color_series = Vec::new();
|
|
||||||
let mut lightness = hct.lightness;
|
|
||||||
for _ in 1..=expand_amount {
|
|
||||||
lightness += (100.0 - lightness) * step;
|
|
||||||
let lightened_color = Cam16Jch::new(lightness, hct.chroma, hct.hue);
|
|
||||||
let srgb = Srgb::from_color(lightened_color.into_xyz(Parameters::default_static_wp(40.0)));
|
|
||||||
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(color_series)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn tonal_darken_series(
|
|
||||||
color: &str,
|
|
||||||
expand_amount: i16,
|
|
||||||
step: f32,
|
|
||||||
) -> Result<Vec<String>, errors::ColorError> {
|
|
||||||
let origin_color = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let hct = Cam16Jch::from_xyz(
|
|
||||||
origin_color.into_color(),
|
|
||||||
Parameters::default_static_wp(40.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut color_series = Vec::new();
|
|
||||||
let mut lightness = hct.lightness;
|
|
||||||
for _ in 1..=expand_amount {
|
|
||||||
lightness *= 1.0 - step;
|
|
||||||
let darkened_color = Cam16Jch::new(lightness, hct.chroma, hct.hue);
|
|
||||||
let srgb = Srgb::from_color(darkened_color.into_xyz(Parameters::default_static_wp(40.0)));
|
|
||||||
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(color_series.into_iter().rev().collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn color_categories() -> Result<JsValue, String> {
|
|
||||||
let categories = Category::iter()
|
|
||||||
.map(|variant| {
|
|
||||||
serde_json::json!({
|
|
||||||
"label": variant.label(),
|
|
||||||
"value": variant.to_string(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
serde_wasm_bindgen::to_value(&categories).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn search_color_cards(tag: String, category: Option<String>) -> Result<JsValue, String> {
|
|
||||||
let selected_category = category.and_then(|c| Category::from_str(&c).ok());
|
|
||||||
let all_cards = &*color_card::COLOR_CARDS;
|
|
||||||
let mut cards = all_cards
|
|
||||||
.iter()
|
|
||||||
.filter(|card| card.tags.contains(&tag))
|
|
||||||
.filter(|card| {
|
|
||||||
if let Some(category) = &selected_category {
|
|
||||||
let card_category = Category::from_oklch(&card.oklch);
|
|
||||||
card_category == *category
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
cards.sort_by(|a, b| {
|
|
||||||
a.oklch[2]
|
|
||||||
.partial_cmp(&b.oklch[2])
|
|
||||||
.or_else(|| a.oklch[1].partial_cmp(&b.oklch[1]))
|
|
||||||
.or_else(|| a.oklch[0].partial_cmp(&b.oklch[0]))
|
|
||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
|
||||||
});
|
|
||||||
|
|
||||||
serde_wasm_bindgen::to_value(&cards).map_err(|e| e.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn differ_in_rgb(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::rgb::RGBDifference, errors::ColorError> {
|
|
||||||
let srgb = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let other_srgb = Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
Ok(srgb.difference(&other_srgb))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn relative_differ_in_rgb(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::rgb::RGBDifference, errors::ColorError> {
|
|
||||||
let srgb = Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let other_srgb = Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
Ok(srgb.difference_relative(&other_srgb))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn differ_in_hsl(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::hsl::HSLDifference, errors::ColorError> {
|
|
||||||
let hsl = Hsl::from_color(
|
|
||||||
Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
let other_hsl = Hsl::from_color(
|
|
||||||
Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
Ok(hsl.difference(&other_hsl))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn relative_differ_in_hsl(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::hsl::HSLDifference, errors::ColorError> {
|
|
||||||
let hsl = Hsl::from_color(
|
|
||||||
Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
let other_hsl = Hsl::from_color(
|
|
||||||
Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
Ok(hsl.difference_relative(&other_hsl))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn differ_in_hct(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::cam16jch::HctDiffference, errors::ColorError> {
|
|
||||||
let hct = Cam16Jch::from_xyz(
|
|
||||||
Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>()
|
|
||||||
.into_color(),
|
|
||||||
Parameters::default_static_wp(40.0),
|
|
||||||
);
|
|
||||||
let other_hct = Cam16Jch::from_xyz(
|
|
||||||
Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>()
|
|
||||||
.into_color(),
|
|
||||||
Parameters::default_static_wp(40.0),
|
|
||||||
);
|
|
||||||
Ok(hct.difference(&other_hct))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn relative_differ_in_hct(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::cam16jch::HctDiffference, errors::ColorError> {
|
|
||||||
let hct = Cam16Jch::from_xyz(
|
|
||||||
Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>()
|
|
||||||
.into_color(),
|
|
||||||
Parameters::default_static_wp(40.0),
|
|
||||||
);
|
|
||||||
let other_hct = Cam16Jch::from_xyz(
|
|
||||||
Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>()
|
|
||||||
.into_color(),
|
|
||||||
Parameters::default_static_wp(40.0),
|
|
||||||
);
|
|
||||||
Ok(hct.difference_relative(&other_hct))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn differ_in_oklch(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::oklch::OklchDifference, errors::ColorError> {
|
|
||||||
let oklch = Oklch::from_color(
|
|
||||||
Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
let other_oklch = Oklch::from_color(
|
|
||||||
Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
Ok(oklch.difference(&other_oklch))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn relative_differ_in_oklch(
|
|
||||||
color: &str,
|
|
||||||
other: &str,
|
|
||||||
) -> Result<color_differ::oklch::OklchDifference, errors::ColorError> {
|
|
||||||
let oklch = Oklch::from_color(
|
|
||||||
Srgb::from_str(color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
let other_oklch = Oklch::from_color(
|
|
||||||
Srgb::from_str(other)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(other.to_string()))?
|
|
||||||
.into_format::<f32>(),
|
|
||||||
);
|
|
||||||
Ok(oklch.difference_relative(&other_oklch))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn tint_scale(
|
|
||||||
basic_color: &str,
|
|
||||||
mixed_color: &str,
|
|
||||||
) -> Result<reversing::MixReversing, errors::ColorError> {
|
|
||||||
let basic_color = Srgb::from_str(basic_color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(basic_color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let mixed_color = Srgb::from_str(mixed_color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(mixed_color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
Ok(reversing::MixReversing::from_tint_rgb(
|
|
||||||
basic_color,
|
|
||||||
mixed_color,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn shade_scale(
|
|
||||||
basic_color: &str,
|
|
||||||
mixed_color: &str,
|
|
||||||
) -> Result<reversing::MixReversing, errors::ColorError> {
|
|
||||||
let basic_color = Srgb::from_str(basic_color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(basic_color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
let mixed_color = Srgb::from_str(mixed_color)
|
|
||||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(mixed_color.to_string()))?
|
|
||||||
.into_format::<f32>();
|
|
||||||
Ok(reversing::MixReversing::from_shade_rgb(
|
|
||||||
basic_color,
|
|
||||||
mixed_color,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
87
color-module/src/series.rs
Normal file
87
color-module/src/series.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
|
use palette::{
|
||||||
|
cam16::{Cam16Jch, Parameters},
|
||||||
|
Darken, FromColor, IntoColor, Lighten, Oklch, Srgb,
|
||||||
|
};
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
use crate::errors;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn series(
|
||||||
|
color: &str,
|
||||||
|
expand_amount: i16,
|
||||||
|
step: f32,
|
||||||
|
) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Arc::new(Oklch::from_color(origin_color.clone()));
|
||||||
|
|
||||||
|
let mut color_series = Vec::new();
|
||||||
|
for s in (1..=expand_amount).rev() {
|
||||||
|
let darkened_color = Arc::clone(&oklch).darken(s as f32 * step);
|
||||||
|
let srgb = Srgb::from_color(darkened_color);
|
||||||
|
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
||||||
|
}
|
||||||
|
color_series.push(format!("{:x}", origin_color.into_format::<u8>()));
|
||||||
|
for s in 1..=expand_amount {
|
||||||
|
let lightened_color = Arc::clone(&oklch).lighten(s as f32 * step);
|
||||||
|
let srgb = Srgb::from_color(lightened_color);
|
||||||
|
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
||||||
|
}
|
||||||
|
Ok(color_series)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn tonal_lighten_series(
|
||||||
|
color: &str,
|
||||||
|
expand_amount: i16,
|
||||||
|
step: f32,
|
||||||
|
) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let hct = Cam16Jch::from_xyz(
|
||||||
|
origin_color.into_color(),
|
||||||
|
Parameters::default_static_wp(40.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut color_series = Vec::new();
|
||||||
|
let mut lightness = hct.lightness;
|
||||||
|
for _ in 1..=expand_amount {
|
||||||
|
lightness += (100.0 - lightness) * step;
|
||||||
|
let lightened_color = Cam16Jch::new(lightness, hct.chroma, hct.hue);
|
||||||
|
let srgb = Srgb::from_color(lightened_color.into_xyz(Parameters::default_static_wp(40.0)));
|
||||||
|
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(color_series)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn tonal_darken_series(
|
||||||
|
color: &str,
|
||||||
|
expand_amount: i16,
|
||||||
|
step: f32,
|
||||||
|
) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let hct = Cam16Jch::from_xyz(
|
||||||
|
origin_color.into_color(),
|
||||||
|
Parameters::default_static_wp(40.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut color_series = Vec::new();
|
||||||
|
let mut lightness = hct.lightness;
|
||||||
|
for _ in 1..=expand_amount {
|
||||||
|
lightness *= 1.0 - step;
|
||||||
|
let darkened_color = Cam16Jch::new(lightness, hct.chroma, hct.hue);
|
||||||
|
let srgb = Srgb::from_color(darkened_color.into_xyz(Parameters::default_static_wp(40.0)));
|
||||||
|
color_series.push(format!("{:x}", srgb.into_format::<u8>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(color_series.into_iter().rev().collect())
|
||||||
|
}
|
108
color-module/src/theory.rs
Normal file
108
color-module/src/theory.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use palette::{
|
||||||
|
color_theory::{Analogous, Complementary, SplitComplementary, Tetradic, Triadic},
|
||||||
|
FromColor, Oklch, ShiftHue, Srgb,
|
||||||
|
};
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
use crate::errors;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn shift_hue(color: &str, degree: f32) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let shifted_color = oklch.shift_hue(degree);
|
||||||
|
let srgb = Srgb::from_color(shifted_color);
|
||||||
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn analogous_30(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let (color_n30, color_p30) = oklch.analogous();
|
||||||
|
let srgb_n30 = Srgb::from_color(color_n30);
|
||||||
|
let srgb_p30 = Srgb::from_color(color_p30);
|
||||||
|
Ok(vec![
|
||||||
|
format!("{:x}", srgb_n30.into_format::<u8>()),
|
||||||
|
format!("{:x}", srgb_p30.into_format::<u8>()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn analogous_60(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let (color_n60, color_p60) = oklch.analogous_secondary();
|
||||||
|
let srgb_n60 = Srgb::from_color(color_n60);
|
||||||
|
let srgb_p60 = Srgb::from_color(color_p60);
|
||||||
|
Ok(vec![
|
||||||
|
format!("{:x}", srgb_n60.into_format::<u8>()),
|
||||||
|
format!("{:x}", srgb_p60.into_format::<u8>()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn complementary(color: &str) -> Result<String, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let complementary_color = oklch.complementary();
|
||||||
|
let srgb = Srgb::from_color(complementary_color);
|
||||||
|
Ok(format!("{:x}", srgb.into_format::<u8>()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn split_complementary(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let (color_p150, color_p210) = oklch.split_complementary();
|
||||||
|
let srgb_p150 = Srgb::from_color(color_p150);
|
||||||
|
let srgb_p210 = Srgb::from_color(color_p210);
|
||||||
|
Ok(vec![
|
||||||
|
format!("{:x}", srgb_p150.into_format::<u8>()),
|
||||||
|
format!("{:x}", srgb_p210.into_format::<u8>()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn tetradic(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let (color_p90, color_p180, color_p270) = oklch.tetradic();
|
||||||
|
let srgb_p90 = Srgb::from_color(color_p90);
|
||||||
|
let srgb_p180 = Srgb::from_color(color_p180);
|
||||||
|
let srgb_p270 = Srgb::from_color(color_p270);
|
||||||
|
Ok(vec![
|
||||||
|
format!("{:x}", srgb_p90.into_format::<u8>()),
|
||||||
|
format!("{:x}", srgb_p180.into_format::<u8>()),
|
||||||
|
format!("{:x}", srgb_p270.into_format::<u8>()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn triadic(color: &str) -> Result<Vec<String>, errors::ColorError> {
|
||||||
|
let origin_color = Srgb::from_str(color)
|
||||||
|
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||||
|
.into_format::<f32>();
|
||||||
|
let oklch = Oklch::from_color(origin_color);
|
||||||
|
let (color_p120, color_p240) = oklch.triadic();
|
||||||
|
let srgb_p120 = Srgb::from_color(color_p120);
|
||||||
|
let srgb_p240 = Srgb::from_color(color_p240);
|
||||||
|
Ok(vec![
|
||||||
|
format!("{:x}", srgb_p120.into_format::<u8>()),
|
||||||
|
format!("{:x}", srgb_p240.into_format::<u8>()),
|
||||||
|
])
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user