2
0
Mark Tolmacs 6 сар өмнө
parent
commit
dca9fbe306

+ 2 - 0
excalidraw-app/components/DebugCanvas.tsx

@@ -13,6 +13,8 @@ import { useCallback, useImperativeHandle, useRef } from "react";
 
 import {
   isLineSegment,
+  isCurve,
+  type Curve,
   type GlobalPoint,
   type LineSegment,
 } from "@excalidraw/math";

+ 13 - 61
packages/element/src/binding.ts

@@ -463,23 +463,10 @@ export const maybeBindLinearElement = (
   }
 };
 
-const normalizePointBinding = (
-  binding: { focus: number; gap: number },
-  hoveredElement: ExcalidrawBindableElement,
-) => {
-  let gap = binding.gap;
-  const maxGap = maxBindingGap(
-    hoveredElement,
-    hoveredElement.width,
-    hoveredElement.height,
-  );
-
-  if (gap > maxGap) {
-    gap = BINDING_HIGHLIGHT_THICKNESS + BINDING_HIGHLIGHT_OFFSET;
-  }
+const normalizePointBinding = (binding: { focus: number; gap: number }) => {
   return {
     ...binding,
-    gap,
+    gap: FIXED_BINDING_DISTANCE,
   };
 };
 
@@ -729,7 +716,7 @@ const calculateFocusAndGap = (
 
   return {
     focus: determineFocusDistance(hoveredElement, adjacentPoint, edgePoint),
-    gap: Math.max(1, distanceToBindableElement(hoveredElement, edgePoint)),
+    gap: FIXED_BINDING_DISTANCE,
   };
 };
 
@@ -747,7 +734,7 @@ export const updateBoundElements = (
     changedElements?: Map<string, OrderedExcalidrawElement>;
   },
 ) => {
-  const { newSize, simultaneouslyUpdated } = options ?? {};
+  const { simultaneouslyUpdated } = options ?? {};
   const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(
     simultaneouslyUpdated,
   );
@@ -780,23 +767,13 @@ export const updateBoundElements = (
       startBounds = getElementBounds(startBindingElement, elementsMap);
       endBounds = getElementBounds(endBindingElement, elementsMap);
     }
-
-    const bindings = {
-      startBinding: maybeCalculateNewGapWhenScaling(
-        changedElement,
-        element.startBinding,
-        newSize,
-      ),
-      endBinding: maybeCalculateNewGapWhenScaling(
-        changedElement,
-        element.endBinding,
-        newSize,
-      ),
-    };
-
     // `linearElement` is being moved/scaled already, just update the binding
     if (simultaneouslyUpdatedElementIds.has(element.id)) {
-      mutateElement(element, bindings, true);
+      mutateElement(
+        element,
+        { startBinding: element.startBinding, endBinding: element.endBinding },
+        true,
+      );
       return;
     }
 
@@ -818,7 +795,9 @@ export const updateBoundElements = (
           const point = updateBoundPoint(
             element,
             bindingProp,
-            bindings[bindingProp],
+            bindingProp === "startBinding"
+              ? element.startBinding
+              : element.endBinding,
             bindableElement,
             elementsMap,
           );
@@ -1040,10 +1019,7 @@ export const avoidRectangularCorner = (
   element: ExcalidrawBindableElement,
   p: GlobalPoint,
 ): GlobalPoint => {
-  const center = pointFrom<GlobalPoint>(
-    element.x + element.width / 2,
-    element.y + element.height / 2,
-  );
+  const center = elementCenterPoint(element);
   const nonRotatedPoint = pointRotateRads(p, center, -element.angle as Radians);
 
   if (nonRotatedPoint[0] < element.x && nonRotatedPoint[1] < element.y) {
@@ -1226,7 +1202,6 @@ const updateBoundPoint = (
         linearElement,
         bindableElement,
         startOrEnd === "startBinding" ? "start" : "end",
-        elementsMap,
       ).fixedPoint;
     const globalMidPoint = pointFrom<GlobalPoint>(
       bindableElement.x + bindableElement.width / 2,
@@ -1336,7 +1311,6 @@ export const calculateFixedPointForElbowArrowBinding = (
   linearElement: NonDeleted<ExcalidrawElbowArrowElement>,
   hoveredElement: ExcalidrawBindableElement,
   startOrEnd: "start" | "end",
-  elementsMap: ElementsMap,
 ): { fixedPoint: FixedPoint } => {
   const bounds = [
     hoveredElement.x,
@@ -1369,28 +1343,6 @@ export const calculateFixedPointForElbowArrowBinding = (
   };
 };
 
-const maybeCalculateNewGapWhenScaling = (
-  changedElement: ExcalidrawBindableElement,
-  currentBinding: PointBinding | null | undefined,
-  newSize: { width: number; height: number } | undefined,
-): PointBinding | null | undefined => {
-  if (currentBinding == null || newSize == null) {
-    return currentBinding;
-  }
-  const { width: newWidth, height: newHeight } = newSize;
-  const { width, height } = changedElement;
-  const newGap = Math.max(
-    1,
-    Math.min(
-      maxBindingGap(changedElement, newWidth, newHeight),
-      currentBinding.gap *
-        (newWidth < newHeight ? newWidth / width : newHeight / height),
-    ),
-  );
-
-  return { ...currentBinding, gap: newGap };
-};
-
 const getElligibleElementForBindingElement = (
   linearElement: NonDeleted<ExcalidrawLinearElement>,
   startOrEnd: "start" | "end",

+ 15 - 16
packages/element/tests/elbowArrow.test.tsx

@@ -78,8 +78,8 @@ describe("elbow arrow segment move", () => {
     expect(arrow.points).toCloselyEqualPoints([
       [0, 0],
       [110, 0],
-      [110, 200],
-      [190, 200],
+      [110, 195.01],
+      [190, 195.01],
     ]);
 
     mouse.reset();
@@ -89,8 +89,8 @@ describe("elbow arrow segment move", () => {
     expect(arrow.points).toCloselyEqualPoints([
       [0, 0],
       [110, 0],
-      [110, 200],
-      [190, 200],
+      [110, 195.01],
+      [190, 195.01],
     ]);
   });
 
@@ -198,11 +198,11 @@ describe("elbow arrow routing", () => {
       points: [pointFrom<LocalPoint>(0, 0), pointFrom<LocalPoint>(90, 200)],
     });
 
-    expect(arrow.points).toEqual([
+    expect(arrow.points).toCloselyEqualPoints([
       [0, 0],
       [45, 0],
-      [45, 200],
-      [90, 200],
+      [45, 199.07],
+      [90.07, 199.07],
     ]);
   });
 });
@@ -241,9 +241,9 @@ describe("elbow arrow ui", () => {
     expect(h.state.currentItemArrowType).toBe(ARROW_TYPE.elbow);
 
     mouse.reset();
-    mouse.moveTo(-43, -99);
+    mouse.moveTo(-50, -100);
     mouse.click();
-    mouse.moveTo(43, 99);
+    mouse.moveTo(50, 100);
     mouse.click();
 
     const arrow = h.scene.getSelectedElements(
@@ -252,11 +252,11 @@ describe("elbow arrow ui", () => {
 
     expect(arrow.type).toBe("arrow");
     expect(arrow.elbowed).toBe(true);
-    expect(arrow.points).toEqual([
+    expect(arrow.points).toCloselyEqualPoints([
       [0, 0],
       [45, 0],
-      [45, 200],
-      [90, 200],
+      [45, 195.01],
+      [90, 195.01],
     ]);
   });
 
@@ -293,12 +293,11 @@ describe("elbow arrow ui", () => {
       ".drag-input",
     ) as HTMLInputElement;
     UI.updateInput(inputAngle, String("40"));
-
+console.log(JSON.stringify(h.elements))
     expect(arrow.points.map((point) => point.map(Math.round))).toEqual([
       [0, 0],
-      [35, 0],
-      [35, 165],
-      [103, 165],
+      [109, 0],
+      [109, 152],
     ]);
   });
 

+ 13 - 13
packages/element/tests/resize.test.tsx

@@ -195,7 +195,7 @@ describe("generic element", () => {
     UI.resize(rectangle, "w", [50, 0]);
 
     expect(arrow.endBinding?.elementId).toEqual(rectangle.id);
-    expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80, 0);
+    expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80.62, 0);
   });
 
   it("resizes with a label", async () => {
@@ -510,13 +510,13 @@ describe("arrow element", () => {
       h.state,
     )[0] as ExcalidrawElbowArrowElement;
 
-    expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
-    expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
+    expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
+    expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.78);
 
     UI.resize(rectangle, "se", [-200, -150]);
 
-    expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
-    expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
+    expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
+    expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.78);
   });
 
   it("flips the fixed point binding on negative resize for group selection", () => {
@@ -538,8 +538,8 @@ describe("arrow element", () => {
       h.state,
     )[0] as ExcalidrawElbowArrowElement;
 
-    expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
-    expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
+    expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.05);
+    expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.78);
 
     UI.resize([rectangle, arrow], "nw", [300, 350]);
     expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(0);
@@ -809,7 +809,7 @@ describe("image element", () => {
     });
     API.setElements([image]);
     const arrow = UI.createElement("arrow", {
-      x: -30,
+      x: -29,
       y: 50,
       width: 28,
       height: 5,
@@ -819,14 +819,14 @@ describe("image element", () => {
 
     UI.resize(image, "ne", [40, 0]);
 
-    expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(31, 0);
+    expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30, 0);
 
     const imageWidth = image.width;
     const scale = 20 / image.height;
     UI.resize(image, "nw", [50, 20]);
 
     expect(arrow.endBinding?.elementId).toEqual(image.id);
-    expect(Math.floor(arrow.width + arrow.endBinding!.gap)).toBeCloseTo(
+    expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(
       30 + imageWidth * scale,
       0,
     );
@@ -1033,11 +1033,11 @@ describe("multiple selection", () => {
 
     expect(leftBoundArrow.x).toBeCloseTo(-110);
     expect(leftBoundArrow.y).toBeCloseTo(50);
-    expect(leftBoundArrow.width).toBeCloseTo(143, 0);
+    expect(leftBoundArrow.width).toBeCloseTo(146.46, 0);
     expect(leftBoundArrow.height).toBeCloseTo(7, 0);
     expect(leftBoundArrow.angle).toEqual(0);
     expect(leftBoundArrow.startBinding).toBeNull();
-    expect(leftBoundArrow.endBinding?.gap).toBeCloseTo(10);
+    expect(leftBoundArrow.endBinding?.gap).toEqual(FIXED_BINDING_DISTANCE);
     expect(leftBoundArrow.endBinding?.elementId).toBe(
       leftArrowBinding.elementId,
     );
@@ -1051,7 +1051,7 @@ describe("multiple selection", () => {
     expect(rightBoundArrow.height).toBeCloseTo(0);
     expect(rightBoundArrow.angle).toEqual(0);
     expect(rightBoundArrow.startBinding).toBeNull();
-    expect(rightBoundArrow.endBinding?.gap).toBeCloseTo(8.0952);
+    expect(rightBoundArrow.endBinding?.gap).toEqual(FIXED_BINDING_DISTANCE);
     expect(rightBoundArrow.endBinding?.elementId).toBe(
       rightArrowBinding.elementId,
     );

+ 14 - 14
packages/excalidraw/data/__snapshots__/transform.test.ts.snap

@@ -89,7 +89,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
   "endBinding": {
     "elementId": "ellipse-1",
     "focus": -0.007519379844961235,
-    "gap": 11.562288374879595,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -119,7 +119,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
   "startBinding": {
     "elementId": "id49",
     "focus": -0.0813953488372095,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1864ab",
   "strokeStyle": "solid",
@@ -145,7 +145,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
   "endBinding": {
     "elementId": "ellipse-1",
     "focus": 0.10666666666666667,
-    "gap": 3.8343264684446097,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -175,7 +175,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing s
   "startBinding": {
     "elementId": "diamond-1",
     "focus": 0,
-    "gap": 4.545343408287929,
+    "gap": 5,
   },
   "strokeColor": "#e67700",
   "strokeStyle": "solid",
@@ -335,7 +335,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
   "endBinding": {
     "elementId": "text-2",
     "focus": 0,
-    "gap": 14,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -365,7 +365,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to existing t
   "startBinding": {
     "elementId": "text-1",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -437,7 +437,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
   "endBinding": {
     "elementId": "id42",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -467,7 +467,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to shapes whe
   "startBinding": {
     "elementId": "id41",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -613,7 +613,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
   "endBinding": {
     "elementId": "id46",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -643,7 +643,7 @@ exports[`Test Transform > Test arrow bindings > should bind arrows to text when
   "startBinding": {
     "elementId": "id45",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -1475,7 +1475,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
   "endBinding": {
     "elementId": "Alice",
     "focus": -0,
-    "gap": 5.299874999999986,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -1507,7 +1507,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
   "startBinding": {
     "elementId": "Bob",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -1538,7 +1538,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
   "endBinding": {
     "elementId": "B",
     "focus": 0,
-    "gap": 14,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -1566,7 +1566,7 @@ exports[`Test Transform > should transform the elements correctly when linear el
   "startBinding": {
     "elementId": "Bob",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",

+ 3 - 3
packages/excalidraw/data/transform.test.ts

@@ -433,7 +433,7 @@ describe("Test Transform", () => {
         startBinding: {
           elementId: rectangle.id,
           focus: 0,
-          gap: 1,
+          gap: FIXED_BINDING_DISTANCE,
         },
         endBinding: {
           elementId: ellipse.id,
@@ -518,7 +518,7 @@ describe("Test Transform", () => {
         startBinding: {
           elementId: text2.id,
           focus: 0,
-          gap: 1,
+          gap: FIXED_BINDING_DISTANCE,
         },
         endBinding: {
           elementId: text3.id,
@@ -781,7 +781,7 @@ describe("Test Transform", () => {
       expect((arrow as ExcalidrawArrowElement).endBinding).toStrictEqual({
         elementId: "rect-1",
         focus: -0,
-        gap: 14,
+        gap: 5,
       });
       expect(rect.boundElements).toStrictEqual([
         {

+ 92 - 92
packages/excalidraw/tests/__snapshots__/history.test.tsx.snap

@@ -198,7 +198,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "fillStyle": "solid",
   "frameId": null,
   "groupIds": [],
-  "height": "102.35417",
+  "height": "99.23572",
   "id": "id172",
   "index": "a2",
   "isDeleted": false,
@@ -212,8 +212,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
       0,
     ],
     [
-      "101.77517",
-      "102.35417",
+      "96.42891",
+      "99.23572",
     ],
   ],
   "roughness": 1,
@@ -228,8 +228,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "type": "arrow",
   "updated": 1,
   "version": 40,
-  "width": "101.77517",
-  "x": "0.70711",
+  "width": "96.42891",
+  "x": "3.53553",
   "y": 0,
 }
 `;
@@ -296,46 +296,46 @@ History {
               "endBinding": {
                 "elementId": "id171",
                 "focus": "0.00990",
-                "gap": 1,
+                "gap": 5,
               },
-              "height": "0.98586",
+              "height": "0.92998",
               "points": [
                 [
                   0,
                   0,
                 ],
                 [
-                  "98.58579",
-                  "-0.98586",
+                  "92.92893",
+                  "-0.92998",
                 ],
               ],
               "startBinding": {
                 "elementId": "id170",
                 "focus": "0.02970",
-                "gap": 1,
+                "gap": 5,
               },
             },
             "inserted": {
               "endBinding": {
                 "elementId": "id171",
                 "focus": "-0.02000",
-                "gap": 1,
+                "gap": 5,
               },
-              "height": "0.00000",
+              "height": "0.00611",
               "points": [
                 [
                   0,
                   0,
                 ],
                 [
-                  "98.58579",
-                  "0.00000",
+                  "92.92893",
+                  "0.00611",
                 ],
               ],
               "startBinding": {
                 "elementId": "id170",
                 "focus": "0.02000",
-                "gap": 1,
+                "gap": 5,
               },
             },
           },
@@ -390,15 +390,15 @@ History {
                 "focus": 0,
                 "gap": 1,
               },
-              "height": "102.35417",
+              "height": "99.23572",
               "points": [
                 [
                   0,
                   0,
                 ],
                 [
-                  "101.77517",
-                  "102.35417",
+                  "96.42891",
+                  "99.23572",
                 ],
               ],
               "startBinding": null,
@@ -408,25 +408,25 @@ History {
               "endBinding": {
                 "elementId": "id171",
                 "focus": "0.00990",
-                "gap": 1,
+                "gap": 5,
               },
-              "height": "0.98586",
+              "height": "0.93503",
               "points": [
                 [
                   0,
                   0,
                 ],
                 [
-                  "98.58579",
-                  "-0.98586",
+                  "92.92893",
+                  "-0.93503",
                 ],
               ],
               "startBinding": {
                 "elementId": "id170",
                 "focus": "0.02970",
-                "gap": 1,
+                "gap": 5,
               },
-              "y": "0.99364",
+              "y": "0.97365",
             },
           },
           "id175" => Delta {
@@ -931,7 +931,7 @@ History {
               "endBinding": {
                 "elementId": "id166",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "points": [
                 [
@@ -946,7 +946,7 @@ History {
               "startBinding": {
                 "elementId": "id165",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
             },
           },
@@ -1241,7 +1241,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "fillStyle": "solid",
   "frameId": null,
   "groupIds": [],
-  "height": "1.30038",
+  "height": "2.98409",
   "id": "id178",
   "index": "Zz",
   "isDeleted": false,
@@ -1255,8 +1255,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
       0,
     ],
     [
-      "98.58579",
-      "1.30038",
+      "92.92893",
+      "-2.98409",
     ],
   ],
   "roughness": 1,
@@ -1279,9 +1279,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "type": "arrow",
   "updated": 1,
   "version": 11,
-  "width": "98.58579",
-  "x": "0.70711",
-  "y": 0,
+  "width": "92.92893",
+  "x": "3.53553",
+  "y": "4.70319",
 }
 `;
 
@@ -1613,7 +1613,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "fillStyle": "solid",
   "frameId": null,
   "groupIds": [],
-  "height": "1.30038",
+  "height": "2.98409",
   "id": "id181",
   "index": "a0",
   "isDeleted": false,
@@ -1627,8 +1627,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
       0,
     ],
     [
-      "98.58579",
-      "1.30038",
+      "92.92893",
+      "-2.98409",
     ],
   ],
   "roughness": 1,
@@ -1651,9 +1651,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "type": "arrow",
   "updated": 1,
   "version": 11,
-  "width": "98.58579",
-  "x": "0.70711",
-  "y": 0,
+  "width": "92.92893",
+  "x": "3.53553",
+  "y": "4.70319",
 }
 `;
 
@@ -1771,7 +1771,7 @@ History {
               "fillStyle": "solid",
               "frameId": null,
               "groupIds": [],
-              "height": "11.27227",
+              "height": "22.46459",
               "index": "a0",
               "isDeleted": false,
               "lastCommittedPoint": null,
@@ -1784,8 +1784,8 @@ History {
                   0,
                 ],
                 [
-                  "98.58579",
-                  "11.27227",
+                  "93.46683",
+                  "-22.46459",
                 ],
               ],
               "roughness": 1,
@@ -1806,9 +1806,9 @@ History {
               "strokeStyle": "solid",
               "strokeWidth": 2,
               "type": "arrow",
-              "width": "98.58579",
-              "x": "0.70711",
-              "y": 0,
+              "width": "93.46683",
+              "x": "2.99764",
+              "y": "35.33176",
             },
             "inserted": {
               "isDeleted": true,
@@ -2321,12 +2321,12 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "endBinding": {
     "elementId": "id185",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
   "groupIds": [],
-  "height": "374.05754",
+  "height": "408.02337",
   "id": "id186",
   "index": "a2",
   "isDeleted": false,
@@ -2340,8 +2340,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
       0,
     ],
     [
-      "502.78936",
-      "-374.05754",
+      "495.48945",
+      "-408.02337",
     ],
   ],
   "roughness": 1,
@@ -2352,7 +2352,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "startBinding": {
     "elementId": "id184",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -2360,9 +2360,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
   "type": "arrow",
   "updated": 1,
   "version": 10,
-  "width": "502.78936",
-  "x": "-0.83465",
-  "y": "-36.58211",
+  "width": "495.48945",
+  "x": "3.53553",
+  "y": 0,
 }
 `;
 
@@ -2481,7 +2481,7 @@ History {
               "endBinding": {
                 "elementId": "id185",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "fillStyle": "solid",
               "frameId": null,
@@ -2511,7 +2511,7 @@ History {
               "startBinding": {
                 "elementId": "id184",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
               "strokeColor": "#1e1e1e",
               "strokeStyle": "solid",
@@ -15161,7 +15161,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "endBinding": {
     "elementId": "id58",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -15180,7 +15180,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
       0,
     ],
     [
-      "98.58579",
+      "92.92893",
       0,
     ],
   ],
@@ -15192,7 +15192,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "startBinding": {
     "elementId": "id56",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -15200,8 +15200,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "type": "arrow",
   "updated": 1,
   "version": 10,
-  "width": "98.58579",
-  "x": "0.70711",
+  "width": "92.92893",
+  "x": "3.53553",
   "y": 0,
 }
 `;
@@ -15532,7 +15532,7 @@ History {
               "endBinding": {
                 "elementId": "id58",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "fillStyle": "solid",
               "frameId": null,
@@ -15562,7 +15562,7 @@ History {
               "startBinding": {
                 "elementId": "id56",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
               "strokeColor": "#1e1e1e",
               "strokeStyle": "solid",
@@ -15859,7 +15859,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "endBinding": {
     "elementId": "id52",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -15878,7 +15878,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
       0,
     ],
     [
-      "98.58579",
+      "92.92893",
       0,
     ],
   ],
@@ -15890,7 +15890,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "startBinding": {
     "elementId": "id50",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -15898,8 +15898,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "type": "arrow",
   "updated": 1,
   "version": 10,
-  "width": "98.58579",
-  "x": "0.70711",
+  "width": "92.92893",
+  "x": "3.53553",
   "y": 0,
 }
 `;
@@ -16152,7 +16152,7 @@ History {
               "endBinding": {
                 "elementId": "id52",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "fillStyle": "solid",
               "frameId": null,
@@ -16182,7 +16182,7 @@ History {
               "startBinding": {
                 "elementId": "id50",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
               "strokeColor": "#1e1e1e",
               "strokeStyle": "solid",
@@ -16479,7 +16479,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "endBinding": {
     "elementId": "id64",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -16498,7 +16498,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
       0,
     ],
     [
-      "98.58579",
+      "92.92893",
       0,
     ],
   ],
@@ -16510,7 +16510,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "startBinding": {
     "elementId": "id62",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -16518,8 +16518,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "type": "arrow",
   "updated": 1,
   "version": 10,
-  "width": "98.58579",
-  "x": "0.70711",
+  "width": "92.92893",
+  "x": "3.53553",
   "y": 0,
 }
 `;
@@ -16772,7 +16772,7 @@ History {
               "endBinding": {
                 "elementId": "id64",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "fillStyle": "solid",
               "frameId": null,
@@ -16802,7 +16802,7 @@ History {
               "startBinding": {
                 "elementId": "id62",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
               "strokeColor": "#1e1e1e",
               "strokeStyle": "solid",
@@ -17097,7 +17097,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "endBinding": {
     "elementId": "id70",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -17116,7 +17116,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
       0,
     ],
     [
-      "98.58579",
+      "92.92893",
       0,
     ],
   ],
@@ -17128,7 +17128,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "startBinding": {
     "elementId": "id68",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -17136,8 +17136,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "type": "arrow",
   "updated": 1,
   "version": 10,
-  "width": "98.58579",
-  "x": "0.70711",
+  "width": "92.92893",
+  "x": "3.53553",
   "y": 0,
 }
 `;
@@ -17200,7 +17200,7 @@ History {
               "startBinding": {
                 "elementId": "id68",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
             },
             "inserted": {
@@ -17460,7 +17460,7 @@ History {
               "endBinding": {
                 "elementId": "id70",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "fillStyle": "solid",
               "frameId": null,
@@ -17490,7 +17490,7 @@ History {
               "startBinding": {
                 "elementId": "id68",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
               "strokeColor": "#1e1e1e",
               "strokeStyle": "solid",
@@ -17811,7 +17811,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "endBinding": {
     "elementId": "id77",
     "focus": -0,
-    "gap": 1,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
@@ -17830,7 +17830,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
       0,
     ],
     [
-      "98.58579",
+      "92.92893",
       0,
     ],
   ],
@@ -17842,7 +17842,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "startBinding": {
     "elementId": "id75",
     "focus": 0,
-    "gap": 1,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -17850,8 +17850,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
   "type": "arrow",
   "updated": 1,
   "version": 11,
-  "width": "98.58579",
-  "x": "0.70711",
+  "width": "92.92893",
+  "x": "3.53553",
   "y": 0,
 }
 `;
@@ -17913,7 +17913,7 @@ History {
               "endBinding": {
                 "elementId": "id77",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "points": [
                 [
@@ -17928,7 +17928,7 @@ History {
               "startBinding": {
                 "elementId": "id75",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
             },
             "inserted": {
@@ -18189,7 +18189,7 @@ History {
               "endBinding": {
                 "elementId": "id77",
                 "focus": -0,
-                "gap": 1,
+                "gap": 5,
               },
               "fillStyle": "solid",
               "frameId": null,
@@ -18219,7 +18219,7 @@ History {
               "startBinding": {
                 "elementId": "id75",
                 "focus": 0,
-                "gap": 1,
+                "gap": 5,
               },
               "strokeColor": "#1e1e1e",
               "strokeStyle": "solid",

+ 8 - 8
packages/excalidraw/tests/__snapshots__/move.test.tsx.snap

@@ -191,12 +191,12 @@ exports[`move element > rectangles with binding arrow 7`] = `
   "endBinding": {
     "elementId": "id1",
     "focus": "-0.46667",
-    "gap": 10,
+    "gap": 5,
   },
   "fillStyle": "solid",
   "frameId": null,
   "groupIds": [],
-  "height": "87.29887",
+  "height": "87.97595",
   "id": "id2",
   "index": "a2",
   "isDeleted": false,
@@ -210,8 +210,8 @@ exports[`move element > rectangles with binding arrow 7`] = `
       0,
     ],
     [
-      "86.85786",
-      "87.29887",
+      "87.46447",
+      "87.97595",
     ],
   ],
   "roughness": 1,
@@ -223,7 +223,7 @@ exports[`move element > rectangles with binding arrow 7`] = `
   "startBinding": {
     "elementId": "id0",
     "focus": "-0.60000",
-    "gap": 10,
+    "gap": 5,
   },
   "strokeColor": "#1e1e1e",
   "strokeStyle": "solid",
@@ -232,8 +232,8 @@ exports[`move element > rectangles with binding arrow 7`] = `
   "updated": 1,
   "version": 11,
   "versionNonce": 1051383431,
-  "width": "86.85786",
-  "x": "107.07107",
-  "y": "47.07107",
+  "width": "87.46447",
+  "x": 110,
+  "y": 50,
 }
 `;

+ 2 - 2
packages/excalidraw/tests/history.test.tsx

@@ -4779,12 +4779,12 @@ describe("history", () => {
               startBinding: expect.objectContaining({
                 elementId: rect1.id,
                 focus: 0,
-                gap: 1,
+                gap: FIXED_BINDING_DISTANCE,
               }),
               endBinding: expect.objectContaining({
                 elementId: rect2.id,
                 focus: -0,
-                gap: 1,
+                gap: FIXED_BINDING_DISTANCE,
               }),
               isDeleted: true,
             }),

+ 1 - 1
packages/excalidraw/tests/linearElementEditor.test.tsx

@@ -1266,7 +1266,7 @@ describe("Test Linear Elements", () => {
       mouse.downAt(rect.x, rect.y);
       mouse.moveTo(200, 0);
       mouse.upAt(200, 0);
-      expect(arrow.width).toBeCloseTo(204, 0);
+      expect(arrow.width).toBeCloseTo(206.86, 0);
       expect(rect.x).toBe(200);
       expect(rect.y).toBe(0);
       expect(handleBindTextResizeSpy).toHaveBeenCalledWith(

+ 5 - 5
packages/excalidraw/tests/rotate.test.tsx

@@ -35,7 +35,7 @@ test("unselected bound arrow updates when rotating its target element", async ()
   expect(arrow.endBinding?.elementId).toEqual(rectangle.id);
   expect(arrow.x).toBeCloseTo(-80);
   expect(arrow.y).toBeCloseTo(50);
-  expect(arrow.width).toBeCloseTo(116.7, 1);
+  expect(arrow.width).toBeCloseTo(119.58, 1);
   expect(arrow.height).toBeCloseTo(0);
 });
 
@@ -72,13 +72,13 @@ test("unselected bound arrows update when rotating their target elements", async
   expect(ellipseArrow.x).toEqual(0);
   expect(ellipseArrow.y).toEqual(0);
   expect(ellipseArrow.points[0]).toEqual([0, 0]);
-  expect(ellipseArrow.points[1][0]).toBeCloseTo(48.98, 1);
-  expect(ellipseArrow.points[1][1]).toBeCloseTo(125.79, 1);
+  expect(ellipseArrow.points[1][0]).toBeCloseTo(54.36, 1);
+  expect(ellipseArrow.points[1][1]).toBeCloseTo(139.61, 1);
 
   expect(textArrow.endBinding?.elementId).toEqual(text.id);
   expect(textArrow.x).toEqual(360);
   expect(textArrow.y).toEqual(300);
   expect(textArrow.points[0]).toEqual([0, 0]);
-  expect(textArrow.points[1][0]).toBeCloseTo(-94, 0);
-  expect(textArrow.points[1][1]).toBeCloseTo(-116.1, 0);
+  expect(textArrow.points[1][0]).toBeCloseTo(-100.12, 0);
+  expect(textArrow.points[1][1]).toBeCloseTo(-123.63, 0);
 });

+ 1 - 1
packages/math/src/utils.ts

@@ -6,7 +6,7 @@ export const clamp = (value: number, min: number, max: number) => {
 
 export const round = (
   value: number,
-  precision: number,
+  precision: number = (Math.log(1 / PRECISION) * Math.LOG10E + 1) | 0,
   func: "round" | "floor" | "ceil" = "round",
 ) => {
   const multiplier = Math.pow(10, precision);