kritzel-stencil 0.0.162 → 0.0.164

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 (116) hide show
  1. package/dist/cjs/{default-line-tool.config-D1Ns0NmM.js → default-line-tool.config-DJMYrkSu.js} +340 -9
  2. package/dist/cjs/default-line-tool.config-DJMYrkSu.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +1 -1
  4. package/dist/cjs/kritzel-color_22.cjs.entry.js +760 -27
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/collection/classes/core/core.class.js +19 -3
  8. package/dist/collection/classes/core/core.class.js.map +1 -1
  9. package/dist/collection/classes/core/reviver.class.js +16 -0
  10. package/dist/collection/classes/core/reviver.class.js.map +1 -1
  11. package/dist/collection/classes/core/store.class.js +5 -0
  12. package/dist/collection/classes/core/store.class.js.map +1 -1
  13. package/dist/collection/classes/core/viewport.class.js +8 -0
  14. package/dist/collection/classes/core/viewport.class.js.map +1 -1
  15. package/dist/collection/classes/managers/anchor.manager.js +181 -3
  16. package/dist/collection/classes/managers/anchor.manager.js.map +1 -1
  17. package/dist/collection/classes/objects/path.class.js +85 -0
  18. package/dist/collection/classes/objects/path.class.js.map +1 -1
  19. package/dist/collection/classes/objects/shape.class.js +372 -0
  20. package/dist/collection/classes/objects/shape.class.js.map +1 -0
  21. package/dist/collection/classes/registries/icon-registry.class.js +4 -0
  22. package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
  23. package/dist/collection/classes/tools/brush-tool.class.js +2 -2
  24. package/dist/collection/classes/tools/brush-tool.class.js.map +1 -1
  25. package/dist/collection/classes/tools/line-tool.class.js +2 -2
  26. package/dist/collection/classes/tools/line-tool.class.js.map +1 -1
  27. package/dist/collection/classes/tools/shape-tool.class.js +204 -0
  28. package/dist/collection/classes/tools/shape-tool.class.js.map +1 -0
  29. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +17 -6
  30. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
  31. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +44 -8
  32. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  33. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +143 -5
  34. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +76 -12
  35. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js.map +1 -1
  36. package/dist/collection/configs/default-shape-tool.config.js +26 -0
  37. package/dist/collection/configs/default-shape-tool.config.js.map +1 -0
  38. package/dist/collection/enums/shape-type.enum.js +7 -0
  39. package/dist/collection/enums/shape-type.enum.js.map +1 -0
  40. package/dist/collection/helpers/geometry.helper.js +55 -0
  41. package/dist/collection/helpers/geometry.helper.js.map +1 -1
  42. package/dist/collection/interfaces/toolbar-control.interface.js.map +1 -1
  43. package/dist/components/index.js +3 -3
  44. package/dist/components/kritzel-brush-style.js +1 -1
  45. package/dist/components/kritzel-context-menu.js +1 -1
  46. package/dist/components/kritzel-control-brush-config.js +1 -1
  47. package/dist/components/kritzel-control-text-config.js +1 -1
  48. package/dist/components/kritzel-controls.js +1 -1
  49. package/dist/components/kritzel-editor.js +49 -16
  50. package/dist/components/kritzel-editor.js.map +1 -1
  51. package/dist/components/kritzel-engine.js +1 -1
  52. package/dist/components/kritzel-icon.js +1 -1
  53. package/dist/components/kritzel-menu-item.js +1 -1
  54. package/dist/components/kritzel-menu.js +1 -1
  55. package/dist/components/kritzel-split-button.js +1 -1
  56. package/dist/components/kritzel-utility-panel.js +1 -1
  57. package/dist/components/kritzel-workspace-manager.js +1 -1
  58. package/dist/components/{p-Cqr0Bah5.js → p-5OECjGHq.js} +3 -3
  59. package/dist/components/{p-Cqr0Bah5.js.map → p-5OECjGHq.js.map} +1 -1
  60. package/dist/components/{p-CvLFRlQU.js → p-BSBMBjhq.js} +3 -3
  61. package/dist/components/{p-CvLFRlQU.js.map → p-BSBMBjhq.js.map} +1 -1
  62. package/dist/components/{p-7_lwv0zQ.js → p-BuS7MM1j.js} +4 -4
  63. package/dist/components/{p-7_lwv0zQ.js.map → p-BuS7MM1j.js.map} +1 -1
  64. package/dist/components/{p-BixlbUD7.js → p-Cv4BGNPb.js} +6 -2
  65. package/dist/components/p-Cv4BGNPb.js.map +1 -0
  66. package/dist/components/{p-dMCB4tJA.js → p-D1YAsWrL.js} +3 -3
  67. package/dist/components/{p-dMCB4tJA.js.map → p-D1YAsWrL.js.map} +1 -1
  68. package/dist/components/{p-CDteBYm9.js → p-D8L0t-Ro.js} +3 -3
  69. package/dist/components/{p-CDteBYm9.js.map → p-D8L0t-Ro.js.map} +1 -1
  70. package/dist/components/{p-DZ7kxJUx.js → p-DguzZn_x.js} +3 -3
  71. package/dist/components/{p-DZ7kxJUx.js.map → p-DguzZn_x.js.map} +1 -1
  72. package/dist/components/{p-sokRZ7Vn.js → p-Dz2XHHqa.js} +145 -5
  73. package/dist/components/p-Dz2XHHqa.js.map +1 -0
  74. package/dist/components/{p-CuhOrcET.js → p-DzUUppVL.js} +837 -21
  75. package/dist/components/p-DzUUppVL.js.map +1 -0
  76. package/dist/components/{p-CkD1PQQX.js → p-I3iPEDpx.js} +5 -5
  77. package/dist/components/{p-CkD1PQQX.js.map → p-I3iPEDpx.js.map} +1 -1
  78. package/dist/components/{p-DKwJJuFb.js → p-tp96UZ0l.js} +83 -19
  79. package/dist/components/p-tp96UZ0l.js.map +1 -0
  80. package/dist/esm/{default-line-tool.config-C35m-d1Y.js → default-line-tool.config-C35P3XfD.js} +332 -10
  81. package/dist/esm/default-line-tool.config-C35P3XfD.js.map +1 -0
  82. package/dist/esm/index.js +2 -2
  83. package/dist/esm/kritzel-color_22.entry.js +760 -27
  84. package/dist/esm/loader.js +1 -1
  85. package/dist/esm/stencil.js +1 -1
  86. package/dist/stencil/index.esm.js +1 -1
  87. package/dist/stencil/p-3e2b5c42.entry.js +10 -0
  88. package/dist/stencil/p-3e2b5c42.entry.js.map +1 -0
  89. package/dist/stencil/p-C35P3XfD.js +2 -0
  90. package/dist/stencil/p-C35P3XfD.js.map +1 -0
  91. package/dist/stencil/stencil.esm.js +1 -1
  92. package/dist/types/classes/core/core.class.d.ts +1 -0
  93. package/dist/types/classes/core/store.class.d.ts +2 -0
  94. package/dist/types/classes/core/viewport.class.d.ts +6 -0
  95. package/dist/types/classes/managers/anchor.manager.d.ts +20 -0
  96. package/dist/types/classes/objects/path.class.d.ts +7 -0
  97. package/dist/types/classes/objects/shape.class.d.ts +116 -0
  98. package/dist/types/classes/tools/shape-tool.class.d.ts +37 -0
  99. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -2
  100. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +16 -1
  101. package/dist/types/components.d.ts +5 -5
  102. package/dist/types/configs/default-shape-tool.config.d.ts +2 -0
  103. package/dist/types/enums/shape-type.enum.d.ts +5 -0
  104. package/dist/types/helpers/geometry.helper.d.ts +21 -0
  105. package/dist/types/interfaces/toolbar-control.interface.d.ts +21 -3
  106. package/package.json +1 -1
  107. package/dist/cjs/default-line-tool.config-D1Ns0NmM.js.map +0 -1
  108. package/dist/components/p-BixlbUD7.js.map +0 -1
  109. package/dist/components/p-CuhOrcET.js.map +0 -1
  110. package/dist/components/p-DKwJJuFb.js.map +0 -1
  111. package/dist/components/p-sokRZ7Vn.js.map +0 -1
  112. package/dist/esm/default-line-tool.config-C35m-d1Y.js.map +0 -1
  113. package/dist/stencil/p-C35m-d1Y.js +0 -2
  114. package/dist/stencil/p-C35m-d1Y.js.map +0 -1
  115. package/dist/stencil/p-d142ef46.entry.js +0 -10
  116. package/dist/stencil/p-d142ef46.entry.js.map +0 -1
@@ -121,6 +121,61 @@ class KritzelGeometryHelper {
121
121
  }
122
122
  return closestIntersection;
123
123
  }
124
+ /**
125
+ * Generates a polygon approximation of an ellipse.
126
+ * @param centerX - X coordinate of ellipse center
127
+ * @param centerY - Y coordinate of ellipse center
128
+ * @param rx - Horizontal radius
129
+ * @param ry - Vertical radius
130
+ * @param segments - Number of segments (more = smoother approximation)
131
+ * @param rotation - Optional rotation angle in radians
132
+ */
133
+ static getEllipsePolygonApproximation(centerX, centerY, rx, ry, segments = 32, rotation = 0) {
134
+ const points = [];
135
+ const cos = Math.cos(rotation);
136
+ const sin = Math.sin(rotation);
137
+ for (let i = 0; i < segments; i++) {
138
+ const angle = (2 * Math.PI * i) / segments;
139
+ // Point on unrotated ellipse
140
+ const px = rx * Math.cos(angle);
141
+ const py = ry * Math.sin(angle);
142
+ // Apply rotation around center
143
+ const rotatedX = centerX + px * cos - py * sin;
144
+ const rotatedY = centerY + px * sin + py * cos;
145
+ points.push({ x: rotatedX, y: rotatedY });
146
+ }
147
+ return points;
148
+ }
149
+ /**
150
+ * Finds the closest intersection point between a line segment and a polygon
151
+ * defined as an array of points. Returns the intersection closest to lineStart,
152
+ * or null if no intersection.
153
+ */
154
+ static getLinePointsArrayIntersection(lineStart, lineEnd, polygonPoints) {
155
+ let closestIntersection = null;
156
+ let closestDistance = Infinity;
157
+ for (let i = 0; i < polygonPoints.length; i++) {
158
+ const edgeStart = polygonPoints[i];
159
+ const edgeEnd = polygonPoints[(i + 1) % polygonPoints.length];
160
+ const intersection = this.getLineIntersectionPoint(lineStart, lineEnd, edgeStart, edgeEnd);
161
+ if (intersection) {
162
+ const distance = Math.sqrt(Math.pow(intersection.x - lineStart.x, 2) +
163
+ Math.pow(intersection.y - lineStart.y, 2));
164
+ if (distance < closestDistance) {
165
+ closestDistance = distance;
166
+ closestIntersection = intersection;
167
+ }
168
+ }
169
+ }
170
+ return closestIntersection;
171
+ }
172
+ /**
173
+ * Checks if a point is inside a polygon defined as an array of points.
174
+ * This is a convenience wrapper that works with arbitrary polygon point arrays.
175
+ */
176
+ static isPointInPolygonPoints(point, polygonPoints) {
177
+ return this.isPointInPolygon(point, polygonPoints);
178
+ }
124
179
  }
125
180
 
126
181
  class KritzelBaseObject {
@@ -14439,6 +14494,91 @@ class KritzelPath extends KritzelBaseObject {
14439
14494
  const scaledHeight = this.boundingBox.height * viewportScale;
14440
14495
  return (scaledWidth * scaledHeight) < 500;
14441
14496
  }
14497
+ /**
14498
+ * Finds the point where a line from outsidePoint to the path's center
14499
+ * intersects the stroke edge. Returns null if no intersection found.
14500
+ * This is used for arrow clipping at the actual stroke edge.
14501
+ */
14502
+ getClipPoint(outsidePoint) {
14503
+ const centerX = this.centerX;
14504
+ const centerY = this.centerY;
14505
+ const halfStroke = this.strokeWidth / this.scale / 2;
14506
+ // Ensure adjusted points are computed
14507
+ if (this._adjustedPoints === null) {
14508
+ this._adjustedPoints = this.computeAdjustedPoints();
14509
+ }
14510
+ if (this._adjustedPoints.length < 1) {
14511
+ return null;
14512
+ }
14513
+ // Sample along the line from outsidePoint to center to find stroke edge
14514
+ const steps = 32;
14515
+ let prevOutside = true;
14516
+ for (let i = 1; i <= steps; i++) {
14517
+ const t = i / steps;
14518
+ const sampleX = outsidePoint.x + (centerX - outsidePoint.x) * t;
14519
+ const sampleY = outsidePoint.y + (centerY - outsidePoint.y) * t;
14520
+ // Check distance to path stroke
14521
+ let minDistance = Infinity;
14522
+ if (this._adjustedPoints.length === 1) {
14523
+ const p = this._adjustedPoints[0];
14524
+ const dx = sampleX - p[0];
14525
+ const dy = sampleY - p[1];
14526
+ minDistance = Math.sqrt(dx * dx + dy * dy);
14527
+ }
14528
+ else {
14529
+ for (let j = 0; j < this._adjustedPoints.length - 1; j++) {
14530
+ const p1 = this._adjustedPoints[j];
14531
+ const p2 = this._adjustedPoints[j + 1];
14532
+ const dist = this.pointToLineSegmentDistance(sampleX, sampleY, p1[0], p1[1], p2[0], p2[1]);
14533
+ if (dist < minDistance) {
14534
+ minDistance = dist;
14535
+ }
14536
+ }
14537
+ }
14538
+ const isInside = minDistance <= halfStroke;
14539
+ // Found the edge: transition from outside to inside
14540
+ if (prevOutside && isInside) {
14541
+ // Refine the intersection point using binary search
14542
+ let tLow = (i - 1) / steps;
14543
+ let tHigh = t;
14544
+ for (let k = 0; k < 8; k++) {
14545
+ const tMid = (tLow + tHigh) / 2;
14546
+ const midX = outsidePoint.x + (centerX - outsidePoint.x) * tMid;
14547
+ const midY = outsidePoint.y + (centerY - outsidePoint.y) * tMid;
14548
+ let midDist = Infinity;
14549
+ if (this._adjustedPoints.length === 1) {
14550
+ const p = this._adjustedPoints[0];
14551
+ const dx = midX - p[0];
14552
+ const dy = midY - p[1];
14553
+ midDist = Math.sqrt(dx * dx + dy * dy);
14554
+ }
14555
+ else {
14556
+ for (let j = 0; j < this._adjustedPoints.length - 1; j++) {
14557
+ const p1 = this._adjustedPoints[j];
14558
+ const p2 = this._adjustedPoints[j + 1];
14559
+ const dist = this.pointToLineSegmentDistance(midX, midY, p1[0], p1[1], p2[0], p2[1]);
14560
+ if (dist < midDist) {
14561
+ midDist = dist;
14562
+ }
14563
+ }
14564
+ }
14565
+ if (midDist <= halfStroke) {
14566
+ tHigh = tMid;
14567
+ }
14568
+ else {
14569
+ tLow = tMid;
14570
+ }
14571
+ }
14572
+ const finalT = (tLow + tHigh) / 2;
14573
+ return {
14574
+ x: outsidePoint.x + (centerX - outsidePoint.x) * finalT,
14575
+ y: outsidePoint.y + (centerY - outsidePoint.y) * finalT,
14576
+ };
14577
+ }
14578
+ prevOutside = !isInside;
14579
+ }
14580
+ return null;
14581
+ }
14442
14582
  }
14443
14583
 
14444
14584
  class KritzelImage extends KritzelBaseObject {
@@ -15223,6 +15363,7 @@ class KritzelBrushTool extends KritzelBaseTool {
15223
15363
  });
15224
15364
  updatedPath.id = currentPath.id;
15225
15365
  updatedPath.workspaceId = currentPath.workspaceId;
15366
+ updatedPath.zIndex = currentPath.zIndex;
15226
15367
  updatedPath.isCompleted = false;
15227
15368
  this._core.store.state.objects.update(updatedPath);
15228
15369
  }
@@ -15245,6 +15386,7 @@ class KritzelBrushTool extends KritzelBaseTool {
15245
15386
  });
15246
15387
  updatedPath.id = currentPath.id;
15247
15388
  updatedPath.workspaceId = currentPath.workspaceId;
15389
+ updatedPath.zIndex = currentPath.zIndex;
15248
15390
  updatedPath.isCompleted = false;
15249
15391
  this._core.store.state.objects.update(updatedPath);
15250
15392
  }
@@ -15261,7 +15403,6 @@ class KritzelBrushTool extends KritzelBaseTool {
15261
15403
  const currentPath = this._core.store.currentPath;
15262
15404
  if (currentPath) {
15263
15405
  currentPath.isCompleted = true;
15264
- currentPath.zIndex = this._core.store.currentZIndex;
15265
15406
  this._core.store.state.objects.update(currentPath);
15266
15407
  this._core.engine.emitObjectsChange();
15267
15408
  }
@@ -15273,7 +15414,6 @@ class KritzelBrushTool extends KritzelBaseTool {
15273
15414
  const currentPath = this._core.store.currentPath;
15274
15415
  if (currentPath) {
15275
15416
  currentPath.isCompleted = true;
15276
- currentPath.zIndex = this._core.store.currentZIndex;
15277
15417
  this._core.store.state.objects.update(currentPath);
15278
15418
  this._core.engine.emitObjectsChange();
15279
15419
  }
@@ -15673,6 +15813,7 @@ class KritzelLineTool extends KritzelBaseTool {
15673
15813
  });
15674
15814
  updatedLine.id = currentLine.id;
15675
15815
  updatedLine.workspaceId = currentLine.workspaceId;
15816
+ updatedLine.zIndex = currentLine.zIndex;
15676
15817
  updatedLine.isCompleted = false;
15677
15818
  this._core.store.state.objects.update(updatedLine);
15678
15819
  }
@@ -15699,6 +15840,7 @@ class KritzelLineTool extends KritzelBaseTool {
15699
15840
  });
15700
15841
  updatedLine.id = currentLine.id;
15701
15842
  updatedLine.workspaceId = currentLine.workspaceId;
15843
+ updatedLine.zIndex = currentLine.zIndex;
15702
15844
  updatedLine.isCompleted = false;
15703
15845
  this._core.store.state.objects.update(updatedLine);
15704
15846
  }
@@ -15715,7 +15857,6 @@ class KritzelLineTool extends KritzelBaseTool {
15715
15857
  const currentLine = this._core.store.currentLine;
15716
15858
  if (currentLine) {
15717
15859
  currentLine.isCompleted = true;
15718
- currentLine.zIndex = this._core.store.currentZIndex;
15719
15860
  this._core.store.state.objects.update(currentLine);
15720
15861
  this._core.engine.emitObjectsChange();
15721
15862
  // Switch to selection tool and select the drawn line
@@ -15729,7 +15870,6 @@ class KritzelLineTool extends KritzelBaseTool {
15729
15870
  const currentLine = this._core.store.currentLine;
15730
15871
  if (currentLine) {
15731
15872
  currentLine.isCompleted = true;
15732
- currentLine.zIndex = this._core.store.currentZIndex;
15733
15873
  this._core.store.state.objects.update(currentLine);
15734
15874
  this._core.engine.emitObjectsChange();
15735
15875
  // Switch to selection tool and select the drawn line
@@ -16057,6 +16197,10 @@ KritzelIconRegistry.registerIcons({
16057
16197
  'highlighter': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-highlighter-icon lucide-highlighter"><path d="m9 11-6 6v3h9l3-3"/><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"/></svg>',
16058
16198
  'eraser': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>',
16059
16199
  'type': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" x2="15" y1="20" y2="20"/><line x1="12" x2="12" y1="4" y2="20"/></svg>',
16200
+ 'shapes': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z"/><rect x="3" y="14" width="7" height="7" rx="1"/><circle cx="17.5" cy="17.5" r="3.5"/></svg>',
16201
+ 'shape-rectangle': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>',
16202
+ 'shape-ellipse': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="12" rx="10" ry="8"/></svg>',
16203
+ 'shape-triangle': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3L22 21H2L12 3Z"/></svg>',
16060
16204
  'image': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
16061
16205
  'chevron-down': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>',
16062
16206
  'chevron-up': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>',
@@ -33134,7 +33278,34 @@ class KritzelAnchorManager {
33134
33278
  const reference = endpoint === 'start'
33135
33279
  ? this.lineLocalToWorld(line, line.endX, line.endY)
33136
33280
  : this.lineLocalToWorld(line, line.startX, line.startY);
33137
- const edgeIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
33281
+ // Check for path object (uses stroke edge detection)
33282
+ const pathClipPoint = this.getPathClipPoint(targetObject, reference);
33283
+ if (pathClipPoint) {
33284
+ const localEdge = this.lineWorldToLocal(line, pathClipPoint.x, pathClipPoint.y);
33285
+ const totalLength = Math.sqrt(Math.pow(line.endX - line.startX, 2) +
33286
+ Math.pow(line.endY - line.startY, 2));
33287
+ const distanceFromStart = Math.sqrt(Math.pow(localEdge.x - line.startX, 2) +
33288
+ Math.pow(localEdge.y - line.startY, 2));
33289
+ const t = totalLength > 0 ? distanceFromStart / totalLength : endpoint === 'start' ? 0 : 1;
33290
+ return {
33291
+ localX: localEdge.x,
33292
+ localY: localEdge.y,
33293
+ worldX: pathClipPoint.x,
33294
+ worldY: pathClipPoint.y,
33295
+ t,
33296
+ };
33297
+ }
33298
+ // Check for custom clip polygon (ellipse, triangle shapes)
33299
+ const customClipPolygon = this.getClipPolygonForObject(targetObject);
33300
+ let edgeIntersection;
33301
+ if (customClipPolygon) {
33302
+ // Use custom polygon for non-rectangular shapes
33303
+ edgeIntersection = KritzelGeometryHelper.getLinePointsArrayIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, customClipPolygon);
33304
+ }
33305
+ else {
33306
+ // Fall back to bounding box for rectangles and other objects
33307
+ edgeIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
33308
+ }
33138
33309
  if (!edgeIntersection) {
33139
33310
  return null;
33140
33311
  }
@@ -33153,7 +33324,15 @@ class KritzelAnchorManager {
33153
33324
  };
33154
33325
  }
33155
33326
  computeCurvedClipInfo(line, endpoint, targetObject) {
33156
- const polygonPoints = this.getPolygonPoints(targetObject.rotatedPolygon);
33327
+ // Check for path object (uses stroke edge detection via sampling)
33328
+ if (KritzelClassHelper.isInstanceOf(targetObject, 'KritzelPath')) {
33329
+ // For curved lines to paths, sample along the curve to find stroke edge
33330
+ const path = targetObject;
33331
+ return this.findCurveExitPointForPath(line, endpoint, path);
33332
+ }
33333
+ // Check for custom clip polygon (ellipse, triangle shapes)
33334
+ const customClipPolygon = this.getClipPolygonForObject(targetObject);
33335
+ const polygonPoints = customClipPolygon ?? this.getPolygonPoints(targetObject.rotatedPolygon);
33157
33336
  const exitPoint = this.findCurveExitPoint(line, endpoint, polygonPoints);
33158
33337
  if (exitPoint) {
33159
33338
  return exitPoint;
@@ -33161,7 +33340,13 @@ class KritzelAnchorManager {
33161
33340
  const reference = endpoint === 'start'
33162
33341
  ? this.lineLocalToWorld(line, line.endX, line.endY)
33163
33342
  : this.lineLocalToWorld(line, line.startX, line.startY);
33164
- const fallbackIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
33343
+ let fallbackIntersection;
33344
+ if (customClipPolygon) {
33345
+ fallbackIntersection = KritzelGeometryHelper.getLinePointsArrayIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, customClipPolygon);
33346
+ }
33347
+ else {
33348
+ fallbackIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
33349
+ }
33165
33350
  if (!fallbackIntersection) {
33166
33351
  return null;
33167
33352
  }
@@ -33201,6 +33386,119 @@ class KritzelAnchorManager {
33201
33386
  }
33202
33387
  return null;
33203
33388
  }
33389
+ /**
33390
+ * Finds the exit point for a curved line going to a Path object.
33391
+ * Uses distance-to-stroke sampling instead of polygon containment.
33392
+ */
33393
+ findCurveExitPointForPath(line, endpoint, path) {
33394
+ const steps = 64;
33395
+ const initialT = endpoint === 'start' ? 0 : 1;
33396
+ const halfStroke = path.strokeWidth / path.scale / 2;
33397
+ // Ensure path has computed points
33398
+ const pathPoints = this.getPathAdjustedPoints(path);
33399
+ if (pathPoints.length < 1) {
33400
+ return null;
33401
+ }
33402
+ // Check if starting point is inside the stroke
33403
+ const initialSample = this.evaluateLineAtT(line, initialT);
33404
+ let prevInside = this.isPointInPathStroke(initialSample.worldX, initialSample.worldY, pathPoints, halfStroke);
33405
+ let prevT = initialT;
33406
+ for (let i = 1; i <= steps; i++) {
33407
+ const t = endpoint === 'start' ? i / steps : 1 - i / steps;
33408
+ const sample = this.evaluateLineAtT(line, t);
33409
+ const inside = this.isPointInPathStroke(sample.worldX, sample.worldY, pathPoints, halfStroke);
33410
+ if (prevInside && !inside) {
33411
+ // Refine using binary search
33412
+ const refinedT = this.refineCurveExitParameterForPath(line, pathPoints, halfStroke, prevT, t);
33413
+ const refinedPoint = this.evaluateLineAtT(line, refinedT);
33414
+ return {
33415
+ localX: refinedPoint.localX,
33416
+ localY: refinedPoint.localY,
33417
+ worldX: refinedPoint.worldX,
33418
+ worldY: refinedPoint.worldY,
33419
+ t: refinedT,
33420
+ };
33421
+ }
33422
+ prevInside = inside;
33423
+ prevT = t;
33424
+ }
33425
+ return null;
33426
+ }
33427
+ refineCurveExitParameterForPath(line, pathPoints, halfStroke, insideT, outsideT) {
33428
+ let tInside = insideT;
33429
+ let tOutside = outsideT;
33430
+ for (let i = 0; i < 8; i++) {
33431
+ const mid = (tInside + tOutside) / 2;
33432
+ const sample = this.evaluateLineAtT(line, mid);
33433
+ const inside = this.isPointInPathStroke(sample.worldX, sample.worldY, pathPoints, halfStroke);
33434
+ if (inside) {
33435
+ tInside = mid;
33436
+ }
33437
+ else {
33438
+ tOutside = mid;
33439
+ }
33440
+ }
33441
+ return (tInside + tOutside) / 2;
33442
+ }
33443
+ getPathAdjustedPoints(path) {
33444
+ // Access the path's adjusted points through its internal computation
33445
+ // We'll need to manually compute them since they're private
33446
+ return this.computePathAdjustedPoints(path);
33447
+ }
33448
+ computePathAdjustedPoints(path) {
33449
+ if (!path.points?.length) {
33450
+ return [];
33451
+ }
33452
+ const angle = path.rotation;
33453
+ const cos = Math.cos(angle);
33454
+ const sin = Math.sin(angle);
33455
+ const xs = path.points.map(p => p[0]);
33456
+ const ys = path.points.map(p => p[1]);
33457
+ const pivot = {
33458
+ x: (Math.min(...xs) + Math.max(...xs)) / 2,
33459
+ y: (Math.min(...ys) + Math.max(...ys)) / 2,
33460
+ };
33461
+ const { x: cx, y: cy } = pivot;
33462
+ const rotatedPoints = path.points.map(([x, y]) => {
33463
+ const dx = x - cx;
33464
+ const dy = y - cy;
33465
+ return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
33466
+ });
33467
+ return rotatedPoints.map(([px, py]) => [
33468
+ Math.abs(px - path.x) / path.scale + path.translateX,
33469
+ Math.abs(py - path.y) / path.scale + path.translateY,
33470
+ ]);
33471
+ }
33472
+ isPointInPathStroke(x, y, pathPoints, halfStroke) {
33473
+ if (pathPoints.length === 1) {
33474
+ const p = pathPoints[0];
33475
+ const dx = x - p[0];
33476
+ const dy = y - p[1];
33477
+ return Math.sqrt(dx * dx + dy * dy) <= halfStroke;
33478
+ }
33479
+ for (let i = 0; i < pathPoints.length - 1; i++) {
33480
+ const p1 = pathPoints[i];
33481
+ const p2 = pathPoints[i + 1];
33482
+ const dist = this.pointToSegmentDistance(x, y, p1[0], p1[1], p2[0], p2[1]);
33483
+ if (dist <= halfStroke) {
33484
+ return true;
33485
+ }
33486
+ }
33487
+ return false;
33488
+ }
33489
+ pointToSegmentDistance(px, py, x1, y1, x2, y2) {
33490
+ const dx = x2 - x1;
33491
+ const dy = y2 - y1;
33492
+ const lengthSquared = dx * dx + dy * dy;
33493
+ if (lengthSquared === 0) {
33494
+ return Math.sqrt((px - x1) * (px - x1) + (py - y1) * (py - y1));
33495
+ }
33496
+ let t = ((px - x1) * dx + (py - y1) * dy) / lengthSquared;
33497
+ t = Math.max(0, Math.min(1, t));
33498
+ const nearestX = x1 + t * dx;
33499
+ const nearestY = y1 + t * dy;
33500
+ return Math.sqrt((px - nearestX) * (px - nearestX) + (py - nearestY) * (py - nearestY));
33501
+ }
33204
33502
  refineCurveExitParameter(line, polygonPoints, insideT, outsideT) {
33205
33503
  let tInside = insideT;
33206
33504
  let tOutside = outsideT;
@@ -33334,6 +33632,30 @@ class KritzelAnchorManager {
33334
33632
  getPolygonPoints(polygon) {
33335
33633
  return [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
33336
33634
  }
33635
+ /**
33636
+ * Gets the custom clip polygon for an object if it has one.
33637
+ * Returns null for objects that should use the default rotatedPolygon.
33638
+ */
33639
+ getClipPolygonForObject(object) {
33640
+ // Check if the object is a KritzelShape with a custom clip polygon
33641
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelShape')) {
33642
+ const shape = object;
33643
+ return shape.getClipPolygon();
33644
+ }
33645
+ // Path objects use getPathClipPoint instead of polygon
33646
+ return null;
33647
+ }
33648
+ /**
33649
+ * Gets the clip point for a Path object (stroke edge intersection).
33650
+ * Returns null if the object is not a Path or no intersection found.
33651
+ */
33652
+ getPathClipPoint(object, outsidePoint) {
33653
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelPath')) {
33654
+ const path = object;
33655
+ return path.getClipPoint(outsidePoint);
33656
+ }
33657
+ return null;
33658
+ }
33337
33659
  /**
33338
33660
  * Converts local line coordinates to world coordinates.
33339
33661
  */
@@ -33511,7 +33833,7 @@ const DEFAULT_LINE_TOOL_CONFIG = {
33511
33833
  },
33512
33834
  };
33513
33835
 
33514
- export { KritzelKeyboardHelper as $, isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelPath as E, KritzelImage as F, KritzelLine as G, HocuspocusProvider as H, KritzelBrushTool as I, KritzelLineTool as J, KritzelText as K, KritzelEraserTool as L, KritzelImageTool as M, KritzelTextTool as N, Observable$1 as O, KritzelCursorHelper as P, KritzelSelectionTool as Q, IndexedDBSyncProvider as R, KritzelAppStateMap as S, KritzelWorkspace as T, KritzelAnchorManager as U, DEFAULT_BRUSH_CONFIG as V, DEFAULT_TEXT_CONFIG as W, DEFAULT_LINE_TOOL_CONFIG as X, KritzelDevicesHelper as Y, KritzelMouseButton as Z, KritzelIconRegistry as _, writeVarUint8Array$2 as a, KritzelBaseHandler as a0, KritzelToolRegistry as a1, KritzelSelectionBox as a2, KritzelSelectionGroup as a3, KritzelBaseObject as a4, Doc as a5, DEFAULT_SYNC_CONFIG as a6, UndoManager as a7, ObjectHelper as a8, KritzelEventHelper as a9, KritzelBaseTool as aa, KritzelClassHelper as ab, readVarUint8Array$2 as b, applyUpdate as c, encodeStateVector as d, encodeStateAsUpdate as e, createEncoder$1 as f, createDecoder$1 as g, create$8 as h, fromBase64 as i, toBase64 as j, createUint8ArrayFromArrayBuffer as k, offChange as l, readVarString$2 as m, floor$2 as n, onChange as o, getUnixTime$1 as p, equalityDeep$1 as q, readVarUint$2 as r, setIfUndefined$1 as s, toUint8Array$1 as t, writeVarString$2 as u, varStorage as v, writeVarUint$2 as w, map as x, ObservableV2 as y, length$3 as z };
33515
- //# sourceMappingURL=default-line-tool.config-C35m-d1Y.js.map
33836
+ export { Schema as $, isNode as A, min$2 as B, pow as C, HocuspocusProviderWebsocket as D, KritzelPath as E, KritzelImage as F, KritzelLine as G, HocuspocusProvider as H, KritzelBrushTool as I, KritzelLineTool as J, KritzelText as K, KritzelEraserTool as L, KritzelImageTool as M, KritzelTextTool as N, Observable$1 as O, KritzelCursorHelper as P, KritzelSelectionTool as Q, IndexedDBSyncProvider as R, KritzelAppStateMap as S, KritzelWorkspace as T, KritzelAnchorManager as U, DEFAULT_BRUSH_CONFIG as V, DEFAULT_TEXT_CONFIG as W, DEFAULT_LINE_TOOL_CONFIG as X, KritzelDevicesHelper as Y, KritzelMouseButton as Z, KritzelBaseObject as _, writeVarUint8Array$2 as a, schema as a0, addListNodes as a1, EditorView as a2, EditorState as a3, keymap as a4, TextSelection as a5, KritzelKeyboardHelper as a6, KritzelToolRegistry as a7, KritzelGeometryHelper as a8, baseKeymap as a9, KritzelBaseTool as aa, KritzelEventHelper as ab, KritzelIconRegistry as ac, KritzelBaseHandler as ad, KritzelSelectionBox as ae, KritzelSelectionGroup as af, Doc as ag, DEFAULT_SYNC_CONFIG as ah, UndoManager as ai, ObjectHelper as aj, KritzelClassHelper as ak, readVarUint8Array$2 as b, applyUpdate as c, encodeStateVector as d, encodeStateAsUpdate as e, createEncoder$1 as f, createDecoder$1 as g, create$8 as h, fromBase64 as i, toBase64 as j, createUint8ArrayFromArrayBuffer as k, offChange as l, readVarString$2 as m, floor$2 as n, onChange as o, getUnixTime$1 as p, equalityDeep$1 as q, readVarUint$2 as r, setIfUndefined$1 as s, toUint8Array$1 as t, writeVarString$2 as u, varStorage as v, writeVarUint$2 as w, map as x, ObservableV2 as y, length$3 as z };
33837
+ //# sourceMappingURL=default-line-tool.config-C35P3XfD.js.map
33516
33838
 
33517
- //# sourceMappingURL=default-line-tool.config-C35m-d1Y.js.map
33839
+ //# sourceMappingURL=default-line-tool.config-C35P3XfD.js.map