completed init movement for human

This commit is contained in:
2025-07-03 18:01:11 +05:30
parent 1e715cee50
commit 7cf82629e9
16 changed files with 302 additions and 567 deletions

View File

@@ -40,15 +40,10 @@ function HumanMechanics() {
selectedEventData.selectedPoint selectedEventData.selectedPoint
) as HumanPointSchema | undefined; ) as HumanPointSchema | undefined;
if (point?.actions) { if (point?.action) {
setSelectedPointData(point); setSelectedPointData(point);
if (point.actions.length > 0) { setCurrentAction(point.action);
setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); setSelectedAction(point.action.actionUuid, point.action.actionName);
const asset = getAssetById(selectedEventData.data.modelUuid);
if (asset && asset.animations) {
setAnimationOptions(asset.animations)
}
}
} }
} else { } else {
clearSelectedAction(); clearSelectedAction();
@@ -57,31 +52,17 @@ function HumanMechanics() {
useEffect(() => { useEffect(() => {
if (selectedEventData && selectedProduct.productUuid) { if (selectedEventData && selectedProduct.productUuid) {
const event = getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData.data.modelUuid
) as HumanEventSchema | undefined;
if (event?.speed !== undefined) {
setSpeed(event.speed.toString());
}
const point = getPointByUuid( const point = getPointByUuid(
selectedProduct.productUuid, selectedProduct.productUuid,
selectedEventData.data.modelUuid, selectedEventData.data.modelUuid,
selectedEventData.selectedPoint selectedEventData.selectedPoint
) as HumanPointSchema | undefined; ) as HumanPointSchema | undefined;
if (point?.actions) { if (point?.action) {
setSelectedPointData(point); setSelectedPointData(point);
const action = point.actions.find((a) => a.actionUuid === selectedAction.actionId); setCurrentAction(point.action);
if (action) { setActiveOption(point.action.actionType);
setCurrentAction(action); setSelectedAction(point.action.actionUuid, point.action.actionName);
setActiveOption(action.actionType as "worker");
if (action.animationSequences.length > 0) {
setSelectedAnimation(action.animationSequences[0]);
}
}
} }
} else { } else {
clearSelectedAction(); clearSelectedAction();
@@ -108,17 +89,8 @@ function HumanMechanics() {
const handleSelectActionType = (actionType: string) => { const handleSelectActionType = (actionType: string) => {
if (!selectedAction.actionId || !currentAction || !selectedPointData) return; if (!selectedAction.actionId || !currentAction || !selectedPointData) return;
const updatedAction = { const updatedAction = { ...currentAction, actionType: actionType as "worker" };
...currentAction, const updatedPoint = { ...selectedPointData, action: updatedAction };
actionType: actionType as "worker"
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction( const event = updateAction(
selectedProduct.productUuid, selectedProduct.productUuid,
@@ -134,43 +106,6 @@ function HumanMechanics() {
setSelectedPointData(updatedPoint); setSelectedPointData(updatedPoint);
}; };
const handleChooseAnimation = (animationOption: string) => {
if (!selectedAction.actionId || !currentAction || !selectedAnimation || !selectedPointData) return;
const updatedAnimation = {
...selectedAnimation,
animation: animationOption
};
const updatedAction = {
...currentAction,
animationSequences: currentAction.animationSequences.map(anim =>
anim.animationUuid === selectedAnimation.animationUuid ? updatedAnimation : anim
)
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedAnimation(updatedAnimation);
setSelectedPointData(updatedPoint);
};
const handleSpeedChange = (value: string) => { const handleSpeedChange = (value: string) => {
if (!selectedEventData) return; if (!selectedEventData) return;
@@ -195,31 +130,14 @@ function HumanMechanics() {
setSpeed(value); setSpeed(value);
}; };
const handleClearPoints = (animationUuid: string) => { const handleClearPoints = () => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return; if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAnimation = currentAction.animationSequences.find(anim => const updatedAction = { ...currentAction };
anim.animationUuid === animationUuid delete updatedAction.pickUpPoint;
); delete updatedAction.dropPoint;
if (!updatedAnimation) return; const updatedPoint = { ...selectedPointData, action: updatedAction };
delete updatedAnimation.startPoint;
delete updatedAnimation.endPoint;
const updatedAction = {
...currentAction,
animationSequences: currentAction.animationSequences.map(anim =>
anim.animationUuid === animationUuid ? updatedAnimation : anim
)
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction( const event = updateAction(
selectedProduct.productUuid, selectedProduct.productUuid,
@@ -233,9 +151,6 @@ function HumanMechanics() {
setCurrentAction(updatedAction); setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint); setSelectedPointData(updatedPoint);
if (selectedAnimation?.animationUuid === animationUuid) {
setSelectedAnimation(updatedAnimation);
}
}; };
const handleAddAction = () => { const handleAddAction = () => {
@@ -243,16 +158,8 @@ function HumanMechanics() {
const newAction: HumanAction = { const newAction: HumanAction = {
actionUuid: MathUtils.generateUUID(), actionUuid: MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`, actionName: `Action`,
actionType: "worker", actionType: "worker",
animationSequences: [
{
animationUuid: MathUtils.generateUUID(),
animationName: 'Animation 1',
animationType: 'behaviour',
animation: null
}
],
loadCapacity: 1, loadCapacity: 1,
triggers: [], triggers: [],
}; };
@@ -268,217 +175,27 @@ function HumanMechanics() {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event); updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
} }
const updatedPoint = { ...selectedPointData, actions: [...selectedPointData.actions, newAction] }; const updatedPoint = { ...selectedPointData, action: newAction };
setSelectedPointData(updatedPoint); setSelectedPointData(updatedPoint);
setSelectedAction(newAction.actionUuid, newAction.actionName); setSelectedAction(newAction.actionUuid, newAction.actionName);
}; };
const handleDeleteAction = (actionUuid: string) => { const handleDeleteAction = () => {
if (!selectedPointData) return; if (!selectedPointData) return;
const event = removeAction(selectedProduct.productUuid, actionUuid); const event = removeAction(
selectedProduct.productUuid,
selectedPointData.action.actionUuid
);
if (event) { if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event); updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
} }
const index = selectedPointData.actions.findIndex((a) => a.actionUuid === actionUuid); const updatedPoint = { ...selectedPointData, action: undefined as any };
const newActions = selectedPointData.actions.filter((a) => a.actionUuid !== actionUuid);
const updatedPoint = { ...selectedPointData, actions: newActions };
setSelectedPointData(updatedPoint); setSelectedPointData(updatedPoint);
if (selectedAction.actionId === actionUuid) {
const nextAction = newActions[index] || newActions[index - 1];
if (nextAction) {
setSelectedAction(nextAction.actionUuid, nextAction.actionName);
} else {
clearSelectedAction(); clearSelectedAction();
} setCurrentAction(undefined);
}
};
const handleAddAnimation = () => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const newAnimation = {
animationUuid: MathUtils.generateUUID(),
animationName: `Animation ${currentAction.animationSequences.length + 1}`,
animationType: 'behaviour' as "behaviour",
animation: null
};
const updatedAction = {
...currentAction,
animationSequences: [...currentAction.animationSequences, newAnimation]
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
setSelectedAnimation(newAnimation);
};
const handleRemoveAnimation = (animationUuid: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = {
...currentAction,
animationSequences: currentAction.animationSequences.filter(
anim => anim.animationUuid !== animationUuid
)
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
if (selectedAnimation?.animationUuid === animationUuid) {
setSelectedAnimation(updatedAction.animationSequences[0] || undefined);
}
};
const handleAnimationTypeChange = (animationUuid: string, newType: "behaviour" | "animatedTravel") => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAnimationSequences = currentAction.animationSequences.map(anim => {
if (anim.animationUuid === animationUuid) {
const updatedAnim = {
...anim,
animationType: newType
};
delete updatedAnim.startPoint;
delete updatedAnim.endPoint;
return updatedAnim;
}
return anim;
});
const updatedAction = {
...currentAction,
animationSequences: updatedAnimationSequences
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
if (selectedAnimation?.animationUuid === animationUuid) {
const updatedAnimation = updatedAnimationSequences.find(anim =>
anim.animationUuid === animationUuid
);
if (updatedAnimation) {
setSelectedAnimation(updatedAnimation);
}
}
};
const handleAnimationSelect = (animationUuid: string) => {
if (!currentAction || !selectedAction.actionId) return;
const animation = currentAction.animationSequences.find(
anim => anim.animationUuid === animationUuid
);
if (animation) {
setSelectedAnimation(animation);
}
};
const handleRenameAnimation = (animationUuid: string, newName: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAnimation = currentAction.animationSequences.find(anim =>
anim.animationUuid === animationUuid
);
if (!updatedAnimation) return;
const renamedAnimation = { ...updatedAnimation, animationName: newName };
const updatedAction = {
...currentAction,
animationSequences: currentAction.animationSequences.map(anim =>
anim.animationUuid === animationUuid ? renamedAnimation : anim
)
};
const updatedPoint = {
...selectedPointData,
actions: selectedPointData.actions.map(action =>
action.actionUuid === selectedAction.actionId ? updatedAction : action
)
};
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
if (selectedAnimation?.animationUuid === animationUuid) {
setSelectedAnimation(renamedAnimation);
}
};
const availableActions = {
defaultOption: "worker",
options: ["worker"],
}; };
return ( return (
@@ -503,7 +220,7 @@ function HumanMechanics() {
<section> <section>
<ActionsList <ActionsList
selectedPointData={selectedPointData} selectedPointData={selectedPointData}
multipleAction multipleAction={false}
handleAddAction={handleAddAction} handleAddAction={handleAddAction}
handleDeleteAction={handleDeleteAction} handleDeleteAction={handleDeleteAction}
/> />
@@ -511,69 +228,20 @@ function HumanMechanics() {
{selectedAction.actionId && currentAction && ( {selectedAction.actionId && currentAction && (
<div className="selected-actions-details"> <div className="selected-actions-details">
<div className="selected-actions-header"> <div className="selected-actions-header">
<RenameInput <RenameInput value={selectedAction.actionName || ""} canEdit={false} />
value={selectedAction.actionName || ""}
canEdit={false}
/>
</div> </div>
<div className="selected-actions-list"> <div className="selected-actions-list">
<LabledDropdown <LabledDropdown
label="Action Type" label="Action Type"
defaultOption={activeOption} defaultOption={activeOption}
options={availableActions.options} options={["worker"]}
onSelect={handleSelectActionType} onSelect={handleSelectActionType}
disabled={true} disabled={true}
/> />
</div> </div>
<AnimationList <PickAndPlaceAction clearPoints={handleClearPoints} />
animationOptions={animationOptions}
animationSequences={currentAction?.animationSequences || []}
onAddAnimation={handleAddAnimation}
onRemoveAnimation={handleRemoveAnimation}
handleAnimationSelect={handleAnimationSelect}
handleRenameAnimation={handleRenameAnimation}
/>
{selectedAnimation && (
<>
<div className="selected-actions-header">
<RenameInput
value={selectedAnimation.animationName || ""}
canEdit={false}
/>
</div>
<div className="animation-controls">
<LabledDropdown
label="Animation Type"
defaultOption={selectedAnimation.animationType}
options={["behaviour", "animatedTravel"]}
onSelect={(type) =>
handleAnimationTypeChange(
selectedAnimation.animationUuid,
type as "behaviour" | "animatedTravel"
)
}
/>
<LabledDropdown
label="Animation"
defaultOption={selectedAnimation.animation || ''}
options={animationOptions}
onSelect={handleChooseAnimation}
disabled={true}
/>
{selectedAnimation.animationType === "animatedTravel" && (
<PickAndPlaceAction
clearPoints={() => handleClearPoints(selectedAnimation.animationUuid)}
/>
)}
</div>
</>
)}
<div className="tirgger"> <div className="tirgger">
<Trigger <Trigger selectedPointData={selectedPointData as any} type="Human" />
selectedPointData={selectedPointData as any}
type={"Human"}
/>
</div> </div>
</div> </div>
)} )}

View File

@@ -256,23 +256,14 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
actions: [ action: {
{
actionUuid: THREE.MathUtils.generateUUID(), actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1", actionName: "Action 1",
actionType: "worker", actionType: "worker",
animationSequences: [
{
animationUuid: THREE.MathUtils.generateUUID(),
animationName: 'Animation 1',
animationType: 'behaviour',
animation: null
}
],
loadCapacity: 1, loadCapacity: 1,
triggers: [] triggers: []
} }
]
} }
} }
addEvent(humanEvent); addEvent(humanEvent);

View File

@@ -373,23 +373,13 @@ async function handleModelLoad(
uuid: THREE.MathUtils.generateUUID(), uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z], position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0], rotation: [0, 0, 0],
actions: [ action: {
{
actionUuid: THREE.MathUtils.generateUUID(), actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Action 1", actionName: "Action 1",
actionType: "worker", actionType: "worker",
animationSequences: [
{
animationUuid: THREE.MathUtils.generateUUID(),
animationName: 'Animation 1',
animationType: 'behaviour',
animation: null
}
],
loadCapacity: 1, loadCapacity: 1,
triggers: [] triggers: []
} }
]
} }
} }

View File

@@ -30,7 +30,7 @@ export const handleAddEventToProduct = ({
projectId: projectId || '', projectId: projectId || '',
eventDatas: event eventDatas: event
}).then((data) => { }).then((data) => {
console.log(data); // console.log(data);
}) })
if (clearSelectedAsset) { if (clearSelectedAsset) {

View File

@@ -155,8 +155,8 @@ function TriggerConnector() {
// Handle Human point // Handle Human point
else if (event.type === "human" && 'point' in event) { else if (event.type === "human" && 'point' in event) {
const point = event.point; const point = event.point;
point.actions?.forEach(action => { if (point.action?.triggers) {
action.triggers?.forEach(trigger => { point.action.triggers.forEach(trigger => {
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) {
newConnections.push({ newConnections.push({
id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`,
@@ -166,7 +166,7 @@ function TriggerConnector() {
}); });
} }
}); });
}); }
} }
}); });

View File

@@ -5,7 +5,6 @@ import { useSceneContext } from '../../../scene/sceneContext';
type HumanCallback = { type HumanCallback = {
humanId: string; humanId: string;
actionId: string;
callback: () => void; callback: () => void;
}; };
@@ -25,10 +24,10 @@ export function useHumanEventManager() {
}, [isReset]) }, [isReset])
// Add a new human to monitor // Add a new human to monitor
const addHumanToMonitor = (humanId: string, actionId: string, callback: () => void) => { const addHumanToMonitor = (humanId: string, callback: () => void) => {
// Avoid duplicates // Avoid duplicates
if (!callbacksRef.current.some((entry) => entry.humanId === humanId)) { if (!callbacksRef.current.some((entry) => entry.humanId === humanId)) {
callbacksRef.current.push({ humanId, actionId, callback }); callbacksRef.current.push({ humanId, callback });
} }
// Start monitoring if not already running // Start monitoring if not already running
@@ -53,11 +52,9 @@ export function useHumanEventManager() {
useFrame(() => { useFrame(() => {
if (!isMonitoringRef.current || callbacksRef.current.length === 0 || !isPlaying || isPaused) return; if (!isMonitoringRef.current || callbacksRef.current.length === 0 || !isPlaying || isPaused) return;
callbacksRef.current.forEach(({ humanId, actionId, callback }) => { callbacksRef.current.forEach(({ humanId, callback }) => {
const human = getHumanById(humanId); const human = getHumanById(humanId);
if (!human) return; if (human && human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
const action = human.point.actions.find((action) => action.actionUuid === actionId);
if (action && human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < action.loadCapacity) {
callback(); callback();
removeHumanFromMonitor(humanId); // Remove after triggering removeHumanFromMonitor(humanId); // Remove after triggering
} }

View File

@@ -16,12 +16,7 @@ function Human() {
useEffect(() => { useEffect(() => {
if (selectedEventSphere) { if (selectedEventSphere) {
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid); const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (selectedHuman && if (selectedHuman) {
selectedHuman.point.actions.some((action) =>
action.animationSequences.some((animation) =>
animation.animationUuid === selectedAnimation?.animationUuid && animation.animationType === 'animatedTravel'
)
)) {
setIsHumanSelected(true); setIsHumanSelected(true);
} else { } else {
setIsHumanSelected(false); setIsHumanSelected(false);

View File

@@ -0,0 +1,170 @@
import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
interface HumanAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
startUnloadingProcess: () => void;
currentPhase: string;
human: HumanStatus;
}
function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
const { humanStore } = useSceneContext();
const { getHumanById } = humanStore();
const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isReset, setReset } = useResetButtonStore();
const progressRef = useRef<number>(0);
const movingForward = useRef<boolean>(true);
const completedRef = useRef<boolean>(false);
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>(human.point?.action?.pickUpPoint?.rotation || [0, 0, 0])
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (currentPhase === 'init-pickup' && path.length > 0) {
setCurrentPath(path);
setObjectRotation(human.point.action.pickUpPoint?.rotation ?? null)
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
setObjectRotation(human.point.action?.dropPoint?.rotation ?? null)
setCurrentPath(path);
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
setObjectRotation(human.point.action?.pickUpPoint?.rotation ?? null)
setCurrentPath(path);
}
}, [currentPhase, path, objectRotation]);
useEffect(() => {
completedRef.current = false;
}, [currentPath]);
useEffect(() => {
if (isReset || !isPlaying) {
reset();
setCurrentPath([]);
completedRef.current = false;
movingForward.current = true;
progressRef.current = 0;
setReset(false);
setRestingRotation(true);
const object = scene.getObjectByProperty('uuid', human.modelUuid);
const humanData = getHumanById(human.modelUuid);
if (object && humanData) {
object.position.set(humanData.position[0], humanData.position[1], humanData.position[2]);
object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
}
}
}, [isReset, isPlaying])
const lastTimeRef = useRef(performance.now());
useFrame(() => {
const now = performance.now();
const delta = (now - lastTimeRef.current) / 1000;
lastTimeRef.current = now;
const object = scene.getObjectByProperty('uuid', human.modelUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
let totalDistance = 0;
const distances = [];
let accumulatedDistance = 0;
let index = 0;
const rotationSpeed = 1;
for (let i = 0; i < currentPath.length - 1; i++) {
const start = new THREE.Vector3(...currentPath[i]);
const end = new THREE.Vector3(...currentPath[i + 1]);
const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
totalDistance += segmentDistance;
}
while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
accumulatedDistance += distances[index];
index++;
}
if (index < distances.length) {
const start = new THREE.Vector3(...currentPath[index]);
const end = new THREE.Vector3(...currentPath[index + 1]);
const segmentDistance = distances[index];
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
const currentAngle = object.rotation.y;
let angleDifference = targetAngle - currentAngle;
if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
const maxRotationStep = (rotationSpeed * speed * human.speed) * delta;
object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
const isAligned = Math.abs(angleDifference) < 0.01;
if (isAligned) {
progressRef.current += delta * (speed * human.speed);
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
object.position.copy(position);
}
}
if (progressRef.current >= totalDistance) {
if (restRotation && objectRotation) {
const targetEuler = new THREE.Euler(
objectRotation[0],
objectRotation[1] + Math.PI,
objectRotation[2]
);
const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * human.speed));
if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
object.quaternion.copy(targetQuaternion);
object.rotation.copy(targetEuler);
setRestingRotation(false);
}
return;
}
}
if (progressRef.current >= totalDistance) {
setRestingRotation(true);
progressRef.current = 0;
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === 'pickup-drop') {
requestAnimationFrame(startUnloadingProcess);
}
}
});
return (
<>
{currentPath.length > 0 && (
// helper
<group >
<Line points={currentPath} color="blue" lineWidth={3} />
{currentPath.map((point, index) => (
<mesh key={index} position={point}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
))}
</group>
)}
</>
)
}
export default HumanAnimator

View File

@@ -1,10 +0,0 @@
import React from 'react'
function HumanAnimator() {
return (
<>
</>
)
}
export default HumanAnimator

View File

@@ -7,7 +7,7 @@ import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHa
import { useSceneContext } from '../../../../scene/sceneContext'; import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext'; import { useProductContext } from '../../../products/productContext';
import HumanAnimator from './animator/humanAnimator'; import HumanAnimator from '../animator/humanAnimator';
function HumanInstance({ human }: { human: HumanStatus }) { function HumanInstance({ human }: { human: HumanStatus }) {
const { navMesh } = useNavMesh(); const { navMesh } = useNavMesh();
@@ -66,12 +66,10 @@ function HumanInstance({ human }: { human: HumanStatus }) {
console.error("Failed to compute path"); console.error("Failed to compute path");
return []; return [];
} }
}, }, [navMesh]);
[navMesh]
);
function humanStatus(modelId: string, status: string) { function humanStatus(modelId: string, status: string) {
// console.log(`${modelId} , ${status}`); console.log(`${modelId} , ${status}`);
} }
function reset() { function reset() {
@@ -96,6 +94,25 @@ function HumanInstance({ human }: { human: HumanStatus }) {
useEffect(() => { useEffect(() => {
if (isPlaying) { if (isPlaying) {
if (!human.point.action.pickUpPoint || !human.point.action.dropPoint) return;
if (!human.isActive && human.state === 'idle' && currentPhase === 'init') {
const toPickupPath = computePath(
new THREE.Vector3(human?.position[0], human?.position[1], human?.position[2]),
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(toPickupPath);
setCurrentPhase('init-pickup');
setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, true);
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
return;
}
} }
else { else {
@@ -104,10 +121,28 @@ function HumanInstance({ human }: { human: HumanStatus }) {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [human, currentPhase, path, isPlaying]); }, [human, currentPhase, path, isPlaying]);
function handleCallBack() {
if (currentPhase === 'init-pickup') {
setCurrentPhase('picking');
} else if (currentPhase === 'pickup-drop') {
} else if (currentPhase === 'drop-pickup') {
}
}
function startUnloadingProcess() {
}
return ( return (
<> <>
<HumanAnimator /> <HumanAnimator
path={path}
handleCallBack={handleCallBack}
currentPhase={currentPhase}
human={human}
reset={reset}
startUnloadingProcess={startUnloadingProcess}
/>
</> </>
) )

View File

@@ -60,35 +60,29 @@ function HumanUi() {
useEffect(() => { useEffect(() => {
if (!selectedEventSphere) return; if (!selectedEventSphere) return;
const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (selectedHuman) { const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
if (!selectedHuman || !selectedHuman.point?.action) return;
setSelectedHumanData({ setSelectedHumanData({
position: selectedHuman.position, position: selectedHuman.position,
rotation: selectedHuman.rotation, rotation: selectedHuman.rotation,
}); });
if (selectedHuman.point?.actions && selectedAction.actionId) { const action = selectedHuman.point.action;
const action = selectedHuman.point.actions.find(a => a.actionUuid === selectedAction.actionId);
if (action?.animationSequences[0]) { if (action.pickUpPoint?.position && outerGroup.current) {
const sequence = action.animationSequences[0]; const worldPos = new Vector3(...action.pickUpPoint.position);
if (sequence.startPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...sequence.startPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setStartPosition([localPosition.x, 0.5, localPosition.z]); setStartPosition([localPosition.x, 0.5, localPosition.z]);
setStartRotation(sequence.startPoint.rotation || [0, 0, 0]); setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
} }
if (sequence.endPoint?.position && outerGroup.current) { if (action.dropPoint?.position && outerGroup.current) {
const worldPos = new Vector3(...sequence.endPoint.position); const worldPos = new Vector3(...action.dropPoint.position);
const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
setEndPosition([localPosition.x, 0.5, localPosition.z]); setEndPosition([localPosition.x, 0.5, localPosition.z]);
setEndRotation(sequence.endPoint.rotation || [0, 0, 0]); setEndRotation(action.dropPoint.rotation || [0, 0, 0]);
}
}
}
} }
}, [selectedEventSphere, outerGroup.current, selectedAction, humans]); }, [selectedEventSphere, outerGroup.current, selectedAction, humans]);
@@ -126,29 +120,17 @@ function HumanUi() {
const worldPosEnd = new Vector3(...endPosition); const worldPosEnd = new Vector3(...endPosition);
const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone()); const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());
const updatedActions = selectedHuman.point.actions.map(action => { const updatedAction = {
if (action.actionUuid === selectedAction.actionId) { ...selectedHuman.point.action,
const updatedSequences = action.animationSequences.map(sequence => { pickUpPoint: {
return {
...sequence,
startPoint: {
position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number], position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
rotation: startRotation rotation: startRotation
}, },
endPoint: { dropPoint: {
position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number], position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
rotation: endRotation rotation: endRotation
} }
}; };
});
return {
...action,
animationSequences: updatedSequences
};
}
return action;
});
const event = updateEvent( const event = updateEvent(
selectedProduct.productUuid, selectedProduct.productUuid,
@@ -157,7 +139,7 @@ function HumanUi() {
...selectedHuman, ...selectedHuman,
point: { point: {
...selectedHuman.point, ...selectedHuman.point,
actions: updatedActions action: updatedAction
} }
} }
); );
@@ -195,8 +177,7 @@ function HumanUi() {
const currentPointerX = state.pointer.x; const currentPointerX = state.pointer.x;
const deltaX = currentPointerX - prevMousePos.current.x; const deltaX = currentPointerX - prevMousePos.current.x;
prevMousePos.current.x = currentPointerX; prevMousePos.current.x = currentPointerX;
const marker = const marker =isRotating === "start" ? startMarker.current : endMarker.current;
isRotating === "start" ? startMarker.current : endMarker.current;
if (marker) { if (marker) {
const rotationSpeed = 10; const rotationSpeed = 10;
marker.rotation.y += deltaX * rotationSpeed; marker.rotation.y += deltaX * rotationSpeed;

View File

@@ -301,7 +301,7 @@ export function useTriggerHandler() {
} else { } else {
// Handle current action using Event Manager // Handle current action using Event Manager
addHumanToMonitor(human.modelUuid, triggeredAction.actionUuid, () => { addHumanToMonitor(human.modelUuid, () => {
handleAction(action, materialId); handleAction(action, materialId);
}) })
} }

View File

@@ -32,7 +32,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
useEffect(() => { useEffect(() => {
if (currentPhase === 'stationed-pickup' && path.length > 0) { if (currentPhase === 'stationed-pickup' && path.length > 0) {
// console.log('path: ', path);
setCurrentPath(path); setCurrentPath(path);
setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation) setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation)
} else if (currentPhase === 'pickup-drop' && path.length > 0) { } else if (currentPhase === 'pickup-drop' && path.length > 0) {
@@ -66,84 +65,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
} }
}, [isReset, isPlaying]) }, [isReset, isPlaying])
// useFrame((_, delta) => {
// const object = scene.getObjectByProperty('uuid', agvUuid);
// if (!object || currentPath.length < 2) return;
// if (isPaused) return;
// let totalDistance = 0;
// const distances = [];
// let accumulatedDistance = 0;
// let index = 0;
// const rotationSpeed = 1;
// for (let i = 0; i < currentPath.length - 1; i++) {
// const start = new THREE.Vector3(...currentPath[i]);
// const end = new THREE.Vector3(...currentPath[i + 1]);
// const segmentDistance = start.distanceTo(end);
// distances.push(segmentDistance);
// totalDistance += segmentDistance;
// }
// while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
// accumulatedDistance += distances[index];
// index++;
// }
// if (index < distances.length) {
// const start = new THREE.Vector3(...currentPath[index]);
// const end = new THREE.Vector3(...currentPath[index + 1]);
// const segmentDistance = distances[index];
// const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
// const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
// const currentAngle = object.rotation.y;
// let angleDifference = targetAngle - currentAngle;
// if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
// if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
// const maxRotationStep = (rotationSpeed * speed * agvDetail.speed) * delta;
// object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
// const isAligned = Math.abs(angleDifference) < 0.01;
// if (isAligned) {
// progressRef.current += delta * (speed * agvDetail.speed);
// const t = (progressRef.current - accumulatedDistance) / segmentDistance;
// const position = start.clone().lerp(end, t);
// object.position.copy(position);
// }
// }
// if (progressRef.current >= totalDistance) {
// if (restRotation && objectRotation) {
// const targetEuler = new THREE.Euler(
// objectRotation.x,
// objectRotation.y - (agvDetail.point.action.steeringAngle),
// objectRotation.z
// );
// const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
// object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed));
// if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
// object.quaternion.copy(targetQuaternion);
// object.rotation.copy(targetEuler);
// setRestingRotation(false);
// }
// return;
// }
// }
// if (progressRef.current >= totalDistance) {
// setRestingRotation(true);
// progressRef.current = 0;
// movingForward.current = !movingForward.current;
// setCurrentPath([]);
// handleCallBack();
// if (currentPhase === 'pickup-drop') {
// requestAnimationFrame(startUnloadingProcess);
// }
// }
// });
const lastTimeRef = useRef(performance.now()); const lastTimeRef = useRef(performance.now());
useFrame(() => { useFrame(() => {

View File

@@ -104,6 +104,10 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]), new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
agvDetail?.point?.action?.pickUpPoint?.position agvDetail?.point?.action?.pickUpPoint?.position
); );
// const toPickupPath = computePath(
// new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
// new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2])
// );
setPath(toPickupPath); setPath(toPickupPath);
setCurrentPhase('stationed-pickup'); setCurrentPhase('stationed-pickup');
setVehicleState(agvDetail.modelUuid, 'running'); setVehicleState(agvDetail.modelUuid, 'running');
@@ -193,7 +197,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}; };
}, [agvDetail, isPlaying]); }, [agvDetail, isPlaying]);
function handleCallBack() { function handleCallBack() {
if (currentPhase === 'stationed-pickup') { if (currentPhase === 'stationed-pickup') {
setCurrentPhase('picking'); setCurrentPhase('picking');

View File

@@ -32,13 +32,13 @@ type ProductsStore = {
productUuid: string, productUuid: string,
modelUuid: string, modelUuid: string,
pointUuid: string, pointUuid: string,
action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0] action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']
) => EventsSchema | undefined; ) => EventsSchema | undefined;
removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined; removeAction: (productUuid: string, actionUuid: string) => EventsSchema | undefined;
updateAction: ( updateAction: (
productUuid: string, productUuid: string,
actionUuid: string, actionUuid: string,
updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['actions'][0]> updates: Partial<ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] | HumanPointSchema['action']>
) => EventsSchema | undefined; ) => EventsSchema | undefined;
// Trigger-level actionss // Trigger-level actionss

View File

@@ -73,14 +73,8 @@ interface HumanAction {
actionUuid: string; actionUuid: string;
actionName: string; actionName: string;
actionType: "worker"; actionType: "worker";
animationSequences: { pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
animationUuid: string; dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
animationName: string;
animationType: "behaviour" | "animatedTravel";
animation: string | null;
startPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
endPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
}[]
loadCapacity: number; loadCapacity: number;
triggers: TriggerSchema[]; triggers: TriggerSchema[];
} }
@@ -127,7 +121,7 @@ interface HumanPointSchema {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
rotation: [number, number, number]; rotation: [number, number, number];
actions: HumanAction[]; action: HumanAction;
} }
type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema; type PointsScheme = | ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | HumanPointSchema;