mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-07 06:14:07 +00:00
60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
const PALETTE = [
|
|||
|
|
'#ff0000',
|
||
|
|
'#0000ff',
|
||
|
|
'#46e646',
|
||
|
|
'#00ccff',
|
||
|
|
'#ff9900',
|
||
|
|
'#ff00ff',
|
||
|
|
'#ffff32',
|
||
|
|
'#288228',
|
||
|
|
'#9933ff',
|
||
|
|
'#50f0be',
|
||
|
|
'#8c645a',
|
||
|
|
];
|
||
|
|
|
||
|
|
/** GPX style colors are hex digits, with or without the leading `#`. */
|
||
|
|
export function normalizeColor(color: string): string {
|
||
|
|
return /^[0-9a-f]{6}$/i.test(color) ? `#${color}` : color;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Picks the base color of each file: the color defined by the file itself if any, otherwise a
|
||
|
|
* palette color, the least used one when the file first shows up. A file keeps its palette color
|
||
|
|
* for its whole lifetime, so that it does not change when style information comes and goes
|
||
|
|
* (edits, undo, redo).
|
||
|
|
*/
|
||
|
|
export class FileColorAllocator {
|
||
|
|
/** Palette color reserved for each file. */
|
||
|
|
private _reserved = new Map<string, string>();
|
||
|
|
/** Color currently displayed for each file. */
|
||
|
|
private _base = new Map<string, string>();
|
||
|
|
|
||
|
|
/** Updates (and returns) the base color of a file, given the colors defined by its tracks. */
|
||
|
|
resolve(fileId: string, trackColors: (string | undefined)[]): string {
|
||
|
|
const explicit = trackColors.find((color) => color !== undefined);
|
||
|
|
if (explicit === undefined && !this._reserved.has(fileId)) {
|
||
|
|
this._reserved.set(fileId, this.leastUsed());
|
||
|
|
}
|
||
|
|
const color =
|
||
|
|
explicit !== undefined ? normalizeColor(explicit) : this._reserved.get(fileId)!;
|
||
|
|
this._base.set(fileId, color);
|
||
|
|
return color;
|
||
|
|
}
|
||
|
|
|
||
|
|
release(fileId: string) {
|
||
|
|
this._reserved.delete(fileId);
|
||
|
|
this._base.delete(fileId);
|
||
|
|
}
|
||
|
|
|
||
|
|
private leastUsed(): string {
|
||
|
|
const usage = new Map(PALETTE.map((color) => [color, 0]));
|
||
|
|
this._base.forEach((color) => {
|
||
|
|
const count = usage.get(color);
|
||
|
|
if (count !== undefined) {
|
||
|
|
usage.set(color, count + 1);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return PALETTE.reduce((a, b) => (usage.get(a)! <= usage.get(b)! ? a : b));
|
||
|
|
}
|
||
|
|
}
|