mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-06 16:26:52 +01:00
Handle reverse support in core.scale (#6343)
* Move log10 from core.helpers to helpers.math * Refactor scales
This commit is contained in:
committed by
Evert Timberg
parent
dd6e007ac1
commit
cbace1cfe2
@@ -32,7 +32,7 @@ defaults._set('bar', {
|
||||
* @private
|
||||
*/
|
||||
function computeMinSampleSize(scale, pixels) {
|
||||
var min = scale.isHorizontal() ? scale.width : scale.height;
|
||||
var min = scale._length;
|
||||
var ticks = scale.getTicks();
|
||||
var prev, curr, i, ilen;
|
||||
|
||||
@@ -42,7 +42,7 @@ function computeMinSampleSize(scale, pixels) {
|
||||
|
||||
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
|
||||
curr = scale.getPixelForTick(i);
|
||||
min = i > 0 ? Math.min(min, curr - prev) : min;
|
||||
min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
@@ -262,9 +262,6 @@ module.exports = DatasetController.extend({
|
||||
var scale = me._getIndexScale();
|
||||
var stackCount = me.getStackCount();
|
||||
var datasetIndex = me.index;
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var start = isHorizontal ? scale.left : scale.top;
|
||||
var end = start + (isHorizontal ? scale.width : scale.height);
|
||||
var pixels = [];
|
||||
var i, ilen, min;
|
||||
|
||||
@@ -279,8 +276,8 @@ module.exports = DatasetController.extend({
|
||||
return {
|
||||
min: min,
|
||||
pixels: pixels,
|
||||
start: start,
|
||||
end: end,
|
||||
start: scale._startPixel,
|
||||
end: scale._endPixel,
|
||||
stackCount: stackCount,
|
||||
scale: scale
|
||||
};
|
||||
|
||||
@@ -546,6 +546,11 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
|
||||
|
||||
me._layers = [];
|
||||
helpers.each(me.boxes, function(box) {
|
||||
// _configure is called twice, once in core.scale.update and once here.
|
||||
// Here the boxes are fully updated and at their final positions.
|
||||
if (box._configure) {
|
||||
box._configure();
|
||||
}
|
||||
me._layers.push.apply(me._layers, box._layers());
|
||||
}, me);
|
||||
|
||||
|
||||
@@ -100,19 +100,6 @@ module.exports = function() {
|
||||
}
|
||||
return x > 0 ? 1 : -1;
|
||||
};
|
||||
helpers.log10 = Math.log10 ?
|
||||
function(x) {
|
||||
return Math.log10(x);
|
||||
} :
|
||||
function(x) {
|
||||
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
|
||||
// Check for whole powers of 10,
|
||||
// which due to floating point rounding error should be corrected.
|
||||
var powerOf10 = Math.round(exponent);
|
||||
var isPowerOf10 = x === Math.pow(10, powerOf10);
|
||||
|
||||
return isPowerOf10 ? powerOf10 : exponent;
|
||||
};
|
||||
helpers.toRadians = function(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
};
|
||||
|
||||
@@ -70,9 +70,7 @@ function getPixelForGridLine(scale, index, offsetGridLines) {
|
||||
|
||||
if (offsetGridLines) {
|
||||
if (scale.getTicks().length === 1) {
|
||||
lineValue -= scale.isHorizontal() ?
|
||||
Math.max(lineValue - scale.left, scale.right - lineValue) :
|
||||
Math.max(lineValue - scale.top, scale.bottom - lineValue);
|
||||
lineValue -= Math.max(lineValue - scale._startPixel, scale._endPixel - lineValue);
|
||||
} else if (index === 0) {
|
||||
lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
|
||||
} else {
|
||||
@@ -318,6 +316,12 @@ var Scale = Element.extend({
|
||||
|
||||
me._ticks = ticks;
|
||||
|
||||
// _configure is called twice, once here, once from core.controller.updateLayout.
|
||||
// Here we haven't been positioned yet, but dimensions are correct.
|
||||
// Variables set in _configure are needed for calculateTickRotation, and
|
||||
// it's ok that coordinates are not correct there, only dimensions matter.
|
||||
me._configure();
|
||||
|
||||
// Tick Rotation
|
||||
me.beforeCalculateTickRotation();
|
||||
me.calculateTickRotation();
|
||||
@@ -332,6 +336,30 @@ var Scale = Element.extend({
|
||||
return me.minSize;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_configure: function() {
|
||||
var me = this;
|
||||
var reversePixels = me.options.ticks.reverse;
|
||||
var startPixel, endPixel;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
startPixel = me.left;
|
||||
endPixel = me.right;
|
||||
} else {
|
||||
startPixel = me.top;
|
||||
endPixel = me.bottom;
|
||||
// by default vertical scales are from bottom to top, so pixels are reversed
|
||||
reversePixels = !reversePixels;
|
||||
}
|
||||
me._startPixel = startPixel;
|
||||
me._endPixel = endPixel;
|
||||
me._reversePixels = reversePixels;
|
||||
me._length = endPixel - startPixel;
|
||||
},
|
||||
|
||||
afterUpdate: function() {
|
||||
helpers.callback(this.options.afterUpdate, [this]);
|
||||
},
|
||||
@@ -576,10 +604,11 @@ var Scale = Element.extend({
|
||||
|
||||
// Shared Methods
|
||||
isHorizontal: function() {
|
||||
return this.options.position === 'top' || this.options.position === 'bottom';
|
||||
var pos = this.options.position;
|
||||
return pos === 'top' || pos === 'bottom';
|
||||
},
|
||||
isFullWidth: function() {
|
||||
return (this.options.fullWidth);
|
||||
return this.options.fullWidth;
|
||||
},
|
||||
|
||||
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
|
||||
@@ -693,20 +722,11 @@ var Scale = Element.extend({
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
var numTicks = me._ticks.length;
|
||||
if (index < 0 || index > numTicks - 1) {
|
||||
return null;
|
||||
}
|
||||
if (me.isHorizontal()) {
|
||||
var tickWidth = me.width / Math.max((numTicks - (offset ? 0 : 1)), 1);
|
||||
var pixel = (tickWidth * index);
|
||||
var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1);
|
||||
|
||||
if (offset) {
|
||||
pixel += tickWidth / 2;
|
||||
}
|
||||
|
||||
return me.left + pixel;
|
||||
}
|
||||
return me.top + (index * (me.height / (numTicks - 1)));
|
||||
return index < 0 || index > numTicks - 1
|
||||
? null
|
||||
: me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -715,9 +735,17 @@ var Scale = Element.extend({
|
||||
*/
|
||||
getPixelForDecimal: function(decimal) {
|
||||
var me = this;
|
||||
return me.isHorizontal()
|
||||
? me.left + decimal * me.width
|
||||
: me.top + decimal * me.height;
|
||||
|
||||
if (me._reversePixels) {
|
||||
decimal = 1 - decimal;
|
||||
}
|
||||
|
||||
return me._startPixel + decimal * me._length;
|
||||
},
|
||||
|
||||
getDecimalForPixel: function(pixel) {
|
||||
var decimal = (pixel - this._startPixel) / this._length;
|
||||
return Math.min(1, Math.max(0, this._reversePixels ? 1 - decimal : decimal));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -745,7 +773,6 @@ var Scale = Element.extend({
|
||||
*/
|
||||
_autoSkip: function(ticks) {
|
||||
var me = this;
|
||||
var isHorizontal = me.isHorizontal();
|
||||
var optionTicks = me.options.ticks;
|
||||
var tickCount = ticks.length;
|
||||
var skipRatio = false;
|
||||
@@ -755,9 +782,7 @@ var Scale = Element.extend({
|
||||
// drawn as their center at end of axis, so tickCount-1
|
||||
var ticksLength = me._tickSize() * (tickCount - 1);
|
||||
|
||||
// Axis length
|
||||
var axisLength = isHorizontal ? me.width : me.height;
|
||||
|
||||
var axisLength = me._length;
|
||||
var result = [];
|
||||
var i, tick;
|
||||
|
||||
@@ -788,7 +813,6 @@ var Scale = Element.extend({
|
||||
*/
|
||||
_tickSize: function() {
|
||||
var me = this;
|
||||
var isHorizontal = me.isHorizontal();
|
||||
var optionTicks = me.options.ticks;
|
||||
|
||||
// Calculate space needed by label in axis direction.
|
||||
@@ -802,7 +826,7 @@ var Scale = Element.extend({
|
||||
var h = labelSizes ? labelSizes.highest.height + padding : 0;
|
||||
|
||||
// Calculate space needed for 1 tick in axis direction.
|
||||
return isHorizontal
|
||||
return me.isHorizontal()
|
||||
? h * cos > w * sin ? w / cos : h / sin
|
||||
: h * sin < w * cos ? h / cos : w / sin;
|
||||
},
|
||||
@@ -1130,7 +1154,7 @@ var Scale = Element.extend({
|
||||
var scaleLabelX, scaleLabelY;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
|
||||
scaleLabelX = me.left + me.width / 2; // midpoint of the width
|
||||
scaleLabelY = position === 'bottom'
|
||||
? me.bottom - halfLineHeight - scaleLabelPadding.bottom
|
||||
: me.top + halfLineHeight + scaleLabelPadding.top;
|
||||
@@ -1139,7 +1163,7 @@ var Scale = Element.extend({
|
||||
scaleLabelX = isLeft
|
||||
? me.left + halfLineHeight + scaleLabelPadding.top
|
||||
: me.right - halfLineHeight - scaleLabelPadding.top;
|
||||
scaleLabelY = me.top + ((me.bottom - me.top) / 2);
|
||||
scaleLabelY = me.top + me.height / 2;
|
||||
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var helpers = require('./helpers.core');
|
||||
|
||||
/**
|
||||
* @alias Chart.helpers.math
|
||||
* @namespace
|
||||
@@ -28,7 +30,28 @@ var exports = {
|
||||
return a - b;
|
||||
}).pop();
|
||||
return result;
|
||||
},
|
||||
|
||||
log10: Math.log10 || function(x) {
|
||||
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
|
||||
// Check for whole powers of 10,
|
||||
// which due to floating point rounding error should be corrected.
|
||||
var powerOf10 = Math.round(exponent);
|
||||
var isPowerOf10 = x === Math.pow(10, powerOf10);
|
||||
|
||||
return isPowerOf10 ? powerOf10 : exponent;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = exports;
|
||||
|
||||
// DEPRECATIONS
|
||||
|
||||
/**
|
||||
* Provided for backward compatibility, use Chart.helpers.math.log10 instead.
|
||||
* @namespace Chart.helpers.log10
|
||||
* @deprecated since version 2.9.0
|
||||
* @todo remove at version 3
|
||||
* @private
|
||||
*/
|
||||
helpers.log10 = exports.log10;
|
||||
|
||||
@@ -63,17 +63,30 @@ module.exports = Scale.extend({
|
||||
return me.ticks[index - me.minIndex];
|
||||
},
|
||||
|
||||
_configure: function() {
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
var ticks = me.ticks;
|
||||
|
||||
Scale.prototype._configure.call(me);
|
||||
|
||||
if (!me.isHorizontal()) {
|
||||
// For backward compatibility, vertical category scale reverse is inverted.
|
||||
me._reversePixels = !me._reversePixels;
|
||||
}
|
||||
|
||||
if (!ticks) {
|
||||
return;
|
||||
}
|
||||
|
||||
me._startValue = me.minIndex - (offset ? 0.5 : 0);
|
||||
me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1);
|
||||
},
|
||||
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue: function(value, index, datasetIndex) {
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
|
||||
// 1 is added because we need the length but we have the indexes
|
||||
var offsetAmt = Math.max(me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1), 1);
|
||||
|
||||
var isHorizontal = me.isHorizontal();
|
||||
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
|
||||
var valueCategory, labels, idx, pixel;
|
||||
var valueCategory, labels, idx;
|
||||
|
||||
if (!isNullOrUndef(index) && !isNullOrUndef(datasetIndex)) {
|
||||
value = me.chart.data.datasets[datasetIndex].data[index];
|
||||
@@ -82,53 +95,31 @@ module.exports = Scale.extend({
|
||||
// If value is a data object, then index is the index in the data array,
|
||||
// not the index of the scale. We need to change that.
|
||||
if (!isNullOrUndef(value)) {
|
||||
valueCategory = isHorizontal ? value.x : value.y;
|
||||
valueCategory = me.isHorizontal() ? value.x : value.y;
|
||||
}
|
||||
if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
|
||||
labels = me._getLabels();
|
||||
value = helpers.valueOrDefault(valueCategory, value);
|
||||
idx = labels.indexOf(value);
|
||||
index = idx !== -1 ? idx : index;
|
||||
if (isNaN(index)) {
|
||||
index = value;
|
||||
}
|
||||
}
|
||||
|
||||
pixel = valueDimension * (index - me.minIndex);
|
||||
|
||||
if (offset) {
|
||||
pixel += valueDimension / 2;
|
||||
}
|
||||
|
||||
return (isHorizontal ? me.left : me.top) + pixel;
|
||||
return me.getPixelForDecimal((index - me._startValue) / me._valueRange);
|
||||
},
|
||||
|
||||
getPixelForTick: function(index) {
|
||||
var ticks = this.ticks;
|
||||
if (index < 0 || index > ticks.length - 1) {
|
||||
return null;
|
||||
}
|
||||
return this.getPixelForValue(ticks[index], index + this.minIndex);
|
||||
return index < 0 || index > ticks.length - 1
|
||||
? null
|
||||
: this.getPixelForValue(ticks[index], index + this.minIndex);
|
||||
},
|
||||
|
||||
getValueForPixel: function(pixel) {
|
||||
var me = this;
|
||||
var offset = me.options.offset;
|
||||
var offsetAmt = Math.max(me._ticks.length - (offset ? 0 : 1), 1);
|
||||
var isHorizontal = me.isHorizontal();
|
||||
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
|
||||
var value;
|
||||
|
||||
pixel -= isHorizontal ? me.left : me.top;
|
||||
|
||||
if (offset) {
|
||||
pixel -= valueDimension / 2;
|
||||
}
|
||||
|
||||
if (pixel <= 0) {
|
||||
value = 0;
|
||||
} else {
|
||||
value = Math.round(pixel / valueDimension);
|
||||
}
|
||||
|
||||
return value + me.minIndex;
|
||||
var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
|
||||
return Math.min(Math.max(value, 0), me.ticks.length - 1);
|
||||
},
|
||||
|
||||
getBasePixel: function() {
|
||||
|
||||
@@ -149,29 +149,12 @@ module.exports = LinearScaleBase.extend({
|
||||
|
||||
// Utils
|
||||
getPixelForValue: function(value) {
|
||||
// This must be called after fit has been run so that
|
||||
// this.left, this.top, this.right, and this.bottom have been defined
|
||||
var me = this;
|
||||
var start = me.start;
|
||||
|
||||
var rightValue = +me.getRightValue(value);
|
||||
var pixel;
|
||||
var range = me.end - start;
|
||||
|
||||
if (me.isHorizontal()) {
|
||||
pixel = me.left + (me.width / range * (rightValue - start));
|
||||
} else {
|
||||
pixel = me.bottom - (me.height / range * (rightValue - start));
|
||||
}
|
||||
return pixel;
|
||||
return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange);
|
||||
},
|
||||
|
||||
getValueForPixel: function(pixel) {
|
||||
var me = this;
|
||||
var isHorizontal = me.isHorizontal();
|
||||
var innerDimension = isHorizontal ? me.width : me.height;
|
||||
var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
|
||||
return me.start + ((me.end - me.start) * offset);
|
||||
return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
|
||||
},
|
||||
|
||||
getPixelForTick: function(index) {
|
||||
|
||||
@@ -231,5 +231,24 @@ module.exports = Scale.extend({
|
||||
me.zeroLineIndex = me.ticks.indexOf(0);
|
||||
|
||||
Scale.prototype.convertTicksToLabels.call(me);
|
||||
},
|
||||
|
||||
_configure: function() {
|
||||
var me = this;
|
||||
var ticks = me.getTicks();
|
||||
var start = me.min;
|
||||
var end = me.max;
|
||||
var offset;
|
||||
|
||||
Scale.prototype._configure.call(me);
|
||||
|
||||
if (me.options.offset && ticks.length) {
|
||||
offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
|
||||
start -= offset;
|
||||
end += offset;
|
||||
}
|
||||
me._startValue = start;
|
||||
me._endValue = end;
|
||||
me._valueRange = end - start;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ var Scale = require('../core/core.scale');
|
||||
var Ticks = require('../core/core.ticks');
|
||||
|
||||
var valueOrDefault = helpers.valueOrDefault;
|
||||
var log10 = helpers.math.log10;
|
||||
|
||||
/**
|
||||
* Generate a set of logarithmic ticks
|
||||
@@ -16,20 +17,20 @@ var valueOrDefault = helpers.valueOrDefault;
|
||||
function generateTicks(generationOptions, dataRange) {
|
||||
var ticks = [];
|
||||
|
||||
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
|
||||
var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min))));
|
||||
|
||||
var endExp = Math.floor(helpers.log10(dataRange.max));
|
||||
var endExp = Math.floor(log10(dataRange.max));
|
||||
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
|
||||
var exp, significand;
|
||||
|
||||
if (tickVal === 0) {
|
||||
exp = Math.floor(helpers.log10(dataRange.minNotZero));
|
||||
exp = Math.floor(log10(dataRange.minNotZero));
|
||||
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
|
||||
|
||||
ticks.push(tickVal);
|
||||
tickVal = significand * Math.pow(10, exp);
|
||||
} else {
|
||||
exp = Math.floor(helpers.log10(tickVal));
|
||||
exp = Math.floor(log10(tickVal));
|
||||
significand = Math.floor(tickVal / Math.pow(10, exp));
|
||||
}
|
||||
var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
|
||||
@@ -178,26 +179,26 @@ module.exports = Scale.extend({
|
||||
|
||||
if (me.min === me.max) {
|
||||
if (me.min !== 0 && me.min !== null) {
|
||||
me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
|
||||
me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
|
||||
me.min = Math.pow(10, Math.floor(log10(me.min)) - 1);
|
||||
me.max = Math.pow(10, Math.floor(log10(me.max)) + 1);
|
||||
} else {
|
||||
me.min = DEFAULT_MIN;
|
||||
me.max = DEFAULT_MAX;
|
||||
}
|
||||
}
|
||||
if (me.min === null) {
|
||||
me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);
|
||||
me.min = Math.pow(10, Math.floor(log10(me.max)) - 1);
|
||||
}
|
||||
if (me.max === null) {
|
||||
me.max = me.min !== 0
|
||||
? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)
|
||||
? Math.pow(10, Math.floor(log10(me.min)) + 1)
|
||||
: DEFAULT_MAX;
|
||||
}
|
||||
if (me.minNotZero === null) {
|
||||
if (me.min > 0) {
|
||||
me.minNotZero = me.min;
|
||||
} else if (me.max < 1) {
|
||||
me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));
|
||||
me.minNotZero = Math.pow(10, Math.floor(log10(me.max)));
|
||||
} else {
|
||||
me.minNotZero = DEFAULT_MIN;
|
||||
}
|
||||
@@ -259,87 +260,47 @@ module.exports = Scale.extend({
|
||||
* @private
|
||||
*/
|
||||
_getFirstTickValue: function(value) {
|
||||
var exp = Math.floor(helpers.log10(value));
|
||||
var exp = Math.floor(log10(value));
|
||||
var significand = Math.floor(value / Math.pow(10, exp));
|
||||
|
||||
return significand * Math.pow(10, exp);
|
||||
},
|
||||
|
||||
_configure: function() {
|
||||
var me = this;
|
||||
var start = me.min;
|
||||
var offset = 0;
|
||||
|
||||
Scale.prototype._configure.call(me);
|
||||
|
||||
if (start === 0) {
|
||||
start = me._getFirstTickValue(me.minNotZero);
|
||||
offset = valueOrDefault(me.options.ticks.fontSize, defaults.global.defaultFontSize) / me._length;
|
||||
}
|
||||
|
||||
me._startValue = log10(start);
|
||||
me._valueOffset = offset;
|
||||
me._valueRange = (log10(me.max) - log10(start)) / (1 - offset);
|
||||
},
|
||||
|
||||
getPixelForValue: function(value) {
|
||||
var me = this;
|
||||
var tickOpts = me.options.ticks;
|
||||
var reverse = tickOpts.reverse;
|
||||
var log10 = helpers.log10;
|
||||
var firstTickValue = me._getFirstTickValue(me.minNotZero);
|
||||
var offset = 0;
|
||||
var innerDimension, pixel, start, end, sign;
|
||||
var decimal = 0;
|
||||
|
||||
value = +me.getRightValue(value);
|
||||
if (reverse) {
|
||||
start = me.end;
|
||||
end = me.start;
|
||||
sign = -1;
|
||||
} else {
|
||||
start = me.start;
|
||||
end = me.end;
|
||||
sign = 1;
|
||||
|
||||
if (value > me.min && value > 0) {
|
||||
decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset;
|
||||
}
|
||||
if (me.isHorizontal()) {
|
||||
innerDimension = me.width;
|
||||
pixel = reverse ? me.right : me.left;
|
||||
} else {
|
||||
innerDimension = me.height;
|
||||
sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
|
||||
pixel = reverse ? me.top : me.bottom;
|
||||
}
|
||||
if (value !== start) {
|
||||
if (start === 0) { // include zero tick
|
||||
offset = valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
|
||||
innerDimension -= offset;
|
||||
start = firstTickValue;
|
||||
}
|
||||
if (value !== 0) {
|
||||
offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
|
||||
}
|
||||
pixel += sign * offset;
|
||||
}
|
||||
return pixel;
|
||||
return me.getPixelForDecimal(decimal);
|
||||
},
|
||||
|
||||
getValueForPixel: function(pixel) {
|
||||
var me = this;
|
||||
var tickOpts = me.options.ticks;
|
||||
var reverse = tickOpts.reverse;
|
||||
var log10 = helpers.log10;
|
||||
var firstTickValue = me._getFirstTickValue(me.minNotZero);
|
||||
var innerDimension, start, end, value;
|
||||
|
||||
if (reverse) {
|
||||
start = me.end;
|
||||
end = me.start;
|
||||
} else {
|
||||
start = me.start;
|
||||
end = me.end;
|
||||
}
|
||||
if (me.isHorizontal()) {
|
||||
innerDimension = me.width;
|
||||
value = reverse ? me.right - pixel : pixel - me.left;
|
||||
} else {
|
||||
innerDimension = me.height;
|
||||
value = reverse ? pixel - me.top : me.bottom - pixel;
|
||||
}
|
||||
if (value !== start) {
|
||||
if (start === 0) { // include zero tick
|
||||
var offset = valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
|
||||
value -= offset;
|
||||
innerDimension -= offset;
|
||||
start = firstTickValue;
|
||||
}
|
||||
value *= log10(end) - log10(start);
|
||||
value /= innerDimension;
|
||||
value = Math.pow(10, log10(start) + value);
|
||||
}
|
||||
return value;
|
||||
var decimal = me.getDecimalForPixel(pixel);
|
||||
return decimal === 0 && me.min === 0
|
||||
? 0
|
||||
: Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -596,7 +596,6 @@ module.exports = Scale.extend({
|
||||
me.max = Math.max(min + 1, max);
|
||||
|
||||
// PRIVATE
|
||||
me._horizontal = me.isHorizontal();
|
||||
me._table = [];
|
||||
me._timestamps = {
|
||||
data: timestamps,
|
||||
@@ -611,16 +610,16 @@ module.exports = Scale.extend({
|
||||
var max = me.max;
|
||||
var options = me.options;
|
||||
var timeOpts = options.time;
|
||||
var timestamps = [];
|
||||
var timestamps = me._timestamps;
|
||||
var ticks = [];
|
||||
var i, ilen, timestamp;
|
||||
|
||||
switch (options.ticks.source) {
|
||||
case 'data':
|
||||
timestamps = me._timestamps.data;
|
||||
timestamps = timestamps.data;
|
||||
break;
|
||||
case 'labels':
|
||||
timestamps = me._timestamps.labels;
|
||||
timestamps = timestamps.labels;
|
||||
break;
|
||||
case 'auto':
|
||||
default:
|
||||
@@ -725,13 +724,8 @@ module.exports = Scale.extend({
|
||||
getPixelForOffset: function(time) {
|
||||
var me = this;
|
||||
var offsets = me._offsets;
|
||||
var size = me._horizontal ? me.width : me.height;
|
||||
var pos = interpolate(me._table, 'time', time, 'pos');
|
||||
var offset = size * (offsets.start + pos) * offsets.factor;
|
||||
|
||||
return me.options.ticks.reverse ?
|
||||
(me._horizontal ? me.right : me.bottom) - offset :
|
||||
(me._horizontal ? me.left : me.top) + offset;
|
||||
return me.getPixelForDecimal((offsets.start + pos) * offsets.factor);
|
||||
},
|
||||
|
||||
getPixelForValue: function(value, index, datasetIndex) {
|
||||
@@ -761,11 +755,7 @@ module.exports = Scale.extend({
|
||||
getValueForPixel: function(pixel) {
|
||||
var me = this;
|
||||
var offsets = me._offsets;
|
||||
var size = me._horizontal ? me.width : me.height;
|
||||
var offset = me.options.ticks.reverse ?
|
||||
(me._horizontal ? me.right : me.bottom) - pixel :
|
||||
pixel - (me._horizontal ? me.left : me.top);
|
||||
var pos = offset / size / offsets.factor - offsets.start;
|
||||
var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
|
||||
var time = interpolate(me._table, 'pos', pos, 'time');
|
||||
|
||||
// DEPRECATION, we should return time directly
|
||||
|
||||
@@ -26,16 +26,6 @@ describe('Core helper tests', function() {
|
||||
expect(helpers.sign(-5)).toBe(-1);
|
||||
});
|
||||
|
||||
it('should do a log10 operation', function() {
|
||||
expect(helpers.log10(0)).toBe(-Infinity);
|
||||
|
||||
// Check all allowed powers of 10, which should return integer values
|
||||
var maxPowerOf10 = Math.floor(helpers.log10(Number.MAX_VALUE));
|
||||
for (var i = 0; i < maxPowerOf10; i += 1) {
|
||||
expect(helpers.log10(Math.pow(10, i))).toBe(i);
|
||||
}
|
||||
});
|
||||
|
||||
it('should correctly determine if two numbers are essentially equal', function() {
|
||||
expect(helpers.almostEquals(0, Number.EPSILON, 2 * Number.EPSILON)).toBe(true);
|
||||
expect(helpers.almostEquals(1, 1.1, 0.0001)).toBe(false);
|
||||
|
||||
@@ -94,7 +94,7 @@ describe('Core.scale', function() {
|
||||
labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5'],
|
||||
offsetGridLines: true,
|
||||
offset: true,
|
||||
expected: [-0.5, 102.5, 204.5, 307.5, 409.5]
|
||||
expected: [0.5, 102.5, 204.5, 307.5, 409.5]
|
||||
}, {
|
||||
labels: ['tick1'],
|
||||
offsetGridLines: false,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
describe('Chart.helpers.math', function() {
|
||||
var factorize = Chart.helpers.math._factorize;
|
||||
var math = Chart.helpers.math;
|
||||
var factorize = math._factorize;
|
||||
|
||||
it('should factorize', function() {
|
||||
expect(factorize(1000)).toEqual([1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500]);
|
||||
@@ -13,4 +15,14 @@ describe('Chart.helpers.math', function() {
|
||||
expect(factorize(-1)).toEqual([]);
|
||||
expect(factorize(2.76)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should do a log10 operation', function() {
|
||||
expect(math.log10(0)).toBe(-Infinity);
|
||||
|
||||
// Check all allowed powers of 10, which should return integer values
|
||||
var maxPowerOf10 = Math.floor(math.log10(Number.MAX_VALUE));
|
||||
for (var i = 0; i < maxPowerOf10; i += 1) {
|
||||
expect(math.log10(Math.pow(10, i))).toBe(i);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1196,4 +1196,86 @@ describe('Linear Scale', function() {
|
||||
expect(chart.scales.yScale0).not.toEqual(undefined); // must construct
|
||||
expect(chart.scales.yScale0.max).toBeGreaterThan(chart.scales.yScale0.min);
|
||||
});
|
||||
|
||||
it('Should get correct pixel values when horizontal', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'horizontalBar',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [0.05, -25, 10, 15, 20, 25, 30, 35]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x',
|
||||
type: 'linear',
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var start = chart.chartArea.left;
|
||||
var end = chart.chartArea.right;
|
||||
var min = -30;
|
||||
var max = 40;
|
||||
var scale = chart.scales.x;
|
||||
|
||||
expect(scale.getPixelForValue(max)).toBeCloseToPixel(end);
|
||||
expect(scale.getPixelForValue(min)).toBeCloseToPixel(start);
|
||||
expect(scale.getValueForPixel(end)).toBeCloseTo(max, 4);
|
||||
expect(scale.getValueForPixel(start)).toBeCloseTo(min, 4);
|
||||
|
||||
scale.options.ticks.reverse = true;
|
||||
chart.update();
|
||||
|
||||
start = chart.chartArea.left;
|
||||
end = chart.chartArea.right;
|
||||
|
||||
expect(scale.getPixelForValue(max)).toBeCloseToPixel(start);
|
||||
expect(scale.getPixelForValue(min)).toBeCloseToPixel(end);
|
||||
expect(scale.getValueForPixel(end)).toBeCloseTo(min, 4);
|
||||
expect(scale.getValueForPixel(start)).toBeCloseTo(max, 4);
|
||||
});
|
||||
|
||||
it('Should get correct pixel values when vertical', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'bar',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [0.05, -25, 10, 15, 20, 25, 30, 35]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
id: 'y',
|
||||
type: 'linear',
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var start = chart.chartArea.bottom;
|
||||
var end = chart.chartArea.top;
|
||||
var min = -30;
|
||||
var max = 40;
|
||||
var scale = chart.scales.y;
|
||||
|
||||
expect(scale.getPixelForValue(max)).toBeCloseToPixel(end);
|
||||
expect(scale.getPixelForValue(min)).toBeCloseToPixel(start);
|
||||
expect(scale.getValueForPixel(end)).toBeCloseTo(max, 4);
|
||||
expect(scale.getValueForPixel(start)).toBeCloseTo(min, 4);
|
||||
|
||||
scale.options.ticks.reverse = true;
|
||||
chart.update();
|
||||
|
||||
start = chart.chartArea.bottom;
|
||||
end = chart.chartArea.top;
|
||||
|
||||
expect(scale.getPixelForValue(max)).toBeCloseToPixel(start);
|
||||
expect(scale.getPixelForValue(min)).toBeCloseToPixel(end);
|
||||
expect(scale.getValueForPixel(end)).toBeCloseTo(min, 4);
|
||||
expect(scale.getValueForPixel(start)).toBeCloseTo(max, 4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -862,6 +862,8 @@ describe('Logarithmic Scale tests', function() {
|
||||
type: 'logarithmic'
|
||||
}];
|
||||
Chart.helpers.extend(scaleConfig, setup.scale);
|
||||
scaleConfig[setup.axis + 'Axes'][0].type = 'logarithmic';
|
||||
|
||||
var description = 'dataset has stack option and ' + setup.describe
|
||||
+ ' and axis is "' + setup.axis + '";';
|
||||
describe(description, function() {
|
||||
|
||||
@@ -1551,11 +1551,11 @@ describe('Time scale tests', function() {
|
||||
var firstTickInterval = scale.getPixelForTick(1) - scale.getPixelForTick(0);
|
||||
var lastTickInterval = scale.getPixelForTick(numTicks - 1) - scale.getPixelForTick(numTicks - 2);
|
||||
|
||||
expect(scale.getValueForPixel(scale.left + firstTickInterval / 2)).toBeCloseToTime({
|
||||
expect(scale.getValueForPixel(scale.left + lastTickInterval / 2)).toBeCloseToTime({
|
||||
value: moment('2042-01-01T00:00:00'),
|
||||
unit: 'hour',
|
||||
});
|
||||
expect(scale.getValueForPixel(scale.left + scale.width - lastTickInterval / 2)).toBeCloseToTime({
|
||||
expect(scale.getValueForPixel(scale.left + scale.width - firstTickInterval / 2)).toBeCloseToTime({
|
||||
value: moment('2017-01-01T00:00:00'),
|
||||
unit: 'hour',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user