@elixpo/lixsketch 5.4.7 → 5.5.1

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 (218) hide show
  1. package/dist/react/AIRenderer-M4LMJ66J.js +1105 -0
  2. package/dist/react/AIRenderer-M4LMJ66J.js.map +7 -0
  3. package/dist/react/Arrow-BJDAXPMH.js +8 -0
  4. package/dist/react/Arrow-BJDAXPMH.js.map +7 -0
  5. package/dist/react/Circle-KPG7TKIX.js +8 -0
  6. package/dist/react/Circle-KPG7TKIX.js.map +7 -0
  7. package/dist/react/CodeShape-K4DIZAIV.js +8 -0
  8. package/dist/react/CodeShape-K4DIZAIV.js.map +7 -0
  9. package/dist/react/CopyPaste-4CKJRHJ3.js +569 -0
  10. package/dist/react/CopyPaste-4CKJRHJ3.js.map +7 -0
  11. package/dist/react/EraserTrail-PA6IQLQN.js +20 -0
  12. package/dist/react/EraserTrail-PA6IQLQN.js.map +7 -0
  13. package/dist/react/EventDispatcher-SRFH6LQZ.js +463 -0
  14. package/dist/react/EventDispatcher-SRFH6LQZ.js.map +7 -0
  15. package/dist/react/Frame-6UR5XPAX.js +10 -0
  16. package/dist/react/Frame-6UR5XPAX.js.map +7 -0
  17. package/dist/react/FreehandStroke-CK24NUT3.js +8 -0
  18. package/dist/react/FreehandStroke-CK24NUT3.js.map +7 -0
  19. package/dist/react/GraphEngine-QE5B2PMR.js +340 -0
  20. package/dist/react/GraphEngine-QE5B2PMR.js.map +7 -0
  21. package/dist/react/GraphMathParser-2WMTY4IE.js +10 -0
  22. package/dist/react/GraphMathParser-2WMTY4IE.js.map +7 -0
  23. package/dist/react/IconShape-KMIA6T2H.js +10 -0
  24. package/dist/react/IconShape-KMIA6T2H.js.map +7 -0
  25. package/dist/react/ImageShape-CHHGBXUQ.js +10 -0
  26. package/dist/react/ImageShape-CHHGBXUQ.js.map +7 -0
  27. package/dist/react/LayerOrder-XCHF4MSS.js +66 -0
  28. package/dist/react/LayerOrder-XCHF4MSS.js.map +7 -0
  29. package/dist/react/Line-2KOQEEGA.js +10 -0
  30. package/dist/react/Line-2KOQEEGA.js.map +7 -0
  31. package/dist/react/LixScriptParser-JYWJX375.js +1033 -0
  32. package/dist/react/LixScriptParser-JYWJX375.js.map +7 -0
  33. package/dist/react/MermaidFlowchartRenderer-4QBAZ5MB.js +412 -0
  34. package/dist/react/MermaidFlowchartRenderer-4QBAZ5MB.js.map +7 -0
  35. package/dist/react/MermaidSequenceParser-UKG2NSZY.js +8 -0
  36. package/dist/react/MermaidSequenceParser-UKG2NSZY.js.map +7 -0
  37. package/dist/react/MermaidSequenceRenderer-HPLHY5II.js +387 -0
  38. package/dist/react/MermaidSequenceRenderer-HPLHY5II.js.map +7 -0
  39. package/dist/react/Rectangle-6FE7RN6W.js +8 -0
  40. package/dist/react/Rectangle-6FE7RN6W.js.map +7 -0
  41. package/dist/react/ResizeCode-WAMDTKSW.js +2 -0
  42. package/dist/react/ResizeCode-WAMDTKSW.js.map +7 -0
  43. package/dist/react/ResizeShapes-LKEQTITP.js +250 -0
  44. package/dist/react/ResizeShapes-LKEQTITP.js.map +7 -0
  45. package/dist/react/SceneSerializer-4U4B24JM.js +739 -0
  46. package/dist/react/SceneSerializer-4U4B24JM.js.map +7 -0
  47. package/dist/react/Selection-VVUHY5YJ.js +34 -0
  48. package/dist/react/Selection-VVUHY5YJ.js.map +7 -0
  49. package/dist/react/SketchEngine-YA4RG3KX.js +413 -0
  50. package/dist/react/SketchEngine-YA4RG3KX.js.map +7 -0
  51. package/dist/react/TextShape-54ZJ66E3.js +8 -0
  52. package/dist/react/TextShape-54ZJ66E3.js.map +7 -0
  53. package/dist/react/UndoRedo-M2JZUBKN.js +30 -0
  54. package/dist/react/UndoRedo-M2JZUBKN.js.map +7 -0
  55. package/dist/react/ZoomPan-U3TXC5AJ.js +186 -0
  56. package/dist/react/ZoomPan-U3TXC5AJ.js.map +7 -0
  57. package/dist/react/arrowTool-CWEMIRXK.js +17 -0
  58. package/dist/react/arrowTool-CWEMIRXK.js.map +7 -0
  59. package/dist/react/boxicons-5JNJQPUP.woff +0 -0
  60. package/dist/react/boxicons-6NVWQSCQ.eot +0 -0
  61. package/dist/react/boxicons-AQ5N6WP7.woff2 +0 -0
  62. package/dist/react/boxicons-GDR4JU3X.ttf +0 -0
  63. package/dist/react/boxicons-NNFHLMOR.svg +1660 -0
  64. package/dist/react/chunk-2OQXI7QB.js +564 -0
  65. package/dist/react/chunk-2OQXI7QB.js.map +7 -0
  66. package/dist/react/chunk-5OUBTHHO.js +187 -0
  67. package/dist/react/chunk-5OUBTHHO.js.map +7 -0
  68. package/dist/react/chunk-6PFXZKL2.js +628 -0
  69. package/dist/react/chunk-6PFXZKL2.js.map +7 -0
  70. package/dist/react/chunk-6SFNWGGZ.js +453 -0
  71. package/dist/react/chunk-6SFNWGGZ.js.map +7 -0
  72. package/dist/react/chunk-7TZSIR6V.js +210 -0
  73. package/dist/react/chunk-7TZSIR6V.js.map +7 -0
  74. package/dist/react/chunk-AJAZC37E.js +662 -0
  75. package/dist/react/chunk-AJAZC37E.js.map +7 -0
  76. package/dist/react/chunk-BU4D2WYQ.js +1213 -0
  77. package/dist/react/chunk-BU4D2WYQ.js.map +7 -0
  78. package/dist/react/chunk-BZE4CEBG.js +747 -0
  79. package/dist/react/chunk-BZE4CEBG.js.map +7 -0
  80. package/dist/react/chunk-CB2RANCB.js +212 -0
  81. package/dist/react/chunk-CB2RANCB.js.map +7 -0
  82. package/dist/react/chunk-DNAG7ML6.js +128 -0
  83. package/dist/react/chunk-DNAG7ML6.js.map +7 -0
  84. package/dist/react/chunk-HZWLPXFN.js +198 -0
  85. package/dist/react/chunk-HZWLPXFN.js.map +7 -0
  86. package/dist/react/chunk-I6HCCIXB.js +572 -0
  87. package/dist/react/chunk-I6HCCIXB.js.map +7 -0
  88. package/dist/react/chunk-IZ5OGWGW.js +235 -0
  89. package/dist/react/chunk-IZ5OGWGW.js.map +7 -0
  90. package/dist/react/chunk-JJCJNLNN.js +1490 -0
  91. package/dist/react/chunk-JJCJNLNN.js.map +7 -0
  92. package/dist/react/chunk-JQ3IRFDP.js +222 -0
  93. package/dist/react/chunk-JQ3IRFDP.js.map +7 -0
  94. package/dist/react/chunk-L5OARZJ7.js +1134 -0
  95. package/dist/react/chunk-L5OARZJ7.js.map +7 -0
  96. package/dist/react/chunk-LHRTXNVE.js +1600 -0
  97. package/dist/react/chunk-LHRTXNVE.js.map +7 -0
  98. package/dist/react/chunk-Q2GA4MBZ.js +1653 -0
  99. package/dist/react/chunk-Q2GA4MBZ.js.map +7 -0
  100. package/dist/react/chunk-QNSE5BAY.js +519 -0
  101. package/dist/react/chunk-QNSE5BAY.js.map +7 -0
  102. package/dist/react/chunk-QXFBG7OM.js +191 -0
  103. package/dist/react/chunk-QXFBG7OM.js.map +7 -0
  104. package/dist/react/chunk-T6ZVV5ZO.js +714 -0
  105. package/dist/react/chunk-T6ZVV5ZO.js.map +7 -0
  106. package/dist/react/chunk-URB6MY46.js +974 -0
  107. package/dist/react/chunk-URB6MY46.js.map +7 -0
  108. package/dist/react/chunk-VN62KLOC.js +931 -0
  109. package/dist/react/chunk-VN62KLOC.js.map +7 -0
  110. package/dist/react/chunk-VQVEPJSA.js +158 -0
  111. package/dist/react/chunk-VQVEPJSA.js.map +7 -0
  112. package/dist/react/chunk-XISEAGBV.js +1632 -0
  113. package/dist/react/chunk-XISEAGBV.js.map +7 -0
  114. package/dist/react/chunk-YKJUBFHE.js +575 -0
  115. package/dist/react/chunk-YKJUBFHE.js.map +7 -0
  116. package/dist/react/circleTool-NUATSEI2.js +14 -0
  117. package/dist/react/circleTool-NUATSEI2.js.map +7 -0
  118. package/dist/react/codeTool-MHCB2FNB.js +38 -0
  119. package/dist/react/codeTool-MHCB2FNB.js.map +7 -0
  120. package/dist/react/eraserTool-2GS2LQCS.js +103 -0
  121. package/dist/react/eraserTool-2GS2LQCS.js.map +7 -0
  122. package/dist/react/frameTool-PWVKKXSP.js +14 -0
  123. package/dist/react/frameTool-PWVKKXSP.js.map +7 -0
  124. package/dist/react/freehandTool-SLYGASVP.js +14 -0
  125. package/dist/react/freehandTool-SLYGASVP.js.map +7 -0
  126. package/dist/react/iconTool-GPZ7O7NH.js +18 -0
  127. package/dist/react/iconTool-GPZ7O7NH.js.map +7 -0
  128. package/dist/react/imageTool-D5R4XGEU.js +15 -0
  129. package/dist/react/imageTool-D5R4XGEU.js.map +7 -0
  130. package/dist/react/index.js +5349 -0
  131. package/dist/react/index.js.map +7 -0
  132. package/dist/react/laserTool-XVMV63AV.js +239 -0
  133. package/dist/react/laserTool-XVMV63AV.js.map +7 -0
  134. package/dist/react/lineTool-WNI5EMZ5.js +14 -0
  135. package/dist/react/lineTool-WNI5EMZ5.js.map +7 -0
  136. package/dist/react/lixCode-OJMVUXXI.ttf +0 -0
  137. package/dist/react/lixDefault-JEAPAUJL.ttf +0 -0
  138. package/dist/react/lixFancy-BPXYM5CQ.ttf +0 -0
  139. package/dist/react/lixFont-CPKEUDVM.woff2 +0 -0
  140. package/dist/react/rectangleTool-U7DZ6ZTA.js +14 -0
  141. package/dist/react/rectangleTool-U7DZ6ZTA.js.map +7 -0
  142. package/dist/react/styles.css +6627 -0
  143. package/dist/react/textTool-M7S6F3VC.js +21 -0
  144. package/dist/react/textTool-M7S6F3VC.js.map +7 -0
  145. package/package.json +39 -5
  146. package/src/EngineShortcuts.js +83 -27
  147. package/src/core/AIRenderer.js +30 -18
  148. package/src/core/GraphEngine.js +64 -41
  149. package/src/core/MermaidFlowchartRenderer.js +153 -88
  150. package/src/core/MermaidSequenceRenderer.js +168 -93
  151. package/src/core/SceneSerializer.js +86 -2
  152. package/src/core/Selection.js +119 -19
  153. package/src/core/UndoRedo.js +82 -15
  154. package/src/core/ZoomPan.js +15 -5
  155. package/src/index.js +14 -1
  156. package/src/react/LixSketchCanvas.jsx +347 -0
  157. package/src/react/components/AppMenu.jsx +482 -0
  158. package/src/react/components/Footer.jsx +85 -0
  159. package/src/react/components/Toolbar.jsx +140 -0
  160. package/src/react/components/canvas/CanvasLoadingOverlay.jsx +76 -0
  161. package/src/react/components/canvas/ContextMenu.jsx +466 -0
  162. package/src/react/components/canvas/FindBar.jsx +177 -0
  163. package/src/react/components/canvas/ImageSourcePicker.jsx +133 -0
  164. package/src/react/components/canvas/MultiSelectActions.jsx +120 -0
  165. package/src/react/components/canvas/SVGCanvas.jsx +145 -0
  166. package/src/react/components/canvas/SaveStatusIndicator.jsx +29 -0
  167. package/src/react/components/modals/CanvasPropertiesModal.jsx +358 -0
  168. package/src/react/components/modals/CommandPalette.jsx +307 -0
  169. package/src/react/components/modals/ExportImageModal.jsx +287 -0
  170. package/src/react/components/modals/GraphModal.jsx +174 -0
  171. package/src/react/components/modals/HelpModal.jsx +208 -0
  172. package/src/react/components/modals/LixScriptModal.jsx +130 -0
  173. package/src/react/components/modals/ShortcutsModal.jsx +125 -0
  174. package/src/react/components/sidebars/ArrowSidebar.jsx +173 -0
  175. package/src/react/components/sidebars/CircleSidebar.jsx +134 -0
  176. package/src/react/components/sidebars/FrameSidebar.jsx +263 -0
  177. package/src/react/components/sidebars/IconSidebar.jsx +279 -0
  178. package/src/react/components/sidebars/ImageSidebar.jsx +69 -0
  179. package/src/react/components/sidebars/LineSidebar.jsx +109 -0
  180. package/src/react/components/sidebars/PaintbrushSidebar.jsx +119 -0
  181. package/src/react/components/sidebars/RectangleSidebar.jsx +165 -0
  182. package/src/react/components/sidebars/ShapeSidebar.jsx +130 -0
  183. package/src/react/components/sidebars/TextSidebar.jsx +174 -0
  184. package/src/react/hooks/inertStores.js +58 -0
  185. package/src/react/hooks/useSketchEngine.js +76 -0
  186. package/src/react/hooks/useTranslation.js +92 -0
  187. package/src/react/index.js +41 -0
  188. package/src/react/store/useSketchStore.js +234 -0
  189. package/src/react/store/useUIStore.js +196 -0
  190. package/src/react/styles/arrowSidebar.css +263 -0
  191. package/src/react/styles/canvasStroke.css +47 -0
  192. package/src/react/styles/canvasTools.css +769 -0
  193. package/src/react/styles/circleSidebar.css +206 -0
  194. package/src/react/styles/fonts/fonts.css +29 -0
  195. package/src/react/styles/fonts/lixCode.ttf +0 -0
  196. package/src/react/styles/fonts/lixDefault.ttf +0 -0
  197. package/src/react/styles/fonts/lixDocs.ttf +0 -0
  198. package/src/react/styles/fonts/lixFancy.ttf +0 -0
  199. package/src/react/styles/fonts/lixFont.woff2 +0 -0
  200. package/src/react/styles/lineSidebar.css +207 -0
  201. package/src/react/styles/paintbrushSidebar.css +247 -0
  202. package/src/react/styles/squareSidebar.css +206 -0
  203. package/src/react/styles/writeText.css +149 -0
  204. package/src/react/styles.css +175 -0
  205. package/src/react/toolsEnum.js +20 -0
  206. package/src/react/utils/toast.js +18 -0
  207. package/src/shapes/Frame.js +7 -1
  208. package/src/shapes/Line.js +11 -5
  209. package/src/tools/arrowTool.js +30 -4
  210. package/src/tools/circleTool.js +33 -9
  211. package/src/tools/codeTool.js +5 -3
  212. package/src/tools/frameTool.js +47 -7
  213. package/src/tools/freehandTool.js +12 -4
  214. package/src/tools/iconTool.js +7 -1
  215. package/src/tools/imageTool.js +7 -1
  216. package/src/tools/lineTool.js +27 -3
  217. package/src/tools/rectangleTool.js +45 -8
  218. package/src/tools/textTool.js +23 -6
@@ -0,0 +1,1632 @@
1
+ "use client";
2
+
3
+ // src/shapes/Arrow.js
4
+ var isDragging = false;
5
+ var hoveredFrameArrow = null;
6
+ var dragOldPosArrow = null;
7
+ function getSVGCoordsFromMouse(e) {
8
+ const viewBox = svg.viewBox.baseVal;
9
+ const rect = svg.getBoundingClientRect();
10
+ const mouseX = e.clientX - rect.left;
11
+ const mouseY = e.clientY - rect.top;
12
+ const svgX = viewBox.x + mouseX / rect.width * viewBox.width;
13
+ const svgY = viewBox.y + mouseY / rect.height * viewBox.height;
14
+ return { x: svgX, y: svgY };
15
+ }
16
+ var Arrow = class _Arrow {
17
+ constructor(startPoint, endPoint, options = {}) {
18
+ this.startPoint = startPoint;
19
+ this.endPoint = endPoint;
20
+ this.options = {
21
+ stroke: options.stroke || "#fff",
22
+ strokeWidth: options.strokeWidth || 2,
23
+ strokeDasharray: options.arrowOutlineStyle === "dashed" ? "10,10" : options.arrowOutlineStyle === "dotted" ? "2,8" : "",
24
+ fill: "none",
25
+ ...options
26
+ };
27
+ this.arrowOutlineStyle = options.arrowOutlineStyle || "solid";
28
+ this.arrowHeadStyle = options.arrowHeadStyle || "default";
29
+ this.arrowHeadLength = parseFloat(options.arrowHeadLength || 15);
30
+ this.arrowHeadAngleDeg = parseFloat(options.arrowHeadAngleDeg || 30);
31
+ this.arrowCurved = options.arrowCurved !== void 0 ? options.arrowCurved : "straight";
32
+ this.arrowCurveAmount = options.arrowCurveAmount || 50;
33
+ this.controlPoint1 = options.controlPoint1 || null;
34
+ this.controlPoint2 = options.controlPoint2 || null;
35
+ this.attachedToStart = null;
36
+ this.attachedToEnd = null;
37
+ this.parentFrame = null;
38
+ this.element = null;
39
+ this.elbowX = options.elbowX !== void 0 ? options.elbowX : null;
40
+ this.group = document.createElementNS("http://www.w3.org/2000/svg", "g");
41
+ this.isSelected = false;
42
+ this.anchors = [];
43
+ this.shapeName = "arrow";
44
+ this.shapeID = `arrow-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 1e4)}`;
45
+ this.group.setAttribute("id", this.shapeID);
46
+ this.label = options.label || "";
47
+ this.labelElement = null;
48
+ this.labelColor = options.labelColor || "#e0e0e0";
49
+ this.labelFontSize = options.labelFontSize || 12;
50
+ this._isEditingLabel = false;
51
+ this._hitArea = null;
52
+ this._labelBg = null;
53
+ if (this.arrowCurved === "curved" && !this.controlPoint1 && !this.controlPoint2) {
54
+ this.initializeCurveControlPoints();
55
+ }
56
+ svg.appendChild(this.group);
57
+ this._setupLabelDblClick();
58
+ this.draw();
59
+ }
60
+ get x() {
61
+ return Math.min(this.startPoint.x, this.endPoint.x);
62
+ }
63
+ set x(value) {
64
+ const currentX = this.x;
65
+ const dx = value - currentX;
66
+ this.startPoint.x += dx;
67
+ this.endPoint.x += dx;
68
+ if (this.controlPoint1) this.controlPoint1.x += dx;
69
+ if (this.controlPoint2) this.controlPoint2.x += dx;
70
+ }
71
+ get y() {
72
+ return Math.min(this.startPoint.y, this.endPoint.y);
73
+ }
74
+ set y(value) {
75
+ const currentY = this.y;
76
+ const dy = value - currentY;
77
+ this.startPoint.y += dy;
78
+ this.endPoint.y += dy;
79
+ if (this.controlPoint1) this.controlPoint1.y += dy;
80
+ if (this.controlPoint2) this.controlPoint2.y += dy;
81
+ }
82
+ get width() {
83
+ return Math.abs(this.endPoint.x - this.startPoint.x);
84
+ }
85
+ set width(value) {
86
+ const centerX = (this.startPoint.x + this.endPoint.x) / 2;
87
+ const halfWidth = value / 2;
88
+ this.startPoint.x = centerX - halfWidth;
89
+ this.endPoint.x = centerX + halfWidth;
90
+ }
91
+ get height() {
92
+ return Math.abs(this.endPoint.y - this.startPoint.y);
93
+ }
94
+ set height(value) {
95
+ const centerY = (this.startPoint.y + this.endPoint.y) / 2;
96
+ const halfHeight = value / 2;
97
+ this.startPoint.y = centerY - halfHeight;
98
+ this.endPoint.y = centerY + halfHeight;
99
+ }
100
+ initializeCurveControlPoints() {
101
+ const dx = this.endPoint.x - this.startPoint.x;
102
+ const dy = this.endPoint.y - this.startPoint.y;
103
+ const distance = Math.sqrt(dx * dx + dy * dy);
104
+ if (distance === 0 || isNaN(distance)) {
105
+ this.controlPoint1 = { x: this.startPoint.x + 20, y: this.startPoint.y };
106
+ this.controlPoint2 = { x: this.endPoint.x - 20, y: this.endPoint.y };
107
+ return;
108
+ }
109
+ const perpX = -dy / distance;
110
+ const perpY = dx / distance;
111
+ const curveOffset = this.arrowCurveAmount;
112
+ const t1 = 0.33;
113
+ const point1X = this.startPoint.x + t1 * dx;
114
+ const point1Y = this.startPoint.y + t1 * dy;
115
+ this.controlPoint1 = {
116
+ x: point1X + perpX * curveOffset,
117
+ y: point1Y + perpY * curveOffset
118
+ };
119
+ const t2 = 0.67;
120
+ const point2X = this.startPoint.x + t2 * dx;
121
+ const point2Y = this.startPoint.y + t2 * dy;
122
+ this.controlPoint2 = {
123
+ x: point2X - perpX * curveOffset,
124
+ y: point2Y - perpY * curveOffset
125
+ };
126
+ }
127
+ _buildElbowPath(elbowX, shortenEnd) {
128
+ const x1 = this.startPoint.x, y1 = this.startPoint.y;
129
+ const x2 = this.endPoint.x, y2 = this.endPoint.y;
130
+ const r = Math.min(
131
+ Math.abs(this.arrowCurveAmount),
132
+ Math.abs(elbowX - x1) / 2,
133
+ Math.abs(x2 - elbowX) / 2,
134
+ Math.abs(y2 - y1) / 2
135
+ );
136
+ const dx = elbowX >= x1 ? 1 : -1;
137
+ const ex = x2 >= elbowX ? 1 : -1;
138
+ const dy = y2 >= y1 ? 1 : -1;
139
+ let endX = x2;
140
+ if (shortenEnd) {
141
+ endX = x2 - ex * (this.arrowHeadLength * 0.3);
142
+ }
143
+ if (r > 2 && Math.abs(elbowX - x1) > r * 2 && Math.abs(x2 - elbowX) > r * 2 && Math.abs(y2 - y1) > r * 2) {
144
+ return `M ${x1} ${y1} H ${elbowX - dx * r} Q ${elbowX} ${y1} ${elbowX} ${y1 + dy * r} V ${y2 - dy * r} Q ${elbowX} ${y2} ${elbowX + ex * r} ${y2} H ${endX}`;
145
+ }
146
+ return `M ${x1} ${y1} H ${elbowX} V ${y2} H ${endX}`;
147
+ }
148
+ selectArrow() {
149
+ this.isSelected = true;
150
+ disableAllSideBars();
151
+ arrowSideBar.classList.remove("hidden");
152
+ if (window.__showSidebarForShape) window.__showSidebarForShape("arrow");
153
+ this.updateSidebar();
154
+ this.draw();
155
+ }
156
+ removeSelection() {
157
+ this.anchors.forEach((anchor) => {
158
+ if (anchor.parentNode === this.group) {
159
+ this.group.removeChild(anchor);
160
+ }
161
+ });
162
+ this.anchors = [];
163
+ this.isSelected = false;
164
+ }
165
+ attachToShape(isStartPoint, shape, attachment) {
166
+ if (isStartPoint) {
167
+ this.attachedToStart = {
168
+ shape,
169
+ side: attachment.side,
170
+ offset: attachment.offset
171
+ };
172
+ this.startPoint = attachment.point;
173
+ } else {
174
+ this.attachedToEnd = {
175
+ shape,
176
+ side: attachment.side,
177
+ offset: attachment.offset
178
+ };
179
+ this.endPoint = attachment.point;
180
+ }
181
+ if (this.arrowCurved === "curved") {
182
+ this.initializeCurveControlPoints();
183
+ }
184
+ this.draw();
185
+ }
186
+ draw() {
187
+ if (this._arrowHeadEl) {
188
+ this._arrowHeadEl.remove();
189
+ this._arrowHeadEl = null;
190
+ }
191
+ const childrenToRemove = [];
192
+ const anchorSet = this._skipAnchors ? new Set(this.anchors) : null;
193
+ for (let i = 0; i < this.group.children.length; i++) {
194
+ const child = this.group.children[i];
195
+ if (child !== this.labelElement && child !== this._hitArea && child !== this._labelBg) {
196
+ if (anchorSet && anchorSet.has(child)) continue;
197
+ childrenToRemove.push(child);
198
+ }
199
+ }
200
+ childrenToRemove.forEach((child) => this.group.removeChild(child));
201
+ if (!this._skipAnchors) this.anchors = [];
202
+ let pathData;
203
+ let arrowEndPoint = this.endPoint;
204
+ const elbowX = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
205
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
206
+ if (isNaN(this.controlPoint1.x) || isNaN(this.controlPoint1.y) || isNaN(this.controlPoint2.x) || isNaN(this.controlPoint2.y)) {
207
+ this.initializeCurveControlPoints();
208
+ }
209
+ pathData = `M ${this.startPoint.x} ${this.startPoint.y} C ${this.controlPoint1.x} ${this.controlPoint1.y}, ${this.controlPoint2.x} ${this.controlPoint2.y}, ${this.endPoint.x} ${this.endPoint.y}`;
210
+ const t = 0.95;
211
+ const tangent = this.getCubicBezierTangent(t);
212
+ const curveAngle = Math.atan2(tangent.y, tangent.x);
213
+ if (this.arrowHeadStyle && this.arrowHeadStyle !== "none") {
214
+ arrowEndPoint = {
215
+ x: this.endPoint.x - this.arrowHeadLength * 0.3 * Math.cos(curveAngle),
216
+ y: this.endPoint.y - this.arrowHeadLength * 0.3 * Math.sin(curveAngle)
217
+ };
218
+ pathData = `M ${this.startPoint.x} ${this.startPoint.y} C ${this.controlPoint1.x} ${this.controlPoint1.y}, ${this.controlPoint2.x} ${this.controlPoint2.y}, ${arrowEndPoint.x} ${arrowEndPoint.y}`;
219
+ }
220
+ } else if (this.arrowCurved === "elbow") {
221
+ pathData = this._buildElbowPath(elbowX, false);
222
+ } else {
223
+ pathData = `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
224
+ }
225
+ const headAngle = this._getArrowAngle(elbowX);
226
+ if (this.arrowHeadStyle === "default") {
227
+ const pts = this._getArrowHeadPoints(headAngle);
228
+ pathData += ` M ${pts.x3} ${pts.y3} L ${this.endPoint.x} ${this.endPoint.y} L ${pts.x4} ${pts.y4}`;
229
+ } else {
230
+ this._renderArrowHead(headAngle);
231
+ }
232
+ const arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
233
+ arrowPath.setAttribute("d", pathData);
234
+ arrowPath.setAttribute("stroke", this.options.stroke);
235
+ arrowPath.setAttribute("stroke-width", this.options.strokeWidth);
236
+ arrowPath.setAttribute("fill", this.options.fill);
237
+ if (this.options.strokeDasharray) {
238
+ arrowPath.setAttribute("stroke-dasharray", this.options.strokeDasharray);
239
+ } else {
240
+ arrowPath.removeAttribute("stroke-dasharray");
241
+ }
242
+ arrowPath.setAttribute("stroke-linecap", "round");
243
+ arrowPath.setAttribute("stroke-linejoin", "round");
244
+ arrowPath.classList.add("arrow-path");
245
+ this.element = arrowPath;
246
+ this.group.appendChild(this.element);
247
+ {
248
+ let hitPathData;
249
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
250
+ hitPathData = `M ${this.startPoint.x} ${this.startPoint.y} C ${this.controlPoint1.x} ${this.controlPoint1.y}, ${this.controlPoint2.x} ${this.controlPoint2.y}, ${this.endPoint.x} ${this.endPoint.y}`;
251
+ } else if (this.arrowCurved === "elbow") {
252
+ const ex = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
253
+ hitPathData = `M ${this.startPoint.x} ${this.startPoint.y} L ${ex} ${this.startPoint.y} L ${ex} ${this.endPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
254
+ } else {
255
+ hitPathData = `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
256
+ }
257
+ if (!this._hitArea) {
258
+ this._hitArea = document.createElementNS("http://www.w3.org/2000/svg", "path");
259
+ this._hitArea.setAttribute("fill", "none");
260
+ this._hitArea.setAttribute("stroke", "transparent");
261
+ this._hitArea.setAttribute("stroke-width", "20");
262
+ this._hitArea.setAttribute("style", "pointer-events: stroke;");
263
+ this.group.appendChild(this._hitArea);
264
+ }
265
+ this._hitArea.setAttribute("d", hitPathData);
266
+ }
267
+ this._updateLabelElement();
268
+ if (this.isSelected) {
269
+ if (this._skipAnchors) {
270
+ this.updateSelectionControls();
271
+ } else {
272
+ this.addAnchors();
273
+ this.addAttachmentIndicators();
274
+ }
275
+ }
276
+ }
277
+ _buildFullPathData() {
278
+ let pathData;
279
+ const elbowX = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
280
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
281
+ if (isNaN(this.controlPoint1.x) || isNaN(this.controlPoint1.y) || isNaN(this.controlPoint2.x) || isNaN(this.controlPoint2.y)) {
282
+ this.initializeCurveControlPoints();
283
+ }
284
+ pathData = `M ${this.startPoint.x} ${this.startPoint.y} C ${this.controlPoint1.x} ${this.controlPoint1.y}, ${this.controlPoint2.x} ${this.controlPoint2.y}, ${this.endPoint.x} ${this.endPoint.y}`;
285
+ if (this.arrowHeadStyle && this.arrowHeadStyle !== "none") {
286
+ const t = 0.95;
287
+ const tangent = this.getCubicBezierTangent(t);
288
+ const angle = Math.atan2(tangent.y, tangent.x);
289
+ const arrowEndPoint = {
290
+ x: this.endPoint.x - this.arrowHeadLength * 0.3 * Math.cos(angle),
291
+ y: this.endPoint.y - this.arrowHeadLength * 0.3 * Math.sin(angle)
292
+ };
293
+ pathData = `M ${this.startPoint.x} ${this.startPoint.y} C ${this.controlPoint1.x} ${this.controlPoint1.y}, ${this.controlPoint2.x} ${this.controlPoint2.y}, ${arrowEndPoint.x} ${arrowEndPoint.y}`;
294
+ }
295
+ } else if (this.arrowCurved === "elbow") {
296
+ pathData = this._buildElbowPath(elbowX, false);
297
+ } else {
298
+ pathData = `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
299
+ }
300
+ if (this.arrowHeadStyle === "default") {
301
+ const angle = this._getArrowAngle(elbowX);
302
+ const pts = this._getArrowHeadPoints(angle);
303
+ pathData += ` M ${pts.x3} ${pts.y3} L ${this.endPoint.x} ${this.endPoint.y} L ${pts.x4} ${pts.y4}`;
304
+ }
305
+ return pathData;
306
+ }
307
+ _updatePathElement() {
308
+ if (!this.element) return;
309
+ this.element.setAttribute("d", this._buildFullPathData());
310
+ this._updateArrowHead();
311
+ }
312
+ _updateHitArea() {
313
+ if (!this._hitArea) return;
314
+ let hitPathData;
315
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
316
+ hitPathData = `M ${this.startPoint.x} ${this.startPoint.y} C ${this.controlPoint1.x} ${this.controlPoint1.y}, ${this.controlPoint2.x} ${this.controlPoint2.y}, ${this.endPoint.x} ${this.endPoint.y}`;
317
+ } else if (this.arrowCurved === "elbow") {
318
+ const ex = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
319
+ hitPathData = `M ${this.startPoint.x} ${this.startPoint.y} L ${ex} ${this.startPoint.y} L ${ex} ${this.endPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
320
+ } else {
321
+ hitPathData = `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
322
+ }
323
+ this._hitArea.setAttribute("d", hitPathData);
324
+ }
325
+ _getMidpoint() {
326
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
327
+ const p = this.getCubicBezierPoint(0.5);
328
+ return { x: p.x, y: p.y };
329
+ }
330
+ if (this.arrowCurved === "elbow") {
331
+ const ex = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
332
+ return { x: ex, y: (this.startPoint.y + this.endPoint.y) / 2 };
333
+ }
334
+ return {
335
+ x: (this.startPoint.x + this.endPoint.x) / 2,
336
+ y: (this.startPoint.y + this.endPoint.y) / 2
337
+ };
338
+ }
339
+ _updateAnchorPositions() {
340
+ if (!this.anchors || this.anchors.length === 0) return;
341
+ const anchorSize = 5 / currentZoom;
342
+ let anchorPositions = [this.startPoint, this.endPoint];
343
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
344
+ const midOnCurve = this.getCubicBezierPoint(0.5);
345
+ anchorPositions.push(midOnCurve);
346
+ } else if (this.arrowCurved === "elbow") {
347
+ const elbowXVal = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
348
+ const midY = (this.startPoint.y + this.endPoint.y) / 2;
349
+ anchorPositions.push({ x: elbowXVal, y: midY });
350
+ } else {
351
+ const arrowAngle = Math.atan2(this.endPoint.y - this.startPoint.y, this.endPoint.x - this.startPoint.x);
352
+ const arrowHeadClearance = this.arrowHeadLength + anchorSize - 10;
353
+ anchorPositions[1] = {
354
+ x: this.endPoint.x + arrowHeadClearance * Math.cos(arrowAngle),
355
+ y: this.endPoint.y + arrowHeadClearance * Math.sin(arrowAngle)
356
+ };
357
+ }
358
+ anchorPositions.forEach((point, index) => {
359
+ if (this.anchors[index]) {
360
+ this.anchors[index].setAttribute("cx", point.x);
361
+ this.anchors[index].setAttribute("cy", point.y);
362
+ }
363
+ });
364
+ }
365
+ _updateLabelElement() {
366
+ if (!this.label) {
367
+ if (this.labelElement && this.labelElement.parentNode === this.group) {
368
+ this.group.removeChild(this.labelElement);
369
+ this.labelElement = null;
370
+ }
371
+ if (this._labelBg && this._labelBg.parentNode === this.group) {
372
+ this.group.removeChild(this._labelBg);
373
+ this._labelBg = null;
374
+ }
375
+ return;
376
+ }
377
+ if (!this.labelElement) {
378
+ this.labelElement = document.createElementNS("http://www.w3.org/2000/svg", "text");
379
+ this.labelElement.setAttribute("class", "shape-label");
380
+ this.labelElement.setAttribute("pointer-events", "none");
381
+ }
382
+ const mid = this._getMidpoint();
383
+ this.labelElement.setAttribute("x", mid.x);
384
+ this.labelElement.setAttribute("y", mid.y);
385
+ this.labelElement.setAttribute("text-anchor", "middle");
386
+ this.labelElement.setAttribute("dominant-baseline", "central");
387
+ this.labelElement.setAttribute("fill", this.labelColor);
388
+ this.labelElement.setAttribute("font-size", this.labelFontSize);
389
+ this.labelElement.setAttribute("font-family", "lixFont, sans-serif");
390
+ this.labelElement.textContent = this.label;
391
+ const canvasBg = window.getComputedStyle(svg).backgroundColor || "#000";
392
+ if (!this._labelBg) {
393
+ this._labelBg = document.createElementNS("http://www.w3.org/2000/svg", "rect");
394
+ this._labelBg.setAttribute("pointer-events", "none");
395
+ }
396
+ this._labelBg.setAttribute("fill", canvasBg);
397
+ const hPadding = 4;
398
+ const vPadding = 1;
399
+ const charWidth = this.labelFontSize * 0.6;
400
+ const bgW = this.label.length * charWidth + hPadding * 2;
401
+ const bgH = this.labelFontSize + vPadding * 2;
402
+ this._labelBg.setAttribute("x", mid.x - bgW / 2);
403
+ this._labelBg.setAttribute("y", mid.y - bgH / 2);
404
+ this._labelBg.setAttribute("width", bgW);
405
+ this._labelBg.setAttribute("height", bgH);
406
+ this._labelBg.setAttribute("rx", 2);
407
+ if (this._labelBg.parentNode === this.group) this.group.removeChild(this._labelBg);
408
+ if (this.labelElement.parentNode === this.group) this.group.removeChild(this.labelElement);
409
+ this.group.appendChild(this._labelBg);
410
+ this.group.appendChild(this.labelElement);
411
+ }
412
+ _setupLabelDblClick() {
413
+ this.group.addEventListener("dblclick", (e) => {
414
+ e.stopPropagation();
415
+ e.preventDefault();
416
+ this.startLabelEdit();
417
+ });
418
+ }
419
+ startLabelEdit() {
420
+ if (this._isEditingLabel) return;
421
+ this._isEditingLabel = true;
422
+ if (this.labelElement) {
423
+ this.labelElement.setAttribute("visibility", "hidden");
424
+ }
425
+ if (this._labelBg) {
426
+ this._labelBg.setAttribute("visibility", "hidden");
427
+ }
428
+ const mid = this._getMidpoint();
429
+ const ctm = this.group.getScreenCTM();
430
+ if (!ctm) {
431
+ this._isEditingLabel = false;
432
+ return;
433
+ }
434
+ const pt = svg.createSVGPoint();
435
+ pt.x = mid.x;
436
+ pt.y = mid.y;
437
+ const screenMid = pt.matrixTransform(ctm);
438
+ const editW = 160;
439
+ const editH = 28;
440
+ const overlay = document.createElement("div");
441
+ overlay.className = "shape-label-editor";
442
+ overlay.style.cssText = `
443
+ position: fixed; z-index: 10000;
444
+ left: ${screenMid.x - editW / 2}px; top: ${screenMid.y - editH / 2}px;
445
+ width: ${editW}px; height: ${editH}px;
446
+ display: flex; align-items: center; justify-content: center;
447
+ pointer-events: auto;
448
+ `;
449
+ const canvasBg = window.getComputedStyle(svg).backgroundColor || "#000";
450
+ const input = document.createElement("div");
451
+ input.setAttribute("contenteditable", "true");
452
+ input.style.cssText = `
453
+ width: 100%; height: 100%;
454
+ background: ${canvasBg}; border: none;
455
+ outline: none; padding: 2px 6px;
456
+ color: ${this.labelColor}; font-size: ${this.labelFontSize}px;
457
+ font-family: lixFont, sans-serif; text-align: center;
458
+ display: flex; align-items: center; justify-content: center;
459
+ white-space: pre-wrap; word-break: break-word;
460
+ cursor: text;
461
+ `;
462
+ if (this.label) {
463
+ input.textContent = this.label;
464
+ } else {
465
+ input.innerHTML = "&nbsp;";
466
+ }
467
+ overlay.appendChild(input);
468
+ document.body.appendChild(overlay);
469
+ setTimeout(() => {
470
+ input.focus();
471
+ const sel = window.getSelection();
472
+ const range = document.createRange();
473
+ range.selectNodeContents(input);
474
+ sel.removeAllRanges();
475
+ sel.addRange(range);
476
+ }, 10);
477
+ const finishEdit = () => {
478
+ const newText = input.textContent.trim().replace(/\u00A0/g, "");
479
+ this.label = newText;
480
+ this._isEditingLabel = false;
481
+ if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
482
+ if (this.labelElement) this.labelElement.removeAttribute("visibility");
483
+ if (this._labelBg) this._labelBg.removeAttribute("visibility");
484
+ this.draw();
485
+ };
486
+ input.addEventListener("blur", finishEdit);
487
+ input.addEventListener("keydown", (e) => {
488
+ e.stopPropagation();
489
+ if (e.key === "Enter" && !e.shiftKey) {
490
+ e.preventDefault();
491
+ input.blur();
492
+ }
493
+ if (e.key === "Escape") {
494
+ input.textContent = this.label;
495
+ input.blur();
496
+ }
497
+ });
498
+ input.addEventListener("pointerdown", (e) => e.stopPropagation());
499
+ input.addEventListener("pointermove", (e) => e.stopPropagation());
500
+ input.addEventListener("pointerup", (e) => e.stopPropagation());
501
+ }
502
+ setLabel(text, color, fontSize) {
503
+ this.label = text || "";
504
+ if (color) this.labelColor = color;
505
+ if (fontSize) this.labelFontSize = fontSize;
506
+ this.draw();
507
+ }
508
+ getCubicBezierPoint(t) {
509
+ if (!this.controlPoint1 || !this.controlPoint2) return this.startPoint;
510
+ const mt = 1 - t;
511
+ const mt2 = mt * mt;
512
+ const mt3 = mt2 * mt;
513
+ const t2 = t * t;
514
+ const t3 = t2 * t;
515
+ return {
516
+ x: mt3 * this.startPoint.x + 3 * mt2 * t * this.controlPoint1.x + 3 * mt * t2 * this.controlPoint2.x + t3 * this.endPoint.x,
517
+ y: mt3 * this.startPoint.y + 3 * mt2 * t * this.controlPoint1.y + 3 * mt * t2 * this.controlPoint2.y + t3 * this.endPoint.y
518
+ };
519
+ }
520
+ getCubicBezierTangent(t) {
521
+ if (!this.controlPoint1 || !this.controlPoint2) {
522
+ return { x: this.endPoint.x - this.startPoint.x, y: this.endPoint.y - this.startPoint.y };
523
+ }
524
+ const mt = 1 - t;
525
+ const mt2 = mt * mt;
526
+ const t2 = t * t;
527
+ return {
528
+ x: 3 * mt2 * (this.controlPoint1.x - this.startPoint.x) + 6 * mt * t * (this.controlPoint2.x - this.controlPoint1.x) + 3 * t2 * (this.endPoint.x - this.controlPoint2.x),
529
+ y: 3 * mt2 * (this.controlPoint1.y - this.startPoint.y) + 6 * mt * t * (this.controlPoint2.y - this.controlPoint1.y) + 3 * t2 * (this.endPoint.y - this.controlPoint2.y)
530
+ };
531
+ }
532
+ _getArrowAngle(elbowX) {
533
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
534
+ const tangent = this.getCubicBezierTangent(1);
535
+ return Math.atan2(tangent.y, tangent.x);
536
+ } else if (this.arrowCurved === "elbow") {
537
+ const ex = elbowX !== void 0 ? elbowX : this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
538
+ return Math.atan2(0, this.endPoint.x - ex);
539
+ } else {
540
+ const dx = this.endPoint.x - this.startPoint.x;
541
+ const dy = this.endPoint.y - this.startPoint.y;
542
+ return Math.atan2(dy, dx);
543
+ }
544
+ }
545
+ _getArrowHeadPoints(angle) {
546
+ const rad = this.arrowHeadAngleDeg * Math.PI / 180;
547
+ return {
548
+ x3: this.endPoint.x - this.arrowHeadLength * Math.cos(angle - rad),
549
+ y3: this.endPoint.y - this.arrowHeadLength * Math.sin(angle - rad),
550
+ x4: this.endPoint.x - this.arrowHeadLength * Math.cos(angle + rad),
551
+ y4: this.endPoint.y - this.arrowHeadLength * Math.sin(angle + rad)
552
+ };
553
+ }
554
+ _renderArrowHead(angle) {
555
+ if (this._arrowHeadEl) {
556
+ this._arrowHeadEl.remove();
557
+ this._arrowHeadEl = null;
558
+ }
559
+ const style = this.arrowHeadStyle;
560
+ if (!style || style === "default") return;
561
+ const pts = this._getArrowHeadPoints(angle);
562
+ const tip = this.endPoint;
563
+ let el;
564
+ if (style === "square") {
565
+ const size = this.arrowHeadLength * 0.7;
566
+ const perpX = -Math.sin(angle), perpY = Math.cos(angle);
567
+ const backX = -Math.cos(angle), backY = -Math.sin(angle);
568
+ const p1x = tip.x + perpX * size / 2, p1y = tip.y + perpY * size / 2;
569
+ const p2x = tip.x - perpX * size / 2, p2y = tip.y - perpY * size / 2;
570
+ const p3x = p2x + backX * size, p3y = p2y + backY * size;
571
+ const p4x = p1x + backX * size, p4y = p1y + backY * size;
572
+ el = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
573
+ el.setAttribute("points", `${p1x},${p1y} ${p2x},${p2y} ${p3x},${p3y} ${p4x},${p4y}`);
574
+ el.setAttribute("fill", "none");
575
+ } else {
576
+ el = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
577
+ el.setAttribute("points", `${pts.x3},${pts.y3} ${tip.x},${tip.y} ${pts.x4},${pts.y4}`);
578
+ el.setAttribute("fill", style === "solid" ? this.options.stroke : "none");
579
+ }
580
+ el.setAttribute("stroke", this.options.stroke);
581
+ el.setAttribute("stroke-width", this.options.strokeWidth);
582
+ el.setAttribute("stroke-linejoin", "round");
583
+ el.classList.add("arrow-head");
584
+ this._arrowHeadEl = el;
585
+ this.group.appendChild(el);
586
+ }
587
+ _updateArrowHead() {
588
+ if (!this._arrowHeadEl) return;
589
+ const style = this.arrowHeadStyle;
590
+ if (style === "default" || !style) return;
591
+ const elbowX = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
592
+ const angle = this._getArrowAngle(elbowX);
593
+ const pts = this._getArrowHeadPoints(angle);
594
+ const tip = this.endPoint;
595
+ if (style === "square") {
596
+ const size = this.arrowHeadLength * 0.7;
597
+ const perpX = -Math.sin(angle), perpY = Math.cos(angle);
598
+ const backX = -Math.cos(angle), backY = -Math.sin(angle);
599
+ const p1x = tip.x + perpX * size / 2, p1y = tip.y + perpY * size / 2;
600
+ const p2x = tip.x - perpX * size / 2, p2y = tip.y - perpY * size / 2;
601
+ const p3x = p2x + backX * size, p3y = p2y + backY * size;
602
+ const p4x = p1x + backX * size, p4y = p1y + backY * size;
603
+ this._arrowHeadEl.setAttribute("points", `${p1x},${p1y} ${p2x},${p2y} ${p3x},${p3y} ${p4x},${p4y}`);
604
+ } else {
605
+ this._arrowHeadEl.setAttribute("points", `${pts.x3},${pts.y3} ${tip.x},${tip.y} ${pts.x4},${pts.y4}`);
606
+ }
607
+ }
608
+ updateSelectionControls() {
609
+ if (!this.anchors || this.anchors.length === 0) return;
610
+ const anchorSize = 5 / currentZoom;
611
+ let anchorPositions = [this.startPoint, this.endPoint];
612
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
613
+ const midOnCurve = this.getCubicBezierPoint(0.5);
614
+ anchorPositions.push(midOnCurve);
615
+ } else if (this.arrowCurved === "elbow") {
616
+ const elbowXVal = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
617
+ const midY = (this.startPoint.y + this.endPoint.y) / 2;
618
+ anchorPositions.push({ x: elbowXVal, y: midY });
619
+ } else {
620
+ const arrowAngle = Math.atan2(this.endPoint.y - this.startPoint.y, this.endPoint.x - this.startPoint.x);
621
+ const arrowHeadClearance = this.arrowHeadLength + anchorSize - 10;
622
+ anchorPositions[1] = {
623
+ x: this.endPoint.x + arrowHeadClearance * Math.cos(arrowAngle),
624
+ y: this.endPoint.y + arrowHeadClearance * Math.sin(arrowAngle)
625
+ };
626
+ }
627
+ anchorPositions.forEach((point, index) => {
628
+ if (this.anchors[index]) {
629
+ this.anchors[index].setAttribute("cx", point.x);
630
+ this.anchors[index].setAttribute("cy", point.y);
631
+ this.anchors[index].setAttribute("r", anchorSize);
632
+ }
633
+ });
634
+ }
635
+ addAnchors() {
636
+ const anchorSize = 5 / currentZoom;
637
+ const anchorStrokeWidth = 2 / currentZoom;
638
+ let anchorPositions = [this.startPoint, this.endPoint];
639
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
640
+ const midOnCurve = this.getCubicBezierPoint(0.5);
641
+ anchorPositions.push(midOnCurve);
642
+ } else if (this.arrowCurved === "elbow") {
643
+ const elbowXVal = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
644
+ const midY = (this.startPoint.y + this.endPoint.y) / 2;
645
+ anchorPositions.push({ x: elbowXVal, y: midY });
646
+ } else {
647
+ const arrowAngle = Math.atan2(this.endPoint.y - this.startPoint.y, this.endPoint.x - this.startPoint.x);
648
+ const arrowHeadClearance = this.arrowHeadLength + anchorSize - 10;
649
+ anchorPositions[1] = {
650
+ x: this.endPoint.x + arrowHeadClearance * Math.cos(arrowAngle),
651
+ y: this.endPoint.y + arrowHeadClearance * Math.sin(arrowAngle)
652
+ };
653
+ }
654
+ disableAllSideBars();
655
+ arrowSideBar.classList.remove("hidden");
656
+ if (window.__showSidebarForShape) window.__showSidebarForShape("arrow");
657
+ this.updateSidebar();
658
+ anchorPositions.forEach((point, index) => {
659
+ const anchor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
660
+ anchor.setAttribute("cx", point.x);
661
+ anchor.setAttribute("cy", point.y);
662
+ anchor.setAttribute("r", anchorSize);
663
+ if (this.arrowCurved && index >= 2) {
664
+ anchor.setAttribute("fill", "#121212");
665
+ anchor.setAttribute("stroke", "#5B57D1");
666
+ } else {
667
+ anchor.setAttribute("fill", "#121212");
668
+ anchor.setAttribute("stroke", "#5B57D1");
669
+ }
670
+ anchor.setAttribute("stroke-width", anchorStrokeWidth);
671
+ anchor.setAttribute("vector-effect", "non-scaling-stroke");
672
+ anchor.setAttribute("class", "anchor arrow-anchor");
673
+ anchor.setAttribute("data-index", index);
674
+ anchor.style.cursor = "grab";
675
+ anchor.style.pointerEvents = "all";
676
+ anchor.addEventListener("pointerdown", (e) => this.startAnchorDrag(e, index));
677
+ this.group.appendChild(anchor);
678
+ this.anchors[index] = anchor;
679
+ });
680
+ }
681
+ addAttachmentIndicators() {
682
+ if (this.attachedToStart) {
683
+ const attachPoint = this.calculateAttachedPoint(this.attachedToStart);
684
+ const indicator = document.createElementNS("http://www.w3.org/2000/svg", "circle");
685
+ indicator.setAttribute("cx", attachPoint.x);
686
+ indicator.setAttribute("cy", attachPoint.y);
687
+ indicator.setAttribute("r", 4);
688
+ indicator.setAttribute("fill", "#5B57D1");
689
+ indicator.setAttribute("stroke", "#121212");
690
+ indicator.setAttribute("stroke-width", 1);
691
+ indicator.setAttribute("class", "attachment-indicator");
692
+ this.group.appendChild(indicator);
693
+ }
694
+ if (this.attachedToEnd) {
695
+ const attachPoint = this.calculateAttachedPoint(this.attachedToEnd);
696
+ const indicator = document.createElementNS("http://www.w3.org/2000/svg", "circle");
697
+ indicator.setAttribute("cx", attachPoint.x);
698
+ indicator.setAttribute("cy", attachPoint.y);
699
+ indicator.setAttribute("r", 4);
700
+ indicator.setAttribute("fill", "#5B57D1");
701
+ indicator.setAttribute("stroke", "#121212");
702
+ indicator.setAttribute("stroke-width", 1);
703
+ indicator.setAttribute("class", "attachment-indicator");
704
+ this.group.appendChild(indicator);
705
+ }
706
+ }
707
+ getAttachmentState() {
708
+ return {
709
+ attachedToStart: this.attachedToStart ? {
710
+ shapeId: this.attachedToStart.shape.shapeID,
711
+ side: this.attachedToStart.side,
712
+ offset: { ...this.attachedToStart.offset }
713
+ } : null,
714
+ attachedToEnd: this.attachedToEnd ? {
715
+ shapeId: this.attachedToEnd.shape.shapeID,
716
+ side: this.attachedToEnd.side,
717
+ offset: { ...this.attachedToEnd.offset }
718
+ } : null
719
+ };
720
+ }
721
+ restoreAttachmentState(attachmentState) {
722
+ this.attachedToStart = null;
723
+ this.attachedToEnd = null;
724
+ if (attachmentState.attachedToStart) {
725
+ const shape = shapes.find((s) => s.shapeID === attachmentState.attachedToStart.shapeId);
726
+ if (shape) {
727
+ this.attachedToStart = {
728
+ shape,
729
+ side: attachmentState.attachedToStart.side,
730
+ offset: { ...attachmentState.attachedToStart.offset }
731
+ };
732
+ this.startPoint = this.calculateAttachedPoint(this.attachedToStart);
733
+ }
734
+ }
735
+ if (attachmentState.attachedToEnd) {
736
+ const shape = shapes.find((s) => s.shapeID === attachmentState.attachedToEnd.shapeId);
737
+ if (shape) {
738
+ this.attachedToEnd = {
739
+ shape,
740
+ side: attachmentState.attachedToEnd.side,
741
+ offset: { ...attachmentState.attachedToEnd.offset }
742
+ };
743
+ this.endPoint = this.calculateAttachedPoint(this.attachedToEnd);
744
+ }
745
+ }
746
+ if (this.arrowCurved === "curved") {
747
+ this.initializeCurveControlPoints();
748
+ }
749
+ this.draw();
750
+ }
751
+ static getEllipsePerimeterPoint(circle, angle) {
752
+ const cosAngle = Math.cos(angle);
753
+ const sinAngle = Math.sin(angle);
754
+ const a = circle.rx;
755
+ const b = circle.ry;
756
+ const t = Math.atan2(a * sinAngle, b * cosAngle);
757
+ return {
758
+ x: circle.x + a * Math.cos(t),
759
+ y: circle.y + b * Math.sin(t)
760
+ };
761
+ }
762
+ static findNearbyShape(point, tolerance = 20) {
763
+ for (let shape of shapes) {
764
+ if (shape.shapeName === "arrow" || shape.shapeName === "line") continue;
765
+ let attachment = null;
766
+ switch (shape.shapeName) {
767
+ case "rectangle":
768
+ attachment = _Arrow.getRectangleAttachmentPoint(point, shape, tolerance);
769
+ break;
770
+ case "circle":
771
+ attachment = _Arrow.getCircleAttachmentPoint(point, shape, tolerance);
772
+ break;
773
+ case "frame":
774
+ attachment = _Arrow.getFrameAttachmentPoint(point, shape, tolerance);
775
+ break;
776
+ case "text":
777
+ case "code":
778
+ if (shape.group) {
779
+ attachment = _Arrow.getTextAttachmentPoint(point, shape.group, tolerance);
780
+ }
781
+ break;
782
+ case "image":
783
+ if (shape.element) {
784
+ attachment = _Arrow.getImageAttachmentPoint(point, shape.element, tolerance);
785
+ }
786
+ break;
787
+ case "icon":
788
+ if (shape.element) {
789
+ attachment = _Arrow.getIconAttachmentPoint(point, shape.element, tolerance);
790
+ }
791
+ break;
792
+ case "freehandStroke":
793
+ attachment = _Arrow.getBoundingBoxAttachmentPoint(point, shape, tolerance);
794
+ break;
795
+ case "line":
796
+ attachment = _Arrow.getLineAttachmentPoint(point, shape, tolerance);
797
+ break;
798
+ }
799
+ if (attachment) {
800
+ return { shape, attachment };
801
+ }
802
+ }
803
+ return null;
804
+ }
805
+ static getBoundingBoxAttachmentPoint(point, shape, tolerance = 20) {
806
+ const bounds = shape.boundingBox || { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
807
+ if (!bounds || bounds.width === 0 || bounds.height === 0) return null;
808
+ const sides = [
809
+ { name: "top", start: { x: bounds.x, y: bounds.y }, end: { x: bounds.x + bounds.width, y: bounds.y } },
810
+ { name: "right", start: { x: bounds.x + bounds.width, y: bounds.y }, end: { x: bounds.x + bounds.width, y: bounds.y + bounds.height } },
811
+ { name: "bottom", start: { x: bounds.x + bounds.width, y: bounds.y + bounds.height }, end: { x: bounds.x, y: bounds.y + bounds.height } },
812
+ { name: "left", start: { x: bounds.x, y: bounds.y + bounds.height }, end: { x: bounds.x, y: bounds.y } }
813
+ ];
814
+ let closestSide = null;
815
+ let minDistance = tolerance;
816
+ let attachPoint = null;
817
+ sides.forEach((side) => {
818
+ const distance = _Arrow.pointToLineSegmentDistance(point, side.start, side.end);
819
+ if (distance < minDistance) {
820
+ minDistance = distance;
821
+ closestSide = side.name;
822
+ attachPoint = _Arrow.closestPointOnLineSegment(point, side.start, side.end);
823
+ }
824
+ });
825
+ if (closestSide && attachPoint) {
826
+ const offset = {
827
+ x: attachPoint.x - bounds.x,
828
+ y: attachPoint.y - bounds.y,
829
+ side: closestSide
830
+ };
831
+ return { side: closestSide, point: attachPoint, offset };
832
+ }
833
+ return null;
834
+ }
835
+ static getLineAttachmentPoint(point, line, tolerance = 20) {
836
+ const start = line.startPoint;
837
+ const end = line.endPoint;
838
+ if (!start || !end) return null;
839
+ const distance = _Arrow.pointToLineSegmentDistance(point, start, end);
840
+ if (distance >= tolerance) return null;
841
+ const attachPoint = _Arrow.closestPointOnLineSegment(point, start, end);
842
+ const dx = end.x - start.x;
843
+ const dy = end.y - start.y;
844
+ const lenSq = dx * dx + dy * dy;
845
+ const t = lenSq > 0 ? ((attachPoint.x - start.x) * dx + (attachPoint.y - start.y) * dy) / lenSq : 0;
846
+ return {
847
+ side: "line",
848
+ point: attachPoint,
849
+ offset: { t: Math.max(0, Math.min(1, t)), side: "line" }
850
+ };
851
+ }
852
+ static getIconAttachmentPoint(point, iconElement, tolerance = 20) {
853
+ if (!iconElement || iconElement.tagName !== "g" && (!iconElement.getAttribute || iconElement.getAttribute("type") !== "icon")) {
854
+ console.warn("Invalid icon element for attachment:", iconElement);
855
+ return null;
856
+ }
857
+ const iconX = parseFloat(iconElement.getAttribute("data-shape-x") || iconElement.getAttribute("x"));
858
+ const iconY = parseFloat(iconElement.getAttribute("data-shape-y") || iconElement.getAttribute("y"));
859
+ const iconWidth = parseFloat(iconElement.getAttribute("data-shape-width") || iconElement.getAttribute("width"));
860
+ const iconHeight = parseFloat(iconElement.getAttribute("data-shape-height") || iconElement.getAttribute("height"));
861
+ let rotation = 0;
862
+ const dataRotation = iconElement.getAttribute("data-shape-rotation");
863
+ if (dataRotation) {
864
+ rotation = parseFloat(dataRotation) * Math.PI / 180;
865
+ } else {
866
+ const transform = iconElement.getAttribute("transform");
867
+ if (transform) {
868
+ const rotateMatch = transform.match(/rotate\(([^,]+)/);
869
+ if (rotateMatch) {
870
+ rotation = parseFloat(rotateMatch[1]) * Math.PI / 180;
871
+ }
872
+ }
873
+ }
874
+ const centerX = iconX + iconWidth / 2;
875
+ const centerY = iconY + iconHeight / 2;
876
+ const corners = [
877
+ { x: iconX, y: iconY },
878
+ // top-left
879
+ { x: iconX + iconWidth, y: iconY },
880
+ // top-right
881
+ { x: iconX + iconWidth, y: iconY + iconHeight },
882
+ // bottom-right
883
+ { x: iconX, y: iconY + iconHeight }
884
+ // bottom-left
885
+ ];
886
+ const transformedCorners = corners.map((corner) => {
887
+ if (rotation === 0) return corner;
888
+ const dx = corner.x - centerX;
889
+ const dy = corner.y - centerY;
890
+ return {
891
+ x: centerX + dx * Math.cos(rotation) - dy * Math.sin(rotation),
892
+ y: centerY + dx * Math.sin(rotation) + dy * Math.cos(rotation)
893
+ };
894
+ });
895
+ const sides = [
896
+ { name: "top", start: transformedCorners[0], end: transformedCorners[1] },
897
+ { name: "right", start: transformedCorners[1], end: transformedCorners[2] },
898
+ { name: "bottom", start: transformedCorners[2], end: transformedCorners[3] },
899
+ { name: "left", start: transformedCorners[3], end: transformedCorners[0] }
900
+ ];
901
+ let closestSide = null;
902
+ let minDistance = tolerance;
903
+ let attachPoint = null;
904
+ sides.forEach((side) => {
905
+ const distance = _Arrow.pointToLineSegmentDistance(point, side.start, side.end);
906
+ if (distance < minDistance) {
907
+ minDistance = distance;
908
+ closestSide = side.name;
909
+ attachPoint = _Arrow.closestPointOnLineSegment(point, side.start, side.end);
910
+ }
911
+ });
912
+ if (closestSide && attachPoint) {
913
+ let localPoint = attachPoint;
914
+ if (rotation !== 0) {
915
+ const dx = attachPoint.x - centerX;
916
+ const dy = attachPoint.y - centerY;
917
+ localPoint = {
918
+ x: centerX + dx * Math.cos(-rotation) - dy * Math.sin(-rotation),
919
+ y: centerY + dx * Math.sin(-rotation) + dy * Math.cos(-rotation)
920
+ };
921
+ }
922
+ const offset = {
923
+ x: localPoint.x - iconX,
924
+ y: localPoint.y - iconY,
925
+ side: closestSide
926
+ };
927
+ return { side: closestSide, point: attachPoint, offset };
928
+ }
929
+ return null;
930
+ }
931
+ static getImageAttachmentPoint(point, imageElement, tolerance = 20) {
932
+ if (!imageElement || imageElement.tagName !== "image" && (!imageElement.getAttribute || imageElement.getAttribute("type") !== "image")) {
933
+ console.warn("Invalid image element for attachment:", imageElement);
934
+ return null;
935
+ }
936
+ const imgX = parseFloat(imageElement.getAttribute("data-shape-x") || imageElement.getAttribute("x"));
937
+ const imgY = parseFloat(imageElement.getAttribute("data-shape-y") || imageElement.getAttribute("y"));
938
+ const imgWidth = parseFloat(imageElement.getAttribute("data-shape-width") || imageElement.getAttribute("width"));
939
+ const imgHeight = parseFloat(imageElement.getAttribute("data-shape-height") || imageElement.getAttribute("height"));
940
+ let rotation = 0;
941
+ const dataRotation = imageElement.getAttribute("data-shape-rotation");
942
+ if (dataRotation) {
943
+ rotation = parseFloat(dataRotation) * Math.PI / 180;
944
+ } else {
945
+ const transform = imageElement.getAttribute("transform");
946
+ if (transform) {
947
+ const rotateMatch = transform.match(/rotate\(([^,]+)/);
948
+ if (rotateMatch) {
949
+ rotation = parseFloat(rotateMatch[1]) * Math.PI / 180;
950
+ }
951
+ }
952
+ }
953
+ const centerX = imgX + imgWidth / 2;
954
+ const centerY = imgY + imgHeight / 2;
955
+ const corners = [
956
+ { x: imgX, y: imgY },
957
+ // top-left
958
+ { x: imgX + imgWidth, y: imgY },
959
+ // top-right
960
+ { x: imgX + imgWidth, y: imgY + imgHeight },
961
+ // bottom-right
962
+ { x: imgX, y: imgY + imgHeight }
963
+ // bottom-left
964
+ ];
965
+ const transformedCorners = corners.map((corner) => {
966
+ if (rotation === 0) return corner;
967
+ const dx = corner.x - centerX;
968
+ const dy = corner.y - centerY;
969
+ return {
970
+ x: centerX + dx * Math.cos(rotation) - dy * Math.sin(rotation),
971
+ y: centerY + dx * Math.sin(rotation) + dy * Math.cos(rotation)
972
+ };
973
+ });
974
+ const sides = [
975
+ { name: "top", start: transformedCorners[0], end: transformedCorners[1] },
976
+ { name: "right", start: transformedCorners[1], end: transformedCorners[2] },
977
+ { name: "bottom", start: transformedCorners[2], end: transformedCorners[3] },
978
+ { name: "left", start: transformedCorners[3], end: transformedCorners[0] }
979
+ ];
980
+ let closestSide = null;
981
+ let minDistance = tolerance;
982
+ let attachPoint = null;
983
+ sides.forEach((side) => {
984
+ const distance = _Arrow.pointToLineSegmentDistance(point, side.start, side.end);
985
+ if (distance < minDistance) {
986
+ minDistance = distance;
987
+ closestSide = side.name;
988
+ attachPoint = _Arrow.closestPointOnLineSegment(point, side.start, side.end);
989
+ }
990
+ });
991
+ if (closestSide && attachPoint) {
992
+ let localPoint = attachPoint;
993
+ if (rotation !== 0) {
994
+ const dx = attachPoint.x - centerX;
995
+ const dy = attachPoint.y - centerY;
996
+ localPoint = {
997
+ x: centerX + dx * Math.cos(-rotation) - dy * Math.sin(-rotation),
998
+ y: centerY + dx * Math.sin(-rotation) + dy * Math.cos(-rotation)
999
+ };
1000
+ }
1001
+ const offset = {
1002
+ x: localPoint.x - imgX,
1003
+ y: localPoint.y - imgY,
1004
+ side: closestSide
1005
+ };
1006
+ return { side: closestSide, point: attachPoint, offset };
1007
+ }
1008
+ return null;
1009
+ }
1010
+ static getFrameAttachmentPoint(point, frame, tolerance = 20) {
1011
+ const rect = {
1012
+ left: frame.x,
1013
+ right: frame.x + frame.width,
1014
+ top: frame.y,
1015
+ bottom: frame.y + frame.height
1016
+ };
1017
+ const distances = {
1018
+ top: Math.abs(point.y - rect.top),
1019
+ bottom: Math.abs(point.y - rect.bottom),
1020
+ left: Math.abs(point.x - rect.left),
1021
+ right: Math.abs(point.x - rect.right)
1022
+ };
1023
+ let closestSide = null;
1024
+ let minDistance = tolerance;
1025
+ for (let side in distances) {
1026
+ if (distances[side] < minDistance) {
1027
+ if ((side === "top" || side === "bottom") && point.x >= rect.left - tolerance && point.x <= rect.right + tolerance) {
1028
+ closestSide = side;
1029
+ minDistance = distances[side];
1030
+ } else if ((side === "left" || side === "right") && point.y >= rect.top - tolerance && point.y <= rect.bottom + tolerance) {
1031
+ closestSide = side;
1032
+ minDistance = distances[side];
1033
+ }
1034
+ }
1035
+ }
1036
+ if (closestSide) {
1037
+ let attachPoint, offset;
1038
+ switch (closestSide) {
1039
+ case "top":
1040
+ attachPoint = { x: Math.max(rect.left, Math.min(rect.right, point.x)), y: rect.top };
1041
+ offset = { x: attachPoint.x - frame.x, y: 0 };
1042
+ break;
1043
+ case "bottom":
1044
+ attachPoint = { x: Math.max(rect.left, Math.min(rect.right, point.x)), y: rect.bottom };
1045
+ offset = { x: attachPoint.x - frame.x, y: frame.height };
1046
+ break;
1047
+ case "left":
1048
+ attachPoint = { x: rect.left, y: Math.max(rect.top, Math.min(rect.bottom, point.y)) };
1049
+ offset = { x: 0, y: attachPoint.y - frame.y };
1050
+ break;
1051
+ case "right":
1052
+ attachPoint = { x: rect.right, y: Math.max(rect.top, Math.min(rect.bottom, point.y)) };
1053
+ offset = { x: frame.width, y: attachPoint.y - frame.y };
1054
+ break;
1055
+ }
1056
+ return { side: closestSide, point: attachPoint, offset };
1057
+ }
1058
+ return null;
1059
+ }
1060
+ static getRectangleAttachmentPoint(point, rectangle, tolerance = 20) {
1061
+ const rect = {
1062
+ left: rectangle.x,
1063
+ right: rectangle.x + rectangle.width,
1064
+ top: rectangle.y,
1065
+ bottom: rectangle.y + rectangle.height
1066
+ };
1067
+ const distances = {
1068
+ top: Math.abs(point.y - rect.top),
1069
+ bottom: Math.abs(point.y - rect.bottom),
1070
+ left: Math.abs(point.x - rect.left),
1071
+ right: Math.abs(point.x - rect.right)
1072
+ };
1073
+ let closestSide = null;
1074
+ let minDistance = tolerance;
1075
+ for (let side in distances) {
1076
+ if (distances[side] < minDistance) {
1077
+ if ((side === "top" || side === "bottom") && point.x >= rect.left - tolerance && point.x <= rect.right + tolerance) {
1078
+ closestSide = side;
1079
+ minDistance = distances[side];
1080
+ } else if ((side === "left" || side === "right") && point.y >= rect.top - tolerance && point.y <= rect.bottom + tolerance) {
1081
+ closestSide = side;
1082
+ minDistance = distances[side];
1083
+ }
1084
+ }
1085
+ }
1086
+ if (closestSide) {
1087
+ let attachPoint, offset;
1088
+ switch (closestSide) {
1089
+ case "top":
1090
+ attachPoint = { x: Math.max(rect.left, Math.min(rect.right, point.x)), y: rect.top };
1091
+ offset = { x: attachPoint.x - rectangle.x, y: 0 };
1092
+ break;
1093
+ case "bottom":
1094
+ attachPoint = { x: Math.max(rect.left, Math.min(rect.right, point.x)), y: rect.bottom };
1095
+ offset = { x: attachPoint.x - rectangle.x, y: rectangle.height };
1096
+ break;
1097
+ case "left":
1098
+ attachPoint = { x: rect.left, y: Math.max(rect.top, Math.min(rect.bottom, point.y)) };
1099
+ offset = { x: 0, y: attachPoint.y - rectangle.y };
1100
+ break;
1101
+ case "right":
1102
+ attachPoint = { x: rect.right, y: Math.max(rect.top, Math.min(rect.bottom, point.y)) };
1103
+ offset = { x: rectangle.width, y: attachPoint.y - rectangle.y };
1104
+ break;
1105
+ }
1106
+ return { side: closestSide, point: attachPoint, offset };
1107
+ }
1108
+ return null;
1109
+ }
1110
+ static getCircleAttachmentPoint(point, circle, tolerance = 20) {
1111
+ const dx = point.x - circle.x;
1112
+ const dy = point.y - circle.y;
1113
+ const distanceToCenter = Math.sqrt(dx * dx + dy * dy);
1114
+ const averageRadius = (circle.rx + circle.ry) / 2;
1115
+ const distanceToPerimeter = Math.abs(distanceToCenter - averageRadius);
1116
+ if (distanceToPerimeter <= tolerance) {
1117
+ const angle = Math.atan2(dy, dx);
1118
+ const attachPoint = this.getEllipsePerimeterPoint(circle, angle);
1119
+ const offset = {
1120
+ angle,
1121
+ radiusRatioX: (attachPoint.x - circle.x) / circle.rx,
1122
+ radiusRatioY: (attachPoint.y - circle.y) / circle.ry
1123
+ };
1124
+ return {
1125
+ side: "perimeter",
1126
+ point: attachPoint,
1127
+ offset
1128
+ };
1129
+ }
1130
+ return null;
1131
+ }
1132
+ static getTextAttachmentPoint(point, textGroup, tolerance = 20) {
1133
+ if (!textGroup) return null;
1134
+ const groupType = textGroup.getAttribute ? textGroup.getAttribute("type") : textGroup.type;
1135
+ if (groupType !== "text" && groupType !== "code") return null;
1136
+ const textElement = textGroup.querySelector("text");
1137
+ if (!textElement) return null;
1138
+ const bbox = textElement.getBBox();
1139
+ const groupTransform = textGroup.transform.baseVal.consolidate();
1140
+ const matrix = groupTransform ? groupTransform.matrix : { e: 0, f: 0, a: 1, b: 0, c: 0, d: 1 };
1141
+ const corners = [
1142
+ { x: bbox.x, y: bbox.y },
1143
+ // top-left
1144
+ { x: bbox.x + bbox.width, y: bbox.y },
1145
+ // top-right
1146
+ { x: bbox.x + bbox.width, y: bbox.y + bbox.height },
1147
+ // bottom-right
1148
+ { x: bbox.x, y: bbox.y + bbox.height }
1149
+ // bottom-left
1150
+ ];
1151
+ const transformedCorners = corners.map((corner) => ({
1152
+ x: corner.x * matrix.a + corner.y * matrix.c + matrix.e,
1153
+ y: corner.x * matrix.b + corner.y * matrix.d + matrix.f
1154
+ }));
1155
+ const sides = [
1156
+ { name: "top", start: transformedCorners[0], end: transformedCorners[1] },
1157
+ { name: "right", start: transformedCorners[1], end: transformedCorners[2] },
1158
+ { name: "bottom", start: transformedCorners[2], end: transformedCorners[3] },
1159
+ { name: "left", start: transformedCorners[3], end: transformedCorners[0] }
1160
+ ];
1161
+ let closestSide = null;
1162
+ let minDistance = tolerance;
1163
+ let attachPoint = null;
1164
+ sides.forEach((side) => {
1165
+ const distance = _Arrow.pointToLineSegmentDistance(point, side.start, side.end);
1166
+ if (distance < minDistance) {
1167
+ minDistance = distance;
1168
+ closestSide = side.name;
1169
+ attachPoint = _Arrow.closestPointOnLineSegment(point, side.start, side.end);
1170
+ }
1171
+ });
1172
+ if (closestSide && attachPoint) {
1173
+ const det = matrix.a * matrix.d - matrix.b * matrix.c;
1174
+ if (det === 0) return null;
1175
+ const invMatrix = {
1176
+ a: matrix.d / det,
1177
+ b: -matrix.b / det,
1178
+ c: -matrix.c / det,
1179
+ d: matrix.a / det,
1180
+ e: (matrix.c * matrix.f - matrix.d * matrix.e) / det,
1181
+ f: (matrix.b * matrix.e - matrix.a * matrix.f) / det
1182
+ };
1183
+ const localPoint = {
1184
+ x: attachPoint.x * invMatrix.a + attachPoint.y * invMatrix.c + invMatrix.e,
1185
+ y: attachPoint.x * invMatrix.b + attachPoint.y * invMatrix.d + invMatrix.f
1186
+ };
1187
+ const offset = {
1188
+ x: localPoint.x - bbox.x,
1189
+ y: localPoint.y - bbox.y,
1190
+ side: closestSide
1191
+ };
1192
+ return { side: closestSide, point: attachPoint, offset };
1193
+ }
1194
+ return null;
1195
+ }
1196
+ static pointToLineSegmentDistance(point, lineStart, lineEnd) {
1197
+ const A = point.x - lineStart.x;
1198
+ const B = point.y - lineStart.y;
1199
+ const C = lineEnd.x - lineStart.x;
1200
+ const D = lineEnd.y - lineStart.y;
1201
+ const dot = A * C + B * D;
1202
+ const lenSq = C * C + D * D;
1203
+ if (lenSq === 0) {
1204
+ return Math.sqrt(A * A + B * B);
1205
+ }
1206
+ let param = dot / lenSq;
1207
+ param = Math.max(0, Math.min(1, param));
1208
+ const xx = lineStart.x + param * C;
1209
+ const yy = lineStart.y + param * D;
1210
+ const dx = point.x - xx;
1211
+ const dy = point.y - yy;
1212
+ return Math.sqrt(dx * dx + dy * dy);
1213
+ }
1214
+ static closestPointOnLineSegment(point, lineStart, lineEnd) {
1215
+ const A = point.x - lineStart.x;
1216
+ const B = point.y - lineStart.y;
1217
+ const C = lineEnd.x - lineStart.x;
1218
+ const D = lineEnd.y - lineStart.y;
1219
+ const dot = A * C + B * D;
1220
+ const lenSq = C * C + D * D;
1221
+ if (lenSq === 0) {
1222
+ return { x: lineStart.x, y: lineStart.y };
1223
+ }
1224
+ let param = dot / lenSq;
1225
+ param = Math.max(0, Math.min(1, param));
1226
+ return {
1227
+ x: lineStart.x + param * C,
1228
+ y: lineStart.y + param * D
1229
+ };
1230
+ }
1231
+ calculateAttachedPoint(attachment) {
1232
+ const shape = attachment.shape;
1233
+ const side = attachment.side;
1234
+ const offset = attachment.offset;
1235
+ if (shape.shapeName === "rectangle") {
1236
+ return _Arrow._calcRectAttachedPoint(shape.x, shape.y, shape.width, shape.height, shape.rotation, side, offset);
1237
+ }
1238
+ if (shape.shapeName === "circle") {
1239
+ if (side === "perimeter") {
1240
+ return _Arrow.getEllipsePerimeterPoint(shape, offset.angle);
1241
+ }
1242
+ }
1243
+ if (shape.shapeName === "text" || shape.shapeName === "code") {
1244
+ const groupEl = shape.group;
1245
+ if (!groupEl) return { x: shape.x || 0, y: shape.y || 0 };
1246
+ const textElement = groupEl.querySelector("text") || groupEl.querySelector("foreignObject");
1247
+ if (!textElement) return { x: shape.x || 0, y: shape.y || 0 };
1248
+ const bbox = textElement.getBBox();
1249
+ const groupTransform = groupEl.transform.baseVal.consolidate();
1250
+ const matrix = groupTransform ? groupTransform.matrix : { e: 0, f: 0, a: 1, b: 0, c: 0, d: 1 };
1251
+ let localPoint = { x: bbox.x + offset.x, y: bbox.y + offset.y };
1252
+ return {
1253
+ x: localPoint.x * matrix.a + localPoint.y * matrix.c + matrix.e,
1254
+ y: localPoint.x * matrix.b + localPoint.y * matrix.d + matrix.f
1255
+ };
1256
+ }
1257
+ if (shape.shapeName === "image") {
1258
+ return _Arrow._calcRectAttachedPoint(shape.x, shape.y, shape.width, shape.height, shape.rotation, side, offset);
1259
+ }
1260
+ if (shape.shapeName === "icon") {
1261
+ return _Arrow._calcRectAttachedPoint(shape.x, shape.y, shape.width, shape.height, shape.rotation, side, offset);
1262
+ }
1263
+ if (shape.shapeName === "frame") {
1264
+ return _Arrow._calcRectAttachedPoint(shape.x, shape.y, shape.width, shape.height, 0, side, offset);
1265
+ }
1266
+ if (shape.shapeName === "freehandStroke") {
1267
+ const bounds = shape.boundingBox || { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
1268
+ return _Arrow._calcRectAttachedPoint(bounds.x, bounds.y, bounds.width, bounds.height, 0, side, offset);
1269
+ }
1270
+ if (shape.shapeName === "line") {
1271
+ if (side === "line" && offset.t !== void 0) {
1272
+ const t = offset.t;
1273
+ return {
1274
+ x: shape.startPoint.x + t * (shape.endPoint.x - shape.startPoint.x),
1275
+ y: shape.startPoint.y + t * (shape.endPoint.y - shape.startPoint.y)
1276
+ };
1277
+ }
1278
+ }
1279
+ return { x: shape.x || 0, y: shape.y || 0 };
1280
+ }
1281
+ static _calcRectAttachedPoint(rx, ry, rw, rh, rotation, side, offset) {
1282
+ let localPoint;
1283
+ switch (side) {
1284
+ case "top":
1285
+ localPoint = { x: rx + offset.x, y: ry };
1286
+ break;
1287
+ case "bottom":
1288
+ localPoint = { x: rx + offset.x, y: ry + rh };
1289
+ break;
1290
+ case "left":
1291
+ localPoint = { x: rx, y: ry + offset.y };
1292
+ break;
1293
+ case "right":
1294
+ localPoint = { x: rx + rw, y: ry + offset.y };
1295
+ break;
1296
+ default:
1297
+ localPoint = { x: rx + offset.x, y: ry + offset.y };
1298
+ }
1299
+ if (rotation) {
1300
+ const rad = rotation * Math.PI / 180;
1301
+ const cx = rx + rw / 2;
1302
+ const cy = ry + rh / 2;
1303
+ const dx = localPoint.x - cx;
1304
+ const dy = localPoint.y - cy;
1305
+ return {
1306
+ x: cx + dx * Math.cos(rad) - dy * Math.sin(rad),
1307
+ y: cy + dx * Math.sin(rad) + dy * Math.cos(rad)
1308
+ };
1309
+ }
1310
+ return localPoint;
1311
+ }
1312
+ detachFromShape(isStartPoint) {
1313
+ if (isStartPoint) {
1314
+ this.attachedToStart = null;
1315
+ } else {
1316
+ this.attachedToEnd = null;
1317
+ }
1318
+ }
1319
+ updateAttachments() {
1320
+ let needsRedraw = false;
1321
+ if (this.attachedToStart && this.attachedToStart.shape) {
1322
+ const newPoint = this.calculateAttachedPoint(this.attachedToStart);
1323
+ if (newPoint.x !== this.startPoint.x || newPoint.y !== this.startPoint.y) {
1324
+ this.startPoint = newPoint;
1325
+ needsRedraw = true;
1326
+ }
1327
+ }
1328
+ if (this.attachedToEnd && this.attachedToEnd.shape) {
1329
+ const newPoint = this.calculateAttachedPoint(this.attachedToEnd);
1330
+ if (newPoint.x !== this.endPoint.x || newPoint.y !== this.endPoint.y) {
1331
+ this.endPoint = newPoint;
1332
+ needsRedraw = true;
1333
+ }
1334
+ }
1335
+ if (needsRedraw) {
1336
+ if (this.arrowCurved) {
1337
+ this.initializeCurveControlPoints();
1338
+ }
1339
+ this.draw();
1340
+ }
1341
+ }
1342
+ move(dx, dy) {
1343
+ if (!this.attachedToStart) {
1344
+ this.startPoint.x += dx;
1345
+ this.startPoint.y += dy;
1346
+ }
1347
+ if (!this.attachedToEnd) {
1348
+ this.endPoint.x += dx;
1349
+ this.endPoint.y += dy;
1350
+ }
1351
+ if (this.controlPoint1 && (!this.attachedToStart && !this.attachedToEnd)) {
1352
+ this.controlPoint1.x += dx;
1353
+ this.controlPoint1.y += dy;
1354
+ }
1355
+ if (this.controlPoint2 && (!this.attachedToStart && !this.attachedToEnd)) {
1356
+ this.controlPoint2.x += dx;
1357
+ this.controlPoint2.y += dy;
1358
+ }
1359
+ this._updatePathElement();
1360
+ this._updateHitArea();
1361
+ this._updateLabelElement();
1362
+ this._updateAnchorPositions();
1363
+ if (isDragging && !this.isBeingMovedByFrame) {
1364
+ this.updateFrameContainment();
1365
+ }
1366
+ }
1367
+ updateFrameContainment() {
1368
+ if (this.isBeingMovedByFrame) return;
1369
+ let targetFrame = null;
1370
+ shapes.forEach((shape) => {
1371
+ if (shape.shapeName === "frame" && shape.isShapeInFrame(this)) {
1372
+ targetFrame = shape;
1373
+ }
1374
+ });
1375
+ if (this.parentFrame && isDragging) {
1376
+ this.parentFrame.temporarilyRemoveFromFrame(this);
1377
+ }
1378
+ if (hoveredFrameArrow && hoveredFrameArrow !== targetFrame) {
1379
+ hoveredFrameArrow.removeHighlight();
1380
+ }
1381
+ if (targetFrame && targetFrame !== hoveredFrameArrow) {
1382
+ targetFrame.highlightFrame();
1383
+ }
1384
+ hoveredFrameArrow = targetFrame;
1385
+ }
1386
+ isNearAnchor(x, y) {
1387
+ const anchorSize = 10 / currentZoom;
1388
+ for (let i = 0; i < this.anchors.length; i++) {
1389
+ const anchor = this.anchors[i];
1390
+ if (anchor) {
1391
+ const anchorX = parseFloat(anchor.getAttribute("cx"));
1392
+ const anchorY = parseFloat(anchor.getAttribute("cy"));
1393
+ const distance = Math.sqrt(Math.pow(x - anchorX, 2) + Math.pow(y - anchorY, 2));
1394
+ if (distance <= anchorSize) {
1395
+ return { type: "anchor", index: i };
1396
+ }
1397
+ }
1398
+ }
1399
+ return null;
1400
+ }
1401
+ startAnchorDrag(e, index) {
1402
+ e.stopPropagation();
1403
+ e.preventDefault();
1404
+ dragOldPosArrow = {
1405
+ startPoint: { x: this.startPoint.x, y: this.startPoint.y },
1406
+ endPoint: { x: this.endPoint.x, y: this.endPoint.y },
1407
+ controlPoint1: this.controlPoint1 ? { x: this.controlPoint1.x, y: this.controlPoint1.y } : null,
1408
+ controlPoint2: this.controlPoint2 ? { x: this.controlPoint2.x, y: this.controlPoint2.y } : null,
1409
+ attachments: this.getAttachmentState()
1410
+ };
1411
+ const onPointerMove = (event) => {
1412
+ const { x, y } = getSVGCoordsFromMouse(event);
1413
+ if (index === 0 || index === 1) {
1414
+ const nearbyShape = _Arrow.findNearbyShape({ x, y });
1415
+ if (nearbyShape) {
1416
+ const existingPreview = svg.querySelector(".attachment-preview");
1417
+ if (existingPreview) existingPreview.remove();
1418
+ const preview = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1419
+ preview.setAttribute("cx", nearbyShape.attachment.point.x);
1420
+ preview.setAttribute("cy", nearbyShape.attachment.point.y);
1421
+ preview.setAttribute("r", 6);
1422
+ preview.setAttribute("fill", "none");
1423
+ preview.setAttribute("stroke", "#5B57D1");
1424
+ preview.setAttribute("stroke-width", 2);
1425
+ preview.setAttribute("class", "attachment-preview");
1426
+ preview.setAttribute("opacity", "0.7");
1427
+ svg.appendChild(preview);
1428
+ this.updatePosition(index, nearbyShape.attachment.point.x, nearbyShape.attachment.point.y);
1429
+ } else {
1430
+ const existingPreview = svg.querySelector(".attachment-preview");
1431
+ if (existingPreview) existingPreview.remove();
1432
+ this.updatePosition(index, x, y);
1433
+ }
1434
+ } else {
1435
+ this.updatePosition(index, x, y);
1436
+ }
1437
+ };
1438
+ const onPointerUp = () => {
1439
+ const existingPreview = svg.querySelector(".attachment-preview");
1440
+ if (existingPreview) existingPreview.remove();
1441
+ if (index === 0) {
1442
+ const startAttachment = _Arrow.findNearbyShape(this.startPoint);
1443
+ if (startAttachment) {
1444
+ if (this.attachedToStart && this.attachedToStart.shape !== startAttachment.shape) {
1445
+ this.detachFromShape(true);
1446
+ }
1447
+ this.attachToShape(true, startAttachment.shape, startAttachment.attachment);
1448
+ } else {
1449
+ if (this.attachedToStart) {
1450
+ this.detachFromShape(true);
1451
+ }
1452
+ }
1453
+ } else if (index === 1) {
1454
+ const endAttachment = _Arrow.findNearbyShape(this.endPoint);
1455
+ if (endAttachment) {
1456
+ if (this.attachedToEnd && this.attachedToEnd.shape !== endAttachment.shape) {
1457
+ this.detachFromShape(false);
1458
+ }
1459
+ this.attachToShape(false, endAttachment.shape, endAttachment.attachment);
1460
+ } else {
1461
+ if (this.attachedToEnd) {
1462
+ this.detachFromShape(false);
1463
+ }
1464
+ }
1465
+ }
1466
+ if (dragOldPosArrow) {
1467
+ const newPos = {
1468
+ startPoint: { x: this.startPoint.x, y: this.startPoint.y },
1469
+ endPoint: { x: this.endPoint.x, y: this.endPoint.y },
1470
+ controlPoint1: this.controlPoint1 ? { x: this.controlPoint1.x, y: this.controlPoint1.y } : null,
1471
+ controlPoint2: this.controlPoint2 ? { x: this.controlPoint2.x, y: this.controlPoint2.y } : null,
1472
+ attachments: this.getAttachmentState()
1473
+ };
1474
+ const stateChanged = dragOldPosArrow.startPoint.x !== newPos.startPoint.x || dragOldPosArrow.startPoint.y !== newPos.startPoint.y || dragOldPosArrow.endPoint.x !== newPos.endPoint.x || dragOldPosArrow.endPoint.y !== newPos.endPoint.y || JSON.stringify(dragOldPosArrow.attachments) !== JSON.stringify(newPos.attachments);
1475
+ if (stateChanged) {
1476
+ pushTransformAction(this, dragOldPosArrow, newPos);
1477
+ }
1478
+ dragOldPosArrow = null;
1479
+ }
1480
+ svg.removeEventListener("pointermove", onPointerMove);
1481
+ svg.removeEventListener("pointerup", onPointerUp);
1482
+ };
1483
+ svg.addEventListener("pointermove", onPointerMove);
1484
+ svg.addEventListener("pointerup", onPointerUp);
1485
+ }
1486
+ updatePosition(anchorIndex, newViewBoxX, newViewBoxY) {
1487
+ if (anchorIndex === 0) {
1488
+ this.startPoint.x = newViewBoxX;
1489
+ this.startPoint.y = newViewBoxY;
1490
+ } else if (anchorIndex === 1) {
1491
+ this.endPoint.x = newViewBoxX;
1492
+ this.endPoint.y = newViewBoxY;
1493
+ } else if (anchorIndex === 2 && this.arrowCurved === "elbow") {
1494
+ this.elbowX = newViewBoxX;
1495
+ } else if (anchorIndex === 2 && this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
1496
+ const dx = this.endPoint.x - this.startPoint.x;
1497
+ const dy = this.endPoint.y - this.startPoint.y;
1498
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
1499
+ const perpX = -dy / dist;
1500
+ const perpY = dx / dist;
1501
+ const lineMidX = (this.startPoint.x + this.endPoint.x) / 2;
1502
+ const lineMidY = (this.startPoint.y + this.endPoint.y) / 2;
1503
+ const offsetX = newViewBoxX - lineMidX;
1504
+ const offsetY = newViewBoxY - lineMidY;
1505
+ const curveAmount = offsetX * perpX + offsetY * perpY;
1506
+ const t1 = 0.33, t2 = 0.67;
1507
+ this.controlPoint1 = {
1508
+ x: this.startPoint.x + t1 * dx + perpX * curveAmount * (4 / 3),
1509
+ y: this.startPoint.y + t1 * dy + perpY * curveAmount * (4 / 3)
1510
+ };
1511
+ this.controlPoint2 = {
1512
+ x: this.startPoint.x + t2 * dx + perpX * curveAmount * (4 / 3),
1513
+ y: this.startPoint.y + t2 * dy + perpY * curveAmount * (4 / 3)
1514
+ };
1515
+ }
1516
+ this.draw();
1517
+ }
1518
+ contains(viewBoxX, viewBoxY) {
1519
+ const tolerance = Math.max(5, this.options.strokeWidth * 2) / currentZoom;
1520
+ if (this.arrowCurved === "curved" && this.controlPoint1 && this.controlPoint2) {
1521
+ return this.pointToCubicBezierDistance(viewBoxX, viewBoxY) <= tolerance;
1522
+ } else if (this.arrowCurved === "elbow") {
1523
+ const ex = this.elbowX !== null ? this.elbowX : (this.startPoint.x + this.endPoint.x) / 2;
1524
+ const d1 = this.pointToLineDistance(viewBoxX, viewBoxY, this.startPoint.x, this.startPoint.y, ex, this.startPoint.y);
1525
+ const d2 = this.pointToLineDistance(viewBoxX, viewBoxY, ex, this.startPoint.y, ex, this.endPoint.y);
1526
+ const d3 = this.pointToLineDistance(viewBoxX, viewBoxY, ex, this.endPoint.y, this.endPoint.x, this.endPoint.y);
1527
+ return Math.min(d1, d2, d3) <= tolerance;
1528
+ } else {
1529
+ return this.pointToLineDistance(viewBoxX, viewBoxY, this.startPoint.x, this.startPoint.y, this.endPoint.x, this.endPoint.y) <= tolerance;
1530
+ }
1531
+ }
1532
+ pointToCubicBezierDistance(x, y) {
1533
+ let minDistance = Infinity;
1534
+ const steps = 100;
1535
+ for (let i = 0; i <= steps; i++) {
1536
+ const t = i / steps;
1537
+ const point = this.getCubicBezierPoint(t);
1538
+ const distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
1539
+ minDistance = Math.min(minDistance, distance);
1540
+ }
1541
+ return minDistance;
1542
+ }
1543
+ pointToLineDistance(x, y, x1, y1, x2, y2) {
1544
+ const A = x - x1;
1545
+ const B = y - y1;
1546
+ const C = x2 - x1;
1547
+ const D = y2 - y1;
1548
+ const dot = A * C + B * D;
1549
+ const lenSq = C * C + D * D;
1550
+ let param = -1;
1551
+ if (lenSq !== 0) {
1552
+ param = dot / lenSq;
1553
+ }
1554
+ let xx, yy;
1555
+ if (param < 0) {
1556
+ xx = x1;
1557
+ yy = y1;
1558
+ } else if (param > 1) {
1559
+ xx = x2;
1560
+ yy = y2;
1561
+ } else {
1562
+ xx = x1 + param * C;
1563
+ yy = y1 + param * D;
1564
+ }
1565
+ const dx = x - xx;
1566
+ const dy = y - yy;
1567
+ return Math.sqrt(dx * dx + dy * dy);
1568
+ }
1569
+ updateStyle(newOptions) {
1570
+ if (newOptions.arrowOutlineStyle !== void 0) {
1571
+ this.arrowOutlineStyle = newOptions.arrowOutlineStyle;
1572
+ const style = this.arrowOutlineStyle;
1573
+ this.options.strokeDasharray = style === "dashed" ? "10,10" : style === "dotted" ? "2,8" : "";
1574
+ }
1575
+ if (newOptions.arrowHeadStyle !== void 0) {
1576
+ this.arrowHeadStyle = newOptions.arrowHeadStyle;
1577
+ }
1578
+ if (newOptions.arrowCurved !== void 0) {
1579
+ const wasCurved = this.arrowCurved;
1580
+ this.arrowCurved = newOptions.arrowCurved;
1581
+ if (this.arrowCurved === "curved" && wasCurved !== "curved") {
1582
+ this.initializeCurveControlPoints();
1583
+ } else if (this.arrowCurved !== "curved") {
1584
+ this.controlPoint1 = null;
1585
+ this.controlPoint2 = null;
1586
+ }
1587
+ if (this.arrowCurved !== "elbow") {
1588
+ this.elbowX = null;
1589
+ }
1590
+ }
1591
+ if (newOptions.elbowX !== void 0) {
1592
+ this.elbowX = newOptions.elbowX;
1593
+ }
1594
+ if (newOptions.stroke !== void 0) {
1595
+ this.options.stroke = newOptions.stroke;
1596
+ }
1597
+ if (newOptions.strokeWidth !== void 0) {
1598
+ this.options.strokeWidth = parseFloat(newOptions.strokeWidth);
1599
+ }
1600
+ if (newOptions.arrowCurveAmount !== void 0) {
1601
+ this.arrowCurveAmount = newOptions.arrowCurveAmount;
1602
+ if (this.arrowCurved) {
1603
+ this.initializeCurveControlPoints();
1604
+ }
1605
+ }
1606
+ Object.keys(newOptions).forEach((key) => newOptions[key] === void 0 && delete newOptions[key]);
1607
+ this.options = { ...this.options, ...newOptions };
1608
+ if (this.arrowOutlineStyle === "solid" && this.options.strokeDasharray) {
1609
+ delete this.options.strokeDasharray;
1610
+ }
1611
+ this.draw();
1612
+ }
1613
+ updateSidebar() {
1614
+ }
1615
+ destroy() {
1616
+ if (this.group && this.group.parentNode) {
1617
+ this.group.parentNode.removeChild(this.group);
1618
+ }
1619
+ const index = shapes.indexOf(this);
1620
+ if (index > -1) {
1621
+ shapes.splice(index, 1);
1622
+ }
1623
+ if (currentShape === this) {
1624
+ currentShape = null;
1625
+ }
1626
+ }
1627
+ };
1628
+
1629
+ export {
1630
+ Arrow
1631
+ };
1632
+ //# sourceMappingURL=chunk-XISEAGBV.js.map