diff --git a/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx b/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx index e12fc17..123c7e3 100644 --- a/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx +++ b/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx @@ -1,61 +1,74 @@ -import { useCallback, useMemo } from 'react'; +import { useCallback } from 'react'; import * as THREE from 'three'; import { useAisleStore } from '../../../../store/builder/useAisleStore'; -const SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters -const SNAP_STRENGTH = 0.2; // How strongly the point snaps (0-1) +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(); - // Find all points connected to the current point via aisles - const connectedPoints = useMemo(() => { - return getConnectedPoints(point.uuid); - }, [point.uuid]); + 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: [] }; - // Snapping function - const snapPosition = useCallback((newPosition: [number, number, number]): [number, number, number] => { - if (connectedPoints.length === 0) return newPosition; - - const newPos = new THREE.Vector3(...newPosition); - let snappedX = newPos.x; - let snappedZ = newPos.z; - let snapCount = 0; - - // Check against all connected points - connectedPoints.forEach(connectedPoint => { - const connectedPos = new THREE.Vector3(...connectedPoint.position); - - // Check X axis - if (Math.abs(newPos.x - connectedPos.x) < SNAP_THRESHOLD) { - // Apply soft snapping (lerp) - snappedX = THREE.MathUtils.lerp( - newPos.x, - connectedPos.x, - SNAP_STRENGTH - ); - snapCount++; - } - - // Check Z axis - if (Math.abs(newPos.z - connectedPos.z) < SNAP_THRESHOLD) { - // Apply soft snapping (lerp) - snappedZ = THREE.MathUtils.lerp( - newPos.z, - connectedPos.z, - SNAP_STRENGTH - ); - snapCount++; - } - }); - - // Only return snapped position if we actually snapped to something - if (snapCount > 0) { - return [snappedX, newPos.y, snappedZ]; + const connectedPoints = getConnectedPoints(point.uuid); + if (connectedPoints.length === 0) { + return { + position: newPosition, + isSnapped: false, + snapSources: [] + }; } - return newPosition; - }, [connectedPoints]); + 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.uuid, getConnectedPoints]); return { snapPosition }; -} \ No newline at end of file +} diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index 9155b86..a6c22c9 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -7,6 +7,7 @@ import { useAisleStore } from '../../../store/builder/useAisleStore'; import { useThree } from '@react-three/fiber'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { usePointSnapping } from './helpers/usePointSnapping'; +import { useAislePointSnapping } from './helpers/useAisleDragSnap'; function Point({ point }: { readonly point: Point }) { const materialRef = useRef(null); @@ -15,6 +16,7 @@ function Point({ point }: { readonly point: Point }) { const [isHovered, setIsHovered] = useState(false); const { toolMode } = useToolMode(); const { setPosition, removePoint } = useAisleStore(); + const { snapPosition } = useAislePointSnapping(point); const { checkSnapForAisle } = usePointSnapping({ uuid: point.uuid, pointType: point.pointType, position: point.position }); const { hoveredPoint, setHoveredPoint } = useBuilderStore(); const { deletePointOrLine } = useDeletePointOrLine(); @@ -58,9 +60,10 @@ function Point({ point }: { readonly point: Point }) { if (point.pointType === 'Aisle') { if (position) { const newPosition: [number, number, number] = [position.x, position.y, position.z]; - const snappedPosition = checkSnapForAisle(newPosition); + const aisleSnappedPosition = snapPosition(newPosition); + const finalSnappedPosition = checkSnapForAisle(aisleSnappedPosition.position); - setPosition(point.uuid, snappedPosition.position); + setPosition(point.uuid, finalSnappedPosition.position); } } } diff --git a/app/src/store/builder/useAisleStore.ts b/app/src/store/builder/useAisleStore.ts index 7f091a8..0eb24a1 100644 --- a/app/src/store/builder/useAisleStore.ts +++ b/app/src/store/builder/useAisleStore.ts @@ -193,6 +193,7 @@ export const useAisleStore = create()( } } } + console.log('connected: ', connected); return connected; },