Skip to content

Commit

Permalink
Choose colors by hash (#239)
Browse files Browse the repository at this point in the history
* Choose colors by hash

* fixed doc comment
  • Loading branch information
semicolin authored Mar 22, 2024
1 parent 4873d74 commit 96b7344
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,23 @@ describe('DataLayerColorService', () => {
});

describe('chooseColorsFromPalette', () => {
it('should cycle through the palette', inject([DataLayerColorService], (service: DataLayerColorService) => {
const layer: any = {
it('should always choose the same color for a dataset', () => {
const service1 = new DataLayerColorService(palette);
const service2 = new DataLayerColorService(palette);
const layer1: any = {
datasets: [{ label: 'One' }, { label: 'Two' }, { label: 'Three' }],
annotations: [{ label: { display: true } }],
};
service.chooseColorsFromPalette(layer);
expect(layer.datasets[0].borderColor).toEqual(palette[0]);
expect(layer.datasets[1].borderColor).toEqual(palette[1]);
expect(layer.datasets[2].borderColor).toEqual(palette[0]);
}));
const layer2: any = {
datasets: [{ label: 'One' }, { label: 'Two' }, { label: 'Three' }],
annotations: [{ label: { display: true } }],
};
service1.chooseColorsFromPalette(layer1);
service2.chooseColorsFromPalette(layer2);
expect(layer1.datasets[0].borderColor).toEqual(layer2.datasets[0].borderColor);
expect(layer1.datasets[1].borderColor).toEqual(layer2.datasets[1].borderColor);
expect(layer1.datasets[2].borderColor).toEqual(layer2.datasets[2].borderColor);
});

it('should reuse color from matching dataset', inject([DataLayerColorService], (service: DataLayerColorService) => {
const layer: any = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Inject, Injectable, InjectionToken } from '@angular/core';
import { DataLayer, Dataset } from './data-layer';
import tinycolor from 'tinycolor2';
import { BoxAnnotationOptions } from 'chartjs-plugin-annotation';
import { hashCode } from '../utils';
/**
* Injection Token used by `DataLayerColorService` to define the available chart colors.
*
Expand Down Expand Up @@ -29,18 +30,12 @@ export class DataLayerColorService {
constructor(@Inject(COLOR_PALETTE) private readonly palette: string[]) {}

private lightPalette = this.palette.map((c) => tinycolor(c).brighten(20).toString());
private nextColorIndex = 0;

/** Reset the service to its initial state so `chooseColorsFromPalette` will select the first color in the palette */
reset() {
this.nextColorIndex = 0;
}

/** Chooses colors for all of the datasets and annotations in the Layer by cycling through the palette */
/** Chooses colors for all of the datasets and annotations in the Layer */
chooseColorsFromPalette(layer: DataLayer): void {
for (let dataset of layer.datasets) {
if (!this.hasColor(dataset)) {
const colorIndex = this.getMatchingDatasetColorIndex(layer, dataset) ?? this.getNextPaletteIndex();
const colorIndex = this.getMatchingDatasetColorIndex(layer, dataset) ?? this.getPaletteIndex(dataset);
const palette = this.getPalette(dataset);
const color = palette[colorIndex];
this.setColor(dataset, color);
Expand All @@ -49,10 +44,9 @@ export class DataLayerColorService {
}
}

private getNextPaletteIndex() {
const currentIndex = this.nextColorIndex;
this.nextColorIndex = (this.nextColorIndex + 1) % this.palette.length;
return currentIndex;
/** Gets the palette index that should be used for the given dataset */
private getPaletteIndex(dataset: Dataset) {
return Math.abs(hashCode(dataset.label ?? '')) % this.palette.length;
}

/** Gets the palette that should be used for the given dataset */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ export class DataLayerManagerService {
reset() {
this.cancel$.next();
this.state = { ...this.state, layers: {}, selected: [] };
this.colorService.reset();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DataLayer, DataLayerCollection, Dataset, ManagedDataLayer, TimelineData
import { produce, castDraft } from 'immer';
import { DataLayerColorService } from './data-layer-color.service';
import { FhirChartTagsService } from '../fhir-chart-legend/fhir-chart-tags-legend/fhir-chart-tags.service';
import { hashCode } from '../utils';

/**
* Merges a `DataLayer` into the matching layer in a `DataLayerCollection`.
Expand Down Expand Up @@ -69,16 +70,6 @@ function generateId(layer: DataLayer): string {
return hashCode(JSON.stringify(extractMetadata(layer))).toFixed(0);
}

function hashCode(str: string): number {
let hash = 0;
for (let i = 0, len = str.length; i < len; i++) {
let chr = str.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}

function extractMetadata(layer: DataLayer): DataLayer {
return {
...layer,
Expand Down
10 changes: 10 additions & 0 deletions libs/ngx-charts-on-fhir/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,13 @@ export function formatMonthRange(range: MonthRange) {
return formatDays(range);
}
}

export function hashCode(str: string): number {
let hash = 0;
for (let i = 0, len = str.length; i < len; i++) {
let chr = str.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}

0 comments on commit 96b7344

Please sign in to comment.