Add selfJoin option for doughnut graphs (#12054)

Co-authored-by: Pierre Gueguen <gueguenpierre.pro@gmail.com>
This commit is contained in:
Pierre
2025-04-15 15:32:13 +02:00
committed by GitHub
parent 9b1306ab0a
commit 74961d1c86
6 changed files with 97 additions and 2 deletions

View File

@@ -1,9 +1,42 @@
import Element from '../core/core.element.js';
import {_angleBetween, getAngleFromPoint, TAU, HALF_PI, valueOrDefault} from '../helpers/index.js';
import {PI, _isBetween, _limitValue} from '../helpers/helpers.math.js';
import {PI, _angleDiff, _normalizeAngle, _isBetween, _limitValue} from '../helpers/helpers.math.js';
import {_readValueToProps} from '../helpers/helpers.options.js';
import type {ArcOptions, Point} from '../types/index.js';
function clipSelf(ctx: CanvasRenderingContext2D, element: ArcElement, endAngle: number) {
const {startAngle, x, y, outerRadius, innerRadius, options} = element;
const {borderWidth, borderJoinStyle} = options;
const outerAngleClip = Math.min(borderWidth / outerRadius, _normalizeAngle(startAngle - endAngle));
ctx.beginPath();
ctx.arc(x, y, outerRadius - borderWidth / 2, startAngle + outerAngleClip / 2, endAngle - outerAngleClip / 2);
if (innerRadius > 0) {
const innerAngleClip = Math.min(borderWidth / innerRadius, _normalizeAngle(startAngle - endAngle));
ctx.arc(x, y, innerRadius + borderWidth / 2, endAngle - innerAngleClip / 2, startAngle + innerAngleClip / 2, true);
} else {
const clipWidth = Math.min(borderWidth / 2, outerRadius * _normalizeAngle(startAngle - endAngle));
if (borderJoinStyle === 'round') {
ctx.arc(x, y, clipWidth, endAngle - PI / 2, startAngle + PI / 2, true);
} else if (borderJoinStyle === 'bevel') {
const r = 2 * clipWidth * clipWidth;
const endX = -r * Math.cos(endAngle + PI / 2) + x;
const endY = -r * Math.sin(endAngle + PI / 2) + y;
const startX = r * Math.cos(startAngle + PI / 2) + x;
const startY = r * Math.sin(startAngle + PI / 2) + y;
ctx.lineTo(endX, endY);
ctx.lineTo(startX, startY);
}
}
ctx.closePath();
ctx.moveTo(0, 0);
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.clip('evenodd');
}
function clipArc(ctx: CanvasRenderingContext2D, element: ArcElement, endAngle: number) {
const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element;
@@ -213,7 +246,7 @@ function drawBorder(
circular: boolean,
) {
const {fullCircles, startAngle, circumference, options} = element;
const {borderWidth, borderJoinStyle, borderDash, borderDashOffset} = options;
const {borderWidth, borderJoinStyle, borderDash, borderDashOffset, borderRadius} = options;
const inner = options.borderAlign === 'inner';
if (!borderWidth) {
@@ -246,6 +279,10 @@ function drawBorder(
clipArc(ctx, element, endAngle);
}
if (options.selfJoin && endAngle - startAngle >= PI && borderRadius === 0 && borderJoinStyle !== 'miter') {
clipSelf(ctx, element, endAngle);
}
if (!fullCircles) {
pathArc(ctx, element, offset, spacing, endAngle, circular);
ctx.stroke();
@@ -276,6 +313,7 @@ export default class ArcElement extends Element<ArcProps, ArcOptions> {
spacing: 0,
angle: undefined,
circular: true,
selfJoin: false,
};
static defaultRoutes = {

View File

@@ -1847,6 +1847,12 @@ export interface ArcBorderRadius {
}
export interface ArcOptions extends CommonElementOptions {
/**
* If true, Arc can take up 100% of a circular graph without any visual split or cut. This option doesn't support borderRadius and borderJoinStyle miter
* @default true
*/
selfJoin: boolean;
/**
* Arc stroke alignment.
*/

View File

@@ -0,0 +1,25 @@
module.exports = {
config: {
type: 'doughnut',
data: {
labels: ['Red'],
datasets: [
{
// option in dataset
data: [100],
borderWidth: 15,
backgroundColor: '#FF0000',
borderColor: '#000000',
borderAlign: 'center',
selfJoin: true
}
]
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,26 @@
module.exports = {
config: {
type: 'pie',
data: {
labels: ['Red'],
datasets: [
{
// option in dataset
data: [100],
borderWidth: 15,
backgroundColor: '#FF0000',
borderColor: '#000000',
borderAlign: 'center',
borderJoinStyle: 'round',
selfJoin: true
}
]
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB