diff --git a/src/core/core.ticks.js b/src/core/core.ticks.js index 8d84f1f236a..dadaabab377 100644 --- a/src/core/core.ticks.js +++ b/src/core/core.ticks.js @@ -116,7 +116,7 @@ module.exports = function(Chart) { if (tickVal === 0) { exp = Math.floor(helpers.log10(dataRange.minNotZero)); - significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); + significand = 1; ticks.push(tickVal); tickVal = significand * Math.pow(10, exp); diff --git a/src/scales/scale.logarithmic.js b/src/scales/scale.logarithmic.js index 13d1d5a5312..e5212b312bd 100644 --- a/src/scales/scale.logarithmic.js +++ b/src/scales/scale.logarithmic.js @@ -178,51 +178,38 @@ module.exports = function(Chart) { }, getPixelForValue: function(value) { var me = this; - var innerDimension; - var pixel; - + var innerDimension = me.isHorizontal() ? me.width : me.height; var start = me.start; + var end = me.end; var newVal = +me.getRightValue(value); - var range; var opts = me.options; var tickOpts = opts.ticks; + var zero = me.isHorizontal() ? (tickOpts.reverse ? me.right : me.left) : (tickOpts.reverse ? me.top : me.bottom); - 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))); - } - } 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))); + var pixel, range, valueOffset; + + if (start === 0 || end === 0) { // 0 is in range + var min = Math.pow(10, Math.floor(helpers.log10(me.minNotZero))); + var max = tickOpts.reverse ? start : end; + range = helpers.log10(max) - Math.floor(helpers.log10(me.minNotZero)); + var fontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize); + var diff = fontSize; + innerDimension -= diff; + + if (newVal === 0) { // 0 + pixel = zero; + } else if (newVal === min) { // 0 in range, minNotZero + pixel = me.isHorizontal() ? (tickOpts.reverse ? zero - diff : zero + diff) : (tickOpts.reverse ? zero + diff : zero - diff); + } else { // 0 in range, common case + valueOffset = innerDimension / range * (helpers.log10(newVal) - Math.floor(helpers.log10(me.minNotZero))); + pixel = me.isHorizontal() + ? (tickOpts.reverse ? zero - diff - valueOffset : zero + diff + valueOffset) + : (tickOpts.reverse ? zero + diff + valueOffset : zero - diff - valueOffset); } + } else { // 0 is not in range + range = helpers.log10(end) - helpers.log10(start); + valueOffset = innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)); + pixel = me.isHorizontal() ? me.left + valueOffset : me.bottom - valueOffset; } return pixel; }, @@ -230,11 +217,38 @@ module.exports = function(Chart) { var me = this; var range = helpers.log10(me.end) - helpers.log10(me.start); var value, innerDimension; + var tickOpts = me.options.ticks; + var reverse = tickOpts.reverse; + + if (me.start === 0 || me.end === 0) { + var zeroPixel = me.isHorizontal() ? (reverse ? me.right : me.left) : (reverse ? me.top : me.bottom); + range = helpers.log10(reverse ? me.start : me.end) - Math.floor(helpers.log10(me.minNotZero)); + var fontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize); + var diff = fontSize; + innerDimension -= diff; + + // + var pixelMinNotZero; + if (me.isHorizontal()) { + pixelMinNotZero = reverse ? zeroPixel - diff : zeroPixel + diff; + } else { + pixelMinNotZero = reverse ? zeroPixel + diff : zeroPixel - diff; + } - if (me.isHorizontal()) { + if (pixel === zeroPixel) { + value = 0; + } else if (pixel === pixelMinNotZero) { + value = me.minNotZero; + } else { + var p = me.isHorizontal() + ? (reverse ? zeroPixel - diff - pixel : zeroPixel + diff + pixel) + : (reverse ? zeroPixel + diff + pixel : zeroPixel - diff - pixel); + value = me.minNotZero * Math.pow(10, p * range / innerDimension); + } + } else if (me.isHorizontal()) { innerDimension = me.width; value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension); - } else { // todo: if start === 0 + } else { innerDimension = me.height; value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start; } diff --git a/test/specs/scale.logarithmic.tests.js b/test/specs/scale.logarithmic.tests.js index b1053eefce2..2a71965d895 100644 --- a/test/specs/scale.logarithmic.tests.js +++ b/test/specs/scale.logarithmic.tests.js @@ -514,7 +514,7 @@ describe('Logarithmic Scale tests', function() { type: 'bar', data: { datasets: [{ - data: [11, 0.8, 0, 28, 7] + data: [11, 76, 0, 28, 7] }], labels: [] }, @@ -535,9 +535,9 @@ describe('Logarithmic Scale tests', function() { // Counts down because the lines are drawn top to bottom expect(chart.scales.yScale).toEqual(jasmine.objectContaining({ - ticks: [30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0.9, 0.8, 0], + ticks: [80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], start: 0, - end: 30 + end: 80 })); }); @@ -602,7 +602,7 @@ describe('Logarithmic Scale tests', function() { // Counts down because the lines are drawn top to bottom expect(chart.scales.yScale).toEqual(jasmine.objectContaining({ - ticks: [0, 9, 10, 20, 30], + ticks: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30], start: 30, end: 0 })); @@ -735,7 +735,7 @@ describe('Logarithmic Scale tests', function() { expect(yScale.getValueForPixel(246)).toBeCloseTo(10, 1e-4); }); - it('should get the correct pixel value for a point when 0 values are present', function() { + it('should get the correct pixel value for a point when 0 values are present (vertical)', function() { var chart = window.acquireChart({ type: 'bar', data: { @@ -761,10 +761,11 @@ describe('Logarithmic Scale tests', function() { 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(0.01, 0, 0)).toBeCloseToPixel(472); // first possible tick + expect(yScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(381); // minNotZero + expect(yScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(278); + expect(yScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(174); + expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(127); expect(yScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(38.5); chart.options.scales.yAxes[0].ticks.reverse = true; // Reverse mode @@ -772,10 +773,64 @@ describe('Logarithmic Scale tests', function() { 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(0.01, 0, 0)).toBeCloseToPixel(44); // first possible tick + expect(yScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(135); // minNotZero + expect(yScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(238); + expect(yScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(342); + expect(yScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(389); expect(yScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(477); }); + + it('should get the correct pixel value for a point when 0 values are present (horizontal)', function() { + var chart = window.acquireChart({ + type: 'horizontalBar', + data: { + labels: ['data 1', 'data 2', 'data 3', 'data 4', 'data 5', 'data 6'], + datasets: [{ + xAxisID: 'xScale', + yAxisID: 'yScale', + data: [0.063, 4, 0, 63, 10, 0.5], + }], + }, + options: { + scales: { + xAxes: [{ + id: 'xScale', + type: 'logarithmic', + display: true, + ticks: { + reverse: false, + }, + }], + yAxes: [{ + id: 'yScale', + type: 'category', + display: true, + }] + } + } + }); + + var xScale = chart.scales.xScale; + expect(xScale.getPixelForValue(70, 0, 0)).toBeCloseToPixel(505); + expect(xScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(44); + expect(xScale.getPixelForValue(0.01, 0, 0)).toBeCloseToPixel(56); + expect(xScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(149); + expect(xScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(254); + expect(xScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(360); + expect(xScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(406); + expect(xScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(500); + + chart.options.scales.xAxes[0].ticks.reverse = true; // Reverse mode + chart.update(); + + expect(xScale.getPixelForValue(70, 0, 0)).toBeCloseToPixel(44); + expect(xScale.getPixelForValue(0, 0, 0)).toBeCloseToPixel(505); + expect(xScale.getPixelForValue(0.01, 0, 0)).toBeCloseToPixel(493); + expect(xScale.getPixelForValue(0.063, 0, 0)).toBeCloseToPixel(400); + expect(xScale.getPixelForValue(0.5, 0, 0)).toBeCloseToPixel(295); + expect(xScale.getPixelForValue(4, 0, 0)).toBeCloseToPixel(189); + expect(xScale.getPixelForValue(10, 0, 0)).toBeCloseToPixel(143); + expect(xScale.getPixelForValue(63, 0, 0)).toBeCloseToPixel(49); + }); });