From 844c8c3366d52029f0aff845052975523fb2b7a6 Mon Sep 17 00:00:00 2001 From: SreeNath14 <153710861+SreeNath14@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:10:44 +0530 Subject: [PATCH 1/9] updated new animation --- .../simulation/process/animationWorker.js | 4 + .../simulation/process/processAnimator.tsx | 149 ++--- .../simulation/process/processCreator.tsx | 542 +++++++++++++++--- 3 files changed, 514 insertions(+), 181 deletions(-) create mode 100644 app/src/modules/simulation/process/animationWorker.js diff --git a/app/src/modules/simulation/process/animationWorker.js b/app/src/modules/simulation/process/animationWorker.js new file mode 100644 index 0000000..e9dddd1 --- /dev/null +++ b/app/src/modules/simulation/process/animationWorker.js @@ -0,0 +1,4 @@ + +const animationWorker = () => { + return; +}; diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx index 123ec80..d8e35e6 100644 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -5,11 +5,13 @@ 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; name: string; type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap"; + objectType: string; material: string; delay: string | number; spawnInterval: string | number; @@ -54,18 +56,8 @@ interface AnimationState { currentDelayDuration: number; delayComplete: boolean; currentPathIndex: number; - spawnPoints: Record< - string, - { - position: THREE.Vector3; - interval: number; - lastSpawnTime: number; - } - >; } -const MAX_SPAWNED_OBJECTS = 20; - const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ processes, }) => { @@ -76,7 +68,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const groupRef = useRef(null); const meshRef = useRef(null); const [visible, setVisible] = useState(false); - const spawnedObjectsRef = useRef([]); + const [currentPathIndex, setCurrentPathIndex] = useState(0); const materials = useMemo( () => ({ @@ -125,18 +117,26 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ currentDelayDuration: 0, delayComplete: false, currentPathIndex: 0, - spawnPoints: {}, }); const getPointDataForAnimationIndex = (index: number) => { if (!processes[0]?.paths) return null; - if (index < 3) { - return processes[0].paths[0]?.points[index]; - } else { - const path2Index = index - 3; - return processes[0].paths[1]?.points[path2Index]; + let cumulativePoints = 0; + console.log("cumulativePoints: ", cumulativePoints); + + for (const path of processes[0].paths) { + const pointCount = path.points?.length || 0; + + if (index < cumulativePoints + pointCount) { + const pointIndex = index - cumulativePoints; + return path.points?.[pointIndex] || null; + } + + cumulativePoints += pointCount; } + + return null; }; useEffect(() => { @@ -152,22 +152,8 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ currentDelayDuration: 0, delayComplete: false, currentPathIndex: 0, - spawnPoints: {}, }; - // Clear spawned objects - if (groupRef.current) { - spawnedObjectsRef.current.forEach((obj) => { - if (groupRef.current?.children.includes(obj)) { - groupRef.current.remove(obj); - } - if (obj instanceof THREE.Mesh) { - obj.material.dispose(); - } - }); - spawnedObjectsRef.current = []; - } - const currentRef = gltf?.scene ? groupRef.current : meshRef.current; if (currentRef && animationPath.length > 0) { currentRef.position.copy(animationPath[0]); @@ -178,15 +164,9 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ }, [isPlaying, currentProcess, animationPath]); const handleMaterialSwap = (materialType: string) => { - const newMaterial = - materials[materialType as keyof typeof materials] || materials.Default; - setCurrentMaterial(newMaterial); - - spawnedObjectsRef.current.forEach((obj) => { - if (obj instanceof THREE.Mesh) { - obj.material = newMaterial.clone(); - } - }); + setCurrentMaterial( + materials[materialType as keyof typeof materials] || materials.Default + ); }; const hasNonInheritActions = (actions: PointAction[] = []) => { @@ -195,8 +175,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const handlePointActions = ( actions: PointAction[] = [], - currentTime: number, - currentPosition: THREE.Vector3 + currentTime: number ) => { let shouldStopAnimation = false; @@ -231,18 +210,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ break; case "Spawn": - const spawnInterval = - typeof action.spawnInterval === "number" - ? action.spawnInterval - : parseFloat(action.spawnInterval as string) || 1; - - const positionKey = currentPosition.toArray().join(","); - - animationStateRef.current.spawnPoints[positionKey] = { - position: currentPosition.clone(), - interval: spawnInterval, - lastSpawnTime: currentTime - spawnInterval, // Force immediate spawn - }; + setVisible(true); break; case "Swap": @@ -252,6 +220,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ break; case "Inherit": + // Handle inherit logic if needed break; } }); @@ -273,24 +242,38 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const path = animationPath; const stateRef = animationStateRef.current; + // Check if we need to switch paths (specific to your structure) if (stateRef.currentIndex === 3 && stateRef.currentPathIndex === 0) { + setCurrentPathIndex(1); stateRef.currentPathIndex = 1; } + // Get the current point data const currentPointData = getPointDataForAnimationIndex( stateRef.currentIndex ); + // 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, - currentRef.position + currentTime ); if (shouldStop) return; } + // Handle delays if (stateRef.isDelaying) { + console.log( + `Delaying... ${currentTime - stateRef.delayStartTime}/${ + stateRef.currentDelayDuration + }` + ); if ( currentTime - stateRef.delayStartTime >= stateRef.currentDelayDuration @@ -298,46 +281,10 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ stateRef.isDelaying = false; stateRef.delayComplete = true; } else { - return; + return; // Keep waiting } } - // Handle spawning - this is the key updated part - Object.entries(stateRef.spawnPoints).forEach(([key, spawnPoint]) => { - if (currentTime - spawnPoint.lastSpawnTime >= spawnPoint.interval) { - spawnPoint.lastSpawnTime = currentTime; - - if (gltf?.scene && groupRef?.current) { - const newObject = gltf.scene.clone(); - newObject.position.copy(spawnPoint.position); - - newObject.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material = currentMaterial.clone(); - } - }); - - groupRef.current.add(newObject); - spawnedObjectsRef.current.push(newObject); - - // Clean up old objects if needed - console.log( - "spawnedObjectsRef.current.length: ", - spawnedObjectsRef.current.length - ); - if (spawnedObjectsRef.current.length > MAX_SPAWNED_OBJECTS) { - const oldest = spawnedObjectsRef.current.shift(); - if (oldest && groupRef.current.children.includes(oldest)) { - groupRef.current.remove(oldest); - if (oldest instanceof THREE.Mesh) { - oldest.material.dispose(); - } - } - } - } - } - }); - const nextPointIdx = stateRef.currentIndex + 1; const isLastPoint = nextPointIdx >= path.length; @@ -374,22 +321,6 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ } }); - useEffect(() => { - return () => { - if (groupRef.current) { - spawnedObjectsRef.current.forEach((obj) => { - if (groupRef.current?.children.includes(obj)) { - groupRef.current.remove(obj); - } - if (obj instanceof THREE.Mesh) { - obj.material.dispose(); - } - }); - spawnedObjectsRef.current = []; - } - }; - }, []); - if (!processes || processes.length === 0) { return null; } diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx index 8e1ed1d..e70408b 100644 --- a/app/src/modules/simulation/process/processCreator.tsx +++ b/app/src/modules/simulation/process/processCreator.tsx @@ -1,3 +1,402 @@ +// import React, { +// useEffect, +// useMemo, +// useState, +// useCallback, +// useRef, +// } from "react"; +// import { useSimulationPaths } from "../../../store/store"; +// import * as THREE from "three"; +// import { useThree } from "@react-three/fiber"; +// import { +// ConveyorEventsSchema, +// VehicleEventsSchema, +// } from "../../../types/world/worldTypes"; + +// // Type definitions +// export interface PointAction { +// uuid: string; +// name: string; +// type: string; +// material: string; +// delay: number | string; +// spawnInterval: string | number; +// isUsed: boolean; +// } + +// export interface PathPoint { +// uuid: string; +// position: [number, number, number]; +// actions: PointAction[]; +// connections: { +// targets: Array<{ pathUUID: string }>; +// }; +// } + +// export interface SimulationPath { +// modeluuid: string; +// points: PathPoint[]; +// pathPosition: [number, number, number]; +// speed?: number; +// } + +// export interface Process { +// id: string; +// paths: SimulationPath[]; +// animationPath: THREE.Vector3[]; +// pointActions: PointAction[][]; +// speed: number; +// } + +// interface ProcessCreatorProps { +// onProcessesCreated: (processes: Process[]) => void; +// } + +// // Convert event schemas to SimulationPath +// function convertToSimulationPath( +// path: ConveyorEventsSchema | VehicleEventsSchema +// ): SimulationPath { +// const { modeluuid } = path; + +// // Simplified normalizeAction function that preserves exact original properties +// const normalizeAction = (action: any): PointAction => { +// return { ...action }; // Return exact copy with no modifications +// }; + +// if (path.type === "Conveyor") { +// return { +// modeluuid, +// 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, +// })), +// }, +// })), +// pathPosition: path.position, +// speed: +// typeof path.speed === "string" +// ? parseFloat(path.speed) || 1 +// : path.speed || 1, +// }; +// } else { +// 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, +// }; +// } +// } + +// // Custom shallow comparison for arrays +// const areArraysEqual = (a: any[], b: any[]) => { +// if (a.length !== b.length) return false; +// for (let i = 0; i < a.length; i++) { +// if (a[i] !== b[i]) return false; +// } +// return true; +// }; + +// // Helper function to create an empty process +// const createEmptyProcess = (): Process => ({ +// id: `process-${Math.random().toString(36).substring(2, 11)}`, +// paths: [], +// animationPath: [], +// pointActions: [], +// speed: 1, +// }); + +// // Enhanced connection checking function +// function shouldReverseNextPath( +// currentPath: SimulationPath, +// 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]; + +// // Check if current last connects to next last (requires reversal) +// const connectsToLast = currentLastPoint.connections.targets.some( +// (target) => +// target.pathUUID === nextPath.modeluuid && +// nextLastPoint.connections.targets.some( +// (t) => t.pathUUID === currentPath.modeluuid +// ) +// ); + +// // Check if current last connects to next first (no reversal needed) +// const connectsToFirst = currentLastPoint.connections.targets.some( +// (target) => +// target.pathUUID === nextPath.modeluuid && +// nextFirstPoint.connections.targets.some( +// (t) => t.pathUUID === currentPath.modeluuid +// ) +// ); + +// // Only reverse if connected to last point and not to first point +// return connectsToLast && !connectsToFirst; +// } + +// // Updated path adjustment function +// function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] { +// if (paths.length < 2) return paths; + +// const adjustedPaths = [...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; +// } + +// // Main hook for process creation +// export function useProcessCreation() { +// const { scene } = useThree(); +// const [processes, setProcesses] = useState([]); + +// const hasSpawnAction = useCallback((path: SimulationPath): boolean => { +// return path.points.some((point) => +// point.actions.some((action) => action.type.toLowerCase() === "spawn") +// ); +// }, []); + +// const createProcess = useCallback( +// (paths: SimulationPath[]): Process => { +// 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); +// if (!obj) { +// 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, +// animationPath, +// pointActions, +// speed: processSpeed, +// }; +// }, +// [scene] +// ); + +// const getAllConnectedPaths = useCallback( +// ( +// initialPath: SimulationPath, +// allPaths: SimulationPath[], +// visited: Set = new Set() +// ): SimulationPath[] => { +// const connectedPaths: SimulationPath[] = []; +// const queue: SimulationPath[] = [initialPath]; +// visited.add(initialPath.modeluuid); + +// const pathMap = new Map(); +// allPaths.forEach((path) => pathMap.set(path.modeluuid, path)); + +// while (queue.length > 0) { +// const currentPath = queue.shift()!; +// connectedPaths.push(currentPath); + +// // Process outgoing connections +// for (const point of currentPath.points) { +// for (const target of point.connections.targets) { +// if (!visited.has(target.pathUUID)) { +// const targetPath = pathMap.get(target.pathUUID); +// if (targetPath) { +// visited.add(target.pathUUID); +// queue.push(targetPath); +// } +// } +// } +// } + +// // Process incoming connections +// for (const [uuid, path] of pathMap) { +// if (!visited.has(uuid)) { +// const hasConnectionToCurrent = path.points.some((point) => +// point.connections.targets.some( +// (t) => t.pathUUID === currentPath.modeluuid +// ) +// ); +// if (hasConnectionToCurrent) { +// visited.add(uuid); +// queue.push(path); +// } +// } +// } +// } + +// return connectedPaths; +// }, +// [] +// ); + +// const createProcessesFromPaths = useCallback( +// (paths: SimulationPath[]): Process[] => { +// if (!paths || paths.length === 0) return []; + +// const visited = new Set(); +// const processes: Process[] = []; +// const pathMap = new Map(); +// 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); +// const adjustedPaths = adjustPathPointsOrder(connectedPaths); +// const process = createProcess(adjustedPaths); +// processes.push(process); +// } +// } + +// return processes; +// }, +// [createProcess, getAllConnectedPaths, hasSpawnAction] +// ); + +// return { +// processes, +// createProcessesFromPaths, +// setProcesses, +// }; +// } + +// const ProcessCreator: React.FC = React.memo( +// ({ onProcessesCreated }) => { +// const { simulationPaths } = useSimulationPaths(); +// const { createProcessesFromPaths } = useProcessCreation(); +// const prevPathsRef = useRef([]); +// const prevProcessesRef = useRef([]); + +// const convertedPaths = useMemo((): SimulationPath[] => { +// if (!simulationPaths) return []; +// return simulationPaths.map((path) => +// convertToSimulationPath( +// path as ConveyorEventsSchema | VehicleEventsSchema +// ) +// ); +// }, [simulationPaths]); + +// 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") +// ), +// connections: path.points +// .flatMap((p: PathPoint) => +// p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID) +// ) +// .join(","), +// })); +// }, [convertedPaths]); + +// useEffect(() => { +// if (!convertedPaths || convertedPaths.length === 0) { +// if (prevProcessesRef.current.length > 0) { +// onProcessesCreated([]); +// prevProcessesRef.current = []; +// } +// return; +// } + +// if (areArraysEqual(prevPathsRef.current, convertedPaths)) { +// return; +// } + +// prevPathsRef.current = convertedPaths; +// const newProcesses = createProcessesFromPaths(convertedPaths); + +// // console.log("--- Action Types in Paths ---"); +// // convertedPaths.forEach((path) => { +// // path.points.forEach((point) => { +// // point.actions.forEach((action) => { +// // console.log( +// // `Path ${path.modeluuid}, Point ${point.uuid}: ${action.type}` +// // ); +// // }); +// // }); +// // }); +// // console.log("New processes:", newProcesses); + +// 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; +// } +// }, [ +// pathsDependency, +// onProcessesCreated, +// convertedPaths, +// createProcessesFromPaths, +// ]); + +// return null; +// } +// ); + +// export default ProcessCreator; + import React, { useEffect, useMemo, @@ -23,7 +422,6 @@ export interface PointAction { spawnInterval: string | number; isUsed: boolean; } - export interface PathPoint { uuid: string; position: [number, number, number]; @@ -32,14 +430,12 @@ 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[]; @@ -47,7 +443,6 @@ export interface Process { pointActions: PointAction[][]; speed: number; } - interface ProcessCreatorProps { onProcessesCreated: (processes: Process[]) => void; } @@ -58,51 +453,83 @@ function convertToSimulationPath( ): SimulationPath { const { modeluuid } = path; - // Simplified normalizeAction function that preserves exact original properties + // Helper function to normalize actions 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") { - return { - modeluuid, - points: path.points.map((point) => ({ - uuid: point.uuid, - position: point.position, - actions: point.actions.map(normalizeAction), // Preserve exact actions + 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)], connections: { - targets: point.connections.targets.map((target) => ({ + targets: path.point.connections.targets.map((target) => ({ pathUUID: target.pathUUID, })), }, - })), - pathPosition: path.position, - speed: - typeof path.speed === "string" - ? parseFloat(path.speed) || 1 - : path.speed || 1, - }; - } else { - 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, - }; + }, + ]; } + + // 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; + } else { + // For VehicleEventsSchema, use a default speed of 1 + speed = 1; + } + + return { + modeluuid, + points, + pathPosition: path.position, + speed, + }; } // Custom shallow comparison for arrays @@ -129,7 +556,6 @@ 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]; @@ -159,27 +585,22 @@ function shouldReverseNextPath( // Updated path adjustment function function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] { if (paths.length < 2) return paths; - const adjustedPaths = [...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; } @@ -187,7 +608,6 @@ function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] { export function useProcessCreation() { const { scene } = useThree(); const [processes, setProcesses] = useState([]); - const hasSpawnAction = useCallback((path: SimulationPath): boolean => { return path.points.some((point) => point.actions.some((action) => action.type.toLowerCase() === "spawn") @@ -199,11 +619,9 @@ 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); @@ -211,13 +629,11 @@ 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, @@ -238,10 +654,8 @@ export function useProcessCreation() { const connectedPaths: SimulationPath[] = []; const queue: SimulationPath[] = [initialPath]; visited.add(initialPath.modeluuid); - const pathMap = new Map(); allPaths.forEach((path) => pathMap.set(path.modeluuid, path)); - while (queue.length > 0) { const currentPath = queue.shift()!; connectedPaths.push(currentPath); @@ -274,7 +688,6 @@ export function useProcessCreation() { } } } - return connectedPaths; }, [] @@ -283,12 +696,10 @@ export function useProcessCreation() { const createProcessesFromPaths = useCallback( (paths: SimulationPath[]): Process[] => { if (!paths || paths.length === 0) return []; - const visited = new Set(); const processes: Process[] = []; const pathMap = new Map(); 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); @@ -297,7 +708,6 @@ export function useProcessCreation() { processes.push(process); } } - return processes; }, [createProcess, getAllConnectedPaths, hasSpawnAction] @@ -338,6 +748,9 @@ const ProcessCreator: React.FC = React.memo( p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID) ) .join(","), + actions: JSON.stringify( + path.points.flatMap((p: PathPoint) => p.actions) + ), // Serialize all actions })); }, [convertedPaths]); @@ -349,26 +762,11 @@ const ProcessCreator: React.FC = React.memo( } return; } - if (areArraysEqual(prevPathsRef.current, convertedPaths)) { return; } - prevPathsRef.current = convertedPaths; const newProcesses = createProcessesFromPaths(convertedPaths); - - // console.log("--- Action Types in Paths ---"); - // convertedPaths.forEach((path) => { - // path.points.forEach((point) => { - // point.actions.forEach((action) => { - // console.log( - // `Path ${path.modeluuid}, Point ${point.uuid}: ${action.type}` - // ); - // }); - // }); - // }); - // console.log("New processes:", newProcesses); - if ( newProcesses.length !== prevProcessesRef.current.length || !newProcesses.every( @@ -382,7 +780,7 @@ const ProcessCreator: React.FC = React.memo( ) ) { onProcessesCreated(newProcesses); - // prevProcessesRef.current = newProcesses; + prevProcessesRef.current = newProcesses; } }, [ pathsDependency, From 7b9695f006ecf20a5d2d690b297198d66529cf82 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 3 Apr 2025 09:56:20 +0530 Subject: [PATCH 2/9] update socket connection URL to include '/Builder' path --- app/src/store/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 87f937c..bd903f2 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -12,7 +12,7 @@ export const useSocketStore = create((set: any, get: any) => ({ } const socket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}`, + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { reconnection: false, auth: { email, organization }, From 6d639edc43483f2f22f3ad4d23a1291754b065b2 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Thu, 3 Apr 2025 09:58:55 +0530 Subject: [PATCH 3/9] update socket connection URL to include '/Builder' path --- app/src/store/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 87f937c..bd903f2 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -12,7 +12,7 @@ export const useSocketStore = create((set: any, get: any) => ({ } const socket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}`, + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { reconnection: false, auth: { email, organization }, From cf364d707ce328ef76225e72e4404d438f2a84a1 Mon Sep 17 00:00:00 2001 From: SreeNath14 <153710861+SreeNath14@users.noreply.github.com> Date: Thu, 3 Apr 2025 10:28:13 +0530 Subject: [PATCH 4/9] "updated animation" --- .../simulation/process/animation.Worker.js | 916 ++++++++++++++++++ .../simulation/process/animationWorker.js | 4 - .../simulation/process/processAnimator.tsx | 631 +++++++----- .../simulation/process/processContainer.tsx | 1 + .../simulation/process/processCreator.tsx | 192 ++-- app/src/modules/simulation/simulation.tsx | 77 +- 6 files changed, 1460 insertions(+), 361 deletions(-) create mode 100644 app/src/modules/simulation/process/animation.Worker.js delete mode 100644 app/src/modules/simulation/process/animationWorker.js diff --git a/app/src/modules/simulation/process/animation.Worker.js b/app/src/modules/simulation/process/animation.Worker.js new file mode 100644 index 0000000..faa6587 --- /dev/null +++ b/app/src/modules/simulation/process/animation.Worker.js @@ -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, + }, + }); + } + } + }); +} diff --git a/app/src/modules/simulation/process/animationWorker.js b/app/src/modules/simulation/process/animationWorker.js deleted file mode 100644 index e9dddd1..0000000 --- a/app/src/modules/simulation/process/animationWorker.js +++ /dev/null @@ -1,4 +0,0 @@ - -const animationWorker = () => { - return; -}; diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx index d8e35e6..45db8be 100644 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -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; + renderAs?: "box" | "custom"; } interface AnimationState { @@ -58,19 +59,35 @@ interface AnimationState { currentPathIndex: number; } +interface SpawnedObject { + ref: React.RefObject; + 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(null); - const meshRef = useRef(null); - const [visible, setVisible] = useState(false); - const [currentPathIndex, setCurrentPathIndex] = useState(0); - const materials = useMemo( + const [animationStates, setAnimationStates] = useState< + Record + >({}); + + // 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( - 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 = {}; + 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({ - 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 ? ( - - - - ) : 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 ? ( - - - - ) : null; + return renderAs === "box" ? ( + } + material={obj.material} + > + + + ) : ( + gltf?.scene && ( + } + > + + + ) + ); + }) + )} + + ); }; export default ProcessAnimator; diff --git a/app/src/modules/simulation/process/processContainer.tsx b/app/src/modules/simulation/process/processContainer.tsx index 967376c..bf8fa48 100644 --- a/app/src/modules/simulation/process/processContainer.tsx +++ b/app/src/modules/simulation/process/processContainer.tsx @@ -10,6 +10,7 @@ const ProcessContainer: React.FC = () => { <> {processes.length > 0 && } + ); }; diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx index e70408b..91bc4c1 100644 --- a/app/src/modules/simulation/process/processCreator.tsx +++ b/app/src/modules/simulation/process/processCreator.tsx @@ -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([]); + 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(); 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(); const processes: Process[] = []; const pathMap = new Map(); 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 = 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 = 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, diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 0212d50..59074a7 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -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; - const { simulationPaths, setSimulationPaths } = useSimulationPaths(); - const [processes, setProcesses] = useState([]); + const { activeModule } = useModuleStore(); + const pathsGroupRef = useRef() as React.MutableRefObject; + 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 ( + <> + + {activeModule === "simulation" && ( <> - - {activeModule === 'simulation' && ( - <> - - - - - )} + + + - ); + )} + + ); } -export default Simulation; \ No newline at end of file +export default Simulation; From e848875fa0336b328eb7759f7bf2bfe17d5a8e56 Mon Sep 17 00:00:00 2001 From: gabriel Date: Thu, 3 Apr 2025 18:15:26 +0530 Subject: [PATCH 5/9] changed api for get iot inputs --- .../visualization/IotInputCards/BarChartInput.tsx | 2 +- .../IotInputCards/FleetEfficiencyInputComponent.tsx | 2 +- .../IotInputCards/FlotingWidgetInput.tsx | 2 +- .../visualization/IotInputCards/LineGrapInput.tsx | 2 +- .../visualization/IotInputCards/PieChartInput.tsx | 2 +- .../visualization/IotInputCards/Progress1Input.tsx | 2 +- .../visualization/IotInputCards/Progress2Input.tsx | 2 +- .../WarehouseThroughputInputComponent.tsx | 2 +- .../IotInputCards/Widget2InputCard3D.tsx | 2 +- .../IotInputCards/Widget4InputCard3D.tsx | 2 +- app/src/components/ui/componets/DraggableWidget.tsx | 12 ++++++------ 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx index 8fe71a6..e5debde 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx @@ -25,7 +25,7 @@ const BarChartInput = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx index 3a86ea9..cd60d24 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -25,7 +25,7 @@ const FleetEfficiencyInputComponent = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index 0a58634..e389802 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -25,7 +25,7 @@ const FlotingWidgetInput = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index 017dc6d..6ab5ec3 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -144,7 +144,7 @@ const LineGrapInput = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx index e3bed25..56ef990 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx @@ -25,7 +25,7 @@ const PieChartInput = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx index 545df45..cd347b3 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx @@ -25,7 +25,7 @@ const Progress1Input = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx index 3a11a29..ba56e27 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx @@ -25,7 +25,7 @@ const Progress2Input = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx index cd8cb96..c35211e 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -25,7 +25,7 @@ const WarehouseThroughputInputComponent = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx index bad0afc..ce13496 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx @@ -27,7 +27,7 @@ const Widget2InputCard3D = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx index 0be5489..43af417 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx @@ -27,7 +27,7 @@ const Widget4InputCard3D = (props: Props) => { useEffect(() => { const fetchZoneData = async () => { try { - const response = await axios.get(`http://${iotApiUrl}/getinput`); + const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index e70fe2e..41727db 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -17,6 +17,7 @@ import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneD import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi"; import { useClickOutside } from "./functions/handleWidgetsOuterClick"; import { useSocketStore } from "../../../store/store"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; type Side = "top" | "bottom" | "left" | "right"; @@ -243,6 +244,7 @@ export const DraggableWidget = ({ // useClickOutside(chartWidget, () => { // setSelectedChartId(null); // }); + const { isPlaying } = usePlayButtonStore(); console.log('isPanelHidden: ', isPanelHidden); return ( @@ -250,9 +252,8 @@ export const DraggableWidget = ({
From 5fb911f1a1c1ab969e94edff383c30a384cb1c61 Mon Sep 17 00:00:00 2001 From: SreeNath14 <153710861+SreeNath14@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:24:06 +0530 Subject: [PATCH 6/9] "updated smooth animation" --- .../modules/simulation/path/pathCreation.tsx | 6 +- app/src/modules/simulation/process/mesh.tsx | 7 + .../simulation/process/processAnimator.tsx | 852 +++++++++++++++++- app/src/modules/simulation/simulation.tsx | 2 +- 4 files changed, 829 insertions(+), 38 deletions(-) create mode 100644 app/src/modules/simulation/process/mesh.tsx diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 7af4afd..dd531a9 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -14,12 +14,14 @@ import { } from "../../../store/store"; import { useFrame, useThree } from "@react-three/fiber"; import { useSubModuleStore } from "../../../store/useModuleStore"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { + const { isPlaying } = usePlayButtonStore(); const { renderDistance } = useRenderDistance(); const { setSubModule } = useSubModuleStore(); const { setSelectedActionSphere, selectedActionSphere } = @@ -66,7 +68,7 @@ function PathCreation({ const distance = new THREE.Vector3( ...group.position.toArray() ).distanceTo(camera.position); - group.visible = distance <= renderDistance; + group.visible = ((distance <= renderDistance) && !isPlaying); } }); }); @@ -193,7 +195,7 @@ function PathCreation({ }; return ( - + {simulationPaths.map((path) => { if (path.type === "Conveyor") { const points = path.points.map( diff --git a/app/src/modules/simulation/process/mesh.tsx b/app/src/modules/simulation/process/mesh.tsx new file mode 100644 index 0000000..f6b94bb --- /dev/null +++ b/app/src/modules/simulation/process/mesh.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const Mesh: React.FC = () => { + return ; +}; + +export default Mesh; diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx index 45db8be..512b165 100644 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -1,3 +1,582 @@ +// import React, { useRef, useState, useEffect, useMemo } from "react"; +// import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +// import { GLTFLoader } from "three-stdlib"; +// 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"; + +// interface PointAction { +// uuid: string; +// name: string; +// type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap"; +// objectType: string; +// material: string; +// delay: string | number; +// spawnInterval: string | number; +// isUsed: boolean; +// } + +// interface ProcessPoint { +// uuid: string; +// position: number[]; +// rotation: number[]; +// actions: PointAction[]; +// connections: { +// source: { pathUUID: string; pointUUID: string }; +// targets: { pathUUID: string; pointUUID: string }[]; +// }; +// } + +// interface ProcessPath { +// modeluuid: string; +// modelName: string; +// points: ProcessPoint[]; +// pathPosition: number[]; +// pathRotation: number[]; +// speed: number; +// } + +// interface ProcessData { +// id: string; +// paths: ProcessPath[]; +// animationPath: { x: number; y: number; z: number }[]; +// pointActions: PointAction[][]; +// speed: number; +// customMaterials?: Record; +// renderAs?: "box" | "custom"; +// } + +// interface AnimationState { +// currentIndex: number; +// progress: number; +// isAnimating: boolean; +// speed: number; +// isDelaying: boolean; +// delayStartTime: number; +// currentDelayDuration: number; +// delayComplete: boolean; +// currentPathIndex: number; +// } + +// interface SpawnedObject { +// ref: React.RefObject; +// state: AnimationState; +// visible: boolean; +// material: THREE.Material; +// spawnTime: number; +// currentMaterialType: string; +// position: THREE.Vector3; // The position of the object +// } + +// interface ProcessAnimationState { +// spawnedObjects: { [objectId: string]: SpawnedObject }; +// nextSpawnTime: number; +// objectIdCounter: number; +// } + +// const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ +// processes, +// }) => { +// +// const gltf = useLoader(GLTFLoader, boxGltb) as GLTF; +// const { isPlaying } = usePlayButtonStore(); +// const groupRef = useRef(null); + +// const [animationStates, setAnimationStates] = useState< +// Record +// >({}); + +// // Base materials +// const baseMaterials = useMemo( +// () => ({ +// Wood: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), +// Box: new THREE.MeshStandardMaterial({ +// color: 0xcccccc, +// metalness: 0.8, +// roughness: 0.2, +// }), +// Crate: new THREE.MeshStandardMaterial({ +// color: 0x00aaff, +// metalness: 0.1, +// roughness: 0.5, +// }), +// Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }), +// }), +// [] +// ); + +// // Initialize animation states when processes or play state changes +// useEffect(() => { +// if (!isPlaying) { +// setAnimationStates({}); +// return; +// } + +// const newStates: Record = {}; +// 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; +// }; + +// // Find the corresponding animation path point for a spawn point +// const findAnimationPathPoint = (process: ProcessData, spawnPoint: ProcessPoint): THREE.Vector3 => { +// // If we have an animation path, use the first point +// if (process.animationPath && process.animationPath.length > 0) { +// // Find the index of this point in the path +// let pointIndex = 0; + +// // Try to find the corresponding point in the animation path +// for (const path of process.paths || []) { +// for (let i = 0; i < (path.points?.length || 0); i++) { +// const point = path.points?.[i]; +// if (point && point.uuid === spawnPoint.uuid) { +// // Found the matching point +// if (process.animationPath[pointIndex]) { +// const p = process.animationPath[pointIndex]; +// return new THREE.Vector3(p.x, p.y, p.z); +// } +// } +// pointIndex++; +// } +// } + +// // Fallback to the spawn point's position +// return new THREE.Vector3( +// spawnPoint.position[0], +// spawnPoint.position[1], +// spawnPoint.position[2] +// ); +// } + +// // If no animation path, use the spawn point's position +// return new THREE.Vector3( +// spawnPoint.position[0], +// spawnPoint.position[1], +// spawnPoint.position[2] +// ); +// }; + +// // Create a new spawned object +// const createSpawnedObject = ( +// process: ProcessData, +// currentTime: number, +// materialType: string, +// spawnPoint: ProcessPoint +// ): SpawnedObject => { +// const processMaterials = { +// ...baseMaterials, +// ...(process.customMaterials || {}), +// }; + +// // Get the position where we should spawn +// const spawnPosition = findAnimationPathPoint(process, spawnPoint); + +// return { +// 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, +// position: spawnPosition, // Store the position directly +// }; +// }; + +// // 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 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; +// 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; +// }; + +// // Spawn objects for all processes +// useFrame((state) => { +// 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 spawnPoint = findSpawnPoint(process); +// if (!spawnPoint || !spawnPoint.actions) return; + +// const spawnAction = spawnPoint.actions.find( +// (a) => a.isUsed && a.type === "Spawn" +// ); +// if (!spawnAction) return; + +// const spawnInterval = +// typeof spawnAction.spawnInterval === "number" +// ? spawnAction.spawnInterval +// : parseFloat(spawnAction.spawnInterval as string) || 0; + +// if (currentTime >= processState.nextSpawnTime) { +// const objectId = `obj-${process.id}-${processState.objectIdCounter}`; + +// // Create the new object with the spawn point +// const newObject = createSpawnedObject( +// process, +// currentTime, +// spawnAction.material || "Default", +// spawnPoint +// ); + +// newStates[process.id] = { +// ...processState, +// spawnedObjects: { +// ...processState.spawnedObjects, +// [objectId]: newObject, +// }, +// 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; + +// // Set the position when the reference is first available +// if (obj.position && obj.state.currentIndex === 0 && obj.state.progress === 0) { +// currentRef.position.copy(obj.position); +// } + +// 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; +// } + +// 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); +// const renderAs = process?.renderAs || "custom"; + +// return renderAs === "box" ? ( +// } +// material={obj.material} +// position={obj.position} // Set position directly in the JSX +// > +// +// +// ) : ( +// gltf?.scene && ( +// } +// position={obj.position} // Set position directly in the JSX +// > +// +// +// ) +// ); +// }) +// )} +// +// ); +// }; + +// export default ProcessAnimator; + import React, { useRef, useState, useEffect, useMemo } from "react"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { GLTFLoader } from "three-stdlib"; @@ -5,6 +584,7 @@ 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; @@ -66,18 +646,23 @@ interface SpawnedObject { material: THREE.Material; spawnTime: number; currentMaterialType: string; + position: THREE.Vector3; // The position of the object } interface ProcessAnimationState { spawnedObjects: { [objectId: string]: SpawnedObject }; nextSpawnTime: number; objectIdCounter: number; + // New fields for process-wide delay + isProcessDelaying: boolean; + processDelayStartTime: number; + processDelayDuration: number; } const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ processes, }) => { - console.log("processes: ", processes); + const gltf = useLoader(GLTFLoader, boxGltb) as GLTF; const { isPlaying } = usePlayButtonStore(); const groupRef = useRef(null); @@ -90,10 +675,8 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const baseMaterials = useMemo( () => ({ Wood: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), - Box: new THREE.MeshStandardMaterial({ + Box: new THREE.MeshPhongMaterial({ color: 0xcccccc, - metalness: 0.8, - roughness: 0.2, }), Crate: new THREE.MeshStandardMaterial({ color: 0x00aaff, @@ -118,6 +701,10 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ spawnedObjects: {}, nextSpawnTime: 0, objectIdCounter: 0, + // Initialize process-wide delay state + isProcessDelaying: false, + processDelayStartTime: 0, + processDelayDuration: 0, }; }); setAnimationStates(newStates); @@ -138,17 +725,62 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ return null; }; + // Find the corresponding animation path point for a spawn point + const findAnimationPathPoint = ( + process: ProcessData, + spawnPoint: ProcessPoint + ): THREE.Vector3 => { + // If we have an animation path, use the first point + if (process.animationPath && process.animationPath.length > 0) { + // Find the index of this point in the path + let pointIndex = 0; + + // Try to find the corresponding point in the animation path + for (const path of process.paths || []) { + for (let i = 0; i < (path.points?.length || 0); i++) { + const point = path.points?.[i]; + if (point && point.uuid === spawnPoint.uuid) { + // Found the matching point + if (process.animationPath[pointIndex]) { + const p = process.animationPath[pointIndex]; + return new THREE.Vector3(p.x, p.y, p.z); + } + } + pointIndex++; + } + } + + // Fallback to the spawn point's position + return new THREE.Vector3( + spawnPoint.position[0], + spawnPoint.position[1], + spawnPoint.position[2] + ); + } + + // If no animation path, use the spawn point's position + return new THREE.Vector3( + spawnPoint.position[0], + spawnPoint.position[1], + spawnPoint.position[2] + ); + }; + // Create a new spawned object const createSpawnedObject = ( process: ProcessData, currentTime: number, - materialType: string + materialType: string, + spawnPoint: ProcessPoint ): SpawnedObject => { const processMaterials = { ...baseMaterials, ...(process.customMaterials || {}), }; + // Get the position where we should spawn + const spawnPosition = findAnimationPathPoint(process, spawnPoint); + return { ref: React.createRef(), state: { @@ -168,6 +800,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ baseMaterials.Default, currentMaterialType: materialType, spawnTime: currentTime, + position: spawnPosition, // Store the position directly }; }; @@ -224,11 +857,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ case "Delay": setAnimationStates((prev) => { const processState = prev[processId]; - if ( - !processState || - !processState.spawnedObjects[objectId] || - processState.spawnedObjects[objectId].state.isDelaying - ) { + if (!processState || processState.isProcessDelaying) { return prev; } @@ -242,12 +871,18 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ ...prev, [processId]: { ...processState, + // Set process-wide delay instead of object-specific delay + isProcessDelaying: true, + processDelayStartTime: currentTime, + processDelayDuration: delayDuration, + // Update the specific object's state as well spawnedObjects: { ...processState.spawnedObjects, [objectId]: { ...processState.spawnedObjects[objectId], state: { ...processState.spawnedObjects[objectId].state, + isAnimating: false, // Explicitly pause animation during delay isDelaying: true, delayStartTime: currentTime, currentDelayDuration: delayDuration, @@ -335,6 +970,43 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const processState = newStates[process.id]; if (!processState) return; + // Skip spawning if the process is currently in a delay + if (processState.isProcessDelaying) { + // Check if delay is over + if ( + currentTime - processState.processDelayStartTime >= + processState.processDelayDuration + ) { + // Reset process delay state + newStates[process.id] = { + ...processState, + isProcessDelaying: false, + // Reset delay state on all objects in this process + spawnedObjects: Object.entries( + processState.spawnedObjects + ).reduce( + (acc, [id, obj]) => ({ + ...acc, + [id]: { + ...obj, + state: { + ...obj.state, + isDelaying: false, + delayComplete: true, + isAnimating: true, // Ensure animation resumes + // Force a small progress to ensure movement starts + progress: + obj.state.progress === 0 ? 0.001 : obj.state.progress, + }, + }, + }), + {} + ), + }; + } + return; // Skip spawning while delaying + } + const spawnPoint = findSpawnPoint(process); if (!spawnPoint || !spawnPoint.actions) return; @@ -346,20 +1018,24 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const spawnInterval = typeof spawnAction.spawnInterval === "number" ? spawnAction.spawnInterval - : 0; + : parseFloat(spawnAction.spawnInterval as string) || 0; if (currentTime >= processState.nextSpawnTime) { const objectId = `obj-${process.id}-${processState.objectIdCounter}`; + // Create the new object with the spawn point + const newObject = createSpawnedObject( + process, + currentTime, + spawnAction.material || "Default", + spawnPoint + ); + newStates[process.id] = { ...processState, spawnedObjects: { ...processState.spawnedObjects, - [objectId]: createSpawnedObject( - process, - currentTime, - spawnAction.material || "Default" - ), + [objectId]: newObject, }, objectIdCounter: processState.objectIdCounter + 1, nextSpawnTime: currentTime + spawnInterval, @@ -383,6 +1059,47 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const processState = newStates[process.id]; if (!processState) return; + // Check if the process-wide delay is active + if (processState.isProcessDelaying) { + // Check if the delay has completed + if ( + currentTime - processState.processDelayStartTime >= + processState.processDelayDuration + ) { + // Reset process delay state AND resume animation + newStates[process.id] = { + ...processState, + isProcessDelaying: false, + // Reset delay state on all objects in this process AND ensure isAnimating is true + spawnedObjects: Object.entries( + processState.spawnedObjects + ).reduce( + (acc, [id, obj]) => ({ + ...acc, + [id]: { + ...obj, + state: { + ...obj.state, + isDelaying: false, + delayComplete: true, + isAnimating: true, // Ensure animation resumes + // Important: Force progress to a small positive value to ensure movement + progress: + obj.state.progress === 0 ? 0.005 : obj.state.progress, + }, + }, + }), + {} + ), + }; + // Skip the rest of the processing for this frame to allow the state update to take effect + return newStates; + } else { + // If we're still in a process-wide delay, don't animate anything + return newStates; + } + } + const path = process.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) || []; @@ -392,13 +1109,63 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ Object.entries(processState.spawnedObjects).forEach( ([objectId, obj]) => { - if (!obj.visible || !obj.state.isAnimating) return; + // Skip objects that are explicitly not visible + if (!obj.visible) return; const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current; if (!currentRef) return; + // Set the position when the reference is first available + if ( + obj.position && + obj.state.currentIndex === 0 && + obj.state.progress === 0 + ) { + currentRef.position.copy(obj.position); + } + const stateRef = obj.state; + // Check if we're delaying at the object level and update accordingly + if (stateRef.isDelaying) { + if ( + currentTime - stateRef.delayStartTime >= + stateRef.currentDelayDuration + ) { + // Delay is complete, resume animation + stateRef.isDelaying = false; + stateRef.delayComplete = true; + stateRef.isAnimating = true; // Explicitly resume animation + + // Force movement from the current point by setting progress to a small value + // if we're at the start of a segment + if (stateRef.progress === 0) { + stateRef.progress = 0.005; + } + + // Force an immediate position update to ensure visually accurate position + const nextPointIdx = stateRef.currentIndex + 1; + if (nextPointIdx < path.length) { + // Calculate the position slightly ahead of the current point + const slightProgress = Math.max(stateRef.progress, 0.005); + currentRef.position.lerpVectors( + path[stateRef.currentIndex], + nextPointIdx < path.length + ? path[nextPointIdx] + : path[stateRef.currentIndex], + slightProgress + ); + } + } else { + // Still delaying, don't animate this object + updatedObjects[objectId] = { ...obj, state: { ...stateRef } }; + return; + } + } + + // Skip animation if the object shouldn't be animating + if (!stateRef.isAnimating) return; + // Get current point data const currentPointData = getPointDataForAnimationIndex( process, @@ -413,19 +1180,9 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ 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 + if (shouldStop) { + updatedObjects[objectId] = { ...obj, state: { ...stateRef } }; + return; } } @@ -438,8 +1195,10 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ currentPointData.actions ); if (shouldStop) { - currentRef.position.copy(path[stateRef.currentIndex]); - delete updatedObjects[objectId]; + // uncomment this or write own logic to handle the object when reaching the last point of the process + + // currentRef.position.copy(path[stateRef.currentIndex]); + // delete updatedObjects[objectId]; return; } } @@ -450,14 +1209,36 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ const distance = path[stateRef.currentIndex].distanceTo(nextPoint); const movement = stateRef.speed * delta; - stateRef.progress += movement / distance; + + // If we just resumed from a delay, ensure we make actual progress + if (stateRef.delayComplete && stateRef.progress < 0.01) { + // Boost initial movement after delay to ensure visible progress + stateRef.progress = 0.05; // Small but visible initial progress + stateRef.delayComplete = false; // Reset flag so we don't do this again + } else { + // Normal progress calculation + stateRef.progress += movement / distance; + } if (stateRef.progress >= 1) { + // We've reached the next point stateRef.currentIndex = nextPointIdx; stateRef.progress = 0; - stateRef.delayComplete = false; currentRef.position.copy(nextPoint); + + // Check if we need to execute actions at this new point + const newPointData = getPointDataForAnimationIndex( + process, + stateRef.currentIndex + ); + + if (newPointData?.actions) { + // We've arrived at a new point with actions, handle them in the next frame + // We don't call handlePointActions directly here to avoid state update issues + // The actions will be handled in the next frame when progress is 0 + } } else { + // Normal path interpolation currentRef.position.lerpVectors( path[stateRef.currentIndex], nextPoint, @@ -491,7 +1272,6 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ .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 renderAs === "box" ? ( @@ -499,6 +1279,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ key={objectId} ref={obj.ref as React.RefObject} material={obj.material} + position={obj.position} // Set position directly in the JSX > @@ -507,6 +1288,7 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ } + position={obj.position} // Set position directly in the JSX > { - // console.log('simulationPaths: ', simulationPaths); + console.log('simulationPaths: ', simulationPaths); }, [simulationPaths]); // useEffect(() => { From 22bb0332accbc3abfb1cb69a7ba72814e39c5642 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Thu, 3 Apr 2025 19:31:25 +0530 Subject: [PATCH 7/9] 3d widget editoption function added. --- .../components/layout/3D-cards/CardsScene.tsx | 6 +- .../3D-cards/cards/ProductionCapacity.tsx | 34 ++-- .../3D-cards/cards/ReturnOfInvestment.tsx | 28 ++- .../layout/3D-cards/cards/StateWorking.tsx | 54 +++--- .../layout/3D-cards/cards/Throughput.tsx | 24 ++- .../IotInputCards/InputSelecterComponent.tsx | 2 - .../IotInputCards/Widget2InputCard3D.tsx | 2 - .../IotInputCards/Widget3InputCard3D.tsx | 4 +- .../IotInputCards/Widget4InputCard3D.tsx | 4 +- .../ui/componets/Dropped3dWidget.tsx | 160 ++++++++++++++++-- .../ui/componets/RealTimeVisulization.tsx | 27 ++- .../ui/componets/handleDropTemplate .tsx | 29 ---- .../components/ui/menu/EditWidgetOption.tsx | 17 +- app/src/store/store.ts | 2 +- app/src/store/useZone3DWidgetStore.ts | 90 +++++++--- app/src/styles/pages/realTimeViz.scss | 5 - 16 files changed, 312 insertions(+), 176 deletions(-) delete mode 100644 app/src/components/ui/componets/handleDropTemplate .tsx diff --git a/app/src/components/layout/3D-cards/CardsScene.tsx b/app/src/components/layout/3D-cards/CardsScene.tsx index 9318ce3..a23cdd0 100644 --- a/app/src/components/layout/3D-cards/CardsScene.tsx +++ b/app/src/components/layout/3D-cards/CardsScene.tsx @@ -8,7 +8,7 @@ import StateWorking from "./cards/StateWorking"; const CardsScene = () => { return (
- + {/* */} {/* 3d-cards */} {/* */} @@ -16,8 +16,8 @@ const CardsScene = () => { {/* */} {/* */} - - + {/* */} + {/* */}
); }; diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index 05c2814..4ec9fdd 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -30,11 +30,13 @@ interface ProductionCapacityProps { id: string; type: string; position: [number, number, number]; + onContextMenu?: (event: React.MouseEvent) => void; + // onPointerDown:any } -const ProductionCapacity : React.FC = ({ id, type, position }) => { +const ProductionCapacity: React.FC = ({ id, type, position, onContextMenu }) => { - const { selectedChartId,setSelectedChartId } = useWidgetStore(); + const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); @@ -111,7 +113,7 @@ const ProductionCapacity : React.FC = ({ id, type, posi }; - const startStream = () => { + const startStream = () => { socket.emit("lineInput", inputData); }; @@ -146,8 +148,8 @@ const ProductionCapacity : React.FC = ({ id, type, posi }; }, [measurements, duration, iotApiUrl]); - const fetchSavedInputes = async() => { - + const fetchSavedInputes = async () => { + if (id !== "") { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); @@ -173,20 +175,18 @@ const ProductionCapacity : React.FC = ({ id, type, posi fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration, widgetName]) + , [chartMeasurements, chartDuration, widgetName]) return ( - +
setSelectedChartId({ - id: id, - type: type - }) - }> + onClick={() => setSelectedChartId({ id: id, type: type })} + onContextMenu={onContextMenu} + >
Production Capacity
@@ -207,7 +207,7 @@ const ProductionCapacity : React.FC = ({ id, type, posi
{" "}
{/* Bar Chart */} - 0 ? chartData : defaultChartData } options={chartOptions} /> + 0 ? chartData : defaultChartData} options={chartOptions} />
diff --git a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx index 8c2f3ea..96fc305 100644 --- a/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx +++ b/app/src/components/layout/3D-cards/cards/ReturnOfInvestment.tsx @@ -43,10 +43,11 @@ interface ReturnOfInvestmentProps { id: string; type: string; position: [number, number, number]; + onContextMenu?: (event: React.MouseEvent) => void; } -const ReturnOfInvestment: React.FC = ({ id, type, position }) => { +const ReturnOfInvestment: React.FC = ({ id, type, position, onContextMenu }) => { - const { selectedChartId,setSelectedChartId } = useWidgetStore(); + const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") @@ -138,7 +139,7 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit }; - const startStream = () => { + const startStream = () => { socket.emit("lineInput", inputData); }; @@ -155,8 +156,8 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit return { label: datasetKey, data: responseData[datasetKey]?.values ?? [], - borderColor: index === 0 ? "rgba(75, 192, 192, 1)": "rgba(255, 99, 132, 1)", // Light blue color - backgroundColor: index === 0 ? "rgba(75, 192, 192, 0.2)": "rgba(255, 99, 132, 0.2)", + borderColor: index === 0 ? "rgba(75, 192, 192, 1)" : "rgba(255, 99, 132, 1)", // Light blue color + backgroundColor: index === 0 ? "rgba(75, 192, 192, 0.2)" : "rgba(255, 99, 132, 0.2)", fill: true, tension: 0.4, // Smooth curve effect pointRadius: 0, // Hide dots @@ -174,8 +175,8 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit }; }, [measurements, duration, iotApiUrl]); - const fetchSavedInputes = async() => { - + const fetchSavedInputes = async () => { + if (id !== "") { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); @@ -201,21 +202,18 @@ const ReturnOfInvestment: React.FC = ({ id, type, posit fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration, widgetName]) + , [chartMeasurements, chartDuration, widgetName]) return (
setSelectedChartId({ - id: id, - type: type - }) - }> + onClick={() => setSelectedChartId({ id: id, type: type })} + onContextMenu={onContextMenu} + >
Return of Investment
{/* Smooth curve graph with two datasets */} diff --git a/app/src/components/layout/3D-cards/cards/StateWorking.tsx b/app/src/components/layout/3D-cards/cards/StateWorking.tsx index d0fdb95..efd4e7d 100644 --- a/app/src/components/layout/3D-cards/cards/StateWorking.tsx +++ b/app/src/components/layout/3D-cards/cards/StateWorking.tsx @@ -7,11 +7,12 @@ import io from "socket.io-client"; // import image from "../../../../assets/image/temp/image.png"; interface StateWorkingProps { - id:string; + id: string; type: string; position: [number, number, number]; + onContextMenu?: (event: React.MouseEvent) => void; } -const StateWorking: React.FC = ({ id, type, position }) => { +const StateWorking: React.FC = ({ id, type, position, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); @@ -38,14 +39,14 @@ const StateWorking: React.FC = ({ id, type, position }) => { duration, interval: 1000, }; - const startStream = () => { + const startStream = () => { socket.emit("lastInput", inputData); }; socket.on("connect", startStream); socket.on("lastOutput", (response) => { const responseData = response; - console.log("responceeeeeeeeeee",response); - + console.log("responceeeeeeeeeee", response); + setDatas(responseData); }); @@ -56,8 +57,8 @@ const StateWorking: React.FC = ({ id, type, position }) => { }; }, [measurements, duration, iotApiUrl]); - const fetchSavedInputes = async() => { - + const fetchSavedInputes = async () => { + if (id !== "") { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); @@ -74,8 +75,8 @@ const StateWorking: React.FC = ({ id, type, position }) => { } } - console.log("dataaaaa",datas); - + console.log("dataaaaa", datas); + useEffect(() => { fetchSavedInputes(); @@ -86,27 +87,24 @@ const StateWorking: React.FC = ({ id, type, position }) => { fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration, widgetName]) + , [chartMeasurements, chartDuration, widgetName]) return (
setSelectedChartId({ - id: id, - type: type - }) - }> + onClick={() => setSelectedChartId({ id: id, type: type })} + onContextMenu={onContextMenu} + >
State - {datas?.input1 ? datas.input1 : 'input1'} . + {datas?.input1 ? datas.input1 : 'input1'} .
@@ -126,24 +124,24 @@ const StateWorking: React.FC = ({ id, type, position }) => {
{datas?.input2 ? datas.input2 : 'data'}
-
{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}
-
{datas?.input3 ? datas.input3 : 'data'}
+
{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}
+
{datas?.input3 ? datas.input3 : 'data'}
-
{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}
-
{datas?.input4 ? datas.input4 : 'data'}
+
{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}
+
{datas?.input4 ? datas.input4 : 'data'}
-
{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}
-
{datas?.input5 ? datas.input5 : 'data'}
+
{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}
+
{datas?.input5 ? datas.input5 : 'data'}
-
{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}
-
{datas?.input6 ? datas.input6 : 'data'}
+
{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}
+
{datas?.input6 ? datas.input6 : 'data'}
-
{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}
-
{datas?.input7 ? datas.input7 : 'data'}
+
{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}
+
{datas?.input7 ? datas.input7 : 'data'}
diff --git a/app/src/components/layout/3D-cards/cards/Throughput.tsx b/app/src/components/layout/3D-cards/cards/Throughput.tsx index 32cd350..5d6fab9 100644 --- a/app/src/components/layout/3D-cards/cards/Throughput.tsx +++ b/app/src/components/layout/3D-cards/cards/Throughput.tsx @@ -45,11 +45,12 @@ interface ThroughputProps { id: string; type: string; position: [number, number, number]; + onContextMenu?: (event: React.MouseEvent) => void; } -const Throughput: React.FC = ({ id, type, position }) => { +const Throughput: React.FC = ({ id, type, position, onContextMenu }) => { - const { selectedChartId,setSelectedChartId } = useWidgetStore(); + const { selectedChartId, setSelectedChartId } = useWidgetStore(); const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") @@ -121,7 +122,7 @@ const Throughput: React.FC = ({ id, type, position }) => { }; - const startStream = () => { + const startStream = () => { socket.emit("lineInput", inputData); }; @@ -154,8 +155,8 @@ const Throughput: React.FC = ({ id, type, position }) => { }; }, [measurements, duration, iotApiUrl]); - const fetchSavedInputes = async() => { - + const fetchSavedInputes = async () => { + if (id !== "") { try { const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`); @@ -181,7 +182,7 @@ const Throughput: React.FC = ({ id, type, position }) => { fetchSavedInputes(); } } - ,[chartMeasurements, chartDuration, widgetName]) + , [chartMeasurements, chartDuration, widgetName]) return ( = ({ id, type, position }) => { transform zIndexRange={[1, 0]} sprite> -
setSelectedChartId({ - id: id, - type: type - }) - }> +
setSelectedChartId({ id: id, type: type })} + onContextMenu={onContextMenu} + >
{name}
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx index 3b44fc2..2d82225 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/InputSelecterComponent.tsx @@ -14,8 +14,6 @@ import { useWidgetStore } from '../../../../../store/useWidgetStore' const InputSelecterComponent = () => { const { selectedChartId } = useWidgetStore(); - console.log('selectedChartId:',selectedChartId); - if (selectedChartId && selectedChartId.type && selectedChartId.type === 'bar' ) { return ( diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx index bad0afc..c6d1509 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx @@ -21,8 +21,6 @@ const Widget2InputCard3D = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] - console.log(selectedChartId); - useEffect(() => { const fetchZoneData = async () => { diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx index c720e66..95fdc33 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx @@ -19,9 +19,7 @@ const Widget3InputCard3D = () => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] - console.log(selectedChartId); - - + useEffect(() => { const fetchZoneData = async () => { try { diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx index 0be5489..3244773 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx @@ -21,9 +21,7 @@ const Widget4InputCard3D = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] - console.log(selectedChartId); - - + useEffect(() => { const fetchZoneData = async () => { try { diff --git a/app/src/components/ui/componets/Dropped3dWidget.tsx b/app/src/components/ui/componets/Dropped3dWidget.tsx index f0d5e66..f43cce5 100644 --- a/app/src/components/ui/componets/Dropped3dWidget.tsx +++ b/app/src/components/ui/componets/Dropped3dWidget.tsx @@ -1,5 +1,5 @@ import { useThree } from "@react-three/fiber"; -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { ThreeState } from "../../../types/world/worldTypes"; @@ -13,30 +13,43 @@ import { generateUniqueId } from "../../../functions/generateUniqueId"; import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { use3DWidget } from "../../../store/useDroppedObjectsStore"; -import { useZoneWidgetStore } from "../../../store/useZone3DWidgetStore"; +import { useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../store/useZone3DWidgetStore"; +import { useWidgetStore } from "../../../store/useWidgetStore"; +import EditWidgetOption from "../menu/EditWidgetOption"; export default function Dropped3dWidgets() { const { widgetSelect } = useAsset3dWidget(); const { activeModule } = useModuleStore(); - const { raycaster, gl, scene }: ThreeState = useThree(); + const { raycaster, gl, scene, mouse, camera }: ThreeState = useThree(); const { widgetSubOption } = useWidgetSubOption(); const { selectedZone } = useSelectedZoneStore(); + const { top, setTop } = useTopData() + const { left, setLeft } = useLeftData() + const { rightSelect, setRightSelect } = useRightSelected() // ✅ Use Zustand Store instead of useState - const { zoneWidgetData, setZoneWidgetData, addWidget } = useZoneWidgetStore(); + const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition } = useZoneWidgetStore(); const { setWidgets3D } = use3DWidget(); const { visualizationSocket } = useSocketStore(); + const { rightClickSelected, setRightClickSelected } = useRightClickSelected() + + const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move + const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move + const planeIntersect = useRef(new THREE.Vector3()); + // let [verticalPlane, setFloorPlanesVertical] = useState( + // new THREE.Plane(new THREE.Vector3(0, 1, 0)) + // ); useEffect(() => { if (activeModule !== "visualization") return; if (!selectedZone.zoneId) return; - const email = localStorage.getItem("email") || ""; - const organization = email?.split("@")[1]?.split(".")[0]; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); - console.log('result: ', result); + setWidgets3D(result); const formattedWidgets = result.map((widget: any) => ({ @@ -89,13 +102,13 @@ export default function Dropped3dWidgets() { widget: newWidget, zoneId: selectedZone.zoneId } - console.log('add3dWidget: ', add3dWidget); + if (visualizationSocket) { visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget) } // let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget); - // console.log('response: ', response); + // // if (response.message === "Widget created successfully") { addWidget(selectedZone.zoneId, newWidget); @@ -111,23 +124,142 @@ export default function Dropped3dWidgets() { const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; + useEffect(() => { + if (!rightClickSelected) return; + const email = localStorage.getItem("email") || ""; + const organization = email?.split("@")[1]?.split(".")[0]; + if (rightSelect === "Duplicate") { + const widgetToDuplicate = activeZoneWidgets.find(w => w.id === rightClickSelected); + if (!widgetToDuplicate) return; + const newWidget = { + id: generateUniqueId(), + type: widgetToDuplicate.type, + position: [ + widgetToDuplicate.position[0] + 0.5, // Slightly shift position + widgetToDuplicate.position[1], + widgetToDuplicate.position[2] + 0.5, + ] as [number, number, number], + }; + let add3dWidget = { + organization, + widget: newWidget, + zoneId: selectedZone.zoneId + }; + // if (visualizationSocket) { + // visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); + // } + addWidget(selectedZone.zoneId, newWidget); + setRightSelect(null); + setRightClickSelected(null); + } + if (rightSelect === "Delete") { + let deleteWidget = { + organization, + widgetId: rightClickSelected, + zoneId: selectedZone.zoneId + }; + // if (visualizationSocket) { + // visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget); + // } + setZoneWidgetData(selectedZone.zoneId, activeZoneWidgets.filter(w => w.id !== rightClickSelected)); + setRightClickSelected(null); + setRightSelect(null); + } + if (rightSelect === "Horizontal Move") { + + } + if (rightSelect === "Vertical Move") { + + } + + }, [rightSelect, rightClickSelected]); + + useEffect(() => { + const handleMouseMove = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + + + const selectedZone = Object.keys(zoneWidgetData).find(zoneId => + zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected) + ); + if (!selectedZone) return; + + const selectedWidget = zoneWidgetData[selectedZone].find(widget => widget.id === rightClickSelected); + if (!selectedWidget) return; + + const rect = gl.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + + raycaster.setFromCamera(mouse, camera); + + if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { + + updateWidgetPosition(selectedZone, rightClickSelected, [ + planeIntersect.current.x, + selectedWidget.position[1], + planeIntersect.current.z + ]); + } + + if (rightSelect === "Vertical Move") { + if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { + updateWidgetPosition(selectedZone, rightClickSelected, [ + selectedWidget.position[0], + planeIntersect.current.y, // Ensure Y value updates correctly + selectedWidget.position[2] + ]); + } else { + console.log("No Intersection with Vertical Plane"); + } + } + }; + + const handleMouseUp = () => { + + if (rightClickSelected && (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move")) { + + setTimeout(() => { + setRightClickSelected(null); + setRightSelect(null); + }, 50); + } + }; + + // Attach events to window instead of gl.domElement + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + + return () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + }; + }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); + + + + return ( <> {activeZoneWidgets.map(({ id, type, position }) => { - console.log('Typeeeeeeeeeeee',type); + const handleRightClick = (event: React.MouseEvent) => { + event.preventDefault(); + setRightClickSelected(id) + }; switch (type) { case "ui-Widget 1": - return ; + return ; case "ui-Widget 2": - return ; + return ; case "ui-Widget 3": - return ; + return ; case "ui-Widget 4": - return ; + return ; default: return null; } })} + ); } diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 619cbb0..915e3b3 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -23,6 +23,8 @@ import SocketRealTimeViz from "../../../modules/visualization/realTimeVizSocket. import RenderOverlay from "../../templates/Overlay"; import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup"; import DroppedObjects from "./DroppedFloatingWidgets"; +import EditWidgetOption from "../menu/EditWidgetOption"; +import { useRightClickSelected } from "../../../store/useZone3DWidgetStore"; type Side = "top" | "bottom" | "left" | "right"; @@ -55,8 +57,9 @@ const RealTimeVisulization: React.FC = () => { const [droppedObjects, setDroppedObjects] = useState([]); const [zonesData, setZonesData] = useState({}); const { selectedZone, setSelectedZone } = useSelectedZoneStore(); - const { zones } = useZones(); + + const { rightClickSelected, setRightClickSelected } = useRightClickSelected() const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); const [floatingWidgets, setFloatingWidgets] = useState< @@ -93,7 +96,7 @@ const RealTimeVisulization: React.FC = () => { {} ); setZonesData(formattedData); - } catch (error) {} + } catch (error) { } } GetZoneData(); @@ -178,7 +181,6 @@ const RealTimeVisulization: React.FC = () => { .getState() .addObject(selectedZone.zoneName, newObject); // } - // Update floating widgets state setFloatingWidgets((prevWidgets) => ({ ...prevWidgets, @@ -192,9 +194,10 @@ const RealTimeVisulization: React.FC = () => { ], }, })); - } catch (error) {} + } catch (error) { } }; + function handleRightClickSel(){} return (
{ left: isPlaying || activeModule !== "visualization" ? "0%" : "", }} > - {/* - - */} {openConfirmationPopup && ( {
{activeModule === "visualization" && selectedZone.zoneName !== "" && } {activeModule === "visualization" && } - {/* */} - {/* - - */} + + {activeModule === "visualization" && widgetSubOption === "3D" && rightClickSelected && } + {activeModule === "visualization" && ( <> { -// const { getTemplate } = useTemplateStore.getState(); -// const { setSelectedZone } = useSelectedZoneStore.getState(); - -// // Find the template by ID -// const template: Template | undefined = getTemplate(templateId); - -// if (!template) { -// console.error("Template not found!"); -// return; -// } - -// // Update the selected zone with the template data -// setSelectedZone((prev) => ({ -// ...prev, -// panelOrder: template.panelOrder, -// activeSides: Array.from(new Set([...prev.activeSides, ...template.panelOrder])), -// widgets: template.widgets, // Keep widget structure the same -// })); - -// console.log("Dropped template applied:", template); -// }; diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index 9ef67af..ce5261a 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -1,15 +1,26 @@ -import React from "react"; +import React, { useEffect } from "react"; +import { useLeftData, useRightSelected, useTopData } from "../../../store/useZone3DWidgetStore"; interface EditWidgetOptionProps { options: string[]; } const EditWidgetOption: React.FC = ({ options }) => { + const { top, setTop } = useTopData() + const { left, setLeft } = useLeftData() + const { rightSelect, setRightSelect } = useRightSelected() + + useEffect(() => { + + console.log('left: ', left); + console.log('top: ', top); + }, [top, left]) + return ( -
+
{options.map((option, index) => ( -
+
setRightSelect(option)}> {option}
))} diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 87f937c..bd903f2 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -12,7 +12,7 @@ export const useSocketStore = create((set: any, get: any) => ({ } const socket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}`, + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { reconnection: false, auth: { email, organization }, diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index fbb8e74..850623f 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -1,33 +1,77 @@ + import { create } from "zustand"; type WidgetData = { - id: string; - type: string; - position: [number, number, number]; + id: string; + type: string; + position: [number, number, number]; + tempPosition?: [number, number, number]; }; type ZoneWidgetStore = { - zoneWidgetData: Record; - setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; - addWidget: (zoneId: string, widget: WidgetData) => void; + zoneWidgetData: Record; + setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; + addWidget: (zoneId: string, widget: WidgetData) => void; + updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void; }; export const useZoneWidgetStore = create((set) => ({ - zoneWidgetData: {}, - - setZoneWidgetData: (zoneId, widgets) => - set((state) => ({ - zoneWidgetData: { - ...state.zoneWidgetData, - [zoneId]: widgets, - }, - })), - - addWidget: (zoneId, widget) => - set((state) => ({ - zoneWidgetData: { - ...state.zoneWidgetData, - [zoneId]: [...(state.zoneWidgetData[zoneId] || []), widget], - }, - })), + zoneWidgetData: {}, + + setZoneWidgetData: (zoneId, widgets) => + set((state) => ({ + zoneWidgetData: { ...state.zoneWidgetData, [zoneId]: widgets }, + })), + + addWidget: (zoneId, widget) => + set((state) => ({ + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: [...(state.zoneWidgetData[zoneId] || []), widget], + }, + })), + + updateWidgetPosition: (zoneId, widgetId, newPosition) => + set((state) => { + const widgets = state.zoneWidgetData[zoneId] || []; + return { + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: widgets.map((widget) => + widget.id === widgetId ? { ...widget, position: newPosition } : widget + ), + }, + }; + }), +})); + + +interface RightClickStore { + rightClickSelected: string | null; + setRightClickSelected: (x: string | null) => void; +} + +export const useRightClickSelected = create((set) => ({ + rightClickSelected: null, // Default to null + setRightClickSelected: (x) => set({ rightClickSelected: x }), +})); + +export const useTopData = create((set: any) => ({ + top: 0, + setTop: (x: any) => set({ top: x }), +})); + +export const useLeftData = create((set: any) => ({ + left: 0, + setLeft: (x: any) => set({ left: x }), +})); + +interface RightSelectStore { + rightSelect: string | null; + setRightSelect: (x: string | null) => void; +} + +export const useRightSelected = create((set) => ({ + rightSelect: null, // Default state is null + setRightSelect: (x) => set({ rightSelect: x }), })); diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index e64ec08..a79823c 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -716,11 +716,6 @@ } -.editWidgetOptions-wrapper { - - height: 100vh; - width: 100vw; -} .editWidgetOptions { position: absolute; From d29ee03c44fa7d50c692cb935013a79cd1e84c20 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 3 Apr 2025 19:46:52 +0530 Subject: [PATCH 8/9] refactor: remove console logs and enhance model userData structure --- .../visualization/widgets/Widgets2D.tsx | 1 - .../mechanics/ConveyorMechanics.tsx | 508 +++++++++++------- .../properties/GlobalProperties.tsx | 2 +- .../geomentries/assets/addAssetModel.ts | 141 +++-- .../geomentries/assets/assetManager.ts | 2 +- .../geomentries/assets/deleteFloorItems.ts | 7 + .../builder/groups/floorItemsGroup.tsx | 30 +- .../collaboration/socketResponses.dev.tsx | 4 +- .../scene/IntialLoad/loadInitialFloorItems.ts | 140 +++-- .../controls/selection/copyPasteControls.tsx | 142 ++++- .../selection/duplicationControls.tsx | 144 ++++- .../scene/controls/selection/moveControls.tsx | 113 +++- .../controls/selection/rotateControls.tsx | 113 +++- .../controls/selection/selectionControls.tsx | 8 +- .../scene/postProcessing/postProcessing.tsx | 12 +- .../simulation/behaviour/behaviour.tsx | 128 ++--- .../simulation/process/processAnimator.tsx | 9 +- .../assest/floorAsset/setEventsApt.ts | 32 ++ .../assest/floorAsset/setFloorItemApi.ts | 1 - app/src/store/store.ts | 13 +- 20 files changed, 1060 insertions(+), 490 deletions(-) create mode 100644 app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx index 561c82b..0192a4f 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets2D.tsx @@ -101,7 +101,6 @@ const ProgressBarWidget = ({
); }; -console.log(chartTypes, "chartTypes"); const Widgets2D = () => { return ( diff --git a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx index bd10613..913bdd1 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx @@ -10,6 +10,7 @@ import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { handleResize } from "../../../../functions/handleResizePannel"; import { + useFloorItems, useSelectedActionSphere, useSelectedPath, useSimulationPaths, @@ -17,11 +18,14 @@ import { import * as THREE from "three"; import * as Types from "../../../../types/world/worldTypes"; import InputToggle from "../../../ui/inputs/InputToggle"; +import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; +import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; const ConveyorMechanics: React.FC = () => { const { selectedActionSphere } = useSelectedActionSphere(); const { selectedPath, setSelectedPath } = useSelectedPath(); const { simulationPaths, setSimulationPaths } = useSimulationPaths(); + const { floorItems, setFloorItems } = useFloorItems(); const actionsContainerRef = useRef(null); const triggersContainerRef = useRef(null); @@ -36,6 +40,19 @@ const ConveyorMechanics: React.FC = () => { .find((point) => point.uuid === selectedActionSphere.point.uuid); }, [selectedActionSphere, simulationPaths]); + const updateBackend = async (updatedPath: Types.ConveyorEventsSchema | undefined) => { + if (!updatedPath) return; + // const email = localStorage.getItem("email"); + // const organization = email ? email.split("@")[1].split(".")[0] : ""; + // console.log('updatedPath: ', updatedPath); + // const a = await setEventApi( + // organization, + // updatedPath.modeluuid, + // updatedPath.points + // ); + // console.log('a: ', a); + } + const handleAddAction = () => { if (!selectedActionSphere) return; @@ -65,6 +82,15 @@ const ConveyorMechanics: React.FC = () => { return path; }); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); }; @@ -74,21 +100,30 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.filter( - (action) => action.uuid !== uuid - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.filter( + (action) => action.uuid !== uuid + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); }; @@ -98,36 +133,45 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid - ? { - ...action, - type: actionType, - material: - actionType === "Spawn" || actionType === "Swap" - ? "Inherit" - : action.material, - delay: - actionType === "Delay" ? "Inherit" : action.delay, - spawnInterval: - actionType === "Spawn" - ? "Inherit" - : action.spawnInterval, - } - : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid + ? { + ...action, + type: actionType, + material: + actionType === "Spawn" || actionType === "Swap" + ? "Inherit" + : action.material, + delay: + actionType === "Delay" ? "Inherit" : action.delay, + spawnInterval: + actionType === "Spawn" + ? "Inherit" + : action.spawnInterval, + } + : action + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); // Update the selected item to reflect changes @@ -156,24 +200,33 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid && - (action.type === "Spawn" || action.type === "Swap") - ? { ...action, material } - : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid && + (action.type === "Spawn" || action.type === "Swap") + ? { ...action, material } + : action + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); // Update selected item if it's the current action @@ -194,21 +247,30 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid ? { ...action, delay } : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid ? { ...action, delay } : action + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); }; @@ -221,23 +283,32 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid - ? { ...action, spawnInterval } - : action - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => + action.uuid === uuid + ? { ...action, spawnInterval } + : action + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); }; @@ -248,6 +319,15 @@ const ConveyorMechanics: React.FC = () => { path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } }); }; @@ -258,26 +338,35 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => { - if (point.uuid === selectedActionSphere.point.uuid) { - const triggerIndex = point.triggers.length; - const newTrigger = { - uuid: THREE.MathUtils.generateUUID(), - name: `Trigger ${triggerIndex + 1}`, - type: "", - bufferTime: 0, - isUsed: false, - }; + ...path, + points: path.points.map((point) => { + if (point.uuid === selectedActionSphere.point.uuid) { + const triggerIndex = point.triggers.length; + const newTrigger = { + uuid: THREE.MathUtils.generateUUID(), + name: `Trigger ${triggerIndex + 1}`, + type: "", + bufferTime: 0, + isUsed: false, + }; - return { ...point, triggers: [...point.triggers, newTrigger] }; - } - return point; - }), - } + return { ...point, triggers: [...point.triggers, newTrigger] }; + } + return point; + }), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); }; @@ -287,21 +376,30 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.filter( - (trigger) => trigger.uuid !== uuid - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.filter( + (trigger) => trigger.uuid !== uuid + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); }; @@ -311,23 +409,32 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid - ? { ...trigger, type: triggerType } - : trigger - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => + trigger.uuid === uuid + ? { ...trigger, type: triggerType } + : trigger + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); // Ensure the selectedItem is updated immediately @@ -347,22 +454,31 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - actions: point.actions.map((action) => ({ - ...action, - isUsed: action.uuid === uuid ? !action.isUsed : false, - })), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + actions: point.actions.map((action) => ({ + ...action, + isUsed: action.uuid === uuid ? !action.isUsed : false, + })), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); // Immediately update the selected item if it's the one being toggled @@ -384,22 +500,31 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => ({ - ...trigger, - isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, - })), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => ({ + ...trigger, + isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, + })), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); // Immediately update the selected item if it's the one being toggled @@ -420,23 +545,32 @@ const ConveyorMechanics: React.FC = () => { const updatedPaths = simulationPaths.map((path) => path.type === "Conveyor" ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.point.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid - ? { ...trigger, bufferTime } - : trigger - ), - } - : point - ), - } + ...path, + points: path.points.map((point) => + point.uuid === selectedActionSphere.point.uuid + ? { + ...point, + triggers: point.triggers.map((trigger) => + trigger.uuid === uuid + ? { ...trigger, bufferTime } + : trigger + ), + } + : point + ), + } : path ); + const updatedPath = updatedPaths.find( + (path): path is Types.ConveyorEventsSchema => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.point.uuid + ) + ); + updateBackend(updatedPath); + setSimulationPaths(updatedPaths); // Immediately update selectedItem if it's the currently selected trigger @@ -493,12 +627,11 @@ const ConveyorMechanics: React.FC = () => { {selectedPoint?.actions.map((action) => (
{ setSelectedItem({ type: "action", item: action }) } > - +
{ {selectedPoint?.triggers.map((trigger) => (
{ {selectedItem.type === "action" && ( <> handleActionToggle(selectedItem.item.uuid)} /> @@ -604,19 +736,19 @@ const ConveyorMechanics: React.FC = () => { {/* Only show material dropdown for Spawn/Swap actions */} {(selectedItem.item.type === "Spawn" || selectedItem.item.type === "Swap") && ( - - handleMaterialSelect(selectedItem.item.uuid, option) - } - /> - )} + + handleMaterialSelect(selectedItem.item.uuid, option) + } + /> + )} {/* Only show delay input for Delay actions */} {selectedItem.item.type === "Delay" && ( diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 4e6a537..adae69e 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -33,7 +33,7 @@ const GlobalProperties: React.FC = () => { const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance(); useEffect(() => { - console.log(gridValue, planeValue, "values"); + // console.log(gridValue, planeValue, "values"); }, [gridValue, planeValue]); const { socket } = useSocketStore(); const { limitDistance, setLimitDistance } = useLimitDistance(); diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index a4255bd..2666dbf 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -24,6 +24,7 @@ async function addAssetModel( socket: Socket, selectedItem: any, setSelectedItem: any, + setSimulationPaths: any, plane: Types.RefMesh, ): Promise { @@ -64,7 +65,7 @@ async function addAssetModel( const cachedModel = THREE.Cache.get(selectedItem.id); if (cachedModel) { // console.log(`[Cache] Fetching ${selectedItem.name}`); - handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, socket); + handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationPaths, socket); return; } else { const cachedModelBlob = await retrieveGLTF(selectedItem.id); @@ -77,7 +78,7 @@ async function addAssetModel( URL.revokeObjectURL(blobUrl); THREE.Cache.remove(blobUrl); THREE.Cache.add(selectedItem.id, gltf); - handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, socket); + handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationPaths, socket); }, () => { TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); @@ -89,7 +90,7 @@ async function addAssetModel( const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v1/AssetFile/${selectedItem.id}`).then((res) => res.blob()); await storeGLTF(selectedItem.id, modelBlob); THREE.Cache.add(selectedItem.id, gltf); - await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, socket); + await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationPaths, socket); }, () => { TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); @@ -112,10 +113,11 @@ async function handleModelLoad( tempLoader: Types.RefMesh, isTempLoader: Types.RefBoolean, setFloorItems: Types.setFloorItemSetState, + setSimulationPaths: any, socket: Socket ) { const model = gltf.scene.clone(); - model.userData = { name: selectedItem.name, modelId: selectedItem.id }; + model.userData = { name: selectedItem.name, modelId: selectedItem.id, modeluuid: model.uuid }; model.position.set(intersectPoint!.x, 3 + intersectPoint!.y, intersectPoint!.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); @@ -152,7 +154,7 @@ async function handleModelLoad( if (res.type === "Conveyor") { const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID()); - const eventData: Extract = { + const backendEventData: Extract = { type: 'Conveyor', points: res.points.map((point: any, index: number) => ({ uuid: pointUUIDs[index], @@ -165,7 +167,7 @@ async function handleModelLoad( material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', - isUsed: false + isUsed: true }], triggers: [], connections: { @@ -176,49 +178,94 @@ async function handleModelLoad( speed: 'Inherit' }; - // console.log('eventData: ', eventData); - newFloorItem.eventData = eventData; + // API + + // await setFloorItemApi( + // organization, + // newFloorItem.modeluuid, + // newFloorItem.modelname, + // newFloorItem.modelfileID, + // newFloorItem.position, + // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + // false, + // true, + // newFloorItem.eventData + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + eventData: backendEventData, + socketId: socket.id + }; + + console.log('data: ', data); + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + const eventData: any = backendEventData; + eventData.modeluuid = newFloorItem.modeluuid; + eventData.modelName = newFloorItem.modelname; + eventData.position = newFloorItem.position; + eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z]; + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => [ + ...(prevEvents || []), + eventData as Types.ConveyorEventsSchema | Types.VehicleEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + + } else { + + // API + + // await setFloorItemApi( + // organization, + // newFloorItem.modeluuid, + // newFloorItem.modelname, + // newFloorItem.modelfileID, + // newFloorItem.position, + // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + // false, + // true + // ); + + // SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id + }; + + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + socket.emit("v2:model-asset:add", data); + } - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - // API - - // await setFloorItemApi( - // organization, - // newFloorItem.modeluuid, - // newFloorItem.modelname, - // newFloorItem.modelfileID, - // newFloorItem.position, - // { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - // false, - // true, - // newFloorItem.eventData - // ); - - // SOCKET - - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true, - eventData: newFloorItem.eventData, - socketId: socket.id - }; - console.log('data: ', data); - - - socket.emit("v2:model-asset:add", data); - gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); }); diff --git a/app/src/modules/builder/geomentries/assets/assetManager.ts b/app/src/modules/builder/geomentries/assets/assetManager.ts index 38d0721..240e7a1 100644 --- a/app/src/modules/builder/geomentries/assets/assetManager.ts +++ b/app/src/modules/builder/geomentries/assets/assetManager.ts @@ -121,7 +121,7 @@ export default async function assetManager( const model = gltf; model.uuid = item.modeluuid; - model.userData = { name: item.modelname, modelId: item.modelfileID }; + model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.position.set(...item.position); model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index 3bbe2cc..a71aaea 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -10,6 +10,7 @@ async function DeleteFloorItems( itemsGroup: Types.RefGroup, hoveredDeletableFloorItem: Types.RefMesh, setFloorItems: Types.setFloorItemSetState, + setSimulationPaths: any, socket: Socket ): Promise { @@ -74,6 +75,12 @@ async function DeleteFloorItems( itemsGroup.current.remove(hoveredDeletableFloorItem.current); } setFloorItems(updatedItems); + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => { + const updatedEvents = (prevEvents || []).filter(event => event.modeluuid !== removedItem.modeluuid); + return updatedEvents; + }); + toast.success("Model Removed!"); } } diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index bee47b5..2b77fb2 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -10,6 +10,7 @@ import { useRenderDistance, useselectedFloorItem, useSelectedItem, + useSimulationPaths, useSocketStore, useToggleView, useTransformMode, @@ -64,6 +65,7 @@ const FloorItemsGroup = ({ const { setselectedFloorItem } = useselectedFloorItem(); const { activeTool } = useActiveTool(); const { selectedItem, setSelectedItem } = useSelectedItem(); + const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { setLoadingProgress } = useLoadingProgress(); const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); @@ -96,16 +98,23 @@ const FloorItemsGroup = ({ }; getFloorAssets(organization).then((data) => { - const uniqueItems = (data as Types.FloorItems).filter( - (item, index, self) => - index === self.findIndex((t) => t.modelfileID === item.modelfileID) - ); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { + if (data.length > 0) { + const uniqueItems = (data as Types.FloorItems).filter( + (item, index, self) => + index === self.findIndex((t) => t.modelfileID === item.modelfileID) + ); + totalAssets = uniqueItems.length; + if (totalAssets === 0) { + updateLoadingProgress(100); + return; + } + gltfLoaderWorker.postMessage({ floorItems: data }); + } else { + console.log('data: ', data); + gltfLoaderWorker.postMessage({ floorItems: [] }); + loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationPaths); updateLoadingProgress(100); - return; } - gltfLoaderWorker.postMessage({ floorItems: data }); }); gltfLoaderWorker.onmessage = async (event) => { @@ -122,7 +131,7 @@ const FloorItemsGroup = ({ updateLoadingProgress(progress); if (loadedAssets === totalAssets) { - loadInitialFloorItems(itemsGroup, setFloorItems); + loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationPaths); updateLoadingProgress(100); } }); @@ -240,6 +249,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, setFloorItems, + setSimulationPaths, socket ); } @@ -365,6 +375,7 @@ const FloorItemsGroup = ({ socket, selectedItem, setSelectedItem, + setSimulationPaths, plane ); } @@ -408,7 +419,6 @@ const FloorItemsGroup = ({ activeModule, ]); - useEffect(() => {}, [floorItems]); useFrame(() => { if (controls) diff --git a/app/src/modules/collaboration/socketResponses.dev.tsx b/app/src/modules/collaboration/socketResponses.dev.tsx index d17557d..b8cec55 100644 --- a/app/src/modules/collaboration/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socketResponses.dev.tsx @@ -105,7 +105,7 @@ export default function SocketResponses({ // console.log(`Getting ${data.data.modelname} from cache`); const model = cachedModel.scene.clone(); model.uuid = data.data.modeluuid; - model.userData = { name: data.data.modelname, modelId: data.data.modelFileID }; + model.userData = { name: data.data.modelname, modelId: data.data.modelFileID, modeluuid: data.data.modeluuid }; model.position.set(...data.data.position as [number, number, number]); model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); @@ -183,7 +183,7 @@ export default function SocketResponses({ THREE.Cache.remove(url); const model = gltf.scene; model.uuid = data.data.modeluuid; - model.userData = { name: data.data.modelname, modelId: data.data.modelFileID }; + model.userData = { name: data.data.modelname, modelId: data.data.modelFileID, modeluuid: data.data.modeluuid }; model.position.set(...data.data.position as [number, number, number]); model.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index dfff78d..3630378 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -11,7 +11,8 @@ import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAss async function loadInitialFloorItems( itemsGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState + setFloorItems: Types.setFloorItemSetState, + setSimulationPaths: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => void ): Promise { if (!itemsGroup.current) return; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; @@ -22,6 +23,8 @@ async function loadInitialFloorItems( localStorage.setItem("FloorItems", JSON.stringify(items)); await initializeDB(); + if (items.message === "floorItems not found") return; + if (items) { const storedFloorItems: Types.FloorItems = items; const loader = new GLTFLoader(); @@ -50,6 +53,7 @@ async function loadInitialFloorItems( }); for (const item of storedFloorItems) { + console.log('item: ', item); if (!item.modelfileID) return; const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]); let storedPosition; @@ -68,7 +72,7 @@ async function loadInitialFloorItems( const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { // console.log(`[Cache] Fetching ${item.modelname}`); - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems); + processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, setSimulationPaths); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); return; @@ -85,7 +89,7 @@ async function loadInitialFloorItems( URL.revokeObjectURL(blobUrl); THREE.Cache.remove(blobUrl); THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); + processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationPaths); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }, @@ -108,7 +112,7 @@ async function loadInitialFloorItems( const modelBlob = await fetch(modelUrl).then((res) => res.blob()); await storeGLTF(item.modelfileID!, modelBlob); THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); + processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationPaths); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }, @@ -121,34 +125,23 @@ async function loadInitialFloorItems( }); } else { // console.log(`Item ${item.modelname} is not near`); + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + }, + ]); + if (item.eventData) { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - // eventData: item.eventData, - }, - ]); - } else { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); + processEventData(item, setSimulationPaths); } + modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); } @@ -164,12 +157,13 @@ function processLoadedModel( gltf: any, item: Types.FloorItemType, itemsGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState + setFloorItems: Types.setFloorItemSetState, + setSimulationPaths: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => void ) { const model = gltf; model.uuid = item.modeluuid; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - model.userData = { name: item.modelname, modelId: item.modelfileID }; + model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; model.position.set(...item.position); model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); @@ -186,39 +180,67 @@ function processLoadedModel( itemsGroup?.current?.add(model); - if (item.eventData) { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - // eventData: item.eventData, - }, - ]); - } else { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modeluuid: item.modeluuid, - modelname: item.modelname, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + }, + ]); + + if (item.eventData || item.modelfileID === '67e3da19c2e8f37134526e6a') { + processEventData(item, setSimulationPaths); } gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' }); } +function processEventData(item: Types.FloorItemType, setSimulationPaths: any) { + + if (item.eventData?.type === 'Conveyor') { + + const data: any = item.eventData; + data.modeluuid = item.modeluuid; + data.modelName = item.modelname; + data.position = item.position; + data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z]; + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => [ + ...(prevEvents || []), + data as Types.ConveyorEventsSchema + ]); + } else { + + const pointUUID = THREE.MathUtils.generateUUID(); + const pointPosition = new THREE.Vector3(0, 1.3, 0); + + const newVehiclePath: Types.VehicleEventsSchema = { + modeluuid: item.modeluuid, + modelName: item.modelname, + type: 'Vehicle', + point: { + uuid: pointUUID, + position: [pointPosition.x, pointPosition.y, pointPosition.z], + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: {}, hitCount: 1, end: {}, buffer: 0 }, + connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, + speed: 2, + }, + position: [...item.position], + }; + + setSimulationPaths((prevEvents: (Types.VehicleEventsSchema)[]) => [ + ...(prevEvents || []), + newVehiclePath as Types.VehicleEventsSchema + ]); + } +} + function checkLoadingCompletion( modelsLoaded: number, modelsToLoad: number, diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index 4628311..dea1da6 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSimulationPaths, useSocketStore, useToggleView } from "../../../../store/store"; import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; @@ -10,6 +10,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore() @@ -150,37 +151,128 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas return updatedItems; }); + let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | undefined = simulationPaths.find((events) => events.modeluuid === obj.userData.modeluuid); + const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : "default"; + if (eventData) { + if (eventData.type === 'Conveyor' && eventData) { + const createConveyorPoint = (index: number) => { + const pointUUID = THREE.MathUtils.generateUUID(); + const hasActions = (eventData as Types.ConveyorEventsSchema)?.points[index].actions.length > 0; - //REST + const defaultAction = { + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + type: 'Inherit', + material: 'Inherit', + delay: 'Inherit', + spawnInterval: 'Inherit', + isUsed: true + }; - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // obj.userData.modelId, - // false, - // true, - // ); + return { + uuid: pointUUID, + position: (eventData as Types.ConveyorEventsSchema)?.points[index].position, + rotation: (eventData as Types.ConveyorEventsSchema)?.points[index].rotation, + actions: hasActions + ? (eventData as Types.ConveyorEventsSchema)?.points[index].actions.map(action => ({ + ...action, + uuid: THREE.MathUtils.generateUUID() + })) + : [defaultAction], + triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers, + connections: { + source: { pathUUID: obj.uuid, pointUUID }, + targets: [] + } + }; + }; - //SOCKET + const backendEventData = { + type: 'Conveyor', + points: [ + createConveyorPoint(0), // point1 + createConveyorPoint(1), // middlePoint + createConveyorPoint(2) // point2 + ], + speed: (eventData as Types.ConveyorEventsSchema)?.speed + }; - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id, - }; + //REST - socket.emit("v2:model-asset:add", data); + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // backendEventData + // ); + + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: backendEventData, + socketId: socket.id, + }; + + const newEventData: any = backendEventData; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => [ + ...(prevEvents || []), + newEventData as Types.ConveyorEventsSchema | Types.VehicleEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + } + } else { + + //REST + + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:add", data); + + } itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index 6b32195..185ca68 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSimulationPaths, useSocketStore, useToggleView } from "../../../../store/store"; import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; @@ -10,11 +10,11 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); - useEffect(() => { if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; @@ -131,37 +131,129 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return updatedItems; }); + let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | undefined = simulationPaths.find((events) => events.modeluuid === obj.userData.modeluuid); + const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST + if (eventData) { + if (eventData.type === 'Conveyor' && eventData) { + const createConveyorPoint = (index: number) => { + const pointUUID = THREE.MathUtils.generateUUID(); + const hasActions = (eventData as Types.ConveyorEventsSchema)?.points[index].actions.length > 0; - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // obj.userData.modelId, - // false, - // true, - // ); + const defaultAction = { + uuid: THREE.MathUtils.generateUUID(), + name: 'Action 1', + type: 'Inherit', + material: 'Inherit', + delay: 'Inherit', + spawnInterval: 'Inherit', + isUsed: true + }; - //SOCKET + return { + uuid: pointUUID, + position: (eventData as Types.ConveyorEventsSchema)?.points[index].position, + rotation: (eventData as Types.ConveyorEventsSchema)?.points[index].rotation, + actions: hasActions + ? (eventData as Types.ConveyorEventsSchema)?.points[index].actions.map(action => ({ + ...action, + uuid: THREE.MathUtils.generateUUID() + })) + : [defaultAction], + triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers, + connections: { + source: { pathUUID: obj.uuid, pointUUID }, + targets: [] + } + }; + }; - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id, - }; + const backendEventData = { + type: 'Conveyor', + points: [ + createConveyorPoint(0), // point1 + createConveyorPoint(1), // middlePoint + createConveyorPoint(2) // point2 + ], + speed: (eventData as Types.ConveyorEventsSchema)?.speed + }; - socket.emit("v2:model-asset:add", data); + //REST + + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // backendEventData + // ); + + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: backendEventData, + socketId: socket.id, + }; + + const newEventData: any = backendEventData; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => [ + ...(prevEvents || []), + newEventData as Types.ConveyorEventsSchema | Types.VehicleEventsSchema + ]); + + socket.emit("v2:model-asset:add", data); + } + } else { + + //REST + + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:add", data); + + } itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx index 15973d8..350b487 100644 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ b/app/src/modules/scene/controls/selection/moveControls.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSimulationPaths, useSocketStore, useToggleView } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; @@ -12,6 +12,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); @@ -179,37 +180,99 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje return updatedItems; }); + let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | undefined = simulationPaths.find((events) => events.modeluuid === obj.userData.modeluuid); + const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST + if (eventData) { + if (eventData.type === 'Conveyor' && eventData) { - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // obj.userData.modelId, - // false, - // true, - // ); + const backendEventData = { + type: 'Conveyor', + points: eventData.points, + speed: (eventData as Types.ConveyorEventsSchema)?.speed + }; - //SOCKET + //REST - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id, - }; + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // backendEventData + // ); - socket.emit("v2:model-asset:add", data); + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: backendEventData, + socketId: socket.id, + }; + + const newEventData: any = backendEventData; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => { + const updatedEvents = (prevEvents || []).map(event => + event.modeluuid === newFloorItem.modeluuid + ? { ...event, ...newEventData } + : event + ); + return updatedEvents; + }); + + // socket.emit("v2:model-asset:add", data); + } + } else { + + + //REST + + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:add", data); + + } itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx index f8771be..6ad8309 100644 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ b/app/src/modules/scene/controls/selection/rotateControls.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSimulationPaths, useSocketStore, useToggleView } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; @@ -12,6 +12,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); @@ -182,37 +183,99 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo return updatedItems; }); + let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | undefined = simulationPaths.find((events) => events.modeluuid === obj.userData.modeluuid); + const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST + if (eventData) { + if (eventData.type === 'Conveyor' && eventData) { - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // obj.userData.modelId, - // false, - // true, - // ); + const backendEventData = { + type: 'Conveyor', + points: eventData.points, + speed: (eventData as Types.ConveyorEventsSchema)?.speed + }; - //SOCKET + //REST - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id, - }; + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // backendEventData + // ); - socket.emit("v2:model-asset:add", data); + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + eventData: backendEventData, + socketId: socket.id, + }; + + const newEventData: any = backendEventData; + newEventData.modeluuid = newFloorItem.modeluuid; + newEventData.modelName = newFloorItem.modelname; + newEventData.position = newFloorItem.position; + newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; + + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => { + const updatedEvents = (prevEvents || []).map(event => + event.modeluuid === newFloorItem.modeluuid + ? { ...event, ...newEventData } + : event + ); + return updatedEvents; + }); + + // socket.emit("v2:model-asset:add", data); + } + } else { + + + //REST + + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:add", data); + + } itemsGroupRef.current.add(obj); } diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index 8e9ac96..fd7e19e 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionHelper } from "./selectionHelper"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSimulationPaths, useSocketStore, useToggleView } from "../../../../store/store"; import BoundingBox from "./boundingBoxHelper"; import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; @@ -20,6 +20,7 @@ const SelectionControls: React.FC = () => { const itemsGroupRef = useRef(undefined); const selectionGroup = useRef() as Types.RefGroup; const { toggleView } = useToggleView(); + const { setSimulationPaths } = useSimulationPaths(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const [movedObjects, setMovedObjects] = useState([]); const [rotatedObjects, setRotatedObjects] = useState([]); @@ -239,6 +240,11 @@ const SelectionControls: React.FC = () => { } }); + setSimulationPaths((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => { + const updatedEvents = (prevEvents || []).filter(event => event.modeluuid !== selectedMesh.uuid); + return updatedEvents; + }); + itemsGroupRef.current?.remove(selectedMesh); }); diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 15597b8..ffa020e 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -93,13 +93,13 @@ export default function PostProcessing() { @@ -108,9 +108,9 @@ export default function PostProcessing() { { const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; - floorItems.forEach((item: Types.FloorItemType) => { - if (item.modelfileID === "672a090f80d91ac979f4d0bd") { - console.log('item: ', item); - const point1Position = new THREE.Vector3(0, 0.85, 2.2); - const middlePointPosition = new THREE.Vector3(0, 0.85, 0); - const point2Position = new THREE.Vector3(0, 0.85, -2.2); + // floorItems.forEach((item: Types.FloorItemType) => { + // if (item.modelfileID === "672a090f80d91ac979f4d0bd") { + // const point1Position = new THREE.Vector3(0, 0.85, 2.2); + // const middlePointPosition = new THREE.Vector3(0, 0.85, 0); + // const point2Position = new THREE.Vector3(0, 0.85, -2.2); - const point1UUID = THREE.MathUtils.generateUUID(); - const middlePointUUID = THREE.MathUtils.generateUUID(); - const point2UUID = THREE.MathUtils.generateUUID(); + // const point1UUID = THREE.MathUtils.generateUUID(); + // const middlePointUUID = THREE.MathUtils.generateUUID(); + // const point2UUID = THREE.MathUtils.generateUUID(); - const newPath: Types.ConveyorEventsSchema = { - modeluuid: item.modeluuid, - modelName: item.modelname, - type: 'Conveyor', - points: [ - { - uuid: point1UUID, - position: [point1Position.x, point1Position.y, point1Position.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] }, - }, - { - uuid: middlePointUUID, - position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] }, - }, - { - uuid: point2UUID, - position: [point2Position.x, point2Position.y, point2Position.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, - }, - ], - position: [...item.position], - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - speed: 'Inherit', - }; + // const newPath: Types.ConveyorEventsSchema = { + // modeluuid: item.modeluuid, + // modelName: item.modelname, + // type: 'Conveyor', + // points: [ + // { + // uuid: point1UUID, + // position: [point1Position.x, point1Position.y, point1Position.z], + // rotation: [0, 0, 0], + // actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + // triggers: [], + // connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] }, + // }, + // { + // uuid: middlePointUUID, + // position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], + // rotation: [0, 0, 0], + // actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + // triggers: [], + // connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] }, + // }, + // { + // uuid: point2UUID, + // position: [point2Position.x, point2Position.y, point2Position.z], + // rotation: [0, 0, 0], + // actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + // triggers: [], + // connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, + // }, + // ], + // position: [...item.position], + // rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + // speed: 'Inherit', + // }; - newPaths.push(newPath); - } else if (item.modelfileID === "67e3da19c2e8f37134526e6a") { - const pointUUID = THREE.MathUtils.generateUUID(); - const pointPosition = new THREE.Vector3(0, 1.3, 0); + // newPaths.push(newPath); + // } else if (item.modelfileID === "67e3da19c2e8f37134526e6a") { + // const pointUUID = THREE.MathUtils.generateUUID(); + // const pointPosition = new THREE.Vector3(0, 1.3, 0); - const newVehiclePath: Types.VehicleEventsSchema = { - modeluuid: item.modeluuid, - modelName: item.modelname, - type: 'Vehicle', - point: { - uuid: pointUUID, - position: [pointPosition.x, pointPosition.y, pointPosition.z], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: {}, hitCount: 1, end: {}, buffer: 0 }, - connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, - speed: 2, - }, - position: [...item.position], - }; + // const newVehiclePath: Types.VehicleEventsSchema = { + // modeluuid: item.modeluuid, + // modelName: item.modelname, + // type: 'Vehicle', + // point: { + // uuid: pointUUID, + // position: [pointPosition.x, pointPosition.y, pointPosition.z], + // actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: {}, hitCount: 1, end: {}, buffer: 0 }, + // connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] }, + // speed: 2, + // }, + // position: [...item.position], + // }; - newPaths.push(newVehiclePath); - } - }); + // newPaths.push(newVehiclePath); + // } + // }); - setSimulationPaths(newPaths); + // setSimulationPaths(newPaths); + // console.log('floorItems: ', floorItems); }, [floorItems]); return null; diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx index 123ec80..a7516e4 100644 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -69,7 +69,6 @@ const MAX_SPAWNED_OBJECTS = 20; const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ processes, }) => { - console.log("processes: ", processes); const gltf = useLoader(GLTFLoader, boxGltb) as GLTF; const { isPlaying, setIsPlaying } = usePlayButtonStore(); @@ -321,10 +320,10 @@ const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ spawnedObjectsRef.current.push(newObject); // Clean up old objects if needed - console.log( - "spawnedObjectsRef.current.length: ", - spawnedObjectsRef.current.length - ); + // console.log( + // "spawnedObjectsRef.current.length: ", + // spawnedObjectsRef.current.length + // ); if (spawnedObjectsRef.current.length > MAX_SPAWNED_OBJECTS) { const oldest = spawnedObjectsRef.current.shift(); if (oldest && groupRef.current.children.includes(oldest)) { diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts b/app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts new file mode 100644 index 0000000..86c8f71 --- /dev/null +++ b/app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts @@ -0,0 +1,32 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const setEventApi = async ( + organization: string, + modeluuid: string, + eventData: any +) => { + try { + const body: any = { organization, modeluuid, eventData }; + + const response = await fetch(`${url_Backend_dwinzo}/api/v2/eventDataUpdate`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error("Failed to set or update Floor Item"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; \ No newline at end of file diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts index 75583cc..e25e05e 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts @@ -30,7 +30,6 @@ export const setFloorItemApi = async ( } const result = await response.json(); - console.log('result: ', result); return result; } catch (error) { if (error instanceof Error) { diff --git a/app/src/store/store.ts b/app/src/store/store.ts index bd903f2..365eb97 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -343,14 +343,21 @@ export const useSelectedPath = create((set: any) => ({ interface SimulationPathsStore { simulationPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]; setSimulationPaths: ( - paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] + paths: + | (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] + | ((prev: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] + ) => (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) ) => void; } export const useSimulationPaths = create((set) => ({ simulationPaths: [], - setSimulationPaths: (paths) => set({ simulationPaths: paths }), -})); + setSimulationPaths: (paths) => + set((state) => ({ + simulationPaths: + typeof paths === "function" ? paths(state.simulationPaths) : paths, + })), +})) export const useIsConnecting = create((set: any) => ({ isConnecting: false, From 4491383093938f0dad10e12bb411b1e3189e0f02 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 3 Apr 2025 19:51:46 +0530 Subject: [PATCH 9/9] fix: update asset model to set isUsed to false --- app/src/modules/builder/geomentries/assets/addAssetModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 2666dbf..a1ed87b 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -167,7 +167,7 @@ async function handleModelLoad( material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', - isUsed: true + isUsed: false }], triggers: [], connections: {