Compare commits

...

2 Commits

Author SHA1 Message Date
徐涛
c9626f3b8e 基本完成自动色板生成功能。 2025-01-16 15:47:35 +08:00
徐涛
b3cdb517a5 WASM包中增加自动生成色板的功能。 2025-01-16 15:18:56 +08:00
10 changed files with 238 additions and 44 deletions

View File

@ -15,6 +15,7 @@ use wasm_bindgen::prelude::*;
mod color_card;
mod color_differ;
mod errors;
mod palettes;
mod reversing;
#[wasm_bindgen]

View File

@ -0,0 +1,94 @@
use palette::{FromColor, Oklch, Srgb};
use std::str::FromStr;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::errors;
#[wasm_bindgen]
pub fn generate_palette_from_color(
reference_color: &str,
swatch_amount: i16,
minimum_lightness: f32,
maximum_lightness: f32,
use_reference_color: Option<bool>,
reference_color_bias: Option<i16>,
) -> Result<Vec<String>, errors::ColorError> {
let reference_color_bias = reference_color_bias.unwrap_or(0);
let original_color = Oklch::from_color(
Srgb::from_str(reference_color)
.map_err(|_| errors::ColorError::UnrecogniazedRGB(reference_color.to_string()))?
.into_format::<f32>(),
);
match use_reference_color {
Some(true) => Ok(generate_incontinuous_palette(
&original_color,
swatch_amount,
minimum_lightness,
maximum_lightness,
reference_color_bias,
)),
Some(false) | None => Ok(generate_continuous_palette(
&original_color,
swatch_amount,
minimum_lightness,
maximum_lightness,
)),
}
}
fn generate_continuous_palette(
original_color: &Oklch,
swatch_amount: i16,
minimum_lightness: f32,
maximum_lightness: f32,
) -> Vec<String> {
let mut palette = Vec::new();
let step = (maximum_lightness - minimum_lightness) / (swatch_amount - 1) as f32;
for i in 0..swatch_amount {
let lightness = minimum_lightness + step * i as f32;
let color = Oklch {
l: lightness,
..*original_color
};
palette.push(format!("{:x}", Srgb::from_color(color).into_format::<u8>()));
}
palette
}
fn generate_incontinuous_palette(
original_color: &Oklch,
swatch_amount: i16,
minimum_lightness: f32,
maximum_lightness: f32,
original_place: i16,
) -> Vec<String> {
let mut palette = Vec::new();
let midpoint = swatch_amount / 2;
let dark_side_amount = midpoint + original_place;
let step = (original_color.l - minimum_lightness) / dark_side_amount as f32;
for i in 0..dark_side_amount {
let lightness = minimum_lightness + step * i as f32;
let color = Oklch {
l: lightness,
..*original_color
};
palette.push(format!("{:x}", Srgb::from_color(color).into_format::<u8>()));
}
let light_side_amount = swatch_amount - dark_side_amount;
let step = (maximum_lightness - original_color.l) / (light_side_amount - 1) as f32;
for i in 0..light_side_amount {
let lightness = original_color.l + step * i as f32;
let color = Oklch {
l: lightness,
..*original_color
};
palette.push(format!("{:x}", Srgb::from_color(color).into_format::<u8>()));
}
palette
}

View File

@ -0,0 +1 @@
pub mod auto_palette;

View File

@ -40,6 +40,7 @@ export function differ_in_oklch(color: string, other: string): OklchDifference;
export function relative_differ_in_oklch(color: string, other: string): OklchDifference;
export function tint_scale(basic_color: string, mixed_color: string): MixReversing;
export function shade_scale(basic_color: string, mixed_color: string): MixReversing;
export function generate_palette_from_color(reference_color: string, swatch_amount: number, minimum_lightness: number, maximum_lightness: number, use_reference_color?: boolean, reference_color_bias?: number): (string)[];
export class Differ {
private constructor();
free(): void;
@ -132,6 +133,20 @@ export interface InitOutput {
readonly __wbg_set_differ_delta: (a: number, b: number) => void;
readonly __wbg_get_differ_percent: (a: number) => number;
readonly __wbg_set_differ_percent: (a: number, b: number) => void;
readonly __wbg_hsldifference_free: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_hue: (a: number) => number;
readonly __wbg_set_hsldifference_hue: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_saturation: (a: number) => number;
readonly __wbg_set_hsldifference_saturation: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_lightness: (a: number) => number;
readonly __wbg_set_hsldifference_lightness: (a: number, b: number) => void;
readonly __wbg_oklchdifference_free: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_hue: (a: number) => number;
readonly __wbg_set_oklchdifference_hue: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_chroma: (a: number) => number;
readonly __wbg_set_oklchdifference_chroma: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_lightness: (a: number) => number;
readonly __wbg_set_oklchdifference_lightness: (a: number, b: number) => void;
readonly __wbg_mixreversing_free: (a: number, b: number) => void;
readonly __wbg_get_mixreversing_r_factor: (a: number) => number;
readonly __wbg_set_mixreversing_r_factor: (a: number, b: number) => void;
@ -141,27 +156,6 @@ export interface InitOutput {
readonly __wbg_set_mixreversing_b_factor: (a: number, b: number) => void;
readonly __wbg_get_mixreversing_average: (a: number) => number;
readonly __wbg_set_mixreversing_average: (a: number, b: number) => void;
readonly __wbg_oklchdifference_free: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_hue: (a: number) => number;
readonly __wbg_set_oklchdifference_hue: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_chroma: (a: number) => number;
readonly __wbg_set_oklchdifference_chroma: (a: number, b: number) => void;
readonly __wbg_get_oklchdifference_lightness: (a: number) => number;
readonly __wbg_set_oklchdifference_lightness: (a: number, b: number) => void;
readonly __wbg_rgbdifference_free: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_r: (a: number) => number;
readonly __wbg_set_rgbdifference_r: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_g: (a: number) => number;
readonly __wbg_set_rgbdifference_g: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_b: (a: number) => number;
readonly __wbg_set_rgbdifference_b: (a: number, b: number) => void;
readonly __wbg_hsldifference_free: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_hue: (a: number) => number;
readonly __wbg_set_hsldifference_hue: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_saturation: (a: number) => number;
readonly __wbg_set_hsldifference_saturation: (a: number, b: number) => void;
readonly __wbg_get_hsldifference_lightness: (a: number) => number;
readonly __wbg_set_hsldifference_lightness: (a: number, b: number) => void;
readonly __wbg_hctdiffference_free: (a: number, b: number) => void;
readonly __wbg_get_hctdiffference_hue: (a: number) => number;
readonly __wbg_set_hctdiffference_hue: (a: number, b: number) => void;
@ -169,6 +163,14 @@ export interface InitOutput {
readonly __wbg_set_hctdiffference_chroma: (a: number, b: number) => void;
readonly __wbg_get_hctdiffference_lightness: (a: number) => number;
readonly __wbg_set_hctdiffference_lightness: (a: number, b: number) => void;
readonly __wbg_rgbdifference_free: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_r: (a: number) => number;
readonly __wbg_set_rgbdifference_r: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_g: (a: number) => number;
readonly __wbg_set_rgbdifference_g: (a: number, b: number) => void;
readonly __wbg_get_rgbdifference_b: (a: number) => number;
readonly __wbg_set_rgbdifference_b: (a: number, b: number) => void;
readonly generate_palette_from_color: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number, number];
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_2: WebAssembly.Table;

View File

@ -914,6 +914,26 @@ function _assertClass(instance, klass) {
throw new Error(`expected instance of ${klass.name}`);
}
}
/**
* @param {string} reference_color
* @param {number} swatch_amount
* @param {number} minimum_lightness
* @param {number} maximum_lightness
* @param {boolean | undefined} [use_reference_color]
* @param {number | undefined} [reference_color_bias]
* @returns {(string)[]}
*/
export function generate_palette_from_color(reference_color, swatch_amount, minimum_lightness, maximum_lightness, use_reference_color, reference_color_bias) {
const ptr0 = passStringToWasm0(reference_color, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.generate_palette_from_color(ptr0, len0, swatch_amount, minimum_lightness, maximum_lightness, isLikeNone(use_reference_color) ? 0xFFFFFF : use_reference_color ? 1 : 0, isLikeNone(reference_color_bias) ? 0xFFFFFF : reference_color_bias);
if (ret[3]) {
throw takeFromExternrefTable0(ret[2]);
}
var v2 = getArrayJsValueFromWasm0(ret[0], ret[1]).slice();
wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
return v2;
}
const DifferFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }

View File

@ -46,6 +46,20 @@ export const __wbg_get_differ_delta: (a: number) => number;
export const __wbg_set_differ_delta: (a: number, b: number) => void;
export const __wbg_get_differ_percent: (a: number) => number;
export const __wbg_set_differ_percent: (a: number, b: number) => void;
export const __wbg_hsldifference_free: (a: number, b: number) => void;
export const __wbg_get_hsldifference_hue: (a: number) => number;
export const __wbg_set_hsldifference_hue: (a: number, b: number) => void;
export const __wbg_get_hsldifference_saturation: (a: number) => number;
export const __wbg_set_hsldifference_saturation: (a: number, b: number) => void;
export const __wbg_get_hsldifference_lightness: (a: number) => number;
export const __wbg_set_hsldifference_lightness: (a: number, b: number) => void;
export const __wbg_oklchdifference_free: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_hue: (a: number) => number;
export const __wbg_set_oklchdifference_hue: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_chroma: (a: number) => number;
export const __wbg_set_oklchdifference_chroma: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_lightness: (a: number) => number;
export const __wbg_set_oklchdifference_lightness: (a: number, b: number) => void;
export const __wbg_mixreversing_free: (a: number, b: number) => void;
export const __wbg_get_mixreversing_r_factor: (a: number) => number;
export const __wbg_set_mixreversing_r_factor: (a: number, b: number) => void;
@ -55,27 +69,6 @@ export const __wbg_get_mixreversing_b_factor: (a: number) => number;
export const __wbg_set_mixreversing_b_factor: (a: number, b: number) => void;
export const __wbg_get_mixreversing_average: (a: number) => number;
export const __wbg_set_mixreversing_average: (a: number, b: number) => void;
export const __wbg_oklchdifference_free: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_hue: (a: number) => number;
export const __wbg_set_oklchdifference_hue: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_chroma: (a: number) => number;
export const __wbg_set_oklchdifference_chroma: (a: number, b: number) => void;
export const __wbg_get_oklchdifference_lightness: (a: number) => number;
export const __wbg_set_oklchdifference_lightness: (a: number, b: number) => void;
export const __wbg_rgbdifference_free: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_r: (a: number) => number;
export const __wbg_set_rgbdifference_r: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_g: (a: number) => number;
export const __wbg_set_rgbdifference_g: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_b: (a: number) => number;
export const __wbg_set_rgbdifference_b: (a: number, b: number) => void;
export const __wbg_hsldifference_free: (a: number, b: number) => void;
export const __wbg_get_hsldifference_hue: (a: number) => number;
export const __wbg_set_hsldifference_hue: (a: number, b: number) => void;
export const __wbg_get_hsldifference_saturation: (a: number) => number;
export const __wbg_set_hsldifference_saturation: (a: number, b: number) => void;
export const __wbg_get_hsldifference_lightness: (a: number) => number;
export const __wbg_set_hsldifference_lightness: (a: number, b: number) => void;
export const __wbg_hctdiffference_free: (a: number, b: number) => void;
export const __wbg_get_hctdiffference_hue: (a: number) => number;
export const __wbg_set_hctdiffference_hue: (a: number, b: number) => void;
@ -83,6 +76,14 @@ export const __wbg_get_hctdiffference_chroma: (a: number) => number;
export const __wbg_set_hctdiffference_chroma: (a: number, b: number) => void;
export const __wbg_get_hctdiffference_lightness: (a: number) => number;
export const __wbg_set_hctdiffference_lightness: (a: number, b: number) => void;
export const __wbg_rgbdifference_free: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_r: (a: number) => number;
export const __wbg_set_rgbdifference_r: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_g: (a: number) => number;
export const __wbg_set_rgbdifference_g: (a: number, b: number) => void;
export const __wbg_get_rgbdifference_b: (a: number) => number;
export const __wbg_set_rgbdifference_b: (a: number, b: number) => void;
export const generate_palette_from_color: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number, number];
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export_2: WebAssembly.Table;

View File

@ -0,0 +1,55 @@
import { useMemo } from 'react';
import { useColorFunction } from '../../ColorFunctionContext';
import { FlexColorStand } from '../../components/FlexColorStand';
type PaletteColorsProps = {
color: string;
swatchAmount: number;
referenceBias: number;
useReference: boolean;
min: number;
max: number;
copyMode: 'hex' | 'rgb' | 'hsl' | 'lab' | 'oklch';
};
export function PaletteColors({
color = '000000',
swatchAmount,
referenceBias,
useReference,
min,
max,
copyMode,
}: PaletteColorsProps) {
const { colorFn } = useColorFunction();
const colors = useMemo(() => {
if (!colorFn) {
return Array.from({ length: swatchAmount }, () => color);
}
try {
if (!useReference) {
return colorFn.generate_palette_from_color(color, swatchAmount, min, max);
} else {
return colorFn.generate_palette_from_color(
color,
swatchAmount,
min,
max,
useReference,
referenceBias,
);
}
} catch (e) {
console.error('[Generate Auto Palette]', e);
}
return Array.from({ length: swatchAmount }, () => color);
}, [color, swatchAmount, referenceBias, useReference, min, max]);
return (
<>
{colors.map((c, index) => (
<FlexColorStand key={`${c}-${index}`} color={c} valueMode={copyMode} />
))}
</>
);
}

View File

@ -43,5 +43,13 @@
gap: var(--spacing-s);
font-size: var(--font-size-s);
}
.colors_booth {
display: flex;
flex-direction: row;
justify-content: center;
align-items: stretch;
gap: var(--spacing-xs);
min-height: 12em;
}
}
}

View File

@ -7,6 +7,7 @@ import { Labeled } from '../components/Labeled';
import { LabeledPicker } from '../components/LabeledPicker';
import { ScrollArea } from '../components/ScrollArea';
import { Switch } from '../components/Switch';
import { PaletteColors } from '../page-components/auto-palette/PaletteColors';
import { currentPickedColor } from '../stores/colors';
import styles from './Palette.module.css';
@ -53,14 +54,14 @@ export function AutomaticPalette() {
<LabeledPicker
title="Swatch Amount"
min={3}
max={15}
max={12}
step={1}
value={swatchAmount}
onChange={(value) => setSwatchAmount(value)}
/>
<LabeledPicker
title="Reference Bias"
min={0}
min={-maxBias}
max={maxBias}
step={1}
value={referenceBias}
@ -89,6 +90,17 @@ export function AutomaticPalette() {
</aside>
<div className={styles.palette_content}>
<h5>Generated Palette</h5>
<div className={styles.colors_booth}>
<PaletteColors
color={selectedColor}
swatchAmount={swatchAmount}
min={minLightness / 100}
max={maxLightness / 100}
useReference={useReferenceColor}
referenceBias={referenceBias}
copyMode={mode}
/>
</div>
<div className={styles.color_value_mode}>
<label>Copy color value in</label>
<HSegmentedControl