import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import { useRef, useState, useEffect, useMemo } from 'react'; import { Sphere, TransformControls } from '@react-three/drei'; import { useEditingPoint, useEyeDropMode, useIsConnecting, usePreviewPosition, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; import { useFrame, useThree } from '@react-three/fiber'; import { useSubModuleStore } from '../../../store/useModuleStore'; function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }) { const { renderDistance } = useRenderDistance(); const { setSubModule } = useSubModuleStore(); const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere(); const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); const { editingPoint, setEditingPoint } = useEditingPoint(); const { previewPosition, setPreviewPosition } = usePreviewPosition(); const { raycaster, camera, pointer, gl } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { setSelectedPath } = useSelectedPath(); const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { isConnecting } = useIsConnecting(); const groupRefs = useRef<{ [key: string]: THREE.Group }>({}); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); const isMovingRef = useRef(false); const transformRef = useRef(null); const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | null>(null); useEffect(() => { setTransformMode(null); const handleKeyDown = (e: KeyboardEvent) => { if (!selectedActionSphere) return; if (e.key === 'g') { setTransformMode(prev => prev === 'translate' ? null : 'translate'); } if (e.key === 'r') { setTransformMode(prev => prev === 'rotate' ? null : 'rotate'); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [selectedActionSphere]); useFrame(() => { Object.values(groupRefs.current).forEach(group => { if (group) { const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); group.visible = distance <= renderDistance; } }); }); const updateSimulationPaths = () => { if (!selectedActionSphere) return; const updatedPaths = simulationPaths.map((path) => { if (path.type === "Conveyor") { return { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.point.uuid ? { ...point, position: [ selectedActionSphere.point.position.x, selectedActionSphere.point.position.y, selectedActionSphere.point.position.z, ], rotation: [ selectedActionSphere.point.rotation.x, selectedActionSphere.point.rotation.y, selectedActionSphere.point.rotation.z, ] } : point ), }; } return path; }) as Types.ConveyorEventsSchema[]; setSimulationPaths(updatedPaths); }; useFrame(() => { if (eyeDropMode) { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const point = raycaster.ray.intersectPlane(plane, intersectionPoint); if (point) { setPreviewPosition({ x: point.x, y: point.z }); } } else { setPreviewPosition(null); } }); useEffect(() => { if (!camera) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; const onPointerDown = () => { isMovingRef.current = false; }; const onPointerMove = () => { isMovingRef.current = true; }; const onPointerUp = (event: PointerEvent) => { if (!isMovingRef.current && eyeDropMode && event.button === 0 && previewPosition) { event.preventDefault(); if (editingPoint) { handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y); setEditingPoint(null); setEyeDropMode(false); } } }; if (eyeDropMode) { canvasElement.addEventListener("pointerdown", onPointerDown); canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointerup", onPointerUp); } return () => { canvasElement.removeEventListener("pointerdown", onPointerDown); canvasElement.removeEventListener("pointermove", onPointerMove); canvasElement.removeEventListener("pointerup", onPointerUp); }; }, [eyeDropMode, editingPoint, previewPosition]); const handlePointUpdate = (pointType: 'start' | 'end', x: number, z: number) => { if (!selectedActionSphere?.point?.uuid) return; const updatedPaths = simulationPaths.map((path) => { if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) { return { ...path, point: { ...path.point, actions: { ...path.point.actions, [pointType]: { ...path.point.actions[pointType], x: x, y: z } } } }; } return path; }); setSimulationPaths(updatedPaths); }; return ( {simulationPaths.map((path) => { if (path.type === 'Conveyor') { const points = path.points.map(point => new THREE.Vector3(...point.position)); return ( (groupRefs.current[path.modeluuid] = el!)} position={path.assetPosition} rotation={path.assetRotation} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedPath({ path, group: groupRefs.current[path.modeluuid] }); setSelectedActionSphere(null); setTransformMode(null); setSubModule('mechanics'); }} onPointerMissed={() => { if (eyeDropMode) return; setSelectedPath(null); setSubModule('properties'); }} > {path.points.map((point, index) => ( (sphereRefs.current[point.uuid] = el!)} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, point: sphereRefs.current[point.uuid] }); setSubModule('mechanics'); setSelectedPath(null); }} userData={{ point, path }} onPointerMissed={() => { if (eyeDropMode) return; setSubModule('properties'); setSelectedActionSphere(null); }} > ))} {points.slice(0, -1).map((point, index) => { const nextPoint = points[index + 1]; const segmentCurve = new THREE.CatmullRomCurve3([point, nextPoint]); const tubeGeometry = new THREE.TubeGeometry(segmentCurve, 20, 0.1, 16, false); return ( ); })} ); } else if (path.type === 'Vehicle') { return ( (groupRefs.current[path.modeluuid] = el!)} position={path.assetPosition} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedPath({ path, group: groupRefs.current[path.modeluuid] }); setSelectedActionSphere(null); setTransformMode(null); setSubModule('mechanics'); }} onPointerMissed={() => { if (eyeDropMode) return; setSelectedPath(null); setSubModule('properties'); }} > (sphereRefs.current[path.point.uuid] = el!)} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, point: sphereRefs.current[path.point.uuid] }); setSubModule('mechanics'); setSelectedPath(null); }} userData={{ point: path.point, path }} onPointerMissed={() => { if (eyeDropMode) return; setSubModule('properties'); setSelectedActionSphere(null); }} > ); } return null; })} {selectedActionSphere && transformMode && ( )} ); } export default PathCreation;