From 2760e73ab8393a9627b1ffff2ec38961019dd99b Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 7 Aug 2025 16:49:14 +0530 Subject: [PATCH] crane animator --- .../instances/animator/pillarJibAnimator.tsx | 68 +++++++++++++------ .../crane/instances/craneInstances.tsx | 2 +- .../instances/instance/pillarJibInstance.tsx | 18 +++++ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx index 8c28254..d287d04 100644 --- a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx +++ b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx @@ -5,7 +5,19 @@ import { Sphere, Box } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useSceneContext } from '../../../../scene/sceneContext'; -function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THREE.Vector3, THREE.Vector3]; }) { +function PillarJibAnimator({ + crane, + points, + animationPhase, + setAnimationPhase, + onAnimationComplete +}: { + crane: CraneStatus; + points: [THREE.Vector3, THREE.Vector3]; + animationPhase: string; + setAnimationPhase: (phase: string) => void; + onAnimationComplete: (action: string) => void; +}) { const { scene } = useThree(); const { assetStore } = useSceneContext(); const { resetAsset } = assetStore(); @@ -15,12 +27,13 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>(); const [isInside, setIsInside] = useState<[boolean, boolean]>([false, false]); - const [animationPhase, setAnimationPhase] = useState('idle'); // 0: idle, 1: init-hook-adjust, 2: 'rotate-base', 3: 'move-trolley', 4: 'final-hook-adjust' + const [currentTargetIndex, setCurrentTargetIndex] = useState(0); useEffect(() => { if (!isPlaying) { resetAsset(crane.modelUuid); setAnimationPhase('idle'); + setCurrentTargetIndex(0); } else if (animationPhase === 'idle') { setAnimationPhase('init-hook-adjust'); } @@ -89,10 +102,10 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR setClampedPoints(newClampedPoints); setIsInside(newIsInside); - }, [scene, points, crane.modelUuid]); + }, [crane.modelUuid]); useFrame(() => { - if (!isPlaying || isPaused || !points || animationPhase === 'idle') return; + if (!isPlaying || isPaused || !points || !clampedPoints || animationPhase === 'idle') return; const model = scene.getObjectByProperty('uuid', crane.modelUuid); if (!model) return; @@ -101,7 +114,7 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR const trolley = model.getObjectByName('trolley'); const hook = model.getObjectByName('hook'); - if (!base || !trolley || !hook || !clampedPoints || !trolley.parent) return; + if (!base || !trolley || !hook || !trolley.parent) return; const baseWorld = new THREE.Vector3(); base.getWorldPosition(baseWorld); @@ -112,10 +125,10 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR if (!model.userData.animationData) { model.userData.animationData = { originalHookY: hook.position.y, - targetHookY: points[0].y - baseWorld.y + 0.5, + targetHookY: clampedPoints[currentTargetIndex].y - baseWorld.y + 0.5, targetDirection: new THREE.Vector2(), targetTrolleyX: 0, - targetWorldPosition: points[0].clone(), + targetWorldPosition: clampedPoints[currentTargetIndex].clone(), finalHookTargetY: 0, }; } @@ -132,22 +145,19 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR if (parseFloat(Math.abs(hook.position.y - animationData.targetHookY).toFixed(2)) < 0.05) { hook.position.y = animationData.targetHookY; - setAnimationPhase('rotate-base'); + setAnimationPhase('init-rotate-base'); } break; } - case 'rotate-base': { - const baseWorld = new THREE.Vector3(); - base.getWorldPosition(baseWorld); - + case 'init-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[0]; + const targetWorld = clampedPoints[currentTargetIndex]; const targetDir = new THREE.Vector2( targetWorld.x - baseWorld.x, targetWorld.z - baseWorld.z @@ -163,15 +173,14 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR base.rotation.y += Math.sign(angleDiff) * rotationSpeed; } else { base.rotation.y += angleDiff; - const localTarget = trolley.parent.worldToLocal(clampedPoints[0].clone()); + const localTarget = trolley.parent.worldToLocal(clampedPoints[currentTargetIndex].clone()); animationData.targetTrolleyX = localTarget?.x; - setAnimationPhase('move-trolley'); + setAnimationPhase('init-move-trolley'); } - break; } - case 'move-trolley': { + case 'init-move-trolley': { const dx = animationData.targetTrolleyX - trolley.position.x; const direction = Math.sign(dx); trolley.position.x += direction * trolleySpeed; @@ -180,19 +189,38 @@ function PillarJibAnimator({ crane, points }: { crane: CraneStatus; points: [THR trolley.position.x = animationData.targetTrolleyX; animationData.finalHookTargetY = hook.position.y - 0.5; - setAnimationPhase('final-hook-adjust'); + setAnimationPhase('init-final-hook-adjust'); } break; } - case 'final-hook-adjust': { + case 'init-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; - setAnimationPhase('idle'); + + if (currentTargetIndex < points.length - 1) { + setCurrentTargetIndex(currentTargetIndex + 1); + + 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('picking'); + onAnimationComplete('picking'); + + } else { + setAnimationPhase('dropping'); + onAnimationComplete('dropping'); + } } break; } diff --git a/app/src/modules/simulation/crane/instances/craneInstances.tsx b/app/src/modules/simulation/crane/instances/craneInstances.tsx index bd6a96b..240062c 100644 --- a/app/src/modules/simulation/crane/instances/craneInstances.tsx +++ b/app/src/modules/simulation/crane/instances/craneInstances.tsx @@ -12,7 +12,7 @@ function CraneInstances() { {crane.subType === "pillarJib" && - + } diff --git a/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx b/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx index a736a24..d6b8fd8 100644 --- a/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx +++ b/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx @@ -1,21 +1,39 @@ +import { useState } from 'react'; import * as THREE from 'three' import PillarJibAnimator from '../animator/pillarJibAnimator' import PillarJibHelper from '../helper/pillarJibHelper' function PillarJibInstance({ crane }: { crane: CraneStatus }) { + const [animationPhase, setAnimationPhase] = useState('idle'); const position1: [number, number, number] = [5, 1, -4]; const position2: [number, number, number] = [-2, 2, -2]; + const handleAnimationComplete = (action: string) => { + if (action === 'picking') { + setTimeout(() => { + setAnimationPhase('init-hook-adjust'); + }, 3000) + } else if (action === 'dropping') { + setTimeout(() => { + setAnimationPhase('idle'); + }, 3000) + } + } + return ( <>