import { useRef, useMemo, useState, useCallback } from "react"; import { useThree, useFrame } from "@react-three/fiber"; import * as THREE from "three"; import { useSceneContext } from "../../../sceneContext"; type ColliderArrowProps = { arrowId: string; colliderId: string; startPosition: [number, number, number]; endPosition: [number, number, number]; colliderRotation: [number, number, number]; thickness?: number; depth?: number; color?: string; }; export function ColliderArrow({ arrowId, colliderId, startPosition, endPosition, colliderRotation, thickness = 0.05, depth = 0.01, color = "green", }: ColliderArrowProps) { const groupRef = useRef(null); const { raycaster, pointer, controls } = useThree(); const { colliderStore } = useSceneContext(); const { updateArrow, selectedArrowId, clearSelectedArrow } = colliderStore(); const [dragging, setDragging] = useState(false); const { dir, length } = useMemo(() => { const start = new THREE.Vector3(...startPosition); const end = new THREE.Vector3(...endPosition); const d = new THREE.Vector3().subVectors(end, start); return { dir: d.clone().normalize(), length: d.length() }; }, [startPosition, endPosition]); const arrowShape = useMemo(() => { const shaftWidth = thickness; const headLength = Math.min(length * 0.3, 0.4); const headWidth = thickness * 3; const shape = new THREE.Shape(); shape.moveTo(0, -shaftWidth / 2); shape.lineTo(length - headLength, -shaftWidth / 2); shape.lineTo(length - headLength, -headWidth / 2); shape.lineTo(length, 0); shape.lineTo(length - headLength, headWidth / 2); shape.lineTo(length - headLength, shaftWidth / 2); shape.lineTo(0, shaftWidth / 2); shape.closePath(); return shape; }, [length, thickness]); const extrudeSettings = useMemo(() => ({ depth, bevelEnabled: false, }), [depth]); const geometry = useMemo(() => new THREE.ExtrudeGeometry(arrowShape, extrudeSettings), [arrowShape, extrudeSettings]); const quaternion = useMemo(() => { const q = new THREE.Quaternion(); q.setFromUnitVectors(new THREE.Vector3(1, 0, 0), dir); const colliderQuat = new THREE.Quaternion().setFromEuler( new THREE.Euler().fromArray(colliderRotation) ); return colliderQuat.multiply(q); }, [dir, colliderRotation]); const handlePointerDown = useCallback((e: any) => { e.stopPropagation(); setDragging(true); (controls as any).enabled = false; // Disable controls while dragging }, []); const handlePointerUp = useCallback((e: any) => { e.stopPropagation(); clearSelectedArrow() setDragging(false); (controls as any).enabled = true; }, []); useFrame(({ camera }) => { if (dragging) { if (selectedArrowId) { const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -startPosition[1]); raycaster.setFromCamera(pointer, camera); const hit = new THREE.Vector3(); if (raycaster.ray.intersectPlane(plane, hit)) { updateArrow(colliderId, arrowId, { position: [hit.x, startPosition[1], hit.z], }); } } } }); return ( ); }