Chart.js/src/scales/scale.logarithmic.js
Jukka Kurkela 25a9969489
Enable esnext and fix all lint errors (#7094)
* enable esnext and fix all lint errors

* Review update

* Missed some

* Some cleanup still

* Remove leftover eslint disable
2020-02-13 18:41:49 -05:00

202 lines
4.7 KiB
JavaScript

import {isFinite} from '../helpers/helpers.core';
import {_setMinAndMaxByKey, log10} from '../helpers/helpers.math';
import Scale from '../core/core.scale';
import LinearScaleBase from './scale.linearbase';
import Ticks from '../core/core.ticks';
function isMajor(tickVal) {
const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal))));
return remain === 1;
}
function finiteOrDefault(value, def) {
return isFinite(value) ? value : def;
}
/**
* Generate a set of logarithmic ticks
* @param generationOptions the options used to generate the ticks
* @param dataRange the range of the data
* @returns {object[]} array of tick objects
*/
function generateTicks(generationOptions, dataRange) {
const endExp = Math.floor(log10(dataRange.max));
const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
const ticks = [];
let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min))));
let exp = Math.floor(log10(tickVal));
let significand = Math.floor(tickVal / Math.pow(10, exp));
let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
do {
ticks.push({value: tickVal, major: isMajor(tickVal)});
++significand;
if (significand === 10) {
significand = 1;
++exp;
precision = exp >= 0 ? 1 : precision;
}
tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
} while (exp < endExp || (exp === endExp && significand < endSignificand));
const lastTick = finiteOrDefault(generationOptions.max, tickVal);
ticks.push({value: lastTick, major: isMajor(tickVal)});
return ticks;
}
const defaultConfig = {
// label settings
ticks: {
callback: Ticks.formatters.numeric,
major: {
enabled: true
}
}
};
class LogarithmicScale extends Scale {
constructor(cfg) {
super(cfg);
/** @type {number} */
this.start = undefined;
/** @type {number} */
this.end = undefined;
/** @type {number} */
this._startValue = undefined;
/** @type {number} */
this._valueRange = undefined;
}
/**
* @private
*/
_parse(raw, index) {
const value = LinearScaleBase.prototype._parse.apply(this, [raw, index]);
if (value === 0) {
return undefined;
}
return isFinite(value) && value > 0 ? value : NaN;
}
determineDataLimits() {
const me = this;
const minmax = me._getMinMax(true);
const min = minmax.min;
const max = minmax.max;
me.min = isFinite(min) ? Math.max(0, min) : null;
me.max = isFinite(max) ? Math.max(0, max) : null;
me.handleTickRangeOptions();
}
handleTickRangeOptions() {
const me = this;
const DEFAULT_MIN = 1;
const DEFAULT_MAX = 10;
let min = me.min;
let max = me.max;
if (min === max) {
if (min <= 0) { // includes null
min = DEFAULT_MIN;
max = DEFAULT_MAX;
} else {
min = Math.pow(10, Math.floor(log10(min)) - 1);
max = Math.pow(10, Math.floor(log10(max)) + 1);
}
}
if (min <= 0) {
min = Math.pow(10, Math.floor(log10(max)) - 1);
}
if (max <= 0) {
max = Math.pow(10, Math.floor(log10(min)) + 1);
}
me.min = min;
me.max = max;
}
buildTicks() {
const me = this;
const opts = me.options;
const generationOptions = {
min: me._userMin,
max: me._userMax
};
const ticks = generateTicks(generationOptions, me);
let reverse = !me.isHorizontal();
// At this point, we need to update our max and min given the tick values since we have expanded the
// range of the scale
_setMinAndMaxByKey(ticks, me, 'value');
if (opts.reverse) {
reverse = !reverse;
me.start = me.max;
me.end = me.min;
} else {
me.start = me.min;
me.end = me.max;
}
if (reverse) {
ticks.reverse();
}
return ticks;
}
/**
* @param {number} value
* @return {string}
*/
getLabelForValue(value) {
return value === undefined ? '0' : new Intl.NumberFormat(this.options.locale).format(value);
}
getPixelForTick(index) {
const ticks = this.ticks;
if (index < 0 || index > ticks.length - 1) {
return null;
}
return this.getPixelForValue(ticks[index].value);
}
/**
* @private
*/
_configure() {
const me = this;
const start = me.min;
Scale.prototype._configure.call(me);
me._startValue = log10(start);
me._valueRange = log10(me.max) - log10(start);
}
getPixelForValue(value) {
const me = this;
if (value === undefined || value === 0) {
value = me.min;
}
return me.getPixelForDecimal(value === me.min
? 0
: (log10(value) - me._startValue) / me._valueRange);
}
getValueForPixel(pixel) {
const me = this;
const decimal = me.getDecimalForPixel(pixel);
return Math.pow(10, me._startValue + decimal * me._valueRange);
}
}
// INTERNAL: static default options, registered in src/index.js
LogarithmicScale._defaults = defaultConfig;
export default LogarithmicScale;