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, useSimulationStates, } from "../../../store/store"; import { useFrame, useThree } from "@react-three/fiber"; import { useSubModuleStore } from "../../../store/useModuleStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; 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 { setSelectedPath } = useSelectedPath(); const { simulationStates, setSimulationStates } = useSimulationStates(); const { isConnecting } = useIsConnecting(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); 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 = simulationStates.map((path) => { if (path.type === "Conveyor") { return { ...path, points: path.points.map((point) => point.uuid === selectedActionSphere.points.uuid ? { ...point, position: [ selectedActionSphere.points.position.x, selectedActionSphere.points.position.y, selectedActionSphere.points.position.z, ], rotation: [ selectedActionSphere.points.rotation.x, selectedActionSphere.points.rotation.y, selectedActionSphere.points.rotation.z, ], } : point ), }; } else { return path; } }) as Types.ConveyorEventsSchema[]; const updatedPath = updatedPaths.find( (path) => path.type === "Conveyor" && path.points.some((point) => point.uuid === selectedActionSphere.points.uuid) ); // console.log("Updated Path:", updatedPath); setSimulationStates(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 updateBackend = async (updatedPath: Types.VehicleEventsSchema | undefined) => { if (!updatedPath) return; const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : ""; await setEventApi( organization, updatedPath.modeluuid, { type: "Vehicle", points: updatedPath.points } ); } const handlePointUpdate = (pointType: "start" | "end", x: number, z: number) => { if (!selectedActionSphere?.points?.uuid) return; const updatedPaths = simulationStates.map((path) => { if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) { return { ...path, points: { ...path.points, actions: { ...path.points.actions, [pointType]: { ...path.points.actions[pointType], x: x, y: z, }, }, }, }; } return path; }); const updatedPath = updatedPaths.find((path): path is Types.VehicleEventsSchema => path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid); updateBackend(updatedPath); setSimulationStates(updatedPaths); }; return ( {simulationStates.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, points: sphereRefs.current[point.uuid], }); setSubModule("mechanics"); setSelectedPath(null); }} userData={{ points, 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.points.uuid] = el!)} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, points: sphereRefs.current[path.points.uuid], }); setSubModule("mechanics"); setSelectedPath(null); }} userData={{ points: path.points, path }} onPointerMissed={() => { if (eyeDropMode) return; setSubModule("properties"); setSelectedActionSphere(null); }} > ); } else if (path.type === "StaticMachine") { 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"); }} > (sphereRefs.current[path.points.uuid] = el!)} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, points: sphereRefs.current[path.points.uuid], }); setSubModule("mechanics"); setSelectedPath(null); }} userData={{ points: path.points, path }} onPointerMissed={() => { if (eyeDropMode) return; setSubModule("properties"); setSelectedActionSphere(null); }} > ); } else if (path.type === "ArmBot") { 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"); }} > (sphereRefs.current[path.points.uuid] = el!)} onClick={(e) => { if (isConnecting || eyeDropMode) return; e.stopPropagation(); setSelectedActionSphere({ path, points: sphereRefs.current[path.points.uuid], }); setSubModule("mechanics"); setSelectedPath(null); }} userData={{ points: path.points, path }} onPointerMissed={() => { if (eyeDropMode) return; setSubModule("properties"); setSelectedActionSphere(null); }} > ); } return null; })} {selectedActionSphere && transformMode && ( )} ); } export default PathCreation;