Skip to content

Commit

Permalink
Handle reverse support in core.scale (chartjs#6343)
Browse files Browse the repository at this point in the history
* Move log10 from core.helpers to helpers.math

* Refactor scales
  • Loading branch information
kurkle authored and etimberg committed Jul 15, 2019
1 parent 1568627 commit a4a41c0
Show file tree
Hide file tree
Showing 16 changed files with 278 additions and 212 deletions.
11 changes: 4 additions & 7 deletions src/controllers/controller.bar.js
Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -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;

Expand All @@ -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
};
Expand Down
5 changes: 5 additions & 0 deletions src/core/core.controller.js
Expand Up @@ -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);

Expand Down
13 changes: 0 additions & 13 deletions src/core/core.helpers.js
Expand Up @@ -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);
};
Expand Down
82 changes: 53 additions & 29 deletions src/core/core.scale.js
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand All @@ -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]);
},
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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));
},

/**
Expand All @@ -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));
},

/**
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -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;
},
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
23 changes: 23 additions & 0 deletions src/helpers/helpers.math.js
@@ -1,5 +1,7 @@
'use strict';

var helpers = require('./helpers.core');

/**
* @alias Chart.helpers.math
* @namespace
Expand Down Expand Up @@ -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;
69 changes: 30 additions & 39 deletions src/scales/scale.category.js
Expand Up @@ -63,17 +63,30 @@ module.exports = Scale.extend({
return me.ticks[index - me.minIndex];
},

// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index, datasetIndex) {
_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;
}

// 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);
me._startValue = me.minIndex - (offset ? 0.5 : 0);
me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1);
},

var isHorizontal = me.isHorizontal();
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
var valueCategory, labels, idx, pixel;
// 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 valueCategory, labels, idx;

if (!isNullOrUndef(index) && !isNullOrUndef(datasetIndex)) {
value = me.chart.data.datasets[datasetIndex].data[index];
Expand All @@ -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() {
Expand Down

0 comments on commit a4a41c0

Please sign in to comment.