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..125cef9 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -10,25 +10,19 @@ 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 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"; 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 @@ -112,46 +106,9 @@ 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..9cd9a7c 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -3,7 +3,7 @@ import RenameInput from "../../../ui/inputs/RenameInput"; import Vector3Input from "../customInput/Vector3Input"; import { useSelectedZoneStore } from "../../../../store/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/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..cf5bb17 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -15,7 +15,7 @@ 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"; diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index b2f05b9..f29475e 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -4,7 +4,6 @@ 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"; 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..efcf328 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import RenameInput from "../inputs/RenameInput"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; -import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones"; +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/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 100% rename from app/src/modules/collaboration/users/functions/getAvatarColor.ts rename to app/src/functions/users/functions/getAvatarColor.ts 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 faa344b..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/navMeshCreator.tsx b/app/src/modules/builder/agv/navMeshCreator.tsx deleted file mode 100644 index c0f8808..0000000 --- a/app/src/modules/builder/agv/navMeshCreator.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useRef } from "react"; -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"; - -type NavMeshCreatorProps = { - lines: Types.RefLines -}; - -function NavMeshCreator({ lines }: NavMeshCreatorProps) { - let groupRef = useRef() as Types.RefGroup; - const { setNavMesh } = useNavMesh(); - - return ( - <> - - - - - - - - - - - ) -} - -export default NavMeshCreator \ No newline at end of file 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 c1aac29..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/scene/world/world.tsx b/app/src/modules/builder/builder.tsx similarity index 88% rename from app/src/modules/scene/world/world.tsx rename to app/src/modules/builder/builder.tsx index dcd1adb..cfc5dcd 100644 --- a/app/src/modules/scene/world/world.tsx +++ b/app/src/modules/builder/builder.tsx @@ -1,374 +1,373 @@ -////////// 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 ( - <> - - - - - - - - - - - - - - - - - {/* */} - - - - - - {/* */} - - - - - ); -} +////////// 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/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"; + +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..5a42404 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -5,12 +5,10 @@ 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'; async function addAssetModel( @@ -25,7 +23,6 @@ async function addAssetModel( socket: Socket, selectedItem: any, setSelectedItem: any, - setSimulationStates: any, plane: Types.RefMesh, ): Promise { @@ -66,7 +63,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, socket); return; } else { const cachedModelBlob = await retrieveGLTF(selectedItem.id); @@ -79,7 +76,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, socket); }, () => { TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); @@ -91,7 +88,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, socket); }, () => { TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); @@ -114,7 +111,6 @@ async function handleModelLoad( tempLoader: Types.RefMesh, isTempLoader: Types.RefBoolean, setFloorItems: Types.setFloorItemSetState, - setSimulationStates: any, socket: Socket ) { const model = gltf.scene.clone(); @@ -137,7 +133,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 +146,44 @@ 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: [] - } - })), - speed: 'Inherit' - }; + // SOCKET - // 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, - 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: [] }, - } - } - - // 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: [] }, - } - } - - // 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, - position: newFloorItem.position, - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id - }; + const data = { + organization, + modeluuid: newFloorItem.modeluuid, + modelname: newFloorItem.modelname, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id + }; - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - socket.emit("v2:model-asset:add", data); - } - - 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..99d3291 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,7 +11,7 @@ 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"; @@ -32,7 +32,6 @@ 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(); @@ -73,7 +72,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 +91,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject updateLoadingProgress(progress); if (loadedAssets === totalAssets) { - loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates); + loadInitialFloorItems(itemsGroup, setFloorItems); updateLoadingProgress(100); } }); @@ -192,9 +191,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 +275,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, plane); } }; 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/collaboration/camera/collabCams.tsx b/app/src/modules/collaboration/camera/collabCams.tsx new file mode 100644 index 0000000..63ab2a1 --- /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/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..41ededb 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -1,63 +1,33 @@ 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"; - 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/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/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 2c701e9..0000000 --- a/app/src/modules/simulation/process/useProcessAnimations.tsx +++ /dev/null @@ -1,671 +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, useSimulationStates } 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(); - const { simulationStates } = useSimulationStates(); - - // 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 isConveyor = simulationStates.find((state) => state.modeluuid === connectedModelUUID && state.type === "Conveyor"); - - if (!isConveyor) { - 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(() => { - // console.log('deferredArmBotUpdates: ', deferredArmBotUpdates); - 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/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 84d3651..bde8106 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -1,74 +1,10 @@ -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 from 'react' 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([]); - - return ( - <> - {activeModule === "simulation" && ( - <> - - - - - - - - - - )} - - - - ); + return ( + <> + + ) } -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/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index e039592..e341fcd 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -3,7 +3,7 @@ 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 DisplayZone from "./zone/DisplayZone"; import Scene from "../scene/scene"; import useModuleStore from "../../store/useModuleStore"; @@ -17,10 +17,10 @@ import { 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"; @@ -31,7 +31,6 @@ import { useRightClickSelected, useRightSelected, } from "../../store/useZone3DWidgetStore"; -import Dropped3dWidgets from "./widgets/3d/Dropped3dWidget"; 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 96% rename from app/src/modules/scene/mqttTemp/drieHtmlTemp.tsx rename to app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx index 68c8c37..8604bcc 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({ 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 && + + + + + + } + + ) } \ 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/template/Templates.tsx b/app/src/modules/visualization/template/Templates.tsx index 8370112..59a38ce 100644 --- a/app/src/modules/visualization/template/Templates.tsx +++ b/app/src/modules/visualization/template/Templates.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import useTemplateStore from "../../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { useSocketStore } from "../../../store/store"; -import { getTemplateData } from "../../../services/realTimeVisulization/zoneData/getTemplate"; +import { getTemplateData } from "../../../services/visulization/zone/getTemplate"; import { useDroppedObjectsStore } from "../../../store/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..36c5537 --- /dev/null +++ b/app/src/modules/visualization/visualization.tsx @@ -0,0 +1,22 @@ +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' + +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..ca70d45 100644 --- a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx +++ b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx @@ -13,8 +13,6 @@ 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"; diff --git a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx index 6acc994..c094109 100644 --- a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx +++ b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx @@ -7,7 +7,7 @@ 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 { get3dWidgetZoneData } from "../../../../services/visulization/zone/get3dWidgetData"; import { generateUniqueId } from "../../../../functions/generateUniqueId"; import ProductionCapacity from "./cards/ProductionCapacity"; import ReturnOfInvestment from "./cards/ReturnOfInvestment"; @@ -16,10 +16,6 @@ import Throughput from "./cards/Throughput"; import { useWidgetStore } from "../../../../store/useWidgetStore"; import useChartStore from "../../../../store/useChartStore"; - - - - type WidgetData = { id: string; type: string; diff --git a/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx b/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx index cfe29aa..1121e27 100644 --- a/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx +++ b/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx @@ -7,14 +7,14 @@ import { 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"; 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..66fdba2 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/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 100% rename from app/src/modules/visualization/functions/zoneCameraTarget.tsx rename to app/src/modules/visualization/zone/zoneCameraTarget.tsx 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/modules/builder/temp.md b/app/src/services/simulation/temp.md similarity index 100% rename from app/src/modules/builder/temp.md rename to app/src/services/simulation/temp.md diff --git a/app/src/services/temp.md b/app/src/services/temp.md deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/modules/simulation/temp.md b/app/src/services/visulization/temp.md similarity index 100% rename from app/src/modules/simulation/temp.md rename to app/src/services/visulization/temp.md 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/store.ts b/app/src/store/store.ts index 765712b..2165b82 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"; @@ -338,49 +337,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 +352,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 +369,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/useDroppedObjectsStore.ts b/app/src/store/useDroppedObjectsStore.ts index 0c03eec..ded9f0a 100644 --- a/app/src/store/useDroppedObjectsStore.ts +++ b/app/src/store/useDroppedObjectsStore.ts @@ -1,5 +1,5 @@ import { create } from "zustand"; -import { addingFloatingWidgets } from "../services/realTimeVisulization/zoneData/addFloatingWidgets"; +import { addingFloatingWidgets } from "../services/visulization/zone/addFloatingWidgets"; import { useSocketStore } from "./store"; import useChartStore from "./useChartStore"; @@ -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/useSimulationStore.ts b/app/src/store/useSimulationStore.ts index aaa1697..c1d76cd 100644 --- a/app/src/store/useSimulationStore.ts +++ b/app/src/store/useSimulationStore.ts @@ -142,7 +142,7 @@ type Store = { removeEventFromProduct: (productId: string, modelUuid: string) => void; updateEventInProduct: (productId: string, modelUuid: string, updates: Partial) => void; - // Point-level actions (for transfer, vehicle, etc.) + // Point-level actions addPointToEvent: (productId: string, modelUuid: string, point: TransferPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; removePointFromEvent: (productId: string, modelUuid: string, pointUuid: string) => void; updatePointInEvent: ( @@ -153,35 +153,25 @@ type Store = { ) => void; // Action-level actions - addActionToPoint: ( + addAction: ( productId: string, modelUuid: string, pointUuid: string, action: TransferPointSchema['actions'][0] | VehiclePointSchema['actions'][0] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['actions'][0] | StoragePointSchema['actions'][0] ) => void; - removeActionFromPoint: (productId: string, modelUuid: string, pointUuid: string, actionUuid: string) => void; - updateActionInPoint: ( - productId: string, - modelUuid: string, - pointUuid: string, + removeAction: (actionUuid: string) => void; + updateAction: ( actionUuid: string, updates: Partial ) => void; // Trigger-level actions - addTriggerToAction: ( - productId: string, - modelUuid: string, - pointUuid: string, + addTrigger: ( actionUuid: string, trigger: TriggerSchema ) => void; - removeTriggerFromAction: (productId: string, modelUuid: string, pointUuid: string, actionUuid: string, triggerUuid: string) => void; - updateTriggerInAction: ( - productId: string, - modelUuid: string, - pointUuid: string, - actionUuid: string, + removeTrigger: (triggerUuid: string) => void; + updateTrigger: ( triggerUuid: string, updates: Partial ) => void; @@ -190,6 +180,8 @@ type Store = { getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined; getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined; getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => TransferPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; + getActionByUuid: (actionUuid: string) => (TransferPointSchema['actions'][0] | VehiclePointSchema['actions'][0] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['actions'][0] | StoragePointSchema['actions'][0]) | undefined; + getTriggerByUuid: (triggerUuid: string) => TriggerSchema | undefined; }; export const useProductStore = create()( @@ -303,7 +295,7 @@ export const useProductStore = create()( }, // Action-level actions - addActionToPoint: (productId, modelUuid, pointUuid, action) => { + addAction: (productId, modelUuid, pointUuid, action) => { set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { @@ -320,115 +312,120 @@ export const useProductStore = create()( }); }, - removeActionFromPoint: (productId, modelUuid, pointUuid, actionUuid) => { + removeAction: (actionUuid) => { 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 TransferEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { - point.actions = point.actions.filter(a => a.actionUuid !== actionUuid); + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as TransferEventSchema).points) { + point.actions = point.actions.filter(a => a.actionUuid !== actionUuid); + } + } else if ('point' in event) { + (event as any).point.actions = (event as any).point.actions.filter((a: any) => a.actionUuid !== actionUuid); } - } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - (event as any).point.actions = (event as any).point.actions.filter((a: any) => a.actionUuid !== actionUuid); } } }); }, - updateActionInPoint: (productId, modelUuid, pointUuid, actionUuid, updates) => { + updateAction: (actionUuid, 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 TransferEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { - const action = point.actions.find(a => a.actionUuid === actionUuid); + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as TransferEventSchema).points) { + const action = point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + Object.assign(action, updates); + return; + } + } + } else if ('point' in event) { + const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); if (action) { Object.assign(action, updates); + return; } } - } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); - if (action) { - Object.assign(action, updates); - } } } }); }, // Trigger-level actions - addTriggerToAction: (productId, modelUuid, pointUuid, actionUuid, trigger) => { + addTrigger: (actionUuid, trigger) => { 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 TransferEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { - const action = point.actions.find(a => a.actionUuid === actionUuid); + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as TransferEventSchema).points) { + const action = point.actions.find(a => a.actionUuid === actionUuid); + if (action && 'triggers' in action) { + action.triggers.push(trigger); + return; + } + } + } else if ('point' in event) { + const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); if (action && 'triggers' in action) { action.triggers.push(trigger); + return; } } - } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); - if (action && 'triggers' in action) { - action.triggers.push(trigger); - } } } }); }, - removeTriggerFromAction: (productId, modelUuid, pointUuid, actionUuid, triggerUuid) => { + removeTrigger: (triggerUuid) => { 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 TransferEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { - const action = point.actions.find(a => a.actionUuid === actionUuid); - if (action && 'triggers' in action) { - action.triggers = action.triggers.filter(t => t.triggerUuid !== triggerUuid); + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as TransferEventSchema).points) { + for (const action of point.actions) { + if ('triggers' in action) { + action.triggers = action.triggers.filter(t => t.triggerUuid !== triggerUuid); + } + } } - } - } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); - if (action && 'triggers' in action) { - action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); - } - } - } - }); - }, - - updateTriggerInAction: (productId, modelUuid, pointUuid, actionUuid, triggerUuid, 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 TransferEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { - const action = point.actions.find(a => a.actionUuid === actionUuid); - if (action && 'triggers' in action) { - const trigger = action.triggers.find(t => t.triggerUuid === triggerUuid); - if (trigger) { - Object.assign(trigger, updates); + } else if ('point' in event) { + for (const action of (event as any).point.actions) { + if ('triggers' in action) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); } } } - } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); - if (action && 'triggers' in action) { - const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); - if (trigger) { - Object.assign(trigger, updates); + } + } + }); + }, + + 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 TransferEventSchema).points) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } + } + } + } else if ('point' in event) { + for (const action of (event as any).point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + Object.assign(trigger, updates); + return; + } + } } } } @@ -460,6 +457,50 @@ export const useProductStore = create()( } } return undefined; + }, + + getActionByUuid: (actionUuid) => { + const state = get(); + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as TransferEventSchema).points) { + const action = point.actions.find(a => a.actionUuid === actionUuid); + if (action) return action; + } + } else if ('point' in event) { + const action = (event as any).point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return action; + } + } + } + return undefined; + }, + + getTriggerByUuid: (triggerUuid) => { + const state = get(); + for (const product of state.products) { + for (const event of product.eventsData) { + if ('points' in event) { + for (const point of (event as TransferEventSchema).points) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } + } + } + } else if ('point' in event) { + for (const action of (event as any).point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) return trigger; + } + } + } + } + } + return undefined; } })) ); \ No newline at end of file diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index e21d52f..0e30be4 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -94,75 +94,6 @@ interface ArmBotEventsSchema { rotation: [number, number, number]; } -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 AssetEventSchema { modelUuid: string; modelName: string;