From 5155a240a34fd165c844b1278c79d9321ba31c9a Mon Sep 17 00:00:00 2001 From: CadenH Date: Mon, 30 Jun 2025 01:24:21 -0700 Subject: [PATCH 1/4] add corner radius for regular polygon --- src/Util.ts | 42 +++++++++++++++++++++++++++++ src/shapes/RegularPolygon.ts | 45 ++++++++++++++++++++++++++++---- test/unit/RegularPolygon-test.ts | 28 ++++++++++++++++++++ 3 files changed, 110 insertions(+), 5 deletions(-) diff --git a/src/Util.ts b/src/Util.ts index ae5075a5..0891118b 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -1042,4 +1042,46 @@ export const Util = { context.lineTo(0, topLeft); context.arc(topLeft, topLeft, topLeft, Math.PI, (Math.PI * 3) / 2, false); }, + drawRoundedPolygonPath( + context: Context, + points: Vector2d[], + sides: number, + radius: number, + cornerRadius: number | number[] + ) { + for (let i = 0; i < sides; i++) { + const prev = points[(i - 1 + sides) % sides]; + const curr = points[i]; + const next = points[(i + 1) % sides]; + const vec1 = {x: curr.x - prev.x, y: curr.y - prev.y}; + const vec2 = {x: next.x - curr.x, y: next.y - curr.y}; + const len1 = Math.hypot(vec1.x, vec1.y); + const len2 = Math.hypot(vec2.x, vec2.y); + let currCornerRadius; + if (typeof cornerRadius === 'number') { + currCornerRadius = cornerRadius; + } else { + currCornerRadius = i < cornerRadius.length ? cornerRadius[i] : 0; + } + const maxCornerRadius = radius * Math.cos(Math.PI / sides); + // cornerRadius creates perfect circle at 1/2 radius + currCornerRadius = maxCornerRadius * Math.min(1, (currCornerRadius / radius) * 2); + const normalVec1 = {x: vec1.x / len1, y: vec1.y / len1}; + const normalVec2 = {x: vec2.x / len2, y: vec2.y / len2}; + const p1 = { + x: curr.x - normalVec1.x * currCornerRadius, + y: curr.y - normalVec1.y * currCornerRadius, + }; + const p2 = { + x: curr.x + normalVec2.x * currCornerRadius, + y: curr.y + normalVec2.y * currCornerRadius, + }; + if (i === 0) { + context.moveTo(p1.x, p1.y); + } else { + context.lineTo(p1.x, p1.y); + } + context.arcTo(curr.x, curr.y, p2.x, p2.y, currCornerRadius); + } + } }; diff --git a/src/shapes/RegularPolygon.ts b/src/shapes/RegularPolygon.ts index 6ade36cf..c1bb627d 100644 --- a/src/shapes/RegularPolygon.ts +++ b/src/shapes/RegularPolygon.ts @@ -1,13 +1,15 @@ import { Factory } from '../Factory'; import { Shape, ShapeConfig } from '../Shape'; import { GetSet, Vector2d } from '../types'; -import { getNumberValidator } from '../Validators'; +import { getNumberOrArrayOfNumbersValidator, getNumberValidator } from '../Validators'; import { _registerNode } from '../Global'; import { Context } from '../Context'; +import { Util } from '../Util'; export interface RegularPolygonConfig extends ShapeConfig { sides: number; radius: number; + cornerRadius?: number | number[]; } /** * RegularPolygon constructor. Examples include triangles, squares, pentagons, hexagons, etc. @@ -15,6 +17,7 @@ export interface RegularPolygonConfig extends ShapeConfig { * @memberof Konva * @augments Konva.Shape * @param {Object} config + * @param {Number} [config.cornerRadius] * @param {Number} config.sides * @param {Number} config.radius * @@shapeParams @@ -32,13 +35,20 @@ export interface RegularPolygonConfig extends ShapeConfig { */ export class RegularPolygon extends Shape { _sceneFunc(context: Context) { - const points = this._getPoints(); + const points = this._getPoints(), + radius = this.radius(), + sides = this.sides(), + cornerRadius = this.cornerRadius(); context.beginPath(); - context.moveTo(points[0].x, points[0].y); - for (let n = 1; n < points.length; n++) { - context.lineTo(points[n].x, points[n].y); + if (!cornerRadius) { + context.moveTo(points[0].x, points[0].y); + for (let n = 1; n < points.length; n++) { + context.lineTo(points[n].x, points[n].y); + } + } else { + Util.drawRoundedPolygonPath(context, points, sides, radius, cornerRadius); } context.closePath(); @@ -91,6 +101,7 @@ export class RegularPolygon extends Shape { radius: GetSet; sides: GetSet; + cornerRadius: GetSet; } RegularPolygon.prototype.className = 'RegularPolygon'; @@ -127,3 +138,27 @@ Factory.addGetterSetter(RegularPolygon, 'radius', 0, getNumberValidator()); * shape.sides(10); */ Factory.addGetterSetter(RegularPolygon, 'sides', 0, getNumberValidator()); + +/** + * get/set corner radius + * @method + * @name Konva.RegularPolygon#cornerRadius + * @param {Number} cornerRadius + * @returns {Number} + * @example + * // get corner radius + * var cornerRadius = rect.cornerRadius(); + * + * // set corner radius + * rect.cornerRadius(10); + * + * // set different corner radius values + * // top-left, top-right, bottom-right, bottom-left + * rect.cornerRadius([0, 10, 20, 30]); + */ +Factory.addGetterSetter( + RegularPolygon, + 'cornerRadius', + 0, + getNumberOrArrayOfNumbersValidator(4) +); diff --git a/test/unit/RegularPolygon-test.ts b/test/unit/RegularPolygon-test.ts index 8e996f80..1370eb33 100644 --- a/test/unit/RegularPolygon-test.ts +++ b/test/unit/RegularPolygon-test.ts @@ -5,6 +5,8 @@ import { Konva, cloneAndCompareLayer, assertAlmostEqual, + createCanvas, + compareLayerAndCanvas, } from './test-utils'; describe('RegularPolygon', function () { @@ -206,4 +208,30 @@ describe('RegularPolygon', function () { assertAlmostEqual(box.width, 91.60254037844388); assertAlmostEqual(box.height, 80.00000000000003); }); + + // ====================================================== + it('limit corner radius', function () { + var stage = addStage(); + var layer = new Konva.Layer(); + var poly = new Konva.RegularPolygon({ + x: 50, + y: 50, + sides: 5, + radius: 50, + fill: 'black', + cornerRadius: 25, + }); + + layer.add(poly); + stage.add(layer); + + // corner radius creates perfect circle at 1/2 radius + var canvas = createCanvas(); + var context = canvas.getContext('2d'); + context.beginPath(); + context.arc(100, 100, 50, 0, Math.PI * 2); + context.fillStyle = 'black'; + context.fill(); + compareLayerAndCanvas(layer, canvas, 100); + }); }); From 089766c7ae2f82bba17878a570937909d631c961 Mon Sep 17 00:00:00 2001 From: CadenH Date: Mon, 30 Jun 2025 13:10:28 -0700 Subject: [PATCH 2/4] fixed bugged test --- test/unit/RegularPolygon-test.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/unit/RegularPolygon-test.ts b/test/unit/RegularPolygon-test.ts index 1370eb33..ed1b50a7 100644 --- a/test/unit/RegularPolygon-test.ts +++ b/test/unit/RegularPolygon-test.ts @@ -213,25 +213,28 @@ describe('RegularPolygon', function () { it('limit corner radius', function () { var stage = addStage(); var layer = new Konva.Layer(); + const sides = 5, + radius = 50; var poly = new Konva.RegularPolygon({ - x: 50, - y: 50, - sides: 5, - radius: 50, + x: 100, + y: 100, + sides: sides, + radius: radius, fill: 'black', cornerRadius: 25, }); - + const resultCircleRadius = radius * Math.cos(Math.PI / sides); + layer.add(poly); stage.add(layer); - + // corner radius creates perfect circle at 1/2 radius var canvas = createCanvas(); var context = canvas.getContext('2d'); context.beginPath(); - context.arc(100, 100, 50, 0, Math.PI * 2); + context.arc(100, 100, resultCircleRadius, 0, Math.PI * 2); context.fillStyle = 'black'; context.fill(); - compareLayerAndCanvas(layer, canvas, 100); + compareLayerAndCanvas(layer, canvas, 200); }); }); From 1adf506a930ca9c1e4ab8c9c8c5a265eda7ec0cf Mon Sep 17 00:00:00 2001 From: CadenH Date: Mon, 30 Jun 2025 16:08:17 -0700 Subject: [PATCH 3/4] changed const to var --- test/unit/RegularPolygon-test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/unit/RegularPolygon-test.ts b/test/unit/RegularPolygon-test.ts index ed1b50a7..1144d37c 100644 --- a/test/unit/RegularPolygon-test.ts +++ b/test/unit/RegularPolygon-test.ts @@ -213,8 +213,9 @@ describe('RegularPolygon', function () { it('limit corner radius', function () { var stage = addStage(); var layer = new Konva.Layer(); - const sides = 5, - radius = 50; + var sides = 5; + var radius = 50; + var poly = new Konva.RegularPolygon({ x: 100, y: 100, @@ -223,7 +224,7 @@ describe('RegularPolygon', function () { fill: 'black', cornerRadius: 25, }); - const resultCircleRadius = radius * Math.cos(Math.PI / sides); + var resultCircleRadius = radius * Math.cos(Math.PI / sides); layer.add(poly); stage.add(layer); From 18956ae62b680f5ce13eda4ec8bb7657f9b65668 Mon Sep 17 00:00:00 2001 From: CadenH Date: Wed, 23 Jul 2025 10:34:30 -0700 Subject: [PATCH 4/4] Fixed negative polygon radius corner radius bug --- src/Util.ts | 1 + src/shapes/RegularPolygon.ts | 9 ++++----- test/unit/RegularPolygon-test.ts | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Util.ts b/src/Util.ts index 0891118b..dcebf4f7 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -1049,6 +1049,7 @@ export const Util = { radius: number, cornerRadius: number | number[] ) { + radius = Math.abs(radius); for (let i = 0; i < sides; i++) { const prev = points[(i - 1 + sides) % sides]; const curr = points[i]; diff --git a/src/shapes/RegularPolygon.ts b/src/shapes/RegularPolygon.ts index c1bb627d..c7e35519 100644 --- a/src/shapes/RegularPolygon.ts +++ b/src/shapes/RegularPolygon.ts @@ -147,14 +147,13 @@ Factory.addGetterSetter(RegularPolygon, 'sides', 0, getNumberValidator()); * @returns {Number} * @example * // get corner radius - * var cornerRadius = rect.cornerRadius(); + * var cornerRadius = poly.cornerRadius(); * * // set corner radius - * rect.cornerRadius(10); + * poly.cornerRadius(10); * - * // set different corner radius values - * // top-left, top-right, bottom-right, bottom-left - * rect.cornerRadius([0, 10, 20, 30]); + * // set different corner radius values (pentagon) + * poly.cornerRadius([0, 10, 20, 30, 40]); */ Factory.addGetterSetter( RegularPolygon, diff --git a/test/unit/RegularPolygon-test.ts b/test/unit/RegularPolygon-test.ts index 1144d37c..30f481d5 100644 --- a/test/unit/RegularPolygon-test.ts +++ b/test/unit/RegularPolygon-test.ts @@ -238,4 +238,29 @@ describe('RegularPolygon', function () { context.fill(); compareLayerAndCanvas(layer, canvas, 200); }); + + // ====================================================== + it('negative polygon radius with cornerRadius', function () { + var stage = addStage(); + var layer = new Konva.Layer(); + + var poly = new Konva.RegularPolygon({ + x: 100, + y: 100, + sides: 5, + radius: -100, + fill: 'black', + cornerRadius: 20, + }); + + layer.add(poly); + stage.add(layer); + layer.draw(); + + var trace = layer.getContext().getTrace(); + assert.equal( + trace, + 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();moveTo(26.18,80.979);arcTo(0,100,-26.18,80.979,32.361);lineTo(-68.925,49.923);arcTo(-95.106,30.902,-85.106,0.125,32.361);lineTo(-68.779,-50.125);arcTo(-58.779,-80.902,-26.418,-80.902,32.361);lineTo(26.418,-80.902);arcTo(58.779,-80.902,68.779,-50.125,32.361);lineTo(85.106,0.125);arcTo(95.106,30.902,68.925,49.923,32.361);closePath();fillStyle=black;fill();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();moveTo(26.18,80.979);arcTo(0,100,-26.18,80.979,32.361);lineTo(-68.925,49.923);arcTo(-95.106,30.902,-85.106,0.125,32.361);lineTo(-68.779,-50.125);arcTo(-58.779,-80.902,-26.418,-80.902,32.361);lineTo(26.418,-80.902);arcTo(58.779,-80.902,68.779,-50.125,32.361);lineTo(85.106,0.125);arcTo(95.106,30.902,68.925,49.923,32.361);closePath();fillStyle=black;fill();restore();' + ); + }); });