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"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { const { isPlaying } = usePlayButtonStore(); 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) && !isPlaying); } }); }); 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.position} rotation={path.rotation} 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.position} 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;