feat: Don't add midpoint until dragged beyond a threshold (#5927)

* Don't add midpoint until dragged beyond a threshold

* remove unnecessary code

* fix tests

* fix

* add spec

* remove isMidpoint

* cleanup

* fix threshold for zoom

* split into shouldAddMidpoint and addMidpoint

* wrap in flushSync for synchronous updates

* remove threshold for line editor and add spec

* [unrelated] fix stack overflow state update

* fix tests

* don't drag arrow when dragging to add mid point

* add specs

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Aakansha Doshi 2022-11-29 00:01:53 +05:30 committed by GitHub
parent baf9651d34
commit 25c6056b03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 258 additions and 63 deletions

View File

@ -2885,7 +2885,10 @@ class App extends React.Component<AppProps, AppState> {
if (editingLinearElement?.lastUncommittedPoint != null) { if (editingLinearElement?.lastUncommittedPoint != null) {
this.maybeSuggestBindingAtCursor(scenePointer); this.maybeSuggestBindingAtCursor(scenePointer);
} else { } else {
// causes stack overflow if not sync
flushSync(() => {
this.setState({ suggestedBindings: [] }); this.setState({ suggestedBindings: [] });
});
} }
} }
@ -3825,7 +3828,7 @@ class App extends React.Component<AppProps, AppState> {
this.setState({ editingLinearElement: ret.linearElementEditor }); this.setState({ editingLinearElement: ret.linearElementEditor });
} }
} }
if (ret.didAddPoint && !ret.isMidPoint) { if (ret.didAddPoint) {
return true; return true;
} }
} }
@ -4315,7 +4318,6 @@ class App extends React.Component<AppProps, AppState> {
// to ensure we don't create a 2-point arrow by mistake when // to ensure we don't create a 2-point arrow by mistake when
// user clicks mouse in a way that it moves a tiny bit (thus // user clicks mouse in a way that it moves a tiny bit (thus
// triggering pointermove) // triggering pointermove)
if ( if (
!pointerDownState.drag.hasOccurred && !pointerDownState.drag.hasOccurred &&
(this.state.activeTool.type === "arrow" || (this.state.activeTool.type === "arrow" ||
@ -4343,6 +4345,56 @@ class App extends React.Component<AppProps, AppState> {
if (this.state.selectedLinearElement) { if (this.state.selectedLinearElement) {
const linearElementEditor = const linearElementEditor =
this.state.editingLinearElement || this.state.selectedLinearElement; this.state.editingLinearElement || this.state.selectedLinearElement;
if (
LinearElementEditor.shouldAddMidpoint(
this.state.selectedLinearElement,
pointerCoords,
this.state,
)
) {
const ret = LinearElementEditor.addMidpoint(
this.state.selectedLinearElement,
pointerCoords,
this.state,
);
if (!ret) {
return;
}
// Since we are reading from previous state which is not possible with
// automatic batching in React 18 hence using flush sync to synchronously
// update the state. Check https://github.com/excalidraw/excalidraw/pull/5508 for more details.
flushSync(() => {
if (this.state.selectedLinearElement) {
this.setState({
selectedLinearElement: {
...this.state.selectedLinearElement,
pointerDownState: ret.pointerDownState,
selectedPointsIndices: ret.selectedPointsIndices,
},
});
}
if (this.state.editingLinearElement) {
this.setState({
editingLinearElement: {
...this.state.editingLinearElement,
pointerDownState: ret.pointerDownState,
selectedPointsIndices: ret.selectedPointsIndices,
},
});
}
});
return;
} else if (
linearElementEditor.pointerDownState.segmentMidpoint.value !== null &&
!linearElementEditor.pointerDownState.segmentMidpoint.added
) {
return;
}
const didDrag = LinearElementEditor.handlePointDragging( const didDrag = LinearElementEditor.handlePointDragging(
event, event,
this.state, this.state,

View File

@ -20,7 +20,7 @@ import {
} from "../math"; } from "../math";
import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from "."; import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from ".";
import { getElementPointsCoords } from "./bounds"; import { getElementPointsCoords } from "./bounds";
import { Point, AppState } from "../types"; import { Point, AppState, PointerCoords } from "../types";
import { mutateElement } from "./mutateElement"; import { mutateElement } from "./mutateElement";
import History from "../history"; import History from "../history";
@ -33,6 +33,7 @@ import {
import { tupleToCoors } from "../utils"; import { tupleToCoors } from "../utils";
import { isBindingElement } from "./typeChecks"; import { isBindingElement } from "./typeChecks";
import { shouldRotateWithDiscreteAngle } from "../keys"; import { shouldRotateWithDiscreteAngle } from "../keys";
import { DRAGGING_THRESHOLD } from "../constants";
const editorMidPointsCache: { const editorMidPointsCache: {
version: number | null; version: number | null;
@ -51,6 +52,12 @@ export class LinearElementEditor {
prevSelectedPointsIndices: readonly number[] | null; prevSelectedPointsIndices: readonly number[] | null;
/** index */ /** index */
lastClickedPoint: number; lastClickedPoint: number;
origin: Readonly<{ x: number; y: number }> | null;
segmentMidpoint: {
value: Point | null;
index: number | null;
added: boolean;
};
}>; }>;
/** whether you're dragging a point */ /** whether you're dragging a point */
@ -81,6 +88,13 @@ export class LinearElementEditor {
this.pointerDownState = { this.pointerDownState = {
prevSelectedPointsIndices: null, prevSelectedPointsIndices: null,
lastClickedPoint: -1, lastClickedPoint: -1,
origin: null,
segmentMidpoint: {
value: null,
index: null,
added: false,
},
}; };
this.hoverPointIndex = -1; this.hoverPointIndex = -1;
this.segmentMidPointHoveredCoords = null; this.segmentMidPointHoveredCoords = null;
@ -180,6 +194,7 @@ export class LinearElementEditor {
const draggingPoint = element.points[ const draggingPoint = element.points[
linearElementEditor.pointerDownState.lastClickedPoint linearElementEditor.pointerDownState.lastClickedPoint
] as [number, number] | undefined; ] as [number, number] | undefined;
if (selectedPointsIndices && draggingPoint) { if (selectedPointsIndices && draggingPoint) {
if ( if (
shouldRotateWithDiscreteAngle(event) && shouldRotateWithDiscreteAngle(event) &&
@ -551,7 +566,7 @@ export class LinearElementEditor {
} }
const midPoints = LinearElementEditor.getEditorMidPoints(element, appState); const midPoints = LinearElementEditor.getEditorMidPoints(element, appState);
let index = 0; let index = 0;
while (index < midPoints.length - 1) { while (index < midPoints.length) {
if (LinearElementEditor.arePointsEqual(midPoint, midPoints[index])) { if (LinearElementEditor.arePointsEqual(midPoint, midPoints[index])) {
return index + 1; return index + 1;
} }
@ -570,13 +585,11 @@ export class LinearElementEditor {
didAddPoint: boolean; didAddPoint: boolean;
hitElement: NonDeleted<ExcalidrawElement> | null; hitElement: NonDeleted<ExcalidrawElement> | null;
linearElementEditor: LinearElementEditor | null; linearElementEditor: LinearElementEditor | null;
isMidPoint: boolean;
} { } {
const ret: ReturnType<typeof LinearElementEditor["handlePointerDown"]> = { const ret: ReturnType<typeof LinearElementEditor["handlePointerDown"]> = {
didAddPoint: false, didAddPoint: false,
hitElement: null, hitElement: null,
linearElementEditor: null, linearElementEditor: null,
isMidPoint: false,
}; };
if (!linearElementEditor) { if (!linearElementEditor) {
@ -589,43 +602,18 @@ export class LinearElementEditor {
if (!element) { if (!element) {
return ret; return ret;
} }
const segmentMidPoint = LinearElementEditor.getSegmentMidpointHitCoords( const segmentMidpoint = LinearElementEditor.getSegmentMidpointHitCoords(
linearElementEditor, linearElementEditor,
scenePointer, scenePointer,
appState, appState,
); );
if (segmentMidPoint) { let segmentMidpointIndex = null;
const index = LinearElementEditor.getSegmentMidPointIndex( if (segmentMidpoint) {
segmentMidpointIndex = LinearElementEditor.getSegmentMidPointIndex(
linearElementEditor, linearElementEditor,
appState, appState,
segmentMidPoint, segmentMidpoint,
); );
const newMidPoint = LinearElementEditor.createPointAt(
element,
segmentMidPoint[0],
segmentMidPoint[1],
appState.gridSize,
);
const points = [
...element.points.slice(0, index),
newMidPoint,
...element.points.slice(index),
];
mutateElement(element, {
points,
});
ret.didAddPoint = true;
ret.isMidPoint = true;
ret.linearElementEditor = {
...linearElementEditor,
selectedPointsIndices: element.points[1],
pointerDownState: {
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
lastClickedPoint: -1,
},
lastUncommittedPoint: null,
};
} }
if (event.altKey && appState.editingLinearElement) { if (event.altKey && appState.editingLinearElement) {
if (linearElementEditor.lastUncommittedPoint == null) { if (linearElementEditor.lastUncommittedPoint == null) {
@ -648,6 +636,12 @@ export class LinearElementEditor {
pointerDownState: { pointerDownState: {
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices, prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
lastClickedPoint: -1, lastClickedPoint: -1,
origin: { x: scenePointer.x, y: scenePointer.y },
segmentMidpoint: {
value: segmentMidpoint,
index: segmentMidpointIndex,
added: false,
},
}, },
selectedPointsIndices: [element.points.length - 1], selectedPointsIndices: [element.points.length - 1],
lastUncommittedPoint: null, lastUncommittedPoint: null,
@ -670,7 +664,7 @@ export class LinearElementEditor {
// if we clicked on a point, set the element as hitElement otherwise // if we clicked on a point, set the element as hitElement otherwise
// it would get deselected if the point is outside the hitbox area // it would get deselected if the point is outside the hitbox area
if (clickedPointIndex >= 0 || segmentMidPoint) { if (clickedPointIndex >= 0 || segmentMidpoint) {
ret.hitElement = element; ret.hitElement = element;
} else { } else {
// You might be wandering why we are storing the binding elements on // You might be wandering why we are storing the binding elements on
@ -716,6 +710,12 @@ export class LinearElementEditor {
pointerDownState: { pointerDownState: {
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices, prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
lastClickedPoint: clickedPointIndex, lastClickedPoint: clickedPointIndex,
origin: { x: scenePointer.x, y: scenePointer.y },
segmentMidpoint: {
value: segmentMidpoint,
index: segmentMidpointIndex,
added: false,
},
}, },
selectedPointsIndices: nextSelectedPointsIndices, selectedPointsIndices: nextSelectedPointsIndices,
pointerOffset: targetPoint pointerOffset: targetPoint
@ -1111,6 +1111,94 @@ export class LinearElementEditor {
); );
} }
static shouldAddMidpoint(
linearElementEditor: LinearElementEditor,
pointerCoords: PointerCoords,
appState: AppState,
) {
const element = LinearElementEditor.getElement(
linearElementEditor.elementId,
);
if (!element) {
return false;
}
const { segmentMidpoint } = linearElementEditor.pointerDownState;
if (
segmentMidpoint.added ||
segmentMidpoint.value === null ||
segmentMidpoint.index === null ||
linearElementEditor.pointerDownState.origin === null
) {
return false;
}
const origin = linearElementEditor.pointerDownState.origin!;
const dist = distance2d(
origin.x,
origin.y,
pointerCoords.x,
pointerCoords.y,
);
if (
!appState.editingLinearElement &&
dist < DRAGGING_THRESHOLD / appState.zoom.value
) {
return false;
}
return true;
}
static addMidpoint(
linearElementEditor: LinearElementEditor,
pointerCoords: PointerCoords,
appState: AppState,
) {
const element = LinearElementEditor.getElement(
linearElementEditor.elementId,
);
if (!element) {
return;
}
const { segmentMidpoint } = linearElementEditor.pointerDownState;
const ret: {
pointerDownState: LinearElementEditor["pointerDownState"];
selectedPointsIndices: LinearElementEditor["selectedPointsIndices"];
} = {
pointerDownState: linearElementEditor.pointerDownState,
selectedPointsIndices: linearElementEditor.selectedPointsIndices,
};
const midpoint = LinearElementEditor.createPointAt(
element,
pointerCoords.x,
pointerCoords.y,
appState.gridSize,
);
const points = [
...element.points.slice(0, segmentMidpoint.index!),
midpoint,
...element.points.slice(segmentMidpoint.index!),
];
mutateElement(element, {
points,
});
ret.pointerDownState = {
...linearElementEditor.pointerDownState,
segmentMidpoint: {
...linearElementEditor.pointerDownState.segmentMidpoint,
added: true,
},
lastClickedPoint: segmentMidpoint.index!,
};
ret.selectedPointsIndices = [segmentMidpoint.index!];
return ret;
}
private static _updatePoints( private static _updatePoints(
element: NonDeleted<ExcalidrawLinearElement>, element: NonDeleted<ExcalidrawLinearElement>,
nextPoints: readonly Point[], nextPoints: readonly Point[],

View File

@ -10989,7 +10989,13 @@ Object {
"lastUncommittedPoint": null, "lastUncommittedPoint": null,
"pointerDownState": Object { "pointerDownState": Object {
"lastClickedPoint": -1, "lastClickedPoint": -1,
"origin": null,
"prevSelectedPointsIndices": null, "prevSelectedPointsIndices": null,
"segmentMidpoint": Object {
"added": false,
"index": null,
"value": null,
},
}, },
"pointerOffset": Object { "pointerOffset": Object {
"x": 0, "x": 0,
@ -11216,7 +11222,13 @@ Object {
"lastUncommittedPoint": null, "lastUncommittedPoint": null,
"pointerDownState": Object { "pointerDownState": Object {
"lastClickedPoint": -1, "lastClickedPoint": -1,
"origin": null,
"prevSelectedPointsIndices": null, "prevSelectedPointsIndices": null,
"segmentMidpoint": Object {
"added": false,
"index": null,
"value": null,
},
}, },
"pointerOffset": Object { "pointerOffset": Object {
"x": 0, "x": 0,
@ -11671,7 +11683,13 @@ Object {
"lastUncommittedPoint": null, "lastUncommittedPoint": null,
"pointerDownState": Object { "pointerDownState": Object {
"lastClickedPoint": -1, "lastClickedPoint": -1,
"origin": null,
"prevSelectedPointsIndices": null, "prevSelectedPointsIndices": null,
"segmentMidpoint": Object {
"added": false,
"index": null,
"value": null,
},
}, },
"pointerOffset": Object { "pointerOffset": Object {
"x": 0, "x": 0,
@ -12078,7 +12096,13 @@ Object {
"lastUncommittedPoint": null, "lastUncommittedPoint": null,
"pointerDownState": Object { "pointerDownState": Object {
"lastClickedPoint": -1, "lastClickedPoint": -1,
"origin": null,
"prevSelectedPointsIndices": null, "prevSelectedPointsIndices": null,
"segmentMidpoint": Object {
"added": false,
"index": null,
"value": null,
},
}, },
"pointerOffset": Object { "pointerOffset": Object {
"x": 0, "x": 0,

View File

@ -21,10 +21,6 @@ Object {
0, 0,
0, 0,
], ],
Array [
15,
25,
],
Array [ Array [
30, 30,
50, 50,
@ -40,8 +36,8 @@ Object {
"strokeWidth": 1, "strokeWidth": 1,
"type": "arrow", "type": "arrow",
"updated": 1, "updated": 1,
"version": 4, "version": 3,
"versionNonce": 453191, "versionNonce": 449462985,
"width": 30, "width": 30,
"x": 10, "x": 10,
"y": 10, "y": 10,
@ -69,10 +65,6 @@ Object {
0, 0,
0, 0,
], ],
Array [
15,
25,
],
Array [ Array [
30, 30,
50, 50,
@ -88,8 +80,8 @@ Object {
"strokeWidth": 1, "strokeWidth": 1,
"type": "line", "type": "line",
"updated": 1, "updated": 1,
"version": 4, "version": 3,
"versionNonce": 453191, "versionNonce": 449462985,
"width": 30, "width": 30,
"x": 10, "x": 10,
"y": 10, "y": 10,

View File

@ -129,6 +129,28 @@ describe("Test Linear Elements", () => {
Keyboard.keyPress(KEYS.DELETE); Keyboard.keyPress(KEYS.DELETE);
}; };
it("should not drag line and add midpoint until dragged beyond a threshold", () => {
createTwoPointerLinearElement("line");
const line = h.elements[0] as ExcalidrawLinearElement;
const originalX = line.x;
const originalY = line.y;
expect(line.points.length).toEqual(2);
mouse.clickAt(midpoint[0], midpoint[1]);
drag(midpoint, [midpoint[0] + 1, midpoint[1] + 1]);
expect(line.points.length).toEqual(2);
expect(line.x).toBe(originalX);
expect(line.y).toBe(originalY);
expect(line.points.length).toEqual(2);
drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]);
expect(line.x).toBe(originalX);
expect(line.y).toBe(originalY);
expect(line.points.length).toEqual(3);
});
it("should allow dragging line from midpoint in 2 pointer lines outside editor", async () => { it("should allow dragging line from midpoint in 2 pointer lines outside editor", async () => {
createTwoPointerLinearElement("line"); createTwoPointerLinearElement("line");
const line = h.elements[0] as ExcalidrawLinearElement; const line = h.elements[0] as ExcalidrawLinearElement;
@ -138,7 +160,7 @@ describe("Test Linear Elements", () => {
// drag line from midpoint // drag line from midpoint
drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]); drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]);
expect(renderScene).toHaveBeenCalledTimes(10); expect(renderScene).toHaveBeenCalledTimes(11);
expect(line.points.length).toEqual(3); expect(line.points.length).toEqual(3);
expect(line.points).toMatchInlineSnapshot(` expect(line.points).toMatchInlineSnapshot(`
Array [ Array [
@ -195,6 +217,24 @@ describe("Test Linear Elements", () => {
}); });
describe("Inside editor", () => { describe("Inside editor", () => {
it("should not drag line and add midpoint when dragged irrespective of threshold", () => {
createTwoPointerLinearElement("line");
const line = h.elements[0] as ExcalidrawLinearElement;
const originalX = line.x;
const originalY = line.y;
enterLineEditingMode(line);
expect(line.points.length).toEqual(2);
mouse.clickAt(midpoint[0], midpoint[1]);
expect(line.points.length).toEqual(2);
drag(midpoint, [midpoint[0] + 1, midpoint[1] + 1]);
expect(line.x).toBe(originalX);
expect(line.y).toBe(originalY);
expect(line.points.length).toEqual(3);
});
it("should allow dragging line from midpoint in 2 pointer lines", async () => { it("should allow dragging line from midpoint in 2 pointer lines", async () => {
createTwoPointerLinearElement("line"); createTwoPointerLinearElement("line");
@ -203,7 +243,7 @@ describe("Test Linear Elements", () => {
// drag line from midpoint // drag line from midpoint
drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]); drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]);
expect(renderScene).toHaveBeenCalledTimes(14); expect(renderScene).toHaveBeenCalledTimes(15);
expect(line.points.length).toEqual(3); expect(line.points.length).toEqual(3);
expect(line.points).toMatchInlineSnapshot(` expect(line.points).toMatchInlineSnapshot(`
@ -282,7 +322,7 @@ describe("Test Linear Elements", () => {
// Move the element // Move the element
drag(startPoint, endPoint); drag(startPoint, endPoint);
expect(renderScene).toHaveBeenCalledTimes(15); expect(renderScene).toHaveBeenCalledTimes(16);
expect([line.x, line.y]).toEqual([ expect([line.x, line.y]).toEqual([
points[0][0] + deltaX, points[0][0] + deltaX,
points[0][1] + deltaY, points[0][1] + deltaY,
@ -339,7 +379,7 @@ describe("Test Linear Elements", () => {
lastSegmentMidpoint[1] + delta, lastSegmentMidpoint[1] + delta,
]); ]);
expect(renderScene).toHaveBeenCalledTimes(19); expect(renderScene).toHaveBeenCalledTimes(21);
expect(line.points.length).toEqual(5); expect(line.points.length).toEqual(5);
expect((h.elements[0] as ExcalidrawLinearElement).points) expect((h.elements[0] as ExcalidrawLinearElement).points)
@ -359,7 +399,7 @@ describe("Test Linear Elements", () => {
], ],
Array [ Array [
105, 105,
75, 70,
], ],
Array [ Array [
40, 40,
@ -378,7 +418,7 @@ describe("Test Linear Elements", () => {
// Drag from first point // Drag from first point
drag(hitCoords, [hitCoords[0] - delta, hitCoords[1] - delta]); drag(hitCoords, [hitCoords[0] - delta, hitCoords[1] - delta]);
expect(renderScene).toHaveBeenCalledTimes(15); expect(renderScene).toHaveBeenCalledTimes(16);
const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line); const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
expect([newPoints[0][0], newPoints[0][1]]).toEqual([ expect([newPoints[0][0], newPoints[0][1]]).toEqual([
@ -404,7 +444,7 @@ describe("Test Linear Elements", () => {
// Drag from first point // Drag from first point
drag(hitCoords, [hitCoords[0] + delta, hitCoords[1] + delta]); drag(hitCoords, [hitCoords[0] + delta, hitCoords[1] + delta]);
expect(renderScene).toHaveBeenCalledTimes(15); expect(renderScene).toHaveBeenCalledTimes(16);
const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line); const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
expect([newPoints[0][0], newPoints[0][1]]).toEqual([ expect([newPoints[0][0], newPoints[0][1]]).toEqual([
@ -438,7 +478,7 @@ describe("Test Linear Elements", () => {
// delete 3rd point // delete 3rd point
deletePoint(points[2]); deletePoint(points[2]);
expect(line.points.length).toEqual(3); expect(line.points.length).toEqual(3);
expect(renderScene).toHaveBeenCalledTimes(20); expect(renderScene).toHaveBeenCalledTimes(21);
const newMidPoints = LinearElementEditor.getEditorMidPoints( const newMidPoints = LinearElementEditor.getEditorMidPoints(
line, line,
@ -483,7 +523,7 @@ describe("Test Linear Elements", () => {
lastSegmentMidpoint[0] + delta, lastSegmentMidpoint[0] + delta,
lastSegmentMidpoint[1] + delta, lastSegmentMidpoint[1] + delta,
]); ]);
expect(renderScene).toHaveBeenCalledTimes(19); expect(renderScene).toHaveBeenCalledTimes(21);
expect(line.points.length).toEqual(5); expect(line.points.length).toEqual(5);
@ -503,8 +543,8 @@ describe("Test Linear Elements", () => {
50, 50,
], ],
Array [ Array [
104.58050066266131, 106.08587175006699,
74.24758482724201, 73.29416593965323,
], ],
Array [ Array [
40, 40,
@ -559,7 +599,7 @@ describe("Test Linear Elements", () => {
// Drag from first point // Drag from first point
drag(hitCoords, [hitCoords[0] + delta, hitCoords[1] + delta]); drag(hitCoords, [hitCoords[0] + delta, hitCoords[1] + delta]);
expect(renderScene).toHaveBeenCalledTimes(15); expect(renderScene).toHaveBeenCalledTimes(16);
const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line); const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
expect([newPoints[0][0], newPoints[0][1]]).toEqual([ expect([newPoints[0][0], newPoints[0][1]]).toEqual([
@ -627,7 +667,6 @@ describe("Test Linear Elements", () => {
fillStyle: "solid", fillStyle: "solid",
}), }),
]; ];
const origPoints = line.points.map((point) => [...point]); const origPoints = line.points.map((point) => [...point]);
const dragEndPositionOffset = [100, 100] as const; const dragEndPositionOffset = [100, 100] as const;
API.setSelectedElements([line]); API.setSelectedElements([line]);