From 0fe736cd3fbd10d0bb4f510ca897a87774983559 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Wed, 19 Mar 2025 13:49:28 +0100 Subject: [PATCH] Fix incorrect elbow arrow geometry --- packages/excalidraw/element/binding.ts | 21 +++++++----- packages/excalidraw/element/elbowArrow.ts | 13 ++++---- .../excalidraw/element/linearElementEditor.ts | 33 ++++++++++++++++--- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/packages/excalidraw/element/binding.ts b/packages/excalidraw/element/binding.ts index 38b7d8636..6799d1a6b 100644 --- a/packages/excalidraw/element/binding.ts +++ b/packages/excalidraw/element/binding.ts @@ -839,14 +839,19 @@ export const updateBoundElements = ( }> => update !== null, ); - LinearElementEditor.movePoints(element, updates, { - ...(changedElement.id === element.startBinding?.elementId - ? { startBinding: bindings.startBinding } - : {}), - ...(changedElement.id === element.endBinding?.elementId - ? { endBinding: bindings.endBinding } - : {}), - }); + LinearElementEditor.movePoints( + element, + updates, + { + ...(changedElement.id === element.startBinding?.elementId + ? { startBinding: bindings.startBinding } + : {}), + ...(changedElement.id === element.endBinding?.elementId + ? { endBinding: bindings.endBinding } + : {}), + }, + elementsMap as NonDeletedSceneElementsMap, + ); const boundText = getBoundTextElement(element, elementsMap); if (boundText && !boundText.isDeleted) { diff --git a/packages/excalidraw/element/elbowArrow.ts b/packages/excalidraw/element/elbowArrow.ts index 533d2c228..a8c22abcc 100644 --- a/packages/excalidraw/element/elbowArrow.ts +++ b/packages/excalidraw/element/elbowArrow.ts @@ -984,7 +984,7 @@ export const updateElbowArrowPoints = ( : updates.points.slice() : arrow.points.slice(); - // 0. During all element replacement in the scene, we just need to renormalize + // During all element replacement in the scene, we just need to renormalize // the arrow // TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed const { @@ -1005,11 +1005,12 @@ export const updateElbowArrowPoints = ( getBindableElementForId(startBinding.elementId, elementsMap); const endElement = endBinding && getBindableElementForId(endBinding.elementId, elementsMap); + const areUpdatedPointsValid = validateElbowPoints(updatedPoints); if ( - (startBinding && !startElement) || - (endBinding && !endElement) || - (elementsMap.size === 0 && validateElbowPoints(updatedPoints)) || + (startBinding && !startElement && areUpdatedPointsValid) || + (endBinding && !endElement && areUpdatedPointsValid) || + (elementsMap.size === 0 && areUpdatedPointsValid) || (Object.keys(restOfTheUpdates).length === 0 && (startElement?.id !== startBinding?.elementId || endElement?.id !== endBinding?.elementId)) @@ -1050,7 +1051,7 @@ export const updateElbowArrowPoints = ( // 0. During all element replacement in the scene, we just need to renormalize // the arrow // TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed - if (elementsMap.size === 0 && validateElbowPoints(updatedPoints)) { + if (elementsMap.size === 0 && areUpdatedPointsValid) { return normalizeArrowElementUpdate( updatedPoints.map((p) => pointFrom(arrow.x + p[0], arrow.y + p[1]), @@ -1083,7 +1084,7 @@ export const updateElbowArrowPoints = ( arrow.points[i] ?? pointFrom(Infinity, Infinity), ), ) && - validateElbowPoints(updatedPoints) + areUpdatedPointsValid ) { return {}; } diff --git a/packages/excalidraw/element/linearElementEditor.ts b/packages/excalidraw/element/linearElementEditor.ts index 5368ab3d0..7db35801f 100644 --- a/packages/excalidraw/element/linearElementEditor.ts +++ b/packages/excalidraw/element/linearElementEditor.ts @@ -25,11 +25,16 @@ import { import { getGridPoint } from "../snapping"; import { invariant, tupleToCoors } from "../utils"; +import Scene from "../scene/Scene"; + import { bindOrUnbindLinearElement, getHoveredElementForBinding, isBindingEnabled, } from "./binding"; + +import { updateElbowArrowPoints } from "./elbowArrow"; + import { getElementPointsCoords, getMinMaxXYFromCurvePathOps } from "./bounds"; import { headingIsHorizontal, vectorToHeading } from "./heading"; import { mutateElement } from "./mutateElement"; @@ -57,7 +62,6 @@ import type { FixedSegment, ExcalidrawElbowArrowElement, } from "./types"; -import type Scene from "../scene/Scene"; import type { Store } from "../store"; import type { AppState, @@ -67,6 +71,7 @@ import type { NullableGridSize, Zoom, } from "../types"; + import type { Mutable } from "../utility-types"; const editorMidPointsCache: { @@ -1274,6 +1279,7 @@ export class LinearElementEditor { startBinding?: PointBinding | null; endBinding?: PointBinding | null; }, + sceneElementsMap?: NonDeletedSceneElementsMap, ) { const { points } = element; @@ -1317,6 +1323,7 @@ export class LinearElementEditor { dragging || targetPoint.isDragging === true, false, ), + sceneElementsMap, }, ); } @@ -1430,6 +1437,7 @@ export class LinearElementEditor { options?: { isDragging?: boolean; zoom?: AppState["zoom"]; + sceneElementsMap?: NonDeletedSceneElementsMap; }, ) { if (isElbowArrow(element)) { @@ -1455,9 +1463,26 @@ export class LinearElementEditor { updates.points = Array.from(nextPoints); - mutateElement(element, updates, true, { - isDragging: options?.isDragging, - }); + if (!options?.sceneElementsMap || Scene.getScene(element)) { + mutateElement(element, updates, true, { + isDragging: options?.isDragging, + }); + } else { + // The element is not in the scene, so we need to use the provided + // scene map. + Object.assign(element, { + ...updates, + angle: 0 as Radians, + ...updateElbowArrowPoints( + element, + options.sceneElementsMap, + updates, + { + isDragging: options?.isDragging, + }, + ), + }); + } } else { const nextCoords = getElementPointsCoords(element, nextPoints); const prevCoords = getElementPointsCoords(element, element.points);