UNPKG

@deepsource/charts

Version:

<div align="center"> <img src="https://github.com/frappe/design/blob/master/logos/logo-2019/frappe-charts-logo.png" height="128"> <a href="https://frappe.github.io/charts"> <h2>Frappe Charts</h2> </a> </div>

248 lines (195 loc) 6.28 kB
import { floatTwo } from './helpers'; function normalize(x) { // Calculates mantissa and exponent of a number // Returns normalized number and exponent // https://stackoverflow.com/q/9383593/6495043 if (x === 0) { return [0, 0]; } if (isNaN(x)) { return { mantissa: -6755399441055744, exponent: 972 }; } var sig = x > 0 ? 1 : -1; if (!isFinite(x)) { return { mantissa: sig * 4503599627370496, exponent: 972 }; } x = Math.abs(x); var exp = Math.floor(Math.log10(x)); var man = x / Math.pow(10, exp); return [sig * man, exp]; } function getChartRangeIntervals(max, min = 0) { let upperBound = Math.ceil(max); let lowerBound = Math.floor(min); let range = upperBound - lowerBound; let noOfParts = range; let partSize = 1; // To avoid too many partitions if (range > 5) { if (range % 2 !== 0) { upperBound++; // Recalc range range = upperBound - lowerBound; } noOfParts = range / 2; partSize = 2; } // Special case: 1 and 2 if (range <= 2) { noOfParts = 4; partSize = range / noOfParts; } // Special case: 0 if (range === 0) { noOfParts = 5; partSize = 1; } let intervals = []; for (var i = 0; i <= noOfParts; i++) { intervals.push(lowerBound + partSize * i); } return intervals; } function getChartIntervals(maxValue, minValue = 0) { let [normalMaxValue, exponent] = normalize(maxValue); let normalMinValue = minValue ? minValue / Math.pow(10, exponent) : 0; // Allow only 7 significant digits normalMaxValue = normalMaxValue.toFixed(6); let intervals = getChartRangeIntervals(normalMaxValue, normalMinValue); intervals = intervals.map(value => value * Math.pow(10, exponent)); return intervals; } export function calcChartIntervals(values, withMinimum = false, range = {}) { //*** Where the magic happens *** // Calculates best-fit y intervals from given values // and returns the interval array let maxValue = Math.max(...values); let minValue = Math.min(...values); if (range.max !== undefined) { maxValue = maxValue > range.max ? maxValue : range.max; } if (range.min !== undefined) { minValue = minValue < range.min ? minValue : range.min; } // Exponent to be used for pretty print let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars function getPositiveFirstIntervals(maxValue, absMinValue) { let intervals = getChartIntervals(maxValue); let intervalSize = intervals[1] - intervals[0]; // Then unshift the negative values let value = 0; for (var i = 1; value < absMinValue; i++) { value += intervalSize; intervals.unshift((-1) * value); } return intervals; } // CASE I: Both non-negative if (maxValue >= 0 && minValue >= 0) { exponent = normalize(maxValue)[1]; if (!withMinimum) { intervals = getChartIntervals(maxValue); } else { intervals = getChartIntervals(maxValue, minValue); } } // CASE II: Only minValue negative else if (maxValue > 0 && minValue < 0) { // `withMinimum` irrelevant in this case, // We'll be handling both sides of zero separately // (both starting from zero) // Because ceil() and floor() behave differently // in those two regions let absMinValue = Math.abs(minValue); if (maxValue >= absMinValue) { exponent = normalize(maxValue)[1]; intervals = getPositiveFirstIntervals(maxValue, absMinValue); } else { // Mirror: maxValue => absMinValue, then change sign exponent = normalize(absMinValue)[1]; let posIntervals = getPositiveFirstIntervals(absMinValue, maxValue); intervals = posIntervals.reverse().map(d => d * (-1)); } } // CASE III: Both non-positive else if (maxValue <= 0 && minValue <= 0) { // Mirrored Case I: // Work with positives, then reverse the sign and array let pseudoMaxValue = Math.abs(minValue); let pseudoMinValue = Math.abs(maxValue); exponent = normalize(pseudoMaxValue)[1]; if (!withMinimum) { intervals = getChartIntervals(pseudoMaxValue); } else { intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue); } intervals = intervals.reverse().map(d => d * (-1)); } return intervals; } export function getZeroIndex(yPts) { let zeroIndex; let interval = getIntervalSize(yPts); if (yPts.indexOf(0) >= 0) { // the range has a given zero // zero-line on the chart zeroIndex = yPts.indexOf(0); } else if (yPts[0] > 0) { // Minimum value is positive // zero-line is off the chart: below let min = yPts[0]; zeroIndex = (-1) * min / interval; } else { // Maximum value is negative // zero-line is off the chart: above let max = yPts[yPts.length - 1]; zeroIndex = (-1) * max / interval + (yPts.length - 1); } return zeroIndex; } export function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) { let range = max - min; let part = range * 1.0 / noOfIntervals; let intervals = []; for (var i = 0; i <= noOfIntervals; i++) { intervals.push(min + part * i); } return asc ? intervals : intervals.reverse(); } export function getIntervalSize(orderedArray) { return orderedArray[1] - orderedArray[0]; } export function getValueRange(orderedArray) { return orderedArray[orderedArray.length - 1] - orderedArray[0]; } export function scale(val, yAxis) { return floatTwo(yAxis.zeroLine - val * yAxis.scaleMultiplier); } export function isInRange(val, min, max) { return val > min && val < max; } export function isInRange2D(coord, minCoord, maxCoord) { return isInRange(coord[0], minCoord[0], maxCoord[0]) && isInRange(coord[1], minCoord[1], maxCoord[1]); } export function getClosestInArray(goal, arr, index = false) { let closest = arr.reduce(function (prev, curr) { return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev); }, []); return index ? arr.indexOf(closest) : closest; } export function calcDistribution(values, distributionSize) { // Assume non-negative values, // implying distribution minimum at zero let dataMaxValue = Math.max(...values); let distributionStep = 1 / (distributionSize - 1); let distribution = []; for (var i = 0; i < distributionSize; i++) { let checkpoint = dataMaxValue * (distributionStep * i); distribution.push(checkpoint); } return distribution; } export function getMaxCheckpoint(value, distribution) { return distribution.filter(d => d < value).length; }