@excalidraw/excalidraw 0.17.1-1d71f84 → 0.17.1-4689a6b

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 (111) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/dist/browser/dev/excalidraw-assets-dev/{chunk-AK7SWNLN.js → chunk-23CKV3WP.js} +4 -2
  3. package/dist/browser/dev/excalidraw-assets-dev/chunk-23CKV3WP.js.map +7 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/{chunk-RWZVJAQU.js → chunk-7D5BMEAB.js} +2227 -1976
  5. package/dist/browser/dev/excalidraw-assets-dev/chunk-7D5BMEAB.js.map +7 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js → en-W7TECCRB.js} +2 -2
  7. package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js → image-JKT6GXZD.js} +2 -2
  8. package/dist/browser/dev/index.css +20 -0
  9. package/dist/browser/dev/index.css.map +2 -2
  10. package/dist/browser/dev/index.js +770 -585
  11. package/dist/browser/dev/index.js.map +4 -4
  12. package/dist/browser/prod/excalidraw-assets/chunk-DWOM5R6H.js +55 -0
  13. package/dist/browser/prod/excalidraw-assets/{chunk-CTYINSWT.js → chunk-SK23VHAR.js} +2 -2
  14. package/dist/browser/prod/excalidraw-assets/{en-LROPV2RN.js → en-SMMH575S.js} +1 -1
  15. package/dist/browser/prod/excalidraw-assets/image-WDEQS5RL.js +1 -0
  16. package/dist/browser/prod/index.css +1 -1
  17. package/dist/browser/prod/index.js +22 -22
  18. package/dist/{prod/en-II4GK66F.json → dev/en-CVBEBUBY.json} +3 -1
  19. package/dist/dev/index.css +20 -0
  20. package/dist/dev/index.css.map +2 -2
  21. package/dist/dev/index.js +2383 -2074
  22. package/dist/dev/index.js.map +4 -4
  23. package/dist/excalidraw/actions/actionBoundText.js +4 -1
  24. package/dist/excalidraw/actions/actionCanvas.js +3 -1
  25. package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -0
  26. package/dist/excalidraw/actions/actionExport.d.ts +1 -1
  27. package/dist/excalidraw/actions/actionFinalize.d.ts +1 -1
  28. package/dist/excalidraw/actions/actionFinalize.js +3 -3
  29. package/dist/excalidraw/actions/actionFlip.d.ts +3 -3
  30. package/dist/excalidraw/actions/actionFlip.js +6 -6
  31. package/dist/excalidraw/actions/actionGroup.js +4 -2
  32. package/dist/excalidraw/actions/actionHistory.js +3 -0
  33. package/dist/excalidraw/actions/actionZindex.d.ts +11 -11
  34. package/dist/excalidraw/actions/shortcuts.js +1 -1
  35. package/dist/excalidraw/analytics.js +1 -1
  36. package/dist/excalidraw/components/App.d.ts +13 -3
  37. package/dist/excalidraw/components/App.js +212 -83
  38. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +24 -10
  39. package/dist/excalidraw/components/DarkModeToggle.js +3 -1
  40. package/dist/excalidraw/components/HelpDialog.js +8 -6
  41. package/dist/excalidraw/components/RadioGroup.d.ts +2 -1
  42. package/dist/excalidraw/components/RadioGroup.js +1 -1
  43. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.js +6 -2
  44. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +18 -0
  45. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.js +9 -0
  46. package/dist/excalidraw/components/hyperlink/Hyperlink.js +3 -3
  47. package/dist/excalidraw/components/hyperlink/helpers.js +2 -3
  48. package/dist/excalidraw/components/icons.d.ts +3 -0
  49. package/dist/excalidraw/components/icons.js +5 -1
  50. package/dist/excalidraw/components/main-menu/DefaultItems.d.ts +12 -2
  51. package/dist/excalidraw/components/main-menu/DefaultItems.js +38 -7
  52. package/dist/excalidraw/constants.d.ts +0 -3
  53. package/dist/excalidraw/constants.js +0 -3
  54. package/dist/excalidraw/data/magic.js +2 -1
  55. package/dist/excalidraw/data/reconcile.d.ts +6 -0
  56. package/dist/excalidraw/data/reconcile.js +49 -0
  57. package/dist/excalidraw/data/restore.d.ts +3 -3
  58. package/dist/excalidraw/data/restore.js +5 -6
  59. package/dist/excalidraw/data/transform.d.ts +1 -1
  60. package/dist/excalidraw/data/transform.js +12 -3
  61. package/dist/excalidraw/element/binding.d.ts +22 -9
  62. package/dist/excalidraw/element/binding.js +403 -26
  63. package/dist/excalidraw/element/bounds.d.ts +0 -1
  64. package/dist/excalidraw/element/bounds.js +0 -3
  65. package/dist/excalidraw/element/collision.d.ts +14 -19
  66. package/dist/excalidraw/element/collision.js +36 -713
  67. package/dist/excalidraw/element/embeddable.js +18 -43
  68. package/dist/excalidraw/element/index.d.ts +0 -1
  69. package/dist/excalidraw/element/index.js +0 -1
  70. package/dist/excalidraw/element/linearElementEditor.d.ts +10 -10
  71. package/dist/excalidraw/element/linearElementEditor.js +6 -4
  72. package/dist/excalidraw/element/newElement.d.ts +1 -1
  73. package/dist/excalidraw/element/newElement.js +2 -1
  74. package/dist/excalidraw/element/textElement.d.ts +0 -1
  75. package/dist/excalidraw/element/textElement.js +0 -30
  76. package/dist/excalidraw/element/types.d.ts +17 -2
  77. package/dist/excalidraw/errors.d.ts +3 -0
  78. package/dist/excalidraw/errors.js +3 -0
  79. package/dist/excalidraw/fractionalIndex.d.ts +40 -0
  80. package/dist/excalidraw/fractionalIndex.js +241 -0
  81. package/dist/excalidraw/frame.d.ts +1 -1
  82. package/dist/excalidraw/hooks/useCreatePortalContainer.js +2 -1
  83. package/dist/excalidraw/locales/en.json +3 -1
  84. package/dist/excalidraw/renderer/helpers.js +2 -2
  85. package/dist/excalidraw/renderer/interactiveScene.js +1 -1
  86. package/dist/excalidraw/renderer/renderElement.js +3 -3
  87. package/dist/excalidraw/renderer/renderSnaps.js +2 -1
  88. package/dist/excalidraw/scene/Scene.d.ts +7 -6
  89. package/dist/excalidraw/scene/Scene.js +28 -13
  90. package/dist/excalidraw/scene/export.js +4 -3
  91. package/dist/excalidraw/types.d.ts +4 -3
  92. package/dist/excalidraw/utils.d.ts +1 -0
  93. package/dist/excalidraw/utils.js +1 -0
  94. package/dist/excalidraw/zindex.d.ts +2 -2
  95. package/dist/excalidraw/zindex.js +9 -13
  96. package/dist/{dev/en-II4GK66F.json → prod/en-CVBEBUBY.json} +3 -1
  97. package/dist/prod/index.css +1 -1
  98. package/dist/prod/index.js +36 -36
  99. package/dist/utils/collision.d.ts +4 -0
  100. package/dist/utils/collision.js +48 -0
  101. package/dist/utils/geometry/geometry.d.ts +71 -0
  102. package/dist/utils/geometry/geometry.js +674 -0
  103. package/dist/utils/geometry/shape.d.ts +55 -0
  104. package/dist/utils/geometry/shape.js +149 -0
  105. package/package.json +2 -1
  106. package/dist/browser/dev/excalidraw-assets-dev/chunk-AK7SWNLN.js.map +0 -7
  107. package/dist/browser/dev/excalidraw-assets-dev/chunk-RWZVJAQU.js.map +0 -7
  108. package/dist/browser/prod/excalidraw-assets/chunk-LL4GORAM.js +0 -55
  109. package/dist/browser/prod/excalidraw-assets/image-EFCJDJH3.js +0 -1
  110. /package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js.map → en-W7TECCRB.js.map} +0 -0
  111. /package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js.map → image-JKT6GXZD.js.map} +0 -0
@@ -5127,7 +5127,6 @@ var ROUNDNESS = {
5127
5127
  // (see DEFAULT_ADAPTIVE_RADIUS constant)
5128
5128
  ADAPTIVE_RADIUS: 3
5129
5129
  };
5130
- var PRECEDING_ELEMENT_KEY = "__precedingElement__";
5131
5130
  var ROUGHNESS = {
5132
5131
  architect: 0,
5133
5132
  artist: 1,
@@ -5777,6 +5776,7 @@ var arrayToMapWithIndex = (elements) => elements.reduce((acc, element, idx) => {
5777
5776
  return acc;
5778
5777
  }, /* @__PURE__ */ new Map());
5779
5778
  var isTestEnv = () => define_import_meta_env_default.MODE === "test";
5779
+ var isDevEnv = () => define_import_meta_env_default.MODE === "development";
5780
5780
  var wrapEvent = (name, nativeEvent) => {
5781
5781
  return new CustomEvent(name, {
5782
5782
  detail: {
@@ -6967,14 +6967,14 @@ var helper = {
6967
6967
  function line(x1, y1, x2, y2, o2) {
6968
6968
  return { type: "path", ops: _doubleLine(x1, y1, x2, y2, o2) };
6969
6969
  }
6970
- function linearPath(points, close, o2) {
6970
+ function linearPath(points, close2, o2) {
6971
6971
  const len = (points || []).length;
6972
6972
  if (len > 2) {
6973
6973
  const ops = [];
6974
6974
  for (let i2 = 0; i2 < len - 1; i2++) {
6975
6975
  ops.push(..._doubleLine(points[i2][0], points[i2][1], points[i2 + 1][0], points[i2 + 1][1], o2));
6976
6976
  }
6977
- if (close) {
6977
+ if (close2) {
6978
6978
  ops.push(..._doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o2));
6979
6979
  }
6980
6980
  return { type: "path", ops };
@@ -7527,8 +7527,8 @@ function getPointsOnBezierCurveWithSplitting(points, offset2, tolerance, newPoin
7527
7527
  }
7528
7528
  return outPoints;
7529
7529
  }
7530
- function simplify(points, distance5) {
7531
- return simplifyPoints(points, 0, points.length, distance5);
7530
+ function simplify(points, distance4) {
7531
+ return simplifyPoints(points, 0, points.length, distance4);
7532
7532
  }
7533
7533
  function simplifyPoints(points, start, end, epsilon, newPoints) {
7534
7534
  const outPoints = newPoints || [];
@@ -7554,22 +7554,22 @@ function simplifyPoints(points, start, end, epsilon, newPoints) {
7554
7554
  }
7555
7555
  return outPoints;
7556
7556
  }
7557
- function pointsOnBezierCurves(points, tolerance = 0.15, distance5) {
7557
+ function pointsOnBezierCurves(points, tolerance = 0.15, distance4) {
7558
7558
  const newPoints = [];
7559
7559
  const numSegments = (points.length - 1) / 3;
7560
7560
  for (let i2 = 0; i2 < numSegments; i2++) {
7561
7561
  const offset2 = i2 * 3;
7562
7562
  getPointsOnBezierCurveWithSplitting(points, offset2, tolerance, newPoints);
7563
7563
  }
7564
- if (distance5 && distance5 > 0) {
7565
- return simplifyPoints(newPoints, 0, newPoints.length, distance5);
7564
+ if (distance4 && distance4 > 0) {
7565
+ return simplifyPoints(newPoints, 0, newPoints.length, distance4);
7566
7566
  }
7567
7567
  return newPoints;
7568
7568
  }
7569
7569
 
7570
7570
  // ../../node_modules/points-on-path/lib/index.js
7571
7571
  init_define_import_meta_env();
7572
- function pointsOnPath(path, tolerance, distance5) {
7572
+ function pointsOnPath(path, tolerance, distance4) {
7573
7573
  const segments = parsePath(path);
7574
7574
  const normalized2 = normalize(absolutize(segments));
7575
7575
  const sets = [];
@@ -7616,12 +7616,12 @@ function pointsOnPath(path, tolerance, distance5) {
7616
7616
  }
7617
7617
  }
7618
7618
  appendPendingPoints();
7619
- if (!distance5) {
7619
+ if (!distance4) {
7620
7620
  return sets;
7621
7621
  }
7622
7622
  const out = [];
7623
7623
  for (const set of sets) {
7624
- const simplifiedSet = simplify(set, distance5);
7624
+ const simplifiedSet = simplify(set, distance4);
7625
7625
  if (simplifiedSet.length) {
7626
7626
  out.push(simplifiedSet);
7627
7627
  }
@@ -7786,8 +7786,8 @@ var RoughGenerator = class {
7786
7786
  const hasFill = o2.fill && o2.fill !== "transparent" && o2.fill !== NOS;
7787
7787
  const hasStroke = o2.stroke !== NOS;
7788
7788
  const simplified = !!(o2.simplification && o2.simplification < 1);
7789
- const distance5 = simplified ? 4 - 4 * (o2.simplification || 1) : (1 + o2.roughness) / 2;
7790
- const sets = pointsOnPath(d, 1, distance5);
7789
+ const distance4 = simplified ? 4 - 4 * (o2.simplification || 1) : (1 + o2.roughness) / 2;
7790
+ const sets = pointsOnPath(d, 1, distance4);
7791
7791
  const shape = svgPath(d, o2);
7792
7792
  if (hasFill) {
7793
7793
  if (o2.fillStyle === "solid") {
@@ -8341,127 +8341,6 @@ init_define_import_meta_env();
8341
8341
  // element/binding.ts
8342
8342
  init_define_import_meta_env();
8343
8343
 
8344
- // scene/index.ts
8345
- init_define_import_meta_env();
8346
-
8347
- // scene/scroll.ts
8348
- init_define_import_meta_env();
8349
- var isOutsideViewPort = (appState, cords) => {
8350
- const [x1, y1, x2, y2] = cords;
8351
- const { x: viewportX1, y: viewportY1 } = sceneCoordsToViewportCoords(
8352
- { sceneX: x1, sceneY: y1 },
8353
- appState
8354
- );
8355
- const { x: viewportX2, y: viewportY2 } = sceneCoordsToViewportCoords(
8356
- { sceneX: x2, sceneY: y2 },
8357
- appState
8358
- );
8359
- return viewportX2 - viewportX1 > appState.width || viewportY2 - viewportY1 > appState.height;
8360
- };
8361
- var centerScrollOn = ({
8362
- scenePoint,
8363
- viewportDimensions,
8364
- zoom
8365
- }) => {
8366
- return {
8367
- scrollX: viewportDimensions.width / 2 / zoom.value - scenePoint.x,
8368
- scrollY: viewportDimensions.height / 2 / zoom.value - scenePoint.y
8369
- };
8370
- };
8371
- var calculateScrollCenter = (elements, appState) => {
8372
- elements = getVisibleElements(elements);
8373
- if (!elements.length) {
8374
- return {
8375
- scrollX: 0,
8376
- scrollY: 0
8377
- };
8378
- }
8379
- let [x1, y1, x2, y2] = getCommonBounds(elements);
8380
- if (isOutsideViewPort(appState, [x1, y1, x2, y2])) {
8381
- [x1, y1, x2, y2] = getClosestElementBounds(
8382
- elements,
8383
- viewportCoordsToSceneCoords(
8384
- { clientX: appState.scrollX, clientY: appState.scrollY },
8385
- appState
8386
- )
8387
- );
8388
- }
8389
- const centerX = (x1 + x2) / 2;
8390
- const centerY = (y1 + y2) / 2;
8391
- return centerScrollOn({
8392
- scenePoint: { x: centerX, y: centerY },
8393
- viewportDimensions: { width: appState.width, height: appState.height },
8394
- zoom: appState.zoom
8395
- });
8396
- };
8397
-
8398
- // scene/comparisons.ts
8399
- init_define_import_meta_env();
8400
- var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
8401
- var hasStrokeColor = (type) => type !== "image" && type !== "frame" && type !== "magicframe";
8402
- var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
8403
- var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
8404
- var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "arrow" || type === "line" || type === "diamond" || type === "image";
8405
- var canHaveArrowheads = (type) => type === "arrow";
8406
- var getElementAtPosition = (elements, isAtPositionFn) => {
8407
- let hitElement = null;
8408
- for (let index = elements.length - 1; index >= 0; --index) {
8409
- const element = elements[index];
8410
- if (element.isDeleted) {
8411
- continue;
8412
- }
8413
- if (isAtPositionFn(element)) {
8414
- hitElement = element;
8415
- break;
8416
- }
8417
- }
8418
- return hitElement;
8419
- };
8420
- var getElementsAtPosition = (elements, isAtPositionFn) => {
8421
- const iframeLikes = [];
8422
- const elsAtPos = elements.filter((element) => {
8423
- const hit = !element.isDeleted && isAtPositionFn(element);
8424
- if (hit) {
8425
- if (isIframeElement(element)) {
8426
- iframeLikes.push(element);
8427
- return false;
8428
- }
8429
- return true;
8430
- }
8431
- return false;
8432
- });
8433
- return elsAtPos.concat(iframeLikes);
8434
- };
8435
-
8436
- // scene/zoom.ts
8437
- init_define_import_meta_env();
8438
- var getNormalizedZoom = (zoom) => {
8439
- return Math.max(MIN_ZOOM, Math.min(zoom, 30));
8440
- };
8441
- var getStateForZoom = ({
8442
- viewportX,
8443
- viewportY,
8444
- nextZoom
8445
- }, appState) => {
8446
- const appLayerX = viewportX - appState.offsetLeft;
8447
- const appLayerY = viewportY - appState.offsetTop;
8448
- const currentZoom = appState.zoom.value;
8449
- const baseScrollX = appState.scrollX + (appLayerX - appLayerX / currentZoom);
8450
- const baseScrollY = appState.scrollY + (appLayerY - appLayerY / currentZoom);
8451
- const zoomOffsetScrollX = -(appLayerX - appLayerX / nextZoom);
8452
- const zoomOffsetScrollY = -(appLayerY - appLayerY / nextZoom);
8453
- return {
8454
- scrollX: baseScrollX + zoomOffsetScrollX,
8455
- scrollY: baseScrollY + zoomOffsetScrollY,
8456
- zoom: {
8457
- value: nextZoom
8458
- }
8459
- };
8460
- };
8461
-
8462
- // element/collision.ts
8463
- init_define_import_meta_env();
8464
-
8465
8344
  // ga.ts
8466
8345
  init_define_import_meta_env();
8467
8346
  var point = (x, y) => [0, 0, 0, 0, y, x, 1, 0];
@@ -8671,1728 +8550,1373 @@ var translation = (direction) => [
8671
8550
  0,
8672
8551
  0
8673
8552
  ];
8674
- var translationOrthogonal = (direction, distance5) => {
8675
- const scale = 0.5 * distance5;
8553
+ var translationOrthogonal = (direction, distance4) => {
8554
+ const scale = 0.5 * distance4;
8676
8555
  return [1, 0, 0, 0, scale * direction[4], scale * direction[5], 0, 0];
8677
8556
  };
8678
8557
  var compose = (motor1, motor2) => mul(motor2, motor1);
8679
8558
  var apply = (motor, nvector2) => normalized(mul(mul(motor, nvector2), reverse(motor)));
8680
8559
 
8681
- // node_modules/points-on-curve/lib/index.js
8560
+ // ../utils/collision.ts
8682
8561
  init_define_import_meta_env();
8683
- function distance4(p1, p2) {
8684
- return Math.sqrt(distanceSq2(p1, p2));
8685
- }
8686
- function distanceSq2(p1, p2) {
8687
- return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2);
8688
- }
8689
- function distanceToSegmentSq2(p, v, w) {
8690
- const l2 = distanceSq2(v, w);
8691
- if (l2 === 0) {
8692
- return distanceSq2(p, v);
8693
- }
8694
- let t2 = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2;
8695
- t2 = Math.max(0, Math.min(1, t2));
8696
- return distanceSq2(p, lerp2(v, w, t2));
8697
- }
8698
- function lerp2(a2, b2, t2) {
8562
+
8563
+ // ../utils/geometry/geometry.ts
8564
+ init_define_import_meta_env();
8565
+ var DEFAULT_THRESHOLD = 1e-4;
8566
+ var isClosed = (polygon2) => {
8567
+ const first = polygon2[0];
8568
+ const last = polygon2[polygon2.length - 1];
8569
+ return first[0] === last[0] && first[1] === last[1];
8570
+ };
8571
+ var close = (polygon2) => {
8572
+ return isClosed(polygon2) ? polygon2 : [...polygon2, polygon2[0]];
8573
+ };
8574
+ var angleToDegrees = (angle) => {
8575
+ return angle * 180 / Math.PI;
8576
+ };
8577
+ var angleToRadians = (angle) => {
8578
+ return angle / 180 * Math.PI;
8579
+ };
8580
+ var rotate2 = (point2, angle) => {
8699
8581
  return [
8700
- a2[0] + (b2[0] - a2[0]) * t2,
8701
- a2[1] + (b2[1] - a2[1]) * t2
8582
+ point2[0] * Math.cos(angle) - point2[1] * Math.sin(angle),
8583
+ point2[0] * Math.sin(angle) + point2[1] * Math.cos(angle)
8702
8584
  ];
8703
- }
8704
- function flatness2(points, offset2) {
8705
- const p1 = points[offset2 + 0];
8706
- const p2 = points[offset2 + 1];
8707
- const p3 = points[offset2 + 2];
8708
- const p4 = points[offset2 + 3];
8709
- let ux = 3 * p2[0] - 2 * p1[0] - p4[0];
8710
- ux *= ux;
8711
- let uy = 3 * p2[1] - 2 * p1[1] - p4[1];
8712
- uy *= uy;
8713
- let vx = 3 * p3[0] - 2 * p4[0] - p1[0];
8714
- vx *= vx;
8715
- let vy = 3 * p3[1] - 2 * p4[1] - p1[1];
8716
- vy *= vy;
8717
- if (ux < vx) {
8718
- ux = vx;
8719
- }
8720
- if (uy < vy) {
8721
- uy = vy;
8585
+ };
8586
+ var isOrigin = (point2) => {
8587
+ return point2[0] === 0 && point2[1] === 0;
8588
+ };
8589
+ var pointRotate = (point2, angle, origin) => {
8590
+ const r = angleToRadians(angle);
8591
+ if (!origin || isOrigin(origin)) {
8592
+ return rotate2(point2, r);
8722
8593
  }
8723
- return ux + uy;
8724
- }
8725
- function getPointsOnBezierCurveWithSplitting2(points, offset2, tolerance, newPoints) {
8726
- const outPoints = newPoints || [];
8727
- if (flatness2(points, offset2) < tolerance) {
8728
- const p0 = points[offset2 + 0];
8729
- if (outPoints.length) {
8730
- const d = distance4(outPoints[outPoints.length - 1], p0);
8731
- if (d > 1) {
8732
- outPoints.push(p0);
8733
- }
8734
- } else {
8735
- outPoints.push(p0);
8736
- }
8737
- outPoints.push(points[offset2 + 3]);
8594
+ return rotate2(point2.map((c, i2) => c - origin[i2]), r).map(
8595
+ (c, i2) => c + origin[i2]
8596
+ );
8597
+ };
8598
+ var pointInverse = (point2) => {
8599
+ return [-point2[0], -point2[1]];
8600
+ };
8601
+ var pointAdd = (pointA, pointB) => {
8602
+ return [pointA[0] + pointB[0], pointA[1] + pointB[1]];
8603
+ };
8604
+ var distanceToPoint = (p1, p2) => {
8605
+ return distance2d(...p1, ...p2);
8606
+ };
8607
+ var pointRelativeToCenter = (point2, center, angle) => {
8608
+ const translated = pointAdd(point2, pointInverse(center));
8609
+ const rotated = pointRotate(translated, -angleToDegrees(angle));
8610
+ return rotated;
8611
+ };
8612
+ var distanceToSegment = (point2, line2) => {
8613
+ const [x, y] = point2;
8614
+ const [[x1, y1], [x2, y2]] = line2;
8615
+ const A2 = x - x1;
8616
+ const B2 = y - y1;
8617
+ const C2 = x2 - x1;
8618
+ const D = y2 - y1;
8619
+ const dot2 = A2 * C2 + B2 * D;
8620
+ const len_sq = C2 * C2 + D * D;
8621
+ let param = -1;
8622
+ if (len_sq !== 0) {
8623
+ param = dot2 / len_sq;
8624
+ }
8625
+ let xx;
8626
+ let yy;
8627
+ if (param < 0) {
8628
+ xx = x1;
8629
+ yy = y1;
8630
+ } else if (param > 1) {
8631
+ xx = x2;
8632
+ yy = y2;
8738
8633
  } else {
8739
- const t2 = 0.5;
8740
- const p1 = points[offset2 + 0];
8741
- const p2 = points[offset2 + 1];
8742
- const p3 = points[offset2 + 2];
8743
- const p4 = points[offset2 + 3];
8744
- const q1 = lerp2(p1, p2, t2);
8745
- const q2 = lerp2(p2, p3, t2);
8746
- const q3 = lerp2(p3, p4, t2);
8747
- const r1 = lerp2(q1, q2, t2);
8748
- const r2 = lerp2(q2, q3, t2);
8749
- const red = lerp2(r1, r2, t2);
8750
- getPointsOnBezierCurveWithSplitting2([p1, q1, r1, red], 0, tolerance, outPoints);
8751
- getPointsOnBezierCurveWithSplitting2([red, r2, q3, p4], 0, tolerance, outPoints);
8634
+ xx = x1 + param * C2;
8635
+ yy = y1 + param * D;
8752
8636
  }
8753
- return outPoints;
8754
- }
8755
- function simplify2(points, distance5) {
8756
- return simplifyPoints2(points, 0, points.length, distance5);
8757
- }
8758
- function simplifyPoints2(points, start, end, epsilon, newPoints) {
8759
- const outPoints = newPoints || [];
8760
- const s2 = points[start];
8761
- const e2 = points[end - 1];
8762
- let maxDistSq = 0;
8763
- let maxNdx = 1;
8764
- for (let i2 = start + 1; i2 < end - 1; ++i2) {
8765
- const distSq = distanceToSegmentSq2(points[i2], s2, e2);
8766
- if (distSq > maxDistSq) {
8767
- maxDistSq = distSq;
8768
- maxNdx = i2;
8769
- }
8637
+ const dx = x - xx;
8638
+ const dy = y - yy;
8639
+ return Math.sqrt(dx * dx + dy * dy);
8640
+ };
8641
+ var pointOnLine = (point2, line2, threshold = DEFAULT_THRESHOLD) => {
8642
+ const distance4 = distanceToSegment(point2, line2);
8643
+ if (distance4 === 0) {
8644
+ return true;
8770
8645
  }
8771
- if (Math.sqrt(maxDistSq) > epsilon) {
8772
- simplifyPoints2(points, start, maxNdx + 1, epsilon, outPoints);
8773
- simplifyPoints2(points, maxNdx, end, epsilon, outPoints);
8774
- } else {
8775
- if (!outPoints.length) {
8776
- outPoints.push(s2);
8646
+ return distance4 < threshold;
8647
+ };
8648
+ var pointOnPolyline = (point2, polyline, threshold = DEFAULT_THRESHOLD) => {
8649
+ return polyline.some((line2) => pointOnLine(point2, line2, threshold));
8650
+ };
8651
+ var cubicBezierEquation = (curve2) => {
8652
+ const [p0, p1, p2, p3] = curve2;
8653
+ return (t2, idx) => Math.pow(1 - t2, 3) * p3[idx] + 3 * t2 * Math.pow(1 - t2, 2) * p2[idx] + 3 * Math.pow(t2, 2) * (1 - t2) * p1[idx] + p0[idx] * Math.pow(t2, 3);
8654
+ };
8655
+ var polyLineFromCurve = (curve2, segments = 10) => {
8656
+ const equation2 = cubicBezierEquation(curve2);
8657
+ let startingPoint = [equation2(0, 0), equation2(0, 1)];
8658
+ const lineSegments = [];
8659
+ let t2 = 0;
8660
+ const increment = 1 / segments;
8661
+ for (let i2 = 0; i2 < segments; i2++) {
8662
+ t2 += increment;
8663
+ if (t2 <= 1) {
8664
+ const nextPoint = [equation2(t2, 0), equation2(t2, 1)];
8665
+ lineSegments.push([startingPoint, nextPoint]);
8666
+ startingPoint = nextPoint;
8667
+ }
8668
+ }
8669
+ return lineSegments;
8670
+ };
8671
+ var pointOnCurve = (point2, curve2, threshold = DEFAULT_THRESHOLD) => {
8672
+ return pointOnPolyline(point2, polyLineFromCurve(curve2), threshold);
8673
+ };
8674
+ var pointOnPolycurve = (point2, polycurve, threshold = DEFAULT_THRESHOLD) => {
8675
+ return polycurve.some((curve2) => pointOnCurve(point2, curve2, threshold));
8676
+ };
8677
+ var pointInPolygon = (point2, polygon2) => {
8678
+ const x = point2[0];
8679
+ const y = point2[1];
8680
+ let inside = false;
8681
+ for (let i2 = 0, j = polygon2.length - 1; i2 < polygon2.length; j = i2++) {
8682
+ const xi = polygon2[i2][0];
8683
+ const yi = polygon2[i2][1];
8684
+ const xj = polygon2[j][0];
8685
+ const yj = polygon2[j][1];
8686
+ if ((yi > y && yj <= y || yi <= y && yj > y) && x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
8687
+ inside = !inside;
8688
+ }
8689
+ }
8690
+ return inside;
8691
+ };
8692
+ var pointOnPolygon = (point2, polygon2, threshold = DEFAULT_THRESHOLD) => {
8693
+ let on = false;
8694
+ const closed = close(polygon2);
8695
+ for (let i2 = 0, l2 = closed.length - 1; i2 < l2; i2++) {
8696
+ if (pointOnLine(point2, [closed[i2], closed[i2 + 1]], threshold)) {
8697
+ on = true;
8698
+ break;
8777
8699
  }
8778
- outPoints.push(e2);
8779
- }
8780
- return outPoints;
8781
- }
8782
- function pointsOnBezierCurves2(points, tolerance = 0.15, distance5) {
8783
- const newPoints = [];
8784
- const numSegments = (points.length - 1) / 3;
8785
- for (let i2 = 0; i2 < numSegments; i2++) {
8786
- const offset2 = i2 * 3;
8787
- getPointsOnBezierCurveWithSplitting2(points, offset2, tolerance, newPoints);
8788
8700
  }
8789
- if (distance5 && distance5 > 0) {
8790
- return simplifyPoints2(newPoints, 0, newPoints.length, distance5);
8701
+ return on;
8702
+ };
8703
+ var distanceToEllipse = (point2, ellipse2) => {
8704
+ const { angle, halfWidth, halfHeight, center } = ellipse2;
8705
+ const a2 = halfWidth;
8706
+ const b2 = halfHeight;
8707
+ const [rotatedPointX, rotatedPointY] = pointRelativeToCenter(
8708
+ point2,
8709
+ center,
8710
+ angle
8711
+ );
8712
+ const px = Math.abs(rotatedPointX);
8713
+ const py = Math.abs(rotatedPointY);
8714
+ let tx = 0.707;
8715
+ let ty = 0.707;
8716
+ for (let i2 = 0; i2 < 3; i2++) {
8717
+ const x = a2 * tx;
8718
+ const y = b2 * ty;
8719
+ const ex = (a2 * a2 - b2 * b2) * tx ** 3 / a2;
8720
+ const ey = (b2 * b2 - a2 * a2) * ty ** 3 / b2;
8721
+ const rx = x - ex;
8722
+ const ry = y - ey;
8723
+ const qx = px - ex;
8724
+ const qy = py - ey;
8725
+ const r = Math.hypot(ry, rx);
8726
+ const q = Math.hypot(qy, qx);
8727
+ tx = Math.min(1, Math.max(0, (qx * r / q + ex) / a2));
8728
+ ty = Math.min(1, Math.max(0, (qy * r / q + ey) / b2));
8729
+ const t2 = Math.hypot(ty, tx);
8730
+ tx /= t2;
8731
+ ty /= t2;
8791
8732
  }
8792
- return newPoints;
8793
- }
8794
-
8795
- // element/transformHandles.ts
8796
- init_define_import_meta_env();
8797
- var transformHandleSizes = {
8798
- mouse: 8,
8799
- pen: 16,
8800
- touch: 28
8733
+ const [minX, minY] = [
8734
+ a2 * tx * Math.sign(rotatedPointX),
8735
+ b2 * ty * Math.sign(rotatedPointY)
8736
+ ];
8737
+ return distanceToPoint([rotatedPointX, rotatedPointY], [minX, minY]);
8801
8738
  };
8802
- var ROTATION_RESIZE_HANDLE_GAP = 16;
8803
- var OMIT_SIDES_FOR_MULTIPLE_ELEMENTS = {
8804
- e: true,
8805
- s: true,
8806
- n: true,
8807
- w: true
8739
+ var pointOnEllipse = (point2, ellipse2, threshold = DEFAULT_THRESHOLD) => {
8740
+ return distanceToEllipse(point2, ellipse2) <= threshold;
8808
8741
  };
8809
- var OMIT_SIDES_FOR_FRAME = {
8810
- e: true,
8811
- s: true,
8812
- n: true,
8813
- w: true,
8814
- rotation: true
8742
+ var pointInEllipse = (point2, ellipse2) => {
8743
+ const { center, angle, halfWidth, halfHeight } = ellipse2;
8744
+ const [rotatedPointX, rotatedPointY] = pointRelativeToCenter(
8745
+ point2,
8746
+ center,
8747
+ angle
8748
+ );
8749
+ return rotatedPointX / halfWidth * (rotatedPointX / halfWidth) + rotatedPointY / halfHeight * (rotatedPointY / halfHeight) <= 1;
8815
8750
  };
8816
- var OMIT_SIDES_FOR_TEXT_ELEMENT = {
8817
- e: true,
8818
- s: true,
8819
- n: true,
8820
- w: true
8821
- };
8822
- var OMIT_SIDES_FOR_LINE_SLASH = {
8823
- e: true,
8824
- s: true,
8825
- n: true,
8826
- w: true,
8827
- nw: true,
8828
- se: true
8829
- };
8830
- var OMIT_SIDES_FOR_LINE_BACKSLASH = {
8831
- e: true,
8832
- s: true,
8833
- n: true,
8834
- w: true
8835
- };
8836
- var generateTransformHandle = (x, y, width, height, cx, cy, angle) => {
8837
- const [xx, yy] = rotate2(x + width / 2, y + height / 2, cx, cy, angle);
8838
- return [xx - width / 2, yy - height / 2, width, height];
8839
- };
8840
- var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, pointerType, omitSides = {}, margin = 4) => {
8841
- const size = transformHandleSizes[pointerType];
8842
- const handleWidth = size / zoom.value;
8843
- const handleHeight = size / zoom.value;
8844
- const handleMarginX = size / zoom.value;
8845
- const handleMarginY = size / zoom.value;
8846
- const width = x2 - x1;
8847
- const height = y2 - y1;
8848
- const dashedLineMargin = margin / zoom.value;
8849
- const centeringOffset = (size - DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / (2 * zoom.value);
8850
- const transformHandles = {
8851
- nw: omitSides.nw ? void 0 : generateTransformHandle(
8852
- x1 - dashedLineMargin - handleMarginX + centeringOffset,
8853
- y1 - dashedLineMargin - handleMarginY + centeringOffset,
8854
- handleWidth,
8855
- handleHeight,
8856
- cx,
8857
- cy,
8858
- angle
8859
- ),
8860
- ne: omitSides.ne ? void 0 : generateTransformHandle(
8861
- x2 + dashedLineMargin - centeringOffset,
8862
- y1 - dashedLineMargin - handleMarginY + centeringOffset,
8863
- handleWidth,
8864
- handleHeight,
8865
- cx,
8866
- cy,
8867
- angle
8868
- ),
8869
- sw: omitSides.sw ? void 0 : generateTransformHandle(
8870
- x1 - dashedLineMargin - handleMarginX + centeringOffset,
8871
- y2 + dashedLineMargin - centeringOffset,
8872
- handleWidth,
8873
- handleHeight,
8874
- cx,
8875
- cy,
8876
- angle
8877
- ),
8878
- se: omitSides.se ? void 0 : generateTransformHandle(
8879
- x2 + dashedLineMargin - centeringOffset,
8880
- y2 + dashedLineMargin - centeringOffset,
8881
- handleWidth,
8882
- handleHeight,
8883
- cx,
8884
- cy,
8885
- angle
8886
- ),
8887
- rotation: omitSides.rotation ? void 0 : generateTransformHandle(
8888
- x1 + width / 2 - handleWidth / 2,
8889
- y1 - dashedLineMargin - handleMarginY + centeringOffset - ROTATION_RESIZE_HANDLE_GAP / zoom.value,
8890
- handleWidth,
8891
- handleHeight,
8892
- cx,
8893
- cy,
8894
- angle
8895
- )
8896
- };
8897
- const minimumSizeForEightHandles = 5 * transformHandleSizes.mouse / zoom.value;
8898
- if (Math.abs(width) > minimumSizeForEightHandles) {
8899
- if (!omitSides.n) {
8900
- transformHandles.n = generateTransformHandle(
8901
- x1 + width / 2 - handleWidth / 2,
8902
- y1 - dashedLineMargin - handleMarginY + centeringOffset,
8903
- handleWidth,
8904
- handleHeight,
8905
- cx,
8906
- cy,
8907
- angle
8908
- );
8909
- }
8910
- if (!omitSides.s) {
8911
- transformHandles.s = generateTransformHandle(
8912
- x1 + width / 2 - handleWidth / 2,
8913
- y2 + dashedLineMargin - centeringOffset,
8914
- handleWidth,
8915
- handleHeight,
8916
- cx,
8917
- cy,
8918
- angle
8919
- );
8920
- }
8751
+
8752
+ // ../utils/collision.ts
8753
+ var isPointOnShape = (point2, shape, tolerance = 0) => {
8754
+ switch (shape.type) {
8755
+ case "polygon":
8756
+ return pointOnPolygon(point2, shape.data, tolerance);
8757
+ case "ellipse":
8758
+ return pointOnEllipse(point2, shape.data, tolerance);
8759
+ case "line":
8760
+ return pointOnLine(point2, shape.data, tolerance);
8761
+ case "polyline":
8762
+ return pointOnPolyline(point2, shape.data, tolerance);
8763
+ case "curve":
8764
+ return pointOnCurve(point2, shape.data, tolerance);
8765
+ case "polycurve":
8766
+ return pointOnPolycurve(point2, shape.data, tolerance);
8767
+ default:
8768
+ throw Error(`shape ${shape} is not implemented`);
8921
8769
  }
8922
- if (Math.abs(height) > minimumSizeForEightHandles) {
8923
- if (!omitSides.w) {
8924
- transformHandles.w = generateTransformHandle(
8925
- x1 - dashedLineMargin - handleMarginX + centeringOffset,
8926
- y1 + height / 2 - handleHeight / 2,
8927
- handleWidth,
8928
- handleHeight,
8929
- cx,
8930
- cy,
8931
- angle
8932
- );
8770
+ };
8771
+ var isPointInShape = (point2, shape) => {
8772
+ switch (shape.type) {
8773
+ case "polygon":
8774
+ return pointInPolygon(point2, shape.data);
8775
+ case "line":
8776
+ return false;
8777
+ case "curve":
8778
+ return false;
8779
+ case "ellipse":
8780
+ return pointInEllipse(point2, shape.data);
8781
+ case "polyline": {
8782
+ const polygon2 = close(shape.data.flat());
8783
+ return pointInPolygon(point2, polygon2);
8933
8784
  }
8934
- if (!omitSides.e) {
8935
- transformHandles.e = generateTransformHandle(
8936
- x2 + dashedLineMargin - centeringOffset,
8937
- y1 + height / 2 - handleHeight / 2,
8938
- handleWidth,
8939
- handleHeight,
8940
- cx,
8941
- cy,
8942
- angle
8943
- );
8785
+ case "polycurve": {
8786
+ return false;
8944
8787
  }
8788
+ default:
8789
+ throw Error(`shape ${shape} is not implemented`);
8945
8790
  }
8946
- return transformHandles;
8947
8791
  };
8948
- var getTransformHandles = (element, zoom, elementsMap, pointerType = "mouse") => {
8949
- if (element.locked) {
8950
- return {};
8951
- }
8952
- let omitSides = {};
8953
- if (element.type === "freedraw" || isLinearElement(element)) {
8954
- if (element.points.length === 2) {
8955
- const [, p1] = element.points;
8956
- if (p1[0] === 0 || p1[1] === 0) {
8957
- omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
8958
- } else if (p1[0] > 0 && p1[1] < 0) {
8959
- omitSides = OMIT_SIDES_FOR_LINE_SLASH;
8960
- } else if (p1[0] > 0 && p1[1] > 0) {
8961
- omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
8962
- } else if (p1[0] < 0 && p1[1] > 0) {
8963
- omitSides = OMIT_SIDES_FOR_LINE_SLASH;
8964
- } else if (p1[0] < 0 && p1[1] < 0) {
8965
- omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
8966
- }
8967
- }
8968
- } else if (isTextElement(element)) {
8969
- omitSides = OMIT_SIDES_FOR_TEXT_ELEMENT;
8970
- } else if (isFrameLikeElement(element)) {
8971
- omitSides = {
8972
- rotation: true
8973
- };
8974
- }
8975
- const dashedLineMargin = isLinearElement(element) ? DEFAULT_TRANSFORM_HANDLE_SPACING + 8 : DEFAULT_TRANSFORM_HANDLE_SPACING;
8976
- return getTransformHandlesFromCoords(
8977
- getElementAbsoluteCoords(element, elementsMap, true),
8978
- element.angle,
8979
- zoom,
8980
- pointerType,
8981
- omitSides,
8982
- dashedLineMargin
8792
+
8793
+ // scene/index.ts
8794
+ init_define_import_meta_env();
8795
+
8796
+ // scene/scroll.ts
8797
+ init_define_import_meta_env();
8798
+ var isOutsideViewPort = (appState, cords) => {
8799
+ const [x1, y1, x2, y2] = cords;
8800
+ const { x: viewportX1, y: viewportY1 } = sceneCoordsToViewportCoords(
8801
+ { sceneX: x1, sceneY: y1 },
8802
+ appState
8803
+ );
8804
+ const { x: viewportX2, y: viewportY2 } = sceneCoordsToViewportCoords(
8805
+ { sceneX: x2, sceneY: y2 },
8806
+ appState
8983
8807
  );
8808
+ return viewportX2 - viewportX1 > appState.width || viewportY2 - viewportY1 > appState.height;
8984
8809
  };
8985
- var shouldShowBoundingBox = (elements, appState) => {
8986
- if (appState.editingLinearElement) {
8987
- return false;
8810
+ var centerScrollOn = ({
8811
+ scenePoint,
8812
+ viewportDimensions,
8813
+ zoom
8814
+ }) => {
8815
+ return {
8816
+ scrollX: viewportDimensions.width / 2 / zoom.value - scenePoint.x,
8817
+ scrollY: viewportDimensions.height / 2 / zoom.value - scenePoint.y
8818
+ };
8819
+ };
8820
+ var calculateScrollCenter = (elements, appState) => {
8821
+ elements = getVisibleElements(elements);
8822
+ if (!elements.length) {
8823
+ return {
8824
+ scrollX: 0,
8825
+ scrollY: 0
8826
+ };
8988
8827
  }
8989
- if (elements.length > 1) {
8990
- return true;
8828
+ let [x1, y1, x2, y2] = getCommonBounds(elements);
8829
+ if (isOutsideViewPort(appState, [x1, y1, x2, y2])) {
8830
+ [x1, y1, x2, y2] = getClosestElementBounds(
8831
+ elements,
8832
+ viewportCoordsToSceneCoords(
8833
+ { clientX: appState.scrollX, clientY: appState.scrollY },
8834
+ appState
8835
+ )
8836
+ );
8991
8837
  }
8992
- const element = elements[0];
8993
- if (!isLinearElement(element)) {
8994
- return true;
8838
+ const centerX = (x1 + x2) / 2;
8839
+ const centerY = (y1 + y2) / 2;
8840
+ return centerScrollOn({
8841
+ scenePoint: { x: centerX, y: centerY },
8842
+ viewportDimensions: { width: appState.width, height: appState.height },
8843
+ zoom: appState.zoom
8844
+ });
8845
+ };
8846
+
8847
+ // scene/comparisons.ts
8848
+ init_define_import_meta_env();
8849
+ var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
8850
+ var hasStrokeColor = (type) => type !== "image" && type !== "frame" && type !== "magicframe";
8851
+ var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
8852
+ var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
8853
+ var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "arrow" || type === "line" || type === "diamond" || type === "image";
8854
+ var canHaveArrowheads = (type) => type === "arrow";
8855
+ var getElementAtPosition = (elements, isAtPositionFn) => {
8856
+ let hitElement = null;
8857
+ for (let index = elements.length - 1; index >= 0; --index) {
8858
+ const element = elements[index];
8859
+ if (element.isDeleted) {
8860
+ continue;
8861
+ }
8862
+ if (isAtPositionFn(element)) {
8863
+ hitElement = element;
8864
+ break;
8865
+ }
8995
8866
  }
8996
- return element.points.length > 2;
8867
+ return hitElement;
8997
8868
  };
8998
8869
 
8999
- // scene/ShapeCache.ts
8870
+ // scene/zoom.ts
9000
8871
  init_define_import_meta_env();
9001
- var ShapeCache = class _ShapeCache {
9002
- static rg = new RoughGenerator();
9003
- static cache = /* @__PURE__ */ new WeakMap();
9004
- /**
9005
- * Retrieves shape from cache if available. Use this only if shape
9006
- * is optional and you have a fallback in case it's not cached.
9007
- */
9008
- static get = (element) => {
9009
- return _ShapeCache.cache.get(
9010
- element
9011
- );
9012
- };
9013
- static set = (element, shape) => _ShapeCache.cache.set(element, shape);
9014
- static delete = (element) => _ShapeCache.cache.delete(element);
9015
- static destroy = () => {
9016
- _ShapeCache.cache = /* @__PURE__ */ new WeakMap();
9017
- };
9018
- /**
9019
- * Generates & caches shape for element if not already cached, otherwise
9020
- * returns cached shape.
9021
- */
9022
- static generateElementShape = (element, renderConfig) => {
9023
- const cachedShape = renderConfig?.isExporting ? void 0 : _ShapeCache.get(element);
9024
- if (cachedShape !== void 0) {
9025
- return cachedShape;
8872
+ var getNormalizedZoom = (zoom) => {
8873
+ return Math.max(MIN_ZOOM, Math.min(zoom, 30));
8874
+ };
8875
+ var getStateForZoom = ({
8876
+ viewportX,
8877
+ viewportY,
8878
+ nextZoom
8879
+ }, appState) => {
8880
+ const appLayerX = viewportX - appState.offsetLeft;
8881
+ const appLayerY = viewportY - appState.offsetTop;
8882
+ const currentZoom = appState.zoom.value;
8883
+ const baseScrollX = appState.scrollX + (appLayerX - appLayerX / currentZoom);
8884
+ const baseScrollY = appState.scrollY + (appLayerY - appLayerY / currentZoom);
8885
+ const zoomOffsetScrollX = -(appLayerX - appLayerX / nextZoom);
8886
+ const zoomOffsetScrollY = -(appLayerY - appLayerY / nextZoom);
8887
+ return {
8888
+ scrollX: baseScrollX + zoomOffsetScrollX,
8889
+ scrollY: baseScrollY + zoomOffsetScrollY,
8890
+ zoom: {
8891
+ value: nextZoom
9026
8892
  }
9027
- elementWithCanvasCache.delete(element);
9028
- const shape = _generateElementShape(
9029
- element,
9030
- _ShapeCache.rg,
9031
- renderConfig || {
9032
- isExporting: false,
9033
- canvasBackgroundColor: COLOR_PALETTE.white,
9034
- embedsValidationStatus: null
9035
- }
9036
- );
9037
- _ShapeCache.cache.set(element, shape);
9038
- return shape;
9039
8893
  };
9040
8894
  };
9041
8895
 
9042
- // element/collision.ts
9043
- var isElementDraggableFromInside = (element) => {
9044
- if (element.type === "arrow") {
9045
- return false;
9046
- }
9047
- if (element.type === "freedraw") {
9048
- return true;
9049
- }
9050
- const isDraggableFromInside = !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element);
9051
- if (element.type === "line") {
9052
- return isDraggableFromInside && isPathALoop(element.points);
9053
- }
9054
- return isDraggableFromInside || isImageElement(element);
8896
+ // keys.ts
8897
+ init_define_import_meta_env();
8898
+ var CODES = {
8899
+ EQUAL: "Equal",
8900
+ MINUS: "Minus",
8901
+ NUM_ADD: "NumpadAdd",
8902
+ NUM_SUBTRACT: "NumpadSubtract",
8903
+ NUM_ZERO: "Numpad0",
8904
+ BRACKET_RIGHT: "BracketRight",
8905
+ BRACKET_LEFT: "BracketLeft",
8906
+ ONE: "Digit1",
8907
+ TWO: "Digit2",
8908
+ THREE: "Digit3",
8909
+ NINE: "Digit9",
8910
+ QUOTE: "Quote",
8911
+ ZERO: "Digit0",
8912
+ SLASH: "Slash",
8913
+ C: "KeyC",
8914
+ D: "KeyD",
8915
+ H: "KeyH",
8916
+ V: "KeyV",
8917
+ Z: "KeyZ",
8918
+ R: "KeyR",
8919
+ S: "KeyS"
9055
8920
  };
9056
- var hitTest = (element, appState, frameNameBoundsCache, x, y, elementsMap) => {
9057
- const threshold = 10 / appState.zoom.value;
9058
- const point2 = [x, y];
9059
- if (isElementSelected(appState, element) && shouldShowBoundingBox([element], appState)) {
9060
- return isPointHittingElementBoundingBox(
9061
- element,
9062
- elementsMap,
9063
- point2,
9064
- threshold,
9065
- frameNameBoundsCache
9066
- );
9067
- }
9068
- const boundTextElement = getBoundTextElement(element, elementsMap);
9069
- if (boundTextElement) {
9070
- const isHittingBoundTextElement = hitTest(
9071
- boundTextElement,
9072
- appState,
9073
- frameNameBoundsCache,
9074
- x,
9075
- y,
9076
- elementsMap
9077
- );
9078
- if (isHittingBoundTextElement) {
9079
- return true;
8921
+ var KEYS = {
8922
+ ARROW_DOWN: "ArrowDown",
8923
+ ARROW_LEFT: "ArrowLeft",
8924
+ ARROW_RIGHT: "ArrowRight",
8925
+ ARROW_UP: "ArrowUp",
8926
+ PAGE_UP: "PageUp",
8927
+ PAGE_DOWN: "PageDown",
8928
+ BACKSPACE: "Backspace",
8929
+ ALT: "Alt",
8930
+ CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey",
8931
+ DELETE: "Delete",
8932
+ ENTER: "Enter",
8933
+ ESCAPE: "Escape",
8934
+ QUESTION_MARK: "?",
8935
+ SPACE: " ",
8936
+ TAB: "Tab",
8937
+ CHEVRON_LEFT: "<",
8938
+ CHEVRON_RIGHT: ">",
8939
+ PERIOD: ".",
8940
+ COMMA: ",",
8941
+ SUBTRACT: "-",
8942
+ SLASH: "/",
8943
+ A: "a",
8944
+ C: "c",
8945
+ D: "d",
8946
+ E: "e",
8947
+ F: "f",
8948
+ G: "g",
8949
+ H: "h",
8950
+ I: "i",
8951
+ L: "l",
8952
+ O: "o",
8953
+ P: "p",
8954
+ Q: "q",
8955
+ R: "r",
8956
+ S: "s",
8957
+ T: "t",
8958
+ V: "v",
8959
+ X: "x",
8960
+ Y: "y",
8961
+ Z: "z",
8962
+ K: "k",
8963
+ W: "w",
8964
+ 0: "0",
8965
+ 1: "1",
8966
+ 2: "2",
8967
+ 3: "3",
8968
+ 4: "4",
8969
+ 5: "5",
8970
+ 6: "6",
8971
+ 7: "7",
8972
+ 8: "8",
8973
+ 9: "9"
8974
+ };
8975
+ var isArrowKey = (key) => key === KEYS.ARROW_LEFT || key === KEYS.ARROW_RIGHT || key === KEYS.ARROW_DOWN || key === KEYS.ARROW_UP;
8976
+ var shouldResizeFromCenter = (event) => event.altKey;
8977
+ var shouldMaintainAspectRatio = (event) => event.shiftKey;
8978
+ var shouldRotateWithDiscreteAngle = (event) => event.shiftKey;
8979
+
8980
+ // element/binding.ts
8981
+ var shouldEnableBindingForPointerEvent = (event) => {
8982
+ return !event[KEYS.CTRL_OR_CMD];
8983
+ };
8984
+ var isBindingEnabled = (appState) => {
8985
+ return appState.isBindingEnabled;
8986
+ };
8987
+ var getNonDeletedElements = (scene, ids) => {
8988
+ const result = [];
8989
+ ids.forEach((id) => {
8990
+ const element = scene.getNonDeletedElement(id);
8991
+ if (element != null) {
8992
+ result.push(element);
9080
8993
  }
9081
- }
9082
- return isHittingElementNotConsideringBoundingBox(
9083
- element,
9084
- appState,
9085
- frameNameBoundsCache,
9086
- point2,
8994
+ });
8995
+ return result;
8996
+ };
8997
+ var bindOrUnbindLinearElement = (linearElement, startBindingElement, endBindingElement, elementsMap) => {
8998
+ const boundToElementIds = /* @__PURE__ */ new Set();
8999
+ const unboundFromElementIds = /* @__PURE__ */ new Set();
9000
+ bindOrUnbindLinearElementEdge(
9001
+ linearElement,
9002
+ startBindingElement,
9003
+ endBindingElement,
9004
+ "start",
9005
+ boundToElementIds,
9006
+ unboundFromElementIds,
9087
9007
  elementsMap
9088
9008
  );
9089
- };
9090
- var isHittingElementBoundingBoxWithoutHittingElement = (element, appState, frameNameBoundsCache, x, y, elementsMap) => {
9091
- const threshold = 10 / appState.zoom.value;
9092
- const boundTextElement = getBoundTextElement(element, elementsMap);
9093
- if (boundTextElement && hitTest(boundTextElement, appState, frameNameBoundsCache, x, y, elementsMap)) {
9094
- return false;
9095
- }
9096
- return !isHittingElementNotConsideringBoundingBox(
9097
- element,
9098
- appState,
9099
- frameNameBoundsCache,
9100
- [x, y],
9009
+ bindOrUnbindLinearElementEdge(
9010
+ linearElement,
9011
+ endBindingElement,
9012
+ startBindingElement,
9013
+ "end",
9014
+ boundToElementIds,
9015
+ unboundFromElementIds,
9101
9016
  elementsMap
9102
- ) && isPointHittingElementBoundingBox(
9103
- element,
9104
- elementsMap,
9105
- [x, y],
9106
- threshold,
9107
- frameNameBoundsCache
9108
9017
  );
9109
- };
9110
- var isHittingElementNotConsideringBoundingBox = (element, appState, frameNameBoundsCache, point2, elementsMap) => {
9111
- const threshold = 10 / appState.zoom.value;
9112
- const check = isTextElement(element) ? isStrictlyInside : isElementDraggableFromInside(element) ? isInsideCheck : isNearCheck;
9113
- return hitTestPointAgainstElement({
9114
- element,
9115
- elementsMap,
9116
- point: point2,
9117
- threshold,
9118
- check,
9119
- frameNameBoundsCache
9120
- });
9121
- };
9122
- var isElementSelected = (appState, element) => appState.selectedElementIds[element.id];
9123
- var isPointHittingElementBoundingBox = (element, elementsMap, [x, y], threshold, frameNameBoundsCache) => {
9124
- if (isFrameLikeElement(element)) {
9125
- return hitTestPointAgainstElement({
9126
- element,
9127
- elementsMap,
9128
- point: [x, y],
9129
- threshold,
9130
- check: isInsideCheck,
9131
- frameNameBoundsCache
9132
- });
9133
- }
9134
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9135
- const elementCenterX = (x1 + x2) / 2;
9136
- const elementCenterY = (y1 + y2) / 2;
9137
- const [rotatedX, rotatedY] = rotate2(
9138
- x,
9139
- y,
9140
- elementCenterX,
9141
- elementCenterY,
9142
- -element.angle
9018
+ const onlyUnbound = Array.from(unboundFromElementIds).filter(
9019
+ (id) => !boundToElementIds.has(id)
9020
+ );
9021
+ getNonDeletedElements(Scene_default.getScene(linearElement), onlyUnbound).forEach(
9022
+ (element) => {
9023
+ mutateElement(element, {
9024
+ boundElements: element.boundElements?.filter(
9025
+ (element2) => element2.type !== "arrow" || element2.id !== linearElement.id
9026
+ )
9027
+ });
9028
+ }
9143
9029
  );
9144
- return rotatedX > x1 - threshold && rotatedX < x2 + threshold && rotatedY > y1 - threshold && rotatedY < y2 + threshold;
9145
9030
  };
9146
- var bindingBorderTest = (element, { x, y }, elementsMap) => {
9147
- const threshold = maxBindingGap(element, element.width, element.height);
9148
- const check = isOutsideCheck;
9149
- const point2 = [x, y];
9150
- return hitTestPointAgainstElement({
9151
- element,
9152
- elementsMap,
9153
- point: point2,
9154
- threshold,
9155
- check,
9156
- frameNameBoundsCache: null
9157
- });
9031
+ var bindOrUnbindLinearElementEdge = (linearElement, bindableElement, otherEdgeBindableElement, startOrEnd, boundToElementIds, unboundFromElementIds, elementsMap) => {
9032
+ if (bindableElement !== "keep") {
9033
+ if (bindableElement != null) {
9034
+ if (otherEdgeBindableElement == null || (otherEdgeBindableElement === "keep" ? !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
9035
+ linearElement,
9036
+ bindableElement,
9037
+ startOrEnd
9038
+ ) : startOrEnd === "start" || otherEdgeBindableElement.id !== bindableElement.id)) {
9039
+ bindLinearElement(
9040
+ linearElement,
9041
+ bindableElement,
9042
+ startOrEnd,
9043
+ elementsMap
9044
+ );
9045
+ boundToElementIds.add(bindableElement.id);
9046
+ }
9047
+ } else {
9048
+ const unbound = unbindLinearElement(linearElement, startOrEnd);
9049
+ if (unbound != null) {
9050
+ unboundFromElementIds.add(unbound);
9051
+ }
9052
+ }
9053
+ }
9158
9054
  };
9159
- var maxBindingGap = (element, elementWidth, elementHeight) => {
9160
- const shapeRatio = element.type === "diamond" ? 1 / Math.sqrt(2) : 1;
9161
- const smallerDimension = shapeRatio * Math.min(elementWidth, elementHeight);
9162
- return Math.max(16, Math.min(0.25 * smallerDimension, 32));
9163
- };
9164
- var hitTestPointAgainstElement = (args) => {
9165
- switch (args.element.type) {
9166
- case "rectangle":
9167
- case "iframe":
9168
- case "embeddable":
9169
- case "image":
9170
- case "text":
9171
- case "diamond":
9172
- case "ellipse":
9173
- const distance5 = distanceToBindableElement(
9174
- args.element,
9175
- args.point,
9176
- args.elementsMap
9177
- );
9178
- return args.check(distance5, args.threshold);
9179
- case "freedraw": {
9180
- if (!args.check(
9181
- distanceToRectangle(args.element, args.point, args.elementsMap),
9182
- args.threshold
9183
- )) {
9184
- return false;
9185
- }
9186
- return hitTestFreeDrawElement(
9187
- args.element,
9188
- args.point,
9189
- args.threshold,
9190
- args.elementsMap
9055
+ var bindOrUnbindSelectedElements = (selectedElements, app) => {
9056
+ selectedElements.forEach((selectedElement) => {
9057
+ if (isBindingElement(selectedElement)) {
9058
+ bindOrUnbindLinearElement(
9059
+ selectedElement,
9060
+ getElligibleElementForBindingElement(selectedElement, "start", app),
9061
+ getElligibleElementForBindingElement(selectedElement, "end", app),
9062
+ app.scene.getNonDeletedElementsMap()
9191
9063
  );
9192
- }
9193
- case "arrow":
9194
- case "line":
9195
- return hitTestLinear(args);
9196
- case "selection":
9197
- console.warn(
9198
- "This should not happen, we need to investigate why it does."
9064
+ } else if (isBindableElement(selectedElement)) {
9065
+ maybeBindBindableElement(
9066
+ selectedElement,
9067
+ app.scene.getNonDeletedElementsMap(),
9068
+ app
9199
9069
  );
9200
- return false;
9201
- case "frame":
9202
- case "magicframe": {
9203
- if (args.check(
9204
- distanceToBindableElement(args.element, args.point, args.elementsMap),
9205
- args.threshold
9206
- )) {
9207
- return true;
9208
- }
9209
- const frameNameBounds = args.frameNameBoundsCache?.get(args.element);
9210
- if (frameNameBounds) {
9211
- return args.check(
9212
- distanceToRectangleBox(frameNameBounds, args.point),
9213
- args.threshold
9214
- );
9215
- }
9216
- return false;
9217
9070
  }
9218
- }
9071
+ });
9219
9072
  };
9220
- var distanceToBindableElement = (element, point2, elementsMap) => {
9221
- switch (element.type) {
9222
- case "rectangle":
9223
- case "image":
9224
- case "text":
9225
- case "iframe":
9226
- case "embeddable":
9227
- case "frame":
9228
- case "magicframe":
9229
- return distanceToRectangle(element, point2, elementsMap);
9230
- case "diamond":
9231
- return distanceToDiamond(element, point2, elementsMap);
9232
- case "ellipse":
9233
- return distanceToEllipse(element, point2, elementsMap);
9073
+ var maybeBindBindableElement = (bindableElement, elementsMap, app) => {
9074
+ getElligibleElementsForBindableElementAndWhere(bindableElement, app).forEach(
9075
+ ([linearElement, where]) => bindOrUnbindLinearElement(
9076
+ linearElement,
9077
+ where === "end" ? "keep" : bindableElement,
9078
+ where === "start" ? "keep" : bindableElement,
9079
+ elementsMap
9080
+ )
9081
+ );
9082
+ };
9083
+ var maybeBindLinearElement = (linearElement, appState, pointerCoords, app) => {
9084
+ if (appState.startBoundElement != null) {
9085
+ bindLinearElement(
9086
+ linearElement,
9087
+ appState.startBoundElement,
9088
+ "start",
9089
+ app.scene.getNonDeletedElementsMap()
9090
+ );
9091
+ }
9092
+ const hoveredElement = getHoveredElementForBinding(pointerCoords, app);
9093
+ if (hoveredElement != null && !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
9094
+ linearElement,
9095
+ hoveredElement,
9096
+ "end"
9097
+ )) {
9098
+ bindLinearElement(
9099
+ linearElement,
9100
+ hoveredElement,
9101
+ "end",
9102
+ app.scene.getNonDeletedElementsMap()
9103
+ );
9234
9104
  }
9235
9105
  };
9236
- var isStrictlyInside = (distance5, threshold) => {
9237
- return distance5 < 0;
9106
+ var bindLinearElement = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
9107
+ mutateElement(linearElement, {
9108
+ [startOrEnd === "start" ? "startBinding" : "endBinding"]: {
9109
+ elementId: hoveredElement.id,
9110
+ ...calculateFocusAndGap(
9111
+ linearElement,
9112
+ hoveredElement,
9113
+ startOrEnd,
9114
+ elementsMap
9115
+ )
9116
+ }
9117
+ });
9118
+ const boundElementsMap = arrayToMap(hoveredElement.boundElements || []);
9119
+ if (!boundElementsMap.has(linearElement.id)) {
9120
+ mutateElement(hoveredElement, {
9121
+ boundElements: (hoveredElement.boundElements || []).concat({
9122
+ id: linearElement.id,
9123
+ type: "arrow"
9124
+ })
9125
+ });
9126
+ }
9238
9127
  };
9239
- var isInsideCheck = (distance5, threshold) => {
9240
- return distance5 < threshold;
9128
+ var isLinearElementSimpleAndAlreadyBoundOnOppositeEdge = (linearElement, bindableElement, startOrEnd) => {
9129
+ const otherBinding = linearElement[startOrEnd === "start" ? "endBinding" : "startBinding"];
9130
+ return isLinearElementSimpleAndAlreadyBound(
9131
+ linearElement,
9132
+ otherBinding?.elementId,
9133
+ bindableElement
9134
+ );
9241
9135
  };
9242
- var isNearCheck = (distance5, threshold) => {
9243
- return Math.abs(distance5) < threshold;
9136
+ var isLinearElementSimpleAndAlreadyBound = (linearElement, alreadyBoundToId, bindableElement) => {
9137
+ return alreadyBoundToId === bindableElement.id && linearElement.points.length < 3;
9244
9138
  };
9245
- var isOutsideCheck = (distance5, threshold) => {
9246
- return 0 <= distance5 && distance5 < threshold;
9139
+ var unbindLinearElements = (elements, elementsMap) => {
9140
+ elements.forEach((element) => {
9141
+ if (isBindingElement(element)) {
9142
+ bindOrUnbindLinearElement(element, null, null, elementsMap);
9143
+ }
9144
+ });
9247
9145
  };
9248
- var distanceToRectangle = (element, point2, elementsMap) => {
9249
- const [, pointRel, hwidth, hheight] = pointRelativeToElement(
9250
- element,
9251
- point2,
9252
- elementsMap
9253
- );
9254
- return Math.max(
9255
- distanceToLine(pointRel, equation(0, 1, -hheight)),
9256
- distanceToLine(pointRel, equation(1, 0, -hwidth))
9257
- );
9146
+ var unbindLinearElement = (linearElement, startOrEnd) => {
9147
+ const field = startOrEnd === "start" ? "startBinding" : "endBinding";
9148
+ const binding = linearElement[field];
9149
+ if (binding == null) {
9150
+ return null;
9151
+ }
9152
+ mutateElement(linearElement, { [field]: null });
9153
+ return binding.elementId;
9258
9154
  };
9259
- var distanceToRectangleBox = (box, point2) => {
9260
- const [, pointRel, hwidth, hheight] = pointRelativeToDivElement(point2, box);
9261
- return Math.max(
9262
- distanceToLine(pointRel, equation(0, 1, -hheight)),
9263
- distanceToLine(pointRel, equation(1, 0, -hwidth))
9155
+ var getHoveredElementForBinding = (pointerCoords, app) => {
9156
+ const hoveredElement = getElementAtPosition(
9157
+ app.scene.getNonDeletedElements(),
9158
+ (element) => isBindableElement(element, false) && bindingBorderTest(element, pointerCoords, app)
9264
9159
  );
9160
+ return hoveredElement;
9265
9161
  };
9266
- var distanceToDiamond = (element, point2, elementsMap) => {
9267
- const [, pointRel, hwidth, hheight] = pointRelativeToElement(
9268
- element,
9269
- point2,
9162
+ var calculateFocusAndGap = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
9163
+ const direction = startOrEnd === "start" ? -1 : 1;
9164
+ const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
9165
+ const adjacentPointIndex = edgePointIndex - direction;
9166
+ const edgePoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
9167
+ linearElement,
9168
+ edgePointIndex,
9270
9169
  elementsMap
9271
9170
  );
9272
- const side = equation(hheight, hwidth, -hheight * hwidth);
9273
- return distanceToLine(pointRel, side);
9274
- };
9275
- var distanceToEllipse = (element, point2, elementsMap) => {
9276
- const [pointRel, tangent] = ellipseParamsForTest(element, point2, elementsMap);
9277
- return -sign(tangent) * distanceToLine(pointRel, tangent);
9278
- };
9279
- var ellipseParamsForTest = (element, point2, elementsMap) => {
9280
- const [, pointRel, hwidth, hheight] = pointRelativeToElement(
9281
- element,
9282
- point2,
9171
+ const adjacentPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
9172
+ linearElement,
9173
+ adjacentPointIndex,
9283
9174
  elementsMap
9284
9175
  );
9285
- const [px, py] = toTuple(pointRel);
9286
- let tx = 0.707;
9287
- let ty = 0.707;
9288
- const a2 = hwidth;
9289
- const b2 = hheight;
9290
- [0, 1, 2, 3].forEach((_) => {
9291
- const xx = a2 * tx;
9292
- const yy = b2 * ty;
9293
- const ex = (a2 * a2 - b2 * b2) * tx ** 3 / a2;
9294
- const ey = (b2 * b2 - a2 * a2) * ty ** 3 / b2;
9295
- const rx = xx - ex;
9296
- const ry = yy - ey;
9297
- const qx = px - ex;
9298
- const qy = py - ey;
9299
- const r = Math.hypot(ry, rx);
9300
- const q = Math.hypot(qy, qx);
9301
- tx = Math.min(1, Math.max(0, (qx * r / q + ex) / a2));
9302
- ty = Math.min(1, Math.max(0, (qy * r / q + ey) / b2));
9303
- const t2 = Math.hypot(ty, tx);
9304
- tx /= t2;
9305
- ty /= t2;
9306
- });
9307
- const closestPoint = point(a2 * tx, b2 * ty);
9308
- const tangent = orthogonalThrough(pointRel, closestPoint);
9309
- return [pointRel, tangent];
9310
- };
9311
- var hitTestFreeDrawElement = (element, point2, threshold, elementsMap) => {
9312
- let x;
9313
- let y;
9314
- if (element.angle === 0) {
9315
- x = point2[0] - element.x;
9316
- y = point2[1] - element.y;
9317
- } else {
9318
- const [minX, minY, maxX, maxY] = getElementAbsoluteCoords(
9319
- element,
9176
+ return {
9177
+ focus: determineFocusDistance(
9178
+ hoveredElement,
9179
+ adjacentPoint,
9180
+ edgePoint,
9320
9181
  elementsMap
9321
- );
9322
- const rotatedPoint = rotatePoint(
9323
- point2,
9324
- [minX + (maxX - minX) / 2, minY + (maxY - minY) / 2],
9325
- -element.angle
9326
- );
9327
- x = rotatedPoint[0] - element.x;
9328
- y = rotatedPoint[1] - element.y;
9329
- }
9330
- let [A2, B2] = element.points;
9331
- let P;
9332
- if (distance2d(A2[0], A2[1], x, y) < threshold || distance2d(B2[0], B2[1], x, y) < threshold) {
9333
- return true;
9334
- }
9335
- for (let i2 = 0; i2 < element.points.length; i2++) {
9336
- const delta = [B2[0] - A2[0], B2[1] - A2[1]];
9337
- const length = Math.hypot(delta[1], delta[0]);
9338
- const U = [delta[0] / length, delta[1] / length];
9339
- const C2 = [x - A2[0], y - A2[1]];
9340
- const d = (C2[0] * U[0] + C2[1] * U[1]) / Math.hypot(U[1], U[0]);
9341
- P = [A2[0] + U[0] * d, A2[1] + U[1] * d];
9342
- const da = distance2d(P[0], P[1], A2[0], A2[1]);
9343
- const db = distance2d(P[0], P[1], B2[0], B2[1]);
9344
- P = db < da && da > length ? B2 : da < db && db > length ? A2 : P;
9345
- if (Math.hypot(y - P[1], x - P[0]) < threshold) {
9346
- return true;
9347
- }
9348
- A2 = B2;
9349
- B2 = element.points[i2 + 1];
9350
- }
9351
- const shape = ShapeCache.get(element);
9352
- if (shape && shape.sets.length) {
9353
- return element.fillStyle === "solid" ? hitTestCurveInside(shape, x, y, "round") : hitTestRoughShape(shape, x, y, threshold);
9354
- }
9355
- return false;
9182
+ ),
9183
+ gap: Math.max(
9184
+ 1,
9185
+ distanceToBindableElement(hoveredElement, edgePoint, elementsMap)
9186
+ )
9187
+ };
9356
9188
  };
9357
- var hitTestLinear = (args) => {
9358
- const { element, threshold } = args;
9359
- if (!ShapeCache.get(element)) {
9360
- return false;
9361
- }
9362
- const [point2, pointAbs, hwidth, hheight] = pointRelativeToElement(
9363
- args.element,
9364
- args.point,
9365
- args.elementsMap
9189
+ var updateBoundElements = (changedElement, elementsMap, options) => {
9190
+ const boundLinearElements = (changedElement.boundElements ?? []).filter(
9191
+ (el) => el.type === "arrow"
9366
9192
  );
9367
- const side1 = equation(0, 1, -hheight);
9368
- const side2 = equation(1, 0, -hwidth);
9369
- if (!isInsideCheck(distanceToLine(pointAbs, side1), threshold) || !isInsideCheck(distanceToLine(pointAbs, side2), threshold)) {
9370
- return false;
9371
- }
9372
- const [relX, relY] = toTuple(point2);
9373
- const shape = ShapeCache.get(element);
9374
- if (!shape) {
9375
- return false;
9193
+ if (boundLinearElements.length === 0) {
9194
+ return;
9376
9195
  }
9377
- if (args.check === isInsideCheck) {
9378
- const hit = shape.some(
9379
- (subshape) => hitTestCurveInside(
9380
- subshape,
9381
- relX,
9382
- relY,
9383
- element.roundness ? "round" : "sharp"
9384
- )
9196
+ const { newSize, simultaneouslyUpdated } = options ?? {};
9197
+ const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(
9198
+ simultaneouslyUpdated
9199
+ );
9200
+ const scene = Scene_default.getScene(changedElement);
9201
+ getNonDeletedElements(
9202
+ scene,
9203
+ boundLinearElements.map((el) => el.id)
9204
+ ).forEach((element) => {
9205
+ if (!isLinearElement(element)) {
9206
+ return;
9207
+ }
9208
+ const bindableElement = changedElement;
9209
+ if (!doesNeedUpdate(element, bindableElement)) {
9210
+ return;
9211
+ }
9212
+ const startBinding = maybeCalculateNewGapWhenScaling(
9213
+ bindableElement,
9214
+ element.startBinding,
9215
+ newSize
9385
9216
  );
9386
- if (hit) {
9387
- return true;
9217
+ const endBinding = maybeCalculateNewGapWhenScaling(
9218
+ bindableElement,
9219
+ element.endBinding,
9220
+ newSize
9221
+ );
9222
+ if (simultaneouslyUpdatedElementIds.has(element.id)) {
9223
+ mutateElement(element, { startBinding, endBinding });
9224
+ return;
9388
9225
  }
9389
- }
9390
- return shape.some(
9391
- (subshape) => hitTestRoughShape(subshape, relX, relY, threshold)
9392
- );
9393
- };
9394
- var pointRelativeToElement = (element, pointTuple, elementsMap) => {
9395
- const point2 = from(pointTuple);
9396
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9397
- const center = coordsCenter(x1, y1, x2, y2);
9398
- const rotate3 = rotation(center, element.angle);
9399
- const pointRotated = apply(rotate3, point2);
9400
- const pointRelToCenter = sub(pointRotated, from2(center));
9401
- const pointRelToCenterAbs = abs(pointRelToCenter);
9402
- const elementPos = offset(element.x, element.y);
9403
- const pointRelToPos = sub(pointRotated, elementPos);
9404
- const halfWidth = (x2 - x1) / 2;
9405
- const halfHeight = (y2 - y1) / 2;
9406
- return [pointRelToPos, pointRelToCenterAbs, halfWidth, halfHeight];
9407
- };
9408
- var pointRelativeToDivElement = (pointTuple, rectangle2) => {
9409
- const point2 = from(pointTuple);
9410
- const [x1, y1, x2, y2] = getRectangleBoxAbsoluteCoords(rectangle2);
9411
- const center = coordsCenter(x1, y1, x2, y2);
9412
- const rotate3 = rotation(center, rectangle2.angle);
9413
- const pointRotated = apply(rotate3, point2);
9414
- const pointRelToCenter = sub(pointRotated, from2(center));
9415
- const pointRelToCenterAbs = abs(pointRelToCenter);
9416
- const elementPos = offset(rectangle2.x, rectangle2.y);
9417
- const pointRelToPos = sub(pointRotated, elementPos);
9418
- const halfWidth = (x2 - x1) / 2;
9419
- const halfHeight = (y2 - y1) / 2;
9420
- return [pointRelToPos, pointRelToCenterAbs, halfWidth, halfHeight];
9421
- };
9422
- var relativizationToElementCenter = (element, elementsMap) => {
9423
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9424
- const center = coordsCenter(x1, y1, x2, y2);
9425
- const rotate3 = rotation(center, element.angle);
9426
- const translate2 = reverse(
9427
- translation(from2(center))
9428
- );
9429
- return compose(rotate3, translate2);
9226
+ updateBoundPoint(
9227
+ element,
9228
+ "start",
9229
+ startBinding,
9230
+ changedElement,
9231
+ elementsMap
9232
+ );
9233
+ updateBoundPoint(
9234
+ element,
9235
+ "end",
9236
+ endBinding,
9237
+ changedElement,
9238
+ elementsMap
9239
+ );
9240
+ const boundText = getBoundTextElement(
9241
+ element,
9242
+ scene.getNonDeletedElementsMap()
9243
+ );
9244
+ if (boundText) {
9245
+ handleBindTextResize(element, scene.getNonDeletedElementsMap(), false);
9246
+ }
9247
+ });
9430
9248
  };
9431
- var coordsCenter = (x1, y1, x2, y2) => {
9432
- return point((x1 + x2) / 2, (y1 + y2) / 2);
9249
+ var doesNeedUpdate = (boundElement, changedElement) => {
9250
+ return boundElement.startBinding?.elementId === changedElement.id || boundElement.endBinding?.elementId === changedElement.id;
9433
9251
  };
9434
- var determineFocusDistance = (element, a2, b2, elementsMap) => {
9435
- const relateToCenter = relativizationToElementCenter(element, elementsMap);
9436
- const aRel = apply(relateToCenter, from(a2));
9437
- const bRel = apply(relateToCenter, from(b2));
9438
- const line2 = through(aRel, bRel);
9439
- const q = element.height / element.width;
9440
- const hwidth = element.width / 2;
9441
- const hheight = element.height / 2;
9442
- const n2 = line2[2];
9443
- const m = line2[3];
9444
- const c = line2[1];
9445
- const mabs = Math.abs(m);
9446
- const nabs = Math.abs(n2);
9447
- let ret;
9448
- switch (element.type) {
9449
- case "rectangle":
9450
- case "image":
9451
- case "text":
9452
- case "iframe":
9453
- case "embeddable":
9454
- case "frame":
9455
- case "magicframe":
9456
- ret = c / (hwidth * (nabs + q * mabs));
9457
- break;
9458
- case "diamond":
9459
- ret = mabs < nabs ? c / (nabs * hwidth) : c / (mabs * hheight);
9460
- break;
9461
- case "ellipse":
9462
- ret = c / (hwidth * Math.sqrt(n2 ** 2 + q ** 2 * m ** 2));
9463
- break;
9464
- }
9465
- return ret || 0;
9252
+ var getSimultaneouslyUpdatedElementIds = (simultaneouslyUpdated) => {
9253
+ return new Set((simultaneouslyUpdated || []).map((element) => element.id));
9466
9254
  };
9467
- var determineFocusPoint = (element, focus, adjecentPoint, elementsMap) => {
9468
- if (focus === 0) {
9469
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9470
- const center = coordsCenter(x1, y1, x2, y2);
9471
- return toTuple(center);
9255
+ var updateBoundPoint = (linearElement, startOrEnd, binding, changedElement, elementsMap) => {
9256
+ if (binding == null || // We only need to update the other end if this is a 2 point line element
9257
+ binding.elementId !== changedElement.id && linearElement.points.length > 2) {
9258
+ return;
9472
9259
  }
9473
- const relateToCenter = relativizationToElementCenter(element, elementsMap);
9474
- const adjecentPointRel = apply(
9475
- relateToCenter,
9476
- from(adjecentPoint)
9260
+ const bindingElement = Scene_default.getScene(linearElement).getElement(
9261
+ binding.elementId
9477
9262
  );
9478
- const reverseRelateToCenter = reverse(relateToCenter);
9479
- let point2;
9480
- switch (element.type) {
9481
- case "rectangle":
9482
- case "image":
9483
- case "text":
9484
- case "diamond":
9485
- case "iframe":
9486
- case "embeddable":
9487
- case "frame":
9488
- case "magicframe":
9489
- point2 = findFocusPointForRectangulars(element, focus, adjecentPointRel);
9490
- break;
9491
- case "ellipse":
9492
- point2 = findFocusPointForEllipse(element, focus, adjecentPointRel);
9493
- break;
9263
+ if (bindingElement == null) {
9264
+ return;
9494
9265
  }
9495
- return toTuple(apply(reverseRelateToCenter, point2));
9496
- };
9497
- var intersectElementWithLine = (element, a2, b2, gap = 0, elementsMap) => {
9498
- const relateToCenter = relativizationToElementCenter(element, elementsMap);
9499
- const aRel = apply(relateToCenter, from(a2));
9500
- const bRel = apply(relateToCenter, from(b2));
9501
- const line2 = through(aRel, bRel);
9502
- const reverseRelateToCenter = reverse(relateToCenter);
9503
- const intersections = getSortedElementLineIntersections(
9504
- element,
9505
- line2,
9506
- aRel,
9507
- gap
9266
+ const direction = startOrEnd === "start" ? -1 : 1;
9267
+ const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
9268
+ const adjacentPointIndex = edgePointIndex - direction;
9269
+ const adjacentPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
9270
+ linearElement,
9271
+ adjacentPointIndex,
9272
+ elementsMap
9508
9273
  );
9509
- return intersections.map(
9510
- (point2) => toTuple(apply(reverseRelateToCenter, point2))
9274
+ const focusPointAbsolute = determineFocusPoint(
9275
+ bindingElement,
9276
+ binding.focus,
9277
+ adjacentPoint,
9278
+ elementsMap
9511
9279
  );
9512
- };
9513
- var getSortedElementLineIntersections = (element, line2, nearPoint, gap = 0) => {
9514
- let intersections;
9515
- switch (element.type) {
9516
- case "rectangle":
9517
- case "image":
9518
- case "text":
9519
- case "diamond":
9520
- case "iframe":
9521
- case "embeddable":
9522
- case "frame":
9523
- case "magicframe":
9524
- const corners = getCorners(element);
9525
- intersections = corners.flatMap((point2, i2) => {
9526
- const edge = [point2, corners[(i2 + 1) % 4]];
9527
- return intersectSegment(line2, offsetSegment(edge, gap));
9528
- }).concat(
9529
- corners.flatMap((point2) => getCircleIntersections(point2, gap, line2))
9530
- );
9531
- break;
9532
- case "ellipse":
9533
- intersections = getEllipseIntersections(element, gap, line2);
9534
- break;
9535
- }
9536
- if (intersections.length < 2) {
9537
- return [];
9280
+ let newEdgePoint;
9281
+ if (binding.gap === 0) {
9282
+ newEdgePoint = focusPointAbsolute;
9283
+ } else {
9284
+ const intersections = intersectElementWithLine(
9285
+ bindingElement,
9286
+ adjacentPoint,
9287
+ focusPointAbsolute,
9288
+ binding.gap,
9289
+ elementsMap
9290
+ );
9291
+ if (intersections.length === 0) {
9292
+ newEdgePoint = focusPointAbsolute;
9293
+ } else {
9294
+ newEdgePoint = intersections[0];
9295
+ }
9538
9296
  }
9539
- const sortedIntersections = intersections.sort(
9540
- (i1, i2) => distance3(i1, nearPoint) - distance3(i2, nearPoint)
9297
+ LinearElementEditor.movePoints(
9298
+ linearElement,
9299
+ [
9300
+ {
9301
+ index: edgePointIndex,
9302
+ point: LinearElementEditor.pointFromAbsoluteCoords(
9303
+ linearElement,
9304
+ newEdgePoint,
9305
+ elementsMap
9306
+ )
9307
+ }
9308
+ ],
9309
+ { [startOrEnd === "start" ? "startBinding" : "endBinding"]: binding }
9541
9310
  );
9542
- return [
9543
- sortedIntersections[0],
9544
- sortedIntersections[sortedIntersections.length - 1]
9545
- ];
9546
- };
9547
- var getCorners = (element, scale = 1) => {
9548
- const hx = scale * element.width / 2;
9549
- const hy = scale * element.height / 2;
9550
- switch (element.type) {
9551
- case "rectangle":
9552
- case "image":
9553
- case "text":
9554
- case "iframe":
9555
- case "embeddable":
9556
- case "frame":
9557
- case "magicframe":
9558
- return [
9559
- point(hx, hy),
9560
- point(hx, -hy),
9561
- point(-hx, -hy),
9562
- point(-hx, hy)
9563
- ];
9564
- case "diamond":
9565
- return [
9566
- point(0, hy),
9567
- point(hx, 0),
9568
- point(0, -hy),
9569
- point(-hx, 0)
9570
- ];
9571
- }
9572
9311
  };
9573
- var intersectSegment = (line2, segment) => {
9574
- const [a2, b2] = segment;
9575
- const aDist = distanceToLine(a2, line2);
9576
- const bDist = distanceToLine(b2, line2);
9577
- if (aDist * bDist >= 0) {
9578
- return [];
9312
+ var maybeCalculateNewGapWhenScaling = (changedElement, currentBinding, newSize) => {
9313
+ if (currentBinding == null || newSize == null) {
9314
+ return currentBinding;
9579
9315
  }
9580
- return [intersect(line2, through(a2, b2))];
9581
- };
9582
- var offsetSegment = (segment, distance5) => {
9583
- const [a2, b2] = segment;
9584
- const offset2 = translationOrthogonal(
9585
- fromTo(a2, b2),
9586
- distance5
9316
+ const { gap, focus, elementId } = currentBinding;
9317
+ const { width: newWidth, height: newHeight } = newSize;
9318
+ const { width, height } = changedElement;
9319
+ const newGap = Math.max(
9320
+ 1,
9321
+ Math.min(
9322
+ maxBindingGap(changedElement, newWidth, newHeight),
9323
+ gap * (newWidth < newHeight ? newWidth / width : newHeight / height)
9324
+ )
9587
9325
  );
9588
- return [apply(offset2, a2), apply(offset2, b2)];
9326
+ return { elementId, gap: newGap, focus };
9589
9327
  };
9590
- var getEllipseIntersections = (element, gap, line2) => {
9591
- const a2 = element.width / 2 + gap;
9592
- const b2 = element.height / 2 + gap;
9593
- const m = line2[2];
9594
- const n2 = line2[3];
9595
- const c = line2[1];
9596
- const squares = a2 * a2 * m * m + b2 * b2 * n2 * n2;
9597
- const discr = squares - c * c;
9598
- if (squares === 0 || discr <= 0) {
9599
- return [];
9600
- }
9601
- const discrRoot = Math.sqrt(discr);
9602
- const xn = -a2 * a2 * m * c;
9603
- const yn = -b2 * b2 * n2 * c;
9604
- return [
9605
- point(
9606
- (xn + a2 * b2 * n2 * discrRoot) / squares,
9607
- (yn - a2 * b2 * m * discrRoot) / squares
9608
- ),
9609
- point(
9610
- (xn - a2 * b2 * n2 * discrRoot) / squares,
9611
- (yn + a2 * b2 * m * discrRoot) / squares
9612
- )
9613
- ];
9328
+ var getEligibleElementsForBinding = (selectedElements, app) => {
9329
+ const includedElementIds = new Set(selectedElements.map(({ id }) => id));
9330
+ return selectedElements.flatMap(
9331
+ (selectedElement) => isBindingElement(selectedElement, false) ? getElligibleElementsForBindingElement(
9332
+ selectedElement,
9333
+ app
9334
+ ).filter(
9335
+ (element) => !includedElementIds.has(element.id)
9336
+ ) : isBindableElement(selectedElement, false) ? getElligibleElementsForBindableElementAndWhere(
9337
+ selectedElement,
9338
+ app
9339
+ ).filter((binding) => !includedElementIds.has(binding[0].id)) : []
9340
+ );
9614
9341
  };
9615
- var getCircleIntersections = (center, radius, line2) => {
9616
- if (radius === 0) {
9617
- return distanceToLine(line2, center) === 0 ? [center] : [];
9618
- }
9619
- const m = line2[2];
9620
- const n2 = line2[3];
9621
- const c = line2[1];
9622
- const [a2, b2] = toTuple(center);
9623
- const r = radius;
9624
- const squares = m * m + n2 * n2;
9625
- const discr = r * r * squares - (m * a2 + n2 * b2 + c) ** 2;
9626
- if (squares === 0 || discr <= 0) {
9627
- return [];
9628
- }
9629
- const discrRoot = Math.sqrt(discr);
9630
- const xn = a2 * n2 * n2 - b2 * m * n2 - m * c;
9631
- const yn = b2 * m * m - a2 * m * n2 - n2 * c;
9342
+ var getElligibleElementsForBindingElement = (linearElement, app) => {
9632
9343
  return [
9633
- point((xn + n2 * discrRoot) / squares, (yn - m * discrRoot) / squares),
9634
- point((xn - n2 * discrRoot) / squares, (yn + m * discrRoot) / squares)
9635
- ];
9344
+ getElligibleElementForBindingElement(linearElement, "start", app),
9345
+ getElligibleElementForBindingElement(linearElement, "end", app)
9346
+ ].filter(
9347
+ (element) => element != null
9348
+ );
9636
9349
  };
9637
- var findFocusPointForEllipse = (ellipse2, relativeDistance, point2) => {
9638
- const relativeDistanceAbs = Math.abs(relativeDistance);
9639
- const a2 = ellipse2.width * relativeDistanceAbs / 2;
9640
- const b2 = ellipse2.height * relativeDistanceAbs / 2;
9641
- const orientation = Math.sign(relativeDistance);
9642
- const [px, pyo] = toTuple(point2);
9643
- const py = pyo === 0 ? 1e-4 : pyo;
9644
- const squares = px ** 2 * b2 ** 2 + py ** 2 * a2 ** 2;
9645
- const m = (-px * b2 ** 2 + orientation * py * Math.sqrt(Math.max(0, squares - a2 ** 2 * b2 ** 2))) / squares;
9646
- let n2 = (-m * px - 1) / py;
9647
- if (n2 === 0) {
9648
- n2 = (Object.is(n2, -0) ? -1 : 1) * 0.01;
9649
- }
9650
- const x = -(a2 ** 2 * m) / (n2 ** 2 * b2 ** 2 + m ** 2 * a2 ** 2);
9651
- return point(x, (-m * x - 1) / n2);
9350
+ var getElligibleElementForBindingElement = (linearElement, startOrEnd, app) => {
9351
+ return getHoveredElementForBinding(
9352
+ getLinearElementEdgeCoors(
9353
+ linearElement,
9354
+ startOrEnd,
9355
+ app.scene.getNonDeletedElementsMap()
9356
+ ),
9357
+ app
9358
+ );
9652
9359
  };
9653
- var findFocusPointForRectangulars = (element, relativeDistance, point2) => {
9654
- const relativeDistanceAbs = Math.abs(relativeDistance);
9655
- const orientation = Math.sign(relativeDistance);
9656
- const corners = getCorners(element, relativeDistanceAbs);
9657
- let maxDistance = 0;
9658
- let tangentPoint = null;
9659
- corners.forEach((corner) => {
9660
- const distance5 = orientation * through(point2, corner)[1];
9661
- if (distance5 > maxDistance) {
9662
- maxDistance = distance5;
9663
- tangentPoint = corner;
9664
- }
9665
- });
9666
- return tangentPoint;
9360
+ var getLinearElementEdgeCoors = (linearElement, startOrEnd, elementsMap) => {
9361
+ const index = startOrEnd === "start" ? 0 : -1;
9362
+ return tupleToCoors(
9363
+ LinearElementEditor.getPointAtIndexGlobalCoordinates(
9364
+ linearElement,
9365
+ index,
9366
+ elementsMap
9367
+ )
9368
+ );
9667
9369
  };
9668
- var pointInBezierEquation = (p0, p1, p2, p3, [mx, my], lineThreshold) => {
9669
- const equation2 = (t3, idx) => Math.pow(1 - t3, 3) * p3[idx] + 3 * t3 * Math.pow(1 - t3, 2) * p2[idx] + 3 * Math.pow(t3, 2) * (1 - t3) * p1[idx] + p0[idx] * Math.pow(t3, 3);
9670
- let t2 = 0;
9671
- while (t2 <= 1) {
9672
- const tx = equation2(t2, 0);
9673
- const ty = equation2(t2, 1);
9674
- const diff = Math.sqrt(Math.pow(tx - mx, 2) + Math.pow(ty - my, 2));
9675
- if (diff < lineThreshold) {
9676
- return true;
9370
+ var getElligibleElementsForBindableElementAndWhere = (bindableElement, app) => {
9371
+ const scene = Scene_default.getScene(bindableElement);
9372
+ return scene.getNonDeletedElements().map((element) => {
9373
+ if (!isBindingElement(element, false)) {
9374
+ return null;
9677
9375
  }
9678
- t2 += 0.01;
9679
- }
9680
- return false;
9376
+ const canBindStart = isLinearElementEligibleForNewBindingByBindable(
9377
+ element,
9378
+ "start",
9379
+ bindableElement,
9380
+ scene.getNonDeletedElementsMap(),
9381
+ app
9382
+ );
9383
+ const canBindEnd = isLinearElementEligibleForNewBindingByBindable(
9384
+ element,
9385
+ "end",
9386
+ bindableElement,
9387
+ scene.getNonDeletedElementsMap(),
9388
+ app
9389
+ );
9390
+ if (!canBindStart && !canBindEnd) {
9391
+ return null;
9392
+ }
9393
+ return [
9394
+ element,
9395
+ canBindStart && canBindEnd ? "both" : canBindStart ? "start" : "end",
9396
+ bindableElement
9397
+ ];
9398
+ }).filter((maybeElement) => maybeElement != null);
9681
9399
  };
9682
- var hitTestCurveInside = (drawable, x, y, roundness) => {
9683
- const ops = getCurvePathOps(drawable);
9684
- const points = [];
9685
- let odd = false;
9686
- for (const operation of ops) {
9687
- if (operation.op === "move") {
9688
- odd = !odd;
9689
- if (odd) {
9690
- points.push([operation.data[0], operation.data[1]]);
9400
+ var isLinearElementEligibleForNewBindingByBindable = (linearElement, startOrEnd, bindableElement, elementsMap, app) => {
9401
+ const existingBinding = linearElement[startOrEnd === "start" ? "startBinding" : "endBinding"];
9402
+ return existingBinding == null && !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
9403
+ linearElement,
9404
+ bindableElement,
9405
+ startOrEnd
9406
+ ) && bindingBorderTest(
9407
+ bindableElement,
9408
+ getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap),
9409
+ app
9410
+ );
9411
+ };
9412
+ var fixBindingsAfterDuplication = (sceneElements, oldElements, oldIdToDuplicatedId, duplicatesServeAsOld) => {
9413
+ const allBoundElementIds = /* @__PURE__ */ new Set();
9414
+ const allBindableElementIds = /* @__PURE__ */ new Set();
9415
+ const shouldReverseRoles = duplicatesServeAsOld === "duplicatesServeAsOld";
9416
+ oldElements.forEach((oldElement) => {
9417
+ const { boundElements } = oldElement;
9418
+ if (boundElements != null && boundElements.length > 0) {
9419
+ boundElements.forEach((boundElement) => {
9420
+ if (shouldReverseRoles && !oldIdToDuplicatedId.has(boundElement.id)) {
9421
+ allBoundElementIds.add(boundElement.id);
9422
+ }
9423
+ });
9424
+ allBindableElementIds.add(oldIdToDuplicatedId.get(oldElement.id));
9425
+ }
9426
+ if (isBindingElement(oldElement)) {
9427
+ if (oldElement.startBinding != null) {
9428
+ const { elementId } = oldElement.startBinding;
9429
+ if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) {
9430
+ allBindableElementIds.add(elementId);
9431
+ }
9691
9432
  }
9692
- } else if (operation.op === "bcurveTo") {
9693
- if (odd) {
9694
- points.push([operation.data[0], operation.data[1]]);
9695
- points.push([operation.data[2], operation.data[3]]);
9696
- points.push([operation.data[4], operation.data[5]]);
9433
+ if (oldElement.endBinding != null) {
9434
+ const { elementId } = oldElement.endBinding;
9435
+ if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) {
9436
+ allBindableElementIds.add(elementId);
9437
+ }
9697
9438
  }
9698
- } else if (operation.op === "lineTo") {
9699
- if (odd) {
9700
- points.push([operation.data[0], operation.data[1]]);
9439
+ if (oldElement.startBinding != null || oldElement.endBinding != null) {
9440
+ allBoundElementIds.add(oldIdToDuplicatedId.get(oldElement.id));
9701
9441
  }
9702
9442
  }
9703
- }
9704
- if (points.length >= 4) {
9705
- if (roundness === "sharp") {
9706
- return isPointInPolygon(points, x, y);
9443
+ });
9444
+ sceneElements.filter(
9445
+ ({ id }) => allBoundElementIds.has(id)
9446
+ ).forEach((element) => {
9447
+ const { startBinding, endBinding } = element;
9448
+ mutateElement(element, {
9449
+ startBinding: newBindingAfterDuplication(
9450
+ startBinding,
9451
+ oldIdToDuplicatedId
9452
+ ),
9453
+ endBinding: newBindingAfterDuplication(endBinding, oldIdToDuplicatedId)
9454
+ });
9455
+ });
9456
+ sceneElements.filter(({ id }) => allBindableElementIds.has(id)).forEach((bindableElement) => {
9457
+ const { boundElements } = bindableElement;
9458
+ if (boundElements != null && boundElements.length > 0) {
9459
+ mutateElement(bindableElement, {
9460
+ boundElements: boundElements.map(
9461
+ (boundElement) => oldIdToDuplicatedId.has(boundElement.id) ? {
9462
+ id: oldIdToDuplicatedId.get(boundElement.id),
9463
+ type: boundElement.type
9464
+ } : boundElement
9465
+ )
9466
+ });
9707
9467
  }
9708
- const polygonPoints = pointsOnBezierCurves2(points, 10, 5);
9709
- return isPointInPolygon(polygonPoints, x, y);
9468
+ });
9469
+ };
9470
+ var newBindingAfterDuplication = (binding, oldIdToDuplicatedId) => {
9471
+ if (binding == null) {
9472
+ return null;
9710
9473
  }
9711
- return false;
9474
+ const { elementId, focus, gap } = binding;
9475
+ return {
9476
+ focus,
9477
+ gap,
9478
+ elementId: oldIdToDuplicatedId.get(elementId) ?? elementId
9479
+ };
9712
9480
  };
9713
- var hitTestRoughShape = (drawable, x, y, lineThreshold) => {
9714
- const ops = getCurvePathOps(drawable);
9715
- let currentP = [0, 0];
9716
- return ops.some(({ op, data }, idx) => {
9717
- if (op === "move") {
9718
- currentP = data;
9719
- } else if (op === "bcurveTo") {
9720
- const p1 = [data[0], data[1]];
9721
- const p2 = [data[2], data[3]];
9722
- const p3 = [data[4], data[5]];
9723
- const p0 = currentP;
9724
- currentP = p3;
9725
- const retVal = pointInBezierEquation(
9726
- p0,
9727
- p1,
9728
- p2,
9729
- p3,
9730
- [x, y],
9731
- lineThreshold
9732
- );
9733
- return retVal;
9734
- } else if (op === "lineTo") {
9735
- return hitTestCurveInside(drawable, x, y, "sharp");
9736
- } else if (op === "qcurveTo") {
9737
- console.warn("qcurveTo is not implemented yet");
9481
+ var fixBindingsAfterDeletion = (sceneElements, deletedElements) => {
9482
+ const deletedElementIds = new Set(
9483
+ deletedElements.map((element) => element.id)
9484
+ );
9485
+ const affectedElements = /* @__PURE__ */ new Set();
9486
+ deletedElements.forEach((deletedElement) => {
9487
+ if (isBindableElement(deletedElement)) {
9488
+ deletedElement.boundElements?.forEach((element) => {
9489
+ if (!deletedElementIds.has(element.id)) {
9490
+ affectedElements.add(element.id);
9491
+ }
9492
+ });
9493
+ } else if (isBindingElement(deletedElement)) {
9494
+ if (deletedElement.startBinding) {
9495
+ affectedElements.add(deletedElement.startBinding.elementId);
9496
+ }
9497
+ if (deletedElement.endBinding) {
9498
+ affectedElements.add(deletedElement.endBinding.elementId);
9499
+ }
9738
9500
  }
9739
- return false;
9740
9501
  });
9741
- };
9742
-
9743
- // keys.ts
9744
- init_define_import_meta_env();
9745
- var CODES = {
9746
- EQUAL: "Equal",
9747
- MINUS: "Minus",
9748
- NUM_ADD: "NumpadAdd",
9749
- NUM_SUBTRACT: "NumpadSubtract",
9750
- NUM_ZERO: "Numpad0",
9751
- BRACKET_RIGHT: "BracketRight",
9752
- BRACKET_LEFT: "BracketLeft",
9753
- ONE: "Digit1",
9754
- TWO: "Digit2",
9755
- THREE: "Digit3",
9756
- NINE: "Digit9",
9757
- QUOTE: "Quote",
9758
- ZERO: "Digit0",
9759
- SLASH: "Slash",
9760
- C: "KeyC",
9761
- D: "KeyD",
9762
- H: "KeyH",
9763
- V: "KeyV",
9764
- Z: "KeyZ",
9765
- R: "KeyR",
9766
- S: "KeyS"
9767
- };
9768
- var KEYS = {
9769
- ARROW_DOWN: "ArrowDown",
9770
- ARROW_LEFT: "ArrowLeft",
9771
- ARROW_RIGHT: "ArrowRight",
9772
- ARROW_UP: "ArrowUp",
9773
- PAGE_UP: "PageUp",
9774
- PAGE_DOWN: "PageDown",
9775
- BACKSPACE: "Backspace",
9776
- ALT: "Alt",
9777
- CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey",
9778
- DELETE: "Delete",
9779
- ENTER: "Enter",
9780
- ESCAPE: "Escape",
9781
- QUESTION_MARK: "?",
9782
- SPACE: " ",
9783
- TAB: "Tab",
9784
- CHEVRON_LEFT: "<",
9785
- CHEVRON_RIGHT: ">",
9786
- PERIOD: ".",
9787
- COMMA: ",",
9788
- SUBTRACT: "-",
9789
- SLASH: "/",
9790
- A: "a",
9791
- C: "c",
9792
- D: "d",
9793
- E: "e",
9794
- F: "f",
9795
- G: "g",
9796
- H: "h",
9797
- I: "i",
9798
- L: "l",
9799
- O: "o",
9800
- P: "p",
9801
- Q: "q",
9802
- R: "r",
9803
- S: "s",
9804
- T: "t",
9805
- V: "v",
9806
- X: "x",
9807
- Y: "y",
9808
- Z: "z",
9809
- K: "k",
9810
- W: "w",
9811
- 0: "0",
9812
- 1: "1",
9813
- 2: "2",
9814
- 3: "3",
9815
- 4: "4",
9816
- 5: "5",
9817
- 6: "6",
9818
- 7: "7",
9819
- 8: "8",
9820
- 9: "9"
9821
- };
9822
- var isArrowKey = (key) => key === KEYS.ARROW_LEFT || key === KEYS.ARROW_RIGHT || key === KEYS.ARROW_DOWN || key === KEYS.ARROW_UP;
9823
- var shouldResizeFromCenter = (event) => event.altKey;
9824
- var shouldMaintainAspectRatio = (event) => event.shiftKey;
9825
- var shouldRotateWithDiscreteAngle = (event) => event.shiftKey;
9826
-
9827
- // element/binding.ts
9828
- var shouldEnableBindingForPointerEvent = (event) => {
9829
- return !event[KEYS.CTRL_OR_CMD];
9830
- };
9831
- var isBindingEnabled = (appState) => {
9832
- return appState.isBindingEnabled;
9833
- };
9834
- var getNonDeletedElements = (scene, ids) => {
9835
- const result = [];
9836
- ids.forEach((id) => {
9837
- const element = scene.getNonDeletedElement(id);
9838
- if (element != null) {
9839
- result.push(element);
9840
- }
9841
- });
9842
- return result;
9843
- };
9844
- var bindOrUnbindLinearElement = (linearElement, startBindingElement, endBindingElement, elementsMap) => {
9845
- const boundToElementIds = /* @__PURE__ */ new Set();
9846
- const unboundFromElementIds = /* @__PURE__ */ new Set();
9847
- bindOrUnbindLinearElementEdge(
9848
- linearElement,
9849
- startBindingElement,
9850
- endBindingElement,
9851
- "start",
9852
- boundToElementIds,
9853
- unboundFromElementIds,
9854
- elementsMap
9855
- );
9856
- bindOrUnbindLinearElementEdge(
9857
- linearElement,
9858
- endBindingElement,
9859
- startBindingElement,
9860
- "end",
9861
- boundToElementIds,
9862
- unboundFromElementIds,
9863
- elementsMap
9864
- );
9865
- const onlyUnbound = Array.from(unboundFromElementIds).filter(
9866
- (id) => !boundToElementIds.has(id)
9867
- );
9868
- getNonDeletedElements(Scene_default.getScene(linearElement), onlyUnbound).forEach(
9869
- (element) => {
9502
+ sceneElements.filter(({ id }) => affectedElements.has(id)).forEach((element) => {
9503
+ if (isBindableElement(element)) {
9870
9504
  mutateElement(element, {
9871
- boundElements: element.boundElements?.filter(
9872
- (element2) => element2.type !== "arrow" || element2.id !== linearElement.id
9505
+ boundElements: newBoundElementsAfterDeletion(
9506
+ element.boundElements,
9507
+ deletedElementIds
9508
+ )
9509
+ });
9510
+ } else if (isBindingElement(element)) {
9511
+ mutateElement(element, {
9512
+ startBinding: newBindingAfterDeletion(
9513
+ element.startBinding,
9514
+ deletedElementIds
9515
+ ),
9516
+ endBinding: newBindingAfterDeletion(
9517
+ element.endBinding,
9518
+ deletedElementIds
9873
9519
  )
9874
9520
  });
9875
9521
  }
9876
- );
9522
+ });
9877
9523
  };
9878
- var bindOrUnbindLinearElementEdge = (linearElement, bindableElement, otherEdgeBindableElement, startOrEnd, boundToElementIds, unboundFromElementIds, elementsMap) => {
9879
- if (bindableElement !== "keep") {
9880
- if (bindableElement != null) {
9881
- if (otherEdgeBindableElement == null || (otherEdgeBindableElement === "keep" ? !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
9882
- linearElement,
9883
- bindableElement,
9884
- startOrEnd
9885
- ) : startOrEnd === "start" || otherEdgeBindableElement.id !== bindableElement.id)) {
9886
- bindLinearElement(
9887
- linearElement,
9888
- bindableElement,
9889
- startOrEnd,
9890
- elementsMap
9891
- );
9892
- boundToElementIds.add(bindableElement.id);
9893
- }
9894
- } else {
9895
- const unbound = unbindLinearElement(linearElement, startOrEnd);
9896
- if (unbound != null) {
9897
- unboundFromElementIds.add(unbound);
9898
- }
9899
- }
9524
+ var newBindingAfterDeletion = (binding, deletedElementIds) => {
9525
+ if (binding == null || deletedElementIds.has(binding.elementId)) {
9526
+ return null;
9900
9527
  }
9528
+ return binding;
9901
9529
  };
9902
- var bindOrUnbindSelectedElements = (selectedElements, elements, elementsMap) => {
9903
- selectedElements.forEach((selectedElement) => {
9904
- if (isBindingElement(selectedElement)) {
9905
- bindOrUnbindLinearElement(
9906
- selectedElement,
9907
- getElligibleElementForBindingElement(
9908
- selectedElement,
9909
- "start",
9910
- elements,
9911
- elementsMap
9912
- ),
9913
- getElligibleElementForBindingElement(
9914
- selectedElement,
9915
- "end",
9916
- elements,
9917
- elementsMap
9918
- ),
9919
- elementsMap
9920
- );
9921
- } else if (isBindableElement(selectedElement)) {
9922
- maybeBindBindableElement(selectedElement, elementsMap);
9923
- }
9924
- });
9530
+ var newBoundElementsAfterDeletion = (boundElements, deletedElementIds) => {
9531
+ if (!boundElements) {
9532
+ return null;
9533
+ }
9534
+ return boundElements.filter((ele) => !deletedElementIds.has(ele.id));
9925
9535
  };
9926
- var maybeBindBindableElement = (bindableElement, elementsMap) => {
9927
- getElligibleElementsForBindableElementAndWhere(
9928
- bindableElement,
9536
+ var bindingBorderTest = (element, { x, y }, app) => {
9537
+ const threshold = maxBindingGap(element, element.width, element.height);
9538
+ const shape = app.getElementShape(element);
9539
+ return isPointOnShape([x, y], shape, threshold);
9540
+ };
9541
+ var maxBindingGap = (element, elementWidth, elementHeight) => {
9542
+ const shapeRatio = element.type === "diamond" ? 1 / Math.sqrt(2) : 1;
9543
+ const smallerDimension = shapeRatio * Math.min(elementWidth, elementHeight);
9544
+ return Math.max(16, Math.min(0.25 * smallerDimension, 32));
9545
+ };
9546
+ var distanceToBindableElement = (element, point2, elementsMap) => {
9547
+ switch (element.type) {
9548
+ case "rectangle":
9549
+ case "image":
9550
+ case "text":
9551
+ case "iframe":
9552
+ case "embeddable":
9553
+ case "frame":
9554
+ case "magicframe":
9555
+ return distanceToRectangle(element, point2, elementsMap);
9556
+ case "diamond":
9557
+ return distanceToDiamond(element, point2, elementsMap);
9558
+ case "ellipse":
9559
+ return distanceToEllipse2(element, point2, elementsMap);
9560
+ }
9561
+ };
9562
+ var distanceToRectangle = (element, point2, elementsMap) => {
9563
+ const [, pointRel, hwidth, hheight] = pointRelativeToElement(
9564
+ element,
9565
+ point2,
9929
9566
  elementsMap
9930
- ).forEach(
9931
- ([linearElement, where]) => bindOrUnbindLinearElement(
9932
- linearElement,
9933
- where === "end" ? "keep" : bindableElement,
9934
- where === "start" ? "keep" : bindableElement,
9935
- elementsMap
9936
- )
9567
+ );
9568
+ return Math.max(
9569
+ distanceToLine(pointRel, equation(0, 1, -hheight)),
9570
+ distanceToLine(pointRel, equation(1, 0, -hwidth))
9937
9571
  );
9938
9572
  };
9939
- var maybeBindLinearElement = (linearElement, appState, scene, pointerCoords, elementsMap) => {
9940
- if (appState.startBoundElement != null) {
9941
- bindLinearElement(
9942
- linearElement,
9943
- appState.startBoundElement,
9944
- "start",
9945
- elementsMap
9946
- );
9947
- }
9948
- const hoveredElement = getHoveredElementForBinding(
9949
- pointerCoords,
9950
- scene.getNonDeletedElements(),
9573
+ var distanceToDiamond = (element, point2, elementsMap) => {
9574
+ const [, pointRel, hwidth, hheight] = pointRelativeToElement(
9575
+ element,
9576
+ point2,
9951
9577
  elementsMap
9952
9578
  );
9953
- if (hoveredElement != null && !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
9954
- linearElement,
9955
- hoveredElement,
9956
- "end"
9957
- )) {
9958
- bindLinearElement(linearElement, hoveredElement, "end", elementsMap);
9959
- }
9579
+ const side = equation(hheight, hwidth, -hheight * hwidth);
9580
+ return distanceToLine(pointRel, side);
9960
9581
  };
9961
- var bindLinearElement = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
9962
- mutateElement(linearElement, {
9963
- [startOrEnd === "start" ? "startBinding" : "endBinding"]: {
9964
- elementId: hoveredElement.id,
9965
- ...calculateFocusAndGap(
9966
- linearElement,
9967
- hoveredElement,
9968
- startOrEnd,
9969
- elementsMap
9970
- )
9971
- }
9972
- });
9973
- const boundElementsMap = arrayToMap(hoveredElement.boundElements || []);
9974
- if (!boundElementsMap.has(linearElement.id)) {
9975
- mutateElement(hoveredElement, {
9976
- boundElements: (hoveredElement.boundElements || []).concat({
9977
- id: linearElement.id,
9978
- type: "arrow"
9979
- })
9980
- });
9981
- }
9582
+ var distanceToEllipse2 = (element, point2, elementsMap) => {
9583
+ const [pointRel, tangent] = ellipseParamsForTest(element, point2, elementsMap);
9584
+ return -sign(tangent) * distanceToLine(pointRel, tangent);
9982
9585
  };
9983
- var isLinearElementSimpleAndAlreadyBoundOnOppositeEdge = (linearElement, bindableElement, startOrEnd) => {
9984
- const otherBinding = linearElement[startOrEnd === "start" ? "endBinding" : "startBinding"];
9985
- return isLinearElementSimpleAndAlreadyBound(
9986
- linearElement,
9987
- otherBinding?.elementId,
9988
- bindableElement
9586
+ var ellipseParamsForTest = (element, point2, elementsMap) => {
9587
+ const [, pointRel, hwidth, hheight] = pointRelativeToElement(
9588
+ element,
9589
+ point2,
9590
+ elementsMap
9989
9591
  );
9990
- };
9991
- var isLinearElementSimpleAndAlreadyBound = (linearElement, alreadyBoundToId, bindableElement) => {
9992
- return alreadyBoundToId === bindableElement.id && linearElement.points.length < 3;
9993
- };
9994
- var unbindLinearElements = (elements, elementsMap) => {
9995
- elements.forEach((element) => {
9996
- if (isBindingElement(element)) {
9997
- bindOrUnbindLinearElement(element, null, null, elementsMap);
9998
- }
9592
+ const [px, py] = toTuple(pointRel);
9593
+ let tx = 0.707;
9594
+ let ty = 0.707;
9595
+ const a2 = hwidth;
9596
+ const b2 = hheight;
9597
+ [0, 1, 2, 3].forEach((_) => {
9598
+ const xx = a2 * tx;
9599
+ const yy = b2 * ty;
9600
+ const ex = (a2 * a2 - b2 * b2) * tx ** 3 / a2;
9601
+ const ey = (b2 * b2 - a2 * a2) * ty ** 3 / b2;
9602
+ const rx = xx - ex;
9603
+ const ry = yy - ey;
9604
+ const qx = px - ex;
9605
+ const qy = py - ey;
9606
+ const r = Math.hypot(ry, rx);
9607
+ const q = Math.hypot(qy, qx);
9608
+ tx = Math.min(1, Math.max(0, (qx * r / q + ex) / a2));
9609
+ ty = Math.min(1, Math.max(0, (qy * r / q + ey) / b2));
9610
+ const t2 = Math.hypot(ty, tx);
9611
+ tx /= t2;
9612
+ ty /= t2;
9999
9613
  });
9614
+ const closestPoint = point(a2 * tx, b2 * ty);
9615
+ const tangent = orthogonalThrough(pointRel, closestPoint);
9616
+ return [pointRel, tangent];
10000
9617
  };
10001
- var unbindLinearElement = (linearElement, startOrEnd) => {
10002
- const field = startOrEnd === "start" ? "startBinding" : "endBinding";
10003
- const binding = linearElement[field];
10004
- if (binding == null) {
10005
- return null;
10006
- }
10007
- mutateElement(linearElement, { [field]: null });
10008
- return binding.elementId;
9618
+ var pointRelativeToElement = (element, pointTuple, elementsMap) => {
9619
+ const point2 = from(pointTuple);
9620
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9621
+ const center = coordsCenter(x1, y1, x2, y2);
9622
+ const rotate4 = rotation(center, element.angle);
9623
+ const pointRotated = apply(rotate4, point2);
9624
+ const pointRelToCenter = sub(pointRotated, from2(center));
9625
+ const pointRelToCenterAbs = abs(pointRelToCenter);
9626
+ const elementPos = offset(element.x, element.y);
9627
+ const pointRelToPos = sub(pointRotated, elementPos);
9628
+ const halfWidth = (x2 - x1) / 2;
9629
+ const halfHeight = (y2 - y1) / 2;
9630
+ return [pointRelToPos, pointRelToCenterAbs, halfWidth, halfHeight];
10009
9631
  };
10010
- var getHoveredElementForBinding = (pointerCoords, elements, elementsMap) => {
10011
- const hoveredElement = getElementAtPosition(
10012
- elements,
10013
- (element) => isBindableElement(element, false) && bindingBorderTest(element, pointerCoords, elementsMap)
9632
+ var relativizationToElementCenter = (element, elementsMap) => {
9633
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9634
+ const center = coordsCenter(x1, y1, x2, y2);
9635
+ const rotate4 = rotation(center, element.angle);
9636
+ const translate2 = reverse(
9637
+ translation(from2(center))
10014
9638
  );
10015
- return hoveredElement;
9639
+ return compose(rotate4, translate2);
10016
9640
  };
10017
- var calculateFocusAndGap = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
10018
- const direction = startOrEnd === "start" ? -1 : 1;
10019
- const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
10020
- const adjacentPointIndex = edgePointIndex - direction;
10021
- const edgePoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
10022
- linearElement,
10023
- edgePointIndex,
10024
- elementsMap
10025
- );
10026
- const adjacentPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
10027
- linearElement,
10028
- adjacentPointIndex,
10029
- elementsMap
10030
- );
10031
- return {
10032
- focus: determineFocusDistance(
10033
- hoveredElement,
10034
- adjacentPoint,
10035
- edgePoint,
10036
- elementsMap
10037
- ),
10038
- gap: Math.max(
10039
- 1,
10040
- distanceToBindableElement(hoveredElement, edgePoint, elementsMap)
10041
- )
10042
- };
9641
+ var coordsCenter = (x1, y1, x2, y2) => {
9642
+ return point((x1 + x2) / 2, (y1 + y2) / 2);
10043
9643
  };
10044
- var updateBoundElements = (changedElement, elementsMap, options) => {
10045
- const boundLinearElements = (changedElement.boundElements ?? []).filter(
10046
- (el) => el.type === "arrow"
10047
- );
10048
- if (boundLinearElements.length === 0) {
10049
- return;
9644
+ var determineFocusDistance = (element, a2, b2, elementsMap) => {
9645
+ const relateToCenter = relativizationToElementCenter(element, elementsMap);
9646
+ const aRel = apply(relateToCenter, from(a2));
9647
+ const bRel = apply(relateToCenter, from(b2));
9648
+ const line2 = through(aRel, bRel);
9649
+ const q = element.height / element.width;
9650
+ const hwidth = element.width / 2;
9651
+ const hheight = element.height / 2;
9652
+ const n2 = line2[2];
9653
+ const m = line2[3];
9654
+ const c = line2[1];
9655
+ const mabs = Math.abs(m);
9656
+ const nabs = Math.abs(n2);
9657
+ let ret;
9658
+ switch (element.type) {
9659
+ case "rectangle":
9660
+ case "image":
9661
+ case "text":
9662
+ case "iframe":
9663
+ case "embeddable":
9664
+ case "frame":
9665
+ case "magicframe":
9666
+ ret = c / (hwidth * (nabs + q * mabs));
9667
+ break;
9668
+ case "diamond":
9669
+ ret = mabs < nabs ? c / (nabs * hwidth) : c / (mabs * hheight);
9670
+ break;
9671
+ case "ellipse":
9672
+ ret = c / (hwidth * Math.sqrt(n2 ** 2 + q ** 2 * m ** 2));
9673
+ break;
10050
9674
  }
10051
- const { newSize, simultaneouslyUpdated } = options ?? {};
10052
- const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(
10053
- simultaneouslyUpdated
10054
- );
10055
- const scene = Scene_default.getScene(changedElement);
10056
- getNonDeletedElements(
10057
- scene,
10058
- boundLinearElements.map((el) => el.id)
10059
- ).forEach((element) => {
10060
- if (!isLinearElement(element)) {
10061
- return;
10062
- }
10063
- const bindableElement = changedElement;
10064
- if (!doesNeedUpdate(element, bindableElement)) {
10065
- return;
10066
- }
10067
- const startBinding = maybeCalculateNewGapWhenScaling(
10068
- bindableElement,
10069
- element.startBinding,
10070
- newSize
10071
- );
10072
- const endBinding = maybeCalculateNewGapWhenScaling(
10073
- bindableElement,
10074
- element.endBinding,
10075
- newSize
10076
- );
10077
- if (simultaneouslyUpdatedElementIds.has(element.id)) {
10078
- mutateElement(element, { startBinding, endBinding });
10079
- return;
10080
- }
10081
- updateBoundPoint(
10082
- element,
10083
- "start",
10084
- startBinding,
10085
- changedElement,
10086
- elementsMap
10087
- );
10088
- updateBoundPoint(
10089
- element,
10090
- "end",
10091
- endBinding,
10092
- changedElement,
10093
- elementsMap
10094
- );
10095
- const boundText = getBoundTextElement(
10096
- element,
10097
- scene.getNonDeletedElementsMap()
10098
- );
10099
- if (boundText) {
10100
- handleBindTextResize(element, scene.getNonDeletedElementsMap(), false);
10101
- }
10102
- });
10103
- };
10104
- var doesNeedUpdate = (boundElement, changedElement) => {
10105
- return boundElement.startBinding?.elementId === changedElement.id || boundElement.endBinding?.elementId === changedElement.id;
10106
- };
10107
- var getSimultaneouslyUpdatedElementIds = (simultaneouslyUpdated) => {
10108
- return new Set((simultaneouslyUpdated || []).map((element) => element.id));
9675
+ return ret || 0;
10109
9676
  };
10110
- var updateBoundPoint = (linearElement, startOrEnd, binding, changedElement, elementsMap) => {
10111
- if (binding == null || // We only need to update the other end if this is a 2 point line element
10112
- binding.elementId !== changedElement.id && linearElement.points.length > 2) {
10113
- return;
9677
+ var determineFocusPoint = (element, focus, adjecentPoint, elementsMap) => {
9678
+ if (focus === 0) {
9679
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
9680
+ const center = coordsCenter(x1, y1, x2, y2);
9681
+ return toTuple(center);
10114
9682
  }
10115
- const bindingElement = Scene_default.getScene(linearElement).getElement(
10116
- binding.elementId
9683
+ const relateToCenter = relativizationToElementCenter(element, elementsMap);
9684
+ const adjecentPointRel = apply(
9685
+ relateToCenter,
9686
+ from(adjecentPoint)
10117
9687
  );
10118
- if (bindingElement == null) {
10119
- return;
9688
+ const reverseRelateToCenter = reverse(relateToCenter);
9689
+ let point2;
9690
+ switch (element.type) {
9691
+ case "rectangle":
9692
+ case "image":
9693
+ case "text":
9694
+ case "diamond":
9695
+ case "iframe":
9696
+ case "embeddable":
9697
+ case "frame":
9698
+ case "magicframe":
9699
+ point2 = findFocusPointForRectangulars(element, focus, adjecentPointRel);
9700
+ break;
9701
+ case "ellipse":
9702
+ point2 = findFocusPointForEllipse(element, focus, adjecentPointRel);
9703
+ break;
10120
9704
  }
10121
- const direction = startOrEnd === "start" ? -1 : 1;
10122
- const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
10123
- const adjacentPointIndex = edgePointIndex - direction;
10124
- const adjacentPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
10125
- linearElement,
10126
- adjacentPointIndex,
10127
- elementsMap
10128
- );
10129
- const focusPointAbsolute = determineFocusPoint(
10130
- bindingElement,
10131
- binding.focus,
10132
- adjacentPoint,
10133
- elementsMap
9705
+ return toTuple(apply(reverseRelateToCenter, point2));
9706
+ };
9707
+ var intersectElementWithLine = (element, a2, b2, gap = 0, elementsMap) => {
9708
+ const relateToCenter = relativizationToElementCenter(element, elementsMap);
9709
+ const aRel = apply(relateToCenter, from(a2));
9710
+ const bRel = apply(relateToCenter, from(b2));
9711
+ const line2 = through(aRel, bRel);
9712
+ const reverseRelateToCenter = reverse(relateToCenter);
9713
+ const intersections = getSortedElementLineIntersections(
9714
+ element,
9715
+ line2,
9716
+ aRel,
9717
+ gap
10134
9718
  );
10135
- let newEdgePoint;
10136
- if (binding.gap === 0) {
10137
- newEdgePoint = focusPointAbsolute;
10138
- } else {
10139
- const intersections = intersectElementWithLine(
10140
- bindingElement,
10141
- adjacentPoint,
10142
- focusPointAbsolute,
10143
- binding.gap,
10144
- elementsMap
10145
- );
10146
- if (intersections.length === 0) {
10147
- newEdgePoint = focusPointAbsolute;
10148
- } else {
10149
- newEdgePoint = intersections[0];
10150
- }
10151
- }
10152
- LinearElementEditor.movePoints(
10153
- linearElement,
10154
- [
10155
- {
10156
- index: edgePointIndex,
10157
- point: LinearElementEditor.pointFromAbsoluteCoords(
10158
- linearElement,
10159
- newEdgePoint,
10160
- elementsMap
10161
- )
10162
- }
10163
- ],
10164
- { [startOrEnd === "start" ? "startBinding" : "endBinding"]: binding }
9719
+ return intersections.map(
9720
+ (point2) => toTuple(apply(reverseRelateToCenter, point2))
10165
9721
  );
10166
9722
  };
10167
- var maybeCalculateNewGapWhenScaling = (changedElement, currentBinding, newSize) => {
10168
- if (currentBinding == null || newSize == null) {
10169
- return currentBinding;
9723
+ var getSortedElementLineIntersections = (element, line2, nearPoint, gap = 0) => {
9724
+ let intersections;
9725
+ switch (element.type) {
9726
+ case "rectangle":
9727
+ case "image":
9728
+ case "text":
9729
+ case "diamond":
9730
+ case "iframe":
9731
+ case "embeddable":
9732
+ case "frame":
9733
+ case "magicframe":
9734
+ const corners = getCorners(element);
9735
+ intersections = corners.flatMap((point2, i2) => {
9736
+ const edge = [point2, corners[(i2 + 1) % 4]];
9737
+ return intersectSegment(line2, offsetSegment(edge, gap));
9738
+ }).concat(
9739
+ corners.flatMap((point2) => getCircleIntersections(point2, gap, line2))
9740
+ );
9741
+ break;
9742
+ case "ellipse":
9743
+ intersections = getEllipseIntersections(element, gap, line2);
9744
+ break;
10170
9745
  }
10171
- const { gap, focus, elementId } = currentBinding;
10172
- const { width: newWidth, height: newHeight } = newSize;
10173
- const { width, height } = changedElement;
10174
- const newGap = Math.max(
10175
- 1,
10176
- Math.min(
10177
- maxBindingGap(changedElement, newWidth, newHeight),
10178
- gap * (newWidth < newHeight ? newWidth / width : newHeight / height)
10179
- )
10180
- );
10181
- return { elementId, gap: newGap, focus };
10182
- };
10183
- var getEligibleElementsForBinding = (selectedElements, elements, elementsMap) => {
10184
- const includedElementIds = new Set(selectedElements.map(({ id }) => id));
10185
- return selectedElements.flatMap(
10186
- (selectedElement) => isBindingElement(selectedElement, false) ? getElligibleElementsForBindingElement(
10187
- selectedElement,
10188
- elements,
10189
- elementsMap
10190
- ).filter(
10191
- (element) => !includedElementIds.has(element.id)
10192
- ) : isBindableElement(selectedElement, false) ? getElligibleElementsForBindableElementAndWhere(
10193
- selectedElement,
10194
- elementsMap
10195
- ).filter((binding) => !includedElementIds.has(binding[0].id)) : []
9746
+ if (intersections.length < 2) {
9747
+ return [];
9748
+ }
9749
+ const sortedIntersections = intersections.sort(
9750
+ (i1, i2) => distance3(i1, nearPoint) - distance3(i2, nearPoint)
10196
9751
  );
10197
- };
10198
- var getElligibleElementsForBindingElement = (linearElement, elements, elementsMap) => {
10199
9752
  return [
10200
- getElligibleElementForBindingElement(
10201
- linearElement,
10202
- "start",
10203
- elements,
10204
- elementsMap
10205
- ),
10206
- getElligibleElementForBindingElement(
10207
- linearElement,
10208
- "end",
10209
- elements,
10210
- elementsMap
10211
- )
10212
- ].filter(
10213
- (element) => element != null
10214
- );
10215
- };
10216
- var getElligibleElementForBindingElement = (linearElement, startOrEnd, elements, elementsMap) => {
10217
- return getHoveredElementForBinding(
10218
- getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap),
10219
- elements,
10220
- elementsMap
10221
- );
10222
- };
10223
- var getLinearElementEdgeCoors = (linearElement, startOrEnd, elementsMap) => {
10224
- const index = startOrEnd === "start" ? 0 : -1;
10225
- return tupleToCoors(
10226
- LinearElementEditor.getPointAtIndexGlobalCoordinates(
10227
- linearElement,
10228
- index,
10229
- elementsMap
10230
- )
10231
- );
10232
- };
10233
- var getElligibleElementsForBindableElementAndWhere = (bindableElement, elementsMap) => {
10234
- const scene = Scene_default.getScene(bindableElement);
10235
- return scene.getNonDeletedElements().map((element) => {
10236
- if (!isBindingElement(element, false)) {
10237
- return null;
10238
- }
10239
- const canBindStart = isLinearElementEligibleForNewBindingByBindable(
10240
- element,
10241
- "start",
10242
- bindableElement,
10243
- elementsMap
10244
- );
10245
- const canBindEnd = isLinearElementEligibleForNewBindingByBindable(
10246
- element,
10247
- "end",
10248
- bindableElement,
10249
- elementsMap
10250
- );
10251
- if (!canBindStart && !canBindEnd) {
10252
- return null;
10253
- }
10254
- return [
10255
- element,
10256
- canBindStart && canBindEnd ? "both" : canBindStart ? "start" : "end",
10257
- bindableElement
10258
- ];
10259
- }).filter((maybeElement) => maybeElement != null);
10260
- };
10261
- var isLinearElementEligibleForNewBindingByBindable = (linearElement, startOrEnd, bindableElement, elementsMap) => {
10262
- const existingBinding = linearElement[startOrEnd === "start" ? "startBinding" : "endBinding"];
10263
- return existingBinding == null && !isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
10264
- linearElement,
10265
- bindableElement,
10266
- startOrEnd
10267
- ) && bindingBorderTest(
10268
- bindableElement,
10269
- getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap),
10270
- elementsMap
10271
- );
9753
+ sortedIntersections[0],
9754
+ sortedIntersections[sortedIntersections.length - 1]
9755
+ ];
10272
9756
  };
10273
- var fixBindingsAfterDuplication = (sceneElements, oldElements, oldIdToDuplicatedId, duplicatesServeAsOld) => {
10274
- const allBoundElementIds = /* @__PURE__ */ new Set();
10275
- const allBindableElementIds = /* @__PURE__ */ new Set();
10276
- const shouldReverseRoles = duplicatesServeAsOld === "duplicatesServeAsOld";
10277
- oldElements.forEach((oldElement) => {
10278
- const { boundElements } = oldElement;
10279
- if (boundElements != null && boundElements.length > 0) {
10280
- boundElements.forEach((boundElement) => {
10281
- if (shouldReverseRoles && !oldIdToDuplicatedId.has(boundElement.id)) {
10282
- allBoundElementIds.add(boundElement.id);
10283
- }
10284
- });
10285
- allBindableElementIds.add(oldIdToDuplicatedId.get(oldElement.id));
10286
- }
10287
- if (isBindingElement(oldElement)) {
10288
- if (oldElement.startBinding != null) {
10289
- const { elementId } = oldElement.startBinding;
10290
- if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) {
10291
- allBindableElementIds.add(elementId);
10292
- }
10293
- }
10294
- if (oldElement.endBinding != null) {
10295
- const { elementId } = oldElement.endBinding;
10296
- if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) {
10297
- allBindableElementIds.add(elementId);
10298
- }
10299
- }
10300
- if (oldElement.startBinding != null || oldElement.endBinding != null) {
10301
- allBoundElementIds.add(oldIdToDuplicatedId.get(oldElement.id));
10302
- }
10303
- }
10304
- });
10305
- sceneElements.filter(
10306
- ({ id }) => allBoundElementIds.has(id)
10307
- ).forEach((element) => {
10308
- const { startBinding, endBinding } = element;
10309
- mutateElement(element, {
10310
- startBinding: newBindingAfterDuplication(
10311
- startBinding,
10312
- oldIdToDuplicatedId
10313
- ),
10314
- endBinding: newBindingAfterDuplication(endBinding, oldIdToDuplicatedId)
10315
- });
10316
- });
10317
- sceneElements.filter(({ id }) => allBindableElementIds.has(id)).forEach((bindableElement) => {
10318
- const { boundElements } = bindableElement;
10319
- if (boundElements != null && boundElements.length > 0) {
10320
- mutateElement(bindableElement, {
10321
- boundElements: boundElements.map(
10322
- (boundElement) => oldIdToDuplicatedId.has(boundElement.id) ? {
10323
- id: oldIdToDuplicatedId.get(boundElement.id),
10324
- type: boundElement.type
10325
- } : boundElement
10326
- )
10327
- });
10328
- }
10329
- });
9757
+ var getCorners = (element, scale = 1) => {
9758
+ const hx = scale * element.width / 2;
9759
+ const hy = scale * element.height / 2;
9760
+ switch (element.type) {
9761
+ case "rectangle":
9762
+ case "image":
9763
+ case "text":
9764
+ case "iframe":
9765
+ case "embeddable":
9766
+ case "frame":
9767
+ case "magicframe":
9768
+ return [
9769
+ point(hx, hy),
9770
+ point(hx, -hy),
9771
+ point(-hx, -hy),
9772
+ point(-hx, hy)
9773
+ ];
9774
+ case "diamond":
9775
+ return [
9776
+ point(0, hy),
9777
+ point(hx, 0),
9778
+ point(0, -hy),
9779
+ point(-hx, 0)
9780
+ ];
9781
+ }
10330
9782
  };
10331
- var newBindingAfterDuplication = (binding, oldIdToDuplicatedId) => {
10332
- if (binding == null) {
10333
- return null;
9783
+ var intersectSegment = (line2, segment) => {
9784
+ const [a2, b2] = segment;
9785
+ const aDist = distanceToLine(a2, line2);
9786
+ const bDist = distanceToLine(b2, line2);
9787
+ if (aDist * bDist >= 0) {
9788
+ return [];
10334
9789
  }
10335
- const { elementId, focus, gap } = binding;
10336
- return {
10337
- focus,
10338
- gap,
10339
- elementId: oldIdToDuplicatedId.get(elementId) ?? elementId
10340
- };
9790
+ return [intersect(line2, through(a2, b2))];
10341
9791
  };
10342
- var fixBindingsAfterDeletion = (sceneElements, deletedElements) => {
10343
- const deletedElementIds = new Set(
10344
- deletedElements.map((element) => element.id)
9792
+ var offsetSegment = (segment, distance4) => {
9793
+ const [a2, b2] = segment;
9794
+ const offset2 = translationOrthogonal(
9795
+ fromTo(a2, b2),
9796
+ distance4
10345
9797
  );
10346
- const affectedElements = /* @__PURE__ */ new Set();
10347
- deletedElements.forEach((deletedElement) => {
10348
- if (isBindableElement(deletedElement)) {
10349
- deletedElement.boundElements?.forEach((element) => {
10350
- if (!deletedElementIds.has(element.id)) {
10351
- affectedElements.add(element.id);
10352
- }
10353
- });
10354
- } else if (isBindingElement(deletedElement)) {
10355
- if (deletedElement.startBinding) {
10356
- affectedElements.add(deletedElement.startBinding.elementId);
10357
- }
10358
- if (deletedElement.endBinding) {
10359
- affectedElements.add(deletedElement.endBinding.elementId);
10360
- }
10361
- }
10362
- });
10363
- sceneElements.filter(({ id }) => affectedElements.has(id)).forEach((element) => {
10364
- if (isBindableElement(element)) {
10365
- mutateElement(element, {
10366
- boundElements: newBoundElementsAfterDeletion(
10367
- element.boundElements,
10368
- deletedElementIds
10369
- )
10370
- });
10371
- } else if (isBindingElement(element)) {
10372
- mutateElement(element, {
10373
- startBinding: newBindingAfterDeletion(
10374
- element.startBinding,
10375
- deletedElementIds
10376
- ),
10377
- endBinding: newBindingAfterDeletion(
10378
- element.endBinding,
10379
- deletedElementIds
10380
- )
10381
- });
10382
- }
10383
- });
9798
+ return [apply(offset2, a2), apply(offset2, b2)];
10384
9799
  };
10385
- var newBindingAfterDeletion = (binding, deletedElementIds) => {
10386
- if (binding == null || deletedElementIds.has(binding.elementId)) {
10387
- return null;
9800
+ var getEllipseIntersections = (element, gap, line2) => {
9801
+ const a2 = element.width / 2 + gap;
9802
+ const b2 = element.height / 2 + gap;
9803
+ const m = line2[2];
9804
+ const n2 = line2[3];
9805
+ const c = line2[1];
9806
+ const squares = a2 * a2 * m * m + b2 * b2 * n2 * n2;
9807
+ const discr = squares - c * c;
9808
+ if (squares === 0 || discr <= 0) {
9809
+ return [];
10388
9810
  }
10389
- return binding;
9811
+ const discrRoot = Math.sqrt(discr);
9812
+ const xn = -a2 * a2 * m * c;
9813
+ const yn = -b2 * b2 * n2 * c;
9814
+ return [
9815
+ point(
9816
+ (xn + a2 * b2 * n2 * discrRoot) / squares,
9817
+ (yn - a2 * b2 * m * discrRoot) / squares
9818
+ ),
9819
+ point(
9820
+ (xn - a2 * b2 * n2 * discrRoot) / squares,
9821
+ (yn + a2 * b2 * m * discrRoot) / squares
9822
+ )
9823
+ ];
10390
9824
  };
10391
- var newBoundElementsAfterDeletion = (boundElements, deletedElementIds) => {
10392
- if (!boundElements) {
10393
- return null;
9825
+ var getCircleIntersections = (center, radius, line2) => {
9826
+ if (radius === 0) {
9827
+ return distanceToLine(line2, center) === 0 ? [center] : [];
10394
9828
  }
10395
- return boundElements.filter((ele) => !deletedElementIds.has(ele.id));
9829
+ const m = line2[2];
9830
+ const n2 = line2[3];
9831
+ const c = line2[1];
9832
+ const [a2, b2] = toTuple(center);
9833
+ const r = radius;
9834
+ const squares = m * m + n2 * n2;
9835
+ const discr = r * r * squares - (m * a2 + n2 * b2 + c) ** 2;
9836
+ if (squares === 0 || discr <= 0) {
9837
+ return [];
9838
+ }
9839
+ const discrRoot = Math.sqrt(discr);
9840
+ const xn = a2 * n2 * n2 - b2 * m * n2 - m * c;
9841
+ const yn = b2 * m * m - a2 * m * n2 - n2 * c;
9842
+ return [
9843
+ point((xn + n2 * discrRoot) / squares, (yn - m * discrRoot) / squares),
9844
+ point((xn - n2 * discrRoot) / squares, (yn + m * discrRoot) / squares)
9845
+ ];
9846
+ };
9847
+ var findFocusPointForEllipse = (ellipse2, relativeDistance, point2) => {
9848
+ const relativeDistanceAbs = Math.abs(relativeDistance);
9849
+ const a2 = ellipse2.width * relativeDistanceAbs / 2;
9850
+ const b2 = ellipse2.height * relativeDistanceAbs / 2;
9851
+ const orientation = Math.sign(relativeDistance);
9852
+ const [px, pyo] = toTuple(point2);
9853
+ const py = pyo === 0 ? 1e-4 : pyo;
9854
+ const squares = px ** 2 * b2 ** 2 + py ** 2 * a2 ** 2;
9855
+ const m = (-px * b2 ** 2 + orientation * py * Math.sqrt(Math.max(0, squares - a2 ** 2 * b2 ** 2))) / squares;
9856
+ let n2 = (-m * px - 1) / py;
9857
+ if (n2 === 0) {
9858
+ n2 = (Object.is(n2, -0) ? -1 : 1) * 0.01;
9859
+ }
9860
+ const x = -(a2 ** 2 * m) / (n2 ** 2 * b2 ** 2 + m ** 2 * a2 ** 2);
9861
+ return point(x, (-m * x - 1) / n2);
9862
+ };
9863
+ var findFocusPointForRectangulars = (element, relativeDistance, point2) => {
9864
+ const relativeDistanceAbs = Math.abs(relativeDistance);
9865
+ const orientation = Math.sign(relativeDistance);
9866
+ const corners = getCorners(element, relativeDistanceAbs);
9867
+ let maxDistance = 0;
9868
+ let tangentPoint = null;
9869
+ corners.forEach((corner) => {
9870
+ const distance4 = orientation * through(point2, corner)[1];
9871
+ if (distance4 > maxDistance) {
9872
+ maxDistance = distance4;
9873
+ tangentPoint = corner;
9874
+ }
9875
+ });
9876
+ return tangentPoint;
9877
+ };
9878
+
9879
+ // scene/ShapeCache.ts
9880
+ init_define_import_meta_env();
9881
+ var ShapeCache = class _ShapeCache {
9882
+ static rg = new RoughGenerator();
9883
+ static cache = /* @__PURE__ */ new WeakMap();
9884
+ /**
9885
+ * Retrieves shape from cache if available. Use this only if shape
9886
+ * is optional and you have a fallback in case it's not cached.
9887
+ */
9888
+ static get = (element) => {
9889
+ return _ShapeCache.cache.get(
9890
+ element
9891
+ );
9892
+ };
9893
+ static set = (element, shape) => _ShapeCache.cache.set(element, shape);
9894
+ static delete = (element) => _ShapeCache.cache.delete(element);
9895
+ static destroy = () => {
9896
+ _ShapeCache.cache = /* @__PURE__ */ new WeakMap();
9897
+ };
9898
+ /**
9899
+ * Generates & caches shape for element if not already cached, otherwise
9900
+ * returns cached shape.
9901
+ */
9902
+ static generateElementShape = (element, renderConfig) => {
9903
+ const cachedShape = renderConfig?.isExporting ? void 0 : _ShapeCache.get(element);
9904
+ if (cachedShape !== void 0) {
9905
+ return cachedShape;
9906
+ }
9907
+ elementWithCanvasCache.delete(element);
9908
+ const shape = _generateElementShape(
9909
+ element,
9910
+ _ShapeCache.rg,
9911
+ renderConfig || {
9912
+ isExporting: false,
9913
+ canvasBackgroundColor: COLOR_PALETTE.white,
9914
+ embedsValidationStatus: null
9915
+ }
9916
+ );
9917
+ _ShapeCache.cache.set(element, shape);
9918
+ return shape;
9919
+ };
10396
9920
  };
10397
9921
 
10398
9922
  // element/linearElementEditor.ts
@@ -10576,7 +10100,8 @@ var LinearElementEditor = class _LinearElementEditor {
10576
10100
  }
10577
10101
  return false;
10578
10102
  }
10579
- static handlePointerUp(event, editingLinearElement, appState, elements, elementsMap) {
10103
+ static handlePointerUp(event, editingLinearElement, appState, app) {
10104
+ const elementsMap = app.scene.getNonDeletedElementsMap();
10580
10105
  const { elementId, selectedPointsIndices, isDragging, pointerDownState } = editingLinearElement;
10581
10106
  const element = _LinearElementEditor.getElement(elementId, elementsMap);
10582
10107
  if (!element) {
@@ -10602,8 +10127,7 @@ var LinearElementEditor = class _LinearElementEditor {
10602
10127
  elementsMap
10603
10128
  )
10604
10129
  ),
10605
- elements,
10606
- elementsMap
10130
+ app
10607
10131
  ) : null;
10608
10132
  bindings[selectedPoint === 0 ? "startBindingElement" : "endBindingElement"] = bindingElement;
10609
10133
  }
@@ -10697,13 +10221,13 @@ var LinearElementEditor = class _LinearElementEditor {
10697
10221
  const threshold = _LinearElementEditor.POINT_HANDLE_SIZE / appState.zoom.value;
10698
10222
  const existingSegmentMidpointHitCoords = linearElementEditor.segmentMidPointHoveredCoords;
10699
10223
  if (existingSegmentMidpointHitCoords) {
10700
- const distance5 = distance2d(
10224
+ const distance4 = distance2d(
10701
10225
  existingSegmentMidpointHitCoords[0],
10702
10226
  existingSegmentMidpointHitCoords[1],
10703
10227
  scenePointer.x,
10704
10228
  scenePointer.y
10705
10229
  );
10706
- if (distance5 <= threshold) {
10230
+ if (distance4 <= threshold) {
10707
10231
  return existingSegmentMidpointHitCoords;
10708
10232
  }
10709
10233
  }
@@ -10711,13 +10235,13 @@ var LinearElementEditor = class _LinearElementEditor {
10711
10235
  const midPoints = _LinearElementEditor.getEditorMidPoints(element, elementsMap, appState);
10712
10236
  while (index < midPoints.length) {
10713
10237
  if (midPoints[index] !== null) {
10714
- const distance5 = distance2d(
10238
+ const distance4 = distance2d(
10715
10239
  midPoints[index][0],
10716
10240
  midPoints[index][1],
10717
10241
  scenePointer.x,
10718
10242
  scenePointer.y
10719
10243
  );
10720
- if (distance5 <= threshold) {
10244
+ if (distance4 <= threshold) {
10721
10245
  return midPoints[index];
10722
10246
  }
10723
10247
  }
@@ -10726,16 +10250,16 @@ var LinearElementEditor = class _LinearElementEditor {
10726
10250
  return null;
10727
10251
  };
10728
10252
  static isSegmentTooShort(element, startPoint, endPoint, zoom) {
10729
- let distance5 = distance2d(
10253
+ let distance4 = distance2d(
10730
10254
  startPoint[0],
10731
10255
  startPoint[1],
10732
10256
  endPoint[0],
10733
10257
  endPoint[1]
10734
10258
  );
10735
10259
  if (element.points.length > 2 && element.roundness) {
10736
- distance5 = getBezierCurveLength(element, endPoint);
10260
+ distance4 = getBezierCurveLength(element, endPoint);
10737
10261
  }
10738
- return distance5 * zoom.value < _LinearElementEditor.POINT_HANDLE_SIZE * 4;
10262
+ return distance4 * zoom.value < _LinearElementEditor.POINT_HANDLE_SIZE * 4;
10739
10263
  }
10740
10264
  static getSegmentMidPoint(element, startPoint, endPoint, endPointIndex, elementsMap) {
10741
10265
  let segmentMidPoint = centerPoint(startPoint, endPoint);
@@ -10788,7 +10312,8 @@ var LinearElementEditor = class _LinearElementEditor {
10788
10312
  }
10789
10313
  return -1;
10790
10314
  }
10791
- static handlePointerDown(event, appState, history, scenePointer, linearElementEditor, elements, elementsMap) {
10315
+ static handlePointerDown(event, appState, history, scenePointer, linearElementEditor, app) {
10316
+ const elementsMap = app.scene.getNonDeletedElementsMap();
10792
10317
  const ret = {
10793
10318
  didAddPoint: false,
10794
10319
  hitElement: null,
@@ -10848,11 +10373,7 @@ var LinearElementEditor = class _LinearElementEditor {
10848
10373
  },
10849
10374
  selectedPointsIndices: [element.points.length - 1],
10850
10375
  lastUncommittedPoint: null,
10851
- endBindingElement: getHoveredElementForBinding(
10852
- scenePointer,
10853
- elements,
10854
- elementsMap
10855
- )
10376
+ endBindingElement: getHoveredElementForBinding(scenePointer, app)
10856
10377
  };
10857
10378
  ret.didAddPoint = true;
10858
10379
  return ret;
@@ -10880,7 +10401,7 @@ var LinearElementEditor = class _LinearElementEditor {
10880
10401
  const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
10881
10402
  const cx = (x1 + x2) / 2;
10882
10403
  const cy = (y1 + y2) / 2;
10883
- const targetPoint = clickedPointIndex > -1 && rotate2(
10404
+ const targetPoint = clickedPointIndex > -1 && rotate3(
10884
10405
  element.x + element.points[clickedPointIndex][0],
10885
10406
  element.y + element.points[clickedPointIndex][1],
10886
10407
  cx,
@@ -10984,7 +10505,7 @@ var LinearElementEditor = class _LinearElementEditor {
10984
10505
  const cx = (x1 + x2) / 2;
10985
10506
  const cy = (y1 + y2) / 2;
10986
10507
  let { x, y } = element;
10987
- [x, y] = rotate2(x + point2[0], y + point2[1], cx, cy, element.angle);
10508
+ [x, y] = rotate3(x + point2[0], y + point2[1], cx, cy, element.angle);
10988
10509
  return [x, y];
10989
10510
  }
10990
10511
  /** scene coords */
@@ -10994,7 +10515,7 @@ var LinearElementEditor = class _LinearElementEditor {
10994
10515
  const cy = (y1 + y2) / 2;
10995
10516
  return element.points.map((point2) => {
10996
10517
  let { x, y } = element;
10997
- [x, y] = rotate2(x + point2[0], y + point2[1], cx, cy, element.angle);
10518
+ [x, y] = rotate3(x + point2[0], y + point2[1], cx, cy, element.angle);
10998
10519
  return [x, y];
10999
10520
  });
11000
10521
  }
@@ -11005,13 +10526,13 @@ var LinearElementEditor = class _LinearElementEditor {
11005
10526
  const cy = (y1 + y2) / 2;
11006
10527
  const point2 = element.points[index];
11007
10528
  const { x, y } = element;
11008
- return point2 ? rotate2(x + point2[0], y + point2[1], cx, cy, element.angle) : rotate2(x, y, cx, cy, element.angle);
10529
+ return point2 ? rotate3(x + point2[0], y + point2[1], cx, cy, element.angle) : rotate3(x, y, cx, cy, element.angle);
11009
10530
  }
11010
10531
  static pointFromAbsoluteCoords(element, absoluteCoords, elementsMap) {
11011
10532
  const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
11012
10533
  const cx = (x1 + x2) / 2;
11013
10534
  const cy = (y1 + y2) / 2;
11014
- const [x, y] = rotate2(
10535
+ const [x, y] = rotate3(
11015
10536
  absoluteCoords[0],
11016
10537
  absoluteCoords[1],
11017
10538
  cx,
@@ -11040,7 +10561,7 @@ var LinearElementEditor = class _LinearElementEditor {
11040
10561
  const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
11041
10562
  const cx = (x1 + x2) / 2;
11042
10563
  const cy = (y1 + y2) / 2;
11043
- const [rotatedX, rotatedY] = rotate2(
10564
+ const [rotatedX, rotatedY] = rotate3(
11044
10565
  pointerOnGrid[0],
11045
10566
  pointerOnGrid[1],
11046
10567
  cx,
@@ -11216,7 +10737,7 @@ var LinearElementEditor = class _LinearElementEditor {
11216
10737
  pointerDownState: linearElementEditor.pointerDownState,
11217
10738
  selectedPointsIndices: linearElementEditor.selectedPointsIndices
11218
10739
  };
11219
- const midpoint = _LinearElementEditor.createPointAt(
10740
+ const midpoint2 = _LinearElementEditor.createPointAt(
11220
10741
  element,
11221
10742
  elementsMap,
11222
10743
  pointerCoords.x,
@@ -11225,7 +10746,7 @@ var LinearElementEditor = class _LinearElementEditor {
11225
10746
  );
11226
10747
  const points = [
11227
10748
  ...element.points.slice(0, segmentMidpoint.index),
11228
- midpoint,
10749
+ midpoint2,
11229
10750
  ...element.points.slice(segmentMidpoint.index)
11230
10751
  ];
11231
10752
  mutateElement(element, {
@@ -11251,7 +10772,7 @@ var LinearElementEditor = class _LinearElementEditor {
11251
10772
  const prevCenterY = (prevCoords[1] + prevCoords[3]) / 2;
11252
10773
  const dX = prevCenterX - nextCenterX;
11253
10774
  const dY = prevCenterY - nextCenterY;
11254
- const rotated = rotate2(offsetX, offsetY, dX, dY, element.angle);
10775
+ const rotated = rotate3(offsetX, offsetY, dX, dY, element.angle);
11255
10776
  mutateElement(element, {
11256
10777
  ...otherUpdates,
11257
10778
  points: nextPoints,
@@ -11941,36 +11462,6 @@ var suppportsHorizontalAlign = (selectedElements, elementsMap) => {
11941
11462
  return isTextElement(element);
11942
11463
  });
11943
11464
  };
11944
- var getTextBindableContainerAtPosition = (elements, appState, x, y, elementsMap) => {
11945
- const selectedElements = getSelectedElements(elements, appState);
11946
- if (selectedElements.length === 1) {
11947
- return isTextBindableContainer(selectedElements[0], false) ? selectedElements[0] : null;
11948
- }
11949
- let hitElement = null;
11950
- for (let index = elements.length - 1; index >= 0; --index) {
11951
- if (elements[index].isDeleted) {
11952
- continue;
11953
- }
11954
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(
11955
- elements[index],
11956
- elementsMap
11957
- );
11958
- if (isArrowElement(elements[index]) && isHittingElementNotConsideringBoundingBox(
11959
- elements[index],
11960
- appState,
11961
- null,
11962
- [x, y],
11963
- elementsMap
11964
- )) {
11965
- hitElement = elements[index];
11966
- break;
11967
- } else if (x1 < x && x < x2 && y1 < y && y < y2) {
11968
- hitElement = elements[index];
11969
- break;
11970
- }
11971
- }
11972
- return isTextBindableContainer(hitElement, false) ? hitElement : null;
11973
- };
11974
11465
  var VALID_CONTAINER_TYPES = /* @__PURE__ */ new Set([
11975
11466
  "rectangle",
11976
11467
  "ellipse",
@@ -12076,7 +11567,7 @@ var IMAGE_INVERT_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
12076
11567
  var defaultAppState = getDefaultAppState();
12077
11568
  var isPendingImageElement = (element, renderConfig) => isInitializedImageElement(element) && !renderConfig.imageCache.has(element.fileId);
12078
11569
  var shouldResetImageFilter = (element, renderConfig, appState) => {
12079
- return appState.theme === "dark" && isInitializedImageElement(element) && !isPendingImageElement(element, renderConfig) && renderConfig.imageCache.get(element.fileId)?.mimeType !== MIME_TYPES.svg;
11570
+ return appState.theme === THEME.DARK && isInitializedImageElement(element) && !isPendingImageElement(element, renderConfig) && renderConfig.imageCache.get(element.fileId)?.mimeType !== MIME_TYPES.svg;
12080
11571
  };
12081
11572
  var getCanvasPadding = (element) => element.type === "freedraw" ? element.strokeWidth * 12 : 20;
12082
11573
  var getRenderOpacity = (element, containingFrame, elementsPendingErasure) => {
@@ -12414,7 +11905,7 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
12414
11905
  context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
12415
11906
  context.strokeStyle = FRAME_STYLE.strokeColor;
12416
11907
  if (isMagicFrameElement(element)) {
12417
- context.strokeStyle = appState.theme === "light" ? "#7affd7" : "#1d8264";
11908
+ context.strokeStyle = appState.theme === THEME.LIGHT ? "#7affd7" : "#1d8264";
12418
11909
  }
12419
11910
  if (FRAME_STYLE.radius && context.roundRect) {
12420
11911
  context.beginPath();
@@ -12637,6 +12128,54 @@ function getSvgPathFromStroke2(points) {
12637
12128
  ).join(" ").replace(TO_FIXED_PRECISION, "$1");
12638
12129
  }
12639
12130
 
12131
+ // node_modules/points-on-curve/lib/index.js
12132
+ init_define_import_meta_env();
12133
+ function distanceSq2(p1, p2) {
12134
+ return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2);
12135
+ }
12136
+ function distanceToSegmentSq2(p, v, w) {
12137
+ const l2 = distanceSq2(v, w);
12138
+ if (l2 === 0) {
12139
+ return distanceSq2(p, v);
12140
+ }
12141
+ let t2 = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2;
12142
+ t2 = Math.max(0, Math.min(1, t2));
12143
+ return distanceSq2(p, lerp2(v, w, t2));
12144
+ }
12145
+ function lerp2(a2, b2, t2) {
12146
+ return [
12147
+ a2[0] + (b2[0] - a2[0]) * t2,
12148
+ a2[1] + (b2[1] - a2[1]) * t2
12149
+ ];
12150
+ }
12151
+ function simplify2(points, distance4) {
12152
+ return simplifyPoints2(points, 0, points.length, distance4);
12153
+ }
12154
+ function simplifyPoints2(points, start, end, epsilon, newPoints) {
12155
+ const outPoints = newPoints || [];
12156
+ const s2 = points[start];
12157
+ const e2 = points[end - 1];
12158
+ let maxDistSq = 0;
12159
+ let maxNdx = 1;
12160
+ for (let i2 = start + 1; i2 < end - 1; ++i2) {
12161
+ const distSq = distanceToSegmentSq2(points[i2], s2, e2);
12162
+ if (distSq > maxDistSq) {
12163
+ maxDistSq = distSq;
12164
+ maxNdx = i2;
12165
+ }
12166
+ }
12167
+ if (Math.sqrt(maxDistSq) > epsilon) {
12168
+ simplifyPoints2(points, start, maxNdx + 1, epsilon, outPoints);
12169
+ simplifyPoints2(points, maxNdx, end, epsilon, outPoints);
12170
+ } else {
12171
+ if (!outPoints.length) {
12172
+ outPoints.push(s2);
12173
+ }
12174
+ outPoints.push(e2);
12175
+ }
12176
+ return outPoints;
12177
+ }
12178
+
12640
12179
  // scene/Shape.ts
12641
12180
  var getDashArrayDashed = (strokeWidth) => [8, 8 + strokeWidth];
12642
12181
  var getDashArrayDotted = (strokeWidth) => [1.5, 6 + strokeWidth];
@@ -13034,7 +12573,7 @@ var ElementBounds = class _ElementBounds {
13034
12573
  if (isFreeDrawElement(element)) {
13035
12574
  const [minX, minY, maxX, maxY] = getBoundsFromPoints(
13036
12575
  element.points.map(
13037
- ([x, y]) => rotate2(x, y, cx - element.x, cy - element.y, element.angle)
12576
+ ([x, y]) => rotate3(x, y, cx - element.x, cy - element.y, element.angle)
13038
12577
  )
13039
12578
  );
13040
12579
  return [
@@ -13046,10 +12585,10 @@ var ElementBounds = class _ElementBounds {
13046
12585
  } else if (isLinearElement(element)) {
13047
12586
  bounds = getLinearElementRotatedBounds(element, cx, cy, elementsMap);
13048
12587
  } else if (element.type === "diamond") {
13049
- const [x11, y11] = rotate2(cx, y1, cx, cy, element.angle);
13050
- const [x12, y12] = rotate2(cx, y2, cx, cy, element.angle);
13051
- const [x22, y22] = rotate2(x1, cy, cx, cy, element.angle);
13052
- const [x21, y21] = rotate2(x2, cy, cx, cy, element.angle);
12588
+ const [x11, y11] = rotate3(cx, y1, cx, cy, element.angle);
12589
+ const [x12, y12] = rotate3(cx, y2, cx, cy, element.angle);
12590
+ const [x22, y22] = rotate3(x1, cy, cx, cy, element.angle);
12591
+ const [x21, y21] = rotate3(x2, cy, cx, cy, element.angle);
13053
12592
  const minX = Math.min(x11, x12, x22, x21);
13054
12593
  const minY = Math.min(y11, y12, y22, y21);
13055
12594
  const maxX = Math.max(x11, x12, x22, x21);
@@ -13064,10 +12603,10 @@ var ElementBounds = class _ElementBounds {
13064
12603
  const hh = Math.hypot(h * cos, w * sin);
13065
12604
  bounds = [cx - ww, cy - hh, cx + ww, cy + hh];
13066
12605
  } else {
13067
- const [x11, y11] = rotate2(x1, y1, cx, cy, element.angle);
13068
- const [x12, y12] = rotate2(x1, y2, cx, cy, element.angle);
13069
- const [x22, y22] = rotate2(x2, y2, cx, cy, element.angle);
13070
- const [x21, y21] = rotate2(x2, y1, cx, cy, element.angle);
12606
+ const [x11, y11] = rotate3(x1, y1, cx, cy, element.angle);
12607
+ const [x12, y12] = rotate3(x1, y2, cx, cy, element.angle);
12608
+ const [x22, y22] = rotate3(x2, y2, cx, cy, element.angle);
12609
+ const [x21, y21] = rotate3(x2, y1, cx, cy, element.angle);
13071
12610
  const minX = Math.min(x11, x12, x22, x21);
13072
12611
  const minY = Math.min(y11, y12, y22, y21);
13073
12612
  const maxX = Math.max(x11, x12, x22, x21);
@@ -13186,16 +12725,6 @@ var getElementLineSegments = (element, elementsMap) => {
13186
12725
  [se2, w]
13187
12726
  ];
13188
12727
  };
13189
- var getRectangleBoxAbsoluteCoords = (boxSceneCoords) => {
13190
- return [
13191
- boxSceneCoords.x,
13192
- boxSceneCoords.y,
13193
- boxSceneCoords.x + boxSceneCoords.width,
13194
- boxSceneCoords.y + boxSceneCoords.height,
13195
- boxSceneCoords.x + boxSceneCoords.width / 2,
13196
- boxSceneCoords.y + boxSceneCoords.height / 2
13197
- ];
13198
- };
13199
12728
  var getDiamondPoints = (element) => {
13200
12729
  const topX = Math.floor(element.width / 2) + 1;
13201
12730
  const topY = 0;
@@ -13364,9 +12893,9 @@ var getArrowheadPoints = (element, shape, position, arrowhead) => {
13364
12893
  const equation2 = (t2, idx) => Math.pow(1 - t2, 3) * p3[idx] + 3 * t2 * Math.pow(1 - t2, 2) * p2[idx] + 3 * Math.pow(t2, 2) * (1 - t2) * p1[idx] + p0[idx] * Math.pow(t2, 3);
13365
12894
  const [x2, y2] = position === "start" ? p0 : p3;
13366
12895
  const [x1, y1] = [equation2(0.3, 0), equation2(0.3, 1)];
13367
- const distance5 = Math.hypot(x2 - x1, y2 - y1);
13368
- const nx = (x2 - x1) / distance5;
13369
- const ny = (y2 - y1) / distance5;
12896
+ const distance4 = Math.hypot(x2 - x1, y2 - y1);
12897
+ const nx = (x2 - x1) / distance4;
12898
+ const ny = (y2 - y1) / distance4;
13370
12899
  const size = getArrowheadSize(arrowhead);
13371
12900
  let length = 0;
13372
12901
  {
@@ -13383,14 +12912,14 @@ var getArrowheadPoints = (element, shape, position, arrowhead) => {
13383
12912
  return [x2, y2, diameter];
13384
12913
  }
13385
12914
  const angle = getArrowheadAngle(arrowhead);
13386
- const [x3, y3] = rotate2(xs, ys, x2, y2, -angle * Math.PI / 180);
13387
- const [x4, y4] = rotate2(xs, ys, x2, y2, angle * Math.PI / 180);
12915
+ const [x3, y3] = rotate3(xs, ys, x2, y2, -angle * Math.PI / 180);
12916
+ const [x4, y4] = rotate3(xs, ys, x2, y2, angle * Math.PI / 180);
13388
12917
  if (arrowhead === "diamond" || arrowhead === "diamond_outline") {
13389
12918
  let ox;
13390
12919
  let oy;
13391
12920
  if (position === "start") {
13392
12921
  const [px, py] = element.points.length > 1 ? element.points[1] : [0, 0];
13393
- [ox, oy] = rotate2(
12922
+ [ox, oy] = rotate3(
13394
12923
  x2 + minSize * 2,
13395
12924
  y2,
13396
12925
  x2,
@@ -13399,7 +12928,7 @@ var getArrowheadPoints = (element, shape, position, arrowhead) => {
13399
12928
  );
13400
12929
  } else {
13401
12930
  const [px, py] = element.points.length > 1 ? element.points[element.points.length - 2] : [0, 0];
13402
- [ox, oy] = rotate2(
12931
+ [ox, oy] = rotate3(
13403
12932
  x2 - minSize * 2,
13404
12933
  y2,
13405
12934
  x2,
@@ -13429,7 +12958,7 @@ var getLinearElementRotatedBounds = (element, cx, cy, elementsMap) => {
13429
12958
  const boundTextElement = getBoundTextElement(element, elementsMap);
13430
12959
  if (element.points.length < 2) {
13431
12960
  const [pointX, pointY] = element.points[0];
13432
- const [x, y] = rotate2(
12961
+ const [x, y] = rotate3(
13433
12962
  element.x + pointX,
13434
12963
  element.y + pointY,
13435
12964
  cx,
@@ -13456,7 +12985,7 @@ var getLinearElementRotatedBounds = (element, cx, cy, elementsMap) => {
13456
12985
  const cachedShape = ShapeCache.get(element)?.[0];
13457
12986
  const shape = cachedShape ?? generateLinearElementShape(element);
13458
12987
  const ops = getCurvePathOps(shape);
13459
- const transformXY = (x, y) => rotate2(element.x + x, element.y + y, cx, cy, element.angle);
12988
+ const transformXY = (x, y) => rotate3(element.x + x, element.y + y, cx, cy, element.angle);
13460
12989
  const res = getMinMaxXYFromCurvePathOps(ops, transformXY);
13461
12990
  let coords = [res[0], res[1], res[2], res[3]];
13462
12991
  if (boundTextElement) {
@@ -13564,9 +13093,9 @@ var getClosestElementBounds = (elements, from3) => {
13564
13093
  const elementsMap = arrayToMap(elements);
13565
13094
  elements.forEach((element) => {
13566
13095
  const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
13567
- const distance5 = distance2d((x1 + x2) / 2, (y1 + y2) / 2, from3.x, from3.y);
13568
- if (distance5 < minDistance) {
13569
- minDistance = distance5;
13096
+ const distance4 = distance2d((x1 + x2) / 2, (y1 + y2) / 2, from3.x, from3.y);
13097
+ if (distance4 < minDistance) {
13098
+ minDistance = distance4;
13570
13099
  closestElement = element;
13571
13100
  }
13572
13101
  });
@@ -13601,7 +13130,7 @@ var getVisibleSceneBounds = ({
13601
13130
  };
13602
13131
 
13603
13132
  // math.ts
13604
- var rotate2 = (x, y, cx, cy, angle) => (
13133
+ var rotate3 = (x, y, cx, cy, angle) => (
13605
13134
  // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
13606
13135
  // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
13607
13136
  // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
@@ -13610,7 +13139,7 @@ var rotate2 = (x, y, cx, cy, angle) => (
13610
13139
  (x - cx) * Math.sin(angle) + (y - cy) * Math.cos(angle) + cy
13611
13140
  ]
13612
13141
  );
13613
- var rotatePoint = (point2, center, angle) => rotate2(point2[0], point2[1], center[0], center[1], angle);
13142
+ var rotatePoint = (point2, center, angle) => rotate3(point2[0], point2[1], center[0], center[1], angle);
13614
13143
  var adjustXYWithRotation = (sides, x, y, angle, deltaX1, deltaY1, deltaX2, deltaY2) => {
13615
13144
  const cos = Math.cos(angle);
13616
13145
  const sin = Math.sin(angle);
@@ -13653,63 +13182,14 @@ var centerPoint = (a2, b2) => {
13653
13182
  var isPathALoop = (points, zoomValue = 1) => {
13654
13183
  if (points.length >= 3) {
13655
13184
  const [first, last] = [points[0], points[points.length - 1]];
13656
- const distance5 = distance2d(first[0], first[1], last[0], last[1]);
13657
- return distance5 <= LINE_CONFIRM_THRESHOLD / zoomValue;
13185
+ const distance4 = distance2d(first[0], first[1], last[0], last[1]);
13186
+ return distance4 <= LINE_CONFIRM_THRESHOLD / zoomValue;
13658
13187
  }
13659
13188
  return false;
13660
13189
  };
13661
- var isPointInPolygon = (points, x, y) => {
13662
- const vertices = points.length;
13663
- if (vertices < 3) {
13664
- return false;
13665
- }
13666
- const extreme = [Number.MAX_SAFE_INTEGER, y];
13667
- const p = [x, y];
13668
- let count = 0;
13669
- for (let i2 = 0; i2 < vertices; i2++) {
13670
- const current = points[i2];
13671
- const next = points[(i2 + 1) % vertices];
13672
- if (doSegmentsIntersect(current, next, p, extreme)) {
13673
- if (orderedColinearOrientation(current, p, next) === 0) {
13674
- return isPointWithinBounds(current, p, next);
13675
- }
13676
- count++;
13677
- }
13678
- }
13679
- return count % 2 === 1;
13680
- };
13681
13190
  var isPointWithinBounds = (p, q, r) => {
13682
13191
  return q[0] <= Math.max(p[0], r[0]) && q[0] >= Math.min(p[0], r[0]) && q[1] <= Math.max(p[1], r[1]) && q[1] >= Math.min(p[1], r[1]);
13683
13192
  };
13684
- var orderedColinearOrientation = (p, q, r) => {
13685
- const val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
13686
- if (val === 0) {
13687
- return 0;
13688
- }
13689
- return val > 0 ? 1 : 2;
13690
- };
13691
- var doSegmentsIntersect = (p1, q1, p2, q2) => {
13692
- const o1 = orderedColinearOrientation(p1, q1, p2);
13693
- const o2 = orderedColinearOrientation(p1, q1, q2);
13694
- const o3 = orderedColinearOrientation(p2, q2, p1);
13695
- const o4 = orderedColinearOrientation(p2, q2, q1);
13696
- if (o1 !== o2 && o3 !== o4) {
13697
- return true;
13698
- }
13699
- if (o1 === 0 && isPointWithinBounds(p1, p2, q1)) {
13700
- return true;
13701
- }
13702
- if (o2 === 0 && isPointWithinBounds(p1, q2, q1)) {
13703
- return true;
13704
- }
13705
- if (o3 === 0 && isPointWithinBounds(p2, p1, q2)) {
13706
- return true;
13707
- }
13708
- if (o4 === 0 && isPointWithinBounds(p2, q1, q2)) {
13709
- return true;
13710
- }
13711
- return false;
13712
- };
13713
13193
  var getGridPoint = (x, y, gridSize) => {
13714
13194
  if (gridSize) {
13715
13195
  return [
@@ -13753,9 +13233,9 @@ var getControlPointsForBezierCurve = (element, endPoint) => {
13753
13233
  const p1 = [data[0], data[1]];
13754
13234
  const p2 = [data[2], data[3]];
13755
13235
  const p3 = [data[4], data[5]];
13756
- const distance5 = distance2d(p3[0], p3[1], endPoint[0], endPoint[1]);
13757
- if (distance5 < minDistance) {
13758
- minDistance = distance5;
13236
+ const distance4 = distance2d(p3[0], p3[1], endPoint[0], endPoint[1]);
13237
+ if (distance4 < minDistance) {
13238
+ minDistance = distance4;
13759
13239
  controlPoints = [p0, p1, p2, p3];
13760
13240
  }
13761
13241
  currentP = p3;
@@ -13803,7 +13283,7 @@ var getBezierCurveArcLengths = (element, endPoint) => {
13803
13283
  arcLengths[0] = 0;
13804
13284
  const points = getPointsInBezierCurve(element, endPoint);
13805
13285
  let index = 0;
13806
- let distance5 = 0;
13286
+ let distance4 = 0;
13807
13287
  while (index < points.length - 1) {
13808
13288
  const segmentDistance = distance2d(
13809
13289
  points[index][0],
@@ -13811,8 +13291,8 @@ var getBezierCurveArcLengths = (element, endPoint) => {
13811
13291
  points[index + 1][0],
13812
13292
  points[index + 1][1]
13813
13293
  );
13814
- distance5 += segmentDistance;
13815
- arcLengths.push(distance5);
13294
+ distance4 += segmentDistance;
13295
+ arcLengths.push(distance4);
13816
13296
  index++;
13817
13297
  }
13818
13298
  return arcLengths;
@@ -14216,6 +13696,7 @@ var _newElementBase = (type, {
14216
13696
  angle = 0,
14217
13697
  groupIds = [],
14218
13698
  frameId = null,
13699
+ index = null,
14219
13700
  roundness = null,
14220
13701
  boundElements = null,
14221
13702
  link = null,
@@ -14239,6 +13720,7 @@ var _newElementBase = (type, {
14239
13720
  opacity,
14240
13721
  groupIds,
14241
13722
  frameId,
13723
+ index,
14242
13724
  roundness,
14243
13725
  seed: rest.seed ?? randomInteger(),
14244
13726
  version: rest.version || 1,
@@ -14578,13 +14060,13 @@ var duplicateElements = (elements, opts) => {
14578
14060
  // element/embeddable.ts
14579
14061
  var embeddedLinkCache = /* @__PURE__ */ new Map();
14580
14062
  var RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/;
14581
- var RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
14063
+ var RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
14582
14064
  var RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
14583
14065
  var RE_GH_GIST = /^https:\/\/gist\.github\.com/;
14584
- var RE_GH_GIST_EMBED = /^<script[\s\S]*?\ssrc=["'](https:\/\/gist.github.com\/.*?)\.js["']/i;
14585
- var RE_TWITTER = /(?:http(?:s)?:\/\/)?(?:(?:w){3}.)?(?:twitter|x).com/;
14586
- var RE_TWITTER_EMBED = /^<blockquote[\s\S]*?\shref=["'](https:\/\/(?:twitter|x).com\/[^"']*)/i;
14587
- var RE_VALTOWN = /^https:\/\/(?:www\.)?val.town\/(v|embed)\/[a-zA-Z_$][0-9a-zA-Z_$]+\.[a-zA-Z_$][0-9a-zA-Z_$]+/;
14066
+ var RE_GH_GIST_EMBED = /https?:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)\.js["']/i;
14067
+ var RE_TWITTER = /(?:https?:\/\/)?(?:(?:w){3}\.)?(?:twitter|x)\.com/;
14068
+ var RE_TWITTER_EMBED = /^<blockquote[\s\S]*?\shref=["'](https?:\/\/(?:twitter|x)\.com\/[^"']*)/i;
14069
+ var RE_VALTOWN = /^https:\/\/(?:www\.)?val\.town\/(v|embed)\/[a-zA-Z_$][0-9a-zA-Z_$]+\.[a-zA-Z_$][0-9a-zA-Z_$]+/;
14588
14070
  var RE_GENERIC_EMBED = /^<(?:iframe|blockquote)[\s\S]*?\s(?:src|href)=["']([^"']*)["'][\s\S]*?>$/i;
14589
14071
  var RE_GIPHY = /giphy.com\/(?:clips|embed|gifs)\/[a-zA-Z0-9]*?-?([a-zA-Z0-9]+)(?:[^a-zA-Z0-9]|$)/;
14590
14072
  var ALLOWED_DOMAINS = /* @__PURE__ */ new Set([
@@ -14683,39 +14165,21 @@ var getEmbedLink = (link) => {
14683
14165
  }
14684
14166
  if (RE_TWITTER.test(link)) {
14685
14167
  link = link.replace(/\bx.com\b/, "twitter.com");
14686
- let ret;
14687
- if (/<blockquote/.test(link)) {
14688
- const srcDoc = createSrcDoc(link);
14689
- ret = {
14690
- type: "document",
14691
- srcdoc: () => srcDoc,
14692
- intrinsicSize: { w: 480, h: 480 }
14693
- };
14694
- } else {
14695
- ret = {
14696
- type: "document",
14697
- srcdoc: (theme) => createSrcDoc(
14698
- `<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"><\/script>`
14699
- ),
14700
- intrinsicSize: { w: 480, h: 480 }
14701
- };
14702
- }
14168
+ const ret = {
14169
+ type: "document",
14170
+ srcdoc: (theme) => createSrcDoc(
14171
+ `<blockquote class="twitter-tweet" data-dnt="true" data-theme="${theme}"><a href="${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"><\/script>`
14172
+ ),
14173
+ intrinsicSize: { w: 480, h: 480 },
14174
+ sandbox: { allowSameOrigin: true }
14175
+ };
14703
14176
  embeddedLinkCache.set(originalLink, ret);
14704
14177
  return ret;
14705
14178
  }
14706
14179
  if (RE_GH_GIST.test(link)) {
14707
- let ret;
14708
- if (/<script>/.test(link)) {
14709
- const srcDoc = createSrcDoc(link);
14710
- ret = {
14711
- type: "document",
14712
- srcdoc: () => srcDoc,
14713
- intrinsicSize: { w: 550, h: 720 }
14714
- };
14715
- } else {
14716
- ret = {
14717
- type: "document",
14718
- srcdoc: () => createSrcDoc(`
14180
+ const ret = {
14181
+ type: "document",
14182
+ srcdoc: () => createSrcDoc(`
14719
14183
  <script src="${link}.js"><\/script>
14720
14184
  <style type="text/css">
14721
14185
  * { margin: 0px; }
@@ -14723,9 +14187,8 @@ var getEmbedLink = (link) => {
14723
14187
  .gist .gist-file { height: calc(100vh - 2px); padding: 0px; display: grid; grid-template-rows: 1fr auto; }
14724
14188
  </style>
14725
14189
  `),
14726
- intrinsicSize: { w: 550, h: 720 }
14727
- };
14728
- }
14190
+ intrinsicSize: { w: 550, h: 720 }
14191
+ };
14729
14192
  embeddedLinkCache.set(link, ret);
14730
14193
  return ret;
14731
14194
  }
@@ -14810,8 +14273,8 @@ var maybeParseEmbedSrc = (str) => {
14810
14273
  return twitterMatch[1];
14811
14274
  }
14812
14275
  const gistMatch = str.match(RE_GH_GIST_EMBED);
14813
- if (gistMatch && gistMatch.length === 2) {
14814
- return gistMatch[1];
14276
+ if (gistMatch && gistMatch.length === 3) {
14277
+ return `https://gist.github.com/${gistMatch[1]}/${gistMatch[2]}`;
14815
14278
  }
14816
14279
  if (RE_GIPHY.test(str)) {
14817
14280
  return `https://giphy.com/embed/${RE_GIPHY.exec(str)[1]}`;
@@ -15382,6 +14845,9 @@ var ImageSceneDataError = class extends Error {
15382
14845
  this.code = code;
15383
14846
  }
15384
14847
  };
14848
+ var InvalidFractionalIndexError = class extends Error {
14849
+ code = "ELEMENT_HAS_INVALID_INDEX";
14850
+ };
15385
14851
 
15386
14852
  // data/filesystem.ts
15387
14853
  var INPUT_CHANGE_INTERVAL_MS = 500;
@@ -15616,12 +15082,576 @@ var normalizeSVG = async (SVGString) => {
15616
15082
  return svg.outerHTML;
15617
15083
  }
15618
15084
  };
15619
-
15620
- // renderer/staticScene.ts
15621
- init_define_import_meta_env();
15085
+
15086
+ // fractionalIndex.ts
15087
+ init_define_import_meta_env();
15088
+
15089
+ // ../../node_modules/fractional-indexing/src/index.js
15090
+ init_define_import_meta_env();
15091
+ var BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
15092
+ function midpoint(a2, b2, digits) {
15093
+ const zero = digits[0];
15094
+ if (b2 != null && a2 >= b2) {
15095
+ throw new Error(a2 + " >= " + b2);
15096
+ }
15097
+ if (a2.slice(-1) === zero || b2 && b2.slice(-1) === zero) {
15098
+ throw new Error("trailing zero");
15099
+ }
15100
+ if (b2) {
15101
+ let n2 = 0;
15102
+ while ((a2[n2] || zero) === b2[n2]) {
15103
+ n2++;
15104
+ }
15105
+ if (n2 > 0) {
15106
+ return b2.slice(0, n2) + midpoint(a2.slice(n2), b2.slice(n2), digits);
15107
+ }
15108
+ }
15109
+ const digitA = a2 ? digits.indexOf(a2[0]) : 0;
15110
+ const digitB = b2 != null ? digits.indexOf(b2[0]) : digits.length;
15111
+ if (digitB - digitA > 1) {
15112
+ const midDigit = Math.round(0.5 * (digitA + digitB));
15113
+ return digits[midDigit];
15114
+ } else {
15115
+ if (b2 && b2.length > 1) {
15116
+ return b2.slice(0, 1);
15117
+ } else {
15118
+ return digits[digitA] + midpoint(a2.slice(1), null, digits);
15119
+ }
15120
+ }
15121
+ }
15122
+ function validateInteger(int) {
15123
+ if (int.length !== getIntegerLength(int[0])) {
15124
+ throw new Error("invalid integer part of order key: " + int);
15125
+ }
15126
+ }
15127
+ function getIntegerLength(head) {
15128
+ if (head >= "a" && head <= "z") {
15129
+ return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
15130
+ } else if (head >= "A" && head <= "Z") {
15131
+ return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
15132
+ } else {
15133
+ throw new Error("invalid order key head: " + head);
15134
+ }
15135
+ }
15136
+ function getIntegerPart(key) {
15137
+ const integerPartLength = getIntegerLength(key[0]);
15138
+ if (integerPartLength > key.length) {
15139
+ throw new Error("invalid order key: " + key);
15140
+ }
15141
+ return key.slice(0, integerPartLength);
15142
+ }
15143
+ function validateOrderKey(key, digits) {
15144
+ if (key === "A" + digits[0].repeat(26)) {
15145
+ throw new Error("invalid order key: " + key);
15146
+ }
15147
+ const i2 = getIntegerPart(key);
15148
+ const f = key.slice(i2.length);
15149
+ if (f.slice(-1) === digits[0]) {
15150
+ throw new Error("invalid order key: " + key);
15151
+ }
15152
+ }
15153
+ function incrementInteger(x, digits) {
15154
+ validateInteger(x);
15155
+ const [head, ...digs] = x.split("");
15156
+ let carry = true;
15157
+ for (let i2 = digs.length - 1; carry && i2 >= 0; i2--) {
15158
+ const d = digits.indexOf(digs[i2]) + 1;
15159
+ if (d === digits.length) {
15160
+ digs[i2] = digits[0];
15161
+ } else {
15162
+ digs[i2] = digits[d];
15163
+ carry = false;
15164
+ }
15165
+ }
15166
+ if (carry) {
15167
+ if (head === "Z") {
15168
+ return "a" + digits[0];
15169
+ }
15170
+ if (head === "z") {
15171
+ return null;
15172
+ }
15173
+ const h = String.fromCharCode(head.charCodeAt(0) + 1);
15174
+ if (h > "a") {
15175
+ digs.push(digits[0]);
15176
+ } else {
15177
+ digs.pop();
15178
+ }
15179
+ return h + digs.join("");
15180
+ } else {
15181
+ return head + digs.join("");
15182
+ }
15183
+ }
15184
+ function decrementInteger(x, digits) {
15185
+ validateInteger(x);
15186
+ const [head, ...digs] = x.split("");
15187
+ let borrow = true;
15188
+ for (let i2 = digs.length - 1; borrow && i2 >= 0; i2--) {
15189
+ const d = digits.indexOf(digs[i2]) - 1;
15190
+ if (d === -1) {
15191
+ digs[i2] = digits.slice(-1);
15192
+ } else {
15193
+ digs[i2] = digits[d];
15194
+ borrow = false;
15195
+ }
15196
+ }
15197
+ if (borrow) {
15198
+ if (head === "a") {
15199
+ return "Z" + digits.slice(-1);
15200
+ }
15201
+ if (head === "A") {
15202
+ return null;
15203
+ }
15204
+ const h = String.fromCharCode(head.charCodeAt(0) - 1);
15205
+ if (h < "Z") {
15206
+ digs.push(digits.slice(-1));
15207
+ } else {
15208
+ digs.pop();
15209
+ }
15210
+ return h + digs.join("");
15211
+ } else {
15212
+ return head + digs.join("");
15213
+ }
15214
+ }
15215
+ function generateKeyBetween(a2, b2, digits = BASE_62_DIGITS) {
15216
+ if (a2 != null) {
15217
+ validateOrderKey(a2, digits);
15218
+ }
15219
+ if (b2 != null) {
15220
+ validateOrderKey(b2, digits);
15221
+ }
15222
+ if (a2 != null && b2 != null && a2 >= b2) {
15223
+ throw new Error(a2 + " >= " + b2);
15224
+ }
15225
+ if (a2 == null) {
15226
+ if (b2 == null) {
15227
+ return "a" + digits[0];
15228
+ }
15229
+ const ib2 = getIntegerPart(b2);
15230
+ const fb2 = b2.slice(ib2.length);
15231
+ if (ib2 === "A" + digits[0].repeat(26)) {
15232
+ return ib2 + midpoint("", fb2, digits);
15233
+ }
15234
+ if (ib2 < b2) {
15235
+ return ib2;
15236
+ }
15237
+ const res = decrementInteger(ib2, digits);
15238
+ if (res == null) {
15239
+ throw new Error("cannot decrement any more");
15240
+ }
15241
+ return res;
15242
+ }
15243
+ if (b2 == null) {
15244
+ const ia2 = getIntegerPart(a2);
15245
+ const fa2 = a2.slice(ia2.length);
15246
+ const i3 = incrementInteger(ia2, digits);
15247
+ return i3 == null ? ia2 + midpoint(fa2, null, digits) : i3;
15248
+ }
15249
+ const ia = getIntegerPart(a2);
15250
+ const fa = a2.slice(ia.length);
15251
+ const ib = getIntegerPart(b2);
15252
+ const fb = b2.slice(ib.length);
15253
+ if (ia === ib) {
15254
+ return ia + midpoint(fa, fb, digits);
15255
+ }
15256
+ const i2 = incrementInteger(ia, digits);
15257
+ if (i2 == null) {
15258
+ throw new Error("cannot increment any more");
15259
+ }
15260
+ if (i2 < b2) {
15261
+ return i2;
15262
+ }
15263
+ return ia + midpoint(fa, null, digits);
15264
+ }
15265
+ function generateNKeysBetween(a2, b2, n2, digits = BASE_62_DIGITS) {
15266
+ if (n2 === 0) {
15267
+ return [];
15268
+ }
15269
+ if (n2 === 1) {
15270
+ return [generateKeyBetween(a2, b2, digits)];
15271
+ }
15272
+ if (b2 == null) {
15273
+ let c2 = generateKeyBetween(a2, b2, digits);
15274
+ const result = [c2];
15275
+ for (let i2 = 0; i2 < n2 - 1; i2++) {
15276
+ c2 = generateKeyBetween(c2, b2, digits);
15277
+ result.push(c2);
15278
+ }
15279
+ return result;
15280
+ }
15281
+ if (a2 == null) {
15282
+ let c2 = generateKeyBetween(a2, b2, digits);
15283
+ const result = [c2];
15284
+ for (let i2 = 0; i2 < n2 - 1; i2++) {
15285
+ c2 = generateKeyBetween(a2, c2, digits);
15286
+ result.push(c2);
15287
+ }
15288
+ result.reverse();
15289
+ return result;
15290
+ }
15291
+ const mid = Math.floor(n2 / 2);
15292
+ const c = generateKeyBetween(a2, b2, digits);
15293
+ return [
15294
+ ...generateNKeysBetween(a2, c, mid, digits),
15295
+ c,
15296
+ ...generateNKeysBetween(c, b2, n2 - mid - 1, digits)
15297
+ ];
15298
+ }
15299
+
15300
+ // fractionalIndex.ts
15301
+ var validateFractionalIndices = (indices) => {
15302
+ for (const [i2, index] of indices.entries()) {
15303
+ const predecessorIndex = indices[i2 - 1];
15304
+ const successorIndex = indices[i2 + 1];
15305
+ if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
15306
+ throw new InvalidFractionalIndexError(
15307
+ `Fractional indices invariant for element has been compromised - ["${predecessorIndex}", "${index}", "${successorIndex}"] [predecessor, current, successor]`
15308
+ );
15309
+ }
15310
+ }
15311
+ };
15312
+ var syncMovedIndices = (elements, movedElements) => {
15313
+ try {
15314
+ const indicesGroups = getMovedIndicesGroups(elements, movedElements);
15315
+ const elementsUpdates = generateIndices(elements, indicesGroups);
15316
+ validateFractionalIndices(
15317
+ elements.map((x) => elementsUpdates.get(x)?.index || x.index)
15318
+ );
15319
+ for (const [element, update] of elementsUpdates) {
15320
+ mutateElement(element, update, false);
15321
+ }
15322
+ } catch (e2) {
15323
+ syncInvalidIndices(elements);
15324
+ }
15325
+ return elements;
15326
+ };
15327
+ var syncInvalidIndices = (elements) => {
15328
+ const indicesGroups = getInvalidIndicesGroups(elements);
15329
+ const elementsUpdates = generateIndices(elements, indicesGroups);
15330
+ for (const [element, update] of elementsUpdates) {
15331
+ mutateElement(element, update, false);
15332
+ }
15333
+ return elements;
15334
+ };
15335
+ var getMovedIndicesGroups = (elements, movedElements) => {
15336
+ const indicesGroups = [];
15337
+ let i2 = 0;
15338
+ while (i2 < elements.length) {
15339
+ if (movedElements.has(elements[i2].id) && !isValidFractionalIndex(
15340
+ elements[i2]?.index,
15341
+ elements[i2 - 1]?.index,
15342
+ elements[i2 + 1]?.index
15343
+ )) {
15344
+ const indicesGroup = [i2 - 1, i2];
15345
+ while (++i2 < elements.length) {
15346
+ if (!(movedElements.has(elements[i2].id) && !isValidFractionalIndex(
15347
+ elements[i2]?.index,
15348
+ elements[i2 - 1]?.index,
15349
+ elements[i2 + 1]?.index
15350
+ ))) {
15351
+ break;
15352
+ }
15353
+ indicesGroup.push(i2);
15354
+ }
15355
+ indicesGroup.push(i2);
15356
+ indicesGroups.push(indicesGroup);
15357
+ } else {
15358
+ i2++;
15359
+ }
15360
+ }
15361
+ return indicesGroups;
15362
+ };
15363
+ var getInvalidIndicesGroups = (elements) => {
15364
+ const indicesGroups = [];
15365
+ let lowerBound = void 0;
15366
+ let upperBound = void 0;
15367
+ let lowerBoundIndex = -1;
15368
+ let upperBoundIndex = 0;
15369
+ const getLowerBound = (index) => {
15370
+ const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
15371
+ const candidate = elements[index - 1]?.index;
15372
+ if (!lowerBound2 && candidate || // first lowerBound
15373
+ lowerBound2 && candidate && candidate > lowerBound2) {
15374
+ return [candidate, index - 1];
15375
+ }
15376
+ return [lowerBound2, lowerBoundIndex];
15377
+ };
15378
+ const getUpperBound = (index) => {
15379
+ const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
15380
+ if (upperBound2 && index < upperBoundIndex) {
15381
+ return [upperBound2, upperBoundIndex];
15382
+ }
15383
+ let i3 = upperBoundIndex;
15384
+ while (++i3 < elements.length) {
15385
+ const candidate = elements[i3]?.index;
15386
+ if (!upperBound2 && candidate || // first upperBound
15387
+ upperBound2 && candidate && candidate > upperBound2) {
15388
+ return [candidate, i3];
15389
+ }
15390
+ }
15391
+ return [void 0, i3];
15392
+ };
15393
+ let i2 = 0;
15394
+ while (i2 < elements.length) {
15395
+ const current = elements[i2].index;
15396
+ [lowerBound, lowerBoundIndex] = getLowerBound(i2);
15397
+ [upperBound, upperBoundIndex] = getUpperBound(i2);
15398
+ if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
15399
+ const indicesGroup = [lowerBoundIndex, i2];
15400
+ while (++i2 < elements.length) {
15401
+ const current2 = elements[i2].index;
15402
+ const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i2);
15403
+ const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i2);
15404
+ if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
15405
+ break;
15406
+ }
15407
+ [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
15408
+ [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
15409
+ indicesGroup.push(i2);
15410
+ }
15411
+ indicesGroup.push(upperBoundIndex);
15412
+ indicesGroups.push(indicesGroup);
15413
+ } else {
15414
+ i2++;
15415
+ }
15416
+ }
15417
+ return indicesGroups;
15418
+ };
15419
+ var isValidFractionalIndex = (index, predecessor, successor) => {
15420
+ if (!index) {
15421
+ return false;
15422
+ }
15423
+ if (predecessor && successor) {
15424
+ return predecessor < index && index < successor;
15425
+ }
15426
+ if (!predecessor && successor) {
15427
+ return index < successor;
15428
+ }
15429
+ if (predecessor && !successor) {
15430
+ return predecessor < index;
15431
+ }
15432
+ return !!index;
15433
+ };
15434
+ var generateIndices = (elements, indicesGroups) => {
15435
+ const elementsUpdates = /* @__PURE__ */ new Map();
15436
+ for (const indices of indicesGroups) {
15437
+ const lowerBoundIndex = indices.shift();
15438
+ const upperBoundIndex = indices.pop();
15439
+ const fractionalIndices = generateNKeysBetween(
15440
+ elements[lowerBoundIndex]?.index,
15441
+ elements[upperBoundIndex]?.index,
15442
+ indices.length
15443
+ );
15444
+ for (let i2 = 0; i2 < indices.length; i2++) {
15445
+ const element = elements[indices[i2]];
15446
+ elementsUpdates.set(element, {
15447
+ index: fractionalIndices[i2]
15448
+ });
15449
+ }
15450
+ }
15451
+ return elementsUpdates;
15452
+ };
15453
+
15454
+ // renderer/staticScene.ts
15455
+ init_define_import_meta_env();
15456
+
15457
+ // components/hyperlink/helpers.ts
15458
+ init_define_import_meta_env();
15459
+
15460
+ // element/collision.ts
15461
+ init_define_import_meta_env();
15462
+
15463
+ // ../utils/geometry/shape.ts
15464
+ init_define_import_meta_env();
15465
+ var getPolygonShape = (element) => {
15466
+ const { angle, width, height, x, y } = element;
15467
+ const angleInDegrees = angleToDegrees(angle);
15468
+ const cx = x + width / 2;
15469
+ const cy = y + height / 2;
15470
+ const center = [cx, cy];
15471
+ let data = [];
15472
+ if (element.type === "diamond") {
15473
+ data = [
15474
+ pointRotate([cx, y], angleInDegrees, center),
15475
+ pointRotate([x + width, cy], angleInDegrees, center),
15476
+ pointRotate([cx, y + height], angleInDegrees, center),
15477
+ pointRotate([x, cy], angleInDegrees, center)
15478
+ ];
15479
+ } else {
15480
+ data = [
15481
+ pointRotate([x, y], angleInDegrees, center),
15482
+ pointRotate([x + width, y], angleInDegrees, center),
15483
+ pointRotate([x + width, y + height], angleInDegrees, center),
15484
+ pointRotate([x, y + height], angleInDegrees, center)
15485
+ ];
15486
+ }
15487
+ return {
15488
+ type: "polygon",
15489
+ data
15490
+ };
15491
+ };
15492
+ var getEllipseShape = (element) => {
15493
+ const { width, height, angle, x, y } = element;
15494
+ return {
15495
+ type: "ellipse",
15496
+ data: {
15497
+ center: [x + width / 2, y + height / 2],
15498
+ angle,
15499
+ halfWidth: width / 2,
15500
+ halfHeight: height / 2
15501
+ }
15502
+ };
15503
+ };
15504
+ var getCurvePathOps2 = (shape) => {
15505
+ for (const set of shape.sets) {
15506
+ if (set.type === "path") {
15507
+ return set.ops;
15508
+ }
15509
+ }
15510
+ return shape.sets[0].ops;
15511
+ };
15512
+ var getCurveShape = (roughShape, startingPoint = [0, 0], angleInRadian, center) => {
15513
+ const transform = (p) => pointRotate(
15514
+ [p[0] + startingPoint[0], p[1] + startingPoint[1]],
15515
+ angleToDegrees(angleInRadian),
15516
+ center
15517
+ );
15518
+ const ops = getCurvePathOps2(roughShape);
15519
+ const polycurve = [];
15520
+ let p0 = [0, 0];
15521
+ for (const op of ops) {
15522
+ if (op.op === "move") {
15523
+ p0 = transform(op.data);
15524
+ }
15525
+ if (op.op === "bcurveTo") {
15526
+ const p1 = transform([op.data[0], op.data[1]]);
15527
+ const p2 = transform([op.data[2], op.data[3]]);
15528
+ const p3 = transform([op.data[4], op.data[5]]);
15529
+ polycurve.push([p0, p1, p2, p3]);
15530
+ p0 = p3;
15531
+ }
15532
+ }
15533
+ return {
15534
+ type: "polycurve",
15535
+ data: polycurve
15536
+ };
15537
+ };
15538
+ var polylineFromPoints = (points) => {
15539
+ let previousPoint = points[0];
15540
+ const polyline = [];
15541
+ for (let i2 = 1; i2 < points.length; i2++) {
15542
+ const nextPoint = points[i2];
15543
+ polyline.push([previousPoint, nextPoint]);
15544
+ previousPoint = nextPoint;
15545
+ }
15546
+ return polyline;
15547
+ };
15548
+ var getFreedrawShape = (element, center, isClosed2 = false) => {
15549
+ const angle = angleToDegrees(element.angle);
15550
+ const transform = (p) => pointRotate(pointAdd(p, [element.x, element.y]), angle, center);
15551
+ const polyline = polylineFromPoints(
15552
+ element.points.map((p) => transform(p))
15553
+ );
15554
+ return isClosed2 ? {
15555
+ type: "polygon",
15556
+ data: close(polyline.flat())
15557
+ } : {
15558
+ type: "polyline",
15559
+ data: polyline
15560
+ };
15561
+ };
15562
+ var getClosedCurveShape = (element, roughShape, startingPoint = [0, 0], angleInRadian, center) => {
15563
+ const transform = (p) => pointRotate(
15564
+ [p[0] + startingPoint[0], p[1] + startingPoint[1]],
15565
+ angleToDegrees(angleInRadian),
15566
+ center
15567
+ );
15568
+ if (element.roundness === null) {
15569
+ return {
15570
+ type: "polygon",
15571
+ data: close(element.points.map((p) => transform(p)))
15572
+ };
15573
+ }
15574
+ const ops = getCurvePathOps2(roughShape);
15575
+ const points = [];
15576
+ let odd = false;
15577
+ for (const operation of ops) {
15578
+ if (operation.op === "move") {
15579
+ odd = !odd;
15580
+ if (odd) {
15581
+ points.push([operation.data[0], operation.data[1]]);
15582
+ }
15583
+ } else if (operation.op === "bcurveTo") {
15584
+ if (odd) {
15585
+ points.push([operation.data[0], operation.data[1]]);
15586
+ points.push([operation.data[2], operation.data[3]]);
15587
+ points.push([operation.data[4], operation.data[5]]);
15588
+ }
15589
+ } else if (operation.op === "lineTo") {
15590
+ if (odd) {
15591
+ points.push([operation.data[0], operation.data[1]]);
15592
+ }
15593
+ }
15594
+ }
15595
+ const polygonPoints = pointsOnBezierCurves(points, 10, 5).map(
15596
+ (p) => transform(p)
15597
+ );
15598
+ return {
15599
+ type: "polygon",
15600
+ data: polygonPoints
15601
+ };
15602
+ };
15603
+
15604
+ // element/collision.ts
15605
+ var shouldTestInside = (element) => {
15606
+ if (element.type === "arrow") {
15607
+ return false;
15608
+ }
15609
+ const isDraggableFromInside = !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
15610
+ if (element.type === "line") {
15611
+ return isDraggableFromInside && isPathALoop(element.points);
15612
+ }
15613
+ if (element.type === "freedraw") {
15614
+ return isDraggableFromInside && isPathALoop(element.points);
15615
+ }
15616
+ return isDraggableFromInside || isImageElement(element);
15617
+ };
15618
+ var hitElementItself = ({
15619
+ x,
15620
+ y,
15621
+ element,
15622
+ shape,
15623
+ threshold = 10,
15624
+ frameNameBound = null
15625
+ }) => {
15626
+ let hit = shouldTestInside(element) ? (
15627
+ // Since `inShape` tests STRICTLY againt the insides of a shape
15628
+ // we would need `onShape` as well to include the "borders"
15629
+ isPointInShape([x, y], shape) || isPointOnShape([x, y], shape, threshold)
15630
+ ) : isPointOnShape([x, y], shape, threshold);
15631
+ if (!hit && frameNameBound) {
15632
+ hit = isPointInShape([x, y], {
15633
+ type: "polygon",
15634
+ data: getPolygonShape(frameNameBound).data
15635
+ });
15636
+ }
15637
+ return hit;
15638
+ };
15639
+ var hitElementBoundingBox = (x, y, element, elementsMap, tolerance = 0) => {
15640
+ let [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
15641
+ x1 -= tolerance;
15642
+ y1 -= tolerance;
15643
+ x2 += tolerance;
15644
+ y2 += tolerance;
15645
+ return isPointWithinBounds([x1, y1], [x, y], [x2, y2]);
15646
+ };
15647
+ var hitElementBoundingBoxOnly = (hitArgs, elementsMap) => {
15648
+ return !hitElementItself(hitArgs) && hitElementBoundingBox(hitArgs.x, hitArgs.y, hitArgs.element, elementsMap);
15649
+ };
15650
+ var hitElementBoundText = (x, y, textShape) => {
15651
+ return textShape && isPointInShape([x, y], textShape);
15652
+ };
15622
15653
 
15623
15654
  // components/hyperlink/helpers.ts
15624
- init_define_import_meta_env();
15625
15655
  var EXTERNAL_LINK_IMG = document.createElement("img");
15626
15656
  EXTERNAL_LINK_IMG.src = `data:${MIME_TYPES.svg}, ${encodeURIComponent(
15627
15657
  `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1971c2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`
@@ -15637,7 +15667,7 @@ var getLinkHandleFromCoords = ([x1, y1, x2, y2], angle, appState) => {
15637
15667
  const dashedLineMargin = 4 / appState.zoom.value;
15638
15668
  const x = x2 + dashedLineMargin - centeringOffset;
15639
15669
  const y = y1 - dashedLineMargin - linkMarginY + centeringOffset;
15640
- const [rotatedX, rotatedY] = rotate2(
15670
+ const [rotatedX, rotatedY] = rotate3(
15641
15671
  x + linkWidth / 2,
15642
15672
  y + linkHeight / 2,
15643
15673
  centerX,
@@ -15666,14 +15696,7 @@ var isPointHittingLink = (element, elementsMap, appState, [x, y], isMobile) => {
15666
15696
  if (!element.link || appState.selectedElementIds[element.id]) {
15667
15697
  return false;
15668
15698
  }
15669
- const threshold = 4 / appState.zoom.value;
15670
- if (!isMobile && appState.viewModeEnabled && isPointHittingElementBoundingBox(
15671
- element,
15672
- elementsMap,
15673
- [x, y],
15674
- threshold,
15675
- null
15676
- )) {
15699
+ if (!isMobile && appState.viewModeEnabled && hitElementBoundingBox(x, y, element, elementsMap)) {
15677
15700
  return true;
15678
15701
  }
15679
15702
  return isPointHittingLinkIcon(element, elementsMap, appState, [x, y]);
@@ -15704,7 +15727,7 @@ var bootstrapCanvas = ({
15704
15727
  const context = canvas2.getContext("2d");
15705
15728
  context.setTransform(1, 0, 0, 1, 0, 0);
15706
15729
  context.scale(scale, scale);
15707
- if (isExporting && theme === "dark") {
15730
+ if (isExporting && theme === THEME.DARK) {
15708
15731
  context.filter = THEME_FILTER;
15709
15732
  }
15710
15733
  if (typeof viewBackgroundColor === "string") {
@@ -16100,7 +16123,7 @@ var exportToCanvas = async (elements, appState, files, {
16100
16123
  arrayToMap(elementsForRender)
16101
16124
  ),
16102
16125
  allElementsMap: toBrandedType(
16103
- arrayToMap(elements)
16126
+ arrayToMap(syncInvalidIndices(elements))
16104
16127
  ),
16105
16128
  visibleElements: elementsForRender,
16106
16129
  scale,
@@ -16112,7 +16135,7 @@ var exportToCanvas = async (elements, appState, files, {
16112
16135
  scrollY: -minY + exportPadding,
16113
16136
  zoom: defaultAppState2.zoom,
16114
16137
  shouldCacheIgnoreZoom: false,
16115
- theme: appState.exportWithDarkMode ? "dark" : "light"
16138
+ theme: appState.exportWithDarkMode ? THEME.DARK : THEME.LIGHT
16116
16139
  },
16117
16140
  renderConfig: {
16118
16141
  canvasBackgroundColor: viewBackgroundColor,
@@ -16151,7 +16174,7 @@ var exportToSvg = async (elements, appState, files, opts) => {
16151
16174
  let metadata = "";
16152
16175
  if (exportEmbedScene) {
16153
16176
  try {
16154
- metadata = await (await import("./image-EDKQZH7Z.js")).encodeSvgMetadata({
16177
+ metadata = await (await import("./image-JKT6GXZD.js")).encodeSvgMetadata({
16155
16178
  // when embedding scene, we want to embed the origionally supplied
16156
16179
  // elements which don't contain the temp frame labels.
16157
16180
  // But it also requires that the exportToSvg is being supplied with
@@ -16305,6 +16328,7 @@ var restoreElementWithProperties = (element, extra) => {
16305
16328
  // newly added elements
16306
16329
  version: element.version || 1,
16307
16330
  versionNonce: element.versionNonce ?? 0,
16331
+ index: element.index ?? null,
16308
16332
  isDeleted: element.isDeleted ?? false,
16309
16333
  id: element.id || randomId(),
16310
16334
  fillStyle: element.fillStyle || DEFAULT_ELEMENT_PROPS.fillStyle,
@@ -16335,9 +16359,6 @@ var restoreElementWithProperties = (element, extra) => {
16335
16359
  if ("customData" in element || "customData" in extra) {
16336
16360
  base.customData = "customData" in extra ? extra.customData : element.customData;
16337
16361
  }
16338
- if (PRECEDING_ELEMENT_KEY in element) {
16339
- base[PRECEDING_ELEMENT_KEY] = element[PRECEDING_ELEMENT_KEY];
16340
- }
16341
16362
  return {
16342
16363
  ...base,
16343
16364
  ...getNormalizedDimensions(base),
@@ -16488,23 +16509,28 @@ var repairFrameMembership = (element, elementsMap) => {
16488
16509
  var restoreElements = (elements, localElements, opts) => {
16489
16510
  const existingIds = /* @__PURE__ */ new Set();
16490
16511
  const localElementsMap = localElements ? arrayToMap(localElements) : null;
16491
- const restoredElements = (elements || []).reduce((elements2, element) => {
16492
- if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
16493
- let migratedElement = restoreElement(element);
16494
- if (migratedElement) {
16495
- const localElement = localElementsMap?.get(element.id);
16496
- if (localElement && localElement.version > migratedElement.version) {
16497
- migratedElement = bumpVersion(migratedElement, localElement.version);
16498
- }
16499
- if (existingIds.has(migratedElement.id)) {
16500
- migratedElement = { ...migratedElement, id: randomId() };
16512
+ const restoredElements = syncInvalidIndices(
16513
+ (elements || []).reduce((elements2, element) => {
16514
+ if (element.type !== "selection" && !isInvisiblySmallElement(element)) {
16515
+ let migratedElement = restoreElement(element);
16516
+ if (migratedElement) {
16517
+ const localElement = localElementsMap?.get(element.id);
16518
+ if (localElement && localElement.version > migratedElement.version) {
16519
+ migratedElement = bumpVersion(
16520
+ migratedElement,
16521
+ localElement.version
16522
+ );
16523
+ }
16524
+ if (existingIds.has(migratedElement.id)) {
16525
+ migratedElement = { ...migratedElement, id: randomId() };
16526
+ }
16527
+ existingIds.add(migratedElement.id);
16528
+ elements2.push(migratedElement);
16501
16529
  }
16502
- existingIds.add(migratedElement.id);
16503
- elements2.push(migratedElement);
16504
16530
  }
16505
- }
16506
- return elements2;
16507
- }, []);
16531
+ return elements2;
16532
+ }, [])
16533
+ );
16508
16534
  if (!opts?.repairBindings) {
16509
16535
  return restoredElements;
16510
16536
  }
@@ -18117,7 +18143,10 @@ var getNonDeletedElements3 = (allElements) => {
18117
18143
  for (const element of allElements) {
18118
18144
  if (!element.isDeleted) {
18119
18145
  elements.push(element);
18120
- elementsMap.set(element.id, element);
18146
+ elementsMap.set(
18147
+ element.id,
18148
+ element
18149
+ );
18121
18150
  }
18122
18151
  }
18123
18152
  return { elementsMap, elements };
@@ -18164,6 +18193,7 @@ var Scene = class _Scene {
18164
18193
  nonDeletedElementsMap = toBrandedType(
18165
18194
  /* @__PURE__ */ new Map()
18166
18195
  );
18196
+ // ideally all elements within the scene should be wrapped around with `Ordered` type, but right now there is no real benefit doing so
18167
18197
  elements = [];
18168
18198
  nonDeletedFramesLikes = [];
18169
18199
  frames = [];
@@ -18255,9 +18285,15 @@ var Scene = class _Scene {
18255
18285
  return didChange;
18256
18286
  }
18257
18287
  replaceAllElements(nextElements) {
18258
- this.elements = // ts doesn't like `Array.isArray` of `instanceof Map`
18259
- nextElements instanceof Array ? nextElements : Array.from(nextElements.values());
18288
+ const _nextElements = (
18289
+ // ts doesn't like `Array.isArray` of `instanceof Map`
18290
+ nextElements instanceof Array ? nextElements : Array.from(nextElements.values())
18291
+ );
18260
18292
  const nextFrameLikes = [];
18293
+ if (define_import_meta_env_default.DEV || define_import_meta_env_default.MODE === ENV.TEST) {
18294
+ validateFractionalIndices(_nextElements.map((x) => x.index));
18295
+ }
18296
+ this.elements = syncInvalidIndices(_nextElements);
18261
18297
  this.elementsMap.clear();
18262
18298
  this.elements.forEach((element) => {
18263
18299
  if (isFrameLikeElement(element)) {
@@ -18292,8 +18328,8 @@ var Scene = class _Scene {
18292
18328
  };
18293
18329
  }
18294
18330
  destroy() {
18295
- this.nonDeletedElements = [];
18296
18331
  this.elements = [];
18332
+ this.nonDeletedElements = [];
18297
18333
  this.nonDeletedFramesLikes = [];
18298
18334
  this.frames = [];
18299
18335
  this.elementsMap.clear();
@@ -18318,6 +18354,7 @@ var Scene = class _Scene {
18318
18354
  element,
18319
18355
  ...this.elements.slice(index)
18320
18356
  ];
18357
+ syncMovedIndices(nextElements, arrayToMap([element]));
18321
18358
  this.replaceAllElements(nextElements);
18322
18359
  }
18323
18360
  insertElementsAtIndex(elements, index) {
@@ -18331,14 +18368,16 @@ var Scene = class _Scene {
18331
18368
  ...elements,
18332
18369
  ...this.elements.slice(index)
18333
18370
  ];
18371
+ syncMovedIndices(nextElements, arrayToMap(elements));
18334
18372
  this.replaceAllElements(nextElements);
18335
18373
  }
18336
- addNewElement = (element) => {
18337
- if (element.frameId) {
18338
- this.insertElementAtIndex(element, this.getElementIndex(element.frameId));
18339
- } else {
18340
- this.replaceAllElements([...this.elements, element]);
18341
- }
18374
+ insertElement = (element) => {
18375
+ const index = element.frameId ? this.getElementIndex(element.frameId) : this.elements.length;
18376
+ this.insertElementAtIndex(element, index);
18377
+ };
18378
+ insertElements = (elements) => {
18379
+ const index = elements[0].frameId ? this.getElementIndex(elements[0].frameId) : this.elements.length;
18380
+ this.insertElementsAtIndex(elements, index);
18342
18381
  };
18343
18382
  getElementIndex(elementId) {
18344
18383
  return this.elements.findIndex((element) => element.id === elementId);
@@ -18527,6 +18566,210 @@ var getNormalizedDimensions = (element) => {
18527
18566
  return ret;
18528
18567
  };
18529
18568
 
18569
+ // element/transformHandles.ts
18570
+ init_define_import_meta_env();
18571
+ var transformHandleSizes = {
18572
+ mouse: 8,
18573
+ pen: 16,
18574
+ touch: 28
18575
+ };
18576
+ var ROTATION_RESIZE_HANDLE_GAP = 16;
18577
+ var OMIT_SIDES_FOR_MULTIPLE_ELEMENTS = {
18578
+ e: true,
18579
+ s: true,
18580
+ n: true,
18581
+ w: true
18582
+ };
18583
+ var OMIT_SIDES_FOR_FRAME = {
18584
+ e: true,
18585
+ s: true,
18586
+ n: true,
18587
+ w: true,
18588
+ rotation: true
18589
+ };
18590
+ var OMIT_SIDES_FOR_TEXT_ELEMENT = {
18591
+ e: true,
18592
+ s: true,
18593
+ n: true,
18594
+ w: true
18595
+ };
18596
+ var OMIT_SIDES_FOR_LINE_SLASH = {
18597
+ e: true,
18598
+ s: true,
18599
+ n: true,
18600
+ w: true,
18601
+ nw: true,
18602
+ se: true
18603
+ };
18604
+ var OMIT_SIDES_FOR_LINE_BACKSLASH = {
18605
+ e: true,
18606
+ s: true,
18607
+ n: true,
18608
+ w: true
18609
+ };
18610
+ var generateTransformHandle = (x, y, width, height, cx, cy, angle) => {
18611
+ const [xx, yy] = rotate3(x + width / 2, y + height / 2, cx, cy, angle);
18612
+ return [xx - width / 2, yy - height / 2, width, height];
18613
+ };
18614
+ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, pointerType, omitSides = {}, margin = 4) => {
18615
+ const size = transformHandleSizes[pointerType];
18616
+ const handleWidth = size / zoom.value;
18617
+ const handleHeight = size / zoom.value;
18618
+ const handleMarginX = size / zoom.value;
18619
+ const handleMarginY = size / zoom.value;
18620
+ const width = x2 - x1;
18621
+ const height = y2 - y1;
18622
+ const dashedLineMargin = margin / zoom.value;
18623
+ const centeringOffset = (size - DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / (2 * zoom.value);
18624
+ const transformHandles = {
18625
+ nw: omitSides.nw ? void 0 : generateTransformHandle(
18626
+ x1 - dashedLineMargin - handleMarginX + centeringOffset,
18627
+ y1 - dashedLineMargin - handleMarginY + centeringOffset,
18628
+ handleWidth,
18629
+ handleHeight,
18630
+ cx,
18631
+ cy,
18632
+ angle
18633
+ ),
18634
+ ne: omitSides.ne ? void 0 : generateTransformHandle(
18635
+ x2 + dashedLineMargin - centeringOffset,
18636
+ y1 - dashedLineMargin - handleMarginY + centeringOffset,
18637
+ handleWidth,
18638
+ handleHeight,
18639
+ cx,
18640
+ cy,
18641
+ angle
18642
+ ),
18643
+ sw: omitSides.sw ? void 0 : generateTransformHandle(
18644
+ x1 - dashedLineMargin - handleMarginX + centeringOffset,
18645
+ y2 + dashedLineMargin - centeringOffset,
18646
+ handleWidth,
18647
+ handleHeight,
18648
+ cx,
18649
+ cy,
18650
+ angle
18651
+ ),
18652
+ se: omitSides.se ? void 0 : generateTransformHandle(
18653
+ x2 + dashedLineMargin - centeringOffset,
18654
+ y2 + dashedLineMargin - centeringOffset,
18655
+ handleWidth,
18656
+ handleHeight,
18657
+ cx,
18658
+ cy,
18659
+ angle
18660
+ ),
18661
+ rotation: omitSides.rotation ? void 0 : generateTransformHandle(
18662
+ x1 + width / 2 - handleWidth / 2,
18663
+ y1 - dashedLineMargin - handleMarginY + centeringOffset - ROTATION_RESIZE_HANDLE_GAP / zoom.value,
18664
+ handleWidth,
18665
+ handleHeight,
18666
+ cx,
18667
+ cy,
18668
+ angle
18669
+ )
18670
+ };
18671
+ const minimumSizeForEightHandles = 5 * transformHandleSizes.mouse / zoom.value;
18672
+ if (Math.abs(width) > minimumSizeForEightHandles) {
18673
+ if (!omitSides.n) {
18674
+ transformHandles.n = generateTransformHandle(
18675
+ x1 + width / 2 - handleWidth / 2,
18676
+ y1 - dashedLineMargin - handleMarginY + centeringOffset,
18677
+ handleWidth,
18678
+ handleHeight,
18679
+ cx,
18680
+ cy,
18681
+ angle
18682
+ );
18683
+ }
18684
+ if (!omitSides.s) {
18685
+ transformHandles.s = generateTransformHandle(
18686
+ x1 + width / 2 - handleWidth / 2,
18687
+ y2 + dashedLineMargin - centeringOffset,
18688
+ handleWidth,
18689
+ handleHeight,
18690
+ cx,
18691
+ cy,
18692
+ angle
18693
+ );
18694
+ }
18695
+ }
18696
+ if (Math.abs(height) > minimumSizeForEightHandles) {
18697
+ if (!omitSides.w) {
18698
+ transformHandles.w = generateTransformHandle(
18699
+ x1 - dashedLineMargin - handleMarginX + centeringOffset,
18700
+ y1 + height / 2 - handleHeight / 2,
18701
+ handleWidth,
18702
+ handleHeight,
18703
+ cx,
18704
+ cy,
18705
+ angle
18706
+ );
18707
+ }
18708
+ if (!omitSides.e) {
18709
+ transformHandles.e = generateTransformHandle(
18710
+ x2 + dashedLineMargin - centeringOffset,
18711
+ y1 + height / 2 - handleHeight / 2,
18712
+ handleWidth,
18713
+ handleHeight,
18714
+ cx,
18715
+ cy,
18716
+ angle
18717
+ );
18718
+ }
18719
+ }
18720
+ return transformHandles;
18721
+ };
18722
+ var getTransformHandles = (element, zoom, elementsMap, pointerType = "mouse") => {
18723
+ if (element.locked) {
18724
+ return {};
18725
+ }
18726
+ let omitSides = {};
18727
+ if (element.type === "freedraw" || isLinearElement(element)) {
18728
+ if (element.points.length === 2) {
18729
+ const [, p1] = element.points;
18730
+ if (p1[0] === 0 || p1[1] === 0) {
18731
+ omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
18732
+ } else if (p1[0] > 0 && p1[1] < 0) {
18733
+ omitSides = OMIT_SIDES_FOR_LINE_SLASH;
18734
+ } else if (p1[0] > 0 && p1[1] > 0) {
18735
+ omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
18736
+ } else if (p1[0] < 0 && p1[1] > 0) {
18737
+ omitSides = OMIT_SIDES_FOR_LINE_SLASH;
18738
+ } else if (p1[0] < 0 && p1[1] < 0) {
18739
+ omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
18740
+ }
18741
+ }
18742
+ } else if (isTextElement(element)) {
18743
+ omitSides = OMIT_SIDES_FOR_TEXT_ELEMENT;
18744
+ } else if (isFrameLikeElement(element)) {
18745
+ omitSides = {
18746
+ rotation: true
18747
+ };
18748
+ }
18749
+ const dashedLineMargin = isLinearElement(element) ? DEFAULT_TRANSFORM_HANDLE_SPACING + 8 : DEFAULT_TRANSFORM_HANDLE_SPACING;
18750
+ return getTransformHandlesFromCoords(
18751
+ getElementAbsoluteCoords(element, elementsMap, true),
18752
+ element.angle,
18753
+ zoom,
18754
+ pointerType,
18755
+ omitSides,
18756
+ dashedLineMargin
18757
+ );
18758
+ };
18759
+ var shouldShowBoundingBox = (elements, appState) => {
18760
+ if (appState.editingLinearElement) {
18761
+ return false;
18762
+ }
18763
+ if (elements.length > 1) {
18764
+ return true;
18765
+ }
18766
+ const element = elements[0];
18767
+ if (!isLinearElement(element)) {
18768
+ return true;
18769
+ }
18770
+ return element.points.length > 2;
18771
+ };
18772
+
18530
18773
  // element/resizeTest.ts
18531
18774
  init_define_import_meta_env();
18532
18775
  var isInsideTransformHandle = (transformHandle, x, y) => x >= transformHandle[0] && x <= transformHandle[0] + transformHandle[2] && y >= transformHandle[1] && y <= transformHandle[1] + transformHandle[3];
@@ -18765,7 +19008,7 @@ var resizeSingleTextElement = (element, elementsMap, transformHandleType, should
18765
19008
  const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
18766
19009
  const cx = (x1 + x2) / 2;
18767
19010
  const cy = (y1 + y2) / 2;
18768
- const [rotatedX, rotatedY] = rotate2(
19011
+ const [rotatedX, rotatedY] = rotate3(
18769
19012
  pointerX,
18770
19013
  pointerY,
18771
19014
  cx,
@@ -19229,7 +19472,7 @@ var rotateMultipleElements = (originalElements, elements, elementsMap, pointerX,
19229
19472
  const cx = (x1 + x2) / 2;
19230
19473
  const cy = (y1 + y2) / 2;
19231
19474
  const origAngle = originalElements.get(element.id)?.angle ?? element.angle;
19232
- const [rotatedCX, rotatedCY] = rotate2(
19475
+ const [rotatedCX, rotatedCY] = rotate3(
19233
19476
  cx,
19234
19477
  cy,
19235
19478
  centerX,
@@ -19268,24 +19511,24 @@ var getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x,
19268
19511
  const cx = (x1 + x2) / 2;
19269
19512
  const cy = (y1 + y2) / 2;
19270
19513
  const angle = selectedElements.length === 1 ? selectedElements[0].angle : 0;
19271
- [x, y] = rotate2(x, y, cx, cy, -angle);
19514
+ [x, y] = rotate3(x, y, cx, cy, -angle);
19272
19515
  switch (transformHandleType) {
19273
19516
  case "n":
19274
- return rotate2(x - (x1 + x2) / 2, y - y1, 0, 0, angle);
19517
+ return rotate3(x - (x1 + x2) / 2, y - y1, 0, 0, angle);
19275
19518
  case "s":
19276
- return rotate2(x - (x1 + x2) / 2, y - y2, 0, 0, angle);
19519
+ return rotate3(x - (x1 + x2) / 2, y - y2, 0, 0, angle);
19277
19520
  case "w":
19278
- return rotate2(x - x1, y - (y1 + y2) / 2, 0, 0, angle);
19521
+ return rotate3(x - x1, y - (y1 + y2) / 2, 0, 0, angle);
19279
19522
  case "e":
19280
- return rotate2(x - x2, y - (y1 + y2) / 2, 0, 0, angle);
19523
+ return rotate3(x - x2, y - (y1 + y2) / 2, 0, 0, angle);
19281
19524
  case "nw":
19282
- return rotate2(x - x1, y - y1, 0, 0, angle);
19525
+ return rotate3(x - x1, y - y1, 0, 0, angle);
19283
19526
  case "ne":
19284
- return rotate2(x - x2, y - y1, 0, 0, angle);
19527
+ return rotate3(x - x2, y - y1, 0, 0, angle);
19285
19528
  case "sw":
19286
- return rotate2(x - x1, y - y2, 0, 0, angle);
19529
+ return rotate3(x - x1, y - y2, 0, 0, angle);
19287
19530
  case "se":
19288
- return rotate2(x - x2, y - y2, 0, 0, angle);
19531
+ return rotate3(x - x2, y - y2, 0, 0, angle);
19289
19532
  default:
19290
19533
  return [0, 0];
19291
19534
  }
@@ -19455,7 +19698,7 @@ var parseFileContents = async (blob) => {
19455
19698
  let contents;
19456
19699
  if (blob.type === MIME_TYPES.png) {
19457
19700
  try {
19458
- return await (await import("./image-EDKQZH7Z.js")).decodePngMetadata(blob);
19701
+ return await (await import("./image-JKT6GXZD.js")).decodePngMetadata(blob);
19459
19702
  } catch (error) {
19460
19703
  if (error.message === "INVALID") {
19461
19704
  throw new ImageSceneDataError(
@@ -19482,7 +19725,7 @@ var parseFileContents = async (blob) => {
19482
19725
  }
19483
19726
  if (blob.type === MIME_TYPES.svg) {
19484
19727
  try {
19485
- return await (await import("./image-EDKQZH7Z.js")).decodeSvgMetadata({
19728
+ return await (await import("./image-JKT6GXZD.js")).decodeSvgMetadata({
19486
19729
  svg: contents
19487
19730
  });
19488
19731
  } catch (error) {
@@ -20054,6 +20297,7 @@ export {
20054
20297
  arrayToMap,
20055
20298
  arrayToMapWithIndex,
20056
20299
  isTestEnv,
20300
+ isDevEnv,
20057
20301
  wrapEvent,
20058
20302
  updateObject,
20059
20303
  getFrame,
@@ -20095,6 +20339,10 @@ export {
20095
20339
  getDefaultRoundnessTypeForElement,
20096
20340
  randomInteger,
20097
20341
  randomId,
20342
+ AbortError,
20343
+ ImageSceneDataError,
20344
+ syncMovedIndices,
20345
+ syncInvalidIndices,
20098
20346
  Scene_default,
20099
20347
  getSizeFromPoints,
20100
20348
  rotatePoint,
@@ -20110,17 +20358,7 @@ export {
20110
20358
  hasStrokeStyle,
20111
20359
  canChangeRoundness,
20112
20360
  canHaveArrowheads,
20113
- getElementsAtPosition,
20114
- OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
20115
- OMIT_SIDES_FOR_FRAME,
20116
- getTransformHandlesFromCoords,
20117
- getTransformHandles,
20118
- shouldShowBoundingBox,
20119
- hitTest,
20120
- isHittingElementBoundingBoxWithoutHittingElement,
20121
- isHittingElementNotConsideringBoundingBox,
20122
- isPointHittingElementBoundingBox,
20123
- maxBindingGap,
20361
+ isPointInShape,
20124
20362
  CODES,
20125
20363
  KEYS,
20126
20364
  isArrowKey,
@@ -20140,6 +20378,7 @@ export {
20140
20378
  getEligibleElementsForBinding,
20141
20379
  fixBindingsAfterDuplication,
20142
20380
  fixBindingsAfterDeletion,
20381
+ maxBindingGap,
20143
20382
  LinearElementEditor,
20144
20383
  originalContainerCache,
20145
20384
  updateOriginalContainerCache,
@@ -20162,7 +20401,6 @@ export {
20162
20401
  getTextElementAngle,
20163
20402
  shouldAllowVerticalAlign,
20164
20403
  suppportsHorizontalAlign,
20165
- getTextBindableContainerAtPosition,
20166
20404
  isValidTextContainer,
20167
20405
  computeContainerDimensionForBoundText,
20168
20406
  getBoundTextMaxWidth,
@@ -20218,8 +20456,6 @@ export {
20218
20456
  maybeParseEmbedSrc,
20219
20457
  embeddableURLValidator,
20220
20458
  e,
20221
- AbortError,
20222
- ImageSceneDataError,
20223
20459
  fileOpen,
20224
20460
  fileSave,
20225
20461
  restoreElements,
@@ -20257,6 +20493,16 @@ export {
20257
20493
  updateImageCache,
20258
20494
  getInitializedImageElements,
20259
20495
  normalizeSVG,
20496
+ getPolygonShape,
20497
+ getEllipseShape,
20498
+ getCurveShape,
20499
+ getFreedrawShape,
20500
+ getClosedCurveShape,
20501
+ shouldTestInside,
20502
+ hitElementItself,
20503
+ hitElementBoundingBox,
20504
+ hitElementBoundingBoxOnly,
20505
+ hitElementBoundText,
20260
20506
  getLinkHandleFromCoords,
20261
20507
  isPointHittingLinkIcon,
20262
20508
  isPointHittingLink,
@@ -20313,6 +20559,11 @@ export {
20313
20559
  isElementInViewport,
20314
20560
  getLockedLinearCursorAlignSize,
20315
20561
  getNormalizedDimensions,
20562
+ OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
20563
+ OMIT_SIDES_FOR_FRAME,
20564
+ getTransformHandlesFromCoords,
20565
+ getTransformHandles,
20566
+ shouldShowBoundingBox,
20316
20567
  getElementWithTransformHandleType,
20317
20568
  getTransformHandleTypeFromCoords,
20318
20569
  getCursorForResizingElement,
@@ -20342,4 +20593,4 @@ export {
20342
20593
  getNormalizedZoom,
20343
20594
  getStateForZoom
20344
20595
  };
20345
- //# sourceMappingURL=chunk-RWZVJAQU.js.map
20596
+ //# sourceMappingURL=chunk-7D5BMEAB.js.map