diff --git a/docs/charts/doughnut.md b/docs/charts/doughnut.md index 4f1656f3063..9fda1bbf719 100644 --- a/docs/charts/doughnut.md +++ b/docs/charts/doughnut.md @@ -55,12 +55,21 @@ The doughnut/pie chart allows a number of properties to be specified for each da | Name | Type | Description | ---- | ---- | ----------- -| `backgroundColor` | `Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors). -| `borderColor` | `Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors). -| `borderWidth` | `Number[]` | The border width of the arcs in the dataset. -| `hoverBackgroundColor` | `Color[]` | The fill colour of the arcs when hovered. -| `hoverBorderColor` | `Color[]` | The stroke colour of the arcs when hovered. -| `hoverBorderWidth` | `Number[]` | The stroke width of the arcs when hovered. +| `backgroundColor` | `Color/Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors). +| `borderColor` | `Color/Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors). +| `borderWidth` | `Number/Number[]` | The border width of the arcs in the dataset. +| `borderAlign` | `String/String[]` | The border alignment of the arcs in the dataset. [more...](#border-alignment) +| `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the arcs when hovered. +| `hoverBorderColor` | `Color/Color[]` | The stroke colour of the arcs when hovered. +| `hoverBorderWidth` | `Number/Number[]` | The stroke width of the arcs when hovered. + +### Border Alignment + +The following values are supported for `borderAlign`. +* `'center'` (default) +* `'inner'` + +When `'center'` is set, the borders of arcs next to each other will overlap. When `'inner'` is set, it is guaranteed that all the borders are not overlap. ## Config Options diff --git a/docs/charts/polar.md b/docs/charts/polar.md index 2ae8b5ec3dd..20dc93c283b 100644 --- a/docs/charts/polar.md +++ b/docs/charts/polar.md @@ -46,12 +46,21 @@ The following options can be included in a polar area chart dataset to configure | Name | Type | Description | ---- | ---- | ----------- -| `backgroundColor` | `Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors). -| `borderColor` | `Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors). -| `borderWidth` | `Number[]` | The border width of the arcs in the dataset. -| `hoverBackgroundColor` | `Color[]` | The fill colour of the arcs when hovered. -| `hoverBorderColor` | `Color[]` | The stroke colour of the arcs when hovered. -| `hoverBorderWidth` | `Number[]` | The stroke width of the arcs when hovered. +| `backgroundColor` | `Color/Color[]` | The fill color of the arcs in the dataset. See [Colors](../general/colors.md#colors). +| `borderColor` | `Color/Color[]` | The border color of the arcs in the dataset. See [Colors](../general/colors.md#colors). +| `borderWidth` | `Number/Number[]` | The border width of the arcs in the dataset. +| `borderAlign` | `String/String[]` | The border alignment of the arcs in the dataset. [more...](#border-alignment) +| `hoverBackgroundColor` | `Color/Color[]` | The fill colour of the arcs when hovered. +| `hoverBorderColor` | `Color/Color[]` | The stroke colour of the arcs when hovered. +| `hoverBorderWidth` | `Number/Number[]` | The stroke width of the arcs when hovered. + +### Border Alignment + +The following values are supported for `borderAlign`. +* `'center'` (default) +* `'inner'` + +When `'center'` is set, the borders of arcs next to each other will overlap. When `'inner'` is set, it is guaranteed that all the borders are not overlap. ## Config Options diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index b24a7a00e9f..dd679bd6785 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -143,9 +143,8 @@ module.exports = DatasetController.extend({ var chart = me.chart; var chartArea = chart.chartArea; var opts = chart.options; - var arcOpts = opts.elements.arc; - var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; - var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; + var availableWidth = chartArea.right - chartArea.left; + var availableHeight = chartArea.bottom - chartArea.top; var minSize = Math.min(availableWidth, availableHeight); var offset = {x: 0, y: 0}; var meta = me.getMeta(); @@ -171,7 +170,7 @@ module.exports = DatasetController.extend({ offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; } - chart.borderWidth = me.getMaxBorderWidth(meta.data); + chart.borderWidth = me.getMaxBorderWidth(); chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); @@ -231,6 +230,7 @@ module.exports = DatasetController.extend({ model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); + model.borderAlign = custom.borderAlign ? custom.borderAlign : valueOrDefault(dataset.borderAlign, index, elementOpts.borderAlign); // Set correct angles if not resetting if (!reset || !animationOpts.animateRotate) { @@ -276,18 +276,41 @@ module.exports = DatasetController.extend({ // gets the max border or hover width to properly scale pie charts getMaxBorderWidth: function(arcs) { + var me = this; var max = 0; - var index = this.index; - var length = arcs.length; - var borderWidth; - var hoverWidth; + var index = me.index; + var chart = me.chart; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + var elementOpts = chart.options.elements.arc; + var i, ilen, dataset, custom, borderAlign, borderWidth, hoverWidth; + + if (!arcs) { + // Find the outmost visible dataset + for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + arcs = chart.getDatasetMeta(i).data; + index = i; + break; + } + } + } + + if (!arcs) { + return 0; + } - for (var i = 0; i < length; i++) { - borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; - hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; + dataset = chart.data.datasets[index]; - max = borderWidth > max ? borderWidth : max; - max = hoverWidth > max ? hoverWidth : max; + for (i = 0, ilen = arcs.length; i < ilen; ++i) { + custom = arcs[i].custom || {}; + borderAlign = custom.borderAlign ? custom.borderAlign : valueAtIndexOrDefault(dataset.borderAlign, i, elementOpts.borderAlign); + if (borderAlign !== 'inner') { + borderWidth = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(dataset.borderWidth, i, elementOpts.borderWidth); + hoverWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueAtIndexOrDefault(dataset.hoverBorderWidth, i, borderWidth); + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } } return max; } diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index fb045e30271..39013d872a4 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -148,10 +148,9 @@ module.exports = DatasetController.extend({ var chart = me.chart; var chartArea = chart.chartArea; var opts = chart.options; - var arcOpts = opts.elements.arc; var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); - chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); + chart.outerRadius = Math.max(minSize / 2, 0); chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); @@ -206,6 +205,7 @@ module.exports = DatasetController.extend({ model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); + model.borderAlign = custom.borderAlign ? custom.borderAlign : valueOrDefault(dataset.borderAlign, index, elementOpts.borderAlign); arc.pivot(); }, diff --git a/src/elements/element.arc.js b/src/elements/element.arc.js index 0d665d71740..82d836c6b80 100644 --- a/src/elements/element.arc.js +++ b/src/elements/element.arc.js @@ -9,7 +9,8 @@ defaults._set('global', { arc: { backgroundColor: defaults.global.defaultColor, borderColor: '#fff', - borderWidth: 2 + borderWidth: 2, + borderAlign: 'center' } } }); @@ -85,23 +86,51 @@ module.exports = Element.extend({ var vm = this._view; var sA = vm.startAngle; var eA = vm.endAngle; + var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; + var angleMargin; - ctx.beginPath(); + ctx.save(); - ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); + ctx.beginPath(); + ctx.arc(vm.x, vm.y, vm.outerRadius - pixelMargin, sA, eA); ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); - ctx.closePath(); - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = vm.borderWidth; ctx.fillStyle = vm.backgroundColor; - ctx.fill(); - ctx.lineJoin = 'bevel'; if (vm.borderWidth) { + if (vm.borderAlign === 'inner') { + // Draw an inner border by cliping the arc and drawing a double-width border + // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders + ctx.beginPath(); + angleMargin = pixelMargin / vm.outerRadius; + ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin); + if (vm.innerRadius > pixelMargin) { + angleMargin = pixelMargin / vm.innerRadius; + ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true); + } else { + ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2); + } + ctx.closePath(); + ctx.clip(); + + ctx.beginPath(); + ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); + ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); + ctx.closePath(); + + ctx.lineWidth = vm.borderWidth * 2; + ctx.lineJoin = 'round'; + } else { + ctx.lineWidth = vm.borderWidth; + ctx.lineJoin = 'bevel'; + } + + ctx.strokeStyle = vm.borderColor; ctx.stroke(); } + + ctx.restore(); } }); diff --git a/test/context.js b/test/context.js index 3497c721918..089d5072e8c 100644 --- a/test/context.js +++ b/test/context.js @@ -79,6 +79,7 @@ Context.prototype._initMethods = function() { beginPath: function() {}, bezierCurveTo: function() {}, clearRect: function() {}, + clip: function() {}, closePath: function() {}, fill: function() {}, fillRect: function() {}, diff --git a/test/fixtures/controller.doughnut/doughnut-border-align-center.json b/test/fixtures/controller.doughnut/doughnut-border-align-center.json new file mode 100644 index 00000000000..b76b2e82fd1 --- /dev/null +++ b/test/fixtures/controller.doughnut/doughnut-border-align-center.json @@ -0,0 +1,31 @@ +{ + "config": { + "type": "doughnut", + "data": { + "labels": ["A", "B", "C", "D", "E"], + "datasets": [{ + "data": [1, 5, 10, 50, 100], + "backgroundColor": [ + "rgba(255, 99, 132, 0.8)", + "rgba(54, 162, 235, 0.8)", + "rgba(255, 206, 86, 0.8)", + "rgba(75, 192, 192, 0.8)", + "rgba(153, 102, 255, 0.8)" + ], + "borderWidth": 20, + "borderColor": [ + "rgb(255, 99, 132)", + "rgb(54, 162, 235)", + "rgb(255, 206, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)" + ] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false + } + } +} diff --git a/test/fixtures/controller.doughnut/doughnut-border-align-center.png b/test/fixtures/controller.doughnut/doughnut-border-align-center.png new file mode 100644 index 00000000000..3eec51ff807 Binary files /dev/null and b/test/fixtures/controller.doughnut/doughnut-border-align-center.png differ diff --git a/test/fixtures/controller.doughnut/doughnut-border-align-inner.json b/test/fixtures/controller.doughnut/doughnut-border-align-inner.json new file mode 100644 index 00000000000..7fb4a76e166 --- /dev/null +++ b/test/fixtures/controller.doughnut/doughnut-border-align-inner.json @@ -0,0 +1,32 @@ +{ + "config": { + "type": "doughnut", + "data": { + "labels": ["A", "B", "C", "D", "E"], + "datasets": [{ + "data": [1, 5, 10, 50, 100], + "backgroundColor": [ + "rgba(255, 99, 132, 0.8)", + "rgba(54, 162, 235, 0.8)", + "rgba(255, 206, 86, 0.8)", + "rgba(75, 192, 192, 0.8)", + "rgba(153, 102, 255, 0.8)" + ], + "borderWidth": 20, + "borderColor": [ + "rgb(255, 99, 132)", + "rgb(54, 162, 235)", + "rgb(255, 206, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)" + ], + "borderAlign": "inner" + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false + } + } +} diff --git a/test/fixtures/controller.doughnut/doughnut-border-align-inner.png b/test/fixtures/controller.doughnut/doughnut-border-align-inner.png new file mode 100644 index 00000000000..d33adf4c206 Binary files /dev/null and b/test/fixtures/controller.doughnut/doughnut-border-align-inner.png differ diff --git a/test/fixtures/controller.doughnut/pie-border-align-center.json b/test/fixtures/controller.doughnut/pie-border-align-center.json new file mode 100644 index 00000000000..59ecf544cd4 --- /dev/null +++ b/test/fixtures/controller.doughnut/pie-border-align-center.json @@ -0,0 +1,31 @@ +{ + "config": { + "type": "pie", + "data": { + "labels": ["A", "B", "C", "D", "E"], + "datasets": [{ + "data": [1, 5, 10, 50, 100], + "backgroundColor": [ + "rgba(255, 99, 132, 0.8)", + "rgba(54, 162, 235, 0.8)", + "rgba(255, 206, 86, 0.8)", + "rgba(75, 192, 192, 0.8)", + "rgba(153, 102, 255, 0.8)" + ], + "borderWidth": 20, + "borderColor": [ + "rgb(255, 99, 132)", + "rgb(54, 162, 235)", + "rgb(255, 206, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)" + ] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false + } + } +} diff --git a/test/fixtures/controller.doughnut/pie-border-align-center.png b/test/fixtures/controller.doughnut/pie-border-align-center.png new file mode 100644 index 00000000000..77070fe6fa1 Binary files /dev/null and b/test/fixtures/controller.doughnut/pie-border-align-center.png differ diff --git a/test/fixtures/controller.doughnut/pie-border-align-inner.json b/test/fixtures/controller.doughnut/pie-border-align-inner.json new file mode 100644 index 00000000000..7b1bd0361b7 --- /dev/null +++ b/test/fixtures/controller.doughnut/pie-border-align-inner.json @@ -0,0 +1,32 @@ +{ + "config": { + "type": "pie", + "data": { + "labels": ["A", "B", "C", "D", "E"], + "datasets": [{ + "data": [1, 5, 10, 50, 100], + "backgroundColor": [ + "rgba(255, 99, 132, 0.8)", + "rgba(54, 162, 235, 0.8)", + "rgba(255, 206, 86, 0.8)", + "rgba(75, 192, 192, 0.8)", + "rgba(153, 102, 255, 0.8)" + ], + "borderWidth": 20, + "borderColor": [ + "rgb(255, 99, 132)", + "rgb(54, 162, 235)", + "rgb(255, 206, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)" + ], + "borderAlign": "inner" + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false + } + } +} diff --git a/test/fixtures/controller.doughnut/pie-border-align-inner.png b/test/fixtures/controller.doughnut/pie-border-align-inner.png new file mode 100644 index 00000000000..a2f348eb7dd Binary files /dev/null and b/test/fixtures/controller.doughnut/pie-border-align-inner.png differ diff --git a/test/fixtures/controller.polarArea/border-align-center.json b/test/fixtures/controller.polarArea/border-align-center.json new file mode 100644 index 00000000000..23471200106 --- /dev/null +++ b/test/fixtures/controller.polarArea/border-align-center.json @@ -0,0 +1,41 @@ +{ + "config": { + "type": "polarArea", + "data": { + "labels": ["A", "B", "C", "D", "E"], + "datasets": [{ + "data": [11, 16, 21, 1, 10], + "backgroundColor": [ + "rgba(255, 99, 132, 0.8)", + "rgba(54, 162, 235, 0.8)", + "rgba(255, 206, 86, 0.8)", + "rgba(75, 192, 192, 0.8)", + "rgba(153, 102, 255, 0.8)" + ], + "borderWidth": 20, + "borderColor": [ + "rgb(255, 99, 132)", + "rgb(54, 162, 235)", + "rgb(255, 206, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)" + ] + }] + }, + "options": { + "elements": { + "arc": { + "angle": [ + 0.0378, 0.1892, 0.3786, 1.8925, 3.7849 + ] + } + }, + "responsive": false, + "legend": false, + "title": false, + "scale": { + "display": false + } + } + } +} diff --git a/test/fixtures/controller.polarArea/border-align-center.png b/test/fixtures/controller.polarArea/border-align-center.png new file mode 100644 index 00000000000..ab0941bbb4d Binary files /dev/null and b/test/fixtures/controller.polarArea/border-align-center.png differ diff --git a/test/fixtures/controller.polarArea/border-align-inner.json b/test/fixtures/controller.polarArea/border-align-inner.json new file mode 100644 index 00000000000..fa165484de8 --- /dev/null +++ b/test/fixtures/controller.polarArea/border-align-inner.json @@ -0,0 +1,42 @@ +{ + "config": { + "type": "polarArea", + "data": { + "labels": ["A", "B", "C", "D", "E"], + "datasets": [{ + "data": [11, 16, 21, 1, 10], + "backgroundColor": [ + "rgba(255, 99, 132, 0.8)", + "rgba(54, 162, 235, 0.8)", + "rgba(255, 206, 86, 0.8)", + "rgba(75, 192, 192, 0.8)", + "rgba(153, 102, 255, 0.8)" + ], + "borderWidth": 20, + "borderColor": [ + "rgb(255, 99, 132)", + "rgb(54, 162, 235)", + "rgb(255, 206, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)" + ], + "borderAlign": "inner" + }] + }, + "options": { + "elements": { + "arc": { + "angle": [ + 0.0378, 0.1892, 0.3786, 1.8925, 3.7849 + ] + } + }, + "responsive": false, + "legend": false, + "title": false, + "scale": { + "display": false + } + } + } +} diff --git a/test/fixtures/controller.polarArea/border-align-inner.png b/test/fixtures/controller.polarArea/border-align-inner.png new file mode 100644 index 00000000000..f3911a4754d Binary files /dev/null and b/test/fixtures/controller.polarArea/border-align-inner.png differ diff --git a/test/specs/controller.doughnut.tests.js b/test/specs/controller.doughnut.tests.js index e7bc951fd57..e060e0e5a53 100644 --- a/test/specs/controller.doughnut.tests.js +++ b/test/specs/controller.doughnut.tests.js @@ -1,4 +1,6 @@ describe('Chart.controllers.doughnut', function() { + describe('auto', jasmine.fixture.specs('controller.doughnut')); + it('should be registered as dataset controller', function() { expect(typeof Chart.controllers.doughnut).toBe('function'); expect(Chart.controllers.doughnut).toBe(Chart.controllers.pie); @@ -106,8 +108,8 @@ describe('Chart.controllers.doughnut', function() { ].forEach(function(expected, i) { expect(meta.data[i]._model.x).toBeCloseToPixel(256); expect(meta.data[i]._model.y).toBeCloseToPixel(256); - expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(254); - expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(190); + expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(256); + expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(192); expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8); expect(meta.data[i]._model).toEqual(jasmine.objectContaining({ startAngle: Math.PI * -0.5, @@ -129,8 +131,8 @@ describe('Chart.controllers.doughnut', function() { ].forEach(function(expected, i) { expect(meta.data[i]._model.x).toBeCloseToPixel(256); expect(meta.data[i]._model.y).toBeCloseToPixel(256); - expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(254); - expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(190); + expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(256); + expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(192); expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8); expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8); expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8); @@ -200,10 +202,10 @@ describe('Chart.controllers.doughnut', function() { {c: Math.PI / 8, s: Math.PI, e: Math.PI + Math.PI / 8}, {c: 3 * Math.PI / 8, s: Math.PI + Math.PI / 8, e: Math.PI + Math.PI / 2} ].forEach(function(expected, i) { - expect(meta.data[i]._model.x).toBeCloseToPixel(511); - expect(meta.data[i]._model.y).toBeCloseToPixel(511); - expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(510); - expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(382); + expect(meta.data[i]._model.x).toBeCloseToPixel(512); + expect(meta.data[i]._model.y).toBeCloseToPixel(512); + expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(512); + expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(384); expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8); expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8); expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8); @@ -405,4 +407,52 @@ describe('Chart.controllers.doughnut', function() { expect(arc._model.borderColor).toBe('rgb(17, 17, 17)'); expect(arc._model.borderWidth).toBe(3.14159); }); + + it ('should calculate radiuses based on the border widths of the visible outermost dataset', function() { + var chart = window.acquireChart({ + type: 'doughnut', + data: { + datasets: [{ + data: [2, 4], + borderWidth: 4, + hidden: true + }, { + data: [1, 3], + borderWidth: 8 + }, { + data: [1, 0], + borderWidth: 12 + }], + labels: ['label0', 'label1'] + }, + options: { + legend: false, + title: false + } + }); + + chart.update(); + + expect(chart.chartArea.bottom - chart.chartArea.top).toBe(512); + expect(chart.borderWidth).toBe(8); + expect(chart.outerRadius).toBe(252); + expect(chart.innerRadius).toBe(126); + expect(chart.radiusLength).toBe(63); + + var controller = chart.getDatasetMeta(0).controller; + expect(controller.getMaxBorderWidth()).toBe(8); + expect(controller.outerRadius).toBe(252); + expect(controller.innerRadius).toBe(189); + + controller = chart.getDatasetMeta(1).controller; + expect(controller.getMaxBorderWidth()).toBe(8); + expect(controller.outerRadius).toBe(252); + expect(controller.innerRadius).toBe(189); + + controller = chart.getDatasetMeta(2).controller; + expect(controller.getMaxBorderWidth()).toBe(8); + expect(controller.outerRadius).toBe(189); + expect(controller.innerRadius).toBe(126); + }); + }); diff --git a/test/specs/core.tooltip.tests.js b/test/specs/core.tooltip.tests.js index c342fe64618..e6ed5cfcea6 100755 --- a/test/specs/core.tooltip.tests.js +++ b/test/specs/core.tooltip.tests.js @@ -943,7 +943,7 @@ describe('Core.Tooltip', function() { if (model.width <= chart.width) { expect(model.x + model.width).toBeLessThanOrEqual(chart.width); } - expect(model.caretX).toBe(tooltipPosition.x); + expect(model.caretX).toBeCloseToPixel(tooltipPosition.x); // if tooltip is longer than chart area then all tests done if (model.width > chart.width) { break; diff --git a/test/specs/element.arc.tests.js b/test/specs/element.arc.tests.js index b2caaadaa4c..dd373643519 100644 --- a/test/specs/element.arc.tests.js +++ b/test/specs/element.arc.tests.js @@ -126,6 +126,9 @@ describe('Arc element tests', function() { arc.draw(); expect(mockContext.getCalls()).toEqual([{ + name: 'save', + args: [] + }, { name: 'beginPath', args: [] }, { @@ -137,12 +140,6 @@ describe('Arc element tests', function() { }, { name: 'closePath', args: [] - }, { - name: 'setStrokeStyle', - args: ['rgb(255, 0, 0)'] - }, { - name: 'setLineWidth', - args: [undefined] }, { name: 'setFillStyle', args: ['rgb(0, 0, 255)'] @@ -150,8 +147,8 @@ describe('Arc element tests', function() { name: 'fill', args: [] }, { - name: 'setLineJoin', - args: ['bevel'] + name: 'restore', + args: [] }]); }); @@ -182,6 +179,9 @@ describe('Arc element tests', function() { arc.draw(); expect(mockContext.getCalls()).toEqual([{ + name: 'save', + args: [] + }, { name: 'beginPath', args: [] }, { @@ -194,23 +194,119 @@ describe('Arc element tests', function() { name: 'closePath', args: [] }, { - name: 'setStrokeStyle', - args: ['rgb(255, 0, 0)'] + name: 'setFillStyle', + args: ['rgb(0, 0, 255)'] + }, { + name: 'fill', + args: [] }, { name: 'setLineWidth', args: [5] + }, { + name: 'setLineJoin', + args: ['bevel'] + }, { + name: 'setStrokeStyle', + args: ['rgb(255, 0, 0)'] + }, { + name: 'stroke', + args: [] + }, { + name: 'restore', + args: [] + }]); + }); + + it ('should draw correctly with an inner border', function() { + var mockContext = window.createMockContext(); + var arc = new Chart.elements.Arc({ + _datasetIndex: 2, + _index: 1, + _chart: { + ctx: mockContext, + } + }); + + // Mock out the view as if the controller put it there + arc._view = { + startAngle: 0, + endAngle: Math.PI / 2, + x: 10, + y: 5, + innerRadius: 1, + outerRadius: 3, + + backgroundColor: 'rgb(0, 0, 255)', + borderColor: 'rgb(255, 0, 0)', + borderWidth: 5, + borderAlign: 'inner' + }; + + arc.draw(); + + expect(mockContext.getCalls()).toEqual([{ + name: 'save', + args: [] + }, { + name: 'beginPath', + args: [] + }, { + name: 'arc', + args: [10, 5, 2.67, 0, Math.PI / 2] + }, { + name: 'arc', + args: [10, 5, 1, Math.PI / 2, 0, true] + }, { + name: 'closePath', + args: [] }, { name: 'setFillStyle', args: ['rgb(0, 0, 255)'] }, { name: 'fill', args: [] + }, { + name: 'beginPath', + args: [] + }, { + name: 'arc', + args: [10, 5, 3, -0.11, Math.PI / 2 + 0.11] + }, { + name: 'arc', + args: [10, 5, 1 - 0.33, Math.PI / 2 + 0.33, -0.33, true] + }, { + name: 'closePath', + args: [] + }, { + name: 'clip', + args: [] + }, { + name: 'beginPath', + args: [] + }, { + name: 'arc', + args: [10, 5, 3, 0, Math.PI / 2] + }, { + name: 'arc', + args: [10, 5, 1, Math.PI / 2, 0, true] + }, { + name: 'closePath', + args: [] + }, { + name: 'setLineWidth', + args: [10] }, { name: 'setLineJoin', - args: ['bevel'] + args: ['round'] + }, { + name: 'setStrokeStyle', + args: ['rgb(255, 0, 0)'] }, { name: 'stroke', args: [] + }, { + name: 'restore', + args: [] }]); }); });