From 702e02975535cfc1de278ddeb8d628396880c597 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Tue, 28 Apr 2026 16:14:19 +1000 Subject: [PATCH] refactor(linear): split point snapping helpers --- packages/element/src/linearElementEditor.ts | 257 +++++++++++++------- 1 file changed, 171 insertions(+), 86 deletions(-) diff --git a/packages/element/src/linearElementEditor.ts b/packages/element/src/linearElementEditor.ts index a5ff78acb9..92fef508cc 100644 --- a/packages/element/src/linearElementEditor.ts +++ b/packages/element/src/linearElementEditor.ts @@ -1884,6 +1884,150 @@ export class LinearElementEditor { : app.getEffectiveGridSize(); } + private static _getSnappedScenePointForLinearElement({ + app, + event, + elementsMap, + element, + pointIndex, + scenePoint, + selectedPointsIndices, + }: { + app: AppClassProperties; + event: PointerEvent | React.PointerEvent; + elementsMap: ElementsMap; + element: NonDeleted; + pointIndex: number; + scenePoint: GlobalPoint; + selectedPointsIndices?: readonly number[]; + }): { + point: GlobalPoint; + snapLines: SnapLine[]; + } { + const { snapOffset, snapLines } = snapLinearElementPoint( + app.scene.getNonDeletedElements(), + element, + pointIndex, + scenePoint, + app, + event, + elementsMap, + { + includeSelfPoints: true, + selectedPointsIndices, + }, + ); + + return { + point: pointFrom( + scenePoint[0] + snapOffset.x, + scenePoint[1] + snapOffset.y, + ), + snapLines, + }; + } + + private static _getShiftLockedPointForLinearElement({ + app, + event, + elementsMap, + element, + pointIndex, + scenePointerX, + scenePointerY, + gridSize, + referencePoint, + selectedPointsIndices, + customLineAngle, + }: { + app: AppClassProperties; + event: PointerEvent | React.PointerEvent; + elementsMap: ElementsMap; + element: NonDeleted; + pointIndex: number; + scenePointerX: number; + scenePointerY: number; + gridSize: NullableGridSize; + referencePoint: LocalPoint; + selectedPointsIndices?: readonly number[]; + customLineAngle?: number | null; + }): { + point: LocalPoint; + snapLines: SnapLine[]; + } { + const referencePointCoords = LinearElementEditor.getPointGlobalCoordinates( + element, + referencePoint, + elementsMap, + ); + const [gridX, gridY] = getGridPoint(scenePointerX, scenePointerY, gridSize); + + let { width: dxFromReference, height: dyFromReference } = + getLockedLinearCursorAlignSize( + referencePointCoords[0], + referencePointCoords[1], + gridX, + gridY, + customLineAngle ?? undefined, + ); + + const lockedScenePoint = pointFrom( + referencePointCoords[0] + dxFromReference, + referencePointCoords[1] + dyFromReference, + ); + let snapLines: SnapLine[] = []; + + if (!isElbowArrow(element)) { + const snappedScenePoint = + LinearElementEditor._getSnappedScenePointForLinearElement({ + app, + event, + elementsMap, + element, + pointIndex, + scenePoint: lockedScenePoint, + selectedPointsIndices, + }); + + snapLines = snappedScenePoint.snapLines; + + if (snapLines.length > 0) { + const result = snapToDiscreteAngle( + snapLines, + line( + lockedScenePoint, + pointFrom(referencePointCoords[0], referencePointCoords[1]), + ), + pointFrom(gridX, gridY), + referencePointCoords, + ); + + if (result.snapLines.length > 0) { + dxFromReference = result.dxFromReference; + dyFromReference = result.dyFromReference; + snapLines = result.snapLines; + } else { + dxFromReference = snappedScenePoint.point[0] - referencePointCoords[0]; + dyFromReference = snappedScenePoint.point[1] - referencePointCoords[1]; + } + } + } + + const [rotatedX, rotatedY] = pointRotateRads( + pointFrom(dxFromReference, dyFromReference), + pointFrom(0, 0), + -element.angle as Radians, + ); + + return { + point: pointFrom( + referencePoint[0] + rotatedX, + referencePoint[1] + rotatedY, + ), + snapLines, + }; + } + private static _getSnappedPointForLinearElement({ app, event, @@ -1919,101 +2063,42 @@ export class LinearElementEditor { ); if (referencePoint) { - const referencePointCoords = LinearElementEditor.getPointGlobalCoordinates( - element, - referencePoint, + return LinearElementEditor._getShiftLockedPointForLinearElement({ + app, + event, elementsMap, - ); - const [gridX, gridY] = getGridPoint(scenePointerX, scenePointerY, gridSize); - - let { width: dxFromReference, height: dyFromReference } = - getLockedLinearCursorAlignSize( - referencePointCoords[0], - referencePointCoords[1], - gridX, - gridY, - customLineAngle ?? undefined, - ); - - const effectiveGridX = referencePointCoords[0] + dxFromReference; - const effectiveGridY = referencePointCoords[1] + dyFromReference; - - let snapLines: SnapLine[] = []; - - if (!isElbowArrow(element)) { - const { snapOffset, snapLines: nextSnapLines } = snapLinearElementPoint( - app.scene.getNonDeletedElements(), - element, - pointIndex, - pointFrom(effectiveGridX, effectiveGridY), - app, - event, - elementsMap, - { - includeSelfPoints: true, - selectedPointsIndices, - }, - ); - - snapLines = nextSnapLines; - - if (nextSnapLines.length > 0) { - const result = snapToDiscreteAngle( - nextSnapLines, - line( - pointFrom(effectiveGridX, effectiveGridY), - pointFrom(referencePointCoords[0], referencePointCoords[1]), - ), - pointFrom(gridX, gridY), - referencePointCoords, - ); - - if (result.snapLines.length > 0) { - dxFromReference = result.dxFromReference; - dyFromReference = result.dyFromReference; - snapLines = result.snapLines; - } else { - dxFromReference = effectiveGridX + snapOffset.x - referencePointCoords[0]; - dyFromReference = effectiveGridY + snapOffset.y - referencePointCoords[1]; - } - } - } - - const [rotatedX, rotatedY] = pointRotateRads( - pointFrom(dxFromReference, dyFromReference), - pointFrom(0, 0), - -element.angle as Radians, - ); - - return { - point: pointFrom(referencePoint[0] + rotatedX, referencePoint[1] + rotatedY), - snapLines, - }; + element, + pointIndex, + scenePointerX, + scenePointerY, + gridSize, + referencePoint, + selectedPointsIndices, + customLineAngle, + }); } - const originalPointerX = scenePointerX - pointerOffset.x; - const originalPointerY = scenePointerY - pointerOffset.y; - - const { snapOffset, snapLines } = snapLinearElementPoint( - app.scene.getNonDeletedElements(), - element, - pointIndex, - pointFrom(originalPointerX, originalPointerY), - app, - event, - elementsMap, - { - includeSelfPoints: true, - selectedPointsIndices, - }, + const originalPointerPoint = pointFrom( + scenePointerX - pointerOffset.x, + scenePointerY - pointerOffset.y, ); + const { point: scenePoint, snapLines } = + LinearElementEditor._getSnappedScenePointForLinearElement({ + app, + event, + elementsMap, + element, + pointIndex, + scenePoint: originalPointerPoint, + selectedPointsIndices, + }); return { point: LinearElementEditor.createPointAt( element, elementsMap, - originalPointerX + snapOffset.x, - originalPointerY + snapOffset.y, + scenePoint[0], + scenePoint[1], gridSize, ), snapLines,