color.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /**
  2. * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { NumberArray } from '../../mol-util/type-helpers';
  7. import { Vec3 } from '../../mol-math/linear-algebra';
  8. import { Hcl } from './spaces/hcl';
  9. import { Lab } from './spaces/lab';
  10. /** RGB color triplet expressed as a single number */
  11. export type Color = { readonly '@type': 'color' } & number
  12. export function Color(hex: number) { return hex as Color; }
  13. export namespace Color {
  14. export function toStyle(hexColor: Color) {
  15. return `rgb(${hexColor >> 16 & 255}, ${hexColor >> 8 & 255}, ${hexColor & 255})`;
  16. }
  17. export function toHexStyle(hexColor: Color) {
  18. return '#' + ('000000' + hexColor.toString(16)).slice(-6);
  19. }
  20. export function toHexString(hexColor: Color) {
  21. return '0x' + ('000000' + hexColor.toString(16)).slice(-6);
  22. }
  23. export function toRgbString(hexColor: Color) {
  24. return `RGB: ${Color.toRgb(hexColor).join(', ')}`;
  25. }
  26. export function toRgb(hexColor: Color): [number, number, number] {
  27. return [hexColor >> 16 & 255, hexColor >> 8 & 255, hexColor & 255];
  28. }
  29. export function toRgbNormalized(hexColor: Color): [number, number, number] {
  30. return [(hexColor >> 16 & 255) / 255, (hexColor >> 8 & 255) / 255, (hexColor & 255) / 255];
  31. }
  32. export function fromHexStyle(s: string): Color {
  33. return parseInt(s.replace('#', '0x')) as Color;
  34. }
  35. export function fromHexString(s: string): Color {
  36. return parseInt(s) as Color;
  37. }
  38. export function fromRgb(r: number, g: number, b: number): Color {
  39. return ((r << 16) | (g << 8) | b) as Color;
  40. }
  41. export function fromNormalizedRgb(r: number, g: number, b: number): Color {
  42. return (((r * 255) << 16) | ((g * 255) << 8) | (b * 255)) as Color;
  43. }
  44. export function fromArray(array: NumberArray, offset: number): Color {
  45. return fromRgb(array[offset], array[offset + 1], array[offset + 2]);
  46. }
  47. export function fromNormalizedArray(array: NumberArray, offset: number): Color {
  48. return fromNormalizedRgb(array[offset], array[offset + 1], array[offset + 2]);
  49. }
  50. /** Copies hex color to rgb array */
  51. export function toArray(hexColor: Color, array: NumberArray, offset: number) {
  52. array[offset] = (hexColor >> 16 & 255);
  53. array[offset + 1] = (hexColor >> 8 & 255);
  54. array[offset + 2] = (hexColor & 255);
  55. return array;
  56. }
  57. /** Copies normalized (0 to 1) hex color to rgb array */
  58. export function toArrayNormalized<T extends NumberArray>(hexColor: Color, array: T, offset: number) {
  59. array[offset] = (hexColor >> 16 & 255) / 255;
  60. array[offset + 1] = (hexColor >> 8 & 255) / 255;
  61. array[offset + 2] = (hexColor & 255) / 255;
  62. return array;
  63. }
  64. /** Copies hex color to rgb vec3 */
  65. export function toVec3(out: Vec3, hexColor: Color) {
  66. out[0] = (hexColor >> 16 & 255);
  67. out[1] = (hexColor >> 8 & 255);
  68. out[2] = (hexColor & 255);
  69. return out;
  70. }
  71. /** Copies normalized (0 to 1) hex color to rgb vec3 */
  72. export function toVec3Normalized(out: Vec3, hexColor: Color) {
  73. out[0] = (hexColor >> 16 & 255) / 255;
  74. out[1] = (hexColor >> 8 & 255) / 255;
  75. out[2] = (hexColor & 255) / 255;
  76. return out;
  77. }
  78. /** Linear interpolation between two colors in rgb space */
  79. export function interpolate(c1: Color, c2: Color, t: number): Color {
  80. const r1 = c1 >> 16 & 255;
  81. const g1 = c1 >> 8 & 255;
  82. const b1 = c1 & 255;
  83. const r2 = c2 >> 16 & 255;
  84. const g2 = c2 >> 8 & 255;
  85. const b2 = c2 & 255;
  86. const r = r1 + (r2 - r1) * t;
  87. const g = g1 + (g2 - g1) * t;
  88. const b = b1 + (b2 - b1) * t;
  89. return ((r << 16) | (g << 8) | b) as Color;
  90. }
  91. const tmpSaturateHcl = [0, 0, 0] as Hcl;
  92. export function saturate(c: Color, amount: number): Color {
  93. Hcl.fromColor(tmpSaturateHcl, c);
  94. return Hcl.toColor(Hcl.saturate(tmpSaturateHcl, tmpSaturateHcl, amount));
  95. }
  96. export function desaturate(c: Color, amount: number): Color {
  97. return saturate(c, -amount);
  98. }
  99. const tmpDarkenLab = [0, 0, 0] as Lab;
  100. export function darken(c: Color, amount: number): Color {
  101. Lab.fromColor(tmpDarkenLab, c);
  102. return Lab.toColor(Lab.darken(tmpDarkenLab, tmpDarkenLab, amount));
  103. }
  104. export function lighten(c: Color, amount: number): Color {
  105. return darken(c, -amount);
  106. }
  107. //
  108. function _sRGBToLinear(c: number) {
  109. return (c < 0.04045) ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4);
  110. }
  111. export function sRGBToLinear(c: Color) {
  112. return fromNormalizedRgb(
  113. _sRGBToLinear((c >> 16 & 255) / 255),
  114. _sRGBToLinear((c >> 8 & 255) / 255),
  115. _sRGBToLinear((c & 255) / 255)
  116. );
  117. }
  118. function _linearToSRGB(c: number) {
  119. return (c < 0.0031308) ? c * 12.92 : 1.055 * (Math.pow(c, 0.41666)) - 0.055;
  120. }
  121. export function linearToSRGB(c: Color) {
  122. return fromNormalizedRgb(
  123. _linearToSRGB((c >> 16 & 255) / 255),
  124. _linearToSRGB((c >> 8 & 255) / 255),
  125. _linearToSRGB((c & 255) / 255)
  126. );
  127. }
  128. }
  129. export type ColorListEntry = Color | [Color, number /** normalized value from 0 to 1 */]
  130. export interface ColorList {
  131. label: string
  132. description: string
  133. list: ColorListEntry[]
  134. type: 'sequential' | 'diverging' | 'qualitative'
  135. }
  136. export function ColorList(label: string, type: 'sequential' | 'diverging' | 'qualitative', description: string, list: (number | [number, number])[]): ColorList {
  137. return { label, description, list: list as Color[], type };
  138. }
  139. export type ColorTable<T extends { [k: string]: number[] }> = { [k in keyof T]: Color[] }
  140. export function ColorTable<T extends { [k: string]: number[] }>(o: T) { return o as unknown as ColorTable<T>; }
  141. export type ColorMap<T extends { [k: string]: number }> = { [k in keyof T]: Color }
  142. export function ColorMap<T extends { [k: string]: number }>(o: T) { return o as unknown as ColorMap<T>; }
  143. export function getAdjustedColorMap<T extends { [k: string]: number }>(map: ColorMap<T>, saturation: number, lightness: number) {
  144. const adjustedMap: { [k: string]: Color } = {};
  145. for (const e in map) {
  146. let c = map[e];
  147. c = Color.saturate(c, saturation);
  148. c = Color.darken(c, -lightness);
  149. adjustedMap[e] = c;
  150. }
  151. return adjustedMap as ColorMap<T>;
  152. }
  153. export type ColorSwatch = [string, Color][]
  154. export function ColorSwatch(l: [string, number][]) { return l as unknown as ColorSwatch; }