import { useEffect, useRef, useState } from 'react' import { useGLTF } from '@react-three/drei'; import { useFrame, useThree } from '@react-three/fiber'; import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore'; import { useProductContext } from '../../../products/productContext'; import { useSceneContext } from '../../../../scene/sceneContext'; import { Group, Plane, Vector2, Vector3 } from 'three'; import { useVersionContext } from '../../../../builder/version/versionContext'; import { useParams } from 'react-router-dom'; import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb"; import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb"; import assembly from "../../../../../assets/gltf-glb/ui/human-ui-assembly.glb"; import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi'; function HumanUi() { const { scene: startScene } = useGLTF(startPoint) as any; const { scene: endScene } = useGLTF(startEnd) as any; const { scene: assemblyScene } = useGLTF(assembly) as any; const startMarker = useRef(null); const endMarker = useRef(null); const assemblyMarker = useRef(null); const outerGroup = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); const { controls, raycaster, camera } = useThree(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedProductStore } = useProductContext(); const { humanStore, productStore } = useSceneContext(); const { selectedProduct } = selectedProductStore(); const { humans, getHumanById } = humanStore(); const { updateEvent, updateAction, getActionByUuid } = productStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]); const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]); const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]); const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 0]); const [assemblyRotation, setAssemblyRotation] = useState<[number, number, number]>([0, 0, 0]); const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]); const { isDragging, setIsDragging } = useIsDragging(); const { isRotating, setIsRotating } = useIsRotating(); const plane = useRef(new Plane(new Vector3(0, 1, 0), 0)); const dragOffset = useRef(new Vector3()); const [selectedHumanData, setSelectedHumanData] = useState<{ position: [number, number, number]; rotation: [number, number, number]; }>({ position: [0, 0, 0], rotation: [0, 0, 0] }); const { selectedAction } = useSelectedAction(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const currentAction = getActionByUuid(selectedProduct.productUuid, selectedAction.actionId || ''); const isAssembly = currentAction?.actionType === 'assembly'; 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 || !selectedAction?.actionId) return; const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid); if (!selectedHuman || !selectedHuman.point?.actions) return; setSelectedHumanData({ position: selectedHuman.position, rotation: selectedHuman.rotation, }); if (outerGroup.current) { outerGroup.current.position.set( selectedHuman.position[0], selectedHuman.position[1], selectedHuman.position[2] ); } const action = selectedHuman.point.actions.find(a => a.actionUuid === selectedAction.actionId); if (!action) return; if (isAssembly) { if (action.assemblyPoint?.position && outerGroup.current) { const worldPos = new Vector3(...action.assemblyPoint.position); const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); setAssemblyPosition([localPosition.x, 1, localPosition.z]); setAssemblyRotation(action.assemblyPoint.rotation || [0, 0, 0]); } else { setAssemblyPosition([0, 1, 0]); setAssemblyRotation([0, 0, 0]); } } else { if (action.pickUpPoint?.position && outerGroup.current) { const worldPos = new Vector3(...action.pickUpPoint.position); const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); setStartPosition([localPosition.x, 1, localPosition.z]); setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]); } else { setStartPosition([0, 1, 0]); setStartRotation([0, Math.PI, 0]); } if (action.dropPoint?.position && outerGroup.current) { const worldPos = new Vector3(...action.dropPoint.position); const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); setEndPosition([localPosition.x, 1, localPosition.z]); setEndRotation(action.dropPoint.rotation || [0, Math.PI, 0]); } else { setEndPosition([0, 1, 0]); setEndRotation([0, 0, 0]); } } }, [selectedEventSphere, outerGroup.current, selectedAction, humans]); const handlePointerDown = ( e: any, state: "start" | "end" | "assembly", rotation: "start" | "end" | "assembly" ) => { e.stopPropagation(); const intersection = new Vector3(); const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1); raycaster.setFromCamera(pointer, camera); const intersects = raycaster.ray.intersectPlane(plane.current, intersection); if (e.object.parent.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); setIsDragging(null); } else { setIsDragging(state); setIsRotating(null); } if (intersects) { let localPoint: Vector3 | null = null; if (outerGroup.current) { localPoint = outerGroup.current.worldToLocal(intersection.clone()); } const marker = state === "start" ? startMarker.current : state === "end" ? endMarker.current : assemblyMarker.current; if (marker && localPoint) { const markerPos = new Vector3().copy(marker.position); dragOffset.current.copy(markerPos.sub(localPoint)); } } if (controls) (controls as any).enabled = false; }; const handlePointerUp = () => { (controls as any).enabled = true; setIsDragging(null); setIsRotating(null); if (!selectedEventSphere?.userData.modelUuid || !selectedAction?.actionId) return; const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid); if (!selectedHuman || !outerGroup.current || !currentAction) return; const updatedActions = selectedHuman.point.actions.map(action => { if (action.actionUuid !== currentAction.actionUuid) return action; if (isAssembly) { if (!assemblyMarker.current || !outerGroup.current) return action; const worldPosAssembly = new Vector3(...assemblyPosition); const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone()); return { ...action, assemblyPoint: { position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number], rotation: assemblyRotation }, }; } else { if (!startMarker.current || !endMarker.current || !outerGroup.current) return action; const worldPosStart = new Vector3(...startPosition); const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone()); const worldPosEnd = new Vector3(...endPosition); const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone()); return { ...action, pickUpPoint: { position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number], rotation: startRotation, }, dropPoint: { position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number], rotation: endRotation, }, }; } }); const event = updateEvent( selectedProduct.productUuid, selectedEventSphere.userData.modelUuid, { ...selectedHuman, point: { ...selectedHuman.point, actions: updatedActions, }, } ); 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()).add(dragOffset.current); if (isDragging === "start") { setStartPosition([localPoint.x, 1, localPoint.z]); } else if (isDragging === "end") { setEndPosition([localPoint.x, 1, localPoint.z]); } else if (isDragging === "assembly") { setAssemblyPosition([localPoint.x, 1, localPoint.z]); } }); 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 : isRotating === "end" ? endMarker.current : assemblyMarker.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 if (isRotating === "end") { setEndRotation([ marker.rotation.x, marker.rotation.y, marker.rotation.z, ]); } else { setAssemblyRotation([ marker.rotation.x, marker.rotation.y, marker.rotation.z, ]); } } }); useEffect(() => { const handleGlobalPointerUp = () => { setIsDragging(null); setIsRotating(null); if (controls) (controls as any).enabled = true; handlePointerUp(); }; if (isDragging || isRotating) { window.addEventListener("pointerup", handleGlobalPointerUp); } return () => { window.removeEventListener("pointerup", handleGlobalPointerUp); }; }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyPosition, assemblyRotation]); return ( <> {selectedHumanData && ( {isAssembly ? ( { if (e.object.parent.name === "handle") { handlePointerDown(e, "assembly", "assembly"); } else { handlePointerDown(e, "assembly", "assembly"); } }} onPointerMissed={() => { setIsDragging(null); setIsRotating(null); if (controls) (controls as any).enabled = true; }} /> ) : ( <> { if (e.object.parent.name === "handle") { handlePointerDown(e, "start", "start"); } else { handlePointerDown(e, "start", "start"); } }} onPointerMissed={() => { setIsDragging(null); setIsRotating(null); if (controls) (controls as any).enabled = true; }} /> { if (e.object.parent.name === "handle") { handlePointerDown(e, "end", "end"); } else { handlePointerDown(e, "end", "end"); } }} onPointerMissed={() => { setIsDragging(null); setIsRotating(null); if (controls) (controls as any).enabled = true; }} /> )} )} ); } export default HumanUi;