1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. type ColorSpace = number | {min: number, max: number, count?: number};
    
  11. 
    
  12. // Docstrings from https://www.w3schools.com/css/css_colors_hsl.asp
    
  13. type HslaColor = $ReadOnly<{
    
  14.   /** Hue is a degree on the color wheel from 0 to 360. 0 is red, 120 is green, and 240 is blue. */
    
  15.   h: number,
    
  16.   /** Saturation is a percentage value, 0% means a shade of gray, and 100% is the full color. */
    
  17.   s: number,
    
  18.   /** Lightness is a percentage, 0% is black, 50% is neither light or dark, 100% is white. */
    
  19.   l: number,
    
  20.   /** Alpha is a percentage, 0% is fully transparent, and 100 is not transparent at all. */
    
  21.   a: number,
    
  22. }>;
    
  23. 
    
  24. export function hslaColorToString({h, s, l, a}: HslaColor): string {
    
  25.   return `hsl(${h}deg ${s}% ${l}% / ${a})`;
    
  26. }
    
  27. 
    
  28. export function dimmedColor(color: HslaColor, dimDelta: number): HslaColor {
    
  29.   return {
    
  30.     ...color,
    
  31.     l: color.l - dimDelta,
    
  32.   };
    
  33. }
    
  34. 
    
  35. // Source: https://source.chromium.org/chromium/chromium/src/+/master:out/Debug/gen/devtools/platform/utilities.js;l=120
    
  36. function hashCode(string: string): number {
    
  37.   // Hash algorithm for substrings is described in "Über die Komplexität der Multiplikation in
    
  38.   // eingeschränkten Branchingprogrammmodellen" by Woelfe.
    
  39.   // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
    
  40.   const p = (1 << 30) * 4 - 5; // prime: 2^32 - 5
    
  41.   const z = 0x5033d967; // 32 bits from random.org
    
  42.   const z2 = 0x59d2f15d; // random odd 32 bit number
    
  43.   let s = 0;
    
  44.   let zi = 1;
    
  45.   for (let i = 0; i < string.length; i++) {
    
  46.     const xi = string.charCodeAt(i) * z2;
    
  47.     s = (s + zi * xi) % p;
    
  48.     zi = (zi * z) % p;
    
  49.   }
    
  50.   s = (s + zi * (p - 1)) % p;
    
  51.   return Math.abs(s | 0);
    
  52. }
    
  53. 
    
  54. function indexToValueInSpace(index: number, space: ColorSpace): number {
    
  55.   if (typeof space === 'number') {
    
  56.     return space;
    
  57.   }
    
  58.   const count = space.count || space.max - space.min;
    
  59.   index %= count;
    
  60.   return (
    
  61.     space.min + Math.floor((index / (count - 1)) * (space.max - space.min))
    
  62.   );
    
  63. }
    
  64. 
    
  65. /**
    
  66.  * Deterministic color generator.
    
  67.  *
    
  68.  * Adapted from: https://source.chromium.org/chromium/chromium/src/+/master:out/Debug/gen/devtools/common/Color.js
    
  69.  */
    
  70. export class ColorGenerator {
    
  71.   _hueSpace: ColorSpace;
    
  72.   _satSpace: ColorSpace;
    
  73.   _lightnessSpace: ColorSpace;
    
  74.   _alphaSpace: ColorSpace;
    
  75.   _colors: Map<string, HslaColor>;
    
  76. 
    
  77.   constructor(
    
  78.     hueSpace?: ColorSpace,
    
  79.     satSpace?: ColorSpace,
    
  80.     lightnessSpace?: ColorSpace,
    
  81.     alphaSpace?: ColorSpace,
    
  82.   ) {
    
  83.     this._hueSpace = hueSpace || {min: 0, max: 360};
    
  84.     this._satSpace = satSpace || 67;
    
  85.     this._lightnessSpace = lightnessSpace || 80;
    
  86.     this._alphaSpace = alphaSpace || 1;
    
  87.     this._colors = new Map();
    
  88.   }
    
  89. 
    
  90.   setColorForID(id: string, color: HslaColor) {
    
  91.     this._colors.set(id, color);
    
  92.   }
    
  93. 
    
  94.   colorForID(id: string): HslaColor {
    
  95.     const cachedColor = this._colors.get(id);
    
  96.     if (cachedColor) {
    
  97.       return cachedColor;
    
  98.     }
    
  99.     const color = this._generateColorForID(id);
    
  100.     this._colors.set(id, color);
    
  101.     return color;
    
  102.   }
    
  103. 
    
  104.   _generateColorForID(id: string): HslaColor {
    
  105.     const hash = hashCode(id);
    
  106.     return {
    
  107.       h: indexToValueInSpace(hash, this._hueSpace),
    
  108.       s: indexToValueInSpace(hash >> 8, this._satSpace),
    
  109.       l: indexToValueInSpace(hash >> 16, this._lightnessSpace),
    
  110.       a: indexToValueInSpace(hash >> 24, this._alphaSpace),
    
  111.     };
    
  112.   }
    
  113. }