From 5cef9bdb8aacc1ebd29e5a28d1a4868a64e64e1f Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 15 Apr 2025 14:15:39 +0530 Subject: [PATCH] Refactor simulation types and update imports - Renamed simulation type imports from `simulation` to `simulationTypes` across multiple files for consistency. - Consolidated simulation type definitions into a new `simulationTypes.d.ts` file. - Updated relevant components (e.g., `ArmBot`, `IkInstances`, `PathConnector`, etc.) to use the new type definitions. - Removed the old `simulation.d.ts` file to clean up the codebase. - Adjusted function signatures and state management in components to align with the new type structure. --- .../mechanics/ArmBotMechanics.tsx | 25 +- .../mechanics/ConveyorMechanics.tsx | 2 +- .../mechanics/StaticMachineMechanics.tsx | 2 +- .../mechanics/VehicleMechanics.tsx | 2 +- .../ui/inputs/InputWithDropDown.tsx | 5 +- .../geomentries/assets/addAssetModel.ts | 2 +- .../geomentries/assets/deleteFloorItems.ts | 2 +- .../scene/IntialLoad/loadInitialFloorItems.ts | 3 +- .../controls/selection/copyPasteControls.tsx | 2 +- .../selection/duplicationControls.tsx | 2 +- .../scene/controls/selection/moveControls.tsx | 2 +- .../controls/selection/rotateControls.tsx | 2 +- .../controls/selection/selectionControls.tsx | 3 +- app/src/modules/simulation/armbot/ArmBot.tsx | 14 +- .../simulation/armbot/ArmBotInstances.tsx | 63 +++- .../armbot/IKAnimationController.tsx | 269 ++++++++++++++---- .../modules/simulation/armbot/IkInstances.tsx | 62 ++-- .../modules/simulation/path/pathConnector.tsx | 3 +- .../modules/simulation/path/pathCreation.tsx | 42 +-- .../simulation/process/processCreator.tsx | 2 +- app/src/modules/simulation/simulation.tsx | 61 ++-- app/src/store/store.ts | 2 +- .../{simulation.d.ts => simulationTypes.d.ts} | 0 23 files changed, 383 insertions(+), 189 deletions(-) rename app/src/types/{simulation.d.ts => simulationTypes.d.ts} (100%) diff --git a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx index e157d07..70e297b 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo, useCallback, useState } from "react"; import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; -import * as SimulationTypes from '../../../../types/simulation'; +import * as SimulationTypes from '../../../../types/simulationTypes'; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { handleResize } from "../../../../functions/handleResizePannel"; @@ -224,19 +224,28 @@ const ArmBotMechanics: React.FC = () => { }, [selectedPoint, selectedProcessIndex, handleProcessChange]); const handleTriggerSelect = useCallback((displayName: string, index: number) => { - const selected = connectedTriggers.find(t => t.displayName === displayName); + const availableOptions = getFilteredTriggerOptions(index); + const selectedDisplayIndex = availableOptions.indexOf(displayName); + + const filteredTriggers = connectedTriggers.filter(trigger => + !selectedPoint?.actions.processes + ?.filter((_, i) => i !== index) + .map(p => p.triggerId) + .includes(trigger.uuid) + ); + + const selected = filteredTriggers[selectedDisplayIndex]; + if (!selected || !selectedPoint?.actions.processes) return; const oldProcess = selectedPoint.actions.processes[index]; const updatedProcesses = [...selectedPoint.actions.processes]; - - // Only reset start/end if new trigger invalidates them (your logic can expand this) updatedProcesses[index] = { ...oldProcess, triggerId: selected.uuid, - startPoint: oldProcess.startPoint || "", // preserve if exists - endPoint: oldProcess.endPoint || "" // preserve if exists + startPoint: oldProcess.startPoint || "", + endPoint: oldProcess.endPoint || "" }; handleProcessChange(updatedProcesses); @@ -298,8 +307,10 @@ const ArmBotMechanics: React.FC = () => { handleSpeedChange(parseInt(value))} + onChange={(value) => handleSpeedChange(parseFloat(value))} />
diff --git a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx index 080aba5..5e84d10 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx @@ -17,7 +17,7 @@ import { useSocketStore, } from "../../../../store/store"; import * as THREE from "three"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import InputToggle from "../../../ui/inputs/InputToggle"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; diff --git a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx index 812ed54..6342bac 100644 --- a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo, useCallback } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; -import * as SimulationTypes from '../../../../types/simulation'; +import * as SimulationTypes from '../../../../types/simulationTypes'; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx index 73199c9..147d5cb 100644 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; -import * as SimulationTypes from '../../../../types/simulation'; +import * as SimulationTypes from '../../../../types/simulationTypes'; import PositionInput from "../customInput/PositionInputs"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; import LabeledButton from "../../../ui/inputs/LabledButton"; diff --git a/app/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx index b0fc135..b672313 100644 --- a/app/src/components/ui/inputs/InputWithDropDown.tsx +++ b/app/src/components/ui/inputs/InputWithDropDown.tsx @@ -4,7 +4,8 @@ import RenameInput from "./RenameInput"; type InputWithDropDownProps = { label: string; value: string; - min?: number + min?: number; + step?: number; defaultValue?: string; options?: string[]; // Array of dropdown options activeOption?: string; // The currently active dropdown option @@ -18,6 +19,7 @@ const InputWithDropDown: React.FC = ({ label, value, min, + step, defaultValue, options, activeOption, @@ -45,6 +47,7 @@ const InputWithDropDown: React.FC = ({
{ diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 97adec3..0db803f 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -5,7 +5,7 @@ import { toast } from 'react-toastify'; import TempLoader from './tempLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { Socket } from 'socket.io-client'; diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index e904947..dbd7e9b 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -2,7 +2,7 @@ import { toast } from 'react-toastify'; import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import { Socket } from 'socket.io-client'; import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 08bed84..7ca7db9 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -5,7 +5,7 @@ import * as THREE from 'three'; import * as CONSTANTS from '../../../types/world/worldConstants'; import { toast } from 'react-toastify'; import * as Types from "../../../types/world/worldTypes"; -import * as SimulationTypes from "../../..//types/simulation"; +import * as SimulationTypes from "../../../types/simulationTypes"; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; @@ -228,7 +228,6 @@ function processEventData(item: SimulationTypes.EventData, setSimulationStates: } else if (item.eventData?.type === 'StaticMachine') { const data: any = item.eventData; - item.eventData.points.position = [0, 1.5, 1] data.modeluuid = item.modeluuid; data.modelName = item.modelname; data.position = item.position; diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index 87ed646..84d6047 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index 41bbce3..5ce0603 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx index 99b9a43..d717ecb 100644 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ b/app/src/modules/scene/controls/selection/moveControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx index 611d14b..f602bc4 100644 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ b/app/src/modules/scene/controls/selection/rotateControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index d1eed1e..d26889d 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -8,7 +8,7 @@ import BoundingBox from "./boundingBoxHelper"; import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import DuplicationControls from "./duplicationControls"; import CopyPasteControls from "./copyPasteControls"; @@ -199,6 +199,7 @@ const SelectionControls: React.FC = () => { setDuplicatedObjects([]); setSelectedAssets([]); }; + const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { if (updatedPaths.length === 0) return; const email = localStorage.getItem("email"); diff --git a/app/src/modules/simulation/armbot/ArmBot.tsx b/app/src/modules/simulation/armbot/ArmBot.tsx index db0f956..2e77206 100644 --- a/app/src/modules/simulation/armbot/ArmBot.tsx +++ b/app/src/modules/simulation/armbot/ArmBot.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { useThree } from "@react-three/fiber"; import useModuleStore from "../../../store/useModuleStore"; import { useSimulationStates } from "../../../store/store"; -import * as SimulationTypes from '../../../types/simulation'; +import * as SimulationTypes from '../../../types/simulationTypes'; import { ArmbotInstances } from "./ArmBotInstances"; interface ArmBotState { @@ -12,14 +12,18 @@ interface ArmBotState { status: string; material: string; triggerId: string; - connections: any + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; } -const ArmBot: React.FC = () => { +interface ArmBotProps { + armBots: ArmBotState[]; + setArmBots: React.Dispatch>; +} + +const ArmBot = ({ armBots, setArmBots }: ArmBotProps) => { const { activeModule } = useModuleStore(); const { scene } = useThree(); const { simulationStates } = useSimulationStates(); - const [armBots, setArmBots] = useState([]); useEffect(() => { const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot"); @@ -30,7 +34,7 @@ const ArmBot: React.FC = () => { status: "idle", material: "default", triggerId: '', - connections: bot.points.connections + actions: bot.points.actions })); setArmBots(initialStates); }, [simulationStates]); diff --git a/app/src/modules/simulation/armbot/ArmBotInstances.tsx b/app/src/modules/simulation/armbot/ArmBotInstances.tsx index 6ce4e01..8a1380b 100644 --- a/app/src/modules/simulation/armbot/ArmBotInstances.tsx +++ b/app/src/modules/simulation/armbot/ArmBotInstances.tsx @@ -1,5 +1,15 @@ import IkInstances from "./IkInstances"; import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb"; +import { useEffect, useState } from "react"; +import { useThree } from "@react-three/fiber"; +import { Vector3 } from "three"; + +interface Process { + triggerId: string; + startPoint?: Vector3; + endPoint?: Vector3; + speed: number; +} interface ArmBotState { uuid: string; @@ -8,18 +18,65 @@ interface ArmBotState { status: string; material: string; triggerId: string; - connections: any + actions: { + uuid: string; + name: string; + speed: number; + processes: { + triggerId: string; + startPoint: string; + endPoint: string; + }[]; + }; } interface ArmbotInstancesProps { index: number; armBot: ArmBotState; - setArmBots: (armBots: ArmBotState[]) => void; + setArmBots: React.Dispatch>; } export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots }) => { + const { scene } = useThree(); + const [processes, setProcesses] = useState([]); + + useEffect(() => { + + if (armBot.actions.processes.length > 0) { + const mappedProcesses = armBot.actions.processes.map((process) => { + return { + triggerId: process.triggerId, + startPoint: scene.getObjectByProperty('uuid', process.startPoint)?.getWorldPosition(new Vector3()), + endPoint: scene.getObjectByProperty('uuid', process.endPoint)?.getWorldPosition(new Vector3()), + speed: armBot.actions.speed + }; + }); + setProcesses(mappedProcesses); + } else { + setProcesses([]); + } + }, [armBot, scene]); + + const updateArmBotStatus = (status: string) => { + setArmBots((prevArmBots) => { + return prevArmBots.map(bot => { + if (bot.uuid === armBot.uuid) { + return { ...bot, status }; + } + return bot; + }); + }); + }; return ( - + ); }; \ No newline at end of file diff --git a/app/src/modules/simulation/armbot/IKAnimationController.tsx b/app/src/modules/simulation/armbot/IKAnimationController.tsx index 45d482f..538f0aa 100644 --- a/app/src/modules/simulation/armbot/IKAnimationController.tsx +++ b/app/src/modules/simulation/armbot/IKAnimationController.tsx @@ -1,96 +1,245 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, useRef } from "react"; import { useFrame } from "@react-three/fiber"; import * as THREE from "three"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; + +type IKAnimationControllerProps = { + ikSolver: any; + process: { + triggerId: string; + startPoint: THREE.Vector3; + endPoint: THREE.Vector3; + speed: number; + }[]; + selectedTrigger: string; + targetBoneName: string; + uuid: string; + logStatus: (status: string) => void; + groupRef: React.RefObject; + updateArmBotStatus: (status: string) => void; +} const IKAnimationController = ({ ikSolver, process, selectedTrigger, targetBoneName, -}: { - ikSolver: any; - process: { - trigger: string; - start: THREE.Vector3; - end: THREE.Vector3; - speed: number; - }[]; - selectedTrigger: string; - targetBoneName: string; -}) => { + uuid, + logStatus, + groupRef, + updateArmBotStatus +}: IKAnimationControllerProps) => { const [progress, setProgress] = useState(0); + const [initialProgress, setInitialProgress] = useState(0); + const [needsInitialMovement, setNeedsInitialMovement] = useState(true); + const [isInitializing, setIsInitializing] = useState(true); const restSpeed = 0.1; + const restPosition = new THREE.Vector3(0, 2, 1.6); + const { isPlaying } = usePlayButtonStore(); + + // Track previous states for comparison + const prevStateRef = useRef({ + isInitializing: true, + needsInitialMovement: true, + selectedTrigger: "", + progress: 0 + }); + + // Track previous status for comparison + const prevStatusRef = useRef(""); + + const initialCurveRef = useRef(null); + const initialStartPositionRef = useRef(null); useEffect(() => { setProgress(0); }, [selectedTrigger]); - const processedCurves = useMemo(() => { - const restPosition = new THREE.Vector3(0.2, 2.3, 1.6); - return process.map((p) => { - const mid = new THREE.Vector3( - (p.start.x + p.end.x) / 1, - Math.max(p.start.y, p.end.y) + 0.8, - (p.start.z + p.end.z) / 0.9 + useEffect(() => { + if (ikSolver) { + const targetBone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBoneName ); - const points = [ - restPosition.clone(), - p.start.clone(), - mid.clone(), - p.end.clone(), - restPosition.clone(), - ]; - const curve = new THREE.CatmullRomCurve3(points); - const restToStartDist = points[0].distanceTo(points[1]); - const startToEndDist = points[1].distanceTo(points[3]); - const endToRestDist = points[3].distanceTo(points[4]); + if (targetBone) { + initialStartPositionRef.current = targetBone.position.clone(); + calculateInitialCurve(targetBone.position); + logStatus(`[Arm ${uuid}] Initializing IK system, starting position: ${targetBone.position.toArray()}`); + } + } + }, [ikSolver]); - const totalDist = restToStartDist + startToEndDist + endToRestDist; - const restToStartRange = [0, restToStartDist / totalDist]; - const startToEndRange = [ - restToStartRange[1], - restToStartRange[1] + startToEndDist / totalDist, - ]; - const endToRestRange = [startToEndRange[1], 1]; + // Log state changes + useEffect(() => { + const prev = prevStateRef.current; - return { - trigger: p.trigger, - curve, - speed: p.speed, - restToStartRange, - startToEndRange, - endToRestRange, - }; + if (prev.isInitializing !== isInitializing) { + if (!isInitializing) { + logStatus(`[Arm ${uuid}] Completed initialization, now at rest position`); + } + } + + if (prev.needsInitialMovement !== needsInitialMovement && !needsInitialMovement) { + logStatus(`[Arm ${uuid}] Reached rest position, ready for animation`); + } + + if (prev.selectedTrigger !== selectedTrigger) { + logStatus(`[Arm ${uuid}] Processing new trigger: ${selectedTrigger}`); + } + + // Update previous state + prevStateRef.current = { + isInitializing, + needsInitialMovement, + selectedTrigger, + progress + }; + }, [isInitializing, needsInitialMovement, selectedTrigger, progress]); + + const calculateInitialCurve = (startPosition: THREE.Vector3) => { + const direction = new THREE.Vector3().subVectors(restPosition, startPosition); + const distance = direction.length(); + direction.normalize(); + + const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const midHeight = 0.5; + const tiltAmount = 1; + const mid = new THREE.Vector3() + .addVectors(startPosition, restPosition) + .multiplyScalar(0.5) + .add(perpendicular.clone().multiplyScalar(distance * 0.3 * tiltAmount)) + .add(new THREE.Vector3(0, midHeight, 0)); + + initialCurveRef.current = new THREE.CatmullRomCurve3([ + startPosition, + new THREE.Vector3().lerpVectors(startPosition, mid, 0.33), + mid, + new THREE.Vector3().lerpVectors(mid, restPosition, 0.66), + restPosition + ]); + }; + + const processedCurves = useMemo(() => { + return process.map((p) => { + const tempLift = 0.5; + const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); + const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); + + if (localStart && localEnd) { + + const mid = new THREE.Vector3( + (localStart.x + localEnd.x) / 1, + Math.max(localStart.y, localEnd.y) + 0.8, + (localStart.z + localEnd.z) / 0.9 + ); + + const points = [ + restPosition.clone(), + localStart.clone(), + mid.clone(), + localEnd.clone(), + restPosition.clone(), + ]; + const curve = new THREE.CatmullRomCurve3(points); + const restToStartDist = points[0].distanceTo(points[1]); + const startToEndDist = points[1].distanceTo(points[3]); + const endToRestDist = points[3].distanceTo(points[4]); + + const totalDist = restToStartDist + startToEndDist + endToRestDist; + const restToStartRange = [0, restToStartDist / totalDist]; + const startToEndRange = [ + restToStartRange[1], + restToStartRange[1] + startToEndDist / totalDist, + ]; + const endToRestRange = [startToEndRange[1], 1]; + + return { + trigger: p.triggerId, + curve, + speed: p.speed, + restToStartRange, + startToEndRange, + endToRestRange, + }; + } }); - }, [process]); + }, [process, groupRef]); const activeCurve = useMemo(() => { - return processedCurves.find((c) => c.trigger === selectedTrigger); + return processedCurves.find((c) => c?.trigger === selectedTrigger); }, [processedCurves, selectedTrigger]); + // Initial movement to rest position useFrame((_, delta) => { - if (!ikSolver || !activeCurve) return; + if (!ikSolver || !needsInitialMovement || !isInitializing || !initialCurveRef.current) return; - const { curve, speed, startToEndRange } = activeCurve; + const targetBone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBoneName + ); + if (!targetBone) return; + + setInitialProgress((prev) => { + const next = prev + delta * 0.5; + if (next >= 1) { + targetBone.position.copy(restPosition); + setNeedsInitialMovement(false); + setIsInitializing(false); + return 1; + } + targetBone.position.copy(initialCurveRef.current!.getPoint(next)); + return next; + }); + + ikSolver.update(); + }); + + // Main animation loop + useFrame((_, delta) => { + if (!ikSolver || !activeCurve || isInitializing) return; + + const { curve, speed, restToStartRange, startToEndRange, endToRestRange } = activeCurve; const targetBone = ikSolver.mesh.skeleton.bones.find( (b: any) => b.name === targetBoneName ); if (!targetBone) return; let currentSpeed = restSpeed; - if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { + let currentStatus = "idle"; // Default status + + // Determine current phase and status + if (progress < restToStartRange[1]) { + currentSpeed = restSpeed; + currentStatus = "moving"; // Moving to start point + } else if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { currentSpeed = speed; + currentStatus = "moving"; // Moving between points + } else if (progress >= endToRestRange[0] && progress < 1) { + currentSpeed = restSpeed; + currentStatus = "moving"; // Returning to rest + } else if (progress >= 1) { + currentStatus = "idle"; // Completed cycle } - setProgress((prev) => { - const next = prev + delta * currentSpeed; - if (next >= 1) { - targetBone.position.copy(curve.getPoint(1)); - return 1; - } - targetBone.position.copy(curve.getPoint(next)); - return next; - }); + // Update status when it changes + if (prevStatusRef.current !== currentStatus) { + updateArmBotStatus(currentStatus); + prevStatusRef.current = currentStatus; + } + + // Only update progress if we're not already at the end + if (progress < 1) { + setProgress((prev) => { + const next = prev + delta * currentSpeed; + return Math.min(next, 1); // Cap at 1 + }); + } + + // Update bone position based on progress + if (progress < 1) { + targetBone.position.copy(curve.getPoint(progress)); + } else { + targetBone.position.copy(curve.getPoint(1)); + } ikSolver.update(); }); @@ -98,4 +247,4 @@ const IKAnimationController = ({ return null; }; -export default IKAnimationController; +export default IKAnimationController; \ No newline at end of file diff --git a/app/src/modules/simulation/armbot/IkInstances.tsx b/app/src/modules/simulation/armbot/IkInstances.tsx index b789f3a..7b02941 100644 --- a/app/src/modules/simulation/armbot/IkInstances.tsx +++ b/app/src/modules/simulation/armbot/IkInstances.tsx @@ -1,43 +1,40 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; -import { useLoader } from "@react-three/fiber"; +import { useFrame, useLoader } from "@react-three/fiber"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { clone } from "three/examples/jsm/utils/SkeletonUtils"; import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver"; import IKAnimationController from "./IKAnimationController"; +import { TransformControls } from "@react-three/drei"; -const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; position: [number, number, number]; rotation: [number, number, number]; }) => { +const IkInstances = ({ + uuid, + modelUrl, + processes, + position, + rotation, + updateArmBotStatus +}: { + uuid: string; + modelUrl: string; + processes: any; + position: [number, number, number]; + rotation: [number, number, number]; + updateArmBotStatus: (status: string) => void; +}) => { const [ikSolver, setIkSolver] = useState(null); - const [selectedTrigger, setSelectedTrigger] = useState("idle"); + const [selectedTrigger, setSelectedTrigger] = useState(""); const gltf = useLoader(GLTFLoader, modelUrl, (loader) => { const draco = new DRACOLoader(); - draco.setDecoderPath( - "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/" - ); + draco.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/"); loader.setDRACOLoader(draco); }); const cloned = useMemo(() => clone(gltf.scene), [gltf]); const groupRef = useRef(null); - const [selectedArm, setSelectedArm] = useState(); const targetBoneName = "Target"; const skinnedMeshName = "link_0"; - const process = useMemo(() => [ - { - trigger: "Trigger1", - start: new THREE.Vector3(-0.75, 1.5, -2.2), - end: new THREE.Vector3(0, 1.2, 2.2), - speed: 0.25, - }, - { - trigger: "Trigger2", - start: new THREE.Vector3(0, 1.2, 2.2), - end: new THREE.Vector3(0.75, 1.5, -2.2), - speed: 0.22, - } - ], []); - useEffect(() => { if (!gltf) return; const OOI: any = {}; @@ -86,7 +83,7 @@ const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; posit }, [gltf]); useEffect(() => { - const triggers = ['Trigger1', 'Trigger2']; + const triggers = ["9f4a9b8b-e60d-4754-8c99-d71979da0e71", "b77b4f0a-ce55-4fe0-a181-a43ab3d01c83"]; let index = 0; const cycleTriggers = setInterval(() => { @@ -97,30 +94,33 @@ const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; posit return () => clearInterval(cycleTriggers); }, []); + const logStatus = (status: string) => { + // console.log(status); + } + return ( <> { - e.stopPropagation(); - setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) - }} + position={position} + rotation={rotation} > - {/* {selectedArm && } */} ); }; diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 0071240..0008a73 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -2,7 +2,7 @@ import { useFrame, useThree } from '@react-three/fiber'; import React, { useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; -import * as SimulationTypes from '../../../types/simulation'; +import * as SimulationTypes from '../../../types/simulationTypes'; import { QuadraticBezierLine } from '@react-three/drei'; import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store'; import useModuleStore from '../../../store/useModuleStore'; @@ -982,7 +982,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec setSimulationStates(updatedStates); }; - return ( {simulationStates.flatMap(path => { diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 4a8da49..1e9601d 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -1,5 +1,5 @@ import * as THREE from "three"; -import * as SimulationTypes from "../../../types/simulation"; +import * as SimulationTypes from "../../../types/simulationTypes"; import { useRef, useState, useEffect, useMemo } from "react"; import { Sphere, TransformControls } from "@react-three/drei"; import { @@ -66,45 +66,6 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec }); }); - const updateSimulationPaths = () => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Conveyor") { - return { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - position: [ - selectedActionSphere.points.position.x, - selectedActionSphere.points.position.y, - selectedActionSphere.points.position.z, - ], - rotation: [ - selectedActionSphere.points.rotation.x, - selectedActionSphere.points.rotation.y, - selectedActionSphere.points.rotation.z, - ], - } - : point - ), - }; - } else { - return path; - } - }) as SimulationTypes.ConveyorEventsSchema[]; - - const updatedPath = updatedPaths.find( - (path) => path.type === "Conveyor" && path.points.some((point) => point.uuid === selectedActionSphere.points.uuid) - ); - - // console.log("Updated Path:", updatedPath); - - setSimulationStates(updatedPaths); - }; - useFrame(() => { if (eyeDropMode) { raycaster.setFromCamera(pointer, camera); @@ -445,7 +406,6 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec ref={transformRef} object={selectedActionSphere.points} mode={transformMode} - onMouseUp={updateSimulationPaths} /> )} diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx index f7f9974..068b5ce 100644 --- a/app/src/modules/simulation/process/processCreator.tsx +++ b/app/src/modules/simulation/process/processCreator.tsx @@ -458,7 +458,7 @@ import { useThree } from "@react-three/fiber"; import { ConveyorEventsSchema, VehicleEventsSchema, -} from "../../../types/simulation"; +} from "../../../types/simulationTypes"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; // Type definitions diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index b1f961e..f13fdf4 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -7,39 +7,50 @@ import ProcessContainer from "./process/processContainer"; import Agv from "../builder/agv/agv"; import ArmBot from "./armbot/ArmBot"; +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; +} + function Simulation() { - const { activeModule } = useModuleStore(); - const pathsGroupRef = useRef() as React.MutableRefObject; - const [processes, setProcesses] = useState([]); - const agvRef = useRef([]); - const MaterialRef = useRef([]); + const { activeModule } = useModuleStore(); + const pathsGroupRef = useRef() as React.MutableRefObject; + const [armBots, setArmBots] = useState([]); + const [processes, setProcesses] = useState([]); + const agvRef = useRef([]); + const MaterialRef = useRef([]); - return ( - <> - {activeModule === "simulation" && ( + return ( <> - + {activeModule === "simulation" && ( + <> + - + - + - + + + )} + - )} - - - ); + ); } export default Simulation; diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 0b15b3e..765712b 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -1,6 +1,6 @@ import * as THREE from "three"; import * as Types from "../types/world/worldTypes"; -import * as SimulationTypes from "../types/simulation"; +import * as SimulationTypes from "../types/simulationTypes"; import { create } from "zustand"; import { io } from "socket.io-client"; diff --git a/app/src/types/simulation.d.ts b/app/src/types/simulationTypes.d.ts similarity index 100% rename from app/src/types/simulation.d.ts rename to app/src/types/simulationTypes.d.ts