import { useEffect, useMemo, useRef, useState } from 'react'; import * as THREE from 'three'; import { useFrame, useThree } from '@react-three/fiber'; import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store'; import * as Constants from '../../../../types/world/worldConstants'; import { Extrude } from '@react-three/drei'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping'; import { usePointSnapping } from '../../point/helpers/usePointSnapping'; interface ReferenceAisleProps { tempPoints: Point[]; } function ReferenceAisle({ tempPoints }: Readonly) { const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setSnappedPosition, setSnappedPoint } = useBuilderStore(); const { pointer, raycaster, camera } = useThree(); const { toolMode } = useToolMode(); const { toggleView } = useToggleView(); const { activeLayer } = useActiveLayer(); const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); const finalPosition = useRef<[number, number, number] | null>(null); const [tempAisle, setTempAisle] = useState(null); 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(() => { if (toolMode === "Aisle" && toggleView && tempPoints.length === 1) { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); raycaster.ray.intersectPlane(plane, intersectionPoint); setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); if (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') { setTempAisle({ uuid: 'temp-aisle', points: [ tempPoints[0], { uuid: 'temp-point', pointType: 'Aisle', position: finalPosition.current, layer: activeLayer } ], type: { aisleType: aisleType, aisleColor: aisleColor, aisleWidth: aisleWidth } }); } else if (aisleType === 'dashed-aisle') { setTempAisle({ uuid: 'temp-aisle', points: [ tempPoints[0], { uuid: 'temp-point', pointType: 'Aisle', position: finalPosition.current, layer: activeLayer } ], type: { aisleType: aisleType, aisleColor: aisleColor, aisleWidth: aisleWidth, dashLength: dashLength, gapLength: gapLength } }); } else if (aisleType === 'dotted-aisle') { setTempAisle({ uuid: 'temp-aisle', points: [ tempPoints[0], { uuid: 'temp-point', pointType: 'Aisle', position: finalPosition.current, layer: activeLayer } ], type: { aisleType: aisleType, aisleColor: aisleColor, dotRadius: dotRadius, gapLength: gapLength } }); } else if (aisleType === 'arrow-aisle') { console.log(); } else if (aisleType === 'arrows-aisle') { setTempAisle({ uuid: 'temp-aisle', points: [ tempPoints[0], { uuid: 'temp-point', pointType: 'Aisle', position: finalPosition.current, layer: activeLayer } ], type: { aisleType: aisleType, aisleColor: aisleColor, aisleWidth: aisleWidth, aisleLength: aisleLength, gapLength: gapLength } }); } else if (aisleType === 'arc-aisle') { console.log(); } else if (aisleType === 'circle-aisle') { console.log(); } else if (aisleType === 'junction-aisle') { console.log(); } } } else if (tempAisle !== null) { setTempAisle(null); } }); useEffect(() => { setTempAisle(null); }, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor]); if (!tempAisle) return null; const renderAisle = () => { switch (aisleType) { case 'solid-aisle': return ; case 'dashed-aisle': return ; case 'dotted-aisle': return ; case 'arrows-aisle': return default: return null; } }; return ( {renderAisle()} ); } export default ReferenceAisle; function SolidAisle({ aisle }: { readonly aisle: Aisle }) { const shape = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null; const start = new THREE.Vector3(...aisle.points[0].position); const end = new THREE.Vector3(...aisle.points[1].position); const width = aisle.type.aisleWidth || 0.1; const direction = new THREE.Vector3().subVectors(end, start).normalize(); const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, width / 2); const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -width / 2); const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, width / 2); const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -width / 2); const shape = new THREE.Shape(); shape.moveTo(leftStart.x, leftStart.z); shape.lineTo(leftEnd.x, leftEnd.z); shape.lineTo(rightEnd.x, rightEnd.z); shape.lineTo(rightStart.x, rightStart.z); shape.closePath(); return shape; }, [aisle]); if (!shape) return null; return ( ); } function DashedAisle({ aisle }: { readonly aisle: Aisle }) { const shapes = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return []; const start = new THREE.Vector3(...aisle.points[0].position); const end = new THREE.Vector3(...aisle.points[1].position); const width = aisle.type.aisleWidth || 0.1; const dashLength = aisle.type.dashLength || 0.5; const gapLength = aisle.type.gapLength || 0.3; const direction = new THREE.Vector3().subVectors(end, start).normalize(); const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); const totalLength = new THREE.Vector3().subVectors(end, start).length(); const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength)); const shapes = []; const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); for (let i = 0; i < segmentCount; i++) { const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength)); const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength); const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2); const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2); const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2); const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2); const shape = new THREE.Shape(); shape.moveTo(leftStart.x, leftStart.z); shape.lineTo(leftEnd.x, leftEnd.z); shape.lineTo(rightEnd.x, rightEnd.z); shape.lineTo(rightStart.x, rightStart.z); shape.closePath(); shapes.push(shape); } return shapes; }, [aisle]); if (shapes.length === 0) return null; return ( {shapes.map((shape, index) => ( ))} ); } function DottedAisle({ aisle }: { readonly aisle: Aisle }) { const shapes = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return []; const start = new THREE.Vector3(...aisle.points[0].position); const end = new THREE.Vector3(...aisle.points[1].position); const width = aisle.type.dotRadius || 0.1; const dotSpacing = aisle.type.gapLength || 0.5; const dotRadius = width * 0.6; const totalLength = new THREE.Vector3().subVectors(end, start).length(); const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing); const shapes = []; const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); for (let i = 0; i < dotCount; i++) { const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2); const shape = new THREE.Shape(); shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false); shapes.push(shape); } return shapes; }, [aisle]); if (shapes.length === 0) return null; return ( {shapes.map((shape, index) => ( ))} ); } function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) { const arrows = useMemo(() => { if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return []; const start = new THREE.Vector3(...aisle.points[0].position); const end = new THREE.Vector3(...aisle.points[1].position); const width = aisle.type.aisleWidth || 0.1; const arrowLength = aisle.type.aisleLength || 0.6; const spacing = aisle.type.gapLength || 0.6; const direction = new THREE.Vector3().subVectors(end, start); const length = direction.length(); direction.normalize(); const count = Math.floor((length + spacing) / (arrowLength + spacing)); const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = []; for (let i = 0; i < count; i++) { const initialOffset = arrowLength; const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing)); const shape = new THREE.Shape(); const w = width * 0.8; const h = arrowLength; shape.moveTo(0, 0); shape.lineTo(w, h * 0.6); shape.lineTo(w * 0.4, h * 0.6); shape.lineTo(w * 0.4, h); shape.lineTo(-w * 0.4, h); shape.lineTo(-w * 0.4, h * 0.6); shape.lineTo(-w, h * 0.6); shape.lineTo(0, 0); const angle = Math.atan2(direction.x, direction.z) + Math.PI; arrowShapes.push({ shape, position: center, rotationY: angle }); } return arrowShapes; }, [aisle]); if (arrows.length === 0) return null; return ( {arrows.map(({ shape, position, rotationY }, index) => ( ))} ); }