Fixing tests

This commit is contained in:
Mark Tolmacs 2025-05-08 12:48:36 +02:00
parent 65cecf041a
commit 3b037a7d82
No known key found for this signature in database
5 changed files with 375 additions and 664 deletions

View File

@ -2465,7 +2465,7 @@ exports[`regression tests > can drag element that covers another element, while
"scrolledOutside": false, "scrolledOutside": false,
"searchMatches": null, "searchMatches": null,
"selectedElementIds": { "selectedElementIds": {
"id3": true, "id0": true,
}, },
"selectedElementsAreBeingDragged": false, "selectedElementsAreBeingDragged": false,
"selectedGroupIds": {}, "selectedGroupIds": {},
@ -2667,7 +2667,7 @@ exports[`regression tests > can drag element that covers another element, while
"delta": Delta { "delta": Delta {
"deleted": { "deleted": {
"selectedElementIds": { "selectedElementIds": {
"id3": true, "id0": true,
}, },
}, },
"inserted": { "inserted": {
@ -2681,7 +2681,7 @@ exports[`regression tests > can drag element that covers another element, while
"added": {}, "added": {},
"removed": {}, "removed": {},
"updated": { "updated": {
"id3": { "id0": {
"deleted": { "deleted": {
"x": 300, "x": 300,
"y": 300, "y": 300,

View File

@ -304,12 +304,12 @@ describe("contextMenu element", () => {
it("selecting 'Copy styles' in context menu copies styles", () => { it("selecting 'Copy styles' in context menu copies styles", () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
mouse.down(10, 10); mouse.down(13, 10);
mouse.up(20, 20); mouse.up(20, 20);
fireEvent.contextMenu(GlobalTestState.interactiveCanvas, { fireEvent.contextMenu(GlobalTestState.interactiveCanvas, {
button: 2, button: 2,
clientX: 3, clientX: 13,
clientY: 3, clientY: 3,
}); });
const contextMenu = UI.queryContextMenu(); const contextMenu = UI.queryContextMenu();
@ -389,12 +389,12 @@ describe("contextMenu element", () => {
it("selecting 'Delete' in context menu deletes element", () => { it("selecting 'Delete' in context menu deletes element", () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
mouse.down(10, 10); mouse.down(13, 10);
mouse.up(20, 20); mouse.up(20, 20);
fireEvent.contextMenu(GlobalTestState.interactiveCanvas, { fireEvent.contextMenu(GlobalTestState.interactiveCanvas, {
button: 2, button: 2,
clientX: 3, clientX: 13,
clientY: 3, clientY: 3,
}); });
const contextMenu = UI.queryContextMenu(); const contextMenu = UI.queryContextMenu();
@ -405,12 +405,12 @@ describe("contextMenu element", () => {
it("selecting 'Add to library' in context menu adds element to library", async () => { it("selecting 'Add to library' in context menu adds element to library", async () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
mouse.down(10, 10); mouse.down(13, 10);
mouse.up(20, 20); mouse.up(20, 20);
fireEvent.contextMenu(GlobalTestState.interactiveCanvas, { fireEvent.contextMenu(GlobalTestState.interactiveCanvas, {
button: 2, button: 2,
clientX: 3, clientX: 13,
clientY: 3, clientY: 3,
}); });
const contextMenu = UI.queryContextMenu(); const contextMenu = UI.queryContextMenu();
@ -424,12 +424,12 @@ describe("contextMenu element", () => {
it("selecting 'Duplicate' in context menu duplicates element", () => { it("selecting 'Duplicate' in context menu duplicates element", () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
mouse.down(10, 10); mouse.down(13, 10);
mouse.up(20, 20); mouse.up(20, 20);
fireEvent.contextMenu(GlobalTestState.interactiveCanvas, { fireEvent.contextMenu(GlobalTestState.interactiveCanvas, {
button: 2, button: 2,
clientX: 3, clientX: 13,
clientY: 3, clientY: 3,
}); });
const contextMenu = UI.queryContextMenu(); const contextMenu = UI.queryContextMenu();

View File

@ -704,7 +704,7 @@ describe("regression tests", () => {
// pointer down on rectangle // pointer down on rectangle
mouse.reset(); mouse.reset();
mouse.down(100, 100); mouse.down(110, 100); // Rectangle is rounded, there is no selection at the corner
mouse.up(200, 200); mouse.up(200, 200);
expect(API.getSelectedElement().type).toBe("rectangle"); expect(API.getSelectedElement().type).toBe("rectangle");
@ -989,6 +989,7 @@ describe("regression tests", () => {
// select rectangle // select rectangle
mouse.reset(); mouse.reset();
mouse.moveTo(30, 0); // Rectangle is rounded, there is no selection at the corner
mouse.click(); mouse.click();
// click on intersection between ellipse and rectangle // click on intersection between ellipse and rectangle
@ -1155,6 +1156,7 @@ it(
// Select first rectangle while keeping third one selected. // Select first rectangle while keeping third one selected.
// Third rectangle is selected because it was the last element to be created. // Third rectangle is selected because it was the last element to be created.
mouse.reset(); mouse.reset();
mouse.moveTo(30, 0);
Keyboard.withModifierKeys({ shift: true }, () => { Keyboard.withModifierKeys({ shift: true }, () => {
mouse.click(); mouse.click();
}); });
@ -1176,6 +1178,7 @@ it(
// Pointer down o first rectangle that is part of the group // Pointer down o first rectangle that is part of the group
mouse.reset(); mouse.reset();
mouse.moveTo(30, 0);
Keyboard.withModifierKeys({ shift: true }, () => { Keyboard.withModifierKeys({ shift: true }, () => {
mouse.down(); mouse.down();
}); });

View File

@ -6,6 +6,8 @@ import {
type GlobalPoint, type GlobalPoint,
type LocalPoint, type LocalPoint,
type Polygon, type Polygon,
vectorCross,
vectorFromPoint,
} from "@excalidraw/math"; } from "@excalidraw/math";
import { intersectElementWithLineSegment } from "@excalidraw/element/collision"; import { intersectElementWithLineSegment } from "@excalidraw/element/collision";
@ -40,10 +42,19 @@ export const isPointInShape = (
point: GlobalPoint, point: GlobalPoint,
element: ExcalidrawElement, element: ExcalidrawElement,
) => { ) => {
if ( if (isLinearElement(element) || isFreeDrawElement(element)) {
(isLinearElement(element) || isFreeDrawElement(element)) && if (isPathALoop(element.points)) {
!isPathALoop(element.points) // for a closed path, we need to check if the point is inside the path
) { const r = isPointInClosedPath(
element.points.map((p) =>
pointFrom<GlobalPoint>(element.x + p[0], element.y + p[1]),
),
point,
);
//console.log(r);
return r;
}
// There isn't any "inside" for a non-looping path // There isn't any "inside" for a non-looping path
return false; return false;
} }
@ -56,6 +67,36 @@ export const isPointInShape = (
return intersections.length === 0; return intersections.length === 0;
}; };
/**
* Determine if a closed path contains a point.
*
* Implementation notes: We'll use the fact that the path is a consecutive
* sequence of line segments, these line segments have a winding order and
* the fact that if a point is inside the closed path, the cross product of the
* start point of a line segment to the point p and the end point of the line
* segment will be negative for all segments.
*
* @param points
* @param p
*/
const isPointInClosedPath = (
points: readonly GlobalPoint[],
p: GlobalPoint,
) => {
const segments = points.slice(1).map((point, i) => {
return lineSegment(points[i], point);
});
return segments.every((segment) => {
const c = vectorCross(
vectorFromPoint(segment[0], p),
vectorFromPoint(segment[0], segment[1]),
);
return c < 0;
});
};
// check if the given element is in the given bounds // check if the given element is in the given bounds
export const isPointInBounds = <Point extends GlobalPoint | LocalPoint>( export const isPointInBounds = <Point extends GlobalPoint | LocalPoint>(
point: Point, point: Point,