import { useCallback } from 'react'; import * as THREE from 'three'; import { useAisleStore } from '../../../../store/builder/useAisleStore'; const SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters const CAN_SNAP = true; // Whether snapping is enabled or not export function useAislePointSnapping(point: Point) { const { getConnectedPoints } = useAisleStore(); const snapPosition = useCallback((newPosition: [number, number, number]): { position: [number, number, number], isSnapped: boolean, snapSources: THREE.Vector3[] } => { if (!CAN_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] }; const connectedPoints = getConnectedPoints(point.pointUuid); if (connectedPoints.length === 0) { return { position: newPosition, isSnapped: false, snapSources: [] }; } const newPos = new THREE.Vector3(...newPosition); let closestX: { pos: THREE.Vector3, dist: number } | null = null; let closestZ: { pos: THREE.Vector3, dist: number } | null = null; for (const connectedPoint of connectedPoints) { const cPos = new THREE.Vector3(...connectedPoint.position); const xDist = Math.abs(newPos.x - cPos.x); const zDist = Math.abs(newPos.z - cPos.z); if (xDist < SNAP_DISTANCE_THRESHOLD) { if (!closestX || xDist < closestX.dist) { closestX = { pos: cPos, dist: xDist }; } } if (zDist < SNAP_DISTANCE_THRESHOLD) { if (!closestZ || zDist < closestZ.dist) { closestZ = { pos: cPos, dist: zDist }; } } } const snappedPos = newPos.clone(); const snapSources: THREE.Vector3[] = []; if (closestX) { snappedPos.x = closestX.pos.x; snapSources.push(closestX.pos.clone()); } if (closestZ) { snappedPos.z = closestZ.pos.z; snapSources.push(closestZ.pos.clone()); } const isSnapped = snapSources.length > 0; return { position: [snappedPos.x, snappedPos.y, snappedPos.z], isSnapped, snapSources }; }, [point.pointUuid, getConnectedPoints]); return { snapPosition }; }