增加内置的颜色色卡支持。
This commit is contained in:
parent
5e7b1e709d
commit
f2031f3d8c
89
color-module/src/color_card.rs
Normal file
89
color-module/src/color_card.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use strum::{Display, EnumIter, EnumString};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ColorDescription {
|
||||||
|
pub name: String,
|
||||||
|
pub pinyin: Vec<String>,
|
||||||
|
pub hue: f32,
|
||||||
|
pub lightness: f32,
|
||||||
|
pub category: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub rgb: [u8; 3],
|
||||||
|
pub hsl: [f32; 3],
|
||||||
|
pub lab: [f32; 3],
|
||||||
|
pub oklch: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLOR_CARDS_JSON: &str = include_str!("colorcards.json");
|
||||||
|
pub const COLOR_CARDS: LazyLock<Vec<ColorDescription>> =
|
||||||
|
LazyLock::new(|| serde_json::from_str(COLOR_CARDS_JSON).expect("Failed to parse color cards"));
|
||||||
|
const CHROMA_EPSILON: f32 = 0.0001;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Display, EnumString, EnumIter)]
|
||||||
|
#[strum(serialize_all = "lowercase")]
|
||||||
|
pub enum Category {
|
||||||
|
Red,
|
||||||
|
Orange,
|
||||||
|
Yellow,
|
||||||
|
Green,
|
||||||
|
Cyan,
|
||||||
|
Blue,
|
||||||
|
Purple,
|
||||||
|
Magenta,
|
||||||
|
White,
|
||||||
|
Black,
|
||||||
|
Gray,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Category {
|
||||||
|
pub fn from_oklch_components(lightness: f32, chroma: f32, hue: f32) -> Self {
|
||||||
|
if chroma < CHROMA_EPSILON {
|
||||||
|
if lightness < 0.1 {
|
||||||
|
Category::Black
|
||||||
|
} else if lightness > 0.9 {
|
||||||
|
Category::White
|
||||||
|
} else {
|
||||||
|
Category::Gray
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let processed_hue = hue % 360.0;
|
||||||
|
match processed_hue {
|
||||||
|
0.0..=30.0 => Category::Red,
|
||||||
|
30.0..=60.0 => Category::Orange,
|
||||||
|
60.0..=90.0 => Category::Yellow,
|
||||||
|
90.0..=150.0 => Category::Green,
|
||||||
|
150.0..=210.0 => Category::Cyan,
|
||||||
|
210.0..=270.0 => Category::Blue,
|
||||||
|
270.0..=300.0 => Category::Purple,
|
||||||
|
300.0..=330.0 => Category::Magenta,
|
||||||
|
330.0..=360.0 => Category::Red,
|
||||||
|
_ => Category::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_oklch(oklch: &[f32; 3]) -> Self {
|
||||||
|
Category::from_oklch_components(oklch[0], oklch[1], oklch[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Category::Red => "Red".to_string(),
|
||||||
|
Category::Orange => "Orange".to_string(),
|
||||||
|
Category::Yellow => "Yellow".to_string(),
|
||||||
|
Category::Green => "Green".to_string(),
|
||||||
|
Category::Cyan => "Cyan".to_string(),
|
||||||
|
Category::Blue => "Blue".to_string(),
|
||||||
|
Category::Purple => "Purple".to_string(),
|
||||||
|
Category::Magenta => "Magenta".to_string(),
|
||||||
|
Category::White => "White".to_string(),
|
||||||
|
Category::Black => "Black".to_string(),
|
||||||
|
Category::Gray => "Gray".to_string(),
|
||||||
|
Category::Unknown => "Unknown".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
color-module/src/colorcards.json
Normal file
1
color-module/src/colorcards.json
Normal file
File diff suppressed because one or more lines are too long
@ -1,5 +1,6 @@
|
|||||||
use std::{str::FromStr, sync::Arc};
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
|
use color_card::Category;
|
||||||
use palette::{
|
use palette::{
|
||||||
cam16::{Cam16Jch, Parameters},
|
cam16::{Cam16Jch, Parameters},
|
||||||
color_difference::Wcag21RelativeContrast,
|
color_difference::Wcag21RelativeContrast,
|
||||||
@ -7,8 +8,10 @@ use palette::{
|
|||||||
convert::FromColorUnclamped,
|
convert::FromColorUnclamped,
|
||||||
Darken, FromColor, Hsl, IntoColor, IsWithinBounds, Lab, Lighten, Mix, Oklch, ShiftHue, Srgb,
|
Darken, FromColor, Hsl, IntoColor, IsWithinBounds, Lab, Lighten, Mix, Oklch, ShiftHue, Srgb,
|
||||||
};
|
};
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
mod color_card;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@ -377,3 +380,45 @@ pub fn tonal_darken_series(
|
|||||||
|
|
||||||
Ok(color_series.into_iter().rev().collect())
|
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())
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user