diff --git a/src/components/d3/D3MonitoringCharts.tsx b/src/components/d3/D3MonitoringCharts.tsx index ceacb05..9beb235 100644 --- a/src/components/d3/D3MonitoringCharts.tsx +++ b/src/components/d3/D3MonitoringCharts.tsx @@ -27,16 +27,32 @@ import D3MouseZone from './D3MouseZone' import { getByAccessor, getChartClass, getGroupClass, getTicks } from './functions' import { renderArea, renderLine, renderNeedle, renderPoint } from './renders' +const roundTo = (v: number, to: number = 50) => { + if (to == 0) return v + if (v < 0) return Math.round(v / to) * to + return Math.ceil(v / to) * to +} + +const calculateDomain = (mm: MinMax, round: number = 100): Required => { + let min = roundTo(mm.min ?? 0, round) + let max = roundTo(mm.max ?? round, round) + if (min - max < round) { + const mid = (min + max) / 2 + min = mid - round + max = mid + round + } + return { min, max } +} + type AxisScale = d3.ScaleTime | d3.ScaleLinear type ExtendedChartDataset = ChartDataset & { - yDomain: MinMax + xDomain: MinMax hideXAxis?: boolean } type ExtendedChartRegistry = ExtendedChartDataset & { (): d3.Selection - scale: d3.ScaleLinear y: (value: any) => number x: (value: any) => number } @@ -110,7 +126,7 @@ export type ChartSizes = ChartOffset & { } const axisHeight = 20 -const space = 20 +const space = 30 const _D3MonitoringCharts = >({ width: givenWidth = '100%', @@ -188,6 +204,32 @@ const _D3MonitoringCharts = >({ } ), [chartArea, axesArea]) + const chartDomains: Record, + domain: Required, + }>[] = useMemo(() => { + return groups.map((group) => { + const out = group.charts.map((chart) => { + const mm = { ...chart.xDomain } + let domain: Required = { min: 0, max: 100 } + if (mm.min && mm.max) { + domain = mm as Required + } else if (data) { + const [min, max] = d3.extent(data, chart.x) + domain = calculateDomain({ min, max, ...mm }, 100) + } + return [chart.key, { + scale: d3.scaleLinear().domain([domain.min, domain.max]), + domain, + }] + }) + + return Object.fromEntries(out) + }) + }, [groups, data]) + + console.log(chartDomains) + useEffect(() => { if (isDev()) { datasetGroups.forEach((sets, i) => { @@ -228,15 +270,6 @@ const _D3MonitoringCharts = >({ group.charts[chartIdx]().selectAll('*').remove() } - const yDomain = { - min: 0, - max: 10, - ...dataset.yDomain, - } - - const scale = d3.scaleLinear() - .domain([yDomain.min, yDomain.max]) - // Пересоздаём график const newChart: ExtendedChartRegistry = Object.assign( () => group().select('.' + getChartClass(dataset.key)) as d3.Selection, @@ -248,8 +281,6 @@ const _D3MonitoringCharts = >({ animDurationMs, ...dataset, yAxis: dataset.yAxis ?? yAxisConfig, - yDomain, - scale, y: getByAccessor(dataset.yAxis.accessor ?? yAxisConfig.accessor), x: getByAccessor(dataset.xAxis?.accessor), } @@ -288,7 +319,7 @@ const _D3MonitoringCharts = >({ .attr('class', (g) => `charts-group ${getGroupClass(g.key)}`) .attr('transform', (g) => `translate(${sizes.groupLeft(g.key)}, 0)`) - actualAxesGroups.each(function(group) { + actualAxesGroups.each(function(group, i) { const groupAxes = d3.select(this) const chartsData = group.charts.filter((chart) => !chart.hideXAxis) const charts = groupAxes.selectChildren().data(chartsData) @@ -302,17 +333,18 @@ const _D3MonitoringCharts = >({ .style('color', (d) => d.color ?? null) actualCharts.each(function (chart, j) { - let axis = d3.axisTop(chart.scale.range([0, sizes.groupWidth])) + let axis = d3.axisTop(chartDomains[i][chart.key].scale.range([0, sizes.groupWidth])) + const domain = chartDomains[i][chart.key].domain if (j === chartsData.length - 1) { axis = axis .ticks(5) .tickSize(-sizes.chartsHeight) .tickFormat((d, i) => i === 0 || i === 5 ? String(d) : '') - .tickValues(getTicks(chart.yDomain, 5)) + .tickValues(getTicks(domain, 5)) } else { axis = axis.ticks(1) - .tickValues(getTicks(chart.yDomain, 1)) + .tickValues(getTicks(domain, 1)) } d3.select(this).call(axis as any) @@ -335,7 +367,7 @@ const _D3MonitoringCharts = >({ .attr('stroke', j === chartsData.length - 1 ? 'gray' : 'currentColor') }) }) - }, [groups, groupScales, sizes, space]) + }, [groups, groupScales, sizes, space, chartDomains]) useEffect(() => { // Рисуем ось Y if (!yAxis) return @@ -361,7 +393,7 @@ const _D3MonitoringCharts = >({ useEffect(() => { if (!data || !yAxis) return - groups.forEach((group) => { + groups.forEach((group, i) => { group() .attr('transform', `translate(${sizes.groupLeft(group.key)}, 0)`) .attr('clip-path', `url(#chart-clip)`) @@ -377,7 +409,7 @@ const _D3MonitoringCharts = >({ let chartData = data if (!chartData) return - const xAxis = chart.scale.range([0, sizes.groupWidth]) + const xAxis = chartDomains[i][chart.key].scale.range([0, sizes.groupWidth]) switch (chart.type) { case 'needle': @@ -397,12 +429,12 @@ const _D3MonitoringCharts = >({ } if (chart.point) - renderPoint(chart.scale, yAxis, chart, chartData, true) + renderPoint(xAxis, yAxis, chart, chartData, true) chart.afterDraw?.(chart) }) }) - }, [data, groups, groupScales, height, offset, sizes]) + }, [data, groups, groupScales, height, offset, sizes, chartDomains]) return (