| import type { ColorAlleles, Hsv, Rgb } from './types'; |
|
|
| export function clampByte(value: number): number { |
| return Math.max(0, Math.min(255, Math.trunc(value))); |
| } |
|
|
| export function hsvToRgb(hueDeg: number, saturation: number, value: number): Rgb { |
| const h = (((hueDeg % 360) + 360) % 360) / 60; |
| const c = value * saturation; |
| const x = c * (1 - Math.abs((h % 2) - 1)); |
| const m = value - c; |
| let r = 0; |
| let g = 0; |
| let b = 0; |
|
|
| if (h < 1) { |
| r = c; |
| g = x; |
| } else if (h < 2) { |
| r = x; |
| g = c; |
| } else if (h < 3) { |
| g = c; |
| b = x; |
| } else if (h < 4) { |
| g = x; |
| b = c; |
| } else if (h < 5) { |
| r = x; |
| b = c; |
| } else { |
| r = c; |
| b = x; |
| } |
|
|
| return [ |
| clampByte((r + m) * 255), |
| clampByte((g + m) * 255), |
| clampByte((b + m) * 255), |
| ]; |
| } |
|
|
| export function rgbToHsv(rgb: Rgb): Hsv { |
| const [r, g, b] = rgb.map((channel) => channel / 255) as Rgb; |
| const max = Math.max(r, g, b); |
| const min = Math.min(r, g, b); |
| const delta = max - min; |
| let hue = 0; |
|
|
| if (delta !== 0) { |
| if (max === r) { |
| hue = 60 * (((g - b) / delta) % 6); |
| } else if (max === g) { |
| hue = 60 * ((b - r) / delta + 2); |
| } else { |
| hue = 60 * ((r - g) / delta + 4); |
| } |
| } |
|
|
| return [((hue % 360) + 360) % 360, max === 0 ? 0 : delta / max, max]; |
| } |
|
|
| export function expressedRgbFromAlleles(hsvAlleles: ColorAlleles): Rgb { |
| const hue = (hsvAlleles.Hue[0] + hsvAlleles.Hue[1]) / 2; |
| const saturation = (hsvAlleles.Saturation[0] + hsvAlleles.Saturation[1]) / 2; |
| const value = (hsvAlleles.Value[0] + hsvAlleles.Value[1]) / 2; |
| return hsvToRgb(hue, saturation, value); |
| } |
|
|
| export function derivePlantColor(budRgb: Rgb, leafRgb: Rgb): Rgb { |
| const bud = rgbToHsv(budRgb); |
| const leaf = rgbToHsv(leafRgb); |
| return hsvToRgb( |
| (bud[0] + leaf[0]) / 2, |
| (bud[1] + leaf[1]) / 2, |
| (bud[2] + leaf[2]) / 2 |
| ); |
| } |
|
|
| export function hueFromRgb(rgb: Rgb): number { |
| return Math.round(rgbToHsv(rgb)[0] * 10) / 10; |
| } |
|
|
| export function vibrance(rgb: Rgb): number { |
| const [red, green, blue] = rgb; |
| return Math.round(((Math.max(red, green, blue) - Math.min(red, green, blue)) / 255) * 1000) / 10; |
| } |
|
|
| export function colorFamily(hue: number): string { |
| if (hue < 15 || hue >= 330) return 'red'; |
| if (hue < 40) return 'orange'; |
| if (hue < 70) return 'yellow'; |
| if (hue < 160) return 'green'; |
| if (hue < 200) return 'cyan'; |
| if (hue < 260) return 'blue'; |
| if (hue < 290) return 'purple'; |
| return 'magenta'; |
| } |
|
|