type ColorSpace = number | {min: number, max: number, count?: number};
type HslaColor = $ReadOnly<{
h: number,
s: number,
l: number,
a: number,
}>;
export function hslaColorToString({h, s, l, a}: HslaColor): string {
return `hsl(${h}deg ${s}% ${l}% / ${a})`;
}
export function dimmedColor(color: HslaColor, dimDelta: number): HslaColor {
return {
...color,
l: color.l - dimDelta,
};
}
function hashCode(string: string): number {
const p = (1 << 30) * 4 - 5;
const z = 0x5033d967;
const z2 = 0x59d2f15d;
let s = 0;
let zi = 1;
for (let i = 0; i < string.length; i++) {
const xi = string.charCodeAt(i) * z2;
s = (s + zi * xi) % p;
zi = (zi * z) % p;
}
s = (s + zi * (p - 1)) % p;
return Math.abs(s | 0);
}
function indexToValueInSpace(index: number, space: ColorSpace): number {
if (typeof space === 'number') {
return space;
}
const count = space.count || space.max - space.min;
index %= count;
return (
space.min + Math.floor((index / (count - 1)) * (space.max - space.min))
);
}
export class ColorGenerator {
_hueSpace: ColorSpace;
_satSpace: ColorSpace;
_lightnessSpace: ColorSpace;
_alphaSpace: ColorSpace;
_colors: Map<string, HslaColor>;
constructor(
hueSpace?: ColorSpace,
satSpace?: ColorSpace,
lightnessSpace?: ColorSpace,
alphaSpace?: ColorSpace,
) {
this._hueSpace = hueSpace || {min: 0, max: 360};
this._satSpace = satSpace || 67;
this._lightnessSpace = lightnessSpace || 80;
this._alphaSpace = alphaSpace || 1;
this._colors = new Map();
}
setColorForID(id: string, color: HslaColor) {
this._colors.set(id, color);
}
colorForID(id: string): HslaColor {
const cachedColor = this._colors.get(id);
if (cachedColor) {
return cachedColor;
}
const color = this._generateColorForID(id);
this._colors.set(id, color);
return color;
}
_generateColorForID(id: string): HslaColor {
const hash = hashCode(id);
return {
h: indexToValueInSpace(hash, this._hueSpace),
s: indexToValueInSpace(hash >> 8, this._satSpace),
l: indexToValueInSpace(hash >> 16, this._lightnessSpace),
a: indexToValueInSpace(hash >> 24, this._alphaSpace),
};
}
}