palette.ts 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /**
  2. * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { Color } from '.';
  8. import { ScaleLegend, TableLegend } from '../legend';
  9. import { ParamDefinition as PD } from '../param-definition';
  10. import { distinctColors, DistinctColorsParams } from './distinct';
  11. import { ColorListName, getColorListFromName } from './lists';
  12. import { ColorScale } from './scale';
  13. type PaletteType = 'generate' | 'colors'
  14. const DefaultGetPaletteProps = {
  15. type: 'generate' as PaletteType,
  16. colorList: 'red-yellow-blue' as ColorListName
  17. };
  18. type GetPaletteProps = typeof DefaultGetPaletteProps
  19. export function getPaletteParams(props: Partial<GetPaletteProps> = {}) {
  20. const p = { ...DefaultGetPaletteProps, ...props };
  21. return {
  22. palette: PD.MappedStatic(p.type, {
  23. colors: PD.Group({
  24. list: PD.ColorList(p.colorList),
  25. }, { isFlat: true }),
  26. generate: PD.Group({
  27. ...DistinctColorsParams,
  28. maxCount: PD.Numeric(75, { min: 1, max: 250, step: 1 }),
  29. }, { isFlat: true })
  30. }, {
  31. options: [
  32. ['colors', 'Color List'],
  33. ['generate', 'Generate Distinct']
  34. ]
  35. })
  36. };
  37. }
  38. const DefaultPaletteProps = PD.getDefaultValues(getPaletteParams());
  39. type PaletteProps = typeof DefaultPaletteProps
  40. const DefaultLabelOptions = {
  41. valueLabel: (i: number) => `${i + 1}`,
  42. minLabel: 'Start',
  43. maxLabel: 'End'
  44. };
  45. type LabelOptions = typeof DefaultLabelOptions
  46. export interface Palette {
  47. color: (i: number) => Color
  48. legend?: TableLegend | ScaleLegend
  49. }
  50. export function getPalette(count: number, props: PaletteProps, labelOptions: Partial<LabelOptions> = {}) {
  51. let color: (i: number) => Color;
  52. let legend: ScaleLegend | TableLegend | undefined;
  53. if (props.palette.name === 'colors' && props.palette.params.list.kind === 'interpolate') {
  54. const { list } = props.palette.params;
  55. const domain: [number, number] = [0, count - 1];
  56. const { minLabel, maxLabel } = { ...DefaultLabelOptions, ...labelOptions };
  57. let colors = list.colors;
  58. if (colors.length === 0) colors = getColorListFromName(DefaultGetPaletteProps.colorList).list;
  59. const scale = ColorScale.create({ listOrName: colors, domain, minLabel, maxLabel });
  60. legend = scale.legend;
  61. color = scale.color;
  62. } else {
  63. let colors: Color[];
  64. if (props.palette.name === 'colors') {
  65. colors = props.palette.params.list.colors.map(c => Array.isArray(c) ? c[0] : c);
  66. if (colors.length === 0) colors = getColorListFromName('dark-2').list.map(c => Array.isArray(c) ? c[0] : c);
  67. } else {
  68. count = Math.min(count, props.palette.params.maxCount);
  69. colors = distinctColors(count, props.palette.params);
  70. }
  71. const valueLabel = labelOptions.valueLabel ?? DefaultLabelOptions.valueLabel;
  72. const colorsLength = colors.length;
  73. const table: [string, Color][] = [];
  74. for (let i = 0; i < count; ++i) {
  75. const j = i % colorsLength;
  76. if (table[j] === undefined) {
  77. table[j] = [valueLabel(i), colors[j]];
  78. } else {
  79. table[j][0] += `, ${valueLabel(i)}`;
  80. }
  81. }
  82. legend = TableLegend(table);
  83. color = (i: number) => colors[i % colorsLength];
  84. }
  85. return { color, legend };
  86. }