diff --git a/app/package-lock.json b/app/package-lock.json index 5d92f68..fa04c9f 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -30,6 +30,7 @@ "glob": "^11.0.0", "gsap": "^3.12.5", "html2canvas": "^1.4.1", + "immer": "^10.1.1", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", @@ -12746,9 +12747,10 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -18010,6 +18012,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react-dev-utils/node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/react-dev-utils/node_modules/loader-utils": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", diff --git a/app/package.json b/app/package.json index e555d5f..66158c0 100644 --- a/app/package.json +++ b/app/package.json @@ -25,6 +25,7 @@ "glob": "^11.0.0", "gsap": "^3.12.5", "html2canvas": "^1.4.1", + "immer": "^10.1.1", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 5477790..66e185a 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -73,7 +73,7 @@ const Assets: React.FC = () => { try { const filt = await fetchAssets(); setFiltereredAssets(filt); - } catch { } + } catch {} }; filteredAssets(); }, [categoryAssets]); @@ -135,7 +135,7 @@ const Assets: React.FC = () => { const res = await getCategoryAsset(asset); setCategoryAssets(res); setFiltereredAssets(res); - } catch (error) { } + } catch (error) {} } }; return ( @@ -151,7 +151,12 @@ const Assets: React.FC = () => {
{categoryAssets && categoryAssets?.map((asset: any, index: number) => ( -
+
{asset.filename} {
{categoryAssets && categoryAssets?.map((asset: any, index: number) => ( -
+
{asset.filename} + onPointerDown={() => { setSelectedItem({ name: asset.filename, id: asset.AssetID, - }) - } + type: + asset.type === "undefined" ? undefined : asset.type, + }); + }} />
diff --git a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx index e0b56d4..dc412f7 100644 --- a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx +++ b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx @@ -5,8 +5,8 @@ import Header from "./Header"; import useToggleStore from "../../../store/useUIToggleStore"; import Assets from "./Assets"; import useModuleStore from "../../../store/useModuleStore"; -import Widgets from "./visualization/widgets/Widgets"; -import Templates from "../../../modules/visualization/template/Templates"; +import Widgets from ".//visualization/widgets/Widgets"; +import Templates from "../../../modules//visualization/template/Templates"; import Search from "../../ui/inputs/Search"; const SideBarLeft: React.FC = () => { diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx index b5ae0bb..50d9712 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx @@ -5,11 +5,11 @@ import { GlobeIcon, WalletIcon, } from "../../../../icons/3dChartIcons"; -import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; +import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard"; -import WarehouseThroughput from "../../../../../modules/visualization/widgets/floating/cards/WarehouseThroughput"; -import ProductivityDashboard from "../../../../../modules/visualization/widgets/floating/cards/ProductivityDashboard"; -import FleetEfficiency from "../../../../../modules/visualization/widgets/floating/cards/FleetEfficiency"; +import WarehouseThroughput from "../../../../../modules//visualization/widgets/floating/cards/WarehouseThroughput"; +import ProductivityDashboard from "../../../../../modules//visualization/widgets/floating/cards/ProductivityDashboard"; +import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency"; interface Widget { id: string; diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index 6922314..f9f48fb 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { AppDockIcon } from "../../icons/HeaderIcons"; import orgImg from "../../../assets/orgTemp.png"; import { useActiveUsers } from "../../../store/store"; -import { getAvatarColor } from "../../../modules/collaboration/users/functions/getAvatarColor"; +import { getAvatarColor } from "../../../functions/users/functions/getAvatarColor"; import { ActiveUser } from "../../../types/users"; import CollaborationPopup from "../../templates/CollaborationPopup"; diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index f991478..277637a 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -10,33 +10,61 @@ import { SimulationIcon, } from "../../icons/SimulationIcons"; import useToggleStore from "../../../store/useUIToggleStore"; -import ConveyorMechanics from "./mechanics/ConveyorMechanics"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; -import { - useSelectedActionSphere, - useSelectedFloorItem, -} from "../../../store/store"; +import { useSelectedFloorItem } from "../../../store/store"; import GlobalProperties from "./properties/GlobalProperties"; import AsstePropertiies from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; -import VehicleMechanics from "./mechanics/VehicleMechanics"; -import StaticMachineMechanics from "./mechanics/StaticMachineMechanics"; -import ArmBotMechanics from "./mechanics/ArmBotMechanics"; +import EventProperties from "./properties/eventProperties/EventProperties"; const SideBarRight: React.FC = () => { const { activeModule } = useModuleStore(); const { toggleUI } = useToggleStore(); - const { selectedActionSphere } = useSelectedActionSphere(); const { subModule, setSubModule } = useSubModuleStore(); const { selectedFloorItem } = useSelectedFloorItem(); + // Reset activeList whenever activeModule changes useEffect(() => { if (activeModule !== "simulation") setSubModule("properties"); if (activeModule === "simulation") setSubModule("mechanics"); }, [activeModule]); + // romove late + const dummyData = { + assetType: "store", + selectedPoint: { + name: "Point A", + uuid: "123e4567-e89b-12d3-a456-426614174000", + actions: [ + { + uuid: "action-1", + name: "Action One", + }, + { + uuid: "action-2", + name: "Action Two", + }, + { + uuid: "action-3", + name: "Action Three", + }, + ], + }, + selectedItem: { + item: { + uuid: "item-1", + name: "Item One", + isUsed: false, + }, + }, + setSelectedPoint: (value: string) => { + console.log(`Selected point updated to: ${value}`); + }, + selectedActionSphere: "Sphere A", + }; + return (
@@ -44,8 +72,9 @@ const SideBarRight: React.FC = () => {
{/* {activeModule === "builder" && ( */}
setSubModule("properties")} > @@ -54,22 +83,25 @@ const SideBarRight: React.FC = () => { {activeModule === "simulation" && ( <>
setSubModule("mechanics")} >
setSubModule("simulations")} >
setSubModule("analysis")} > @@ -112,46 +144,10 @@ const SideBarRight: React.FC = () => { {toggleUI && activeModule === "simulation" && ( <> - {subModule === "mechanics" && - selectedActionSphere && - selectedActionSphere.path.type === "Conveyor" && ( -
-
- -
-
- )} - {subModule === "mechanics" && - selectedActionSphere && - selectedActionSphere.path.type === "Vehicle" && ( -
-
- -
-
- )} - {subModule === "mechanics" && - selectedActionSphere && - selectedActionSphere.path.type === "StaticMachine" && ( -
-
- -
-
- )} - {subModule === "mechanics" && - selectedActionSphere && - selectedActionSphere.path.type === "ArmBot" && ( -
-
- -
-
- )} - {subModule === "mechanics" && !selectedActionSphere && ( + {subModule === "mechanics" && (
- {/* default */} +
)} diff --git a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx deleted file mode 100644 index 70e297b..0000000 --- a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx +++ /dev/null @@ -1,411 +0,0 @@ -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/simulationTypes'; -import LabledDropdown from "../../../ui/inputs/LabledDropdown"; -import { handleResize } from "../../../../functions/handleResizePannel"; - -interface ConnectedModel { - modelUUID: string; - modelName: string; - points: { - uuid: string; - position: [number, number, number]; - index?: number; - }[]; - triggers?: { - uuid: string; - name: string; - type: string; - isUsed: boolean; - }[]; -} - -const ArmBotMechanics: React.FC = () => { - const { selectedActionSphere } = useSelectedActionSphere(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { socket } = useSocketStore(); - const [selectedProcessIndex, setSelectedProcessIndex] = useState(null); - const actionsContainerRef = useRef(null); - - // Get connected models and their triggers - const connectedModels = useMemo(() => { - if (!selectedActionSphere?.points?.uuid) return []; - - const armBotPaths = simulationStates.filter( - (path): path is SimulationTypes.ArmBotEventsSchema => path.type === "ArmBot" - ); - - const currentPoint = armBotPaths.find( - (path) => path.points.uuid === selectedActionSphere.points.uuid - )?.points; - - if (!currentPoint?.connections?.targets) return []; - - return currentPoint.connections.targets.reduce((acc, target) => { - const connectedModel = simulationStates.find( - (model) => model.modeluuid === target.modelUUID - ); - - if (!connectedModel) return acc; - - let triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] = []; - let points: { uuid: string; position: [number, number, number] }[] = []; - - if (connectedModel.type === "Conveyor") { - const conveyor = connectedModel as SimulationTypes.ConveyorEventsSchema; - - const connectedPointUUIDs = currentPoint?.connections?.targets - .filter(t => t.modelUUID === connectedModel.modeluuid) - .map(t => t.pointUUID) || []; - - points = conveyor.points - .map((point, idx) => ({ - uuid: point.uuid, - position: point.position, - index: idx - })) - .filter(point => connectedPointUUIDs.includes(point.uuid)); - - - triggers = conveyor.points.flatMap(p => p.triggers?.filter(t => t.isUsed) || []); - } - else if (connectedModel.type === "StaticMachine") { - const staticMachine = connectedModel as SimulationTypes.StaticMachineEventsSchema; - - points = [{ - uuid: staticMachine.points.uuid, - position: staticMachine.points.position - }]; - - triggers = staticMachine.points.triggers ? - [{ - uuid: staticMachine.points.triggers.uuid, - name: staticMachine.points.triggers.name, - type: staticMachine.points.triggers.type, - isUsed: true // StaticMachine triggers are always considered used - }] : []; - } - - if (!acc.some(m => m.modelUUID === connectedModel.modeluuid)) { - acc.push({ - modelUUID: connectedModel.modeluuid, - modelName: connectedModel.modelName, - points, - triggers - }); - } - - return acc; - }, []); - }, [selectedActionSphere, simulationStates]); - - // Get triggers from connected models - const connectedTriggers = useMemo(() => { - return connectedModels.flatMap(model => - (model.triggers || []).map(trigger => ({ - ...trigger, - displayName: `${model.modelName} - ${trigger.name}`, - modelUUID: model.modelUUID - })) - ); - }, [connectedModels]); - - // Get all points from connected models - const connectedPoints = useMemo(() => { - return connectedModels.flatMap(model => - model.points.map(point => ({ - ...point, - displayName: `${model.modelName} - Point${typeof point.index === 'number' ? ` ${point.index}` : ''}`, - modelUUID: model.modelUUID - })) - ); - }, [connectedModels]); - - - const { selectedPoint } = useMemo(() => { - if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null }; - - const armBotPaths = simulationStates.filter( - (path): path is SimulationTypes.ArmBotEventsSchema => path.type === "ArmBot" - ); - - const points = armBotPaths.find( - (path) => path.points.uuid === selectedActionSphere.points.uuid - )?.points; - - return { - selectedPoint: points || null - }; - }, [selectedActionSphere, simulationStates]); - - const updateBackend = async (updatedPath: SimulationTypes.ArmBotEventsSchema | undefined) => { - if (!updatedPath) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); - } - - const handleActionUpdate = useCallback((updatedAction: Partial) => { - if (!selectedActionSphere?.points?.uuid || !selectedPoint) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "ArmBot" && path.points.uuid === selectedActionSphere.points.uuid) { - return { - ...path, - points: { - ...path.points, - actions: { - ...path.points.actions, - ...updatedAction - } - } - }; - } - return path; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ArmBotEventsSchema => - path.type === "ArmBot" && - path.points.uuid === selectedActionSphere.points.uuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }, [selectedActionSphere?.points?.uuid, selectedPoint, simulationStates, setSimulationStates]); - - const handleSpeedChange = useCallback((speed: number) => { - handleActionUpdate({ speed }); - }, [handleActionUpdate]); - - const handleProcessChange = useCallback((processes: SimulationTypes.ArmBotEventsSchema['points']['actions']['processes']) => { - handleActionUpdate({ processes }); - }, [handleActionUpdate]); - - const handleAddProcess = useCallback(() => { - if (!selectedPoint) return; - - const newProcess: any = { - triggerId: "", - startPoint: "", - endPoint: "" - }; - - const updatedProcesses = selectedPoint.actions.processes ? [...selectedPoint.actions.processes, newProcess] : [newProcess]; - - handleProcessChange(updatedProcesses); - setSelectedProcessIndex(updatedProcesses.length - 1); - }, [selectedPoint, handleProcessChange]); - - const handleDeleteProcess = useCallback((index: number) => { - if (!selectedPoint?.actions.processes) return; - - const updatedProcesses = [...selectedPoint.actions.processes]; - updatedProcesses.splice(index, 1); - - handleProcessChange(updatedProcesses); - - // Reset selection if deleting the currently selected process - if (selectedProcessIndex === index) { - setSelectedProcessIndex(null); - } else if (selectedProcessIndex !== null && selectedProcessIndex > index) { - // Adjust selection index if needed - setSelectedProcessIndex(selectedProcessIndex - 1); - } - }, [selectedPoint, selectedProcessIndex, handleProcessChange]); - - const handleTriggerSelect = useCallback((displayName: string, index: number) => { - 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]; - updatedProcesses[index] = { - ...oldProcess, - triggerId: selected.uuid, - startPoint: oldProcess.startPoint || "", - endPoint: oldProcess.endPoint || "" - }; - - handleProcessChange(updatedProcesses); - }, [connectedTriggers, selectedPoint, handleProcessChange]); - - const handleStartPointSelect = useCallback((displayName: string, index: number) => { - if (!selectedPoint?.actions.processes) return; - - const point = connectedPoints.find(p => p.displayName === displayName); - if (!point) return; - - const updatedProcesses = [...selectedPoint.actions.processes]; - updatedProcesses[index] = { - ...updatedProcesses[index], - startPoint: point.uuid - }; - - handleProcessChange(updatedProcesses); - }, [selectedPoint, connectedPoints, handleProcessChange]); - - const handleEndPointSelect = useCallback((displayName: string, index: number) => { - if (!selectedPoint?.actions.processes) return; - - const point = connectedPoints.find(p => p.displayName === displayName); - if (!point) return; - - const updatedProcesses = [...selectedPoint.actions.processes]; - updatedProcesses[index] = { - ...updatedProcesses[index], - endPoint: point.uuid - }; - - handleProcessChange(updatedProcesses); - }, [selectedPoint, connectedPoints, handleProcessChange]); - - const getProcessByIndex = useCallback((index: number) => { - if (!selectedPoint?.actions.processes || index >= selectedPoint.actions.processes.length) return null; - return selectedPoint.actions.processes[index]; - }, [selectedPoint]); - - const getFilteredTriggerOptions = (currentIndex: number) => { - const usedTriggerUUIDs = selectedPoint?.actions.processes?.filter((_, i) => i !== currentIndex).map(p => p.triggerId).filter(Boolean) ?? []; - - return connectedTriggers.filter(trigger => !usedTriggerUUIDs.includes(trigger.uuid)).map(trigger => trigger.displayName); - }; - - return ( -
-
- {selectedActionSphere?.path?.modelName || "ArmBot point not found"} -
- -
-
-
ArmBot Properties
- - {selectedPoint && ( - <> - handleSpeedChange(parseFloat(value))} - /> - -
-
-
Processes
-
- Add -
-
-
-
- {selectedPoint.actions.processes?.map((process, index) => ( -
-
setSelectedProcessIndex(index)} - > - Process {index + 1} -
-
handleDeleteProcess(index)} - > - -
-
- ))} -
-
handleResize(e, actionsContainerRef)} - > - -
-
-
- - {selectedProcessIndex !== null && ( -
- - t.uuid === getProcessByIndex(selectedProcessIndex)?.triggerId - )?.displayName || 'Select a trigger' - } - onSelect={(value) => handleTriggerSelect(value, selectedProcessIndex)} - options={getFilteredTriggerOptions(selectedProcessIndex)} - /> - - - p.uuid === getProcessByIndex(selectedProcessIndex)?.startPoint - )?.displayName || 'Select start point' - } - onSelect={(value) => handleStartPointSelect(value, selectedProcessIndex)} - options={connectedPoints.map(point => point.displayName)} - /> - - - p.uuid === getProcessByIndex(selectedProcessIndex)?.endPoint - )?.displayName || 'Select end point' - } - onSelect={(value) => handleEndPointSelect(value, selectedProcessIndex)} - options={connectedPoints.map(point => point.displayName)} - /> -
- )} - - )} -
- -
- - Configure ArmBot properties and trigger-based processes. -
-
-
- ); -}; - -export default React.memo(ArmBotMechanics); \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx deleted file mode 100644 index 5e84d10..0000000 --- a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx +++ /dev/null @@ -1,879 +0,0 @@ -import React, { useRef, useState, useMemo, useEffect } from "react"; -import { - AddIcon, - InfoIcon, - RemoveIcon, - ResizeHeightIcon, -} from "../../../icons/ExportCommonIcons"; -import RenameInput from "../../../ui/inputs/RenameInput"; -import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import LabledDropdown from "../../../ui/inputs/LabledDropdown"; -import { handleResize } from "../../../../functions/handleResizePannel"; -import { - useFloorItems, - useSelectedActionSphere, - useSelectedPath, - useSimulationStates, - useSocketStore, -} from "../../../../store/store"; -import * as THREE from "three"; -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"; - -const ConveyorMechanics: React.FC = () => { - const { selectedActionSphere } = useSelectedActionSphere(); - const { selectedPath, setSelectedPath } = useSelectedPath(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { floorItems, setFloorItems } = useFloorItems(); - const { socket } = useSocketStore(); - - const actionsContainerRef = useRef(null); - const triggersContainerRef = useRef(null); - - const selectedPoint = useMemo(() => { - if (!selectedActionSphere) return null; - return simulationStates - .filter( - (path): path is SimulationTypes.ConveyorEventsSchema => path.type === "Conveyor" - ) - .flatMap((path) => path.points) - .find((point) => point.uuid === selectedActionSphere.points.uuid); - }, [selectedActionSphere, simulationStates]); - - const updateBackend = async (updatedPath: SimulationTypes.ConveyorEventsSchema | undefined) => { - if (!updatedPath) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - } - - socket.emit('v2:model-asset:updateEventData', data); - - } - - const handleAddAction = () => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Conveyor") { - return { - ...path, - points: path.points.map((point) => { - if (point.uuid === selectedActionSphere.points.uuid) { - const actionIndex = point.actions.length; - const newAction = { - uuid: THREE.MathUtils.generateUUID(), - name: `Action ${actionIndex + 1}`, - type: "Inherit", - material: "Inherit", - delay: "Inherit", - spawnInterval: "Inherit", - isUsed: false, - }; - - return { ...point, actions: [...point.actions, newAction] }; - } - return point; - }), - }; - } - return path; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - const handleDeleteAction = (uuid: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - actions: point.actions.filter( - (action) => action.uuid !== uuid - ), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - const handleActionSelect = (uuid: string, actionType: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.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 SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - - // Update the selected item to reflect changes - if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { - const updatedAction = updatedPaths - .filter( - (path): path is SimulationTypes.ConveyorEventsSchema => path.type === "Conveyor" - ) - .flatMap((path) => path.points) - .find((p) => p.uuid === selectedActionSphere.points.uuid) - ?.actions.find((a) => a.uuid === uuid); - - if (updatedAction) { - setSelectedItem({ - type: "action", - item: updatedAction, - }); - } - } - }; - - // Modified handleMaterialSelect to ensure it only applies to relevant action types - const handleMaterialSelect = (uuid: string, material: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.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 SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - - // Update selected item if it's the current action - if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - material, - }, - }); - } - }; - - const handleDelayChange = (uuid: string, delay: number | string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid ? { ...action, delay } : action - ), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - const handleSpawnIntervalChange = ( - uuid: string, - spawnInterval: number | string - ) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - actions: point.actions.map((action) => - action.uuid === uuid - ? { ...action, spawnInterval } - : action - ), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - const handleSpeedChange = (speed: number | string) => { - if (!selectedPath) return; - - const updatedPaths = simulationStates.map((path) => - path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.modeluuid === selectedPath.path.modeluuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } }); - }; - - const handleAddTrigger = () => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => { - if (point.uuid === selectedActionSphere.points.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; - }), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - const handleDeleteTrigger = (uuid: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - triggers: point.triggers.filter( - (trigger) => trigger.uuid !== uuid - ), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - const handleTriggerSelect = (uuid: string, triggerType: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid - ? { ...trigger, type: triggerType } - : trigger - ), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - - // Ensure the selectedItem is updated immediately - const updatedTrigger = updatedPaths - .flatMap((path) => (path.type === "Conveyor" ? path.points : [])) - .flatMap((point) => point.triggers) - .find((trigger) => trigger.uuid === uuid); - - if (updatedTrigger) { - setSelectedItem({ type: "trigger", item: updatedTrigger }); - } - }; - - // Update the toggle handlers to immediately update the selected item - const handleActionToggle = (uuid: string) => { - if (!selectedActionSphere) return; - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - actions: point.actions.map((action) => ({ - ...action, - isUsed: action.uuid === uuid ? !action.isUsed : false, - })), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - - // Immediately update the selected item if it's the one being toggled - if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - isUsed: !selectedItem.item.isUsed, - }, - }); - } - }; - - // Do the same for trigger toggle - const handleTriggerToggle = (uuid: string) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => ({ - ...trigger, - isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, - })), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - - // Immediately update the selected item if it's the one being toggled - if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - isUsed: !selectedItem.item.isUsed, - }, - }); - } - }; - - const handleTriggerBufferTimeChange = (uuid: string, bufferTime: number) => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => - path.type === "Conveyor" - ? { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - triggers: point.triggers.map((trigger) => - trigger.uuid === uuid - ? { ...trigger, bufferTime } - : trigger - ), - } - : point - ), - } - : path - ); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.ConveyorEventsSchema => - path.type === "Conveyor" && - path.points.some( - (point) => point.uuid === selectedActionSphere.points.uuid - ) - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - - // Immediately update selectedItem if it's the currently selected trigger - if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) { - setSelectedItem({ - ...selectedItem, - item: { - ...selectedItem.item, - bufferTime, - }, - }); - } - }; - - const [selectedItem, setSelectedItem] = useState<{ - type: "action" | "trigger"; - item: any; - } | null>(null); - - useEffect(() => { - setSelectedItem(null); - }, [selectedActionSphere]); - - return ( -
- {!selectedPath && ( -
- {selectedActionSphere?.path?.modelName || "point name not found"} -
- )} - - {selectedPath && ( -
- {selectedPath.path.modelName || "path name not found"} -
- )} - -
- {!selectedPath && ( - <> -
-
-
Actions
-
- Add -
-
-
-
- {selectedPoint?.actions.map((action) => ( -
-
- setSelectedItem({ type: "action", item: action }) - } - > - - -
-
handleDeleteAction(action.uuid)} - > - -
-
- ))} -
-
handleResize(e, actionsContainerRef)} - > - -
-
-
-
-
-
Triggers
-
- Add -
-
-
-
- {selectedPoint?.triggers.map((trigger) => ( -
-
- setSelectedItem({ type: "trigger", item: trigger }) - } - > - - -
-
handleDeleteTrigger(trigger.uuid)} - > - -
-
- ))} -
-
handleResize(e, triggersContainerRef)} - > - -
-
-
- - )} - -
- {selectedItem && ( - <> -
{selectedItem.item.name}
- - {selectedItem.type === "action" && ( - <> - handleActionToggle(selectedItem.item.uuid)} - /> - - handleActionSelect(selectedItem.item.uuid, option) - } - /> - - {/* Only show material dropdown for Spawn/Swap actions */} - {(selectedItem.item.type === "Spawn" || - selectedItem.item.type === "Swap") && ( - - handleMaterialSelect(selectedItem.item.uuid, option) - } - /> - )} - - {/* Only show delay input for Delay actions */} - {selectedItem.item.type === "Delay" && ( - { - const numValue = parseInt(value); - handleDelayChange( - selectedItem.item.uuid, - !value ? "Inherit" : numValue - ); - }} - /> - )} - - {/* Only show spawn interval for Spawn actions */} - {selectedItem.item.type === "Spawn" && ( - { - handleSpawnIntervalChange( - selectedItem.item.uuid, - value === "" ? "Inherit" : parseInt(value) - ); - }} - /> - )} - - )} - - {selectedItem.type === "trigger" && ( - <> - handleTriggerToggle(selectedItem.item.uuid)} - /> - - - handleTriggerSelect(selectedItem.item.uuid, option) - } - /> - - {selectedItem.item.type === "Buffer" && ( - { - handleTriggerBufferTimeChange( - selectedItem.item.uuid, - parseInt(value) - ); - }} - /> - )} - - )} - - )} - - {selectedPath && !selectedItem && ( -
- - handleSpeedChange(value === "" ? "Inherit" : parseInt(value)) - } - /> -
- )} -
- {!selectedPath && ( -
- - Configure the point's action and trigger properties. -
- )} - {selectedPath && ( -
- - Configure the path properties. -
- )} -
-
- ); -}; - -export default ConveyorMechanics; diff --git a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx deleted file mode 100644 index 6342bac..0000000 --- a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx +++ /dev/null @@ -1,188 +0,0 @@ -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/simulationTypes'; -import LabledDropdown from "../../../ui/inputs/LabledDropdown"; -import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; - -const StaticMachineMechanics: React.FC = () => { - const { selectedActionSphere } = useSelectedActionSphere(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { socket } = useSocketStore(); - - const propertiesContainerRef = useRef(null); - - const { selectedPoint, connectedPointUuids } = useMemo(() => { - if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] }; - - const staticMachinePaths = simulationStates.filter( - (path): path is SimulationTypes.StaticMachineEventsSchema => path.type === "StaticMachine" - ); - - const points = staticMachinePaths.find( - (path) => path.points.uuid === selectedActionSphere.points.uuid - )?.points; - - if (!points) return { selectedPoint: null, connectedPointUuids: [] }; - - const connectedUuids: string[] = []; - if (points.connections?.targets) { - points.connections.targets.forEach(target => { - connectedUuids.push(target.pointUUID); - }); - } - - return { - selectedPoint: points, - connectedPointUuids: connectedUuids - }; - }, [selectedActionSphere, simulationStates]); - - const updateBackend = async (updatedPath: SimulationTypes.StaticMachineEventsSchema | undefined) => { - if (!updatedPath) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); - } - - const handleActionUpdate = useCallback((updatedAction: Partial) => { - if (!selectedActionSphere?.points?.uuid) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) { - return { - ...path, - points: { - ...path.points, - actions: { - ...path.points.actions, - ...updatedAction - } - } - }; - } - return path; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.StaticMachineEventsSchema => - path.type === "StaticMachine" && - path.points.uuid === selectedActionSphere.points.uuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); - - const handleBufferChange = useCallback((buffer: number) => { - handleActionUpdate({ buffer }); - }, [handleActionUpdate]); - - const handleMaterialChange = useCallback((material: string) => { - handleActionUpdate({ material }); - }, [handleActionUpdate]); - - const handleTriggerChange = useCallback((updatedTrigger: Partial) => { - if (!selectedActionSphere?.points?.uuid) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) { - return { - ...path, - points: { - ...path.points, - triggers: { - ...path.points.triggers, - ...updatedTrigger - } - } - }; - } - return path; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.StaticMachineEventsSchema => - path.type === "StaticMachine" && - path.points.uuid === selectedActionSphere.points.uuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); - - const handleTriggerTypeChange = useCallback((type: string) => { - handleTriggerChange({ type }); - }, [handleTriggerChange]); - - return ( -
-
- {selectedActionSphere?.path?.modelName || "Machine point not found"} -
- - -
-
-
Machine Properties
- - {selectedPoint && ( - <> - handleBufferChange(parseInt(value))} - /> - - handleMaterialChange(value)} - options={["Inherit", "Crate", "Box"]} - /> - - handleTriggerTypeChange(value)} - options={["OnComplete", "OnStart"]} - /> - - {/* { - // Implement reset functionality if needed - }} - /> */} - - )} -
- -
- - Configure machine interaction properties and triggers. -
-
-
- ); -}; - -export default React.memo(StaticMachineMechanics); \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx deleted file mode 100644 index 147d5cb..0000000 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ /dev/null @@ -1,265 +0,0 @@ -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/simulationTypes'; -import PositionInput from "../customInput/PositionInputs"; -import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; -import LabeledButton from "../../../ui/inputs/LabledButton"; - -const VehicleMechanics: React.FC = () => { - const { selectedActionSphere } = useSelectedActionSphere(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); - const { editingPoint, setEditingPoint } = useEditingPoint(); - const { previewPosition, setPreviewPosition } = usePreviewPosition(); - const { socket } = useSocketStore(); - - const propertiesContainerRef = useRef(null); - - const { selectedPoint, connectedPointUuids } = useMemo(() => { - if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] }; - - const vehiclePaths = simulationStates.filter( - (path): path is SimulationTypes.VehicleEventsSchema => path.type === "Vehicle" - ); - - const points = vehiclePaths.find( - (path) => path.points.uuid === selectedActionSphere.points.uuid - )?.points; - - if (!points) return { selectedPoint: null, connectedPointUuids: [] }; - - const connectedUuids: string[] = []; - if (points.connections?.targets) { - points.connections.targets.forEach(target => { - connectedUuids.push(target.pointUUID); - }); - } - - return { - selectedPoint: points, - connectedPointUuids: connectedUuids - }; - }, [selectedActionSphere, simulationStates]); - - const updateBackend = async (updatedPath: SimulationTypes.VehicleEventsSchema | undefined) => { - if (!updatedPath) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); - - } - - const handleActionUpdate = React.useCallback((updatedAction: Partial) => { - if (!selectedActionSphere?.points?.uuid) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) { - return { - ...path, - points: { - ...path.points, - actions: { - ...path.points.actions, - ...updatedAction - } - } - }; - } - return path; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.VehicleEventsSchema => - path.type === "Vehicle" && - path.points.uuid === selectedActionSphere.points.uuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); - - const handleHitCountChange = React.useCallback((hitCount: number) => { - handleActionUpdate({ hitCount }); - }, [handleActionUpdate]); - - const handleBufferChange = React.useCallback((buffer: number) => { - handleActionUpdate({ buffer }); - }, [handleActionUpdate]); - - const handleSpeedChange = React.useCallback((speed: number) => { - if (!selectedActionSphere?.points?.uuid) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) { - return { - ...path, - points: { - ...path.points, - speed: speed - } - }; - } - return path; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.VehicleEventsSchema => - path.type === "Vehicle" && - path.points.uuid === selectedActionSphere.points.uuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); - - - const ResetVehicleState = React.useCallback(() => { - if (!selectedActionSphere?.points?.uuid) return; - - const updatedPaths = simulationStates.map((state) => { - if (state.type === "Vehicle" && state.points.uuid === selectedActionSphere.points.uuid) { - return { - ...state, - points: { - ...state.points, - actions: { ...state.points.actions, start: {}, end: {} } - } - }; - } - return state; - }); - - const updatedPath = updatedPaths.find( - (path): path is SimulationTypes.VehicleEventsSchema => - path.type === "Vehicle" && - path.points.uuid === selectedActionSphere.points.uuid - ); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]); - - const handleStartEyeDropClick = () => { - setEditingPoint('start'); - setEyeDropMode(true); - }; - - const handleEndEyeDropClick = () => { - setEditingPoint('end'); - setEyeDropMode(true); - }; - - return ( -
-
- {selectedActionSphere?.path?.modelName || "Vehicle point not found"} -
- -
-
-
Vehicle Properties
- - {selectedPoint && ( - <> - { }} - disabled={true} - value1={ - editingPoint === 'start' && previewPosition - ? parseFloat(previewPosition.x.toFixed(4)) - : selectedPoint.actions.start && 'x' in selectedPoint.actions.start - ? parseFloat(selectedPoint.actions.start.x.toFixed(4)) - : 0 - } - value2={ - editingPoint === 'start' && previewPosition - ? parseFloat(previewPosition.y.toFixed(4)) - : selectedPoint.actions.start && 'y' in selectedPoint.actions.start - ? parseFloat(selectedPoint.actions.start.y.toFixed(4)) - : 0 - } - - isEyedrop={true} - handleEyeDropClick={handleStartEyeDropClick} - /> - - { }} - disabled={true} - value1={ - editingPoint === 'end' && previewPosition - ? parseFloat(previewPosition.x.toFixed(4)) - : selectedPoint.actions.end && 'x' in selectedPoint.actions.end - ? parseFloat(selectedPoint.actions.end.x.toFixed(4)) - : 0 - } - value2={ - editingPoint === 'end' && previewPosition - ? parseFloat(previewPosition.y.toFixed(4)) - : selectedPoint.actions.end && 'y' in selectedPoint.actions.end - ? parseFloat(selectedPoint.actions.end.y.toFixed(4)) - : 0 - } - isEyedrop={true} - handleEyeDropClick={handleEndEyeDropClick} - /> - - { - ResetVehicleState(); - }} - /> - - handleHitCountChange(parseInt(value))} - /> - - handleBufferChange(parseInt(value))} - /> - - handleSpeedChange(parseFloat(value))} - /> - - )} -
- -
- - Configure vehicle's movement and interaction properties. -
-
-
- ); -}; - -export default React.memo(VehicleMechanics); \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx index ab1430c..a9f8cc0 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -1,9 +1,9 @@ import React, { useEffect, useState } from "react"; import RenameInput from "../../../ui/inputs/RenameInput"; import Vector3Input from "../customInput/Vector3Input"; -import { useSelectedZoneStore } from "../../../../store/useZoneStore"; +import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore"; import { useEditPosition, usezonePosition, useZones, usezoneTarget } from "../../../../store/store"; -import { zoneCameraUpdate } from "../../../../services/realTimeVisulization/zoneData/zoneCameraUpdation"; +import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCameraUpdation"; const ZoneProperties: React.FC = () => { const { Edit, setEdit } = useEditPosition(); diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx new file mode 100644 index 0000000..c3ec6e7 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx @@ -0,0 +1,210 @@ +import React, { useRef, useState } from "react"; +import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown"; +import LabledDropdown from "../../../../ui/inputs/LabledDropdown"; +import { + AddIcon, + RemoveIcon, + ResizeHeightIcon, +} from "../../../../icons/ExportCommonIcons"; +import RenameInput from "../../../../ui/inputs/RenameInput"; +import { handleResize } from "../../../../../functions/handleResizePannel"; +import { handleActionToggle } from "./functions/handleActionToggle"; +import { handleDeleteAction } from "./functions/handleDeleteAction"; +import DefaultAction from "./actions/DefaultAction"; +import SpawnAction from "./actions/SpawnAction"; +import SwapAction from "./actions/SwapAction"; +import DespawnAction from "./actions/DespawnAction"; +import TravelAction from "./actions/TravelAction"; +import PickAndPlaceAction from "./actions/PickAndPlaceAction"; +import ProcessAction from "./actions/ProcessAction"; +import StorageAction from "./actions/StorageAction"; +import Trigger from "./trigger/Trigger"; + +interface EventPropertiesProps { + assetType: string; + selectedPoint: { + name: string; + uuid: string; + actions: { + uuid: string; + name: string; + }[]; + }; + selectedItem: { + item: { + uuid: string; + name: string; + } | null; + }; + setSelectedPoint: (value: string) => void; + selectedActionSphere: string; +} + +const EventProperties: React.FC = ({ + assetType, + selectedPoint, + selectedItem, + setSelectedPoint, + selectedActionSphere, +}) => { + const actionsContainerRef = useRef(null); + + const [activeOption, setActiveOption] = useState("default"); + const [dummyactiveOption, setTypeOption] = useState("default"); + + const getAvailableActions = () => { + if (assetType === "conveyor") { + return { + defaultOption: "default", + options: ["default", "spawn", "swap", "despawn"], + }; + } + if (assetType === "vehicle") { + return { + defaultOption: "travel", + options: ["travel"], + }; + } + if (assetType === "roboticArm") { + return { + defaultOption: "pickAndPlace", + options: ["pickAndPlace"], + }; + } + if (assetType === "machine") { + return { + defaultOption: "process", + options: ["process"], + }; + } + if (assetType === "store") { + return { + defaultOption: "store", + options: ["store", "spawn"], + }; + } else { + return { + defaultOption: "default", + options: ["default"], + }; + } + }; + + return ( +
+
+
{selectedPoint.name}
+
+
+
+ {/*
+ setTypeOption(option)} + /> +
*/} +
+ {}} + onChange={(value) => console.log(value)} + /> +
+
+ {}} + onChange={(value) => console.log(value)} + /> +
+
+
+
+
+
+
Actions
+
{}}> + Add +
+
+
+
+ {selectedPoint?.actions.map((action) => ( +
+
handleActionToggle(action.uuid)} + > + +
+ {selectedPoint?.actions.length > 1 && ( +
handleDeleteAction(action.uuid)} + > + +
+ )} +
+ ))} +
+
handleResize(e, actionsContainerRef)} + > + +
+
+
+
+
+
+ +
+
+ setActiveOption(option)} + /> + {activeOption === "default" && } {/* done */} + {activeOption === "spawn" && } {/* done */} + {activeOption === "swap" && } {/* done */} + {activeOption === "despawn" && } {/* done */} + {activeOption === "travel" && } {/* done */} + {activeOption === "pickAndPlace" && } {/* done */} + {activeOption === "process" && } {/* done */} + {activeOption === "store" && } {/* done */} +
+
+
+ +
+
+ ); +}; + +export default EventProperties; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DefaultAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DefaultAction.tsx new file mode 100644 index 0000000..88f515e --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DefaultAction.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const DefaultAction:React.FC = () => { + return <>; +}; + +export default DefaultAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx new file mode 100644 index 0000000..a0f081f --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +const DespawnAction: React.FC = () => { + return ( + <> + {}} + onChange={(value) => console.log(value)} + /> + + ); +}; + +export default DespawnAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx new file mode 100644 index 0000000..9574669 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx @@ -0,0 +1,13 @@ +import React from "react"; +import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; + +const PickAndPlaceAction: React.FC = () => { + return ( + <> + {}} /> + {}} /> + + ); +}; + +export default PickAndPlaceAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx new file mode 100644 index 0000000..a27894e --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; +import SwapAction from "./SwapAction"; + +const ProcessAction: React.FC = () => { + return ( + <> + {}} + onChange={(value) => console.log(value)} + /> + + + ); +}; + +export default ProcessAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx new file mode 100644 index 0000000..0c37cdb --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +const SpawnAction: React.FC = () => { + return ( + <> + {}} + onChange={(value) => console.log(value)} + /> + {}} + onChange={(value) => console.log(value)} + /> + + + ); +}; + +export default SpawnAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx new file mode 100644 index 0000000..ab2109b --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +const StorageAction: React.FC = () => { + return ( + {}} + onChange={(value) => console.log(value)} + /> + ); +}; + +export default StorageAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx new file mode 100644 index 0000000..2e18d80 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload"; + +const SwapAction: React.FC = () => { + return ( + <> + + + ); +}; + +export default SwapAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx new file mode 100644 index 0000000..ee4bda0 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; +import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; + +const TravelAction: React.FC = () => { + return ( + <> + {}} + onChange={(value) => console.log(value)} + /> + {}} + onChange={(value) => console.log(value)} + /> + {}} /> + {}} /> + + ); +}; + +export default TravelAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleActionToggle.ts b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleActionToggle.ts new file mode 100644 index 0000000..96e986a --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleActionToggle.ts @@ -0,0 +1,6 @@ +export function handleActionToggle(uuid: string) { + // This function handles the action toggle for the event properties. + // It updates the selected action and its properties based on the provided UUID. + // The function is currently empty and needs to be implemented. + // You can add your logic here to handle the action toggle. +} \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleDeleteAction.ts b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleDeleteAction.ts new file mode 100644 index 0000000..6a367d5 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleDeleteAction.ts @@ -0,0 +1,6 @@ +export function handleDeleteAction(uuid: string) { + // This function handles the action toggle for the event properties. + // It updates the selected action and its properties based on the provided UUID. + // The function is currently empty and needs to be implemented. + // You can add your logic here to handle the action toggle. +} \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx new file mode 100644 index 0000000..f287b63 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -0,0 +1,107 @@ +import React, { useState } from "react"; +import { AddIcon, RemoveIcon } from "../../../../../icons/ExportCommonIcons"; +import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; + +const Trigger: React.FC = () => { + // State to hold the list of triggers + const [triggers, setTriggers] = useState([]); + const [activeOption, setActiveOption] = useState("onComplete"); + + // States for dropdowns + const [triggeredModel, setTriggeredModel] = useState([]); + const [triggeredPoint, setTriggeredPoint] = useState([]); + const [triggeredAction, setTriggeredAction] = useState([]); + + // Function to handle adding a new trigger + const addTrigger = (): void => { + const newTrigger = `Trigger ${triggers.length + 1}`; + setTriggers([...triggers, newTrigger]); // Add new trigger to the state + + // Initialize the states for the new trigger + setTriggeredModel([...triggeredModel, ""]); + setTriggeredPoint([...triggeredPoint, ""]); + setTriggeredAction([...triggeredAction, ""]); + }; + + // Function to handle removing a trigger + const removeTrigger = (index: number): void => { + setTriggers(triggers.filter((_, i) => i !== index)); // Remove trigger by index + setTriggeredModel(triggeredModel.filter((_, i) => i !== index)); + setTriggeredPoint(triggeredPoint.filter((_, i) => i !== index)); + setTriggeredAction(triggeredAction.filter((_, i) => i !== index)); + }; + + return ( +
+
+
Trigger
+
+ Add +
+
+
+ {/* Map over triggers and render them */} + {triggers.map((trigger, index) => ( +
+
+ {trigger} +
removeTrigger(index)} + style={{ cursor: "pointer" }} + > + +
+
+ setActiveOption(option)} + /> +
+
+ { + const newModel = [...triggeredModel]; + newModel[index] = option; + setTriggeredModel(newModel); + }} + /> +
+
+ { + const newPoint = [...triggeredPoint]; + newPoint[index] = option; + setTriggeredPoint(newPoint); + }} + /> +
+
+ { + const newAction = [...triggeredAction]; + newAction[index] = option; + setTriggeredAction(newAction); + }} + /> +
+
+
+ ))} +
+
+ ); +}; + +export default Trigger; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx index 9fb7b78..a729a9e 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx index 78b82c8..9c6c831 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index 271b4fc..0165257 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index 50330d1..cb51491 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -121,8 +121,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx index e7486f2..9dff7d6 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx index 797f63a..ed84458 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx index a7d2934..c97a380 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx index 764be54..d48f2ee 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx index 345de09..62edae4 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx index c41447a..7481622 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx index 20369bd..f8cdef6 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown"; import { AddIcon } from "../../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; -import useChartStore from "../../../../../store/useChartStore"; -import { useSelectedZoneStore } from "../../../../../store/useZoneStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; +import { useSelectedZoneStore } from "../../../../../store/visualization/useZoneStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; diff --git a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx index 234b936..04a569a 100644 --- a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx +++ b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx @@ -1,9 +1,9 @@ import { useState, useEffect, useRef } from "react"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent"; +import ChartComponent from "../../../sidebarLeft//visualization/widgets/ChartComponent"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; import { WalletIcon } from "../../../../icons/3dChartIcons"; -import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; +import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard"; interface Widget { id: string; diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 0f125ad..70b1461 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -15,10 +15,10 @@ import { } from "../icons/ExportToolsIcons"; import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons"; import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; -import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate"; +import { handleSaveTemplate } from "../../modules//visualization/functions/handleSaveTemplate"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import useTemplateStore from "../../store/useTemplateStore"; -import { useSelectedZoneStore } from "../../store/useZoneStore"; +import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; import { useActiveTool, useAddAction, @@ -38,7 +38,7 @@ import { use3DWidget, useDroppedObjectsStore, useFloatingWidget, -} from "../../store/useDroppedObjectsStore"; +} from "../../store/visualization/useDroppedObjectsStore"; const Tools: React.FC = () => { const { templates } = useTemplateStore(); diff --git a/app/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx index b672313..3d42917 100644 --- a/app/src/components/ui/inputs/InputWithDropDown.tsx +++ b/app/src/components/ui/inputs/InputWithDropDown.tsx @@ -5,6 +5,7 @@ type InputWithDropDownProps = { label: string; value: string; min?: number; + max?: number; step?: number; defaultValue?: string; options?: string[]; // Array of dropdown options @@ -19,6 +20,7 @@ const InputWithDropDown: React.FC = ({ label, value, min, + max, step, defaultValue, options, @@ -47,6 +49,7 @@ const InputWithDropDown: React.FC = ({
{ + const [showPreview, setSetshowPreview] = useState(false); + return ( +
+
setSetshowPreview(!showPreview)} + > +
Preview
+
+ +
+
+ {showPreview && ( +
+
+
+ )} +
+
+
Upload Product
+ + +
+ console.log(option)} + /> +
+
+ ); +}; + +export default PreviewSelectionWithUpload; diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index b2f05b9..0416a9e 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -3,8 +3,7 @@ import List from "./List"; import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons"; import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect"; import { useFloorItems, useZones } from "../../../store/store"; -import { useSelectedZoneStore } from "../../../store/useZoneStore"; -import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData"; +import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; interface DropDownListProps { value?: string; // Value to display in the DropDownList diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index 49e86f4..cac0b43 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from "react"; import RenameInput from "../inputs/RenameInput"; -import { useSelectedZoneStore } from "../../../store/useZoneStore"; -import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones"; +import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; +import { getZoneData } from "../../../services/visulization/zone/getZones"; import useModuleStore, { useSubModuleStore, } from "../../../store/useModuleStore"; @@ -14,7 +14,7 @@ import { } from "../../icons/ExportCommonIcons"; import { useThree } from "@react-three/fiber"; import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store"; -import { zoneCameraUpdate } from "../../../services/realTimeVisulization/zoneData/zoneCameraUpdation"; +import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation"; import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; interface Asset { diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index 1c1fa2e..6fd1e94 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -5,7 +5,7 @@ import { useRightClickSelected, useRightSelected, useTopData, -} from "../../../store/useZone3DWidgetStore"; +} from "../../../store/visualization/useZone3DWidgetStore"; interface EditWidgetOptionProps { options: string[]; diff --git a/app/src/modules/collaboration/collabUserIcon.tsx b/app/src/functions/collabUserIcon.tsx similarity index 95% rename from app/src/modules/collaboration/collabUserIcon.tsx rename to app/src/functions/collabUserIcon.tsx index a8738ce..9e6802b 100644 --- a/app/src/modules/collaboration/collabUserIcon.tsx +++ b/app/src/functions/collabUserIcon.tsx @@ -1,31 +1,31 @@ -import React from "react"; -import CustomAvatar from "./users/Avatar"; - -interface CollabUserIconProps { - userName: string; - userImage?: string; - color: string; -} - -const CollabUserIcon: React.FC = ({ - userImage, - userName, - color, -}) => { - return ( -
-
- {userImage ? ( - {userName} - ) : ( - - )} -
-
- {userName} -
-
- ); -}; - -export default CollabUserIcon; +import React from "react"; +import CustomAvatar from "./users/Avatar"; + +interface CollabUserIconProps { + userName: string; + userImage?: string; + color: string; +} + +const CollabUserIcon: React.FC = ({ + userImage, + userName, + color, +}) => { + return ( +
+
+ {userImage ? ( + {userName} + ) : ( + + )} +
+
+ {userName} +
+
+ ); +}; + +export default CollabUserIcon; diff --git a/app/src/modules/collaboration/users/Avatar.tsx b/app/src/functions/users/Avatar.tsx similarity index 100% rename from app/src/modules/collaboration/users/Avatar.tsx rename to app/src/functions/users/Avatar.tsx diff --git a/app/src/modules/collaboration/users/functions/getAvatarColor.ts b/app/src/functions/users/functions/getAvatarColor.ts similarity index 82% rename from app/src/modules/collaboration/users/functions/getAvatarColor.ts rename to app/src/functions/users/functions/getAvatarColor.ts index f2bd816..d9a5d37 100644 --- a/app/src/modules/collaboration/users/functions/getAvatarColor.ts +++ b/app/src/functions/users/functions/getAvatarColor.ts @@ -25,19 +25,8 @@ const avatarColors: string[] = [ export function getAvatarColor(index: number, name?: string): string { // Check if the color is already stored in localStorage const localStorageKey = "userAvatarColors"; - // Helper function to check if local storage is available - function isLocalStorageAvailable(): boolean { - try { - const testKey = "__test__"; - localStorage.setItem(testKey, "test"); - localStorage.removeItem(testKey); - return true; - } catch (e) { - return false; - } - } // Check if local storage is available - if (isLocalStorageAvailable() && name) { + if (name) { let userColors = JSON.parse(localStorage.getItem(localStorageKey) || "{}"); // Check if the user already has an assigned color diff --git a/app/src/modules/collaboration/users/functions/getInitials.ts b/app/src/functions/users/functions/getInitials.ts similarity index 100% rename from app/src/modules/collaboration/users/functions/getInitials.ts rename to app/src/functions/users/functions/getInitials.ts diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts similarity index 68% rename from app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts rename to app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index 7ca7db9..e88dc3c 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -1,270 +1,202 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; -import gsap from 'gsap'; -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/simulationTypes"; -import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; -import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; -import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; - -async function loadInitialFloorItems( - itemsGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState, - setSimulationStates: (paths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => void -): Promise { - if (!itemsGroup.current) return; - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - const email = localStorage.getItem('email'); - const organization = (email!.split("@")[1]).split(".")[0]; - - const items = await getFloorAssets(organization); - localStorage.setItem("FloorItems", JSON.stringify(items)); - await initializeDB(); - - if (items.message === "floorItems not found") return; - - if (items) { - const storedFloorItems: SimulationTypes.EventData[] = items; - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); - loader.setDRACOLoader(dracoLoader); - - let modelsLoaded = 0; - const modelsToLoad = storedFloorItems.length; - - const camData = await getCamera(organization, localStorage.getItem('userId')!); - let storedPosition; - if (camData && camData.position) { - storedPosition = camData?.position; - } else { - storedPosition = new THREE.Vector3(0, 40, 30); - } - if (!storedPosition) return; - const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); - - storedFloorItems.sort((a, b) => { - const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]); - const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]); - return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition); - }); - - for (const item of storedFloorItems) { - if (!item.modelfileID) return; - const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]); - let storedPosition; - if (localStorage.getItem("cameraPosition")) { - storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!); - } else { - storedPosition = new THREE.Vector3(0, 40, 30); - } - - const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); - - if (cameraPosition.distanceTo(itemPosition) < 50) { - await new Promise(async (resolve) => { - - // Check Three.js Cache - const cachedModel = THREE.Cache.get(item.modelfileID!); - if (cachedModel) { - // console.log(`[Cache] Fetching ${item.modelname}`); - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - return; - } - - // Check IndexedDB - const indexedDBModel = await retrieveGLTF(item.modelfileID!); - if (indexedDBModel) { - // console.log(`[IndexedDB] Fetching ${item.modelname}`); - const blobUrl = URL.createObjectURL(indexedDBModel); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - }, - undefined, - (error) => { - toast.error(`[IndexedDB] Error loading ${item.modelname}:`); - URL.revokeObjectURL(blobUrl); - resolve(); - } - ); - return; - } - - // Fetch from Backend - // console.log(`[Backend] Fetching ${item.modelname}`); - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; - loader.load(modelUrl, async (gltf) => { - 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, setSimulationStates); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - }, - undefined, - (error) => { - toast.error(`[Backend] Error loading ${item.modelname}:`); - resolve(); - } - ); - }); - } 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) { - processEventData(item, setSimulationStates); - } - - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); - } - } - - // Dispose loader after all models - dracoLoader.dispose(); - } -} - - -function processLoadedModel( - gltf: any, - item: SimulationTypes.EventData, - itemsGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState, - setSimulationStates: (paths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => void -) { - const model = gltf; - model.uuid = item.modeluuid; - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - 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); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - - itemsGroup?.current?.add(model); - - 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) { - processEventData(item, setSimulationStates); - } - - 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: SimulationTypes.EventData, setSimulationStates: 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]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - data as SimulationTypes.ConveyorEventsSchema - ]); - - } else if (item.eventData?.type === 'Vehicle') { - - 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]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - data as SimulationTypes.VehicleEventsSchema - ]); - - } else if (item.eventData?.type === 'StaticMachine') { - - 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]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - data as SimulationTypes.StaticMachineEventsSchema - ]); - - } else if (item.eventData?.type === 'ArmBot') { - - 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]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - data as SimulationTypes.ArmBotEventsSchema - ]); - - } -} - -function checkLoadingCompletion( - modelsLoaded: number, - modelsToLoad: number, - dracoLoader: DRACOLoader, - resolve: () => void -) { - if (modelsLoaded === modelsToLoad) { - toast.success("Models Loaded!"); - dracoLoader.dispose(); - } - resolve(); -} - +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; +import gsap from 'gsap'; +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 { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; +import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; +import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; + +async function loadInitialFloorItems( + itemsGroup: Types.RefGroup, + setFloorItems: Types.setFloorItemSetState, +): Promise { + if (!itemsGroup.current) return; + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + const email = localStorage.getItem('email'); + const organization = (email!.split("@")[1]).split(".")[0]; + + const items = await getFloorAssets(organization); + 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(); + const dracoLoader = new DRACOLoader(); + + dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + loader.setDRACOLoader(dracoLoader); + + let modelsLoaded = 0; + const modelsToLoad = storedFloorItems.length; + + const camData = await getCamera(organization, localStorage.getItem('userId')!); + let storedPosition; + if (camData && camData.position) { + storedPosition = camData?.position; + } else { + storedPosition = new THREE.Vector3(0, 40, 30); + } + if (!storedPosition) return; + const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); + + storedFloorItems.sort((a, b) => { + const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]); + const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]); + return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition); + }); + + for (const item of storedFloorItems) { + if (!item.modelfileID) return; + const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]); + let storedPosition; + if (localStorage.getItem("cameraPosition")) { + storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!); + } else { + storedPosition = new THREE.Vector3(0, 40, 30); + } + + const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); + + if (cameraPosition.distanceTo(itemPosition) < 50) { + await new Promise(async (resolve) => { + + // Check Three.js Cache + const cachedModel = THREE.Cache.get(item.modelfileID!); + if (cachedModel) { + // console.log(`[Cache] Fetching ${item.modelname}`); + processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems); + modelsLoaded++; + checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); + return; + } + + // Check IndexedDB + const indexedDBModel = await retrieveGLTF(item.modelfileID!); + if (indexedDBModel) { + // console.log(`[IndexedDB] Fetching ${item.modelname}`); + const blobUrl = URL.createObjectURL(indexedDBModel); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(item.modelfileID!, gltf); + processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); + modelsLoaded++; + checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); + }, + undefined, + (error) => { + toast.error(`[IndexedDB] Error loading ${item.modelname}:`); + URL.revokeObjectURL(blobUrl); + resolve(); + } + ); + return; + } + + // Fetch from Backend + // console.log(`[Backend] Fetching ${item.modelname}`); + const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; + loader.load(modelUrl, async (gltf) => { + 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); + modelsLoaded++; + checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); + }, + undefined, + (error) => { + toast.error(`[Backend] Error loading ${item.modelname}:`); + resolve(); + } + ); + }); + } 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, + }, + ]); + + modelsLoaded++; + checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); + } + } + + // Dispose loader after all models + dracoLoader.dispose(); + } +} + + +function processLoadedModel( + gltf: any, + item: Types.FloorItemType, + itemsGroup: Types.RefGroup, + setFloorItems: Types.setFloorItemSetState, +) { + const model = gltf; + model.uuid = item.modeluuid; + model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); + 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); + + model.traverse((child: any) => { + if (child.isMesh) { + // Clone the material to ensure changes are independent + // child.material = child.material.clone(); + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + + itemsGroup?.current?.add(model); + + setFloorItems((prevItems) => [ + ...(prevItems || []), + { + modeluuid: item.modeluuid, + modelname: item.modelname, + position: item.position, + rotation: item.rotation, + modelfileID: item.modelfileID, + isLocked: item.isLocked, + isVisible: item.isVisible, + }, + ]); + + 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 checkLoadingCompletion( + modelsLoaded: number, + modelsToLoad: number, + dracoLoader: DRACOLoader, + resolve: () => void +) { + if (modelsLoaded === modelsToLoad) { + toast.success("Models Loaded!"); + dracoLoader.dispose(); + } + resolve(); +} + export default loadInitialFloorItems; \ No newline at end of file diff --git a/app/src/modules/scene/IntialLoad/loadInitialLine.ts b/app/src/modules/builder/IntialLoad/loadInitialLine.ts similarity index 97% rename from app/src/modules/scene/IntialLoad/loadInitialLine.ts rename to app/src/modules/builder/IntialLoad/loadInitialLine.ts index 231d5aa..f8c3132 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialLine.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialLine.ts @@ -1,30 +1,30 @@ -import addLineToScene from '../../builder/geomentries/lines/addLineToScene'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import * as Types from "../../../types/world/worldTypes"; - -function loadInitialLine( - floorPlanGroupLine: Types.RefGroup, - lines: Types.RefLines -): void { - - if (!floorPlanGroupLine.current) return - - ////////// Load the Lines initially if there are any ////////// - - floorPlanGroupLine.current.children = []; - lines.current.forEach((line) => { - let colour; - if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName) { - colour = CONSTANTS.lineConfig.wallColor; - } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName) { - colour = CONSTANTS.lineConfig.floorColor; - } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName) { - colour = CONSTANTS.lineConfig.aisleColor; - } - if (colour) { - addLineToScene(line[0][0], line[1][0], colour, line, floorPlanGroupLine); - } - }); -} - -export default loadInitialLine; +import addLineToScene from '../../builder/geomentries/lines/addLineToScene'; +import * as CONSTANTS from '../../../types/world/worldConstants'; +import * as Types from "../../../types/world/worldTypes"; + +function loadInitialLine( + floorPlanGroupLine: Types.RefGroup, + lines: Types.RefLines +): void { + + if (!floorPlanGroupLine.current) return + + ////////// Load the Lines initially if there are any ////////// + + floorPlanGroupLine.current.children = []; + lines.current.forEach((line) => { + let colour; + if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName) { + colour = CONSTANTS.lineConfig.wallColor; + } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName) { + colour = CONSTANTS.lineConfig.floorColor; + } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName) { + colour = CONSTANTS.lineConfig.aisleColor; + } + if (colour) { + addLineToScene(line[0][0], line[1][0], colour, line, floorPlanGroupLine); + } + }); +} + +export default loadInitialLine; diff --git a/app/src/modules/scene/IntialLoad/loadInitialPoint.ts b/app/src/modules/builder/IntialLoad/loadInitialPoint.ts similarity index 97% rename from app/src/modules/scene/IntialLoad/loadInitialPoint.ts rename to app/src/modules/builder/IntialLoad/loadInitialPoint.ts index f042427..7dfdf1d 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialPoint.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialPoint.ts @@ -1,87 +1,87 @@ -import * as THREE from 'three'; - -import * as CONSTANTS from '../../../types/world/worldConstants'; -import * as Types from "../../../types/world/worldTypes"; - -////////// Load the Boxes initially if there are any ////////// - -function loadInitialPoint( - lines: Types.RefLines, - floorPlanGroupPoint: Types.RefGroup, - currentLayerPoint: Types.RefMeshArray, - dragPointControls: Types.RefDragControl -): void { - - if (!floorPlanGroupPoint.current) return - - floorPlanGroupPoint.current.children = []; - currentLayerPoint.current = []; - lines.current.forEach((line) => { - const colour = getPointColor(line[0][3]); - line.forEach((pointData) => { - const [point, id] = pointData; - - /////////// Check if a box with this id already exists ////////// - - const existingBox = floorPlanGroupPoint.current?.getObjectByProperty('uuid', id); - if (existingBox) { - return; - } - - const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); - const material = new THREE.ShaderMaterial({ - uniforms: { - uColor: { value: new THREE.Color(colour) }, // Blue color for the border - uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square - }, - vertexShader: ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uColor; - uniform vec3 uInnerColor; - - void main() { - // Define the size of the white square as a proportion of the face - float borderThickness = 0.2; // Adjust this value for border thickness - if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && - vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { - gl_FragColor = vec4(uInnerColor, 1.0); // White inner square - } else { - gl_FragColor = vec4(uColor, 1.0); // Blue border - } - } - `, - }); - const box = new THREE.Mesh(geometry, material); - box.name = "point"; - box.uuid = id; - box.userData = { type: line[0][3], color: colour }; - box.position.set(point.x, point.y, point.z); - currentLayerPoint.current.push(box); - - floorPlanGroupPoint.current?.add(box); - }); - }); - - function getPointColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; - case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; - case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; - default: return CONSTANTS.pointConfig.defaultOuterColor; - } - } - - if (dragPointControls.current) { - dragPointControls.current!.objects = currentLayerPoint.current; - } -} - -export default loadInitialPoint; +import * as THREE from 'three'; + +import * as CONSTANTS from '../../../types/world/worldConstants'; +import * as Types from "../../../types/world/worldTypes"; + +////////// Load the Boxes initially if there are any ////////// + +function loadInitialPoint( + lines: Types.RefLines, + floorPlanGroupPoint: Types.RefGroup, + currentLayerPoint: Types.RefMeshArray, + dragPointControls: Types.RefDragControl +): void { + + if (!floorPlanGroupPoint.current) return + + floorPlanGroupPoint.current.children = []; + currentLayerPoint.current = []; + lines.current.forEach((line) => { + const colour = getPointColor(line[0][3]); + line.forEach((pointData) => { + const [point, id] = pointData; + + /////////// Check if a box with this id already exists ////////// + + const existingBox = floorPlanGroupPoint.current?.getObjectByProperty('uuid', id); + if (existingBox) { + return; + } + + const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); + const material = new THREE.ShaderMaterial({ + uniforms: { + uColor: { value: new THREE.Color(colour) }, // Blue color for the border + uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square + }, + vertexShader: ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + varying vec2 vUv; + uniform vec3 uColor; + uniform vec3 uInnerColor; + + void main() { + // Define the size of the white square as a proportion of the face + float borderThickness = 0.2; // Adjust this value for border thickness + if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && + vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { + gl_FragColor = vec4(uInnerColor, 1.0); // White inner square + } else { + gl_FragColor = vec4(uColor, 1.0); // Blue border + } + } + `, + }); + const box = new THREE.Mesh(geometry, material); + box.name = "point"; + box.uuid = id; + box.userData = { type: line[0][3], color: colour }; + box.position.set(point.x, point.y, point.z); + currentLayerPoint.current.push(box); + + floorPlanGroupPoint.current?.add(box); + }); + }); + + function getPointColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; + case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; + case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; + default: return CONSTANTS.pointConfig.defaultOuterColor; + } + } + + if (dragPointControls.current) { + dragPointControls.current!.objects = currentLayerPoint.current; + } +} + +export default loadInitialPoint; diff --git a/app/src/modules/scene/IntialLoad/loadInitialWallItems.ts b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts similarity index 97% rename from app/src/modules/scene/IntialLoad/loadInitialWallItems.ts rename to app/src/modules/builder/IntialLoad/loadInitialWallItems.ts index c5184bb..34273af 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialWallItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts @@ -1,54 +1,54 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; - -import * as Types from "../../../types/world/worldTypes"; -import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi'; - -////////// Load the Wall Items's intially of there is any ////////// - -async function loadInitialWallItems( - setWallItems: Types.setWallItemSetState, - AssetConfigurations: Types.AssetConfigurations -): Promise { - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - const items = await getWallItems(organization); - - localStorage.setItem("WallItems", JSON.stringify(items)); - if (items.length > 0) { - const storedWallItems: Types.wallItems = items; - - const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { - const loader = new GLTFLoader(); - return new Promise((resolve) => { - loader.load(AssetConfigurations[item.modelname!].modelUrl, (gltf) => { - const model = gltf.scene; - model.uuid = item.modeluuid!; - - model.children[0].children.forEach((child: any) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } - }); - - resolve({ - type: item.type, - model: model, - modelname: item.modelname, - scale: item.scale, - csgscale: item.csgscale, - csgposition: item.csgposition, - position: item.position, - quaternion: item.quaternion, - }); - }); - }); - })); - - setWallItems(loadedWallItems); - } -} - -export default loadInitialWallItems; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; + +import * as Types from "../../../types/world/worldTypes"; +import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi'; + +////////// Load the Wall Items's intially of there is any ////////// + +async function loadInitialWallItems( + setWallItems: Types.setWallItemSetState, + AssetConfigurations: Types.AssetConfigurations +): Promise { + + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const items = await getWallItems(organization); + + localStorage.setItem("WallItems", JSON.stringify(items)); + if (items.length > 0) { + const storedWallItems: Types.wallItems = items; + + const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { + const loader = new GLTFLoader(); + return new Promise((resolve) => { + loader.load(AssetConfigurations[item.modelname!].modelUrl, (gltf) => { + const model = gltf.scene; + model.uuid = item.modeluuid!; + + model.children[0].children.forEach((child: any) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + resolve({ + type: item.type, + model: model, + modelname: item.modelname, + scale: item.scale, + csgscale: item.csgscale, + csgposition: item.csgposition, + position: item.position, + quaternion: item.quaternion, + }); + }); + }); + })); + + setWallItems(loadedWallItems); + } +} + +export default loadInitialWallItems; diff --git a/app/src/modules/builder/agv/agv.tsx b/app/src/modules/builder/agv/agv.tsx deleted file mode 100644 index 0a2d14e..0000000 --- a/app/src/modules/builder/agv/agv.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import { Line } from "@react-three/drei"; -import { - useNavMesh, - usePlayAgv, - useSimulationStates, -} from "../../../store/store"; -import PathNavigator from "./pathNavigator"; -import { useAnimationPlaySpeed, usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore"; - -type PathPoints = { - modelUuid: string; - modelSpeed: number; - bufferTime: number; - points: { x: number; y: number; z: number }[]; - hitCount: number; -}; -interface ProcessContainerProps { - processes: any[]; - agvRef: any; - MaterialRef: any; -} - -const Agv: React.FC = ({ - processes, - agvRef, - MaterialRef, -}) => { - const [pathPoints, setPathPoints] = useState([]); - const { simulationStates } = useSimulationStates(); - const { navMesh } = useNavMesh(); - const { isPlaying } = usePlayButtonStore(); - const { isReset, setReset } = useResetButtonStore(); - const { speed } = useAnimationPlaySpeed(); - const globalSpeed = useRef(1); - - useEffect(() => { globalSpeed.current = speed }, [speed]) - - useEffect(() => { - if (!isPlaying || isReset) { - agvRef.current = []; - } - }, [isPlaying, isReset]) - - useEffect(() => { - if (simulationStates.length > 0) { - const agvModels = simulationStates.filter( - (val) => val.modelName === "agv" && val.type === "Vehicle" - ); - - const newPathPoints = agvModels - .filter( - (model: any) => - model.points && - model.points.actions && - typeof model.points.actions.start === "object" && - typeof model.points.actions.end === "object" && - "x" in model.points.actions.start && - "y" in model.points.actions.start && - "x" in model.points.actions.end && - "y" in model.points.actions.end - ) - .map((model: any) => ({ - modelUuid: model.modeluuid, - modelSpeed: model.points.speed, - bufferTime: model.points.actions.buffer, - hitCount: model.points.actions.hitCount, - points: [ - { x: model.position[0], y: model.position[1], z: model.position[2], }, - { x: model.points.actions.start.x, y: 0, z: model.points.actions.start.y, }, - { x: model.points.actions.end.x, y: 0, z: model.points.actions.end.y, }, - ], - })); - - setPathPoints(newPathPoints); - } - }, [simulationStates]); - - return ( - <> - {pathPoints.map((pair, i) => ( - - - - {pair.points.slice(1).map((point, idx) => ( - - - - - ))} - - ))} - - ); -}; - -export default Agv; diff --git a/app/src/modules/builder/agv/navMeshDetails.tsx b/app/src/modules/builder/agv/navMeshDetails.tsx deleted file mode 100644 index 697d89b..0000000 --- a/app/src/modules/builder/agv/navMeshDetails.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { init as initRecastNavigation } from "@recast-navigation/core"; -import { generateSoloNavMesh } from "@recast-navigation/generators"; -import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three"; -import { useThree } from "@react-three/fiber"; -import * as THREE from "three"; -import * as Types from "../../../types/world/worldTypes"; - -interface NavMeshDetailsProps { - setNavMesh: (navMesh: any) => void; - groupRef: React.MutableRefObject; - lines: Types.RefLines; -} - -export default function NavMeshDetails({ - lines, - setNavMesh, - groupRef, -}: NavMeshDetailsProps) { - const { scene } = useThree(); - - useEffect(() => { - const initializeNavigation = async () => { - try { - await initRecastNavigation(); - - if (!groupRef.current || groupRef.current.children.length === 0) { - return; - } - - const meshes = groupRef?.current?.children as THREE.Mesh[]; - - const [positions, indices] = getPositionsAndIndices(meshes); - - const cellSize = 0.2; - const cellHeight = 0.7; - const walkableRadius = 0.5; - const { success, navMesh } = generateSoloNavMesh(positions, indices, { - cs: cellSize, - ch: cellHeight, - walkableRadius: Math.round(walkableRadius / cellHeight), - }); - - if (!success || !navMesh) { - return; - } - - setNavMesh(navMesh); - - scene.children - .filter((child) => child instanceof DebugDrawer) - .forEach((child) => scene.remove(child)); - - const debugDrawer = new DebugDrawer(); - debugDrawer.drawNavMesh(navMesh); - // scene.add(debugDrawer); - } catch (error) { } - }; - - initializeNavigation(); - }, [scene, groupRef, lines.current]); - - return null; -} diff --git a/app/src/modules/builder/agv/pathNavigator.tsx b/app/src/modules/builder/agv/pathNavigator.tsx deleted file mode 100644 index a82b53c..0000000 --- a/app/src/modules/builder/agv/pathNavigator.tsx +++ /dev/null @@ -1,480 +0,0 @@ -import React, { useEffect, useState, useRef, useMemo } from "react"; -import * as THREE from "three"; -import { useFrame, useThree } from "@react-three/fiber"; -import { NavMeshQuery } from "@recast-navigation/core"; -import { Line } from "@react-three/drei"; -import { - useAnimationPlaySpeed, - usePlayButtonStore, -} from "../../../store/usePlayButtonStore"; -import { usePlayAgv } from "../../../store/store"; - -interface PathNavigatorProps { - navMesh: any; - pathPoints: any; - id: string; - speed: number; - globalSpeed: number; - bufferTime: number; - hitCount: number; - processes: any[]; - agvRef: any; - MaterialRef: any; -} -interface AGVData { - processId: string; - vehicleId: string; - hitCount: number; - totalHits: number; -} -type Phase = "initial" | "toDrop" | "toPickup"; -type MaterialType = "Box" | "Crate"; -export default function PathNavigator({ - navMesh, - pathPoints, - id, - speed, - globalSpeed, - bufferTime, - hitCount, - processes, - agvRef, - MaterialRef, -}: PathNavigatorProps) { - const [currentPhase, setCurrentPhase] = useState("initial"); - const [path, setPath] = useState<[number, number, number][]>([]); - const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>( - [] - ); - const [pickupDropPath, setPickupDropPath] = useState< - [number, number, number][] - >([]); - const [dropPickupPath, setDropPickupPath] = useState< - [number, number, number][] - >([]); - const [initialPosition, setInitialPosition] = useState( - null - ); - const [initialRotation, setInitialRotation] = useState( - null - ); - const [boxVisible, setBoxVisible] = useState(false); - - const distancesRef = useRef([]); - const totalDistanceRef = useRef(0); - const progressRef = useRef(0); - const isWaiting = useRef(false); - const timeoutRef = useRef(null); - const hasStarted = useRef(false); - const hasReachedPickup = useRef(false); - - const { scene } = useThree(); - const { isPlaying } = usePlayButtonStore(); - const { PlayAgv, setPlayAgv } = usePlayAgv(); - - const boxRef = useRef(null); - - const baseMaterials = useMemo( - () => ({ - Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), - Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }), - Default: new THREE.MeshStandardMaterial({ color: 0xcccccc }), - }), - [] - ); - - useEffect(() => { - const object = scene.getObjectByProperty("uuid", id); - if (object) { - setInitialPosition(object.position.clone()); - setInitialRotation(object.rotation.clone()); - } - }, [scene, id]); - - const computePath = (start: any, end: any) => { - try { - const navMeshQuery = new NavMeshQuery(navMesh); - const { path: segmentPath } = navMeshQuery.computePath(start, end); - return ( - segmentPath?.map( - ({ x, y, z }) => [x, y + 0.1, z] as [number, number, number] - ) || [] - ); - } catch { - return []; - } - }; - - const resetState = () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - timeoutRef.current = null; - } - - setPath([]); - setCurrentPhase("initial"); - setToPickupPath([]); - setPickupDropPath([]); - setDropPickupPath([]); - setBoxVisible(false); - distancesRef.current = []; - totalDistanceRef.current = 0; - progressRef.current = 0; - isWaiting.current = false; - hasStarted.current = false; - hasReachedPickup.current = false; - - if (initialPosition && initialRotation) { - const object = scene.getObjectByProperty("uuid", id); - if (object) { - object.position.copy(initialPosition); - object.rotation.copy(initialRotation); - } - } - }; - - useEffect(() => { - if (!isPlaying) { - resetState(); - } - - if (!navMesh || pathPoints.length < 2) return; - - const [pickup, drop] = pathPoints.slice(-2); - - const object = scene.getObjectByProperty("uuid", id); - if (!object) return; - - const currentPosition = object.position; - - const toPickupPath = computePath(currentPosition, pickup); - const pickupToDropPath = computePath(pickup, drop); - const dropToPickupPath = computePath(drop, pickup); - - if ( - toPickupPath.length && - pickupToDropPath.length && - dropToPickupPath.length - ) { - setPickupDropPath(pickupToDropPath); - setDropPickupPath(dropToPickupPath); - setToPickupPath(toPickupPath); - setPath(toPickupPath); - setCurrentPhase("initial"); - } - }, [navMesh, pathPoints, hitCount, isPlaying, PlayAgv]); - - useEffect(() => { - if (path.length < 2) return; - - let total = 0; - const segmentDistances = path.slice(0, -1).map((point, i) => { - const dist = new THREE.Vector3(...point).distanceTo( - new THREE.Vector3(...path[i + 1]) - ); - total += dist; - return dist; - }); - - distancesRef.current = segmentDistances; - totalDistanceRef.current = total; - progressRef.current = 0; - isWaiting.current = false; - }, [path]); - - function logAgvStatus(id: string, status: string) { - // console.log( - // `AGV ${id}: ${status}` - - // ); - } - - function findProcessByTargetModelUUID(processes: any, targetModelUUID: any) { - for (const process of processes) { - for (const path of process.paths) { - for (const point of path.points) { - if ( - point.connections?.targets?.some( - (target: any) => target.modelUUID === targetModelUUID - ) - ) { - return process.id; - } - } - } - } - return null; - } - - useEffect(() => { - if (!scene || !boxRef || !processes || !MaterialRef.current) return; - - const existingObject = scene.getObjectByProperty("uuid", id); - if (!existingObject) return; - - if (boxRef.current?.parent) { - boxRef.current.parent.remove(boxRef.current); - boxRef.current = null; - } - - if (boxVisible) { - const matchedProcess = findProcessByTargetModelUUID(processes, id); - let materialType: "Box" | "Crate" | "Default" = "Default"; - - if (matchedProcess) { - const materialEntry = MaterialRef.current.find((item: any) => - item.objects.some((obj: any) => obj.processId === matchedProcess) - ); - if (materialEntry) { - materialType = materialEntry.material; - } - } - - const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5); - const boxMesh = new THREE.Mesh(boxGeometry, baseMaterials[materialType]); - boxMesh.position.y = 1; - boxMesh.name = `box-${id}`; - existingObject.add(boxMesh); - boxRef.current = boxMesh; - } - - return () => { - if (boxRef.current?.parent) { - boxRef.current.parent.remove(boxRef.current); - } - }; - }, [processes, MaterialRef, boxVisible, scene, id, baseMaterials]); - - useFrame((_, delta) => { - const currentAgv = (agvRef.current || []).find( - (agv: AGVData) => agv.vehicleId === id - ); - - if (!scene || !id || !isPlaying) return; - - const object = scene.getObjectByProperty("uuid", id); - if (!object) return; - - if (isPlaying && !hasStarted.current) { - hasStarted.current = false; - progressRef.current = 0; - isWaiting.current = false; - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - timeoutRef.current = null; - } - } - - const isAgvReady = () => { - if (!agvRef.current || agvRef.current.length === 0) return false; - if (!currentAgv) return false; - - return currentAgv.isActive && hitCount >= currentAgv.maxHitCount; - }; - - if (isPlaying && !hasStarted.current && toPickupPath.length > 0) { - setBoxVisible(false); - const startPoint = new THREE.Vector3(...toPickupPath[0]); - object.position.copy(startPoint); - - if (toPickupPath.length > 1) { - const nextPoint = new THREE.Vector3(...toPickupPath[1]); - const direction = nextPoint.clone().sub(startPoint).normalize(); - object.rotation.y = Math.atan2(direction.x, direction.z); - } - - hasStarted.current = true; - progressRef.current = 0; - hasReachedPickup.current = false; - setToPickupPath(toPickupPath.slice(-1)); - logAgvStatus(id, "Started from station, heading to pickup"); - return; - } - - if (isPlaying && currentPhase === "initial" && !hasReachedPickup.current) { - const reached = moveAlongPath( - object, - path, - distancesRef.current, - speed, - delta, - progressRef - ); - - if (reached) { - hasReachedPickup.current = true; - if (currentAgv) { - currentAgv.status = "picking"; - } - logAgvStatus(id, "Reached pickup point, Waiting for material"); - } - return; - } - - if (isPlaying && currentAgv?.isActive && currentPhase === "initial") { - if (!isAgvReady()) return; - setTimeout(() => { - setBoxVisible(true); - setPath([...pickupDropPath]); - setCurrentPhase("toDrop"); - progressRef.current = 0; - logAgvStatus(id, "Started from pickup point, heading to drop point"); - if (currentAgv) { - currentAgv.status = "toDrop"; - } - }, 0); - return; - } - - if (isPlaying && currentPhase === "toDrop") { - const reached = moveAlongPath( - object, - path, - distancesRef.current, - speed, - delta, - progressRef - ); - - if (reached && !isWaiting.current) { - isWaiting.current = true; - logAgvStatus(id, "Reached drop point"); - if (currentAgv) { - currentAgv.status = "droping"; - currentAgv.hitCount = currentAgv.hitCount--; - } - timeoutRef.current = setTimeout(() => { - setPath([...dropPickupPath]); - setCurrentPhase("toPickup"); - progressRef.current = 0; - isWaiting.current = false; - setBoxVisible(false); - if (currentAgv) { - currentAgv.status = "toPickup"; - } - logAgvStatus( - id, - "Started from droping point, heading to pickup point" - ); - }, bufferTime * 1000); - } - return; - } - - if (isPlaying && currentPhase === "toPickup") { - const reached = moveAlongPath( - object, - path, - distancesRef.current, - speed, - delta, - progressRef - ); - - if (reached) { - if (currentAgv) { - currentAgv.isActive = false; - } - setCurrentPhase("initial"); - if (currentAgv) { - currentAgv.status = "picking"; - } - logAgvStatus(id, "Reached pickup point again, cycle complete"); - } - return; - } - - moveAlongPath( - object, - path, - distancesRef.current, - speed, - delta, - progressRef - ); - }); - - function moveAlongPath( - object: THREE.Object3D, - path: [number, number, number][], - distances: number[], - speed: number, - delta: number, - progressRef: React.MutableRefObject - ): boolean { - if (path.length < 2) return false; - - progressRef.current += delta * (speed * globalSpeed); - let covered = progressRef.current; - let accumulated = 0; - let index = 0; - - for (; index < distances.length; index++) { - const dist = distances[index]; - if (accumulated + dist >= covered) break; - accumulated += dist; - } - - if (index >= path.length - 1) { - if (path.length > 1) { - const lastDirection = new THREE.Vector3(...path[path.length - 1]) - .sub(new THREE.Vector3(...path[path.length - 2])) - .normalize(); - object.rotation.y = Math.atan2(lastDirection.x, lastDirection.z); - } - return true; - } - - const start = new THREE.Vector3(...path[index]); - const end = new THREE.Vector3(...path[index + 1]); - const dist = distances[index]; - - const t = THREE.MathUtils.clamp((covered - accumulated) / dist, 0, 1); - object.position.copy(start.clone().lerp(end, t)); - - if (dist > 0.1) { - const targetDirection = end.clone().sub(start).normalize(); - const targetRotationY = Math.atan2(targetDirection.x, targetDirection.z); - - const rotationSpeed = Math.min(5 * delta, 1); - object.rotation.y = THREE.MathUtils.lerp( - object.rotation.y, - targetRotationY, - rotationSpeed - ); - } - - return false; - } - - useEffect(() => { - return () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - }; - }, []); - - return ( - - {toPickupPath.length > 0 && ( - - )} - - {pickupDropPath.length > 0 && ( - - )} - - {dropPickupPath.length > 0 && ( - - )} - - ); -} diff --git a/app/src/modules/builder/agv/polygonGenerator.tsx b/app/src/modules/builder/agv/polygonGenerator.tsx deleted file mode 100644 index 2462018..0000000 --- a/app/src/modules/builder/agv/polygonGenerator.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useState } from "react"; -import * as turf from "@turf/turf"; -import * as Types from "../../../types/world/worldTypes"; -import arrayLinesToObject from "../geomentries/lines/lineConvertions/arrayLinesToObject"; -interface PolygonGeneratorProps { - groupRef: React.MutableRefObject; - lines: Types.RefLines; -} - -export default function PolygonGenerator({ - groupRef, - lines, -}: PolygonGeneratorProps) { - - useEffect(() => { - let allLines = arrayLinesToObject(lines.current); - const wallLines = allLines?.filter((line) => line?.type === "WallLine"); - const aisleLines = allLines?.filter((line) => line?.type === "AisleLine"); - - const wallPoints = wallLines - .map((pair) => pair?.line.map((vals) => vals.position)) - .filter((wall): wall is THREE.Vector3[] => !!wall); - - const result = aisleLines.map((pair) => - pair?.line.map((point) => ({ - position: [point.position.x, point.position.z], - uuid: point.uuid, - })) - ); - - if (!result || result.some((line) => !line)) { - return; - } - - const lineFeatures = result?.map((line: any) => - turf.lineString(line.map((p: any) => p?.position)) - ); - - const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); - renderWallGeometry(wallPoints); - - if (polygons.features.length > 1) { - polygons.features.forEach((feature) => { - if (feature.geometry.type === "Polygon") { - - const shape = new THREE.Shape(); - const coords = feature.geometry.coordinates[0]; - - shape.moveTo(coords[0][0], coords[0][1]); - - for (let i = 1; i < coords.length; i++) { - shape.lineTo(coords[i][0], coords[i][1]); - } - shape.lineTo(coords[0][0], coords[0][1]); - - const extrudeSettings = { - depth: 5, - bevelEnabled: false, - }; - - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - - const material = new THREE.MeshBasicMaterial({ color: "blue", transparent: true, opacity: 0.5 }); - const mesh = new THREE.Mesh(geometry, material); - mesh.rotateX(Math.PI / 2); - mesh.name = "agv-collider"; - mesh.position.y = 5; - - mesh.receiveShadow = true; - groupRef.current?.add(mesh); - } - }); - } - - }, [lines.current]); - - const renderWallGeometry = (walls: THREE.Vector3[][]) => { - walls.forEach((wall) => { - if (wall.length < 2) return; - - for (let i = 0; i < wall.length - 1; i++) { - const start = new THREE.Vector3(wall[i].x, wall[i].y, wall[i].z); - const end = new THREE.Vector3( - wall[i + 1].x, - wall[i + 1].y, - wall[i + 1].z - ); - - const wallHeight = 10; - const direction = new THREE.Vector3().subVectors(end, start); - const length = direction.length(); - direction.normalize(); - - const wallGeometry = new THREE.BoxGeometry(length, wallHeight); - const wallMaterial = new THREE.MeshBasicMaterial({ - color: "#aaa", - transparent: true, - opacity: 0.5, - }); - - const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial); - const midPoint = new THREE.Vector3() - .addVectors(start, end) - .multiplyScalar(0.5); - wallMesh.position.set(midPoint.x, wallHeight / 2, midPoint.z); - - const quaternion = new THREE.Quaternion(); - quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction); - wallMesh.quaternion.copy(quaternion); - - groupRef.current?.add(wallMesh); - } - }); - }; - - return null; -} diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx new file mode 100644 index 0000000..e7f83e2 --- /dev/null +++ b/app/src/modules/builder/builder.tsx @@ -0,0 +1,355 @@ +////////// Three and React Three Fiber Imports ////////// + +import * as THREE from "three"; +import { useEffect, useRef, useState } from "react"; +import { useThree, useFrame } from "@react-three/fiber"; + +////////// Component Imports ////////// + +import DistanceText from "./geomentries/lines/distanceText/distanceText"; +import ReferenceDistanceText from "./geomentries/lines/distanceText/referenceDistanceText"; + +////////// Assests Imports ////////// + +import arch from "../../assets/gltf-glb/arch.glb"; +import door from "../../assets/gltf-glb/door.glb"; +import Window from "../../assets/gltf-glb/window.glb"; + +////////// Zustand State Imports ////////// + +import { + useToggleView, + useDeletePointOrLine, + useMovePoint, + useActiveLayer, + useSocketStore, + useWallVisibility, + useRoofVisibility, + useShadows, + useUpdateScene, + useWalls, + useToolMode, + useRefTextUpdate, + useRenderDistance, + useLimitDistance, +} from "../../store/store"; + +////////// 3D Function Imports ////////// + +import loadWalls from "./geomentries/walls/loadWalls"; + +import * as Types from "../../types/world/worldTypes"; + +import SocketResponses from "../collaboration/socket/socketResponses.dev"; +import FloorItemsGroup from "./groups/floorItemsGroup"; +import FloorPlanGroup from "./groups/floorPlanGroup"; +import FloorGroup from "./groups/floorGroup"; +import FloorGroupAilse from "./groups/floorGroupAisle"; +import Draw from "./functions/draw"; +import WallsAndWallItems from "./groups/wallsAndWallItems"; +import Ground from "../scene/environment/ground"; +// import ZoneGroup from "../groups/zoneGroup1"; +import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment"; +import Layer2DVisibility from "./geomentries/layers/layer2DVisibility"; +import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp"; +import ZoneGroup from "./groups/zoneGroup"; +import useModuleStore from "../../store/useModuleStore"; +import MeasurementTool from "../scene/tools/measurementTool"; +import NavMesh from "../simulation/vehicle/navMesh/navMesh"; + +export default function Builder() { + const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. + const csg = useRef(); // Reference for CSG object, used for 3D modeling. + const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. + const scene = useRef() as Types.RefScene; // Reference to the scene. + const camera = useRef() as Types.RefCamera; // Reference to the camera object. + const controls = useRef(); // Reference to the controls object. + const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. + const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. + + // Assigning the scene and camera from the Three.js state to the references. + + scene.current = state.scene; + camera.current = state.camera; + controls.current = state.controls; + raycaster.current = state.raycaster; + + const plane = useRef(null); // Reference for a plane object for raycaster reference. + const grid = useRef() as any; // Reference for a grid object for raycaster reference. + const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. + const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). + const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... + const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. + const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. + const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). + const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. + const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. + const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. + const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. + const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. + const onlyFloorline = useRef([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. + const onlyFloorlines = useRef([]); // Reference for all the floor lines that are ever drawn. + const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. + const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. + const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. + const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). + const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. + const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. + const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. + const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. + const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. + const floorGroupAisle = useRef() as Types.RefGroup; + const zoneGroup = useRef() as Types.RefGroup; + const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. + const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. + const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. + const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. + const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. + const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. + const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... + + const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. + + const [selectedItemsIndex, setSelectedItemsIndex] = useState(null); // State for tracking the index of the selected item. + const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. + const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D. + const { toolMode, setToolMode } = useToolMode(); + const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not. + const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); + const { socket } = useSocketStore(); + const { roofVisibility, setRoofVisibility } = useRoofVisibility(); + const { wallVisibility, setWallVisibility } = useWallVisibility(); + const { shadows, setShadows } = useShadows(); + const { renderDistance, setRenderDistance } = useRenderDistance(); + const { limitDistance, setLimitDistance } = useLimitDistance(); + const { updateScene, setUpdateScene } = useUpdateScene(); + const { walls, setWalls } = useWalls(); + const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); + const { activeModule } = useModuleStore(); + + // const loader = new GLTFLoader(); + // const dracoLoader = new DRACOLoader(); + + // dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + // loader.setDRACOLoader(dracoLoader); + + ////////// Assest Configuration Values ////////// + + const AssetConfigurations: Types.AssetConfigurations = { + arch: { + modelUrl: arch, + scale: [0.75, 0.75, 0.75], + csgscale: [2, 4, 0.5], + csgposition: [0, 2, 0], + positionY: () => 0, + type: "Fixed-Move", + }, + door: { + modelUrl: door, + scale: [0.75, 0.75, 0.75], + csgscale: [2, 4, 0.5], + csgposition: [0, 2, 0], + positionY: () => 0, + type: "Fixed-Move", + }, + window: { + modelUrl: Window, + scale: [0.75, 0.75, 0.75], + csgscale: [5, 3, 0.5], + csgposition: [0, 1.5, 0], + positionY: (intersectionPoint) => intersectionPoint.point.y, + type: "Free-Move", + }, + }; + + ////////// All Toggle's ////////// + + useEffect(() => { + setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); + if (dragPointControls.current) { + dragPointControls.current.enabled = false; + } + if (toggleView) { + Layer2DVisibility( + activeLayer, + floorPlanGroup, + floorPlanGroupLine, + floorPlanGroupPoint, + currentLayerPoint, + dragPointControls + ); + } else { + setToolMode(null); + setDeletePointOrLine(false); + setMovePoint(false); + loadWalls(lines, setWalls); + setUpdateScene(true); + line.current = []; + } + }, [toggleView]); + + useEffect(() => { + THREE.Cache.clear(); + THREE.Cache.enabled = true; + }, []); + + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + async function fetchVisibility() { + const visibility = await findEnvironment( + organization, + localStorage.getItem("userId")! + ); + if (visibility) { + setRoofVisibility(visibility.roofVisibility); + setWallVisibility(visibility.wallVisibility); + setShadows(visibility.shadowVisibility); + setRenderDistance(visibility.renderDistance); + setLimitDistance(visibility.limitDistance); + } + } + fetchVisibility(); + }, []); + + ////////// UseFrame is Here ////////// + + useFrame(() => { + if (toolMode) { + Draw( + state, + plane, + cursorPosition, + floorPlanGroupPoint, + floorPlanGroupLine, + snappedPoint, + isSnapped, + isSnappedUUID, + line, + lines, + ispreSnapped, + floorPlanGroup, + ReferenceLineMesh, + LineCreated, + setRefTextUpdate, + Tube, + anglesnappedPoint, + isAngleSnapped, + toolMode + ); + } + }); + + ////////// Return ////////// + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 29b0627..193dd41 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -5,13 +5,12 @@ 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/simulationTypes"; import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { Socket } from 'socket.io-client'; import * as CONSTANTS from '../../../../types/world/worldConstants'; -import { getAssetEventType } from '../../../../services/simulation/getAssetEventType'; import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; +import PointsCalculator from '../../../simulation/events/points/pointsCalculator'; async function addAssetModel( raycaster: THREE.Raycaster, @@ -25,7 +24,7 @@ async function addAssetModel( socket: Socket, selectedItem: any, setSelectedItem: any, - setSimulationStates: any, + addEvent: (event: EventsSchema) => void, plane: Types.RefMesh, ): Promise { @@ -66,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, setSimulationStates, socket); + handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); return; } else { const cachedModelBlob = await retrieveGLTF(selectedItem.id); @@ -79,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, setSimulationStates, socket); + handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); }, () => { TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); @@ -91,7 +90,7 @@ async function addAssetModel( const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/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, setSimulationStates, socket); + await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); }, () => { TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); @@ -114,7 +113,7 @@ async function handleModelLoad( tempLoader: Types.RefMesh, isTempLoader: Types.RefBoolean, setFloorItems: Types.setFloorItemSetState, - setSimulationStates: any, + addEvent: (event: EventsSchema) => void, socket: Socket ) { const model = gltf.scene.clone(); @@ -137,7 +136,7 @@ async function handleModelLoad( tempLoader.current = undefined; } - const newFloorItem: SimulationTypes.EventData = { + const newFloorItem: Types.FloorItemType = { modeluuid: model.uuid, modelname: selectedItem.name, modelfileID: selectedItem.id, @@ -150,316 +149,159 @@ async function handleModelLoad( const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : ""; - getAssetEventType(selectedItem.id, organization).then(async (res) => { + // API - if (res.type === "Conveyor") { - const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID()); + // 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, + // ); - const backendEventData: Extract = { - type: 'Conveyor', - points: res.points.map((point: any, index: number) => ({ - uuid: pointUUIDs[index], - position: point.position as [number, number, number], - rotation: point.rotation as [number, number, number], - actions: [{ - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - type: 'Inherit', - material: 'Inherit', - delay: 'Inherit', - spawnInterval: 'Inherit', - isUsed: true - }], - triggers: [], - connections: { - source: { modelUUID: model.uuid, pointUUID: pointUUIDs[index] }, - targets: [] + // 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 + }; + + if (selectedItem.type) { + const data = PointsCalculator( + selectedItem.type, + gltf.scene.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + if (selectedItem.type === "Conveyor") { + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: 'transfer', + speed: 1, + points: data.points.map((point: THREE.Vector3, index: number) => ({ + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action ${index}`, + actionType: 'default', + material: 'inherit', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] } - })), - speed: 'Inherit' - }; - - // 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, - // { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed } - // ); - - // 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 - }; - - 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]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - eventData as SimulationTypes.ConveyorEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (res.type === "Vehicle") { - - const pointUUID = THREE.MathUtils.generateUUID(); - - const backendEventData: Extract = { - type: "Vehicle", - points: { - uuid: pointUUID, - position: res.points.position as [number, number, number], - rotation: res.points.rotation as [number, number, number], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: '', start: {}, hitCount: 1, end: {}, buffer: 0 }, - connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, - speed: 2, - } + })) } - - // 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, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // SOCKET - - const data = { - organization, - modeluuid: newFloorItem.modeluuid, - modelname: newFloorItem.modelname, - modelfileID: newFloorItem.modelfileID, + addEvent(ConveyorEvent); + } else if (selectedItem.type === "Vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, position: newFloorItem.position, - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true, - eventData: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id - }; - - const eventData: any = backendEventData; - eventData.modeluuid = newFloorItem.modeluuid; - eventData.modelName = newFloorItem.modelname; - eventData.position = newFloorItem.position; - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - eventData as SimulationTypes.VehicleEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (res.type === "StaticMachine") { - - const pointUUID = THREE.MathUtils.generateUUID(); - - const backendEventData: Extract = { - type: "StaticMachine", - points: { - uuid: pointUUID, - position: res.points.position as [number, number, number], - rotation: res.points.rotation as [number, number, number], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 0, material: 'Inherit' }, - triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, - connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Vehicle Action", + actionType: "travel", + material: null, + unLoadDuration: 5, + loadCapacity: 10, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } } - } - - // 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, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id }; - - 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]; - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - eventData as SimulationTypes.StaticMachineEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (res.type === "ArmBot") { - - const pointUUID = THREE.MathUtils.generateUUID(); - - const backendEventData: Extract = { - type: "ArmBot", - points: { - uuid: pointUUID, - position: res.points.position as [number, number, number], - rotation: res.points.rotation as [number, number, number], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 0.2, processes: [] }, - triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, - connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, + addEvent(vehicleEvent); + } else if (selectedItem.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Pick and Place", + actionType: "pickAndPlace", + process: { + startPoint: [0, 0, 0], + endPoint: [0, 0, 0] + }, + triggers: [] + } + ] } - } - - // 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, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id }; - - 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]; - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - eventData as SimulationTypes.ArmBotEventsSchema - ]); - - 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, + addEvent(roboticArmEvent); + } else if (selectedItem.type === "Machine") { + const machineEvent: MachineEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, position: newFloorItem.position, - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Process Action", + actionType: "process", + processTime: 10, + swapMaterial: "material-id", + triggers: [] + } + } }; - - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - socket.emit("v2:model-asset:add", data); + addEvent(machineEvent); } + } - 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!"); } }); + setFloorItems((prevItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; }); + + 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!"); } }); } export default addAssetModel; diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index dbd7e9b..18136b3 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -2,7 +2,6 @@ import { toast } from 'react-toastify'; import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; -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'; @@ -11,7 +10,6 @@ async function DeleteFloorItems( itemsGroup: Types.RefGroup, hoveredDeletableFloorItem: Types.RefMesh, setFloorItems: Types.setFloorItemSetState, - setSimulationStates: any, socket: Socket ): Promise { @@ -77,11 +75,6 @@ async function DeleteFloorItems( } setFloorItems(updatedItems); - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - 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 acc284f..2a3c2cc 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -1,5 +1,5 @@ import { useFrame, useThree } from "@react-three/fiber"; -import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; +import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; import assetVisibility from "../geomentries/assets/assetVisibility"; import { useEffect } from "react"; import * as THREE from "three"; @@ -11,11 +11,13 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems"; import DeleteFloorItems from "../geomentries/assets/deleteFloorItems"; -import loadInitialFloorItems from "../../scene/IntialLoad/loadInitialFloorItems"; +import loadInitialFloorItems from "../IntialLoad/loadInitialFloorItems"; import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; +import { useEventsStore } from "../../../store/simulation/useEventsStore"; + const assetManagerWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", import.meta.url)); const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url)); @@ -32,12 +34,12 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject const { setSelectedFloorItem } = useSelectedFloorItem(); const { activeTool } = useActiveTool(); const { selectedItem, setSelectedItem } = useSelectedItem(); - const { simulationStates, setSimulationStates } = useSimulationStates(); const { setLoadingProgress } = useLoadingProgress(); const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); const loader = new GLTFLoader(); const dracoLoader = new DRACOLoader(); + const { addEvent } = useEventsStore(); dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"); loader.setDRACOLoader(dracoLoader); @@ -73,7 +75,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject gltfLoaderWorker.postMessage({ floorItems: data }); } else { gltfLoaderWorker.postMessage({ floorItems: [] }); - loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates); + loadInitialFloorItems(itemsGroup, setFloorItems); updateLoadingProgress(100); } }); @@ -92,7 +94,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject updateLoadingProgress(progress); if (loadedAssets === totalAssets) { - loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates); + loadInitialFloorItems(itemsGroup, setFloorItems); updateLoadingProgress(100); } }); @@ -192,9 +194,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject if (drag) return; if (deleteTool) { - DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, setSimulationStates, socket); - - // Remove EventData if there are any in the asset. + DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket); } const Mode = transformMode; @@ -278,7 +278,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject if (!event.dataTransfer?.files[0]) return; if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { - addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, setSimulationStates, plane); + addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, addEvent, plane); } }; @@ -314,14 +314,14 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject useFrame(() => { if (controls) // assetVisibility(itemsGroup, state.camera.position, renderDistance); - if (deleteTool && activeModule === "builder") { - DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); - } else if (!deleteTool) { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); + if (deleteTool && activeModule === "builder") { + DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem); + } else if (!deleteTool) { + if (hoveredDeletableFloorItem.current) { + hoveredDeletableFloorItem.current = undefined; + setDeletableFloorItem(null); + } } - } }); return ; diff --git a/app/src/modules/builder/groups/floorPlanGroup.tsx b/app/src/modules/builder/groups/floorPlanGroup.tsx index 6369e1e..12d75f0 100644 --- a/app/src/modules/builder/groups/floorPlanGroup.tsx +++ b/app/src/modules/builder/groups/floorPlanGroup.tsx @@ -9,15 +9,14 @@ import removeReferenceLine from "../geomentries/lines/removeReferenceLine"; import DeleteLayer from "../geomentries/layers/deleteLayer"; import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; -import loadInitialPoint from "../../scene/IntialLoad/loadInitialPoint"; -import loadInitialLine from "../../scene/IntialLoad/loadInitialLine"; +import loadInitialPoint from "../IntialLoad/loadInitialPoint"; +import loadInitialLine from "../IntialLoad/loadInitialLine"; import deletePoint from "../geomentries/points/deletePoint"; import deleteLine from "../geomentries/lines/deleteLine"; import drawWall from "../geomentries/lines/drawWall"; import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor"; import addDragControl from "../eventDeclaration/dragControlDeclaration"; - const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => { const state = useThree(); const { scene, camera, gl, raycaster, controls } = state; diff --git a/app/src/modules/builder/groups/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index cdc326e..43845e7 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -7,7 +7,7 @@ import * as THREE from "three"; import { useThree } from "@react-three/fiber"; import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import DeleteWallItems from "../geomentries/walls/deleteWallItems"; -import loadInitialWallItems from "../../scene/IntialLoad/loadInitialWallItems"; +import loadInitialWallItems from "../IntialLoad/loadInitialWallItems"; import AddWallItems from "../geomentries/walls/addWallItems"; import useModuleStore from "../../../store/useModuleStore"; @@ -37,51 +37,6 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable ////////// Update the Rotation value changes in the selected item ////////// - useEffect(() => { - if (objectScale.x && objectScale.y && objectScale.z) { - let ScaledWallItems: Types.wallItems = []; - wallItems.forEach((items: any) => { - if (items.model?.uuid === currentWallItem.current?.parent?.uuid) { - items.scale = [objectScale.x, objectScale.y, objectScale.z]; - } - ScaledWallItems.push(items); - }); - setWallItems(ScaledWallItems); - } - }, [objectScale]); - - useEffect(() => { - if (objectPosition.x && objectPosition.y && objectPosition.z) { - let ScaledWallItems: Types.wallItems = []; - wallItems.forEach((items: any) => { - if (items.model?.uuid === currentWallItem.current?.parent?.uuid) { - items.position = [objectPosition.x, objectPosition.y, objectPosition.z]; - } - ScaledWallItems.push(items); - }); - setWallItems(ScaledWallItems); - } - }, [objectPosition]); - - useEffect(() => { - if (objectRotation.x && objectRotation.y && objectRotation.z) { - let ScaledWallItems: Types.wallItems = []; - wallItems.forEach((items: any) => { - if (items.model?.uuid === currentWallItem.current?.parent?.uuid) { - const radiansX = objectRotation.x * (Math.PI / 180); - const radiansY = objectRotation.y * (Math.PI / 180); - const radiansZ = objectRotation.z * (Math.PI / 180); - const quaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(radiansX, radiansY, radiansZ) - ); - items.quaternion = [quaternion.x, quaternion.y, quaternion.z, quaternion.w]; - } - ScaledWallItems.push(items); - }); - setWallItems(ScaledWallItems); - } - }, [objectRotation]); - useEffect(() => { const canvasElement = state.gl.domElement; function handlePointerMove(e: any) { diff --git a/app/src/modules/builder/groups/zoneGroup1.tsx b/app/src/modules/builder/groups/zoneGroup1.tsx deleted file mode 100644 index 80be9a3..0000000 --- a/app/src/modules/builder/groups/zoneGroup1.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import { useEffect } from "react"; -import * as THREE from 'three'; -import * as Types from '../../../types/world/worldTypes'; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import { useActiveLayer, useSocketStore, useDeleteTool, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store"; -import { useThree } from "@react-three/fiber"; -import arrayLineToObject from "../geomentries/lines/lineConvertions/arrayLineToObject"; -import addPointToScene from "../geomentries/points/addPointToScene"; -import addLineToScene from "../geomentries/lines/addLineToScene"; -import removeSoloPoint from "../geomentries/points/removeSoloPoint"; -import removeReferenceLine from "../geomentries/lines/removeReferenceLine"; -import getClosestIntersection from "../geomentries/lines/getClosestIntersection"; -import loadZones from "../geomentries/zones/loadZones"; - -const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint, line, lines, currentLayerPoint, dragPointControls, floorPlanGroup, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => { - const { toggleView, setToggleView } = useToggleView(); - const { deleteTool, setDeleteTool } = useDeleteTool(); - const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); - const { toolMode, setToolMode } = useToolMode(); - const { movePoint, setMovePoint } = useMovePoint(); - const { socket } = useSocketStore(); - const { activeLayer } = useActiveLayer(); - const { gl, raycaster, camera, pointer } = useThree(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { newLines, setNewLines } = useNewLines(); - - useEffect(() => { - if (updateScene) { - loadZones(lines, zoneGroup); - setUpdateScene(false); - } - }, [updateScene]) - - useEffect(() => { - if (toolMode === "Zone") { - setDeletePointOrLine(false); - setMovePoint(false); - setDeleteTool(false); - } else { - removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint); - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - } - }, [toolMode]) - - useEffect(() => { - - const canvasElement = gl.domElement; - - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = true; - drag = false; - } - }; - - const onMouseUp = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - } - } - - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onContextMenu = (e: any) => { - e.preventDefault(); - if (toolMode === "Zone") { - removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint); - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - } - }; - - const onMouseClick = (evt: any) => { - if (!plane.current || drag) return; - const intersects = raycaster.intersectObject(plane.current, true); - let intersectionPoint = intersects[0].point; - const points = floorPlanGroupPoint.current?.children ?? []; - const intersectsPoint = raycaster.intersectObjects(points, true).find(intersect => intersect.object.visible); - let intersectsLines: any = raycaster.intersectObjects(floorPlanGroupLine.current.children, true); - - if (intersectsLines.length > 0 && intersects && intersects.length > 0 && !intersectsPoint) { - const lineType = intersectsLines[0].object.userData.linePoints[0][3]; - if (lineType === CONSTANTS.lineConfig.zoneName) { - // console.log("intersected a zone line"); - - const ThroughPoint = (intersectsLines[0].object.geometry.parameters.path).getPoints(300); - let intersection = getClosestIntersection(ThroughPoint, intersectionPoint); - if (!intersection) return; - const point = addPointToScene(intersection, CONSTANTS.pointConfig.zoneOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.zoneName); - (line.current as Types.Line).push([new THREE.Vector3(intersection.x, 0.01, intersection.z), point.uuid, activeLayer, CONSTANTS.lineConfig.zoneName,]); - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - lines.current.push(line.current as Types.Line); - - const data = arrayLineToObject(line.current as Types.Line); - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - //REST - - // setLine(organization, data.layer!, data.line!, data.type!); - - //SOCKET - - const input = { - organization: organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id - } - - socket.emit('v1:Line:create', input); - - setNewLines([line.current]); - - addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.zoneColor, line.current, floorPlanGroupLine); - let lastPoint = line.current[line.current.length - 1]; - line.current = [lastPoint]; - } - } - } else if (intersectsPoint && intersects && intersects.length > 0) { - if (intersectsPoint.object.userData.type === CONSTANTS.lineConfig.zoneName) { - // console.log("intersected a zone point"); - - intersectionPoint = intersectsPoint.object.position; - (line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), intersectsPoint.object.uuid, activeLayer, CONSTANTS.lineConfig.zoneName]); - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - lines.current.push(line.current as Types.Line); - - const data = arrayLineToObject(line.current as Types.Line); - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - //REST - - // setLine(organization, data.layer!, data.line!, data.type!); - - //SOCKET - - const input = { - organization: organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id - } - - socket.emit('v1:Line:create', input); - - setNewLines([line.current]); - - addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.zoneColor, line.current, floorPlanGroupLine); - let lastPoint = line.current[line.current.length - 1]; - line.current = [lastPoint]; - ispreSnapped.current = false; - isSnapped.current = false; - } - } - } else if (intersects && intersects.length > 0) { - // console.log("intersected a empty area"); - - let uuid: string = ""; - if (isAngleSnapped.current && anglesnappedPoint.current && line.current.length > 0) { - intersectionPoint = anglesnappedPoint.current; - const point = addPointToScene(intersectionPoint, CONSTANTS.pointConfig.zoneOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.zoneName); - uuid = point.uuid; - } else if (isSnapped.current && snappedPoint.current && line.current.length > 0) { - intersectionPoint = snappedPoint.current; - uuid = isSnappedUUID.current!; - } else if (ispreSnapped.current && snappedPoint.current) { - intersectionPoint = snappedPoint.current; - uuid = isSnappedUUID.current!; - } else { - const point = addPointToScene(intersectionPoint, CONSTANTS.pointConfig.zoneOuterColor, currentLayerPoint, floorPlanGroupPoint, dragPointControls, undefined, CONSTANTS.lineConfig.zoneName); - uuid = point.uuid; - } - - (line.current as Types.Line).push([new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), uuid, activeLayer, CONSTANTS.lineConfig.zoneName]); - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - lines.current.push(line.current as Types.Line); - - const data = arrayLineToObject(line.current as Types.Line); - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - //REST - - // setLine(organization, data.layer!, data.line!, data.type!); - - //SOCKET - - const input = { - organization: organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id - } - - socket.emit('v1:Line:create', input); - - setNewLines([line.current]); - - addLineToScene(line.current[0][0], line.current[1][0], CONSTANTS.lineConfig.zoneColor, line.current, floorPlanGroupLine); - let lastPoint = line.current[line.current.length - 1]; - line.current = [lastPoint]; - ispreSnapped.current = false; - isSnapped.current = false; - } - } - } - - if (toolMode === 'Zone') { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("click", onMouseClick); - canvasElement.addEventListener("contextmenu", onContextMenu); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("click", onMouseClick); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [toolMode]) - - return ( - - - ) -} - -export default ZoneGroup; \ No newline at end of file diff --git a/app/src/modules/collaboration/camera/collabCams.tsx b/app/src/modules/collaboration/camera/collabCams.tsx new file mode 100644 index 0000000..1347a2a --- /dev/null +++ b/app/src/modules/collaboration/camera/collabCams.tsx @@ -0,0 +1,231 @@ +import * as THREE from "three"; +import { useEffect, useRef, useState } from "react"; +import { useFrame } from "@react-three/fiber"; +import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; +import camModel from "../../../assets/gltf-glb/camera face 2.gltf"; +import getActiveUsersData from "../../../services/factoryBuilder/collab/getActiveUsers"; +import { useActiveUsers, useSocketStore } from "../../../store/store"; +import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; +import { useNavigate } from "react-router-dom"; +import { Html } from "@react-three/drei"; +import CollabUserIcon from "../../../functions/collabUserIcon"; +import { getAvatarColor } from "../../../functions/users/functions/getAvatarColor"; +import useModuleStore from "../../../store/useModuleStore"; + +const CamModelsGroup = () => { + const navigate = useNavigate(); + const groupRef = useRef(null); + const email = localStorage.getItem("email"); + const { setActiveUsers } = useActiveUsers(); + const { socket } = useSocketStore(); + const { activeModule } = useModuleStore(); + + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); + loader.setDRACOLoader(dracoLoader); + + const [cams, setCams] = useState([]); + const [models, setModels] = useState>({}); + + const dedupeCams = (cams: any[]) => { + const seen = new Set(); + return cams.filter((cam) => { + if (seen.has(cam.uuid)) return false; + seen.add(cam.uuid); + return true; + }); + }; + + const dedupeUsers = (users: any[]) => { + const seen = new Set(); + return users.filter((user) => { + if (seen.has(user._id)) return false; + seen.add(user._id); + return true; + }); + }; + + useEffect(() => { + if (!email) navigate("/"); + + if (!socket) return; + const organization = email!.split("@")[1].split(".")[0]; + + socket.on("userConnectResponse", (data: any) => { + if (!groupRef.current) return; + if (data.data.userData.email === email) return; + if (socket.id === data.socketId || organization !== data.organization) + return; + + const model = groupRef.current.getObjectByProperty( + "uuid", + data.data.userData._id + ); + if (model) { + groupRef.current.remove(model); + } + + loader.load(camModel, (gltf) => { + const newModel = gltf.scene.clone(); + newModel.uuid = data.data.userData._id; + newModel.position.set( + data.data.position.x, + data.data.position.y, + data.data.position.z + ); + newModel.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); + newModel.userData = data.data.userData; + + setCams((prev) => dedupeCams([...prev, newModel])); + setActiveUsers((prev: any) => + dedupeUsers([...prev, data.data.userData]) + ); + }); + }); + + socket.on("userDisConnectResponse", (data: any) => { + if (!groupRef.current) return; + if (socket.id === data.socketId || organization !== data.organization) + return; + + setCams((prev) => + prev.filter((cam) => cam.uuid !== data.data.userData._id) + ); + setActiveUsers((prev: any) => + prev.filter((user: any) => user._id !== data.data.userData._id) + ); + }); + + socket.on("cameraUpdateResponse", (data: any) => { + if ( + !groupRef.current || + socket.id === data.socketId || + organization !== data.organization + ) + return; + + setModels((prev) => ({ + ...prev, + [data.data.userId]: { + targetPosition: new THREE.Vector3( + data.data.position.x, + data.data.position.y, + data.data.position.z + ), + targetRotation: new THREE.Euler( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ), + }, + })); + }); + + return () => { + socket.off("userConnectResponse"); + socket.off("userDisConnectResponse"); + socket.off("cameraUpdateResponse"); + }; + }, [socket]); + + useFrame(() => { + if (!groupRef.current) return; + Object.keys(models).forEach((uuid) => { + const model = groupRef.current!.getObjectByProperty("uuid", uuid); + if (!model) return; + + const { targetPosition, targetRotation } = models[uuid]; + model.position.lerp(targetPosition, 0.1); + model.rotation.x = THREE.MathUtils.lerp( + model.rotation.x, + targetRotation.x, + 0.1 + ); + model.rotation.y = THREE.MathUtils.lerp( + model.rotation.y, + targetRotation.y, + 0.1 + ); + model.rotation.z = THREE.MathUtils.lerp( + model.rotation.z, + targetRotation.z, + 0.1 + ); + }); + }); + + useEffect(() => { + if (!groupRef.current) return; + const organization = email!.split("@")[1].split(".")[0]; + + getActiveUsersData(organization).then((data) => { + const filteredData = data.cameraDatas.filter( + (camera: any) => camera.userData.email !== email + ); + + if (filteredData.length > 0) { + loader.load(camModel, (gltf) => { + const newCams = filteredData.map((cam: any) => { + const newModel = gltf.scene.clone(); + newModel.uuid = cam.userData._id; + newModel.position.set( + cam.position.x, + cam.position.y, + cam.position.z + ); + newModel.rotation.set( + cam.rotation.x, + cam.rotation.y, + cam.rotation.z + ); + newModel.userData = cam.userData; + return newModel; + }); + + const users = filteredData.map((cam: any) => cam.userData); + setActiveUsers((prev: any) => dedupeUsers([...prev, ...users])); + setCams((prev) => dedupeCams([...prev, ...newCams])); + }); + } + }); + }, []); + + return ( + + {cams.map((cam, index) => ( + + + + + + ))} + + ); +}; + +export default CamModelsGroup; diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx deleted file mode 100644 index ca0d1c5..0000000 --- a/app/src/modules/collaboration/collabCams.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useRef, useState } from "react"; -import { useFrame } from "@react-three/fiber"; -import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -import camModel from "../../assets/gltf-glb/camera face 2.gltf"; -import getActiveUsersData from "../../services/factoryBuilder/collab/getActiveUsers"; -import { useActiveUsers, useSocketStore } from "../../store/store"; -import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { useNavigate } from "react-router-dom"; -import { Html } from "@react-three/drei"; -import CollabUserIcon from "./collabUserIcon"; -import { getAvatarColor } from "./users/functions/getAvatarColor"; -import useModuleStore from "../../store/useModuleStore"; - -const CamModelsGroup = () => { - const navigate = useNavigate(); - const groupRef = useRef(null); - const email = localStorage.getItem("email"); - const { setActiveUsers } = useActiveUsers(); - const { socket } = useSocketStore(); - const { activeModule } = useModuleStore(); - - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/"); - loader.setDRACOLoader(dracoLoader); - - const [cams, setCams] = useState([]); - const [models, setModels] = useState>({}); - - const dedupeCams = (cams: any[]) => { - const seen = new Set(); - return cams.filter((cam) => { - if (seen.has(cam.uuid)) return false; - seen.add(cam.uuid); - return true; - }); - }; - - const dedupeUsers = (users: any[]) => { - const seen = new Set(); - return users.filter((user) => { - if (seen.has(user._id)) return false; - seen.add(user._id); - return true; - }); - }; - - useEffect(() => { - if (!email) navigate("/"); - - if (!socket) return; - const organization = email!.split("@")[1].split(".")[0]; - - socket.on("userConnectResponse", (data: any) => { - if (!groupRef.current) return; - if (data.data.userData.email === email) return; - if (socket.id === data.socketId || organization !== data.organization) - return; - - const model = groupRef.current.getObjectByProperty( - "uuid", - data.data.userData._id - ); - if (model) { - groupRef.current.remove(model); - } - - loader.load(camModel, (gltf) => { - const newModel = gltf.scene.clone(); - newModel.uuid = data.data.userData._id; - newModel.position.set( - data.data.position.x, - data.data.position.y, - data.data.position.z - ); - newModel.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - newModel.userData = data.data.userData; - - setCams((prev) => dedupeCams([...prev, newModel])); - setActiveUsers((prev: any) => - dedupeUsers([...prev, data.data.userData]) - ); - }); - }); - - socket.on("userDisConnectResponse", (data: any) => { - if (!groupRef.current) return; - if (socket.id === data.socketId || organization !== data.organization) - return; - - setCams((prev) => - prev.filter((cam) => cam.uuid !== data.data.userData._id) - ); - setActiveUsers((prev: any) => - prev.filter((user: any) => user._id !== data.data.userData._id) - ); - }); - - socket.on("cameraUpdateResponse", (data: any) => { - if ( - !groupRef.current || - socket.id === data.socketId || - organization !== data.organization - ) - return; - - setModels((prev) => ({ - ...prev, - [data.data.userId]: { - targetPosition: new THREE.Vector3( - data.data.position.x, - data.data.position.y, - data.data.position.z - ), - targetRotation: new THREE.Euler( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ), - }, - })); - }); - - return () => { - socket.off("userConnectResponse"); - socket.off("userDisConnectResponse"); - socket.off("cameraUpdateResponse"); - }; - }, [socket]); - - useFrame(() => { - if (!groupRef.current) return; - Object.keys(models).forEach((uuid) => { - const model = groupRef.current!.getObjectByProperty("uuid", uuid); - if (!model) return; - - const { targetPosition, targetRotation } = models[uuid]; - model.position.lerp(targetPosition, 0.1); - model.rotation.x = THREE.MathUtils.lerp( - model.rotation.x, - targetRotation.x, - 0.1 - ); - model.rotation.y = THREE.MathUtils.lerp( - model.rotation.y, - targetRotation.y, - 0.1 - ); - model.rotation.z = THREE.MathUtils.lerp( - model.rotation.z, - targetRotation.z, - 0.1 - ); - }); - }); - - useEffect(() => { - if (!groupRef.current) return; - const organization = email!.split("@")[1].split(".")[0]; - - getActiveUsersData(organization).then((data) => { - const filteredData = data.cameraDatas.filter( - (camera: any) => camera.userData.email !== email - ); - - if (filteredData.length > 0) { - loader.load(camModel, (gltf) => { - const newCams = filteredData.map((cam: any) => { - const newModel = gltf.scene.clone(); - newModel.uuid = cam.userData._id; - newModel.position.set( - cam.position.x, - cam.position.y, - cam.position.z - ); - newModel.rotation.set( - cam.rotation.x, - cam.rotation.y, - cam.rotation.z - ); - newModel.userData = cam.userData; - return newModel; - }); - - const users = filteredData.map((cam: any) => cam.userData); - setActiveUsers((prev: any) => dedupeUsers([...prev, ...users])); - setCams((prev) => dedupeCams([...prev, ...newCams])); - }); - } - }); - }, []); - - return ( - - {cams.map((cam, index) => ( - - - - - - ))} - - ); -}; - -export default CamModelsGroup; diff --git a/app/src/modules/collaboration/collaboration.tsx b/app/src/modules/collaboration/collaboration.tsx new file mode 100644 index 0000000..84d3ab1 --- /dev/null +++ b/app/src/modules/collaboration/collaboration.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import CamModelsGroup from './camera/collabCams' + +const Collaboration = () => { + return ( + <> + + + + + ) +} + +export default Collaboration \ No newline at end of file diff --git a/app/src/modules/collaboration/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx similarity index 94% rename from app/src/modules/collaboration/socketResponses.dev.tsx rename to app/src/modules/collaboration/socket/socketResponses.dev.tsx index 6e578a1..3d9cddf 100644 --- a/app/src/modules/collaboration/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -1,827 +1,827 @@ -import { useEffect } from "react"; -import * as THREE from 'three'; -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import gsap from 'gsap'; -import { toast } from 'react-toastify'; -import { useSocketStore, useActiveLayer, useWallItems, useFloorItems, useLayers, useUpdateScene, useWalls, useDeletedLines, useNewLines, useZonePoints, useZones } from "../../store/store"; - -import * as Types from "../../types/world/worldTypes"; -import * as CONSTANTS from '../../types/world/worldConstants'; -import TempLoader from "../builder/geomentries/assets/tempLoader"; - -// import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; -import objectLineToArray from "../builder/geomentries/lines/lineConvertions/objectLineToArray"; -import addLineToScene from "../builder/geomentries/lines/addLineToScene"; -import updateLinesPositions from "../builder/geomentries/lines/updateLinesPositions"; -import updateLines from "../builder/geomentries/lines/updateLines"; -import updateDistanceText from "../builder/geomentries/lines/updateDistanceText"; -import updateFloorLines from "../builder/geomentries/floors/updateFloorLines"; -import loadWalls from "../builder/geomentries/walls/loadWalls"; -import RemoveConnectedLines from "../builder/geomentries/lines/removeConnectedLines"; -import Layer2DVisibility from "../builder/geomentries/layers/layer2DVisibility"; -import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { retrieveGLTF, storeGLTF } from "../../utils/indexDB/idbUtils"; -import { getZonesApi } from "../../services/factoryBuilder/zones/getZonesApi"; - - -export default function SocketResponses({ - floorPlanGroup, - lines, - floorGroup, - floorGroupAisle, - scene, - onlyFloorlines, - AssetConfigurations, - itemsGroup, - isTempLoader, - tempLoader, - currentLayerPoint, - floorPlanGroupPoint, - floorPlanGroupLine, - zoneGroup, - dragPointControls -}: any) { - - const { socket } = useSocketStore(); - const { activeLayer, setActiveLayer } = useActiveLayer(); - const { wallItems, setWallItems } = useWallItems(); - const { layers, setLayers } = useLayers(); - const { floorItems, setFloorItems } = useFloorItems(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { walls, setWalls } = useWalls(); - const { deletedLines, setDeletedLines } = useDeletedLines(); - const { newLines, setNewLines } = useNewLines(); - const { zones, setZones } = useZones(); - const { zonePoints, setZonePoints } = useZonePoints(); - - useEffect(() => { - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - if (!socket) return - - socket.on('cameraCreateResponse', (data: any) => { - // console.log('data: ', data); - }) - - socket.on('userConnectRespones', (data: any) => { - // console.log('data: ', data); - }) - - socket.on('userDisConnectRespones', (data: any) => { - // console.log('data: ', data); - }) - - socket.on('cameraUpdateResponse', (data: any) => { - // console.log('data: ', data); - }) - - socket.on('EnvironmentUpdateResponse', (data: any) => { - // console.log('data: ', data); - }) - - socket.on('model-asset:response:updates', async (data: any) => { - // console.log('data: ', data); - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "Model created successfully") { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); - loader.setDRACOLoader(dracoLoader); - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - - try { - isTempLoader.current = true; - const cachedModel = THREE.Cache.get(data.data.modelname); - let url; - if (cachedModel) { - // 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, 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); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { - modeluuid: data.data.modeluuid, - modelname: data.data.modelname, - modelfileID: data.data.modelfileID, - position: [...data.data.position as [number, number, number]], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - gsap.to(model.position, { y: data.data.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!") } }); - - } else { - const indexedDBModel = await retrieveGLTF(data.data.modelname); - if (indexedDBModel) { - // console.log(`Getting ${data.data.modelname} from IndexedDB`); - url = URL.createObjectURL(indexedDBModel); - } else { - // console.log(`Getting ${data.data.modelname} from Backend`); - url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; - const modelBlob = await fetch(url).then((res) => res.blob()); - await storeGLTF(data.data.modelfileID, modelBlob); - } - } - - if (url) { - loadModel(url); - } - - } catch (error) { - console.error('Error fetching asset model:', error); - } - - function loadModel(url: string) { - loader.load(url, (gltf) => { - URL.revokeObjectURL(url); - THREE.Cache.remove(url); - const model = gltf.scene; - model.uuid = data.data.modeluuid; - 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); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { - modeluuid: data.data.modeluuid, - modelname: data.data.modelname, - modelfileID: data.data.modelfileID, - position: [...data.data.position as [number, number, number]], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - gsap.to(model.position, { y: data.data.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!") } }); - - THREE.Cache.add(data.data.modelname, gltf); - }, () => { - TempLoader(new THREE.Vector3(...data.data.position), isTempLoader, tempLoader, itemsGroup); - }); - } - - } else if (data.message === "Model updated successfully") { - itemsGroup.current?.children.forEach((item: THREE.Group) => { - if (item.uuid === data.data.modeluuid) { - item.position.set(...data.data.position as [number, number, number]); - item.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); - } - }) - - setFloorItems((prevItems: Types.FloorItems) => { - if (!prevItems) { - return - } - let updatedItem: any = null; - const updatedItems = prevItems.map((item) => { - if (item.modeluuid === data.data.modeluuid) { - updatedItem = { - ...item, - position: [...data.data.position] as [number, number, number], - rotation: { x: data.data.rotation.x, y: data.data.rotation.y, z: data.data.rotation.z, }, - }; - return updatedItem; - } - return item; - }); - return updatedItems; - }) - } - }) - - socket.on('model-asset:response:updates', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "Model deleted successfully") { - const deletedUUID = data.data.modeluuid; - let items = JSON.parse(localStorage.getItem("FloorItems")!); - - const updatedItems = items.filter( - (item: { modeluuid: string }) => item.modeluuid !== deletedUUID - ); - - const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); - const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => item.modeluuid !== deletedUUID); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - itemsGroup.current.children.forEach((item: any) => { - if (item.uuid === deletedUUID) { - itemsGroup.current.remove(item); - } - }) - setFloorItems(updatedItems); - toast.success("Model Removed!"); - } - }) - - socket.on('Line:response:update', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "line updated") { - const DraggedUUID = data.data.uuid; - const DraggedPosition = new THREE.Vector3(data.data.position.x, data.data.position.y, data.data.position.z); - - const point = floorPlanGroupPoint.current.getObjectByProperty('uuid', DraggedUUID); - point.position.set(DraggedPosition.x, DraggedPosition.y, DraggedPosition.z); - const affectedLines = updateLinesPositions({ uuid: DraggedUUID, position: DraggedPosition }, lines); - - updateLines(floorPlanGroupLine, affectedLines); - updateDistanceText(scene, floorPlanGroupLine, affectedLines); - updateFloorLines(onlyFloorlines, { uuid: DraggedUUID, position: DraggedPosition }); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - } - }) - - socket.on('Line:response:delete', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "line deleted") { - const line = objectLineToArray(data.data); - const linePoints = line; - const connectedpoints = [linePoints[0][1], linePoints[1][1]]; - - - onlyFloorlines.current = onlyFloorlines.current.map((floorline: any) => - floorline.filter((line: any) => line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1]) - ).filter((floorline: any) => floorline.length > 0); - - const removedLine = lines.current.find((item: any) => (item[0][1] === linePoints[0][1] && item[1][1] === linePoints[1][1] || (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]))); - lines.current = lines.current.filter((item: any) => item !== removedLine); - - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints as [number, string, number][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if ((uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1] || (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]))) { - line.material.dispose(); - line.geometry.dispose(); - floorPlanGroupLine.current.remove(line); - setDeletedLines([line.userData.linePoints]) - } - }); - - connectedpoints.forEach((pointUUID) => { - let isConnected = false; - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - if (uuid1 === pointUUID || uuid2 === pointUUID) { - isConnected = true; - } - }); - - if (!isConnected) { - floorPlanGroupPoint.current.children.forEach((point: any) => { - if (point.uuid === pointUUID) { - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point); - } - }); - } - }); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Line Removed!"); - } - }) - - socket.on('Line:response:delete:point', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "point deleted") { - const point = floorPlanGroupPoint.current?.getObjectByProperty('uuid', data.data); - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point); - - onlyFloorlines.current = onlyFloorlines.current.map((floorline: any) => - floorline.filter((line: any) => line[0][1] !== data.data && line[1][1] !== data.data) - ).filter((floorline: any) => floorline.length > 0); - - RemoveConnectedLines(data.data, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Point Removed!"); - } - }) - - socket.on('Line:response:delete:layer', async (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "layer deleted") { - setActiveLayer(1) - const removedLayer = data.data; - const removedLines: Types.Lines = lines.current.filter((line: any) => line[0][2] === removedLayer); - - ////////// Remove Points and lines from the removed layer ////////// - - removedLines.forEach(async (line) => { - line.forEach(async (removedPoint) => { - const removableLines: THREE.Mesh[] = []; - const connectedpoints: string[] = []; - - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints as [number, string, number][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { - connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); - removableLines.push(line as THREE.Mesh); - } - }); - - if (removableLines.length > 0) { - removableLines.forEach((line: any) => { - lines.current = lines.current.filter((item: any) => JSON.stringify(item) !== JSON.stringify(line.userData.linePoints)); - line.material.dispose(); - line.geometry.dispose(); - floorPlanGroupLine.current.remove(line); - }); - } - - const point = floorPlanGroupPoint.current.getObjectByProperty('uuid', removedPoint[1]); - if (point) { - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point) - } - }); - }); - - ////////// Update the remaining lines layer values in the userData and in lines.current ////////// - - let remaining = lines.current.filter((line: any) => line[0][2] !== removedLayer); - let updatedLines: Types.Lines = []; - remaining.forEach((line: any) => { - let newLines = JSON.parse(JSON.stringify(line)); - if (newLines[0][2] > removedLayer) { - newLines[0][2] -= 1; - newLines[1][2] -= 1; - } - - const matchingLine = floorPlanGroupLine.current.children.find((l: any) => l.userData.linePoints[0][1] === line[0][1] && l.userData.linePoints[1][1] === line[1][1]); - if (matchingLine) { - const updatedUserData = JSON.parse(JSON.stringify(matchingLine.userData)); - updatedUserData.linePoints[0][2] = newLines[0][2]; - updatedUserData.linePoints[1][2] = newLines[1][2]; - matchingLine.userData = updatedUserData; - } - updatedLines.push(newLines); - }); - - lines.current = updatedLines; - localStorage.setItem("Lines", JSON.stringify(lines.current)); - - ////////// Also remove OnlyFloorLines and update it in localstorage ////////// - - onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { - return floor[0][0][2] !== removedLayer; - }); - const meshToRemove = floorGroup.current?.children.find((mesh: any) => - mesh.name === `Only_Floor_Line_${removedLayer}` - ); - if (meshToRemove) { - meshToRemove.geometry.dispose(); - meshToRemove.material.dispose(); - floorGroup.current?.remove(meshToRemove); - } - - const zonesData = await getZonesApi(organization); - const highestLayer = Math.max( - 1, - lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), - zonesData.data.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) - ); - - setLayers(highestLayer); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Layer Removed!"); - } - }) - }, [socket]) - - useEffect(() => { - if (!socket) return - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - socket.on('wallItemsDeleteResponse', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "wallitem deleted") { - const deletedUUID = data.data.modeluuid; - let WallItemsRef = wallItems; - const Items = WallItemsRef.filter((item: any) => item.model?.uuid !== deletedUUID); - - setWallItems([]); - setTimeout(async () => { - WallItemsRef = Items; - setWallItems(WallItemsRef); - const WallItemsForStorage = WallItemsRef.map((item: any) => { - const { model, ...rest } = item; - return { - ...rest, - modeluuid: model?.uuid, - }; - }); - - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Removed!"); - }, 50); - } - }) - - socket.on('wallItemsUpdateResponse', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "wallIitem created") { - const loader = new GLTFLoader(); - loader.load(AssetConfigurations[data.data.modelname].modelUrl, async (gltf) => { - const model = gltf.scene; - model.uuid = data.data.modeluuid; - model.children[0].children.forEach((child) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } - }); - - const newWallItem = { - type: data.data.type, - model: model, - modelname: data.data.modelname, - scale: data.data.scale, - csgscale: data.data.csgscale, - csgposition: data.data.csgposition, - position: data.data.position, - quaternion: data.data.quaternion - }; - - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems, newWallItem]; - - const WallItemsForStorage = updatedItems.map(item => { - const { model, ...rest } = item; - return { - ...rest, - modeluuid: model?.uuid, - }; - }); - - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Added!"); - - return updatedItems; - }); - }); - } else if (data.message === "wallIitem updated") { - const updatedUUID = data.data.modeluuid; - - setWallItems((prevItems: any) => { - const updatedItems = prevItems.map((item: any) => { - if (item.model.uuid === updatedUUID) { - return { - ...item, - position: data.data.position, - quaternion: data.data.quaternion, - scale: data.data.scale, - csgscale: data.data.csgscale, - csgposition: data.data.csgposition, - }; - } - return item; - }); - - const WallItemsForStorage = updatedItems.map((item: any) => { - const { model, ...rest } = item; - return { - ...rest, - modeluuid: model?.uuid, - }; - }); - - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Updated!"); - - return updatedItems; - }); - - } - - }) - - return () => { - socket.off('wallItemsDeleteResponse'); - socket.off('wallItemsUpdateResponse'); - }; - }, [wallItems]) - - function getPointColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; - case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; - case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; - default: return CONSTANTS.pointConfig.defaultOuterColor; - } - } - - function getLineColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: return CONSTANTS.lineConfig.wallColor; - case CONSTANTS.lineConfig.floorName: return CONSTANTS.lineConfig.floorColor; - case CONSTANTS.lineConfig.aisleName: return CONSTANTS.lineConfig.aisleColor; - default: return CONSTANTS.lineConfig.defaultColor; - } - } - - useEffect(() => { - if (!socket) return - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - socket.on('Line:response:create', async (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "line create") { - const line: Types.Line = objectLineToArray(data.data); - const type = line[0][3]; - const pointColour = getPointColor(type); - const lineColour = getLineColor(type); - setNewLines([line]) - - line.forEach((line) => { - const existingPoint = floorPlanGroupPoint.current?.getObjectByProperty('uuid', line[1]); - if (existingPoint) { - return; - } - const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); - const material = new THREE.ShaderMaterial({ - uniforms: { - uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border - uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square - }, - vertexShader: ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uColor; - uniform vec3 uInnerColor; - - void main() { - // Define the size of the white square as a proportion of the face - float borderThickness = 0.2; // Adjust this value for border thickness - if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && - vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { - gl_FragColor = vec4(uInnerColor, 1.0); // White inner square - } else { - gl_FragColor = vec4(uColor, 1.0); // Blue border - } - } - `, - }); - const point = new THREE.Mesh(geometry, material); - point.name = "point"; - point.uuid = line[1]; - point.userData = { type: type, color: pointColour }; - point.position.set(line[0].x, line[0].y, line[0].z); - currentLayerPoint.current.push(point); - - floorPlanGroupPoint.current?.add(point); - }) - if (dragPointControls.current) { - dragPointControls.current!.objects = currentLayerPoint.current; - } - addLineToScene( - new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), - new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), - lineColour, - line, - floorPlanGroupLine - ) - lines.current.push(line); - - const zonesData = await getZonesApi(organization); - const highestLayer = Math.max( - 1, - lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), - zonesData.data.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) - ); - - setLayers(highestLayer); - - Layer2DVisibility(activeLayer, floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls) - - loadWalls(lines, setWalls); - setUpdateScene(true); - } - }) - - return () => { - socket.off('Line:response:create'); - }; - }, [socket, activeLayer]) - - useEffect(() => { - if (!socket) return - const email = localStorage.getItem('email'); - const organization = (email!.split("@")[1]).split(".")[0]; - - socket.on('zone:response:updates', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - - if (data.message === "zone created") { - const pointsArray: [number, number, number][] = data.data.points; - const vector3Array = pointsArray.map(([x, y, z]) => new THREE.Vector3(x, y, z)); - const newZones = [...zones, data.data]; - setZones(newZones); - const updatedZonePoints = [...zonePoints, ...vector3Array]; - setZonePoints(updatedZonePoints); - - const highestLayer = Math.max( - 1, - lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), - newZones.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) - ); - - setLayers(highestLayer); - setUpdateScene(true); - } - - if (data.message === "zone updated") { - const updatedZones = zones.map((zone: any) => - zone.zoneId === data.data.zoneId ? data.data : zone - ); - setZones(updatedZones); - setUpdateScene(true); - } - - - }) - - socket.on('zone:response:delete', (data: any) => { - if (socket.id === data.socketId) { - return - } - if (organization !== data.organization) { - return - } - if (data.message === "zone deleted") { - const updatedZones = zones.filter((zone: any) => zone.zoneId !== data.data.zoneId); - setZones(updatedZones); - - const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === data.data.zoneId); - if (zoneIndex !== -1) { - const updatedzonePoints = zonePoints.filter((_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4); - setZonePoints(updatedzonePoints); - } - - const highestLayer = Math.max( - 1, - lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), - updatedZones.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) - ); - - setLayers(highestLayer); - setUpdateScene(true); - } - }) - - return () => { - socket.off('zone:response:updates'); - socket.off('zone:response:delete'); - }; - }, [socket, zones, zonePoints]) - - return ( - <> - ) +import { useEffect } from "react"; +import * as THREE from 'three'; +import gsap from 'gsap'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; +import { toast } from 'react-toastify'; +import { useSocketStore, useActiveLayer, useWallItems, useFloorItems, useLayers, useUpdateScene, useWalls, useDeletedLines, useNewLines, useZonePoints, useZones } from "../../../store/store"; + +import * as Types from "../../../types/world/worldTypes"; +import * as CONSTANTS from '../../../types/world/worldConstants'; +import TempLoader from "../../builder/geomentries/assets/tempLoader"; + +// import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; +import objectLineToArray from "../../builder/geomentries/lines/lineConvertions/objectLineToArray"; +import addLineToScene from "../../builder/geomentries/lines/addLineToScene"; +import updateLinesPositions from "../../builder/geomentries/lines/updateLinesPositions"; +import updateLines from "../../builder/geomentries/lines/updateLines"; +import updateDistanceText from "../../builder/geomentries/lines/updateDistanceText"; +import updateFloorLines from "../../builder/geomentries/floors/updateFloorLines"; +import loadWalls from "../../builder/geomentries/walls/loadWalls"; +import RemoveConnectedLines from "../../builder/geomentries/lines/removeConnectedLines"; +import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; +import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; +import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; + + +export default function SocketResponses({ + floorPlanGroup, + lines, + floorGroup, + floorGroupAisle, + scene, + onlyFloorlines, + AssetConfigurations, + itemsGroup, + isTempLoader, + tempLoader, + currentLayerPoint, + floorPlanGroupPoint, + floorPlanGroupLine, + zoneGroup, + dragPointControls +}: any) { + + const { socket } = useSocketStore(); + const { activeLayer, setActiveLayer } = useActiveLayer(); + const { wallItems, setWallItems } = useWallItems(); + const { layers, setLayers } = useLayers(); + const { floorItems, setFloorItems } = useFloorItems(); + const { updateScene, setUpdateScene } = useUpdateScene(); + const { walls, setWalls } = useWalls(); + const { deletedLines, setDeletedLines } = useDeletedLines(); + const { newLines, setNewLines } = useNewLines(); + const { zones, setZones } = useZones(); + const { zonePoints, setZonePoints } = useZonePoints(); + + useEffect(() => { + + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + if (!socket) return + + socket.on('cameraCreateResponse', (data: any) => { + // console.log('data: ', data); + }) + + socket.on('userConnectRespones', (data: any) => { + // console.log('data: ', data); + }) + + socket.on('userDisConnectRespones', (data: any) => { + // console.log('data: ', data); + }) + + socket.on('cameraUpdateResponse', (data: any) => { + // console.log('data: ', data); + }) + + socket.on('EnvironmentUpdateResponse', (data: any) => { + // console.log('data: ', data); + }) + + socket.on('model-asset:response:updates', async (data: any) => { + // console.log('data: ', data); + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "Model created successfully") { + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + + dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + loader.setDRACOLoader(dracoLoader); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + + try { + isTempLoader.current = true; + const cachedModel = THREE.Cache.get(data.data.modelname); + let url; + if (cachedModel) { + // 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, 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); + + model.traverse((child: any) => { + if (child.isMesh) { + // Clone the material to ensure changes are independent + // child.material = child.material.clone(); + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + itemsGroup.current.add(model); + + if (tempLoader.current) { + tempLoader.current.material.dispose(); + tempLoader.current.geometry.dispose(); + itemsGroup.current.remove(tempLoader.current); + tempLoader.current = undefined; + } + + const newFloorItem: Types.FloorItemType = { + modeluuid: data.data.modeluuid, + modelname: data.data.modelname, + modelfileID: data.data.modelfileID, + position: [...data.data.position as [number, number, number]], + rotation: { + x: model.rotation.x, + y: model.rotation.y, + z: model.rotation.z, + }, + isLocked: data.data.isLocked, + isVisible: data.data.isVisible, + }; + + setFloorItems((prevItems: any) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + gsap.to(model.position, { y: data.data.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!") } }); + + } else { + const indexedDBModel = await retrieveGLTF(data.data.modelname); + if (indexedDBModel) { + // console.log(`Getting ${data.data.modelname} from IndexedDB`); + url = URL.createObjectURL(indexedDBModel); + } else { + // console.log(`Getting ${data.data.modelname} from Backend`); + url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; + const modelBlob = await fetch(url).then((res) => res.blob()); + await storeGLTF(data.data.modelfileID, modelBlob); + } + } + + if (url) { + loadModel(url); + } + + } catch (error) { + console.error('Error fetching asset model:', error); + } + + function loadModel(url: string) { + loader.load(url, (gltf) => { + URL.revokeObjectURL(url); + THREE.Cache.remove(url); + const model = gltf.scene; + model.uuid = data.data.modeluuid; + 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); + + model.traverse((child: any) => { + if (child.isMesh) { + // Clone the material to ensure changes are independent + // child.material = child.material.clone(); + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + itemsGroup.current.add(model); + + if (tempLoader.current) { + tempLoader.current.material.dispose(); + tempLoader.current.geometry.dispose(); + itemsGroup.current.remove(tempLoader.current); + tempLoader.current = undefined; + } + + const newFloorItem: Types.FloorItemType = { + modeluuid: data.data.modeluuid, + modelname: data.data.modelname, + modelfileID: data.data.modelfileID, + position: [...data.data.position as [number, number, number]], + rotation: { + x: model.rotation.x, + y: model.rotation.y, + z: model.rotation.z, + }, + isLocked: data.data.isLocked, + isVisible: data.data.isVisible, + }; + + setFloorItems((prevItems: any) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + gsap.to(model.position, { y: data.data.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!") } }); + + THREE.Cache.add(data.data.modelname, gltf); + }, () => { + TempLoader(new THREE.Vector3(...data.data.position), isTempLoader, tempLoader, itemsGroup); + }); + } + + } else if (data.message === "Model updated successfully") { + itemsGroup.current?.children.forEach((item: THREE.Group) => { + if (item.uuid === data.data.modeluuid) { + item.position.set(...data.data.position as [number, number, number]); + item.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z); + } + }) + + setFloorItems((prevItems: Types.FloorItems) => { + if (!prevItems) { + return + } + let updatedItem: any = null; + const updatedItems = prevItems.map((item) => { + if (item.modeluuid === data.data.modeluuid) { + updatedItem = { + ...item, + position: [...data.data.position] as [number, number, number], + rotation: { x: data.data.rotation.x, y: data.data.rotation.y, z: data.data.rotation.z, }, + }; + return updatedItem; + } + return item; + }); + return updatedItems; + }) + } + }) + + socket.on('model-asset:response:updates', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "Model deleted successfully") { + const deletedUUID = data.data.modeluuid; + let items = JSON.parse(localStorage.getItem("FloorItems")!); + + const updatedItems = items.filter( + (item: { modeluuid: string }) => item.modeluuid !== deletedUUID + ); + + const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); + const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => item.modeluuid !== deletedUUID); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + itemsGroup.current.children.forEach((item: any) => { + if (item.uuid === deletedUUID) { + itemsGroup.current.remove(item); + } + }) + setFloorItems(updatedItems); + toast.success("Model Removed!"); + } + }) + + socket.on('Line:response:update', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "line updated") { + const DraggedUUID = data.data.uuid; + const DraggedPosition = new THREE.Vector3(data.data.position.x, data.data.position.y, data.data.position.z); + + const point = floorPlanGroupPoint.current.getObjectByProperty('uuid', DraggedUUID); + point.position.set(DraggedPosition.x, DraggedPosition.y, DraggedPosition.z); + const affectedLines = updateLinesPositions({ uuid: DraggedUUID, position: DraggedPosition }, lines); + + updateLines(floorPlanGroupLine, affectedLines); + updateDistanceText(scene, floorPlanGroupLine, affectedLines); + updateFloorLines(onlyFloorlines, { uuid: DraggedUUID, position: DraggedPosition }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + } + }) + + socket.on('Line:response:delete', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "line deleted") { + const line = objectLineToArray(data.data); + const linePoints = line; + const connectedpoints = [linePoints[0][1], linePoints[1][1]]; + + + onlyFloorlines.current = onlyFloorlines.current.map((floorline: any) => + floorline.filter((line: any) => line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1]) + ).filter((floorline: any) => floorline.length > 0); + + const removedLine = lines.current.find((item: any) => (item[0][1] === linePoints[0][1] && item[1][1] === linePoints[1][1] || (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]))); + lines.current = lines.current.filter((item: any) => item !== removedLine); + + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints as [number, string, number][]; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + + if ((uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1] || (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]))) { + line.material.dispose(); + line.geometry.dispose(); + floorPlanGroupLine.current.remove(line); + setDeletedLines([line.userData.linePoints]) + } + }); + + connectedpoints.forEach((pointUUID) => { + let isConnected = false; + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + if (uuid1 === pointUUID || uuid2 === pointUUID) { + isConnected = true; + } + }); + + if (!isConnected) { + floorPlanGroupPoint.current.children.forEach((point: any) => { + if (point.uuid === pointUUID) { + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point); + } + }); + } + }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + toast.success("Line Removed!"); + } + }) + + socket.on('Line:response:delete:point', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "point deleted") { + const point = floorPlanGroupPoint.current?.getObjectByProperty('uuid', data.data); + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point); + + onlyFloorlines.current = onlyFloorlines.current.map((floorline: any) => + floorline.filter((line: any) => line[0][1] !== data.data && line[1][1] !== data.data) + ).filter((floorline: any) => floorline.length > 0); + + RemoveConnectedLines(data.data, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + toast.success("Point Removed!"); + } + }) + + socket.on('Line:response:delete:layer', async (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "layer deleted") { + setActiveLayer(1) + const removedLayer = data.data; + const removedLines: Types.Lines = lines.current.filter((line: any) => line[0][2] === removedLayer); + + ////////// Remove Points and lines from the removed layer ////////// + + removedLines.forEach(async (line) => { + line.forEach(async (removedPoint) => { + const removableLines: THREE.Mesh[] = []; + const connectedpoints: string[] = []; + + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints as [number, string, number][]; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + + if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { + connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); + removableLines.push(line as THREE.Mesh); + } + }); + + if (removableLines.length > 0) { + removableLines.forEach((line: any) => { + lines.current = lines.current.filter((item: any) => JSON.stringify(item) !== JSON.stringify(line.userData.linePoints)); + line.material.dispose(); + line.geometry.dispose(); + floorPlanGroupLine.current.remove(line); + }); + } + + const point = floorPlanGroupPoint.current.getObjectByProperty('uuid', removedPoint[1]); + if (point) { + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point) + } + }); + }); + + ////////// Update the remaining lines layer values in the userData and in lines.current ////////// + + let remaining = lines.current.filter((line: any) => line[0][2] !== removedLayer); + let updatedLines: Types.Lines = []; + remaining.forEach((line: any) => { + let newLines = JSON.parse(JSON.stringify(line)); + if (newLines[0][2] > removedLayer) { + newLines[0][2] -= 1; + newLines[1][2] -= 1; + } + + const matchingLine = floorPlanGroupLine.current.children.find((l: any) => l.userData.linePoints[0][1] === line[0][1] && l.userData.linePoints[1][1] === line[1][1]); + if (matchingLine) { + const updatedUserData = JSON.parse(JSON.stringify(matchingLine.userData)); + updatedUserData.linePoints[0][2] = newLines[0][2]; + updatedUserData.linePoints[1][2] = newLines[1][2]; + matchingLine.userData = updatedUserData; + } + updatedLines.push(newLines); + }); + + lines.current = updatedLines; + localStorage.setItem("Lines", JSON.stringify(lines.current)); + + ////////// Also remove OnlyFloorLines and update it in localstorage ////////// + + onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { + return floor[0][0][2] !== removedLayer; + }); + const meshToRemove = floorGroup.current?.children.find((mesh: any) => + mesh.name === `Only_Floor_Line_${removedLayer}` + ); + if (meshToRemove) { + meshToRemove.geometry.dispose(); + meshToRemove.material.dispose(); + floorGroup.current?.remove(meshToRemove); + } + + const zonesData = await getZonesApi(organization); + const highestLayer = Math.max( + 1, + lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), + zonesData.data.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) + ); + + setLayers(highestLayer); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + toast.success("Layer Removed!"); + } + }) + }, [socket]) + + useEffect(() => { + if (!socket) return + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + socket.on('wallItemsDeleteResponse', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "wallitem deleted") { + const deletedUUID = data.data.modeluuid; + let WallItemsRef = wallItems; + const Items = WallItemsRef.filter((item: any) => item.model?.uuid !== deletedUUID); + + setWallItems([]); + setTimeout(async () => { + WallItemsRef = Items; + setWallItems(WallItemsRef); + const WallItemsForStorage = WallItemsRef.map((item: any) => { + const { model, ...rest } = item; + return { + ...rest, + modeluuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + toast.success("Model Removed!"); + }, 50); + } + }) + + socket.on('wallItemsUpdateResponse', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "wallIitem created") { + const loader = new GLTFLoader(); + loader.load(AssetConfigurations[data.data.modelname].modelUrl, async (gltf) => { + const model = gltf.scene; + model.uuid = data.data.modeluuid; + model.children[0].children.forEach((child) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + const newWallItem = { + type: data.data.type, + model: model, + modelname: data.data.modelname, + scale: data.data.scale, + csgscale: data.data.csgscale, + csgposition: data.data.csgposition, + position: data.data.position, + quaternion: data.data.quaternion + }; + + setWallItems((prevItems: any) => { + const updatedItems = [...prevItems, newWallItem]; + + const WallItemsForStorage = updatedItems.map(item => { + const { model, ...rest } = item; + return { + ...rest, + modeluuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + toast.success("Model Added!"); + + return updatedItems; + }); + }); + } else if (data.message === "wallIitem updated") { + const updatedUUID = data.data.modeluuid; + + setWallItems((prevItems: any) => { + const updatedItems = prevItems.map((item: any) => { + if (item.model.uuid === updatedUUID) { + return { + ...item, + position: data.data.position, + quaternion: data.data.quaternion, + scale: data.data.scale, + csgscale: data.data.csgscale, + csgposition: data.data.csgposition, + }; + } + return item; + }); + + const WallItemsForStorage = updatedItems.map((item: any) => { + const { model, ...rest } = item; + return { + ...rest, + modeluuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + toast.success("Model Updated!"); + + return updatedItems; + }); + + } + + }) + + return () => { + socket.off('wallItemsDeleteResponse'); + socket.off('wallItemsUpdateResponse'); + }; + }, [wallItems]) + + function getPointColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; + case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; + case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; + default: return CONSTANTS.pointConfig.defaultOuterColor; + } + } + + function getLineColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: return CONSTANTS.lineConfig.wallColor; + case CONSTANTS.lineConfig.floorName: return CONSTANTS.lineConfig.floorColor; + case CONSTANTS.lineConfig.aisleName: return CONSTANTS.lineConfig.aisleColor; + default: return CONSTANTS.lineConfig.defaultColor; + } + } + + useEffect(() => { + if (!socket) return + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + socket.on('Line:response:create', async (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "line create") { + const line: Types.Line = objectLineToArray(data.data); + const type = line[0][3]; + const pointColour = getPointColor(type); + const lineColour = getLineColor(type); + setNewLines([line]) + + line.forEach((line) => { + const existingPoint = floorPlanGroupPoint.current?.getObjectByProperty('uuid', line[1]); + if (existingPoint) { + return; + } + const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); + const material = new THREE.ShaderMaterial({ + uniforms: { + uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border + uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square + }, + vertexShader: ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + varying vec2 vUv; + uniform vec3 uColor; + uniform vec3 uInnerColor; + + void main() { + // Define the size of the white square as a proportion of the face + float borderThickness = 0.2; // Adjust this value for border thickness + if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && + vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { + gl_FragColor = vec4(uInnerColor, 1.0); // White inner square + } else { + gl_FragColor = vec4(uColor, 1.0); // Blue border + } + } + `, + }); + const point = new THREE.Mesh(geometry, material); + point.name = "point"; + point.uuid = line[1]; + point.userData = { type: type, color: pointColour }; + point.position.set(line[0].x, line[0].y, line[0].z); + currentLayerPoint.current.push(point); + + floorPlanGroupPoint.current?.add(point); + }) + if (dragPointControls.current) { + dragPointControls.current!.objects = currentLayerPoint.current; + } + addLineToScene( + new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), + new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), + lineColour, + line, + floorPlanGroupLine + ) + lines.current.push(line); + + const zonesData = await getZonesApi(organization); + const highestLayer = Math.max( + 1, + lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), + zonesData.data.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) + ); + + setLayers(highestLayer); + + Layer2DVisibility(activeLayer, floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls) + + loadWalls(lines, setWalls); + setUpdateScene(true); + } + }) + + return () => { + socket.off('Line:response:create'); + }; + }, [socket, activeLayer]) + + useEffect(() => { + if (!socket) return + const email = localStorage.getItem('email'); + const organization = (email!.split("@")[1]).split(".")[0]; + + socket.on('zone:response:updates', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + + if (data.message === "zone created") { + const pointsArray: [number, number, number][] = data.data.points; + const vector3Array = pointsArray.map(([x, y, z]) => new THREE.Vector3(x, y, z)); + const newZones = [...zones, data.data]; + setZones(newZones); + const updatedZonePoints = [...zonePoints, ...vector3Array]; + setZonePoints(updatedZonePoints); + + const highestLayer = Math.max( + 1, + lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), + newZones.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) + ); + + setLayers(highestLayer); + setUpdateScene(true); + } + + if (data.message === "zone updated") { + const updatedZones = zones.map((zone: any) => + zone.zoneId === data.data.zoneId ? data.data : zone + ); + setZones(updatedZones); + setUpdateScene(true); + } + + + }) + + socket.on('zone:response:delete', (data: any) => { + if (socket.id === data.socketId) { + return + } + if (organization !== data.organization) { + return + } + if (data.message === "zone deleted") { + const updatedZones = zones.filter((zone: any) => zone.zoneId !== data.data.zoneId); + setZones(updatedZones); + + const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === data.data.zoneId); + if (zoneIndex !== -1) { + const updatedzonePoints = zonePoints.filter((_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4); + setZonePoints(updatedzonePoints); + } + + const highestLayer = Math.max( + 1, + lines.current.reduce((maxLayer: number, segment: any) => Math.max(maxLayer, segment.layer || 0), 0), + updatedZones.reduce((maxLayer: number, zone: any) => Math.max(maxLayer, zone.layer || 0), 0) + ); + + setLayers(highestLayer); + setUpdateScene(true); + } + }) + + return () => { + socket.off('zone:response:updates'); + socket.off('zone:response:delete'); + }; + }, [socket, zones, zonePoints]) + + return ( + <> + ) } \ No newline at end of file diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index 0e7581f..8bdd3aa 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -9,128 +9,135 @@ import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi" import updateCamPosition from "../camera/updateCameraPosition"; import CamMode from "../camera/camMode"; import SwitchView from "../camera/switchView"; +import SelectionControls from "./selectionControls/selectionControls"; export default function Controls() { - const controlsRef = useRef(null); + const controlsRef = useRef(null); - const { toggleView } = useToggleView(); - const { resetCamera, setResetCamera } = useResetCamera(); - const { socket } = useSocketStore(); - const state = useThree(); + const { toggleView } = useToggleView(); + const { resetCamera, setResetCamera } = useResetCamera(); + const { socket } = useSocketStore(); + const state = useThree(); - useEffect(() => { - if (controlsRef.current) { - (controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; - (controlsRef.current as any).mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; - } - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - getCamera(organization, localStorage.getItem("userId")!).then((data) => { - if (data && data.position && data.target) { - controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z); - controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z); - } else { - controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); - controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); - } - }) - .catch((error) => console.error("Failed to fetch camera data:", error)); - }, []); + useEffect(() => { + if (controlsRef.current) { + (controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; + (controlsRef.current as any).mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; + } + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + getCamera(organization, localStorage.getItem("userId")!).then((data) => { + if (data && data.position && data.target) { + controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z); + controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z); + } else { + controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); + controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); + } + }) + .catch((error) => console.error("Failed to fetch camera data:", error)); + }, []); - useEffect(() => { - if (resetCamera) { - controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); - controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); - controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth); + useEffect(() => { + if (resetCamera) { + controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); + controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); + controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth); - localStorage.setItem("cameraPosition", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition))); - localStorage.setItem("controlTarget", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget))); + localStorage.setItem("cameraPosition", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition))); + localStorage.setItem("controlTarget", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget))); - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; - const camData = { - organization: organization, - userId: localStorage.getItem('userId')!, - position: new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition), - target: new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget), - rotation: new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation), - socketId: socket.id - }; - socket.emit('v1:Camera:set', camData) + const camData = { + organization: organization, + userId: localStorage.getItem('userId')!, + position: new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition), + target: new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget), + rotation: new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation), + socketId: socket.id + }; + socket.emit('v1:Camera:set', camData) - setResetCamera(false); - } - }, [resetCamera]); + setResetCamera(false); + } + }, [resetCamera]); - useEffect(() => { - controlsRef.current?.setBoundary(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop))); - // state.scene.add(new THREE.Box3Helper(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop)), 0xffff00)); - let hasInteracted = false; - let intervalId: NodeJS.Timeout | null = null; + useEffect(() => { + controlsRef.current?.setBoundary(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop))); + // state.scene.add(new THREE.Box3Helper(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop)), 0xffff00)); + let hasInteracted = false; + let intervalId: NodeJS.Timeout | null = null; - const handleRest = () => { - if (hasInteracted && controlsRef.current && state.camera.position && !toggleView) { - const position = state.camera.position; - if (position.x === 0 && position.y === 0 && position.z === 0) return; - updateCamPosition(controlsRef, socket, position, state.camera.rotation); - stopInterval(); - } - }; + const handleRest = () => { + if (hasInteracted && controlsRef.current && state.camera.position && !toggleView) { + const position = state.camera.position; + if (position.x === 0 && position.y === 0 && position.z === 0) return; + updateCamPosition(controlsRef, socket, position, state.camera.rotation); + stopInterval(); + } + }; - const startInterval = () => { - hasInteracted = true; - if (!intervalId) { - intervalId = setInterval(() => { - if (controlsRef.current && !toggleView) { - handleRest(); - } - }, CONSTANTS.camPositionUpdateInterval); - } - }; + const startInterval = () => { + hasInteracted = true; + if (!intervalId) { + intervalId = setInterval(() => { + if (controlsRef.current && !toggleView) { + handleRest(); + } + }, CONSTANTS.camPositionUpdateInterval); + } + }; - const stopInterval = () => { - if (intervalId) { - clearInterval(intervalId); - intervalId = null; - } - }; + const stopInterval = () => { + if (intervalId) { + clearInterval(intervalId); + intervalId = null; + } + }; - const controls = controlsRef.current; - if (controls) { - controls.addEventListener("sleep", handleRest); - controls.addEventListener("control", startInterval); - controls.addEventListener("controlend", stopInterval); - } + const controls = controlsRef.current; + if (controls) { + controls.addEventListener("sleep", handleRest); + controls.addEventListener("control", startInterval); + controls.addEventListener("controlend", stopInterval); + } - return () => { - if (controls) { - controls.removeEventListener("sleep", handleRest); - controls.removeEventListener("control", startInterval); - controls.removeEventListener("controlend", stopInterval); - } - stopInterval(); - }; - }, [toggleView, state, socket]); + return () => { + if (controls) { + controls.removeEventListener("sleep", handleRest); + controls.removeEventListener("control", startInterval); + controls.removeEventListener("controlend", stopInterval); + } + stopInterval(); + }; + }, [toggleView, state, socket]); - return ( - <> - - - - - - ); + return ( + <> + + + + + + + + + + + + ); } \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx deleted file mode 100644 index 1f83513..0000000 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ /dev/null @@ -1,581 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useMemo } from "react"; -import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSimulationStates, 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"; -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) => { - const { camera, controls, gl, scene, pointer, raycaster } = useThree(); - const { toggleView } = useToggleView(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - 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; - canvasElement.tabIndex = 0; - - let isMoving = false; - - const onPointerDown = () => { - isMoving = false; - }; - - const onPointerMove = () => { - isMoving = true; - }; - - const onPointerUp = (event: PointerEvent) => { - if (!isMoving && pastedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { - event.preventDefault(); - addPastedObjects(); - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - const keyCombination = detectModifierKeys(event); - - if (keyCombination === "Ctrl+C" && movedObjects.length === 0 && rotatedObjects.length === 0) { - copySelection(); - } - if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { - pasteCopiedObjects(); - } - }; - - if (!toggleView) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - canvasElement.addEventListener("keydown", onKeyDown); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - }; - - }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, floorItems, rotatedObjects]); - - useFrame(() => { - if (pastedObjects.length > 0) { - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - if (point) { - const position = new THREE.Vector3(); - if (boundingBoxRef.current) { - boundingBoxRef.current?.getWorldPosition(position) - selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); - } else { - const box = new THREE.Box3(); - pastedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); - const center = new THREE.Vector3(); - box.getCenter(center); - selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); - } - } - } - }); - - const copySelection = () => { - if (selectedAssets.length > 0) { - const newClones = selectedAssets.map((asset: any) => { - const clone = asset.clone(); - clone.position.copy(asset.position); - return clone; - }); - setCopiedObjects(newClones); - toast.info("Objects copied!"); - } - }; - - const pasteCopiedObjects = () => { - if (copiedObjects.length > 0 && pastedObjects.length === 0) { - const newClones = copiedObjects.map((obj: THREE.Object3D) => { - const clone = obj.clone(); - clone.position.copy(obj.position); - return clone; - }); - selectionGroup.current.add(...newClones); - setpastedObjects([...newClones]); - setSelectedAssets([...newClones]); - - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - - if (point) { - const position = new THREE.Vector3(); - if (boundingBoxRef.current) { - boundingBoxRef.current?.getWorldPosition(position) - selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); - } else { - const box = new THREE.Box3(); - newClones.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); - const center = new THREE.Vector3(); - box.getCenter(center); - selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); - } - } - } - }; - - const addPastedObjects = () => { - if (pastedObjects.length === 0) return; - pastedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); - obj.position.copy(worldPosition); - - if (itemsGroupRef.current) { - - const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, - modelfileID: obj.userData.modelId, - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, - isLocked: false, - isVisible: true - }; - - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema | undefined = simulationStates.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 SimulationTypes.ConveyorEventsSchema)?.points[index].actions.length > 0; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - type: 'Inherit', - material: 'Inherit', - delay: 'Inherit', - spawnInterval: 'Inherit', - isUsed: true - }; - - return { - uuid: pointUUID, - position: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].position, - rotation: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].rotation, - actions: hasActions - ? (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].actions.map(action => ({ - ...action, - uuid: THREE.MathUtils.generateUUID() - })) - : [defaultAction], - triggers: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({ - ...trigger, - uuid: THREE.MathUtils.generateUUID() - })), - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - } - }; - }; - - const backendEventData = { - type: 'Conveyor', - points: [ - createConveyorPoint(0), // point1 - createConveyorPoint(1), // middlePoint - createConveyorPoint(2) // point2 - ], - speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed - }; - - //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: { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.ConveyorEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'Vehicle' && eventData) { - const createVehiclePoint = () => { - const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.VehicleEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - type: 'Inherit', - start: {}, - hitCount: 0, - end: {}, - buffer: 0 - }; - - return { - uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, - actions: hasActions - ? { - ...vehiclePoint.actions, - uuid: THREE.MathUtils.generateUUID() - } - : defaultAction, - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - }, - speed: vehiclePoint?.speed || 1 - }; - }; - - const backendEventData = { - type: 'Vehicle', - points: createVehiclePoint(), - }; - - // API - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.VehicleEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'StaticMachine' && eventData) { - const createStaticMachinePoint = () => { - const pointUUID = THREE.MathUtils.generateUUID(); - const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; - const hasActions = staticMachinePoint?.actions !== undefined; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - buffer: 0, - material: 'Inherit', - }; - - return { - uuid: pointUUID, - position: staticMachinePoint?.position, - rotation: staticMachinePoint?.rotation, - actions: hasActions - ? { - ...staticMachinePoint.actions, - uuid: THREE.MathUtils.generateUUID() - } - : defaultAction, - triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - } - }; - }; - - const backendEventData = { - type: 'StaticMachine', - points: createStaticMachinePoint() - }; - - // API - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.StaticMachineEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'ArmBot' && eventData) { - const createArmBotPoint = () => { - const pointUUID = THREE.MathUtils.generateUUID(); - const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; - const hasActions = armBotPoint?.actions !== undefined; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - buffer: 0, - material: 'Inherit', - }; - - return { - uuid: pointUUID, - position: armBotPoint?.position, - rotation: armBotPoint?.rotation, - actions: hasActions - ? { - ...armBotPoint.actions, - uuid: THREE.MathUtils.generateUUID(), - processes: [] - } - : defaultAction, - triggers: { - uuid: THREE.MathUtils.generateUUID(), - name: armBotPoint.triggers.name, - type: armBotPoint.triggers.type, - }, - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - } - }; - }; - - const backendEventData = { - type: 'ArmBot', - points: createArmBotPoint() - }; - - // API - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.ArmBotEventsSchema - ]); - - 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); - - } - } 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); - - } - - obj.userData.modeluuid = newFloorItem.modeluuid; - itemsGroupRef.current.add(obj); - } - }); - - toast.success("Object added!"); - clearSelection(); - }; - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setMovedObjects([]); - setpastedObjects([]); - setDuplicatedObjects([]); - setRotatedObjects([]); - setSelectedAssets([]); - } - - return null; // No visible output, but the component handles copy-paste functionality -}; - -export default CopyPasteControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx deleted file mode 100644 index 915cb61..0000000 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ /dev/null @@ -1,560 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useMemo } from "react"; -import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSimulationStates, 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"; -import * as SimulationTypes from "../../../../types/simulationTypes"; -import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; -import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; - -const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { - const { camera, controls, gl, scene, pointer, raycaster } = useThree(); - const { toggleView } = useToggleView(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - 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; - canvasElement.tabIndex = 0; - - let isMoving = false; - - const onPointerDown = () => { - isMoving = false; - }; - - const onPointerMove = () => { - isMoving = true; - }; - - const onPointerUp = (event: PointerEvent) => { - if (!isMoving && duplicatedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { - event.preventDefault(); - addDuplicatedAssets(); - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - const keyCombination = detectModifierKeys(event); - - if (keyCombination === "Ctrl+D" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { - duplicateSelection(); - } - }; - - if (!toggleView) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - canvasElement.addEventListener("keydown", onKeyDown); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - }; - - }, [camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]); - - useFrame(() => { - if (duplicatedObjects.length > 0) { - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - if (point) { - const position = new THREE.Vector3(); - if (boundingBoxRef.current) { - boundingBoxRef.current?.getWorldPosition(position) - selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); - } else { - const box = new THREE.Box3(); - duplicatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); - const center = new THREE.Vector3(); - box.getCenter(center); - selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); - } - } - } - }); - - const duplicateSelection = () => { - if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { - const newClones = selectedAssets.map((asset: any) => { - const clone = asset.clone(); - clone.position.copy(asset.position); - return clone; - }); - - selectionGroup.current.add(...newClones); - setDuplicatedObjects(newClones); - - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - - if (point) { - const position = new THREE.Vector3(); - boundingBoxRef.current?.getWorldPosition(position) - selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); - } - } - }; - - const addDuplicatedAssets = () => { - if (duplicatedObjects.length === 0) return; - duplicatedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); - obj.position.copy(worldPosition); - - if (itemsGroupRef.current) { - - const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, - modelfileID: obj.userData.modelId, - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, - isLocked: false, - isVisible: true - }; - - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema | undefined = simulationStates.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 SimulationTypes.ConveyorEventsSchema)?.points[index].actions.length > 0; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - type: 'Inherit', - material: 'Inherit', - delay: 'Inherit', - spawnInterval: 'Inherit', - isUsed: true - }; - - return { - uuid: pointUUID, - position: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].position, - rotation: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].rotation, - actions: hasActions - ? (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].actions.map(action => ({ - ...action, - uuid: THREE.MathUtils.generateUUID() - })) - : [defaultAction], - triggers: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({ - ...trigger, - uuid: THREE.MathUtils.generateUUID() - })), - connections: { - source: { modelUUID: newFloorItem.modeluuid, pointUUID }, - targets: [] - } - }; - }; - - const backendEventData = { - type: 'Conveyor', - points: [ - createConveyorPoint(0), - createConveyorPoint(1), - createConveyorPoint(2) - ], - speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed - }; - - //REST - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.ConveyorEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'Vehicle' && eventData) { - const createVehiclePoint = () => { - const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.VehicleEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - type: 'Inherit', - start: {}, - hitCount: 0, - end: {}, - buffer: 0 - }; - - return { - uuid: pointUUID, - position: vehiclePoint?.position, - rotation: vehiclePoint?.rotation, - actions: hasActions - ? { - ...vehiclePoint.actions, - uuid: THREE.MathUtils.generateUUID() - } - : defaultAction, - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - }, - speed: vehiclePoint?.speed || 2 - }; - }; - - const backendEventData = { - type: 'Vehicle', - points: createVehiclePoint() - }; - - // API - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.VehicleEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'StaticMachine' && eventData) { - const createStaticMachinePoint = () => { - const pointUUID = THREE.MathUtils.generateUUID(); - const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; - const hasActions = staticMachinePoint?.actions !== undefined; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - buffer: 0, - material: 'Inherit', - }; - - return { - uuid: pointUUID, - position: staticMachinePoint?.position, - rotation: staticMachinePoint?.rotation, - actions: hasActions - ? { - ...staticMachinePoint.actions, - uuid: THREE.MathUtils.generateUUID() - } - : defaultAction, - triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - } - }; - }; - - const backendEventData = { - type: 'StaticMachine', - points: createStaticMachinePoint() - }; - - // API - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.StaticMachineEventsSchema - ]); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'ArmBot' && eventData) { - const createArmBotPoint = () => { - const pointUUID = THREE.MathUtils.generateUUID(); - const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; - const hasActions = armBotPoint?.actions !== undefined; - - const defaultAction = { - uuid: THREE.MathUtils.generateUUID(), - name: 'Action 1', - buffer: 0, - material: 'Inherit', - }; - - return { - uuid: pointUUID, - position: armBotPoint?.position, - rotation: armBotPoint?.rotation, - actions: hasActions - ? { - ...armBotPoint.actions, - uuid: THREE.MathUtils.generateUUID(), - processes: [] - } - : defaultAction, - triggers: { - uuid: THREE.MathUtils.generateUUID(), - name: armBotPoint.triggers.name, - type: armBotPoint.triggers.type, - }, - connections: { - source: { modelUUID: obj.uuid, pointUUID }, - targets: [] - } - }; - }; - - const backendEventData = { - type: 'ArmBot', - points: createArmBotPoint() - }; - - // API - - // setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - // 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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [ - ...(prevEvents || []), - newEventData as SimulationTypes.ArmBotEventsSchema - ]); - - 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); - - } - } 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); - - } - - obj.userData.modeluuid = newFloorItem.modeluuid; - itemsGroupRef.current.add(obj); - } - }); - - toast.success("Object duplicated!"); - clearSelection(); - } - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setMovedObjects([]); - setpastedObjects([]); - setDuplicatedObjects([]); - setRotatedObjects([]); - setSelectedAssets([]); - } - - return null; // This component does not render any UI -}; - -export default DuplicationControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx deleted file mode 100644 index d717ecb..0000000 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ /dev/null @@ -1,463 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useMemo, useRef, useState } from "react"; -import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSimulationStates, 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"; -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) { - const { camera, controls, gl, scene, pointer, raycaster } = useThree(); - const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); - - const { toggleView } = useToggleView(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { floorItems, setFloorItems } = useFloorItems(); - const { socket } = useSocketStore(); - const itemsData = useRef([]); - - useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; - - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; - - let isMoving = false; - - const onPointerDown = () => { - isMoving = false; - }; - - const onPointerMove = () => { - isMoving = true; - }; - - const onPointerUp = (event: PointerEvent) => { - if (!isMoving && movedObjects.length > 0 && event.button === 0) { - event.preventDefault(); - placeMovedAssets(); - } - if (!isMoving && movedObjects.length > 0 && event.button === 2) { - event.preventDefault(); - - clearSelection(); - movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); - } - }); - - setFloorItems([...floorItems, ...itemsData.current]); - - setMovedObjects([]); - itemsData.current = []; - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - const keyCombination = detectModifierKeys(event); - - if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return; - if (keyCombination === "G") { - if (selectedAssets.length > 0) { - moveAssets(); - itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); - } - } - if (keyCombination === "ESCAPE") { - event.preventDefault(); - - clearSelection(); - movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); - } - }); - - setFloorItems([...floorItems, ...itemsData.current]); - - setMovedObjects([]); - itemsData.current = []; - } - }; - - if (!toggleView) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - canvasElement.addEventListener("keydown", onKeyDown); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]); - - const gridSize = 0.25; - const moveSpeed = 0.25; - const isGridSnap = false; - - useFrame(() => { - if (movedObjects.length > 0) { - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - - if (point) { - let targetX = point.x; - let targetZ = point.z; - - if (isGridSnap) { - targetX = Math.round(point.x / gridSize) * gridSize; - targetZ = Math.round(point.z / gridSize) * gridSize; - } - - const position = new THREE.Vector3(); - if (boundingBoxRef.current) { - boundingBoxRef.current.getWorldPosition(position); - selectionGroup.current.position.lerp( - new THREE.Vector3( - targetX - (position.x - selectionGroup.current.position.x), - selectionGroup.current.position.y, - targetZ - (position.z - selectionGroup.current.position.z) - ), - moveSpeed - ); - } else { - const box = new THREE.Box3(); - movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj)); - const center = new THREE.Vector3(); - box.getCenter(center); - - selectionGroup.current.position.lerp( - new THREE.Vector3( - targetX - (center.x - selectionGroup.current.position.x), - selectionGroup.current.position.y, - targetZ - (center.z - selectionGroup.current.position.z) - ), - moveSpeed - ); - } - } - } - }); - - - const moveAssets = () => { - const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); - setFloorItems(updatedItems); - setMovedObjects(selectedAssets); - selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); }); - } - - const placeMovedAssets = () => { - if (movedObjects.length === 0) return; - - movedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); - - selectionGroup.current.remove(obj); - obj.position.copy(worldPosition); - - if (itemsGroupRef.current) { - - const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, - modelfileID: obj.userData.modelId, - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, - isLocked: false, - isVisible: true - }; - - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema | undefined = simulationStates.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 backendEventData = { - type: 'Conveyor', - points: eventData.points, - speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed - }; - - //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, - // { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).map(event => - event.modeluuid === newFloorItem.modeluuid - ? { ...event, ...newEventData } - : event - ); - return updatedEvents; - }); - - socket.emit("v2:model-asset:add", data); - } else if (eventData.type === 'Vehicle' && eventData) { - - const backendEventData = { - type: 'Vehicle', - points: eventData.points - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).map(event => - event.modeluuid === newFloorItem.modeluuid - ? { ...event, ...newEventData } - : event - ); - return updatedEvents; - }); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'StaticMachine' && eventData) { - - const backendEventData = { - type: 'StaticMachine', - points: eventData.points, - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).map(event => - event.modeluuid === newFloorItem.modeluuid - ? { ...event, ...newEventData } - : event - ); - return updatedEvents; - }); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'ArmBot' && eventData) { - - const backendEventData = { - type: 'ArmBot', - points: eventData.points, - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - 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); - } - }); - toast.success("Object moved!"); - - itemsData.current = []; - clearSelection(); - } - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setpastedObjects([]); - setDuplicatedObjects([]); - setMovedObjects([]); - setRotatedObjects([]); - setSelectedAssets([]); - } - - return null; // No need to return anything, as this component is used for its side effects -} - -export default MoveControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx deleted file mode 100644 index f602bc4..0000000 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ /dev/null @@ -1,464 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useMemo, useRef, useState } from "react"; -import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSimulationStates, 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"; -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) { - const { camera, controls, gl, scene, pointer, raycaster } = useThree(); - const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); - - const { toggleView } = useToggleView(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { floorItems, setFloorItems } = useFloorItems(); - const { socket } = useSocketStore(); - const itemsData = useRef([]); - - const prevPointerPosition = useRef(null); - - useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; - - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; - - let isMoving = false; - - const onPointerDown = () => { - isMoving = false; - }; - - const onPointerMove = () => { - isMoving = true; - }; - - const onPointerUp = (event: PointerEvent) => { - if (!isMoving && rotatedObjects.length > 0 && event.button === 0) { - event.preventDefault(); - placeRotatedAssets(); - } - if (!isMoving && rotatedObjects.length > 0 && event.button === 2) { - event.preventDefault(); - - clearSelection(); - rotatedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); - } - }); - - setFloorItems([...floorItems, ...itemsData.current]); - - setRotatedObjects([]); - itemsData.current = []; - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return; - if (event.key.toLowerCase() === "r") { - if (selectedAssets.length > 0) { - rotateAssets(); - itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); - } - } - if (event.key.toLowerCase() === "escape") { - event.preventDefault(); - - clearSelection(); - rotatedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); - } - }); - - setFloorItems([...floorItems, ...itemsData.current]); - - setRotatedObjects([]); - itemsData.current = []; - } - }; - - if (!toggleView) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - canvasElement.addEventListener("keydown", onKeyDown); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); - - useFrame(() => { - if (rotatedObjects.length > 0) { - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - - if (point && prevPointerPosition.current) { - const box = new THREE.Box3(); - rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj)); - const center = new THREE.Vector3(); - box.getCenter(center); - - const delta = new THREE.Vector3().subVectors(point, center); - const prevPointerPosition3D = new THREE.Vector3(prevPointerPosition.current.x, 0, prevPointerPosition.current.y); - - const angle = Math.atan2(delta.z, delta.x) - Math.atan2(prevPointerPosition3D.z - center.z, prevPointerPosition3D.x - center.x); - - selectionGroup.current.rotation.y += -angle; - - selectionGroup.current.position.sub(center); - selectionGroup.current.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -angle); - selectionGroup.current.position.add(center); - - prevPointerPosition.current = new THREE.Vector2(point.x, point.z); - } - } - }); - - const rotateAssets = () => { - const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); - setFloorItems(updatedItems); - - const box = new THREE.Box3(); - selectedAssets.forEach((asset: any) => box.expandByObject(asset)); - const center = new THREE.Vector3(); - box.getCenter(center); - - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - - if (point) { - prevPointerPosition.current = new THREE.Vector2(point.x, point.z); - } - - selectedAssets.forEach((asset: any) => { - selectionGroup.current.attach(asset); - }); - - setRotatedObjects(selectedAssets); - }; - - const placeRotatedAssets = () => { - if (rotatedObjects.length === 0) return; - - rotatedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - const worldQuaternion = new THREE.Quaternion(); - - obj.getWorldPosition(worldPosition); - obj.getWorldQuaternion(worldQuaternion); - - selectionGroup.current.remove(obj); - - obj.position.copy(worldPosition); - obj.quaternion.copy(worldQuaternion); - - - if (itemsGroupRef.current) { - - const newFloorItem: Types.FloorItemType = { - modeluuid: obj.uuid, - modelname: obj.userData.name, - modelfileID: obj.userData.modelId, - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, - isLocked: false, - isVisible: true - }; - - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema | undefined = simulationStates.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 backendEventData = { - type: 'Conveyor', - points: eventData.points, - speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).map(event => - event.modeluuid === newFloorItem.modeluuid - ? { ...event, ...newEventData } - : event - ); - return updatedEvents; - }); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'Vehicle' && eventData) { - - const backendEventData = { - type: 'Vehicle', - points: eventData.points - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).map(event => - event.modeluuid === newFloorItem.modeluuid - ? { ...event, ...newEventData } - : event - ); - return updatedEvents; - }); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'StaticMachine' && eventData) { - - const backendEventData = { - type: 'StaticMachine', - points: eventData.points, - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).map(event => - event.modeluuid === newFloorItem.modeluuid - ? { ...event, ...newEventData } - : event - ); - return updatedEvents; - }); - - socket.emit("v2:model-asset:add", data); - - } else if (eventData.type === 'ArmBot' && eventData) { - - const backendEventData = { - type: 'ArmBot', - points: eventData.points, - }; - - // REST - - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // obj.userData.modelId, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // false, - // true, - // { type: backendEventData.type, points: backendEventData.points } - // ); - - //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: { type: backendEventData.type, points: backendEventData.points }, - socketId: socket.id, - }; - - const newEventData: any = { type: backendEventData.type, points: backendEventData.points }; - newEventData.modeluuid = newFloorItem.modeluuid; - newEventData.modelName = newFloorItem.modelname; - newEventData.position = newFloorItem.position; - newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; - - setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - 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); - } - }); - toast.success("Object rotated!"); - - itemsData.current = []; - clearSelection(); - } - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setpastedObjects([]); - setDuplicatedObjects([]); - setMovedObjects([]); - setRotatedObjects([]); - setSelectedAssets([]); - } - - return null; // No need to return anything, as this component is used for its side effects -} - -export default RotateControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/boundingBoxHelper.tsx b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx similarity index 97% rename from app/src/modules/scene/controls/selection/boundingBoxHelper.tsx rename to app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx index dd9d251..df57bf9 100644 --- a/app/src/modules/scene/controls/selection/boundingBoxHelper.tsx +++ b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx @@ -1,64 +1,64 @@ -import { Line } from "@react-three/drei"; -import { useMemo } from "react"; -import * as THREE from "three"; -import { useSelectedAssets } from "../../../../store/store"; - -const BoundingBox = ({ boundingBoxRef }: any) => { - const { selectedAssets } = useSelectedAssets(); - - const { points, boxProps } = useMemo(() => { - if (selectedAssets.length === 0) return { points: [], boxProps: {} }; - - const box = new THREE.Box3(); - selectedAssets.forEach((obj: any) => box.expandByObject(obj.clone())); - - const size = new THREE.Vector3(); - box.getSize(size); - const center = new THREE.Vector3(); - box.getCenter(center); - - const halfSize = size.clone().multiplyScalar(0.5); - const min = center.clone().sub(halfSize); - const max = center.clone().add(halfSize); - - const points: any = [ - [min.x, min.y, min.z], [max.x, min.y, min.z], - [max.x, min.y, min.z], [max.x, max.y, min.z], - [max.x, max.y, min.z], [min.x, max.y, min.z], - [min.x, max.y, min.z], [min.x, min.y, min.z], - - [min.x, min.y, max.z], [max.x, min.y, max.z], - [max.x, min.y, max.z], [max.x, max.y, max.z], - [max.x, max.y, max.z], [min.x, max.y, max.z], - [min.x, max.y, max.z], [min.x, min.y, max.z], - - [min.x, min.y, min.z], [min.x, min.y, max.z], - [max.x, min.y, min.z], [max.x, min.y, max.z], - [max.x, max.y, min.z], [max.x, max.y, max.z], - [min.x, max.y, min.z], [min.x, max.y, max.z], - ]; - - return { - points, - boxProps: { position: center.toArray(), args: size.toArray() } - }; - }, [selectedAssets]); - - const savedTheme: string | null = localStorage.getItem("theme") || "light"; - - return ( - <> - {points.length > 0 && ( - <> - - - - - - - )} - - ); -}; - -export default BoundingBox; +import { Line } from "@react-three/drei"; +import { useMemo } from "react"; +import * as THREE from "three"; +import { useSelectedAssets } from "../../../../store/store"; + +const BoundingBox = ({ boundingBoxRef }: any) => { + const { selectedAssets } = useSelectedAssets(); + + const { points, boxProps } = useMemo(() => { + if (selectedAssets.length === 0) return { points: [], boxProps: {} }; + + const box = new THREE.Box3(); + selectedAssets.forEach((obj: any) => box.expandByObject(obj.clone())); + + const size = new THREE.Vector3(); + box.getSize(size); + const center = new THREE.Vector3(); + box.getCenter(center); + + const halfSize = size.clone().multiplyScalar(0.5); + const min = center.clone().sub(halfSize); + const max = center.clone().add(halfSize); + + const points: any = [ + [min.x, min.y, min.z], [max.x, min.y, min.z], + [max.x, min.y, min.z], [max.x, max.y, min.z], + [max.x, max.y, min.z], [min.x, max.y, min.z], + [min.x, max.y, min.z], [min.x, min.y, min.z], + + [min.x, min.y, max.z], [max.x, min.y, max.z], + [max.x, min.y, max.z], [max.x, max.y, max.z], + [max.x, max.y, max.z], [min.x, max.y, max.z], + [min.x, max.y, max.z], [min.x, min.y, max.z], + + [min.x, min.y, min.z], [min.x, min.y, max.z], + [max.x, min.y, min.z], [max.x, min.y, max.z], + [max.x, max.y, min.z], [max.x, max.y, max.z], + [min.x, max.y, min.z], [min.x, max.y, max.z], + ]; + + return { + points, + boxProps: { position: center.toArray(), args: size.toArray() } + }; + }, [selectedAssets]); + + const savedTheme: string | null = localStorage.getItem("theme") || "light"; + + return ( + <> + {points.length > 0 && ( + <> + + + + + + + )} + + ); +}; + +export default BoundingBox; diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx new file mode 100644 index 0000000..9ca4dd0 --- /dev/null +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -0,0 +1,211 @@ +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 { toast } from "react-toastify"; +// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; +import * as Types from "../../../../types/world/worldTypes"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; + +const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { + const { camera, controls, gl, scene, pointer, raycaster } = useThree(); + const { toggleView } = useToggleView(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + 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; + canvasElement.tabIndex = 0; + + let isMoving = false; + + const onPointerDown = () => { + isMoving = false; + }; + + const onPointerMove = () => { + isMoving = true; + }; + + const onPointerUp = (event: PointerEvent) => { + if (!isMoving && pastedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + event.preventDefault(); + addPastedObjects(); + } + }; + + const onKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "Ctrl+C" && movedObjects.length === 0 && rotatedObjects.length === 0) { + copySelection(); + } + if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + pasteCopiedObjects(); + } + }; + + if (!toggleView) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("keydown", onKeyDown); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + }; + + }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, floorItems, rotatedObjects]); + + useFrame(() => { + if (pastedObjects.length > 0) { + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (point) { + const position = new THREE.Vector3(); + if (boundingBoxRef.current) { + boundingBoxRef.current?.getWorldPosition(position) + selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); + } else { + const box = new THREE.Box3(); + pastedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); + const center = new THREE.Vector3(); + box.getCenter(center); + selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); + } + } + } + }); + + const copySelection = () => { + if (selectedAssets.length > 0) { + const newClones = selectedAssets.map((asset: any) => { + const clone = asset.clone(); + clone.position.copy(asset.position); + return clone; + }); + setCopiedObjects(newClones); + toast.info("Objects copied!"); + } + }; + + const pasteCopiedObjects = () => { + if (copiedObjects.length > 0 && pastedObjects.length === 0) { + const newClones = copiedObjects.map((obj: THREE.Object3D) => { + const clone = obj.clone(); + clone.position.copy(obj.position); + return clone; + }); + selectionGroup.current.add(...newClones); + setpastedObjects([...newClones]); + setSelectedAssets([...newClones]); + + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point) { + const position = new THREE.Vector3(); + if (boundingBoxRef.current) { + boundingBoxRef.current?.getWorldPosition(position) + selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); + } else { + const box = new THREE.Box3(); + newClones.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); + const center = new THREE.Vector3(); + box.getCenter(center); + selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); + } + } + } + }; + + const addPastedObjects = () => { + if (pastedObjects.length === 0) return; + pastedObjects.forEach(async (obj: THREE.Object3D) => { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + obj.position.copy(worldPosition); + + if (itemsGroupRef.current) { + + const newFloorItem: Types.FloorItemType = { + modeluuid: obj.uuid, + modelname: obj.userData.name, + modelfileID: obj.userData.modelId, + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, + isLocked: false, + isVisible: true + }; + + setFloorItems((prevItems: Types.FloorItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : "default"; + + //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); + + obj.userData.modeluuid = newFloorItem.modeluuid; + itemsGroupRef.current.add(obj); + } + }); + + toast.success("Object added!"); + clearSelection(); + }; + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setMovedObjects([]); + setpastedObjects([]); + setDuplicatedObjects([]); + setRotatedObjects([]); + setSelectedAssets([]); + } + + return null; // No visible output, but the component handles copy-paste functionality +}; + +export default CopyPasteControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx new file mode 100644 index 0000000..39c3e49 --- /dev/null +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -0,0 +1,189 @@ +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 { toast } from "react-toastify"; +// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; +import * as Types from "../../../../types/world/worldTypes"; +import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; + +const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { + const { camera, controls, gl, scene, pointer, raycaster } = useThree(); + const { toggleView } = useToggleView(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + 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; + canvasElement.tabIndex = 0; + + let isMoving = false; + + const onPointerDown = () => { + isMoving = false; + }; + + const onPointerMove = () => { + isMoving = true; + }; + + const onPointerUp = (event: PointerEvent) => { + if (!isMoving && duplicatedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + event.preventDefault(); + addDuplicatedAssets(); + } + }; + + const onKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === "Ctrl+D" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + duplicateSelection(); + } + }; + + if (!toggleView) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("keydown", onKeyDown); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + }; + + }, [camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]); + + useFrame(() => { + if (duplicatedObjects.length > 0) { + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (point) { + const position = new THREE.Vector3(); + if (boundingBoxRef.current) { + boundingBoxRef.current?.getWorldPosition(position) + selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); + } else { + const box = new THREE.Box3(); + duplicatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); + const center = new THREE.Vector3(); + box.getCenter(center); + selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); + } + } + } + }); + + const duplicateSelection = () => { + if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { + const newClones = selectedAssets.map((asset: any) => { + const clone = asset.clone(); + clone.position.copy(asset.position); + return clone; + }); + + selectionGroup.current.add(...newClones); + setDuplicatedObjects(newClones); + + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point) { + const position = new THREE.Vector3(); + boundingBoxRef.current?.getWorldPosition(position) + selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); + } + } + }; + + const addDuplicatedAssets = () => { + if (duplicatedObjects.length === 0) return; + duplicatedObjects.forEach(async (obj: THREE.Object3D) => { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + obj.position.copy(worldPosition); + + if (itemsGroupRef.current) { + + const newFloorItem: Types.FloorItemType = { + modeluuid: obj.uuid, + modelname: obj.userData.name, + modelfileID: obj.userData.modelId, + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, + isLocked: false, + isVisible: true + }; + + setFloorItems((prevItems: Types.FloorItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : "default"; + + //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); + + obj.userData.modeluuid = newFloorItem.modeluuid; + itemsGroupRef.current.add(obj); + } + }); + + toast.success("Object duplicated!"); + clearSelection(); + } + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setMovedObjects([]); + setpastedObjects([]); + setDuplicatedObjects([]); + setRotatedObjects([]); + setSelectedAssets([]); + } + + return null; // This component does not render any UI +}; + +export default DuplicationControls; \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx new file mode 100644 index 0000000..2cde3e4 --- /dev/null +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -0,0 +1,240 @@ +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 { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; +import { toast } from "react-toastify"; +import * as Types from "../../../../types/world/worldTypes"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; + +function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { + const { camera, controls, gl, scene, pointer, raycaster } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); + + const { toggleView } = useToggleView(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { floorItems, setFloorItems } = useFloorItems(); + const { socket } = useSocketStore(); + const itemsData = useRef([]); + + useEffect(() => { + if (!camera || !scene || toggleView || !itemsGroupRef.current) return; + + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; + + let isMoving = false; + + const onPointerDown = () => { + isMoving = false; + }; + + const onPointerMove = () => { + isMoving = true; + }; + + const onPointerUp = (event: PointerEvent) => { + if (!isMoving && movedObjects.length > 0 && event.button === 0) { + event.preventDefault(); + placeMovedAssets(); + } + if (!isMoving && movedObjects.length > 0 && event.button === 2) { + event.preventDefault(); + + clearSelection(); + movedObjects.forEach((asset: any) => { + if (itemsGroupRef.current) { + itemsGroupRef.current.attach(asset); + } + }); + + setFloorItems([...floorItems, ...itemsData.current]); + + setMovedObjects([]); + itemsData.current = []; + } + }; + + const onKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return; + if (keyCombination === "G") { + if (selectedAssets.length > 0) { + moveAssets(); + itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + } + } + if (keyCombination === "ESCAPE") { + event.preventDefault(); + + clearSelection(); + movedObjects.forEach((asset: any) => { + if (itemsGroupRef.current) { + itemsGroupRef.current.attach(asset); + } + }); + + setFloorItems([...floorItems, ...itemsData.current]); + + setMovedObjects([]); + itemsData.current = []; + } + }; + + if (!toggleView) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("keydown", onKeyDown); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + }; + }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]); + + const gridSize = 0.25; + const moveSpeed = 0.25; + const isGridSnap = false; + + useFrame(() => { + if (movedObjects.length > 0) { + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point) { + let targetX = point.x; + let targetZ = point.z; + + if (isGridSnap) { + targetX = Math.round(point.x / gridSize) * gridSize; + targetZ = Math.round(point.z / gridSize) * gridSize; + } + + const position = new THREE.Vector3(); + if (boundingBoxRef.current) { + boundingBoxRef.current.getWorldPosition(position); + selectionGroup.current.position.lerp( + new THREE.Vector3( + targetX - (position.x - selectionGroup.current.position.x), + selectionGroup.current.position.y, + targetZ - (position.z - selectionGroup.current.position.z) + ), + moveSpeed + ); + } else { + const box = new THREE.Box3(); + movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj)); + const center = new THREE.Vector3(); + box.getCenter(center); + + selectionGroup.current.position.lerp( + new THREE.Vector3( + targetX - (center.x - selectionGroup.current.position.x), + selectionGroup.current.position.y, + targetZ - (center.z - selectionGroup.current.position.z) + ), + moveSpeed + ); + } + } + } + }); + + + const moveAssets = () => { + const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + setFloorItems(updatedItems); + setMovedObjects(selectedAssets); + selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); }); + } + + const placeMovedAssets = () => { + if (movedObjects.length === 0) return; + + movedObjects.forEach(async (obj: THREE.Object3D) => { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + + selectionGroup.current.remove(obj); + obj.position.copy(worldPosition); + + if (itemsGroupRef.current) { + + const newFloorItem: Types.FloorItemType = { + modeluuid: obj.uuid, + modelname: obj.userData.name, + modelfileID: obj.userData.modelId, + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, + isLocked: false, + isVisible: true + }; + + setFloorItems((prevItems: Types.FloorItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : "default"; + + //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); + } + }); + toast.success("Object moved!"); + + itemsData.current = []; + clearSelection(); + } + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setMovedObjects([]); + setRotatedObjects([]); + setSelectedAssets([]); + } + + return null; // No need to return anything, as this component is used for its side effects +} + +export default MoveControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx new file mode 100644 index 0000000..5dfaf08 --- /dev/null +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -0,0 +1,241 @@ +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 { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; +import { toast } from "react-toastify"; +import * as Types from "../../../../types/world/worldTypes"; +import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; + +function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { + const { camera, controls, gl, scene, pointer, raycaster } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); + + const { toggleView } = useToggleView(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { floorItems, setFloorItems } = useFloorItems(); + const { socket } = useSocketStore(); + const itemsData = useRef([]); + + const prevPointerPosition = useRef(null); + + useEffect(() => { + if (!camera || !scene || toggleView || !itemsGroupRef.current) return; + + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; + + let isMoving = false; + + const onPointerDown = () => { + isMoving = false; + }; + + const onPointerMove = () => { + isMoving = true; + }; + + const onPointerUp = (event: PointerEvent) => { + if (!isMoving && rotatedObjects.length > 0 && event.button === 0) { + event.preventDefault(); + placeRotatedAssets(); + } + if (!isMoving && rotatedObjects.length > 0 && event.button === 2) { + event.preventDefault(); + + clearSelection(); + rotatedObjects.forEach((asset: any) => { + if (itemsGroupRef.current) { + itemsGroupRef.current.attach(asset); + } + }); + + setFloorItems([...floorItems, ...itemsData.current]); + + setRotatedObjects([]); + itemsData.current = []; + } + }; + + const onKeyDown = (event: KeyboardEvent) => { + if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return; + if (event.key.toLowerCase() === "r") { + if (selectedAssets.length > 0) { + rotateAssets(); + itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + } + } + if (event.key.toLowerCase() === "escape") { + event.preventDefault(); + + clearSelection(); + rotatedObjects.forEach((asset: any) => { + if (itemsGroupRef.current) { + itemsGroupRef.current.attach(asset); + } + }); + + setFloorItems([...floorItems, ...itemsData.current]); + + setRotatedObjects([]); + itemsData.current = []; + } + }; + + if (!toggleView) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("keydown", onKeyDown); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + }; + }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); + + useFrame(() => { + if (rotatedObjects.length > 0) { + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point && prevPointerPosition.current) { + const box = new THREE.Box3(); + rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj)); + const center = new THREE.Vector3(); + box.getCenter(center); + + const delta = new THREE.Vector3().subVectors(point, center); + const prevPointerPosition3D = new THREE.Vector3(prevPointerPosition.current.x, 0, prevPointerPosition.current.y); + + const angle = Math.atan2(delta.z, delta.x) - Math.atan2(prevPointerPosition3D.z - center.z, prevPointerPosition3D.x - center.x); + + selectionGroup.current.rotation.y += -angle; + + selectionGroup.current.position.sub(center); + selectionGroup.current.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -angle); + selectionGroup.current.position.add(center); + + prevPointerPosition.current = new THREE.Vector2(point.x, point.z); + } + } + }); + + const rotateAssets = () => { + const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid)); + setFloorItems(updatedItems); + + const box = new THREE.Box3(); + selectedAssets.forEach((asset: any) => box.expandByObject(asset)); + const center = new THREE.Vector3(); + box.getCenter(center); + + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point) { + prevPointerPosition.current = new THREE.Vector2(point.x, point.z); + } + + selectedAssets.forEach((asset: any) => { + selectionGroup.current.attach(asset); + }); + + setRotatedObjects(selectedAssets); + }; + + const placeRotatedAssets = () => { + if (rotatedObjects.length === 0) return; + + rotatedObjects.forEach(async (obj: THREE.Object3D) => { + const worldPosition = new THREE.Vector3(); + const worldQuaternion = new THREE.Quaternion(); + + obj.getWorldPosition(worldPosition); + obj.getWorldQuaternion(worldQuaternion); + + selectionGroup.current.remove(obj); + + obj.position.copy(worldPosition); + obj.quaternion.copy(worldQuaternion); + + + if (itemsGroupRef.current) { + + const newFloorItem: Types.FloorItemType = { + modeluuid: obj.uuid, + modelname: obj.userData.name, + modelfileID: obj.userData.modelId, + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, + isLocked: false, + isVisible: true + }; + + setFloorItems((prevItems: Types.FloorItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : "default"; + + //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); + } + }); + toast.success("Object rotated!"); + + itemsData.current = []; + clearSelection(); + } + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setMovedObjects([]); + setRotatedObjects([]); + setSelectedAssets([]); + } + + return null; // No need to return anything, as this component is used for its side effects +} + +export default RotateControls \ No newline at end of file diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx similarity index 54% rename from app/src/modules/scene/controls/selection/selectionControls.tsx rename to app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 8998bc9..49b86d2 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -1,479 +1,272 @@ -import * as THREE from "three"; -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, useSimulationStates, useSocketStore, useToggleView, } from "../../../../store/store"; -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/simulationTypes"; - -import DuplicationControls from "./duplicationControls"; -import CopyPasteControls from "./copyPasteControls"; -import MoveControls from "./moveControls"; -import RotateControls from "./rotateControls"; -import useModuleStore from "../../../../store/useModuleStore"; - -const SelectionControls: React.FC = () => { - const { camera, controls, gl, scene, pointer } = useThree(); - const itemsGroupRef = useRef(undefined); - const selectionGroup = useRef() as Types.RefGroup; - const { toggleView } = useToggleView(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const [movedObjects, setMovedObjects] = useState([]); - const [rotatedObjects, setRotatedObjects] = useState([]); - const [copiedObjects, setCopiedObjects] = useState([]); - const [pastedObjects, setpastedObjects] = useState([]); - const [duplicatedObjects, setDuplicatedObjects] = useState([]); - const boundingBoxRef = useRef(); - const { floorItems, setFloorItems } = useFloorItems(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); - - useEffect(() => { - if (!camera || !scene || toggleView) return; - - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; - - const itemsGroup: any = scene.getObjectByName("itemsGroup"); - itemsGroupRef.current = itemsGroup; - - let isSelecting = false; - let isRightClick = false; - let rightClickMoved = false; - let isCtrlSelecting = false; - - const helper = new SelectionHelper(gl); - - if (!itemsGroup) { - toast.warn("itemsGroup not found in the scene."); - return; - } - - const onPointerDown = (event: PointerEvent) => { - if (event.button === 2) { - isRightClick = true; - rightClickMoved = false; - } else if (event.button === 0) { - isSelecting = false; - isCtrlSelecting = event.ctrlKey; - if (event.ctrlKey && duplicatedObjects.length === 0) { - if (controls) (controls as any).enabled = false; - selectionBox.startPoint.set(pointer.x, pointer.y, 0); - } - } - }; - - const onPointerMove = (event: PointerEvent) => { - if (isRightClick) { - rightClickMoved = true; - } - isSelecting = true; - if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { - selectionBox.endPoint.set(pointer.x, pointer.y, 0); - } - }; - - const onPointerUp = (event: PointerEvent) => { - if (event.button === 2) { - isRightClick = false; - if (!rightClickMoved) { - clearSelection(); - } - return; - } - - if (isSelecting && isCtrlSelecting) { - isCtrlSelecting = false; - isSelecting = false; - if (event.ctrlKey && duplicatedObjects.length === 0) { - selectAssets(); - } - } else if (!isSelecting && selectedAssets.length > 0 && ((pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { - clearSelection(); - helper.enabled = true; - isCtrlSelecting = false; - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - if (movedObjects.length > 0 || rotatedObjects.length > 0) return; - if (event.key.toLowerCase() === "escape") { - event.preventDefault(); - clearSelection(); - } - if (event.key.toLowerCase() === "delete") { - event.preventDefault(); - deleteSelection(); - } - }; - - const onContextMenu = (event: MouseEvent) => { - event.preventDefault(); - if (!rightClickMoved) { - clearSelection(); - } - }; - - if (!toggleView && activeModule === "builder") { - helper.enabled = true; - if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - } else { - helper.enabled = false; - helper.dispose(); - } - canvasElement.addEventListener("contextmenu", onContextMenu); - canvasElement.addEventListener("keydown", onKeyDown); - } else { - helper.enabled = false; - helper.dispose(); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("contextmenu", onContextMenu); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - helper.enabled = false; - helper.dispose(); - }; - }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); - - useEffect(() => { - if (activeModule !== "builder") { - clearSelection(); - } - }, [activeModule]); - - useFrame(() => { - if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { - selectionGroup.current.position.set(0, 0, 0); - } - }); - - const selectAssets = () => { - selectionBox.endPoint.set(pointer.x, pointer.y, 0); - if (controls) (controls as any).enabled = true; - - let selectedObjects = selectionBox.select(); - let Objects = new Set(); - - selectedObjects.map((object) => { - let currentObject: THREE.Object3D | null = object; - while (currentObject) { - if (currentObject.userData.modelId) { - Objects.add(currentObject); - break; - } - currentObject = currentObject.parent || null; - } - }); - - if (Objects.size === 0) { - clearSelection(); - return; - } - - const updatedSelections = new Set(selectedAssets); - Objects.forEach((obj) => { updatedSelections.has(obj) ? updatedSelections.delete(obj) : updatedSelections.add(obj); }); - - const selected = Array.from(updatedSelections); - - setSelectedAssets(selected); - }; - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setpastedObjects([]); - setDuplicatedObjects([]); - setSelectedAssets([]); - }; - const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - if (updatedPaths.length === 0) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === "Conveyor") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { - type: "Conveyor", - points: updatedPath.points, - speed: updatedPath.speed, - }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "Vehicle") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "StaticMachine") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "ArmBot") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } - }); - }; - - const removeConnections = (deletedModelUUIDs: string[]) => { - - const deletedPointUUIDs = new Set(); - simulationStates.forEach(state => { - if (deletedModelUUIDs.includes(state.modeluuid)) { - if (state.type === "Conveyor" && state.points) { - state.points.forEach(point => { - deletedPointUUIDs.add(point.uuid); - }); - } else if (state.points && 'uuid' in state.points) { - deletedPointUUIDs.add(state.points.uuid); - } - } - }); - - const updatedStates = simulationStates.map((state) => { - // Handle Conveyor - if (state.type === "Conveyor") { - const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { - ...state, - points: state.points.map((point) => { - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }; - }), - }; - return updatedConveyor; - } - - // Handle Vehicle - else if (state.type === "Vehicle") { - const updatedVehicle: SimulationTypes.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedVehicle; - } - - // Handle StaticMachine - else if (state.type === "StaticMachine") { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = - { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedStaticMachine; - } - - // Handle ArmBot - else if (state.type === "ArmBot") { - const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target: any) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - actions: { - ...state.points.actions, - processes: state.points.actions.processes?.filter((process) => { - // Check if trigger is from deleted model - const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid)); - - if (matchedStates.length > 0) { - if (matchedStates[0]?.type === "StaticMachine") { - const trigPoints = matchedStates[0]?.points; - if (process.triggerId === trigPoints?.triggers?.uuid) { - return false; - } - } else if (matchedStates[0]?.type === "Conveyor") { - const trigPoints = matchedStates[0]?.points; - if (Array.isArray(trigPoints)) { - const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0); - const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid); - if (allTriggerUUIDs.includes(process.triggerId)) { - return false; - } - } - } - } - - // Check if startPoint or endPoint is from deleted model - if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) { - return false; - } - - return true; - }), - }, - }, - }; - return updatedArmBot; - } - - return state; - }); - - const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid)); - - updateBackend(filteredStates); - setSimulationStates(filteredStates); - }; - - const deleteSelection = () => { - if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]"); - const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid); - - const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - selectedAssets.forEach((selectedMesh: THREE.Object3D) => { - //REST - - // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); - - //SOCKET - - const data = { - organization: organization, - modeluuid: selectedMesh.uuid, - modelname: selectedMesh.userData.name, - socketId: socket.id, - }; - - socket.emit("v2:model-asset:delete", data); - - selectedMesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - if (child.geometry) child.geometry.dispose(); - if (Array.isArray(child.material)) { - child.material.forEach((material) => { - if (material.map) material.map.dispose(); - material.dispose(); - }); - } else if (child.material) { - if (child.material.map) child.material.map.dispose(); - child.material.dispose(); - } - } - }); - - setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).filter((event) => event.modeluuid !== selectedMesh.uuid); - return updatedEvents; - }); - - itemsGroupRef.current?.remove(selectedMesh); - }); - - const allUUIDs = selectedAssets.map((val: any) => val.uuid); - removeConnections(allUUIDs); - - const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); - setFloorItems(updatedItems); - } - toast.success("Selected models removed!"); - clearSelection(); - }; - - return ( - <> - - - - - - - - - - - - - - - ); -}; - -export default SelectionControls; +import * as THREE from "three"; +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 BoundingBox from "./boundingBoxHelper"; +import { toast } from "react-toastify"; +// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; +import * as Types from "../../../../types/world/worldTypes"; + +import DuplicationControls from "./duplicationControls"; +import CopyPasteControls from "./copyPasteControls"; +import MoveControls from "./moveControls"; +import RotateControls from "./rotateControls"; +import useModuleStore from "../../../../store/useModuleStore"; + +const SelectionControls: React.FC = () => { + const { camera, controls, gl, scene, pointer } = useThree(); + const itemsGroupRef = useRef(undefined); + const selectionGroup = useRef() as Types.RefGroup; + const { toggleView } = useToggleView(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const [movedObjects, setMovedObjects] = useState([]); + const [rotatedObjects, setRotatedObjects] = useState([]); + const [copiedObjects, setCopiedObjects] = useState([]); + const [pastedObjects, setpastedObjects] = useState([]); + const [duplicatedObjects, setDuplicatedObjects] = useState([]); + const boundingBoxRef = useRef(); + const { floorItems, setFloorItems } = useFloorItems(); + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); + + useEffect(() => { + if (!camera || !scene || toggleView) return; + + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; + + const itemsGroup: any = scene.getObjectByName("itemsGroup"); + itemsGroupRef.current = itemsGroup; + + let isSelecting = false; + let isRightClick = false; + let rightClickMoved = false; + let isCtrlSelecting = false; + + const helper = new SelectionHelper(gl); + + if (!itemsGroup) { + toast.warn("itemsGroup not found in the scene."); + return; + } + + const onPointerDown = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = true; + rightClickMoved = false; + } else if (event.button === 0) { + isSelecting = false; + isCtrlSelecting = event.ctrlKey; + if (event.ctrlKey && duplicatedObjects.length === 0) { + if (controls) (controls as any).enabled = false; + selectionBox.startPoint.set(pointer.x, pointer.y, 0); + } + } + }; + + const onPointerMove = (event: PointerEvent) => { + if (isRightClick) { + rightClickMoved = true; + } + isSelecting = true; + if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { + selectionBox.endPoint.set(pointer.x, pointer.y, 0); + } + }; + + const onPointerUp = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = false; + if (!rightClickMoved) { + clearSelection(); + } + return; + } + + if (isSelecting && isCtrlSelecting) { + isCtrlSelecting = false; + isSelecting = false; + if (event.ctrlKey && duplicatedObjects.length === 0) { + selectAssets(); + } + } else if (!isSelecting && selectedAssets.length > 0 && ((pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { + clearSelection(); + helper.enabled = true; + isCtrlSelecting = false; + } + }; + + const onKeyDown = (event: KeyboardEvent) => { + if (movedObjects.length > 0 || rotatedObjects.length > 0) return; + if (event.key.toLowerCase() === "escape") { + event.preventDefault(); + clearSelection(); + } + if (event.key.toLowerCase() === "delete") { + event.preventDefault(); + deleteSelection(); + } + }; + + const onContextMenu = (event: MouseEvent) => { + event.preventDefault(); + if (!rightClickMoved) { + clearSelection(); + } + }; + + if (!toggleView && activeModule === "builder") { + helper.enabled = true; + if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + } else { + helper.enabled = false; + helper.dispose(); + } + canvasElement.addEventListener("contextmenu", onContextMenu); + canvasElement.addEventListener("keydown", onKeyDown); + } else { + helper.enabled = false; + helper.dispose(); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("contextmenu", onContextMenu); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + helper.enabled = false; + helper.dispose(); + }; + }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); + + useEffect(() => { + if (activeModule !== "builder") { + clearSelection(); + } + }, [activeModule]); + + useFrame(() => { + if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + selectionGroup.current.position.set(0, 0, 0); + } + }); + + const selectAssets = () => { + selectionBox.endPoint.set(pointer.x, pointer.y, 0); + if (controls) (controls as any).enabled = true; + + let selectedObjects = selectionBox.select(); + let Objects = new Set(); + + selectedObjects.map((object) => { + let currentObject: THREE.Object3D | null = object; + while (currentObject) { + if (currentObject.userData.modelId) { + Objects.add(currentObject); + break; + } + currentObject = currentObject.parent || null; + } + }); + + if (Objects.size === 0) { + clearSelection(); + return; + } + + const updatedSelections = new Set(selectedAssets); + Objects.forEach((obj) => { updatedSelections.has(obj) ? updatedSelections.delete(obj) : updatedSelections.add(obj); }); + + const selected = Array.from(updatedSelections); + + setSelectedAssets(selected); + }; + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setSelectedAssets([]); + }; + + const deleteSelection = () => { + if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]"); + const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid); + + const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + selectedAssets.forEach((selectedMesh: THREE.Object3D) => { + //REST + + // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); + + //SOCKET + + const data = { + organization: organization, + modeluuid: selectedMesh.uuid, + modelname: selectedMesh.userData.name, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:delete", data); + + selectedMesh.traverse((child: THREE.Object3D) => { + if (child instanceof THREE.Mesh) { + if (child.geometry) child.geometry.dispose(); + if (Array.isArray(child.material)) { + child.material.forEach((material) => { + if (material.map) material.map.dispose(); + material.dispose(); + }); + } else if (child.material) { + if (child.material.map) child.material.map.dispose(); + child.material.dispose(); + } + } + }); + + itemsGroupRef.current?.remove(selectedMesh); + }); + + const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); + setFloorItems(updatedItems); + } + toast.success("Selected models removed!"); + clearSelection(); + }; + + return ( + <> + + + + + + + + + + + + + + + ); +}; + +export default SelectionControls; diff --git a/app/src/modules/scene/controls/selection/selectionHelper.ts b/app/src/modules/scene/controls/selectionControls/selectionHelper.ts similarity index 96% rename from app/src/modules/scene/controls/selection/selectionHelper.ts rename to app/src/modules/scene/controls/selectionControls/selectionHelper.ts index ae37e67..c1acaf6 100644 --- a/app/src/modules/scene/controls/selection/selectionHelper.ts +++ b/app/src/modules/scene/controls/selectionControls/selectionHelper.ts @@ -1,115 +1,115 @@ -import { Vector2, WebGLRenderer } from 'three'; - -class SelectionHelper { - element: HTMLDivElement; - renderer: WebGLRenderer; - startPoint: Vector2; - pointTopLeft: Vector2; - pointBottomRight: Vector2; - isDown: boolean; - enabled: boolean; - - constructor(renderer: WebGLRenderer) { - this.element = document.createElement('div'); - this.element.style.position = 'fixed'; - this.element.style.border = '1px solid #55aaff'; - this.element.style.backgroundColor = 'rgba(75, 160, 255, 0.3)'; - this.element.style.pointerEvents = 'none'; - this.element.style.display = 'none'; - - this.renderer = renderer; - - this.startPoint = new Vector2(); - this.pointTopLeft = new Vector2(); - this.pointBottomRight = new Vector2(); - - this.isDown = false; - this.enabled = true; - - this.onPointerDown = this.onPointerDown.bind(this); - this.onPointerMove = this.onPointerMove.bind(this); - this.onPointerUp = this.onPointerUp.bind(this); - - this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown); - this.renderer.domElement.addEventListener('pointermove', this.onPointerMove); - this.renderer.domElement.addEventListener('pointerup', this.onPointerUp); - window.addEventListener("blur", this.cleanup.bind(this)); - } - - dispose() { - this.enabled = false; - this.isDown = false; - this.cleanup(); - - this.renderer.domElement.removeEventListener("pointerdown", this.onPointerDown); - this.renderer.domElement.removeEventListener("pointermove", this.onPointerMove); - this.renderer.domElement.removeEventListener("pointerup", this.onPointerUp); - window.removeEventListener("blur", this.cleanup); - } - - private cleanup() { - this.isDown = false; - this.element.style.display = 'none'; - if (this.element.parentElement) { - this.element.parentElement.removeChild(this.element); - } - } - - onPointerDown(event: PointerEvent) { - if (!this.enabled || !event.ctrlKey || event.button !== 0) return; - - this.isDown = true; - this.onSelectStart(event); - } - - onPointerMove(event: PointerEvent) { - if (!this.enabled || !this.isDown || !event.ctrlKey) return; - - this.onSelectMove(event); - } - - onPointerUp() { - if (!this.enabled) return; - - this.isDown = false; - this.onSelectOver(); - } - - onSelectStart(event: PointerEvent) { - this.element.style.display = 'none'; - this.renderer.domElement.parentElement?.appendChild(this.element); - - this.element.style.left = `${event.clientX}px`; - this.element.style.top = `${event.clientY}px`; - this.element.style.width = '0px'; - this.element.style.height = '0px'; - - this.startPoint.x = event.clientX; - this.startPoint.y = event.clientY; - } - - onSelectMove(event: PointerEvent) { - if (!this.isDown) return; - - this.element.style.display = 'block'; - - this.pointBottomRight.x = Math.max(this.startPoint.x, event.clientX); - this.pointBottomRight.y = Math.max(this.startPoint.y, event.clientY); - this.pointTopLeft.x = Math.min(this.startPoint.x, event.clientX); - this.pointTopLeft.y = Math.min(this.startPoint.y, event.clientY); - - this.element.style.left = `${this.pointTopLeft.x}px`; - this.element.style.top = `${this.pointTopLeft.y}px`; - this.element.style.width = `${this.pointBottomRight.x - this.pointTopLeft.x}px`; - this.element.style.height = `${this.pointBottomRight.y - this.pointTopLeft.y}px`; - } - - onSelectOver() { - this.element.style.display = 'none'; - if (this.element.parentElement) { - this.element.parentElement.removeChild(this.element); - } - } -} - +import { Vector2, WebGLRenderer } from 'three'; + +class SelectionHelper { + element: HTMLDivElement; + renderer: WebGLRenderer; + startPoint: Vector2; + pointTopLeft: Vector2; + pointBottomRight: Vector2; + isDown: boolean; + enabled: boolean; + + constructor(renderer: WebGLRenderer) { + this.element = document.createElement('div'); + this.element.style.position = 'fixed'; + this.element.style.border = '1px solid #55aaff'; + this.element.style.backgroundColor = 'rgba(75, 160, 255, 0.3)'; + this.element.style.pointerEvents = 'none'; + this.element.style.display = 'none'; + + this.renderer = renderer; + + this.startPoint = new Vector2(); + this.pointTopLeft = new Vector2(); + this.pointBottomRight = new Vector2(); + + this.isDown = false; + this.enabled = true; + + this.onPointerDown = this.onPointerDown.bind(this); + this.onPointerMove = this.onPointerMove.bind(this); + this.onPointerUp = this.onPointerUp.bind(this); + + this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown); + this.renderer.domElement.addEventListener('pointermove', this.onPointerMove); + this.renderer.domElement.addEventListener('pointerup', this.onPointerUp); + window.addEventListener("blur", this.cleanup.bind(this)); + } + + dispose() { + this.enabled = false; + this.isDown = false; + this.cleanup(); + + this.renderer.domElement.removeEventListener("pointerdown", this.onPointerDown); + this.renderer.domElement.removeEventListener("pointermove", this.onPointerMove); + this.renderer.domElement.removeEventListener("pointerup", this.onPointerUp); + window.removeEventListener("blur", this.cleanup); + } + + private cleanup() { + this.isDown = false; + this.element.style.display = 'none'; + if (this.element.parentElement) { + this.element.parentElement.removeChild(this.element); + } + } + + onPointerDown(event: PointerEvent) { + if (!this.enabled || !event.ctrlKey || event.button !== 0) return; + + this.isDown = true; + this.onSelectStart(event); + } + + onPointerMove(event: PointerEvent) { + if (!this.enabled || !this.isDown || !event.ctrlKey) return; + + this.onSelectMove(event); + } + + onPointerUp() { + if (!this.enabled) return; + + this.isDown = false; + this.onSelectOver(); + } + + onSelectStart(event: PointerEvent) { + this.element.style.display = 'none'; + this.renderer.domElement.parentElement?.appendChild(this.element); + + this.element.style.left = `${event.clientX}px`; + this.element.style.top = `${event.clientY}px`; + this.element.style.width = '0px'; + this.element.style.height = '0px'; + + this.startPoint.x = event.clientX; + this.startPoint.y = event.clientY; + } + + onSelectMove(event: PointerEvent) { + if (!this.isDown) return; + + this.element.style.display = 'block'; + + this.pointBottomRight.x = Math.max(this.startPoint.x, event.clientX); + this.pointBottomRight.y = Math.max(this.startPoint.y, event.clientY); + this.pointTopLeft.x = Math.min(this.startPoint.x, event.clientX); + this.pointTopLeft.y = Math.min(this.startPoint.y, event.clientY); + + this.element.style.left = `${this.pointTopLeft.x}px`; + this.element.style.top = `${this.pointTopLeft.y}px`; + this.element.style.width = `${this.pointBottomRight.x - this.pointTopLeft.x}px`; + this.element.style.height = `${this.pointBottomRight.y - this.pointTopLeft.y}px`; + } + + onSelectOver() { + this.element.style.display = 'none'; + if (this.element.parentElement) { + this.element.parentElement.removeChild(this.element); + } + } +} + export { SelectionHelper }; \ No newline at end of file diff --git a/app/src/modules/scene/controls/transformControls.tsx b/app/src/modules/scene/controls/transformControls.tsx deleted file mode 100644 index 2e586cc..0000000 --- a/app/src/modules/scene/controls/transformControls.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { TransformControls } from "@react-three/drei"; -import * as THREE from "three"; -import { useSelectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store"; -import { useThree } from "@react-three/fiber"; - -import * as Types from '../../../types/world/worldTypes'; -import { useEffect } from "react"; - -export default function TransformControl() { - const state = useThree(); - const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); - const { objectPosition, setObjectPosition } = useObjectPosition(); - const { objectScale, setObjectScale } = useObjectScale(); - const { objectRotation, setObjectRotation } = useObjectRotation(); - const { transformMode, setTransformMode } = useTransformMode(); - const { floorItems, setFloorItems } = useFloorItems(); - const { activeTool, setActiveTool } = useActiveTool(); - const { socket } = useSocketStore(); - - function handleObjectChange() { - if (selectedFloorItem && transformMode) { - setObjectPosition(selectedFloorItem.position); - setObjectScale(selectedFloorItem.scale); - setObjectRotation({ - x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x), - y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y), - z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z), - }); - } - } - function handleMouseUp() { - if (selectedFloorItem) { - setObjectPosition(selectedFloorItem.position); - setObjectScale(selectedFloorItem.scale); - setObjectRotation({ - x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x), - y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y), - z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z), - }); - } - setFloorItems((prevItems: Types.FloorItems) => { - if (!prevItems) { - return - } - let updatedItem: any = null; - const updatedItems = prevItems.map((item) => { - if (item.modeluuid === selectedFloorItem?.uuid) { - updatedItem = { - ...item, - position: [selectedFloorItem.position.x, selectedFloorItem.position.y, selectedFloorItem.position.z,] as [number, number, number], - rotation: { x: selectedFloorItem.rotation.x, y: selectedFloorItem.rotation.y, z: selectedFloorItem.rotation.z, }, - }; - return updatedItem; - } - return item; - }); - if (updatedItem && selectedFloorItem) { - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - //REST - - // setFloorItemApi( - // organization, - // updatedItem.modeluuid, - // updatedItem.modelname, - // [selectedFloorItem.position.x, selectedFloorItem.position.y, selectedFloorItem.position.z,], - // { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z }, - // false, - // true, - // ); - - //SOCKET - - const data = { - organization: organization, - modeluuid: updatedItem.modeluuid, - modelname: updatedItem.modelname, - position: [selectedFloorItem.position.x, selectedFloorItem.position.y, selectedFloorItem.position.z], - rotation: { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id - } - - socket.emit('v2:model-asset:add', data); - } - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - } - - useEffect(() => { - if (activeTool === "Add pillar" || activeTool === "Delete") { - if (state.controls) { - const target = (state.controls as any).getTarget(new THREE.Vector3()); - (state.controls as any).setTarget(target.x, 0, target.z, true); - } - setSelectedFloorItem(null); - { - setObjectPosition({ x: undefined, y: undefined, z: undefined }); - setObjectScale({ x: undefined, y: undefined, z: undefined }); - setObjectRotation({ x: undefined, y: undefined, z: undefined }); - } - } - }, [activeTool]); - - return ( - <> - {(selectedFloorItem && transformMode) && - - } - - ); -} diff --git a/app/src/modules/scene/environment/sky.tsx b/app/src/modules/scene/environment/sky.tsx index 3be57ce..dbfda22 100644 --- a/app/src/modules/scene/environment/sky.tsx +++ b/app/src/modules/scene/environment/sky.tsx @@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react"; import * as CONSTANTS from '../../../types/world/worldConstants'; export default function Sun() { + const savedTheme: string | null = localStorage.getItem("theme"); const { elevation, setElevation } = useElevation(); const { sunPosition, setSunPosition } = useSunPosition(); const { azimuth, setAzimuth } = useAzimuth(); @@ -28,7 +29,7 @@ export default function Sun() { return ( <> - {(azimuth !== undefined && elevation !== undefined) && ( + {(azimuth !== undefined && elevation !== undefined && savedTheme !== "dark") && ( <> )} - {selectedActionSphere && ( - - )} - {selectedPath && ( - - )} ); diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index 941616c..6564032 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -1,63 +1,36 @@ import { useMemo } from "react"; import { Canvas } from "@react-three/fiber"; -import { Environment, KeyboardControls, Stars } from "@react-three/drei"; +import { KeyboardControls } from "@react-three/drei"; -import World from "./world/world"; -import Controls from "./controls/controls"; -import TransformControl from "./controls/transformControls"; -import PostProcessing from "./postProcessing/postProcessing"; -import Sun from "./environment/sky"; -import CamModelsGroup from "../collaboration/collabCams"; -import Shadows from "./environment/shadow"; -import MqttEvents from "../../services/factoryBuilder/mqtt/mqttEvents"; - -import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr"; -import SelectionControls from "./controls/selection/selectionControls"; -import MeasurementTool from "./tools/measurementTool"; +import Builder from "../builder/builder"; +import Visualization from "../visualization/visualization"; +import Setup from "./setup/setup"; import Simulation from "../simulation/simulation"; - -// import Simulation from "./simulationtemp/simulation"; -import ZoneCentreTarget from "../visualization/functions/zoneCameraTarget"; -import Dropped3dWidgets from "../../modules/visualization/widgets/3d/Dropped3dWidget"; -import ZoneAssets from "../visualization/zoneAssets"; +import Collaboration from "../collaboration/collaboration"; export default function Scene() { - const map = useMemo( - () => [ - { name: "forward", keys: ["ArrowUp", "w", "W"] }, - { name: "backward", keys: ["ArrowDown", "s", "S"] }, - { name: "left", keys: ["ArrowLeft", "a", "A"] }, - { name: "right", keys: ["ArrowRight", "d", "D"] }, - ], - [] - ); - const savedTheme: string | null = localStorage.getItem("theme"); + const map = useMemo(() => [ + { name: "forward", keys: ["ArrowUp", "w", "W"] }, + { name: "backward", keys: ["ArrowDown", "s", "S"] }, + { name: "left", keys: ["ArrowLeft", "a", "A"] }, + { name: "right", keys: ["ArrowRight", "d", "D"] },], + []); - return ( - - { - e.preventDefault(); - }} - > - - - - - - - - - - - {savedTheme !== "dark" ? : <>} - - - - - - - ); + return ( + + { e.preventDefault(); }}> + + + + + + + + + + + + + + ); } diff --git a/app/src/modules/scene/setup/setup.tsx b/app/src/modules/scene/setup/setup.tsx new file mode 100644 index 0000000..34289f9 --- /dev/null +++ b/app/src/modules/scene/setup/setup.tsx @@ -0,0 +1,25 @@ +import Sun from '../environment/sky' +import Shadows from '../environment/shadow' +import PostProcessing from '../postProcessing/postProcessing' +import Controls from '../controls/controls'; +import { Environment } from '@react-three/drei' + +import background from "../../../assets/hdr/mudroadpuresky2k.hdr"; + +function Setup() { + return ( + <> + + + + + + + + + + + ) +} + +export default Setup \ No newline at end of file diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index 9f9fa03..9a30da3 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -5,240 +5,225 @@ import { useToolMode } from "../../../store/store"; import { Html } from "@react-three/drei"; const MeasurementTool = () => { - const { gl, raycaster, pointer, camera, scene } = useThree(); - const { toolMode } = useToolMode(); + const { gl, raycaster, pointer, camera, scene } = useThree(); + const { toolMode } = useToolMode(); - const [points, setPoints] = useState([]); - const [tubeGeometry, setTubeGeometry] = useState( - null - ); - const groupRef = useRef(null); - const [startConePosition, setStartConePosition] = - useState(null); - const [endConePosition, setEndConePosition] = useState( - null - ); - const [startConeQuaternion, setStartConeQuaternion] = useState( - new THREE.Quaternion() - ); - const [endConeQuaternion, setEndConeQuaternion] = useState( - new THREE.Quaternion() - ); - const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 }); + const [points, setPoints] = useState([]); + const [tubeGeometry, setTubeGeometry] = useState( + null + ); + const groupRef = useRef(null); + const [startConePosition, setStartConePosition] = + useState(null); + const [endConePosition, setEndConePosition] = useState( + null + ); + const [startConeQuaternion, setStartConeQuaternion] = useState( + new THREE.Quaternion() + ); + const [endConeQuaternion, setEndConeQuaternion] = useState( + new THREE.Quaternion() + ); + const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 }); - const MIN_RADIUS = 0.001, - MAX_RADIUS = 0.1; - const MIN_CONE_RADIUS = 0.01, - MAX_CONE_RADIUS = 0.4; - const MIN_CONE_HEIGHT = 0.035, - MAX_CONE_HEIGHT = 2.0; + const MIN_RADIUS = 0.001, MAX_RADIUS = 0.1; + const MIN_CONE_RADIUS = 0.01, MAX_CONE_RADIUS = 0.4; + const MIN_CONE_HEIGHT = 0.035, MAX_CONE_HEIGHT = 2.0; - useEffect(() => { - const canvasElement = gl.domElement; - let drag = false; - let isLeftMouseDown = false; + useEffect(() => { + const canvasElement = gl.domElement; + let drag = false; + let isLeftMouseDown = false; - const onMouseDown = () => { - isLeftMouseDown = true; - drag = false; - }; + const onMouseDown = () => { + isLeftMouseDown = true; + drag = false; + }; - const onMouseUp = (evt: any) => { - isLeftMouseDown = false; - if (evt.button === 0 && !drag) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") - ); + const onMouseUp = (evt: any) => { + isLeftMouseDown = false; + if (evt.button === 0 && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") + ); - if (intersects.length > 0) { - const intersectionPoint = intersects[0].point.clone(); - if (points.length < 2) { - setPoints([...points, intersectionPoint]); - } else { - setPoints([intersectionPoint]); - } + if (intersects.length > 0) { + const intersectionPoint = intersects[0].point.clone(); + if (points.length < 2) { + setPoints([...points, intersectionPoint]); + } else { + setPoints([intersectionPoint]); + } + } + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) drag = true; + }; + + const onContextMenu = (evt: any) => { + evt.preventDefault(); + if (!drag) { + evt.preventDefault(); + setPoints([]); + setTubeGeometry(null); + } + }; + + if (toolMode === "MeasurementScale") { + canvasElement.addEventListener("pointerdown", onMouseDown); + canvasElement.addEventListener("pointermove", onMouseMove); + canvasElement.addEventListener("pointerup", onMouseUp); + canvasElement.addEventListener("contextmenu", onContextMenu); + } else { + resetMeasurement(); + setPoints([]); } - } + + return () => { + canvasElement.removeEventListener("pointerdown", onMouseDown); + canvasElement.removeEventListener("pointermove", onMouseMove); + canvasElement.removeEventListener("pointerup", onMouseUp); + canvasElement.removeEventListener("contextmenu", onContextMenu); + }; + }, [toolMode, camera, raycaster, pointer, scene, points]); + + useFrame(() => { + if (points.length === 1) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") + ); + + if (intersects.length > 0) { + updateMeasurement(points[0], intersects[0].point); + } + } else if (points.length === 2) { + updateMeasurement(points[0], points[1]); + } else { + resetMeasurement(); + } + }); + + const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => { + const distance = start.distanceTo(end); + + const radius = THREE.MathUtils.clamp(distance * 0.02, MIN_RADIUS, MAX_RADIUS); + const coneRadius = THREE.MathUtils.clamp(distance * 0.05, MIN_CONE_RADIUS, MAX_CONE_RADIUS); + const coneHeight = THREE.MathUtils.clamp(distance * 0.2, MIN_CONE_HEIGHT, MAX_CONE_HEIGHT); + + setConeSize({ radius: coneRadius, height: coneHeight }); + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + + const offset = direction.clone().multiplyScalar(coneHeight * 0.5); + + let tubeStart = start.clone().add(offset); + let tubeEnd = end.clone().sub(offset); + + tubeStart.y = Math.max(tubeStart.y, 0); + tubeEnd.y = Math.max(tubeEnd.y, 0); + + const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]); + setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false)); + + setStartConePosition(tubeStart); + setEndConePosition(tubeEnd); + setStartConeQuaternion(getArrowOrientation(start, end)); + setEndConeQuaternion(getArrowOrientation(end, start)); }; - const onMouseMove = () => { - if (isLeftMouseDown) drag = true; - }; - - const onContextMenu = (evt: any) => { - evt.preventDefault(); - if (!drag) { - evt.preventDefault(); - setPoints([]); + const resetMeasurement = () => { setTubeGeometry(null); - } + setStartConePosition(null); + setEndConePosition(null); }; - if (toolMode === "MeasurementScale") { - canvasElement.addEventListener("pointerdown", onMouseDown); - canvasElement.addEventListener("pointermove", onMouseMove); - canvasElement.addEventListener("pointerup", onMouseUp); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - resetMeasurement(); - setPoints([]); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onMouseDown); - canvasElement.removeEventListener("pointermove", onMouseMove); - canvasElement.removeEventListener("pointerup", onMouseUp); - canvasElement.removeEventListener("contextmenu", onContextMenu); + const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => { + const direction = new THREE.Vector3() + .subVectors(end, start) + .normalize() + .negate(); + const quaternion = new THREE.Quaternion(); + quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); + return quaternion; }; - }, [toolMode, camera, raycaster, pointer, scene, points]); - useFrame(() => { - if (points.length === 1) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") - ); + useEffect(() => { + if (points.length === 2) { + // console.log(points[0].distanceTo(points[1])); + } + }, [points]); - if (intersects.length > 0) { - updateMeasurement(points[0], intersects[0].point); - } - } else if (points.length === 2) { - updateMeasurement(points[0], points[1]); - } else { - resetMeasurement(); - } - }); + return ( + + {startConePosition && ( + + + + + )} + {endConePosition && ( + + + + + )} + {tubeGeometry && ( + + + + )} - const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => { - const distance = start.distanceTo(end); - - const radius = THREE.MathUtils.clamp( - distance * 0.02, - MIN_RADIUS, - MAX_RADIUS + {startConePosition && endConePosition && ( + +
+ {startConePosition.distanceTo(endConePosition).toFixed(2)} m +
+ + )} +
); - const coneRadius = THREE.MathUtils.clamp( - distance * 0.05, - MIN_CONE_RADIUS, - MAX_CONE_RADIUS - ); - const coneHeight = THREE.MathUtils.clamp( - distance * 0.2, - MIN_CONE_HEIGHT, - MAX_CONE_HEIGHT - ); - - setConeSize({ radius: coneRadius, height: coneHeight }); - - const direction = new THREE.Vector3().subVectors(end, start).normalize(); - - const offset = direction.clone().multiplyScalar(coneHeight * 0.5); - - let tubeStart = start.clone().add(offset); - let tubeEnd = end.clone().sub(offset); - - tubeStart.y = Math.max(tubeStart.y, 0); - tubeEnd.y = Math.max(tubeEnd.y, 0); - - const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]); - setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false)); - - setStartConePosition(tubeStart); - setEndConePosition(tubeEnd); - setStartConeQuaternion(getArrowOrientation(start, end)); - setEndConeQuaternion(getArrowOrientation(end, start)); - }; - - const resetMeasurement = () => { - setTubeGeometry(null); - setStartConePosition(null); - setEndConePosition(null); - }; - - const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => { - const direction = new THREE.Vector3() - .subVectors(end, start) - .normalize() - .negate(); - const quaternion = new THREE.Quaternion(); - quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); - return quaternion; - }; - - useEffect(() => { - if (points.length === 2) { - console.log(points[0].distanceTo(points[1])); - } - }, [points]); - - return ( - - {startConePosition && ( - - - - - )} - {endConePosition && ( - - - - - )} - {tubeGeometry && ( - - - - )} - - {startConePosition && endConePosition && ( - -
- {startConePosition.distanceTo(endConePosition).toFixed(2)} m -
- - )} -
- ); }; export default MeasurementTool; diff --git a/app/src/modules/scene/world/world.tsx b/app/src/modules/scene/world/world.tsx deleted file mode 100644 index dcd1adb..0000000 --- a/app/src/modules/scene/world/world.tsx +++ /dev/null @@ -1,374 +0,0 @@ -////////// Three and React Three Fiber Imports ////////// - -import * as THREE from "three"; -import { useEffect, useRef, useState } from "react"; -import { useThree, useFrame } from "@react-three/fiber"; - -////////// Component Imports ////////// - -import DistanceText from "../../builder/geomentries/lines/distanceText/distanceText"; -import ReferenceDistanceText from "../../builder/geomentries/lines/distanceText/referenceDistanceText"; - -////////// Assests Imports ////////// - -import arch from "../../../assets/gltf-glb/arch.glb"; -import door from "../../../assets/gltf-glb/door.glb"; -import Window from "../../../assets/gltf-glb/window.glb"; - -////////// Zustand State Imports ////////// - -import { - useToggleView, - useDeletePointOrLine, - useMovePoint, - useActiveLayer, - useSocketStore, - useWallVisibility, - useRoofVisibility, - useShadows, - useUpdateScene, - useWalls, - useToolMode, - useRefTextUpdate, - useRenderDistance, - useLimitDistance, -} from "../../../store/store"; - -////////// 3D Function Imports ////////// - -import loadWalls from "../../builder/geomentries/walls/loadWalls"; - -import * as Types from "../../../types/world/worldTypes"; - -import SocketResponses from "../../collaboration/socketResponses.dev"; -import FloorItemsGroup from "../../builder/groups/floorItemsGroup"; -import FloorPlanGroup from "../../builder/groups/floorPlanGroup"; -import FloorGroup from "../../builder/groups/floorGroup"; -import FloorGroupAilse from "../../builder/groups/floorGroupAisle"; -import Draw from "../../builder/functions/draw"; -import WallsAndWallItems from "../../builder/groups/wallsAndWallItems"; -import Ground from "../environment/ground"; -// import ZoneGroup from "../groups/zoneGroup1"; -import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; -import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; -import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp"; -import ZoneGroup from "../../builder/groups/zoneGroup"; -import useModuleStore from "../../../store/useModuleStore"; -import NavMeshCreator from "../../builder/agv/navMeshCreator"; - -export default function World() { - const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. - const csg = useRef(); // Reference for CSG object, used for 3D modeling. - const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. - const scene = useRef() as Types.RefScene; // Reference to the scene. - const camera = useRef() as Types.RefCamera; // Reference to the camera object. - const controls = useRef(); // Reference to the controls object. - const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. - const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. - - // Assigning the scene and camera from the Three.js state to the references. - - scene.current = state.scene; - camera.current = state.camera; - controls.current = state.controls; - raycaster.current = state.raycaster; - - const plane = useRef(null); // Reference for a plane object for raycaster reference. - const grid = useRef() as any; // Reference for a grid object for raycaster reference. - const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. - const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). - const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... - const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. - const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. - const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). - const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. - const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. - const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. - const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. - const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. - const onlyFloorline = useRef([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. - const onlyFloorlines = useRef([]); // Reference for all the floor lines that are ever drawn. - const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. - const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. - const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. - const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). - const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. - const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. - const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. - const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. - const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. - const floorGroupAisle = useRef() as Types.RefGroup; - const zoneGroup = useRef() as Types.RefGroup; - const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. - const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. - const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. - const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. - const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. - const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. - const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... - - const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. - - const [selectedItemsIndex, setSelectedItemsIndex] = useState(null); // State for tracking the index of the selected item. - const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. - const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D. - const { toolMode, setToolMode } = useToolMode(); - const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not. - const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); - const { socket } = useSocketStore(); - const { roofVisibility, setRoofVisibility } = useRoofVisibility(); - const { wallVisibility, setWallVisibility } = useWallVisibility(); - const { shadows, setShadows } = useShadows(); - const { renderDistance, setRenderDistance } = useRenderDistance(); - const { limitDistance, setLimitDistance } = useLimitDistance(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { walls, setWalls } = useWalls(); - const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); - const { activeModule } = useModuleStore(); - - // const loader = new GLTFLoader(); - // const dracoLoader = new DRACOLoader(); - - // dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); - // loader.setDRACOLoader(dracoLoader); - - ////////// Assest Configuration Values ////////// - - const AssetConfigurations: Types.AssetConfigurations = { - arch: { - modelUrl: arch, - scale: [0.75, 0.75, 0.75], - csgscale: [2, 4, 0.5], - csgposition: [0, 2, 0], - positionY: () => 0, - type: "Fixed-Move", - }, - door: { - modelUrl: door, - scale: [0.75, 0.75, 0.75], - csgscale: [2, 4, 0.5], - csgposition: [0, 2, 0], - positionY: () => 0, - type: "Fixed-Move", - }, - window: { - modelUrl: Window, - scale: [0.75, 0.75, 0.75], - csgscale: [5, 3, 0.5], - csgposition: [0, 1.5, 0], - positionY: (intersectionPoint) => intersectionPoint.point.y, - type: "Free-Move", - }, - }; - - ////////// All Toggle's ////////// - - useEffect(() => { - setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); - if (dragPointControls.current) { - dragPointControls.current.enabled = false; - } - if (toggleView) { - Layer2DVisibility( - activeLayer, - floorPlanGroup, - floorPlanGroupLine, - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls - ); - } else { - setToolMode(null); - setDeletePointOrLine(false); - setMovePoint(false); - loadWalls(lines, setWalls); - setUpdateScene(true); - line.current = []; - } - }, [toggleView]); - - useEffect(() => { - THREE.Cache.clear(); - THREE.Cache.enabled = true; - }, []); - - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - async function fetchVisibility() { - const visibility = await findEnvironment( - organization, - localStorage.getItem("userId")! - ); - if (visibility) { - setRoofVisibility(visibility.roofVisibility); - setWallVisibility(visibility.wallVisibility); - setShadows(visibility.shadowVisibility); - setRenderDistance(visibility.renderDistance); - setLimitDistance(visibility.limitDistance); - } - } - fetchVisibility(); - }, []); - - ////////// UseFrame is Here ////////// - - useFrame(() => { - if (toolMode) { - Draw( - state, - plane, - cursorPosition, - floorPlanGroupPoint, - floorPlanGroupLine, - snappedPoint, - isSnapped, - isSnappedUUID, - line, - lines, - ispreSnapped, - floorPlanGroup, - ReferenceLineMesh, - LineCreated, - setRefTextUpdate, - Tube, - anglesnappedPoint, - isAngleSnapped, - toolMode - ); - } - }); - - ////////// Return ////////// - - return ( - <> - - - - - - - - - - - - - - - - - {/* */} - - - - - - {/* */} - - - - - ); -} diff --git a/app/src/modules/builder/temp.md b/app/src/modules/simulation/actions/temp.md similarity index 100% rename from app/src/modules/builder/temp.md rename to app/src/modules/simulation/actions/temp.md diff --git a/app/src/modules/simulation/armbot/ArmBot.tsx b/app/src/modules/simulation/armbot/ArmBot.tsx deleted file mode 100644 index 066779a..0000000 --- a/app/src/modules/simulation/armbot/ArmBot.tsx +++ /dev/null @@ -1,87 +0,0 @@ -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/simulationTypes'; -import { ArmbotInstances } from "./ArmBotInstances"; -import { useResetButtonStore } from "../../../store/usePlayButtonStore"; - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} - -interface ArmBotProps { - armBots: ArmBotState[]; - setArmBots: React.Dispatch>; - setStaticMachines: React.Dispatch>; -} - -const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => { - const { activeModule } = useModuleStore(); - const { scene } = useThree(); - const { simulationStates } = useSimulationStates(); - const { isReset } = useResetButtonStore(); - - useEffect(() => { - const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot"); - const initialStates: ArmBotState[] = filtered - .filter(bot => bot.points.connections.targets.length > 0) - .map(bot => ({ - uuid: bot.modeluuid, - position: bot.position, - rotation: bot.rotation, - status: "idle", - material: "default", - triggerId: '', - actions: bot.points.actions, - connections: bot.points.connections, - isActive: false - })); - setArmBots(initialStates); - }, [simulationStates, isReset]); - - useEffect(() => { - armBots.forEach((bot) => { - const object = scene.getObjectByProperty("uuid", bot.uuid); - if (object) { - object.visible = activeModule !== "simulation"; - } - }); - }, [scene, activeModule, armBots]); - - return ( - <> - {activeModule === "simulation" && - armBots.map((bot, i) => ( - - ))} - - ); -}; - -export default ArmBot; diff --git a/app/src/modules/simulation/armbot/ArmBotInstances.tsx b/app/src/modules/simulation/armbot/ArmBotInstances.tsx deleted file mode 100644 index a0657db..0000000 --- a/app/src/modules/simulation/armbot/ArmBotInstances.tsx +++ /dev/null @@ -1,90 +0,0 @@ -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; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} - -interface ArmbotInstancesProps { - index: number; - armBot: ArmBotState; - setArmBots: React.Dispatch>; - setStaticMachines: React.Dispatch>; -} - -export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots, setStaticMachines }) => { - 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, triggerId: status === 'idle' ? '' : armBot.triggerId }; - } - 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 deleted file mode 100644 index e2df7f5..0000000 --- a/app/src/modules/simulation/armbot/IKAnimationController.tsx +++ /dev/null @@ -1,379 +0,0 @@ -import { useEffect, useMemo, useState, useRef } from "react"; -import { useFrame } from "@react-three/fiber"; -import * as THREE from "three"; -import { usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore"; -import { useSimulationStates } from "../../../store/store"; -import MaterialInstances from "./MaterialInstances"; -import { Line } from "react-chartjs-2"; -import { QuadraticBezierLine } from "@react-three/drei"; - - -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -type IKAnimationControllerProps = { - ikSolver: any; - processes: { - triggerId: string; - startPoint: THREE.Vector3; - endPoint: THREE.Vector3; - speed: number; - }[]; - selectedTrigger: string; - targetBoneName: string; - uuid: string; - logStatus: (status: string) => void; - groupRef: React.RefObject; - armBot: ArmBotState; - setArmBots: React.Dispatch>; - setStaticMachines: React.Dispatch>; - updateArmBotStatus: (status: string) => void; -} - -const IKAnimationController = ({ - ikSolver, - processes, - selectedTrigger, - targetBoneName, - uuid, - logStatus, - groupRef, - armBot, - setArmBots, - setStaticMachines, - 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();; - const statusRef = useRef("idle"); - const { simulationStates } = useSimulationStates(); - const { isReset } = useResetButtonStore(); - - const initialCurveRef = useRef(null); - const initialStartPositionRef = useRef(null); - - useEffect(() => { - setProgress(0); - }, [selectedTrigger]); - - useEffect(() => { - setProgress(0); - setNeedsInitialMovement(true); - setInitialProgress(0); - setIsInitializing(true); - }, [isReset]); - - useEffect(() => { - if (ikSolver) { - const targetBone = ikSolver.mesh.skeleton.bones.find( - (b: any) => b.name === targetBoneName - ); - if (targetBone) { - initialStartPositionRef.current = targetBone.position.clone(); - calculateInitialCurve(targetBone.position); - logStatus(`[Arm ${uuid}] Initializing IK system, starting position: ${targetBone.position.toArray()}`); - } - } - }, [ikSolver]); - - - 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(() => { - if (!isPlaying) return []; - - return processes.map(process => { - const localStart = groupRef.current?.worldToLocal(process.startPoint.clone()); - const localEnd = groupRef.current?.worldToLocal(process.endPoint.clone()); - - if (!localStart || !localEnd) return null; - - const midPoint = new THREE.Vector3( - (localStart.x + localEnd.x) / 2, - Math.max(localStart.y, localEnd.y) + 1, - (localStart.z + localEnd.z) / 2 - ); - const restToStartCurve = new THREE.CatmullRomCurve3([ - restPosition, - new THREE.Vector3().lerpVectors(restPosition, localStart, 0.5), - localStart - ]); - - const processCurve = new THREE.CatmullRomCurve3([ - localStart, - midPoint, - localEnd - ]); - - const endToRestCurve = new THREE.CatmullRomCurve3([ - localEnd, - new THREE.Vector3().lerpVectors(localEnd, restPosition, 0.5), - restPosition - ]); - - return { - triggerId: process.triggerId, - restToStartCurve, - processCurve, - endToRestCurve, - speed: process.speed, - totalDistance: - restPosition.distanceTo(localStart) + - localStart.distanceTo(localEnd) + - localEnd.distanceTo(restPosition) - }; - }).filter(Boolean); - }, [processes, isPlaying]); - - const activeProcess = useMemo(() => { - if (!selectedTrigger) return null; - return processedCurves.find(p => p?.triggerId === selectedTrigger); - }, [processedCurves, selectedTrigger]); - - // Initial movement to rest position - useFrame((_, delta) => { - if (!ikSolver || !needsInitialMovement || !isInitializing || !initialCurveRef.current) return; - - 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 (isInitializing || !isPlaying || !selectedTrigger || !activeProcess || !ikSolver) return; - - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName); - if (!bone) return; - - const { - restToStartCurve, - processCurve, - endToRestCurve, - speed, - totalDistance - } = activeProcess; - - // Calculate current segment and progress - const restToStartDist = restPosition.distanceTo(restToStartCurve.points[2]); - const processDist = processCurve.getLength(); - const endToRestDist = endToRestCurve.getLength(); - - const restToStartEnd = restToStartDist / totalDistance; - const processEnd = (restToStartDist + processDist) / totalDistance; - - setProgress(prev => { - let currentStatus = statusRef.current; - let currentPosition: THREE.Vector3; - const newProgress = Math.min(prev + delta * ((currentStatus === 'returning to rest') ? restSpeed : speed), 1); - - if (newProgress < restToStartEnd) { - // Moving from rest to start position - currentStatus = "moving to start"; - const segmentProgress = newProgress / restToStartEnd; - currentPosition = restToStartCurve.getPoint(segmentProgress); - } else if (newProgress < processEnd) { - // Processing - moving from start to end - currentStatus = "processing"; - const segmentProgress = (newProgress - restToStartEnd) / (processEnd - restToStartEnd); - currentPosition = processCurve.getPoint(segmentProgress); - if (statusRef.current !== "processing") { - updateConveyorOrStaticMachineStatusOnStart(selectedTrigger); - } - } else { - // Returning to rest position - currentStatus = "returning to rest"; - const segmentProgress = (newProgress - processEnd) / (1 - processEnd); - currentPosition = endToRestCurve.getPoint(segmentProgress); - } - - // Update status if changed - if (currentStatus !== statusRef.current) { - statusRef.current = currentStatus; - // updateArmBotStatus(currentStatus); - logStatus(`[Arm ${uuid}] Status: ${currentStatus}`); - } - - // Only trigger when the entire animation is complete (newProgress === 1) - if (newProgress === 1 && currentStatus === "returning to rest") { - updateConveyorOrStaticMachineStatusOnEnd(selectedTrigger); - } - - bone.position.copy(currentPosition); - ikSolver.update(); - return newProgress; - }); - }); - - const updateConveyorOrStaticMachineStatusOnStart = (selectedTrigger: string) => { - const currentProcess = processes.find(p => p.triggerId === selectedTrigger); - if (currentProcess) { - const triggerId = currentProcess.triggerId; - - const startPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.startPoint; - - const matchedMachine = simulationStates.find((state) => { - if (state.type === "Conveyor") { - return (state).points.some( - (point) => point.uuid === startPoint - ); - } else if (state.type === "StaticMachine") { - return state.points.uuid === startPoint; - } - return false; - }); - - if (matchedMachine) { - if (matchedMachine.type === "Conveyor") { - logStatus(`[Arm ${uuid}] start point which is a conveyor (${matchedMachine.modelName})`); - } else { - logStatus(`[Arm ${uuid}] started form start point which is a static machine (${matchedMachine.modelName})`); - } - - setTimeout(() => { - if (matchedMachine.type === "StaticMachine") { - updateArmBotStatus('dropping'); - } - - if (matchedMachine.type === "Conveyor") { - updateArmBotStatus('picking'); - } - }, 0); - } - } - } - - const updateConveyorOrStaticMachineStatusOnEnd = (selectedTrigger: string) => { - const currentProcess = processes.find(p => p.triggerId === selectedTrigger); - if (currentProcess) { - const triggerId = currentProcess.triggerId; - - const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint; - - const matchedMachine = simulationStates.find((state) => { - if (state.type === "Conveyor") { - return (state).points.some( - (point) => point.uuid === endPoint - ); - } else if (state.type === "StaticMachine") { - return state.points.uuid === endPoint; - } - return false; - }); - - if (matchedMachine) { - if (matchedMachine.type === "Conveyor") { - logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`); - } else { - logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`); - } - - setTimeout(() => { - if (matchedMachine.type === "StaticMachine") { - setStaticMachines((machines) => { - return machines.map((machine) => { - if (machine.uuid === matchedMachine.modeluuid) { - return { ...machine, status: "running" }; - } else { - return machine; - } - }); - }); - updateArmBotStatus('idle'); - } - - if (matchedMachine.type === "Conveyor") { - setArmBots((prev) => - prev.map((arm) => { - if (arm.uuid === uuid && arm.isActive === true) { - return { - ...arm, - isActive: false, - status: "idle", - }; - } - else { - return arm; - } - }) - ); - } - }, 0); - } - } - } - - return ( - <> - - - ); -}; - -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 deleted file mode 100644 index 6e7dc13..0000000 --- a/app/src/modules/simulation/armbot/IkInstances.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useMemo, useRef, useState } from "react"; -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"; - -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -const IkInstances = ({ - uuid, - selectedTrigger, - modelUrl, - processes, - position, - rotation, - armBot, - setArmBots, - setStaticMachines, - updateArmBotStatus -}: { - uuid: string; - selectedTrigger: string; - modelUrl: string; - processes: any; - position: [number, number, number]; - rotation: [number, number, number]; - armBot: ArmBotState; - setArmBots: React.Dispatch>; - setStaticMachines: React.Dispatch>; - updateArmBotStatus: (status: string) => void; -}) => { - const [ikSolver, setIkSolver] = useState(null); - 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/"); - loader.setDRACOLoader(draco); - }); - const cloned = useMemo(() => clone(gltf.scene), [gltf]); - const groupRef = useRef(null); - const targetBoneName = "Target"; - const skinnedMeshName = "link_0"; - - useEffect(() => { - if (!gltf) return; - const OOI: any = {}; - cloned.traverse((n: any) => { - if (n.name === targetBoneName) OOI.Target_Bone = n; - if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n; - }); - - if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return; - - const iks = [ - { - target: 7, - effector: 6, - links: [ - { - index: 5, - enabled: true, - rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0), - rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0), - }, - { - index: 4, - enabled: true, - rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0), - rotationMax: new THREE.Vector3(0, 0, 0), - }, - { - index: 3, - enabled: true, - rotationMin: new THREE.Vector3(0, 0, 0), - rotationMax: new THREE.Vector3(2, 0, 0), - }, - { index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) }, - { index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) }, - ], - }, - ]; - - const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks); - setIkSolver(solver); - - const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05); - // groupRef.current.add(helper); - - }, [gltf]); - - - const logStatus = (status: string) => { - // console.log(status); - } - - return ( - <> - - - - - - ); -}; - -export default IkInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/armbot/MaterialInstances.tsx b/app/src/modules/simulation/armbot/MaterialInstances.tsx deleted file mode 100644 index c83cd51..0000000 --- a/app/src/modules/simulation/armbot/MaterialInstances.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import * as THREE from 'three'; -import { Box } from '@react-three/drei'; - -type MaterialInstancesProps = { - statusRef: React.RefObject; - ikSolver: any; - targetBoneName: string; -}; - -function MaterialInstances({ - statusRef, - ikSolver, - targetBoneName -}: MaterialInstancesProps) { - if (!ikSolver) return null; - - const targetBone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName); - if (!targetBone) return null; - - const worldPos = new THREE.Vector3(); - targetBone.getWorldPosition(worldPos); - - return ( - - - - ); -} - -export default MaterialInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/conveyor.tsx b/app/src/modules/simulation/conveyor/conveyor.tsx new file mode 100644 index 0000000..bd21523 --- /dev/null +++ b/app/src/modules/simulation/conveyor/conveyor.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import ConveyorInstances from './instances/conveyorInstances' + +function Conveyor() { + return ( + <> + + + + + ) +} + +export default Conveyor \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx new file mode 100644 index 0000000..9c9d612 --- /dev/null +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +function ConveyorInstance() { + return ( + <> + + ) +} + +export default ConveyorInstance \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx new file mode 100644 index 0000000..3f53784 --- /dev/null +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import ConveyorInstance from './conveyorInstance/conveyorInstance' + +function ConveyorInstances() { + return ( + <> + + + + + ) +} + +export default ConveyorInstances \ No newline at end of file diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx new file mode 100644 index 0000000..335f1f5 --- /dev/null +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useRef, useState } from 'react'; +import * as THREE from 'three'; +import { useEventsStore } from '../../../../../store/simulation/useEventsStore'; +import useModuleStore from '../../../../../store/useModuleStore'; +import { TransformControls } from '@react-three/drei'; +import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys'; + +function PointsCreator() { + const { events, updatePoint, getPointByUuid } = useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(null); + const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); + const [selectedPoint, setSelectedPoint] = useState(null); + const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + + useEffect(() => { + setTransformMode(null); + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedPoint) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedPoint]); + + const updatePointToState = (selectedPoint: THREE.Mesh) => { + let point = JSON.parse(JSON.stringify(getPointByUuid(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid))); + if (point) { + point.position = [selectedPoint.position.x, selectedPoint.position.y, selectedPoint.position.z]; + updatePoint(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid, point) + } + } + + return ( + <> + {activeModule === 'simulation' && + <> + + {events.map((event, i) => { + if (event.type === 'transfer') { + return ( + + {event.points.map((point, j) => ( + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + key={`${i}-${j}`} + position={new THREE.Vector3(...point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }} + > + + + + ))} + + ); + } else if (event.type === 'vehicle') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === 'roboticArm') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === 'machine') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else { + return null; + } + })} + + {(selectedPoint && transformMode) && + { updatePointToState(selectedPoint) }} /> + } + + } + + ); +} + +export default PointsCreator; diff --git a/app/src/modules/simulation/events/points/points.tsx b/app/src/modules/simulation/events/points/points.tsx new file mode 100644 index 0000000..2a50f2d --- /dev/null +++ b/app/src/modules/simulation/events/points/points.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import PointsCreator from './creator/pointsCreator' + +function Points() { + return ( + <> + + + ) +} + +export default Points \ No newline at end of file diff --git a/app/src/modules/simulation/events/points/pointsCalculator.ts b/app/src/modules/simulation/events/points/pointsCalculator.ts new file mode 100644 index 0000000..86d368e --- /dev/null +++ b/app/src/modules/simulation/events/points/pointsCalculator.ts @@ -0,0 +1,47 @@ +import * as THREE from 'three'; +import { Group } from '../../../../types/world/worldTypes'; + +function PointsCalculator( + type: string, + model: Group, + rotation: THREE.Vector3 = new THREE.Vector3() +): { points?: THREE.Vector3[] } | null { + if (!model) return null; + + const box = new THREE.Box3().setFromObject(model); + + const size = new THREE.Vector3(); + box.getSize(size); + const center = new THREE.Vector3(); + box.getCenter(center); + + const rotationMatrix = new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(rotation.x, rotation.y, rotation.z)); + + const localTopMiddle = new THREE.Vector3(0, size.y / 2, 0); + const worldTopMiddle = localTopMiddle.clone().applyMatrix4(rotationMatrix).add(center); + + if (type === 'Conveyor') { + const isWidthLonger = size.x > size.z; + const longerSize = isWidthLonger ? size.x : size.z; + const shorterSize = isWidthLonger ? size.z : size.x; + const halfLongerSize = longerSize / 2; + const halfShorterSize = shorterSize / 2; + + const localEndPoint1 = new THREE.Vector3(isWidthLonger ? -halfLongerSize + halfShorterSize : 0, size.y / 2, isWidthLonger ? 0 : -halfLongerSize + halfShorterSize); + + const localEndPoint2 = new THREE.Vector3(isWidthLonger ? halfLongerSize - halfShorterSize : 0, size.y / 2, isWidthLonger ? 0 : halfLongerSize - halfShorterSize); + + const worldEndPoint1 = localEndPoint1.applyMatrix4(rotationMatrix).add(center); + const worldEndPoint2 = localEndPoint2.applyMatrix4(rotationMatrix).add(center); + + return { + points: [worldEndPoint1, worldTopMiddle, worldEndPoint2] + }; + } + + return { + points: [worldTopMiddle] + }; +} + +export default PointsCalculator; diff --git a/app/src/modules/simulation/temp.md b/app/src/modules/simulation/events/temp.md similarity index 100% rename from app/src/modules/simulation/temp.md rename to app/src/modules/simulation/events/temp.md diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx new file mode 100644 index 0000000..1445e70 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function MaterialAnimator() { + return ( + <> + ) +} + +export default MaterialAnimator \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx new file mode 100644 index 0000000..466e235 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function MaterialInstance() { + return ( + <> + ) +} + +export default MaterialInstance \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx new file mode 100644 index 0000000..519cba9 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import MaterialInstance from './instance/materialInstance' +import MaterialAnimator from './animator/materialAnimator' + +function MaterialInstances() { + return ( + <> + + + + + + + ) +} + +export default MaterialInstances \ No newline at end of file diff --git a/app/src/modules/simulation/materials/materials.tsx b/app/src/modules/simulation/materials/materials.tsx new file mode 100644 index 0000000..432d815 --- /dev/null +++ b/app/src/modules/simulation/materials/materials.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import MaterialInstances from './instances/materialInstances' + +function Materials() { + return ( + <> + + + + + ) +} + +export default Materials \ No newline at end of file diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx deleted file mode 100644 index 3db399e..0000000 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ /dev/null @@ -1,1428 +0,0 @@ -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/simulationTypes"; -import { QuadraticBezierLine } from "@react-three/drei"; -import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore, } from "../../../store/store"; -import useModuleStore from "../../../store/useModuleStore"; -import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; - -function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { - const { activeModule } = useModuleStore(); - const { gl, raycaster, scene, pointer, camera } = useThree(); - const { deleteTool } = useDeleteTool(); - const { renderDistance } = useRenderDistance(); - const { setIsConnecting } = useIsConnecting(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { isPlaying } = usePlayButtonStore(); - const { socket } = useSocketStore(); - const groupRefs = useRef<{ [key: string]: any }>({}); - - const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null); - const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null); - const [helperlineColor, setHelperLineColor] = useState("red"); - const [hoveredLineKey, setHoveredLineKey] = useState(null); - - const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => { - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Conveyor") { - // Handle outgoing connections from Conveyor - if (path.modeluuid === fromModelUUID) { - return { - ...path, - points: path.points.map((point) => { - if (point.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - const existingTargets = point.connections.targets || []; - - // Check connection limits - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); - if (toPath) { - // Check if we already have this type of connection - const hasConveyor = existingTargets.some(t => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "Conveyor"; - }); - const hasArmBot = existingTargets.some(t => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "ArmBot"; - }); - const hasVehicle = existingTargets.some(t => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "Vehicle"; - }); - - if (toPath.type === "Conveyor" && hasConveyor) { - console.log("Conveyor can only connect to one other conveyor"); - return point; - } - if (toPath.type === "ArmBot" && hasArmBot) { - console.log("Conveyor can only connect to one ArmBot"); - return point; - } - if (toPath.type === "Vehicle" && hasVehicle) { - console.log("Conveyor can only connect to one Vehicle"); - return point; - } - } - - if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { - return { - ...point, - connections: { - ...point.connections, - targets: [...existingTargets, newTarget], - }, - }; - } - } - return point; - }), - }; - } - // Handle incoming connections to Conveyor - else if (path.modeluuid === toModelUUID) { - return { - ...path, - points: path.points.map((point) => { - if (point.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - const existingTargets = point.connections.targets || []; - - // Check connection limits - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); - if (fromPath) { - const hasConveyor = existingTargets.some(t => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "Conveyor"; - }); - const hasArmBot = existingTargets.some(t => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "ArmBot"; - }); - const hasVehicle = existingTargets.some(t => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "Vehicle"; - }); - - if (fromPath.type === "Conveyor" && hasConveyor) { - console.log("Conveyor can only connect to one other conveyor"); - return point; - } - if (fromPath.type === "ArmBot" && hasArmBot) { - console.log("Conveyor can only connect to one ArmBot"); - return point; - } - if (fromPath.type === "Vehicle" && hasVehicle) { - console.log("Conveyor can only connect to one Vehicle"); - return point; - } - } - - if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { - return { - ...point, - connections: { - ...point.connections, - targets: [...existingTargets, reverseTarget], - }, - }; - } - } - return point; - }), - }; - } - } else if (path.type === "Vehicle") { - // Handle outgoing connections from Vehicle - if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - const existingTargets = path.points.connections.targets || []; - - // Check if target is a Conveyor - const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); - if (toPath?.type !== "Conveyor") { - console.log("Vehicle can only connect to Conveyors"); - return path; - } - - // Check if already has a connection - if (existingTargets.length >= 1) { - console.log("Vehicle can have only one connection"); - return path; - } - - if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget], - }, - }, - }; - } - } - // Handle incoming connections to Vehicle - else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - const existingTargets = path.points.connections.targets || []; - - // Check if source is a Conveyor - const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); - if (fromPath?.type !== "Conveyor") { - console.log("Vehicle can only connect to Conveyors"); - return path; - } - - // Check if already has a connection - if (existingTargets.length >= 1) { - console.log("Vehicle can have only one connection"); - return path; - } - - if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget], - }, - }, - }; - } - } - return path; - } else if (path.type === "StaticMachine") { - // Handle outgoing connections from StaticMachine - if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - - // Ensure target is an ArmBot - const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); - if (toPath?.type !== "ArmBot") { - console.log("StaticMachine can only connect to ArmBot"); - return path; - } - - const existingTargets = path.points.connections.targets || []; - - // Allow only one connection - if (existingTargets.length >= 1) { - console.log("StaticMachine can only have one connection"); - return path; - } - - if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget], - }, - }, - }; - } - } - - // Handle incoming connections to StaticMachine - else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - - const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); - if (fromPath?.type !== "ArmBot") { - console.log("StaticMachine can only be connected from ArmBot"); - return path; - } - - const existingTargets = path.points.connections.targets || []; - - if (existingTargets.length >= 1) { - console.log("StaticMachine can only have one connection"); - return path; - } - - if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget], - }, - }, - }; - } - } - return path; - } else if (path.type === "ArmBot") { - // Handle outgoing connections from ArmBot - if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - - const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); - if (!toPath) return path; - - const existingTargets = path.points.connections.targets || []; - - // Check if connecting to a StaticMachine and already connected to one - const alreadyConnectedToStatic = existingTargets.some((target) => { - const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); - return targetPath?.type === "StaticMachine"; - }); - - if (toPath.type === "StaticMachine") { - if (alreadyConnectedToStatic) { - console.log("ArmBot can only connect to one StaticMachine"); - return path; - } - } - - if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget], - }, - }, - }; - } - } - - // Handle incoming connections to ArmBot - else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - - const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); - if (!fromPath) return path; - - const existingTargets = path.points.connections.targets || []; - - const alreadyConnectedFromStatic = existingTargets.some((target) => { - const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); - return targetPath?.type === "StaticMachine"; - }); - - if (fromPath.type === "StaticMachine") { - if (alreadyConnectedFromStatic) { - console.log( - "ArmBot can only be connected from one StaticMachine" - ); - return path; - } - } - - if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget], - }, - }, - }; - } - } - return path; - } - - return path; - }); - - setSimulationStates(updatedPaths); - - const updatedPathDetails = updatedPaths.filter((path) => path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID); - - updateBackend(updatedPathDetails); - }; - - const updateBackend = async (updatedPaths: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - if (updatedPaths.length === 0) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === "Conveyor") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { - type: "Conveyor", - points: updatedPath.points, - speed: updatedPath.speed, - }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "Vehicle") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "StaticMachine") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "ArmBot") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } - }); - }; - - const handleAddConnection = (fromModelUUID: string, fromUUID: string, toModelUUID: string, toUUID: string) => { - updatePathConnections(fromModelUUID, fromUUID, toModelUUID, toUUID); - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - }; - - useEffect(() => { - const canvasElement = gl.domElement; - let drag = false; - let MouseDown = false; - - const onMouseDown = () => { - MouseDown = true; - drag = false; - }; - - const onMouseUp = () => { - MouseDown = false; - }; - - const onMouseMove = () => { - if (MouseDown) { - drag = true; - } - }; - - const onContextMenu = (evt: MouseEvent) => { - evt.preventDefault(); - if (drag || evt.button === 0) return; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects( - pathsGroupRef.current.children, - true - ); - - if (intersects.length > 0) { - const intersected = intersects[0].object; - - if (intersected.name.includes("events-sphere")) { - const modelUUID = intersected.userData.path.modeluuid; - const sphereUUID = intersected.uuid; - const worldPosition = new THREE.Vector3(); - intersected.getWorldPosition(worldPosition); - - let isStartOrEnd = false; - - if (intersected.userData.path.points && intersected.userData.path.points.length > 1) { - isStartOrEnd = intersected.userData.path.points.length > 0 - && (sphereUUID === intersected.userData.path.points[0].uuid || sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid); - } else if (intersected.userData.path.points) { - isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid; - } - - if (modelUUID) { - const firstPath = simulationStates.find((p) => p.modeluuid === firstSelected?.modelUUID); - const secondPath = simulationStates.find((p) => p.modeluuid === modelUUID); - - // Prevent vehicle-to-vehicle connections - if (firstPath && secondPath && firstPath.type === "Vehicle" && secondPath.type === "Vehicle") { - console.log("Cannot connect two vehicle paths together"); - return; - } - - // Prevent conveyor middle point to conveyor connections - if (firstPath && secondPath && firstPath.type === "Conveyor" && secondPath.type === "Conveyor" && (!firstSelected?.isCorner || !isStartOrEnd)) { - console.log("Conveyor connections must be between start/end points"); - return; - } - - // Check if this specific connection already exists - const isDuplicateConnection = firstSelected - ? simulationStates.some((path) => { - if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === "Conveyor") { - const point = path.points.find( - (p) => p.uuid === firstSelected.sphereUUID - ); - return point?.connections.targets.some( - (t) => - t.modelUUID === modelUUID && - t.pointUUID === sphereUUID - ); - } else if (path.type === "Vehicle") { - return path.points.connections.targets.some( - (t) => - t.modelUUID === modelUUID && - t.pointUUID === sphereUUID - ); - } - } - return false; - }) - : false; - - if (isDuplicateConnection) { - console.log("These points are already connected. Ignoring."); - return; - } - - // For Vehicles, check if they're already connected to anything - if (intersected.userData.path.type === "Vehicle") { - const vehicleConnections = - intersected.userData.path.points.connections.targets.length; - if (vehicleConnections >= 1) { - console.log("Vehicle can only have one connection"); - return; - } - } - - // For Conveyors, check connection limits in BOTH DIRECTIONS - if (firstSelected && (firstPath?.type === "Conveyor" || secondPath?.type === "Conveyor")) { - const checkConveyorLimits = (path: any, pointUUID: string) => { - if (path?.type === "Conveyor") { - const point = path.points.find((p: { uuid: string }) => p.uuid === pointUUID); - if (point) { - return { - hasConveyor: point.connections.targets.some((t: { modelUUID: string }) => { - const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID); - return targetPath?.type === "Conveyor"; - }), - hasArmBot: point.connections.targets.some((t: { modelUUID: string }) => { - const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID); - return targetPath?.type === "ArmBot"; - }), - hasVehicle: point.connections.targets.some((t: { modelUUID: string }) => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === "Vehicle"; - }) - }; - } - } - return { hasConveyor: false, hasArmBot: false, hasVehicle: false }; - }; - - const firstConveyorLimits = checkConveyorLimits(firstPath, firstSelected?.sphereUUID); - const secondConveyorLimits = checkConveyorLimits(secondPath, sphereUUID); - - // Check if trying to connect two conveyors - if (firstPath?.type === "Conveyor" && secondPath?.type === "Conveyor") { - if (firstConveyorLimits.hasConveyor || secondConveyorLimits.hasConveyor) { - console.log("Conveyor can only connect to one other conveyor"); - return; - } - } - - // Check if trying to connect to an ArmBot when already connected to one - if (secondPath?.type === "ArmBot" && firstConveyorLimits.hasArmBot) { - console.log("Conveyor can only connect to one ArmBot"); - return; - } - if (firstPath?.type === "ArmBot" && secondConveyorLimits.hasArmBot) { - console.log("Conveyor can only connect to one ArmBot"); - return; - } - - // Check if trying to connect to a Vehicle when already connected to one - if (secondPath?.type === "Vehicle" && firstConveyorLimits.hasVehicle) { - console.log("Conveyor can only connect to one Vehicle"); - return; - } - if (firstPath?.type === "Vehicle" && secondConveyorLimits.hasVehicle) { - console.log("Conveyor can only connect to one Vehicle"); - return; - } - } - - if (firstSelected) { - // Check if trying to connect Vehicle to non-Conveyor - if ((firstPath?.type === "Vehicle" && secondPath?.type !== "Conveyor") || (secondPath?.type === "Vehicle" && firstPath?.type !== "Conveyor")) { - console.log("Vehicle can only connect to Conveyors"); - return; - } - - // Prevent same-path connections - if (firstSelected.modelUUID === modelUUID) { - console.log("Cannot connect spheres on the same path."); - return; - } - - // Check if StaticMachine is involved in the connection - if ((firstPath?.type === "StaticMachine" && secondPath?.type !== "ArmBot") || (secondPath?.type === "StaticMachine" && firstPath?.type !== "ArmBot")) { - console.log("StaticMachine can only connect to ArmBot"); - return; - } - - // Check if StaticMachine already has a connection - if (firstPath?.type === "StaticMachine") { - const staticConnections = firstPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - if (secondPath?.type === "StaticMachine") { - const staticConnections = secondPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - - // Check if ArmBot is involved - if ((firstPath?.type === "ArmBot" && secondPath?.type === "StaticMachine") || (secondPath?.type === "ArmBot" && firstPath?.type === "StaticMachine")) { - const armBotPath = firstPath?.type === "ArmBot" ? firstPath : secondPath; - const staticPath = firstPath?.type === "StaticMachine" ? firstPath : secondPath; - - const armBotConnections = armBotPath.points.connections.targets || []; - const alreadyConnectedToStatic = armBotConnections.some( - (target) => { - const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); - return targetPath?.type === "StaticMachine"; - } - ); - - if (alreadyConnectedToStatic) { - console.log("ArmBot can only connect to one StaticMachine"); - return; - } - - const staticConnections = staticPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - - // Prevent ArmBot ↔ ArmBot - if (firstPath?.type === "ArmBot" && secondPath?.type === "ArmBot") { - console.log("Cannot connect two ArmBots together"); - return; - } - - // If one is ArmBot, ensure the other is StaticMachine or Conveyor - if (firstPath?.type === "ArmBot" || secondPath?.type === "ArmBot") { - const otherType = firstPath?.type === "ArmBot" ? secondPath?.type : firstPath?.type; - if (otherType !== "StaticMachine" && otherType !== "Conveyor") { - console.log("ArmBot can only connect to Conveyors or one StaticMachine"); - return; - } - } - - // At least one must be start/end point - if (!firstSelected.isCorner && !isStartOrEnd) { - console.log("At least one of the selected spheres must be a start or end point."); - return; - } - - // All checks passed - make the connection - handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID); - } else { - // First selection - just store it - setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd }); - setIsConnecting(true); - } - } - } - } else { - // Clicked outside - cancel connection - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - } - }; - - if (activeModule === "simulation" && !deleteTool) { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]); - - useFrame(() => { - Object.values(groupRefs.current).forEach((group) => { - if (group) { - const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); - group.visible = distance <= renderDistance && !isPlaying; - } - }); - }); - - useFrame(() => { - if (firstSelected) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") - ); - - let point: THREE.Vector3 | null = null; - let snappedSphere: { sphereUUID: string; position: THREE.Vector3; modelUUID: string; isCorner: boolean; } | null = null; - let isInvalidConnection = false; - - if (intersects.length > 0) { - point = intersects[0].point; - if (point.y < 0.05) { - point = new THREE.Vector3(point.x, 0.05, point.z); - } - } - - const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere")); - - if (sphereIntersects.length > 0) { - const sphere = sphereIntersects[0].object; - const sphereUUID = sphere.uuid; - const spherePosition = new THREE.Vector3(); - sphere.getWorldPosition(spherePosition); - const pathData = sphere.userData.path; - const modelUUID = pathData.modeluuid; - - const firstPath = simulationStates.find((p) => p.modeluuid === firstSelected.modelUUID); - const secondPath = simulationStates.find((p) => p.modeluuid === modelUUID); - const isVehicleToVehicle = firstPath?.type === "Vehicle" && secondPath?.type === "Vehicle"; - - // Inside the useFrame hook, where we check for snapped spheres: - const isConnectable = ( - pathData.type === 'Vehicle' || - pathData.type === 'ArmBot' || - (pathData.points.length > 0 && ( - sphereUUID === pathData.points[0].uuid || - sphereUUID === pathData.points[pathData.points.length - 1].uuid || - (pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points - )) - ) && - !isVehicleToVehicle && - !( - firstPath?.type === 'Conveyor' && - pathData.type === 'Conveyor' && - !firstSelected.isCorner - ); - - // Check for duplicate connection (regardless of path type) - const isDuplicateConnection = simulationStates.some(path => { - if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === 'Conveyor') { - const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); - return point?.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } else if (path.type === 'Vehicle') { - return path.points.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } - } - return false; - }); - - // For non-Vehicle paths, check if already connected - const isNonVehicleAlreadyConnected = pathData.type !== 'Vehicle' && - simulationStates.some(path => { - if (path.type === 'Conveyor') { - return path.points.some(point => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } - return false; - }); - - // Check vehicle connection rules - const isVehicleAtMaxConnections = pathData.type === 'Vehicle' && - pathData.points.connections.targets.length >= 1; - const isVehicleConnectingToNonConveyor = - (firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || - (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor'); - - // Check if StaticMachine is connecting to non-ArmBot - const isStaticMachineToNonArmBot = - (firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') || - (secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot'); - - // Check if StaticMachine already has a connection - const isStaticMachineAtMaxConnections = - (firstPath?.type === 'StaticMachine' && firstPath.points.connections.targets.length >= 1) || - (secondPath?.type === 'StaticMachine' && secondPath.points.connections.targets.length >= 1); - - // Check if ArmBot is connecting to StaticMachine - const isArmBotToStaticMachine = - (firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') || - (secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine'); - - // Prevent multiple StaticMachine connections to ArmBot - let isArmBotAlreadyConnectedToStatic = false; - if (isArmBotToStaticMachine) { - const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath; - isArmBotAlreadyConnectedToStatic = armBotPath.points.connections.targets.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; - }); - } - - // Prevent ArmBot to ArmBot - const isArmBotToArmBot = firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot'; - - // If ArmBot is involved, other must be Conveyor or StaticMachine - const isArmBotToInvalidType = (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') && - !(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' || - secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine'); - - // NEW: Check conveyor connection limits - let isConveyorAtMaxConnections = false; - if (firstPath?.type === 'Conveyor' || secondPath?.type === 'Conveyor') { - const conveyorPath = firstPath?.type === 'Conveyor' ? firstPath : secondPath; - const otherPath = firstPath?.type === 'Conveyor' ? secondPath : firstPath; - - if (conveyorPath) { - const conveyorPoint = Array.isArray(conveyorPath.points) - ? conveyorPath.points.find((p: { uuid: string }) => p.uuid === - (firstPath?.type === 'Conveyor' ? firstSelected.sphereUUID : sphereUUID)) - : undefined; - - if (conveyorPoint) { - const hasConveyor = conveyorPoint.connections.targets.some((t: { modelUUID: string }) => { - const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID); - return targetPath?.type === 'Conveyor'; - }); - const hasArmBot = conveyorPoint.connections.targets.some((t: { modelUUID: string }) => { - const targetPath = simulationStates.find((p: { modeluuid: string }) => p.modeluuid === t.modelUUID); - return targetPath?.type === 'ArmBot'; - }); - const hasVehicle = conveyorPoint.connections.targets.some((t: { modelUUID: string }) => { - const targetPath = simulationStates.find(p => p.modeluuid === t.modelUUID); - return targetPath?.type === 'Vehicle'; - }); - - if (otherPath?.type === 'Conveyor' && hasConveyor) { - isConveyorAtMaxConnections = true; - } - if (otherPath?.type === 'ArmBot' && hasArmBot) { - isConveyorAtMaxConnections = true; - } - if (otherPath?.type === 'Vehicle' && hasVehicle) { - isConveyorAtMaxConnections = true; - } - } - } - } - - if ( - !isDuplicateConnection && - !isVehicleToVehicle && - !isNonVehicleAlreadyConnected && - !isVehicleAtMaxConnections && - !isVehicleConnectingToNonConveyor && - !isStaticMachineToNonArmBot && - !isStaticMachineAtMaxConnections && - !isArmBotToArmBot && - !isArmBotToInvalidType && - !isArmBotAlreadyConnectedToStatic && - !isConveyorAtMaxConnections && // NEW: Check conveyor limits - firstSelected.sphereUUID !== sphereUUID && - firstSelected.modelUUID !== modelUUID && - (firstSelected.isCorner || isConnectable) && - !(firstPath?.type === 'Conveyor' && - pathData.type === 'Conveyor' && - !(firstSelected.isCorner && isConnectable)) - ) { - snappedSphere = { - sphereUUID, - position: spherePosition, - modelUUID, - isCorner: isConnectable - }; - } else { - isInvalidConnection = true; - } - } - - if (snappedSphere) { - point = snappedSphere.position; - } - - if (point) { - const distance = firstSelected.position.distanceTo(point); - const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (firstSelected.position.x + point.x) / 2, - Math.max(firstSelected.position.y, point.y) + heightFactor, - (firstSelected.position.z + point.z) / 2 - ); - - setCurrentLine({ - start: firstSelected.position, - end: point, - mid: midPoint, - }); - - if (sphereIntersects.length > 0) { - setHelperLineColor(isInvalidConnection ? "red" : "#6cf542"); - } else { - setHelperLineColor("yellow"); - } - } else { - setCurrentLine(null); - setIsConnecting(false); - } - } else { - setCurrentLine(null); - setIsConnecting(false); - } - }); - - const removeConnection = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => { - const updatedStates = simulationStates.map((state) => { - - // Handle Conveyor (which has multiple points) - if (state.type === "Conveyor") { - const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { - ...state, - points: state.points.map((point) => { - // Check if this point is either connection1 or connection2 - if ((state.modeluuid === connection1.model && point.uuid === connection1.point) || (state.modeluuid === connection2.model && point.uuid === connection2.point)) { - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter((target) => { - // Remove the target that matches the other connection - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - }; - } - return point; - }), - }; - return updatedConveyor; - } - - // Handle Vehicle - else if (state.type === "Vehicle") { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - const updatedVehicle: SimulationTypes.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - // Ensure all required Vehicle point properties are included - speed: state.points.speed, - actions: state.points.actions, - }, - }; - return updatedVehicle; - } - } - - // Handle StaticMachine - else if (state.type === "StaticMachine") { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = - { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - // Ensure all required StaticMachine point properties are included - actions: state.points.actions, - triggers: state.points.triggers, - }, - }; - return updatedStaticMachine; - } - } - - // Handle ArmBot - else if (state.type === "ArmBot") { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - actions: { - ...state.points.actions, - processes: - state.points.actions.processes?.filter((process) => { - return !( - process.startPoint === connection1.point || - process.endPoint === connection1.point || - process.startPoint === connection2.point || - process.endPoint === connection2.point - ); - }) || [], - }, - triggers: state.points.triggers, - }, - }; - return updatedArmBot; - } - } - return state; - }); - - const updatedPaths = updatedStates.filter( - (state) => - state.modeluuid === connection1.model || - state.modeluuid === connection2.model - ); - - updateBackend(updatedPaths); - - setSimulationStates(updatedStates); - }; - - const removeConnections = (deletedModelUUIDs: string[]) => { - - const deletedPointUUIDs = new Set(); - simulationStates.forEach(state => { - if (deletedModelUUIDs.includes(state.modeluuid)) { - if (state.type === "Conveyor" && state.points) { - state.points.forEach(point => { - deletedPointUUIDs.add(point.uuid); - }); - } else if (state.points && 'uuid' in state.points) { - deletedPointUUIDs.add(state.points.uuid); - } - } - }); - - const updatedStates = simulationStates.map((state) => { - // Handle Conveyor - if (state.type === "Conveyor") { - const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { - ...state, - points: state.points.map((point) => { - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }; - }), - }; - return updatedConveyor; - } - - // Handle Vehicle - else if (state.type === "Vehicle") { - const updatedVehicle: SimulationTypes.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedVehicle; - } - - // Handle StaticMachine - else if (state.type === "StaticMachine") { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = - { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedStaticMachine; - } - - // Handle ArmBot - else if (state.type === "ArmBot") { - const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target: any) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - actions: { - ...state.points.actions, - processes: state.points.actions.processes?.filter((process) => { - // Check if trigger is from deleted model - const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid)); - - if (matchedStates.length > 0) { - if (matchedStates[0]?.type === "StaticMachine") { - const trigPoints = matchedStates[0]?.points; - if (process.triggerId === trigPoints?.triggers?.uuid) { - return false; - } - } else if (matchedStates[0]?.type === "Conveyor") { - const trigPoints = matchedStates[0]?.points; - if (Array.isArray(trigPoints)) { - const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0); - const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid); - if (allTriggerUUIDs.includes(process.triggerId)) { - return false; - } - } - } - } - - // Check if startPoint or endPoint is from deleted model - if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) { - return false; - } - - return true; - }), - }, - }, - }; - return updatedArmBot; - } - - return state; - }); - - const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid)); - - updateBackend(filteredStates); - setSimulationStates(filteredStates); - }; - - return ( - - {simulationStates.flatMap((path) => { - if (path.type === "Conveyor") { - return path.points.flatMap((point) => - point.connections.targets.map((target, index) => { - const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); - if (targetPath?.type !== "Conveyor" && targetPath?.type !== "ArmBot") return null; - - const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", point.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty("uuid", target.pointUUID); - - if (fromSphere && toSphere) { - const fromWorldPosition = new THREE.Vector3(); - const toWorldPosition = new THREE.Vector3(); - fromSphere.getWorldPosition(fromWorldPosition); - toSphere.getWorldPosition(toWorldPosition); - - const distance = fromWorldPosition.distanceTo(toWorldPosition); - const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (fromWorldPosition.x + toWorldPosition.x) / 2, - Math.max(fromWorldPosition.y, toWorldPosition.y) + - heightFactor, - (fromWorldPosition.z + toWorldPosition.z) / 2 - ); - - return ( - (groupRefs.current[`${point.uuid}-${target.pointUUID}-${index}`] = el!)} - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}` ? "red" : targetPath?.type === "ArmBot" ? "#42a5f5" : "white"} - lineWidth={4} - dashed={deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}` ? false : true} - dashSize={0.75} - dashScale={20} - onPointerOver={() => setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)} - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - const connection1 = { - model: path.modeluuid, - point: point.uuid, - }; - const connection2 = { - model: target.modelUUID, - point: target.pointUUID, - }; - - removeConnection(connection1, connection2); - } - }} - userData={target} - /> - ); - } - return null; - }) - ); - } - - if (path.type === "Vehicle") { - return path.points.connections.targets.map((target, index) => { - const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", path.points.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty("uuid", target.pointUUID); - - if (fromSphere && toSphere) { - const fromWorldPosition = new THREE.Vector3(); - const toWorldPosition = new THREE.Vector3(); - fromSphere.getWorldPosition(fromWorldPosition); - toSphere.getWorldPosition(toWorldPosition); - - const distance = fromWorldPosition.distanceTo(toWorldPosition); - const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (fromWorldPosition.x + toWorldPosition.x) / 2, - Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, - (fromWorldPosition.z + toWorldPosition.z) / 2 - ); - - return ( - (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)} - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? "red" : "orange"} - lineWidth={4} - dashed={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? false : true} - dashSize={0.75} - dashScale={20} - onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - const connection1 = { - model: path.modeluuid, - point: path.points.uuid, - }; - const connection2 = { - model: target.modelUUID, - point: target.pointUUID, - }; - - removeConnection(connection1, connection2); - } - }} - userData={target} - /> - ); - } - return null; - }); - } - - if (path.type === "StaticMachine") { - return path.points.connections.targets.map((target, index) => { - const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); - if (targetPath?.type !== "ArmBot") return null; - - const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", path.points.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty("uuid", target.pointUUID); - - if (fromSphere && toSphere) { - const fromWorldPosition = new THREE.Vector3(); - const toWorldPosition = new THREE.Vector3(); - fromSphere.getWorldPosition(fromWorldPosition); - toSphere.getWorldPosition(toWorldPosition); - - const distance = fromWorldPosition.distanceTo(toWorldPosition); - const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (fromWorldPosition.x + toWorldPosition.x) / 2, - Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, - (fromWorldPosition.z + toWorldPosition.z) / 2 - ); - - return ( - (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)} - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? "red" : "#42a5f5"} - lineWidth={4} - dashed={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? false : true} - dashSize={0.75} - dashScale={20} - onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - const connection1 = { - model: path.modeluuid, - point: path.points.uuid, - }; - const connection2 = { - model: target.modelUUID, - point: target.pointUUID, - }; - - removeConnection(connection1, connection2); - } - }} - userData={target} - /> - ); - } - return null; - }); - } - - return []; - })} - - {currentLine && ( - - )} - - ); -} - -export default PathConnector; diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx deleted file mode 100644 index 1e9601d..0000000 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ /dev/null @@ -1,415 +0,0 @@ -import * as THREE from "three"; -import * as SimulationTypes from "../../../types/simulationTypes"; -import { useRef, useState, useEffect, useMemo } from "react"; -import { Sphere, TransformControls } from "@react-three/drei"; -import { - useEditingPoint, - useEyeDropMode, - useIsConnecting, - usePreviewPosition, - useRenderDistance, - useSelectedActionSphere, - useSelectedPath, - useSimulationStates, -} from "../../../store/store"; -import { useFrame, useThree } from "@react-three/fiber"; -import { useSubModuleStore } from "../../../store/useModuleStore"; -import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; -import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys"; - -function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { - const { isPlaying } = usePlayButtonStore(); - const { renderDistance } = useRenderDistance(); - const { setSubModule } = useSubModuleStore(); - const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere(); - const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); - const { editingPoint, setEditingPoint } = useEditingPoint(); - const { previewPosition, setPreviewPosition } = usePreviewPosition(); - const { raycaster, camera, pointer, gl } = useThree(); - const { setSelectedPath } = useSelectedPath(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { isConnecting } = useIsConnecting(); - const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); - - const groupRefs = useRef<{ [key: string]: THREE.Group }>({}); - const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); - const isMovingRef = useRef(false); - const transformRef = useRef(null); - const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); - - useEffect(() => { - setTransformMode(null); - const handleKeyDown = (e: KeyboardEvent) => { - const keyCombination = detectModifierKeys(e); - if (!selectedActionSphere) return; - if (keyCombination === "G") { - setTransformMode((prev) => (prev === "translate" ? null : "translate")); - } - if (keyCombination === "R") { - setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [selectedActionSphere]); - - useFrame(() => { - Object.values(groupRefs.current).forEach((group) => { - if (group) { - const distance = new THREE.Vector3( - ...group.position.toArray() - ).distanceTo(camera.position); - group.visible = ((distance <= renderDistance) && !isPlaying); - } - }); - }); - - useFrame(() => { - if (eyeDropMode) { - raycaster.setFromCamera(pointer, camera); - const intersectionPoint = new THREE.Vector3(); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - - if (point) { - setPreviewPosition({ x: point.x, y: point.z }); - } - } else { - setPreviewPosition(null); - } - }); - - useEffect(() => { - if (!camera) return; - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; - - const onPointerDown = () => { - isMovingRef.current = false; - }; - - const onPointerMove = () => { - isMovingRef.current = true; - }; - - const onPointerUp = (event: PointerEvent) => { - if ( - !isMovingRef.current && - eyeDropMode && - event.button === 0 && - previewPosition - ) { - event.preventDefault(); - if (editingPoint) { - handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y); - setEditingPoint(null); - setEyeDropMode(false); - } - } - }; - - if (eyeDropMode) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - }; - }, [eyeDropMode, editingPoint, previewPosition]); - - const updateBackend = async (updatedPath: SimulationTypes.VehicleEventsSchema | undefined) => { - if (!updatedPath) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - await setEventApi( - organization, - updatedPath.modeluuid, - { type: "Vehicle", points: updatedPath.points } - ); - } - - const handlePointUpdate = (pointType: "start" | "end", x: number, z: number) => { - if (!selectedActionSphere?.points?.uuid) return; - const updatedPaths = simulationStates.map((path) => { - - if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) { - return { - ...path, - points: { - ...path.points, - actions: { - ...path.points.actions, - [pointType]: { ...path.points.actions[pointType], x: x, y: z, }, - }, - }, - }; - } - return path; - }); - - const updatedPath = updatedPaths.find((path): path is SimulationTypes.VehicleEventsSchema => path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid); - updateBackend(updatedPath); - - setSimulationStates(updatedPaths); - }; - - return ( - - {simulationStates.map((path) => { - if (path.type === "Conveyor") { - const points = path.points.map( - (point) => new THREE.Vector3(...point.position) - ); - - return ( - (groupRefs.current[path.modeluuid] = el!)} - position={path.position} - rotation={path.rotation} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedPath({ - path, - group: groupRefs.current[path.modeluuid], - }); - setSelectedActionSphere(null); - setTransformMode(null); - setSubModule("mechanics"); - }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSelectedPath(null); - setSubModule("properties"); - }} - > - {path.points.map((point, index) => ( - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedActionSphere({ - path, - points: sphereRefs.current[point.uuid], - }); - setSubModule("mechanics"); - setSelectedPath(null); - }} - userData={{ points, path }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSubModule("properties"); - setSelectedActionSphere(null); - }} - > - - - ))} - - {points.slice(0, -1).map((point, index) => { - const nextPoint = points[index + 1]; - const segmentCurve = new THREE.CatmullRomCurve3([point, nextPoint,]); - const tubeGeometry = new THREE.TubeGeometry(segmentCurve, 20, 0.1, 16, false); - - return ( - - - - ); - })} - - ); - } else if (path.type === "Vehicle") { - return ( - (groupRefs.current[path.modeluuid] = el!)} - position={path.position} - rotation={path.rotation} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedPath({ - path, - group: groupRefs.current[path.modeluuid], - }); - setSelectedActionSphere(null); - setTransformMode(null); - setSubModule("mechanics"); - }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSelectedPath(null); - setSubModule("properties"); - }} - > - (sphereRefs.current[path.points.uuid] = el!)} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedActionSphere({ - path, - points: sphereRefs.current[path.points.uuid], - }); - setSubModule("mechanics"); - setSelectedPath(null); - }} - userData={{ points: path.points, path }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSubModule("properties"); - setSelectedActionSphere(null); - }} - > - - - - ); - } else if (path.type === "StaticMachine") { - return ( - (groupRefs.current[path.modeluuid] = el!)} - position={path.position} - rotation={path.rotation} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedPath({ - path, - group: groupRefs.current[path.modeluuid], - }); - setSelectedActionSphere(null); - setTransformMode(null); - setSubModule("mechanics"); - }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSelectedPath(null); - setSubModule("properties"); - }} - > - (sphereRefs.current[path.points.uuid] = el!)} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedActionSphere({ - path, - points: sphereRefs.current[path.points.uuid], - }); - setSubModule("mechanics"); - setSelectedPath(null); - }} - userData={{ points: path.points, path }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSubModule("properties"); - setSelectedActionSphere(null); - }} - > - - - - ); - } else if (path.type === "ArmBot") { - return ( - (groupRefs.current[path.modeluuid] = el!)} - position={path.position} - rotation={path.rotation} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedPath({ - path, - group: groupRefs.current[path.modeluuid], - }); - setSelectedActionSphere(null); - setTransformMode(null); - setSubModule("mechanics"); - }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSelectedPath(null); - setSubModule("properties"); - }} - > - (sphereRefs.current[path.points.uuid] = el!)} - onClick={(e) => { - if (isConnecting || eyeDropMode) return; - e.stopPropagation(); - setSelectedActionSphere({ - path, - points: sphereRefs.current[path.points.uuid], - }); - setSubModule("mechanics"); - setSelectedPath(null); - }} - userData={{ points: path.points, path }} - onPointerMissed={() => { - if (eyeDropMode) return; - setSubModule("properties"); - setSelectedActionSphere(null); - }} - > - - - - ); - } - return null; - })} - - {selectedActionSphere && transformMode && ( - - )} - - ); -} - -export default PathCreation; diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx deleted file mode 100644 index 2cd574f..0000000 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ /dev/null @@ -1,611 +0,0 @@ -import React, { useRef, useEffect, useMemo, useCallback } from "react"; -import { useLoader, useFrame } from "@react-three/fiber"; -import { GLTFLoader } from "three-stdlib"; -import * as THREE from "three"; -import { GLTF } from "three-stdlib"; -import crate from "../../../assets/gltf-glb/crate_box.glb"; - -import { useProcessAnimation } from "./useProcessAnimations"; -import ProcessObject from "./processObject"; -import { ProcessData } from "./types"; - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { - uuid: string; - name: string; - speed: number; - processes: { triggerId: string; startPoint: string; endPoint: string }[]; - }; - isActive?: boolean; -} -interface ProcessContainerProps { - processes: ProcessData[]; - setProcesses: React.Dispatch>; - agvRef: any; - MaterialRef: any; - armBots: ArmBotState[]; - setArmBots: React.Dispatch>; -} - -const ProcessAnimator: React.FC = ({ - processes, - setProcesses, - agvRef, - MaterialRef, - armBots, - setArmBots, -}) => { - const gltf = useLoader(GLTFLoader, crate) as GLTF; - const groupRef = useRef(null); - const tempStackedObjectsRef = useRef>({}); - - const { - animationStates, - setAnimationStates, - clockRef, - elapsedBeforePauseRef, - speedRef, - debugRef, - findSpawnPoint, - createSpawnedObject, - handlePointActions, - hasNonInheritActions, - getPointDataForAnimationIndex, - processes: processedProcesses, - checkAndCountTriggers, - } = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots); - - const baseMaterials = useMemo( - () => ({ - Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), - Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }), - Default: new THREE.MeshStandardMaterial(), - }), - [] - ); - - useEffect(() => { - // Update material references for all spawned objects - Object.entries(animationStates).forEach(([processId, processState]) => { - Object.keys(processState.spawnedObjects).forEach((objectId) => { - const entry = { processId, objectId }; - - const materialType = - processState.spawnedObjects[objectId]?.currentMaterialType; - - if (!materialType) { - return; - } - - const matRefArray = MaterialRef.current; - - // Find existing material group - const existing = matRefArray.find( - (entryGroup: { material: string; objects: any[] }) => - entryGroup.material === materialType - ); - - if (existing) { - // Check if this processId + objectId already exists - const alreadyExists = existing.objects.some( - (o: any) => - o.processId === entry.processId && o.objectId === entry.objectId - ); - - if (!alreadyExists) { - existing.objects.push(entry); - } - } else { - // Create new group for this material type - matRefArray.push({ - material: materialType, - objects: [entry], - }); - } - }); - }); - }, [animationStates, MaterialRef, agvRef]); - - // In processAnimator.tsx - only the relevant spawn logic part that needs fixes - - // Add this function to ProcessAnimator component - const isConnectedToActiveArmBot = useCallback( - (processId: any) => { - // Check if any active armbot is connected to this process - return armBots.some((armbot) => { - if (!armbot.isActive) return false; - - // Check if this armbot is connected to the process - return armbot.connections?.targets?.some((connection) => { - // Find the process that owns this modelUUID - const connectedProcess = processes.find((p) => - p.paths?.some((path) => path.modeluuid === connection.modelUUID) - ); - return connectedProcess?.id === processId; - }); - }); - }, - [armBots, processes] - ); - - // First useFrame for spawn logic - useFrame(() => { - // Spawn logic frame - const currentTime = - clockRef.current.getElapsedTime() - elapsedBeforePauseRef.current; - - setAnimationStates((prev) => { - const newStates = { ...prev }; - - processedProcesses.forEach((process) => { - const processState = newStates[process.id]; - if (!processState) return; - - // Check connection status - const isConnected = isConnectedToActiveArmBot(process.id); - - if (processState.isProcessDelaying) { - // Existing delay handling logic... - return; - } - - if (isConnected) { - newStates[process.id] = { - ...processState, - nextSpawnTime: Infinity, // Prevent future spawns - }; - return; - } - - const spawnPoint = findSpawnPoint(process); - if (!spawnPoint || !spawnPoint.actions) { - // console.log( - // `Process ${process.id} has no valid spawn point or 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 || "0") || 0; - - // Check if this is a zero interval spawn and we already spawned an object - if ( - spawnInterval === 0 && - processState.hasSpawnedZeroIntervalObject === true - ) { - return; // Don't spawn more objects for zero interval - } - - const effectiveSpawnInterval = spawnInterval / speedRef.current; - - if (currentTime >= processState.nextSpawnTime) { - const objectId = `obj-${process.id}-${processState.objectIdCounter}`; - const newObject = createSpawnedObject( - process, - currentTime, - spawnAction.material || "Default", - spawnPoint, - baseMaterials - ); - - // Initialize state properly to ensure animation - newObject.state = { - ...newObject.state, - isAnimating: true, - isDelaying: false, - delayComplete: false, - progress: 0.005, // Start with tiny progress to ensure animation begins - }; - - // Update state with the new object and flag for zero interval - newStates[process.id] = { - ...processState, - spawnedObjects: { - ...processState.spawnedObjects, - [objectId]: newObject, - }, - objectIdCounter: processState.objectIdCounter + 1, - nextSpawnTime: currentTime + effectiveSpawnInterval, - // Mark that we've spawned an object for zero interval case - hasSpawnedZeroIntervalObject: - spawnInterval === 0 - ? true - : processState.hasSpawnedZeroIntervalObject, - }; - } - }); - - return newStates; - }); - }); - - // Second useFrame for animation logic - useFrame((_, delta) => { - // Animation logic frame - const currentTime = - clockRef.current.getElapsedTime() - elapsedBeforePauseRef.current; - - setAnimationStates((prev) => { - const newStates = { ...prev }; - - processedProcesses.forEach((process) => { - const processState = newStates[process.id]; - if (!processState) { - return; - } - - // Check connection status with debugging - const isConnected = isConnectedToActiveArmBot(process.id); - // console.log( - // `Process ${process.id} animation - connected:`, - // isConnected - // ); - - if (isConnected) { - // Stop all animations when connected to active arm bot - newStates[process.id] = { - ...processState, - spawnedObjects: Object.entries(processState.spawnedObjects).reduce( - (acc, [id, obj]) => ({ - ...acc, - [id]: { - ...obj, - state: { - ...obj.state, - isAnimating: false, // Stop animation - isDelaying: false, // Clear delays - delayComplete: false, // Reset delays - progress: 0, // Reset progress - }, - }, - }), - {} - ), - }; - return; - } - - // Process delay handling - if (processState.isProcessDelaying) { - const effectiveDelayTime = - processState.processDelayDuration / speedRef.current; - - if ( - currentTime - processState.processDelayStartTime >= - effectiveDelayTime - ) { - // console.log( - // `Process ${process.id} delay completed, resuming animation` - // ); - newStates[process.id] = { - ...processState, - isProcessDelaying: false, - spawnedObjects: Object.entries( - processState.spawnedObjects - ).reduce( - (acc, [id, obj]) => ({ - ...acc, - [id]: { - ...obj, - state: { - ...obj.state, - isDelaying: false, - delayComplete: true, - isAnimating: true, - progress: - obj.state.progress === 0 ? 0.005 : obj.state.progress, - }, - }, - }), - {} - ), - }; - return; - } else { - return; - } - } - - // Ensure we have a valid path to follow - const path = - process.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) || - []; - - if (path.length < 2) { - // console.log( - // `Process ${process.id} has insufficient path points: ${path.length}` - // ); - return; - } - - const updatedObjects = { ...processState.spawnedObjects }; - let animationOccurring = false; // Track if any animation is happening - - Object.entries(processState.spawnedObjects).forEach( - ([objectId, obj]) => { - if (!obj.visible) { - return; - } - - const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current; - if (!currentRef) { - // console.log( - // `No reference for object ${objectId}, skipping animation` - // ); - return; - } - - // Initialize position for new objects - if ( - obj.position && - obj.state.currentIndex === 0 && - obj.state.progress === 0 - ) { - currentRef.position.copy(obj.position); - } - - const stateRef = obj.state; - - // Ensure animation state is properly set for objects - if (!stateRef.isAnimating && !stateRef.isDelaying && !isConnected) { - stateRef.isAnimating = true; - stateRef.progress = - stateRef.progress > 0 ? stateRef.progress : 0.005; - } - - // Handle delay logic - if (stateRef.isDelaying) { - const effectiveDelayTime = - stateRef.currentDelayDuration / speedRef.current; - - if (currentTime - stateRef.delayStartTime >= effectiveDelayTime) { - // console.log( - // `Delay complete for object ${objectId}, resuming animation` - // ); - stateRef.isDelaying = false; - stateRef.delayComplete = true; - stateRef.isAnimating = true; - - if (stateRef.progress === 0) { - stateRef.progress = 0.005; - } - - const nextPointIdx = stateRef.currentIndex + 1; - if (nextPointIdx < path.length) { - const slightProgress = Math.max(stateRef.progress, 0.005); - currentRef.position.lerpVectors( - path[stateRef.currentIndex], - nextPointIdx < path.length - ? path[nextPointIdx] - : path[stateRef.currentIndex], - slightProgress - ); - } - } else { - updatedObjects[objectId] = { ...obj, state: { ...stateRef } }; - return; - } - } - - // Skip non-animating objects - if (!stateRef.isAnimating) { - // console.log( - // `Object ${objectId} not animating, skipping animation updates` - // ); - return; - } - - animationOccurring = true; // Mark that animation is happening - - // Handle point actions - const currentPointData = getPointDataForAnimationIndex( - process, - stateRef.currentIndex - ); - - // Handle point actions when first arriving at point - if (stateRef.progress === 0 && currentPointData?.actions) { - const shouldStop = handlePointActions( - process.id, - objectId, - currentPointData.actions, - currentTime, - processedProcesses, - baseMaterials - ); - if (shouldStop) { - updatedObjects[objectId] = { ...obj, state: { ...stateRef } }; - return; - } - } - - const nextPointIdx = stateRef.currentIndex + 1; - const isLastPoint = nextPointIdx >= path.length; - - // Handle objects at the last point - if (isLastPoint) { - const isAgvPicking = agvRef.current.some( - (agv: any) => - agv.processId === process.id && agv.status === "picking" - ); - - const shouldHide = - !currentPointData?.actions || - !hasNonInheritActions(currentPointData.actions); - - if (shouldHide) { - if (isAgvPicking) { - // console.log( - // `AGV picking at last point for object ${objectId}, hiding object` - // ); - updatedObjects[objectId] = { - ...obj, - visible: false, - state: { - ...stateRef, - isAnimating: false, - }, - }; - } else { - tempStackedObjectsRef.current[objectId] = true; - - updatedObjects[objectId] = { - ...obj, - visible: true, - state: { - ...stateRef, - isAnimating: true, - }, - }; - } - - return; - } - } - - // Handle stacked objects when AGV picks - if (tempStackedObjectsRef.current[objectId]) { - const isAgvPicking = agvRef.current.some( - (agv: any) => - agv.processId === process.id && agv.status === "picking" - ); - - if (isAgvPicking) { - delete tempStackedObjectsRef.current[objectId]; - - updatedObjects[objectId] = { - ...obj, - visible: false, - state: { - ...stateRef, - isAnimating: false, - }, - }; - - return; - } - } - - // Handle normal animation progress for objects not at last point - if (!isLastPoint) { - const nextPoint = path[nextPointIdx]; - const distance = - path[stateRef.currentIndex].distanceTo(nextPoint); - const effectiveSpeed = stateRef.speed * speedRef.current; - const movement = effectiveSpeed * delta; - - // Ensure progress is always moving forward - if (stateRef.delayComplete && stateRef.progress < 0.01) { - stateRef.progress = 0.05; - stateRef.delayComplete = false; - // console.log( - // `Boosting progress for object ${objectId} after delay` - // ); - } else { - stateRef.progress += movement / distance; - // console.log( - // `Object ${objectId} progress: ${stateRef.progress.toFixed(3)}` - // ); - } - - // Handle point transition - if (stateRef.progress >= 1) { - stateRef.currentIndex = nextPointIdx; - stateRef.progress = 0; - currentRef.position.copy(nextPoint); - - // TRIGGER CHECK - When object arrives at new point - checkAndCountTriggers( - process.id, - objectId, - stateRef.currentIndex, // The new point index - processedProcesses, - currentTime - ); - - const newPointData = getPointDataForAnimationIndex( - process, - stateRef.currentIndex - ); - - // No action needed with newPointData here - will be handled in next frame - } else { - // Update position with lerp - currentRef.position.lerpVectors( - path[stateRef.currentIndex], - nextPoint, - stateRef.progress - ); - } - } - - updatedObjects[objectId] = { ...obj, state: { ...stateRef } }; - } - ); - - // Log if no animation is occurring when it should - if (!animationOccurring && !isConnected) { - // console.log( - // `Warning: No animation occurring for process ${process.id} despite not being connected` - // ); - } - - newStates[process.id] = { - ...processState, - spawnedObjects: updatedObjects, - }; - }); - - return newStates; - }); - }); - - if (!processedProcesses || processedProcesses.length === 0) { - return null; - } - - return ( - - {Object.entries(animationStates).flatMap(([processId, processState]) => - Object.entries(processState.spawnedObjects) - .filter(([_, obj]) => obj.visible) - .map(([objectId, obj]) => { - const process = processedProcesses.find((p) => p.id === processId); - - const renderAs = process?.renderAs || "custom"; - - return ( - - ); - }) - )} - - ); -}; - -export default ProcessAnimator; diff --git a/app/src/modules/simulation/process/processContainer.tsx b/app/src/modules/simulation/process/processContainer.tsx deleted file mode 100644 index 0bcdb13..0000000 --- a/app/src/modules/simulation/process/processContainer.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useState } from "react"; -import ProcessCreator from "./processCreator"; -import ProcessAnimator from "./processAnimator"; - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -interface ProcessContainerProps { - processes: any[]; - setProcesses: React.Dispatch>; - agvRef: any; - MaterialRef: any; - armBots: ArmBotState[]; - setArmBots: React.Dispatch>; -} - -const ProcessContainer: React.FC = ({ - processes, - setProcesses, - agvRef, - MaterialRef, - armBots, - setArmBots -}) => { - return ( - <> - - - - ); -}; - -export default ProcessContainer; diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx deleted file mode 100644 index 0a486c9..0000000 --- a/app/src/modules/simulation/process/processCreator.tsx +++ /dev/null @@ -1,492 +0,0 @@ -import React, { - useEffect, - useMemo, - useState, - useCallback, - useRef, -} from "react"; -import { useSimulationStates } from "../../../store/store"; -import * as THREE from "three"; -import { useThree } from "@react-three/fiber"; -import { - ArmBotEventsSchema, - ConveyorEventsSchema, - VehicleEventsSchema, -} from "../../../types/simulationTypes"; -import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; - -// Type definitions -export interface PointAction { - uuid: string; - name: string; - type: string; - material: string; - delay: number | string; - spawnInterval: string | number; - isUsed: boolean; -} - -export interface PointTrigger { - uuid: string; - bufferTime: number; - name: string; - type: string; - isUsed: boolean; -} - -// Update the connections type in your interfaces -export interface PathPoint { - uuid: string; - position: [number, number, number]; - actions: PointAction[]; - triggers: PointTrigger[]; - connections: { - targets: Array<{ modelUUID: string; pointUUID?: string }>; - }; -} - -export interface SimulationPath { - type: string; - modeluuid: string; - points: PathPoint[]; - pathPosition: [number, number, number]; - speed?: number; - isActive: boolean; -} -export interface ArmBot { - type: string; - modeluuid: string; - points: PathPoint[]; - pathPosition: [number, number, number]; - speed?: number; - isActive: boolean; -} - -export interface Process { - id: string; - paths: SimulationPath[]; - animationPath: THREE.Vector3[]; - pointActions: PointAction[][]; - pointTriggers: PointTrigger[][]; - speed: number; - isActive: boolean; -} - -interface ProcessCreatorProps { - onProcessesCreated: (processes: Process[]) => void; -} - -// Convert event schemas to SimulationPath -function convertToSimulationPath( - path: ConveyorEventsSchema | VehicleEventsSchema | ArmBotEventsSchema -): SimulationPath { - const { modeluuid } = path; - - // Normalized action handler - const normalizeAction = (action: any): PointAction => { - return { ...action }; // Return exact copy with no modifications - }; - - // Normalized trigger handler - const normalizeTrigger = (trigger: any): PointTrigger => { - return { ...trigger }; // Return exact copy with no modifications - }; - - if (path.type === "Conveyor") { - return { - type: path.type, - modeluuid, - points: path.points.map((point) => ({ - uuid: point.uuid, - position: point.position, - actions: Array.isArray(point.actions) - ? point.actions.map(normalizeAction) - : point.actions - ? [normalizeAction(point.actions)] - : [], - triggers: Array.isArray(point.triggers) - ? point.triggers.map(normalizeTrigger) - : point.triggers - ? [normalizeTrigger(point.triggers)] - : [], - connections: { - targets: point.connections.targets.map((target) => ({ - modelUUID: target.modelUUID, - })), - }, - })), - pathPosition: path.position, - speed: - typeof path.speed === "string" - ? parseFloat(path.speed) || 1 - : path.speed || 1, - isActive: false, // Added missing property - }; - } else if (path.type === "ArmBot") { - return { - type: path.type, - modeluuid, - points: [ - { - uuid: path.points.uuid, - position: path.points.position, - actions: Array.isArray(path.points.actions) - ? path.points.actions.map(normalizeAction) - : path.points.actions - ? [normalizeAction(path.points.actions)] - : [], - triggers: Array.isArray(path.points.triggers) - ? path.points.triggers.map(normalizeTrigger) - : path.points.triggers - ? [normalizeTrigger(path.points.triggers)] - : [], - connections: { - targets: path.points.connections.targets.map((target) => ({ - modelUUID: target.modelUUID, - pointUUID: target.pointUUID, // Include if available - })), - }, - }, - ], - pathPosition: path.position, - speed: path.points.actions?.speed || 1, - isActive: false, - }; - } else { - // For vehicle paths, handle the case where triggers might not exist - return { - type: path.type, - modeluuid, - points: [ - { - uuid: path.points.uuid, - position: path.points.position, - actions: Array.isArray(path.points.actions) - ? path.points.actions.map(normalizeAction) - : path.points.actions - ? [normalizeAction(path.points.actions)] - : [], - triggers: [], - connections: { - targets: path.points.connections.targets.map((target) => ({ - modelUUID: target.modelUUID, - })), - }, - }, - ], - pathPosition: path.position, - speed: path.points.speed || 1, - isActive: false, - }; - } -} - -// Helper function to create an empty process -const createEmptyProcess = (): Process => ({ - id: `process-${Math.random().toString(36).substring(2, 11)}`, - paths: [], - animationPath: [], - pointActions: [], - pointTriggers: [], // Added point triggers array - speed: 1, - isActive: false, -}); - -// 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.modelUUID === nextPath.modeluuid && - nextLastPoint.connections.targets.some( - (t) => t.modelUUID === currentPath.modeluuid - ) - ); - - // Check if current last connects to next first (no reversal needed) - const connectsToFirst = currentLastPoint.connections.targets.some( - (target) => - target.modelUUID === nextPath.modeluuid && - nextFirstPoint.connections.targets.some( - (t) => t.modelUUID === currentPath.modeluuid - ) - ); - - // Only reverse if connected to last point and not to first point - 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 < 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; -} - -// Main hook for process creation -export function useProcessCreation() { - const { scene } = useThree(); - const [processes, setProcesses] = useState([]); - - const hasSpawnAction = useCallback((path: SimulationPath): boolean => { - if (path.type !== "Conveyor") return false; - 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 pointTriggers: PointTrigger[][] = []; // Added point triggers collection - const processSpeed = paths[0]?.speed || 1; - - for (const path of paths) { - for (const point of path.points) { - if (path.type === "Conveyor") { - 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); - pointTriggers.push(point.triggers); // Collect triggers for each point - } - } - } - - return { - id: `process-${Math.random().toString(36).substring(2, 11)}`, - paths, - animationPath, - pointActions, - pointTriggers, - speed: processSpeed, - isActive: false, - }; - }, - [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.modelUUID)) { - const targetPath = pathMap.get(target.modelUUID); - if (targetPath) { - visited.add(target.modelUUID); - 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.modelUUID === 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 { simulationStates } = useSimulationStates(); - const { createProcessesFromPaths } = useProcessCreation(); - const prevPathsRef = useRef([]); - const prevProcessesRef = useRef([]); - const { isPlaying } = usePlayButtonStore(); - - const convertedPaths = useMemo((): SimulationPath[] => { - if (!simulationStates) return []; - return simulationStates.map((path) => - convertToSimulationPath( - path as - | ConveyorEventsSchema - | VehicleEventsSchema - | ArmBotEventsSchema - ) - ); - }, [simulationStates]); - - // Enhanced dependency tracking that includes action and trigger types - const pathsDependency = useMemo(() => { - if (!convertedPaths) return null; - return convertedPaths.map((path) => ({ - id: path.modeluuid, - // Track all action types for each point - actionSignature: path.points - .map((point, index) => - point.actions.map((action) => `${index}-${action.type}`).join("|") - ) - .join(","), - // Track all trigger types for each point - triggerSignature: path.points - .map((point, index) => - point.triggers - .map((trigger) => `${index}-${trigger.type}`) - .join("|") - ) - .join(","), - connections: path.points - .flatMap((p: PathPoint) => - p.connections.targets.map((t: { modelUUID: string }) => t.modelUUID) - ) - .join(","), - isActive: false, - })); - }, [convertedPaths]); - - // Force process recreation when paths change - useEffect(() => { - if (!convertedPaths || convertedPaths.length === 0) { - if (prevProcessesRef.current.length > 0) { - onProcessesCreated([]); - prevProcessesRef.current = []; - } - return; - } - - // Always regenerate processes if the pathsDependency has changed - // This ensures action and trigger type changes will be detected - const newProcesses = createProcessesFromPaths(convertedPaths); - prevPathsRef.current = convertedPaths; - - // Always update processes when action or trigger types change - onProcessesCreated(newProcesses); - prevProcessesRef.current = newProcesses; - }, [ - pathsDependency, // This now includes action and trigger types - onProcessesCreated, - convertedPaths, - createProcessesFromPaths, - ]); - - return null; - } -); - -export default ProcessCreator; diff --git a/app/src/modules/simulation/process/processObject.tsx b/app/src/modules/simulation/process/processObject.tsx deleted file mode 100644 index 01ef41d..0000000 --- a/app/src/modules/simulation/process/processObject.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { useMemo } from "react"; -import * as THREE from "three"; -import { GLTF } from "three-stdlib"; -import { SpawnedObject } from "./types"; - -interface ProcessObjectProps { - objectId: string; - obj: SpawnedObject; - renderAs?: "box" | "custom"; - gltf?: GLTF; -} - -const ProcessObject: React.FC = ({ - objectId, - obj, - renderAs = "custom", - gltf, -}) => { - const renderedObject = useMemo(() => { - if (renderAs === "box") { - return ( - } - material={obj.material} - position={obj.position} - > - - - ); - } - - if (gltf?.scene) { - const clonedScene = gltf.scene.clone(); - clonedScene.traverse((child) => { - if (child instanceof THREE.Mesh) { - child.material = obj.material; - } - }); - - return ( - } - position={obj.position} - > - - - ); - } - - return null; - }, [objectId, obj, renderAs, gltf]); - - return renderedObject; -}; - -export default ProcessObject; diff --git a/app/src/modules/simulation/process/types.ts b/app/src/modules/simulation/process/types.ts deleted file mode 100644 index 9c9a1bc..0000000 --- a/app/src/modules/simulation/process/types.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from "three"; - -export interface Trigger { - uuid: string; - name: string; - type: string; - bufferTime: number; - isUsed: boolean; -} - -export interface PointAction { - uuid: string; - name: string; - type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap"; - objectType: string; - material: string; - delay: string | number; - spawnInterval: string | number; - isUsed: boolean; - hitCount?: number; -} - -export interface ProcessPoint { - uuid: string; - position: number[]; - rotation: number[]; - actions: PointAction[]; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - triggers?: Trigger[]; -} -export interface ProcessPath { - modeluuid: string; - modelName: string; - points: ProcessPoint[]; - pathPosition: number[]; - pathRotation: number[]; - speed: number; - type: "Conveyor" | "Vehicle" | "ArmBot"; - isActive: boolean -} - -export interface ProcessData { - id: string; - paths: ProcessPath[]; - animationPath: { x: number; y: number; z: number }[]; - pointActions: PointAction[][]; - speed: number; - customMaterials?: Record; - renderAs?: "box" | "custom"; - pointTriggers: []; -} - -export interface AnimationState { - currentIndex: number; - progress: number; - isAnimating: boolean; - speed: number; - isDelaying: boolean; - delayStartTime: number; - currentDelayDuration: number; - delayComplete: boolean; - currentPathIndex: number; -} - -export interface SpawnedObject { - ref: React.RefObject; - state: AnimationState; - visible: boolean; - material: THREE.Material; - spawnTime: number; - currentMaterialType: string; - position: THREE.Vector3; -} - -export interface ProcessAnimationState { - spawnedObjects: { [objectId: string]: SpawnedObject }; - nextSpawnTime: number; - objectIdCounter: number; - isProcessDelaying: boolean; - processDelayStartTime: number; - processDelayDuration: number; - hasSpawnedZeroIntervalObject?: boolean; -} diff --git a/app/src/modules/simulation/process/useProcessAnimations.tsx b/app/src/modules/simulation/process/useProcessAnimations.tsx deleted file mode 100644 index 579ee3f..0000000 --- a/app/src/modules/simulation/process/useProcessAnimations.tsx +++ /dev/null @@ -1,665 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import * as THREE from "three"; -import { - ProcessData, - ProcessAnimationState, - SpawnedObject, - AnimationState, - ProcessPoint, - PointAction, - Trigger, -} from "./types"; -import { - useAnimationPlaySpeed, - usePauseButtonStore, - usePlayButtonStore, - useResetButtonStore, -} from "../../../store/usePlayButtonStore"; -import { usePlayAgv } from "../../../store/store"; - -interface ArmBotProcess { - triggerId: string; - startPoint: string; - endPoint: string; -} - -// Enhanced ProcessAnimationState with trigger tracking -interface EnhancedProcessAnimationState extends ProcessAnimationState { - triggerCounts: Record; - triggerLogs: Array<{ - timestamp: number; - pointId: string; - objectId: string; - triggerId: string; - }>; -} - -interface ProcessContainerProps { - processes: ProcessData[]; - setProcesses: React.Dispatch>; - agvRef: any; -} - -interface PlayAgvState { - playAgv: Record; - setPlayAgv: (data: any) => void; -} - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -export const useProcessAnimation = ( - processes: ProcessData[], - setProcesses: React.Dispatch>, - agvRef: any, - armBots: ArmBotState[], - setArmBots: React.Dispatch> -) => { - // State and refs initialization - const { isPlaying, setIsPlaying } = usePlayButtonStore(); - const { isPaused, setIsPaused } = usePauseButtonStore(); - const { isReset, setReset } = useResetButtonStore(); - const debugRef = useRef(false); - const clockRef = useRef(new THREE.Clock()); - const pauseTimeRef = useRef(0); - const elapsedBeforePauseRef = useRef(0); - const animationStatesRef = useRef>({}); - const { speed } = useAnimationPlaySpeed(); - const prevIsPlaying = useRef(null); - const [internalResetFlag, setInternalResetFlag] = useState(false); - const [animationStates, setAnimationStates] = useState>({}); - const speedRef = useRef(speed); - const { PlayAgv, setPlayAgv } = usePlayAgv(); - - // Effect hooks - useEffect(() => { - speedRef.current = speed; - }, [speed]); - - useEffect(() => { - if (prevIsPlaying.current !== null || !isPlaying) { - setAnimationStates({}); - } - prevIsPlaying.current = isPlaying; - }, [isPlaying]); - - useEffect(() => { - animationStatesRef.current = animationStates; - }, [animationStates]); - - // Reset handler - useEffect(() => { - if (isReset) { - setInternalResetFlag(true); - setIsPlaying(false); - setIsPaused(false); - setAnimationStates({}); - animationStatesRef.current = {}; - clockRef.current = new THREE.Clock(); - elapsedBeforePauseRef.current = 0; - pauseTimeRef.current = 0; - setReset(false); - setTimeout(() => { - setInternalResetFlag(false); - setIsPlaying(true); - }, 0); - } - }, [isReset, setReset, setIsPlaying, setIsPaused]); - - // Pause handler - useEffect(() => { - if (isPaused) { - pauseTimeRef.current = clockRef.current.getElapsedTime(); - } else if (pauseTimeRef.current > 0) { - const pausedDuration = clockRef.current.getElapsedTime() - pauseTimeRef.current; - elapsedBeforePauseRef.current += pausedDuration; - } - }, [isPaused]); - - // Initialize animation states with trigger tracking - useEffect(() => { - if (isPlaying && !internalResetFlag) { - const newStates: Record = {}; - - processes.forEach((process) => { - const triggerCounts: Record = {}; - - // Initialize trigger counts for all On-Hit triggers - process.paths?.forEach((path) => { - path.points?.forEach((point) => { - point.triggers?.forEach((trigger: Trigger) => { - if (trigger.type === "On-Hit" && trigger.isUsed) { - triggerCounts[`${point.uuid}-${trigger.uuid}`] = 0; - } - }); - }); - }); - - newStates[process.id] = { - spawnedObjects: {}, - nextSpawnTime: 0, - objectIdCounter: 0, - isProcessDelaying: false, - processDelayStartTime: 0, - processDelayDuration: 0, - triggerCounts, - triggerLogs: [], - }; - }); - - setAnimationStates(newStates); - animationStatesRef.current = newStates; - clockRef.current.start(); - } - }, [isPlaying, processes, internalResetFlag]); - - useEffect(() => { - if (isPlaying && !internalResetFlag) { - const newStates: Record = {}; - - // Initialize AGVs for each process first - processes.forEach((process) => { - // Find all vehicle paths for this process - const vehiclePaths = process.paths?.filter( - (path) => path.type === "Vehicle" - ) || []; - - // Initialize AGVs for each vehicle path - vehiclePaths.forEach((vehiclePath) => { - if (vehiclePath.points?.length > 0) { - const vehiclePoint = vehiclePath.points[0]; - const action = vehiclePoint.actions?.[0]; - const maxHitCount = action?.hitCount; - - const vehicleId = vehiclePath.modeluuid; - const processId = process.id; - - // Check if this AGV already exists - const existingAgv = agvRef.current.find( - (v: any) => v.vehicleId === vehicleId && v.processId === processId - ); - - if (!existingAgv) { - // Initialize the AGV in a stationed state - agvRef.current.push({ - processId, - vehicleId, - maxHitCount: maxHitCount || 0, - isActive: false, - hitCount: 0, - status: 'stationed', - lastUpdated: 0 - }); - } - } - }); - - // Then initialize trigger counts as before - const triggerCounts: Record = {}; - process.paths?.forEach((path) => { - path.points?.forEach((point) => { - point.triggers?.forEach((trigger: Trigger) => { - if (trigger.type === "On-Hit" && trigger.isUsed) { - triggerCounts[`${point.uuid}-${trigger.uuid}`] = 0; - } - }); - }); - }); - - newStates[process.id] = { - spawnedObjects: {}, - nextSpawnTime: 0, - objectIdCounter: 0, - isProcessDelaying: false, - processDelayStartTime: 0, - processDelayDuration: 0, - triggerCounts, - triggerLogs: [], - }; - }); - - setAnimationStates(newStates); - animationStatesRef.current = newStates; - clockRef.current.start(); - } - }, [isPlaying, processes, internalResetFlag]); - - // Helper functions - 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; - }; - - const findAnimationPathPoint = ( - process: ProcessData, - spawnPoint: ProcessPoint - ): THREE.Vector3 => { - if (process.animationPath && process.animationPath.length > 0) { - let pointIndex = 0; - 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) { - if (process.animationPath[pointIndex]) { - const p = process.animationPath[pointIndex]; - return new THREE.Vector3(p.x, p.y, p.z); - } - } - pointIndex++; - } - } - } - return new THREE.Vector3( - spawnPoint.position[0], - spawnPoint.position[1], - spawnPoint.position[2] - ); - }; - - // Optimized object creation - const createSpawnedObject = useCallback( - ( - process: ProcessData, - currentTime: number, - materialType: string, - spawnPoint: ProcessPoint, - baseMaterials: Record - ): SpawnedObject => { - const processMaterials = { - ...baseMaterials, - ...(process.customMaterials || {}), - }; - - const spawnPosition = findAnimationPathPoint(process, spawnPoint); - const material = - processMaterials[materialType as keyof typeof processMaterials] || - baseMaterials.Default; - - return { - ref: { current: null }, - state: { - currentIndex: 0, - progress: 0, - isAnimating: true, - speed: process.speed || 1, - isDelaying: false, - delayStartTime: 0, - currentDelayDuration: 0, - delayComplete: false, - currentPathIndex: 0, - }, - visible: true, - material: material, - currentMaterialType: materialType, - spawnTime: currentTime, - position: spawnPosition, - }; - }, - [] - ); - - // Material handling - const handleMaterialSwap = useCallback( - ( - processId: string, - objectId: string, - materialType: string, - processes: ProcessData[], - baseMaterials: Record - ) => { - setAnimationStates((prev) => { - const processState = prev[processId]; - if (!processState || !processState.spawnedObjects[objectId]) - return prev; - - const process = processes.find((p) => p.id === processId); - if (!process) return prev; - - const processMaterials = { - ...baseMaterials, - ...(process.customMaterials || {}), - }; - - const newMaterial = - processMaterials[materialType as keyof typeof processMaterials]; - if (!newMaterial) return prev; - - return { - ...prev, - [processId]: { - ...processState, - spawnedObjects: { - ...processState.spawnedObjects, - [objectId]: { - ...processState.spawnedObjects[objectId], - material: newMaterial, - currentMaterialType: materialType, - }, - }, - }, - }; - }); - }, - [] - ); - - // Point action handler with trigger counting - const handlePointActions = useCallback( - ( - processId: string, - objectId: string, - actions: PointAction[] = [], - currentTime: number, - processes: ProcessData[], - baseMaterials: Record - ): 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.isProcessDelaying) return prev; - - const delayDuration = - typeof action.delay === "number" - ? action.delay - : parseFloat(action.delay as string) || 0; - - if (delayDuration > 0) { - return { - ...prev, - [processId]: { - ...processState, - isProcessDelaying: true, - processDelayStartTime: currentTime, - processDelayDuration: delayDuration, - spawnedObjects: { - ...processState.spawnedObjects, - [objectId]: { - ...processState.spawnedObjects[objectId], - state: { - ...processState.spawnedObjects[objectId].state, - isAnimating: false, - 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, - processes, - baseMaterials - ); - } - break; - - default: - break; - } - }); - - return shouldStopAnimation; - }, - [handleMaterialSwap] - ); - - const deferredArmBotUpdates = useRef<{ uuid: string; triggerId: string }[]>([]); - - // Trigger counting system - const checkAndCountTriggers = useCallback( - ( - processId: string, - objectId: string, - currentPointIndex: number, - processes: ProcessData[], - currentTime: number - ) => { - setAnimationStates((prev) => { - const processState = prev[processId]; - if (!processState) return prev; - - const process = processes.find((p) => p.id === processId); - if (!process) return prev; - - const point = getPointDataForAnimationIndex(process, currentPointIndex); - if (!point?.triggers) return prev; - - const onHitTriggers = point.triggers.filter((t: Trigger) => t.type === "On-Hit" && t.isUsed); - - if (onHitTriggers.length === 0) return prev; - - let newTriggerCounts = { ...processState.triggerCounts }; - const newTriggerLogs = [...processState.triggerLogs]; - let shouldLog = false; - - const vehiclePaths = process.paths.filter((path) => path.type === "Vehicle"); - const armBotPaths = process.paths.filter((path) => path.type === "ArmBot"); - - const activeVehicles = vehiclePaths.filter((path) => { - const vehicleId = path.modeluuid; - const vehicleEntry = agvRef.current.find((v: any) => v.vehicleId === vehicleId && v.processId === processId); - return vehicleEntry?.isActive; - }); - - // Check if any ArmBot is active for this process - // const activeArmBots = armBotPaths.filter((path) => { - // const armBotId = path.modeluuid; - // const armBotEntry = armBots.find((a: any) => a.uuid === armBotId); - // return armBotEntry; - // }); - - // Only count triggers if no vehicles and no ArmBots are active for this process - - if (activeVehicles.length === 0) { - onHitTriggers.forEach((trigger: Trigger) => { - const triggerKey = `${point.uuid}-${trigger.uuid}`; - - newTriggerCounts[triggerKey] = (newTriggerCounts[triggerKey] || 0) + 1; - - newTriggerLogs.push({ timestamp: currentTime, pointId: point.uuid, objectId, triggerId: trigger.uuid, }); - - const connections = point.connections?.targets || []; - - connections.forEach((connection) => { - const connectedModelUUID = connection.modelUUID; - - const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID); - - if (matchingArmPath) { - deferredArmBotUpdates.current.push({ - uuid: connectedModelUUID, - triggerId: trigger.uuid, - }); - } else { - shouldLog = true; - } - }); - }); - } - - let processTotalHits = Object.values(newTriggerCounts).reduce((a, b) => a + b, 0); - - // Handle logic for vehicles when a trigger is hit - if (shouldLog) { - vehiclePaths.forEach((vehiclePath) => { - if (vehiclePath.points?.length > 0) { - const vehiclePoint = vehiclePath.points[0]; - const action = vehiclePoint.actions?.[0]; - const maxHitCount = action?.hitCount; - - if (maxHitCount !== undefined) { - const vehicleId = vehiclePath.modeluuid; - let vehicleEntry = agvRef.current.find( - (v: any) => - v.vehicleId === vehicleId && v.processId === processId - ); - - if (!vehicleEntry) { - vehicleEntry = { - processId, - vehicleId, - maxHitCount: maxHitCount, - isActive: false, - hitCount: 0, - status: "stationed", - }; - agvRef.current.push(vehicleEntry); - } - - if (!vehicleEntry.isActive) { - vehicleEntry.hitCount++; - vehicleEntry.lastUpdated = currentTime; - - if (vehicleEntry.hitCount >= vehicleEntry.maxHitCount) { - vehicleEntry.isActive = true; - newTriggerCounts = {}; - processTotalHits = 0; - } - } - } - } - }); - } - - return { - ...prev, - [processId]: { - ...processState, - triggerCounts: newTriggerCounts, - triggerLogs: newTriggerLogs, - totalHits: processTotalHits, - }, - }; - }); - }, []); - - useEffect(() => { - if (deferredArmBotUpdates.current.length > 0) { - const updates = [...deferredArmBotUpdates.current]; - deferredArmBotUpdates.current = []; - - setArmBots((prev) => - prev.map((bot) => { - const update = updates.find((u) => u.uuid === bot.uuid); - - return update - ? { ...bot, triggerId: update.triggerId, isActive: true } - : bot; - }) - ); - } - }, [animationStates]); - - // Utility functions - const hasNonInheritActions = useCallback( - (actions: PointAction[] = []): boolean => { - return actions.some( - (action) => action.isUsed && action.type !== "Inherit" - ); - }, []); - - const getPointDataForAnimationIndex = useCallback( - (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; - }, - [] - ); - - const getTriggerCounts = useCallback((processId: string) => { - return animationStatesRef.current[processId]?.triggerCounts || {}; - }, []); - - const getTriggerLogs = useCallback((processId: string) => { - return animationStatesRef.current[processId]?.triggerLogs || []; - }, []); - - return { - animationStates, - setAnimationStates, - clockRef, - elapsedBeforePauseRef, - speedRef, - debugRef, - findSpawnPoint, - createSpawnedObject, - handlePointActions, - hasNonInheritActions, - getPointDataForAnimationIndex, - checkAndCountTriggers, - getTriggerCounts, - getTriggerLogs, - processes, - }; -}; diff --git a/app/src/services/temp.md b/app/src/modules/simulation/products/temp.md similarity index 100% rename from app/src/services/temp.md rename to app/src/modules/simulation/products/temp.md diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx new file mode 100644 index 0000000..0cd4fe2 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function RoboticArmAnimator() { + return ( + <> + ) +} + +export default RoboticArmAnimator; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx new file mode 100644 index 0000000..2817906 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import IKInstance from '../ikInstance/ikInstance'; +import RoboticArmAnimator from '../animator/roboticArmAnimator'; + +function RoboticArmInstance() { + return ( + <> + + + + + + + ) +} + +export default RoboticArmInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx new file mode 100644 index 0000000..52a8610 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function IKInstance() { + return ( + <> + ) +} + +export default IKInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx new file mode 100644 index 0000000..d44ddd2 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import IKInstance from './ikInstance/ikInstance'; + +function IkInstances() { + return ( + <> + + + + + ) +} + +export default IkInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx new file mode 100644 index 0000000..6e8a70a --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import RoboticArmInstance from './armInstance/roboticArmInstance'; + +function RoboticArmInstances() { + return ( + <> + + + + + ) +} + +export default RoboticArmInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx new file mode 100644 index 0000000..1270d93 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import RoboticArmInstances from './instances/roboticArmInstances'; +import IkInstances from './instances/ikInstances'; + +function RoboticArm() { + return ( + <> + + + + + ) +} + +export default RoboticArm; \ No newline at end of file diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 84d3651..b7bf36d 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -1,74 +1,36 @@ -import { useState, useRef } from "react"; -import * as THREE from "three"; -import PathCreation from "./path/pathCreation"; -import PathConnector from "./path/pathConnector"; -import useModuleStore from "../../store/useModuleStore"; -import ProcessContainer from "./process/processContainer"; -import Agv from "../builder/agv/agv"; -import ArmBot from "./armbot/ArmBot"; -import StaticMachine from "./staticMachine/staticMachine"; - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} - -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} +import React, { useEffect } from 'react'; +import { useEventsStore } from '../../store/simulation/useEventsStore'; +import { useProductStore } from '../../store/simulation/useProductStore'; +import Vehicles from './vehicle/vehicles'; +import Points from './events/points/points'; +import Conveyor from './conveyor/conveyor'; +import RoboticArm from './roboticArm/roboticArm'; function Simulation() { - const { activeModule } = useModuleStore(); - const pathsGroupRef = useRef() as React.MutableRefObject; - const [armBots, setArmBots] = useState([]); - const [staticMachines, setStaticMachines] = useState([]); - const [processes, setProcesses] = useState([]); - const agvRef = useRef([]); - const MaterialRef = useRef([]); + const { events } = useEventsStore(); + const { products } = useProductStore(); + + useEffect(() => { + // console.log('events: ', events); + }, [events]) + + useEffect(() => { + // console.log('products: ', products); + }, [products]) return ( <> - {activeModule === "simulation" && ( - <> - - + - + - + + + - - )} - - - ); + ) } -export default Simulation; +export default Simulation \ No newline at end of file diff --git a/app/src/modules/simulation/simulationUI.tsx b/app/src/modules/simulation/simulationUI.tsx deleted file mode 100644 index 055fb36..0000000 --- a/app/src/modules/simulation/simulationUI.tsx +++ /dev/null @@ -1,409 +0,0 @@ -// import { useMemo, useState } from 'react'; -// import { useSelectedActionSphere, useToggleView, useSimulationStates, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store'; -// import * as THREE from 'three'; -// import useModuleStore from '../../store/useModuleStore'; - -// function SimulationUI() { -// const { ToggleView } = useToggleView(); -// const { activeModule } = useModuleStore(); -// const { startSimulation, setStartSimulation } = useStartSimulation(); -// const { selectedActionSphere } = useSelectedActionSphere(); -// const { selectedPath, setSelectedPath } = useSelectedPath(); -// const { simulationStates, setSimulationStates } = useSimulationStates(); -// const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath(); -// const [activeButton, setActiveButton] = useState(null); - -// const handleAddAction = () => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => { -// if (point.uuid === selectedActionSphere.points.uuid) { -// const actionIndex = point.actions.length; -// const newAction = { -// uuid: THREE.MathUtils.generateUUID(), -// name: `Action ${actionIndex + 1}`, // Assign action name based on index -// type: 'Inherit', -// material: 'Inherit', -// delay: 'Inherit', -// spawnInterval: 'Inherit', -// isUsed: false -// }; - -// return { ...point, actions: [...point.actions, newAction] }; -// } -// return point; -// }), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleDeleteAction = (uuid: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleActionSelect = (uuid: string, actionType: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// actions: point.actions.map((action) => -// action.uuid === uuid ? { ...action, type: actionType } : action -// ), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleMaterialSelect = (uuid: string, material: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// actions: point.actions.map((action) => -// action.uuid === uuid ? { ...action, material } : action -// ), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleDelayChange = (uuid: string, delay: number | string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// actions: point.actions.map((action) => -// action.uuid === uuid ? { ...action, delay } : action -// ), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// actions: point.actions.map((action) => -// action.uuid === uuid ? { ...action, spawnInterval } : action -// ), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleSpeedChange = (speed: number) => { -// if (!selectedPath) return; - -// const updatedPaths = simulationStates.map((path) => -// path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path -// ); - -// setSimulationStates(updatedPaths); -// setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } }); -// }; - -// const handleAddTrigger = () => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => { -// if (point.uuid === selectedActionSphere.points.uuid) { -// const triggerIndex = point.triggers.length; -// const newTrigger = { -// uuid: THREE.MathUtils.generateUUID(), -// name: `Trigger ${triggerIndex + 1}`, // Assign name based on index -// type: '', -// isUsed: false -// }; - -// return { ...point, triggers: [...point.triggers, newTrigger] }; -// } -// return point; -// }), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleDeleteTrigger = (uuid: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleTriggerSelect = (uuid: string, triggerType: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// triggers: point.triggers.map((trigger) => -// trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger -// ), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleResetPath = () => { -// if (!selectedPath) return; - -// }; - - -// const handleActionToggle = (uuid: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// actions: point.actions.map((action) => ({ -// ...action, -// isUsed: action.uuid === uuid ? !action.isUsed : false, -// })), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const handleTriggerToggle = (uuid: string) => { -// if (!selectedActionSphere) return; - -// const updatedPaths = simulationStates.map((path) => ({ -// ...path, -// points: path.points.map((point) => -// point.uuid === selectedActionSphere.points.uuid -// ? { -// ...point, -// triggers: point.triggers.map((trigger) => ({ -// ...trigger, -// isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, -// })), -// } -// : point -// ), -// })); - -// setSimulationStates(updatedPaths); -// }; - -// const selectedPoint = useMemo(() => { -// if (!selectedActionSphere) return null; -// return simulationStates.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.points.uuid); -// }, [selectedActionSphere, simulationStates]); - -// const createPath = () => { -// setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null); -// setDrawMaterialPath(!drawMaterialPath); -// } - -// return ( -// <> -// {activeModule === "simulation" && ( -//
-// {!ToggleView && ( -// <> -// - -//
-// {!ToggleView && } -//
- -// {selectedPath && ( -//
-// -// handleSpeedChange(parseFloat(e.target.value))} -// /> -//
-// )} - -// {selectedActionSphere && ( -//
-// -// - -// {selectedPoint?.actions.map((action) => ( -//
-// -// -// - -// {(action.type === 'Spawn' || action.type === 'Swap') && ( -//
-// -//
-// )} - -// {action.type === 'Delay' && ( -//
-// -// handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')} -// /> - -//
-// )} - -// {action.type === 'Spawn' && ( -//
-// -// handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')} -// /> - -//
-// )} -//
-//
-// ))} - -//
- -// {selectedPoint?.triggers.map((trigger) => ( -//
-// -// -// -//
-//
-// ))} - - -//
-// )} - -// {selectedPath && ( -//
-// -//
-// )} -// -// )} -//
-// )} -// -// ); -// } - -// export default SimulationUI; \ No newline at end of file diff --git a/app/src/modules/simulation/simulationtemp/collider/colliderCreator.tsx b/app/src/modules/simulation/simulationtemp/collider/colliderCreator.tsx deleted file mode 100644 index ec5e14b..0000000 --- a/app/src/modules/simulation/simulationtemp/collider/colliderCreator.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -function ColliderCreator() { - return ( - <> - ) -} - -export default ColliderCreator \ No newline at end of file diff --git a/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx b/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx deleted file mode 100644 index c9dd36b..0000000 --- a/app/src/modules/simulation/simulationtemp/path/pathCreator.tsx +++ /dev/null @@ -1,407 +0,0 @@ -import { useEffect, useState } from 'react'; -import * as THREE from 'three'; -import { useThree, useFrame } from '@react-three/fiber'; -import { Line, TransformControls } from '@react-three/drei'; -import { useDrawMaterialPath } from '../../../../store/store'; - -type PathPoint = { - position: THREE.Vector3; - rotation: THREE.Quaternion; - uuid: string; -}; - -type PathCreatorProps = { - simulationStates: PathPoint[][]; - setSimulationStates: React.Dispatch>; - connections: { start: PathPoint; end: PathPoint }[]; - setConnections: React.Dispatch> -}; - -const PathCreator = ({ simulationStates, setSimulationStates, connections, setConnections }: PathCreatorProps) => { - const { camera, scene, raycaster, pointer, gl } = useThree(); - const { drawMaterialPath } = useDrawMaterialPath(); - - const [currentPath, setCurrentPath] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }[]>([]); - const [temporaryPoint, setTemporaryPoint] = useState(null); - const [selectedPoint, setSelectedPoint] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string } | null>(null); - const [selectedConnectionPoint, setSelectedConnectionPoint] = useState<{ point: PathPoint; pathIndex: number } | null>(null); - const [previewConnection, setPreviewConnection] = useState<{ start: PathPoint; end?: THREE.Vector3 } | null>(null); - const [transformMode, setTransformMode] = useState<'translate' | 'rotate'>('translate'); - - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if (selectedPoint) { - if (event.key === 'g') { - setTransformMode('translate'); - } else if (event.key === 'r') { - setTransformMode('rotate'); - } - } - }; - - document.addEventListener('keydown', handleKeyDown); - - return () => { - document.removeEventListener('keydown', handleKeyDown); - }; - }, [selectedPoint]); - - useEffect(() => { - const canvasElement = gl.domElement; - - let drag = false; - let MouseDown = false; - - const onMouseDown = () => { - MouseDown = true; - drag = false; - }; - - const onMouseUp = () => { - MouseDown = false; - }; - - const onMouseMove = () => { - if (MouseDown) { - drag = true; - } - }; - - const onContextMenu = (e: any) => { - e.preventDefault(); - if (drag || e.button === 0) return; - if (currentPath.length > 1) { - setSimulationStates((prevPaths) => [...prevPaths, currentPath]); - } - setCurrentPath([]); - setTemporaryPoint(null); - setPreviewConnection(null); - setSelectedConnectionPoint(null); - }; - - const onMouseClick = (evt: any) => { - if (drag || evt.button !== 0) return; - - evt.preventDefault(); - raycaster.setFromCamera(pointer, camera); - - let intersects = raycaster.intersectObjects(scene.children, true); - - if (intersects.some((intersect) => intersect.object.name.includes("path-point"))) { - intersects = []; - } else { - intersects = intersects.filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") - ); - } - - if (intersects.length > 0 && selectedPoint === null) { - let point = intersects[0].point; - if (point.y < 0.05) { - point = new THREE.Vector3(point.x, 0.05, point.z); - } - const newPoint = { - position: point, - rotation: new THREE.Quaternion(), - uuid: THREE.MathUtils.generateUUID(), - }; - setCurrentPath((prevPath) => [...prevPath, newPoint]); - setTemporaryPoint(null); - } else { - setSelectedPoint(null); - } - }; - - if (drawMaterialPath) { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("click", onMouseClick); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - if (currentPath.length > 1) { - setSimulationStates((prevPaths) => [...prevPaths, currentPath]); - } - setCurrentPath([]); - setTemporaryPoint(null); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("click", onMouseClick); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [camera, scene, raycaster, currentPath, drawMaterialPath, selectedPoint]); - - useFrame(() => { - if (drawMaterialPath && currentPath.length > 0) { - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(scene.children, true).filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") - ); - - if (intersects.length > 0) { - let point = intersects[0].point; - if (point.y < 0.05) { - point = new THREE.Vector3(point.x, 0.05, point.z); - } - setTemporaryPoint(point); - } else { - setTemporaryPoint(null); - } - } else { - setTemporaryPoint(null); - } - }); - - const handlePointClick = (point: { position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }) => { - if (currentPath.length === 0 && drawMaterialPath) { - setSelectedPoint(point); - } else { - setSelectedPoint(null); - } - }; - - const handleTransform = (e: any) => { - if (selectedPoint) { - const updatedPosition = e.target.object.position.clone(); - const updatedRotation = e.target.object.quaternion.clone(); - const updatedPaths = simulationStates.map((path) => - path.map((p) => - p.uuid === selectedPoint.uuid ? { ...p, position: updatedPosition, rotation: updatedRotation } : p - ) - ); - setSimulationStates(updatedPaths); - } - }; - - - const meshContext = (uuid: string) => { - const pathIndex = simulationStates.findIndex(path => path.some(point => point.uuid === uuid)); - if (pathIndex === -1) return; - - const clickedPoint = simulationStates[pathIndex].find(point => point.uuid === uuid); - if (!clickedPoint) return; - - const isStart = simulationStates[pathIndex][0].uuid === uuid; - const isEnd = simulationStates[pathIndex][simulationStates[pathIndex].length - 1].uuid === uuid; - - if (pathIndex === 0 && isStart) { - console.log("The first-ever point is not connectable."); - setSelectedConnectionPoint(null); - setPreviewConnection(null); - return; - } - - if (!isStart && !isEnd) { - console.log("Selected point is not a valid connection point (not start or end)"); - setSelectedConnectionPoint(null); - setPreviewConnection(null); - return; - } - - if (connections.some(conn => conn.start.uuid === uuid || conn.end.uuid === uuid)) { - console.log("The selected point is already connected."); - setSelectedConnectionPoint(null); - setPreviewConnection(null); - return; - } - - if (!selectedConnectionPoint) { - setSelectedConnectionPoint({ point: clickedPoint, pathIndex }); - setPreviewConnection({ start: clickedPoint }); - console.log("First point selected for connection:", clickedPoint); - return; - } - - if (selectedConnectionPoint.pathIndex === pathIndex) { - console.log("Cannot connect points within the same path."); - setSelectedConnectionPoint(null); - setPreviewConnection(null); - return; - } - - if (connections.some(conn => conn.start.uuid === clickedPoint.uuid || conn.end.uuid === clickedPoint.uuid)) { - console.log("The target point is already connected."); - setSelectedConnectionPoint(null); - setPreviewConnection(null); - return; - } - - setConnections(prevConnections => [ - ...prevConnections, - { start: selectedConnectionPoint.point, end: clickedPoint }, - ]); - - - setSelectedConnectionPoint(null); - setPreviewConnection(null); - }; - - useEffect(() => { - if (!selectedConnectionPoint) { - setPreviewConnection(null); - } - }, [selectedConnectionPoint, connections]); - - useFrame(() => { - if (selectedConnectionPoint) { - raycaster.setFromCamera(pointer, camera); - - const intersects = raycaster.intersectObjects(scene.children, true).filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") - ); - - if (intersects.length > 0) { - let point = intersects[0].point; - if (point.y < 0.05) { - point = new THREE.Vector3(point.x, 0.05, point.z); - } - setPreviewConnection({ start: selectedConnectionPoint.point, end: point }); - } else { - setPreviewConnection(null); - } - } - }); - - return ( - <> - - {/* Render finalized simulationStates */} - {simulationStates.map((path, pathIndex) => ( - - point.position)} - color="yellow" - lineWidth={5} - userData={{ isPathObject: true }} - /> - - ))} - - {/* Render finalized points */} - {simulationStates.map((path) => - path.map((point) => ( - handlePointClick(point)} - onPointerMissed={() => { setSelectedPoint(null) }} - onContextMenu={() => { meshContext(point.uuid); }} - > - - - - )) - )} - - {connections.map((conn, index) => ( - - ))} - - - - {/* Render current path */} - {currentPath.length > 1 && ( - - point.position)} - color="red" - lineWidth={5} - userData={{ isPathObject: true }} - /> - - )} - - {/* Render current path points */} - {currentPath.map((point) => ( - - - - - ))} - - {/* Render temporary indicator line */} - {temporaryPoint && currentPath.length > 0 && ( - - - - )} - - {/* Render dashed preview connection */} - {previewConnection && previewConnection.end && ( - - )} - - {/* Render temporary point */} - {temporaryPoint && ( - - - - - )} - - {/* Attach TransformControls to the selected point */} - {selectedPoint && ( - - )} - - ); -}; - -export default PathCreator; \ No newline at end of file diff --git a/app/src/modules/simulation/simulationtemp/path/pathFlow.tsx b/app/src/modules/simulation/simulationtemp/path/pathFlow.tsx deleted file mode 100644 index 586fd3a..0000000 --- a/app/src/modules/simulation/simulationtemp/path/pathFlow.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import * as THREE from 'three'; -import { useState, useEffect, useRef, useMemo } from "react"; -import { useLoader, useFrame } from "@react-three/fiber"; -import { GLTFLoader } from "three-stdlib"; -import crate from "../../../../assets/gltf-glb/crate_box.glb"; -import { useOrganization } from '../../../../store/store'; -import { useControls } from 'leva'; - -type PathPoint = { - position: THREE.Vector3; - rotation: THREE.Quaternion; - uuid: string; -}; - -type PathFlowProps = { - path: PathPoint[]; - connections: { start: PathPoint; end: PathPoint }[]; -}; - -export default function PathFlow({ path, connections }: PathFlowProps) { - const { organization } = useOrganization(); - const [isPaused, setIsPaused] = useState(false); - const [isStopped, setIsStopped] = useState(false); - - const { spawnInterval, speed, pauseResume, startStop } = useControls({ - spawnInterval: { value: 1000, min: 500, max: 5000, step: 100 }, - speed: { value: 2, min: 1, max: 20, step: 0.5 }, - pauseResume: { value: false, label: "Pause/Resume" }, - startStop: { value: false, label: "Start/Stop" }, - }); - - const [meshes, setMeshes] = useState<{ id: number }[]>([]); - const gltf = useLoader(GLTFLoader, crate); - - const meshIdRef = useRef(0); - const lastSpawnTime = useRef(performance.now()); - const totalPausedTime = useRef(0); - const pauseStartTime = useRef(null); - - useEffect(() => { - setIsPaused(pauseResume); - setIsStopped(startStop); - }, [pauseResume, startStop]); - - const removeMesh = (id: number) => { - setMeshes((prev) => prev.filter((m) => m.id !== id)); - }; - - useFrame(() => { - if (isStopped || !path) return; - - const now = performance.now(); - - if (isPaused) { - if (pauseStartTime.current === null) { - pauseStartTime.current = now; - } - return; - } - - if (pauseStartTime.current !== null) { - totalPausedTime.current += now - pauseStartTime.current; - pauseStartTime.current = null; - } - - const adjustedTime = now - totalPausedTime.current; - - if (adjustedTime - lastSpawnTime.current >= spawnInterval) { - setMeshes((prev) => [...prev, { id: meshIdRef.current++ }]); - lastSpawnTime.current = adjustedTime; - } - }); - - return ( - <> - {meshes.map((mesh) => ( - - ))} - - ); -} - -function MovingMesh({ meshId, points, speed, gltf, removeMesh, isPaused }: any) { - const meshRef = useRef(); - const startTime = useRef(null); // Initialize as null - const pausedTime = useRef(0); - const pauseStartTime = useRef(null); - - const distances = useMemo(() => { - if (!points || points.length < 2) return []; - return points.slice(1).map((point: any, i: number) => points[i].position.distanceTo(point.position)); - }, [points]); - - useFrame(() => { - if (!points || points.length < 2) return; - - if (startTime.current === null && points.length > 0) { - startTime.current = performance.now(); - } - - if (!meshRef.current) return; - - if (isPaused) { - if (pauseStartTime.current === null) { - pauseStartTime.current = performance.now(); - } - return; - } - - if (pauseStartTime.current !== null) { - pausedTime.current += performance.now() - pauseStartTime.current; - pauseStartTime.current = null; - } - - if (startTime.current === null) return; - - const elapsed = performance.now() - startTime.current - pausedTime.current; - - const distanceTraveled = elapsed / 1000 * speed; - - let remainingDistance = distanceTraveled; - let currentSegmentIndex = 0; - - while (currentSegmentIndex < distances.length && remainingDistance > distances[currentSegmentIndex]) { - remainingDistance -= distances[currentSegmentIndex]; - currentSegmentIndex++; - } - - if (currentSegmentIndex >= distances.length) { - removeMesh(meshId); - return; - } - - const progress = remainingDistance / distances[currentSegmentIndex]; - const start = points[currentSegmentIndex].position; - const end = points[currentSegmentIndex + 1].position; - - meshRef.current.position.lerpVectors(start, end, Math.min(progress, 1)); - - const startRotation = points[currentSegmentIndex].rotation; - const endRotation = points[currentSegmentIndex + 1].rotation; - const interpolatedRotation = new THREE.Quaternion().slerpQuaternions(startRotation, endRotation, Math.min(progress, 1)); - - meshRef.current.quaternion.copy(interpolatedRotation); - }); - - return ( - <> - {points && points.length > 0 && - - - - } - - ); -} \ No newline at end of file diff --git a/app/src/modules/simulation/simulationtemp/process/processCreator.tsx b/app/src/modules/simulation/simulationtemp/process/processCreator.tsx deleted file mode 100644 index 1c5241d..0000000 --- a/app/src/modules/simulation/simulationtemp/process/processCreator.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' - -function ProcessCreator() { - return ( - <> - ) -} - -export default ProcessCreator \ No newline at end of file diff --git a/app/src/modules/simulation/simulationtemp/simulation.tsx b/app/src/modules/simulation/simulationtemp/simulation.tsx deleted file mode 100644 index 1c7847b..0000000 --- a/app/src/modules/simulation/simulationtemp/simulation.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { useState } from 'react'; -import * as THREE from 'three'; -import PathCreator from './path/pathCreator'; -import PathFlow from './path/pathFlow'; - -type PathPoint = { - position: THREE.Vector3; - rotation: THREE.Quaternion; - uuid: string; -}; - -function Simulation() { - const [simulationStates, setSimulationStates] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }[][]>([]); - const [connections, setConnections] = useState<{ start: PathPoint; end: PathPoint }[]>([]); - - return ( - <> - - {simulationStates.map((path, index) => ( - - ))} - - ); -} - -export default Simulation; \ No newline at end of file diff --git a/app/src/modules/simulation/staticMachine/staticMachine.tsx b/app/src/modules/simulation/staticMachine/staticMachine.tsx deleted file mode 100644 index 3f61993..0000000 --- a/app/src/modules/simulation/staticMachine/staticMachine.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useEffect } from 'react' -import * as SimulationTypes from '../../../types/simulationTypes'; -import { useSimulationStates } from '../../../store/store'; -import StaticMachineInstances from './staticMachineInstances'; -import { useResetButtonStore } from '../../../store/usePlayButtonStore'; - -interface ArmBotState { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - status: string; - material: string; - triggerId: string; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - isActive?: boolean; -} -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} - -type StaticMachineProps = { - setArmBots: React.Dispatch>; - staticMachines: StaticMachineState[]; - setStaticMachines: React.Dispatch>; -} - -function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) { - - const { simulationStates } = useSimulationStates(); - const { isReset } = useResetButtonStore(); - - useEffect(() => { - const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine"); - const initialStates: StaticMachineState[] = filtered - .filter(machine => machine.points.connections.targets.length > 0) - .map(machine => ({ - uuid: machine.modeluuid, - status: "idle", - actions: machine.points.actions, - machineTriggerId: machine.points.triggers.uuid, - connectedArmBot: machine.points.connections.targets[0].modelUUID - })); - setStaticMachines(initialStates); - }, [simulationStates, isReset]); - - const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => { - setArmBots((prevArmBots) => { - return prevArmBots.map(bot => { - if (bot.uuid === armBotUuid) { - return { ...bot, triggerId: triggerId }; - } - return bot; - }); - }); - setStaticMachines((prevStaticMachines) => { - return prevStaticMachines.map(machine => { - if (machine.uuid === machineId) { - return { ...machine, status: "idle" }; - } else { - return machine; - } - }); - }); - } - - return ( - <> - {staticMachines.map((machine, index) => ( - - ))} - - ) -} - -export default StaticMachine; \ No newline at end of file diff --git a/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx b/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx deleted file mode 100644 index 94a2faa..0000000 --- a/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useEffect } from 'react' -import { useAnimationPlaySpeed } from '../../../store/usePlayButtonStore'; - -interface StaticMachineState { - uuid: string; - status: string; - actions: { uuid: string; name: string; buffer: number; material: string; }; - machineTriggerId: string; - connectedArmBot: string; -} - -type StaticMachineInstancesProps = { - machine: StaticMachineState, - updateArmBotTriggerAndMachineStatus: (armBotUuid: string, triggerId: string, machineId: string) => void; -} - -function StaticMachineInstances({ machine, updateArmBotTriggerAndMachineStatus }: StaticMachineInstancesProps) { - const { speed } = useAnimationPlaySpeed(); - - useEffect(() => { - if (machine.status === 'running') { - setTimeout(() => { - updateArmBotTriggerAndMachineStatus(machine.connectedArmBot, machine.machineTriggerId, machine.uuid); - }, machine.actions.buffer * 1000 * speed); - } - }, [machine]) - - return ( - <> - ) -} - -export default StaticMachineInstances \ No newline at end of file diff --git a/app/src/modules/simulation/triggers/temp.md b/app/src/modules/simulation/triggers/temp.md new file mode 100644 index 0000000..e69de29 diff --git a/app/src/modules/simulation/ui/temp.md b/app/src/modules/simulation/ui/temp.md new file mode 100644 index 0000000..e69de29 diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx new file mode 100644 index 0000000..cf5fd81 --- /dev/null +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -0,0 +1,62 @@ +import { useEffect, useState } from 'react' +import { useFrame, useThree } from '@react-three/fiber'; + +interface VehicleAnimatorProps { + path: [number, number, number][]; + handleCallBack: () => void; + currentPhase: string; + agvUuid: number +} + + +function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) { + const [progress, setProgress] = useState(0) + const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); + const { scene } = useThree(); + + useEffect(() => { + + if (currentPhase === 'stationed-pickup' && path.length > 0) { + setCurrentPath(path); + } + + }, [currentPhase, path]) + + useFrame((_, delta) => { + if (!path || path.length < 2) return; + + const object = scene.getObjectByProperty("uuid", agvUuid) + if (!object) return; + + setProgress(prev => { + const next = prev + delta * 0.1; // speed + return next >= 1 ? 1 : next; + }); + + const totalSegments = path.length - 1; + const segmentIndex = Math.floor(progress * totalSegments); + const t = progress * totalSegments - segmentIndex; + + const start = path[segmentIndex]; + const end = path[segmentIndex + 1] || start; + + // Directly set position without creating a new Vector3 + object.position.x = start[0] + (end[0] - start[0]) * t; + object.position.y = start[1] + (end[1] - start[1]) * t; + object.position.z = start[2] + (end[2] - start[2]) * t; + }); + // useFrame(() => { + // if (currentPath.length === 0) return; + // const object = scene.getObjectByProperty("uuid", agvUuid); + // if (!object) return; + + + + // }) + return ( + <> + + ) +} + +export default VehicleAnimator \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx new file mode 100644 index 0000000..bf767ec --- /dev/null +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -0,0 +1,66 @@ +import React, { useCallback, useEffect, useState } from 'react' +import VehicleAnimator from '../animator/vehicleAnimator' +import * as THREE from "three"; +import { NavMeshQuery } from '@recast-navigation/core'; +import { useNavMesh } from '../../../../../store/store'; +import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; + +function VehicleInstance({ agvDetails }: any) { + const { navMesh } = useNavMesh(); + const { isPlaying } = usePlayButtonStore(); + const { setVehicleActive, setVehicleState } = useVehicleStore(); + const [currentPhase, setCurrentPhase] = useState<(string)>("stationed"); + const [path, setPath] = useState<[number, number, number][]>([]); + + const computePath = useCallback((start: any, end: any) => { + + + try { + const navMeshQuery = new NavMeshQuery(navMesh); + const { path: segmentPath } = navMeshQuery.computePath(start, end); + return ( + segmentPath?.map( + ({ x, y, z }) => [x, y + 0.1, z] as [number, number, number] + ) || [] + ); + } catch { + return []; + } + }, [navMesh]); + + useEffect(() => { + + + if (isPlaying) { + if (!agvDetails.isActive && agvDetails.state == "idle" && currentPhase == "stationed") { + const toPickupPath = computePath(new THREE.Vector3(agvDetails.position[0], agvDetails.position[1], agvDetails.position[2]), agvDetails.point.action.pickUpPoint); + setPath(toPickupPath) + setVehicleActive(agvDetails.modelUuid, true) + setVehicleState(agvDetails.modelUuid, "running") + setCurrentPhase("stationed-pickup") + // + } + } + }, [agvDetails, currentPhase, path, isPlaying]) + + function handleCallBack() { + if (currentPhase === "stationed-pickup") { + setVehicleActive(agvDetails.modelUuid, false) + setVehicleState(agvDetails.modelUuid, "idle") + setCurrentPhase("picking") + setPath([]) + } + } + + + return ( + <> + + + + + ) +} + +export default VehicleInstance \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx new file mode 100644 index 0000000..0848883 --- /dev/null +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import VehicleInstance from './instance/vehicleInstance' +import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; + +function VehicleInstances() { + const { vehicles } = useVehicleStore(); + + return ( + <> + + {vehicles.map((val: any, i: any) => + + )} + + + ) +} + +export default VehicleInstances \ No newline at end of file diff --git a/app/src/modules/builder/agv/navMeshCreator.tsx b/app/src/modules/simulation/vehicle/navMesh/navMesh.tsx similarity index 73% rename from app/src/modules/builder/agv/navMeshCreator.tsx rename to app/src/modules/simulation/vehicle/navMesh/navMesh.tsx index c0f8808..8652172 100644 --- a/app/src/modules/builder/agv/navMeshCreator.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/navMesh.tsx @@ -1,15 +1,15 @@ import { useRef } from "react"; -import { useNavMesh } from "../../../store/store"; +import { useNavMesh } from "../../../../store/store"; import PolygonGenerator from "./polygonGenerator"; import NavMeshDetails from "./navMeshDetails"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import * as Types from "../../../types/world/worldTypes"; +import * as CONSTANTS from "../../../../types/world/worldConstants"; +import * as Types from "../../../../types/world/worldTypes"; -type NavMeshCreatorProps = { +type NavMeshProps = { lines: Types.RefLines }; -function NavMeshCreator({ lines }: NavMeshCreatorProps) { +function NavMesh({ lines }: NavMeshProps) { let groupRef = useRef() as Types.RefGroup; const { setNavMesh } = useNavMesh(); @@ -28,4 +28,4 @@ function NavMeshCreator({ lines }: NavMeshCreatorProps) { ) } -export default NavMeshCreator \ No newline at end of file +export default NavMesh \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx b/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx new file mode 100644 index 0000000..aee91e0 --- /dev/null +++ b/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx @@ -0,0 +1,64 @@ +import React, { useEffect } from "react"; +import { init as initRecastNavigation } from "@recast-navigation/core"; +import { generateSoloNavMesh } from "@recast-navigation/generators"; +import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three"; +import { useThree } from "@react-three/fiber"; +import * as THREE from "three"; +import * as Types from "../../../../types/world/worldTypes"; + +interface NavMeshDetailsProps { + setNavMesh: (navMesh: any) => void; + groupRef: React.MutableRefObject; + lines: Types.RefLines; +} + +export default function NavMeshDetails({ + lines, + setNavMesh, + groupRef, +}: NavMeshDetailsProps) { + const { scene } = useThree(); + + useEffect(() => { + const initializeNavigation = async () => { + try { + await initRecastNavigation(); + + if (!groupRef.current || groupRef.current.children.length === 0) { + return; + } + + const meshes = groupRef?.current?.children as THREE.Mesh[]; + + const [positions, indices] = getPositionsAndIndices(meshes); + + const cellSize = 0.2; + const cellHeight = 0.7; + const walkableRadius = 0.5; + const { success, navMesh } = generateSoloNavMesh(positions, indices, { + cs: cellSize, + ch: cellHeight, + walkableRadius: Math.round(walkableRadius / cellHeight), + }); + + if (!success || !navMesh) { + return; + } + + setNavMesh(navMesh); + + scene.children + .filter((child) => child instanceof DebugDrawer) + .forEach((child) => scene.remove(child)); + + const debugDrawer = new DebugDrawer(); + debugDrawer.drawNavMesh(navMesh); + // scene.add(debugDrawer); + } catch (error) { } + }; + + initializeNavigation(); + }, [scene, groupRef, lines.current]); + + return null; +} diff --git a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx new file mode 100644 index 0000000..fa0d9dd --- /dev/null +++ b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx @@ -0,0 +1,119 @@ +import * as THREE from "three"; +import { useEffect } from "react"; +import * as turf from "@turf/turf"; +import * as Types from "../../../../types/world/worldTypes"; +import arrayLinesToObject from "../../../builder/geomentries/lines/lineConvertions/arrayLinesToObject"; + +interface PolygonGeneratorProps { + groupRef: React.MutableRefObject; + lines: Types.RefLines; +} + +export default function PolygonGenerator({ + groupRef, + lines, +}: PolygonGeneratorProps) { + + useEffect(() => { + let allLines = arrayLinesToObject(lines.current); + const wallLines = allLines?.filter((line) => line?.type === "WallLine"); + const aisleLines = allLines?.filter((line) => line?.type === "AisleLine"); + + const wallPoints = wallLines + .map((pair) => pair?.line.map((vals) => vals.position)) + .filter((wall): wall is THREE.Vector3[] => !!wall); + + const result = aisleLines.map((pair) => + pair?.line.map((point) => ({ + position: [point.position.x, point.position.z], + uuid: point.uuid, + })) + ); + + if (!result || result.some((line) => !line)) { + return; + } + + const lineFeatures = result?.map((line: any) => + turf.lineString(line.map((p: any) => p?.position)) + ); + + const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); + renderWallGeometry(wallPoints); + + if (polygons.features.length > 1) { + polygons.features.forEach((feature) => { + if (feature.geometry.type === "Polygon") { + + const shape = new THREE.Shape(); + const coords = feature.geometry.coordinates[0]; + + shape.moveTo(coords[0][0], coords[0][1]); + + for (let i = 1; i < coords.length; i++) { + shape.lineTo(coords[i][0], coords[i][1]); + } + shape.lineTo(coords[0][0], coords[0][1]); + + const extrudeSettings = { + depth: 5, + bevelEnabled: false, + }; + + const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); + + const material = new THREE.MeshBasicMaterial({ color: "blue", transparent: true, opacity: 0.5 }); + const mesh = new THREE.Mesh(geometry, material); + mesh.rotateX(Math.PI / 2); + mesh.name = "agv-collider"; + mesh.position.y = 5; + + mesh.receiveShadow = true; + groupRef.current?.add(mesh); + } + }); + } + + }, [lines.current]); + + const renderWallGeometry = (walls: THREE.Vector3[][]) => { + walls.forEach((wall) => { + if (wall.length < 2) return; + + for (let i = 0; i < wall.length - 1; i++) { + const start = new THREE.Vector3(wall[i].x, wall[i].y, wall[i].z); + const end = new THREE.Vector3( + wall[i + 1].x, + wall[i + 1].y, + wall[i + 1].z + ); + + const wallHeight = 10; + const direction = new THREE.Vector3().subVectors(end, start); + const length = direction.length(); + direction.normalize(); + + const wallGeometry = new THREE.BoxGeometry(length, wallHeight); + const wallMaterial = new THREE.MeshBasicMaterial({ + color: "#aaa", + transparent: true, + opacity: 0.5, + }); + + const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial); + const midPoint = new THREE.Vector3() + .addVectors(start, end) + .multiplyScalar(0.5); + wallMesh.position.set(midPoint.x, wallHeight / 2, midPoint.z); + + const quaternion = new THREE.Quaternion(); + quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction); + wallMesh.quaternion.copy(quaternion); + + groupRef.current?.add(wallMesh); + } + }); + }; + + return null; +} diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx new file mode 100644 index 0000000..3364717 --- /dev/null +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -0,0 +1,121 @@ +import React, { useEffect } from 'react' +import VehicleInstances from './instances/vehicleInstances'; + +import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; + +function Vehicles() { + + const { vehicles, addVehicle } = useVehicleStore(); + + const vehicleStatusSample: VehicleEventSchema[] = [ + { + modelUuid: "veh-123", + modelName: "Autonomous Truck A1", + position: [10, 0, 5], + rotation: [0, 0, 0], + state: "idle", + type: "vehicle", + speed: 2.5, + point: { + uuid: "point-789", + position: [0, 1, 0], + rotation: [0, 0, 0], + action: { + actionUuid: "action-456", + actionName: "Deliver to Zone A", + actionType: "travel", + material: "crate", + unLoadDuration: 15, + loadCapacity: 5, + pickUpPoint: { x: 5, y: 0, z: 3 }, + unLoadPoint: { x: 20, y: 0, z: 10 }, + triggers: [ + { + triggerUuid: "trig-001", + triggerName: "Start Travel", + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, + triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, + triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } + } + }, + { + triggerUuid: "trig-002", + triggerName: "Complete Travel", + triggerType: "onComplete", + delay: 2, + triggeredAsset: null + } + ] + } + } + }, + { + modelUuid: "veh-123", + modelName: "Autonomous Truck A1", + position: [10, 0, 5], + rotation: [0, 0, 0], + state: "idle", + type: "vehicle", + speed: 2.5, + point: { + uuid: "point-789", + position: [0, 1, 0], + rotation: [0, 0, 0], + action: { + actionUuid: "action-456", + actionName: "Deliver to Zone A", + actionType: "travel", + material: "crate", + unLoadDuration: 15, + loadCapacity: 5, + pickUpPoint: { x: 5, y: 0, z: 3 }, + unLoadPoint: { x: 20, y: 0, z: 10 }, + triggers: [ + { + triggerUuid: "trig-001", + triggerName: "Start Travel", + triggerType: "onStart", + delay: 0, + triggeredAsset: { + triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, + triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, + triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } + } + }, + { + triggerUuid: "trig-002", + triggerName: "Complete Travel", + triggerType: "onComplete", + delay: 2, + triggeredAsset: null + } + ] + } + } + } + ]; + + + useEffect(() => { + addVehicle('123', vehicleStatusSample[0]); + addVehicle('123', vehicleStatusSample[1]); + }, []) + + useEffect(() => { + // console.log('vehicles: ', vehicles); + }, [vehicles]) + + + return ( + <> + + + + + ) +} + +export default Vehicles; \ No newline at end of file diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index e039592..cd2c57c 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -2,25 +2,25 @@ import React, { useEffect, useState, useRef } from "react"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import Panel from "./widgets/panel/Panel"; import AddButtons from "./widgets/panel/AddButtons"; -import { useSelectedZoneStore } from "../../store/useZoneStore"; -import DisplayZone from "./DisplayZone"; +import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; +import DisplayZone from "./zone/DisplayZone"; import Scene from "../scene/scene"; import useModuleStore from "../../store/useModuleStore"; import { useDroppedObjectsStore, useFloatingWidget, -} from "../../store/useDroppedObjectsStore"; +} from "../../store/visualization/useDroppedObjectsStore"; import { useAsset3dWidget, useSocketStore, useWidgetSubOption, useZones, } from "../../store/store"; -import { getZone2dData } from "../../services/realTimeVisulization/zoneData/getZoneData"; +import { getZone2dData } from "../../services/visulization/zone/getZoneData"; import { generateUniqueId } from "../../functions/generateUniqueId"; import { determinePosition } from "./functions/determinePosition"; -import { addingFloatingWidgets } from "../../services/realTimeVisulization/zoneData/addFloatingWidgets"; +import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets"; import SocketRealTimeViz from "./socket/realTimeVizSocket.dev"; import RenderOverlay from "../../components/templates/Overlay"; import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup"; @@ -30,8 +30,7 @@ import { useEditWidgetOptionsStore, useRightClickSelected, useRightSelected, -} from "../../store/useZone3DWidgetStore"; -import Dropped3dWidgets from "./widgets/3d/Dropped3dWidget"; +} from "../../store/visualization/useZone3DWidgetStore"; import OuterClick from "../../utils/outerClick"; import { useWidgetStore } from "../../store/useWidgetStore"; import { getActiveProperties } from "./functions/getActiveProperties"; diff --git a/app/src/modules/scene/mqttTemp/drieHtmlTemp.tsx b/app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx similarity index 89% rename from app/src/modules/scene/mqttTemp/drieHtmlTemp.tsx rename to app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx index 68c8c37..3e0adba 100644 --- a/app/src/modules/scene/mqttTemp/drieHtmlTemp.tsx +++ b/app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx @@ -1,109 +1,109 @@ -import { Html } from "@react-three/drei"; -import * as THREE from "three"; -import * as Types from "../../../types/world/worldTypes"; -import { useDrieTemp, useDrieUIValue } from "../../../store/store" -import UI from "./ui"; -import { useEffect } from "react"; -import { useThree } from "@react-three/fiber"; - -export default function DrieHtmlTemp({ itemsGroup }: { itemsGroup: Types.RefGroup }) { - const { drieTemp, setDrieTemp } = useDrieTemp(); - const { drieUIValue, setDrieUIValue } = useDrieUIValue(); - const state = useThree(); - const { camera, raycaster } = state; - - useEffect(() => { - const canvasElement = state.gl.domElement; - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = true; - drag = false; - } - }; - - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onMouseUp = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - if (!itemsGroup.current) return - let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); - if (intersects.length > 0) { - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - if (currentObject && (currentObject.userData.name === "SV2 Controll pannel" || currentObject.userData.name === "forklift")) { - const worldPos = new THREE.Vector3(); - currentObject.getWorldPosition(worldPos); - - const rightOffset = new THREE.Vector3(1, 0, 0); - const upOffset = new THREE.Vector3(0, 1, 0); - - currentObject.localToWorld(rightOffset); - currentObject.localToWorld(upOffset); - - const finalPosition = worldPos.clone().addScaledVector(rightOffset.sub(currentObject.position).normalize(), 2.5).addScaledVector(upOffset.sub(currentObject.position).normalize(), 2.3); - - setDrieTemp(finalPosition); - } else { - setDrieTemp(undefined); - } - } - else { - setDrieTemp(undefined); - } - } - }; - - - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - }; - }, []) - - return ( - <> - {drieTemp && - - - - - - } - - ) +import { Html } from "@react-three/drei"; +import * as THREE from "three"; +import * as Types from "../../../types/world/worldTypes"; +import { useDrieTemp, useDrieUIValue } from "../../../store/store" +import UI from "./ui"; +import { useEffect } from "react"; +import { useThree } from "@react-three/fiber"; + +export default function DrieHtmlTemp() { + const { drieTemp, setDrieTemp } = useDrieTemp(); + const { drieUIValue, setDrieUIValue } = useDrieUIValue(); + const state = useThree(); + const { camera, raycaster, scene } = state; + + useEffect(() => { + const canvasElement = state.gl.domElement; + let drag = false; + let isLeftMouseDown = false; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = true; + drag = false; + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag = true; + } + }; + + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = false; + if (drag) return; + if (!scene) return + let intersects = raycaster.intersectObjects(scene.children, true); + if (intersects.length > 0) { + let currentObject = intersects[0].object; + + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + if (currentObject && (currentObject.userData.name === "SV2 Controll pannel" || currentObject.userData.name === "forklift")) { + const worldPos = new THREE.Vector3(); + currentObject.getWorldPosition(worldPos); + + const rightOffset = new THREE.Vector3(1, 0, 0); + const upOffset = new THREE.Vector3(0, 1, 0); + + currentObject.localToWorld(rightOffset); + currentObject.localToWorld(upOffset); + + const finalPosition = worldPos.clone().addScaledVector(rightOffset.sub(currentObject.position).normalize(), 2.5).addScaledVector(upOffset.sub(currentObject.position).normalize(), 2.3); + + setDrieTemp(finalPosition); + } else { + setDrieTemp(undefined); + } + } + else { + setDrieTemp(undefined); + } + } + }; + + + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + }; + }, []) + + return ( + <> + {drieTemp && + + + + + + } + + ) } \ No newline at end of file diff --git a/app/src/modules/scene/mqttTemp/ui.jsx b/app/src/modules/visualization/mqttTemp/ui.jsx similarity index 97% rename from app/src/modules/scene/mqttTemp/ui.jsx rename to app/src/modules/visualization/mqttTemp/ui.jsx index 4d4c5fb..cafff38 100644 --- a/app/src/modules/scene/mqttTemp/ui.jsx +++ b/app/src/modules/visualization/mqttTemp/ui.jsx @@ -1,141 +1,141 @@ -export default function UI({ temperature, humidity, touch, header }) { - return ( -
-
- {header ? header : "Sensor Details"} -
-
-
-
- - - - - -
-
- Temperature -
-
- {temperature} -
-
-
-
- - - - - -
-
- Humidity -
-
- {humidity} -
-
-
-
-
-
- Touch Sensor -
-
- {touch === "True" ? "Active" : "In active"} -
-
-
-
- ); -} +export default function UI({ temperature, humidity, touch, header }) { + return ( +
+
+ {header ? header : "Sensor Details"} +
+
+
+
+ + + + + +
+
+ Temperature +
+
+ {temperature} +
+
+
+
+ + + + + +
+
+ Humidity +
+
+ {humidity} +
+
+
+
+
+
+ Touch Sensor +
+
+ {touch === "True" ? "Active" : "In active"} +
+
+
+
+ ); +} diff --git a/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx b/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx index b5291a3..1dbc175 100644 --- a/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx +++ b/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx @@ -1,8 +1,8 @@ import { useEffect } from "react"; import { useSocketStore } from "../../../store/store"; -import { useSelectedZoneStore } from "../../../store/useZoneStore"; -import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; -import { useZoneWidgetStore } from "../../../store/useZone3DWidgetStore"; +import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; +import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore"; +import { useZoneWidgetStore } from "../../../store/visualization/useZone3DWidgetStore"; import useTemplateStore from "../../../store/useTemplateStore"; type WidgetData = { diff --git a/app/src/modules/visualization/template/Templates.tsx b/app/src/modules/visualization/template/Templates.tsx index 8370112..e0ece1c 100644 --- a/app/src/modules/visualization/template/Templates.tsx +++ b/app/src/modules/visualization/template/Templates.tsx @@ -1,9 +1,9 @@ import { useEffect } from "react"; import useTemplateStore from "../../../store/useTemplateStore"; -import { useSelectedZoneStore } from "../../../store/useZoneStore"; +import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; import { useSocketStore } from "../../../store/store"; -import { getTemplateData } from "../../../services/realTimeVisulization/zoneData/getTemplate"; -import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; +import { getTemplateData } from "../../../services/visulization/zone/getTemplate"; +import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore"; import RenameInput from "../../../components/ui/inputs/RenameInput"; diff --git a/app/src/modules/visualization/visualization.tsx b/app/src/modules/visualization/visualization.tsx new file mode 100644 index 0000000..e5b1692 --- /dev/null +++ b/app/src/modules/visualization/visualization.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import Dropped3dWidgets from './widgets/3d/Dropped3dWidget' +import ZoneCentreTarget from './zone/zoneCameraTarget' +import ZoneAssets from './zone/zoneAssets' +import MqttEvents from '../../services/factoryBuilder/mqtt/mqttEvents' +import DrieHtmlTemp from './mqttTemp/drieHtmlTemp' + +const Visualization = () => { + return ( + <> + + + + + + + + + + {/* */} + + + ) +} + +export default Visualization; \ No newline at end of file diff --git a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx index f865719..0c8fe6f 100644 --- a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx +++ b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx @@ -13,13 +13,11 @@ import { KebabIcon, } from "../../../../components/icons/ExportCommonIcons"; import { useEffect, useRef, useState } from "react"; -import { duplicateWidgetApi } from "../../../../services/realTimeVisulization/zoneData/duplicateWidget"; -import { deleteWidgetApi } from "../../../../services/realTimeVisulization/zoneData/deleteWidgetApi"; import { useClickOutside } from "../../functions/handleWidgetsOuterClick"; import { useSocketStore } from "../../../../store/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import OuterClick from "../../../../utils/outerClick"; -import useChartStore from "../../../../store/useChartStore"; +import useChartStore from "../../../../store/visualization/useChartStore"; type Side = "top" | "bottom" | "left" | "right"; diff --git a/app/src/modules/visualization/widgets/2d/charts/BarGraphComponent.tsx b/app/src/modules/visualization/widgets/2d/charts/BarGraphComponent.tsx index 4f3cfd9..e68e1d3 100644 --- a/app/src/modules/visualization/widgets/2d/charts/BarGraphComponent.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/BarGraphComponent.tsx @@ -192,7 +192,7 @@ import io from "socket.io-client"; import axios from "axios"; import { useThemeStore } from "../../../../../store/useThemeStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; interface ChartComponentProps { id: string; diff --git a/app/src/modules/visualization/widgets/2d/charts/DoughnutGraphComponent.tsx b/app/src/modules/visualization/widgets/2d/charts/DoughnutGraphComponent.tsx index 3108d09..74d3fec 100644 --- a/app/src/modules/visualization/widgets/2d/charts/DoughnutGraphComponent.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/DoughnutGraphComponent.tsx @@ -5,7 +5,7 @@ import io from "socket.io-client"; import axios from "axios"; import { useThemeStore } from "../../../../../store/useThemeStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; interface ChartComponentProps { id: string; diff --git a/app/src/modules/visualization/widgets/2d/charts/LineGraphComponent.tsx b/app/src/modules/visualization/widgets/2d/charts/LineGraphComponent.tsx index 4a6906a..9e5d616 100644 --- a/app/src/modules/visualization/widgets/2d/charts/LineGraphComponent.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/LineGraphComponent.tsx @@ -4,7 +4,7 @@ import io from "socket.io-client"; import axios from "axios"; import { useThemeStore } from "../../../../../store/useThemeStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; interface ChartComponentProps { diff --git a/app/src/modules/visualization/widgets/2d/charts/PieGraphComponent.tsx b/app/src/modules/visualization/widgets/2d/charts/PieGraphComponent.tsx index b77e964..931d567 100644 --- a/app/src/modules/visualization/widgets/2d/charts/PieGraphComponent.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/PieGraphComponent.tsx @@ -189,7 +189,7 @@ import io from "socket.io-client"; import axios from "axios"; import { useThemeStore } from "../../../../../store/useThemeStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; interface ChartComponentProps { diff --git a/app/src/modules/visualization/widgets/2d/charts/PolarAreaGraphComponent.tsx b/app/src/modules/visualization/widgets/2d/charts/PolarAreaGraphComponent.tsx index e4056dc..d5024d6 100644 --- a/app/src/modules/visualization/widgets/2d/charts/PolarAreaGraphComponent.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/PolarAreaGraphComponent.tsx @@ -4,7 +4,7 @@ import io from "socket.io-client"; import axios from "axios"; import { useThemeStore } from "../../../../../store/useThemeStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; interface ChartComponentProps { diff --git a/app/src/modules/visualization/widgets/2d/charts/ProgressCard1.tsx b/app/src/modules/visualization/widgets/2d/charts/ProgressCard1.tsx index 70f09a2..5d1f59e 100644 --- a/app/src/modules/visualization/widgets/2d/charts/ProgressCard1.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/ProgressCard1.tsx @@ -3,7 +3,7 @@ import { Line } from "react-chartjs-2"; import io from "socket.io-client"; import axios from "axios"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import { StockIncreseIcon } from "../../../../../components/icons/RealTimeVisulationIcons"; diff --git a/app/src/modules/visualization/widgets/2d/charts/ProgressCard2.tsx b/app/src/modules/visualization/widgets/2d/charts/ProgressCard2.tsx index 67ae415..325445c 100644 --- a/app/src/modules/visualization/widgets/2d/charts/ProgressCard2.tsx +++ b/app/src/modules/visualization/widgets/2d/charts/ProgressCard2.tsx @@ -3,7 +3,7 @@ import { Line } from "react-chartjs-2"; import io from "socket.io-client"; import axios from "axios"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import { StockIncreseIcon } from "../../../../../components/icons/RealTimeVisulationIcons"; diff --git a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx index 6acc994..4ca3c31 100644 --- a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx +++ b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx @@ -4,21 +4,17 @@ import React, { useEffect, useRef, useState } from "react"; import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../../store/store"; import useModuleStore from "../../../../store/useModuleStore"; import { ThreeState } from "../../../../types/world/worldTypes"; -import { useSelectedZoneStore } from "../../../../store/useZoneStore"; -import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../../store/useZone3DWidgetStore"; -import { use3DWidget } from "../../../../store/useDroppedObjectsStore"; -import { get3dWidgetZoneData } from "../../../../services/realTimeVisulization/zoneData/get3dWidgetData"; +import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore"; +import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../../store/visualization/useZone3DWidgetStore"; +import { use3DWidget } from "../../../../store/visualization/useDroppedObjectsStore"; +import { get3dWidgetZoneData } from "../../../../services/visulization/zone/get3dWidgetData"; import { generateUniqueId } from "../../../../functions/generateUniqueId"; import ProductionCapacity from "./cards/ProductionCapacity"; import ReturnOfInvestment from "./cards/ReturnOfInvestment"; import StateWorking from "./cards/StateWorking"; import Throughput from "./cards/Throughput"; import { useWidgetStore } from "../../../../store/useWidgetStore"; -import useChartStore from "../../../../store/useChartStore"; - - - - +import useChartStore from "../../../../store/visualization/useChartStore"; type WidgetData = { id: string; @@ -48,7 +44,7 @@ export default function Dropped3dWidgets() { const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); const { setSelectedChartId } = useWidgetStore(); - const { measurements, duration} = useChartStore(); + const { measurements, duration } = useChartStore(); let [floorPlanesVertical, setFloorPlanesVertical] = useState( new THREE.Plane(new THREE.Vector3(0, 1, 0)) ); @@ -121,7 +117,6 @@ export default function Dropped3dWidgets() { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && !(intersect.object.type === "GridHelper") ); @@ -158,7 +153,6 @@ export default function Dropped3dWidgets() { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && !(intersect.object.type === "GridHelper") ); // Update widget's position in memory @@ -173,28 +167,28 @@ export default function Dropped3dWidgets() { const onDrop = (event: any) => { event.preventDefault(); event.stopPropagation(); - + hasEntered.current = false; - + const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - + const newWidget = createdWidgetRef.current; if (!newWidget || !widgetSelect.startsWith("ui")) return; - + // ✅ Extract 2D drop position let [x, y, z] = newWidget.position; - + // ✅ Clamp Y to at least 0 y = Math.max(y, 0); newWidget.position = [x, y, z]; - + // ✅ Prepare polygon from selectedZone.points const points3D = selectedZone.points as Array<[number, number, number]>; const zonePolygonXZ = points3D.map(([x, , z]) => [x, z] as [number, number]); - + const isInside = isPointInPolygon([x, z], zonePolygonXZ); - + // ✅ Remove temp widget const prevWidgets = useZoneWidgetStore.getState().zoneWidgetData[selectedZone.zoneId] || []; const cleanedWidgets = prevWidgets.filter(w => w.id !== newWidget.id); @@ -204,29 +198,29 @@ export default function Dropped3dWidgets() { [selectedZone.zoneId]: cleanedWidgets, }, })); - + // (Optional) Prevent adding if dropped outside zone // if (!isInside) { // createdWidgetRef.current = null; // return; // } - + // ✅ Add widget addWidget(selectedZone.zoneId, newWidget); - + const add3dWidget = { organization, widget: newWidget, zoneId: selectedZone.zoneId, }; - + if (visualizationSocket) { visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } - + createdWidgetRef.current = null; }; - + canvasElement.addEventListener("dragenter", handleDragEnter); @@ -262,7 +256,7 @@ export default function Dropped3dWidgets() { widgetToDuplicate.position[2] + 0.5, ], rotation: widgetToDuplicate.rotation || [0, 0, 0], - Data:{ + Data: { measurements: measurements, duration: duration }, @@ -369,7 +363,7 @@ export default function Dropped3dWidgets() { // floorPlanesVertical, // planeIntersect.current // ); - + // setintersectcontextmenu(intersect1.y); if (rightSelect === "RotateX" || rightSelect === "RotateY") { @@ -389,7 +383,7 @@ export default function Dropped3dWidgets() { rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; } } - + }; const handleMouseMove = (event: MouseEvent) => { @@ -433,7 +427,7 @@ export default function Dropped3dWidgets() { intersect.z + horizontalZ, ]; - + updateWidgetPosition(selectedZoneId, rightClickSelected, newPosition); } } @@ -441,24 +435,24 @@ export default function Dropped3dWidgets() { if (rightSelect === "Vertical Move") { const intersect = raycaster.ray.intersectPlane(floorPlanesVertical, planeIntersect.current); - + if (intersect && typeof intersectcontextmenu === "number") { const diff = intersect.y - intersectcontextmenu; const unclampedY = selectedWidget.position[1] + diff; const newY = Math.max(0, unclampedY); // Prevent going below floor (y=0) - + setintersectcontextmenu(intersect.y); - + const newPosition: [number, number, number] = [ selectedWidget.position[0], newY, selectedWidget.position[2], ]; - + updateWidgetPosition(selectedZoneId, rightClickSelected, newPosition); } } - + if (rightSelect?.startsWith("Rotate")) { const axis = rightSelect.slice(-1).toLowerCase(); // "x", "y", or "z" const currentX = event.pageX; diff --git a/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx b/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx index 274d6dd..6666452 100644 --- a/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx +++ b/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx @@ -15,7 +15,7 @@ import { import axios from "axios"; import io from "socket.io-client"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; // Register ChartJS components ChartJS.register( diff --git a/app/src/modules/visualization/widgets/3d/cards/ReturnOfInvestment.tsx b/app/src/modules/visualization/widgets/3d/cards/ReturnOfInvestment.tsx index 5a975f1..6aeafa6 100644 --- a/app/src/modules/visualization/widgets/3d/cards/ReturnOfInvestment.tsx +++ b/app/src/modules/visualization/widgets/3d/cards/ReturnOfInvestment.tsx @@ -16,7 +16,7 @@ import { import axios from "axios"; import io from "socket.io-client"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { WavyIcon } from "../../../../../components/icons/3dChartIcons"; // Register Chart.js components diff --git a/app/src/modules/visualization/widgets/3d/cards/StateWorking.tsx b/app/src/modules/visualization/widgets/3d/cards/StateWorking.tsx index 4d10955..048dd7d 100644 --- a/app/src/modules/visualization/widgets/3d/cards/StateWorking.tsx +++ b/app/src/modules/visualization/widgets/3d/cards/StateWorking.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useMemo, useState } from "react"; import axios from "axios"; import io from "socket.io-client"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; // import image from "../../../../assets/image/temp/image.png"; interface StateWorkingProps { diff --git a/app/src/modules/visualization/widgets/3d/cards/Throughput.tsx b/app/src/modules/visualization/widgets/3d/cards/Throughput.tsx index 67397bd..857813c 100644 --- a/app/src/modules/visualization/widgets/3d/cards/Throughput.tsx +++ b/app/src/modules/visualization/widgets/3d/cards/Throughput.tsx @@ -18,7 +18,7 @@ import { import axios from "axios"; import io from "socket.io-client"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { ThroughputIcon } from "../../../../../components/icons/3dChartIcons"; // Register Chart.js components diff --git a/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx b/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx index cfe29aa..b4a328e 100644 --- a/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx +++ b/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx @@ -3,18 +3,18 @@ import { useEffect, useRef, useState } from "react"; import { useDroppedObjectsStore, Zones, -} from "../../../../store/useDroppedObjectsStore"; +} from "../../../../store/visualization/useDroppedObjectsStore"; import useModuleStore from "../../../../store/useModuleStore"; import { determinePosition } from "../../functions/determinePosition"; import { getActiveProperties } from "../../functions/getActiveProperties"; -import { addingFloatingWidgets } from "../../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; +import { addingFloatingWidgets } from "../../../../services/visulization/zone/addFloatingWidgets"; import { DublicateIcon, KebabIcon, DeleteIcon, } from "../../../../components/icons/ExportCommonIcons"; import DistanceLines from "./DistanceLines"; // Import the DistanceLines component -import { deleteFloatingWidgetApi } from "../../../../services/realTimeVisulization/zoneData/deleteFloatingWidget"; +import { deleteFloatingWidgetApi } from "../../../../services/visulization/zone/deleteFloatingWidget"; import TotalCardComponent from "./cards/TotalCardComponent"; import WarehouseThroughputComponent from "./cards/WarehouseThroughputComponent"; @@ -23,7 +23,7 @@ import { useWidgetStore } from "../../../../store/useWidgetStore"; import { useSocketStore } from "../../../../store/store"; import { useClickOutside } from "../../functions/handleWidgetsOuterClick"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; -import { useSelectedZoneStore } from "../../../../store/useZoneStore"; +import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore"; interface DraggingState { zone: string; index: number; diff --git a/app/src/modules/visualization/widgets/floating/cards/FleetEfficiencyComponent.tsx b/app/src/modules/visualization/widgets/floating/cards/FleetEfficiencyComponent.tsx index f50bec9..bdaf648 100644 --- a/app/src/modules/visualization/widgets/floating/cards/FleetEfficiencyComponent.tsx +++ b/app/src/modules/visualization/widgets/floating/cards/FleetEfficiencyComponent.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { Line } from "react-chartjs-2"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import io from "socket.io-client"; diff --git a/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx b/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx index 6e8d692..61096c5 100644 --- a/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx +++ b/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { Line } from "react-chartjs-2"; -import useChartStore from "../../../../../store/useChartStore"; +import useChartStore from "../../../../../store/visualization/useChartStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import io from "socket.io-client"; diff --git a/app/src/modules/visualization/widgets/floating/cards/WarehouseThroughputComponent.tsx b/app/src/modules/visualization/widgets/floating/cards/WarehouseThroughputComponent.tsx index 012c2e7..67793f9 100644 --- a/app/src/modules/visualization/widgets/floating/cards/WarehouseThroughputComponent.tsx +++ b/app/src/modules/visualization/widgets/floating/cards/WarehouseThroughputComponent.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react' import { Line } from 'react-chartjs-2' -import useChartStore from '../../../../../store/useChartStore'; +import useChartStore from '../../../../../store/visualization/useChartStore'; import { useWidgetStore } from '../../../../../store/useWidgetStore'; import axios from 'axios'; import io from "socket.io-client"; diff --git a/app/src/modules/visualization/widgets/panel/AddButtons.tsx b/app/src/modules/visualization/widgets/panel/AddButtons.tsx index 5d45d3f..d066cd4 100644 --- a/app/src/modules/visualization/widgets/panel/AddButtons.tsx +++ b/app/src/modules/visualization/widgets/panel/AddButtons.tsx @@ -6,8 +6,8 @@ import { } from "../../../../components/icons/RealTimeVisulationIcons"; import { AddIcon } from "../../../../components/icons/ExportCommonIcons"; import { useSocketStore } from "../../../../store/store"; -import { clearPanel } from "../../../../services/realTimeVisulization/zoneData/clearPanel"; -import { lockPanel } from "../../../../services/realTimeVisulization/zoneData/lockPanel"; +import { clearPanel } from "../../../../services/visulization/zone/clearPanel"; +import { lockPanel } from "../../../../services/visulization/zone/lockPanel"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; diff --git a/app/src/modules/visualization/DisplayZone.tsx b/app/src/modules/visualization/zone/DisplayZone.tsx similarity index 92% rename from app/src/modules/visualization/DisplayZone.tsx rename to app/src/modules/visualization/zone/DisplayZone.tsx index e2f3b2f..578bdab 100644 --- a/app/src/modules/visualization/DisplayZone.tsx +++ b/app/src/modules/visualization/zone/DisplayZone.tsx @@ -1,18 +1,18 @@ import React, { useEffect, useRef, useState, useCallback } from "react"; -import { useWidgetStore, Widget } from "../../store/useWidgetStore"; +import { useWidgetStore, Widget } from "../../../store/useWidgetStore"; import { useDroppedObjectsStore, useFloatingWidget, -} from "../../store/useDroppedObjectsStore"; -import { getSelect2dZoneData } from "../../services/realTimeVisulization/zoneData/getSelect2dZoneData"; -import { getFloatingZoneData } from "../../services/realTimeVisulization/zoneData/getFloatingData"; -import { get3dWidgetZoneData } from "../../services/realTimeVisulization/zoneData/get3dWidgetData"; +} from "../../../store/visualization/useDroppedObjectsStore"; +import { getSelect2dZoneData } from "../../../services/visulization/zone/getSelect2dZoneData"; +import { getFloatingZoneData } from "../../../services/visulization/zone/getFloatingData"; +import { get3dWidgetZoneData } from "../../../services/visulization/zone/get3dWidgetData"; import { MoveArrowLeft, MoveArrowRight, -} from "../../components/icons/SimulationIcons"; -import { InfoIcon } from "../../components/icons/ExportCommonIcons"; +} from "../../../components/icons/SimulationIcons"; +import { InfoIcon } from "../../../components/icons/ExportCommonIcons"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; diff --git a/app/src/modules/visualization/zoneAssets.tsx b/app/src/modules/visualization/zone/zoneAssets.tsx similarity index 89% rename from app/src/modules/visualization/zoneAssets.tsx rename to app/src/modules/visualization/zone/zoneAssets.tsx index 7a48196..af8b849 100644 --- a/app/src/modules/visualization/zoneAssets.tsx +++ b/app/src/modules/visualization/zone/zoneAssets.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useRef } from 'react' -import { useSelectedFloorItem, useZoneAssetId } from '../../store/store'; +import { useSelectedFloorItem, useZoneAssetId } from '../../../store/store'; import * as THREE from "three"; import { useThree } from '@react-three/fiber'; -import * as Types from "../../types/world/worldTypes"; +import * as Types from "../../../types/world/worldTypes"; export default function ZoneAssets() { const { zoneAssetId, setZoneAssetId } = useZoneAssetId(); const { setSelectedFloorItem } = useSelectedFloorItem(); @@ -10,7 +10,6 @@ export default function ZoneAssets() { useEffect(() => { // console.log('zoneAssetId: ', zoneAssetId); if (!zoneAssetId) return - console.log('zoneAssetId: ', zoneAssetId); let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id); if (AssetMesh) { const bbox = new THREE.Box3().setFromObject(AssetMesh); @@ -30,20 +29,17 @@ export default function ZoneAssets() { setSelectedFloorItem(AssetMesh); } else { - console.log('zoneAssetId: ', zoneAssetId) if (Array.isArray(zoneAssetId.position) && zoneAssetId.position.length >= 3) { let selectedAssetPosition = [ zoneAssetId.position[0], 10, zoneAssetId.position[2] ]; - console.log('selectedAssetPosition: ', selectedAssetPosition); let selectedAssetTarget = [ zoneAssetId.position[0], zoneAssetId.position[1], zoneAssetId.position[2] ]; - console.log('selectedAssetTarget: ', selectedAssetTarget); const setCam = async () => { await controls?.setLookAt(...selectedAssetPosition, ...selectedAssetTarget, true); setTimeout(() => { diff --git a/app/src/modules/visualization/functions/zoneCameraTarget.tsx b/app/src/modules/visualization/zone/zoneCameraTarget.tsx similarity index 97% rename from app/src/modules/visualization/functions/zoneCameraTarget.tsx rename to app/src/modules/visualization/zone/zoneCameraTarget.tsx index f022b32..d1587de 100644 --- a/app/src/modules/visualization/functions/zoneCameraTarget.tsx +++ b/app/src/modules/visualization/zone/zoneCameraTarget.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import * as THREE from "three"; -import { useSelectedZoneStore } from "../../../store/useZoneStore"; +import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; import { useEditPosition, usezonePosition, usezoneTarget } from "../../../store/store"; export default function ZoneCentreTarget() { diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts b/app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts deleted file mode 100644 index b419963..0000000 --- a/app/src/services/factoryBuilder/assest/floorAsset/setEventsApt.ts +++ /dev/null @@ -1,32 +0,0 @@ -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 Event Data"); - } - - 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 3e156cb..d587f06 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts +++ b/app/src/services/factoryBuilder/assest/floorAsset/setFloorItemApi.ts @@ -8,13 +8,9 @@ export const setFloorItemApi = async ( rotation?: Object, isLocked?: boolean, isVisible?: boolean, - eventData?: any ) => { try { const body: any = { organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible }; - if (eventData) { - body.eventData = eventData; - } const response = await fetch(`${url_Backend_dwinzo}/api/v2/setasset`, { method: "POST", diff --git a/app/src/services/simulation/getAssetEventType.ts b/app/src/services/simulation/getAssetEventType.ts deleted file mode 100644 index 84433ec..0000000 --- a/app/src/services/simulation/getAssetEventType.ts +++ /dev/null @@ -1,25 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const getAssetEventType = async (modelId: string, organization: string) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v2/pointData/${modelId}/${organization}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - if (!response.ok) { - throw new Error("Failed to fetch model event type"); - } - - 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/simulation/temp.md b/app/src/services/simulation/temp.md new file mode 100644 index 0000000..e69de29 diff --git a/app/src/services/visulization/temp.md b/app/src/services/visulization/temp.md new file mode 100644 index 0000000..e69de29 diff --git a/app/src/services/realTimeVisulization/zoneData/add3dWidget.ts b/app/src/services/visulization/zone/add3dWidget.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/add3dWidget.ts rename to app/src/services/visulization/zone/add3dWidget.ts diff --git a/app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts b/app/src/services/visulization/zone/addFloatingWidgets.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/addFloatingWidgets.ts rename to app/src/services/visulization/zone/addFloatingWidgets.ts diff --git a/app/src/services/realTimeVisulization/zoneData/addWidgets.ts b/app/src/services/visulization/zone/addWidgets.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/addWidgets.ts rename to app/src/services/visulization/zone/addWidgets.ts diff --git a/app/src/services/realTimeVisulization/zoneData/clearPanel.ts b/app/src/services/visulization/zone/clearPanel.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/clearPanel.ts rename to app/src/services/visulization/zone/clearPanel.ts diff --git a/app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts b/app/src/services/visulization/zone/delete3dWidget.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/delete3dWidget.ts rename to app/src/services/visulization/zone/delete3dWidget.ts diff --git a/app/src/services/realTimeVisulization/zoneData/deleteFloatingWidget.ts b/app/src/services/visulization/zone/deleteFloatingWidget.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/deleteFloatingWidget.ts rename to app/src/services/visulization/zone/deleteFloatingWidget.ts diff --git a/app/src/services/realTimeVisulization/zoneData/deletePanel.ts b/app/src/services/visulization/zone/deletePanel.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/deletePanel.ts rename to app/src/services/visulization/zone/deletePanel.ts diff --git a/app/src/services/realTimeVisulization/zoneData/deleteTemplate.ts b/app/src/services/visulization/zone/deleteTemplate.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/deleteTemplate.ts rename to app/src/services/visulization/zone/deleteTemplate.ts diff --git a/app/src/services/realTimeVisulization/zoneData/deleteWidgetApi.ts b/app/src/services/visulization/zone/deleteWidgetApi.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/deleteWidgetApi.ts rename to app/src/services/visulization/zone/deleteWidgetApi.ts diff --git a/app/src/services/realTimeVisulization/zoneData/duplicateWidget.ts b/app/src/services/visulization/zone/duplicateWidget.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/duplicateWidget.ts rename to app/src/services/visulization/zone/duplicateWidget.ts diff --git a/app/src/services/realTimeVisulization/zoneData/get3dWidgetData.ts b/app/src/services/visulization/zone/get3dWidgetData.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/get3dWidgetData.ts rename to app/src/services/visulization/zone/get3dWidgetData.ts diff --git a/app/src/services/realTimeVisulization/zoneData/getFloatingData.ts b/app/src/services/visulization/zone/getFloatingData.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/getFloatingData.ts rename to app/src/services/visulization/zone/getFloatingData.ts diff --git a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts b/app/src/services/visulization/zone/getSelect2dZoneData.ts similarity index 85% rename from app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts rename to app/src/services/visulization/zone/getSelect2dZoneData.ts index 00d4dfe..b2c39e9 100644 --- a/app/src/services/realTimeVisulization/zoneData/getSelect2dZoneData.ts +++ b/app/src/services/visulization/zone/getSelect2dZoneData.ts @@ -7,7 +7,7 @@ export const getSelect2dZoneData = async ( ) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/ZoneVisualization/${ZoneId}?organization=${organization}`, + `${url_Backend_dwinzo}/api/v2/Zone/visualization/${ZoneId}?organization=${organization}`, { method: "GET", headers: { diff --git a/app/src/services/realTimeVisulization/zoneData/getTemplate.ts b/app/src/services/visulization/zone/getTemplate.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/getTemplate.ts rename to app/src/services/visulization/zone/getTemplate.ts diff --git a/app/src/services/realTimeVisulization/zoneData/getZoneData.ts b/app/src/services/visulization/zone/getZoneData.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/getZoneData.ts rename to app/src/services/visulization/zone/getZoneData.ts diff --git a/app/src/services/realTimeVisulization/zoneData/getZones.ts b/app/src/services/visulization/zone/getZones.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/getZones.ts rename to app/src/services/visulization/zone/getZones.ts diff --git a/app/src/services/realTimeVisulization/zoneData/loadTemplate.ts b/app/src/services/visulization/zone/loadTemplate.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/loadTemplate.ts rename to app/src/services/visulization/zone/loadTemplate.ts diff --git a/app/src/services/realTimeVisulization/zoneData/lockPanel.ts b/app/src/services/visulization/zone/lockPanel.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/lockPanel.ts rename to app/src/services/visulization/zone/lockPanel.ts diff --git a/app/src/services/realTimeVisulization/zoneData/panel.ts b/app/src/services/visulization/zone/panel.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/panel.ts rename to app/src/services/visulization/zone/panel.ts diff --git a/app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts b/app/src/services/visulization/zone/saveTempleteApi.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/saveTempleteApi.ts rename to app/src/services/visulization/zone/saveTempleteApi.ts diff --git a/app/src/services/realTimeVisulization/zoneData/update3dWidget.ts b/app/src/services/visulization/zone/update3dWidget.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/update3dWidget.ts rename to app/src/services/visulization/zone/update3dWidget.ts diff --git a/app/src/services/realTimeVisulization/zoneData/zoneCameraUpdation.ts b/app/src/services/visulization/zone/zoneCameraUpdation.ts similarity index 100% rename from app/src/services/realTimeVisulization/zoneData/zoneCameraUpdation.ts rename to app/src/services/visulization/zone/zoneCameraUpdation.ts diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts new file mode 100644 index 0000000..493a068 --- /dev/null +++ b/app/src/store/simulation/useArmBotStore.ts @@ -0,0 +1,160 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface ArmBotStore { + armBots: ArmBotStatus[]; + + addArmBot: (productId: string, event: RoboticArmEventSchema) => void; + removeArmBot: (modelUuid: string) => void; + updateArmBot: ( + modelUuid: string, + updates: Partial> + ) => void; + + addCurrentAction: (modelUuid: string, actionUuid: string) => void; + removeCurrentAction: (modelUuid: string) => void; + + addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; + removeAction: (modelUuid: string, actionUuid: string) => void; + + setArmBotActive: (modelUuid: string, isActive: boolean) => void; + + incrementActiveTime: (modelUuid: string, incrementBy: number) => void; + incrementIdleTime: (modelUuid: string, incrementBy: number) => void; + + getArmBotById: (modelUuid: string) => ArmBotStatus | undefined; + getArmBotsByProduct: (productId: string) => ArmBotStatus[]; + getArmBotsByState: (state: string) => ArmBotStatus[]; + getActiveArmBots: () => ArmBotStatus[]; + getIdleArmBots: () => ArmBotStatus[]; + getArmBotsByCurrentAction: (actionUuid: string) => ArmBotStatus[]; +} + +export const useArmBotStore = create()( + immer((set, get) => ({ + armBots: [], + + addArmBot: (productId, event) => { + set((state) => { + state.armBots.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + }); + }, + + removeArmBot: (modelUuid) => { + set((state) => { + state.armBots = state.armBots.filter(a => a.modelUuid !== modelUuid); + }); + }, + + updateArmBot: (modelUuid, updates) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + Object.assign(armBot, updates); + } + }); + }, + + addCurrentAction: (modelUuid, actionUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + armBot.currentAction = { + actionUuid: action.actionUuid, + actionName: action.actionName, + }; + armBot.isActive = true; + } + } + }); + }, + + removeCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.currentAction = undefined; + armBot.isActive = false; + } + }); + }, + + addAction: (modelUuid, action) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.point.actions.push(action); + } + }); + }, + + removeAction: (modelUuid, actionUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.point.actions = armBot.point.actions.filter(a => a.actionUuid !== actionUuid); + } + }); + }, + + setArmBotActive: (modelUuid, isActive) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.isActive = isActive; + } + }); + }, + + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.activeTime += incrementBy; + } + }); + }, + + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.idleTime += incrementBy; + } + }); + }, + + getArmBotById: (modelUuid) => { + return get().armBots.find(a => a.modelUuid === modelUuid); + }, + + getArmBotsByProduct: (productId) => { + return get().armBots.filter(a => a.productId === productId); + }, + + getArmBotsByState: (state) => { + return get().armBots.filter(a => a.state === state); + }, + + getActiveArmBots: () => { + return get().armBots.filter(a => a.isActive); + }, + + getIdleArmBots: () => { + return get().armBots.filter(a => !a.isActive && a.state === 'idle'); + }, + + getArmBotsByCurrentAction: (actionUuid) => { + return get().armBots.filter(a => a.currentAction?.actionUuid === actionUuid); + } + })) +); diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts new file mode 100644 index 0000000..15dbf34 --- /dev/null +++ b/app/src/store/simulation/useConveyorStore.ts @@ -0,0 +1,115 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface ConveyorStore { + conveyors: ConveyorStatus[]; + + addConveyor: (productId: string, event: ConveyorEventSchema) => void; + removeConveyor: (modelUuid: string) => void; + updateConveyor: ( + modelUuid: string, + updates: Partial> + ) => void; + + setConveyorActive: (modelUuid: string, isActive: boolean) => void; + setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void; + + incrementActiveTime: (modelUuid: string, incrementBy: number) => void; + incrementIdleTime: (modelUuid: string, incrementBy: number) => void; + + getConveyorById: (modelUuid: string) => ConveyorStatus | undefined; + getConveyorsByProduct: (productId: string) => ConveyorStatus[]; + getConveyorsByState: (state: string) => ConveyorStatus[]; + getActiveConveyors: () => ConveyorStatus[]; + getIdleConveyors: () => ConveyorStatus[]; +} + +export const useConveyorStore = create()( + immer((set, get) => ({ + conveyors: [], + + addConveyor: (productId, event) => { + set((state) => { + state.conveyors.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + }); + }, + + removeConveyor: (modelUuid) => { + set((state) => { + state.conveyors = state.conveyors.filter(c => c.modelUuid !== modelUuid); + }); + }, + + updateConveyor: (modelUuid, updates) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + Object.assign(conveyor, updates); + } + }); + }, + + setConveyorActive: (modelUuid, isActive) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.isActive = isActive; + } + }); + }, + + setConveyorState: (modelUuid, newState) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.state = newState; + } + }); + }, + + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.activeTime += incrementBy; + } + }); + }, + + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.idleTime += incrementBy; + } + }); + }, + + getConveyorById: (modelUuid) => { + return get().conveyors.find(c => c.modelUuid === modelUuid); + }, + + getConveyorsByProduct: (productId) => { + return get().conveyors.filter(c => c.productId === productId); + }, + + getConveyorsByState: (state) => { + return get().conveyors.filter(c => c.state === state); + }, + + getActiveConveyors: () => { + return get().conveyors.filter(c => c.isActive); + }, + + getIdleConveyors: () => { + return get().conveyors.filter(c => !c.isActive && c.state === 'idle'); + }, + })) +); diff --git a/app/src/store/simulation/useEventsStore.ts b/app/src/store/simulation/useEventsStore.ts new file mode 100644 index 0000000..2d92fc2 --- /dev/null +++ b/app/src/store/simulation/useEventsStore.ts @@ -0,0 +1,331 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +type EventsStore = { + events: EventsSchema[]; + + // Event-level actions + addEvent: (event: EventsSchema) => void; + removeEvent: (modelUuid: string) => void; + updateEvent: (modelUuid: string, updates: Partial) => void; + + // Point-level actions + addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; + removePoint: (modelUuid: string, pointUuid: string) => void; + updatePoint: ( + modelUuid: string, + pointUuid: string, + updates: Partial + ) => void; + + // Action-level actions + addAction: ( + modelUuid: string, + pointUuid: string, + action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] + ) => void; + removeAction: (actionUuid: string) => void; + updateAction: ( + actionUuid: string, + updates: Partial + ) => void; + + // Trigger-level actions + addTrigger: (actionUuid: string, trigger: TriggerSchema) => void; + removeTrigger: (triggerUuid: string) => void; + updateTrigger: (triggerUuid: string, updates: Partial) => void; + + // Helper functions + getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined; + getPointByUuid: (modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; + getActionByUuid: (actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getTriggerByUuid: (triggerUuid: string) => TriggerSchema | undefined; +}; + +export const useEventsStore = create()( + immer((set, get) => ({ + events: [], + + // Event-level actions + addEvent: (event) => { + set((state) => { + state.events.push(event); + }); + }, + + removeEvent: (modelUuid) => { + set((state) => { + state.events = state.events.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + }); + }, + + updateEvent: (modelUuid, updates) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event) { + Object.assign(event, updates); + } + }); + }, + + // Point-level actions + addPoint: (modelUuid, point) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } else if (event && 'point' in event) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } + }); + }, + + removePoint: (modelUuid, pointUuid) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + (event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid); + } + // For single-point events, you might want to handle differently + }); + }, + + updatePoint: (modelUuid, pointUuid, updates) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point) { + Object.assign(point, updates); + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + Object.assign((event as any).point, updates); + } + }); + }, + + // Action-level actions + addAction: (modelUuid, pointUuid, action) => { + set((state) => { + const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point) { + point.action = action as any; + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + if ('action' in (event as any).point) { + (event as any).point.action = action; + } else if ('actions' in (event as any).point) { + (event as any).point.actions.push(action); + } + } + }); + }, + + removeAction: (actionUuid) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + // Handle removal for single action points + } + } + } else if ('point' in event) { + const point = (event as any).point; + if (event.type === "roboticArm") { + if ('actions' in point) { + point.actions = point.actions.filter((a: any) => a.actionUuid !== actionUuid); + } + } else if ('action' in point && point.action?.actionUuid === actionUuid) { + // Handle single action + } + } + } + }); + }, + + updateAction: (actionUuid, updates) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + Object.assign(action, updates); + return; + } + } + } + } + }); + }, + + // Trigger-level actions + addTrigger: (actionUuid, trigger) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + point.action.triggers.push(trigger); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + point.action.triggers.push(trigger); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + action.triggers.push(trigger); + return; + } + } + } + } + }); + }, + + removeTrigger: (triggerUuid) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + } + } + } + } + } + }); + }, + + updateTrigger: (triggerUuid, updates) => { + set((state) => { + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } + } + } + }); + }, + + // Helper functions + getEventByModelUuid: (modelUuid) => { + return get().events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + }, + + getPointByUuid: (modelUuid, pointUuid) => { + const event = get().events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + return (event as any).point; + } + return undefined; + }, + + getActionByUuid: (actionUuid) => { + const state = get(); + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + return point.action; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return action; + } + } + } + return undefined; + }, + + getTriggerByUuid: (triggerUuid) => { + const state = get(); + for (const event of state.events) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } + } + } + } + } + return undefined; + } + })) +); diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts new file mode 100644 index 0000000..cc927f7 --- /dev/null +++ b/app/src/store/simulation/useMachineStore.ts @@ -0,0 +1,123 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface MachineStore { + machines: MachineStatus[]; + + // Actions + addMachine: (productId: string, machine: MachineEventSchema) => void; + removeMachine: (modelUuid: string) => void; + updateMachine: ( + modelUuid: string, + updates: Partial> + ) => void; + + // Status updates + setMachineActive: (modelUuid: string, isActive: boolean) => void; + setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void; + + // Time tracking + incrementActiveTime: (modelUuid: string, incrementBy: number) => void; + incrementIdleTime: (modelUuid: string, incrementBy: number) => void; + + // Helpers + getMachineById: (modelUuid: string) => MachineStatus | undefined; + getMachinesByProduct: (productId: string) => MachineStatus[]; + getMachinesBystate: (state: string) => MachineStatus[]; + getActiveMachines: () => MachineStatus[]; + getIdleMachines: () => MachineStatus[]; +} + +export const useMachineStore = create()( + immer((set, get) => ({ + machines: [], + + // Actions + addMachine: (productId, machine) => { + set((state) => { + state.machines.push({ + ...machine, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + }); + }, + + removeMachine: (modelUuid) => { + set((state) => { + state.machines = state.machines.filter(m => m.modelUuid !== modelUuid); + }); + }, + + updateMachine: (modelUuid, updates) => { + set((state) => { + const machine = state.machines.find(m => m.modelUuid === modelUuid); + if (machine) { + Object.assign(machine, updates); + } + }); + }, + + // Status updates + setMachineActive: (modelUuid, isActive) => { + set((state) => { + const machine = state.machines.find(m => m.modelUuid === modelUuid); + if (machine) { + machine.isActive = isActive; + } + }); + }, + + setMachineState: (modelUuid, newState) => { + set((state) => { + const machine = state.machines.find(m => m.modelUuid === modelUuid); + if (machine) { + machine.state = newState; + } + }); + }, + + // Time tracking + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const machine = state.machines.find(m => m.modelUuid === modelUuid); + if (machine) { + machine.activeTime += incrementBy; + } + }); + }, + + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const machine = state.machines.find(m => m.modelUuid === modelUuid); + if (machine) { + machine.idleTime += incrementBy; + } + }); + }, + + // Helpers + getMachineById: (modelUuid) => { + return get().machines.find(m => m.modelUuid === modelUuid); + }, + + getMachinesByProduct: (productId) => { + return get().machines.filter(m => m.productId === productId); + }, + + getMachinesBystate: (state) => { + return get().machines.filter(m => m.state === state); + }, + + getActiveMachines: () => { + return get().machines.filter(m => m.isActive); + }, + + getIdleMachines: () => { + return get().machines.filter(m => !m.isActive && m.state === 'idle'); + }, + })) +); diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts new file mode 100644 index 0000000..41a13f6 --- /dev/null +++ b/app/src/store/simulation/useProductStore.ts @@ -0,0 +1,339 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +type ProductsStore = { + products: productsSchema; + + // Product-level actions + addProduct: (productName: string, productId: string) => void; + removeProduct: (productId: string) => void; + updateProduct: (productId: string, updates: Partial<{ productName: string; eventsData: EventsSchema[] }>) => void; + + // Event-level actions + addEvent: (productId: string, event: EventsSchema) => void; + removeEvent: (productId: string, modelUuid: string) => void; + updateEvent: (productId: string, modelUuid: string, updates: Partial) => void; + + // Point-level actions + addPoint: (productId: string, modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; + removePoint: (productId: string, modelUuid: string, pointUuid: string) => void; + updatePoint: ( + productId: string, + modelUuid: string, + pointUuid: string, + updates: Partial + ) => void; + + // Action-level actions + addAction: ( + productId: string, + modelUuid: string, + pointUuid: string, + action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] + ) => void; + removeAction: (actionUuid: string) => void; + updateAction: ( + actionUuid: string, + updates: Partial + ) => void; + + // Trigger-level actions + addTrigger: ( + actionUuid: string, + trigger: TriggerSchema + ) => void; + removeTrigger: (triggerUuid: string) => void; + updateTrigger: ( + triggerUuid: string, + updates: Partial + ) => void; + + // Helper functions + getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined; +}; + +export const useProductStore = create()( + immer((set, get) => ({ + products: [], + + // Product-level actions + addProduct: (productName, productId) => { + set((state) => { + const newProduct = { + productName, + productId: productId, + eventsData: [] + }; + state.products.push(newProduct); + }); + }, + + removeProduct: (productId) => { + set((state) => { + state.products = state.products.filter(p => p.productId !== productId); + }); + }, + + updateProduct: (productId, updates) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + Object.assign(product, updates); + } + }); + }, + + // Event-level actions + addEvent: (productId, event) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + product.eventsData.push(event); + } + }); + }, + + removeEvent: (productId, modelUuid) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + product.eventsData = product.eventsData.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + } + }); + }, + + updateEvent: (productId, modelUuid, updates) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event) { + Object.assign(event, updates); + } + } + }); + }, + + // Point-level actions + addPoint: (productId, modelUuid, point) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } else if (event && 'point' in event) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } + } + }); + }, + + removePoint: (productId, modelUuid, pointUuid) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + (event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid); + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + // For events with single point, we can't remove it, only reset to empty + } + } + }); + }, + + updatePoint: (productId, modelUuid, pointUuid, updates) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point) { + Object.assign(point, updates); + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + Object.assign((event as any).point, updates); + } + } + }); + }, + + // Action-level actions + addAction: (productId, modelUuid, pointUuid, action) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + if (event && 'points' in event) { + const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + if (point) { + point.action = action as any; + } + } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { + if ('action' in (event as any).point) { + (event as any).point.action = action; + } else if ('actions' in (event as any).point) { + (event as any).point.actions.push(action); + } + } + } + }); + }, + + removeAction: (actionUuid: string) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + // Handle ConveyorEventSchema + for (const point of (event as ConveyorEventSchema).points) { + } + } else if ('point' in event) { + const point = (event as any).point; + if (event.type === "roboticArm") { + // Handle RoboticArmEventSchema + if ('actions' in point) { + point.actions = point.actions.filter((a: any) => a.actionUuid !== actionUuid); + } + } else if ('action' in point && point.action?.actionUuid === actionUuid) { + // For other schemas with a single 'action' + } + } + } + } + }); + }, + + updateAction: (actionUuid, updates) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + Object.assign(point.action, updates); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + Object.assign(action, updates); + return; + } + } + } + } + } + }); + }, + + // Trigger-level actions + addTrigger: (actionUuid, trigger) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + point.action.triggers.push(trigger); + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + point.action.triggers.push(trigger); + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + action.triggers.push(trigger); + return; + } + } + } + } + } + }); + }, + + removeTrigger: (triggerUuid) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + } + } + } + } + } + } + }); + }, + + updateTrigger: (triggerUuid, updates) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } + } + } + } + }); + }, + + // Helper functions + getProductById: (productId) => { + return get().products.find(p => p.productId === productId); + } + })) +); diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts new file mode 100644 index 0000000..d729708 --- /dev/null +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -0,0 +1,149 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface StorageUnitStore { + storageUnits: StorageUnitStatus[]; + + // Actions + addStorageUnit: (productId: string, storageUnit: StorageEventSchema) => void; + removeStorageUnit: (modelUuid: string) => void; + updateStorageUnit: ( + modelUuid: string, + updates: Partial> + ) => void; + + // Status updates + setStorageUnitActive: (modelUuid: string, isActive: boolean) => void; + setStorageUnitState: (modelUuid: string, newState: StorageUnitStatus['state']) => void; + + // Load updates + updateStorageUnitLoad: (modelUuid: string, incrementBy: number) => void; + + // Time tracking + incrementActiveTime: (modelUuid: string, incrementBy: number) => void; + incrementIdleTime: (modelUuid: string, incrementBy: number) => void; + + // Helpers + getStorageUnitById: (modelUuid: string) => StorageUnitStatus | undefined; + getStorageUnitsByProduct: (productId: string) => StorageUnitStatus[]; + getStorageUnitsBystate: (state: string) => StorageUnitStatus[]; + getActiveStorageUnits: () => StorageUnitStatus[]; + getIdleStorageUnits: () => StorageUnitStatus[]; + getFullStorageUnits: () => StorageUnitStatus[]; + getEmptyStorageUnits: () => StorageUnitStatus[]; +} + +export const useStorageUnitStore = create()( + immer((set, get) => ({ + storageUnits: [], + + // Actions + addStorageUnit: (productId, storageUnit) => { + set((state) => { + state.storageUnits.push({ + ...storageUnit, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + state: 'idle', + }); + }); + }, + + removeStorageUnit: (modelUuid) => { + set((state) => { + state.storageUnits = state.storageUnits.filter(s => s.modelUuid !== modelUuid); + }); + }, + + updateStorageUnit: (modelUuid, updates) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + Object.assign(unit, updates); + } + }); + }, + + // Status updates + setStorageUnitActive: (modelUuid, isActive) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.isActive = isActive; + } + }); + }, + + setStorageUnitState: (modelUuid, newState) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.state = newState; + } + }); + }, + + // Load updates + updateStorageUnitLoad: (modelUuid, incrementBy) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.currentLoad += incrementBy; + } + }); + }, + + // Time tracking + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.activeTime += incrementBy; + } + }); + }, + + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.idleTime += incrementBy; + } + }); + }, + + // Helpers + getStorageUnitById: (modelUuid) => { + return get().storageUnits.find(s => s.modelUuid === modelUuid); + }, + + getStorageUnitsByProduct: (productId) => { + return get().storageUnits.filter(s => s.productId === productId); + }, + + getStorageUnitsBystate: (state) => { + return get().storageUnits.filter(s => s.state === state); + }, + + getActiveStorageUnits: () => { + return get().storageUnits.filter(s => s.isActive); + }, + + getIdleStorageUnits: () => { + return get().storageUnits.filter(s => !s.isActive && s.state === 'idle'); + }, + + getFullStorageUnits: () => { + return get().storageUnits.filter( + s => s.currentLoad >= s.point.action.storageCapacity + ); + }, + + getEmptyStorageUnits: () => { + return get().storageUnits.filter(s => s.currentLoad === 0); + }, + })) +); diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts new file mode 100644 index 0000000..ce28916 --- /dev/null +++ b/app/src/store/simulation/useVehicleStore.ts @@ -0,0 +1,139 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface VehicleStatus extends VehicleEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; + distanceTraveled: number; +} + +interface VehiclesStore { + vehicles: VehicleStatus[]; + + addVehicle: (productId: string, event: VehicleEventSchema) => void; + removeVehicle: (modelUuid: string) => void; + updateVehicle: ( + modelUuid: string, + updates: Partial> + ) => void; + + setVehicleActive: (modelUuid: string, isActive: boolean) => void; + incrementVehicleLoad: (modelUuid: string, incrementBy: number) => void; + decrementVehicleLoad: (modelUuid: string, decrementBy: number) => void; + setVehicleState: (modelUuid: string, newState: VehicleStatus['state']) => void; + incrementActiveTime: (modelUuid: string, incrementBy: number) => void; + incrementIdleTime: (modelUuid: string, incrementBy: number) => void; + + getVehicleById: (modelUuid: string) => VehicleStatus | undefined; + getVehiclesByProduct: (productId: string) => VehicleStatus[]; + getVehiclesByState: (state: string) => VehicleStatus[]; + getActiveVehicles: () => VehicleStatus[]; +} + +export const useVehicleStore = create()( + immer((set, get) => ({ + vehicles: [], + + addVehicle: (productId, event) => { + set((state) => { + state.vehicles.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + distanceTraveled: 0, + }); + }); + }, + + removeVehicle: (modelUuid) => { + set((state) => { + state.vehicles = state.vehicles.filter(v => v.modelUuid !== modelUuid); + }); + }, + + updateVehicle: (modelUuid, updates) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + Object.assign(vehicle, updates); + } + }); + }, + + setVehicleActive: (modelUuid, isActive) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.isActive = isActive; + } + }); + }, + + incrementVehicleLoad: (modelUuid, incrementBy) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad += incrementBy; + } + }); + }, + + decrementVehicleLoad: (modelUuid, decrementBy) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad = decrementBy; + } + }); + }, + + setVehicleState: (modelUuid, newState) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.state = newState; + } + }); + }, + + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.activeTime += incrementBy; + } + }); + }, + + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.idleTime += incrementBy; + } + }); + }, + + getVehicleById: (modelUuid) => { + return get().vehicles.find(v => v.modelUuid === modelUuid); + }, + + getVehiclesByProduct: (productId) => { + return get().vehicles.filter(v => v.productId === productId); + }, + + getVehiclesByState: (state) => { + return get().vehicles.filter(v => v.state === state); + }, + + getActiveVehicles: () => { + return get().vehicles.filter(v => v.isActive); + } + })) +); diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 765712b..b16e1ff 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -1,6 +1,5 @@ import * as THREE from "three"; import * as Types from "../types/world/worldTypes"; -import * as SimulationTypes from "../types/simulationTypes"; import { create } from "zustand"; import { io } from "socket.io-client"; @@ -86,10 +85,15 @@ export const useZonePoints = create((set) => ({ })); export const useSelectedItem = create((set: any) => ({ - selectedItem: { name: "", id: "" }, + selectedItem: { name: "", id: "", type: undefined }, setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), })); +export const useNavMesh = create((set: any) => ({ + navMesh: null, + setNavMesh: (x: any) => set({ navMesh: x }), +})); + export const useSelectedAssets = create((set: any) => ({ selectedAssets: [], setSelectedAssets: (x: any) => set(() => ({ selectedAssets: x })), @@ -338,49 +342,6 @@ export const useDrieUIValue = create((set: any) => ({ })), })); -export const useDrawMaterialPath = create((set: any) => ({ - drawMaterialPath: false, - setDrawMaterialPath: (x: any) => set({ drawMaterialPath: x }), -})); - -export const useSelectedActionSphere = create((set: any) => ({ - selectedActionSphere: undefined, - setSelectedActionSphere: (x: any) => set({ selectedActionSphere: x }), -})); - -export const useSelectedPath = create((set: any) => ({ - selectedPath: undefined, - setSelectedPath: (x: any) => set({ selectedPath: x }), -})); - -interface SimulationPathsStore { - simulationStates: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]; - setSimulationStates: ( - paths: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[] - | ((prev: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) - => (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) - ) => void; -} - -export const useSimulationStates = create((set) => ({ - simulationStates: [], - setSimulationStates: (paths) => - set((state) => ({ - simulationStates: - typeof paths === "function" ? paths(state.simulationStates) : paths, - })), -})); - -export const useNavMesh = create((set: any) => ({ - navMesh: null, - setNavMesh: (x: any) => set({ navMesh: x }), -})); - -export const useIsConnecting = create((set: any) => ({ - isConnecting: false, - setIsConnecting: (x: any) => set({ isConnecting: x }), -})); - export const useStartSimulation = create((set: any) => ({ startSimulation: false, setStartSimulation: (x: any) => set({ startSimulation: x }), @@ -396,14 +357,6 @@ export const useEditingPoint = create((set: any) => ({ setEditingPoint: (x: any) => set({ editingPoint: x }), })); -export const usePreviewPosition = create<{ - previewPosition: { x: number; y: number } | null; - setPreviewPosition: (position: { x: number; y: number } | null) => void; -}>((set) => ({ - previewPosition: null, - setPreviewPosition: (position) => set({ previewPosition: position }), -})); - export const usezoneTarget = create((set: any) => ({ zoneTarget: [], setZoneTarget: (x: any) => set({ zoneTarget: x }), @@ -421,7 +374,7 @@ interface EditPositionState { export const useEditPosition = create((set) => ({ Edit: false, - setEdit: (value) => set({ Edit: value }), // Properly updating the state + setEdit: (value) => set({ Edit: value }), })); export const useAsset3dWidget = create((set: any) => ({ diff --git a/app/src/store/useChartStore.ts b/app/src/store/visualization/useChartStore.ts similarity index 100% rename from app/src/store/useChartStore.ts rename to app/src/store/visualization/useChartStore.ts diff --git a/app/src/store/useDroppedObjectsStore.ts b/app/src/store/visualization/useDroppedObjectsStore.ts similarity index 96% rename from app/src/store/useDroppedObjectsStore.ts rename to app/src/store/visualization/useDroppedObjectsStore.ts index 0c03eec..5c4527b 100644 --- a/app/src/store/useDroppedObjectsStore.ts +++ b/app/src/store/visualization/useDroppedObjectsStore.ts @@ -1,6 +1,6 @@ import { create } from "zustand"; -import { addingFloatingWidgets } from "../services/realTimeVisulization/zoneData/addFloatingWidgets"; -import { useSocketStore } from "./store"; +import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets"; +import { useSocketStore } from "../store"; import useChartStore from "./useChartStore"; type DroppedObject = { @@ -114,7 +114,7 @@ export const useDroppedObjectsStore = create((set) => ({ // Create a shallow copy of the object with a unique ID and slightly adjusted position const duplicatedObject: DroppedObject = { ...originalObject, - Data:{ + Data: { measurements: iotMeasurements, duration: iotDuration, }, @@ -132,8 +132,8 @@ export const useDroppedObjectsStore = create((set) => ({ : originalObject.position.left, }, }; - console.log("duplicated object",duplicatedObject); - + console.log("duplicated object", duplicatedObject); + let duplicateFloatingWidget = { organization: organization, widget: duplicatedObject, diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/visualization/useZone3DWidgetStore.ts similarity index 100% rename from app/src/store/useZone3DWidgetStore.ts rename to app/src/store/visualization/useZone3DWidgetStore.ts diff --git a/app/src/store/useZoneStore.ts b/app/src/store/visualization/useZoneStore.ts similarity index 95% rename from app/src/store/useZoneStore.ts rename to app/src/store/visualization/useZoneStore.ts index b06824b..eb05790 100644 --- a/app/src/store/useZoneStore.ts +++ b/app/src/store/visualization/useZoneStore.ts @@ -1,53 +1,53 @@ -import { create } from "zustand"; - -type Side = "top" | "bottom" | "left" | "right"; - -interface Widget { - id: string; - type: string; - title: string; - panel: Side; - data: any; -} - -interface SelectedZoneState { - zoneName: string; - activeSides: Side[]; - panelOrder: Side[]; - points: [] - lockedPanels: Side[]; - zoneId: string; - zoneViewPortTarget: number[]; - zoneViewPortPosition: number[]; - widgets: Widget[]; -} - -interface SelectedZoneStore { - selectedZone: SelectedZoneState; - setSelectedZone: ( - zone: - | Partial - | ((prev: SelectedZoneState) => SelectedZoneState) - ) => void; -} - -export const useSelectedZoneStore = create((set) => ({ - selectedZone: { - zoneName: "", // Empty string initially - activeSides: [], // Empty array - panelOrder: [], // Empty array - lockedPanels: [], // Empty array - points: [], - zoneId: "", - zoneViewPortTarget: [], - zoneViewPortPosition: [], - widgets: [], // Empty array - }, - setSelectedZone: (zone) => - set((state) => ({ - selectedZone: - typeof zone === "function" - ? zone(state.selectedZone) // Handle functional updates - : { ...state.selectedZone, ...zone }, // Handle partial updates - })), -})); +import { create } from "zustand"; + +type Side = "top" | "bottom" | "left" | "right"; + +interface Widget { + id: string; + type: string; + title: string; + panel: Side; + data: any; +} + +interface SelectedZoneState { + zoneName: string; + activeSides: Side[]; + panelOrder: Side[]; + points: [] + lockedPanels: Side[]; + zoneId: string; + zoneViewPortTarget: number[]; + zoneViewPortPosition: number[]; + widgets: Widget[]; +} + +interface SelectedZoneStore { + selectedZone: SelectedZoneState; + setSelectedZone: ( + zone: + | Partial + | ((prev: SelectedZoneState) => SelectedZoneState) + ) => void; +} + +export const useSelectedZoneStore = create((set) => ({ + selectedZone: { + zoneName: "", // Empty string initially + activeSides: [], // Empty array + panelOrder: [], // Empty array + lockedPanels: [], // Empty array + points: [], + zoneId: "", + zoneViewPortTarget: [], + zoneViewPortPosition: [], + widgets: [], // Empty array + }, + setSelectedZone: (zone) => + set((state) => ({ + selectedZone: + typeof zone === "function" + ? zone(state.selectedZone) // Handle functional updates + : { ...state.selectedZone, ...zone }, // Handle partial updates + })), +})); diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 883f6c2..21554e8 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -7,8 +7,8 @@ input { width: 100%; padding: 2px 4px; border-radius: #{$border-radius-small}; - outline: 2px solid var(--border-color); - outline-offset: -2px; + outline: 1px solid var(--border-color); + outline-offset: -1px; border: none; background: transparent; color: var(--input-text-color); @@ -30,6 +30,24 @@ input { background-color: var(--background-color) !important; -webkit-box-shadow: 0 0 0px 1000px var(--background-color) inset !important; } + + // File input specific style adjustments + &::file-selector-button { + font-size: 14px; + color: var(--accent-color); + background-color: var(--background-color-secondary); + border: none; + outline: none; + border-radius: #{$border-radius-small}; + padding: 2px; + cursor: pointer; + + // Hover effect for the file button + &:hover { + color: var(--primary-color); + background-color: var(--accent-color); + } + } } .input-value { @@ -712,3 +730,47 @@ input { border: 1px solid var(--accent-color); } } + +.preview-selection-with-upload-wrapper { + .input-header-container { + padding: 6px 12px; + @include flex-space-between; + .arrow-container { + transition: all 0.2s; + @include flex-center; + } + } + .upload-custom-asset-button{ + padding: 6px 12px; + @include flex-space-between; + .title{ + white-space: nowrap; + width: 40%; + } + input{ + display: none; + } + .upload-button{ + width: 60%; + background: var(--highlight-accent-color); + color: var(--accent-color); + padding: 3px 6px; + border-radius: #{$border-radius-small}; + text-align: center; + } + } + .canvas-wrapper { + height: 150px; + width: 100%; + padding: 8px; + padding-right: 4px; + overflow: hidden; + position: relative; + .canvas-container { + width: 100%; + height: 100%; + border-radius: #{$border-radius-small}; + background-color: var(--background-color-gray); + } + } +} diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 305206a..cab078e 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -7,6 +7,7 @@ top: 32px; left: 8px; background-color: var(--background-color); + backdrop-filter: blur(150px); border-radius: #{$border-radius-extra-large}; box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; @@ -131,15 +132,12 @@ } .widgets-wrapper { - min-height: 50vh; max-height: 60vh; overflow: auto; } .widget-left-sideBar { - - .widget2D { overflow: auto; @@ -246,6 +244,7 @@ top: 32px; right: 8px; background-color: var(--background-color); + backdrop-filter: blur(150px); border-radius: #{$border-radius-extra-large}; box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; @@ -643,7 +642,7 @@ path { stroke: var(--accent-color); - strokewidth: 1.5px; + stroke-width: 1.5px; } &:hover { @@ -658,7 +657,8 @@ } .machine-mechanics-content-container, - .simulations-container { + .simulations-container, + .event-proprties-wrapper { max-height: calc(60vh - (47px - 35px)); overflow: auto; overflow-y: scroll; @@ -682,6 +682,52 @@ } } + .global-props { + .property-list-container { + .property-item { + .value-field-container { + margin: 0; + input { + padding: 5px 4px; + } + .dropdown { + top: 4px; + right: 4px; + } + } + } + } + } + + .selected-actions-details { + .selected-actions-header .input-value { + padding: 8px 12px; + color: var(--accent-color); + } + .selected-actions-list { + margin-bottom: 8px; + .eye-dropper-input-container{ + padding: 6px 12px; + .regularDropdown-container { + padding: 5px 8px; + outline: 2px solid var(--border-color); + outline-offset: -2px; + border: none; + } + } + .value-field-container { + margin: 0; + input { + padding: 5px 4px; + } + .dropdown { + top: 4px; + right: 4px; + } + } + } + } + .lists-main-container { margin: 2px 8px; width: calc(100% - 12px); @@ -712,6 +758,7 @@ input { width: fit-content; + outline: none; accent-color: var(--accent-color); } } @@ -1183,25 +1230,21 @@ z-index: 3; padding: 8px; width: 100%; + max-height: 38px; font-size: var(--font-size-regular); - background: color-mix(in srgb, - var(--background-color) 40%, - transparent); + background: color-mix( + in srgb, + var(--background-color) 40%, + transparent + ); backdrop-filter: blur(5px); opacity: 0; transition: opacity 0.3s ease; - - /* Added properties for ellipsis */ display: -webkit-box; - /* Necessary for multiline truncation */ -webkit-line-clamp: 2; - /* Number of lines to show */ -webkit-box-orient: vertical; - /* Box orientation for the ellipsis */ overflow: hidden; - /* Hide overflowing content */ text-overflow: ellipsis; - /* Add ellipsis for truncated content */ } .asset-image { @@ -1271,4 +1314,4 @@ .assets-wrapper { margin: 0; } -} \ No newline at end of file +} diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index 75efcd3..f6b0a0c 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -1,163 +1,171 @@ +interface AssetEventSchema { + modelUuid: string; + modelName: string; + position: [number, number, number]; + rotation: [number, number, number]; + state: "idle" | "running" | "stopped" | "disabled" | "error"; +} -interface PathConnection { - fromModelUUID: string; - fromUUID: string; - toConnections: { - toModelUUID: string; - toUUID: string; +interface TriggerSchema { + triggerUuid: string; + triggerName: string; + triggerType: "onComplete" | "onStart" | "onStop" | "delay" | "onError"; + delay: number; + triggeredAsset: { + triggeredModel: { modelName: string, modelUuid: string }; + triggeredPoint: { pointName: string, pointUuid: string }; + triggeredAction: { actionName: string, actionUuid: string }; + } | null; +} + +interface ConveyorPointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + action: { + actionUuid: string; + actionName: string; + actionType: "default" | "spawn" | "swap" | "despawn"; + material: string; + delay: number | "inherit"; + spawnInterval: number | "inherit"; + spawnCount: number | "inherit"; + triggers: TriggerSchema[]; + }; +} + +interface VehiclePointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + action: { + actionUuid: string; + actionName: string; + actionType: "travel"; + material: string | null; + unLoadDuration: number; + loadCapacity: number; + pickUpPoint: { x: number; y: number, z: number } | null; + unLoadPoint: { x: number; y: number, z: number } | null; + triggers: TriggerSchema[]; + }; +} + +interface RoboticArmPointSchema { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + actions: { + actionUuid: string; + actionName: string; + actionType: "pickAndPlace"; + process: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }; + triggers: TriggerSchema[]; }[]; } -interface ConnectionStore { - connections: PathConnection[]; - setConnections: (connections: PathConnection[]) => void; - addConnection: (newConnection: PathConnection) => void; - removeConnection: (fromUUID: string, toUUID: string) => void; -} - -interface ConveyorEventsSchema { - modeluuid: string; - modelName: string; - type: "Conveyor"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean; }[] | []; - triggers: { uuid: string; name: string; type: string; isUsed: boolean; bufferTime: number; }[] | []; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - }[]; +interface MachinePointSchema { + uuid: string; position: [number, number, number]; rotation: [number, number, number]; - speed: number | string; + action: { + actionUuid: string; + actionName: string; + actionType: "process"; + processTime: number; + swapMaterial: string; + triggers: TriggerSchema[]; + }; } -interface VehicleEventsSchema { - modeluuid: string; - modelName: string; - type: "Vehicle"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; type: string; start: { x: number; y: number } | {}; hitCount: number; end: { x: number; y: number } | {}; buffer: number; }; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - speed: number; - isPlaying: boolean; - }; - +interface StoragePointSchema { + uuid: string; position: [number, number, number]; rotation: [number, number, number]; + action: { + actionUuid: string; + actionName: string; + actionType: "store"; + materials: { materialName: string; materialId: string; }[]; + storageCapacity: number; + }; } -interface StaticMachineEventsSchema { - modeluuid: string; - modelName: string; - type: "StaticMachine"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; buffer: number; material: string; }; - triggers: { uuid: string; name: string; type: string }; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - }; - position: [number, number, number]; - rotation: [number, number, number]; +interface ConveyorEventSchema extends AssetEventSchema { + type: "transfer"; + speed: number; + points: ConveyorPointSchema[]; } -interface ArmBotEventsSchema { - modeluuid: string; - modelName: string; - type: "ArmBot"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; - triggers: { uuid: string; name: string; type: string }; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - }; - position: [number, number, number]; - rotation: [number, number, number]; +interface VehicleEventSchema extends AssetEventSchema { + type: "vehicle"; + speed: number; + point: VehiclePointSchema; } -export type EventData = { - modeluuid: string; - modelname: string; - position: [number, number, number]; - rotation: { x: number; y: number; z: number }; - modelfileID: string; - isLocked: boolean; - isVisible: boolean; - eventData?: - { - type: "Conveyor"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean; }[] | []; - triggers: { uuid: string; name: string; type: string; isUsed: boolean; bufferTime: number; }[] | []; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - }[]; - speed: number | string; - } - | { - type: "Vehicle"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; type: string; start: { x: number; y: number } | {}; hitCount: number; end: { x: number; y: number } | {}; buffer: number; }; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - speed: number; - }; - } - | { - type: "StaticMachine"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; buffer: number; material: string; }; - triggers: { uuid: string; name: string; type: string }; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - }; - } - | { - type: "ArmBot"; - points: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string; }[]; }; - triggers: { uuid: string; name: string; type: string }; - connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; - }; - }; +interface RoboticArmEventSchema extends AssetEventSchema { + type: "roboticArm"; + speed: number; + point: RoboticArmPointSchema; +} + +interface MachineEventSchema extends AssetEventSchema { + type: "machine"; + point: MachinePointSchema; +} + +interface StorageEventSchema extends AssetEventSchema { + type: "storageUnit"; + point: StoragePointSchema; +} + +type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema; + +type productsSchema = { + productName: string; + productId: string; + eventsData: EventsSchema[]; +}[] + + +interface ConveyorStatus extends ConveyorEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; +} + +interface MachineStatus extends MachineEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; +} + +interface ArmBotStatus extends RoboticArmEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentAction?: { + actionUuid: string; + actionName: string; }; -}; +} + +interface VehicleStatus extends VehicleEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; + distanceTraveled: number; +} + +interface StorageUnitStatus extends StorageEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; +} \ No newline at end of file