getPixelForTick: function(index) {
return this.getPixelForValue(this.tickValues[index]);
},
+ /**
+ * Returns the value of the first tick.
+ * @param {Number} value - The minimum not zero value.
+ * @return {Number} The first tick value.
+ * @private
+ */
+ _getFirstTickValue: function(value) {
+ var exp = Math.floor(helpers.log10(value));
+ var significand = Math.floor(value / Math.pow(10, exp));
+
+ return significand * Math.pow(10, exp);
+ },
getPixelForValue: function(value) {
var me = this;
- var start = me.start;
- var newVal = +me.getRightValue(value);
- var opts = me.options;
- var tickOpts = opts.ticks;
- var innerDimension, pixel, range;
+ var reverse = me.options.ticks.reverse;
+ var log10 = helpers.log10;
+ var firstTickValue = me._getFirstTickValue(me.minNotZero);
+ var offset = 0;
+ var innerDimension, pixel, start, end, sign;
+ value = +me.getRightValue(value);
+ if (reverse) {
+ start = me.end;
+ end = me.start;
+ sign = -1;
+ } else {
+ start = me.start;
+ end = me.end;
+ sign = 1;
+ }
if (me.isHorizontal()) {
- range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
- if (newVal === 0) {
- pixel = me.left;
- } else {
- innerDimension = me.width;
- pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
- }
+ innerDimension = me.width;
+ pixel = reverse ? me.right : me.left;
} else {
- // Bottom - top since pixels increase downward on a screen
innerDimension = me.height;
- if (start === 0 && !tickOpts.reverse) {
- range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
- if (newVal === start) {
- pixel = me.bottom;
- } else if (newVal === me.minNotZero) {
- pixel = me.bottom - innerDimension * 0.02;
- } else {
- pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
- }
- } else if (me.end === 0 && tickOpts.reverse) {
- range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
- if (newVal === me.end) {
- pixel = me.top;
- } else if (newVal === me.minNotZero) {
- pixel = me.top + innerDimension * 0.02;
- } else {
- pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
- }
- } else if (newVal === 0) {
- pixel = tickOpts.reverse ? me.top : me.bottom;
- } else {
- range = helpers.log10(me.end) - helpers.log10(start);
- innerDimension = me.height;
- pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
+ 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 = helpers.getValueOrDefault(
+ me.options.ticks.fontSize,
+ Chart.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;
},
getValueForPixel: function(pixel) {
var me = this;
- var range = helpers.log10(me.end) - helpers.log10(me.start);
- var value, innerDimension;
+ var reverse = me.options.ticks.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 = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
- } else { // todo: if start === 0
+ value = reverse ? me.right - pixel : pixel - me.left;
+ } else {
innerDimension = me.height;
- value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
+ value = reverse ? pixel - me.top : me.bottom - pixel;
+ }
+ if (value !== start) {
+ if (start === 0) { // include zero tick
+ var offset = helpers.getValueOrDefault(
+ me.options.ticks.fontSize,
+ Chart.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;
}
expect(yScale.getValueForPixel(246)).toBeCloseTo(10, 1e-4);
});
- it('should get the correct pixel value for a point when 0 values are present', function() {
- var chart = window.acquireChart({
- type: 'bar',
- data: {
- datasets: [{
- yAxisID: 'yScale',
- data: [0.063, 4, 0, 63, 10, 0.5]
- }],
- labels: []
+ it('should get the correct pixel value for a point when 0 values are present or min: 0', function() {
+ var config = [
+ {
+ dataset: [{x: 0, y: 0}, {x: 10, y: 10}, {x: 1.2, y: 1.2}, {x: 25, y: 25}, {x: 78, y: 78}],
+ firstTick: 1, // value of the first tick
+ lastTick: 80
},
- options: {
- scales: {
- yAxes: [{
- id: 'yScale',
- type: 'logarithmic',
- ticks: {
- reverse: false
- }
- }]
+ {
+ dataset: [{x: 0, y: 0}, {x: 10, y: 10}, {x: 6.3, y: 6.3}, {x: 25, y: 25}, {x: 78, y: 78}],
+ firstTick: 6,
+ lastTick: 80
+ },
+ {
+ dataset: [{x: 10, y: 10}, {x: 1.2, y: 1.2}, {x: 25, y: 25}, {x: 78, y: 78}],
+ scale: {ticks: {min: 0}},
+ firstTick: 1,
+ lastTick: 80
+ },
+ {
+ dataset: [{x: 10, y: 10}, {x: 6.3, y: 6.3}, {x: 25, y: 25}, {x: 78, y: 78}],
+ scale: {ticks: {min: 0}},
+ firstTick: 6,
+ lastTick: 80
+ },
+ ];
+ Chart.helpers.each(config, function(setup) {
+ var xScaleConfig = {
+ type: 'logarithmic'
+ };
+ var yScaleConfig = {
+ type: 'logarithmic'
+ };
+ Chart.helpers.extend(xScaleConfig, setup.scale);
+ Chart.helpers.extend(yScaleConfig, setup.scale);
+ var chart = window.acquireChart({
+ type: 'line',
+ data: {
+ datasets: [{
+ data: setup.dataset
+ }],
+ },
+ options: {
+ scales: {
+ xAxes: [xScaleConfig],
+ yAxes: [yScaleConfig]
+ }
}
- }
+ });
+
+ var chartArea = chart.chartArea;
+ var expectations = [
+ {
+ id: 'x-axis-0', // horizontal scale
+ axis: 'xAxes',
+ start: chartArea.left,
+ end: chartArea.right
+ },
+ {
+ id: 'y-axis-0', // vertical scale
+ axis: 'yAxes',
+ start: chartArea.bottom,
+ end: chartArea.top
+ }
+ ];
+ Chart.helpers.each(expectations, function(expectation) {
+ var scale = chart.scales[expectation.id];
+ var firstTick = setup.firstTick;
+ var lastTick = setup.lastTick;
+ var fontSize = chart.options.defaultFontSize;
+ var start = expectation.start;
+ var end = expectation.end;
+ var sign = scale.isHorizontal() ? 1 : -1;
+
+ expect(scale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(start);
+ expect(scale.getPixelForValue(lastTick, 0, 0)).toBeCloseToPixel(end);
+ expect(scale.getPixelForValue(firstTick, 0, 0)).toBeCloseToPixel(start + sign * fontSize);
+
+ expect(scale.getValueForPixel(start)).toBeCloseTo(0);
+ expect(scale.getValueForPixel(end)).toBeCloseTo(lastTick);
+ expect(scale.getValueForPixel(start + sign * fontSize)).toBeCloseTo(firstTick);
+
+ chart.options.scales[expectation.axis][0].ticks.reverse = true; // Reverse mode
+ chart.update();
+
+ expect(scale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(end);
+ expect(scale.getPixelForValue(lastTick, 0, 0)).toBeCloseToPixel(start);
+ expect(scale.getPixelForValue(firstTick, 0, 0)).toBeCloseToPixel(end - sign * fontSize);
+
+ expect(scale.getValueForPixel(end)).toBeCloseTo(0);
+ expect(scale.getValueForPixel(start)).toBeCloseTo(lastTick);
+ expect(scale.getValueForPixel(end - sign * fontSize)).toBeCloseTo(firstTick);
+ });
});
-
- var yScale = chart.scales.yScale;
- expect(yScale.getPixelForValue(70, 0, 0)).toBeCloseToPixel(32); // top + paddingTop
- expect(yScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(484); // bottom - paddingBottom
- expect(yScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(475); // minNotZero 2% from range
- expect(yScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(344);
- expect(yScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(213);
- expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(155);
- expect(yScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(38.5);
-
- chart.options.scales.yAxes[0].ticks.reverse = true; // Reverse mode
- chart.update();
-
- expect(yScale.getPixelForValue(70, 0, 0)).toBeCloseToPixel(484); // bottom - paddingBottom
- expect(yScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(32); // top + paddingTop
- expect(yScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(41); // minNotZero 2% from range
- expect(yScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(172);
- expect(yScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(303);
- expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(361);
- expect(yScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(477);
});
+
});