From: Evert Timberg Date: Sun, 27 Sep 2015 15:58:20 +0000 (-0400) Subject: Convert the radialLinear scale to derive from Core.scale. Radial linear scale is... X-Git-Tag: 2.0.0-alpha4~12^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc7d1d39fd5f2fdfbe55764136fd4aa3efbe7269;p=thirdparty%2FChart.js.git Convert the radialLinear scale to derive from Core.scale. Radial linear scale is now fit using the scale service. Added tests for the radialLinear scale. --- diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index af3657ad1..c962b9c38 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -97,13 +97,6 @@ }, update: function update(reset) { - - Chart.scaleService.update(this, this.chart.width, this.chart.height); - //this.chart.scale.setScaleSize(); - this.chart.scale.calculateRange(); - this.chart.scale.generateTicks(); - this.chart.scale.buildYLabels(); - this.chart.outerRadius = Math.max((helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2, 0); this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0); this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length; diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index 85cf5a459..40fc4a7ab 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -120,11 +120,6 @@ var scale = this.chart.scale; var scaleBase; - scale.setScaleSize(); - scale.calculateRange(); - scale.generateTicks(); - scale.buildYLabels(); - if (scale.min < 0 && scale.max < 0) { scaleBase = scale.getPointPositionForValue(0, scale.max); } else if (scale.min > 0 && scale.max > 0) { diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 81818475e..6d8d5e25e 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -157,6 +157,8 @@ }); this.scale = scale; + + this.scales['radialScale'] = scale; } Chart.scaleService.update(this, this.chart.width, this.chart.height); diff --git a/src/core/core.scaleService.js b/src/core/core.scaleService.js index bd100fed2..d60a4b66d 100644 --- a/src/core/core.scaleService.js +++ b/src/core/core.scaleService.js @@ -46,6 +46,11 @@ return scaleInstance.options.position == "bottom"; }); + // Scales that overlay the chartarea such as the radialLinear scale + var chartAreaScales = helpers.where(chartInstance.scales, function(scaleInstance) { + return scaleInstance.options.position == "chartArea"; + }); + // Essentially we now have any number of scales on each of the 4 sides. // Our canvas looks like the following. // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and @@ -71,6 +76,7 @@ // 6. Refit each axis // 7. Position each axis in the final location // 8. Tell the chart the final location of the chart area + // 9. Tell any axes that overlay the chart area the positions of the chart area // Step 1 var chartWidth = width / 2; // min 50% @@ -311,6 +317,16 @@ right: totalLeftWidth + maxChartWidth, bottom: totalTopHeight + maxChartHeight, }; + + // Step 9 + helpers.each(chartAreaScales, function(scaleInstance) { + scaleInstance.left = chartInstance.chartArea.left; + scaleInstance.top = chartInstance.chartArea.top; + scaleInstance.right = chartInstance.chartArea.right; + scaleInstance.bottom = chartInstance.chartArea.bottom; + + scaleInstance.update(maxChartWidth, maxChartHeight); + }); } } }; diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index 5c24bd5c6..b9fae20a7 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -10,35 +10,17 @@ //Boolean - Whether to animate scaling the chart from the centre animate: true, - lineArc: false, - - // grid line settings - gridLines: { - show: true, - color: "rgba(0, 0, 0, 0.1)", - lineWidth: 1, - }, + position: "chartArea", angleLines: { show: true, - color: "rgba(0,0,0, 0.1)", + color: "rgba(0, 0, 0, 0.1)", lineWidth: 1 }, - // scale numbers - reverse: false, - beginAtZero: true, - // label settings - labels: { - show: true, - template: "<%=value.toLocaleString()%>", - fontSize: 12, - fontStyle: "normal", - fontColor: "#666", - fontFamily: "Helvetica Neue", - + ticks: { //Boolean - Show a backdrop to the scale label showLabelBackdrop: true, @@ -67,29 +49,21 @@ }, }; - var LinearRadialScale = Chart.Element.extend({ - initialize: function() { - this.height = this.chart.height; - this.width = this.chart.width; - this.xCenter = this.chart.width / 2; - this.yCenter = this.chart.height / 2; - this.size = helpers.min([this.height, this.width]); - this.labels = this.data.labels; - this.drawingArea = (this.options.display) ? (this.size / 2) - (this.options.labels.fontSize / 2 + this.options.labels.backdropPaddingY) : (this.size / 2); - }, + var LinearRadialScale = Chart.Scale.extend({ getValueCount: function() { return this.data.labels.length; }, - update: function() { - if (!this.options.lineArc) { - this.setScaleSize(); - } else { - this.drawingArea = (this.options.display) ? (this.size / 2) - (this.fontSize / 2 + this.backdropPaddingY) : (this.size / 2); - } - - this.buildYLabels(); + setDimensions: function() { + // Set the unconstrained dimension before label rotation + this.width = this.maxWidth; + this.height = this.maxHeight; + this.xCenter = Math.round(this.width / 2); + this.yCenter = Math.round(this.height / 2); + + var minSize = helpers.min([this.height, this.width]); + this.drawingArea = (this.options.display) ? (minSize / 2) - (this.options.ticks.fontSize / 2 + this.options.ticks.backdropPaddingY) : (minSize / 2); }, - calculateRange: function() { + buildTicks: function() { this.min = null; this.max = null; @@ -110,105 +84,72 @@ } }, this); }, this); - }, - generateTicks: function() { - // We need to decide how many ticks we are going to have. Each tick draws a grid line. - // There are two possibilities. The first is that the user has manually overridden the scale - // calculations in which case the job is easy. The other case is that we have to do it ourselves - // - // We assume at this point that the scale object has been updated with the following values - // by the chart. - // min: this is the minimum value of the scale - // max: this is the maximum value of the scale - // options: contains the options for the scale. This is referenced from the user settings - // rather than being cloned. This ensures that updates always propogate to a redraw - - // Reset the ticks array. Later on, we will draw a grid line at these positions - // The array simply contains the numerical value of the spots where ticks will be - this.ticks = []; - if (this.options.override) { - // The user has specified the manual override. We use <= instead of < so that - // we get the final line - for (var i = 0; i <= this.options.override.steps; ++i) { - var value = this.options.override.start + (i * this.options.override.stepWidth); - this.ticks.push(value); - } - } else { - // Figure out what the max number of ticks we can support it is based on the size of - // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 - // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on - // the graph - - var maxTicks = Math.min(11, Math.ceil(this.drawingArea / (2 * this.options.labels.fontSize))); - - // Make sure we always have at least 2 ticks - maxTicks = Math.max(2, maxTicks); - - // To get a "nice" value for the tick spacing, we will use the appropriately named - // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks - // for details. - - // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, - // do nothing since that would make the chart weird. If the user really wants a weird chart - // axis, they can manually override it - if (this.options.beginAtZero) { - var minSign = helpers.sign(this.min); - var maxSign = helpers.sign(this.max); - - if (minSign < 0 && maxSign < 0) { - // move the top up to 0 - this.max = 0; - } else if (minSign > 0 && maxSign > 0) { - // move the botttom down to 0 - this.min = 0; - } - } + if (this.min === this.max) { + this.min--; + this.max++; + } - var niceRange = helpers.niceNum(this.max - this.min, false); - var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true); - var niceMin = Math.floor(this.min / spacing) * spacing; - var niceMax = Math.ceil(this.max / spacing) * spacing; + this.ticks = []; - // Put the values into the ticks array - for (var j = niceMin; j <= niceMax; j += spacing) { - this.ticks.push(j); + // Figure out what the max number of ticks we can support it is based on the size of + // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 + // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on + // the graph + var maxTicks = Math.min(11, Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize))); + maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks + + // To get a "nice" value for the tick spacing, we will use the appropriately named + // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks + // for details. + + // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, + // do nothing since that would make the chart weird. If the user really wants a weird chart + // axis, they can manually override it + if (this.options.ticks.beginAtZero) { + var minSign = helpers.sign(this.min); + var maxSign = helpers.sign(this.max); + + if (minSign < 0 && maxSign < 0) { + // move the top up to 0 + this.max = 0; + } else if (minSign > 0 && maxSign > 0) { + // move the botttom down to 0 + this.min = 0; } } - if (this.options.position == "left" || this.options.position == "right") { - // We are in a vertical orientation. The top value is the highest. So reverse the array - this.ticks.reverse(); + var niceRange = helpers.niceNum(this.max - this.min, false); + var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true); + var niceMin = Math.floor(this.min / spacing) * spacing; + var niceMax = Math.ceil(this.max / spacing) * spacing; + + // Put the values into the ticks array + for (var j = niceMin; j <= niceMax; j += spacing) { + this.ticks.push(j); } // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale this.max = helpers.max(this.ticks); this.min = helpers.min(this.ticks); - }, - buildYLabels: function() { - this.yLabels = []; - - helpers.each(this.ticks, function(tick, index, ticks) { - var label; - - if (this.options.labels.userCallback) { - // If the user provided a callback for label generation, use that as first priority - label = this.options.labels.userCallback(tick, index, ticks); - } else if (this.options.labels.template) { - // else fall back to the template string - label = helpers.template(this.options.labels.template, { - value: tick - }); - } - this.yLabels.push(label ? label : ""); - }, this); + if (this.options.ticks.reverse) { + this.ticks.reverse(); + + this.start = this.max; + this.end = this.min; + } else { + this.start = this.min; + this.end = this.max; + } + + this.zeroLineIndex = this.ticks.indexOf(0); }, getCircumference: function() { return ((Math.PI * 2) / this.getValueCount()); }, - setScaleSize: function() { + fit: function() { /* * Right, this is really confusing and there is a lot of maths going on here * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 @@ -260,8 +201,8 @@ for (i = 0; i < this.getValueCount(); i++) { // 5px to space the text slightly out - similar to what we do in the draw function. pointPosition = this.getPointPosition(i, largestPossibleRadius); - textWidth = this.ctx.measureText(helpers.template(this.options.labels.template, { - value: this.labels[i] + textWidth = this.ctx.measureText(helpers.template(this.options.ticks.template, { + value: this.data.labels[i] })).width + 5; if (i === 0 || i === this.getValueCount() / 2) { // If we're at index zero, or exactly the middle, we're at exactly the top/bottom @@ -292,35 +233,29 @@ } xProtrusionLeft = furthestLeft; - xProtrusionRight = Math.ceil(furthestRight - this.width); furthestRightAngle = this.getIndexAngle(furthestRightIndex); - furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2); - radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2); // Ensure we actually need to reduce the size of the chart radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0; radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; - this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2; - - //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2]) + this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2); this.setCenterPoint(radiusReductionLeft, radiusReductionRight); - }, setCenterPoint: function(leftMovement, rightMovement) { var maxRight = this.width - rightMovement - this.drawingArea, maxLeft = leftMovement + this.drawingArea; - this.xCenter = (maxLeft + maxRight) / 2; + this.xCenter = Math.round(((maxLeft + maxRight) / 2) + this.left); // Always vertically in the centre as the text height doesn't change - this.yCenter = (this.height / 2); + this.yCenter = Math.round((this.height / 2) + this.top); }, getIndexAngle: function(index) { @@ -352,7 +287,7 @@ draw: function() { if (this.options.display) { var ctx = this.ctx; - helpers.each(this.yLabels, function(label, index) { + helpers.each(this.ticks, function(label, index) { // Don't draw a centre value (if it is minimum) if (index > 0 || this.options.reverse) { var yCenterOffset = this.getDistanceFromCenterForValue(this.ticks[index]); @@ -385,23 +320,23 @@ } } - if (this.options.labels.show) { - ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily); + if (this.options.ticks.show) { + ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily); - if (this.options.labels.showLabelBackdrop) { + if (this.options.ticks.showLabelBackdrop) { var labelWidth = ctx.measureText(label).width; - ctx.fillStyle = this.options.labels.backdropColor; + ctx.fillStyle = this.options.ticks.backdropColor; ctx.fillRect( - this.xCenter - labelWidth / 2 - this.options.labels.backdropPaddingX, - yHeight - this.fontSize / 2 - this.options.labels.backdropPaddingY, - labelWidth + this.options.labels.backdropPaddingX * 2, - this.options.labels.fontSize + this.options.labels.backdropPaddingY * 2 + this.xCenter - labelWidth / 2 - this.options.ticks.backdropPaddingX, + yHeight - this.options.ticks.fontSize / 2 - this.options.ticks.backdropPaddingY, + labelWidth + this.options.ticks.backdropPaddingX * 2, + this.options.ticks.fontSize + this.options.ticks.backdropPaddingY * 2 ); } ctx.textAlign = 'center'; ctx.textBaseline = "middle"; - ctx.fillStyle = this.options.labels.fontColor; + ctx.fillStyle = this.options.ticks.fontColor; ctx.fillText(label, this.xCenter, yHeight); } } @@ -425,8 +360,8 @@ ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily); ctx.fillStyle = this.options.pointLabels.fontColor; - var labelsCount = this.labels.length, - halfLabelsCount = this.labels.length / 2, + var labelsCount = this.data.labels.length, + halfLabelsCount = this.data.labels.length / 2, quarterLabelsCount = halfLabelsCount / 2, upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); @@ -449,7 +384,7 @@ ctx.textBaseline = 'top'; } - ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y); + ctx.fillText(this.data.labels[i], pointLabelPosition.x, pointLabelPosition.y); } } } diff --git a/test/mockContext.js b/test/mockContext.js index 56b28ffe3..6559bc7b4 100644 --- a/test/mockContext.js +++ b/test/mockContext.js @@ -68,6 +68,7 @@ clearRect: function() {}, closePath: function() {}, fill: function() {}, + fillRect: function() {}, fillText: function() {}, lineTo: function(x, y) {}, measureText: function(text) { diff --git a/test/scale.radialLinear.tests.js b/test/scale.radialLinear.tests.js new file mode 100644 index 000000000..cfbf9d491 --- /dev/null +++ b/test/scale.radialLinear.tests.js @@ -0,0 +1,773 @@ +// Tests for the radial linear scale used by the polar area and radar charts +describe('Test the radial linear scale', function() { + it('Should register the constructor with the scale service', function() { + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + expect(Constructor).not.toBe(undefined); + expect(typeof Constructor).toBe('function'); + }); + + it('Should have the correct default config', function() { + var defaultConfig = Chart.scaleService.getScaleDefaults('radialLinear'); + expect(defaultConfig).toEqual({ + angleLines: { + show: true, + color: "rgba(0, 0, 0, 0.1)", + lineWidth: 1 + }, + animate: true, + display: true, + gridLines: { + color: "rgba(0, 0, 0, 0.1)", + drawOnChartArea: true, + drawTicks: true, + lineWidth: 1, + offsetGridLines: false, + show: true, + zeroLineColor: "rgba(0,0,0,0.25)", + zeroLineWidth: 1, + }, + lineArc: false, + pointLabels: { + fontColor: "#666", + fontFamily: "'Arial'", + fontSize: 10, + fontStyle: "normal", + }, + position: "chartArea", + scaleLabel: { + fontColor: '#666', + fontFamily: 'Helvetica Neue', + fontSize: 12, + fontStyle: 'normal', + labelString: '', + show: false, + }, + ticks: { + backdropColor: "rgba(255,255,255,0.75)", + backdropPaddingY: 2, + backdropPaddingX: 2, + beginAtZero: false, + fontColor: "#666", + fontFamily: "Helvetica Neue", + fontSize: 12, + fontStyle: "normal", + maxRotation: 90, + minRotation: 20, + mirror: false, + padding: 10, + reverse: false, + showLabelBackdrop: true, + show: true, + template: "<%=value%>", + + }, + }); + }); + + it('Should correctly determine the max & min data values', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [10, 5, 0, -5, 78, -100] + }, { + yAxisID: scaleID, + data: [150] + }], + labels: ['lablel1', 'label2', 'label3', 'label4', 'label5', 'label6'] + }; + + var mockContext = window.createMockContext(); + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: Chart.scaleService.getScaleDefaults('radialLinear'), // use default config for scale + data: mockData, + id: scaleID, + }); + + scale.update(200, 300); + expect(scale.min).toBe(-100); + expect(scale.max).toBe(200); + }); + + it('Should ensure that the scale has a max and min that are not equal', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [], + labels: [] + }; + + var mockContext = window.createMockContext(); + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: Chart.scaleService.getScaleDefaults('radialLinear'), // use default config for scale + data: mockData, + id: scaleID, + }); + + scale.update(200, 300); + expect(scale.min).toBe(-1); + 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] + }], + labels: [], + }; + + var mockContext = window.createMockContext(); + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear')); + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: config, + data: mockData, + id: scaleID, + }); + + config.ticks.beginAtZero = false; + scale.update(400, 400); + expect(scale.ticks).toEqual(['20', '25', '30', '35', '40', '45', '50']); + + config.ticks.beginAtZero = true; + scale.update(400, 400); + expect(scale.ticks).toEqual(['0', '5', '10', '15', '20', '25', '30', '35', '40', '45', '50']); + + mockData.datasets[0].data = [-20, -30, -40, -50]; + scale.update(400, 400); + expect(scale.ticks).toEqual(['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']); + + config.ticks.beginAtZero = false; + scale.update(400, 400); + expect(scale.ticks).toEqual(['-50', '-45', '-40', '-35', '-30', '-25', '-20']); + }); + + 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] + }], + labels: [] + }; + + var mockContext = window.createMockContext(); + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear')); + config.ticks.reverse = true; + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: config, + data: mockData, + id: scaleID, + }); + + scale.update(200, 300); + + // Reverse mode makes this count up + expect(scale.ticks).toEqual(['80', '60', '40', '20', '0']); + expect(scale.start).toBe(80); + expect(scale.end).toBe(0); + }); + + it('Should build labels using the user supplied callback', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [10, 5, 0, 25, 78] + }], + labels: [] + }; + + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear')); + config.ticks.userCallback = function(value, index) { + return index.toString(); + }; + + var mockContext = window.createMockContext(); + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: config, + data: mockData, + id: scaleID, + }); + + scale.update(200, 300); + + // Just the index + expect(scale.ticks).toEqual(['0', '1', '2', '3', '4']); + }); + + it('should correctly set the center point', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [10, 5, 0, 25, 78] + }], + labels: ['point1', 'point2', 'point3', 'point4', 'point5'] // used in radar charts which use the same scales + }; + + var mockContext = window.createMockContext(); + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear')); + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: config, + data: mockData, + id: scaleID, + }); + + scale.left = 10; + scale.right = 210; + scale.top = 5; + scale.bottom = 305; + scale.update(200, 300); + + expect(scale.drawingArea).toBe(36); + expect(scale.xCenter).toBe(110); + expect(scale.yCenter).toBe(155); + }); + + it('should get the correct distance from the center point', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [10, 5, 0, 25, 78] + }], + labels: ['point1', 'point2', 'point3', 'point4', 'point5'] // used in radar charts which use the same scales + }; + + var mockContext = window.createMockContext(); + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear')); + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: config, + data: mockData, + id: scaleID, + }); + + scale.left = 0; + scale.right = 200; + scale.top = 0; + scale.bottom = 300; + scale.update(200, 300); + + expect(scale.getDistanceFromCenterForValue(scale.min)).toBe(0); + expect(scale.getDistanceFromCenterForValue(scale.max)).toBe(36); + expect(scale.getPointPositionForValue(1, 5)).toEqual({ + x: 102.13987716166409, + y: 149.30471176265638, + }); + + config.reverse = true; + + scale.update(200, 300); + + expect(scale.getDistanceFromCenterForValue(scale.min)).toBe(36); + expect(scale.getDistanceFromCenterForValue(scale.max)).toBe(0); + }); + + it('should draw correctly when there are no point labels', function() { + var scaleID = 'myScale'; + + var mockData = { + datasets: [{ + yAxisID: scaleID, + data: [10, 5, 0, 25, 78] + }, ], + labels: ['point1', 'point2', 'point3', 'point4', 'point5'] // used in radar charts which use the same scales + }; + + var mockContext = window.createMockContext(); + var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear')); + config.lineArc = true; + var Constructor = Chart.scaleService.getScaleConstructor('radialLinear'); + var scale = new Constructor({ + ctx: mockContext, + options: config, + data: mockData, + id: scaleID, + }); + + scale.left = 0; + scale.right = 200; + scale.top = 0; + scale.bottom = 300; + scale.update(200, 300); + + scale.draw(); + + var expected = [{ + "name": "measureText", + "args": ["0"] + }, { + "name": "measureText", + "args": ["80"] + }, { + "name": "measureText", + "args": ["point1"] + }, { + "name": "measureText", + "args": ["point2"] + }, { + "name": "measureText", + "args": ["point3"] + }, { + "name": "measureText", + "args": ["point4"] + }, { + "name": "measureText", + "args": ["point5"] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "arc", + "args": [100, 150, 9, 0, 6.283185307179586] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["20"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 133, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["20", 100, 141] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "arc", + "args": [100, 150, 18, 0, 6.283185307179586] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["40"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 124, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["40", 100, 132] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "arc", + "args": [100, 150, 27, 0, 6.283185307179586] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["60"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 115, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["60", 100, 123] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "arc", + "args": [100, 150, 36, 0, 6.283185307179586] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["80"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 106, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["80", 100, 114] + }]; + expect(mockContext.getCalls()).toEqual(expected); + + mockContext.resetCalls(); + config.lineArc = false; + scale.draw(); + + expect(mockContext.getCalls()).toEqual([{ + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 141] + }, { + "name": "lineTo", + "args": [108.55950864665638, 147.21884705062547] + }, { + "name": "lineTo", + "args": [105.29006727063226, 157.28115294937453] + }, { + "name": "lineTo", + "args": [94.70993272936774, 157.28115294937453] + }, { + "name": "lineTo", + "args": [91.44049135334362, 147.21884705062547] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["20"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 133, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["20", 100, 141] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 132] + }, { + "name": "lineTo", + "args": [117.11901729331277, 144.43769410125094] + }, { + "name": "lineTo", + "args": [110.58013454126451, 164.56230589874906] + }, { + "name": "lineTo", + "args": [89.41986545873549, 164.56230589874906] + }, { + "name": "lineTo", + "args": [82.88098270668723, 144.43769410125094] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["40"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 124, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["40", 100, 132] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 123] + }, { + "name": "lineTo", + "args": [125.67852593996915, 141.6565411518764] + }, { + "name": "lineTo", + "args": [115.87020181189678, 171.8434588481236] + }, { + "name": "lineTo", + "args": [84.12979818810322, 171.8434588481236] + }, { + "name": "lineTo", + "args": [74.32147406003085, 141.6565411518764] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["60"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 115, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["60", 100, 123] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 114] + }, { + "name": "lineTo", + "args": [134.23803458662553, 138.87538820250188] + }, { + "name": "lineTo", + "args": [121.16026908252903, 179.12461179749812] + }, { + "name": "lineTo", + "args": [78.83973091747097, 179.12461179749812] + }, { + "name": "lineTo", + "args": [65.76196541337447, 138.8753882025019] + }, { + "name": "closePath", + "args": [] + }, { + "name": "stroke", + "args": [] + }, { + "name": "measureText", + "args": ["80"] + }, { + "name": "setFillStyle", + "args": ["rgba(255,255,255,0.75)"] + }, { + "name": "fillRect", + "args": [88, 106, 24, 16] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["80", 100, 114] + }, { + "name": "setLineWidth", + "args": [1] + }, { + "name": "setStrokeStyle", + "args": ["rgba(0, 0, 0, 0.1)"] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 150] + }, { + "name": "lineTo", + "args": [65.76196541337447, 138.8753882025019] + }, { + "name": "stroke", + "args": [] + }, { + "name": "closePath", + "args": [] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["point5", 61.0066828318987, 137.33030323062715] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 150] + }, { + "name": "lineTo", + "args": [78.83973091747097, 179.12461179749812] + }, { + "name": "stroke", + "args": [] + }, { + "name": "closePath", + "args": [] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["point4", 75.9008046560086, 183.16969676937285] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 150] + }, { + "name": "lineTo", + "args": [121.16026908252903, 179.12461179749812] + }, { + "name": "stroke", + "args": [] + }, { + "name": "closePath", + "args": [] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["point3", 124.0991953439914, 183.16969676937285] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 150] + }, { + "name": "lineTo", + "args": [134.23803458662553, 138.87538820250188] + }, { + "name": "stroke", + "args": [] + }, { + "name": "closePath", + "args": [] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["point2", 138.9933171681013, 137.33030323062715] + }, { + "name": "beginPath", + "args": [] + }, { + "name": "moveTo", + "args": [100, 150] + }, { + "name": "lineTo", + "args": [100, 114] + }, { + "name": "stroke", + "args": [] + }, { + "name": "closePath", + "args": [] + }, { + "name": "setFillStyle", + "args": ["#666"] + }, { + "name": "fillText", + "args": ["point1", 100, 109] + }]); + }); +}); \ No newline at end of file