From 02490214d9fc1fafb16f5a2eb1b75d48fe04fad2 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 4 Jul 2025 13:14:39 +0530 Subject: [PATCH] feat: Enhance InputWithDropDown to support disabled state fix: Update Model component to manage animation states and transitions more effectively feat: Implement worker action handling in useWorkerHandler for material management feat: Add MaterialAnimator to HumanInstance for dynamic material loading feat: Extend useTriggerHandler to support interactions between humans and various entities feat: Create WorkerAction component for managing load capacity and actions feat: Introduce MaterialAnimator for human instances to visualize material loads refactor: Update asset store to manage animation completion state fix: Ensure proper handling of human materials in useHumanStore --- .../properties/AssetProperties.tsx | 2 +- .../eventProperties/actions/TravelAction.tsx | 143 ++--- .../eventProperties/actions/workerAction.tsx | 54 ++ .../components/AnimationList.tsx | 97 ---- .../mechanics/humanMechanics.tsx | 55 +- .../mechanics/vehicleMechanics.tsx | 8 - .../ui/inputs/InputWithDropDown.tsx | 3 + .../builder/asset/models/model/model.tsx | 68 +-- .../human/actionHandler/useWorkerHandler.ts | 4 +- .../instances/animator/humanAnimator.tsx | 19 +- .../instances/animator/materialAnimator.tsx | 44 ++ .../instances/instance/humanInstance.tsx | 535 +++++++++++++++++- .../triggerHandler/useTriggerHandler.ts | 188 +++++- .../instances/animator/materialAnimator.tsx | 7 +- app/src/store/builder/useAssetStore.ts | 21 +- app/src/store/simulation/useHumanStore.ts | 2 +- app/src/types/builderTypes.d.ts | 1 + 17 files changed, 990 insertions(+), 261 deletions(-) create mode 100644 app/src/components/layout/sidebarRight/properties/eventProperties/actions/workerAction.tsx delete mode 100644 app/src/components/layout/sidebarRight/properties/eventProperties/components/AnimationList.tsx create mode 100644 app/src/modules/simulation/human/instances/animator/materialAnimator.tsx diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 5cb4b4e..6607937 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -35,7 +35,7 @@ const AssetProperties: React.FC = () => { const handleAnimationClick = (animation: string) => { if (selectedFloorItem) { - setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation); + setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation, true); } } diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx index 219fce3..db11181 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx @@ -1,95 +1,70 @@ import React from "react"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; -import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; interface TravelActionProps { - loadCapacity: { - value: string; - min: number; - max: number; - defaultValue: string; - onChange: (value: string) => void; - }; - unloadDuration: { - value: string; - min: number; - max: number; - defaultValue: string; - onChange: (value: string) => void; - }; - pickPoint?: { - value: string; - onChange: (value: string) => void; - }; - unloadPoint?: { - value: string; - onChange: (value: string) => void; - }; - clearPoints: () => void; + loadCapacity: { + value: string; + min: number; + max: number; + defaultValue: string; + onChange: (value: string) => void; + }; + unloadDuration: { + value: string; + min: number; + max: number; + defaultValue: string; + onChange: (value: string) => void; + }; + clearPoints: () => void; } const TravelAction: React.FC = ({ - loadCapacity, - unloadDuration, - pickPoint, - unloadPoint, - clearPoints, + loadCapacity, + unloadDuration, + clearPoints, }) => { - return ( - <> - {}} - onChange={loadCapacity.onChange} - /> - {}} - onChange={unloadDuration.onChange} - /> -
-
-
Reset
- -
-
- {pickPoint && ( - - )} - {unloadPoint && ( - - )} - - ); + return ( + <> + { }} + onChange={loadCapacity.onChange} + /> + { }} + onChange={unloadDuration.onChange} + /> +
+
+
Reset
+ +
+
+ + ); }; export default TravelAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/workerAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/workerAction.tsx new file mode 100644 index 0000000..6cda9a0 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/workerAction.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +interface WorkerActionProps { + loadCapacity: { + value: string; + min: number; + max: number; + step: number; + defaultValue: string; + disabled?: boolean, + onChange: (value: string) => void; + }; + clearPoints: () => void; +} + +const WorkerAction: React.FC = ({ + loadCapacity, + clearPoints, +}) => { + return ( + <> + { }} + onChange={loadCapacity.onChange} + /> +
+
+
Reset
+ +
+
+ + ); +}; + +export default WorkerAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/components/AnimationList.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/components/AnimationList.tsx deleted file mode 100644 index 836432d..0000000 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/components/AnimationList.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { useRef } from "react"; -import { AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../../../icons/ExportCommonIcons"; -import { handleResize } from "../../../../../../functions/handleResizePannel"; -import { useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore"; -import RenameInput from "../../../../../ui/inputs/RenameInput"; - -interface AnimationListProps { - animationOptions: string[]; - animationSequences: { - animationUuid: string; - animationName: string; - animationType: "behaviour" | "animatedTravel"; - animation: string | null; - travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null }; - }[]; - onAddAnimation: () => void; - onRemoveAnimation: (animationUuid: string) => void; - handleAnimationSelect: (animationUuid: string) => void; - handleRenameAnimation: (animationUuid: string, newName: string) => void; -} - -const AnimationList: React.FC = ({ - animationSequences, - onAddAnimation, - onRemoveAnimation, - handleAnimationSelect, - handleRenameAnimation, -}) => { - const animationContainerRef = useRef(null); - const { selectedAction } = useSelectedAction(); - const { selectedAnimation } = useSelectedAnimation(); - - return ( -
-
-
-
Animation Sequences
- -
-
-
- {animationSequences.map((sequence) => ( -
- - {animationSequences.length > 1 && ( - - )} -
- ))} -
- {animationSequences.length > 0 && ( - - )} -
-
-
- ); -}; - -export default AnimationList; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx index e823697..1e7c423 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx @@ -4,30 +4,27 @@ import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import RenameInput from "../../../../../ui/inputs/RenameInput"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import Trigger from "../trigger/Trigger"; -import PickAndPlaceAction from "../actions/PickAndPlaceAction"; import ActionsList from "../components/ActionsList"; -import AnimationList from "../components/AnimationList"; import { useSelectedEventData, useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; import { useVersionContext } from "../../../../../../modules/builder/version/versionContext"; import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; import { useParams } from "react-router-dom"; +import WorkerAction from "../actions/workerAction"; function HumanMechanics() { const [activeOption, setActiveOption] = useState<"worker">("worker"); - const [animationOptions, setAnimationOptions] = useState([]); const [speed, setSpeed] = useState("0.5"); + const [loadCapacity, setLoadCapacity] = useState("1"); const [currentAction, setCurrentAction] = useState(); const [selectedPointData, setSelectedPointData] = useState(); const { selectedEventData } = useSelectedEventData(); - const { productStore, assetStore } = useSceneContext(); - const { getAssetById } = assetStore(); - const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction, addAction, removeAction } = productStore(); + const { productStore } = useSceneContext(); + const { getPointByUuid, updateEvent, updateAction, addAction, removeAction, getEventByModelUuid } = productStore(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction(); - const { selectedAnimation, setSelectedAnimation } = useSelectedAnimation(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); @@ -44,6 +41,13 @@ function HumanMechanics() { setSelectedPointData(point); setCurrentAction(point.action); setSelectedAction(point.action.actionUuid, point.action.actionName); + setSpeed(( + getEventByModelUuid( + selectedProduct.productUuid, + selectedEventData?.data.modelUuid || "" + ) as HumanEventSchema | undefined + )?.speed?.toString() || "1"); + setLoadCapacity(point.action.loadCapacity.toString()); } } else { clearSelectedAction(); @@ -68,6 +72,7 @@ function HumanMechanics() { clearSelectedAction(); setCurrentAction(undefined); setSpeed("0.5"); + setLoadCapacity("1"); } }, [selectedEventData, selectedProduct, selectedAction]); @@ -130,6 +135,29 @@ function HumanMechanics() { setSpeed(value); }; + const handleLoadCapacityChange = (value: string) => { + if (!currentAction || !selectedPointData || !selectedAction.actionId) return; + + const updatedAction = { ...currentAction }; + updatedAction.loadCapacity = parseInt(value) + + const updatedPoint = { ...selectedPointData, action: updatedAction }; + + const event = updateAction( + selectedProduct.productUuid, + selectedAction.actionId, + updatedAction + ); + + if (event) { + updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event); + } + + setCurrentAction(updatedAction); + setSelectedPointData(updatedPoint); + setLoadCapacity(value); + }; + const handleClearPoints = () => { if (!currentAction || !selectedPointData || !selectedAction.actionId) return; @@ -239,7 +267,18 @@ function HumanMechanics() { disabled={true} /> - +
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 78583d8..eb9e41c 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -272,14 +272,6 @@ function VehicleMechanics() { onChange: handleUnloadDurationChange, }} clearPoints={handleClearPoints} - // pickPoint={{ - // value: currentPickPoint, - // onChange: handlePickPointChange, - // }} - // unloadPoint={{ - // value: currentUnloadPoint, - // onChange: handleUnloadPointChange, - // }} /> )} diff --git a/app/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx index f749af7..61fbaf4 100644 --- a/app/src/components/ui/inputs/InputWithDropDown.tsx +++ b/app/src/components/ui/inputs/InputWithDropDown.tsx @@ -8,6 +8,7 @@ type InputWithDropDownProps = { max?: number; step?: number; defaultValue?: string; + disabled?: boolean; options?: string[]; // Array of dropdown options activeOption?: string; // The currently active dropdown option onClick?: () => void; @@ -23,6 +24,7 @@ const InputWithDropDown: React.FC = ({ max, step, defaultValue, + disabled = false, options, activeOption, onClick, @@ -54,6 +56,7 @@ const InputWithDropDown: React.FC = ({ type="number" defaultValue={value} // value={value} + disabled={disabled} onChange={(e) => { onChange(e.target.value); }} diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index ba47e9e..f3baadd 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -24,7 +24,7 @@ function Model({ asset }: { readonly asset: Asset }) { const { subModule } = useSubModuleStore(); const { activeModule } = useModuleStore(); const { assetStore, eventStore, productStore } = useSceneContext(); - const { removeAsset, setAnimations, resetAnimation } = assetStore(); + const { removeAsset, setAnimations, resetAnimation, setAnimationComplete, setCurrentAnimation: setAnmationAnimation } = assetStore(); const { setTop } = useTopData(); const { setLeft } = useLeftData(); const { getIsEventInProduct } = productStore(); @@ -49,10 +49,9 @@ function Model({ asset }: { readonly asset: Asset }) { const { userId, organization } = getUserData(); const mixerRef = useRef(); const actions = useRef<{ [name: string]: THREE.AnimationAction }>({}); - const [currentAnimation, setCurrentAnimation] = useState(null); const [previousAnimation, setPreviousAnimation] = useState(null); - const [blendFactor, setBlendFactor] = useState(0); - const blendDuration = 0.3; + const blendFactor = useRef(0); + const blendDuration = 0.5; useEffect(() => { setDeletableFloorItem(null); @@ -283,63 +282,48 @@ function Model({ asset }: { readonly asset: Asset }) { } const handleAnimationComplete = useCallback(() => { - console.log(`Animation "${currentAnimation}" completed`); - }, [currentAnimation]); + if (asset.animationState) { + setAnimationComplete(asset.modelUuid, true); + } + }, [asset.animationState]); useFrame((_, delta) => { if (mixerRef.current) { - if (blendFactor < 1) { - setBlendFactor(prev => Math.min(prev + delta / blendDuration, 1)); - } - mixerRef.current.update(delta); } }); useEffect(() => { - if (asset.animationState && asset.animationState.isPlaying) { - if (!mixerRef.current) return; + if (!asset.animationState || !mixerRef.current) return; - if (asset.animationState.current !== currentAnimation) { - setPreviousAnimation(currentAnimation); - setCurrentAnimation(asset.animationState.current); - setBlendFactor(0); + const { current, loopAnimation, isPlaying } = asset.animationState; + const currentAction = actions.current[current]; + const previousAction = previousAnimation ? actions.current[previousAnimation] : null; + + if (isPlaying && currentAction) { + blendFactor.current = 0; + + currentAction.reset(); + currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1); + currentAction.clampWhenFinished = true; + + if (previousAction && previousAction !== currentAction) { + previousAction.crossFadeTo(currentAction, blendDuration, false); } - const currentAction = actions.current[asset.animationState.current]; - const previousAction = previousAnimation ? actions.current[previousAnimation] : null; - - if (currentAction) { - const loopMode = asset.animationState.loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce; - - currentAction.reset(); - currentAction.setLoop(loopMode, loopMode === THREE.LoopRepeat ? Infinity : 1); - - currentAction.play(); - - mixerRef.current.addEventListener('finished', handleAnimationComplete); - - if (previousAction && blendFactor < 1) { - previousAction.crossFadeTo(currentAction, blendDuration, true); - } - } - - Object.entries(actions.current).forEach(([name, action]) => { - if ((asset.animationState && name !== asset.animationState.current) && name !== previousAnimation) { - action.stop(); - } - }); + currentAction.play(); + mixerRef.current.addEventListener('finished', handleAnimationComplete); + setPreviousAnimation(current); } else { Object.values(actions.current).forEach((action) => action.stop()); - setCurrentAnimation(null); } return () => { if (mixerRef.current) { mixerRef.current.removeEventListener('finished', handleAnimationComplete); } - } - }, [asset.animationState, currentAnimation, previousAnimation, handleAnimationComplete]); + }; + }, [asset.animationState?.current, asset.animationState?.isPlaying]); return ( { echo.info(`${materialUuid}, ${status}`); @@ -23,6 +23,8 @@ export function useWorkerHandler() { const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid); if (!modelUuid) return; + incrementHumanLoad(modelUuid, 1); + addCurrentMaterial(modelUuid, material.materialType, material.materialId); workerLogStatus(material.materialName, `performing worker action`); diff --git a/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx b/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx index 82934eb..8e818fa 100644 --- a/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx +++ b/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx @@ -15,8 +15,9 @@ interface HumanAnimatorProps { } function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly) { - const { humanStore } = useSceneContext(); + const { humanStore, assetStore } = useSceneContext(); const { getHumanById } = humanStore(); + const { setCurrentAnimation } = assetStore(); const { isPaused } = usePauseButtonStore(); const { isPlaying } = usePlayButtonStore(); const { speed } = useAnimationPlaySpeed(); @@ -116,6 +117,17 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start const t = (progressRef.current - accumulatedDistance) / segmentDistance; const position = start.clone().lerp(end, t); object.position.copy(position); + if (human.currentMaterials.length > 0) { + setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true); + } else { + setCurrentAnimation(human.modelUuid, 'walking', true, true, true); + } + } else { + if (human.currentMaterials.length > 0) { + setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true); + } else { + setCurrentAnimation(human.modelUuid, 'idle', true, true, true); + } } } @@ -133,6 +145,11 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start object.rotation.copy(targetEuler); setRestingRotation(false); } + if (human.currentMaterials.length > 0) { + setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true); + } else { + setCurrentAnimation(human.modelUuid, 'idle', true, true, true); + } return; } } diff --git a/app/src/modules/simulation/human/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/human/instances/animator/materialAnimator.tsx new file mode 100644 index 0000000..ffea67a --- /dev/null +++ b/app/src/modules/simulation/human/instances/animator/materialAnimator.tsx @@ -0,0 +1,44 @@ +import { useEffect, useRef, useState } from 'react'; +import { useThree } from '@react-three/fiber'; +import * as THREE from 'three'; +import { MaterialModel } from '../../../materials/instances/material/materialModel'; + +const MaterialAnimator = ({ human }: { human: HumanStatus }) => { + const meshRef = useRef(null!); + const [hasLoad, setHasLoad] = useState(false); + const { scene } = useThree(); + + useEffect(() => { + setHasLoad(human.currentLoad > 0); + }, [human.currentLoad]); + + useEffect(() => { + if (!hasLoad || !meshRef.current) return; + + const humanModel = scene.getObjectByProperty("uuid", human.modelUuid) as THREE.Object3D; + if (!humanModel) return; + + const bone = humanModel.getObjectByName('PlaceObjectRefBone') as THREE.Bone; + if (bone) { + bone.add(meshRef.current); + + meshRef.current.position.set(0, 0, 0); + meshRef.current.rotation.set(0, 0, 0); + meshRef.current.scale.set(1, 1, 1); + } + }, [hasLoad, human.modelUuid]); + + return ( + <> + {hasLoad && human.currentMaterials.length > 0 && ( + + )} + + ); +}; + +export default MaterialAnimator; diff --git a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx index d3fe30a..7d75aa7 100644 --- a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx +++ b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx @@ -8,21 +8,24 @@ import { useSceneContext } from '../../../../scene/sceneContext'; import { useProductContext } from '../../../products/productContext'; import HumanAnimator from '../animator/humanAnimator'; +import MaterialAnimator from '../animator/materialAnimator'; function HumanInstance({ human }: { human: HumanStatus }) { const { navMesh } = useNavMesh(); const { isPlaying } = usePlayButtonStore(); - const { materialStore, armBotStore, conveyorStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext(); + const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext(); const { removeMaterial, setEndTime } = materialStore(); const { getStorageUnitById } = storageUnitStore(); const { getArmBotById } = armBotStore(); const { getConveyorById } = conveyorStore(); const { getVehicleById } = vehicleStore(); + const { getMachineById } = machineStore(); const { triggerPointActions } = useTriggerHandler(); + const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore(); const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); - const { humans, setHumanActive, setHumanState, setHumanPicking, clearCurrentMaterials, setHumanLoad, decrementHumanLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore(); + const { setHumanActive, setHumanState, setHumanPicking, clearCurrentMaterials, setHumanLoad, decrementHumanLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore(); const [currentPhase, setCurrentPhase] = useState('init'); const [path, setPath] = useState<[number, number, number][]>([]); @@ -32,11 +35,11 @@ function HumanInstance({ human }: { human: HumanStatus }) { const isPausedRef = useRef(false); const isSpeedRef = useRef(0); let startTime: number; - let fixedInterval: number; const { speed } = useAnimationPlaySpeed(); const { isPaused } = usePauseButtonStore(); const previousTimeRef = useRef(null); const animationFrameIdRef = useRef(null); + const humanAsset = getAssetById(human.modelUuid); useEffect(() => { isPausedRef.current = isPaused; @@ -69,7 +72,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { }, [navMesh]); function humanStatus(modelId: string, status: string) { - console.log(`${modelId} , ${status}`); + // console.log(`${modelId} , ${status}`); } function reset() { @@ -78,6 +81,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { setHumanPicking(human.modelUuid, false); setHumanState(human.modelUuid, 'idle'); setHumanLoad(human.modelUuid, 0); + resetAnimation(human.modelUuid); setPath([]); startTime = 0; isPausedRef.current = false; @@ -110,8 +114,57 @@ function HumanInstance({ human }: { human: HumanStatus }) { setHumanState(human.modelUuid, 'running'); setHumanPicking(human.modelUuid, false); setHumanActive(human.modelUuid, true); + setCurrentAnimation(human.modelUuid, 'walking', true, true, true); humanStatus(human.modelUuid, 'Started from init, heading to pickup'); return; + } else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') { + if (humanAsset && human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) { + if (human.point.action.pickUpPoint && human.point.action.dropPoint) { + const toDrop = computePath( + new THREE.Vector3( + human.point.action.pickUpPoint.position?.[0] ?? 0, + human.point.action.pickUpPoint.position?.[1] ?? 0, + human.point.action.pickUpPoint.position?.[2] ?? 0 + ), + new THREE.Vector3( + human.point.action.dropPoint.position?.[0] ?? 0, + human.point.action.dropPoint.position?.[1] ?? 0, + human.point.action.dropPoint.position?.[2] ?? 0 + ) + ); + setPath(toDrop); + setCurrentPhase('pickup-drop'); + setHumanState(human.modelUuid, 'running'); + setHumanPicking(human.modelUuid, false); + setHumanPicking(human.modelUuid, true); + setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true); + humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point'); + } + } else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0) { + setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); + } + } else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) { + if (human.point.action.pickUpPoint && human.point.action.dropPoint) { + const dropToPickup = computePath( + new THREE.Vector3( + human.point.action.dropPoint.position?.[0] ?? 0, + human.point.action.dropPoint.position?.[1] ?? 0, + human.point.action.dropPoint.position?.[2] ?? 0 + ), + new THREE.Vector3( + human.point.action.pickUpPoint.position?.[0] ?? 0, + human.point.action.pickUpPoint.position?.[1] ?? 0, + human.point.action.pickUpPoint.position?.[2] ?? 0 + ) + ); + setPath(dropToPickup); + setCurrentPhase('drop-pickup'); + setHumanState(human.modelUuid, 'running'); + setHumanPicking(human.modelUuid, false); + setHumanActive(human.modelUuid, true); + setCurrentAnimation(human.modelUuid, 'walking', true, true, true); + humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point'); + } } } @@ -119,17 +172,488 @@ function HumanInstance({ human }: { human: HumanStatus }) { reset() } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [human, currentPhase, path, isPlaying]); + }, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]); function handleCallBack() { if (currentPhase === 'init-pickup') { setCurrentPhase('picking'); + setHumanState(human.modelUuid, 'idle'); + setHumanPicking(human.modelUuid, true); + setHumanActive(human.modelUuid, false); + setCurrentAnimation(human.modelUuid, 'idle', true, true, true); + humanStatus(human.modelUuid, 'Reached pickup point, waiting for material'); + setPath([]); } else if (currentPhase === 'pickup-drop') { + setCurrentPhase('dropping'); + setHumanState(human.modelUuid, 'idle'); + setHumanPicking(human.modelUuid, false); + setHumanActive(human.modelUuid, false); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + humanStatus(human.modelUuid, 'Reached drop point'); + setPath([]); } else if (currentPhase === 'drop-pickup') { + setCurrentPhase('picking'); + setHumanState(human.modelUuid, 'idle'); + setHumanPicking(human.modelUuid, true); + setHumanActive(human.modelUuid, false); + setPath([]); + clearCurrentMaterials(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'idle', true, true, true); + humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete'); } } + function animate(currentTime: number) { + if (previousTimeRef.current === null) { + previousTimeRef.current = currentTime; + } + + const deltaTime = (currentTime - previousTimeRef.current) / 1000; + previousTimeRef.current = currentTime; + + if (human.isActive) { + if (!isPausedRef.current) { + activeTimeRef.current += deltaTime * isSpeedRef.current; + } + } else { + if (!isPausedRef.current) { + idleTimeRef.current += deltaTime * isSpeedRef.current; + } + } + animationFrameIdRef.current = requestAnimationFrame(animate); + } + + useEffect(() => { + if (!isPlaying) return + if (!human.isActive) { + const roundedActiveTime = Math.round(activeTimeRef.current); + incrementActiveTime(human.modelUuid, roundedActiveTime); + activeTimeRef.current = 0; + } else { + const roundedIdleTime = Math.round(idleTimeRef.current); + incrementIdleTime(human.modelUuid, roundedIdleTime); + idleTimeRef.current = 0; + } + + if (animationFrameIdRef.current === null) { + animationFrameIdRef.current = requestAnimationFrame(animate); + } + + return () => { + if (animationFrameIdRef.current !== null) { + cancelAnimationFrame(animationFrameIdRef.current); + animationFrameIdRef.current = null; + } + }; + }, [human, isPlaying]); + + function startUnloadingProcess() { + const humanAsset = getAssetById(human.modelUuid); + if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) { + if (human.point.action.triggers.length > 0) { + const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid); + const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || ''); + + if (trigger && model) { + if (model.type === 'transfer') { + const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid); + if (action) { + handleMaterialDropToConveyor(model); + } + } else if (model.type === 'machine') { + const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid); + if (action) { + handleMaterialDropToMachine(model); + } + } else if (model.type === 'roboticArm') { + const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid); + if (action) { + handleMaterialDropToArmBot(model); + } + } else if (model.type === 'storageUnit') { + const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid); + if (action) { + handleMaterialDropToStorageUnit(model); + } + } else if (model.type === 'vehicle') { + const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid); + if (action) { + handleMaterialDropToVehicle(model); + } + } + } else { + const droppedMaterial = human.currentLoad; + startTime = performance.now(); + handleMaterialDropByDefault(droppedMaterial); + } + } else { + const droppedMaterial = human.currentLoad; + startTime = performance.now(); + handleMaterialDropByDefault(droppedMaterial); + } + } else { + requestAnimationFrame(startUnloadingProcess); + } + } + + function handleMaterialDropToStorageUnit(model: StorageEventSchema) { + if (model && humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } + + const checkAnimation = () => { + if (humanAsset?.animationState?.isCompleted) { + if (model.point.action.actionType === 'store') { + loopMaterialDropToStorage( + human.modelUuid, + human.currentLoad, + model.modelUuid, + model.point.action.storageCapacity, + human.point.action + ); + } + } else { + requestAnimationFrame(checkAnimation); + } + }; + checkAnimation(); + } + + function loopMaterialDropToStorage( + humanId: string, + humanCurrentLoad: number, + storageUnitId: string, + storageMaxCapacity: number, + action: HumanAction + ) { + const storageUnit = getStorageUnitById(storageUnitId); + + if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) { + return; + } + + decrementHumanLoad(humanId, 1); + humanCurrentLoad -= 1; + + const material = removeLastMaterial(humanId); + if (material) { + triggerPointActions(action, material.materialId); + } + + if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) { + resetAnimation(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + + const waitForNextDrop = () => { + if (humanAsset?.animationState?.isCompleted) { + loopMaterialDropToStorage( + humanId, + humanCurrentLoad, + storageUnitId, + storageMaxCapacity, + action + ); + } else { + requestAnimationFrame(waitForNextDrop); + } + }; + waitForNextDrop(); + } + } + + function handleMaterialDropToConveyor(model: ConveyorEventSchema) { + if (humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } + + const checkAnimation = () => { + if (humanAsset?.animationState?.isCompleted) { + const conveyor = getConveyorById(model.modelUuid); + if (conveyor) { + loopMaterialDropToConveyor( + human.modelUuid, + human.currentLoad, + conveyor.modelUuid, + human.point.action + ); + } + } else { + requestAnimationFrame(checkAnimation); + } + }; + checkAnimation(); + } + + function loopMaterialDropToConveyor( + humanId: string, + humanCurrentLoad: number, + conveyorId: string, + action: HumanAction + ) { + const conveyor = getConveyorById(conveyorId); + + if (!conveyor || humanCurrentLoad <= 0) { + return; + } + + decrementHumanLoad(humanId, 1); + humanCurrentLoad -= 1; + + const material = removeLastMaterial(humanId); + if (material) { + triggerPointActions(action, material.materialId); + } + + if (humanCurrentLoad > 0) { + resetAnimation(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + + const waitForNextDrop = () => { + if (humanAsset?.animationState?.isCompleted) { + loopMaterialDropToConveyor( + humanId, + humanCurrentLoad, + conveyorId, + action + ); + } else { + requestAnimationFrame(waitForNextDrop); + } + }; + waitForNextDrop(); + } + } + + function handleMaterialDropToArmBot(model: RoboticArmEventSchema) { + if (humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } + + const checkAnimation = () => { + if (humanAsset?.animationState?.isCompleted) { + const armBot = getArmBotById(model.modelUuid); + if (armBot && armBot.state === 'idle' && !armBot.isActive) { + loopMaterialDropToArmBot( + human.modelUuid, + human.currentLoad, + model.modelUuid, + human.point.action + ); + } + } else { + requestAnimationFrame(checkAnimation); + } + }; + checkAnimation(); + } + + function loopMaterialDropToArmBot( + humanId: string, + humanCurrentLoad: number, + armBotId: string, + action: HumanAction + ) { + const armBot = getArmBotById(armBotId); + + if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) { + return; + } + + decrementHumanLoad(humanId, 1); + humanCurrentLoad -= 1; + + const material = removeLastMaterial(humanId); + if (material) { + triggerPointActions(action, material.materialId); + } + + if (humanCurrentLoad > 0) { + resetAnimation(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + + const waitForNextTransfer = () => { + const currentArmBot = getArmBotById(armBotId); + if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) { + if (humanAsset?.animationState?.isCompleted) { + loopMaterialDropToArmBot( + humanId, + humanCurrentLoad, + armBotId, + action + ); + } else { + requestAnimationFrame(waitForNextTransfer); + } + } else { + requestAnimationFrame(waitForNextTransfer); + } + }; + waitForNextTransfer(); + } + } + + function handleMaterialDropToVehicle(model: VehicleEventSchema) { + if (humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } + + const checkAnimation = () => { + if (humanAsset?.animationState?.isCompleted) { + const vehicle = getVehicleById(model.modelUuid); + if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) { + loopMaterialDropToVehicle( + human.modelUuid, + human.currentLoad, + model.modelUuid, + human.point.action + ); + } + } else { + requestAnimationFrame(checkAnimation); + } + }; + checkAnimation(); + } + + function loopMaterialDropToVehicle( + humanId: string, + humanCurrentLoad: number, + vehicleId: string, + action: HumanAction + ) { + const vehicle = getVehicleById(vehicleId); + + if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) { + return; + } + + decrementHumanLoad(humanId, 1); + humanCurrentLoad -= 1; + + const material = removeLastMaterial(humanId); + if (material) { + triggerPointActions(action, material.materialId); + } + + if (humanCurrentLoad > 0) { + resetAnimation(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + + const waitForNextTransfer = () => { + const currentArmBot = getVehicleById(vehicleId); + if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) { + if (humanAsset?.animationState?.isCompleted) { + loopMaterialDropToArmBot( + humanId, + humanCurrentLoad, + vehicleId, + action + ); + } else { + requestAnimationFrame(waitForNextTransfer); + } + } else { + requestAnimationFrame(waitForNextTransfer); + } + }; + waitForNextTransfer(); + } + } + + function handleMaterialDropToMachine(model: MachineEventSchema) { + if (humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } + + const checkAnimation = () => { + if (humanAsset?.animationState?.isCompleted) { + const machine = getMachineById(model.modelUuid); + if (machine && machine.state === 'idle' && !machine.isActive) { + loopMaterialDropToMachine( + human.modelUuid, + human.currentLoad, + model.modelUuid, + human.point.action + ); + } + } else { + requestAnimationFrame(checkAnimation); + } + }; + checkAnimation(); + } + + function loopMaterialDropToMachine( + humanId: string, + humanCurrentLoad: number, + machineId: string, + action: HumanAction + ) { + const machine = getMachineById(machineId); + + if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) { + return; + } + + decrementHumanLoad(humanId, 1); + humanCurrentLoad -= 1; + + const material = removeLastMaterial(humanId); + if (material) { + triggerPointActions(action, material.materialId); + } + + if (humanCurrentLoad > 0) { + resetAnimation(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + + const waitForNextTransfer = () => { + const currentArmBot = getMachineById(machineId); + if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) { + if (humanAsset?.animationState?.isCompleted) { + loopMaterialDropToArmBot( + humanId, + humanCurrentLoad, + machineId, + action + ); + } else { + requestAnimationFrame(waitForNextTransfer); + } + } else { + requestAnimationFrame(waitForNextTransfer); + } + }; + waitForNextTransfer(); + } + } + + function handleMaterialDropByDefault(droppedMaterial: number) { + if (humanAsset?.animationState?.current !== 'drop') { + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + } + + if (humanAsset?.animationState?.isCompleted) { + const remainingMaterials = droppedMaterial - 1; + decrementHumanLoad(human.modelUuid, 1); + const material = removeLastMaterial(human.modelUuid); + + if (material) { + setEndTime(material.materialId, performance.now()); + removeMaterial(material.materialId); + } + + if (remainingMaterials > 0) { + resetAnimation(human.modelUuid); + setCurrentAnimation(human.modelUuid, 'drop', true, false, false); + + requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials)); + } + return; + } + + requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial)); } return ( @@ -144,6 +668,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { startUnloadingProcess={startUnloadingProcess} /> + ) } diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index cd1a67c..557fcd2 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -264,7 +264,6 @@ export function useTriggerHandler() { if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { const material = getMaterialById(materialId); if (material) { - const triggeredAction = action; // Handle current action of the material handleAction(action, materialId); @@ -291,7 +290,7 @@ export function useTriggerHandler() { if (human) { - if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < (triggeredAction as HumanAction).loadCapacity) { + if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) { setIsVisible(materialId, false); @@ -302,6 +301,9 @@ export function useTriggerHandler() { // Handle current action using Event Manager addHumanToMonitor(human.modelUuid, () => { + + setIsVisible(materialId, false); + handleAction(action, materialId); }) } @@ -433,6 +435,9 @@ export function useTriggerHandler() { } } } + } else if (toEvent?.type === 'human') { + // Vehicle to Human + } } else if (fromEvent?.type === 'machine') { if (toEvent?.type === 'transfer') { @@ -539,6 +544,9 @@ export function useTriggerHandler() { } else if (toEvent?.type === 'storageUnit') { // Machine to Storage Unit + } else if (toEvent?.type === 'human') { + // Machine to Human + } } else if (fromEvent?.type === 'roboticArm') { if (toEvent?.type === 'transfer') { @@ -812,6 +820,53 @@ export function useTriggerHandler() { } } } + } else if (toEvent?.type === 'human') { + // Robotic Arm to Vehicle + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + + setIsPaused(material.materialId, false); + + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid); + + setNextLocation(material.materialId, null); + + if (action) { + + if (human) { + + if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) { + + setIsVisible(materialId, false); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + // Handle current action from human + handleAction(action, materialId); + + } else { + + // Event Manager Needed + + } + } + } + + } + } + } } else if (fromEvent?.type === 'storageUnit') { if (toEvent?.type === 'transfer') { @@ -829,6 +884,135 @@ export function useTriggerHandler() { } else if (toEvent?.type === 'storageUnit') { // Storage Unit to Storage Unit + } else if (toEvent?.type === 'human') { + // Storage Unit to Human + + } + } else if (fromEvent?.type === 'human') { + if (toEvent?.type === 'transfer') { + // Human to Transfer + + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + setIsVisible(materialId, true); + + if (action && + action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid + ) { + setNextLocation(material.materialId, { + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, + }); + + handleAction(action, materialId); + } + + } + } + + } else if (toEvent?.type === 'vehicle') { + // Human to Vehicle + + } else if (toEvent?.type === 'machine') { + // Human to Machine + + } else if (toEvent?.type === 'roboticArm') { + // Human to Robotic Arm + + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + setNextLocation(material.materialId, null); + + setIsVisible(materialId, false); + + if (action && armBot) { + + if (armBot.isActive === false && armBot.state === 'idle') { + + // Handle current action from arm bot + handleAction(action, materialId); + + } else { + + // Event Manager Needed + + } + } + } + } + + } else if (toEvent?.type === 'storageUnit') { + // Human to Storage Unit + + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const storageUnit = getStorageUnitById(trigger.triggeredAsset?.triggeredModel.modelUuid); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + setNextLocation(material.materialId, null); + + setIsVisible(materialId, false); + + if (action && storageUnit) { + + if (storageUnit.currentLoad < storageUnit.point.action.storageCapacity) { + + // Handle current action from vehicle + handleAction(action, materialId); + + } else { + + // Event Manager Needed + + } + } + } + } } } } diff --git a/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx index fb4ee8e..6a6a32a 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx @@ -3,12 +3,7 @@ import { useThree, useFrame } from '@react-three/fiber'; import * as THREE from 'three'; import { MaterialModel } from '../../../materials/instances/material/materialModel'; -type MaterialAnimatorProps = { - agvDetail: VehicleStatus; -}; - - -const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => { +const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => { const meshRef = useRef(null!); const [hasLoad, setHasLoad] = useState(false); const { scene } = useThree(); diff --git a/app/src/store/builder/useAssetStore.ts b/app/src/store/builder/useAssetStore.ts index a9111f4..ca97bf0 100644 --- a/app/src/store/builder/useAssetStore.ts +++ b/app/src/store/builder/useAssetStore.ts @@ -22,7 +22,8 @@ interface AssetsStore { // Animation controls setAnimations: (modelUuid: string, animations: string[]) => void; - setCurrentAnimation: (modelUuid: string, current: string, isisPlaying: boolean, loopAnimation: boolean) => void; + setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean, loopAnimation: boolean, isCompleted: boolean) => void; + setAnimationComplete: (modelUuid: string, isCompleted: boolean) => void; resetAnimation: (modelUuid: string) => void; addAnimation: (modelUuid: string, animation: string) => void; removeAnimation: (modelUuid: string, animation: string) => void; @@ -150,19 +151,29 @@ export const createAssetStore = () => { if (asset) { asset.animations = animations; if (!asset.animationState) { - asset.animationState = { current: '', isPlaying: false, loopAnimation: true }; + asset.animationState = { current: '', isPlaying: false, loopAnimation: true, isCompleted: true }; } } }); }, - setCurrentAnimation: (modelUuid, current, isisPlaying, loopAnimation) => { + setCurrentAnimation: (modelUuid, current, isPlaying, loopAnimation, isCompleted) => { set((state) => { const asset = state.assets.find(a => a.modelUuid === modelUuid); if (asset?.animationState) { asset.animationState.current = current; - asset.animationState.isPlaying = isisPlaying; + asset.animationState.isPlaying = isPlaying; asset.animationState.loopAnimation = loopAnimation; + asset.animationState.isCompleted = isCompleted; + } + }); + }, + + setAnimationComplete: (modelUuid, isCompleted) => { + set((state) => { + const asset = state.assets.find(a => a.modelUuid === modelUuid); + if (asset?.animationState) { + asset.animationState.isCompleted = isCompleted; } }); }, @@ -171,7 +182,7 @@ export const createAssetStore = () => { set((state) => { const asset = state.assets.find(a => a.modelUuid === modelUuid); if (asset?.animationState) { - asset.animationState = { current: '', isPlaying: false, loopAnimation: true }; + asset.animationState = { current: '', isPlaying: false, loopAnimation: true, isCompleted: true }; } }); }, diff --git a/app/src/store/simulation/useHumanStore.ts b/app/src/store/simulation/useHumanStore.ts index 0c6ebd9..496228f 100644 --- a/app/src/store/simulation/useHumanStore.ts +++ b/app/src/store/simulation/useHumanStore.ts @@ -160,7 +160,7 @@ export const createHumanStore = () => { set((state) => { const human = state.humans.find(h => h.modelUuid === modelUuid); if (human && human.currentMaterials.length > 0) { - removed = human.currentMaterials.pop(); + removed = JSON.parse(JSON.stringify(human.currentMaterials.pop())); } }); return removed; diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index a8b0175..b48cd12 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -28,6 +28,7 @@ interface Asset { current: string; isPlaying: boolean; loopAnimation: boolean; + isCompleted: boolean; }; eventData?: { type: string;