@nasser-sw/fabric 7.0.0-beta1 → 7.0.1-beta10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/0 +0 -0
  2. package/debug/{konva → konva-master}/CHANGELOG.md +2 -1
  3. package/debug/{konva → konva-master}/README.md +7 -3
  4. package/debug/{konva → konva-master}/package.json +1 -1
  5. package/debug/{konva → konva-master}/release.sh +1 -4
  6. package/debug/{konva → konva-master}/src/Canvas.ts +37 -0
  7. package/debug/{konva → konva-master}/src/shapes/Text.ts +2 -2
  8. package/dist/index.js +2198 -272
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.js +1 -1
  11. package/dist/index.min.js.map +1 -1
  12. package/dist/index.min.mjs +1 -1
  13. package/dist/index.min.mjs.map +1 -1
  14. package/dist/index.mjs +2198 -272
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/index.node.cjs +2198 -272
  17. package/dist/index.node.cjs.map +1 -1
  18. package/dist/index.node.mjs +2198 -272
  19. package/dist/index.node.mjs.map +1 -1
  20. package/dist/package.json.min.mjs +1 -1
  21. package/dist/package.json.mjs +1 -1
  22. package/dist/src/shapes/Line.d.ts +33 -86
  23. package/dist/src/shapes/Line.d.ts.map +1 -1
  24. package/dist/src/shapes/Line.min.mjs +1 -1
  25. package/dist/src/shapes/Line.min.mjs.map +1 -1
  26. package/dist/src/shapes/Line.mjs +405 -159
  27. package/dist/src/shapes/Line.mjs.map +1 -1
  28. package/dist/src/shapes/Polyline.d.ts +7 -0
  29. package/dist/src/shapes/Polyline.d.ts.map +1 -1
  30. package/dist/src/shapes/Polyline.min.mjs +1 -1
  31. package/dist/src/shapes/Polyline.min.mjs.map +1 -1
  32. package/dist/src/shapes/Polyline.mjs +48 -16
  33. package/dist/src/shapes/Polyline.mjs.map +1 -1
  34. package/dist/src/shapes/Text/Text.d.ts +19 -0
  35. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  36. package/dist/src/shapes/Text/Text.min.mjs +1 -1
  37. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  38. package/dist/src/shapes/Text/Text.mjs +302 -16
  39. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  40. package/dist/src/shapes/Textbox.d.ts +56 -1
  41. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  42. package/dist/src/shapes/Textbox.min.mjs +1 -1
  43. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  44. package/dist/src/shapes/Textbox.mjs +633 -11
  45. package/dist/src/shapes/Textbox.mjs.map +1 -1
  46. package/dist/src/shapes/Triangle.d.ts +27 -2
  47. package/dist/src/shapes/Triangle.d.ts.map +1 -1
  48. package/dist/src/shapes/Triangle.min.mjs +1 -1
  49. package/dist/src/shapes/Triangle.min.mjs.map +1 -1
  50. package/dist/src/shapes/Triangle.mjs +72 -12
  51. package/dist/src/shapes/Triangle.mjs.map +1 -1
  52. package/dist/src/text/examples/arabicTextExample.d.ts +60 -0
  53. package/dist/src/text/examples/arabicTextExample.d.ts.map +1 -0
  54. package/dist/src/text/measure.d.ts +9 -0
  55. package/dist/src/text/measure.d.ts.map +1 -1
  56. package/dist/src/text/measure.min.mjs +1 -1
  57. package/dist/src/text/measure.min.mjs.map +1 -1
  58. package/dist/src/text/measure.mjs +175 -4
  59. package/dist/src/text/measure.mjs.map +1 -1
  60. package/dist/src/text/overlayEditor.d.ts +8 -0
  61. package/dist/src/text/overlayEditor.d.ts.map +1 -1
  62. package/dist/src/text/overlayEditor.min.mjs +1 -1
  63. package/dist/src/text/overlayEditor.min.mjs.map +1 -1
  64. package/dist/src/text/overlayEditor.mjs +395 -56
  65. package/dist/src/text/overlayEditor.mjs.map +1 -1
  66. package/dist/src/text/scriptUtils.d.ts +142 -0
  67. package/dist/src/text/scriptUtils.d.ts.map +1 -0
  68. package/dist/src/text/scriptUtils.min.mjs +2 -0
  69. package/dist/src/text/scriptUtils.min.mjs.map +1 -0
  70. package/dist/src/text/scriptUtils.mjs +212 -0
  71. package/dist/src/text/scriptUtils.mjs.map +1 -0
  72. package/dist/src/util/misc/cornerRadius.d.ts +70 -0
  73. package/dist/src/util/misc/cornerRadius.d.ts.map +1 -0
  74. package/dist/src/util/misc/cornerRadius.min.mjs +2 -0
  75. package/dist/src/util/misc/cornerRadius.min.mjs.map +1 -0
  76. package/dist/src/util/misc/cornerRadius.mjs +181 -0
  77. package/dist/src/util/misc/cornerRadius.mjs.map +1 -0
  78. package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
  79. package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
  80. package/dist-extensions/src/shapes/Line.d.ts +33 -86
  81. package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
  82. package/dist-extensions/src/shapes/Polyline.d.ts +7 -0
  83. package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
  84. package/dist-extensions/src/shapes/Text/Text.d.ts +19 -0
  85. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  86. package/dist-extensions/src/shapes/Textbox.d.ts +56 -1
  87. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  88. package/dist-extensions/src/shapes/Triangle.d.ts +27 -2
  89. package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
  90. package/dist-extensions/src/text/measure.d.ts +9 -0
  91. package/dist-extensions/src/text/measure.d.ts.map +1 -1
  92. package/dist-extensions/src/text/overlayEditor.d.ts +8 -0
  93. package/dist-extensions/src/text/overlayEditor.d.ts.map +1 -1
  94. package/dist-extensions/src/text/scriptUtils.d.ts +142 -0
  95. package/dist-extensions/src/text/scriptUtils.d.ts.map +1 -0
  96. package/dist-extensions/src/util/misc/cornerRadius.d.ts +70 -0
  97. package/dist-extensions/src/util/misc/cornerRadius.d.ts.map +1 -0
  98. package/fabric-test-editor.html +3552 -0
  99. package/fabric-test2.html +647 -0
  100. package/fabric.ts +182 -182
  101. package/fonts/STV Bold.ttf +0 -0
  102. package/fonts/STV Light.ttf +0 -0
  103. package/fonts/STV Regular.ttf +0 -0
  104. package/package.json +164 -164
  105. package/src/shapes/Line.ts +484 -157
  106. package/src/shapes/Polyline.ts +70 -29
  107. package/src/shapes/Text/Text.ts +317 -19
  108. package/src/shapes/Textbox.ts +663 -12
  109. package/src/shapes/Triangle.spec.ts +76 -0
  110. package/src/shapes/Triangle.ts +85 -15
  111. package/src/text/measure.ts +200 -50
  112. package/src/text/overlayEditor.ts +504 -94
  113. package/src/util/misc/cornerRadius.spec.ts +141 -0
  114. package/src/util/misc/cornerRadius.ts +269 -0
  115. /package/debug/{konva → konva-master}/LICENSE +0 -0
  116. /package/debug/{konva → konva-master}/gulpfile.mjs +0 -0
  117. /package/debug/{konva → konva-master}/resources/doc-includes/ContainerParams.txt +0 -0
  118. /package/debug/{konva → konva-master}/resources/doc-includes/NodeParams.txt +0 -0
  119. /package/debug/{konva → konva-master}/resources/doc-includes/ShapeParams.txt +0 -0
  120. /package/debug/{konva → konva-master}/resources/jsdoc.conf.json +0 -0
  121. /package/debug/{konva → konva-master}/rollup.config.mjs +0 -0
  122. /package/debug/{konva → konva-master}/src/Animation.ts +0 -0
  123. /package/debug/{konva → konva-master}/src/BezierFunctions.ts +0 -0
  124. /package/debug/{konva → konva-master}/src/Container.ts +0 -0
  125. /package/debug/{konva → konva-master}/src/Context.ts +0 -0
  126. /package/debug/{konva → konva-master}/src/Core.ts +0 -0
  127. /package/debug/{konva → konva-master}/src/DragAndDrop.ts +0 -0
  128. /package/debug/{konva → konva-master}/src/Factory.ts +0 -0
  129. /package/debug/{konva → konva-master}/src/FastLayer.ts +0 -0
  130. /package/debug/{konva → konva-master}/src/Global.ts +0 -0
  131. /package/debug/{konva → konva-master}/src/Group.ts +0 -0
  132. /package/debug/{konva → konva-master}/src/Layer.ts +0 -0
  133. /package/debug/{konva → konva-master}/src/Node.ts +0 -0
  134. /package/debug/{konva → konva-master}/src/PointerEvents.ts +0 -0
  135. /package/debug/{konva → konva-master}/src/Shape.ts +0 -0
  136. /package/debug/{konva → konva-master}/src/Stage.ts +0 -0
  137. /package/debug/{konva → konva-master}/src/Tween.ts +0 -0
  138. /package/debug/{konva → konva-master}/src/Util.ts +0 -0
  139. /package/debug/{konva → konva-master}/src/Validators.ts +0 -0
  140. /package/debug/{konva → konva-master}/src/_CoreInternals.ts +0 -0
  141. /package/debug/{konva → konva-master}/src/_FullInternals.ts +0 -0
  142. /package/debug/{konva → konva-master}/src/canvas-backend.ts +0 -0
  143. /package/debug/{konva → konva-master}/src/filters/Blur.ts +0 -0
  144. /package/debug/{konva → konva-master}/src/filters/Brighten.ts +0 -0
  145. /package/debug/{konva → konva-master}/src/filters/Brightness.ts +0 -0
  146. /package/debug/{konva → konva-master}/src/filters/Contrast.ts +0 -0
  147. /package/debug/{konva → konva-master}/src/filters/Emboss.ts +0 -0
  148. /package/debug/{konva → konva-master}/src/filters/Enhance.ts +0 -0
  149. /package/debug/{konva → konva-master}/src/filters/Grayscale.ts +0 -0
  150. /package/debug/{konva → konva-master}/src/filters/HSL.ts +0 -0
  151. /package/debug/{konva → konva-master}/src/filters/HSV.ts +0 -0
  152. /package/debug/{konva → konva-master}/src/filters/Invert.ts +0 -0
  153. /package/debug/{konva → konva-master}/src/filters/Kaleidoscope.ts +0 -0
  154. /package/debug/{konva → konva-master}/src/filters/Mask.ts +0 -0
  155. /package/debug/{konva → konva-master}/src/filters/Noise.ts +0 -0
  156. /package/debug/{konva → konva-master}/src/filters/Pixelate.ts +0 -0
  157. /package/debug/{konva → konva-master}/src/filters/Posterize.ts +0 -0
  158. /package/debug/{konva → konva-master}/src/filters/RGB.ts +0 -0
  159. /package/debug/{konva → konva-master}/src/filters/RGBA.ts +0 -0
  160. /package/debug/{konva → konva-master}/src/filters/Sepia.ts +0 -0
  161. /package/debug/{konva → konva-master}/src/filters/Solarize.ts +0 -0
  162. /package/debug/{konva → konva-master}/src/filters/Threshold.ts +0 -0
  163. /package/debug/{konva → konva-master}/src/index.ts +0 -0
  164. /package/debug/{konva → konva-master}/src/shapes/Arc.ts +0 -0
  165. /package/debug/{konva → konva-master}/src/shapes/Arrow.ts +0 -0
  166. /package/debug/{konva → konva-master}/src/shapes/Circle.ts +0 -0
  167. /package/debug/{konva → konva-master}/src/shapes/Ellipse.ts +0 -0
  168. /package/debug/{konva → konva-master}/src/shapes/Image.ts +0 -0
  169. /package/debug/{konva → konva-master}/src/shapes/Label.ts +0 -0
  170. /package/debug/{konva → konva-master}/src/shapes/Line.ts +0 -0
  171. /package/debug/{konva → konva-master}/src/shapes/Path.ts +0 -0
  172. /package/debug/{konva → konva-master}/src/shapes/Rect.ts +0 -0
  173. /package/debug/{konva → konva-master}/src/shapes/RegularPolygon.ts +0 -0
  174. /package/debug/{konva → konva-master}/src/shapes/Ring.ts +0 -0
  175. /package/debug/{konva → konva-master}/src/shapes/Sprite.ts +0 -0
  176. /package/debug/{konva → konva-master}/src/shapes/Star.ts +0 -0
  177. /package/debug/{konva → konva-master}/src/shapes/TextPath.ts +0 -0
  178. /package/debug/{konva → konva-master}/src/shapes/Transformer.ts +0 -0
  179. /package/debug/{konva → konva-master}/src/shapes/Wedge.ts +0 -0
  180. /package/debug/{konva → konva-master}/src/skia-backend.ts +0 -0
  181. /package/debug/{konva → konva-master}/src/types.ts +0 -0
  182. /package/debug/{konva → konva-master}/tsconfig.json +0 -0
  183. /package/debug/{konva → konva-master}/tsconfig.test.json +0 -0
@@ -0,0 +1,141 @@
1
+ import {
2
+ pointDistance,
3
+ normalizeVector,
4
+ angleBetweenVectors,
5
+ getMaxRadius,
6
+ calculateRoundedCorner,
7
+ applyCornerRadiusToPolygon,
8
+ generateRoundedPolygonPath,
9
+ } from './cornerRadius';
10
+
11
+ describe('cornerRadius utilities', () => {
12
+ describe('pointDistance', () => {
13
+ it('should calculate distance between two points correctly', () => {
14
+ expect(pointDistance({ x: 0, y: 0 }, { x: 3, y: 4 })).toBe(5);
15
+ expect(pointDistance({ x: 1, y: 1 }, { x: 1, y: 1 })).toBe(0);
16
+ expect(pointDistance({ x: 0, y: 0 }, { x: 1, y: 0 })).toBe(1);
17
+ });
18
+ });
19
+
20
+ describe('normalizeVector', () => {
21
+ it('should normalize vectors correctly', () => {
22
+ const result = normalizeVector({ x: 3, y: 4 });
23
+ expect(result.x).toBeCloseTo(0.6);
24
+ expect(result.y).toBeCloseTo(0.8);
25
+ });
26
+
27
+ it('should handle zero vector', () => {
28
+ const result = normalizeVector({ x: 0, y: 0 });
29
+ expect(result).toEqual({ x: 0, y: 0 });
30
+ });
31
+ });
32
+
33
+ describe('getMaxRadius', () => {
34
+ it('should return half of the shortest adjacent edge', () => {
35
+ const prevPoint = { x: 0, y: 0 };
36
+ const currentPoint = { x: 10, y: 0 };
37
+ const nextPoint = { x: 10, y: 5 };
38
+
39
+ expect(getMaxRadius(prevPoint, currentPoint, nextPoint)).toBe(2.5);
40
+ });
41
+ });
42
+
43
+ describe('calculateRoundedCorner', () => {
44
+ it('should calculate rounded corner data correctly', () => {
45
+ const prevPoint = { x: 0, y: 0 };
46
+ const currentPoint = { x: 10, y: 0 };
47
+ const nextPoint = { x: 10, y: 10 };
48
+ const radius = 2;
49
+
50
+ const result = calculateRoundedCorner(prevPoint, currentPoint, nextPoint, radius);
51
+
52
+ expect(result.corner).toEqual(currentPoint);
53
+ expect(result.actualRadius).toBe(radius);
54
+ expect(result.start.x).toBeCloseTo(8);
55
+ expect(result.start.y).toBeCloseTo(0);
56
+ expect(result.end.x).toBeCloseTo(10);
57
+ expect(result.end.y).toBeCloseTo(2);
58
+ });
59
+
60
+ it('should constrain radius to maximum allowed', () => {
61
+ const prevPoint = { x: 0, y: 0 };
62
+ const currentPoint = { x: 2, y: 0 };
63
+ const nextPoint = { x: 2, y: 2 };
64
+ const radius = 5; // Request larger radius than possible
65
+
66
+ const result = calculateRoundedCorner(prevPoint, currentPoint, nextPoint, radius);
67
+
68
+ expect(result.actualRadius).toBe(1); // Should be constrained to 1
69
+ });
70
+ });
71
+
72
+ describe('applyCornerRadiusToPolygon', () => {
73
+ it('should apply corner radius to a simple triangle', () => {
74
+ const points = [
75
+ { x: 0, y: 0 },
76
+ { x: 10, y: 0 },
77
+ { x: 5, y: 10 }
78
+ ];
79
+ const radius = 2;
80
+
81
+ const result = applyCornerRadiusToPolygon(points, radius);
82
+
83
+ expect(result).toHaveLength(3);
84
+ expect(result[0].actualRadius).toBeCloseTo(2);
85
+ expect(result[1].actualRadius).toBeCloseTo(2);
86
+ expect(result[2].actualRadius).toBeCloseTo(2);
87
+ });
88
+
89
+ it('should handle percentage-based radius', () => {
90
+ const points = [
91
+ { x: 0, y: 0 },
92
+ { x: 100, y: 0 },
93
+ { x: 100, y: 100 },
94
+ { x: 0, y: 100 }
95
+ ];
96
+ const radius = 10; // 10%
97
+
98
+ const result = applyCornerRadiusToPolygon(points, radius, true);
99
+
100
+ expect(result).toHaveLength(4);
101
+ expect(result[0].actualRadius).toBeCloseTo(10); // 10% of 100px = 10px
102
+ });
103
+
104
+ it('should throw error for polygons with less than 3 points', () => {
105
+ expect(() => {
106
+ applyCornerRadiusToPolygon([{ x: 0, y: 0 }, { x: 1, y: 1 }], 5);
107
+ }).toThrow('Polygon must have at least 3 points');
108
+ });
109
+ });
110
+
111
+ describe('generateRoundedPolygonPath', () => {
112
+ it('should generate valid SVG path data', () => {
113
+ const points = [
114
+ { x: 0, y: 0 },
115
+ { x: 10, y: 0 },
116
+ { x: 5, y: 10 }
117
+ ];
118
+ const roundedCorners = applyCornerRadiusToPolygon(points, 2);
119
+
120
+ const pathData = generateRoundedPolygonPath(roundedCorners, true);
121
+
122
+ expect(pathData).toContain('M '); // Should start with move command
123
+ expect(pathData).toContain('C '); // Should contain bezier curve commands
124
+ expect(pathData).toContain('L '); // Should contain line commands
125
+ expect(pathData).toContain('Z'); // Should end with close command for closed path
126
+ });
127
+
128
+ it('should generate open path when closed=false', () => {
129
+ const points = [
130
+ { x: 0, y: 0 },
131
+ { x: 10, y: 0 },
132
+ { x: 5, y: 10 }
133
+ ];
134
+ const roundedCorners = applyCornerRadiusToPolygon(points, 2);
135
+
136
+ const pathData = generateRoundedPolygonPath(roundedCorners, false);
137
+
138
+ expect(pathData).not.toContain('Z'); // Should not end with close command
139
+ });
140
+ });
141
+ });
@@ -0,0 +1,269 @@
1
+ import type { XY } from '../../Point';
2
+ import { Point } from '../../Point';
3
+ import { kRect } from '../../constants';
4
+
5
+ export interface CornerRadiusOptions {
6
+ /**
7
+ * Corner radius value
8
+ */
9
+ radius: number;
10
+ /**
11
+ * Whether to apply radius as percentage of the smallest dimension
12
+ */
13
+ radiusAsPercentage?: boolean;
14
+ }
15
+
16
+ export interface RoundedCornerPoint {
17
+ /**
18
+ * Original corner point
19
+ */
20
+ corner: XY;
21
+ /**
22
+ * Start point of the rounded corner arc
23
+ */
24
+ start: XY;
25
+ /**
26
+ * End point of the rounded corner arc
27
+ */
28
+ end: XY;
29
+ /**
30
+ * First control point for bezier curve
31
+ */
32
+ cp1: XY;
33
+ /**
34
+ * Second control point for bezier curve
35
+ */
36
+ cp2: XY;
37
+ /**
38
+ * Actual radius used (may be different from requested if constrained)
39
+ */
40
+ actualRadius: number;
41
+ }
42
+
43
+ /**
44
+ * Calculate the distance between two points
45
+ */
46
+ export function pointDistance(p1: XY, p2: XY): number {
47
+ return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
48
+ }
49
+
50
+ /**
51
+ * Normalize a vector
52
+ */
53
+ export function normalizeVector(vector: XY): XY {
54
+ const length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
55
+ if (length === 0) return { x: 0, y: 0 };
56
+ return { x: vector.x / length, y: vector.y / length };
57
+ }
58
+
59
+ /**
60
+ * Calculate the angle between two vectors
61
+ */
62
+ export function angleBetweenVectors(v1: XY, v2: XY): number {
63
+ const dot = v1.x * v2.x + v1.y * v2.y;
64
+ const det = v1.x * v2.y - v1.y * v2.x;
65
+ return Math.atan2(det, dot);
66
+ }
67
+
68
+ /**
69
+ * Get the maximum allowed radius for a corner based on adjacent edge lengths
70
+ */
71
+ export function getMaxRadius(
72
+ prevPoint: XY,
73
+ currentPoint: XY,
74
+ nextPoint: XY,
75
+ ): number {
76
+ const dist1 = pointDistance(prevPoint, currentPoint);
77
+ const dist2 = pointDistance(currentPoint, nextPoint);
78
+ return Math.min(dist1, dist2) / 2;
79
+ }
80
+
81
+ /**
82
+ * Calculate rounded corner data for a single corner
83
+ */
84
+ export function calculateRoundedCorner(
85
+ prevPoint: XY,
86
+ currentPoint: XY,
87
+ nextPoint: XY,
88
+ radius: number,
89
+ ): RoundedCornerPoint {
90
+ // Calculate edge vectors
91
+ const edge1 = {
92
+ x: currentPoint.x - prevPoint.x,
93
+ y: currentPoint.y - prevPoint.y,
94
+ };
95
+ const edge2 = {
96
+ x: nextPoint.x - currentPoint.x,
97
+ y: nextPoint.y - currentPoint.y,
98
+ };
99
+
100
+ // Normalize edge vectors
101
+ const norm1 = normalizeVector(edge1);
102
+ const norm2 = normalizeVector(edge2);
103
+
104
+ // Calculate the maximum allowed radius
105
+ const maxRadius = getMaxRadius(prevPoint, currentPoint, nextPoint);
106
+ const actualRadius = Math.min(radius, maxRadius);
107
+
108
+ // Calculate start and end points of the rounded corner
109
+ const startPoint = {
110
+ x: currentPoint.x - norm1.x * actualRadius,
111
+ y: currentPoint.y - norm1.y * actualRadius,
112
+ };
113
+
114
+ const endPoint = {
115
+ x: currentPoint.x + norm2.x * actualRadius,
116
+ y: currentPoint.y + norm2.y * actualRadius,
117
+ };
118
+
119
+ // Calculate control points for bezier curve
120
+ // Using the magic number kRect for optimal circular approximation
121
+ const controlOffset = actualRadius * kRect;
122
+
123
+ const cp1 = {
124
+ x: startPoint.x + norm1.x * controlOffset,
125
+ y: startPoint.y + norm1.y * controlOffset,
126
+ };
127
+
128
+ const cp2 = {
129
+ x: endPoint.x - norm2.x * controlOffset,
130
+ y: endPoint.y - norm2.y * controlOffset,
131
+ };
132
+
133
+ return {
134
+ corner: currentPoint,
135
+ start: startPoint,
136
+ end: endPoint,
137
+ cp1,
138
+ cp2,
139
+ actualRadius,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Apply corner radius to a polygon defined by points
145
+ */
146
+ export function applyCornerRadiusToPolygon(
147
+ points: XY[],
148
+ radius: number,
149
+ radiusAsPercentage = false,
150
+ ): RoundedCornerPoint[] {
151
+ if (points.length < 3) {
152
+ throw new Error('Polygon must have at least 3 points');
153
+ }
154
+
155
+ // Calculate bounding box if radius is percentage-based
156
+ let actualRadius = radius;
157
+ if (radiusAsPercentage) {
158
+ const minX = Math.min(...points.map((p) => p.x));
159
+ const maxX = Math.max(...points.map((p) => p.x));
160
+ const minY = Math.min(...points.map((p) => p.y));
161
+ const maxY = Math.max(...points.map((p) => p.y));
162
+ const width = maxX - minX;
163
+ const height = maxY - minY;
164
+ const minDimension = Math.min(width, height);
165
+ actualRadius = (radius / 100) * minDimension;
166
+ }
167
+
168
+ const roundedCorners: RoundedCornerPoint[] = [];
169
+
170
+ for (let i = 0; i < points.length; i++) {
171
+ const prevIndex = (i - 1 + points.length) % points.length;
172
+ const nextIndex = (i + 1) % points.length;
173
+
174
+ const prevPoint = points[prevIndex];
175
+ const currentPoint = points[i];
176
+ const nextPoint = points[nextIndex];
177
+
178
+ const roundedCorner = calculateRoundedCorner(
179
+ prevPoint,
180
+ currentPoint,
181
+ nextPoint,
182
+ actualRadius,
183
+ );
184
+
185
+ roundedCorners.push(roundedCorner);
186
+ }
187
+
188
+ return roundedCorners;
189
+ }
190
+
191
+ /**
192
+ * Render a rounded polygon to a canvas context
193
+ */
194
+ export function renderRoundedPolygon(
195
+ ctx: CanvasRenderingContext2D,
196
+ roundedCorners: RoundedCornerPoint[],
197
+ closed = true,
198
+ ) {
199
+ if (roundedCorners.length === 0) return;
200
+
201
+ ctx.beginPath();
202
+
203
+ // Start at the first corner's start point
204
+ const firstCorner = roundedCorners[0];
205
+ ctx.moveTo(firstCorner.start.x, firstCorner.start.y);
206
+
207
+ for (let i = 0; i < roundedCorners.length; i++) {
208
+ const corner = roundedCorners[i];
209
+ const nextIndex = (i + 1) % roundedCorners.length;
210
+ const nextCorner = roundedCorners[nextIndex];
211
+
212
+ // Draw the rounded corner using bezier curve
213
+ ctx.bezierCurveTo(
214
+ corner.cp1.x,
215
+ corner.cp1.y,
216
+ corner.cp2.x,
217
+ corner.cp2.y,
218
+ corner.end.x,
219
+ corner.end.y,
220
+ );
221
+
222
+ // Draw line to next corner's start point (if not the last segment in open path)
223
+ if (i < roundedCorners.length - 1 || closed) {
224
+ ctx.lineTo(nextCorner.start.x, nextCorner.start.y);
225
+ }
226
+ }
227
+
228
+ if (closed) {
229
+ ctx.closePath();
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Generate SVG path data for a rounded polygon
235
+ */
236
+ export function generateRoundedPolygonPath(
237
+ roundedCorners: RoundedCornerPoint[],
238
+ closed = true,
239
+ ): string {
240
+ if (roundedCorners.length === 0) return '';
241
+
242
+ const pathData: string[] = [];
243
+ const firstCorner = roundedCorners[0];
244
+
245
+ // Move to first corner's start point
246
+ pathData.push(`M ${firstCorner.start.x} ${firstCorner.start.y}`);
247
+
248
+ for (let i = 0; i < roundedCorners.length; i++) {
249
+ const corner = roundedCorners[i];
250
+ const nextIndex = (i + 1) % roundedCorners.length;
251
+ const nextCorner = roundedCorners[nextIndex];
252
+
253
+ // Add bezier curve for the rounded corner
254
+ pathData.push(
255
+ `C ${corner.cp1.x} ${corner.cp1.y} ${corner.cp2.x} ${corner.cp2.y} ${corner.end.x} ${corner.end.y}`,
256
+ );
257
+
258
+ // Add line to next corner's start point (if not the last segment in open path)
259
+ if (i < roundedCorners.length - 1 || closed) {
260
+ pathData.push(`L ${nextCorner.start.x} ${nextCorner.start.y}`);
261
+ }
262
+ }
263
+
264
+ if (closed) {
265
+ pathData.push('Z');
266
+ }
267
+
268
+ return pathData.join(' ');
269
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes