Last active
June 21, 2025 14:24
-
-
Save Unbinilium/b0b944e0886006f7e590b59f94c7f5e7 to your computer and use it in GitHub Desktop.
Color Conv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface CIE { | |
x: number; | |
y: number; | |
z: number; | |
}; | |
interface RGB { | |
r: number; | |
g: number; | |
b: number; | |
}; | |
interface HSV { | |
h: number; | |
s: number; | |
v: number; | |
}; | |
const gamma_correction_linear_2_srgb = (value: number): number => { | |
if (value <= 0.0031308) { | |
return 12.92 * value; | |
} else { | |
return (1.055 * Math.pow(value, 1.0 / 2.4)) - 0.055; | |
} | |
}; | |
const reference_brightness_normalized = (color: RGB): number => { | |
return (0.2126 * color.r) + (0.7152 * color.g) + (0.0722 * color.b); | |
}; | |
const raw_rgb_normalized_2_cie = (color: RGB, calibration_matrix: number[][]): CIE => { | |
const r = color.r; | |
const g = color.g; | |
const b = color.b; | |
const x = (r * calibration_matrix[0][0]) + (g * calibration_matrix[0][1]) + (b * calibration_matrix[0][2]); | |
const y = (r * calibration_matrix[1][0]) + (g * calibration_matrix[1][1]) + (b * calibration_matrix[1][2]); | |
const z = (r * calibration_matrix[2][0]) + (g * calibration_matrix[2][1]) + (b * calibration_matrix[2][2]); | |
return { x, y, z }; | |
}; | |
const cie_normalized_2_rgb_linear = (color: CIE): RGB => { | |
const x = color.x; | |
const y = color.y; | |
const z = color.z; | |
const r = (3.2406 * x) - (1.5372 * y) - (0.4986 * z); | |
const g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z); | |
const b = (0.0557 * x) - (0.2040 * y) + (1.0570 * z); | |
return { r, g, b }; | |
}; | |
const rgb_linear_apply_luminance = (color: RGB, luminance: number, eps: number = 0.0001): RGB => { | |
const current = reference_brightness_normalized(color); | |
if (current < eps) { | |
return { r: 0, g: 0, b: 0 }; | |
} | |
const scale = luminance / current; | |
return { | |
r: Math.min(1, color.r * scale), | |
g: Math.min(1, color.g * scale), | |
b: Math.min(1, color.b * scale) | |
}; | |
}; | |
const rgb_linear_2_srgb = (color: RGB): RGB => { | |
return { | |
r: gamma_correction_linear_2_srgb(color.r), | |
g: gamma_correction_linear_2_srgb(color.g), | |
b: gamma_correction_linear_2_srgb(color.b) | |
}; | |
}; | |
const srgb_normalized_2_srgb_quantized = (color: RGB): RGB => { | |
return { | |
r: Math.round(color.r * 255.0), | |
g: Math.round(color.g * 255.0), | |
b: Math.round(color.b * 255.0) | |
}; | |
}; | |
const srgb_normalized_2_hsv_normalized = (color: RGB): HSV => { | |
const r = color.r; | |
const g = color.g; | |
const b = color.b; | |
const maxc = Math.max(r, g, b); | |
const minc = Math.min(r, g, b); | |
const rangec = maxc - minc; | |
const v = maxc; | |
if (minc == maxc) { | |
return { h: 0, s: 0, v: v }; | |
} | |
const s = rangec / maxc; | |
const rc = (maxc - r) / rangec; | |
const gc = (maxc - g) / rangec; | |
const bc = (maxc - b) / rangec; | |
let h: number; | |
if (r == maxc) { | |
h = bc - gc; | |
} | |
else if (g == maxc) { | |
h = 2.0 + rc - bc; | |
} | |
else { | |
h = 4.0 + gc - rc; | |
} | |
h = (h / 6.0) % 1.0; | |
if (h < 0) { | |
h += 1.0; | |
} | |
return { h: h, s: s, v: v }; | |
}; | |
const hsv_normalized_color_strength = (current: HSV, target: HSV, weight: HSV): number => { | |
const delta_h = Math.abs(current.h - target.h); | |
const delta_s = Math.abs(current.s - target.s); | |
const delta_v = Math.abs(current.v - target.v); | |
const weighted_h = Math.min(delta_h, 1.0 - delta_h) * weight.h; | |
const weighted_s = delta_s * weight.s; | |
const weighted_v = delta_v * weight.v; | |
const strength = 1.0 - ((weighted_h + weighted_s + weighted_v) / 3.0); | |
return Math.max(0.0, Math.min(1.0, strength)); | |
}; | |
const hsv_normalized_presets: Map<string, HSV> = new Map([ | |
["red", { h: 0, s: 1, v: 1 }], | |
["green", { h: 0.3333, s: 1, v: 1 }], | |
["blue", { h: 0.6667, s: 1, v: 1 }], | |
["yellow", { h: 0.1667, s: 1, v: 1 }], | |
["cyan", { h: 0.5, s: 1, v: 1 }], | |
["magenta", { h: 0.8333, s: 1, v: 1 }], | |
["white", { h: 0, s: 0, v: 1 }], | |
["black", { h: 0, s: 0, v: 0 }], | |
]); | |
const hexToRgb = (hex: string): RGB => { | |
const bigint = parseInt(hex.slice(1), 16); | |
return { | |
r: ((bigint >> 16) & 0xff) / 255.0, | |
g: ((bigint >> 8) & 0xff) / 255.0, | |
b: (bigint & 0xff) / 255.0 | |
}; | |
}; | |
const printColorInfo = (hex: string): void => { | |
const rgb = hexToRgb(hex); | |
const hsv = srgb_normalized_2_hsv_normalized(rgb); | |
console.log(`Hex: ${hex} \x1b[38;2;${Math.round(rgb.r * 255)};${Math.round(rgb.g * 255)};${Math.round(rgb.b * 255)}m█\x1b[0m`); | |
console.log(`RGB: ${JSON.stringify(rgb)}`); | |
console.log(`HSV: ${JSON.stringify(hsv)}`); | |
console.log(`Strength against presets:`); | |
hsv_normalized_presets.forEach((preset, name) => { | |
const strength = hsv_normalized_color_strength(hsv, preset, { h: 8, s: 3, v: 3 }); | |
console.log(` ${name}: ${strength.toFixed(2)}`); | |
}); | |
}; | |
import * as readline from 'node:readline/promises'; | |
import { stdin as input, stdout as output } from 'node:process'; | |
async function main() { | |
const rl = readline.createInterface({ | |
input: process.stdin, | |
output: process.stdout | |
}); | |
while (true) { | |
try { | |
const hex = await rl.question('Enter a hex color (e.g., #ff5733): '); | |
if (!/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(hex)) { | |
console.log('Invalid hex color format. Please use #RRGGBB or #RGB.'); | |
continue; | |
} | |
console.clear(); | |
console.log(`Processing color: ${hex}`); | |
printColorInfo(hex); | |
} | |
catch (error) { | |
console.error('Error:', error); | |
rl.close(); | |
break; | |
} | |
} | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment