From e58921aa8f070a02bbcc360eed21f6b8b1ebe3d4 Mon Sep 17 00:00:00 2001 From: Yury Molodov Date: Wed, 18 Jan 2023 05:25:37 +0100 Subject: [PATCH] vmui: give more visually different colors to graph lines (#3656) * feat: make more different colors of graph lines * docs/CHANGELOG.md: give more visually different colors to graph lines --- .../components/Views/GraphView/GraphView.tsx | 5 ++- app/vmui/packages/vmui/src/utils/color.ts | 41 ++++++++++++++----- .../packages/vmui/src/utils/uplot/helpers.ts | 3 -- .../packages/vmui/src/utils/uplot/series.ts | 36 +++++++++------- docs/CHANGELOG.md | 1 + 5 files changed, 57 insertions(+), 29 deletions(-) diff --git a/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx b/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx index d9028cf7c..905adcd74 100644 --- a/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx +++ b/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx @@ -1,9 +1,9 @@ -import React, { FC, useEffect, useMemo, useRef, useState } from "preact/compat"; +import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "preact/compat"; import { MetricResult } from "../../../api/types"; import LineChart from "../../Chart/LineChart/LineChart"; import { AlignedData as uPlotData, Series as uPlotSeries } from "uplot"; import Legend from "../../Chart/Legend/Legend"; -import { getHideSeries, getLegendItem, getSeriesItem } from "../../../utils/uplot/series"; +import { getHideSeries, getLegendItem, getSeriesItemContext } from "../../../utils/uplot/series"; import { getLimitsYAxis, getMinMaxBuffer, getTimeSeries } from "../../../utils/uplot/axes"; import { LegendItemType } from "../../../utils/uplot/types"; import { TimeParams } from "../../../types"; @@ -59,6 +59,7 @@ const GraphView: FC = ({ }) => { const { timezone } = useTimeState(); const currentStep = useMemo(() => customStep || period.step || "1s", [period.step, customStep]); + const getSeriesItem = useCallback(getSeriesItemContext(), [data]); const [dataChart, setDataChart] = useState([[]]); const [series, setSeries] = useState([]); diff --git a/app/vmui/packages/vmui/src/utils/color.ts b/app/vmui/packages/vmui/src/utils/color.ts index fc68d7deb..e1e8b0411 100644 --- a/app/vmui/packages/vmui/src/utils/color.ts +++ b/app/vmui/packages/vmui/src/utils/color.ts @@ -1,14 +1,35 @@ -export const getColorFromString = (str: string): string => { - let hash = 0; - for (let i = 0; i < str.length; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash); +export const baseContrastColors = [ + "#e54040", + "#32a9dc", + "#2ee329", + "#7126a1", + "#e38f0f", + "#3d811a", + "#ffea00", + "#2d2d2d", + "#da42a6", + "#a44e0c", +]; + +export const getColorFromString = (text: string): string => { + const SEED = 16777215; + const FACTOR = 49979693; + + let b = 1; + let d = 0; + let f = 1; + + if (text.length > 0) { + for (let i = 0; i < text.length; i++) { + text[i].charCodeAt(0) > d && (d = text[i].charCodeAt(0)); + f = parseInt(String(SEED / d)); + b = (b + text[i].charCodeAt(0) * f * FACTOR) % SEED; + } } - let colour = "#"; - for (let i = 0; i < 3; i++) { - const value = (hash >> (i * 8)) & 0xFF; - colour += ("00" + value.toString(16)).substr(-2); - } - return colour; + + let hex = ((b * text.length) % SEED).toString(16); + hex = hex.padEnd(6, hex); + return `#${hex}`; }; export const hexToRGB = (hex: string): string => { diff --git a/app/vmui/packages/vmui/src/utils/uplot/helpers.ts b/app/vmui/packages/vmui/src/utils/uplot/helpers.ts index 0c3f6c4b7..b095c86f7 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/helpers.ts +++ b/app/vmui/packages/vmui/src/utils/uplot/helpers.ts @@ -1,5 +1,4 @@ import uPlot, { Axis } from "uplot"; -import { getColorFromString } from "../color"; export const defaultOptions = { legend: { @@ -85,6 +84,4 @@ export const sizeAxis = (u: uPlot, values: string[], axisIdx: number, cycleNum: return Math.ceil(axisSize); }; -export const getColorLine = (label: string): string => getColorFromString(label); - export const getDashLine = (group: number): number[] => group <= 1 ? [] : [group*4, group*1.2]; diff --git a/app/vmui/packages/vmui/src/utils/uplot/series.ts b/app/vmui/packages/vmui/src/utils/uplot/series.ts index 31e8c149f..2314a4f1c 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/series.ts +++ b/app/vmui/packages/vmui/src/utils/uplot/series.ts @@ -2,26 +2,34 @@ import { MetricResult } from "../../api/types"; import { Series } from "uplot"; import { getNameForMetric } from "../metric"; import { BarSeriesItem, Disp, Fill, LegendItemType, Stroke } from "./types"; -import { getColorLine } from "./helpers"; import { HideSeriesArgs } from "./types"; +import { baseContrastColors, getColorFromString } from "../color"; interface SeriesItem extends Series { freeFormFields: {[key: string]: string}; } -export const getSeriesItem = (d: MetricResult, hideSeries: string[], alias: string[]): SeriesItem => { - const label = getNameForMetric(d, alias[d.group - 1]); - return { - label, - freeFormFields: d.metric, - width: 1.4, - stroke: getColorLine(label), - show: !includesHideSeries(label, hideSeries), - scale: "1", - points: { - size: 4.2, - width: 1.4 - } +export const getSeriesItemContext = () => { + const colorState: {[key: string]: string} = {}; + + return (d: MetricResult, hideSeries: string[], alias: string[]): SeriesItem => { + const label = getNameForMetric(d, alias[d.group - 1]); + const countSavedColors = Object.keys(colorState).length; + const hasBasicColors = countSavedColors < baseContrastColors.length; + if (hasBasicColors) colorState[label] = colorState[label] || baseContrastColors[countSavedColors]; + + return { + label, + freeFormFields: d.metric, + width: 1.4, + stroke: colorState[label] || getColorFromString(label), + show: !includesHideSeries(label, hideSeries), + scale: "1", + points: { + size: 4.2, + width: 1.4 + } + }; }; }; diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b1433a06c..76fb75647 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -17,6 +17,7 @@ The following tip changes can be tested by building VictoriaMetrics components f * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to show custom dashboards at vmui by specifying a path to a directory with dashboard config files via `-vmui.customDashboardsPath` command-line flag. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3322) and [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): apply the `step` globally to all the displayed graphs. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3574). +* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): improve the appearance of graph lines by using more visually distinct colors. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3656). * BUGFIX: do not slow down concurrently executed queries during assisted merges, since assisted merges already prioritize data ingestion over queries. The probability of assisted merges has been increased starting from [v1.85.0](https://docs.victoriametrics.com/CHANGELOG.html#v1850) because of internal refactoring. This could result in slowed down queries when there is a plenty of free CPU resources. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3647) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3641) issues. * BUGFIX: reduce the increased CPU usage at `vmselect` to v1.85.3 level when processing heavy queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3641).