]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Add support for 'inner' border for arc elements (#5841)
authorAkihiko Kusanagi <nagi@nagi-p.com>
Tue, 18 Dec 2018 12:58:02 +0000 (21:58 +0900)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Tue, 18 Dec 2018 12:58:02 +0000 (13:58 +0100)
21 files changed:
docs/charts/doughnut.md
docs/charts/polar.md
src/controllers/controller.doughnut.js
src/controllers/controller.polarArea.js
src/elements/element.arc.js
test/context.js
test/fixtures/controller.doughnut/doughnut-border-align-center.json [new file with mode: 0644]
test/fixtures/controller.doughnut/doughnut-border-align-center.png [new file with mode: 0644]
test/fixtures/controller.doughnut/doughnut-border-align-inner.json [new file with mode: 0644]
test/fixtures/controller.doughnut/doughnut-border-align-inner.png [new file with mode: 0644]
test/fixtures/controller.doughnut/pie-border-align-center.json [new file with mode: 0644]
test/fixtures/controller.doughnut/pie-border-align-center.png [new file with mode: 0644]
test/fixtures/controller.doughnut/pie-border-align-inner.json [new file with mode: 0644]
test/fixtures/controller.doughnut/pie-border-align-inner.png [new file with mode: 0644]
test/fixtures/controller.polarArea/border-align-center.json [new file with mode: 0644]
test/fixtures/controller.polarArea/border-align-center.png [new file with mode: 0644]
test/fixtures/controller.polarArea/border-align-inner.json [new file with mode: 0644]
test/fixtures/controller.polarArea/border-align-inner.png [new file with mode: 0644]
test/specs/controller.doughnut.tests.js
test/specs/core.tooltip.tests.js
test/specs/element.arc.tests.js

index 4f1656f3063752f379ada5b06ee142da15379aba..9fda1bbf7190acc4cd15b7dab0a615fa257a2b18 100644 (file)
@@ -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
 
index 2ae8b5ec3ddc9ccce74b999ba6581718e0e66d09..20dc93c283b76dc5b980a71c7862f6085a105507 100644 (file)
@@ -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
 
index b24a7a00e9f6f56154c39433867b130d95c258f7..f2a55d84e9021d0f09c87e02097c3d00df54f561 100644 (file)
@@ -143,14 +143,15 @@ 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();
+               var arcs = meta.data;
                var cutoutPercentage = opts.cutoutPercentage;
                var circumference = opts.circumference;
+               var i, ilen;
 
                // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
                if (circumference < Math.PI * 2.0) {
@@ -171,7 +172,11 @@ 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);
+               for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+                       arcs[i]._options = me._resolveElementOptions(arcs[i], i, reset);
+               }
+
+               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();
@@ -183,9 +188,9 @@ module.exports = DatasetController.extend({
                me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
                me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
 
-               helpers.each(meta.data, function(arc, index) {
-                       me.updateElement(arc, index, reset);
-               });
+               for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+                       me.updateElement(arcs[i], i, reset);
+               }
        },
 
        updateElement: function(arc, index, reset) {
@@ -202,7 +207,7 @@ module.exports = DatasetController.extend({
                var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
                var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
                var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
-               var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
+               var options = arc._options || {};
 
                helpers.extend(arc, {
                        // Utility
@@ -211,6 +216,10 @@ module.exports = DatasetController.extend({
 
                        // Desired view properties
                        _model: {
+                               backgroundColor: options.backgroundColor,
+                               borderColor: options.borderColor,
+                               borderWidth: options.borderWidth,
+                               borderAlign: options.borderAlign,
                                x: centerX + chart.offsetX,
                                y: centerY + chart.offsetY,
                                startAngle: startAngle,
@@ -218,20 +227,12 @@ module.exports = DatasetController.extend({
                                circumference: circumference,
                                outerRadius: outerRadius,
                                innerRadius: innerRadius,
-                               label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
+                               label: helpers.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
                        }
                });
 
                var model = arc._model;
 
-               // Resets the visual styles
-               var custom = arc.custom || {};
-               var valueOrDefault = helpers.valueAtIndexOrDefault;
-               var elementOpts = this.chart.options.elements.arc;
-               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);
-
                // Set correct angles if not resetting
                if (!reset || !animationOpts.animateRotate) {
                        if (index === 0) {
@@ -276,19 +277,58 @@ 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 chart = me.chart;
+               var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
+
+               if (!arcs) {
+                       // Find the outmost visible dataset
+                       for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
+                               if (chart.isDatasetVisible(i)) {
+                                       meta = chart.getDatasetMeta(i);
+                                       arcs = meta.data;
+                                       if (i !== me.index) {
+                                               controller = meta.controller;
+                                       }
+                                       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;
+               for (i = 0, ilen = arcs.length; i < ilen; ++i) {
+                       arc = arcs[i];
+                       options = controller ? controller._resolveElementOptions(arc, i) : arc._options;
+                       if (options.borderAlign !== 'inner') {
+                               borderWidth = options.borderWidth;
+                               hoverWidth = options.hoverBorderWidth;
 
-                       max = borderWidth > max ? borderWidth : max;
-                       max = hoverWidth > max ? hoverWidth : max;
+                               max = borderWidth > max ? borderWidth : max;
+                               max = hoverWidth > max ? hoverWidth : max;
+                       }
                }
                return max;
+       },
+
+       /**
+        * @private
+        */
+       _resolveElementOptions: function(arc, index) {
+               var me = this;
+               var dataset = me.getDataset();
+               var custom = arc.custom || {};
+               var options = me.chart.options.elements.arc;
+               var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
+
+               return {
+                       backgroundColor: custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(dataset.backgroundColor, index, options.backgroundColor),
+                       borderColor: custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(dataset.borderColor, index, options.borderColor),
+                       borderWidth: custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(dataset.borderWidth, index, options.borderWidth),
+                       borderAlign: custom.borderAlign ? custom.borderAlign : valueAtIndexOrDefault(dataset.borderAlign, index, options.borderAlign)
+               };
        }
 });
index fb045e302717058d955a49754a82343446e266ce..39013d872a4c4c3375ebe409684a72467592556f 100644 (file)
@@ -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();
        },
index 0d665d7174037a1799195507d1ad351f871797b9..82d836c6b80f47915ad0b68ca7eeec6ac33cd9b7 100644 (file)
@@ -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();
        }
 });
index ecf5f8a4ab250c0f34a8252ef5ed4c7f8f6752ad..4b683aff737081a28722230d2c3cc3cde766a696 100644 (file)
@@ -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 (file)
index 0000000..b76b2e8
--- /dev/null
@@ -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 (file)
index 0000000..3eec51f
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 (file)
index 0000000..7fb4a76
--- /dev/null
@@ -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 (file)
index 0000000..d33adf4
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 (file)
index 0000000..59ecf54
--- /dev/null
@@ -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 (file)
index 0000000..77070fe
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 (file)
index 0000000..7b1bd03
--- /dev/null
@@ -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 (file)
index 0000000..a2f348e
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 (file)
index 0000000..2347120
--- /dev/null
@@ -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 (file)
index 0000000..ab0941b
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 (file)
index 0000000..fa16548
--- /dev/null
@@ -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 (file)
index 0000000..f3911a4
Binary files /dev/null and b/test/fixtures/controller.polarArea/border-align-inner.png differ
index e7bc951fd5702d9fcc02969694d3e1b8ba1b8565..e060e0e5a53678c778584eb6a0965f35ff7d869a 100644 (file)
@@ -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);
+       });
+
 });
index 5ed898a0386a8b4763b9a784937436f17bf1b08e..b9b93d0116a6cb2c283518dc4527e09ec1e5780f 100755 (executable)
@@ -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;
index b2caaadaa4cfe2d159976a7c0669565b21812f28..dd3736435194192d35c11eed3e04e8e6f4afdb0e 100644 (file)
@@ -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: []
                }]);
        });
 });