]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Enhance the rounded rectangle implementation (#5597) 5610/head
authorSimon Brunel <simonbrunel@users.noreply.github.com>
Tue, 26 Jun 2018 15:58:32 +0000 (17:58 +0200)
committerGitHub <noreply@github.com>
Tue, 26 Jun 2018 15:58:32 +0000 (17:58 +0200)
Use `arcTo` instead of `quadraticCurveTo` (both methods have the same compatibility level) because it generates better results when the final rect is a circle but also when it's actually a rectangle and not a square. This change is needed by the datalabels plugin where the user can configure the `borderRadius` and thus generate circle from a rounded rectangle.

src/helpers/helpers.canvas.js
test/jasmine.context.js
test/specs/element.point.tests.js
test/specs/helpers.canvas.tests.js

index 13b110268b92687ca8ad428786148c0e1707f643..4bfae9c48fd89eaa60ed260cdee417c79dac0346 100644 (file)
@@ -27,18 +27,20 @@ var exports = module.exports = {
         */
        roundedRect: function(ctx, x, y, width, height, radius) {
                if (radius) {
-                       var rx = Math.min(radius, width / 2);
-                       var ry = Math.min(radius, height / 2);
-
-                       ctx.moveTo(x + rx, y);
-                       ctx.lineTo(x + width - rx, y);
-                       ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
-                       ctx.lineTo(x + width, y + height - ry);
-                       ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
-                       ctx.lineTo(x + rx, y + height);
-                       ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
-                       ctx.lineTo(x, y + ry);
-                       ctx.quadraticCurveTo(x, y, x + rx, y);
+                       // NOTE(SB) `epsilon` helps to prevent minor artifacts appearing
+                       // on Chrome when `r` is exactly half the height or the width.
+                       var epsilon = 0.0000001;
+                       var r = Math.min(radius, (height / 2) - epsilon, (width / 2) - epsilon);
+
+                       ctx.moveTo(x + r, y);
+                       ctx.lineTo(x + width - r, y);
+                       ctx.arcTo(x + width, y, x + width, y + r, r);
+                       ctx.lineTo(x + width, y + height - r);
+                       ctx.arcTo(x + width, y + height, x + width - r, y + height, r);
+                       ctx.lineTo(x + r, y + height);
+                       ctx.arcTo(x, y + height, x, y + height - r, r);
+                       ctx.lineTo(x, y + r);
+                       ctx.arcTo(x, y, x + r, y, r);
                } else {
                        ctx.rect(x, y, width, height);
                }
@@ -89,7 +91,13 @@ var exports = module.exports = {
                        var topY = y - offset;
                        var sideSize = Math.SQRT2 * radius;
                        ctx.beginPath();
-                       this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
+
+                       // NOTE(SB) the rounded rect implementation changed to use `arcTo`
+                       // instead of `quadraticCurveTo` since it generates better results
+                       // when rect is almost a circle. 0.425 (instead of 0.5) produces
+                       // results visually closer to the previous impl.
+                       this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius * 0.425);
+
                        ctx.closePath();
                        ctx.fill();
                        break;
index 8f4171fee4d57427c157820efdc7f6018e26ae6f..3497c7219181c7c955501ce3100fbf88ec0b603c 100644 (file)
@@ -75,6 +75,7 @@ Context.prototype._initMethods = function() {
        var me = this;
        var methods = {
                arc: function() {},
+               arcTo: function() {},
                beginPath: function() {},
                bezierCurveTo: function() {},
                clearRect: function() {},
index f09b912d3c0288b4f952cb10d4a9fcc2fed5f7e8..0321112fb127ee9f211d4123d1bd71fa475f05c3 100644 (file)
@@ -222,7 +222,7 @@ describe('Point element tests', function() {
                        15 - offset,
                        Math.SQRT2 * 2,
                        Math.SQRT2 * 2,
-                       2 / 2
+                       2 * 0.425
                );
                expect(mockContext.getCalls()).toContain(
                        jasmine.objectContaining({
index 81326529b49d80539c239d1c72c87856919f290b..0c20628d456ef7a1c71741af142260371e2bdafd 100644 (file)
@@ -30,13 +30,13 @@ describe('Chart.helpers.canvas', function() {
                        expect(context.getCalls()).toEqual([
                                {name: 'moveTo', args: [15, 20]},
                                {name: 'lineTo', args: [35, 20]},
-                               {name: 'quadraticCurveTo', args: [40, 20, 40, 25]},
+                               {name: 'arcTo', args: [40, 20, 40, 25, 5]},
                                {name: 'lineTo', args: [40, 55]},
-                               {name: 'quadraticCurveTo', args: [40, 60, 35, 60]},
+                               {name: 'arcTo', args: [40, 60, 35, 60, 5]},
                                {name: 'lineTo', args: [15, 60]},
-                               {name: 'quadraticCurveTo', args: [10, 60, 10, 55]},
+                               {name: 'arcTo', args: [10, 60, 10, 55, 5]},
                                {name: 'lineTo', args: [10, 25]},
-                               {name: 'quadraticCurveTo', args: [10, 20, 15, 20]}
+                               {name: 'arcTo', args: [10, 20, 15, 20, 5]}
                        ]);
                });
                it('should optimize path if radius is 0', function() {