Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix arc size calculation when circumference is under 2PI #6224

Merged
merged 2 commits into from May 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 40 additions & 30 deletions src/controllers/controller.doughnut.js
Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -145,46 +149,52 @@ 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) {
arcs[i]._options = me._resolveElementOptions(arcs[i], i);
}

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();

Expand All @@ -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 || {};
Expand Down Expand Up @@ -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;
},
Expand Down
32 changes: 32 additions & 0 deletions test/fixtures/controller.doughnut/doughnut-circumference.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)"
]
}]
},
"options": {
"circumference": 1,
"responsive": false,
"legend": false,
"title": false
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions test/fixtures/controller.doughnut/pie-circumference.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)"
]
}]
},
"options": {
"circumference": 1,
"responsive": false,
"legend": false,
"title": false
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.