diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/element/distance.ts | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/element/distance.ts')
| -rw-r--r-- | packages/excalidraw/element/distance.ts | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/packages/excalidraw/element/distance.ts b/packages/excalidraw/element/distance.ts new file mode 100644 index 0000000..0010ab9 --- /dev/null +++ b/packages/excalidraw/element/distance.ts @@ -0,0 +1,123 @@ +import type { GlobalPoint, Radians } from "@excalidraw/math"; +import { + curvePointDistance, + distanceToLineSegment, + pointFrom, + pointRotateRads, +} from "@excalidraw/math"; +import { ellipse, ellipseDistanceFromPoint } from "@excalidraw/math/ellipse"; +import type { + ExcalidrawBindableElement, + ExcalidrawDiamondElement, + ExcalidrawEllipseElement, + ExcalidrawRectanguloidElement, +} from "./types"; +import { + deconstructDiamondElement, + deconstructRectanguloidElement, +} from "./utils"; + +export const distanceToBindableElement = ( + element: ExcalidrawBindableElement, + p: GlobalPoint, +): number => { + switch (element.type) { + case "rectangle": + case "image": + case "text": + case "iframe": + case "embeddable": + case "frame": + case "magicframe": + return distanceToRectanguloidElement(element, p); + case "diamond": + return distanceToDiamondElement(element, p); + case "ellipse": + return distanceToEllipseElement(element, p); + } +}; + +/** + * Returns the distance of a point and the provided rectangular-shaped element, + * accounting for roundness and rotation + * + * @param element The rectanguloid element + * @param p The point to consider + * @returns The eucledian distance to the outline of the rectanguloid element + */ +const distanceToRectanguloidElement = ( + element: ExcalidrawRectanguloidElement, + p: GlobalPoint, +) => { + const center = pointFrom<GlobalPoint>( + element.x + element.width / 2, + element.y + element.height / 2, + ); + // To emulate a rotated rectangle we rotate the point in the inverse angle + // instead. It's all the same distance-wise. + const rotatedPoint = pointRotateRads(p, center, -element.angle as Radians); + + // Get the element's building components we can test against + const [sides, corners] = deconstructRectanguloidElement(element); + + return Math.min( + ...sides.map((s) => distanceToLineSegment(rotatedPoint, s)), + ...corners + .map((a) => curvePointDistance(a, rotatedPoint)) + .filter((d): d is number => d !== null), + ); +}; + +/** + * Returns the distance of a point and the provided diamond element, accounting + * for roundness and rotation + * + * @param element The diamond element + * @param p The point to consider + * @returns The eucledian distance to the outline of the diamond + */ +const distanceToDiamondElement = ( + element: ExcalidrawDiamondElement, + p: GlobalPoint, +): number => { + const center = pointFrom<GlobalPoint>( + element.x + element.width / 2, + element.y + element.height / 2, + ); + + // Rotate the point to the inverse direction to simulate the rotated diamond + // points. It's all the same distance-wise. + const rotatedPoint = pointRotateRads(p, center, -element.angle as Radians); + + const [sides, curves] = deconstructDiamondElement(element); + + return Math.min( + ...sides.map((s) => distanceToLineSegment(rotatedPoint, s)), + ...curves + .map((a) => curvePointDistance(a, rotatedPoint)) + .filter((d): d is number => d !== null), + ); +}; + +/** + * Returns the distance of a point and the provided ellipse element, accounting + * for roundness and rotation + * + * @param element The ellipse element + * @param p The point to consider + * @returns The eucledian distance to the outline of the ellipse + */ +const distanceToEllipseElement = ( + element: ExcalidrawEllipseElement, + p: GlobalPoint, +): number => { + const center = pointFrom( + element.x + element.width / 2, + element.y + element.height / 2, + ); + return ellipseDistanceFromPoint( + // Instead of rotating the ellipse, rotate the point to the inverse angle + pointRotateRads(p, center, -element.angle as Radians), + ellipse(center, element.width / 2, element.height / 2), + ); +}; |
