Revert "fix: Arrow angle"

This reverts commit 7f0aa5854a.
This commit is contained in:
Mark Tolmacs
2025-11-17 19:20:18 +01:00
parent 7f0aa5854a
commit d46c38cb27
3 changed files with 63 additions and 100 deletions

View File

@@ -2243,36 +2243,44 @@ const pointDraggingUpdates = (
} }
// Simulate the updated arrow for the bind point calculation // Simulate the updated arrow for the bind point calculation
const updatedPoints = element.points.map((p, idx) => { const originalStartGlobalPoint =
const update = naiveDraggingPoints.get(idx); LinearElementEditor.getPointAtIndexGlobalCoordinates(
return update ? update.point : p; element,
}); 0,
elementsMap,
const offsetX = updatedPoints[0][0]; );
const offsetY = updatedPoints[0][1]; const offsetStartGlobalPoint = startIsDragged
const normalizedPoints = updatedPoints.map((p) => ? pointFrom<GlobalPoint>(
pointFrom<LocalPoint>(p[0] - offsetX, p[1] - offsetY), originalStartGlobalPoint[0] + deltaX,
); originalStartGlobalPoint[1] + deltaY,
)
const nextCoords = getElementPointsCoords(element, normalizedPoints); : originalStartGlobalPoint;
const prevCoords = getElementPointsCoords(element, element.points); const offsetStartLocalPoint = LinearElementEditor.pointFromAbsoluteCoords(
const nextCenterX = (nextCoords[0] + nextCoords[2]) / 2; element,
const nextCenterY = (nextCoords[1] + nextCoords[3]) / 2; offsetStartGlobalPoint,
const prevCenterX = (prevCoords[0] + prevCoords[2]) / 2; elementsMap,
const prevCenterY = (prevCoords[1] + prevCoords[3]) / 2;
const dX = prevCenterX - nextCenterX;
const dY = prevCenterY - nextCenterY;
const rotatedOffset = pointRotateRads(
pointFrom(offsetX, offsetY),
pointFrom(dX, dY),
element.angle,
); );
const offsetEndLocalPoint = endIsDragged
? pointFrom<LocalPoint>(
element.points[element.points.length - 1][0] + deltaX,
element.points[element.points.length - 1][1] + deltaY,
)
: element.points[element.points.length - 1];
const nextArrow = { const nextArrow = {
...element, ...element,
points: normalizedPoints, points: [
x: element.x + rotatedOffset[0], offsetStartLocalPoint,
y: element.y + rotatedOffset[1], ...element.points
.slice(1, -1)
.map((p) =>
pointFrom<LocalPoint>(
p[0] + element.x - offsetStartGlobalPoint[0],
p[1] + element.y - offsetStartGlobalPoint[1],
),
),
offsetEndLocalPoint,
],
startBinding: startBinding:
updates.startBinding === undefined updates.startBinding === undefined
? element.startBinding ? element.startBinding
@@ -2365,16 +2373,6 @@ const pointDraggingUpdates = (
) || nextArrow.points[0] ) || nextArrow.points[0]
: nextArrow.points[0]; : nextArrow.points[0];
const startOffset = pointFrom<LocalPoint>(offsetX, offsetY);
const startLocalPointAbsolute = pointFrom<LocalPoint>(
startLocalPoint[0] + startOffset[0],
startLocalPoint[1] + startOffset[1],
);
const endLocalPointAbsolute = pointFrom<LocalPoint>(
endLocalPoint[0] + startOffset[0],
endLocalPoint[1] + startOffset[1],
);
const endChanged = const endChanged =
pointDistance( pointDistance(
endLocalPoint, endLocalPoint,
@@ -2405,9 +2403,9 @@ const pointDraggingUpdates = (
return [ return [
idx, idx,
idx === 0 idx === 0
? { point: startLocalPointAbsolute, isDragging: true } ? { point: startLocalPoint, isDragging: true }
: idx === element.points.length - 1 : idx === element.points.length - 1
? { point: endLocalPointAbsolute, isDragging: true } ? { point: endLocalPoint, isDragging: true }
: naiveDraggingPoints.get(idx)!, : naiveDraggingPoints.get(idx)!,
]; ];
}), }),

View File

@@ -1,4 +1,4 @@
import { pointCenter, pointFrom, pointRotateRads } from "@excalidraw/math"; import { pointCenter, pointFrom } from "@excalidraw/math";
import { act, queryByTestId, queryByText } from "@testing-library/react"; import { act, queryByTestId, queryByText } from "@testing-library/react";
import { vi } from "vitest"; import { vi } from "vitest";
@@ -24,13 +24,12 @@ import {
unmountComponent, unmountComponent,
} from "@excalidraw/excalidraw/tests/test-utils"; } from "@excalidraw/excalidraw/tests/test-utils";
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math"; import type { GlobalPoint, LocalPoint } from "@excalidraw/math";
import { wrapText } from "../src"; import { wrapText } from "../src";
import * as textElementUtils from "../src/textElement"; import * as textElementUtils from "../src/textElement";
import { getBoundTextElementPosition, getBoundTextMaxWidth } from "../src"; import { getBoundTextElementPosition, getBoundTextMaxWidth } from "../src";
import { LinearElementEditor } from "../src"; import { LinearElementEditor } from "../src";
import { elementCenterPoint } from "../src/bounds";
import { newArrowElement } from "../src"; import { newArrowElement } from "../src";
import { import {
@@ -60,7 +59,7 @@ describe("Test Linear Elements", () => {
beforeEach(async () => { beforeEach(async () => {
unmountComponent(); unmountComponent();
//localStorage.clear(); localStorage.clear();
renderInteractiveScene.mockClear(); renderInteractiveScene.mockClear();
renderStaticScene.mockClear(); renderStaticScene.mockClear();
reseed(7); reseed(7);
@@ -955,62 +954,6 @@ describe("Test Linear Elements", () => {
] ]
`); `);
}); });
it("keeps rotated arrow start point aligned with pointer while dragging", () => {
const arrow = createThreePointerLinearElement("arrow");
const angle = 1.2;
h.app.scene.mutateElement(arrow, { angle: angle as Radians });
const elementsMap = h.app.scene.getNonDeletedElementsMap();
const center = elementCenterPoint(arrow, elementsMap);
const expectedStart = pointRotateRads(
pointFrom<GlobalPoint>(
arrow.x + arrow.points[0][0],
arrow.y + arrow.points[0][1],
),
center,
angle as Radians,
);
const actualStart = LinearElementEditor.getPointAtIndexGlobalCoordinates(
arrow,
0,
elementsMap,
);
const initialOffset = {
x: expectedStart[0] - actualStart[0],
y: expectedStart[1] - actualStart[1],
};
expect(Math.hypot(initialOffset.x, initialOffset.y)).toBeGreaterThan(0);
API.setSelectedElements([arrow]);
enterLineEditingMode(arrow, true);
const dragOffset = { x: 25, y: -15 };
const dragTarget = pointFrom<GlobalPoint>(
expectedStart[0] + dragOffset.x,
expectedStart[1] + dragOffset.y,
);
mouse.downAt(expectedStart[0], expectedStart[1]);
mouse.moveTo(dragTarget[0], dragTarget[1]);
mouse.upAt(dragTarget[0], dragTarget[1]);
const updatedMap = h.app.scene.getNonDeletedElementsMap();
const movedStart = LinearElementEditor.getPointAtIndexGlobalCoordinates(
arrow,
0,
updatedMap,
);
const finalOffset = {
x: dragTarget[0] - movedStart[0],
y: dragTarget[1] - movedStart[1],
};
expect(finalOffset.x).toBeCloseTo(initialOffset.x, 6);
expect(finalOffset.y).toBeCloseTo(initialOffset.y, 6);
});
}); });
describe("Test bound text element", () => { describe("Test bound text element", () => {

View File

@@ -393,14 +393,35 @@ export const restoreElement = (
...getSizeFromPoints(points), ...getSizeFromPoints(points),
}); });
case "arrow": { case "arrow": {
const { startArrowhead = null, endArrowhead = "arrow" } = element; const {
startArrowhead = null,
endArrowhead = "arrow",
angle = 0,
} = element;
const x: number | undefined = element.x; const x: number | undefined = element.x;
const y: number | undefined = element.y; const y: number | undefined = element.y;
const points: readonly LocalPoint[] | undefined = // migrate old arrow model to new one let points: readonly LocalPoint[] | undefined = // migrate old arrow model to new one
!Array.isArray(element.points) || element.points.length < 2 !Array.isArray(element.points) || element.points.length < 2
? [pointFrom(0, 0), pointFrom(element.width, element.height)] ? [pointFrom(0, 0), pointFrom(element.width, element.height)]
: element.points; : element.points;
if (angle !== 0) {
points = LinearElementEditor.getPointsGlobalCoordinates(
element,
elementsMap,
).map((point) =>
LinearElementEditor.pointFromAbsoluteCoords(
element as ExcalidrawArrowElement,
pointRotateRads(
point,
elementCenterPoint(element, elementsMap),
angle,
),
elementsMap,
),
);
}
const base = { const base = {
type: element.type, type: element.type,
startBinding: repairBinding( startBinding: repairBinding(
@@ -422,6 +443,7 @@ export const restoreElement = (
y, y,
elbowed: (element as ExcalidrawArrowElement).elbowed, elbowed: (element as ExcalidrawArrowElement).elbowed,
...getSizeFromPoints(points), ...getSizeFromPoints(points),
angle: 0 as Radians,
}; };
// TODO: Separate arrow from linear element // TODO: Separate arrow from linear element