UNPKG

ag-charts-community

Version:

Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue

219 lines 7.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const continuousScale_1 = require("./continuousScale"); const ticks_1 = require("../util/ticks"); const numberFormat_1 = require("../util/numberFormat"); const identity = (x) => x; class LogScale extends continuousScale_1.ContinuousScale { constructor() { super(...arguments); this.type = 'log'; this._domain = [1, 10]; this.baseLog = identity; // takes a log with base `base` of `x` this.basePow = identity; // raises `base` to the power of `x` this._base = 10; } setDomain(values) { let df = values[0]; let dl = values[values.length - 1]; if (df === 0 || dl === 0 || df < 0 && dl > 0 || df > 0 && dl < 0) { console.warn('Log scale domain should not start at, end at or cross zero.'); if (df === 0 && dl > 0) { df = Number.EPSILON; } else if (dl === 0 && df < 0) { dl = -Number.EPSILON; } else if (df < 0 && dl > 0) { if (Math.abs(dl) >= Math.abs(df)) { df = Number.EPSILON; } else { dl = -Number.EPSILON; } } else if (df > 0 && dl < 0) { if (Math.abs(dl) >= Math.abs(df)) { df = -Number.EPSILON; } else { dl = Number.EPSILON; } } values = values.slice(); values[0] = df; values[values.length - 1] = dl; } super.setDomain(values); } getDomain() { return super.getDomain(); } set base(value) { if (this._base !== value) { this._base = value; this.rescale(); } } get base() { return this._base; } rescale() { const { base } = this; let baseLog = LogScale.makeLogFn(base); let basePow = LogScale.makePowFn(base); if (this.domain[0] < 0) { baseLog = this.reflect(baseLog); basePow = this.reflect(basePow); this.transform = (x) => -Math.log(-x); this.untransform = (x) => -Math.exp(-x); } else { this.transform = (x) => Math.log(x); this.untransform = (x) => Math.exp(x); } this.baseLog = baseLog; this.basePow = basePow; super.rescale(); } /** * For example, if `f` is `Math.log10`, we have * * f(100) == 2 * f(-100) == NaN * rf = reflect(f) * rf(-100) == -2 * * @param f */ reflect(f) { return (x) => -f(-x); } nice() { const domain = this.domain; let i0 = 0; let i1 = domain.length - 1; let x0 = domain[i0]; let x1 = domain[i1]; if (x1 < x0) { [i0, i1] = [i1, i0]; [x0, x1] = [x1, x0]; } // For example, for base == 10: // [ 50, 900] becomes [ 10, 1000 ] domain[i0] = this.basePow(Math.floor(this.baseLog(x0))); domain[i1] = this.basePow(Math.ceil(this.baseLog(x1))); this.domain = domain; } static pow10(x) { return isFinite(x) ? +('1e' + x) // to avoid precision issues, e.g. Math.pow(10, -4) is not 0.0001 : x < 0 ? 0 : x; } static makePowFn(base) { if (base === 10) { return LogScale.pow10; } if (base === Math.E) { return Math.exp; } return (x) => Math.pow(base, x); } // Make a log function witn an arbitrary base or return a native function if exists. static makeLogFn(base) { if (base === Math.E) { return Math.log; } if (base === 10) { return Math.log10; } if (base === 2) { return Math.log2; } const logBase = Math.log(base); return (x) => Math.log(x) / logBase; } ticks(count = 10) { const n = count == null ? 10 : +count; const base = this.base; const domain = this.domain; let d0 = domain[0]; let d1 = domain[domain.length - 1]; const isReversed = d1 < d0; if (isReversed) { [d0, d1] = [d1, d0]; } let p0 = this.baseLog(d0); let p1 = this.baseLog(d1); let z = []; // if `base` is an integer and delta in order of magnitudes is less than n if (!(base % 1) && p1 - p0 < n) { // For example, if n == 10, base == 10 and domain == [10^2, 10^6] // then p1 - p0 < n == true. p0 = Math.round(p0) - 1; p1 = Math.round(p1) + 1; if (d0 > 0) { for (; p0 < p1; ++p0) { for (let k = 1, p = this.basePow(p0); k < base; ++k) { let t = p * k; // The `t` checks are needed because we expanded the [p0, p1] by 1 in each direction. if (t < d0) continue; if (t > d1) break; z.push(t); } } } else { for (; p0 < p1; ++p0) { for (let k = base - 1, p = this.basePow(p0); k >= 1; --k) { let t = p * k; if (t < d0) continue; if (t > d1) break; z.push(t); } } } if (z.length * 2 < n) { z = ticks_1.default(d0, d1, n); } } else { // For example, if n == 4, base == 10 and domain == [10^2, 10^6] // then p1 - p0 < n == false. // `ticks` return [2, 3, 4, 5, 6], then mapped to [10^2, 10^3, 10^4, 10^5, 10^6]. z = ticks_1.default(p0, p1, Math.min(p1 - p0, n)).map(this.basePow); } return isReversed ? z.reverse() : z; } tickFormat(count, specifier) { const { base } = this; if (specifier == null) { specifier = (base === 10 ? '.0e' : ','); } if (typeof specifier !== 'function') { specifier = numberFormat_1.format(specifier); } if (count === Infinity) { return specifier; } if (count == null) { count = 10; } const k = Math.max(1, base * count / this.ticks().length); return (d) => { let i = d / this.basePow(Math.round(this.baseLog(d))); if (i * base < base - 0.5) { i *= base; } return i <= k ? specifier(d) : ''; }; } } exports.LogScale = LogScale; //# sourceMappingURL=logScale.js.map