]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix arc size calculation when circumference is under 2PI (#6224)
authorAkihiko Kusanagi <nagi@nagi-p.com>
Wed, 1 May 2019 16:05:34 +0000 (00:05 +0800)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Wed, 1 May 2019 16:05:34 +0000 (18:05 +0200)
src/controllers/controller.doughnut.js
test/fixtures/controller.doughnut/doughnut-circumference.json [new file with mode: 0644]
test/fixtures/controller.doughnut/doughnut-circumference.png [new file with mode: 0644]
test/fixtures/controller.doughnut/pie-circumference.json [new file with mode: 0644]
test/fixtures/controller.doughnut/pie-circumference.png [new file with mode: 0644]

index a6d4a63775a9093ec5a03df6b8d0bc11d4a2edec..688368edac4902377bd83d068f4b0bf696079a67 100644 (file)
@@ -8,6 +8,10 @@ var helpers = require('../helpers/index');
 var resolve = helpers.options.resolve;
 var valueOrDefault = helpers.valueOrDefault;
 
+var PI = Math.PI;
+var DOUBLE_PI = PI * 2;
+var HALF_PI = PI / 2;
+
 defaults._set('doughnut', {
        animation: {
                // Boolean - Whether we animate the rotation of the Doughnut
@@ -91,10 +95,10 @@ defaults._set('doughnut', {
        cutoutPercentage: 50,
 
        // The rotation of the chart, where the first data arc begins.
-       rotation: Math.PI * -0.5,
+       rotation: -HALF_PI,
 
        // The total circumference of the chart.
-       circumference: Math.PI * 2.0,
+       circumference: DOUBLE_PI,
 
        // Need to override these to give a nice default
        tooltips: {
@@ -145,34 +149,38 @@ module.exports = DatasetController.extend({
                var chart = me.chart;
                var chartArea = chart.chartArea;
                var opts = chart.options;
-               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 ratioX = 1;
+               var ratioY = 1;
+               var offsetX = 0;
+               var offsetY = 0;
                var meta = me.getMeta();
                var arcs = meta.data;
-               var cutoutPercentage = opts.cutoutPercentage;
+               var cutout = opts.cutoutPercentage / 100 || 0;
                var circumference = opts.circumference;
                var chartWeight = me._getRingWeight(me.index);
-               var i, ilen;
+               var maxWidth, maxHeight, 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) {
-                       var startAngle = opts.rotation % (Math.PI * 2.0);
-                       startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
+               // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
+               if (circumference < DOUBLE_PI) {
+                       var startAngle = opts.rotation % DOUBLE_PI;
+                       startAngle += startAngle >= PI ? -DOUBLE_PI : startAngle < -PI ? DOUBLE_PI : 0;
                        var endAngle = startAngle + circumference;
-                       var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
-                       var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
-                       var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
-                       var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
-                       var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
-                       var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
-                       var cutout = cutoutPercentage / 100.0;
-                       var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
-                       var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
-                       var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
-                       minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
-                       offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
+                       var startX = Math.cos(startAngle);
+                       var startY = Math.sin(startAngle);
+                       var endX = Math.cos(endAngle);
+                       var endY = Math.sin(endAngle);
+                       var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI;
+                       var contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= DOUBLE_PI + HALF_PI;
+                       var contains180 = startAngle === -PI || endAngle >= PI;
+                       var contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
+                       var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
+                       var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
+                       var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
+                       var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
+                       ratioX = (maxX - minX) / 2;
+                       ratioY = (maxY - minY) / 2;
+                       offsetX = -(maxX + minX) / 2;
+                       offsetY = -(maxY + minY) / 2;
                }
 
                for (i = 0, ilen = arcs.length; i < ilen; ++i) {
@@ -180,11 +188,13 @@ module.exports = DatasetController.extend({
                }
 
                chart.borderWidth = me.getMaxBorderWidth();
-               chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
-               chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
+               maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
+               maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
+               chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
+               chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
                chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
-               chart.offsetX = offset.x * chart.outerRadius;
-               chart.offsetY = offset.y * chart.outerRadius;
+               chart.offsetX = offsetX * chart.outerRadius;
+               chart.offsetY = offsetY * chart.outerRadius;
 
                meta.total = me.calculateTotal();
 
@@ -207,7 +217,7 @@ module.exports = DatasetController.extend({
                var startAngle = opts.rotation; // non reset case handled later
                var endAngle = opts.rotation; // non reset case handled later
                var dataset = me.getDataset();
-               var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
+               var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI);
                var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
                var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
                var options = arc._options || {};
@@ -273,7 +283,7 @@ module.exports = DatasetController.extend({
        calculateCircumference: function(value) {
                var total = this.getMeta().total;
                if (total > 0 && !isNaN(value)) {
-                       return (Math.PI * 2.0) * (Math.abs(value) / total);
+                       return DOUBLE_PI * (Math.abs(value) / total);
                }
                return 0;
        },
diff --git a/test/fixtures/controller.doughnut/doughnut-circumference.json b/test/fixtures/controller.doughnut/doughnut-circumference.json
new file mode 100644 (file)
index 0000000..427079f
--- /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)"
+                ]
+            }]
+        },
+        "options": {
+            "circumference": 1,
+            "responsive": false,
+            "legend": false,
+            "title": false
+        }
+    }
+}
diff --git a/test/fixtures/controller.doughnut/doughnut-circumference.png b/test/fixtures/controller.doughnut/doughnut-circumference.png
new file mode 100644 (file)
index 0000000..e5bf207
Binary files /dev/null and b/test/fixtures/controller.doughnut/doughnut-circumference.png differ
diff --git a/test/fixtures/controller.doughnut/pie-circumference.json b/test/fixtures/controller.doughnut/pie-circumference.json
new file mode 100644 (file)
index 0000000..b892707
--- /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)"
+                ]
+            }]
+        },
+        "options": {
+            "circumference": 1,
+            "responsive": false,
+            "legend": false,
+            "title": false
+        }
+    }
+}
diff --git a/test/fixtures/controller.doughnut/pie-circumference.png b/test/fixtures/controller.doughnut/pie-circumference.png
new file mode 100644 (file)
index 0000000..7b4a631
Binary files /dev/null and b/test/fixtures/controller.doughnut/pie-circumference.png differ