From: Jukka Kurkela Date: Tue, 18 May 2021 23:24:57 +0000 (+0300) Subject: Fix arc offset calculation (#9118) X-Git-Tag: v3.3.0~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=939ffe02ab8336e092fe30269a8604913b1c2513;p=thirdparty%2FChart.js.git Fix arc offset calculation (#9118) --- diff --git a/src/elements/element.arc.js b/src/elements/element.arc.js index 2bdfd387d..3b8e50075 100644 --- a/src/elements/element.arc.js +++ b/src/elements/element.arc.js @@ -1,6 +1,6 @@ import Element from '../core/core.element'; import {_angleBetween, getAngleFromPoint, TAU, HALF_PI} from '../helpers/index'; -import {_limitValue} from '../helpers/helpers.math'; +import {PI, _limitValue} from '../helpers/helpers.math'; import {_readValueToProps} from '../helpers/helpers.options'; function clipArc(ctx, element) { @@ -93,10 +93,16 @@ function rThetaToXY(r, theta, x, y) { * @param {CanvasRenderingContext2D} ctx * @param {ArcElement} element */ -function pathArc(ctx, element) { - const {x, y, startAngle, endAngle, pixelMargin} = element; - const outerRadius = Math.max(element.outerRadius - pixelMargin, 0); - const innerRadius = element.innerRadius + pixelMargin; +function pathArc(ctx, element, offset) { + const {x, y, startAngle: start, endAngle: end, pixelMargin, innerRadius: innerR} = element; + + const outerRadius = Math.max(element.outerRadius + offset - pixelMargin, 0); + const innerRadius = innerR > 0 ? innerR + offset + pixelMargin : 0; + const alpha = end - start; + const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius; + const angleOffset = (alpha - beta) / 2; + const startAngle = start + angleOffset; + const endAngle = end - angleOffset; const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius(element, innerRadius, outerRadius, endAngle - startAngle); const outerStartAdjustedRadius = outerRadius - outerStart; @@ -152,11 +158,11 @@ function pathArc(ctx, element) { ctx.closePath(); } -function drawArc(ctx, element) { +function drawArc(ctx, element, offset) { if (element.fullCircles) { element.endAngle = element.startAngle + TAU; - pathArc(ctx, element); + pathArc(ctx, element, offset); for (let i = 0; i < element.fullCircles; ++i) { ctx.fill(); @@ -166,7 +172,7 @@ function drawArc(ctx, element) { element.endAngle = element.startAngle + element.circumference % TAU; } - pathArc(ctx, element); + pathArc(ctx, element, offset); ctx.fill(); } @@ -200,7 +206,7 @@ function drawFullCircleBorders(ctx, element, inner) { } } -function drawBorder(ctx, element) { +function drawBorder(ctx, element, offset) { const {options} = element; const inner = options.borderAlign === 'inner'; @@ -224,7 +230,7 @@ function drawBorder(ctx, element) { clipArc(ctx, element); } - pathArc(ctx, element); + pathArc(ctx, element, offset); ctx.stroke(); } @@ -298,7 +304,7 @@ export default class ArcElement extends Element { draw(ctx) { const me = this; const options = me.options; - const offset = options.offset || 0; + const offset = (options.offset || 0) / 2; me.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0; me.fullCircles = Math.floor(me.circumference / TAU); @@ -308,16 +314,21 @@ export default class ArcElement extends Element { ctx.save(); - if (offset && me.circumference < TAU) { + let radiusOffset = 0; + if (offset) { + radiusOffset = offset / 2; const halfAngle = (me.startAngle + me.endAngle) / 2; - ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset); + ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset); + if (me.circumference >= PI) { + radiusOffset = offset; + } } ctx.fillStyle = options.backgroundColor; ctx.strokeStyle = options.borderColor; - drawArc(ctx, me); - drawBorder(ctx, me); + drawArc(ctx, me, radiusOffset); + drawBorder(ctx, me, radiusOffset); ctx.restore(); } diff --git a/test/fixtures/controller.doughnut/doughnut-offset.js b/test/fixtures/controller.doughnut/doughnut-offset.js new file mode 100644 index 000000000..b1ffd32e1 --- /dev/null +++ b/test/fixtures/controller.doughnut/doughnut-offset.js @@ -0,0 +1,18 @@ +module.exports = { + config: { + type: 'doughnut', + data: { + labels: ['Red', 'Blue', 'Yellow'], + datasets: [{ + data: [12, 4, 6], + backgroundColor: ['red', 'blue', 'yellow'] + }] + }, + options: { + offset: 40, + layout: { + padding: 50 + } + } + } +}; diff --git a/test/fixtures/controller.doughnut/doughnut-offset.png b/test/fixtures/controller.doughnut/doughnut-offset.png new file mode 100644 index 000000000..e4e23f6ec Binary files /dev/null and b/test/fixtures/controller.doughnut/doughnut-offset.png differ diff --git a/test/fixtures/controller.doughnut/pie-offset.js b/test/fixtures/controller.doughnut/pie-offset.js new file mode 100644 index 000000000..05689f4b4 --- /dev/null +++ b/test/fixtures/controller.doughnut/pie-offset.js @@ -0,0 +1,18 @@ +module.exports = { + config: { + type: 'pie', + data: { + labels: ['Red', 'Blue', 'Yellow'], + datasets: [{ + data: [12, 4, 6], + backgroundColor: ['red', 'blue', 'yellow'] + }] + }, + options: { + offset: 40, + layout: { + padding: 50 + } + } + } +}; diff --git a/test/fixtures/controller.doughnut/pie-offset.png b/test/fixtures/controller.doughnut/pie-offset.png new file mode 100644 index 000000000..c2cf18bf5 Binary files /dev/null and b/test/fixtures/controller.doughnut/pie-offset.png differ