summaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/element/distance.ts
diff options
context:
space:
mode:
authorkj_sh6042026-03-15 16:19:35 -0400
committerkj_sh6042026-03-15 16:19:35 -0400
commit6ec259a0e71174651bae95d4628138bf6fd68742 (patch)
tree5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/element/distance.ts
parent16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff)
refactor: packages/
Diffstat (limited to 'packages/excalidraw/element/distance.ts')
-rw-r--r--packages/excalidraw/element/distance.ts123
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),
+ );
+};