196 lines
7.1 KiB
TypeScript
196 lines
7.1 KiB
TypeScript
import * as THREE from 'three';
|
|
import { useCallback } from 'react';
|
|
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
|
import { useWallStore } from '../../../../store/builder/useWallStore';
|
|
|
|
const POINT_SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
|
|
|
const CAN_POINT_SNAP = true; // Whether snapping is enabled or not
|
|
|
|
const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
|
|
|
const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not
|
|
|
|
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
|
|
const { aisles, getConnectedPoints: getConnectedAislePoints } = useAisleStore();
|
|
const { walls, getConnectedPoints: getConnectedWallPoints } = useWallStore();
|
|
|
|
// Wall Snapping
|
|
|
|
const getAllOtherWallPoints = useCallback(() => {
|
|
if (!currentPoint) return [];
|
|
return walls.flatMap(wall =>
|
|
wall.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
|
);
|
|
}, [walls, currentPoint]);
|
|
|
|
const snapWallPoint = useCallback((position: [number, number, number]) => {
|
|
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
|
|
|
const otherPoints = getAllOtherWallPoints();
|
|
const currentVec = new THREE.Vector3(...position);
|
|
for (const point of otherPoints) {
|
|
const pointVec = new THREE.Vector3(...point.position);
|
|
const distance = currentVec.distanceTo(pointVec);
|
|
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Wall') {
|
|
return { position: point.position, isSnapped: true, snappedPoint: point };
|
|
}
|
|
}
|
|
return { position: position, isSnapped: false, snappedPoint: null };
|
|
}, [currentPoint, getAllOtherWallPoints]);
|
|
|
|
const snapWallAngle = useCallback((newPosition: [number, number, number]): {
|
|
position: [number, number, number],
|
|
isSnapped: boolean,
|
|
snapSources: THREE.Vector3[]
|
|
} => {
|
|
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
|
|
|
const connectedPoints = getConnectedWallPoints(currentPoint.uuid);
|
|
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 < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
|
if (!closestX || xDist < closestX.dist) {
|
|
closestX = { pos: cPos, dist: xDist };
|
|
}
|
|
}
|
|
|
|
if (zDist < ANGLE_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
|
|
};
|
|
}, [currentPoint, getConnectedAislePoints]);
|
|
|
|
// Aisle Snapping
|
|
|
|
const getAllOtherAislePoints = useCallback(() => {
|
|
if (!currentPoint) return [];
|
|
|
|
return aisles.flatMap(aisle =>
|
|
aisle.points.filter(point => point.pointUuid !== currentPoint.uuid)
|
|
);
|
|
}, [aisles, currentPoint]);
|
|
|
|
const snapAislePoint = useCallback((position: [number, number, number]) => {
|
|
if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
|
|
|
const otherPoints = getAllOtherAislePoints();
|
|
const currentVec = new THREE.Vector3(...position);
|
|
for (const point of otherPoints) {
|
|
const pointVec = new THREE.Vector3(...point.position);
|
|
const distance = currentVec.distanceTo(pointVec);
|
|
if (distance <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') {
|
|
return { position: point.position, isSnapped: true, snappedPoint: point };
|
|
}
|
|
}
|
|
|
|
return { position: position, isSnapped: false, snappedPoint: null };
|
|
}, [currentPoint, getAllOtherAislePoints]);
|
|
|
|
const snapAisleAngle = useCallback((newPosition: [number, number, number]): {
|
|
position: [number, number, number],
|
|
isSnapped: boolean,
|
|
snapSources: THREE.Vector3[]
|
|
} => {
|
|
if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] };
|
|
|
|
const connectedPoints = getConnectedAislePoints(currentPoint.uuid);
|
|
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 < ANGLE_SNAP_DISTANCE_THRESHOLD) {
|
|
if (!closestX || xDist < closestX.dist) {
|
|
closestX = { pos: cPos, dist: xDist };
|
|
}
|
|
}
|
|
|
|
if (zDist < ANGLE_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
|
|
};
|
|
}, [currentPoint, getConnectedAislePoints]);
|
|
|
|
return {
|
|
snapAislePoint,
|
|
snapAisleAngle,
|
|
snapWallPoint,
|
|
snapWallAngle,
|
|
};
|
|
}; |