mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Extract element functions into modules (#207)
This commit is contained in:
parent
e3eef04e00
commit
01805f734d
13 changed files with 547 additions and 474 deletions
124
src/element/collision.ts
Normal file
124
src/element/collision.ts
Normal file
|
@ -0,0 +1,124 @@
|
|||
import { distanceBetweenPointAndSegment } from "../math";
|
||||
|
||||
import { ExcalidrawElement } from "./types";
|
||||
import {
|
||||
getElementAbsoluteX1,
|
||||
getElementAbsoluteX2,
|
||||
getElementAbsoluteY1,
|
||||
getElementAbsoluteY2,
|
||||
getArrowPoints,
|
||||
getDiamondPoints
|
||||
} from "./bounds";
|
||||
|
||||
export function hitTest(
|
||||
element: ExcalidrawElement,
|
||||
x: number,
|
||||
y: number
|
||||
): boolean {
|
||||
// For shapes that are composed of lines, we only enable point-selection when the distance
|
||||
// of the click is less than x pixels of any of the lines that the shape is composed of
|
||||
const lineThreshold = 10;
|
||||
|
||||
if (element.type === "ellipse") {
|
||||
// https://stackoverflow.com/a/46007540/232122
|
||||
const px = Math.abs(x - element.x - element.width / 2);
|
||||
const py = Math.abs(y - element.y - element.height / 2);
|
||||
|
||||
let tx = 0.707;
|
||||
let ty = 0.707;
|
||||
|
||||
const a = element.width / 2;
|
||||
const b = element.height / 2;
|
||||
|
||||
[0, 1, 2, 3].forEach(x => {
|
||||
const xx = a * tx;
|
||||
const yy = b * ty;
|
||||
|
||||
const ex = ((a * a - b * b) * tx ** 3) / a;
|
||||
const ey = ((b * b - a * a) * ty ** 3) / b;
|
||||
|
||||
const rx = xx - ex;
|
||||
const ry = yy - ey;
|
||||
|
||||
const qx = px - ex;
|
||||
const qy = py - ey;
|
||||
|
||||
const r = Math.hypot(ry, rx);
|
||||
const q = Math.hypot(qy, qx);
|
||||
|
||||
tx = Math.min(1, Math.max(0, ((qx * r) / q + ex) / a));
|
||||
ty = Math.min(1, Math.max(0, ((qy * r) / q + ey) / b));
|
||||
const t = Math.hypot(ty, tx);
|
||||
tx /= t;
|
||||
ty /= t;
|
||||
});
|
||||
|
||||
return Math.hypot(a * tx - px, b * ty - py) < lineThreshold;
|
||||
} else if (element.type === "rectangle") {
|
||||
const x1 = getElementAbsoluteX1(element);
|
||||
const x2 = getElementAbsoluteX2(element);
|
||||
const y1 = getElementAbsoluteY1(element);
|
||||
const y2 = getElementAbsoluteY2(element);
|
||||
|
||||
// (x1, y1) --A-- (x2, y1)
|
||||
// |D |B
|
||||
// (x1, y2) --C-- (x2, y2)
|
||||
return (
|
||||
distanceBetweenPointAndSegment(x, y, x1, y1, x2, y1) < lineThreshold || // A
|
||||
distanceBetweenPointAndSegment(x, y, x2, y1, x2, y2) < lineThreshold || // B
|
||||
distanceBetweenPointAndSegment(x, y, x2, y2, x1, y2) < lineThreshold || // C
|
||||
distanceBetweenPointAndSegment(x, y, x1, y2, x1, y1) < lineThreshold // D
|
||||
);
|
||||
} else if (element.type === "diamond") {
|
||||
x -= element.x;
|
||||
y -= element.y;
|
||||
|
||||
const [
|
||||
topX,
|
||||
topY,
|
||||
rightX,
|
||||
rightY,
|
||||
bottomX,
|
||||
bottomY,
|
||||
leftX,
|
||||
leftY
|
||||
] = getDiamondPoints(element);
|
||||
|
||||
return (
|
||||
distanceBetweenPointAndSegment(x, y, topX, topY, rightX, rightY) <
|
||||
lineThreshold ||
|
||||
distanceBetweenPointAndSegment(x, y, rightX, rightY, bottomX, bottomY) <
|
||||
lineThreshold ||
|
||||
distanceBetweenPointAndSegment(x, y, bottomX, bottomY, leftX, leftY) <
|
||||
lineThreshold ||
|
||||
distanceBetweenPointAndSegment(x, y, leftX, leftY, topX, topY) <
|
||||
lineThreshold
|
||||
);
|
||||
} else if (element.type === "arrow") {
|
||||
let [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
|
||||
// The computation is done at the origin, we need to add a translation
|
||||
x -= element.x;
|
||||
y -= element.y;
|
||||
|
||||
return (
|
||||
// \
|
||||
distanceBetweenPointAndSegment(x, y, x3, y3, x2, y2) < lineThreshold ||
|
||||
// -----
|
||||
distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) < lineThreshold ||
|
||||
// /
|
||||
distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold
|
||||
);
|
||||
} else if (element.type === "text") {
|
||||
const x1 = getElementAbsoluteX1(element);
|
||||
const x2 = getElementAbsoluteX2(element);
|
||||
const y1 = getElementAbsoluteY1(element);
|
||||
const y2 = getElementAbsoluteY2(element);
|
||||
|
||||
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
||||
} else if (element.type === "selection") {
|
||||
console.warn("This should not happen, we need to investigate why it does.");
|
||||
return false;
|
||||
} else {
|
||||
throw new Error("Unimplemented type " + element.type);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue