From c07f5a0c80fd3fdd86724d734082beb3b52f7818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Tolm=C3=A1cs?= Date: Tue, 17 Sep 2024 10:11:07 +0200 Subject: [PATCH] feat: Common elbow mid segments (#8440) Common start or end segment length for elbow arrows regardless of arrowhead is present --- excalidraw-app/App.tsx | 7 +++- excalidraw-app/components/DebugCanvas.tsx | 14 ++++++-- packages/excalidraw/element/routing.test.tsx | 25 ++++++------- packages/excalidraw/element/routing.ts | 38 +++++++++++--------- packages/excalidraw/visualdebug.ts | 6 ++-- 5 files changed, 56 insertions(+), 34 deletions(-) diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index 0076eead1..9b7eadff8 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -649,7 +649,12 @@ const ExcalidrawWrapper = () => { // Render the debug scene if the debug canvas is available if (debugCanvasRef.current && excalidrawAPI) { - debugRenderer(debugCanvasRef.current, appState, window.devicePixelRatio); + debugRenderer( + debugCanvasRef.current, + appState, + window.devicePixelRatio, + () => forceRefresh((prev) => !prev), + ); } }; diff --git a/excalidraw-app/components/DebugCanvas.tsx b/excalidraw-app/components/DebugCanvas.tsx index b610ab7b5..471167989 100644 --- a/excalidraw-app/components/DebugCanvas.tsx +++ b/excalidraw-app/components/DebugCanvas.tsx @@ -68,12 +68,17 @@ const _debugRenderer = ( canvas: HTMLCanvasElement, appState: AppState, scale: number, + refresh: () => void, ) => { const [normalizedWidth, normalizedHeight] = getNormalizedCanvasDimensions( canvas, scale, ); + if (appState.height !== canvas.height || appState.width !== canvas.width) { + refresh(); + } + const context = bootstrapCanvas({ canvas, scale, @@ -138,8 +143,13 @@ export const saveDebugState = (debug: { enabled: boolean }) => { }; export const debugRenderer = throttleRAF( - (canvas: HTMLCanvasElement, appState: AppState, scale: number) => { - _debugRenderer(canvas, appState, scale); + ( + canvas: HTMLCanvasElement, + appState: AppState, + scale: number, + refresh: () => void, + ) => { + _debugRenderer(canvas, appState, scale, refresh); }, { trailing: true }, ); diff --git a/packages/excalidraw/element/routing.test.tsx b/packages/excalidraw/element/routing.test.tsx index 9381541a5..e451fae5d 100644 --- a/packages/excalidraw/element/routing.test.tsx +++ b/packages/excalidraw/element/routing.test.tsx @@ -94,7 +94,16 @@ describe("elbow arrow routing", () => { describe("elbow arrow ui", () => { beforeEach(async () => { + localStorage.clear(); await render(); + + fireEvent.contextMenu(GlobalTestState.interactiveCanvas, { + button: 2, + clientX: 1, + clientY: 1, + }); + const contextMenu = UI.queryContextMenu(); + fireEvent.click(queryByTestId(contextMenu!, "stats")!); }); it("can follow bound shapes", async () => { @@ -130,8 +139,8 @@ describe("elbow arrow ui", () => { expect(arrow.elbowed).toBe(true); expect(arrow.points).toEqual([ [0, 0], - [35, 0], - [35, 200], + [45, 0], + [45, 200], [90, 200], ]); }); @@ -163,14 +172,6 @@ describe("elbow arrow ui", () => { h.state, )[0] as ExcalidrawArrowElement; - fireEvent.contextMenu(GlobalTestState.interactiveCanvas, { - button: 2, - clientX: 1, - clientY: 1, - }); - const contextMenu = UI.queryContextMenu(); - fireEvent.click(queryByTestId(contextMenu!, "stats")!); - mouse.click(51, 51); const inputAngle = UI.queryStatsProperty("A")?.querySelector( @@ -182,8 +183,8 @@ describe("elbow arrow ui", () => { [0, 0], [35, 0], [35, 90], - [25, 90], - [25, 165], + [35, 90], // Note that coordinates are rounded above! + [35, 165], [103, 165], ]); }); diff --git a/packages/excalidraw/element/routing.ts b/packages/excalidraw/element/routing.ts index 07f62ca82..4ac621a6e 100644 --- a/packages/excalidraw/element/routing.ts +++ b/packages/excalidraw/element/routing.ts @@ -235,6 +235,8 @@ export const mutateElbowArrow = ( BASE_PADDING, ), boundsOverlap, + hoveredStartElement && aabbForElement(hoveredStartElement), + hoveredEndElement && aabbForElement(hoveredEndElement), ); const startDonglePosition = getDonglePosition( dynamicAABBs[0], @@ -475,7 +477,11 @@ const generateDynamicAABBs = ( startDifference?: [number, number, number, number], endDifference?: [number, number, number, number], disableSideHack?: boolean, + startElementBounds?: Bounds | null, + endElementBounds?: Bounds | null, ): Bounds[] => { + const startEl = startElementBounds ?? a; + const endEl = endElementBounds ?? b; const [startUp, startRight, startDown, startLeft] = startDifference ?? [ 0, 0, 0, 0, ]; @@ -484,29 +490,29 @@ const generateDynamicAABBs = ( const first = [ a[0] > b[2] ? a[1] > b[3] || a[3] < b[1] - ? Math.min((a[0] + b[2]) / 2, a[0] - startLeft) - : (a[0] + b[2]) / 2 + ? Math.min((startEl[0] + endEl[2]) / 2, a[0] - startLeft) + : (startEl[0] + endEl[2]) / 2 : a[0] > b[0] ? a[0] - startLeft : common[0] - startLeft, a[1] > b[3] ? a[0] > b[2] || a[2] < b[0] - ? Math.min((a[1] + b[3]) / 2, a[1] - startUp) - : (a[1] + b[3]) / 2 + ? Math.min((startEl[1] + endEl[3]) / 2, a[1] - startUp) + : (startEl[1] + endEl[3]) / 2 : a[1] > b[1] ? a[1] - startUp : common[1] - startUp, a[2] < b[0] ? a[1] > b[3] || a[3] < b[1] - ? Math.max((a[2] + b[0]) / 2, a[2] + startRight) - : (a[2] + b[0]) / 2 + ? Math.max((startEl[2] + endEl[0]) / 2, a[2] + startRight) + : (startEl[2] + endEl[0]) / 2 : a[2] < b[2] ? a[2] + startRight : common[2] + startRight, a[3] < b[1] ? a[0] > b[2] || a[2] < b[0] - ? Math.max((a[3] + b[1]) / 2, a[3] + startDown) - : (a[3] + b[1]) / 2 + ? Math.max((startEl[3] + endEl[1]) / 2, a[3] + startDown) + : (startEl[3] + endEl[1]) / 2 : a[3] < b[3] ? a[3] + startDown : common[3] + startDown, @@ -514,29 +520,29 @@ const generateDynamicAABBs = ( const second = [ b[0] > a[2] ? b[1] > a[3] || b[3] < a[1] - ? Math.min((b[0] + a[2]) / 2, b[0] - endLeft) - : (b[0] + a[2]) / 2 + ? Math.min((endEl[0] + startEl[2]) / 2, b[0] - endLeft) + : (endEl[0] + startEl[2]) / 2 : b[0] > a[0] ? b[0] - endLeft : common[0] - endLeft, b[1] > a[3] ? b[0] > a[2] || b[2] < a[0] - ? Math.min((b[1] + a[3]) / 2, b[1] - endUp) - : (b[1] + a[3]) / 2 + ? Math.min((endEl[1] + startEl[3]) / 2, b[1] - endUp) + : (endEl[1] + startEl[3]) / 2 : b[1] > a[1] ? b[1] - endUp : common[1] - endUp, b[2] < a[0] ? b[1] > a[3] || b[3] < a[1] - ? Math.max((b[2] + a[0]) / 2, b[2] + endRight) - : (b[2] + a[0]) / 2 + ? Math.max((endEl[2] + startEl[0]) / 2, b[2] + endRight) + : (endEl[2] + startEl[0]) / 2 : b[2] < a[2] ? b[2] + endRight : common[2] + endRight, b[3] < a[1] ? b[0] > a[2] || b[2] < a[0] - ? Math.max((b[3] + a[1]) / 2, b[3] + endDown) - : (b[3] + a[1]) / 2 + ? Math.max((endEl[3] + startEl[1]) / 2, b[3] + endDown) + : (endEl[3] + startEl[1]) / 2 : b[3] < a[3] ? b[3] + endDown : common[3] + endDown, diff --git a/packages/excalidraw/visualdebug.ts b/packages/excalidraw/visualdebug.ts index 7181719f7..86f4d39a8 100644 --- a/packages/excalidraw/visualdebug.ts +++ b/packages/excalidraw/visualdebug.ts @@ -110,8 +110,8 @@ export const debugDrawBoundingBox = ( export const debugDrawBounds = ( box: Bounds | Bounds[], opts?: { - color: string; - permanent: boolean; + color?: string; + permanent?: boolean; }, ) => { (isBounds(box) ? [box] : box).forEach((bbox) => @@ -136,7 +136,7 @@ export const debugDrawBounds = ( ], { color: opts?.color ?? "green", - permanent: opts?.permanent, + permanent: !!opts?.permanent, }, ), );