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

Rename cutoutPercentage to cutout + chores #8514

Merged
merged 1 commit into from
Feb 24, 2021
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
8 changes: 4 additions & 4 deletions docs/docs/charts/doughnut.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Pie and doughnut charts are probably the most commonly used charts. They are div

They are excellent at showing the relational proportions between data.

Pie and doughnut charts are effectively the same class in Chart.js, but have one different default value - their `cutoutPercentage`. This equates to what percentage of the inner should be cut out. This defaults to `0` for pie charts, and `50` for doughnuts.
Pie and doughnut charts are effectively the same class in Chart.js, but have one different default value - their `cutout`. This equates to what portion of the inner should be cut out. This defaults to `0` for pie charts, and `'50%'` for doughnuts.

They are also registered under two aliases in the `Chart` core. Other than their different default value, and different alias, they are exactly the same.

Expand Down Expand Up @@ -163,16 +163,16 @@ These are the customisation options specific to Pie & Doughnut charts. These opt

| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `cutoutPercentage` | `number` | `50` - for doughnut, `0` - for pie | The percentage of the chart that is cut out of the middle.
| `outerRadius` | `number`\|`string` | `100%` | The outer radius of the chart. If `string` and ending with '%', percentage of the maximum radius. `number` is considered to be pixels.
| `cutout` | `number`\|`string` | `50%` - for doughnut, `0` - for pie | The portion of the chart that is cut out of the middle. If `string` and ending with '%', percentage of the chart radius. `number` is considered to be pixels.
| `radius` | `number`\|`string` | `100%` | The outer radius of the chart. If `string` and ending with '%', percentage of the maximum radius. `number` is considered to be pixels.
| `rotation` | `number` | 0 | Starting angle to draw arcs from.
| `circumference` | `number` | 360 | Sweep to allow arcs to cover.
| `animation.animateRotate` | `boolean` | `true` | If true, the chart will animate in with a rotation animation. This property is in the `options.animation` object.
| `animation.animateScale` | `boolean` | `false` | If true, will animate scaling the chart from the center outwards.

## Default Options

We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.controllers.doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.controllers.pie`, with the only difference being `cutoutPercentage` being set to 0.
We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.controllers.doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.controllers.pie`, with the only difference being `cutout` being set to 0.

## Data Structure

Expand Down
1 change: 1 addition & 0 deletions docs/docs/getting-started/v3-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ A number of changes were made to the configuration options passed to the `Chart`
* Polar area `startAngle` option is now consistent with `Radar`, 0 is at top and value is in degrees. Default is changed from `-½π` to `0`.
* Doughnut `rotation` option is now in degrees and 0 is at top. Default is changed from `-½π` to `0`.
* Doughnut `circumference` option is now in degrees. Default is changed from `2π` to `360`.
* Doughnut `cutoutPercentage` was renamed to `cutout`and accepts pixels as numer and percent as string ending with `%`.
etimberg marked this conversation as resolved.
Show resolved Hide resolved
* `scale` option was removed in favor of `options.scales.r` (or any other scale id, with `axis: 'r'`)
* `scales.[x/y]Axes` arrays were removed. Scales are now configured directly to `options.scales` object with the object key being the scale Id.
* `scales.[x/y]Axes.barPercentage` was moved to dataset option `barPercentage`
Expand Down
6 changes: 3 additions & 3 deletions samples/scriptable/pie.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@

// eslint-disable-next-line no-unused-vars
function togglePieDoughnut() {
if (chart.options.cutoutPercentage) {
chart.options.cutoutPercentage = 0;
if (chart.options.cutout) {
chart.options.cutout = 0;
} else {
chart.options.cutoutPercentage = 50;
chart.options.cutout = '50%';
}
chart.update();
}
Expand Down
37 changes: 17 additions & 20 deletions src/controllers/controller.doughnut.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
import DatasetController from '../core/core.datasetController';
import {formatNumber} from '../core/core.intl';
import {isArray, numberOrPercentageOf, valueOrDefault} from '../helpers/helpers.core';
import {toRadians, PI, TAU, HALF_PI} from '../helpers/helpers.math';
import {isArray, toPercentage, toPixels, valueOrDefault} from '../helpers/helpers.core';
import {toRadians, PI, TAU, HALF_PI, _angleBetween} from '../helpers/helpers.math';

/**
* @typedef { import("../core/core.controller").default } Chart
*/


function getRatioAndOffset(rotation, circumference, cutout) {
let ratioX = 1;
let ratioY = 1;
let offsetX = 0;
let offsetY = 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 < TAU) {
let startAngle = rotation % TAU;
startAngle += startAngle >= PI ? -TAU : startAngle < -PI ? TAU : 0;
const startAngle = rotation;
const endAngle = startAngle + circumference;
const startX = Math.cos(startAngle);
const startY = Math.sin(startAngle);
const endX = Math.cos(endAngle);
const endY = Math.sin(endAngle);
const contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= TAU;
const contains90 = (startAngle <= HALF_PI && endAngle >= HALF_PI) || endAngle >= TAU + HALF_PI;
const contains180 = startAngle === -PI || endAngle >= PI;
const contains270 = (startAngle <= -HALF_PI && endAngle >= -HALF_PI) || endAngle >= PI + HALF_PI;
const minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
const minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
const maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
const maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
const calcMax = (angle, a, b) => _angleBetween(angle, startAngle, endAngle) ? 1 : Math.max(a, a * cutout, b, b * cutout);
const calcMin = (angle, a, b) => _angleBetween(angle, startAngle, endAngle) ? -1 : Math.min(a, a * cutout, b, b * cutout);
const maxX = calcMax(0, startX, endX);
const maxY = calcMax(HALF_PI, startY, endY);
const minX = calcMin(PI, startX, endX);
const minY = calcMin(PI + HALF_PI, startY, endY);
ratioX = (maxX - minX) / 2;
ratioY = (maxY - minY) / 2;
offsetX = -(maxX + minX) / 2;
Expand Down Expand Up @@ -127,19 +123,20 @@ export default class DoughnutController extends DatasetController {
const {chartArea} = chart;
const meta = me._cachedMeta;
const arcs = meta.data;
const cutout = me.options.cutoutPercentage / 100 || 0;
const spacing = me.getMaxBorderWidth() + me.getMaxOffset(arcs);
const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);
const cutout = Math.min(toPercentage(me.options.cutout, maxSize), 1);
const chartWeight = me._getRingWeight(me.index);

// Compute the maximal rotation & circumference limits.
// If we only consider our dataset, this can cause problems when two datasets
// are both less than a circle with different rotations (starting angles)
const {circumference, rotation} = me._getRotationExtents();
const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout);
const spacing = me.getMaxBorderWidth() + me.getMaxOffset(arcs);
const maxWidth = (chartArea.right - chartArea.left - spacing) / ratioX;
const maxHeight = (chartArea.bottom - chartArea.top - spacing) / ratioY;
const maxWidth = (chartArea.width - spacing) / ratioX;
const maxHeight = (chartArea.height - spacing) / ratioY;
const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
const outerRadius = numberOrPercentageOf(me.options.outerRadius, maxRadius);
const outerRadius = toPixels(me.options.radius, maxRadius);
const innerRadius = Math.max(outerRadius * cutout, 0);
const radiusLength = (outerRadius - innerRadius) / me._getVisibleDatasetWeightTotal();
me.offsetX = offsetX * outerRadius;
Expand Down Expand Up @@ -345,7 +342,7 @@ DoughnutController.defaults = {

datasets: {
// The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
cutout: '50%',

// The rotation of the chart, where the first data arc begins.
rotation: 0,
Expand All @@ -354,7 +351,7 @@ DoughnutController.defaults = {
circumference: 360,

// The outr radius of the chart
outerRadius: '100%'
radius: '100%'
},

indexAxis: 'r',
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/controller.pie.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ PieController.defaults = {
circumference: 360,

// The outr radius of the chart
outerRadius: '100%'
radius: '100%'
}
};
7 changes: 6 additions & 1 deletion src/helpers/helpers.core.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ export function valueOrDefault(value, defaultValue) {
return typeof value === 'undefined' ? defaultValue : value;
}

export const numberOrPercentageOf = (value, dimension) =>
export const toPercentage = (value, dimension) =>
typeof value === 'string' && value.endsWith('%') ?
parseFloat(value) / 100
: value / dimension;

export const toPixels = (value, dimension) =>
typeof value === 'string' && value.endsWith('%') ?
parseFloat(value) / 100 * dimension
: +value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = {
}]
},
options: {
outerRadius: '30%',
radius: '30%',
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = {
}]
},
options: {
outerRadius: 150,
radius: 150,
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext('2d');

module.exports = {
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)'
],
borderColor: [
'rgb(255, 99, 132)',
'rgb(54, 162, 235)',
'rgb(255, 206, 86)',
'rgb(75, 192, 192)',
'rgb(153, 102, 255)'
]
}]
},
options: {
rotation: -360,
circumference: 180,
events: []
}
},
options: {
canvas: {
height: 512,
width: 512
},
run: function(chart) {
return new Promise((resolve) => {
for (let i = 0; i < 64; i++) {
const col = i % 8;
const row = Math.floor(i / 8);
const evenodd = row % 2 ? 1 : -1;
chart.options.rotation = col * 45 * evenodd;
chart.options.circumference = 360 - row * 45;
chart.update();
ctx.drawImage(chart.canvas, col * 64, row * 64, 64, 64);
}
ctx.strokeStyle = 'red';
ctx.lineWidth = 0.5;
ctx.beginPath();
for (let i = 1; i < 8; i++) {
ctx.moveTo(i * 64, 0);
ctx.lineTo(i * 64, 511);
ctx.moveTo(0, i * 64);
ctx.lineTo(511, i * 64);
}
ctx.stroke();
Chart.helpers.clearCanvas(chart.canvas);
chart.ctx.drawImage(canvas, 0, 0);
resolve();
});
}
}
};
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions test/specs/controller.doughnut.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('Chart.controllers.doughnut', function() {
animateRotate: true,
animateScale: false
},
cutoutPercentage: 50,
cutout: '50%',
rotation: 0,
circumference: 360,
elements: {
Expand Down Expand Up @@ -169,7 +169,7 @@ describe('Chart.controllers.doughnut', function() {
legend: false,
title: false,
},
cutoutPercentage: 50,
cutout: '50%',
rotation: 270,
circumference: 90,
elements: {
Expand Down Expand Up @@ -215,7 +215,7 @@ describe('Chart.controllers.doughnut', function() {
legend: false,
title: false
},
cutoutPercentage: 50,
cutout: '50%',
rotation: 270,
circumference: 90,
elements: {
Expand Down Expand Up @@ -325,7 +325,7 @@ describe('Chart.controllers.doughnut', function() {
}]
},
options: {
cutoutPercentage: 0,
cutout: '50%',
elements: {
arc: {
backgroundColor: 'rgb(100, 150, 200)',
Expand Down
7 changes: 4 additions & 3 deletions types/index.esm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,16 +285,17 @@ export interface DoughnutControllerChartOptions {
circumference: number;

/**
* The percentage of the chart that is cut out of the middle. (50 - for doughnut, 0 - for pie)
* The portion of the chart that is cut out of the middle. ('50%' - for doughnut, 0 - for pie)
* String ending with '%' means percentage, number means pixels.
* @default 50
*/
cutoutPercentage: number;
cutout: Scriptable<number | string, ScriptableContext<number>>;

/**
* The outer radius of the chart. String ending with '%' means percentage of maximum radius, number means pixels.
* @default '100%'
*/
outerRadius: Scriptable<number | string, ScriptableContext<number>>;
radius: Scriptable<number | string, ScriptableContext<number>>;

/**
* Starting angle to draw arcs from.
Expand Down
2 changes: 1 addition & 1 deletion types/tests/controllers/doughnut_outer_radius.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ const chart = new Chart('id', {
}]
},
options: {
outerRadius: () => Math.random() > 0.5 ? 50 : '50%',
radius: () => Math.random() > 0.5 ? 50 : '50%',
}
});