]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Refactor/cleanup range option handling (#8057)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Mon, 16 Nov 2020 18:19:59 +0000 (20:19 +0200)
committerGitHub <noreply@github.com>
Mon, 16 Nov 2020 18:19:59 +0000 (20:19 +0200)
src/core/core.scale.js
src/helpers/helpers.core.js
src/scales/scale.linear.js
src/scales/scale.linearbase.js
src/scales/scale.logarithmic.js

index eac35448a4261f8257414859404c6266f41b801b..bad77d4c712a3bba4130330c8588f6124b4c6dad 100644 (file)
@@ -1,7 +1,7 @@
 import defaults from './core.defaults';
 import Element from './core.element';
 import {_alignPixel, _measureText} from '../helpers/helpers.canvas';
-import {callback as call, each, isArray, isFinite, isNullOrUndef, isObject, valueOrDefault} from '../helpers/helpers.core';
+import {callback as call, each, finiteOrDefault, isArray, isFinite, isNullOrUndef, isObject, valueOrDefault} from '../helpers/helpers.core';
 import {_factorize, toDegrees, toRadians, _int16Range, HALF_PI} from '../helpers/helpers.math';
 import {toFont, resolve, toPadding} from '../helpers/helpers.options';
 import Ticks from './core.ticks';
@@ -367,6 +367,8 @@ export default class Scale extends Element {
                this._reversePixels = false;
                this._userMax = undefined;
                this._userMin = undefined;
+               this._suggestedMax = undefined;
+               this._suggestedMin = undefined;
                this._ticksLength = 0;
                this._borderValue = 0;
                this._cache = {};
@@ -386,6 +388,8 @@ export default class Scale extends Element {
                // parse min/max value, so we can properly determine min/max for other scales
                me._userMin = me.parse(options.min);
                me._userMax = me.parse(options.max);
+               me._suggestedMin = me.parse(options.suggestedMin);
+               me._suggestedMax = me.parse(options.suggestedMax);
        }
 
        /**
@@ -404,15 +408,17 @@ export default class Scale extends Element {
         * @since 3.0
         */
        getUserBounds() {
-               let min = this._userMin;
-               let max = this._userMax;
-               if (isNullOrUndef(min) || isNaN(min)) {
-                       min = Number.POSITIVE_INFINITY;
-               }
-               if (isNullOrUndef(max) || isNaN(max)) {
-                       max = Number.NEGATIVE_INFINITY;
-               }
-               return {min, max, minDefined: isFinite(min), maxDefined: isFinite(max)};
+               let {_userMin, _userMax, _suggestedMin, _suggestedMax} = this;
+               _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);
+               _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);
+               _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);
+               _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);
+               return {
+                       min: finiteOrDefault(_userMin, _suggestedMin),
+                       max: finiteOrDefault(_userMax, _suggestedMax),
+                       minDefined: isFinite(_userMin),
+                       maxDefined: isFinite(_userMax)
+               };
        }
 
        /**
@@ -442,7 +448,10 @@ export default class Scale extends Element {
                        }
                }
 
-               return {min, max};
+               return {
+                       min: finiteOrDefault(min, finiteOrDefault(max, min)),
+                       max: finiteOrDefault(max, finiteOrDefault(min, max))
+               };
        }
 
        invalidateCaches() {
index 7066e578ee8905ebde38c6a6a052d788929ba228..5a4da3489a45f2d7737b85ba3b285d4592e08572 100644 (file)
@@ -65,6 +65,16 @@ export {
        isNumberFinite as isFinite,
 };
 
+/**
+ * Returns `value` if finite, else returns `defaultValue`.
+ * @param {*} value - The value to return if defined.
+ * @param {*} defaultValue - The value to return if `value` is not finite.
+ * @returns {*}
+ */
+export function finiteOrDefault(value, defaultValue) {
+       return isNumberFinite(value) ? value : defaultValue;
+}
+
 /**
  * Returns `value` if defined, else returns `defaultValue`.
  * @param {*} value - The value to return if defined.
index bd3fc5af6985e1431a78ac0847134805aaa89a82..8c14d0e225d9976cb6d5d8821d3f9f1422137937 100644 (file)
@@ -1,4 +1,4 @@
-import {isFinite, valueOrDefault} from '../helpers/helpers.core';
+import {isFinite} from '../helpers/helpers.core';
 import LinearScaleBase from './scale.linearbase';
 import Ticks from '../core/core.ticks';
 
@@ -6,16 +6,10 @@ export default class LinearScale extends LinearScaleBase {
 
        determineDataLimits() {
                const me = this;
-               const options = me.options;
                const {min, max} = me.getMinMax(true);
 
-               me.min = isFinite(min) ? min : valueOrDefault(options.suggestedMin, 0);
-               me.max = isFinite(max) ? max : valueOrDefault(options.suggestedMax, 1);
-
-               // Backward compatible inconsistent min for stacked
-               if (options.stacked && min > 0) {
-                       me.min = 0;
-               }
+               me.min = isFinite(min) ? min : 0;
+               me.max = isFinite(max) ? max : 1;
 
                // Common base implementation to handle min, max, beginAtZero
                me.handleTickRangeOptions();
@@ -37,8 +31,7 @@ export default class LinearScale extends LinearScaleBase {
 
        // Utils
        getPixelForValue(value) {
-               const me = this;
-               return me.getPixelForDecimal((value - me._startValue) / me._valueRange);
+               return this.getPixelForDecimal((value - this._startValue) / this._valueRange);
        }
 
        getValueForPixel(pixel) {
index 621482910e52624eaf5c0c5af8c6f61d9468bc23..8155240d608df0f2c0150dfbd2450b063e5e3320 100644 (file)
@@ -125,68 +125,33 @@ export default class LinearScaleBase extends Scale {
 
        handleTickRangeOptions() {
                const me = this;
-               const opts = me.options;
+               const {beginAtZero, stacked} = me.options;
+               const {minDefined, maxDefined} = me.getUserBounds();
+               let {min, max} = me;
+
+               const setMin = v => (min = minDefined ? min : v);
+               const setMax = v => (max = maxDefined ? max : v);
 
-               // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
-               // do nothing since that would make the chart weird. If the user really wants a weird chart
-               // axis, they can manually override it
-               if (opts.beginAtZero) {
-                       const minSign = sign(me.min);
-                       const maxSign = sign(me.max);
+               if (beginAtZero || stacked) {
+                       const minSign = sign(min);
+                       const maxSign = sign(max);
 
                        if (minSign < 0 && maxSign < 0) {
-                               // move the top up to 0
-                               me.max = 0;
+                               setMax(0);
                        } else if (minSign > 0 && maxSign > 0) {
-                               // move the bottom down to 0
-                               me.min = 0;
-                       }
-               }
-
-               const setMin = opts.min !== undefined || opts.suggestedMin !== undefined;
-               const setMax = opts.max !== undefined || opts.suggestedMax !== undefined;
-
-               if (opts.min !== undefined) {
-                       me.min = opts.min;
-               } else if (opts.suggestedMin !== undefined) {
-                       if (me.min === null) {
-                               me.min = opts.suggestedMin;
-                       } else {
-                               me.min = Math.min(me.min, opts.suggestedMin);
-                       }
-               }
-
-               if (opts.max !== undefined) {
-                       me.max = opts.max;
-               } else if (opts.suggestedMax !== undefined) {
-                       if (me.max === null) {
-                               me.max = opts.suggestedMax;
-                       } else {
-                               me.max = Math.max(me.max, opts.suggestedMax);
-                       }
-               }
-
-               if (setMin !== setMax) {
-                       // We set the min or the max but not both.
-                       // So ensure that our range is good
-                       // Inverted or 0 length range can happen when
-                       // min is set, and no datasets are visible
-                       if (me.min >= me.max) {
-                               if (setMin) {
-                                       me.max = me.min + 1;
-                               } else {
-                                       me.min = me.max - 1;
-                               }
+                               setMin(0);
                        }
                }
 
-               if (me.min === me.max) {
-                       me.max++;
+               if (min === max) {
+                       setMax(max + 1);
 
-                       if (!opts.beginAtZero) {
-                               me.min--;
+                       if (!beginAtZero) {
+                               setMin(min - 1);
                        }
                }
+               me.min = min;
+               me.max = max;
        }
 
        getTickLimit() {
index cee47b7f1c8b01c6ddd4f3b1101e3a445df351f1..849084f06a3ef2815b1b74ab06bf38fc9e99ee52 100644 (file)
@@ -1,4 +1,4 @@
-import {isFinite, isNullOrUndef} from '../helpers/helpers.core';
+import {finiteOrDefault, isFinite} from '../helpers/helpers.core';
 import {_setMinAndMaxByKey, log10} from '../helpers/helpers.math';
 import Scale from '../core/core.scale';
 import LinearScaleBase from './scale.linearbase';
@@ -9,10 +9,6 @@ function isMajor(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
@@ -86,38 +82,33 @@ export default class LogarithmicScale extends Scale {
 
        handleTickRangeOptions() {
                const me = this;
-               const {suggestedMax, suggestedMin} = me.options;
-               const DEFAULT_MIN = 1;
-               const DEFAULT_MAX = 10;
+               const {minDefined, maxDefined} = me.getUserBounds();
                let min = me.min;
                let max = me.max;
 
-               if (!isNullOrUndef(suggestedMin)) {
-                       min = Math.min(min, suggestedMin);
-               }
-               if (!isNullOrUndef(suggestedMax)) {
-                       max = Math.max(max, suggestedMax);
-               }
+               const setMin = v => (min = minDefined ? min : v);
+               const setMax = v => (max = maxDefined ? max : v);
+               const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m);
 
                if (min === max) {
                        if (min <= 0) { // includes null
-                               min = DEFAULT_MIN;
-                               max = DEFAULT_MAX;
+                               setMin(1);
+                               setMax(10);
                        } else {
-                               min = Math.pow(10, Math.floor(log10(min)) - 1);
-                               max = Math.pow(10, Math.floor(log10(max)) + 1);
+                               setMin(exp(min, -1));
+                               setMax(exp(max, +1));
                        }
                }
                if (min <= 0) {
-                       min = Math.pow(10, Math.floor(log10(max)) - 1);
+                       setMin(exp(max, -1));
                }
                if (max <= 0) {
-                       max = Math.pow(10, Math.floor(log10(min)) + 1);
+                       setMax(exp(min, +1));
                }
-               // if data has `0` in it or `beginAtZero` is true, and min (non zero) value is at bottom
-               // of scale, lower the min bound by one exp.
-               if (!me._userMin && me._zero && min === Math.pow(10, Math.floor(log10(me.min)))) {
-                       min = Math.pow(10, Math.floor(log10(min)) - 1);
+               // if data has `0` in it or `beginAtZero` is true, min (non zero) value is at bottom
+               // of scale, and it does not equal suggestedMin, lower the min bound by one exp.
+               if (me._zero && me.min !== me._suggestedMin && min === exp(me.min, 0)) {
+                       setMin(exp(min, -1));
                }
                me.min = min;
                me.max = max;