Skip to content

Commit cbace1c

Browse files
kurkleetimberg
authored andcommitted
Handle reverse support in core.scale (#6343)
* Move log10 from core.helpers to helpers.math * Refactor scales
1 parent dd6e007 commit cbace1c

16 files changed

+278
-212
lines changed

src/controllers/controller.bar.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ defaults._set('bar', {
3232
* @private
3333
*/
3434
function computeMinSampleSize(scale, pixels) {
35-
var min = scale.isHorizontal() ? scale.width : scale.height;
35+
var min = scale._length;
3636
var ticks = scale.getTicks();
3737
var prev, curr, i, ilen;
3838

@@ -42,7 +42,7 @@ function computeMinSampleSize(scale, pixels) {
4242

4343
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
4444
curr = scale.getPixelForTick(i);
45-
min = i > 0 ? Math.min(min, curr - prev) : min;
45+
min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
4646
prev = curr;
4747
}
4848

@@ -262,9 +262,6 @@ module.exports = DatasetController.extend({
262262
var scale = me._getIndexScale();
263263
var stackCount = me.getStackCount();
264264
var datasetIndex = me.index;
265-
var isHorizontal = scale.isHorizontal();
266-
var start = isHorizontal ? scale.left : scale.top;
267-
var end = start + (isHorizontal ? scale.width : scale.height);
268265
var pixels = [];
269266
var i, ilen, min;
270267

@@ -279,8 +276,8 @@ module.exports = DatasetController.extend({
279276
return {
280277
min: min,
281278
pixels: pixels,
282-
start: start,
283-
end: end,
279+
start: scale._startPixel,
280+
end: scale._endPixel,
284281
stackCount: stackCount,
285282
scale: scale
286283
};

src/core/core.controller.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,11 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
546546

547547
me._layers = [];
548548
helpers.each(me.boxes, function(box) {
549+
// _configure is called twice, once in core.scale.update and once here.
550+
// Here the boxes are fully updated and at their final positions.
551+
if (box._configure) {
552+
box._configure();
553+
}
549554
me._layers.push.apply(me._layers, box._layers());
550555
}, me);
551556

src/core/core.helpers.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,6 @@ module.exports = function() {
100100
}
101101
return x > 0 ? 1 : -1;
102102
};
103-
helpers.log10 = Math.log10 ?
104-
function(x) {
105-
return Math.log10(x);
106-
} :
107-
function(x) {
108-
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
109-
// Check for whole powers of 10,
110-
// which due to floating point rounding error should be corrected.
111-
var powerOf10 = Math.round(exponent);
112-
var isPowerOf10 = x === Math.pow(10, powerOf10);
113-
114-
return isPowerOf10 ? powerOf10 : exponent;
115-
};
116103
helpers.toRadians = function(degrees) {
117104
return degrees * (Math.PI / 180);
118105
};

src/core/core.scale.js

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ function getPixelForGridLine(scale, index, offsetGridLines) {
7070

7171
if (offsetGridLines) {
7272
if (scale.getTicks().length === 1) {
73-
lineValue -= scale.isHorizontal() ?
74-
Math.max(lineValue - scale.left, scale.right - lineValue) :
75-
Math.max(lineValue - scale.top, scale.bottom - lineValue);
73+
lineValue -= Math.max(lineValue - scale._startPixel, scale._endPixel - lineValue);
7674
} else if (index === 0) {
7775
lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
7876
} else {
@@ -318,6 +316,12 @@ var Scale = Element.extend({
318316

319317
me._ticks = ticks;
320318

319+
// _configure is called twice, once here, once from core.controller.updateLayout.
320+
// Here we haven't been positioned yet, but dimensions are correct.
321+
// Variables set in _configure are needed for calculateTickRotation, and
322+
// it's ok that coordinates are not correct there, only dimensions matter.
323+
me._configure();
324+
321325
// Tick Rotation
322326
me.beforeCalculateTickRotation();
323327
me.calculateTickRotation();
@@ -332,6 +336,30 @@ var Scale = Element.extend({
332336
return me.minSize;
333337

334338
},
339+
340+
/**
341+
* @private
342+
*/
343+
_configure: function() {
344+
var me = this;
345+
var reversePixels = me.options.ticks.reverse;
346+
var startPixel, endPixel;
347+
348+
if (me.isHorizontal()) {
349+
startPixel = me.left;
350+
endPixel = me.right;
351+
} else {
352+
startPixel = me.top;
353+
endPixel = me.bottom;
354+
// by default vertical scales are from bottom to top, so pixels are reversed
355+
reversePixels = !reversePixels;
356+
}
357+
me._startPixel = startPixel;
358+
me._endPixel = endPixel;
359+
me._reversePixels = reversePixels;
360+
me._length = endPixel - startPixel;
361+
},
362+
335363
afterUpdate: function() {
336364
helpers.callback(this.options.afterUpdate, [this]);
337365
},
@@ -576,10 +604,11 @@ var Scale = Element.extend({
576604

577605
// Shared Methods
578606
isHorizontal: function() {
579-
return this.options.position === 'top' || this.options.position === 'bottom';
607+
var pos = this.options.position;
608+
return pos === 'top' || pos === 'bottom';
580609
},
581610
isFullWidth: function() {
582-
return (this.options.fullWidth);
611+
return this.options.fullWidth;
583612
},
584613

585614
// 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({
693722
var me = this;
694723
var offset = me.options.offset;
695724
var numTicks = me._ticks.length;
696-
if (index < 0 || index > numTicks - 1) {
697-
return null;
698-
}
699-
if (me.isHorizontal()) {
700-
var tickWidth = me.width / Math.max((numTicks - (offset ? 0 : 1)), 1);
701-
var pixel = (tickWidth * index);
725+
var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1);
702726

703-
if (offset) {
704-
pixel += tickWidth / 2;
705-
}
706-
707-
return me.left + pixel;
708-
}
709-
return me.top + (index * (me.height / (numTicks - 1)));
727+
return index < 0 || index > numTicks - 1
728+
? null
729+
: me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0));
710730
},
711731

712732
/**
@@ -715,9 +735,17 @@ var Scale = Element.extend({
715735
*/
716736
getPixelForDecimal: function(decimal) {
717737
var me = this;
718-
return me.isHorizontal()
719-
? me.left + decimal * me.width
720-
: me.top + decimal * me.height;
738+
739+
if (me._reversePixels) {
740+
decimal = 1 - decimal;
741+
}
742+
743+
return me._startPixel + decimal * me._length;
744+
},
745+
746+
getDecimalForPixel: function(pixel) {
747+
var decimal = (pixel - this._startPixel) / this._length;
748+
return Math.min(1, Math.max(0, this._reversePixels ? 1 - decimal : decimal));
721749
},
722750

723751
/**
@@ -745,7 +773,6 @@ var Scale = Element.extend({
745773
*/
746774
_autoSkip: function(ticks) {
747775
var me = this;
748-
var isHorizontal = me.isHorizontal();
749776
var optionTicks = me.options.ticks;
750777
var tickCount = ticks.length;
751778
var skipRatio = false;
@@ -755,9 +782,7 @@ var Scale = Element.extend({
755782
// drawn as their center at end of axis, so tickCount-1
756783
var ticksLength = me._tickSize() * (tickCount - 1);
757784

758-
// Axis length
759-
var axisLength = isHorizontal ? me.width : me.height;
760-
785+
var axisLength = me._length;
761786
var result = [];
762787
var i, tick;
763788

@@ -788,7 +813,6 @@ var Scale = Element.extend({
788813
*/
789814
_tickSize: function() {
790815
var me = this;
791-
var isHorizontal = me.isHorizontal();
792816
var optionTicks = me.options.ticks;
793817

794818
// Calculate space needed by label in axis direction.
@@ -802,7 +826,7 @@ var Scale = Element.extend({
802826
var h = labelSizes ? labelSizes.highest.height + padding : 0;
803827

804828
// Calculate space needed for 1 tick in axis direction.
805-
return isHorizontal
829+
return me.isHorizontal()
806830
? h * cos > w * sin ? w / cos : h / sin
807831
: h * sin < w * cos ? h / cos : w / sin;
808832
},
@@ -1130,7 +1154,7 @@ var Scale = Element.extend({
11301154
var scaleLabelX, scaleLabelY;
11311155

11321156
if (me.isHorizontal()) {
1133-
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
1157+
scaleLabelX = me.left + me.width / 2; // midpoint of the width
11341158
scaleLabelY = position === 'bottom'
11351159
? me.bottom - halfLineHeight - scaleLabelPadding.bottom
11361160
: me.top + halfLineHeight + scaleLabelPadding.top;
@@ -1139,7 +1163,7 @@ var Scale = Element.extend({
11391163
scaleLabelX = isLeft
11401164
? me.left + halfLineHeight + scaleLabelPadding.top
11411165
: me.right - halfLineHeight - scaleLabelPadding.top;
1142-
scaleLabelY = me.top + ((me.bottom - me.top) / 2);
1166+
scaleLabelY = me.top + me.height / 2;
11431167
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
11441168
}
11451169

src/helpers/helpers.math.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
var helpers = require('./helpers.core');
4+
35
/**
46
* @alias Chart.helpers.math
57
* @namespace
@@ -28,7 +30,28 @@ var exports = {
2830
return a - b;
2931
}).pop();
3032
return result;
33+
},
34+
35+
log10: Math.log10 || function(x) {
36+
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
37+
// Check for whole powers of 10,
38+
// which due to floating point rounding error should be corrected.
39+
var powerOf10 = Math.round(exponent);
40+
var isPowerOf10 = x === Math.pow(10, powerOf10);
41+
42+
return isPowerOf10 ? powerOf10 : exponent;
3143
}
3244
};
3345

3446
module.exports = exports;
47+
48+
// DEPRECATIONS
49+
50+
/**
51+
* Provided for backward compatibility, use Chart.helpers.math.log10 instead.
52+
* @namespace Chart.helpers.log10
53+
* @deprecated since version 2.9.0
54+
* @todo remove at version 3
55+
* @private
56+
*/
57+
helpers.log10 = exports.log10;

src/scales/scale.category.js

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,30 @@ module.exports = Scale.extend({
6363
return me.ticks[index - me.minIndex];
6464
},
6565

66-
// Used to get data value locations. Value can either be an index or a numerical value
67-
getPixelForValue: function(value, index, datasetIndex) {
66+
_configure: function() {
6867
var me = this;
6968
var offset = me.options.offset;
69+
var ticks = me.ticks;
70+
71+
Scale.prototype._configure.call(me);
72+
73+
if (!me.isHorizontal()) {
74+
// For backward compatibility, vertical category scale reverse is inverted.
75+
me._reversePixels = !me._reversePixels;
76+
}
77+
78+
if (!ticks) {
79+
return;
80+
}
7081

71-
// 1 is added because we need the length but we have the indexes
72-
var offsetAmt = Math.max(me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1), 1);
82+
me._startValue = me.minIndex - (offset ? 0.5 : 0);
83+
me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1);
84+
},
7385

74-
var isHorizontal = me.isHorizontal();
75-
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
76-
var valueCategory, labels, idx, pixel;
86+
// Used to get data value locations. Value can either be an index or a numerical value
87+
getPixelForValue: function(value, index, datasetIndex) {
88+
var me = this;
89+
var valueCategory, labels, idx;
7790

7891
if (!isNullOrUndef(index) && !isNullOrUndef(datasetIndex)) {
7992
value = me.chart.data.datasets[datasetIndex].data[index];
@@ -82,53 +95,31 @@ module.exports = Scale.extend({
8295
// If value is a data object, then index is the index in the data array,
8396
// not the index of the scale. We need to change that.
8497
if (!isNullOrUndef(value)) {
85-
valueCategory = isHorizontal ? value.x : value.y;
98+
valueCategory = me.isHorizontal() ? value.x : value.y;
8699
}
87100
if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
88101
labels = me._getLabels();
89102
value = helpers.valueOrDefault(valueCategory, value);
90103
idx = labels.indexOf(value);
91104
index = idx !== -1 ? idx : index;
105+
if (isNaN(index)) {
106+
index = value;
107+
}
92108
}
93-
94-
pixel = valueDimension * (index - me.minIndex);
95-
96-
if (offset) {
97-
pixel += valueDimension / 2;
98-
}
99-
100-
return (isHorizontal ? me.left : me.top) + pixel;
109+
return me.getPixelForDecimal((index - me._startValue) / me._valueRange);
101110
},
102111

103112
getPixelForTick: function(index) {
104113
var ticks = this.ticks;
105-
if (index < 0 || index > ticks.length - 1) {
106-
return null;
107-
}
108-
return this.getPixelForValue(ticks[index], index + this.minIndex);
114+
return index < 0 || index > ticks.length - 1
115+
? null
116+
: this.getPixelForValue(ticks[index], index + this.minIndex);
109117
},
110118

111119
getValueForPixel: function(pixel) {
112120
var me = this;
113-
var offset = me.options.offset;
114-
var offsetAmt = Math.max(me._ticks.length - (offset ? 0 : 1), 1);
115-
var isHorizontal = me.isHorizontal();
116-
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
117-
var value;
118-
119-
pixel -= isHorizontal ? me.left : me.top;
120-
121-
if (offset) {
122-
pixel -= valueDimension / 2;
123-
}
124-
125-
if (pixel <= 0) {
126-
value = 0;
127-
} else {
128-
value = Math.round(pixel / valueDimension);
129-
}
130-
131-
return value + me.minIndex;
121+
var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
122+
return Math.min(Math.max(value, 0), me.ticks.length - 1);
132123
},
133124

134125
getBasePixel: function() {

0 commit comments

Comments
 (0)