Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/charts/doughnut.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ The style of each arc can be controlled with the following properties:
| `borderJoinStyle` | arc border join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin).
| `borderWidth` | arc border width (in pixels).
| `offset` | arc offset (in pixels).
| `spacing` | Fixed arc offset (in pixels). Similar to `offset` but applies to all arcs.
| `spacing` | Fixed arc (slice) offset (in pixels). Similar to `offset` but applies to all slices.
| `datasetSpacing` | Fixed spacing between datasets (in pixels). This property only applies to multi-dataset doughnut/pie charts. It adjusts the spacing between concentric rings of data, allowing better visual separation between datasets.
| `weight` | The relative thickness of the dataset. Providing a value for weight will cause the pie or doughnut dataset to be drawn with a thickness relative to the sum of all the dataset weight values.

All these values, if `undefined`, fallback to the associated [`elements.arc.*`](../configuration/elements.md#arc-configuration) options.
Expand Down
12 changes: 10 additions & 2 deletions src/controllers/controller.doughnut.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,16 @@ export default class DoughnutController extends DatasetController {

meta.total = this.calculateTotal();

this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);
this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);
// For index 0, 0 spacing
// For index 1, 1 spacing
// For index 2, 2 spacings (account for the spacing for previous datasets)
// These are triangular numbers, so N*(N+1)/2 are the count of spacings we need to add.
const datasetSpacing = this.index > 0 && this.options.datasetSpacing
? (this.options.datasetSpacing || 0) * (this.index * (this.index + 1) / 2)
: 0;

this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index) - datasetSpacing;
this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight - datasetSpacing, 0);

this.updateElements(arcs, 0, arcs.length, mode);
}
Expand Down
8 changes: 7 additions & 1 deletion src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,17 @@ export interface DoughnutControllerChartOptions {
rotation: number;

/**
* Spacing between the arcs
* Spacing between the arcs (slices)
* @default 0
*/
spacing: number;

/**
* Spacing between the dataSets
* @default 0
*/
datasetSpacing: number;

/**
* Geometry used to apply arc spacing.
* - `proportional`: legacy behavior (default for polarArea).
Expand Down
70 changes: 70 additions & 0 deletions test/fixtures/controller.doughnut/doughnut-dataset-spacing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"config": {
"type": "doughnut",
"data": {
"datasets": [
{
"label": "Dataset 1",
"data": [
10,
20,
40
],
"backgroundColor": [
"rgba(255, 99, 132, 0.8)",
"rgba(54, 162, 235, 0.8)",
"rgba(255, 206, 86, 0.8)"
],
"borderWidth": 0,
"weight": 1
},
{
"label": "Dataset 2",
"data": [
15,
25,
35
],
"backgroundColor": [
"rgba(75, 192, 192, 0.8)",
"rgba(153, 102, 255, 0.8)",
"rgba(255, 159, 64, 0.8)"
],
"borderWidth": 0,
"weight": 1
},
{
"label": "Dataset 3",
"data": [
20,
30,
30
],
"backgroundColor": [
"rgba(255, 99, 132, 0.5)",
"rgba(54, 162, 235, 0.5)",
"rgba(255, 206, 86, 0.5)"
],
"borderWidth": 0,
"weight": 1
}
],
"labels": [
"Category A",
"Category B",
"Category C"
]
},
"options": {
"datasetSpacing": 8,
"responsive": false,
"maintainAspectRatio": true,
"plugins": {
"legend": {
"display": true,
"position": "top"
}
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
113 changes: 113 additions & 0 deletions test/specs/controller.doughnut.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,4 +460,117 @@ describe('Chart.controllers.doughnut', function() {
after: []
}]);
});

it ('should apply datasetSpacing to multiple datasets', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 20],
label: 'Dataset 1',
backgroundColor: ['red', 'blue']
}, {
data: [15, 25],
label: 'Dataset 2',
backgroundColor: ['green', 'yellow']
}, {
data: [20, 30],
label: 'Dataset 3',
backgroundColor: ['orange', 'purple']
}],
labels: ['label0', 'label1']
},
options: {
plugins: {
legend: false,
title: false
},
datasetSpacing: 10
}
});

chart.update();

var controller0 = chart.getDatasetMeta(0).controller;
var controller1 = chart.getDatasetMeta(1).controller;
var controller2 = chart.getDatasetMeta(2).controller;

// Verify that outer/inner radius decrease by datasetSpacing for each dataset
expect(controller0.outerRadius).toBeGreaterThan(0);
expect(controller1.outerRadius).toBeGreaterThan(0);
expect(controller2.outerRadius).toBeGreaterThan(0);

expect(controller0.outerRadius).toBeGreaterThan(controller1.outerRadius);
expect(controller1.outerRadius).toBeGreaterThan(controller2.outerRadius);

// The outer radius should decrease as we move to inner datasets
// Each dataset should have its spacing applied
var spacing0to1 = controller0.outerRadius - controller1.outerRadius;
var spacing1to2 = controller1.outerRadius - controller2.outerRadius;
expect(spacing0to1).toBeGreaterThan(0);
expect(spacing1to2).toBeGreaterThan(0);
});

it ('should handle zero datasetSpacing', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 20],
label: 'Dataset 1'
}, {
data: [15, 25],
label: 'Dataset 2'
}],
labels: ['label0', 'label1']
},
options: {
plugins: {
legend: false,
title: false
},
datasetSpacing: 0
}
});

chart.update();

var controller0 = chart.getDatasetMeta(0).controller;
var controller1 = chart.getDatasetMeta(1).controller;

// With zero spacing, the radius difference should be only due to radiusLength
var radiusLength = (controller0.outerRadius - controller0.innerRadius);
expect(controller0.outerRadius - controller1.outerRadius).toBeCloseTo(radiusLength, 0);
});

it ('should handle undefined datasetSpacing (default to 0)', function() {
var chart = window.acquireChart({
type: 'doughnut',
data: {
datasets: [{
data: [10, 20],
label: 'Dataset 1'
}, {
data: [15, 25],
label: 'Dataset 2'
}],
labels: ['label0', 'label1']
},
options: {
plugins: {
legend: false,
title: false
}
}
});

chart.update();

var controller0 = chart.getDatasetMeta(0).controller;
var controller1 = chart.getDatasetMeta(1).controller;

// With undefined spacing (defaults to 0), the radius difference should be only due to radiusLength
var radiusLength = (controller0.outerRadius - controller0.innerRadius);
expect(controller0.outerRadius - controller1.outerRadius).toBeCloseTo(radiusLength, 0);
});
});
1 change: 1 addition & 0 deletions test/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const doughnutOptions: DoughnutControllerChartOptions = {
radius: 100,
rotation: 0,
spacing: 0,
datasetSpacing: 0,
animation: false,
spacingMode: 'angular',
};
Expand Down
Loading