refactor: Implement point snapping functionality and enhance aisle management with new snapping logic and state management
This commit is contained in:
parent
63bb7c84aa
commit
5254bbd8df
|
@ -18,7 +18,7 @@ function AisleCreator() {
|
||||||
|
|
||||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, } = useBuilderStore();
|
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint } = useBuilderStore();
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (tempPoints.length > 0) {
|
// if (tempPoints.length > 0) {
|
||||||
|
@ -57,7 +57,7 @@ function AisleCreator() {
|
||||||
|
|
||||||
raycaster.setFromCamera(pointer, camera);
|
raycaster.setFromCamera(pointer, camera);
|
||||||
const intersectionPoint = new THREE.Vector3();
|
const intersectionPoint = new THREE.Vector3();
|
||||||
const position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||||
if (!position) return;
|
if (!position) return;
|
||||||
|
|
||||||
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
|
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
|
||||||
|
@ -69,7 +69,17 @@ function AisleCreator() {
|
||||||
layer: activeLayer
|
layer: activeLayer
|
||||||
};
|
};
|
||||||
|
|
||||||
if (intersects) {
|
if (snappedPosition && snappedPoint) {
|
||||||
|
newPoint.uuid = snappedPoint.uuid;
|
||||||
|
newPoint.position = snappedPosition;
|
||||||
|
newPoint.layer = snappedPoint.layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snappedPosition && !snappedPoint) {
|
||||||
|
newPoint.position = snappedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersects && !snappedPoint) {
|
||||||
const point = getAislePointById(intersects.object.uuid);
|
const point = getAislePointById(intersects.object.uuid);
|
||||||
if (point) {
|
if (point) {
|
||||||
newPoint.uuid = point.uuid;
|
newPoint.uuid = point.uuid;
|
||||||
|
@ -210,7 +220,7 @@ function AisleCreator() {
|
||||||
canvasElement.removeEventListener("click", onMouseClick);
|
canvasElement.removeEventListener("click", onMouseClick);
|
||||||
canvasElement.removeEventListener("contextmenu", onContext);
|
canvasElement.removeEventListener("contextmenu", onContext);
|
||||||
};
|
};
|
||||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength]);
|
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -222,7 +232,9 @@ function AisleCreator() {
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<ReferenceAisle tempPoints={tempPoints} />
|
{tempPoints.length > 0 &&
|
||||||
|
<ReferenceAisle tempPoints={tempPoints} />
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -5,21 +5,31 @@ import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/bu
|
||||||
import * as Constants from '../../../../types/world/worldConstants';
|
import * as Constants from '../../../../types/world/worldConstants';
|
||||||
import { Extrude } from '@react-three/drei';
|
import { Extrude } from '@react-three/drei';
|
||||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||||
|
import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping';
|
||||||
|
import { usePointSnapping } from '../../point/helpers/usePointSnapping';
|
||||||
|
|
||||||
interface ReferenceAisleProps {
|
interface ReferenceAisleProps {
|
||||||
tempPoints: Point[];
|
tempPoints: Point[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, } = useBuilderStore();
|
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||||
const { pointer, raycaster, camera } = useThree();
|
const { pointer, raycaster, camera } = useThree();
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { activeLayer } = useActiveLayer();
|
const { activeLayer } = useActiveLayer();
|
||||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||||
|
const finalPosition = useRef<[number, number, number] | null>(null);
|
||||||
|
|
||||||
const [tempAisle, setTempAisle] = useState<Aisle | null>(null);
|
const [tempAisle, setTempAisle] = useState<Aisle | null>(null);
|
||||||
const mousePosRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position);
|
||||||
|
|
||||||
|
// Calculate directional snap based on current and previous points
|
||||||
|
const directionalSnap = useDirectionalSnapping(
|
||||||
|
currentPosition,
|
||||||
|
tempPoints[0]?.position || null
|
||||||
|
);
|
||||||
|
const { checkSnapForAisle } = usePointSnapping({ uuid: 'temp-aisle', pointType: 'Aisle', position: directionalSnap.position || [0, 0, 0] });
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (toolMode === "Aisle" && toggleView && tempPoints.length === 1) {
|
if (toolMode === "Aisle" && toggleView && tempPoints.length === 1) {
|
||||||
|
@ -27,8 +37,27 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||||
const intersectionPoint = new THREE.Vector3();
|
const intersectionPoint = new THREE.Vector3();
|
||||||
raycaster.ray.intersectPlane(plane, intersectionPoint);
|
raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||||
|
|
||||||
|
setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||||
|
|
||||||
if (intersectionPoint) {
|
if (intersectionPoint) {
|
||||||
mousePosRef.current.copy(intersectionPoint);
|
|
||||||
|
const snapped = checkSnapForAisle([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]);
|
||||||
|
|
||||||
|
if (snapped.isSnapped && snapped.snappedPoint) {
|
||||||
|
finalPosition.current = snapped.position;
|
||||||
|
setSnappedPosition(snapped.position);
|
||||||
|
setSnappedPoint(snapped.snappedPoint);
|
||||||
|
} else if (directionalSnap.isSnapped) {
|
||||||
|
finalPosition.current = directionalSnap.position;
|
||||||
|
setSnappedPosition(directionalSnap.position);
|
||||||
|
setSnappedPoint(null);
|
||||||
|
} else {
|
||||||
|
finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z];
|
||||||
|
setSnappedPosition(null);
|
||||||
|
setSnappedPoint(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!finalPosition.current) return;
|
||||||
|
|
||||||
if (aisleType === 'solid-aisle' || aisleType === 'stripped-aisle') {
|
if (aisleType === 'solid-aisle' || aisleType === 'stripped-aisle') {
|
||||||
setTempAisle({
|
setTempAisle({
|
||||||
|
@ -38,7 +67,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||||
{
|
{
|
||||||
uuid: 'temp-point',
|
uuid: 'temp-point',
|
||||||
pointType: 'Aisle',
|
pointType: 'Aisle',
|
||||||
position: [mousePosRef.current.x, mousePosRef.current.y, mousePosRef.current.z],
|
position: finalPosition.current,
|
||||||
layer: activeLayer
|
layer: activeLayer
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -56,7 +85,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||||
{
|
{
|
||||||
uuid: 'temp-point',
|
uuid: 'temp-point',
|
||||||
pointType: 'Aisle',
|
pointType: 'Aisle',
|
||||||
position: [mousePosRef.current.x, mousePosRef.current.y, mousePosRef.current.z],
|
position: finalPosition.current,
|
||||||
layer: activeLayer
|
layer: activeLayer
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -76,7 +105,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||||
{
|
{
|
||||||
uuid: 'temp-point',
|
uuid: 'temp-point',
|
||||||
pointType: 'Aisle',
|
pointType: 'Aisle',
|
||||||
position: [mousePosRef.current.x, mousePosRef.current.y, mousePosRef.current.z],
|
position: finalPosition.current,
|
||||||
layer: activeLayer
|
layer: activeLayer
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -97,7 +126,7 @@ function ReferenceAisle({ tempPoints }: Readonly<ReferenceAisleProps>) {
|
||||||
{
|
{
|
||||||
uuid: 'temp-point',
|
uuid: 'temp-point',
|
||||||
pointType: 'Aisle',
|
pointType: 'Aisle',
|
||||||
position: [mousePosRef.current.x, mousePosRef.current.y, mousePosRef.current.z],
|
position: finalPosition.current,
|
||||||
layer: activeLayer
|
layer: activeLayer
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { useCallback, useMemo } 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)
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPosition;
|
||||||
|
}, [connectedPoints]);
|
||||||
|
|
||||||
|
return { snapPosition };
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
const SNAP_ANGLE_THRESHOLD = 15; // Degrees within which to snap to orthogonal direction
|
||||||
|
|
||||||
|
const SNAP_DISTANCE_THRESHOLD = 0.5; // Minimum distance to consider for directional snap
|
||||||
|
|
||||||
|
const CAN_SNAP = true; // Whether snapping is enabled or not
|
||||||
|
|
||||||
|
export const useDirectionalSnapping = (
|
||||||
|
currentPoint: [number, number, number] | null,
|
||||||
|
previousPoint: [number, number, number] | null
|
||||||
|
): { position: [number, number, number], isSnapped: boolean } => {
|
||||||
|
return useMemo(() => {
|
||||||
|
if (!currentPoint || !previousPoint || !CAN_SNAP) return { position: currentPoint || [0, 0, 0], isSnapped: false }; // No snapping if no points
|
||||||
|
|
||||||
|
const currentVec = new THREE.Vector2(currentPoint[0], currentPoint[2]);
|
||||||
|
const previousVec = new THREE.Vector2(previousPoint[0], previousPoint[2]);
|
||||||
|
const directionVec = new THREE.Vector2().subVectors(currentVec, previousVec);
|
||||||
|
|
||||||
|
const angle = THREE.MathUtils.radToDeg(directionVec.angle());
|
||||||
|
const normalizedAngle = (angle + 360) % 360;
|
||||||
|
const closestAngle = Math.round(normalizedAngle / 90) * 90 % 360;
|
||||||
|
|
||||||
|
const angleDiff = Math.abs(normalizedAngle - closestAngle);
|
||||||
|
const shortestDiff = Math.min(angleDiff, 360 - angleDiff);
|
||||||
|
|
||||||
|
if (shortestDiff <= SNAP_ANGLE_THRESHOLD) {
|
||||||
|
const distance = directionVec.length();
|
||||||
|
const snappedDirection = new THREE.Vector2(
|
||||||
|
Math.cos(THREE.MathUtils.degToRad(closestAngle)),
|
||||||
|
Math.sin(THREE.MathUtils.degToRad(closestAngle))
|
||||||
|
).multiplyScalar(distance);
|
||||||
|
|
||||||
|
const newPosition = new THREE.Vector2(previousPoint[0] + snappedDirection.x, previousPoint[2] + snappedDirection.y)
|
||||||
|
|
||||||
|
if (newPosition.distanceTo(currentVec) > SNAP_DISTANCE_THRESHOLD) {
|
||||||
|
return { position: currentPoint, isSnapped: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: [
|
||||||
|
previousPoint[0] + snappedDirection.x,
|
||||||
|
currentPoint[1],
|
||||||
|
previousPoint[2] + snappedDirection.y
|
||||||
|
],
|
||||||
|
isSnapped: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { position: currentPoint, isSnapped: false };
|
||||||
|
}, [currentPoint, previousPoint]);
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useAisleStore } from '../../../../store/builder/useAisleStore';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
const SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters
|
||||||
|
|
||||||
|
const CAN_SNAP = true; // Whether snapping is enabled or not
|
||||||
|
|
||||||
|
export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => {
|
||||||
|
const { aisles } = useAisleStore();
|
||||||
|
|
||||||
|
const getAllOtherPoints = useCallback(() => {
|
||||||
|
if (!currentPoint) return [];
|
||||||
|
|
||||||
|
return aisles.flatMap(aisle =>
|
||||||
|
aisle.points.filter(point => point.uuid !== currentPoint.uuid)
|
||||||
|
);
|
||||||
|
}, [aisles, currentPoint]);
|
||||||
|
|
||||||
|
const checkSnapForAisle = useCallback((position: [number, number, number]) => {
|
||||||
|
if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null };
|
||||||
|
|
||||||
|
const otherPoints = getAllOtherPoints();
|
||||||
|
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 <= SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') {
|
||||||
|
return { position: point.position, isSnapped: true, snappedPoint: point };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { position: position, isSnapped: false, snappedPoint: null };
|
||||||
|
}, [currentPoint, getAllOtherPoints]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
checkSnapForAisle,
|
||||||
|
};
|
||||||
|
};
|
|
@ -6,7 +6,7 @@ import { DragControls } from '@react-three/drei';
|
||||||
import { useAisleStore } from '../../../store/builder/useAisleStore';
|
import { useAisleStore } from '../../../store/builder/useAisleStore';
|
||||||
import { useThree } from '@react-three/fiber';
|
import { useThree } from '@react-three/fiber';
|
||||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||||
import { usePointSnapping } from './usePointSnapping';
|
import { usePointSnapping } from './helpers/usePointSnapping';
|
||||||
|
|
||||||
function Point({ point }: { readonly point: Point }) {
|
function Point({ point }: { readonly point: Point }) {
|
||||||
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||||
|
@ -15,7 +15,7 @@ function Point({ point }: { readonly point: Point }) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { setPosition, removePoint } = useAisleStore();
|
const { setPosition, removePoint } = useAisleStore();
|
||||||
const { checkSnapForAisle } = usePointSnapping(point);
|
const { checkSnapForAisle } = usePointSnapping({ uuid: point.uuid, pointType: point.pointType, position: point.position });
|
||||||
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
||||||
const { deletePointOrLine } = useDeletePointOrLine();
|
const { deletePointOrLine } = useDeletePointOrLine();
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ function Point({ point }: { readonly point: Point }) {
|
||||||
const newPosition: [number, number, number] = [position.x, position.y, position.z];
|
const newPosition: [number, number, number] = [position.x, position.y, position.z];
|
||||||
const snappedPosition = checkSnapForAisle(newPosition);
|
const snappedPosition = checkSnapForAisle(newPosition);
|
||||||
|
|
||||||
setPosition(point.uuid, snappedPosition);
|
setPosition(point.uuid, snappedPosition.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { useAisleStore } from '../../../store/builder/useAisleStore';
|
|
||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
const SNAP_THRESHOLD = 0.25; // Distance threshold for snapping in meters
|
|
||||||
|
|
||||||
export const usePointSnapping = (currentPoint: Point | null) => {
|
|
||||||
const { aisles } = useAisleStore();
|
|
||||||
const [snappedPosition, setSnappedPosition] = useState<[number, number, number] | null>(null);
|
|
||||||
const [snappedPointId, setSnappedPointId] = useState<string | null>(null);
|
|
||||||
|
|
||||||
// Get all points from all aisles except the current point
|
|
||||||
const getAllOtherPoints = useCallback(() => {
|
|
||||||
if (!currentPoint) return [];
|
|
||||||
|
|
||||||
return aisles.flatMap(aisle =>
|
|
||||||
aisle.points.filter(point => point.uuid !== currentPoint.uuid)
|
|
||||||
);
|
|
||||||
}, [aisles, currentPoint]);
|
|
||||||
|
|
||||||
// Check if there's a nearby Aisle point to snap to
|
|
||||||
const checkSnapForAisle = useCallback((position: [number, number, number]) => {
|
|
||||||
if (!currentPoint) return position;
|
|
||||||
|
|
||||||
const otherPoints = getAllOtherPoints();
|
|
||||||
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 <= SNAP_THRESHOLD && point.pointType === 'Aisle') {
|
|
||||||
setSnappedPointId(point.uuid);
|
|
||||||
return point.position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No snap found
|
|
||||||
setSnappedPointId(null);
|
|
||||||
return position;
|
|
||||||
}, [currentPoint, getAllOtherPoints]);
|
|
||||||
|
|
||||||
// Reset snap state when current point changes
|
|
||||||
useEffect(() => {
|
|
||||||
setSnappedPosition(null);
|
|
||||||
setSnappedPointId(null);
|
|
||||||
}, [currentPoint]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
snappedPosition,
|
|
||||||
snappedPointId,
|
|
||||||
checkSnapForAisle,
|
|
||||||
isSnapped: snappedPointId !== null
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -34,6 +34,7 @@ interface AisleStore {
|
||||||
|
|
||||||
getAisleById: (uuid: string) => Aisle | undefined;
|
getAisleById: (uuid: string) => Aisle | undefined;
|
||||||
getAislePointById: (uuid: string) => Point | undefined;
|
getAislePointById: (uuid: string) => Point | undefined;
|
||||||
|
getConnectedPoints: (uuid: string) => Point[] | [];
|
||||||
getAisleType: <T extends AisleType>(uuid: string) => T | undefined;
|
getAisleType: <T extends AisleType>(uuid: string) => T | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +184,18 @@ export const useAisleStore = create<AisleStore>()(
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getConnectedPoints: (uuid) => {
|
||||||
|
const connected: Point[] = [];
|
||||||
|
for (const aisle of get().aisles) {
|
||||||
|
for (const point of aisle.points) {
|
||||||
|
if (point.uuid === uuid) {
|
||||||
|
connected.push(...aisle.points.filter(p => p.uuid !== uuid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return connected;
|
||||||
|
},
|
||||||
|
|
||||||
getAisleType: <T extends AisleType>(uuid: string) => {
|
getAisleType: <T extends AisleType>(uuid: string) => {
|
||||||
const aisle = get().aisles.find(a => a.uuid === uuid);
|
const aisle = get().aisles.find(a => a.uuid === uuid);
|
||||||
return aisle?.type as T | undefined;
|
return aisle?.type as T | undefined;
|
||||||
|
|
|
@ -6,6 +6,8 @@ interface BuilderState {
|
||||||
// Common properties
|
// Common properties
|
||||||
|
|
||||||
hoveredPoint: Point | null;
|
hoveredPoint: Point | null;
|
||||||
|
snappedPoint: Point | null;
|
||||||
|
snappedPosition: [number, number, number] | null;
|
||||||
|
|
||||||
selectedAisle: Object3D | null;
|
selectedAisle: Object3D | null;
|
||||||
|
|
||||||
|
@ -26,6 +28,8 @@ interface BuilderState {
|
||||||
// Setters for common properties
|
// Setters for common properties
|
||||||
|
|
||||||
setHoveredPoint: (point: Point | null) => void;
|
setHoveredPoint: (point: Point | null) => void;
|
||||||
|
setSnappedPoint: (point: Point | null) => void;
|
||||||
|
setSnappedPosition: (position: [number, number, number] | null) => void;
|
||||||
|
|
||||||
setSelectedAisle: (aisle: Object3D | null) => void;
|
setSelectedAisle: (aisle: Object3D | null) => void;
|
||||||
|
|
||||||
|
@ -55,6 +59,8 @@ export const useBuilderStore = create<BuilderState>()(
|
||||||
// Default values
|
// Default values
|
||||||
|
|
||||||
hoveredPoint: null,
|
hoveredPoint: null,
|
||||||
|
snappedPoint: null,
|
||||||
|
snappedPosition: null,
|
||||||
|
|
||||||
selectedAisle: null,
|
selectedAisle: null,
|
||||||
|
|
||||||
|
@ -74,6 +80,18 @@ export const useBuilderStore = create<BuilderState>()(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setSnappedPoint: (point: Point | null) => {
|
||||||
|
set((state) => {
|
||||||
|
state.snappedPoint = point;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setSnappedPosition: (position: [number, number, number] | null) => {
|
||||||
|
set((state) => {
|
||||||
|
state.snappedPosition = position;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
setSelectedAisle: (aisle: Object3D | null) => {
|
setSelectedAisle: (aisle: Object3D | null) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.selectedAisle = aisle;
|
state.selectedAisle = aisle;
|
||||||
|
|
Loading…
Reference in New Issue