mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-19 04:05:19 +01:00
@@ -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)!,
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user