44 lines
1.9 KiB
TypeScript
44 lines
1.9 KiB
TypeScript
|
|
import type { FinishId } from './TraditionalSurface'
|
||
|
|
|
||
|
|
interface Rgb { r: number; g: number; b: number }
|
||
|
|
interface FinishProfile { background: string; foreground: string; tone: 'dark' | 'light' }
|
||
|
|
|
||
|
|
export const FINISH_PROFILES: Record<FinishId, FinishProfile> = {
|
||
|
|
aurora: { background: '#0a0e1f', foreground: '#eef1ff', tone: 'dark' },
|
||
|
|
nebula: { background: '#170b20', foreground: '#fdeef7', tone: 'dark' },
|
||
|
|
abyss: { background: '#04101c', foreground: '#e8f6ff', tone: 'dark' },
|
||
|
|
jade: { background: '#071410', foreground: '#eafaf2', tone: 'dark' },
|
||
|
|
cinnabar: { background: '#150a0a', foreground: '#fdf0ec', tone: 'dark' },
|
||
|
|
champagne: { background: '#141008', foreground: '#fdf4e6', tone: 'dark' },
|
||
|
|
porcelain: { background: '#e8edf6', foreground: '#1b2340', tone: 'light' },
|
||
|
|
snow: { background: '#e9f1f7', foreground: '#17273f', tone: 'light' },
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseHex(value: string): Rgb {
|
||
|
|
const normalized = value.replace('#', '')
|
||
|
|
return { r: parseInt(normalized.slice(0, 2), 16), g: parseInt(normalized.slice(2, 4), 16), b: parseInt(normalized.slice(4, 6), 16) }
|
||
|
|
}
|
||
|
|
|
||
|
|
function luminance({ r, g, b }: Rgb) {
|
||
|
|
const channel = (value: number) => {
|
||
|
|
const normalized = value / 255
|
||
|
|
return normalized <= .03928 ? normalized / 12.92 : Math.pow((normalized + .055) / 1.055, 2.4)
|
||
|
|
}
|
||
|
|
return .2126 * channel(r) + .7152 * channel(g) + .0722 * channel(b)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function contrastRatio(foreground: string, background: string) {
|
||
|
|
const a = luminance(parseHex(foreground))
|
||
|
|
const b = luminance(parseHex(background))
|
||
|
|
return (Math.max(a, b) + .05) / (Math.min(a, b) + .05)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function resolveVisualBalance(finish: FinishId, phase?: string, weather?: string) {
|
||
|
|
const profile = FINISH_PROFILES[finish]
|
||
|
|
return {
|
||
|
|
tone: profile.tone,
|
||
|
|
contrast: contrastRatio(profile.foreground, profile.background),
|
||
|
|
phase: phase?.toLowerCase() || 'time-pending',
|
||
|
|
weather: weather?.toLowerCase() || 'unavailable',
|
||
|
|
}
|
||
|
|
}
|