增加Material Design 2主题样式的生成和导出。
This commit is contained in:
parent
d07fe9d41a
commit
119436608a
|
@ -1,7 +1,7 @@
|
|||
use palette::{
|
||||
cam16::{Cam16Jch, Parameters},
|
||||
convert::FromColorUnclamped,
|
||||
IsWithinBounds, Srgb,
|
||||
Hsl, IsWithinBounds, Srgb,
|
||||
};
|
||||
|
||||
pub fn map_cam16jch_to_srgb(origin: &Cam16Jch<f32>) -> Srgb {
|
||||
|
@ -24,3 +24,23 @@ pub fn map_cam16jch_to_srgb(origin: &Cam16Jch<f32>) -> Srgb {
|
|||
pub fn map_cam16jch_to_srgb_hex(origin: &Cam16Jch<f32>) -> String {
|
||||
format!("{:x}", map_cam16jch_to_srgb(origin).into_format::<u8>())
|
||||
}
|
||||
|
||||
pub fn map_hsl_to_srgb(origin: &Hsl) -> Srgb {
|
||||
let mut new_original = Hsl::new(origin.hue, origin.saturation, origin.lightness);
|
||||
const FACTOR: f32 = 0.99;
|
||||
loop {
|
||||
let new_srgb = Srgb::from_color_unclamped(new_original);
|
||||
if new_srgb.is_within_bounds() {
|
||||
break new_srgb;
|
||||
}
|
||||
new_original = Hsl::new(
|
||||
new_original.hue,
|
||||
new_original.saturation * FACTOR,
|
||||
new_original.lightness,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_hsl_to_srgb_hex(origin: &Hsl) -> String {
|
||||
format!("{:x}", map_hsl_to_srgb(origin).into_format::<u8>())
|
||||
}
|
||||
|
|
146
color-module/src/schemes/material_design_2/baseline.rs
Normal file
146
color-module/src/schemes/material_design_2/baseline.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use serde::{ser::SerializeStruct, Serialize};
|
||||
|
||||
use crate::{convert::map_hsl_to_srgb_hex, errors, schemes::material_design_2::swatch::M2Swatch};
|
||||
|
||||
use super::{color_set::M2ColorSet, swatch::SwatchIndex};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct M2BaselineColors {
|
||||
pub primary: M2ColorSet,
|
||||
pub secondary: M2ColorSet,
|
||||
pub error: M2ColorSet,
|
||||
pub background: M2ColorSet,
|
||||
pub surface: M2ColorSet,
|
||||
pub shadow: String,
|
||||
pub custom_colors: HashMap<String, M2ColorSet>,
|
||||
neutral_swatch: M2Swatch,
|
||||
dark_set: bool,
|
||||
}
|
||||
|
||||
impl M2BaselineColors {
|
||||
pub fn new(
|
||||
primary_color: &str,
|
||||
secondary_color: &str,
|
||||
error_color: &str,
|
||||
dark_baseline: bool,
|
||||
) -> Result<Self, errors::ColorError> {
|
||||
let primary_swatch = M2Swatch::from_color(primary_color)?;
|
||||
let secondary_swatch = M2Swatch::from_color(secondary_color)?;
|
||||
let error_swatch = M2Swatch::from_color(error_color)?;
|
||||
let neutral_swatch = M2Swatch::default_neutral();
|
||||
|
||||
Ok(Self {
|
||||
primary: M2ColorSet::from_swatch(
|
||||
&primary_swatch,
|
||||
&neutral_swatch,
|
||||
dark_baseline,
|
||||
None,
|
||||
)?,
|
||||
secondary: M2ColorSet::from_swatch(
|
||||
&secondary_swatch,
|
||||
&neutral_swatch,
|
||||
dark_baseline,
|
||||
None,
|
||||
)?,
|
||||
error: M2ColorSet::from_swatch(&error_swatch, &neutral_swatch, dark_baseline, None)?,
|
||||
background: M2ColorSet::from_swatch(
|
||||
&neutral_swatch,
|
||||
&neutral_swatch,
|
||||
dark_baseline,
|
||||
None,
|
||||
)?,
|
||||
surface: M2ColorSet::from_swatch(
|
||||
&neutral_swatch,
|
||||
&neutral_swatch,
|
||||
dark_baseline,
|
||||
None,
|
||||
)?,
|
||||
shadow: map_hsl_to_srgb_hex(&neutral_swatch.tone(SwatchIndex::SI900)),
|
||||
custom_colors: HashMap::new(),
|
||||
neutral_swatch,
|
||||
dark_set: dark_baseline,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_custom_color(&mut self, name: &str, color: &str) -> Result<(), errors::ColorError> {
|
||||
let swatch = M2Swatch::from_color(color)?;
|
||||
self.custom_colors.insert(
|
||||
name.to_string(),
|
||||
M2ColorSet::from_swatch(&swatch, &self.neutral_swatch, self.dark_set, None)?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_css_variable(&self) -> Vec<String> {
|
||||
let mut variable_lines = Vec::new();
|
||||
let prefix = if self.dark_set { "dark" } else { "light" };
|
||||
|
||||
variable_lines.extend(self.primary.to_css_variable(&prefix, "primary"));
|
||||
variable_lines.extend(self.secondary.to_css_variable(&prefix, "secondary"));
|
||||
variable_lines.extend(self.error.to_css_variable(&prefix, "error"));
|
||||
variable_lines.extend(self.background.to_css_variable(&prefix, "background"));
|
||||
variable_lines.extend(self.surface.to_css_variable(&prefix, "surface"));
|
||||
variable_lines.push(format!("--color-{}-shadow: #{};", prefix, self.shadow));
|
||||
|
||||
for (name, color_set) in &self.custom_colors {
|
||||
variable_lines.extend(color_set.to_css_variable(&prefix, name));
|
||||
}
|
||||
|
||||
variable_lines
|
||||
}
|
||||
|
||||
pub fn to_scss_variable(&self) -> Vec<String> {
|
||||
let mut variable_lines = Vec::new();
|
||||
let prefix = if self.dark_set { "dark" } else { "light" };
|
||||
|
||||
variable_lines.extend(self.primary.to_scss_variable(&prefix, "primary"));
|
||||
variable_lines.extend(self.secondary.to_scss_variable(&prefix, "secondary"));
|
||||
variable_lines.extend(self.error.to_scss_variable(&prefix, "error"));
|
||||
variable_lines.extend(self.background.to_scss_variable(&prefix, "background"));
|
||||
variable_lines.extend(self.surface.to_scss_variable(&prefix, "surface"));
|
||||
variable_lines.push(format!("$color-{}-shadow: #{};", prefix, self.shadow));
|
||||
|
||||
for (name, color_set) in &self.custom_colors {
|
||||
variable_lines.extend(color_set.to_scss_variable(&prefix, name));
|
||||
}
|
||||
|
||||
variable_lines
|
||||
}
|
||||
|
||||
pub fn to_javascript_object(&self) -> Vec<String> {
|
||||
let mut variable_lines = Vec::new();
|
||||
let prefix = if self.dark_set { "dark" } else { "light" };
|
||||
|
||||
variable_lines.extend(self.primary.to_javascript_object(&prefix, "primary"));
|
||||
variable_lines.extend(self.secondary.to_javascript_object(&prefix, "secondary"));
|
||||
variable_lines.extend(self.error.to_javascript_object(&prefix, "error"));
|
||||
variable_lines.extend(self.background.to_javascript_object(&prefix, "background"));
|
||||
variable_lines.extend(self.surface.to_javascript_object(&prefix, "surface"));
|
||||
variable_lines.push(format!("{}Shadow: '#{}',", prefix, self.shadow));
|
||||
|
||||
for (name, color_set) in &self.custom_colors {
|
||||
variable_lines.extend(color_set.to_javascript_object(&prefix, name));
|
||||
}
|
||||
|
||||
variable_lines
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for M2BaselineColors {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_struct("baseline", 7)?;
|
||||
map.serialize_field("primary", &self.primary)?;
|
||||
map.serialize_field("secondary", &self.secondary)?;
|
||||
map.serialize_field("error", &self.error)?;
|
||||
map.serialize_field("background", &self.background)?;
|
||||
map.serialize_field("surface", &self.surface)?;
|
||||
map.serialize_field("shadow", &self.shadow)?;
|
||||
map.serialize_field("custom_colors", &self.custom_colors)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
94
color-module/src/schemes/material_design_2/color_set.rs
Normal file
94
color-module/src/schemes/material_design_2/color_set.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{convert::map_hsl_to_srgb_hex, errors};
|
||||
|
||||
use super::swatch::M2Swatch;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct M2ColorSet {
|
||||
pub root: String,
|
||||
pub variant: String,
|
||||
pub on: String,
|
||||
}
|
||||
|
||||
impl M2ColorSet {
|
||||
pub fn from_swatch(
|
||||
swatch: &M2Swatch,
|
||||
neutral: &M2Swatch,
|
||||
dark: bool,
|
||||
required_wacg_ratio: Option<f32>,
|
||||
) -> Result<Self, errors::ColorError> {
|
||||
let root_color = if dark {
|
||||
map_hsl_to_srgb_hex(&swatch.desaturated_key_tone())
|
||||
} else {
|
||||
map_hsl_to_srgb_hex(&swatch.key_tone())
|
||||
};
|
||||
if dark {
|
||||
Ok(Self {
|
||||
variant: map_hsl_to_srgb_hex(&swatch.desaturated_tone(&swatch.key_index - 2)),
|
||||
on: match required_wacg_ratio {
|
||||
Some(ratio) => map_hsl_to_srgb_hex(
|
||||
&neutral.desaturated_wacg_min_above(&root_color, ratio)?,
|
||||
),
|
||||
None => map_hsl_to_srgb_hex(&neutral.desaturated_wacg_max_tone(&root_color)?),
|
||||
},
|
||||
root: root_color,
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
variant: map_hsl_to_srgb_hex(&swatch.tone(&swatch.key_index + 2)),
|
||||
on: match required_wacg_ratio {
|
||||
Some(ratio) => {
|
||||
map_hsl_to_srgb_hex(&neutral.wacg_min_above(&root_color, ratio)?)
|
||||
}
|
||||
None => map_hsl_to_srgb_hex(&neutral.wacg_max_tone(&root_color)?),
|
||||
},
|
||||
root: root_color,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_css_variable(&self, prefix: &str, name: &str) -> Vec<String> {
|
||||
let mut variable_lines = Vec::new();
|
||||
|
||||
variable_lines.push(format!("--color-{}-{}: #{};", prefix, name, self.root));
|
||||
variable_lines.push(format!(
|
||||
"--color-{}-{}-variant: #{};",
|
||||
prefix, name, self.variant
|
||||
));
|
||||
variable_lines.push(format!("--color-{}-on-{}: #{};", prefix, name, self.on));
|
||||
|
||||
variable_lines
|
||||
}
|
||||
|
||||
pub fn to_scss_variable(&self, prefix: &str, name: &str) -> Vec<String> {
|
||||
let mut variable_lines = Vec::new();
|
||||
|
||||
variable_lines.push(format!("$color-{}-{}: #{};", prefix, name, self.root));
|
||||
variable_lines.push(format!(
|
||||
"$color-{}-{}-variant: #{};",
|
||||
prefix, name, self.variant
|
||||
));
|
||||
variable_lines.push(format!("$color-{}-on-{}: #{};", prefix, name, self.on));
|
||||
|
||||
variable_lines
|
||||
}
|
||||
|
||||
pub fn to_javascript_object(&self, prefix: &str, name: &str) -> Vec<String> {
|
||||
let mut variable_lines = Vec::new();
|
||||
let prefix = prefix.to_ascii_lowercase();
|
||||
let name = name
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_ascii_uppercase()
|
||||
.to_string()
|
||||
+ &name[1..];
|
||||
|
||||
variable_lines.push(format!("{}{}: '#{}',", prefix, name, self.root));
|
||||
variable_lines.push(format!("{}{}Variant: '#{}',", prefix, name, self.variant));
|
||||
variable_lines.push(format!("{}On{}: '#{}',", prefix, name, self.on));
|
||||
|
||||
variable_lines
|
||||
}
|
||||
}
|
95
color-module/src/schemes/material_design_2/mod.rs
Normal file
95
color-module/src/schemes/material_design_2/mod.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use baseline::M2BaselineColors;
|
||||
use palette::Hsl;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{convert::map_hsl_to_srgb_hex, errors};
|
||||
|
||||
use super::SchemeExport;
|
||||
|
||||
pub mod baseline;
|
||||
pub mod color_set;
|
||||
pub mod swatch;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct MaterialDesign2Scheme {
|
||||
pub white: String,
|
||||
pub black: String,
|
||||
pub light: M2BaselineColors,
|
||||
pub dark: M2BaselineColors,
|
||||
}
|
||||
|
||||
impl MaterialDesign2Scheme {
|
||||
pub fn new(primary: &str, secondary: &str, error: &str) -> Result<Self, errors::ColorError> {
|
||||
Ok(Self {
|
||||
white: map_hsl_to_srgb_hex(&Hsl::new(0.0, 0.0, 1.0)),
|
||||
black: map_hsl_to_srgb_hex(&Hsl::new(0.0, 0.0, 0.0)),
|
||||
light: M2BaselineColors::new(primary, secondary, error, false)?,
|
||||
dark: M2BaselineColors::new(primary, secondary, error, true)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_custom_color(&mut self, name: &str, color: &str) -> Result<(), errors::ColorError> {
|
||||
self.light.add_custom_color(name, color)?;
|
||||
self.dark.add_custom_color(name, color)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeExport for MaterialDesign2Scheme {
|
||||
fn output_css_variables(&self) -> String {
|
||||
let mut css_variables = Vec::new();
|
||||
|
||||
css_variables.push(format!("--color-white: #{};", self.white));
|
||||
css_variables.push(format!("--color-black: #{};", self.black));
|
||||
|
||||
css_variables.extend(self.light.to_css_variable());
|
||||
css_variables.extend(self.dark.to_css_variable());
|
||||
|
||||
css_variables.join("\n")
|
||||
}
|
||||
|
||||
fn output_scss_variables(&self) -> String {
|
||||
let mut scss_variables = Vec::new();
|
||||
|
||||
scss_variables.push(format!("$color-white: #{};", self.white));
|
||||
scss_variables.push(format!("$color-black: #{};", self.black));
|
||||
|
||||
scss_variables.extend(self.light.to_scss_variable());
|
||||
scss_variables.extend(self.dark.to_scss_variable());
|
||||
|
||||
scss_variables.join("\n")
|
||||
}
|
||||
|
||||
fn output_javascript_object(&self) -> String {
|
||||
let mut js_object = Vec::new();
|
||||
|
||||
js_object.push("{".to_string());
|
||||
|
||||
js_object.push(format!(" white: '#{}'", self.white));
|
||||
js_object.push(format!(" black: '#{}'", self.black));
|
||||
|
||||
js_object.push(" light: {".to_string());
|
||||
js_object.extend(
|
||||
self.light
|
||||
.to_javascript_object()
|
||||
.into_iter()
|
||||
.map(|s| format!(" {}", s))
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
js_object.push(" }".to_string());
|
||||
|
||||
js_object.push(" dark: {".to_string());
|
||||
js_object.extend(
|
||||
self.dark
|
||||
.to_javascript_object()
|
||||
.into_iter()
|
||||
.map(|s| format!(" {}", s))
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
js_object.push(" }".to_string());
|
||||
|
||||
js_object.push("}".to_string());
|
||||
|
||||
js_object.join("\n")
|
||||
}
|
||||
}
|
306
color-module/src/schemes/material_design_2/swatch.rs
Normal file
306
color-module/src/schemes/material_design_2/swatch.rs
Normal file
|
@ -0,0 +1,306 @@
|
|||
use std::{
|
||||
ops::{Add, Sub},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use palette::{color_difference::Wcag21RelativeContrast, FromColor, Hsl, Srgb};
|
||||
|
||||
use crate::{convert::map_hsl_to_srgb, errors};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct M2Swatch {
|
||||
_key_color: Hsl,
|
||||
swatch: Vec<Hsl>,
|
||||
desaturated_swatch: Vec<Hsl>,
|
||||
pub key_index: SwatchIndex,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SwatchIndex {
|
||||
SI900,
|
||||
SI800,
|
||||
SI700,
|
||||
SI600,
|
||||
SI500,
|
||||
SI400,
|
||||
SI300,
|
||||
SI200,
|
||||
SI100,
|
||||
SI50,
|
||||
}
|
||||
|
||||
impl SwatchIndex {
|
||||
pub fn to_usize(&self) -> usize {
|
||||
match self {
|
||||
SwatchIndex::SI900 => 0,
|
||||
SwatchIndex::SI800 => 1,
|
||||
SwatchIndex::SI700 => 2,
|
||||
SwatchIndex::SI600 => 3,
|
||||
SwatchIndex::SI500 => 4,
|
||||
SwatchIndex::SI400 => 5,
|
||||
SwatchIndex::SI300 => 6,
|
||||
SwatchIndex::SI200 => 7,
|
||||
SwatchIndex::SI100 => 8,
|
||||
SwatchIndex::SI50 => 9,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_usize(index: usize) -> Self {
|
||||
match index {
|
||||
0 => SwatchIndex::SI900,
|
||||
1 => SwatchIndex::SI800,
|
||||
2 => SwatchIndex::SI700,
|
||||
3 => SwatchIndex::SI600,
|
||||
4 => SwatchIndex::SI500,
|
||||
5 => SwatchIndex::SI400,
|
||||
6 => SwatchIndex::SI300,
|
||||
7 => SwatchIndex::SI200,
|
||||
8 => SwatchIndex::SI100,
|
||||
9 => SwatchIndex::SI50,
|
||||
_ => panic!("Invalid index"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<i16> for SwatchIndex {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: i16) -> Self::Output {
|
||||
let index = (self.to_usize() as i16 + rhs).clamp(0, 9);
|
||||
SwatchIndex::from_usize(index as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<i16> for &SwatchIndex {
|
||||
type Output = SwatchIndex;
|
||||
|
||||
fn add(self, rhs: i16) -> Self::Output {
|
||||
let index = (self.to_usize() as i16 + rhs).clamp(0, 9);
|
||||
SwatchIndex::from_usize(index as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<i16> for SwatchIndex {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: i16) -> Self::Output {
|
||||
let index = (self.to_usize() as i16 - rhs).clamp(0, 9);
|
||||
SwatchIndex::from_usize(index as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<i16> for &SwatchIndex {
|
||||
type Output = SwatchIndex;
|
||||
|
||||
fn sub(self, rhs: i16) -> Self::Output {
|
||||
let index = (self.to_usize() as i16 - rhs).clamp(0, 9);
|
||||
SwatchIndex::from_usize(index as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_key_color_place(value: f32, min_value: f32, max_value: f32) -> usize {
|
||||
let interval = (max_value - min_value) / 9.0;
|
||||
let values = (0..=9)
|
||||
.map(|i| min_value + interval * i as f32)
|
||||
.collect::<Vec<f32>>();
|
||||
|
||||
let mut closest_index = 0;
|
||||
let mut min_diff = (value - values[closest_index]).abs();
|
||||
|
||||
for (i, v) in values.iter().enumerate() {
|
||||
let diff = (value - v).abs();
|
||||
if diff < min_diff {
|
||||
min_diff = diff;
|
||||
closest_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
closest_index
|
||||
}
|
||||
|
||||
impl M2Swatch {
|
||||
pub fn default_neutral() -> Self {
|
||||
let key_color = Hsl::new(0.0, 0.0, 0.0);
|
||||
let mut swatch = Vec::new();
|
||||
|
||||
let interval: f32 = 1.0 / 9.0;
|
||||
for i in 0..=9 {
|
||||
swatch.push(Hsl::new(0.0, 0.0, interval * i as f32));
|
||||
}
|
||||
|
||||
Self {
|
||||
_key_color: key_color,
|
||||
desaturated_swatch: swatch.clone(),
|
||||
swatch,
|
||||
key_index: SwatchIndex::SI900,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_color(color: &str) -> Result<Self, errors::ColorError> {
|
||||
let key_color = Hsl::from_color(
|
||||
Srgb::from_str(color)
|
||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(color.to_string()))?
|
||||
.into_format::<f32>(),
|
||||
);
|
||||
let mut swatch = Vec::new();
|
||||
let mut desaturated_swatch = Vec::new();
|
||||
|
||||
let max_lightness = key_color.lightness.max(0.95);
|
||||
let min_lightness = key_color.lightness.min(0.2);
|
||||
let max_staturation = key_color.saturation.max(0.80);
|
||||
let min_staturation = key_color.saturation.min(0.45);
|
||||
|
||||
let key_place = find_key_color_place(key_color.lightness, min_lightness, max_lightness);
|
||||
|
||||
if key_place > 0 {
|
||||
let lightness_interval = (key_color.lightness - min_lightness) / key_place as f32;
|
||||
let saturation_interval = (max_staturation - key_color.saturation) / key_place as f32;
|
||||
for i in 0..key_place {
|
||||
swatch.push(Hsl::new(
|
||||
key_color.hue,
|
||||
max_staturation - saturation_interval * i as f32,
|
||||
min_lightness + lightness_interval * i as f32,
|
||||
));
|
||||
desaturated_swatch.push(Hsl::new(
|
||||
key_color.hue,
|
||||
(min_staturation + saturation_interval * i as f32) * 0.7_f32,
|
||||
min_lightness + lightness_interval * i as f32,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
swatch.push(key_color.clone());
|
||||
|
||||
if key_place < 9 {
|
||||
let lightness_interval = (max_lightness - key_color.lightness) / (9 - key_place) as f32;
|
||||
let saturation_interval =
|
||||
(key_color.saturation - min_staturation) / (9 - key_place) as f32;
|
||||
for i in 1..=9 - key_place {
|
||||
swatch.push(Hsl::new(
|
||||
key_color.hue,
|
||||
key_color.saturation - saturation_interval * i as f32,
|
||||
key_color.lightness + lightness_interval * i as f32,
|
||||
));
|
||||
desaturated_swatch.push(Hsl::new(
|
||||
key_color.hue,
|
||||
(min_staturation + saturation_interval * i as f32) * 0.7_f32,
|
||||
key_color.lightness + lightness_interval * i as f32,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
_key_color: key_color,
|
||||
swatch,
|
||||
desaturated_swatch,
|
||||
key_index: SwatchIndex::from_usize(key_place),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tone(&self, index: SwatchIndex) -> Hsl {
|
||||
self.swatch[index.to_usize()].clone()
|
||||
}
|
||||
|
||||
pub fn key_tone(&self) -> Hsl {
|
||||
self.swatch[self.key_index.to_usize()].clone()
|
||||
}
|
||||
|
||||
pub fn desaturated_tone(&self, index: SwatchIndex) -> Hsl {
|
||||
self.desaturated_swatch[index.to_usize()].clone()
|
||||
}
|
||||
|
||||
pub fn desaturated_key_tone(&self) -> Hsl {
|
||||
self.desaturated_swatch[self.key_index.to_usize()].clone()
|
||||
}
|
||||
|
||||
pub fn wacg_max_tone(&self, reference_color: &str) -> Result<Hsl, errors::ColorError> {
|
||||
let reference_color = Srgb::from_str(reference_color)
|
||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(reference_color.to_string()))?
|
||||
.into_format::<f32>();
|
||||
|
||||
let mut max_wacg_index = 0;
|
||||
let mut max_wacg = 0.0;
|
||||
|
||||
for (i, tone) in self.swatch.iter().enumerate() {
|
||||
let swatch_color = map_hsl_to_srgb(tone);
|
||||
let wacg = swatch_color.relative_contrast(reference_color);
|
||||
if wacg > max_wacg {
|
||||
max_wacg = wacg;
|
||||
max_wacg_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.swatch[max_wacg_index].clone())
|
||||
}
|
||||
|
||||
pub fn desaturated_wacg_max_tone(
|
||||
&self,
|
||||
reference_color: &str,
|
||||
) -> Result<Hsl, errors::ColorError> {
|
||||
let reference_color = Srgb::from_str(reference_color)
|
||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(reference_color.to_string()))?
|
||||
.into_format::<f32>();
|
||||
|
||||
let mut max_wacg_index = 0;
|
||||
let mut max_wacg = 0.0;
|
||||
|
||||
for (i, tone) in self.desaturated_swatch.iter().enumerate() {
|
||||
let swatch_color = map_hsl_to_srgb(tone);
|
||||
let wacg = swatch_color.relative_contrast(reference_color);
|
||||
if wacg > max_wacg {
|
||||
max_wacg = wacg;
|
||||
max_wacg_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.desaturated_swatch[max_wacg_index].clone())
|
||||
}
|
||||
|
||||
pub fn wacg_min_above(
|
||||
&self,
|
||||
reference_color: &str,
|
||||
ratio: f32,
|
||||
) -> Result<Hsl, errors::ColorError> {
|
||||
let reference_color = Srgb::from_str(reference_color)
|
||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(reference_color.to_string()))?
|
||||
.into_format::<f32>();
|
||||
|
||||
let mut min_wacg_index = 0;
|
||||
let mut min_wacg = 21.0;
|
||||
|
||||
for (i, tone) in self.swatch.iter().enumerate() {
|
||||
let swatch_color = map_hsl_to_srgb(tone);
|
||||
let wacg = swatch_color.relative_contrast(reference_color);
|
||||
if wacg < min_wacg && wacg > ratio {
|
||||
min_wacg = wacg;
|
||||
min_wacg_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.swatch[min_wacg_index].clone())
|
||||
}
|
||||
|
||||
pub fn desaturated_wacg_min_above(
|
||||
&self,
|
||||
reference_color: &str,
|
||||
ratio: f32,
|
||||
) -> Result<Hsl, errors::ColorError> {
|
||||
let reference_color = Srgb::from_str(reference_color)
|
||||
.map_err(|_| errors::ColorError::UnrecogniazedRGB(reference_color.to_string()))?
|
||||
.into_format::<f32>();
|
||||
|
||||
let mut min_wacg_index = 0;
|
||||
let mut min_wacg = 21.0;
|
||||
|
||||
for (i, tone) in self.desaturated_swatch.iter().enumerate() {
|
||||
let swatch_color = map_hsl_to_srgb(tone);
|
||||
let wacg = swatch_color.relative_contrast(reference_color);
|
||||
if wacg < min_wacg && wacg > ratio {
|
||||
min_wacg = wacg;
|
||||
min_wacg_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.desaturated_swatch[min_wacg_index].clone())
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use material_design_2::MaterialDesign2Scheme;
|
||||
use material_design_3::MaterialDesign3Scheme;
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||
|
||||
use crate::errors;
|
||||
|
||||
pub mod material_design_2;
|
||||
pub mod material_design_3;
|
||||
|
||||
pub trait SchemeExport {
|
||||
|
@ -33,3 +35,25 @@ pub fn generate_material_design_3_scheme(
|
|||
))
|
||||
.map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn generate_material_design_2_scheme(
|
||||
primary_color: &str,
|
||||
secondary_color: &str,
|
||||
error_color: &str,
|
||||
custom_colors: JsValue,
|
||||
) -> Result<JsValue, errors::ColorError> {
|
||||
let custom_colors: HashMap<String, String> = serde_wasm_bindgen::from_value(custom_colors)
|
||||
.map_err(|_| errors::ColorError::UnableToParseArgument)?;
|
||||
let mut scheme = MaterialDesign2Scheme::new(primary_color, secondary_color, error_color)?;
|
||||
for (name, color) in custom_colors {
|
||||
scheme.add_custom_color(&name, &color)?;
|
||||
}
|
||||
Ok(serde_wasm_bindgen::to_value(&(
|
||||
scheme.clone(),
|
||||
scheme.output_css_variables(),
|
||||
scheme.output_scss_variables(),
|
||||
scheme.output_javascript_object(),
|
||||
))
|
||||
.map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user