feat: Integrate reset functionality in ArmBot and StaticMachine components
This commit is contained in:
parent
a59aa1d61c
commit
8b7b7f589a
|
@ -4,6 +4,7 @@ import useModuleStore from "../../../store/useModuleStore";
|
|||
import { useSimulationStates } from "../../../store/store";
|
||||
import * as SimulationTypes from '../../../types/simulationTypes';
|
||||
import { ArmbotInstances } from "./ArmBotInstances";
|
||||
import { useResetButtonStore } from "../../../store/usePlayButtonStore";
|
||||
|
||||
interface ArmBotState {
|
||||
uuid: string;
|
||||
|
@ -38,6 +39,7 @@ const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
|
|||
const { activeModule } = useModuleStore();
|
||||
const { scene } = useThree();
|
||||
const { simulationStates } = useSimulationStates();
|
||||
const { isReset } = useResetButtonStore();
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot");
|
||||
|
@ -54,7 +56,7 @@ const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
|
|||
connections: bot.points.connections
|
||||
}));
|
||||
setArmBots(initialStates);
|
||||
}, [simulationStates]);
|
||||
}, [simulationStates, isReset]);
|
||||
|
||||
useEffect(() => {
|
||||
armBots.forEach((bot) => {
|
||||
|
|
|
@ -31,7 +31,7 @@ interface ArmBotState {
|
|||
|
||||
type IKAnimationControllerProps = {
|
||||
ikSolver: any;
|
||||
process: {
|
||||
processes: {
|
||||
triggerId: string;
|
||||
startPoint: THREE.Vector3;
|
||||
endPoint: THREE.Vector3;
|
||||
|
@ -50,7 +50,7 @@ type IKAnimationControllerProps = {
|
|||
|
||||
const IKAnimationController = ({
|
||||
ikSolver,
|
||||
process,
|
||||
processes,
|
||||
selectedTrigger,
|
||||
targetBoneName,
|
||||
uuid,
|
||||
|
@ -67,20 +67,10 @@ const IKAnimationController = ({
|
|||
const [isInitializing, setIsInitializing] = useState(true);
|
||||
const restSpeed = 0.1;
|
||||
const restPosition = new THREE.Vector3(0, 2, 1.6);
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { isPlaying } = usePlayButtonStore();;
|
||||
const statusRef = useRef("idle");
|
||||
const { simulationStates } = useSimulationStates();
|
||||
|
||||
// Track previous states for comparison
|
||||
const prevStateRef = useRef({
|
||||
isInitializing: true,
|
||||
needsInitialMovement: true,
|
||||
selectedTrigger: "",
|
||||
progress: 0
|
||||
});
|
||||
|
||||
// Track previous status for comparison
|
||||
const prevStatusRef = useRef("");
|
||||
|
||||
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
|
||||
const initialStartPositionRef = useRef<THREE.Vector3 | null>(null);
|
||||
|
||||
|
@ -101,91 +91,6 @@ const IKAnimationController = ({
|
|||
}
|
||||
}, [ikSolver]);
|
||||
|
||||
// Log state changes
|
||||
useEffect(() => {
|
||||
const prev = prevStateRef.current;
|
||||
|
||||
if (prev.isInitializing !== isInitializing) {
|
||||
if (!isInitializing) {
|
||||
logStatus(`[Arm ${uuid}] Completed initialization, now at rest position`);
|
||||
}
|
||||
}
|
||||
|
||||
if (prev.needsInitialMovement !== needsInitialMovement && !needsInitialMovement) {
|
||||
logStatus(`[Arm ${uuid}] Reached rest position, ready for animation`);
|
||||
|
||||
}
|
||||
|
||||
if (prev.selectedTrigger !== selectedTrigger) {
|
||||
logStatus(`[Arm ${uuid}] Processing new trigger: ${selectedTrigger}`);
|
||||
|
||||
const currentProcess = process.find(p => p.triggerId === prev.selectedTrigger);
|
||||
if (currentProcess) {
|
||||
const triggerId = currentProcess.triggerId;
|
||||
|
||||
const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint;
|
||||
|
||||
// Search simulationStates for a StaticMachine or Conveyor that has a point matching this endPointId
|
||||
const matchedMachine = simulationStates.find((state) => {
|
||||
if (state.type === "Conveyor") {
|
||||
// For Conveyor, points is an array
|
||||
return (state).points.some(
|
||||
(point) => point.uuid === endPoint
|
||||
);
|
||||
} else if (state.type === "StaticMachine") {
|
||||
// For StaticMachine, points is an object
|
||||
return state.points.uuid === endPoint;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (matchedMachine) {
|
||||
// Log if the end point is a conveyor
|
||||
if (matchedMachine.type === "Conveyor") {
|
||||
logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`);
|
||||
} else {
|
||||
logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`);
|
||||
}
|
||||
|
||||
if (matchedMachine.type === "StaticMachine") {
|
||||
setStaticMachines((machines) => {
|
||||
return machines.map((machine) => {
|
||||
if (machine.uuid === matchedMachine.modeluuid) {
|
||||
return { ...machine, status: "running" };
|
||||
} else {
|
||||
return machine;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (matchedMachine.type === "Conveyor") {
|
||||
setArmBots((prev) =>
|
||||
prev.map((arm) => {
|
||||
if (arm.uuid === uuid) {
|
||||
return {
|
||||
...arm,
|
||||
isActive: false
|
||||
};
|
||||
}
|
||||
else {
|
||||
return arm;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update previous state
|
||||
prevStateRef.current = {
|
||||
isInitializing,
|
||||
needsInitialMovement,
|
||||
selectedTrigger,
|
||||
progress
|
||||
};
|
||||
}, [isInitializing, needsInitialMovement, selectedTrigger, progress]);
|
||||
|
||||
const calculateInitialCurve = (startPosition: THREE.Vector3) => {
|
||||
const direction = new THREE.Vector3().subVectors(restPosition, startPosition);
|
||||
|
@ -211,61 +116,56 @@ const IKAnimationController = ({
|
|||
]);
|
||||
};
|
||||
|
||||
const processedCurves = useMemo(() => {
|
||||
if (isPlaying)
|
||||
return process.map((p) => {
|
||||
const tempLift = 0.5;
|
||||
const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0)));
|
||||
const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0)));
|
||||
const processCurves = useMemo(() => {
|
||||
if (!isPlaying) return [];
|
||||
|
||||
if (localStart && localEnd) {
|
||||
return processes.map(process => {
|
||||
const localStart = groupRef.current?.worldToLocal(process.startPoint.clone());
|
||||
const localEnd = groupRef.current?.worldToLocal(process.endPoint.clone());
|
||||
|
||||
const mid = new THREE.Vector3(
|
||||
(localStart.x + localEnd.x) / 1,
|
||||
Math.max(localStart.y, localEnd.y) + 0.8,
|
||||
(localStart.z + localEnd.z) / 0.9
|
||||
);
|
||||
if (!localStart || !localEnd) return null;
|
||||
|
||||
const points = [
|
||||
restPosition.clone(),
|
||||
localStart.clone(),
|
||||
mid.clone(),
|
||||
localEnd.clone(),
|
||||
restPosition.clone(),
|
||||
];
|
||||
const curve = new THREE.CatmullRomCurve3(points);
|
||||
const restToStartDist = points[0].distanceTo(points[1]);
|
||||
const startToEndDist = points[1].distanceTo(points[3]);
|
||||
const endToRestDist = points[3].distanceTo(points[4]);
|
||||
const midPoint = new THREE.Vector3(
|
||||
(localStart.x + localEnd.x) / 2,
|
||||
Math.max(localStart.y, localEnd.y) + 1,
|
||||
(localStart.z + localEnd.z) / 2
|
||||
);
|
||||
const restToStartCurve = new THREE.CatmullRomCurve3([
|
||||
restPosition,
|
||||
new THREE.Vector3().lerpVectors(restPosition, localStart, 0.5),
|
||||
localStart
|
||||
]);
|
||||
|
||||
const totalDist = restToStartDist + startToEndDist + endToRestDist;
|
||||
const restToStartRange = [0, restToStartDist / totalDist];
|
||||
const startToEndRange = [
|
||||
restToStartRange[1],
|
||||
restToStartRange[1] + startToEndDist / totalDist,
|
||||
];
|
||||
const endToRestRange = [startToEndRange[1], 1];
|
||||
const processCurve = new THREE.CatmullRomCurve3([
|
||||
localStart,
|
||||
midPoint,
|
||||
localEnd
|
||||
]);
|
||||
|
||||
return {
|
||||
trigger: p.triggerId,
|
||||
curve,
|
||||
speed: p.speed,
|
||||
restToStartRange,
|
||||
startToEndRange,
|
||||
endToRestRange,
|
||||
};
|
||||
}
|
||||
});
|
||||
}, [process, groupRef, isPlaying]);
|
||||
const endToRestCurve = new THREE.CatmullRomCurve3([
|
||||
localEnd,
|
||||
new THREE.Vector3().lerpVectors(localEnd, restPosition, 0.5),
|
||||
restPosition
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('processedCurves: ', processedCurves);
|
||||
}, [processedCurves])
|
||||
return {
|
||||
triggerId: process.triggerId,
|
||||
restToStartCurve,
|
||||
processCurve,
|
||||
endToRestCurve,
|
||||
speed: process.speed,
|
||||
totalDistance:
|
||||
restPosition.distanceTo(localStart) +
|
||||
localStart.distanceTo(localEnd) +
|
||||
localEnd.distanceTo(restPosition)
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}, [processes, isPlaying]);
|
||||
|
||||
const activeCurve = useMemo(() => {
|
||||
if (isPlaying && processedCurves)
|
||||
return processedCurves.find((c) => c?.trigger === selectedTrigger);
|
||||
}, [processedCurves, selectedTrigger, isPlaying]);
|
||||
const activeProcess = useMemo(() => {
|
||||
if (!selectedTrigger) return null;
|
||||
return processCurves.find(p => p?.triggerId === selectedTrigger);
|
||||
}, [processCurves, selectedTrigger]);
|
||||
|
||||
// Initial movement to rest position
|
||||
useFrame((_, delta) => {
|
||||
|
@ -293,68 +193,128 @@ const IKAnimationController = ({
|
|||
|
||||
// Main animation loop
|
||||
useFrame((_, delta) => {
|
||||
if (!ikSolver || !activeCurve || isInitializing || !isPlaying) return;
|
||||
if (isInitializing || !isPlaying || !selectedTrigger || !activeProcess || !ikSolver) return;
|
||||
|
||||
const { curve, speed, restToStartRange, startToEndRange, endToRestRange } = activeCurve;
|
||||
const targetBone = ikSolver.mesh.skeleton.bones.find(
|
||||
(b: any) => b.name === targetBoneName
|
||||
);
|
||||
if (!targetBone) return;
|
||||
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName);
|
||||
if (!bone) return;
|
||||
|
||||
let currentSpeed = restSpeed;
|
||||
let currentStatus = "idle"; // Default status
|
||||
const {
|
||||
restToStartCurve,
|
||||
processCurve,
|
||||
endToRestCurve,
|
||||
speed,
|
||||
totalDistance
|
||||
} = activeProcess;
|
||||
|
||||
// Determine current phase and status
|
||||
if (progress < restToStartRange[1]) {
|
||||
currentSpeed = restSpeed;
|
||||
currentStatus = "moving"; // Moving to start point
|
||||
} else if (progress >= startToEndRange[0] && progress < startToEndRange[1]) {
|
||||
currentSpeed = speed;
|
||||
currentStatus = "moving"; // Moving between points
|
||||
} else if (progress >= endToRestRange[0] && progress < 1) {
|
||||
currentSpeed = restSpeed;
|
||||
currentStatus = "moving"; // Returning to rest
|
||||
} else if (progress >= 1) {
|
||||
currentStatus = "idle"; // Completed cycle
|
||||
}
|
||||
// Calculate current segment and progress
|
||||
const restToStartDist = restPosition.distanceTo(restToStartCurve.points[2]);
|
||||
const processDist = processCurve.getLength();
|
||||
const endToRestDist = endToRestCurve.getLength();
|
||||
|
||||
// Update status when it changes
|
||||
if (prevStatusRef.current !== currentStatus) {
|
||||
updateArmBotStatus(currentStatus);
|
||||
prevStatusRef.current = currentStatus;
|
||||
}
|
||||
const restToStartEnd = restToStartDist / totalDistance;
|
||||
const processEnd = (restToStartDist + processDist) / totalDistance;
|
||||
|
||||
// Only update progress if we're not already at the end
|
||||
if (progress < 1) {
|
||||
setProgress((prev) => {
|
||||
const next = prev + delta * currentSpeed;
|
||||
return Math.min(next, 1); // Cap at 1
|
||||
});
|
||||
}
|
||||
setProgress(prev => {
|
||||
let currentStatus = statusRef.current;
|
||||
let currentPosition: THREE.Vector3;
|
||||
const newProgress = Math.min(prev + delta * ((currentStatus === 'returning to rest') ? restSpeed : speed), 1);
|
||||
|
||||
// Update bone position based on progress
|
||||
if (progress < 1) {
|
||||
targetBone.position.copy(curve.getPoint(progress));
|
||||
} else {
|
||||
targetBone.position.copy(curve.getPoint(1));
|
||||
}
|
||||
if (newProgress < restToStartEnd) {
|
||||
// Moving from rest to start position
|
||||
currentStatus = "moving to start";
|
||||
const segmentProgress = newProgress / restToStartEnd;
|
||||
currentPosition = restToStartCurve.getPoint(segmentProgress);
|
||||
} else if (newProgress < processEnd) {
|
||||
// Processing - moving from start to end
|
||||
currentStatus = "processing";
|
||||
const segmentProgress = (newProgress - restToStartEnd) / (processEnd - restToStartEnd);
|
||||
currentPosition = processCurve.getPoint(segmentProgress);
|
||||
} else {
|
||||
// Returning to rest position
|
||||
currentStatus = "returning to rest";
|
||||
const segmentProgress = (newProgress - processEnd) / (1 - processEnd);
|
||||
currentPosition = endToRestCurve.getPoint(segmentProgress);
|
||||
}
|
||||
|
||||
ikSolver.update();
|
||||
// Update status if changed
|
||||
if (currentStatus !== statusRef.current) {
|
||||
statusRef.current = currentStatus;
|
||||
// updateArmBotStatus(currentStatus);
|
||||
logStatus(`[Arm ${uuid}] Status: ${currentStatus}`);
|
||||
}
|
||||
|
||||
// Only trigger when the entire animation is complete (newProgress === 1)
|
||||
if (newProgress === 1 && currentStatus === "returning to rest") {
|
||||
updateConveyorOrStaticMachineStatus(selectedTrigger);
|
||||
}
|
||||
|
||||
bone.position.copy(currentPosition);
|
||||
ikSolver.update();
|
||||
return newProgress;
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{armBot.status === 'moving' && activeCurve &&
|
||||
<MaterialInstances
|
||||
groupRef={groupRef}
|
||||
activeCurve={activeCurve}
|
||||
progress={progress}
|
||||
ikSolver={ikSolver}
|
||||
targetBoneName={targetBoneName}
|
||||
/>
|
||||
const updateConveyorOrStaticMachineStatus = (selectedTrigger: string) => {
|
||||
const currentProcess = processes.find(p => p.triggerId === selectedTrigger);
|
||||
if (currentProcess) {
|
||||
const triggerId = currentProcess.triggerId;
|
||||
|
||||
const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint;
|
||||
|
||||
const matchedMachine = simulationStates.find((state) => {
|
||||
if (state.type === "Conveyor") {
|
||||
return (state).points.some(
|
||||
(point) => point.uuid === endPoint
|
||||
);
|
||||
} else if (state.type === "StaticMachine") {
|
||||
return state.points.uuid === endPoint;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (matchedMachine) {
|
||||
if (matchedMachine.type === "Conveyor") {
|
||||
logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`);
|
||||
} else {
|
||||
logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (matchedMachine.type === "StaticMachine") {
|
||||
setStaticMachines((machines) => {
|
||||
return machines.map((machine) => {
|
||||
if (machine.uuid === matchedMachine.modeluuid) {
|
||||
return { ...machine, status: "running" };
|
||||
} else {
|
||||
return machine;
|
||||
}
|
||||
});
|
||||
});
|
||||
updateArmBotStatus('idle');
|
||||
}
|
||||
|
||||
if (matchedMachine.type === "Conveyor") {
|
||||
setArmBots((prev) =>
|
||||
prev.map((arm) => {
|
||||
if (arm.uuid === uuid) {
|
||||
return {
|
||||
...arm,
|
||||
isActive: false,
|
||||
status: "idle",
|
||||
};
|
||||
}
|
||||
else {
|
||||
return arm;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default IKAnimationController;
|
|
@ -132,7 +132,7 @@ const IkInstances = ({
|
|||
</group>
|
||||
<IKAnimationController
|
||||
ikSolver={ikSolver}
|
||||
process={processes}
|
||||
processes={processes}
|
||||
selectedTrigger={selectedTrigger}
|
||||
targetBoneName={targetBoneName}
|
||||
uuid={uuid}
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { useEffect } from 'react'
|
|||
import * as SimulationTypes from '../../../types/simulationTypes';
|
||||
import { useSimulationStates } from '../../../store/store';
|
||||
import StaticMachineInstances from './staticMachineInstances';
|
||||
import { useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||
|
||||
interface ArmBotState {
|
||||
uuid: string;
|
||||
|
@ -34,6 +35,7 @@ type StaticMachineProps = {
|
|||
function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) {
|
||||
|
||||
const { simulationStates } = useSimulationStates();
|
||||
const { isReset } = useResetButtonStore();
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine");
|
||||
|
@ -47,7 +49,7 @@ function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: Static
|
|||
connectedArmBot: machine.points.connections.targets[0].modelUUID
|
||||
}));
|
||||
setStaticMachines(initialStates);
|
||||
}, [simulationStates]);
|
||||
}, [simulationStates, isReset]);
|
||||
|
||||
const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => {
|
||||
setArmBots((prevArmBots) => {
|
||||
|
|
Loading…
Reference in New Issue