fix: Reimplement rectangle intersection (#8367)

This commit is contained in:
Márk Tolmács 2024-08-12 19:19:16 +02:00 committed by GitHub
parent 5daf1a1b4e
commit 8420e1aa13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 131 additions and 74 deletions

View file

@ -1,4 +1,13 @@
import { distance2d } from "../../excalidraw/math";
import type { ExcalidrawBindableElement } from "../../excalidraw/element/types";
import {
addVectors,
distance2d,
rotatePoint,
scaleVector,
subtractVectors,
} from "../../excalidraw/math";
import type { LineSegment } from "../bbox";
import { crossProduct } from "../bbox";
import type {
Point,
Line,
@ -968,3 +977,84 @@ export const pointInEllipse = (point: Point, ellipse: Ellipse) => {
1
);
};
/**
* Calculates the point two line segments with a definite start and end point
* intersect at.
*/
export const segmentsIntersectAt = (
a: Readonly<LineSegment>,
b: Readonly<LineSegment>,
): Point | null => {
const r = subtractVectors(a[1], a[0]);
const s = subtractVectors(b[1], b[0]);
const denominator = crossProduct(r, s);
if (denominator === 0) {
return null;
}
const i = subtractVectors(b[0], a[0]);
const u = crossProduct(i, r) / denominator;
const t = crossProduct(i, s) / denominator;
if (u === 0) {
return null;
}
const p = addVectors(a[0], scaleVector(r, t));
if (t >= 0 && t < 1 && u >= 0 && u < 1) {
return p;
}
return null;
};
/**
* Determine intersection of a rectangular shaped element and a
* line segment.
*
* @param element The rectangular element to test against
* @param segment The segment intersecting the element
* @param gap Optional value to inflate the shape before testing
* @returns An array of intersections
*/
// TODO: Replace with final rounded rectangle code
export const segmentIntersectRectangleElement = (
element: ExcalidrawBindableElement,
segment: LineSegment,
gap: number = 0,
): Point[] => {
const bounds = [
element.x - gap,
element.y - gap,
element.x + element.width + gap,
element.y + element.height + gap,
];
const center = [
(bounds[0] + bounds[2]) / 2,
(bounds[1] + bounds[3]) / 2,
] as Point;
return [
[
rotatePoint([bounds[0], bounds[1]], center, element.angle),
rotatePoint([bounds[2], bounds[1]], center, element.angle),
] as LineSegment,
[
rotatePoint([bounds[2], bounds[1]], center, element.angle),
rotatePoint([bounds[2], bounds[3]], center, element.angle),
] as LineSegment,
[
rotatePoint([bounds[2], bounds[3]], center, element.angle),
rotatePoint([bounds[0], bounds[3]], center, element.angle),
] as LineSegment,
[
rotatePoint([bounds[0], bounds[3]], center, element.angle),
rotatePoint([bounds[0], bounds[1]], center, element.angle),
] as LineSegment,
]
.map((s) => segmentsIntersectAt(segment, s))
.filter((i): i is Point => !!i);
};