"updated animation"

This commit is contained in:
SreeNath14 2025-04-03 10:28:13 +05:30
parent 844c8c3366
commit cf364d707c
6 changed files with 1460 additions and 361 deletions

View File

@ -0,0 +1,916 @@
// // animation-worker.js
// // This web worker handles animation calculations off the main thread
// /* eslint-disable no-restricted-globals */
// // The above disables the ESLint rule for this file since 'self' is valid in web workers
// // Store process data, animation states, and objects
// let processes = [];
// let animationStates = {};
// let lastTimestamp = 0;
// // Message handler for communication with main thread
// self.onmessage = function (event) {
// const { type, data } = event.data;
// switch (type) {
// case "initialize":
// processes = data.processes;
// initializeAnimationStates();
// break;
// case "update":
// const { timestamp, isPlaying } = data;
// if (isPlaying) {
// const delta = (timestamp - lastTimestamp) / 1000; // Convert to seconds
// updateAnimations(delta, timestamp);
// }
// lastTimestamp = timestamp;
// break;
// case "reset":
// resetAnimations();
// break;
// case "togglePlay":
// // If resuming from pause, recalculate the time delta
// lastTimestamp = data.timestamp;
// break;
// }
// };
// // Initialize animation states for all processes
// function initializeAnimationStates() {
// animationStates = {};
// processes.forEach((process) => {
// animationStates[process.id] = {
// spawnedObjects: {},
// nextSpawnTime: 0,
// objectIdCounter: 0,
// };
// });
// // Send initial states back to main thread
// self.postMessage({
// type: "statesInitialized",
// data: { animationStates },
// });
// }
// // Reset all animations
// function resetAnimations() {
// initializeAnimationStates();
// }
// // Find spawn point in a process
// function findSpawnPoint(process) {
// for (const path of process.paths || []) {
// for (const point of path.points || []) {
// const spawnAction = point.actions?.find(
// (a) => a.isUsed && a.type === "Spawn"
// );
// if (spawnAction) {
// return { point, path };
// }
// }
// }
// return null;
// }
// // Create a new spawned object with proper initial position
// function createSpawnedObject(process, spawnPoint, currentTime, materialType) {
// // Extract spawn position from the actual spawn point
// const position = spawnPoint.point.position
// ? [...spawnPoint.point.position]
// : [0, 0, 0];
// // Get the path position and add it to the spawn point position
// const pathPosition = spawnPoint.path.pathPosition || [0, 0, 0];
// const absolutePosition = [
// position[0] + pathPosition[0],
// position[1] + pathPosition[1],
// position[2] + pathPosition[2],
// ];
// return {
// id: `obj-${process.id}-${animationStates[process.id].objectIdCounter}`,
// position: absolutePosition,
// state: {
// currentIndex: 0,
// progress: 0,
// isAnimating: true,
// speed: process.speed || 1,
// isDelaying: false,
// delayStartTime: 0,
// currentDelayDuration: 0,
// delayComplete: false,
// currentPathIndex: 0,
// // Store the spawn point index to start animation from correct path point
// spawnPointIndex: getPointIndexInProcess(process, spawnPoint.point),
// },
// visible: true,
// materialType: materialType || "Default",
// spawnTime: currentTime,
// };
// }
// // Get the index of a point within the process animation path
// function getPointIndexInProcess(process, point) {
// if (!process.paths) return 0;
// let cumulativePoints = 0;
// for (const path of process.paths) {
// for (let i = 0; i < (path.points?.length || 0); i++) {
// if (path.points[i].uuid === point.uuid) {
// return cumulativePoints + i;
// }
// }
// cumulativePoints += path.points?.length || 0;
// }
// return 0;
// }
// // Get point data for current animation index
// function getPointDataForAnimationIndex(process, index) {
// if (!process.paths) return null;
// let cumulativePoints = 0;
// for (const path of process.paths) {
// const pointCount = path.points?.length || 0;
// if (index < cumulativePoints + pointCount) {
// const pointIndex = index - cumulativePoints;
// return path.points?.[pointIndex] || null;
// }
// cumulativePoints += pointCount;
// }
// return null;
// }
// // Convert process paths to Vector3 format
// function getProcessPath(process) {
// return process.animationPath?.map((p) => ({ x: p.x, y: p.y, z: p.z })) || [];
// }
// // Handle material swap for an object
// function handleMaterialSwap(processId, objectId, materialType) {
// const processState = animationStates[processId];
// if (!processState || !processState.spawnedObjects[objectId]) return;
// processState.spawnedObjects[objectId].materialType = materialType;
// // Notify main thread about material change
// self.postMessage({
// type: "materialChanged",
// data: {
// processId,
// objectId,
// materialType,
// },
// });
// }
// // Handle point actions for an object
// function handlePointActions(processId, objectId, actions = [], currentTime) {
// let shouldStopAnimation = false;
// const processState = animationStates[processId];
// if (!processState || !processState.spawnedObjects[objectId]) return false;
// const objectState = processState.spawnedObjects[objectId];
// actions.forEach((action) => {
// if (!action.isUsed) return;
// switch (action.type) {
// case "Delay":
// if (objectState.state.isDelaying) return;
// const delayDuration =
// typeof action.delay === "number"
// ? action.delay
// : parseFloat(action.delay || "0");
// if (delayDuration > 0) {
// objectState.state.isDelaying = true;
// objectState.state.delayStartTime = currentTime;
// objectState.state.currentDelayDuration = delayDuration;
// objectState.state.delayComplete = false;
// shouldStopAnimation = true;
// }
// break;
// case "Despawn":
// delete processState.spawnedObjects[objectId];
// shouldStopAnimation = true;
// // Notify main thread about despawn
// self.postMessage({
// type: "objectDespawned",
// data: {
// processId,
// objectId,
// },
// });
// break;
// case "Swap":
// if (action.material) {
// handleMaterialSwap(processId, objectId, action.material);
// }
// break;
// default:
// break;
// }
// });
// return shouldStopAnimation;
// }
// // Check if point has non-inherit actions
// function hasNonInheritActions(actions = []) {
// return actions.some((action) => action.isUsed && action.type !== "Inherit");
// }
// // Calculate vector lerp (linear interpolation)
// function lerpVectors(v1, v2, alpha) {
// return {
// x: v1.x + (v2.x - v1.x) * alpha,
// y: v1.y + (v2.y - v1.y) * alpha,
// z: v1.z + (v2.z - v1.z) * alpha,
// };
// }
// // Calculate vector distance
// function distanceBetweenVectors(v1, v2) {
// const dx = v2.x - v1.x;
// const dy = v2.y - v1.y;
// const dz = v2.z - v1.z;
// return Math.sqrt(dx * dx + dy * dy + dz * dz);
// }
// // Process spawn logic
// function processSpawns(currentTime) {
// processes.forEach((process) => {
// const processState = animationStates[process.id];
// if (!processState) return;
// const spawnPointData = findSpawnPoint(process);
// if (!spawnPointData || !spawnPointData.point.actions) return;
// const spawnAction = spawnPointData.point.actions.find(
// (a) => a.isUsed && a.type === "Spawn"
// );
// if (!spawnAction) return;
// const spawnInterval =
// typeof spawnAction.spawnInterval === "number"
// ? spawnAction.spawnInterval
// : parseFloat(spawnAction.spawnInterval || "0");
// if (currentTime >= processState.nextSpawnTime) {
// const newObject = createSpawnedObject(
// process,
// spawnPointData,
// currentTime,
// spawnAction.material || "Default"
// );
// processState.spawnedObjects[newObject.id] = newObject;
// processState.objectIdCounter++;
// processState.nextSpawnTime = currentTime + spawnInterval;
// // Notify main thread about new object
// self.postMessage({
// type: "objectSpawned",
// data: {
// processId: process.id,
// object: newObject,
// },
// });
// }
// });
// }
// // Update all animations
// function updateAnimations(delta, currentTime) {
// // First handle spawning of new objects
// processSpawns(currentTime);
// // Then animate existing objects
// processes.forEach((process) => {
// const processState = animationStates[process.id];
// if (!processState) return;
// const path = getProcessPath(process);
// if (path.length < 2) return;
// const updatedObjects = {};
// let hasChanges = false;
// Object.entries(processState.spawnedObjects).forEach(([objectId, obj]) => {
// if (!obj.visible || !obj.state.isAnimating) return;
// const stateRef = obj.state;
// // Use the spawnPointIndex as starting point if it's the initial movement
// if (stateRef.currentIndex === 0 && stateRef.progress === 0) {
// stateRef.currentIndex = stateRef.spawnPointIndex || 0;
// }
// // Get current point data
// const currentPointData = getPointDataForAnimationIndex(
// process,
// stateRef.currentIndex
// );
// // Execute actions when arriving at a new point
// if (stateRef.progress === 0 && currentPointData?.actions) {
// const shouldStop = handlePointActions(
// process.id,
// objectId,
// currentPointData.actions,
// currentTime
// );
// if (shouldStop) return;
// }
// // Handle delays
// if (stateRef.isDelaying) {
// if (
// currentTime - stateRef.delayStartTime >=
// stateRef.currentDelayDuration
// ) {
// stateRef.isDelaying = false;
// stateRef.delayComplete = true;
// } else {
// updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
// return; // Keep waiting
// }
// }
// const nextPointIdx = stateRef.currentIndex + 1;
// const isLastPoint = nextPointIdx >= path.length;
// if (isLastPoint) {
// if (currentPointData?.actions) {
// const shouldStop = !hasNonInheritActions(currentPointData.actions);
// if (shouldStop) {
// // Reached the end of path with no more actions
// delete processState.spawnedObjects[objectId];
// // Notify main thread to remove the object
// self.postMessage({
// type: "objectCompleted",
// data: {
// processId: process.id,
// objectId,
// },
// });
// return;
// }
// }
// }
// if (!isLastPoint) {
// const currentPos = path[stateRef.currentIndex];
// const nextPos = path[nextPointIdx];
// const distance = distanceBetweenVectors(currentPos, nextPos);
// const movement = stateRef.speed * delta;
// // Update progress based on distance and speed
// const oldProgress = stateRef.progress;
// stateRef.progress += movement / distance;
// if (stateRef.progress >= 1) {
// // Reached next point
// stateRef.currentIndex = nextPointIdx;
// stateRef.progress = 0;
// stateRef.delayComplete = false;
// obj.position = [nextPos.x, nextPos.y, nextPos.z];
// } else {
// // Interpolate position
// const lerpedPos = lerpVectors(currentPos, nextPos, stateRef.progress);
// obj.position = [lerpedPos.x, lerpedPos.y, lerpedPos.z];
// }
// // Only send updates when there's meaningful movement
// if (Math.abs(oldProgress - stateRef.progress) > 0.01) {
// hasChanges = true;
// }
// }
// updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
// });
// // Update animation state with modified objects
// if (Object.keys(updatedObjects).length > 0) {
// processState.spawnedObjects = {
// ...processState.spawnedObjects,
// ...updatedObjects,
// };
// // Only send position updates when there are meaningful changes
// if (hasChanges) {
// self.postMessage({
// type: "positionsUpdated",
// data: {
// processId: process.id,
// objects: updatedObjects,
// },
// });
// }
// }
// });
// }
// animation-worker.js
// This web worker handles animation calculations off the main thread
/* eslint-disable no-restricted-globals */
// The above disables the ESLint rule for this file since 'self' is valid in web workers
// Store process data, animation states, and objects
let processes = [];
let animationStates = {};
let lastTimestamp = 0;
let debugMode = true;
// Logger function for debugging
function log(...args) {
if (debugMode) {
self.postMessage({
type: "debug",
data: { message: args.join(' ') }
});
}
}
// Message handler for communication with main thread
self.onmessage = function (event) {
const { type, data } = event.data;
log(`Worker received message: ${type}`);
switch (type) {
case "initialize":
processes = data.processes;
log(`Initialized with ${processes.length} processes`);
initializeAnimationStates();
break;
case "update":
const { timestamp, isPlaying } = data;
if (isPlaying) {
const delta = lastTimestamp === 0 ? 0.016 : (timestamp - lastTimestamp) / 1000; // Convert to seconds
updateAnimations(delta, timestamp);
}
lastTimestamp = timestamp;
break;
case "reset":
log("Resetting animations");
resetAnimations();
break;
case "togglePlay":
// If resuming from pause, recalculate the time delta
log(`Toggle play: ${data.isPlaying}`);
lastTimestamp = data.timestamp;
break;
case "setDebug":
debugMode = data.enabled;
log(`Debug mode: ${debugMode}`);
break;
}
};
// Initialize animation states for all processes
function initializeAnimationStates() {
animationStates = {};
processes.forEach((process) => {
if (!process || !process.id) {
log("Invalid process found:", process);
return;
}
animationStates[process.id] = {
spawnedObjects: {},
nextSpawnTime: 0,
objectIdCounter: 0,
};
});
// Send initial states back to main thread
self.postMessage({
type: "statesInitialized",
data: { animationStates },
});
}
// Reset all animations
function resetAnimations() {
initializeAnimationStates();
}
// Find spawn point in a process
function findSpawnPoint(process) {
if (!process || !process.paths) {
log(`No paths found for process ${process?.id}`);
return null;
}
for (const path of process.paths) {
if (!path || !path.points) continue;
for (const point of path.points) {
if (!point || !point.actions) continue;
const spawnAction = point.actions.find(
(a) => a && a.isUsed && a.type === "Spawn"
);
if (spawnAction) {
return { point, path };
}
}
}
log(`No spawn points found for process ${process.id}`);
return null;
}
// Create a new spawned object with proper initial position
function createSpawnedObject(process, spawnPoint, currentTime, materialType) {
// Extract spawn position from the actual spawn point
const position = spawnPoint.point.position
? [...spawnPoint.point.position]
: [0, 0, 0];
// Get the path position and add it to the spawn point position
const pathPosition = spawnPoint.path.pathPosition || [0, 0, 0];
const absolutePosition = [
position[0] + pathPosition[0],
position[1] + pathPosition[1],
position[2] + pathPosition[2],
];
return {
id: `obj-${process.id}-${animationStates[process.id].objectIdCounter}`,
position: absolutePosition,
state: {
currentIndex: 0,
progress: 0,
isAnimating: true,
speed: process.speed || 1,
isDelaying: false,
delayStartTime: 0,
currentDelayDuration: 0,
delayComplete: false,
currentPathIndex: 0,
// Store the spawn point index to start animation from correct path point
spawnPointIndex: getPointIndexInProcess(process, spawnPoint.point),
},
visible: true,
materialType: materialType || "Default",
spawnTime: currentTime,
};
}
// Get the index of a point within the process animation path
function getPointIndexInProcess(process, point) {
if (!process.paths) return 0;
let cumulativePoints = 0;
for (const path of process.paths) {
for (let i = 0; i < (path.points?.length || 0); i++) {
if (path.points[i].uuid === point.uuid) {
return cumulativePoints + i;
}
}
cumulativePoints += path.points?.length || 0;
}
return 0;
}
// Get point data for current animation index
function getPointDataForAnimationIndex(process, index) {
if (!process.paths) return null;
let cumulativePoints = 0;
for (const path of process.paths) {
const pointCount = path.points?.length || 0;
if (index < cumulativePoints + pointCount) {
const pointIndex = index - cumulativePoints;
return path.points?.[pointIndex] || null;
}
cumulativePoints += pointCount;
}
return null;
}
// Convert process paths to Vector3 format
function getProcessPath(process) {
if (!process.animationPath) {
log(`No animation path for process ${process.id}`);
return [];
}
return process.animationPath.map((p) => ({ x: p.x, y: p.y, z: p.z })) || [];
}
// Handle material swap for an object
function handleMaterialSwap(processId, objectId, materialType) {
const processState = animationStates[processId];
if (!processState || !processState.spawnedObjects[objectId]) return;
processState.spawnedObjects[objectId].materialType = materialType;
// Notify main thread about material change
self.postMessage({
type: "materialChanged",
data: {
processId,
objectId,
materialType,
},
});
}
// Handle point actions for an object
function handlePointActions(processId, objectId, actions = [], currentTime) {
let shouldStopAnimation = false;
const processState = animationStates[processId];
if (!processState || !processState.spawnedObjects[objectId]) return false;
const objectState = processState.spawnedObjects[objectId];
actions.forEach((action) => {
if (!action || !action.isUsed) return;
switch (action.type) {
case "Delay":
if (objectState.state.isDelaying) return;
const delayDuration =
typeof action.delay === "number"
? action.delay
: parseFloat(action.delay || "0");
if (delayDuration > 0) {
objectState.state.isDelaying = true;
objectState.state.delayStartTime = currentTime;
objectState.state.currentDelayDuration = delayDuration;
objectState.state.delayComplete = false;
shouldStopAnimation = true;
}
break;
case "Despawn":
delete processState.spawnedObjects[objectId];
shouldStopAnimation = true;
// Notify main thread about despawn
self.postMessage({
type: "objectDespawned",
data: {
processId,
objectId,
},
});
break;
case "Swap":
if (action.material) {
handleMaterialSwap(processId, objectId, action.material);
}
break;
default:
break;
}
});
return shouldStopAnimation;
}
// Check if point has non-inherit actions
function hasNonInheritActions(actions = []) {
return actions.some((action) => action && action.isUsed && action.type !== "Inherit");
}
// Calculate vector lerp (linear interpolation)
function lerpVectors(v1, v2, alpha) {
return {
x: v1.x + (v2.x - v1.x) * alpha,
y: v1.y + (v2.y - v1.y) * alpha,
z: v1.z + (v2.z - v1.z) * alpha,
};
}
// Calculate vector distance
function distanceBetweenVectors(v1, v2) {
const dx = v2.x - v1.x;
const dy = v2.y - v1.y;
const dz = v2.z - v1.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
// Process spawn logic
function processSpawns(currentTime) {
processes.forEach((process) => {
const processState = animationStates[process.id];
if (!processState) return;
const spawnPointData = findSpawnPoint(process);
if (!spawnPointData || !spawnPointData.point.actions) return;
const spawnAction = spawnPointData.point.actions.find(
(a) => a.isUsed && a.type === "Spawn"
);
if (!spawnAction) return;
const spawnInterval =
typeof spawnAction.spawnInterval === "number"
? spawnAction.spawnInterval
: parseFloat(spawnAction.spawnInterval || "2"); // Default to 2 seconds if not specified
if (currentTime >= processState.nextSpawnTime) {
const newObject = createSpawnedObject(
process,
spawnPointData,
currentTime,
spawnAction.material || "Default"
);
processState.spawnedObjects[newObject.id] = newObject;
processState.objectIdCounter++;
processState.nextSpawnTime = currentTime + spawnInterval;
log(`Spawned object ${newObject.id} for process ${process.id}`);
// Notify main thread about new object
self.postMessage({
type: "objectSpawned",
data: {
processId: process.id,
object: newObject,
},
});
}
});
}
// Update all animations
function updateAnimations(delta, currentTime) {
// First handle spawning of new objects
processSpawns(currentTime);
// Then animate existing objects
processes.forEach((process) => {
const processState = animationStates[process.id];
if (!processState) return;
const path = getProcessPath(process);
if (path.length < 2) {
log(`Path too short for process ${process.id}, length: ${path.length}`);
return;
}
const updatedObjects = {};
let hasChanges = false;
Object.entries(processState.spawnedObjects).forEach(([objectId, obj]) => {
if (!obj.visible || !obj.state.isAnimating) return;
const stateRef = obj.state;
// Use the spawnPointIndex as starting point if it's the initial movement
if (stateRef.currentIndex === 0 && stateRef.progress === 0) {
stateRef.currentIndex = stateRef.spawnPointIndex || 0;
}
// Get current point data
const currentPointData = getPointDataForAnimationIndex(
process,
stateRef.currentIndex
);
// Execute actions when arriving at a new point
if (stateRef.progress === 0 && currentPointData?.actions) {
const shouldStop = handlePointActions(
process.id,
objectId,
currentPointData.actions,
currentTime
);
if (shouldStop) return;
}
// Handle delays
if (stateRef.isDelaying) {
if (
currentTime - stateRef.delayStartTime >=
stateRef.currentDelayDuration
) {
stateRef.isDelaying = false;
stateRef.delayComplete = true;
} else {
updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
return; // Keep waiting
}
}
const nextPointIdx = stateRef.currentIndex + 1;
const isLastPoint = nextPointIdx >= path.length;
if (isLastPoint) {
if (currentPointData?.actions) {
const shouldStop = !hasNonInheritActions(currentPointData.actions);
if (shouldStop) {
// Reached the end of path with no more actions
delete processState.spawnedObjects[objectId];
log(`Object ${objectId} completed path`);
// Notify main thread to remove the object
self.postMessage({
type: "objectCompleted",
data: {
processId: process.id,
objectId,
},
});
return;
}
}
}
if (!isLastPoint) {
const currentPos = path[stateRef.currentIndex];
const nextPos = path[nextPointIdx];
const distance = distanceBetweenVectors(currentPos, nextPos);
// Ensure we don't divide by zero
if (distance > 0) {
const movement = stateRef.speed * delta;
// Update progress based on distance and speed
const oldProgress = stateRef.progress;
stateRef.progress += movement / distance;
if (stateRef.progress >= 1) {
// Reached next point
stateRef.currentIndex = nextPointIdx;
stateRef.progress = 0;
stateRef.delayComplete = false;
obj.position = [nextPos.x, nextPos.y, nextPos.z];
} else {
// Interpolate position
const lerpedPos = lerpVectors(currentPos, nextPos, stateRef.progress);
obj.position = [lerpedPos.x, lerpedPos.y, lerpedPos.z];
}
// Only send updates when there's meaningful movement
if (Math.abs(oldProgress - stateRef.progress) > 0.01) {
hasChanges = true;
}
} else {
// Skip to next point if distance is zero
stateRef.currentIndex = nextPointIdx;
stateRef.progress = 0;
obj.position = [nextPos.x, nextPos.y, nextPos.z];
hasChanges = true;
}
}
updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
});
// Update animation state with modified objects
if (Object.keys(updatedObjects).length > 0) {
processState.spawnedObjects = {
...processState.spawnedObjects,
...updatedObjects,
};
// Only send position updates when there are meaningful changes
if (hasChanges) {
self.postMessage({
type: "positionsUpdated",
data: {
processId: process.id,
objects: updatedObjects,
},
});
}
}
});
}

View File

@ -1,4 +0,0 @@
const animationWorker = () => {
return;
};

View File

@ -5,7 +5,6 @@ import { useLoader, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { GLTF } from "three-stdlib";
import boxGltb from "../../../assets/gltf-glb/crate_box.glb";
import camera from "../../../assets/gltf-glb/camera face 2.gltf";
interface PointAction {
uuid: string;
@ -44,6 +43,8 @@ interface ProcessData {
animationPath: { x: number; y: number; z: number }[];
pointActions: PointAction[][];
speed: number;
customMaterials?: Record<string, THREE.Material>;
renderAs?: "box" | "custom";
}
interface AnimationState {
@ -58,19 +59,35 @@ interface AnimationState {
currentPathIndex: number;
}
interface SpawnedObject {
ref: React.RefObject<THREE.Group | THREE.Mesh>;
state: AnimationState;
visible: boolean;
material: THREE.Material;
spawnTime: number;
currentMaterialType: string;
}
interface ProcessAnimationState {
spawnedObjects: { [objectId: string]: SpawnedObject };
nextSpawnTime: number;
objectIdCounter: number;
}
const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({
processes,
}) => {
console.log("processes: ", processes);
const gltf = useLoader(GLTFLoader, boxGltb) as GLTF;
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { isPlaying } = usePlayButtonStore();
const groupRef = useRef<THREE.Group>(null);
const meshRef = useRef<THREE.Mesh>(null);
const [visible, setVisible] = useState(false);
const [currentPathIndex, setCurrentPathIndex] = useState(0);
const materials = useMemo(
const [animationStates, setAnimationStates] = useState<
Record<string, ProcessAnimationState>
>({});
// Base materials
const baseMaterials = useMemo(
() => ({
Wood: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
Box: new THREE.MeshStandardMaterial({
@ -88,44 +105,211 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({
[]
);
const [currentMaterial, setCurrentMaterial] = useState<THREE.Material>(
materials.Default
);
// Initialize animation states when processes or play state changes
useEffect(() => {
if (!isPlaying) {
setAnimationStates({});
return;
}
const { animationPath, currentProcess } = useMemo(() => {
const defaultProcess = {
animationPath: [],
pointActions: [],
speed: 1,
paths: [],
const newStates: Record<string, ProcessAnimationState> = {};
processes.forEach((process) => {
newStates[process.id] = {
spawnedObjects: {},
nextSpawnTime: 0,
objectIdCounter: 0,
};
});
setAnimationStates(newStates);
}, [isPlaying, processes]);
// Find spawn point in a process
const findSpawnPoint = (process: ProcessData): ProcessPoint | null => {
for (const path of process.paths || []) {
for (const point of path.points || []) {
const spawnAction = point.actions?.find(
(a) => a.isUsed && a.type === "Spawn"
);
if (spawnAction) {
return point;
}
}
}
return null;
};
// Create a new spawned object
const createSpawnedObject = (
process: ProcessData,
currentTime: number,
materialType: string
): SpawnedObject => {
const processMaterials = {
...baseMaterials,
...(process.customMaterials || {}),
};
const cp = processes?.[0] || defaultProcess;
return {
animationPath:
cp.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) || [],
currentProcess: cp,
ref: React.createRef(),
state: {
currentIndex: 0,
progress: 0,
isAnimating: true,
speed: process.speed || 1,
isDelaying: false,
delayStartTime: 0,
currentDelayDuration: 0,
delayComplete: false,
currentPathIndex: 0,
},
visible: true,
material:
processMaterials[materialType as keyof typeof processMaterials] ||
baseMaterials.Default,
currentMaterialType: materialType,
spawnTime: currentTime,
};
}, [processes]);
};
const animationStateRef = useRef<AnimationState>({
currentIndex: 0,
progress: 0,
isAnimating: false,
speed: currentProcess.speed,
isDelaying: false,
delayStartTime: 0,
currentDelayDuration: 0,
delayComplete: false,
currentPathIndex: 0,
});
// Handle material swap for an object
const handleMaterialSwap = (
processId: string,
objectId: string,
materialType: string
) => {
setAnimationStates((prev) => {
const processState = prev[processId];
if (!processState || !processState.spawnedObjects[objectId]) return prev;
const getPointDataForAnimationIndex = (index: number) => {
if (!processes[0]?.paths) return null;
const process = processes.find((p) => p.id === processId);
const processMaterials = {
...baseMaterials,
...(process?.customMaterials || {}),
};
const newMaterial =
processMaterials[materialType as keyof typeof processMaterials] ||
baseMaterials.Default;
return {
...prev,
[processId]: {
...processState,
spawnedObjects: {
...processState.spawnedObjects,
[objectId]: {
...processState.spawnedObjects[objectId],
material: newMaterial,
currentMaterialType: materialType,
},
},
},
};
});
};
// Handle point actions for an object
const handlePointActions = (
processId: string,
objectId: string,
actions: PointAction[] = [],
currentTime: number
): boolean => {
let shouldStopAnimation = false;
actions.forEach((action) => {
if (!action.isUsed) return;
switch (action.type) {
case "Delay":
setAnimationStates((prev) => {
const processState = prev[processId];
if (
!processState ||
!processState.spawnedObjects[objectId] ||
processState.spawnedObjects[objectId].state.isDelaying
) {
return prev;
}
const delayDuration =
typeof action.delay === "number"
? action.delay
: parseFloat(action.delay as string) || 0;
if (delayDuration > 0) {
return {
...prev,
[processId]: {
...processState,
spawnedObjects: {
...processState.spawnedObjects,
[objectId]: {
...processState.spawnedObjects[objectId],
state: {
...processState.spawnedObjects[objectId].state,
isDelaying: true,
delayStartTime: currentTime,
currentDelayDuration: delayDuration,
delayComplete: false,
},
},
},
},
};
}
return prev;
});
shouldStopAnimation = true;
break;
case "Despawn":
setAnimationStates((prev) => {
const processState = prev[processId];
if (!processState) return prev;
const newSpawnedObjects = { ...processState.spawnedObjects };
delete newSpawnedObjects[objectId];
return {
...prev,
[processId]: {
...processState,
spawnedObjects: newSpawnedObjects,
},
};
});
shouldStopAnimation = true;
break;
case "Swap":
if (action.material) {
handleMaterialSwap(processId, objectId, action.material);
}
break;
default:
break;
}
});
return shouldStopAnimation;
};
// Check if point has non-inherit actions
const hasNonInheritActions = (actions: PointAction[] = []): boolean => {
return actions.some((action) => action.isUsed && action.type !== "Inherit");
};
// Get point data for current animation index
const getPointDataForAnimationIndex = (
process: ProcessData,
index: number
): ProcessPoint | null => {
if (!process.paths) return null;
let cumulativePoints = 0;
console.log("cumulativePoints: ", cumulativePoints);
for (const path of processes[0].paths) {
for (const path of process.paths) {
const pointCount = path.points?.length || 0;
if (index < cumulativePoints + pointCount) {
@ -139,209 +323,202 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({
return null;
};
useEffect(() => {
if (isPlaying) {
setVisible(true);
animationStateRef.current = {
currentIndex: 0,
progress: 0,
isAnimating: true,
speed: currentProcess.speed,
isDelaying: false,
delayStartTime: 0,
currentDelayDuration: 0,
delayComplete: false,
currentPathIndex: 0,
};
const currentRef = gltf?.scene ? groupRef.current : meshRef.current;
if (currentRef && animationPath.length > 0) {
currentRef.position.copy(animationPath[0]);
}
} else {
animationStateRef.current.isAnimating = false;
}
}, [isPlaying, currentProcess, animationPath]);
const handleMaterialSwap = (materialType: string) => {
setCurrentMaterial(
materials[materialType as keyof typeof materials] || materials.Default
);
};
const hasNonInheritActions = (actions: PointAction[] = []) => {
return actions.some((action) => action.isUsed && action.type !== "Inherit");
};
const handlePointActions = (
actions: PointAction[] = [],
currentTime: number
) => {
let shouldStopAnimation = false;
actions.forEach((action) => {
if (!action.isUsed) return;
switch (action.type) {
case "Delay":
if (
!animationStateRef.current.isDelaying &&
!animationStateRef.current.delayComplete
) {
const delayDuration =
typeof action.delay === "number"
? action.delay
: parseFloat(action.delay as string) || 0;
if (delayDuration > 0) {
animationStateRef.current.isDelaying = true;
animationStateRef.current.delayStartTime = currentTime;
animationStateRef.current.currentDelayDuration = delayDuration;
shouldStopAnimation = true;
}
}
break;
case "Despawn":
setVisible(false);
setIsPlaying(false);
animationStateRef.current.isAnimating = false;
shouldStopAnimation = true;
break;
case "Spawn":
setVisible(true);
break;
case "Swap":
if (action.material) {
handleMaterialSwap(action.material);
}
break;
case "Inherit":
// Handle inherit logic if needed
break;
}
});
return shouldStopAnimation;
};
useFrame((state, delta) => {
const currentRef = gltf?.scene ? groupRef.current : meshRef.current;
if (
!currentRef ||
!animationStateRef.current.isAnimating ||
animationPath.length < 2
) {
return;
}
// Spawn objects for all processes
useFrame((state) => {
if (!isPlaying) return;
const currentTime = state.clock.getElapsedTime();
const path = animationPath;
const stateRef = animationStateRef.current;
setAnimationStates((prev) => {
const newStates = { ...prev };
// Check if we need to switch paths (specific to your structure)
if (stateRef.currentIndex === 3 && stateRef.currentPathIndex === 0) {
setCurrentPathIndex(1);
stateRef.currentPathIndex = 1;
}
processes.forEach((process) => {
const processState = newStates[process.id];
if (!processState) return;
// Get the current point data
const currentPointData = getPointDataForAnimationIndex(
stateRef.currentIndex
);
const spawnPoint = findSpawnPoint(process);
if (!spawnPoint || !spawnPoint.actions) return;
// Execute actions when we first arrive at a point
if (stateRef.progress === 0 && currentPointData?.actions) {
console.log(
`Processing actions at point ${stateRef.currentIndex}`,
currentPointData.actions
);
const shouldStop = handlePointActions(
currentPointData.actions,
currentTime
);
if (shouldStop) return;
}
// Handle delays
if (stateRef.isDelaying) {
console.log(
`Delaying... ${currentTime - stateRef.delayStartTime}/${
stateRef.currentDelayDuration
}`
);
if (
currentTime - stateRef.delayStartTime >=
stateRef.currentDelayDuration
) {
stateRef.isDelaying = false;
stateRef.delayComplete = true;
} else {
return; // Keep waiting
}
}
const nextPointIdx = stateRef.currentIndex + 1;
const isLastPoint = nextPointIdx >= path.length;
if (isLastPoint) {
if (currentPointData?.actions) {
const shouldStop = !hasNonInheritActions(currentPointData.actions);
if (shouldStop) {
currentRef.position.copy(path[stateRef.currentIndex]);
setIsPlaying(false);
stateRef.isAnimating = false;
return;
}
}
}
if (!isLastPoint) {
const nextPoint = path[nextPointIdx];
const distance = path[stateRef.currentIndex].distanceTo(nextPoint);
const movement = stateRef.speed * delta;
stateRef.progress += movement / distance;
if (stateRef.progress >= 1) {
stateRef.currentIndex = nextPointIdx;
stateRef.progress = 0;
stateRef.delayComplete = false;
currentRef.position.copy(nextPoint);
} else {
currentRef.position.lerpVectors(
path[stateRef.currentIndex],
nextPoint,
stateRef.progress
const spawnAction = spawnPoint.actions.find(
(a) => a.isUsed && a.type === "Spawn"
);
}
}
if (!spawnAction) return;
const spawnInterval =
typeof spawnAction.spawnInterval === "number"
? spawnAction.spawnInterval
: 0;
if (currentTime >= processState.nextSpawnTime) {
const objectId = `obj-${process.id}-${processState.objectIdCounter}`;
newStates[process.id] = {
...processState,
spawnedObjects: {
...processState.spawnedObjects,
[objectId]: createSpawnedObject(
process,
currentTime,
spawnAction.material || "Default"
),
},
objectIdCounter: processState.objectIdCounter + 1,
nextSpawnTime: currentTime + spawnInterval,
};
}
});
return newStates;
});
});
// Animate objects for all processes
useFrame((state, delta) => {
if (!isPlaying) return;
const currentTime = state.clock.getElapsedTime();
setAnimationStates((prev) => {
const newStates = { ...prev };
processes.forEach((process) => {
const processState = newStates[process.id];
if (!processState) return;
const path =
process.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) ||
[];
if (path.length < 2) return;
const updatedObjects = { ...processState.spawnedObjects };
Object.entries(processState.spawnedObjects).forEach(
([objectId, obj]) => {
if (!obj.visible || !obj.state.isAnimating) return;
const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current;
if (!currentRef) return;
const stateRef = obj.state;
// Get current point data
const currentPointData = getPointDataForAnimationIndex(
process,
stateRef.currentIndex
);
// Execute actions when arriving at a new point
if (stateRef.progress === 0 && currentPointData?.actions) {
const shouldStop = handlePointActions(
process.id,
objectId,
currentPointData.actions,
currentTime
);
if (shouldStop) return;
}
// Handle delays
if (stateRef.isDelaying) {
if (
currentTime - stateRef.delayStartTime >=
stateRef.currentDelayDuration
) {
stateRef.isDelaying = false;
stateRef.delayComplete = true;
} else {
return; // Keep waiting
}
}
const nextPointIdx = stateRef.currentIndex + 1;
const isLastPoint = nextPointIdx >= path.length;
if (isLastPoint) {
if (currentPointData?.actions) {
const shouldStop = !hasNonInheritActions(
currentPointData.actions
);
if (shouldStop) {
currentRef.position.copy(path[stateRef.currentIndex]);
delete updatedObjects[objectId];
return;
}
}
}
if (!isLastPoint) {
const nextPoint = path[nextPointIdx];
const distance =
path[stateRef.currentIndex].distanceTo(nextPoint);
const movement = stateRef.speed * delta;
stateRef.progress += movement / distance;
if (stateRef.progress >= 1) {
stateRef.currentIndex = nextPointIdx;
stateRef.progress = 0;
stateRef.delayComplete = false;
currentRef.position.copy(nextPoint);
} else {
currentRef.position.lerpVectors(
path[stateRef.currentIndex],
nextPoint,
stateRef.progress
);
}
}
updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
}
);
newStates[process.id] = {
...processState,
spawnedObjects: updatedObjects,
};
});
return newStates;
});
});
if (!processes || processes.length === 0) {
return null;
}
if (!gltf?.scene) {
return visible ? (
<mesh ref={meshRef} material={currentMaterial}>
<boxGeometry args={[1, 1, 1]} />
</mesh>
) : null;
}
return (
<>
{Object.entries(animationStates).flatMap(([processId, processState]) =>
Object.entries(processState.spawnedObjects)
.filter(([_, obj]) => obj.visible)
.map(([objectId, obj]) => {
const process = processes.find((p) => p.id === processId);
console.log("process: ", process);
const renderAs = process?.renderAs || "custom";
return visible ? (
<group ref={groupRef}>
<primitive
object={gltf.scene.clone()}
scale={[1, 1, 1]}
material={currentMaterial}
/>
</group>
) : null;
return renderAs === "box" ? (
<mesh
key={objectId}
ref={obj.ref as React.RefObject<THREE.Mesh>}
material={obj.material}
>
<boxGeometry args={[1, 1, 1]} />
</mesh>
) : (
gltf?.scene && (
<group
key={objectId}
ref={obj.ref as React.RefObject<THREE.Group>}
>
<primitive
object={gltf.scene.clone()}
material={obj.material}
/>
</group>
)
);
})
)}
</>
);
};
export default ProcessAnimator;

View File

@ -10,6 +10,7 @@ const ProcessContainer: React.FC = () => {
<>
<ProcessCreator onProcessesCreated={setProcesses} />
{processes.length > 0 && <ProcessAnimator processes={processes} />}
</>
);
};

View File

@ -422,6 +422,7 @@ export interface PointAction {
spawnInterval: string | number;
isUsed: boolean;
}
export interface PathPoint {
uuid: string;
position: [number, number, number];
@ -430,12 +431,14 @@ export interface PathPoint {
targets: Array<{ pathUUID: string }>;
};
}
export interface SimulationPath {
modeluuid: string;
points: PathPoint[];
pathPosition: [number, number, number];
speed?: number;
}
export interface Process {
id: string;
paths: SimulationPath[];
@ -443,6 +446,7 @@ export interface Process {
pointActions: PointAction[][];
speed: number;
}
interface ProcessCreatorProps {
onProcessesCreated: (processes: Process[]) => void;
}
@ -453,83 +457,51 @@ function convertToSimulationPath(
): SimulationPath {
const { modeluuid } = path;
// Helper function to normalize actions
// Simplified normalizeAction function that preserves exact original properties
const normalizeAction = (action: any): PointAction => {
return { ...action }; // Return exact copy with no modifications
};
// Extract points from the path
let points: PathPoint[];
if (path.type === "Conveyor") {
points = path.points.map((point) => ({
uuid: point.uuid,
position: point.position,
actions: point.actions.map(normalizeAction), // Preserve exact actions
connections: {
targets: point.connections.targets.map((target) => ({
pathUUID: target.pathUUID,
})),
},
}));
} else {
points = [
{
uuid: path.point.uuid,
position: path.point.position,
actions: Array.isArray(path.point.actions)
? path.point.actions.map(normalizeAction)
: [normalizeAction(path.point.actions)],
return {
modeluuid,
points: path.points.map((point) => ({
uuid: point.uuid,
position: point.position,
actions: point.actions.map(normalizeAction), // Preserve exact actions
connections: {
targets: path.point.connections.targets.map((target) => ({
targets: point.connections.targets.map((target) => ({
pathUUID: target.pathUUID,
})),
},
},
];
}
// Check if point 1 or point 2 has a spawn action
const hasSpawnInFirstTwoPoints =
points.length >= 2 &&
(points[0].actions.some(
(action) => action.type.toLowerCase() === "spawn"
) ||
points[1].actions.some(
(action) => action.type.toLowerCase() === "spawn"
));
// Swap points 1 and 3 only if:
// 1. There are at least three points,
// 2. The third point contains a spawn action, and
// 3. Neither point 1 nor point 2 has a spawn action
if (
!hasSpawnInFirstTwoPoints &&
points.length >= 3 &&
points[2].actions.some((action) => action.type.toLowerCase() === "spawn")
) {
const temp = points[0];
points[0] = points[2];
points[2] = temp;
}
// Determine the speed based on the type of path
let speed: number;
if (path.type === "Conveyor") {
speed =
typeof path.speed === "string"
? parseFloat(path.speed) || 1
: path.speed || 1;
})),
pathPosition: path.position,
speed:
typeof path.speed === "string"
? parseFloat(path.speed) || 1
: path.speed || 1,
};
} else {
// For VehicleEventsSchema, use a default speed of 1
speed = 1;
return {
modeluuid,
points: [
{
uuid: path.point.uuid,
position: path.point.position,
actions: Array.isArray(path.point.actions)
? path.point.actions.map(normalizeAction)
: [normalizeAction(path.point.actions)],
connections: {
targets: path.point.connections.targets.map((target) => ({
pathUUID: target.pathUUID,
})),
},
},
],
pathPosition: path.position,
speed: path.point.speed || 1,
};
}
return {
modeluuid,
points,
pathPosition: path.position,
speed,
};
}
// Custom shallow comparison for arrays
@ -556,6 +528,7 @@ function shouldReverseNextPath(
nextPath: SimulationPath
): boolean {
if (nextPath.points.length !== 3) return false;
const currentLastPoint = currentPath.points[currentPath.points.length - 1];
const nextFirstPoint = nextPath.points[0];
const nextLastPoint = nextPath.points[nextPath.points.length - 1];
@ -582,25 +555,56 @@ function shouldReverseNextPath(
return connectsToLast && !connectsToFirst;
}
// Check if a point has a spawn action
function hasSpawnAction(point: PathPoint): boolean {
return point.actions.some((action) => action.type.toLowerCase() === "spawn");
}
// Ensure spawn point is always at the beginning of the path
function ensureSpawnPointIsFirst(path: SimulationPath): SimulationPath {
if (path.points.length !== 3) return path;
// If the third point has spawn action and first doesn't, reverse the array
if (hasSpawnAction(path.points[2]) && !hasSpawnAction(path.points[0])) {
return {
...path,
points: [...path.points].reverse(),
};
}
return path;
}
// Updated path adjustment function
function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] {
if (paths.length < 2) return paths;
if (paths.length < 1) return paths;
const adjustedPaths = [...paths];
// First ensure all paths have spawn points at the beginning
for (let i = 0; i < adjustedPaths.length; i++) {
adjustedPaths[i] = ensureSpawnPointIsFirst(adjustedPaths[i]);
}
// Then handle connections between paths
for (let i = 0; i < adjustedPaths.length - 1; i++) {
const currentPath = adjustedPaths[i];
const nextPath = adjustedPaths[i + 1];
if (shouldReverseNextPath(currentPath, nextPath)) {
const reversedPoints = [
nextPath.points[2],
nextPath.points[1],
nextPath.points[0],
];
adjustedPaths[i + 1] = {
...nextPath,
points: reversedPoints,
};
}
}
return adjustedPaths;
}
@ -608,6 +612,7 @@ function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] {
export function useProcessCreation() {
const { scene } = useThree();
const [processes, setProcesses] = useState<Process[]>([]);
const hasSpawnAction = useCallback((path: SimulationPath): boolean => {
return path.points.some((point) =>
point.actions.some((action) => action.type.toLowerCase() === "spawn")
@ -619,9 +624,11 @@ export function useProcessCreation() {
if (!paths || paths.length === 0) {
return createEmptyProcess();
}
const animationPath: THREE.Vector3[] = [];
const pointActions: PointAction[][] = [];
const processSpeed = paths[0]?.speed || 1;
for (const path of paths) {
for (const point of path.points) {
const obj = scene.getObjectByProperty("uuid", point.uuid);
@ -629,11 +636,13 @@ export function useProcessCreation() {
console.warn(`Object with UUID ${point.uuid} not found in scene`);
continue;
}
const position = obj.getWorldPosition(new THREE.Vector3());
animationPath.push(position.clone());
pointActions.push(point.actions);
}
}
return {
id: `process-${Math.random().toString(36).substring(2, 11)}`,
paths,
@ -654,8 +663,10 @@ export function useProcessCreation() {
const connectedPaths: SimulationPath[] = [];
const queue: SimulationPath[] = [initialPath];
visited.add(initialPath.modeluuid);
const pathMap = new Map<string, SimulationPath>();
allPaths.forEach((path) => pathMap.set(path.modeluuid, path));
while (queue.length > 0) {
const currentPath = queue.shift()!;
connectedPaths.push(currentPath);
@ -688,6 +699,7 @@ export function useProcessCreation() {
}
}
}
return connectedPaths;
},
[]
@ -696,10 +708,12 @@ export function useProcessCreation() {
const createProcessesFromPaths = useCallback(
(paths: SimulationPath[]): Process[] => {
if (!paths || paths.length === 0) return [];
const visited = new Set<string>();
const processes: Process[] = [];
const pathMap = new Map<string, SimulationPath>();
paths.forEach((path) => pathMap.set(path.modeluuid, path));
for (const path of paths) {
if (!visited.has(path.modeluuid) && hasSpawnAction(path)) {
const connectedPaths = getAllConnectedPaths(path, paths, visited);
@ -708,6 +722,7 @@ export function useProcessCreation() {
processes.push(process);
}
}
return processes;
},
[createProcess, getAllConnectedPaths, hasSpawnAction]
@ -736,24 +751,26 @@ const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
);
}, [simulationPaths]);
// Enhanced dependency tracking that includes action types
const pathsDependency = useMemo(() => {
if (!convertedPaths) return null;
return convertedPaths.map((path) => ({
id: path.modeluuid,
hasSpawn: path.points.some((p: PathPoint) =>
p.actions.some((a: PointAction) => a.type.toLowerCase() === "spawn")
),
// Track all action types for each point
actionSignature: path.points
.map((point, index) =>
point.actions.map((action) => `${index}-${action.type}`).join("|")
)
.join(","),
connections: path.points
.flatMap((p: PathPoint) =>
p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID)
)
.join(","),
actions: JSON.stringify(
path.points.flatMap((p: PathPoint) => p.actions)
), // Serialize all actions
}));
}, [convertedPaths]);
// Force process recreation when paths change
useEffect(() => {
if (!convertedPaths || convertedPaths.length === 0) {
if (prevProcessesRef.current.length > 0) {
@ -762,28 +779,17 @@ const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
}
return;
}
if (areArraysEqual(prevPathsRef.current, convertedPaths)) {
return;
}
prevPathsRef.current = convertedPaths;
// Always regenerate processes if the pathsDependency has changed
// This ensures action type changes will be detected
const newProcesses = createProcessesFromPaths(convertedPaths);
if (
newProcesses.length !== prevProcessesRef.current.length ||
!newProcesses.every(
(proc, i) =>
proc.paths.length === prevProcessesRef.current[i]?.paths.length &&
proc.paths.every(
(path, j) =>
path.modeluuid ===
prevProcessesRef.current[i]?.paths[j]?.modeluuid
)
)
) {
onProcessesCreated(newProcesses);
prevProcessesRef.current = newProcesses;
}
prevPathsRef.current = convertedPaths;
// Always update processes when action types change
onProcessesCreated(newProcesses);
prevProcessesRef.current = newProcesses;
}, [
pathsDependency,
pathsDependency, // This now includes action types
onProcessesCreated,
convertedPaths,
createProcessesFromPaths,

View File

@ -1,47 +1,50 @@
import { useState, useEffect, useRef, useMemo } from 'react';
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../store/store';
import * as THREE from 'three';
import Behaviour from './behaviour/behaviour';
import PathCreation from './path/pathCreation';
import PathConnector from './path/pathConnector';
import useModuleStore from '../../store/useModuleStore';
import ProcessContainer from './process/processContainer';
import { useState, useEffect, useRef, useMemo } from "react";
import {
useSelectedActionSphere,
useSelectedPath,
useSimulationPaths,
} from "../../store/store";
import * as THREE from "three";
import Behaviour from "./behaviour/behaviour";
import PathCreation from "./path/pathCreation";
import PathConnector from "./path/pathConnector";
import useModuleStore from "../../store/useModuleStore";
import ProcessContainer from "./process/processContainer";
function Simulation() {
const { activeModule } = useModuleStore();
const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>;
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const [processes, setProcesses] = useState([]);
const { activeModule } = useModuleStore();
const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>;
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const [processes, setProcesses] = useState([]);
useEffect(() => {
// console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]);
useEffect(() => {
// console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]);
// useEffect(() => {
// if (selectedActionSphere) {
// console.log('selectedActionSphere: ', selectedActionSphere);
// }
// }, [selectedActionSphere]);
// useEffect(() => {
// if (selectedActionSphere) {
// console.log('selectedActionSphere: ', selectedActionSphere);
// }
// }, [selectedActionSphere]);
// useEffect(() => {
// if (selectedPath) {
// console.log('selectedPath: ', selectedPath);
// }
// }, [selectedPath]);
// useEffect(() => {
// if (selectedPath) {
// console.log('selectedPath: ', selectedPath);
// }
// }, [selectedPath]);
return (
return (
<>
<Behaviour />
{activeModule === "simulation" && (
<>
<Behaviour />
{activeModule === 'simulation' && (
<>
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer />
</>
)}
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer />
</>
);
)}
</>
);
}
export default Simulation;
export default Simulation;