Files
goldenChart/frontend/src/utils/colorSpaceUtils.ts
T
2026-05-23 15:11:48 +09:00

103 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** HEX · RGB · HSV 변환 (색상 팔레트 UI용) */
export interface Rgb {
r: number;
g: number;
b: number;
}
export interface Hsv {
/** 0360 */
h: number;
/** 01 */
s: number;
/** 01 */
v: number;
}
function clamp(n: number, min: number, max: number): number {
return Math.max(min, Math.min(max, n));
}
export function normalizeHex6(hex: string): string {
const h = hex.trim();
if (/^#[0-9A-Fa-f]{6}$/.test(h)) return h.toUpperCase();
if (/^#[0-9A-Fa-f]{3}$/.test(h)) {
const c = h.slice(1);
return ('#' + c[0] + c[0] + c[1] + c[1] + c[2] + c[2]).toUpperCase();
}
return '#2962FF';
}
export function hexToRgb(hex: string): Rgb {
const h = normalizeHex6(hex).slice(1);
return {
r: parseInt(h.slice(0, 2), 16),
g: parseInt(h.slice(2, 4), 16),
b: parseInt(h.slice(4, 6), 16),
};
}
export function rgbToHex(r: number, g: number, b: number): string {
const to = (n: number) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0');
return `#${to(r)}${to(g)}${to(b)}`.toUpperCase();
}
export function rgbToHsv({ r, g, b }: Rgb): Hsv {
const rn = r / 255;
const gn = g / 255;
const bn = b / 255;
const max = Math.max(rn, gn, bn);
const min = Math.min(rn, gn, bn);
const d = max - min;
let h = 0;
if (d !== 0) {
if (max === rn) h = ((gn - bn) / d) % 6;
else if (max === gn) h = (bn - rn) / d + 2;
else h = (rn - gn) / d + 4;
h *= 60;
if (h < 0) h += 360;
}
const s = max === 0 ? 0 : d / max;
return { h, s, v: max };
}
export function hsvToRgb({ h, s, v }: Hsv): Rgb {
const c = v * s;
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
const m = v - c;
let rp = 0;
let gp = 0;
let bp = 0;
if (h < 60) { rp = c; gp = x; }
else if (h < 120) { rp = x; gp = c; }
else if (h < 180) { gp = c; bp = x; }
else if (h < 240) { gp = x; bp = c; }
else if (h < 300) { rp = x; bp = c; }
else { rp = c; bp = x; }
return {
r: Math.round((rp + m) * 255),
g: Math.round((gp + m) * 255),
b: Math.round((bp + m) * 255),
};
}
export function hexToHsv(hex: string): Hsv {
return rgbToHsv(hexToRgb(hex));
}
export function hsvToHex(hsv: Hsv): string {
const { r, g, b } = hsvToRgb(hsv);
return rgbToHex(r, g, b);
}
/** 채도·명도 영역 배경 (hue 고정) */
export function svPanelBackground(hue: number): string {
const pure = hsvToHex({ h: hue, s: 1, v: 1 });
return `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${pure})`;
}
/** 색상(Hue) 슬라이더 배경 */
export const HUE_SLIDER_BG =
'linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)';