import { useEffect, useRef, useState } from "react"; import * as Types from "../../../../types/world/worldTypes"; import { useGLTF } from "@react-three/drei"; import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedEventSphere, useIsDragging, useIsRotating, } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { DoubleSide, Group, Plane, Vector3 } from "three"; import startPoint from "../../../../assets/gltf-glb/ui/arrow_green.glb"; import startEnd from "../../../../assets/gltf-glb/ui/arrow_red.glb"; import { useSceneContext } from "../../../scene/sceneContext"; import { useProductContext } from "../../products/productContext"; import { useParams } from "react-router-dom"; import { useVersionContext } from "../../../builder/version/versionContext"; const VehicleUI = () => { const { scene: startScene } = useGLTF(startPoint) as any; const { scene: endScene } = useGLTF(startEnd) as any; const startMarker = useRef(null); const endMarker = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedProductStore } = useProductContext(); const { vehicleStore, productStore } = useSceneContext(); const { selectedProduct } = selectedProductStore(); const { vehicles, getVehicleById } = vehicleStore(); const { updateEvent } = productStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0,]); const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0,]); const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0,]); const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0,]); const [steeringRotation, setSteeringRotation] = useState<[number, number, number]>([0, 0, 0]); const { isDragging, setIsDragging } = useIsDragging(); const { isRotating, setIsRotating } = useIsRotating(); const { raycaster } = useThree(); const [point, setPoint] = useState<[number, number, number]>([0, 0, 0]); const plane = useRef(new Plane(new Vector3(0, 1, 0), 0)); const [tubeRotation, setTubeRotation] = useState(false); const tubeRef = useRef(null); const outerGroup = useRef(null); const state: Types.ThreeState = useThree(); const controls: any = state.controls; const [selectedVehicleData, setSelectedVehicleData] = useState<{ position: [number, number, number]; rotation: [number, number, number]; }>({ position: [0, 0, 0], rotation: [0, 0, 0] }); const CIRCLE_RADIUS = 0.8; const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const updateBackend = ( productName: string, productUuid: string, projectId: string, eventData: EventsSchema ) => { upsertProductOrEventApi({ productName: productName, productUuid: productUuid, projectId: projectId, eventDatas: eventData, versionId: selectedVersion?.versionId || '', }); }; useEffect(() => { if (!selectedEventSphere) return; const selectedVehicle = getVehicleById( selectedEventSphere.userData.modelUuid ); if (selectedVehicle) { setSelectedVehicleData({ position: selectedVehicle.position, rotation: selectedVehicle.rotation, }); setPoint(selectedVehicle.point.position); } setTimeout(() => { if (selectedVehicle?.point?.action) { const { pickUpPoint, unLoadPoint, steeringAngle } = selectedVehicle.point.action; if (pickUpPoint && outerGroup.current) { const worldPos = new Vector3( pickUpPoint.position.x, pickUpPoint.position.y, pickUpPoint.position.z ); const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); setStartPosition([ localPosition.x, selectedVehicle.point.position[1], localPosition.z, ]); setStartRotation([ pickUpPoint.rotation.x, pickUpPoint.rotation.y, pickUpPoint.rotation.z, ]); } else { setStartPosition([0, selectedVehicle.point.position[1] + 0.1, 1.5]); setStartRotation([0, 0, 0]); } // end point if (unLoadPoint && outerGroup.current) { const worldPos = new Vector3( unLoadPoint.position.x, unLoadPoint.position.y, unLoadPoint.position.z ); const localPosition = outerGroup.current.worldToLocal(worldPos); setEndPosition([ localPosition.x, selectedVehicle.point.position[1], localPosition.z, ]); setEndRotation([ unLoadPoint.rotation.x, unLoadPoint.rotation.y, unLoadPoint.rotation.z, ]); } else { setEndPosition([0, selectedVehicle.point.position[1] + 0.1, -1.5]); setEndRotation([0, 0, 0]); } setSteeringRotation([0, steeringAngle, 0]); } }, 10); }, [selectedEventSphere, outerGroup.current, vehicles]); const handlePointerDown = ( e: any, state: "start" | "end", rotation: "start" | "end" ) => { if (e.object.name === "handle") { const normalizedX = (e.clientX / window.innerWidth) * 2 - 1; const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1; prevMousePos.current = { x: normalizedX, y: normalizedY }; setIsRotating(rotation); if (controls) controls.enabled = false; setIsDragging(null); } else { setIsDragging(state); setIsRotating(null); if (controls) controls.enabled = false; } }; const handlePointerUp = () => { controls.enabled = true; setIsDragging(null); setIsRotating(null); if (selectedEventSphere?.userData.modelUuid) { const updatedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); let globalStartPosition = null; let globalEndPosition = null; if (outerGroup.current && startMarker.current && endMarker.current) { const worldPosStart = new Vector3(...startPosition); globalStartPosition = outerGroup.current.localToWorld( worldPosStart.clone() ); const worldPosEnd = new Vector3(...endPosition); globalEndPosition = outerGroup.current.localToWorld( worldPosEnd.clone() ); } if (updatedVehicle && globalEndPosition && globalStartPosition) { const event = updateEvent( selectedProduct.productUuid, selectedEventSphere.userData.modelUuid, { point: { ...updatedVehicle.point, action: { ...updatedVehicle.point?.action, pickUpPoint: { position: { x: globalStartPosition.x, y: 0, z: globalStartPosition.z, }, rotation: { x: 0, y: startRotation[1], z: 0 }, }, unLoadPoint: { position: { x: globalEndPosition.x, y: 0, z: globalEndPosition.z, }, rotation: { x: 0, y: endRotation[1], z: 0 }, }, steeringAngle: steeringRotation[1], }, }, } ); if (event) { updateBackend( selectedProduct.productName, selectedProduct.productUuid, projectId || '', event ); } } } }; useFrame(() => { if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return; const intersectPoint = new Vector3(); const intersects = raycaster.ray.intersectPlane( plane.current, intersectPoint ); if (!intersects) return; const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone()); if (isDragging === "start") { setStartPosition([localPoint.x, point[1], localPoint.z]); } else if (isDragging === "end") { setEndPosition([localPoint.x, point[1], localPoint.z]); } }); useEffect(() => { const handleGlobalPointerUp = () => { setIsDragging(null); setIsRotating(null); setTubeRotation(false); if (controls) controls.enabled = true; handlePointerUp(); }; if (isDragging || isRotating || tubeRotation) { window.addEventListener("pointerup", handleGlobalPointerUp); } return () => { window.removeEventListener("pointerup", handleGlobalPointerUp); }; }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, tubeRotation, steeringRotation, outerGroup.current, tubeRef.current,]); const prevSteeringY = useRef(0); useFrame((state) => { if (tubeRotation) { const currentPointerX = state.pointer.x; const deltaX = currentPointerX - prevMousePos.current.x; prevMousePos.current.x = currentPointerX; const marker = tubeRef.current; if (marker) { const rotationSpeed = 2; marker.rotation.y += deltaX * rotationSpeed; setSteeringRotation([ marker.rotation.x, marker.rotation.y, marker.rotation.z, ]); } } else { prevSteeringY.current = 0; } }); useFrame((state) => { if (!isRotating) return; const currentPointerX = state.pointer.x; const deltaX = currentPointerX - prevMousePos.current.x; prevMousePos.current.x = currentPointerX; const marker = isRotating === "start" ? startMarker.current : endMarker.current; if (marker) { const rotationSpeed = 10; marker.rotation.y += deltaX * rotationSpeed; if (isRotating === "start") { setStartRotation([ marker.rotation.x, marker.rotation.y, marker.rotation.z, ]); } else { setEndRotation([ marker.rotation.x, marker.rotation.y, marker.rotation.z, ]); } } }); return selectedVehicleData ? ( { e.stopPropagation(); setTubeRotation(true); prevMousePos.current.x = e.pointer.x; controls.enabled = false; }} onPointerMissed={() => { controls.enabled = true; setTubeRotation(false); }} onPointerUp={() => { controls.enabled = true; setTubeRotation(false); }} > ( ) {/* Start Marker */} { e.stopPropagation(); handlePointerDown(e, "start", "start"); }} onPointerMissed={() => { controls.enabled = true; setIsDragging(null); setIsRotating(null); }} /> {/* End Marker */} { e.stopPropagation(); handlePointerDown(e, "end", "end"); }} onPointerMissed={() => { controls.enabled = true; setIsDragging(null); setIsRotating(null); }} /> ) : null; }; export default VehicleUI;