diff --git a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx index fefad50..1b1332d 100644 --- a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx +++ b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx @@ -1,18 +1,20 @@ import { useEffect, useState } from 'react'; import * as THREE from 'three'; import { useFrame, useThree } from '@react-three/fiber'; -import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useSceneContext } from '../../../../scene/sceneContext'; function PillarJibAnimator({ crane, points, + setPoints, animationPhase, setAnimationPhase, onAnimationComplete }: { crane: CraneStatus; points: [THREE.Vector3, THREE.Vector3] | null; + setPoints: (points: [THREE.Vector3, THREE.Vector3] | null) => void; animationPhase: string; setAnimationPhase: (phase: string) => void; onAnimationComplete: (action: string) => void; @@ -22,20 +24,46 @@ function PillarJibAnimator({ const { resetAsset } = assetStore(); const { isPaused } = usePauseButtonStore(); const { isPlaying } = usePlayButtonStore(); + const { isReset } = useResetButtonStore(); const { speed } = useAnimationPlaySpeed(); const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>(); - const [currentTargetIndex, setCurrentTargetIndex] = useState(0); useEffect(() => { - if (!isPlaying) { + if (!isPlaying || isReset) { resetAsset(crane.modelUuid); setAnimationPhase('idle'); - setCurrentTargetIndex(0); - } else if (animationPhase === 'idle') { - setAnimationPhase('init-hook-adjust'); + setPoints(null); } - }, [isPlaying, scene, crane.modelUuid]); + }, [isPlaying, scene, crane.modelUuid, isReset]); + + useEffect(() => { + const model = scene.getObjectByProperty('uuid', crane.modelUuid); + + if (!model) return; + const hook = model.getObjectByName('hook'); + + if (!hook) return; + const hookWorld = new THREE.Vector3(); + hook.getWorldPosition(hookWorld); + + if (crane.currentPhase === 'init-pickup') { + if (crane.currentMaterials.length > 0) { + const material = scene.getObjectByProperty('uuid', crane.currentMaterials[0].materialId); + if (material) { + const materialWorld = new THREE.Vector3(); + material.getWorldPosition(materialWorld); + setAnimationPhase('init-hook-adjust'); + setPoints( + [ + new THREE.Vector3(hookWorld.x, hookWorld.y, hookWorld.z), + new THREE.Vector3(materialWorld.x, materialWorld.y + 0.5, materialWorld.z) + ] + ); + } + } + } + }, [crane.currentPhase]) useEffect(() => { const model = scene.getObjectByProperty('uuid', crane.modelUuid); @@ -99,7 +127,7 @@ function PillarJibAnimator({ }); setClampedPoints(newClampedPoints); - }, [crane.modelUuid]); + }, [crane.modelUuid, points]); useFrame(() => { if (!isPlaying || isPaused || !points || !clampedPoints || animationPhase === 'idle') return; @@ -122,10 +150,10 @@ function PillarJibAnimator({ if (!model.userData.animationData) { model.userData.animationData = { originalHookY: hook.position.y, - targetHookY: clampedPoints[currentTargetIndex].y - baseWorld.y + 0.5, + targetHookY: clampedPoints[0].y - baseWorld.y, targetDirection: new THREE.Vector2(), targetTrolleyX: 0, - targetWorldPosition: clampedPoints[currentTargetIndex].clone(), + targetWorldPosition: clampedPoints[0].clone(), finalHookTargetY: 0, }; } @@ -137,11 +165,12 @@ function PillarJibAnimator({ switch (animationPhase) { case 'init-hook-adjust': { - const direction = Math.sign(animationData.targetHookY - hook.position.y); + const hookWorld = new THREE.Vector3(); + hook.getWorldPosition(hookWorld); + const direction = Math.sign((clampedPoints[0].y - baseWorld.y) - hookWorld.y); hook.position.y += direction * hookSpeed; - if (parseFloat(Math.abs(hook.position.y - animationData.targetHookY).toFixed(2)) < 0.05) { - hook.position.y = animationData.targetHookY; + if (parseFloat(Math.abs(hookWorld.y - clampedPoints[0].y).toFixed(2)) < 0.05) { setAnimationPhase('init-rotate-base'); } break; @@ -154,7 +183,7 @@ function PillarJibAnimator({ const currentDir = new THREE.Vector2(baseForward.x, baseForward.z).normalize(); - const targetWorld = clampedPoints[currentTargetIndex]; + const targetWorld = clampedPoints[0]; const targetDir = new THREE.Vector2( targetWorld.x - baseWorld.x, targetWorld.z - baseWorld.z @@ -170,7 +199,7 @@ function PillarJibAnimator({ base.rotation.y += Math.sign(angleDiff) * rotationSpeed; } else { base.rotation.y += angleDiff; - const localTarget = trolley.parent.worldToLocal(clampedPoints[currentTargetIndex].clone()); + const localTarget = trolley.parent.worldToLocal(clampedPoints[0].clone()); animationData.targetTrolleyX = localTarget?.x; setAnimationPhase('init-move-trolley'); } @@ -185,7 +214,7 @@ function PillarJibAnimator({ if (parseFloat(Math.abs(dx).toFixed(2)) < 0.05) { trolley.position.x = animationData.targetTrolleyX; - animationData.finalHookTargetY = hook.position.y - 0.5; + animationData.finalHookTargetY = hook.position.y; setAnimationPhase('init-final-hook-adjust'); } break; @@ -199,22 +228,87 @@ function PillarJibAnimator({ if (parseFloat(Math.abs(dy).toFixed(2)) < 0.05) { hook.position.y = animationData.finalHookTargetY; - if (currentTargetIndex < points.length - 1) { - setCurrentTargetIndex(currentTargetIndex + 1); + model.userData.animationData = { + originalHookY: hook.position.y, + targetHookY: clampedPoints[1].y - baseWorld.y + 0.5, + targetDirection: new THREE.Vector2(), + targetTrolleyX: 0, + targetWorldPosition: clampedPoints[1].clone(), + finalHookTargetY: 0, + }; - model.userData.animationData = { - originalHookY: hook.position.y, - targetHookY: clampedPoints[currentTargetIndex + 1].y - baseWorld.y + 0.5, - targetDirection: new THREE.Vector2(), - targetTrolleyX: 0, - targetWorldPosition: clampedPoints[currentTargetIndex + 1].clone(), - finalHookTargetY: 0, - }; + setAnimationPhase('starting'); + onAnimationComplete('starting'); + } + break; + } + case 'first-hook-adjust': { + const direction = Math.sign(animationData.targetHookY - hook.position.y); + hook.position.y += direction * hookSpeed; + + if (parseFloat(Math.abs(hook.position.y - animationData.targetHookY).toFixed(2)) < 0.05) { + hook.position.y = animationData.targetHookY; + setAnimationPhase('first-rotate-base'); + } + break; + } + + case 'first-rotate-base': { + const baseForward = new THREE.Vector3(1, 0, 0); + base.localToWorld(baseForward); + baseForward.sub(baseWorld); + + const currentDir = new THREE.Vector2(baseForward.x, baseForward.z).normalize(); + + const targetWorld = clampedPoints[1]; + const targetDir = new THREE.Vector2( + targetWorld.x - baseWorld.x, + targetWorld.z - baseWorld.z + ).normalize(); + + const currentAngle = Math.atan2(currentDir.y, currentDir.x); + const targetAngle = Math.atan2(targetDir.y, targetDir.x); + let angleDiff = currentAngle - targetAngle; + + angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff)); + + if (parseFloat(Math.abs(angleDiff).toFixed(2)) > 0.05) { + base.rotation.y += Math.sign(angleDiff) * rotationSpeed; + } else { + base.rotation.y += angleDiff; + const localTarget = trolley.parent.worldToLocal(clampedPoints[1].clone()); + animationData.targetTrolleyX = localTarget?.x; + setAnimationPhase('first-move-trolley'); + } + break; + } + + case 'first-move-trolley': { + const dx = animationData.targetTrolleyX - trolley.position.x; + const direction = Math.sign(dx); + trolley.position.x += direction * trolleySpeed; + + if (parseFloat(Math.abs(dx).toFixed(2)) < 0.05) { + trolley.position.x = animationData.targetTrolleyX; + + animationData.finalHookTargetY = hook.position.y - 0.5; + setAnimationPhase('first-final-hook-adjust'); + } + break; + } + + case 'first-final-hook-adjust': { + const dy = animationData.finalHookTargetY - hook.position.y; + const direction = Math.sign(dy); + hook.position.y += direction * hookSpeed; + + if (parseFloat(Math.abs(dy).toFixed(2)) < 0.05) { + hook.position.y = animationData.finalHookTargetY; + if (crane.currentPhase === 'init-pickup') { setAnimationPhase('picking'); onAnimationComplete('picking'); - - } else { + } else if (crane.currentPhase === 'pickup-drop') { setAnimationPhase('dropping'); onAnimationComplete('dropping'); } diff --git a/app/src/modules/simulation/crane/instances/helper/pillarJibHelper.tsx b/app/src/modules/simulation/crane/instances/helper/pillarJibHelper.tsx index 84883fa..8998865 100644 --- a/app/src/modules/simulation/crane/instances/helper/pillarJibHelper.tsx +++ b/app/src/modules/simulation/crane/instances/helper/pillarJibHelper.tsx @@ -8,7 +8,7 @@ function PillarJibHelper({ points }: { crane: CraneStatus, - points: [THREE.Vector3, THREE.Vector3]; + points: [THREE.Vector3, THREE.Vector3] | null; }) { const { scene } = useThree(); const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>(); @@ -22,7 +22,7 @@ function PillarJibHelper({ const trolley = model.getObjectByName('trolley'); const hook = model.getObjectByName('hook'); - if (!base || !trolley || !hook) return { geometry: null, position: null }; + if (!base || !trolley || !hook || !points) return { geometry: null, position: null }; const baseWorld = new THREE.Vector3(); base.getWorldPosition(baseWorld); @@ -94,7 +94,7 @@ function PillarJibHelper({ setIsInside(newIsInside); return { geometry, position }; - }, [scene, crane.modelUuid]); + }, [scene, crane.modelUuid, points]); if (!geometry || !position) return null; @@ -115,7 +115,7 @@ function PillarJibHelper({ /> - {points.map((point, i) => ( + {points && points.map((point, i) => ( ('idle'); const [animationPhase, setAnimationPhase] = useState('idle'); const [points, setPoints] = useState<[THREE.Vector3, THREE.Vector3] | null>(null); @@ -23,22 +22,17 @@ function PillarJibInstance({ crane }: { crane: CraneStatus }) { const action = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || ''); if (!action || action.actionType !== 'pickAndDrop') return; - if (!crane.isActive && currentPhase === 'idle' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length) { - console.log('crane: ', crane); - + if (!crane.isActive && crane.currentPhase === 'init' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length) { + setCurrentPhase(crane.modelUuid, 'init-pickup'); } } - }, [crane, currentPhase]) + }, [crane]) const handleAnimationComplete = (action: string) => { - if (action === 'picking') { - setTimeout(() => { - setAnimationPhase('init-hook-adjust'); - }, 3000) - } else if (action === 'dropping') { - setTimeout(() => { - setAnimationPhase('idle'); - }, 3000) + if (action === 'starting') { + setAnimationPhase('first-hook-adjust'); + } else if (action === 'picking') { + setCurrentPhase(crane.modelUuid, 'picking'); } } @@ -49,18 +43,16 @@ function PillarJibInstance({ crane }: { crane: CraneStatus }) { key={crane.modelUuid} crane={crane} points={points} + setPoints={setPoints} animationPhase={animationPhase} setAnimationPhase={setAnimationPhase} onAnimationComplete={handleAnimationComplete} /> - {/* */} + points={points} + /> )