summaryrefslogtreecommitdiff
path: root/apps/web/src/preview/hit-test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/preview/hit-test.ts')
-rw-r--r--apps/web/src/preview/hit-test.ts102
1 files changed, 102 insertions, 0 deletions
diff --git a/apps/web/src/preview/hit-test.ts b/apps/web/src/preview/hit-test.ts
new file mode 100644
index 00000000..0f5fd45e
--- /dev/null
+++ b/apps/web/src/preview/hit-test.ts
@@ -0,0 +1,102 @@
+import type { ElementWithBounds } from "./element-bounds";
+import type { ElementRef } from "@/timeline/types";
+
+function pointInRotatedRect({
+ px,
+ py,
+ cx,
+ cy,
+ width,
+ height,
+ rotation,
+}: {
+ px: number;
+ py: number;
+ cx: number;
+ cy: number;
+ width: number;
+ height: number;
+ rotation: number;
+}): boolean {
+ const angleRad = (rotation * Math.PI) / 180;
+ const cos = Math.cos(-angleRad);
+ const sin = Math.sin(-angleRad);
+ const dx = px - cx;
+ const dy = py - cy;
+ const localX = dx * cos - dy * sin;
+ const localY = dx * sin + dy * cos;
+ const halfW = Math.abs(width) / 2;
+ const halfH = Math.abs(height) / 2;
+ return (
+ localX >= -halfW && localX <= halfW && localY >= -halfH && localY <= halfH
+ );
+}
+
+export function getHitElements({
+ canvasX,
+ canvasY,
+ elementsWithBounds,
+}: {
+ canvasX: number;
+ canvasY: number;
+ elementsWithBounds: ElementWithBounds[];
+}): ElementWithBounds[] {
+ const hits: ElementWithBounds[] = [];
+
+ for (let i = elementsWithBounds.length - 1; i >= 0; i--) {
+ const { bounds } = elementsWithBounds[i];
+ if (
+ pointInRotatedRect({
+ px: canvasX,
+ py: canvasY,
+ cx: bounds.cx,
+ cy: bounds.cy,
+ width: bounds.width,
+ height: bounds.height,
+ rotation: bounds.rotation,
+ })
+ ) {
+ hits.push(elementsWithBounds[i]);
+ }
+ }
+
+ return hits;
+}
+
+export function hitTest({
+ canvasX,
+ canvasY,
+ elementsWithBounds,
+}: {
+ canvasX: number;
+ canvasY: number;
+ elementsWithBounds: ElementWithBounds[];
+}): ElementWithBounds | null {
+ return (
+ getHitElements({
+ canvasX,
+ canvasY,
+ elementsWithBounds,
+ })[0] ?? null
+ );
+}
+
+export function resolvePreferredHit({
+ hits,
+ preferredElements,
+}: {
+ hits: ElementWithBounds[];
+ preferredElements: ElementRef[];
+}): ElementWithBounds | null {
+ if (preferredElements.length === 0) return null;
+
+ return (
+ hits.find((hit) =>
+ preferredElements.some(
+ (preferredElement) =>
+ preferredElement.trackId === hit.trackId &&
+ preferredElement.elementId === hit.elementId,
+ ),
+ ) ?? null
+ );
+}