Skip to content

Commit 4f722ab

Browse files
nagixsimonbrunel
authored andcommitted
Fix arc size calculation when circumference is under 2PI (#6224)
1 parent 1a2a87b commit 4f722ab

File tree

5 files changed

+104
-30
lines changed

5 files changed

+104
-30
lines changed

src/controllers/controller.doughnut.js

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ var helpers = require('../helpers/index');
88
var resolve = helpers.options.resolve;
99
var valueOrDefault = helpers.valueOrDefault;
1010

11+
var PI = Math.PI;
12+
var DOUBLE_PI = PI * 2;
13+
var HALF_PI = PI / 2;
14+
1115
defaults._set('doughnut', {
1216
animation: {
1317
// Boolean - Whether we animate the rotation of the Doughnut
@@ -91,10 +95,10 @@ defaults._set('doughnut', {
9195
cutoutPercentage: 50,
9296

9397
// The rotation of the chart, where the first data arc begins.
94-
rotation: Math.PI * -0.5,
98+
rotation: -HALF_PI,
9599

96100
// The total circumference of the chart.
97-
circumference: Math.PI * 2.0,
101+
circumference: DOUBLE_PI,
98102

99103
// Need to override these to give a nice default
100104
tooltips: {
@@ -145,46 +149,52 @@ module.exports = DatasetController.extend({
145149
var chart = me.chart;
146150
var chartArea = chart.chartArea;
147151
var opts = chart.options;
148-
var availableWidth = chartArea.right - chartArea.left;
149-
var availableHeight = chartArea.bottom - chartArea.top;
150-
var minSize = Math.min(availableWidth, availableHeight);
151-
var offset = {x: 0, y: 0};
152+
var ratioX = 1;
153+
var ratioY = 1;
154+
var offsetX = 0;
155+
var offsetY = 0;
152156
var meta = me.getMeta();
153157
var arcs = meta.data;
154-
var cutoutPercentage = opts.cutoutPercentage;
158+
var cutout = opts.cutoutPercentage / 100 || 0;
155159
var circumference = opts.circumference;
156160
var chartWeight = me._getRingWeight(me.index);
157-
var i, ilen;
161+
var maxWidth, maxHeight, i, ilen;
158162

159-
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
160-
if (circumference < Math.PI * 2.0) {
161-
var startAngle = opts.rotation % (Math.PI * 2.0);
162-
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
163+
// If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
164+
if (circumference < DOUBLE_PI) {
165+
var startAngle = opts.rotation % DOUBLE_PI;
166+
startAngle += startAngle >= PI ? -DOUBLE_PI : startAngle < -PI ? DOUBLE_PI : 0;
163167
var endAngle = startAngle + circumference;
164-
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
165-
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
166-
var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
167-
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
168-
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
169-
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
170-
var cutout = cutoutPercentage / 100.0;
171-
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))};
172-
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))};
173-
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
174-
minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
175-
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
168+
var startX = Math.cos(startAngle);
169+
var startY = Math.sin(startAngle);
170+
var endX = Math.cos(endAngle);
171+
var endY = Math.sin(endAngle);
172+
var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI;
173+
var contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= DOUBLE_PI + HALF_PI;
174+
var contains180 = startAngle === -PI || endAngle >= PI;
175+
var contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
176+
var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
177+
var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
178+
var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
179+
var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
180+
ratioX = (maxX - minX) / 2;
181+
ratioY = (maxY - minY) / 2;
182+
offsetX = -(maxX + minX) / 2;
183+
offsetY = -(maxY + minY) / 2;
176184
}
177185

178186
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
179187
arcs[i]._options = me._resolveElementOptions(arcs[i], i);
180188
}
181189

182190
chart.borderWidth = me.getMaxBorderWidth();
183-
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
184-
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
191+
maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
192+
maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
193+
chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
194+
chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
185195
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
186-
chart.offsetX = offset.x * chart.outerRadius;
187-
chart.offsetY = offset.y * chart.outerRadius;
196+
chart.offsetX = offsetX * chart.outerRadius;
197+
chart.offsetY = offsetY * chart.outerRadius;
188198

189199
meta.total = me.calculateTotal();
190200

@@ -207,7 +217,7 @@ module.exports = DatasetController.extend({
207217
var startAngle = opts.rotation; // non reset case handled later
208218
var endAngle = opts.rotation; // non reset case handled later
209219
var dataset = me.getDataset();
210-
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
220+
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI);
211221
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
212222
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
213223
var options = arc._options || {};
@@ -273,7 +283,7 @@ module.exports = DatasetController.extend({
273283
calculateCircumference: function(value) {
274284
var total = this.getMeta().total;
275285
if (total > 0 && !isNaN(value)) {
276-
return (Math.PI * 2.0) * (Math.abs(value) / total);
286+
return DOUBLE_PI * (Math.abs(value) / total);
277287
}
278288
return 0;
279289
},
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"config": {
3+
"type": "doughnut",
4+
"data": {
5+
"labels": ["A", "B", "C", "D", "E"],
6+
"datasets": [{
7+
"data": [1, 5, 10, 50, 100],
8+
"backgroundColor": [
9+
"rgba(255, 99, 132, 0.8)",
10+
"rgba(54, 162, 235, 0.8)",
11+
"rgba(255, 206, 86, 0.8)",
12+
"rgba(75, 192, 192, 0.8)",
13+
"rgba(153, 102, 255, 0.8)"
14+
],
15+
"borderWidth": 20,
16+
"borderColor": [
17+
"rgb(255, 99, 132)",
18+
"rgb(54, 162, 235)",
19+
"rgb(255, 206, 86)",
20+
"rgb(75, 192, 192)",
21+
"rgb(153, 102, 255)"
22+
]
23+
}]
24+
},
25+
"options": {
26+
"circumference": 1,
27+
"responsive": false,
28+
"legend": false,
29+
"title": false
30+
}
31+
}
32+
}
38.9 KB
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"config": {
3+
"type": "pie",
4+
"data": {
5+
"labels": ["A", "B", "C", "D", "E"],
6+
"datasets": [{
7+
"data": [1, 5, 10, 50, 100],
8+
"backgroundColor": [
9+
"rgba(255, 99, 132, 0.8)",
10+
"rgba(54, 162, 235, 0.8)",
11+
"rgba(255, 206, 86, 0.8)",
12+
"rgba(75, 192, 192, 0.8)",
13+
"rgba(153, 102, 255, 0.8)"
14+
],
15+
"borderWidth": 20,
16+
"borderColor": [
17+
"rgb(255, 99, 132)",
18+
"rgb(54, 162, 235)",
19+
"rgb(255, 206, 86)",
20+
"rgb(75, 192, 192)",
21+
"rgb(153, 102, 255)"
22+
]
23+
}]
24+
},
25+
"options": {
26+
"circumference": 1,
27+
"responsive": false,
28+
"legend": false,
29+
"title": false
30+
}
31+
}
32+
}
45.8 KB
Loading

0 commit comments

Comments
 (0)