diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 4a7347a04..b2d5823b2 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -72,6 +72,11 @@ this.beforeBuildTicks(); this.buildTicks(); this.afterBuildTicks(); + + this.beforeTickToLabelConversion(); + this.convertTicksToLabels(); + this.afterTickToLabelConversion(); + // Tick Rotation this.beforeCalculateTickRotation(); this.calculateTickRotation(); @@ -107,6 +112,21 @@ buildTicks: helpers.noop, afterBuildTicks: helpers.noop, + beforeTickToLabelConversion: helpers.noop, + convertTicksToLabels: function() { + // Convert ticks to strings + this.ticks = this.ticks.map(function(numericalTick, index, ticks) { + if (this.options.ticks.userCallback) { + return this.options.ticks.userCallback(numericalTick, index, ticks); + } else { + return helpers.template(this.options.ticks.template, { + value: numericalTick + }); + } + }, this); + }, + afterTickToLabelConversion: helpers.noop, + // beforeCalculateTickRotation: helpers.noop, diff --git a/src/scales/scale.linear.js b/src/scales/scale.linear.js index 27cdff3dd..2c9ecd4f0 100644 --- a/src/scales/scale.linear.js +++ b/src/scales/scale.linear.js @@ -140,17 +140,14 @@ this.start = this.max; this.end = this.min; } else { - this.start = this.max; - this.end = this.min; + this.start = this.min; + this.end = this.max; } this.zeroLineIndex = this.ticks.indexOf(0); }, - - // Utils - getPixelForValue: function(value, index, datasetIndex, includeOffset) { // This must be called after fit has been run so that // this.left, this.top, this.right, and this.bottom have been defined @@ -164,8 +161,8 @@ return Math.round(pixel + this.paddingLeft); } else { var innerHeight = this.height - (this.paddingTop + this.paddingBottom); - pixel = this.top + (innerHeight / range * (this.getRightValue(value) - this.start)); - return Math.round(pixel + this.paddingTop); + pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (this.getRightValue(value) - this.start)); + return Math.round(pixel); } }, diff --git a/test/scale.linear.tests.js b/test/scale.linear.tests.js index 1a8a0205f..15c78e71f 100644 --- a/test/scale.linear.tests.js +++ b/test/scale.linear.tests.js @@ -199,6 +199,47 @@ describe('Linear Scale', function() { expect(scale.max).toBe(1); }); + it('should forcibly include 0 in the range if the beginAtZero option is used', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [20, 30, 40, 50] + }] + }; + + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear')); + + + var Constructor = Chart.scaleService.getScaleConstructor('linear'); + var scale = new Constructor({ + ctx: {}, + options: config, + data: mockData, + id: scaleID + }); + + // Set arbitrary width and height for now + scale.width = 50; + scale.height = 400; + + scale.buildTicks(); + expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20]); + + config.beginAtZero = true; + scale.buildTicks(); + expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0]); + + mockData.datasets[0].data = [-20, -30, -40, -50]; + scale.buildTicks(); + expect(scale.ticks).toEqual([0, -5, -10, -15, -20, -25, -30, -35, -40, -45, -50]); + + config.beginAtZero = false; + scale.buildTicks(); + expect(scale.ticks).toEqual([-20, -25, -30, -35, -40, -45, -50]); + }); + it('Should generate tick marks', function() { var scaleID = 'myScale'; @@ -218,14 +259,11 @@ describe('Linear Scale', function() { id: scaleID }); - scale.calculateRange(); + // Set arbitrary width and height for now + scale.width = 50; + scale.height = 400; - expect(scale.ticks).toBe(undefined); // not set - - // Large enough to be unimportant - var maxWidth = 400; - var maxHeight = 400; - scale.generateTicks(maxWidth, maxHeight); + scale.buildTicks(); // Counts down because the lines are drawn top to bottom expect(scale.ticks).toEqual([80, 70, 60, 50, 40, 30, 20, 10, 0]); @@ -233,6 +271,38 @@ describe('Linear Scale', function() { expect(scale.end).toBe(80); }); + it('Should generate tick marks in the correct order in reversed mode', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [10, 5, 0, 25, 78] + }, ] + }; + + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear')); + config.reverse = true; + var Constructor = Chart.scaleService.getScaleConstructor('linear'); + var scale = new Constructor({ + ctx: {}, + options: config, + data: mockData, + id: scaleID + }); + + // Set arbitrary width and height for now + scale.width = 50; + scale.height = 400; + + scale.buildTicks(); + + // Reverse mode makes this count up + expect(scale.ticks).toEqual([0, 10, 20, 30, 40, 50, 60, 70, 80]); + expect(scale.start).toBe(80); + expect(scale.end).toBe(0); + }); + it('Should build labels using the default template', function() { var scaleID = 'myScale'; @@ -243,26 +313,19 @@ describe('Linear Scale', function() { }, ] }; + var mockContext = window.createMockContext(); var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear')); var Constructor = Chart.scaleService.getScaleConstructor('linear'); var scale = new Constructor({ - ctx: {}, + ctx: mockContext, options: config, data: mockData, id: scaleID }); - scale.calculateRange(); - - // Large enough to be unimportant - var maxWidth = 400; - var maxHeight = 400; - scale.generateTicks(maxWidth, maxHeight); - - // Generate labels - scale.buildLabels(); - - expect(scale.labels).toEqual(['80', '70', '60', '50', '40', '30', '20', '10', '0']); + // Set arbitrary width and height for now + scale.update(50, 400); + expect(scale.ticks).toEqual(['80', '70', '60', '50', '40', '30', '20', '10', '0']); }); it('Should build labels using the user supplied callback', function() { @@ -276,30 +339,23 @@ describe('Linear Scale', function() { }; var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear')); - config.labels.userCallback = function(value, index) { + config.ticks.userCallback = function(value, index) { return index.toString(); }; + var mockContext = window.createMockContext(); var Constructor = Chart.scaleService.getScaleConstructor('linear'); var scale = new Constructor({ - ctx: {}, + ctx: mockContext, options: config, data: mockData, id: scaleID }); - scale.calculateRange(); - - // Large enough to be unimportant - var maxWidth = 400; - var maxHeight = 400; - scale.generateTicks(maxWidth, maxHeight); - - // Generate labels - scale.buildLabels(); + scale.update(400, 400); // Just the index - expect(scale.labels).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']); + expect(scale.ticks).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']); }); it('Should get the correct pixel value for a point', function() { @@ -313,17 +369,18 @@ describe('Linear Scale', function() { }] }; + var mockContext = window.createMockContext(); var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear')); var Constructor = Chart.scaleService.getScaleConstructor('linear'); var verticalScale = new Constructor({ - ctx: {}, + ctx: mockContext, options: config, data: mockData, id: scaleID }); - verticalScale.calculateRange(); - verticalScale.generateTicks(50, 100); + // Update + verticalScale.update(50, 100); // Fake out positioning of the scale service verticalScale.left = 0; @@ -335,21 +392,20 @@ describe('Linear Scale', function() { verticalScale.width = 50; verticalScale.height = 110; - expect(verticalScale.getPointPixelForValue(1, 0, 0)).toBe(5); // top + paddingTop - expect(verticalScale.getPointPixelForValue(-1, 0, 0)).toBe(105); // bottom - paddingBottom - expect(verticalScale.getPointPixelForValue(0, 0, 0)).toBe(55); // halfway + expect(verticalScale.getPixelForValue(1, 0, 0)).toBe(5); // top + paddingTop + expect(verticalScale.getPixelForValue(-1, 0, 0)).toBe(105); // bottom - paddingBottom + expect(verticalScale.getPixelForValue(0, 0, 0)).toBe(55); // halfway var horizontalConfig = Chart.helpers.clone(config); horizontalConfig.position = 'bottom'; var horizontalScale = new Constructor({ - ctx: {}, + ctx: mockContext, options: horizontalConfig, data: mockData, id: scaleID, }); - horizontalScale.calculateRange(); - horizontalScale.generateTicks(100, 50); + horizontalScale.update(100, 50); // Fake out positioning of the scale service horizontalScale.left = 0; @@ -362,46 +418,9 @@ describe('Linear Scale', function() { horizontalScale.height = 50; // Range expands to [-2, 2] due to nicenum algorithm - expect(horizontalScale.getPointPixelForValue(2, 0, 0)).toBe(105); // right - paddingRight - expect(horizontalScale.getPointPixelForValue(-2, 0, 0)).toBe(5); // left + paddingLeft - expect(horizontalScale.getPointPixelForValue(0, 0, 0)).toBe(55); // halfway - }); - - it('should get the correct pixel value for a bar', function() { - var scaleID = 'myScale'; - - var mockData = { - datasets: [{ - xAxisID: scaleID, // for the horizontal scale - yAxisID: scaleID, - data: [-5, 0, 2, -3, 5] - }] - }; - - var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear')); - var Constructor = Chart.scaleService.getScaleConstructor('linear'); - var verticalScale = new Constructor({ - ctx: {}, - options: config, - data: mockData, - id: scaleID - }); - - verticalScale.calculateRange(); - verticalScale.generateTicks(50, 100); - - // Fake out positioning of the scale service - verticalScale.left = 0; - verticalScale.top = 0; - verticalScale.right = 50; - verticalScale.bottom = 110; - verticalScale.paddingTop = 5; - verticalScale.paddingBottom = 5; - verticalScale.width = 50; - verticalScale.height = 110; - - expect(verticalScale.calculateBarBase()).toBe(56); // 0 point - expect(verticalScale.calculateBarY(0, 0)).toBe(96.66666666666667); // bottom + expect(horizontalScale.getPixelForValue(2, 0, 0)).toBe(105); // right - paddingRight + expect(horizontalScale.getPixelForValue(-2, 0, 0)).toBe(5); // left + paddingLeft + expect(horizontalScale.getPixelForValue(0, 0, 0)).toBe(55); // halfway }); it('should fit correctly', function() { @@ -425,7 +444,7 @@ describe('Linear Scale', function() { id: scaleID }); - var minSize = verticalScale.fit(100, 300); + var minSize = verticalScale.update(100, 300); expect(minSize).toEqual({ width: 33, height: 300, @@ -438,7 +457,7 @@ describe('Linear Scale', function() { expect(verticalScale.paddingRight).toBe(0); // Refit with margins to see the padding go away - minSize = verticalScale.fit(33, 300, { + minSize = verticalScale.update(33, 300, { left: 0, right: 0, top: 15, @@ -455,7 +474,7 @@ describe('Linear Scale', function() { // Extra size when scale label showing config.scaleLabel.show = true; - minSize = verticalScale.fit(100, 300); + minSize = verticalScale.update(100, 300); expect(minSize).toEqual({ width: 51, height: 300, @@ -484,7 +503,7 @@ describe('Linear Scale', function() { id: scaleID }); - var minSize = verticalScale.fit(100, 300); + var minSize = verticalScale.update(100, 300); expect(minSize).toEqual({ width: 100, height: 28, @@ -497,7 +516,7 @@ describe('Linear Scale', function() { expect(verticalScale.paddingRight).toBe(10); // Refit with margins to see the padding go away - minSize = verticalScale.fit(100, 28, { + minSize = verticalScale.update(100, 28, { left: 10, right: 6, top: 15, @@ -514,7 +533,7 @@ describe('Linear Scale', function() { // Extra size when scale label showing config.scaleLabel.show = true; - minSize = verticalScale.fit(100, 300); + minSize = verticalScale.update(100, 300); expect(minSize).toEqual({ width: 100, height: 46,