diff --git a/packages/excalidraw/element/elbowArrow.ts b/packages/excalidraw/element/elbowArrow.ts index c55d652c9..921e73518 100644 --- a/packages/excalidraw/element/elbowArrow.ts +++ b/packages/excalidraw/element/elbowArrow.ts @@ -244,6 +244,12 @@ const handleSegmentRenormalization = ( ); } + import.meta.env.DEV && + invariant( + validateElbowPoints(nextPoints), + "Invalid elbow points with fixed segments", + ); + return normalizeArrowElementUpdate( nextPoints, filteredNextFixedSegments, @@ -912,7 +918,11 @@ 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 && updates.points) { + if ( + elementsMap.size === 0 && + updates.points && + validateElbowPoints(updates.points) + ) { return normalizeArrowElementUpdate( updates.points.map((p) => pointFrom(arrow.x + p[0], arrow.y + p[1]), @@ -2123,3 +2133,16 @@ const getHoveredElements = ( const gridAddressesEqual = (a: GridAddress, b: GridAddress): boolean => a[0] === b[0] && a[1] === b[1]; + +const validateElbowPoints =

( + points: readonly P[], + tolerance: number = DEDUP_TRESHOLD, +) => + points + .slice(1) + .map( + (p, i) => + Math.abs(p[0] - points[i][0]) < tolerance || + Math.abs(p[1] - points[i][1]) < tolerance, + ) + .every(Boolean); diff --git a/packages/excalidraw/element/flowchart.ts b/packages/excalidraw/element/flowchart.ts index fad72560b..d2b220c45 100644 --- a/packages/excalidraw/element/flowchart.ts +++ b/packages/excalidraw/element/flowchart.ts @@ -10,13 +10,15 @@ import { import { bindLinearElement } from "./binding"; import { LinearElementEditor } from "./linearElementEditor"; import { newArrowElement, newElement } from "./newElement"; -import type { - ElementsMap, - ExcalidrawBindableElement, - ExcalidrawElement, - ExcalidrawFlowchartNodeElement, - NonDeletedSceneElementsMap, - OrderedExcalidrawElement, +import type { SceneElementsMap } from "./types"; +import { + type ElementsMap, + type ExcalidrawBindableElement, + type ExcalidrawElement, + type ExcalidrawFlowchartNodeElement, + type NonDeletedSceneElementsMap, + type Ordered, + type OrderedExcalidrawElement, } from "./types"; import { KEYS } from "../keys"; import type { AppState, PendingExcalidrawElements } from "../types"; @@ -28,9 +30,10 @@ import { isFrameElement, isFlowchartNodeElement, } from "./typeChecks"; -import { invariant } from "../utils"; +import { invariant, toBrandedType } from "../utils"; import { pointFrom, type LocalPoint } from "../../math"; import { aabbForElement } from "../shapes"; +import { updateElbowArrowPoints } from "./elbowArrow"; type LinkDirection = "up" | "right" | "down" | "left"; @@ -467,7 +470,23 @@ const createBindingArrow = ( }, ]); - return bindingArrow; + const update = updateElbowArrowPoints( + bindingArrow, + toBrandedType( + new Map([ + ...elementsMap.entries(), + [startBindingElement.id, startBindingElement], + [endBindingElement.id, endBindingElement], + [bindingArrow.id, bindingArrow], + ] as [string, Ordered][]), + ), + { points: bindingArrow.points }, + ); + + return { + ...bindingArrow, + ...update, + }; }; export class FlowChartNavigator {