import type { DataValue } from '../types'; /** * Finds a "nice" number approximately equal to x. * https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks * * Reference: Paul Heckbert, "Nice Numbers for Graph Labels", * Graphics Gems, pp 61-63. * */ const niceNum = (localRange: number, round: boolean): number => { const exponent = Math.floor(Math.log(localRange) / Math.log(10)); const fraction = localRange / 10 ** exponent; let niceFraction: number; if (round) { if (fraction < 1.5) { niceFraction = 1; } else if (fraction < 3) { niceFraction = 2; } else if (fraction < 7) { niceFraction = 5; } else { niceFraction = 10; } } else if (fraction <= 1) { niceFraction = 1; } else if (fraction <= 2) { niceFraction = 2; } else if (fraction <= 5) { niceFraction = 5; } else { niceFraction = 10; } return niceFraction * 10 ** exponent; }; export const createNiceScale = ( min: number, max: number, maxTicks = 10 ): { niceMin: number; niceMax: number; tickSpacing: number } => { const range = niceNum(max - min, false); const tickSpacing = niceNum(range / (maxTicks - 1), true); const niceMin = Math.floor(min / tickSpacing) * tickSpacing; const niceMax = Math.ceil(max / tickSpacing) * tickSpacing; return { niceMin, niceMax, tickSpacing, }; }; const isNull = (value: DataValue) => value == null; const filterNullValues = (data: DataValue[]): number[] => { return data.filter((d): d is number => !isNull(d)); }; export const maxValueFromDataSet = (data: { data: DataValue[] }[]): number => Math.max( -Infinity, ...data.map((d) => Math.max(...filterNullValues(d.data))) ); export const minValueFromDataSet = (data: { data: DataValue[] }[]): number => Math.min(Infinity, ...data.map((d) => Math.min(...filterNullValues(d.data))));