diff --git a/app/package-lock.json b/app/package-lock.json index 3d5aea4..f95a0c3 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -30,6 +30,7 @@ "glob": "^11.0.0", "gsap": "^3.12.5", "html2canvas": "^1.4.1", + "immer": "^10.1.1", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", @@ -43,6 +44,7 @@ "sass": "^1.78.0", "socket.io-client": "^4.8.1", "three": "^0.168.0", + "three-viewport-gizmo": "^2.2.0", "typescript": "^4.9.5", "web-vitals": "^2.1.4", "zustand": "^5.0.0-rc.2" @@ -2021,7 +2023,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2033,7 +2035,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4136,6 +4138,26 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -4247,25 +4269,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -9019,7 +9041,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -9896,7 +9918,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -12726,9 +12748,10 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -15259,7 +15282,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -17990,6 +18013,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react-dev-utils/node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/react-dev-utils/node_modules/loader-utils": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", @@ -20534,6 +20567,15 @@ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==" }, + "node_modules/three-viewport-gizmo": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/three-viewport-gizmo/-/three-viewport-gizmo-2.2.0.tgz", + "integrity": "sha512-Jo9Liur1rUmdKk75FZumLU/+hbF+RtJHi1qsKZpntjKlCYScK6tjbYoqvJ9M+IJphrlQJF5oReFW7Sambh0N4Q==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.162.0 <1.0.0" + } + }, "node_modules/throat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", @@ -20727,7 +20769,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20770,7 +20812,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "devOptional": true, "dependencies": { "acorn": "^8.11.0" }, @@ -20782,7 +20824,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21278,7 +21320,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22337,7 +22379,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/app/package.json b/app/package.json index 66158c0..18a8228 100644 --- a/app/package.json +++ b/app/package.json @@ -39,6 +39,7 @@ "sass": "^1.78.0", "socket.io-client": "^4.8.1", "three": "^0.168.0", + "three-viewport-gizmo": "^2.2.0", "typescript": "^4.9.5", "web-vitals": "^2.1.4", "zustand": "^5.0.0-rc.2" diff --git a/app/public/index.html b/app/public/index.html index d05ca9d..22359c2 100644 --- a/app/public/index.html +++ b/app/public/index.html @@ -1,24 +1,19 @@ - - - - - - - - - - - Dwinzo (beta) - - - -
-
- - - + + + \ No newline at end of file diff --git a/app/src/assets/image/aisleTypes/Arc.png b/app/src/assets/image/aisleTypes/Arc.png new file mode 100644 index 0000000..74e7286 Binary files /dev/null and b/app/src/assets/image/aisleTypes/Arc.png differ diff --git a/app/src/assets/image/aisleTypes/Arrow.png b/app/src/assets/image/aisleTypes/Arrow.png new file mode 100644 index 0000000..e749443 Binary files /dev/null and b/app/src/assets/image/aisleTypes/Arrow.png differ diff --git a/app/src/assets/image/aisleTypes/Arrows.png b/app/src/assets/image/aisleTypes/Arrows.png new file mode 100644 index 0000000..c025010 Binary files /dev/null and b/app/src/assets/image/aisleTypes/Arrows.png differ diff --git a/app/src/assets/image/aisleTypes/Circle.png b/app/src/assets/image/aisleTypes/Circle.png new file mode 100644 index 0000000..8be9929 Binary files /dev/null and b/app/src/assets/image/aisleTypes/Circle.png differ diff --git a/app/src/assets/image/aisleTypes/Dashed.png b/app/src/assets/image/aisleTypes/Dashed.png new file mode 100644 index 0000000..c7db464 Binary files /dev/null and b/app/src/assets/image/aisleTypes/Dashed.png differ diff --git a/app/src/assets/image/aisleTypes/Directional.png b/app/src/assets/image/aisleTypes/Directional.png new file mode 100644 index 0000000..be7d1f6 Binary files /dev/null and b/app/src/assets/image/aisleTypes/Directional.png differ diff --git a/app/src/assets/image/aisleTypes/Dotted.png b/app/src/assets/image/aisleTypes/Dotted.png new file mode 100644 index 0000000..743ed0c Binary files /dev/null and b/app/src/assets/image/aisleTypes/Dotted.png differ diff --git a/app/src/assets/image/aisleTypes/Solid.png b/app/src/assets/image/aisleTypes/Solid.png new file mode 100644 index 0000000..6bfb8df Binary files /dev/null and b/app/src/assets/image/aisleTypes/Solid.png differ diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index b259666..039a0a9 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -1279,3 +1279,59 @@ export const FinishEditIcon = () => { ); }; + +export const PerformanceIcon = () => { + return ( + + + + + + + + + + ); +}; + +export const GreenTickIcon = () => { + return ( + + + + + ); +}; diff --git a/app/src/components/layout/scenes/ComparisonScene.tsx b/app/src/components/layout/scenes/ComparisonScene.tsx new file mode 100644 index 0000000..d823d5b --- /dev/null +++ b/app/src/components/layout/scenes/ComparisonScene.tsx @@ -0,0 +1,48 @@ +import { useProductContext } from '../../../modules/simulation/products/productContext' +import RegularDropDown from '../../ui/inputs/RegularDropDown'; +import { useProductStore } from '../../../store/simulation/useProductStore'; +import { useSaveVersion } from '../../../store/builder/store'; +import useModuleStore from '../../../store/useModuleStore'; +import CompareLayOut from '../../ui/compareVersion/CompareLayOut'; +import ComparisonResult from '../../ui/compareVersion/ComparisonResult'; +import { useComparisonProduct } from '../../../store/simulation/useSimulationStore'; +import { usePlayButtonStore } from '../../../store/usePlayButtonStore'; + +function ComparisonScene() { + const { isPlaying } = usePlayButtonStore(); + const { products } = useProductStore(); + const { isVersionSaved } = useSaveVersion(); + const { activeModule } = useModuleStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { comparisonProduct, setComparisonProduct } = useComparisonProduct(); + + const handleSelectLayout = (option: string) => { + const product = products.find((product) => product.productName === option); + if (product) { + setComparisonProduct(product.productId, product.productName); + } + }; + return ( + <> + {isVersionSaved && activeModule === "simulation" && selectedProduct && ( + <> + {comparisonProduct && !isPlaying && +
+ l.productName)} // Pass layout names as options + onSelect={handleSelectLayout} + search={false} + /> +
+ } + + {false && } + + )} + + ) +} + +export default ComparisonScene; diff --git a/app/src/components/layout/scenes/ComparisonSceneProvider.tsx b/app/src/components/layout/scenes/ComparisonSceneProvider.tsx new file mode 100644 index 0000000..e47805c --- /dev/null +++ b/app/src/components/layout/scenes/ComparisonSceneProvider.tsx @@ -0,0 +1,12 @@ +import { ProductProvider } from '../../../modules/simulation/products/productContext' +import ComparisonScene from './ComparisonScene'; + +function ComparisonSceneProvider() { + return ( + + + + ) +} + +export default ComparisonSceneProvider \ No newline at end of file diff --git a/app/src/components/layout/scenes/MainScene.tsx b/app/src/components/layout/scenes/MainScene.tsx new file mode 100644 index 0000000..3101be5 --- /dev/null +++ b/app/src/components/layout/scenes/MainScene.tsx @@ -0,0 +1,113 @@ +import React from 'react' +import { useLoadingProgress, useSaveVersion, useSocketStore, useWidgetSubOption } from '../../../store/builder/store'; +import useModuleStore, { useThreeDStore } from '../../../store/useModuleStore'; +import { usePlayButtonStore } from '../../../store/usePlayButtonStore'; +import { useSelectedZoneStore } from '../../../store/visualization/useZoneStore'; +import { useFloatingWidget } from '../../../store/visualization/useDroppedObjectsStore'; +import { useSelectedUserStore } from '../../../store/collaboration/useCollabStore'; +import KeyPressListener from '../../../utils/shortcutkeys/handleShortcutKeys'; +import LoadingPage from '../../templates/LoadingPage'; +import ModuleToggle from '../../ui/ModuleToggle'; +import SideBarLeft from '../sidebarLeft/SideBarLeft'; +import SideBarRight from '../sidebarRight/SideBarRight'; +import RealTimeVisulization from '../../../modules/visualization/RealTimeVisulization'; +import MarketPlace from '../../../modules/market/MarketPlace'; +import Tools from '../../ui/Tools'; +import SimulationPlayer from '../../ui/simulation/simulationPlayer'; +import ControlsPlayer from '../controls/ControlsPlayer'; +import SelectFloorPlan from '../../temporary/SelectFloorPlan'; +import { createHandleDrop } from '../../../modules/visualization/functions/handleUiDrop'; +import Scene from '../../../modules/scene/scene'; +import { useMainProduct } from '../../../store/simulation/useSimulationStore'; +import { useProductContext } from '../../../modules/simulation/products/productContext'; +import { useProductStore } from '../../../store/simulation/useProductStore'; +import RegularDropDown from '../../ui/inputs/RegularDropDown'; + +function MainScene() { + const { products } = useProductStore(); + const { setMainProduct } = useMainProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { isVersionSaved } = useSaveVersion(); + const { activeModule } = useModuleStore(); + const { selectedUser } = useSelectedUserStore(); + const { loadingProgress } = useLoadingProgress(); + const { toggleThreeD } = useThreeDStore(); + const { isPlaying } = usePlayButtonStore(); + const { widgetSubOption } = useWidgetSubOption(); + const { visualizationSocket } = useSocketStore(); + const { selectedZone } = useSelectedZoneStore(); + const { setFloatingWidget } = useFloatingWidget(); + + const handleSelectLayout = (option: string) => { + const product = products.find((product) => product.productName === option); + if (product) { + setMainProduct(product.productId, product.productName); + } + }; + + return ( + <> + {!selectedUser && ( + <> + + {loadingProgress > 0 && } + {!isPlaying && ( + <> + {toggleThreeD && } + + + + )} + + {activeModule === "market" && } + {activeModule !== "market" && !isPlaying && !isVersionSaved && ( + + )} + {isPlaying && activeModule === "simulation" && } + {isPlaying && activeModule !== "simulation" && } + + {/* remove this later */} + {activeModule === "builder" && !toggleThreeD && } + + )} +
+ createHandleDrop({ + widgetSubOption, + visualizationSocket, + selectedZone, + setFloatingWidget, + event, + }) + } + onDragOver={(event) => event.preventDefault()} + > + +
+ + {selectedProduct && isVersionSaved && !isPlaying && ( +
+ l.productName)} // Pass layout names as options + onSelect={handleSelectLayout} + search={false} + /> +
+ )} + + ) +} + +export default MainScene \ No newline at end of file diff --git a/app/src/components/layout/scenes/MainSceneProvider.tsx b/app/src/components/layout/scenes/MainSceneProvider.tsx new file mode 100644 index 0000000..17971fa --- /dev/null +++ b/app/src/components/layout/scenes/MainSceneProvider.tsx @@ -0,0 +1,12 @@ +import { ProductProvider } from '../../../modules/simulation/products/productContext' +import MainScene from './MainScene' + +function MainSceneProvider() { + return ( + + + + ) +} + +export default MainSceneProvider \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 582b085..44199bc 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -16,6 +16,7 @@ import Simulations from "./simulation/Simulations"; import useVersionHistoryStore, { useSaveVersion, useSelectedFloorItem, + useToolMode, } from "../../../store/builder/store"; import { useSelectedEventData, @@ -26,10 +27,12 @@ import AsstePropertiies from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; import EventProperties from "./properties/eventProperties/EventProperties"; import VersionHistory from "./versionHisory/VersionHistory"; +import AisleProperties from "./properties/AisleProperties"; const SideBarRight: React.FC = () => { const { activeModule } = useModuleStore(); const { toggleUIRight } = useToggleStore(); + const { toolMode } = useToolMode(); const { subModule, setSubModule } = useSubModuleStore(); const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventData } = useSelectedEventData(); @@ -62,147 +65,145 @@ const SideBarRight: React.FC = () => { return (
- {toggleUIRight && !isVersionSaved && ( -
- {activeModule !== "simulation" && ( - + {toggleUIRight && ( + <> + {!isVersionSaved && ( +
+ {activeModule !== "simulation" && ( + + )} + {activeModule === "simulation" && ( + <> + + + + + )} +
)} - {activeModule === "simulation" && ( - <> - - - - + + {viewVersionHistory && ( +
+
+ +
+
)} -
+ + {/* process builder */} + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && ( +
+
+ {toolMode === "Aisle" ? ( + ) : ( + + )} +
+
+ )} + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + selectedFloorItem && ( +
+
+ +
+
+ )} + + {!viewVersionHistory && + subModule === "zoneProperties" && + (activeModule === "builder" || activeModule === "simulation") && ( +
+
+ +
+
+ )} + {/* simulation */} + {!isVersionSaved && + !viewVersionHistory && + activeModule === "simulation" && ( + <> + {subModule === "simulations" && ( +
+
+ +
+
+ )} + {subModule === "mechanics" && ( +
+
+ +
+
+ )} + {subModule === "analysis" && ( +
+
+ +
+
+ )} + + )} + {/* realtime visualization */} + {activeModule === "visualization" && } + )} - - {toggleUIRight && viewVersionHistory && ( -
-
- -
-
- )} - - {/* process builder */} - {toggleUIRight && - !viewVersionHistory && - subModule === "properties" && - activeModule !== "visualization" && - !selectedFloorItem && ( -
-
- -
-
- )} - {toggleUIRight && - !viewVersionHistory && - subModule === "properties" && - activeModule !== "visualization" && - selectedFloorItem && ( -
-
- -
-
- )} - - {toggleUIRight && - !viewVersionHistory && - subModule === "zoneProperties" && - (activeModule === "builder" || activeModule === "simulation") && ( -
-
- -
-
- )} - {/* simulation */} - {toggleUIRight && - !isVersionSaved && - !viewVersionHistory && - activeModule === "simulation" && ( - <> - {subModule === "simulations" && ( -
-
- -
-
- )} - {subModule === "mechanics" && ( -
-
- -
-
- )} - {subModule === "analysis" && ( -
-
- -
-
- )} - - )} - {/* realtime visualization */} - {toggleUIRight && activeModule === "visualization" && }
); }; diff --git a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx new file mode 100644 index 0000000..96ef288 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx @@ -0,0 +1,276 @@ +import React, { useMemo, useState } from "react"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; +import { ArrowIcon } from "../../../icons/ExportCommonIcons"; + +// image imports +import Arc from "../../../../assets/image/aisleTypes/Arc.png"; +import Arrow from "../../../../assets/image/aisleTypes/Arrow.png"; +import Arrows from "../../../../assets/image/aisleTypes/Arrows.png"; +import Circle from "../../../../assets/image/aisleTypes/Circle.png"; +import Dashed from "../../../../assets/image/aisleTypes/Dashed.png"; +import Directional from "../../../../assets/image/aisleTypes/Directional.png"; +import Dotted from "../../../../assets/image/aisleTypes/Dotted.png"; +import Solid from "../../../../assets/image/aisleTypes/Solid.png"; +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; + +interface TextureItem { + color: AisleColors; + id: string; + brief: string; + texture: string; +} + +const AisleProperties: React.FC = () => { + const [collapsePresets, setCollapsePresets] = useState(false); + const [collapseTexture, setCollapseTexture] = useState(true); + + const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength } = useBuilderStore(); + + const aisleTextureList: TextureItem[] = [ + { color: "yellow", id: "yellow1", brief: "pedestrian walkways", texture: "" }, + { color: "gray", id: "gray", brief: "basic", texture: "" }, + { color: "green", id: "green1", brief: "pedestrian walkways", texture: "" }, + { color: "orange", id: "orange", brief: "material flow", texture: "" }, + { color: "blue", id: "blue", brief: "vehicle paths", texture: "" }, + { color: "purple", id: "purple", brief: "material flow", texture: "" }, + { color: "red", id: "red", brief: "safety zone", texture: "" }, + { color: "bright green", id: "bright-green", brief: "safety zone", texture: "" }, + { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, + { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, + ]; + + const aisleTypes: { + name: string; + type: AisleTypes; + id: string; + thumbnail: string; + }[] = [ + { name: "Solid", type: "solid-aisle", id: "1", thumbnail: Solid }, + { name: "Dotted", type: "dotted-aisle", id: "2", thumbnail: Dotted }, + { name: "Dashed", type: "dashed-aisle", id: "3", thumbnail: Dashed }, + { name: "Arrow", type: "arrow-aisle", id: "4", thumbnail: Arrow }, + { name: "Continuous Arrows", type: "arrows-aisle", id: "5", thumbnail: Arrows }, + { name: "Directional", type: "junction-aisle", id: "6", thumbnail: Directional }, + { name: "Arc", type: "arc-aisle", id: "7", thumbnail: Arc }, + { name: "Circle", type: "circle-aisle", id: "8", thumbnail: Circle }, + ]; + + const handleAisleWidthChange = (value: string) => { + const width = parseFloat(value); + if (!isNaN(width)) { + setAisleWidth(width); + } + }; + + const handleDashLengthChange = (value: string) => { + const length = parseFloat(value); + if (!isNaN(length)) { + setDashLength(length); + } + }; + + const handleGapLengthChange = (value: string) => { + const length = parseFloat(value); + if (!isNaN(length)) { + setGapLength(length); + } + }; + + const handleDotRadiusChange = (value: string) => { + const radius = parseFloat(value); + if (!isNaN(radius)) { + setDotRadius(radius); + } + }; + + const handleAisleLengthChange = (value: string) => { + const length = parseFloat(value); + if (!isNaN(length)) { + setAisleLength(length); + } + }; + + const dashLengthValue = useMemo(() => { + return dashLength.toString(); + }, [aisleType, dashLength]); + + const dotRadiusValue = useMemo(() => { + return dotRadius.toString(); + }, [aisleType, dotRadius]); + + const gapLengthValue = useMemo(() => { + return gapLength.toString(); + }, [aisleType, gapLength]); + + const aisleWidthValue = useMemo(() => { + return aisleWidth.toString(); + }, [aisleType, aisleWidth]); + + const aisleLengthValue = useMemo(() => { + return aisleLength.toString(); + }, [aisleType, aisleLength]); + + const renderAdvancedProperties = () => { + switch (aisleType) { + case 'dashed-aisle': + return ( + <> + {aisleType && + <> + + + + } + + ); + case 'dotted-aisle': + return ( + <> + {aisleType && + <> + + + } + + ); + case 'arrows-aisle': + return ( + <> + {aisleType && + <> + + + } + + ); + default: + return null; + } + }; + + return ( +
+
Properties
+ + {/* Basic Properties */} +
+ {aisleType !== 'dotted-aisle' && + + } + {renderAdvancedProperties()} +
+ + {/* Presets */} +
+ + {!collapsePresets && ( +
+ {aisleTypes.map((val) => ( +
+ +
+ ))} +
+ )} +
+ + {/* Texture */} +
+ + + {collapseTexture && ( +
+ {aisleTextureList.map((val) => ( + + ))} +
+ )} +
+
+ ); +}; + +export default AisleProperties; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 85a7539..4223129 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -48,7 +48,7 @@ const AssetProperties: React.FC = () => { return (
{/* Name */} -
{selectedFloorItem.userData.name}
+
{selectedFloorItem.userData.modelName}
{objectPosition.x && objectPosition.z && { const { selectedEventData } = useSelectedEventData(); const { getEventByModelUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const [currentEventData, setCurrentEventData] = useState( null ); diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx index 632805b..52cd9db 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx @@ -8,10 +8,10 @@ import RenameInput from "../../../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../../../functions/handleResizePannel"; import { useSelectedAction, - useSelectedProduct, } from "../../../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; +import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; interface ActionsListProps { selectedPointData: any; @@ -33,7 +33,8 @@ const ActionsList: React.FC = ({ // store const { renameAction } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { selectedAction, setSelectedAction } = useSelectedAction(); const handleRenameAction = (newName: string) => { @@ -87,11 +88,10 @@ const ActionsList: React.FC = ({ selectedPointData?.actions?.map((action: any) => (
@@ -286,7 +287,7 @@ function RoboticArmMechanics() { {}} + onSelect={() => { }} disabled={true} /> diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 202daa7..1c27ad1 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -6,18 +6,21 @@ import StorageAction from "../actions/StorageAction"; import ActionsList from "../components/ActionsList"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; import { useProductStore } from "../../../../../../store/simulation/useProductStore"; -import { useStorageUnitStore } from "../../../../../../store/simulation/useStorageUnitStore"; -import { useSelectedAction, useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; +import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore"; import * as THREE from 'three'; +import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; +import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; function StorageMechanics() { + const { storageUnitStore } = useSceneContext(); const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default"); const [selectedPointData, setSelectedPointData] = useState(); const { selectedEventData } = useSelectedEventData(); const { getPointByUuid, updateAction } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { setSelectedAction, clearSelectedAction } = useSelectedAction(); - const { setCurrentMaterials, clearCurrentMaterials, updateCurrentLoad, getStorageUnitById } = useStorageUnitStore(); + const { setCurrentMaterials, clearCurrentMaterials, updateCurrentLoad, getStorageUnitById } = storageUnitStore(); const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 2894e33..786f38e 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -7,27 +7,24 @@ import { useSelectedAction, useSelectedEventData, useSelectedEventSphere, - useSelectedProduct, } from "../../../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import TravelAction from "../actions/TravelAction"; import ActionsList from "../components/ActionsList"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; -import { useVehicleStore } from "../../../../../../store/simulation/useVehicleStore"; +import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; +import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; function VehicleMechanics() { - const [activeOption, setActiveOption] = useState<"default" | "travel">( - "default" - ); - const [selectedPointData, setSelectedPointData] = useState< - VehiclePointSchema | undefined - >(); + const { vehicleStore } = useSceneContext(); + const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); + const [selectedPointData, setSelectedPointData] = useState(); const { selectedEventData } = useSelectedEventData(); - const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = - useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { setSelectedAction, clearSelectedAction } = useSelectedAction(); - const { getVehicleById } = useVehicleStore(); + const { getVehicleById } = vehicleStore(); const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; @@ -255,7 +252,7 @@ function VehicleMechanics() { defaultValue={"0.5"} max={10} activeOption="m/s" - onClick={() => {}} + onClick={() => { }} onChange={handleSpeedChange} />
@@ -294,14 +291,14 @@ function VehicleMechanics() { onChange: handleUnloadDurationChange, }} clearPoints={handleClearPoints} - // pickPoint={{ - // value: currentPickPoint, - // onChange: handlePickPointChange, - // }} - // unloadPoint={{ - // value: currentUnloadPoint, - // onChange: handleUnloadPointChange, - // }} + // pickPoint={{ + // value: currentPickPoint, + // onChange: handlePickPointChange, + // }} + // unloadPoint={{ + // value: currentUnloadPoint, + // onChange: handleUnloadPointChange, + // }} /> )} diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx index fb0b97a..d94b553 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -11,9 +11,9 @@ import { handleResize } from "../../../../../../functions/handleResizePannel"; import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import { useSelectedAction, - useSelectedProduct, } from "../../../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; +import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; type TriggerProps = { selectedPointData?: PointsScheme | undefined; @@ -22,7 +22,8 @@ type TriggerProps = { const Trigger = ({ selectedPointData, type }: TriggerProps) => { const [currentAction, setCurrentAction] = useState(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { getActionByUuid, getEventByModelUuid, @@ -60,10 +61,10 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => { ) { actionUuid = ( selectedPointData as - | ConveyorPointSchema - | VehiclePointSchema - | MachinePointSchema - | StoragePointSchema + | ConveyorPointSchema + | VehiclePointSchema + | MachinePointSchema + | StoragePointSchema ).action?.actionUuid; } else if ( type === "RoboticArm" && @@ -153,10 +154,10 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => { return [ ( model as - | VehicleEventSchema - | RoboticArmEventSchema - | MachineEventSchema - | StorageEventSchema + | VehicleEventSchema + | RoboticArmEventSchema + | MachineEventSchema + | StorageEventSchema ).point, ]; } @@ -416,11 +417,10 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => { {triggers.map((trigger) => (
setSelectedTrigger(trigger)} >
-
- -
+ {comparisonProduct && ( +
+ + + +
+ )} {width !== "0px" && - !selectedLayout && ( // Show only if no layout selected + !comparisonProduct && ( // Show only if no layout selected
@@ -142,7 +151,7 @@ const CompareLayOut: React.FC = ({ dummyLayouts }) => { {showLayoutDropdown && (
Layouts
- {}} /> + { }} />
{products.map((layout) => (
); diff --git a/app/src/components/ui/compareVersion/ComparisonResult.tsx b/app/src/components/ui/compareVersion/ComparisonResult.tsx new file mode 100644 index 0000000..85d1c6c --- /dev/null +++ b/app/src/components/ui/compareVersion/ComparisonResult.tsx @@ -0,0 +1,133 @@ +import React, { useMemo } from "react"; +import PerformanceResult from "./result-card/PerformanceResult"; +import EnergyUsage from "./result-card/EnergyUsage"; +import { Bar } from "react-chartjs-2"; + +const ComparisonResult = () => { + const defaultData = { + labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], + datasets: [ + { + label: "Dataset", + data: [12, 19, 3, 5, 2, 3], + backgroundColor: ["#6f42c1"], + borderColor: "#b392f0", + borderWidth: 1, + }, + ], + }; + + // Memoize Chart Options + const options = useMemo( + () => ({ + responsive: true, + maintainAspectRatio: false, + plugins: { + title: { + display: true, + }, + legend: { + display: false, + }, + }, + scales: { + x: { + display: false, // Hide x-axis + grid: { + display: false, + }, + }, + y: { + display: false, // Hide y-axis + grid: { + display: false, + }, + }, + }, + }), + [] + ); + return ( +
+
Performance Comparison
+
+ +
+

Throughput (units/hr)

+
+
+
Layout 1
+
500/ hr
+
+
+
Layout 2
+
550/ hr
+
+
+
+
+
+
+
Cycle Time
+
+
+
Layout 1
+
120 Sec
+
+ ↑19.6% +
+
+
+
Layout 2
+
110 Sec
+
+ ↑19.6%1.6% +
+
+
+
+
+
+
Overall Downtime
+
+
+
+
Total down time
+
(Simulation 1)
+
+
+
17
+
mins
+
+
+
+
+
+
+
+
+
+
+ +
+
Overall Scrap Rate
+
+
+
Layout 1
+
+ Total scrap produced by +
+
2.7 ton
+
+
+ +
+
+
+ +
+
+ ); +}; + +export default ComparisonResult; diff --git a/app/src/components/ui/compareVersion/result-card/EnergyUsage.tsx b/app/src/components/ui/compareVersion/result-card/EnergyUsage.tsx new file mode 100644 index 0000000..93e80ec --- /dev/null +++ b/app/src/components/ui/compareVersion/result-card/EnergyUsage.tsx @@ -0,0 +1,108 @@ +import React, { useMemo } from "react"; +import { Line } from "react-chartjs-2"; +import { + Chart as ChartJS, + LineElement, + PointElement, + CategoryScale, + LinearScale, + Tooltip, + Legend, +} from "chart.js"; + +ChartJS.register( + LineElement, + PointElement, + CategoryScale, + LinearScale, + Tooltip, + Legend +); + +const EnergyUsage = () => { + const data = { + labels: ["Mon", "Tue", "Wed", "Thu", "Fri"], + datasets: [ + { + label: "Simulation 1", + data: [400, 600, 450, 1000, 1000], + borderColor: "#6a0dad", + fill: false, + tension: 0.5, // More curved line + pointRadius: 0, // Remove point indicators + }, + { + label: "Simulation 2", + data: [300, 500, 700, 950, 1100], + borderColor: "#b19cd9", + fill: false, + tension: 0.5, + pointRadius: 0, + }, + ], + }; + + const options = useMemo( + () => ({ + responsive: true, + maintainAspectRatio: false, + plugins: { + title: { + display: true, + }, + legend: { + display: false, + }, + }, + scales: { + x: { + display: false, // Hide x-axis + grid: { + display: false, + }, + }, + y: { + display: false, // Hide y-axis + grid: { + display: false, + }, + }, + }, + }), + [] + ); + + return ( +
+
+

Energy Usage

+

+ 2500 kWh +

+
+ +
+
+
+
+
Simulation 1
+
98%
+
+
+
+
+
+
Simulation 2
+
97%
+
+
+
+ +
+ +
+
+ ); +}; + +export default EnergyUsage; diff --git a/app/src/components/ui/compareVersion/result-card/PerformanceResult.tsx b/app/src/components/ui/compareVersion/result-card/PerformanceResult.tsx new file mode 100644 index 0000000..f21d573 --- /dev/null +++ b/app/src/components/ui/compareVersion/result-card/PerformanceResult.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import { + GreenTickIcon, + PerformanceIcon, + TickIcon, +} from "../../../icons/ExportCommonIcons"; + +const PerformanceResult = () => { + return ( +
+
+
+ +
+
Performance result
+
+ +
+
+
+
+ Success rate{" "} + + + +
+
98%
+
+
Environmental impact
+
+ +
+
+
Waste generation
+
+
I
+
0.5%
+
+
+ +
+
Risk 
management
+
+
I
+
0.1%
+
+
+ +
+
+
I
+
0.5%
+
+
+
+
+ +
Simulation 1
+
+ ); +}; + +export default PerformanceResult; diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index 77c9c4b..ab5635e 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -2,7 +2,8 @@ import React, { useEffect, useState } from "react"; import List from "./List"; import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons"; import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect"; -import { useFloorItems, useZones } from "../../../store/builder/store"; +import { useZones } from "../../../store/builder/store"; +import { useAssetsStore } from "../../../store/builder/useAssetStore"; interface DropDownListProps { value?: string; // Value to display in the DropDownList @@ -50,7 +51,7 @@ const DropDownList: React.FC = ({ }; const [zoneDataList, setZoneDataList] = useState([]); - const { floorItems } = useFloorItems(); + const { assets } = useAssetsStore(); const isPointInsidePolygon = ( point: [number, number], @@ -80,7 +81,7 @@ const DropDownList: React.FC = ({ p[2], ]); - const assetsInZone = floorItems + const assetsInZone = assets .filter((item: any) => { const [x, , z] = item.position; return isPointInsidePolygon([x, z], polygon2D as [number, number][]); @@ -98,9 +99,9 @@ const DropDownList: React.FC = ({ assets: assetsInZone, }; }); - + setZoneDataList(updatedZoneList); - }, [zones, floorItems]); + }, [zones, assets]); return (
@@ -143,7 +144,7 @@ const DropDownList: React.FC = ({ value="Buildings" showKebabMenu={false} showAddIcon={false} - // items={zoneDataList} + // items={zoneDataList} /> = ({ items = [], remove }) => { const [expandedZones, setExpandedZones] = useState>( {} ); - const { setFloorItems } = useFloorItems(); + const { setName } = useAssetsStore(); useEffect(() => { useSelectedZoneStore.getState().setSelectedZone({ @@ -148,13 +148,8 @@ const List: React.FC = ({ items = [], remove }) => { newName ); // console.log("response: ", response); - setFloorItems((prevFloorItems: any[]) => - prevFloorItems.map((floorItems) => - floorItems.modelUuid === zoneAssetId.id - ? { ...floorItems, modelName: response.modelName } - : floorItems - ) - ); + + setName(zoneAssetId.id, response.modelName); } } const checkZoneNameDuplicate = (name: string) => { diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts deleted file mode 100644 index 096665a..0000000 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ /dev/null @@ -1,343 +0,0 @@ -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 * 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, - addEvent: (event: EventsSchema) => void, - renderDistance: number -): 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) < renderDistance) { - await new Promise(async (resolve) => { - - // Check Three.js Cache - const cachedModel = THREE.Cache.get(item.modelfileID!); - if (cachedModel) { - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - return; - } - - // Check IndexedDB - const indexedDBModel = await retrieveGLTF(item.modelfileID!); - if (indexedDBModel) { - 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, addEvent); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - }, - undefined, - (error) => { - echo.error(`[IndexedDB] Error loading ${item.modelName}:`); - URL.revokeObjectURL(blobUrl); - resolve(); - } - ); - return; - } - - // Fetch from Backend - 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, addEvent); - modelsLoaded++; - checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); - }, - undefined, - (error) => { - echo.error(`[Backend] Error loading ${item.modelName}:`); - resolve(); - } - ); - }); - } else { - // - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); - - 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, - addEvent: (event: EventsSchema) => void, -) { - const model = gltf.clone(); - model.uuid = item.modelUuid; - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; - 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); - - if (item.eventData) { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - eventData: item.eventData, - }, - ]); - - if (item.eventData.type === "Vehicle") { - const vehicleEvent: VehicleEventSchema = { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "vehicle", - speed: 1, - point: { - uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), - position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], - rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "travel", - unLoadDuration: 5, - loadCapacity: 1, - steeringAngle: 0, - pickUpPoint: null, - unLoadPoint: null, - triggers: [] - } - } - }; - addEvent(vehicleEvent); - } else if (item.eventData.type === "Conveyor") { - const ConveyorEvent: ConveyorEventSchema = { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "transfer", - speed: 1, - points: item.eventData.points?.map((point: any, index: number) => ({ - uuid: point.uuid || THREE.MathUtils.generateUUID(), - position: [point.position[0], point.position[1], point.position[2]], - rotation: [point.rotation[0], point.rotation[1], point.rotation[2]], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action 1`, - actionType: 'default', - material: 'Default material', - delay: 0, - spawnInterval: 5, - spawnCount: 1, - triggers: [] - } - })) || [], - }; - addEvent(ConveyorEvent); - } else if (item.eventData.type === "StaticMachine") { - const machineEvent: MachineEventSchema = { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "machine", - point: { - uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), - position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], - rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "process", - processTime: 10, - swapMaterial: "material-id", - triggers: [] - } - } - }; - addEvent(machineEvent); - } else if (item.eventData.type === "ArmBot") { - const roboticArmEvent: RoboticArmEventSchema = { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - state: "idle", - type: "roboticArm", - speed: 1, - point: { - uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), - position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], - rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], - actions: [ - { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "pickAndPlace", - process: { - startPoint: null, - endPoint: null - }, - triggers: [] - } - ] - } - }; - addEvent(roboticArmEvent); - } else if (item.eventData.type === 'Storage') { - const storageEvent: StorageEventSchema = { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", - type: "storageUnit", - point: { - uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), - position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], - rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "store", - storageCapacity: 10, - triggers: [] - } - } - }; - addEvent(storageEvent); - } - } else { - setFloorItems((prevItems) => [ - ...(prevItems || []), - { - modelUuid: item.modelUuid, - modelName: item.modelName, - position: item.position, - rotation: item.rotation, - modelfileID: item.modelfileID, - isLocked: item.isLocked, - isVisible: item.isVisible, - }, - ]); - } - - 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) { - echo.success("Models Loaded!"); - dracoLoader.dispose(); - } - resolve(); -} - -export default loadInitialFloorItems; \ No newline at end of file diff --git a/app/src/modules/builder/IntialLoad/loadInitialPoint.ts b/app/src/modules/builder/IntialLoad/loadInitialPoint.ts index 7dfdf1d..91db577 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialPoint.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialPoint.ts @@ -31,7 +31,7 @@ function loadInitialPoint( 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 + uOuterColor: { 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: ` @@ -44,7 +44,7 @@ function loadInitialPoint( `, fragmentShader: ` varying vec2 vUv; - uniform vec3 uColor; + uniform vec3 uOuterColor; uniform vec3 uInnerColor; void main() { @@ -54,7 +54,7 @@ function loadInitialPoint( 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 + gl_FragColor = vec4(uOuterColor, 1.0); // Blue border } } `, diff --git a/app/src/modules/builder/aisle/Instances/aisleInstances.tsx b/app/src/modules/builder/aisle/Instances/aisleInstances.tsx new file mode 100644 index 0000000..8964434 --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/aisleInstances.tsx @@ -0,0 +1,83 @@ +import React, { useEffect, useMemo } from 'react'; +import { useAisleStore } from '../../../../store/builder/useAisleStore'; +import { useToggleView } from '../../../../store/builder/store'; +import AisleInstance from './instance/aisleInstance'; +import Point from '../../point/point'; +import { Html } from '@react-three/drei'; +import { Vector3 } from 'three'; + +function AisleInstances() { + const { aisles } = useAisleStore(); + const { toggleView } = useToggleView(); + + const allPoints = useMemo(() => { + const points: Point[] = []; + const seenUuids = new Set(); + + aisles.forEach(aisle => { + aisle.points.forEach(point => { + if (!seenUuids.has(point.uuid)) { + seenUuids.add(point.uuid); + points.push(point); + } + }); + }); + + return points; + }, [aisles]); + + useEffect(() => { + console.log('aisles: ', aisles); + }, [aisles]); + + return ( + <> + {toggleView && + + {allPoints.map((point) => ( + + ))} + + } + + + {aisles.map((aisle) => { + const textPosition = new Vector3().addVectors(new Vector3(...aisle.points[0].position), new Vector3(...aisle.points[1].position)).divideScalar(2); + const distance = new Vector3(...aisle.points[0].position).distanceTo(new Vector3(...aisle.points[1].position)); + + return ( + < React.Fragment key={aisle.uuid}> + + + {toggleView && + +
+ {distance.toFixed(2)} m +
+ + } + + + ) + })} +
+ + ) +} + +export default AisleInstances \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx new file mode 100644 index 0000000..9afd3d6 --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleInstance.tsx @@ -0,0 +1,29 @@ +import ArrowsAisle from './aisleTypes/arrowsAisle'; +import DashedAisle from './aisleTypes/dashedAisle'; +import DottedAisle from './aisleTypes/dottedAisle'; +import SolidAisle from './aisleTypes/solidAisle'; + +function AisleInstance({ aisle }: { readonly aisle: Aisle }) { + + return ( + <> + {aisle.type.aisleType === 'solid-aisle' && ( + + )} + + {aisle.type.aisleType === 'dashed-aisle' && ( + + )} + + {aisle.type.aisleType === 'dotted-aisle' && ( + + )} + + {aisle.type.aisleType === 'arrows-aisle' && ( + + )} + + ); +} + +export default AisleInstance; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx new file mode 100644 index 0000000..28fe74a --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx @@ -0,0 +1,93 @@ +import * as THREE from 'three'; +import { useMemo, useRef } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; +import { useToolMode } from '../../../../../../store/builder/store'; +import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore'; + +function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) { + const aisleRef = useRef(null); + const { toolMode } = useToolMode(); + const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + + const arrows = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.1; + const arrowLength = aisle.type.aisleLength || 0.6; + const spacing = aisle.type.gapLength || 0.6; + + const direction = new THREE.Vector3().subVectors(end, start); + const length = direction.length(); + direction.normalize(); + + const count = Math.floor((length + spacing) / (arrowLength + spacing)); + + const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = []; + + for (let i = 0; i < count; i++) { + const initialOffset = arrowLength; + const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing)); + + const shape = new THREE.Shape(); + const w = width * 0.8; + const h = arrowLength; + + shape.moveTo(0, 0); + shape.lineTo(w, h * 0.6); + shape.lineTo(w * 0.4, h * 0.6); + shape.lineTo(w * 0.4, h); + shape.lineTo(-w * 0.4, h); + shape.lineTo(-w * 0.4, h * 0.6); + shape.lineTo(-w, h * 0.6); + shape.lineTo(0, 0); + + const angle = Math.atan2(direction.x, direction.z) + Math.PI; + + arrowShapes.push({ shape, position: center, rotationY: angle }); + } + + return arrowShapes; + }, [aisle]); + + const handleClick = () => { + if (toolMode === 'move' && !hoveredPoint) { + setSelectedAisle(aisleRef.current); + } + } + + if (arrows.length === 0) return null; + + return ( + { + setSelectedAisle(null); + }} + > + {arrows.map(({ shape, position, rotationY }, index) => ( + + + + + + ))} + + ); +} + +export default ArrowsAisle; diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx new file mode 100644 index 0000000..5a8c028 --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx @@ -0,0 +1,90 @@ +import * as THREE from 'three'; +import { useMemo, useRef } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; +import { useToolMode } from '../../../../../../store/builder/store'; +import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore'; + +function DashedAisle({ aisle }: { readonly aisle: Aisle }) { + const aisleRef = useRef(null); + const { toolMode } = useToolMode(); + const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + + const shapes = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.1; + const dashLength = aisle.type.dashLength || 0.5; + const gapLength = aisle.type.gapLength || 0.3; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const totalLength = new THREE.Vector3().subVectors(end, start).length(); + const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength)); + + const shapes = []; + const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); + + for (let i = 0; i < segmentCount; i++) { + const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength)); + const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength); + + const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + shapes.push(shape); + } + + return shapes; + }, [aisle]); + + const handleClick = () => { + if (toolMode === 'move' && !hoveredPoint) { + setSelectedAisle(aisleRef.current); + } + } + + if (shapes.length === 0) return null; + + return ( + { + setSelectedAisle(null); + }} + > + {shapes.map((shape, index) => ( + + + + ))} + + ); +} + +export default DashedAisle; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx new file mode 100644 index 0000000..15a1728 --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx @@ -0,0 +1,77 @@ +import * as THREE from 'three'; +import { useMemo, useRef } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; +import { useToolMode } from '../../../../../../store/builder/store'; +import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore'; + +function DottedAisle({ aisle }: { readonly aisle: Aisle }) { + const aisleRef = useRef(null); + const { toolMode } = useToolMode(); + const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + + const shapes = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.dotRadius || 0.1; + const dotSpacing = aisle.type.gapLength || 0.5; + const dotRadius = width * 0.6; + + const totalLength = new THREE.Vector3().subVectors(end, start).length(); + const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing); + + const shapes = []; + const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); + + for (let i = 0; i < dotCount; i++) { + const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2); + + const shape = new THREE.Shape(); + shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false); + + shapes.push(shape); + } + + return shapes; + }, [aisle]); + + const handleClick = () => { + if (toolMode === 'move' && !hoveredPoint) { + setSelectedAisle(aisleRef.current); + } + } + + if (shapes.length === 0) return null; + + return ( + { + setSelectedAisle(null); + }} + > + {shapes.map((shape, index) => ( + + + + ))} + + ); +} + +export default DottedAisle; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx new file mode 100644 index 0000000..af4b772 --- /dev/null +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx @@ -0,0 +1,72 @@ +import * as THREE from 'three'; +import { useMemo, useRef } from 'react'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../../types/world/worldConstants'; +import { useToolMode } from '../../../../../../store/builder/store'; +import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore'; + +function SolidAisle({ aisle }: { readonly aisle: Aisle }) { + const aisleRef = useRef(null); + const { toolMode } = useToolMode(); + const { setSelectedAisle, hoveredPoint } = useBuilderStore(); + + const shape = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.1; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + return shape; + }, [aisle]); + + const handleClick = () => { + if (toolMode === 'move' && !hoveredPoint) { + setSelectedAisle(aisleRef.current); + } + } + + if (!shape) return null; + + return ( + { + setSelectedAisle(null); + }} + > + + + + + ); +} + +export default SolidAisle; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx new file mode 100644 index 0000000..0b14967 --- /dev/null +++ b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx @@ -0,0 +1,244 @@ +import * as THREE from 'three' +import { useEffect, useMemo, useState } from 'react' +import { useThree } from '@react-three/fiber'; +import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; +import { useAisleStore } from '../../../../store/builder/useAisleStore'; +import ReferenceAisle from './referenceAisle'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import ReferencePoint from '../../point/referencePoint'; + +function AisleCreator() { + const { scene, camera, raycaster, gl, pointer } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); + const { toggleView } = useToggleView(); + const { toolMode } = useToolMode(); + const { activeLayer } = useActiveLayer(); + const { socket } = useSocketStore(); + const { addAisle, getAislePointById } = useAisleStore(); + + const [tempPoints, setTempPoints] = useState([]); + const [isCreating, setIsCreating] = useState(false); + const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint } = useBuilderStore(); + + // useEffect(() => { + // if (tempPoints.length > 0) { + // setTempPoints([]); + // setIsCreating(false); + // } + // }, [aisleType]); + + useEffect(() => { + const canvasElement = gl.domElement; + + let drag = false; + let isLeftMouseDown = false; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = true; + drag = false; + } + }; + + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = false; + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag = true; + } + }; + + const onMouseClick = () => { + if (drag || !toggleView) return; + + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + let position = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (!position) return; + + const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point'); + + const newPoint: Point = { + uuid: THREE.MathUtils.generateUUID(), + pointType: 'Aisle', + position: [position.x, position.y, position.z], + layer: activeLayer + }; + + if (snappedPosition && snappedPoint) { + newPoint.uuid = snappedPoint.uuid; + newPoint.position = snappedPosition; + newPoint.layer = snappedPoint.layer; + } + + if (snappedPosition && !snappedPoint) { + newPoint.position = snappedPosition; + } + + if (intersects && !snappedPoint) { + const point = getAislePointById(intersects.object.uuid); + if (point) { + newPoint.uuid = point.uuid; + newPoint.position = point.position; + newPoint.layer = point.layer; + } + } + + if (aisleType === 'solid-aisle') { + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + aisleType: 'solid-aisle', + aisleColor: aisleColor, + aisleWidth: aisleWidth + } + }; + addAisle(aisle); + setTempPoints([newPoint]); + } + } else if (aisleType === 'dashed-aisle') { + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + aisleType: 'dashed-aisle', + aisleColor: aisleColor, + aisleWidth: aisleWidth, + dashLength: dashLength, + gapLength: gapLength + } + }; + addAisle(aisle); + setTempPoints([newPoint]); + } + } else if (aisleType === 'stripped-aisle') { + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + aisleType: 'stripped-aisle', + aisleColor: aisleColor, + aisleWidth: aisleWidth + } + }; + addAisle(aisle); + setTempPoints([newPoint]); + } + } else if (aisleType === 'dotted-aisle') { + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + aisleType: 'dotted-aisle', + aisleColor: aisleColor, + dotRadius: dotRadius, + gapLength: gapLength + } + }; + addAisle(aisle); + setTempPoints([newPoint]); + } + } else if (aisleType === 'arrow-aisle') { + console.log('Creating arrow-aisle'); + } else if (aisleType === 'arrows-aisle') { + + if (tempPoints.length === 0) { + setTempPoints([newPoint]); + setIsCreating(true); + } else { + const aisle: Aisle = { + uuid: THREE.MathUtils.generateUUID(), + points: [tempPoints[0], newPoint], + type: { + aisleType: 'arrows-aisle', + aisleColor: aisleColor, + aisleWidth: aisleWidth, + aisleLength: aisleLength, + gapLength: gapLength + } + }; + addAisle(aisle); + setTempPoints([newPoint]); + } + } else if (aisleType === 'arc-aisle') { + console.log('Creating arc-aisle'); + } else if (aisleType === 'circle-aisle') { + console.log('Creating circle-aisle'); + } else if (aisleType === 'junction-aisle') { + console.log('Creating junction-aisle'); + } + }; + + const onContext = (event: any) => { + event.preventDefault(); + if (isCreating) { + setTempPoints([]); + setIsCreating(false); + } + }; + + if (toolMode === "Aisle" && toggleView) { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("click", onMouseClick); + canvasElement.addEventListener("contextmenu", onContext); + } else { + setTempPoints([]); + setIsCreating(false); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("click", onMouseClick); + canvasElement.removeEventListener("contextmenu", onContext); + }; + }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint]); + + return ( + <> + {toggleView && + <> + + {tempPoints.map((point) => ( + + ))} + + + {tempPoints.length > 0 && + + } + + } + + ); +} + +export default AisleCreator; \ No newline at end of file diff --git a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx new file mode 100644 index 0000000..a4c331e --- /dev/null +++ b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx @@ -0,0 +1,448 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import * as THREE from 'three'; +import { useFrame, useThree } from '@react-three/fiber'; +import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store'; +import * as Constants from '../../../../types/world/worldConstants'; +import { Extrude, Html } from '@react-three/drei'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping'; +import { usePointSnapping } from '../../point/helpers/usePointSnapping'; + +interface ReferenceAisleProps { + tempPoints: Point[]; +} + +function ReferenceAisle({ tempPoints }: Readonly) { + const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setSnappedPosition, setSnappedPoint } = useBuilderStore(); + const { pointer, raycaster, camera } = useThree(); + const { toolMode } = useToolMode(); + const { toggleView } = useToggleView(); + const { activeLayer } = useActiveLayer(); + const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + const finalPosition = useRef<[number, number, number] | null>(null); + + const [tempAisle, setTempAisle] = useState(null); + const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position); + + // Calculate directional snap based on current and previous points + const directionalSnap = useDirectionalSnapping( + currentPosition, + tempPoints[0]?.position || null + ); + const { checkSnapForAisle } = usePointSnapping({ uuid: 'temp-aisle', pointType: 'Aisle', position: directionalSnap.position || [0, 0, 0] }); + + useFrame(() => { + if (toolMode === "Aisle" && toggleView && tempPoints.length === 1) { + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + raycaster.ray.intersectPlane(plane, intersectionPoint); + + setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); + + if (intersectionPoint) { + + const snapped = checkSnapForAisle([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); + + if (snapped.isSnapped && snapped.snappedPoint) { + finalPosition.current = snapped.position; + setSnappedPosition(snapped.position); + setSnappedPoint(snapped.snappedPoint); + } else if (directionalSnap.isSnapped) { + finalPosition.current = directionalSnap.position; + setSnappedPosition(directionalSnap.position); + setSnappedPoint(null); + } else { + finalPosition.current = [intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]; + setSnappedPosition(null); + setSnappedPoint(null); + } + + if (!finalPosition.current) return; + + if (aisleType === 'solid-aisle' || aisleType === 'stripped-aisle') { + setTempAisle({ + uuid: 'temp-aisle', + points: [ + tempPoints[0], + { + uuid: 'temp-point', + pointType: 'Aisle', + position: finalPosition.current, + layer: activeLayer + } + ], + type: { + aisleType: aisleType, + aisleColor: aisleColor, + aisleWidth: aisleWidth + } + }); + } else if (aisleType === 'dashed-aisle') { + setTempAisle({ + uuid: 'temp-aisle', + points: [ + tempPoints[0], + { + uuid: 'temp-point', + pointType: 'Aisle', + position: finalPosition.current, + layer: activeLayer + } + ], + type: { + aisleType: aisleType, + aisleColor: aisleColor, + aisleWidth: aisleWidth, + dashLength: dashLength, + gapLength: gapLength + } + }); + } else if (aisleType === 'dotted-aisle') { + setTempAisle({ + uuid: 'temp-aisle', + points: [ + tempPoints[0], + { + uuid: 'temp-point', + pointType: 'Aisle', + position: finalPosition.current, + layer: activeLayer + } + ], + type: { + aisleType: aisleType, + aisleColor: aisleColor, + dotRadius: dotRadius, + gapLength: gapLength + } + }); + } else if (aisleType === 'arrow-aisle') { + console.log(); + } else if (aisleType === 'arrows-aisle') { + setTempAisle({ + uuid: 'temp-aisle', + points: [ + tempPoints[0], + { + uuid: 'temp-point', + pointType: 'Aisle', + position: finalPosition.current, + layer: activeLayer + } + ], + type: { + aisleType: aisleType, + aisleColor: aisleColor, + aisleWidth: aisleWidth, + aisleLength: aisleLength, + gapLength: gapLength + } + }); + } else if (aisleType === 'arc-aisle') { + console.log(); + } else if (aisleType === 'circle-aisle') { + console.log(); + } else if (aisleType === 'junction-aisle') { + console.log(); + } + } + } else if (tempAisle !== null) { + setTempAisle(null); + } + }); + + useEffect(() => { + setTempAisle(null); + }, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor]); + + if (!tempAisle) return null; + + const renderAisle = () => { + switch (aisleType) { + case 'solid-aisle': + return ; + case 'dashed-aisle': + return ; + case 'dotted-aisle': + return ; + case 'arrows-aisle': + return + default: + return null; + } + }; + + const textPosition = new THREE.Vector3().addVectors(new THREE.Vector3(...tempAisle.points[0].position), new THREE.Vector3(...tempAisle.points[1].position)).divideScalar(2); + const distance = new THREE.Vector3(...tempAisle.points[0].position).distanceTo(new THREE.Vector3(...tempAisle.points[1].position)); + + + const rendertext = () => { + return ( + <> + {toggleView && + +
+ {distance.toFixed(2)} m +
+ + } + + ) + } + + return ( + + {renderAisle()} + {rendertext()} + + ); +} + +export default ReferenceAisle; + +function SolidAisle({ aisle }: { readonly aisle: Aisle }) { + const shape = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.1; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const leftStart = new THREE.Vector3().copy(start).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(start).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(end).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(end).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + return shape; + }, [aisle]); + + if (!shape) return null; + + return ( + + + + + + ); +} + +function DashedAisle({ aisle }: { readonly aisle: Aisle }) { + const shapes = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.1; + const dashLength = aisle.type.dashLength || 0.5; + const gapLength = aisle.type.gapLength || 0.3; + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const perp = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const totalLength = new THREE.Vector3().subVectors(end, start).length(); + const segmentCount = Math.floor((totalLength + gapLength) / (dashLength + gapLength)); + + const shapes = []; + const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); + + for (let i = 0; i < segmentCount; i++) { + const segmentStart = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * (dashLength + gapLength)); + const segmentEnd = new THREE.Vector3().copy(segmentStart).addScaledVector(directionNormalized, dashLength); + + const leftStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, width / 2); + const rightStart = new THREE.Vector3().copy(segmentStart).addScaledVector(perp, -width / 2); + const leftEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, width / 2); + const rightEnd = new THREE.Vector3().copy(segmentEnd).addScaledVector(perp, -width / 2); + + const shape = new THREE.Shape(); + shape.moveTo(leftStart.x, leftStart.z); + shape.lineTo(leftEnd.x, leftEnd.z); + shape.lineTo(rightEnd.x, rightEnd.z); + shape.lineTo(rightStart.x, rightStart.z); + shape.closePath(); + + shapes.push(shape); + } + + return shapes; + }, [aisle]); + + if (shapes.length === 0) return null; + + return ( + + {shapes.map((shape, index) => ( + + + + ))} + + ); +} + +function DottedAisle({ aisle }: { readonly aisle: Aisle }) { + const shapes = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.dotRadius || 0.1; + const dotSpacing = aisle.type.gapLength || 0.5; + const dotRadius = width * 0.6; + + const totalLength = new THREE.Vector3().subVectors(end, start).length(); + const dotCount = Math.floor((totalLength + (dotSpacing / 2)) / dotSpacing); + + const shapes = []; + const directionNormalized = new THREE.Vector3().subVectors(end, start).normalize(); + + for (let i = 0; i < dotCount; i++) { + const dotCenter = new THREE.Vector3().copy(start).addScaledVector(directionNormalized, i * dotSpacing + dotSpacing / 2); + + const shape = new THREE.Shape(); + shape.absarc(dotCenter.x, dotCenter.z, dotRadius, 0, Math.PI * 2, false); + + shapes.push(shape); + } + + return shapes; + }, [aisle]); + + if (shapes.length === 0) return null; + + return ( + + {shapes.map((shape, index) => ( + + + + ))} + + ); +} + +function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) { + const arrows = useMemo(() => { + if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrows-aisle') return []; + + const start = new THREE.Vector3(...aisle.points[0].position); + const end = new THREE.Vector3(...aisle.points[1].position); + const width = aisle.type.aisleWidth || 0.1; + const arrowLength = aisle.type.aisleLength || 0.6; + const spacing = aisle.type.gapLength || 0.6; + + const direction = new THREE.Vector3().subVectors(end, start); + const length = direction.length(); + direction.normalize(); + + const count = Math.floor((length + spacing) / (arrowLength + spacing)); + + const arrowShapes: { shape: THREE.Shape; position: THREE.Vector3; rotationY: number }[] = []; + + for (let i = 0; i < count; i++) { + const initialOffset = arrowLength; + const center = new THREE.Vector3().copy(start).addScaledVector(direction, initialOffset + i * (arrowLength + spacing)); + + const shape = new THREE.Shape(); + const w = width * 0.8; + const h = arrowLength; + + shape.moveTo(0, 0); + shape.lineTo(w, h * 0.6); + shape.lineTo(w * 0.4, h * 0.6); + shape.lineTo(w * 0.4, h); + shape.lineTo(-w * 0.4, h); + shape.lineTo(-w * 0.4, h * 0.6); + shape.lineTo(-w, h * 0.6); + shape.lineTo(0, 0); + + const angle = Math.atan2(direction.x, direction.z) + Math.PI; + + arrowShapes.push({ shape, position: center, rotationY: angle }); + } + + return arrowShapes; + }, [aisle]); + + if (arrows.length === 0) return null; + + return ( + + {arrows.map(({ shape, position, rotationY }, index) => ( + + + + + + ))} + + ); +} diff --git a/app/src/modules/builder/aisle/aislesGroup.tsx b/app/src/modules/builder/aisle/aislesGroup.tsx new file mode 100644 index 0000000..f163bed --- /dev/null +++ b/app/src/modules/builder/aisle/aislesGroup.tsx @@ -0,0 +1,20 @@ +import AisleCreator from './aisleCreator/aisleCreator' +import AisleInstances from './Instances/aisleInstances' + +function AislesGroup() { + + return ( + + <> + + + + + + + + + ) +} + +export default AislesGroup \ No newline at end of file diff --git a/app/src/modules/builder/asset/assetsGroup.tsx b/app/src/modules/builder/asset/assetsGroup.tsx new file mode 100644 index 0000000..04fd32b --- /dev/null +++ b/app/src/modules/builder/asset/assetsGroup.tsx @@ -0,0 +1,297 @@ +import * as THREE from "three" +import { useEffect } from 'react' +import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; +import { useLoadingProgress, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store'; +import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; +import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; +import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes"; +import { useAssetsStore } from "../../../store/builder/useAssetStore"; +import { useEventsStore } from "../../../store/simulation/useEventsStore"; +import Models from "./models/models"; +import useModuleStore from "../../../store/useModuleStore"; +import { useThree } from "@react-three/fiber"; +import { CameraControls } from "@react-three/drei"; +import addAssetModel from "./functions/addAssetModel"; + +const gltfLoaderWorker = new Worker( + new URL( + "../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", + import.meta.url + ) +); + +function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) { + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const { controls, gl, pointer, camera, raycaster } = useThree(); + const { setLoadingProgress } = useLoadingProgress(); + const { setAssets, addAsset } = useAssetsStore(); + const { addEvent } = useEventsStore(); + const { setSelectedFloorItem } = useSelectedFloorItem(); + const { selectedItem, setSelectedItem } = useSelectedItem(); + + 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); + + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + let totalAssets = 0; + let loadedAssets = 0; + + const updateLoadingProgress = (progress: number) => { + if (progress < 100) { + setLoadingProgress(progress); + } else if (progress === 100) { + setTimeout(() => { + setLoadingProgress(100); + setTimeout(() => { + setLoadingProgress(0); + }, 1500); + }, 1000); + } + }; + + getFloorAssets(organization).then((data) => { + if (data.length > 0) { + const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID)); + totalAssets = uniqueItems.length; + if (totalAssets === 0) { + updateLoadingProgress(100); + return; + } + gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); + } else { + gltfLoaderWorker.postMessage({ floorItems: [] }); + updateLoadingProgress(100); + } + }); + + gltfLoaderWorker.onmessage = async (event) => { + if (event.data.message === "gltfLoaded" && event.data.modelBlob) { + const blobUrl = URL.createObjectURL(event.data.modelBlob); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(event.data.modelID, gltf); + + loadedAssets++; + const progress = Math.round((loadedAssets / totalAssets) * 100); + updateLoadingProgress(progress); + + if (loadedAssets === totalAssets) { + const assets: Asset[] = []; + getFloorAssets(organization).then((data: FloorItems) => { + data.forEach((item) => { + if (item.eventData) { + assets.push({ + modelUuid: item.modelUuid, + modelName: item.modelName, + assetId: item.modelfileID, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + isLocked: item.isLocked, + isCollidable: false, + isVisible: item.isVisible, + opacity: 1, + eventData: item.eventData + }) + + if (item.eventData.type === "Vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "travel", + unLoadDuration: 5, + loadCapacity: 1, + steeringAngle: 0, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } + } + }; + addEvent(vehicleEvent); + } else if (item.eventData.type === "Conveyor") { + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "transfer", + speed: 1, + points: item.eventData.points?.map((point: any, index: number) => ({ + uuid: point.uuid || THREE.MathUtils.generateUUID(), + position: [point.position[0], point.position[1], point.position[2]], + rotation: [point.rotation[0], point.rotation[1], point.rotation[2]], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action 1`, + actionType: 'default', + material: 'Default material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] + } + })) || [], + }; + addEvent(ConveyorEvent); + } else if (item.eventData.type === "StaticMachine") { + const machineEvent: MachineEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "process", + processTime: 10, + swapMaterial: "material-id", + triggers: [] + } + } + }; + addEvent(machineEvent); + } else if (item.eventData.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "pickAndPlace", + process: { + startPoint: null, + endPoint: null + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + } else if (item.eventData.type === 'Storage') { + const storageEvent: StorageEventSchema = { + modelUuid: item.modelUuid, + modelName: item.modelName, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle", + type: "storageUnit", + point: { + uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(), + position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0], + rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "store", + storageCapacity: 10, + triggers: [] + } + } + }; + addEvent(storageEvent); + } + } else { + assets.push({ + modelUuid: item.modelUuid, + modelName: item.modelName, + assetId: item.modelfileID, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + isLocked: item.isLocked, + isCollidable: false, + isVisible: item.isVisible, + opacity: 1, + }) + } + }) + setAssets(assets); + }) + updateLoadingProgress(100); + } + }); + } + }; + }, []); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onDrop = (event: any) => { + if (!event.dataTransfer?.files[0]) return; + + if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { + + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + + addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane); + } + }; + + const onDragOver = (event: any) => { + event.preventDefault(); + }; + + + if (activeModule === "builder") { + canvasElement.addEventListener("drop", onDrop); + canvasElement.addEventListener("dragover", onDragOver); + } else { + if ((controls as CameraControls)) { + const target = (controls as CameraControls).getTarget(new THREE.Vector3()); + (controls as CameraControls).setTarget(target.x, 0, target.z, true); + setSelectedFloorItem(null); + } + } + + return () => { + canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("dragover", onDragOver); + }; + }, [selectedItem, camera, pointer, activeModule, controls]); + + return ( + + ) +} + +export default AssetsGroup; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/asset/functions/addAssetModel.ts similarity index 76% rename from app/src/modules/builder/geomentries/assets/addAssetModel.ts rename to app/src/modules/builder/asset/functions/addAssetModel.ts index 692664e..d0b6461 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/asset/functions/addAssetModel.ts @@ -1,428 +1,406 @@ -import * as THREE from 'three'; -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import gsap from 'gsap'; -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 { 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 { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; -import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator'; - -async function addAssetModel( - raycaster: THREE.Raycaster, - camera: THREE.Camera, - pointer: THREE.Vector2, - floorGroup: Types.RefGroup, - setFloorItems: Types.setFloorItemSetState, - itemsGroup: Types.RefGroup, - isTempLoader: Types.RefBoolean, - tempLoader: Types.RefMesh, - socket: Socket, - selectedItem: any, - setSelectedItem: any, - addEvent: (event: EventsSchema) => void, - plane: Types.RefMesh, -): Promise { - - ////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage ////////// - - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - - try { - isTempLoader.current = true; - 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); - - raycaster.setFromCamera(pointer, camera); - const floorIntersections = raycaster.intersectObjects(floorGroup.current.children, true); - const intersectedFloor = floorIntersections.find(intersect => intersect.object.name.includes("Floor")); - - const planeIntersections = raycaster.intersectObject(plane.current!, true); - const intersectedPlane = planeIntersections[0]; - - let intersectPoint: THREE.Vector3 | null = null; - - if (intersectedFloor && intersectedPlane) { - intersectPoint = intersectedFloor.distance < intersectedPlane.distance ? (new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)) : (new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z)); - } else if (intersectedFloor) { - intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z); - } else if (intersectedPlane) { - intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z); - } - - if (intersectPoint) { - if (intersectPoint.y < 0) { - intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z); - } - // console.log('selectedItem: ', selectedItem); - const cachedModel = THREE.Cache.get(selectedItem.id); - if (cachedModel) { - // console.log(`[Cache] Fetching ${selectedItem.name}`); - handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); - return; - } else { - const cachedModelBlob = await retrieveGLTF(selectedItem.id); - - if (cachedModelBlob) { - // console.log(`Added ${selectedItem.name} from indexDB`); - - const blobUrl = URL.createObjectURL(cachedModelBlob); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(selectedItem.id, gltf); - handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, addEvent, socket); - }, - () => { - TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); - }); - } else { - // console.log(`Added ${selectedItem.name} from Backend`); - - loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`, async (gltf) => { - 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, addEvent, socket); - }, - () => { - TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); - }); - } - } - } - } catch (error) { - echo.error("Failed to add asset"); - console.error('Error fetching asset model:', error); - } finally { - setSelectedItem({}); - } -} - -async function handleModelLoad( - gltf: any, - intersectPoint: THREE.Vector3, - selectedItem: any, - itemsGroup: Types.RefGroup, - tempLoader: Types.RefMesh, - isTempLoader: Types.RefBoolean, - setFloorItems: Types.setFloorItemSetState, - addEvent: (event: EventsSchema) => void, - socket: Socket -) { - const model = gltf.scene.clone(); - model.userData = { name: selectedItem.name, modelId: selectedItem.id, modelUuid: model.uuid }; - model.position.set(intersectPoint!.x, 3 + intersectPoint!.y, intersectPoint!.z); - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - - model.traverse((child: any) => { - if (child) { - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - if (tempLoader.current) { - (tempLoader.current.material).dispose(); - (tempLoader.current.geometry).dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { - modelUuid: model.uuid, - modelName: selectedItem.name, - modelfileID: selectedItem.id, - position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z], - rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, - isLocked: false, - isVisible: true - }; - - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - // 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 - - if (selectedItem.type) { - const data = PointsCalculator( - selectedItem.type, - gltf.scene.clone(), - new THREE.Vector3(...model.rotation) - ); - - if (!data || !data.points) return; - - const eventData: any = { - type: selectedItem.type, - }; - - if (selectedItem.type === "Conveyor") { - const ConveyorEvent: ConveyorEventSchema = { - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], - state: "idle", - type: 'transfer', - speed: 1, - points: data.points.map((point: THREE.Vector3, index: number) => { - const triggers: TriggerSchema[] = []; - - if (data.points && index < data.points.length - 1) { - triggers.push({ - triggerUuid: THREE.MathUtils.generateUUID(), - triggerName: `Trigger 1`, - triggerType: "onComplete", - delay: 0, - triggeredAsset: { - triggeredModel: { - modelName: newFloorItem.modelName, - modelUuid: newFloorItem.modelUuid - }, - triggeredPoint: { - pointName: `Point`, - pointUuid: "" - }, - triggeredAction: { - actionName: `Action 1`, - actionUuid: "" - } - } - }); - } - - return { - uuid: THREE.MathUtils.generateUUID(), - position: [point.x, point.y, point.z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action 1`, - actionType: 'default', - material: 'Default Material', - delay: 0, - spawnInterval: 5, - spawnCount: 1, - triggers: triggers - } - }; - }) - }; - - for (let i = 0; i < ConveyorEvent.points.length - 1; i++) { - const currentPoint = ConveyorEvent.points[i]; - const nextPoint = ConveyorEvent.points[i + 1]; - - if (currentPoint.action.triggers.length > 0) { - currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid; - currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid; - } - } - addEvent(ConveyorEvent); - eventData.points = ConveyorEvent.points.map(point => ({ - uuid: point.uuid, - position: point.position, - rotation: point.rotation - })); - - } else if (selectedItem.type === "Vehicle") { - const vehicleEvent: VehicleEventSchema = { - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], - state: "idle", - type: "vehicle", - speed: 1, - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "travel", - unLoadDuration: 5, - loadCapacity: 1, - steeringAngle: 0, - pickUpPoint: null, - unLoadPoint: null, - triggers: [] - } - } - }; - addEvent(vehicleEvent); - eventData.point = { - uuid: vehicleEvent.point.uuid, - position: vehicleEvent.point.position, - rotation: vehicleEvent.point.rotation - }; - - } else if (selectedItem.type === "ArmBot") { - const roboticArmEvent: RoboticArmEventSchema = { - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], - state: "idle", - type: "roboticArm", - speed: 1, - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - actions: [ - { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "pickAndPlace", - process: { - startPoint: null, - endPoint: null - }, - triggers: [] - } - ] - } - }; - addEvent(roboticArmEvent); - eventData.point = { - uuid: roboticArmEvent.point.uuid, - position: roboticArmEvent.point.position, - rotation: roboticArmEvent.point.rotation - }; - - } else if (selectedItem.type === "StaticMachine") { - const machineEvent: MachineEventSchema = { - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], - state: "idle", - type: "machine", - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "process", - processTime: 10, - swapMaterial: "Default Material", - triggers: [] - } - } - }; - addEvent(machineEvent); - eventData.point = { - uuid: machineEvent.point.uuid, - position: machineEvent.point.position, - rotation: machineEvent.point.rotation - }; - } else if (selectedItem.type === "Storage") { - const storageEvent: StorageEventSchema = { - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - position: newFloorItem.position, - rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], - state: "idle", - type: "storageUnit", - point: { - uuid: THREE.MathUtils.generateUUID(), - position: [data.points[0].x, data.points[0].y, data.points[0].z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Action 1", - actionType: "store", - storageCapacity: 10, - triggers: [] - } - } - } - addEvent(storageEvent); - eventData.point = { - uuid: storageEvent.point.uuid, - position: storageEvent.point.position, - rotation: storageEvent.point.rotation - }; - } - - const completeData = { - 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, - eventData: eventData - }; - - model.userData.eventData = eventData; - - newFloorItem.eventData = eventData; - - setFloorItems((prevItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - socket.emit("v2:model-asset:add", completeData); - - 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: () => { echo.success("Model Added!"); } }); - } else { - - 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: () => { echo.success("Model Added!"); } }); - } -} - -export default addAssetModel; +import * as THREE from 'three'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; +import * as Types from "../../../../types/world/worldTypes"; +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 PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator'; + +async function addAssetModel( + raycaster: THREE.Raycaster, + camera: THREE.Camera, + pointer: THREE.Vector2, + floorGroup: Types.RefGroup, + socket: Socket, + selectedItem: any, + setSelectedItem: any, + addEvent: (event: EventsSchema) => void, + addAsset: (asset: Asset) => void, + plane: Types.RefMesh, +): Promise { + + ////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage ////////// + + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + + try { + 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); + + raycaster.setFromCamera(pointer, camera); + const floorIntersections = raycaster.intersectObjects(floorGroup.current.children, true); + const intersectedFloor = floorIntersections.find(intersect => intersect.object.name.includes("Floor")); + + const planeIntersections = raycaster.intersectObject(plane.current!, true); + const intersectedPlane = planeIntersections[0]; + + let intersectPoint: THREE.Vector3 | null = null; + + if (intersectedFloor && intersectedPlane) { + intersectPoint = intersectedFloor.distance < intersectedPlane.distance ? (new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z)) : (new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z)); + } else if (intersectedFloor) { + intersectPoint = new THREE.Vector3(intersectedFloor.point.x, Math.round(intersectedFloor.point.y), intersectedFloor.point.z); + } else if (intersectedPlane) { + intersectPoint = new THREE.Vector3(intersectedPlane.point.x, 0, intersectedPlane.point.z); + } + + if (intersectPoint) { + if (intersectPoint.y < 0) { + intersectPoint = new THREE.Vector3(intersectPoint.x, 0, intersectPoint.z); + } + const cachedModel = THREE.Cache.get(selectedItem.id); + if (cachedModel) { + handleModelLoad(cachedModel, intersectPoint!, selectedItem, addEvent, addAsset, socket); + return; + } else { + const cachedModelBlob = await retrieveGLTF(selectedItem.id); + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(selectedItem.id, gltf); + handleModelLoad(gltf, intersectPoint!, selectedItem, addEvent, addAsset, socket); + }); + } else { + loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`, async (gltf) => { + 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, addEvent, addAsset, socket); + }) + } + } + } + } catch (error) { + echo.error("Failed to add asset"); + } finally { + setSelectedItem({}); + } +} + +async function handleModelLoad( + gltf: any, + intersectPoint: THREE.Vector3, + selectedItem: any, + addEvent: (event: EventsSchema) => void, + addAsset: (asset: Asset) => void, + socket: Socket +) { + const model = gltf.scene.clone(); + model.userData = { name: selectedItem.name, modelId: selectedItem.id, modelUuid: model.uuid }; + model.position.set(intersectPoint!.x, intersectPoint!.y, intersectPoint!.z); + model.scale.set(...CONSTANTS.assetConfig.defaultScaleAfterGsap); + + model.traverse((child: any) => { + if (child) { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + const newFloorItem: Asset = { + modelUuid: model.uuid, + modelName: selectedItem.name, + assetId: selectedItem.id, + position: [intersectPoint!.x, intersectPoint!.y, intersectPoint!.z], + rotation: [0, 0, 0], + isLocked: false, + isVisible: true, + isCollidable: false, + opacity: 1, + }; + + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : ""; + + // API + + // await setFloorItemApi( + // organization, + // newFloorItem.modelUuid, + // newFloorItem.modelName, + // newFloorItem.modelfileID, + // newFloorItem.position, + // { x: 0, y: 0, z: 0 }, + // false, + // true, + // ); + + // SOCKET + + if (selectedItem.type) { + const data = PointsCalculator( + selectedItem.type, + gltf.scene.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + const eventData: any = { + type: selectedItem.type, + }; + + if (selectedItem.type === "Conveyor") { + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: newFloorItem.rotation, + state: "idle", + type: 'transfer', + speed: 1, + points: data.points.map((point: THREE.Vector3, index: number) => { + const triggers: TriggerSchema[] = []; + + if (data.points && index < data.points.length - 1) { + triggers.push({ + triggerUuid: THREE.MathUtils.generateUUID(), + triggerName: `Trigger 1`, + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: newFloorItem.modelName, + modelUuid: newFloorItem.modelUuid + }, + triggeredPoint: { + pointName: `Point`, + pointUuid: "" + }, + triggeredAction: { + actionName: `Action 1`, + actionUuid: "" + } + } + }); + } + + return { + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action 1`, + actionType: 'default', + material: 'Default Material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: triggers + } + }; + }) + }; + + for (let i = 0; i < ConveyorEvent.points.length - 1; i++) { + const currentPoint = ConveyorEvent.points[i]; + const nextPoint = ConveyorEvent.points[i + 1]; + + if (currentPoint.action.triggers.length > 0) { + currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid; + currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid; + } + } + addEvent(ConveyorEvent); + eventData.points = ConveyorEvent.points.map(point => ({ + uuid: point.uuid, + position: point.position, + rotation: point.rotation + })); + + } else if (selectedItem.type === "Vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: newFloorItem.rotation, + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "travel", + unLoadDuration: 5, + loadCapacity: 1, + steeringAngle: 0, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } + } + }; + addEvent(vehicleEvent); + eventData.point = { + uuid: vehicleEvent.point.uuid, + position: vehicleEvent.point.position, + rotation: vehicleEvent.point.rotation + }; + + } else if (selectedItem.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: newFloorItem.rotation, + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "pickAndPlace", + process: { + startPoint: null, + endPoint: null + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + eventData.point = { + uuid: roboticArmEvent.point.uuid, + position: roboticArmEvent.point.position, + rotation: roboticArmEvent.point.rotation + }; + + } else if (selectedItem.type === "StaticMachine") { + const machineEvent: MachineEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: newFloorItem.rotation, + state: "idle", + type: "machine", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "process", + processTime: 10, + swapMaterial: "Default Material", + triggers: [] + } + } + }; + addEvent(machineEvent); + eventData.point = { + uuid: machineEvent.point.uuid, + position: machineEvent.point.position, + rotation: machineEvent.point.rotation + }; + } else if (selectedItem.type === "Storage") { + const storageEvent: StorageEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: newFloorItem.rotation, + state: "idle", + type: "storageUnit", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "store", + storageCapacity: 10, + triggers: [] + } + } + } + addEvent(storageEvent); + eventData.point = { + uuid: storageEvent.point.uuid, + position: storageEvent.point.position, + rotation: storageEvent.point.rotation + }; + } + + const completeData = { + organization, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + modelfileID: newFloorItem.assetId, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + eventData: eventData + }; + + socket.emit("v2:model-asset:add", completeData); + + const asset: Asset = { + modelUuid: completeData.modelUuid, + modelName: completeData.modelName, + assetId: completeData.modelfileID, + position: completeData.position, + rotation: [completeData.rotation.x, completeData.rotation.y, completeData.rotation.z] as [number, number, number], + isLocked: completeData.isLocked, + isCollidable: false, + isVisible: completeData.isVisible, + opacity: 1, + eventData: completeData.eventData + } + + addAsset(asset); + + } else { + + const data = { + organization, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + modelfileID: newFloorItem.assetId, + position: newFloorItem.position, + rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id + }; + + socket.emit("v2:model-asset:add", data); + + const asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z] as [number, number, number], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1 + } + + addAsset(asset); + + } +} + +export default addAssetModel; diff --git a/app/src/modules/builder/asset/functions/assetBoundingBox.tsx b/app/src/modules/builder/asset/functions/assetBoundingBox.tsx new file mode 100644 index 0000000..d74f37d --- /dev/null +++ b/app/src/modules/builder/asset/functions/assetBoundingBox.tsx @@ -0,0 +1,20 @@ +import { Box3, BoxGeometry, EdgesGeometry, Vector3 } from "three"; + +export const AssetBoundingBox = ({ boundingBox }: { boundingBox: Box3 | null }) => { + if (!boundingBox) return null; + + const size = boundingBox.getSize(new Vector3()); + const center = boundingBox.getCenter(new Vector3()); + + const boxGeometry = new BoxGeometry(size.x, size.y, size.z); + const edges = new EdgesGeometry(boxGeometry); + + return ( + + + + + + + ); +}; \ No newline at end of file diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx new file mode 100644 index 0000000..bbe857d --- /dev/null +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -0,0 +1,292 @@ +import * as THREE from 'three'; +import { useEffect, useRef, useState } from 'react'; +import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; +import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; +import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; +import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView } from '../../../../../store/builder/store'; +import { AssetBoundingBox } from '../../functions/assetBoundingBox'; +import { CameraControls } from '@react-three/drei'; +import { useAssetsStore } from '../../../../../store/builder/useAssetStore'; +import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; +import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore'; +import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore'; +import { useProductContext } from '../../../../simulation/products/productContext'; + +function Model({ asset }: { readonly asset: Asset }) { + const { camera, controls, gl } = useThree(); + const { activeTool } = useActiveTool(); + const { toggleView } = useToggleView(); + const { subModule } = useSubModuleStore(); + const { activeModule } = useModuleStore(); + const { removeAsset } = useAssetsStore(); + const { setTop } = useTopData(); + const { setLeft } = useLeftData(); + const { getIsEventInProduct } = useProductStore(); + const { getEventByModelUuid } = useEventsStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset(); + const { socket } = useSocketStore(); + const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); + const { setSelectedFloorItem } = useSelectedFloorItem(); + const { renderDistance } = useRenderDistance(); + const [isRendered, setIsRendered] = useState(false); + const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + const [gltfScene, setGltfScene] = useState(null); + const [boundingBox, setBoundingBox] = useState(null); + const groupRef = useRef(null); + + useEffect(() => { + 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); + const loadModel = async () => { + try { + // Check Cache + const assetId = asset.assetId; + const cachedModel = THREE.Cache.get(assetId); + if (cachedModel) { + setGltfScene(cachedModel); + calculateBoundingBox(cachedModel.scene); + return; + } + + // Check IndexedDB + const indexedDBModel = await retrieveGLTF(assetId); + if (indexedDBModel) { + const blobUrl = URL.createObjectURL(indexedDBModel); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(assetId, gltf); + setGltfScene(gltf); + calculateBoundingBox(gltf.scene); + }, + undefined, + (error) => { + echo.error(`[IndexedDB] Error loading ${asset.modelName}:`); + URL.revokeObjectURL(blobUrl); + } + ); + return; + } + + // Fetch from Backend + const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`; + const handleBackendLoad = async (gltf: GLTF) => { + try { + const response = await fetch(modelUrl); + const modelBlob = await response.blob(); + await storeGLTF(assetId, modelBlob); + THREE.Cache.add(assetId, gltf); + setGltfScene(gltf); + calculateBoundingBox(gltf.scene); + } catch (error) { + console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error); + } + }; + loader.load( + modelUrl, + handleBackendLoad, + undefined, + (error) => { + echo.error(`[Backend] Error loading ${asset.modelName}:`); + } + ); + } catch (err) { + console.error("Failed to load model:", asset.assetId, err); + } + }; + + const calculateBoundingBox = (scene: THREE.Object3D) => { + const box = new THREE.Box3().setFromObject(scene); + setBoundingBox(box); + }; + + loadModel(); + + }, []); + + useFrame(() => { + const assetPosition = new THREE.Vector3(...asset.position); + if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) { + setIsRendered(true); + } else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) { + setIsRendered(false); + } + }) + + const handleDblClick = (asset: Asset) => { + if (asset) { + if (activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') { + const size = boundingBox.getSize(new THREE.Vector3()); + const center = boundingBox.getCenter(new THREE.Vector3()); + + const front = new THREE.Vector3(0, 0, 1); + groupRef.current.localToWorld(front); + front.sub(groupRef.current.position).normalize(); + + const distance = Math.max(size.x, size.y, size.z) * 2; + const newPosition = center.clone().addScaledVector(front, distance); + + (controls as CameraControls).setPosition( + newPosition.x, + newPosition.y, + newPosition.z, + true + ); + (controls as CameraControls).setTarget(center.x, center.y, center.z, true); + (controls as CameraControls).fitToBox(groupRef.current, true, { + cover: true, + paddingTop: 5, + paddingLeft: 5, + paddingBottom: 5, + paddingRight: 5, + }); + setSelectedFloorItem(groupRef.current); + } + } + }; + + const handleClick = (asset: Asset) => { + if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + //REST + + // const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName); + + //SOCKET + + const data = { + organization: organization, + modelUuid: asset.modelUuid, + modelName: asset.modelName, + socketId: socket.id + } + + const response = socket.emit('v2:model-asset:delete', data) + + useEventsStore.getState().removeEvent(asset.modelUuid); + useProductStore.getState().deleteEvent(asset.modelUuid); + + if (response) { + + removeAsset(asset.modelUuid); + + echo.success("Model Removed!"); + } + + } + } + + const handlePointerOver = (asset: Asset) => { + if (activeTool === "delete") { + if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + return; + } else { + setDeletableFloorItem(groupRef.current); + } + } + } + + const handlePointerOut = (asset: Asset) => { + if (activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + setDeletableFloorItem(null); + } + } + + const handleContextMenu = (asset: Asset, evt: ThreeEvent) => { + if (activeTool === "cursor" && subModule === 'simulations') { + if (asset.modelUuid) { + const canvasElement = gl.domElement; + const isInProduct = getIsEventInProduct(selectedProduct.productId, asset.modelUuid); + if (isInProduct) { + const event = getEventByModelUuid(asset.modelUuid); + if (event) { + setSelectedAsset(event); + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + setTop(relativeY); + setLeft(relativeX); + } else { + clearSelectedAsset(); + } + } else { + const event = getEventByModelUuid(asset.modelUuid); + if (event) { + setSelectedAsset(event) + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + setTop(relativeY); + setLeft(relativeX); + } else { + clearSelectedAsset() + } + } + } else { + clearSelectedAsset() + } + } else { + clearSelectedAsset() + } + } + + return ( + { + if (!toggleView) { + e.stopPropagation(); + handleDblClick(asset); + } + }} + onClick={(e) => { + if (!toggleView) { + e.stopPropagation(); + handleClick(asset); + } + }} + onPointerOver={(e) => { + if (!toggleView) { + e.stopPropagation(); + handlePointerOver(asset); + } + }} + onPointerOut={(e) => { + if (!toggleView) { + e.stopPropagation(); + handlePointerOut(asset); + } + }} + onContextMenu={(e) => { + e.stopPropagation(); + handleContextMenu(asset, e); + }} + > + {gltfScene && ( + isRendered ? ( + + ) : ( + + ) + )} + + ); +} + +export default Model; \ No newline at end of file diff --git a/app/src/modules/builder/asset/models/models.tsx b/app/src/modules/builder/asset/models/models.tsx new file mode 100644 index 0000000..e67a649 --- /dev/null +++ b/app/src/modules/builder/asset/models/models.tsx @@ -0,0 +1,37 @@ +import { useAssetsStore } from '../../../../store/builder/useAssetStore'; +import Model from './model/model'; +import { useThree } from '@react-three/fiber'; +import { CameraControls } from '@react-three/drei'; +import { Vector3 } from 'three'; +import { useSelectedFloorItem } from '../../../../store/builder/store'; +import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore'; + +function Models() { + const { controls } = useThree(); + const { assets } = useAssetsStore(); + const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); + + return ( + { + e.stopPropagation(); + if (selectedFloorItem) { + const target = (controls as CameraControls).getTarget(new Vector3()); + (controls as CameraControls).setTarget(target.x, 0, target.z, true); + setSelectedFloorItem(null); + } + if (selectedAsset) { + clearSelectedAsset(); + } + }} + > + {assets.map((asset) => + + )} + + ) +} + +export default Models \ No newline at end of file diff --git a/app/src/modules/builder/assetGroup/assetsGroup.tsx b/app/src/modules/builder/assetGroup/assetsGroup.tsx deleted file mode 100644 index 9e57fb7..0000000 --- a/app/src/modules/builder/assetGroup/assetsGroup.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import * as THREE from "three" -import { useEffect } from 'react' -import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; -import { useLoadingProgress } from '../../../store/builder/store'; -import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { FloorItems } from "../../../types/world/worldTypes"; -import { useAssetsStore } from "../../../store/builder/useAssetStore"; -import Models from "./models/models"; -import { useGLTF } from "@react-three/drei"; - -const gltfLoaderWorker = new Worker( - new URL( - "../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", - import.meta.url - ) -); - -function AssetsGroup() { - const { setLoadingProgress } = useLoadingProgress(); - const { setAssets } = useAssetsStore(); - - 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); - - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - let totalAssets = 0; - let loadedAssets = 0; - - const updateLoadingProgress = (progress: number) => { - if (progress < 100) { - setLoadingProgress(progress); - } else if (progress === 100) { - setTimeout(() => { - setLoadingProgress(100); - setTimeout(() => { - setLoadingProgress(0); - }, 1500); - }, 1000); - } - }; - - getFloorAssets(organization).then((data) => { - if (data.length > 0) { - const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID)); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { - updateLoadingProgress(100); - return; - } - gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); - } else { - gltfLoaderWorker.postMessage({ floorItems: [] }); - updateLoadingProgress(100); - } - }); - - gltfLoaderWorker.onmessage = async (event) => { - if (event.data.message === "gltfLoaded" && event.data.modelBlob) { - const blobUrl = URL.createObjectURL(event.data.modelBlob); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(event.data.modelID, gltf); - - loadedAssets++; - const progress = Math.round((loadedAssets / totalAssets) * 100); - updateLoadingProgress(progress); - - if (loadedAssets === totalAssets) { - const assets: Asset[] = []; - getFloorAssets(organization).then((data: FloorItems) => { - data.forEach((item) => { - if (item.eventData) { - assets.push({ - modelUuid: item.modelUuid, - modelName: item.modelName, - assetId: item.modelfileID, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - isLocked: item.isLocked, - isCollidable: false, - isVisible: item.isVisible, - opacity: 1, - eventData: item.eventData - }) - } else { - assets.push({ - modelUuid: item.modelUuid, - modelName: item.modelName, - assetId: item.modelfileID, - position: item.position, - rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - isLocked: item.isLocked, - isCollidable: false, - isVisible: item.isVisible, - opacity: 1, - }) - } - }) - setAssets(assets); - }) - updateLoadingProgress(100); - } - }); - } - }; - }, []); - - return ( - <> - - - ) -} - -export default AssetsGroup; \ No newline at end of file diff --git a/app/src/modules/builder/assetGroup/models/model/model.tsx b/app/src/modules/builder/assetGroup/models/model/model.tsx deleted file mode 100644 index 55eb1a6..0000000 --- a/app/src/modules/builder/assetGroup/models/model/model.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { Outlines } from '@react-three/drei'; -import { useEffect, useState } from 'react'; -import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; -import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; -import * as THREE from 'three'; - -function Model({ asset }: { asset: Asset }) { - const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - const [gltfScene, setGltfScene] = useState(null); - - useEffect(() => { - 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); - const loadModel = async () => { - try { - const cachedModel = THREE.Cache.get(asset.assetId!); - if (cachedModel) { - setGltfScene(cachedModel); - return; - } - - // Check IndexedDB - const indexedDBModel = await retrieveGLTF(asset.assetId!); - if (indexedDBModel) { - const blobUrl = URL.createObjectURL(indexedDBModel); - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(asset.assetId!, gltf); - setGltfScene(gltf); - }, - undefined, - (error) => { - echo.error(`[IndexedDB] Error loading ${asset.modelName}:`); - URL.revokeObjectURL(blobUrl); - } - ); - return; - } - - // Fetch from Backend - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${asset.assetId!}`; - loader.load(modelUrl, async (gltf) => { - const modelBlob = await fetch(modelUrl).then((res) => res.blob()); - await storeGLTF(asset.assetId!, modelBlob); - THREE.Cache.add(asset.assetId!, gltf); - setGltfScene(gltf); - }, - undefined, - (error) => { - echo.error(`[Backend] Error loading ${asset.modelName}:`); - } - ); - } catch (err) { - console.error("Failed to load model:", asset.assetId, err); - } - }; - - loadModel(); - - }, [asset.assetId]); - - return ( - <> - {gltfScene && - - - - } - - ); -} - -export default Model; \ No newline at end of file diff --git a/app/src/modules/builder/assetGroup/models/models.tsx b/app/src/modules/builder/assetGroup/models/models.tsx deleted file mode 100644 index 7378812..0000000 --- a/app/src/modules/builder/assetGroup/models/models.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react' -import { useAssetsStore } from '../../../../store/builder/useAssetStore'; -import Model from './model/model'; - -function Models() { - const { assets } = useAssetsStore(); - - return ( - <> - {assets.map((asset) => - - )} - - ) -} - -export default Models \ No newline at end of file diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index 18d407b..3263b90 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -33,7 +33,6 @@ import loadWalls from "./geomentries/walls/loadWalls"; import * as Types from "../../types/world/worldTypes"; import SocketResponses from "../collaboration/socket/socketResponses.dev"; -import FloorItemsGroup from "./groups/floorItemsGroup"; import FloorPlanGroup from "./groups/floorPlanGroup"; import FloorGroup from "./groups/floorGroup"; import FloorGroupAilse from "./groups/floorGroupAisle"; @@ -47,8 +46,9 @@ import MeasurementTool from "../scene/tools/measurementTool"; import NavMesh from "../simulation/vehicle/navMesh/navMesh"; import CalculateAreaGroup from "./groups/calculateAreaGroup"; import LayoutImage from "./layout/layoutImage"; -import AssetsGroup from "./assetGroup/assetsGroup"; +import AssetsGroup from "./asset/assetsGroup"; import { Bvh } from "@react-three/drei"; +import AislesGroup from "./aisle/aislesGroup"; export default function Builder() { const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. @@ -87,7 +87,6 @@ export default function Builder() { 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. @@ -96,7 +95,6 @@ export default function Builder() { 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... @@ -243,15 +241,6 @@ export default function Builder() { /> - - */} + + - {/* */} + diff --git a/app/src/modules/builder/functions/deletableLineOrPoint.ts b/app/src/modules/builder/functions/deletableLineOrPoint.ts index 2690bf7..cbca682 100644 --- a/app/src/modules/builder/functions/deletableLineOrPoint.ts +++ b/app/src/modules/builder/functions/deletableLineOrPoint.ts @@ -46,11 +46,11 @@ function DeletableLineorPoint( hoveredDeletablePoint.current = (visibleIntersectPoint as any).object; (hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(new THREE.Color("red")); - (hoveredDeletablePoint.current as any).material.uniforms.uColor.value.set(new THREE.Color("red")); + (hoveredDeletablePoint.current as any).material.uniforms.uOuterColor.value.set(new THREE.Color("red")); // (hoveredDeletablePoint.current as THREE.Mesh).scale.set(1.5, 1.5, 1.5); } else if (hoveredDeletablePoint.current) { (hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(CONSTANTS.pointConfig.defaultInnerColor); - (hoveredDeletablePoint.current as any).material.uniforms.uColor.value.set((hoveredDeletablePoint.current as any).userData.color); + (hoveredDeletablePoint.current as any).material.uniforms.uOuterColor.value.set((hoveredDeletablePoint.current as any).userData.color); // hoveredDeletablePoint.current.scale.set(1, 1, 1); hoveredDeletablePoint.current = null; } @@ -65,7 +65,7 @@ function DeletableLineorPoint( if (hoveredDeletablePoint.current) { (hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(CONSTANTS.pointConfig.defaultInnerColor); - (hoveredDeletablePoint.current as any).material.uniforms.uColor.value.set((hoveredDeletablePoint.current as any).userData.color); + (hoveredDeletablePoint.current as any).material.uniforms.uOuterColor.value.set((hoveredDeletablePoint.current as any).userData.color); // hoveredDeletablePoint.current.scale.set(1, 1, 1); hoveredDeletablePoint.current = null; } diff --git a/app/src/modules/builder/geomentries/assets/assetManager.ts b/app/src/modules/builder/geomentries/assets/assetManager.ts deleted file mode 100644 index 5d2f90f..0000000 --- a/app/src/modules/builder/geomentries/assets/assetManager.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from "three"; -import gsap from "gsap"; -import * as Types from "../../../../types/world/worldTypes"; -import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -import { initializeDB, retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils"; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import { toast } from 'react-toastify'; - -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; -let currentTaskId = 0; // Track the active task -let activePromises = new Map(); // Map to track task progress - -export default async function assetManager( - data: any, - itemsGroup: Types.RefGroup, - loader: GLTFLoader, -) { - const taskId = ++currentTaskId; // Increment taskId for each call - activePromises.set(taskId, true); // Mark task as active - - // - - if (data.toRemove.length > 0) { - data.toRemove.forEach((uuid: string) => { - const item = itemsGroup.current.getObjectByProperty("uuid", uuid); - if (item) { - // Traverse and dispose of resources - // item.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(); - // } - // } - // }); - - // Remove the object from the scene - itemsGroup.current.remove(item); - } - }); - } - - if (data.toAdd.length > 0) { - await initializeDB(); - - for (const item of data.toAdd) { - if (!activePromises.get(taskId)) return; // Stop processing if task is canceled - - await new Promise(async (resolve) => { - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; - - // Check Three.js Cache - const cachedModel = THREE.Cache.get(item.modelfileID!); - if (cachedModel) { - // - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, resolve); - return; - } - - // Check IndexedDB - const indexedDBModel = await retrieveGLTF(item.modelfileID!); - if (indexedDBModel) { - // - const blobUrl = URL.createObjectURL(indexedDBModel); - loader.load( - blobUrl, - (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(item.modelfileID!, gltf); // Add to cache - processLoadedModel(gltf.scene.clone(), item, itemsGroup, resolve); - }, - undefined, - (error) => { - echo.error(`[IndexedDB] Error loading ${item.modelName}:`); - resolve(); - } - ); - return; - } - - // Fetch from Backend - // - loader.load( - modelUrl, - async (gltf) => { - const modelBlob = await fetch(modelUrl).then((res) => res.blob()); - await storeGLTF(item.modelfileID!, modelBlob); // Store in IndexedDB - THREE.Cache.add(item.modelfileID!, gltf); // Add to cache - processLoadedModel(gltf.scene.clone(), item, itemsGroup, resolve); - }, - undefined, - (error) => { - echo.error(`[Backend] Error loading ${item.modelName}:`); - resolve(); - } - ); - }); - } - - function processLoadedModel( - gltf: any, - item: Types.FloorItemType, - itemsGroup: Types.RefGroup, - resolve: () => void - ) { - if (!activePromises.get(taskId)) return; // Stop processing if task is canceled - - const existingModel = itemsGroup?.current?.getObjectByProperty("uuid", item.modelUuid); - if (existingModel) { - // - resolve(); - return; - } - - const model = gltf; - model.uuid = item.modelUuid; - model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - 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); - - 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: 0.5, ease: "power2.out", onStart: resolve, }); - } - } - - activePromises.delete(taskId); // Mark task as complete -} - -// Cancel ongoing task when new call arrives -export function cancelOngoingTasks() { - activePromises.clear(); // Clear all ongoing tasks -} diff --git a/app/src/modules/builder/geomentries/assets/assetVisibility.ts b/app/src/modules/builder/geomentries/assets/assetVisibility.ts deleted file mode 100644 index c2503c3..0000000 --- a/app/src/modules/builder/geomentries/assets/assetVisibility.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -let lastUpdateTime = 0; - -export default function assetVisibility( - itemsGroup: Types.RefGroup, - cameraPosition: Types.Vector3, - renderDistance: Types.Number, - throttleTime = 100 -): void { - const now = performance.now(); - if (now - lastUpdateTime < throttleTime) return; - lastUpdateTime = now; - - if (!itemsGroup?.current || !cameraPosition) return; - - itemsGroup.current.children.forEach((child) => { - const Distance = cameraPosition.distanceTo(child.position); - if (Distance <= renderDistance) { - child.visible = true; - } else { - child.visible = false; - } - }); -} diff --git a/app/src/modules/builder/geomentries/assets/deletableHoveredFloorItems.ts b/app/src/modules/builder/geomentries/assets/deletableHoveredFloorItems.ts deleted file mode 100644 index 2913b1a..0000000 --- a/app/src/modules/builder/geomentries/assets/deletableHoveredFloorItems.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function DeletableHoveredFloorItems( - state: Types.ThreeState, - itemsGroup: Types.RefGroup, - hoveredDeletableFloorItem: Types.RefMesh, - setDeletableFloorItem: any -): void { - - ////////// Altering the color of the hovered GLTF item during the Deletion time ////////// - - state.raycaster.setFromCamera(state.pointer, state.camera); - const intersects = state.raycaster.intersectObjects(itemsGroup.current.children, true); - - if (intersects.length > 0) { - if (intersects[0].object.name === "Pole") { - return; - } - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - hoveredDeletableFloorItem.current = currentObject as THREE.Mesh; - setDeletableFloorItem(currentObject); - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - } else { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } - } -} - -export default DeletableHoveredFloorItems; diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts deleted file mode 100644 index 5b234b4..0000000 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { toast } from 'react-toastify'; -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; -// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; -import { Socket } from 'socket.io-client'; -import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; -import { useEventsStore } from "../../../../store/simulation/useEventsStore"; -import { useProductStore } from "../../../../store/simulation/useProductStore"; - -async function DeleteFloorItems( - itemsGroup: Types.RefGroup, - hoveredDeletableFloorItem: Types.RefMesh, - setFloorItems: Types.setFloorItemSetState, - socket: Socket -): Promise { - - ////////// Deleting the hovered Floor GLTF from the scene (itemsGroup.current) and from the floorItems and also update it in the localstorage ////////// - - if (hoveredDeletableFloorItem.current) { - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - const items = await getFloorAssets(organization); - const removedItem = items.find( - (item: { modelUuid: string }) => item.modelUuid === hoveredDeletableFloorItem.current?.uuid - ); - - if (!removedItem) { - return - } - - //REST - - // const response = await deleteFloorItem(organization, removedItem.modelUuid, removedItem.modelName); - - //SOCKET - - const data = { - organization: organization, - modelUuid: removedItem.modelUuid, - modelName: removedItem.modelName, - socketId: socket.id - } - - const response = socket.emit('v2:model-asset:delete', data) - - useEventsStore.getState().removeEvent(removedItem.modelUuid); - useProductStore.getState().deleteEvent(removedItem.modelUuid); - - if (response) { - const updatedItems = items.filter( - (item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid - ); - - const storedItems = JSON.parse(localStorage.getItem("FloorItems") || '[]'); - const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => item.modelUuid !== hoveredDeletableFloorItem.current?.uuid); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - if (hoveredDeletableFloorItem.current) { - // Traverse and dispose of resources - hoveredDeletableFloorItem.current.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(); - } - } - }); - - // Remove the object from the scene - itemsGroup.current.remove(hoveredDeletableFloorItem.current); - } - setFloorItems(updatedItems); - - echo.success("Model Removed!"); - } - } -} - -export default DeleteFloorItems; diff --git a/app/src/modules/builder/geomentries/assets/tempLoader.ts b/app/src/modules/builder/geomentries/assets/tempLoader.ts deleted file mode 100644 index 73d2f9e..0000000 --- a/app/src/modules/builder/geomentries/assets/tempLoader.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function TempLoader( - intersectPoint: Types.Vector3, - isTempLoader: Types.RefBoolean, - tempLoader: Types.RefMesh, - itemsGroup: Types.RefGroup -): void { - - ////////// Temporary Loader that indicates the gltf is being loaded ////////// - - ////////// Bug: Can't Load More than one TempLoader if done, it won't leave the scene ////////// - - if (tempLoader.current) { - itemsGroup.current.remove(tempLoader.current); - } - if (isTempLoader.current) { - const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); - const cubeMaterial = new THREE.MeshBasicMaterial({ color: "white" }); - tempLoader.current = new THREE.Mesh(cubeGeometry, cubeMaterial); - tempLoader.current.position.set(intersectPoint.x, 0.5 + intersectPoint.y, intersectPoint.z); - itemsGroup.current.add(tempLoader.current); - isTempLoader.current = false; - } -} - -export default TempLoader; diff --git a/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx index 58302f7..479d820 100644 --- a/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx +++ b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx @@ -31,10 +31,6 @@ const DistanceText = () => { const [linesState, setLinesState] = useState([]); const { roomsState, setRoomsState } = useRoomsState(); - - - - useEffect(() => { if (linesState.length === 0) return; @@ -67,8 +63,6 @@ const DistanceText = () => { } - - // return Math.abs(area) / 2; // Build polygon and compute area diff --git a/app/src/modules/builder/geomentries/points/addPointToScene.ts b/app/src/modules/builder/geomentries/points/addPointToScene.ts index 922a6bb..b182128 100644 --- a/app/src/modules/builder/geomentries/points/addPointToScene.ts +++ b/app/src/modules/builder/geomentries/points/addPointToScene.ts @@ -17,7 +17,7 @@ function addPointToScene( 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 + uOuterColor: { 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: ` @@ -30,7 +30,7 @@ function addPointToScene( `, fragmentShader: ` varying vec2 vUv; - uniform vec3 uColor; + uniform vec3 uOuterColor; uniform vec3 uInnerColor; void main() { @@ -40,7 +40,7 @@ function addPointToScene( 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 + gl_FragColor = vec4(uOuterColor, 1.0); // Blue border } } `, diff --git a/app/src/modules/builder/geomentries/zones/addZonesToScene.ts b/app/src/modules/builder/geomentries/zones/addZonesToScene.ts index 6cfc22d..2832c7a 100644 --- a/app/src/modules/builder/geomentries/zones/addZonesToScene.ts +++ b/app/src/modules/builder/geomentries/zones/addZonesToScene.ts @@ -13,14 +13,14 @@ const baseMaterial = new THREE.ShaderMaterial({ `, fragmentShader: ` varying vec2 vUv; - uniform vec3 uColor; + uniform vec3 uOuterColor; void main(){ float alpha = 1.0 - vUv.y; - gl_FragColor = vec4(uColor, alpha); + gl_FragColor = vec4(uOuterColor, alpha); } `, uniforms: { - uColor: { value: new THREE.Color(CONSTANTS.zoneConfig.defaultColor) }, + uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.defaultColor) }, }, transparent: true, }); @@ -39,7 +39,7 @@ export default function addZonesToScene( const geometry = new THREE.PlaneGeometry(length, 10); const material = baseMaterial.clone(); - material.uniforms.uColor.value.set(color.r, color.g, color.b); + material.uniforms.uOuterColor.value.set(color.r, color.g, color.b); const mesh = new THREE.Mesh(geometry, material); diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx deleted file mode 100644 index 58f8f05..0000000 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ /dev/null @@ -1,445 +0,0 @@ -import { useFrame, useThree } from "@react-three/fiber"; -import { - useActiveTool, - useCamMode, - useDeletableFloorItem, - useDeleteTool, - useFloorItems, - useLoadingProgress, - useRenderDistance, - useSelectedFloorItem, - useSelectedItem, - useSocketStore, - useToggleView, -} from "../../../store/builder/store"; -import { useEffect } from "react"; -import * as THREE from "three"; -import * as Types from "../../../types/world/worldTypes"; -import assetManager, { - cancelOngoingTasks, -} from "../geomentries/assets/assetManager"; -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 "../IntialLoad/loadInitialFloorItems"; -import addAssetModel from "../geomentries/assets/addAssetModel"; -import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; -import useModuleStore from "../../../store/useModuleStore"; -import { useEventsStore } from "../../../store/simulation/useEventsStore"; -import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; - -const assetManagerWorker = new Worker( - new URL( - "../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", - import.meta.url - ) -); -const gltfLoaderWorker = new Worker( - new URL( - "../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", - import.meta.url - ) -); - -const FloorItemsGroup = ({ - itemsGroup, - hoveredDeletableFloorItem, - AttachedObject, - floorGroup, - tempLoader, - isTempLoader, - plane, -}: any) => { - const state: Types.ThreeState = useThree(); - const { raycaster, controls }: any = state; - const { renderDistance } = useRenderDistance(); - const { toggleView } = useToggleView(); - const { floorItems, setFloorItems } = useFloorItems(); - const { camMode } = useCamMode(); - const { deleteTool } = useDeleteTool(); - const { setDeletableFloorItem } = useDeletableFloorItem(); - const { setSelectedFloorItem } = useSelectedFloorItem(); - const { activeTool } = useActiveTool(); - const { selectedItem, setSelectedItem } = useSelectedItem(); - const { setLoadingProgress } = useLoadingProgress(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - const { addEvent } = useEventsStore(); - - dracoLoader.setDecoderPath( - "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" - ); - loader.setDRACOLoader(dracoLoader); - - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - findEnvironment( - organization, - localStorage.getItem("userId")! - ).then((evnironMentData) => { - - let totalAssets = 0; - let loadedAssets = 0; - - const updateLoadingProgress = (progress: number) => { - if (progress < 100) { - setLoadingProgress(progress); - } else if (progress === 100) { - setTimeout(() => { - setLoadingProgress(100); - setTimeout(() => { - setLoadingProgress(0); - }, 1500); - }, 1000); - } - }; - - getFloorAssets(organization).then((data) => { - if (data.length > 0) { - const uniqueItems = (data as Types.FloorItems).filter( - (item, index, self) => - index === self.findIndex((t) => t.modelfileID === item.modelfileID) - ); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { - updateLoadingProgress(100); - return; - } - gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); - } else { - gltfLoaderWorker.postMessage({ floorItems: [] }); - loadInitialFloorItems( - itemsGroup, - setFloorItems, - addEvent, - evnironMentData.renderDistance - ); - updateLoadingProgress(100); - } - }); - - gltfLoaderWorker.onmessage = async (event) => { - if (event.data.message === "gltfLoaded" && event.data.modelBlob) { - const blobUrl = URL.createObjectURL(event.data.modelBlob); - - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(event.data.modelID, gltf); - - loadedAssets++; - const progress = Math.round((loadedAssets / totalAssets) * 100); - updateLoadingProgress(progress); - - if (loadedAssets === totalAssets) { - loadInitialFloorItems( - itemsGroup, - setFloorItems, - addEvent, - evnironMentData.renderDistance - ); - updateLoadingProgress(100); - } - }); - } - }; - }) - }, []); - - useEffect(() => { - assetManagerWorker.onmessage = async (event) => { - cancelOngoingTasks(); // Cancel the ongoing process - await assetManager(event.data, itemsGroup, loader); - }; - }, [assetManagerWorker]); - - useEffect(() => { - if (toggleView) return; - - const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { - uuids.push(child.uuid); - }); - const cameraPosition = state.camera.position; - - assetManagerWorker.postMessage({ - floorItems, - cameraPosition, - uuids, - renderDistance, - }); - }, [camMode, renderDistance]); - - useEffect(() => { - const controls: any = state.controls; - const camera: any = state.camera; - - if (controls) { - let intervalId: NodeJS.Timeout | null = null; - - const handleChange = () => { - if (toggleView) return; - - const uuids: string[] = []; - itemsGroup.current?.children.forEach((child: any) => { - uuids.push(child.uuid); - }); - const cameraPosition = camera.position; - - assetManagerWorker.postMessage({ - floorItems, - cameraPosition, - uuids, - renderDistance, - }); - }; - - const startInterval = () => { - intervalId ??= setInterval(handleChange, 50); - }; - - const stopInterval = () => { - handleChange(); - if (intervalId) { - clearInterval(intervalId); - intervalId = null; - } - }; - - controls.addEventListener("rest", handleChange); - controls.addEventListener("rest", stopInterval); - controls.addEventListener("control", startInterval); - controls.addEventListener("controlend", stopInterval); - - return () => { - controls.removeEventListener("rest", handleChange); - controls.removeEventListener("rest", stopInterval); - controls.removeEventListener("control", startInterval); - controls.removeEventListener("controlend", stopInterval); - if (intervalId) { - clearInterval(intervalId); - } - }; - } - }, [state.controls, floorItems, toggleView, renderDistance]); - - 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 = async (evt: any) => { - if (controls) { - (controls as any).enabled = true; - } - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - if (deleteTool) { - DeleteFloorItems( - itemsGroup, - hoveredDeletableFloorItem, - setFloorItems, - socket - ); - } - - if (activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects( - itemsGroup.current.children, - true - ); - if ( - intersects.length > 0 && - intersects[0]?.object?.parent?.parent?.position && - intersects[0]?.object?.parent?.parent?.scale && - intersects[0]?.object?.parent?.parent?.rotation - ) { - // let currentObject = intersects[0].object; - // while (currentObject) { - // if (currentObject.name === "Scene") { - // break; - // } - // currentObject = currentObject.parent as THREE.Object3D; - // } - // if (currentObject) { - // AttachedObject.current = currentObject as any; - // setSelectedFloorItem(AttachedObject.current!); - // } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - } - }; - - const onDblClick = async (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - if (drag) return; - - - if (activeTool === "cursor") { - if (!itemsGroup.current) return; - let intersects = raycaster.intersectObjects( - itemsGroup.current.children, - true - ); - if ( - intersects.length > 0 && - intersects[0]?.object?.parent?.parent?.position && - intersects[0]?.object?.parent?.parent?.scale && - intersects[0]?.object?.parent?.parent?.rotation - ) { - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - break; - } - currentObject = currentObject.parent as THREE.Object3D; - } - if (currentObject) { - AttachedObject.current = currentObject as any; - // controls.fitToSphere(AttachedObject.current!, true); - - const bbox = new THREE.Box3().setFromObject( - AttachedObject.current - ); - const size = bbox.getSize(new THREE.Vector3()); - const center = bbox.getCenter(new THREE.Vector3()); - - const front = new THREE.Vector3(0, 0, 1); - AttachedObject.current.localToWorld(front); - front.sub(AttachedObject.current.position).normalize(); - - const distance = Math.max(size.x, size.y, size.z) * 2; - const newPosition = center - .clone() - .addScaledVector(front, distance); - - controls.setPosition( - newPosition.x, - newPosition.y, - newPosition.z, - true - ); - controls.setTarget(center.x, center.y, center.z, true); - controls.fitToBox(AttachedObject.current!, true, { - cover: true, - paddingTop: 5, - paddingLeft: 5, - paddingBottom: 5, - paddingRight: 5, - }); - - setSelectedFloorItem(AttachedObject.current!); - } - } else { - const target = controls.getTarget(new THREE.Vector3()); - await controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - } - }; - - const onDrop = (event: any) => { - if (!event.dataTransfer?.files[0]) return; - - if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { - - state.pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - state.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - - addAssetModel( - raycaster, - state.camera, - state.pointer, - floorGroup, - setFloorItems, - itemsGroup, - isTempLoader, - tempLoader, - socket, - selectedItem, - setSelectedItem, - addEvent, - plane - ); - } - }; - - const onDragOver = (event: any) => { - event.preventDefault(); - }; - - if (activeModule === "builder") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("dblclick", onDblClick); - canvasElement.addEventListener("drop", onDrop); - canvasElement.addEventListener("dragover", onDragOver); - } else { - if (controls) { - const target = controls.getTarget(new THREE.Vector3()); - controls.setTarget(target.x, 0, target.z, true); - setSelectedFloorItem(null); - } - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("dblclick", onDblClick); - canvasElement.removeEventListener("drop", onDrop); - canvasElement.removeEventListener("dragover", onDragOver); - }; - }, [deleteTool, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); - - useFrame(() => { - if (controls) - if (deleteTool && activeModule === "builder") { - // assetVisibility(itemsGroup, state.camera.position, renderDistance); - DeletableHoveredFloorItems( - state, - itemsGroup, - hoveredDeletableFloorItem, - setDeletableFloorItem - ); - } else if (!deleteTool) { - if (hoveredDeletableFloorItem.current) { - hoveredDeletableFloorItem.current = undefined; - setDeletableFloorItem(null); - } - } - }); - - return ; -}; - -export default FloorItemsGroup; diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx index 5b567af..a18e265 100644 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ b/app/src/modules/builder/groups/zoneGroup.tsx @@ -58,14 +58,14 @@ const ZoneGroup: React.FC = () => { `, fragmentShader: ` varying vec2 vUv; - uniform vec3 uColor; + uniform vec3 uOuterColor; void main(){ float alpha = 1.0 - vUv.y; - gl_FragColor = vec4(uColor, alpha); + gl_FragColor = vec4(uOuterColor, alpha); } `, uniforms: { - uColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) }, + uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) }, }, transparent: true, depthWrite: false, @@ -184,7 +184,7 @@ const ZoneGroup: React.FC = () => { const target: [number, number, number] | null = calculateCenter( zone.points ); - if (!target) return; + if (!target || zone.points.length < 4) return; const position = [target[0], 10, target[2]]; const input = { @@ -238,7 +238,7 @@ const ZoneGroup: React.FC = () => { const target: [number, number, number] | null = calculateCenter( zone.points ); - if (!target) return; + if (!target || zone.points.length < 4) return; const position = [target[0], 10, target[2]]; const input = { @@ -553,7 +553,7 @@ const ZoneGroup: React.FC = () => { const midpoint = new THREE.Vector3( (point1.x + point2.x) / 2, CONSTANTS.zoneConfig.height / 2 + - (zone.layer - 1) * CONSTANTS.zoneConfig.height, + (zone.layer - 1) * CONSTANTS.zoneConfig.height, (point1.z + point2.z) / 2 ); @@ -583,12 +583,13 @@ const ZoneGroup: React.FC = () => { // Ensure the polygon is closed if ( - coords2D.length >= 3 && + coords2D.length >= 4 && (coords2D[0][0] !== coords2D[coords2D.length - 1][0] || coords2D[0][1] !== coords2D[coords2D.length - 1][1]) ) { coords2D.push(coords2D[0]); } + if (coords2D.length < 4) return null; const polygon = turf.polygon([coords2D]); const center2D = turf.center(polygon).geometry.coordinates; @@ -648,12 +649,13 @@ const ZoneGroup: React.FC = () => { const coords2D = points3D.map((p: any) => [p[0], p[2]]); if ( - coords2D.length < 3 || + coords2D.length < 4 || coords2D[0][0] !== coords2D[coords2D.length - 1][0] || coords2D[0][1] !== coords2D[coords2D.length - 1][1] ) { coords2D.push(coords2D[0]); } + if (coords2D.length < 4) return null; const polygon = turf.polygon([coords2D]); const center2D = turf.center(polygon).geometry.coordinates; diff --git a/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx b/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx new file mode 100644 index 0000000..123c7e3 --- /dev/null +++ b/app/src/modules/builder/point/helpers/useAisleDragSnap.tsx @@ -0,0 +1,74 @@ +import { useCallback } from 'react'; +import * as THREE from 'three'; +import { useAisleStore } from '../../../../store/builder/useAisleStore'; + +const SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping in meters + +const CAN_SNAP = true; // Whether snapping is enabled or not + +export function useAislePointSnapping(point: Point) { + const { getConnectedPoints } = useAisleStore(); + + const snapPosition = useCallback((newPosition: [number, number, number]): { + position: [number, number, number], + isSnapped: boolean, + snapSources: THREE.Vector3[] + } => { + if (!CAN_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] }; + + const connectedPoints = getConnectedPoints(point.uuid); + if (connectedPoints.length === 0) { + return { + position: newPosition, + isSnapped: false, + snapSources: [] + }; + } + + const newPos = new THREE.Vector3(...newPosition); + let closestX: { pos: THREE.Vector3, dist: number } | null = null; + let closestZ: { pos: THREE.Vector3, dist: number } | null = null; + + for (const connectedPoint of connectedPoints) { + const cPos = new THREE.Vector3(...connectedPoint.position); + + const xDist = Math.abs(newPos.x - cPos.x); + const zDist = Math.abs(newPos.z - cPos.z); + + if (xDist < SNAP_DISTANCE_THRESHOLD) { + if (!closestX || xDist < closestX.dist) { + closestX = { pos: cPos, dist: xDist }; + } + } + + if (zDist < SNAP_DISTANCE_THRESHOLD) { + if (!closestZ || zDist < closestZ.dist) { + closestZ = { pos: cPos, dist: zDist }; + } + } + } + + const snappedPos = newPos.clone(); + const snapSources: THREE.Vector3[] = []; + + if (closestX) { + snappedPos.x = closestX.pos.x; + snapSources.push(closestX.pos.clone()); + } + + if (closestZ) { + snappedPos.z = closestZ.pos.z; + snapSources.push(closestZ.pos.clone()); + } + + const isSnapped = snapSources.length > 0; + + return { + position: [snappedPos.x, snappedPos.y, snappedPos.z], + isSnapped, + snapSources + }; + }, [point.uuid, getConnectedPoints]); + + return { snapPosition }; +} diff --git a/app/src/modules/builder/point/helpers/useDirectionalSnapping.tsx b/app/src/modules/builder/point/helpers/useDirectionalSnapping.tsx new file mode 100644 index 0000000..fffdc31 --- /dev/null +++ b/app/src/modules/builder/point/helpers/useDirectionalSnapping.tsx @@ -0,0 +1,53 @@ +import { useMemo } from 'react'; +import * as THREE from 'three'; + +const SNAP_ANGLE_THRESHOLD = 15; // Degrees within which to snap to orthogonal direction + +const SNAP_DISTANCE_THRESHOLD = 0.5; // Minimum distance to consider for directional snap + +const CAN_SNAP = true; // Whether snapping is enabled or not + +export const useDirectionalSnapping = ( + currentPoint: [number, number, number] | null, + previousPoint: [number, number, number] | null +): { position: [number, number, number], isSnapped: boolean } => { + return useMemo(() => { + if (!currentPoint || !previousPoint || !CAN_SNAP) return { position: currentPoint || [0, 0, 0], isSnapped: false }; // No snapping if no points + + const currentVec = new THREE.Vector2(currentPoint[0], currentPoint[2]); + const previousVec = new THREE.Vector2(previousPoint[0], previousPoint[2]); + const directionVec = new THREE.Vector2().subVectors(currentVec, previousVec); + + const angle = THREE.MathUtils.radToDeg(directionVec.angle()); + const normalizedAngle = (angle + 360) % 360; + const closestAngle = Math.round(normalizedAngle / 90) * 90 % 360; + + const angleDiff = Math.abs(normalizedAngle - closestAngle); + const shortestDiff = Math.min(angleDiff, 360 - angleDiff); + + if (shortestDiff <= SNAP_ANGLE_THRESHOLD) { + const distance = directionVec.length(); + const snappedDirection = new THREE.Vector2( + Math.cos(THREE.MathUtils.degToRad(closestAngle)), + Math.sin(THREE.MathUtils.degToRad(closestAngle)) + ).multiplyScalar(distance); + + const newPosition = new THREE.Vector2(previousPoint[0] + snappedDirection.x, previousPoint[2] + snappedDirection.y) + + if (newPosition.distanceTo(currentVec) > SNAP_DISTANCE_THRESHOLD) { + return { position: currentPoint, isSnapped: false }; + } + + return { + position: [ + previousPoint[0] + snappedDirection.x, + currentPoint[1], + previousPoint[2] + snappedDirection.y + ], + isSnapped: true + }; + } + + return { position: currentPoint, isSnapped: false }; + }, [currentPoint, previousPoint]); +}; \ No newline at end of file diff --git a/app/src/modules/builder/point/helpers/usePointSnapping.tsx b/app/src/modules/builder/point/helpers/usePointSnapping.tsx new file mode 100644 index 0000000..4d61b8a --- /dev/null +++ b/app/src/modules/builder/point/helpers/usePointSnapping.tsx @@ -0,0 +1,41 @@ +import { useCallback } from 'react'; +import { useAisleStore } from '../../../../store/builder/useAisleStore'; +import * as THREE from 'three'; + +const SNAP_THRESHOLD = 0.5; // Distance threshold for snapping in meters + +const CAN_SNAP = true; // Whether snapping is enabled or not + +export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => { + const { aisles } = useAisleStore(); + + const getAllOtherPoints = useCallback(() => { + if (!currentPoint) return []; + + return aisles.flatMap(aisle => + aisle.points.filter(point => point.uuid !== currentPoint.uuid) + ); + }, [aisles, currentPoint]); + + const checkSnapForAisle = useCallback((position: [number, number, number]) => { + if (!currentPoint || !CAN_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; + + const otherPoints = getAllOtherPoints(); + const currentVec = new THREE.Vector3(...position); + + for (const point of otherPoints) { + const pointVec = new THREE.Vector3(...point.position); + const distance = currentVec.distanceTo(pointVec); + + if (distance <= SNAP_THRESHOLD && currentPoint.pointType === 'Aisle') { + return { position: point.position, isSnapped: true, snappedPoint: point }; + } + } + + return { position: position, isSnapped: false, snappedPoint: null }; + }, [currentPoint, getAllOtherPoints]); + + return { + checkSnapForAisle, + }; +}; \ No newline at end of file diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx new file mode 100644 index 0000000..a6c22c9 --- /dev/null +++ b/app/src/modules/builder/point/point.tsx @@ -0,0 +1,163 @@ +import * as THREE from 'three'; +import * as Constants from '../../../types/world/worldConstants'; +import { useRef, useState, useEffect, useMemo } from 'react'; +import { useDeletePointOrLine, useToolMode } from '../../../store/builder/store'; +import { DragControls } from '@react-three/drei'; +import { useAisleStore } from '../../../store/builder/useAisleStore'; +import { useThree } from '@react-three/fiber'; +import { useBuilderStore } from '../../../store/builder/useBuilderStore'; +import { usePointSnapping } from './helpers/usePointSnapping'; +import { useAislePointSnapping } from './helpers/useAisleDragSnap'; + +function Point({ point }: { readonly point: Point }) { + const materialRef = useRef(null); + const { raycaster, camera, pointer } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); + const [isHovered, setIsHovered] = useState(false); + const { toolMode } = useToolMode(); + const { setPosition, removePoint } = useAisleStore(); + const { snapPosition } = useAislePointSnapping(point); + const { checkSnapForAisle } = usePointSnapping({ uuid: point.uuid, pointType: point.pointType, position: point.position }); + const { hoveredPoint, setHoveredPoint } = useBuilderStore(); + const { deletePointOrLine } = useDeletePointOrLine(); + + const boxScale: [number, number, number] = Constants.pointConfig.boxScale; + const defaultInnerColor = Constants.pointConfig.defaultInnerColor; + const defaultOuterColor = Constants.pointConfig.aisleOuterColor; + const defaultDeleteColor = Constants.pointConfig.deleteColor; + + useEffect(() => { + if (materialRef.current && (toolMode === 'move' || deletePointOrLine)) { + let innerColor; + let outerColor; + if (isHovered) { + innerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor; + outerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor; + } else { + innerColor = defaultInnerColor; + outerColor = defaultOuterColor; + } + materialRef.current.uniforms.uInnerColor.value.set(innerColor); + materialRef.current.uniforms.uOuterColor.value.set(outerColor); + materialRef.current.uniformsNeedUpdate = true; + } else if (materialRef.current && toolMode !== 'move') { + materialRef.current.uniforms.uInnerColor.value.set(defaultInnerColor); + materialRef.current.uniforms.uOuterColor.value.set(defaultOuterColor); + materialRef.current.uniformsNeedUpdate = true; + } + }, [isHovered, defaultInnerColor, defaultOuterColor, toolMode, deletePointOrLine, defaultDeleteColor]); + + const uniforms = useMemo(() => ({ + uOuterColor: { value: new THREE.Color(defaultOuterColor) }, + uInnerColor: { value: new THREE.Color(defaultInnerColor) }, + }), [defaultInnerColor, defaultInnerColor]); + + const handleDrag = (point: Point) => { + if (toolMode === 'move' && isHovered) { + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + const position = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (point.pointType === 'Aisle') { + if (position) { + const newPosition: [number, number, number] = [position.x, position.y, position.z]; + const aisleSnappedPosition = snapPosition(newPosition); + const finalSnappedPosition = checkSnapForAisle(aisleSnappedPosition.position); + + setPosition(point.uuid, finalSnappedPosition.position); + } + } + } + } + + const handleDragEnd = (point: Point) => { + if (deletePointOrLine) return; + console.log('point: ', point); + } + + const handlePointClick = (point: Point) => { + if (deletePointOrLine) { + const removedAisles = removePoint(point.uuid); + if (removedAisles.length > 0) { + setHoveredPoint(null); + console.log(removedAisles); + } + } + } + + useEffect(() => { + if (hoveredPoint && hoveredPoint.uuid !== point.uuid) { + setIsHovered(false); + } + }, [hoveredPoint]) + + if (!point) { + return null; + } + + return ( + { handleDrag(point) }} + onDragEnd={() => { handleDragEnd(point) }} + > + { + handlePointClick(point); + }} + onPointerOver={() => { + if (!hoveredPoint) { + setHoveredPoint(point); + setIsHovered(true) + } + }} + onPointerOut={() => { + if (hoveredPoint && hoveredPoint.uuid === point.uuid) { + setHoveredPoint(null); + } + setIsHovered(false) + }} + userData={point} + > + + borderThickness && vUv.x < 1.0 - borderThickness && vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { + gl_FragColor = vec4(uInnerColor, 1.0); // Inner square + } else { + gl_FragColor = vec4(uOuterColor, 1.0); // Border + } + } + ` + } + /> + + + ); +} + +export default Point; \ No newline at end of file diff --git a/app/src/modules/builder/point/referencePoint.tsx b/app/src/modules/builder/point/referencePoint.tsx new file mode 100644 index 0000000..ac1666c --- /dev/null +++ b/app/src/modules/builder/point/referencePoint.tsx @@ -0,0 +1,58 @@ +import * as THREE from 'three'; +import * as Constants from '../../../types/world/worldConstants'; +import { useRef, useMemo } from 'react'; + +function ReferencePoint({ point }: { readonly point: Point }) { + const materialRef = useRef(null); + + const boxScale: [number, number, number] = Constants.pointConfig.boxScale; + const defaultInnerColor = Constants.pointConfig.defaultInnerColor; + const defaultOuterColor = Constants.pointConfig.aisleOuterColor; + + const uniforms = useMemo(() => ({ + uOuterColor: { value: new THREE.Color(defaultOuterColor) }, + uInnerColor: { value: new THREE.Color(defaultInnerColor) }, + }), [defaultOuterColor, defaultInnerColor]); + + if (!point) { + return null; + } + + return ( + + + borderThickness && vUv.x < 1.0 - borderThickness && + vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { + gl_FragColor = vec4(uInnerColor, 1.0); // Inner square + } else { + gl_FragColor = vec4(uOuterColor, 1.0); // Border + } + } + `} + /> + + ); +} + +export default ReferencePoint; \ No newline at end of file diff --git a/app/src/modules/collaboration/comments/commentsGroup.tsx b/app/src/modules/collaboration/comments/commentsGroup.tsx index 6c10f3d..e1721ad 100644 --- a/app/src/modules/collaboration/comments/commentsGroup.tsx +++ b/app/src/modules/collaboration/comments/commentsGroup.tsx @@ -45,9 +45,14 @@ function CommentsGroup() { (intersect) => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("commentHolder") && !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" ); if (intersects.length > 0) { @@ -67,9 +72,14 @@ function CommentsGroup() { (intersect) => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("commentHolder") && !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" ); if (intersects.length > 0) { const position = new Vector3(intersects[0].point.x, Math.max(intersects[0].point.y, 0), intersects[0].point.z); diff --git a/app/src/modules/collaboration/socket/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx index 441bae4..2238178 100644 --- a/app/src/modules/collaboration/socket/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -7,7 +7,6 @@ import { useSocketStore, useActiveLayer, useWallItems, - useFloorItems, useLayers, useUpdateScene, useWalls, @@ -19,7 +18,6 @@ import { import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; -import TempLoader from "../../builder/geomentries/assets/tempLoader"; // import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import objectLineToArray from "../../builder/geomentries/lines/lineConvertions/objectLineToArray"; @@ -33,6 +31,9 @@ import RemoveConnectedLines from "../../builder/geomentries/lines/removeConnecte import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; +import { useAssetsStore } from "../../../store/builder/useAssetStore"; +import { useEventsStore } from "../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../store/simulation/useProductStore"; export default function SocketResponses({ floorPlanGroup, @@ -54,13 +55,13 @@ export default function SocketResponses({ const { activeLayer, setActiveLayer } = useActiveLayer(); const { wallItems, setWallItems } = useWallItems(); const { setLayers } = useLayers(); - const { setFloorItems } = useFloorItems(); const { setUpdateScene } = useUpdateScene(); const { setWalls } = useWalls(); const { setDeletedLines } = useDeletedLines(); const { setNewLines } = useNewLines(); const { zones, setZones } = useZones(); const { zonePoints, setZonePoints } = useZonePoints(); + const { addAsset, updateAsset, removeAsset } = useAssetsStore(); useEffect(() => { const email = localStorage.getItem("email"); @@ -96,243 +97,54 @@ export default function SocketResponses({ if (organization !== data.organization) { return; } + console.log('data.data: ', data); if (data.message === "Model created successfully") { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath( - "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" - ); - loader.setDRACOLoader(dracoLoader); - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - try { - isTempLoader.current = true; - const cachedModel = THREE.Cache.get(data.data.modelName); - let url; - if (cachedModel) { - // console.log(`Getting ${data.data.modelName} from cache`); - const model = cachedModel.scene.clone(); - model.uuid = data.data.modelUuid; - model.userData = { - name: data.data.modelName, - modelId: data.data.modelfileID, - modelUuid: data.data.modelUuid, - }; - model.position.set( - ...(data.data.position as [number, number, number]) - ); - model.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - modelfileID: data.data.modelfileID, - position: [...(data.data.position as [number, number, number])], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - gsap.to(model.position, { - y: data.data.position[1], - duration: 1.5, - ease: "power2.out", - }); - gsap.to(model.scale, { - x: 1, - y: 1, - z: 1, - duration: 1.5, - ease: "power2.out", - onComplete: () => { - echo.success("Model Added!"); - }, - }); - } else { - const indexedDBModel = await retrieveGLTF(data.data.modelName); - if (indexedDBModel) { - // console.log(`Getting ${data.data.modelName} from IndexedDB`); - url = URL.createObjectURL(indexedDBModel); - } else { - // console.log(`Getting ${data.data.modelName} from Backend`); - url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; - const modelBlob = await fetch(url).then((res) => res.blob()); - await storeGLTF(data.data.modelfileID, modelBlob); - } + const asset: Asset = { + modelUuid: data.data.modelUuid, + modelName: data.data.modelName, + assetId: data.data.modelfileID, + position: data.data.position, + rotation: [data.data.rotation.x, data.data.rotation.y, data.data.rotation.z], + isLocked: data.data.isLocked, + isCollidable: false, + isVisible: data.data.isVisible, + opacity: 1, } - if (url) { - loadModel(url); - } + addAsset(asset); + + echo.success("Added model through collaboration"); } catch (error) { - echo.error("Failed to update responce"); - console.error("Error fetching asset model:", error); - } - - function loadModel(url: string) { - loader.load( - url, - (gltf) => { - URL.revokeObjectURL(url); - THREE.Cache.remove(url); - const model = gltf.scene; - model.uuid = data.data.modelUuid; - model.userData = { - name: data.data.modelName, - modelId: data.data.modelfileID, - modelUuid: data.data.modelUuid, - }; - model.position.set( - ...(data.data.position as [number, number, number]) - ); - model.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - modelfileID: data.data.modelfileID, - position: [...(data.data.position as [number, number, number])], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem( - "FloorItems", - JSON.stringify(updatedItems) - ); - return updatedItems; - }); - - gsap.to(model.position, { - y: data.data.position[1], - duration: 1.5, - ease: "power2.out", - }); - gsap.to(model.scale, { - x: 1, - y: 1, - z: 1, - duration: 1.5, - ease: "power2.out", - onComplete: () => { - echo.success("Model Added!"); - }, - }); - - THREE.Cache.add(data.data.modelName, gltf); - }, - () => { - TempLoader( - new THREE.Vector3(...data.data.position), - isTempLoader, - tempLoader, - itemsGroup - ); - } - ); + echo.error("Failed to create model through collaboration"); } } else if (data.message === "Model updated successfully") { - itemsGroup.current?.children.forEach((item: THREE.Group) => { - if (item.uuid === data.data.modelUuid) { - item.position.set( - ...(data.data.position as [number, number, number]) - ); - item.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - } - }); + try { - setFloorItems((prevItems: Types.FloorItems) => { - if (!prevItems) { - return; + const asset: Asset = { + modelUuid: data.data.modelUuid, + modelName: data.data.modelName, + assetId: data.data.modelfileID, + position: data.data.position, + rotation: [data.data.rotation.x, data.data.rotation.y, data.data.rotation.z], + isLocked: data.data.isLocked, + isCollidable: false, + isVisible: data.data.isVisible, + opacity: 1, } - let updatedItem: any = null; - const updatedItems = prevItems.map((item) => { - if (item.modelUuid === data.data.modelUuid) { - updatedItem = { - ...item, - position: [...data.data.position] as [number, number, number], - rotation: { - x: data.data.rotation.x, - y: data.data.rotation.y, - z: data.data.rotation.z, - }, - }; - return updatedItem; - } - return item; + + updateAsset(asset.modelUuid, { + position: asset.position, + rotation: asset.rotation, }); - return updatedItems; - }); + + echo.success("Updated model through collaboration"); + } catch (error) { + echo.error("Failed to update model through collaboration"); + } + } else { + echo.error("Failed executing action from collaboration"); } }); @@ -344,28 +156,18 @@ export default function SocketResponses({ return; } if (data.message === "Model deleted successfully") { - const deletedUUID = data.data.modelUuid; - let items = JSON.parse(localStorage.getItem("FloorItems")!); + try { + const deletedUUID = data.data.modelUuid; - const updatedItems = items.filter( - (item: { modelUuid: string }) => item.modelUuid !== deletedUUID - ); + useEventsStore.getState().removeEvent(deletedUUID); + useProductStore.getState().deleteEvent(deletedUUID); - const storedItems = JSON.parse( - localStorage.getItem("FloorItems") || "[]" - ); - const updatedStoredItems = storedItems.filter( - (item: { modelUuid: string }) => item.modelUuid !== deletedUUID - ); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + removeAsset(deletedUUID); - itemsGroup.current.children.forEach((item: any) => { - if (item.uuid === deletedUUID) { - itemsGroup.current.remove(item); - } - }); - setFloorItems(updatedItems); - echo.success("Model Removed!"); + echo.success("Model Removed successfully through collaboration"); + } catch (error) { + echo.error("Failed to remove model through collaboration"); + } } }); @@ -886,7 +688,7 @@ export default function SocketResponses({ ); const material = new THREE.ShaderMaterial({ uniforms: { - uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border + uOuterColor: { value: new THREE.Color(pointColour) }, // Blue color for the border uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor), }, // White color for the inner square @@ -901,7 +703,7 @@ export default function SocketResponses({ `, fragmentShader: ` varying vec2 vUv; - uniform vec3 uColor; + uniform vec3 uOuterColor; uniform vec3 uInnerColor; void main() { @@ -911,7 +713,7 @@ export default function SocketResponses({ 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 + gl_FragColor = vec4(uOuterColor, 1.0); // Blue border } } `, diff --git a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx index 5802705..32fcce7 100644 --- a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx +++ b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx @@ -3,60 +3,98 @@ import { useMemo } from "react"; import * as THREE from "three"; import { useSelectedAssets } from "../../../../store/builder/store"; -const BoundingBox = ({ boundingBoxRef }: any) => { +interface BoundingBoxProps { + boundingBoxRef?: any; + isPerAsset?: boolean; +} + +const getBoxLines = (min: THREE.Vector3, max: THREE.Vector3) => [ + [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], +]; + +const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => { const { selectedAssets } = useSelectedAssets(); + const savedTheme: string = localStorage.getItem("theme") || "light"; - const { points, boxProps } = useMemo(() => { - if (selectedAssets.length === 0) return { points: [], boxProps: {} }; + const boxes = useMemo(() => { + if (selectedAssets.length === 0) return []; - const box = new THREE.Box3(); - selectedAssets.forEach((obj: any) => box.expandByObject(obj.clone())); + if (isPerAsset) { + return selectedAssets.map((obj: any) => { + const box = new THREE.Box3().setFromObject(obj.clone()); + const size = new THREE.Vector3(); + const center = new THREE.Vector3(); + box.getSize(size); + box.getCenter(center); - 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 halfSize = size.clone().multiplyScalar(0.5); - const min = center.clone().sub(halfSize); - const max = center.clone().add(halfSize); + return { + points: getBoxLines(min, max), + position: center.toArray(), + size: size.toArray(), + }; + }); + } else { + const box = new THREE.Box3(); + selectedAssets.forEach((obj: any) => box.expandByObject(obj.clone())); + const size = new THREE.Vector3(); + const center = new THREE.Vector3(); + box.getSize(size); + box.getCenter(center); - 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], + const halfSize = size.clone().multiplyScalar(0.5); + const min = center.clone().sub(halfSize); + const max = center.clone().add(halfSize); - [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], + return [ + { + points: getBoxLines(min, max), + position: center.toArray(), + size: size.toArray(), + }, + ]; + } + }, [selectedAssets, isPerAsset]); - [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 && ( - <> - - - + {boxes.map((box: any, index: number) => ( + + + + - - )} + + ))} ); }; diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index dd52900..f006870 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -1,20 +1,33 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; +import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; -const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { +const CopyPasteControls = ({ + 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(); const { addEvent } = useEventsStore(); + const { assets, addAsset } = useAssetsStore(); useEffect(() => { if (!camera || !scene || toggleView) return; @@ -63,7 +76,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas canvasElement.removeEventListener("keydown", onKeyDown); }; - }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, floorItems, rotatedObjects]); + }, [assets, camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, rotatedObjects]); useFrame(() => { if (pastedObjects.length > 0) { @@ -131,17 +144,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas 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); + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : "default"; - if (itemsGroupRef.current) { + pastedObjects.forEach(async (obj: THREE.Object3D) => { + if (obj) { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + obj.position.copy(worldPosition); const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: THREE.MathUtils.generateUUID(), + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, isLocked: false, @@ -316,15 +331,6 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas newFloorItem.eventData = eventData; - 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( @@ -362,17 +368,22 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas eventData: JSON.parse(JSON.stringify(eventData)) }; - itemsGroupRef.current.add(obj); + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + eventData: data.eventData + } + + addAsset(asset); } else { - 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 @@ -403,13 +414,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas socket.emit("v2:model-asset:add", data); - obj.userData = { - name: newFloorItem.modelName, - modelId: newFloorItem.modelfileID, - modelUuid: newFloorItem.modelUuid, - }; + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + } - itemsGroupRef.current.add(obj); + addAsset(asset); } } }); diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index 2dd9e37..1d011ef 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -1,21 +1,31 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; +import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // 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"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; -const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { +const DuplicationControls = ({ + 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(); const { addEvent } = useEventsStore(); + const { assets, addAsset } = useAssetsStore(); useEffect(() => { if (!camera || !scene || toggleView) return; @@ -61,7 +71,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb canvasElement.removeEventListener("keydown", onKeyDown); }; - }, [camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]); + }, [assets, camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, rotatedObjects]); useFrame(() => { if (duplicatedObjects.length > 0) { @@ -109,17 +119,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb 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); + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : "default"; - if (itemsGroupRef.current) { + duplicatedObjects.forEach(async (obj: THREE.Object3D) => { + if (obj) { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + obj.position.copy(worldPosition); const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: THREE.MathUtils.generateUUID(), + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, isLocked: false, @@ -293,15 +305,6 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb newFloorItem.eventData = eventData; - 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( @@ -332,24 +335,22 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb socket.emit("v2:model-asset:add", data); - obj.userData = { - name: newFloorItem.modelName, - modelId: newFloorItem.modelfileID, - modelUuid: newFloorItem.modelUuid, - eventData: JSON.parse(JSON.stringify(eventData)) - }; + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + eventData: data.eventData + } - itemsGroupRef.current.add(obj); + addAsset(asset); } else { - 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 @@ -380,13 +381,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb socket.emit("v2:model-asset:add", data); - obj.userData = { - name: newFloorItem.modelName, - modelId: newFloorItem.modelfileID, - modelUuid: newFloorItem.modelUuid, - }; + const asset: Asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.modelfileID, + position: data.position, + rotation: [data.rotation.x, data.rotation.y, data.rotation.z], + isLocked: data.isLocked, + isCollidable: false, + isVisible: data.isVisible, + opacity: 1, + } - itemsGroupRef.current.add(obj); + addAsset(asset); } } }); diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index a41755c..3a68e96 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -2,7 +2,6 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { - useFloorItems, useSelectedAssets, useSocketStore, useToggleView, @@ -12,15 +11,15 @@ import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { snapControls } from "../../../../utils/handleSnap"; import DistanceFindingControls from "./distanceFindingControls"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; +import { useProductContext } from "../../../simulation/products/productContext"; function MoveControls({ movedObjects, setMovedObjects, - itemsGroupRef, pastedObjects, setpastedObjects, duplicatedObjects, @@ -35,13 +34,14 @@ function MoveControls({ const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const { selectedProduct } = useSelectedProduct(); - const { floorItems, setFloorItems } = useFloorItems(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { socket } = useSocketStore(); - const itemsData = useRef([]); const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">(""); const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; + const { updateAsset } = useAssetsStore(); + const AssetGroup = useRef(undefined); const updateBackend = ( productName: string, @@ -58,11 +58,19 @@ function MoveControls({ }; useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; + if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; + const itemsGroup: any = scene.getObjectByName("Asset Group"); + AssetGroup.current = itemsGroup; + + if (!AssetGroup.current) { + console.error("Asset Group not found in the scene."); + return; + } + let isMoving = false; const onPointerDown = () => { @@ -91,15 +99,12 @@ function MoveControls({ clearSelection(); movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); - setMovedObjects([]); - itemsData.current = []; } setKeyEvent(""); }; @@ -119,9 +124,6 @@ function MoveControls({ if (keyCombination === "G") { if (selectedAssets.length > 0) { moveAssets(); - itemsData.current = floorItems.filter((item: { modelUuid: string }) => - selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) - ); } } @@ -130,15 +132,12 @@ function MoveControls({ clearSelection(); movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); - setMovedObjects([]); - itemsData.current = []; } }; @@ -157,7 +156,7 @@ function MoveControls({ canvasElement.removeEventListener("keydown", onKeyDown); canvasElement?.removeEventListener("keyup", onKeyUp); }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]); + }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]); let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; @@ -219,11 +218,6 @@ function MoveControls({ }); 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); @@ -234,17 +228,17 @@ function MoveControls({ if (movedObjects.length === 0) return; movedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); + if (obj && AssetGroup.current) { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); - selectionGroup.current.remove(obj); - obj.position.copy(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, + modelUuid: obj.userData.modelUuid, + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, @@ -287,10 +281,9 @@ function MoveControls({ } } - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; + updateAsset(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }); //REST @@ -322,12 +315,12 @@ function MoveControls({ socket.emit("v2:model-asset:add", data); - itemsGroupRef.current.add(obj); + AssetGroup.current.add(obj); } }); + echo.success("Object moved!"); - itemsData.current = []; clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 65536d0..0418d46 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -1,27 +1,40 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; +import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; +import { useProductContext } from "../../../simulation/products/productContext"; + +function RotateControls({ + rotatedObjects, + setRotatedObjects, + movedObjects, + setMovedObjects, + pastedObjects, + setpastedObjects, + duplicatedObjects, + setDuplicatedObjects, + selectionGroup +}: any) { -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 { selectedProduct } = useSelectedProduct(); - const { floorItems, setFloorItems } = useFloorItems(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { socket } = useSocketStore(); - const itemsData = useRef([]); const email = localStorage.getItem('email') const organization = (email?.split("@")[1])?.split(".")[0] ?? null; + const { updateAsset } = useAssetsStore(); + const AssetGroup = useRef(undefined); const updateBackend = ( productName: string, @@ -40,11 +53,19 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const prevPointerPosition = useRef(null); useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; + if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; + const itemsGroup: any = scene.getObjectByName("Asset Group"); + AssetGroup.current = itemsGroup; + + if (!AssetGroup.current) { + console.error("Asset Group not found in the scene."); + return; + } + let isMoving = false; const onPointerDown = () => { @@ -65,15 +86,12 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo clearSelection(); rotatedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); - setRotatedObjects([]); - itemsData.current = []; } }; @@ -82,7 +100,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo 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") { @@ -90,15 +107,13 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo clearSelection(); rotatedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); + if (AssetGroup.current) { + AssetGroup.current.attach(asset); } }); - setFloorItems([...floorItems, ...itemsData.current]); setRotatedObjects([]); - itemsData.current = []; } }; @@ -115,7 +130,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("keydown", onKeyDown); }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); + }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); useFrame(() => { if (rotatedObjects.length > 0) { @@ -146,9 +161,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo }); 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(); @@ -173,33 +185,31 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo if (rotatedObjects.length === 0) return; rotatedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - const worldQuaternion = new THREE.Quaternion(); + if (obj && AssetGroup.current) { + const worldPosition = new THREE.Vector3(); + const worldQuaternion = new THREE.Quaternion(); - obj.getWorldPosition(worldPosition); - obj.getWorldQuaternion(worldQuaternion); + obj.getWorldPosition(worldPosition); + obj.getWorldQuaternion(worldQuaternion); - selectionGroup.current.remove(obj); + selectionGroup.current.remove(obj); - obj.position.copy(worldPosition); - obj.quaternion.copy(worldQuaternion); - - - if (itemsGroupRef.current) { + obj.position.copy(worldPosition); + obj.quaternion.copy(worldQuaternion); const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, + modelUuid: obj.userData.modelUuid, + modelName: obj.userData.modelName, + modelfileID: obj.userData.assetId, position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, isLocked: false, isVisible: true }; if (obj.userData.eventData) { const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid); - const productData = useProductStore.getState().getEventByModelUuid(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid); + const productData = useProductStore.getState().getEventByModelUuid(selectedProductStore.getState().selectedProduct.productId, obj.userData.modelUuid); if (eventData) { useEventsStore.getState().updateEvent(obj.userData.modelUuid, { @@ -208,7 +218,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo }) } if (productData) { - const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + const event = useProductStore.getState().updateEvent(selectedProductStore.getState().selectedProduct.productId, obj.userData.modelUuid, { position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) @@ -221,15 +231,14 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo event ); } - + newFloorItem.eventData = eventData; } } - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; + updateAsset(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }); //REST @@ -261,12 +270,11 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo socket.emit("v2:model-asset:add", data); - itemsGroupRef.current.add(obj); + AssetGroup.current.add(obj); } }); echo.success("Object rotated!"); - itemsData.current = []; clearSelection(); } diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 16f992b..0c158a9 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionHelper } from "./selectionHelper"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/builder/store"; +import { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/builder/store"; import BoundingBox from "./boundingBoxHelper"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; @@ -15,10 +15,10 @@ import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; const SelectionControls: React.FC = () => { - const { camera, controls, gl, scene, pointer } = useThree(); - const itemsGroupRef = useRef(undefined); + const { camera, controls, gl, scene, raycaster, pointer } = useThree(); const selectionGroup = useRef() as Types.RefGroup; const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); @@ -28,9 +28,9 @@ const SelectionControls: React.FC = () => { const [pastedObjects, setpastedObjects] = useState([]); const [duplicatedObjects, setDuplicatedObjects] = useState([]); const boundingBoxRef = useRef(); - const { floorItems, setFloorItems } = useFloorItems(); const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); + const { removeAsset } = useAssetsStore(); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); useEffect(() => { @@ -39,21 +39,16 @@ const SelectionControls: React.FC = () => { const canvasElement = gl.domElement; canvasElement.tabIndex = 0; - const itemsGroup: any = scene.getObjectByName("itemsGroup"); - itemsGroupRef.current = itemsGroup; - + let isDragging = false; + let isLeftMouseDown = false; let isSelecting = false; let isRightClick = false; let rightClickMoved = false; let isCtrlSelecting = false; + let isShiftSelecting = false; const helper = new SelectionHelper(gl); - if (!itemsGroup) { - echo.warn("itemsGroup not found in the scene."); - return; - } - const onPointerDown = (event: PointerEvent) => { if (event.button === 2) { isRightClick = true; @@ -61,6 +56,9 @@ const SelectionControls: React.FC = () => { } else if (event.button === 0) { isSelecting = false; isCtrlSelecting = event.ctrlKey; + isShiftSelecting = event.shiftKey; + isLeftMouseDown = true; + isDragging = false; if (event.ctrlKey && duplicatedObjects.length === 0) { if (controls) (controls as any).enabled = false; selectionBox.startPoint.set(pointer.x, pointer.y, 0); @@ -72,6 +70,9 @@ const SelectionControls: React.FC = () => { if (isRightClick) { rightClickMoved = true; } + if (isLeftMouseDown) { + isDragging = true; + } isSelecting = true; if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { selectionBox.endPoint.set(pointer.x, pointer.y, 0); @@ -79,7 +80,7 @@ const SelectionControls: React.FC = () => { }; const onPointerUp = (event: PointerEvent) => { - if (event.button === 2) { + if (event.button === 2 && !event.ctrlKey && !event.shiftKey) { isRightClick = false; if (!rightClickMoved) { clearSelection(); @@ -93,10 +94,60 @@ const SelectionControls: React.FC = () => { 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)) { + } else if (!isSelecting && selectedAssets.length > 0 && ((!event.ctrlKey && !event.shiftKey && pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { clearSelection(); helper.enabled = true; isCtrlSelecting = false; + } else if (controls) { + (controls as any).enabled = true; + } + + if (!isDragging && isLeftMouseDown && isShiftSelecting && event.shiftKey) { + isShiftSelecting = false; + isLeftMouseDown = false; + isDragging = false; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" + ); + if (intersects.length > 0) { + const intersect = intersects[0]; + const intersectObject = intersect.object; + + let currentObject: THREE.Object3D | null = intersectObject; + while (currentObject) { + if (currentObject.userData.modelUuid) { + break; + } + currentObject = currentObject.parent || null; + } + + if (currentObject) { + const updatedSelections = new Set(selectedAssets); + + if (updatedSelections.has(currentObject)) { + updatedSelections.delete(currentObject); + } else { + updatedSelections.add(currentObject); + } + + const selected = Array.from(updatedSelections); + + setSelectedAssets(selected); + } + } } }; @@ -145,7 +196,7 @@ const SelectionControls: React.FC = () => { helper.enabled = false; helper.dispose(); }; - }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); + }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule,]); useEffect(() => { if (activeModule !== "builder") { @@ -166,10 +217,10 @@ const SelectionControls: React.FC = () => { let selectedObjects = selectionBox.select(); let Objects = new Set(); - selectedObjects.map((object) => { + selectedObjects.forEach((object) => { let currentObject: THREE.Object3D | null = object; while (currentObject) { - if (currentObject.userData.modelId) { + if (currentObject.userData.modelUuid) { Objects.add(currentObject); break; } @@ -213,14 +264,14 @@ const SelectionControls: React.FC = () => { selectedAssets.forEach((selectedMesh: THREE.Object3D) => { //REST - // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); + // const response = await deleteFloorItem(organization, selectedMesh.userData.modelUuid, selectedMesh.userData.modelName); //SOCKET const data = { organization: organization, - modelUuid: selectedMesh.uuid, - modelName: selectedMesh.userData.name, + modelUuid: selectedMesh.userData.modelUuid, + modelName: selectedMesh.userData.modelName, socketId: socket.id, }; @@ -244,11 +295,11 @@ const SelectionControls: React.FC = () => { } }); - itemsGroupRef.current?.remove(selectedMesh); }); - const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); - setFloorItems(updatedItems); + selectedUUIDs.forEach((uuid: string) => { + removeAsset(uuid); + }); } echo.success("Selected models removed!"); clearSelection(); @@ -262,13 +313,13 @@ const SelectionControls: React.FC = () => { - + - + - + - + ); }; diff --git a/app/src/modules/scene/controls/transformControls/transformControls.tsx b/app/src/modules/scene/controls/transformControls/transformControls.tsx index d1167f4..3c66589 100644 --- a/app/src/modules/scene/controls/transformControls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -1,6 +1,6 @@ import { TransformControls } from "@react-three/drei"; import * as THREE from "three"; -import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useFloorItems, useActiveTool, useSocketStore } from "../../../../store/builder/store"; +import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store"; import { useThree } from "@react-three/fiber"; import * as Types from '../../../../types/world/worldTypes'; @@ -8,9 +8,10 @@ import { useEffect, useState } from "react"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; +import { useAssetsStore } from "../../../../store/builder/useAssetStore"; +import { useProductContext } from "../../../simulation/products/productContext"; export default function TransformControl() { const state = useThree(); @@ -18,10 +19,11 @@ export default function TransformControl() { const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { setObjectPosition } = useObjectPosition(); const { setObjectRotation } = useObjectRotation(); - const { setFloorItems } = useFloorItems(); const { activeTool } = useActiveTool(); const { socket } = useSocketStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { updateAsset, getAssetById } = useAssetsStore(); const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; @@ -60,97 +62,76 @@ export default function TransformControl() { 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, + const asset = getAssetById(selectedFloorItem?.uuid); + if (asset) { + if (asset.eventData) { + const eventData = useEventsStore.getState().getEventByModelUuid(asset.modelUuid); + const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, asset.modelUuid); + + if (eventData) { + useEventsStore.getState().updateEvent(asset.modelUuid, { position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number], - rotation: { x: selectedFloorItem.rotation.x, y: selectedFloorItem.rotation.y, z: selectedFloorItem.rotation.z }, - }; - return updatedItem; + rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z] as [number, number, number], + }); } - return item; - }); - if (updatedItem && selectedFloorItem) { - if (updatedItem.eventData) { - const eventData = useEventsStore.getState().getEventByModelUuid(updatedItem.modelUuid); - const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, updatedItem.modelUuid); - if (eventData) { - useEventsStore.getState().updateEvent(updatedItem.modelUuid, { - position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], - rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z], - }); - } + if (productData) { + const event = useProductStore + .getState() + .updateEvent( + selectedProduct.productId, + asset.modelUuid, + { + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number], + rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z] as [number, number, number], + } + ); - if (productData) { - const event = useProductStore - .getState() - .updateEvent( - selectedProduct.productId, - updatedItem.modelUuid, - { - position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], - rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z], - } - ); - - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - - updatedItem.eventData = eventData; + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); } } - - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), updatedItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - // REST - - // setFloorItemApi( - // organization, - // updatedItem.modelUuid, - // updatedItem.modelName, - // updatedItem.modelfileid, - // [selectedFloorItem.position.x, 0, 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, - modelfileID: updatedItem.modelfileID, - position: [selectedFloorItem.position.x, 0, 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; - }); + + updateAsset(asset.modelUuid, { + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], + rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z] as [number, number, number], + }); + + //REST + + // await setFloorItemApi( + // organization, + // asset.modelUuid, + // asset.modelName, + // [selectedFloorItem.position.x, 0, selectedFloorItem.position.z, + // { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z }, + // asset.assetId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modelUuid: asset.modelUuid, + modelName: asset.modelName, + modelfileID: asset.assetId, + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number], + 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); + } } useEffect(() => { diff --git a/app/src/modules/scene/environment/shadow.tsx b/app/src/modules/scene/environment/shadow.tsx index 59b348b..d8c8002 100644 --- a/app/src/modules/scene/environment/shadow.tsx +++ b/app/src/modules/scene/environment/shadow.tsx @@ -6,7 +6,6 @@ import { useElevation, useShadows, useSunPosition, - useFloorItems, useWallItems, useTileDistance, } from "../../../store/builder/store"; @@ -26,14 +25,13 @@ export default function Shadows() { const { controls, gl } = useThree(); const { elevation, setElevation } = useElevation(); const { azimuth, setAzimuth } = useAzimuth(); - const { floorItems } = useFloorItems(); const { wallItems } = useWallItems(); const { planeValue } = useTileDistance(); useEffect(() => { gl.shadowMap.enabled = true; gl.shadowMap.type = THREE.PCFShadowMap; - }, [gl, floorItems, wallItems]); + }, [gl, wallItems]); useEffect(() => { if (lightRef.current && targetRef.current) { diff --git a/app/src/modules/scene/gizmo/gizmo.tsx b/app/src/modules/scene/gizmo/gizmo.tsx new file mode 100644 index 0000000..2a590e2 --- /dev/null +++ b/app/src/modules/scene/gizmo/gizmo.tsx @@ -0,0 +1,63 @@ +import { useEffect, useRef } from 'react' +import { useThree, useFrame } from '@react-three/fiber' +import { ViewportGizmo } from 'three-viewport-gizmo' +import { PerspectiveCamera, WebGLRenderer, Scene } from 'three' + +type Controls = { + enabled: boolean + setPosition: (...args: number[]) => void + getTarget: (target: any) => void + addEventListener: (type: string, listener: (...args: any[]) => void) => void +} + +export const Gizmo = () => { + const { camera, gl, scene, controls } = useThree<{ + camera: PerspectiveCamera + gl: WebGLRenderer + scene: Scene + controls?: Controls + }>() + + const gizmoRef = useRef(null) + + useEffect(() => { + const gizmo = new ViewportGizmo(camera, gl, {}) + gizmoRef.current = gizmo + + const resize = () => { + const width = window.innerWidth + const height = window.innerHeight - 1 + camera.aspect = width / height + camera.updateProjectionMatrix() + gl.setSize(width, height) + gizmo.update() + } + + if (controls) { + gizmo.addEventListener('start', () => { controls.enabled = false }) + gizmo.addEventListener('end', () => { controls.enabled = true }) + gizmo.addEventListener('change', () => { + controls.setPosition(...camera.position.toArray()) + }) + controls.addEventListener('update', () => { + controls.getTarget(gizmo.target) + gizmo.update() + }) + } + + window.addEventListener('resize', resize) + + return () => { + window.removeEventListener('resize', resize) + } + }, [camera, gl, scene, controls]) + + useFrame(() => { + if (gizmoRef.current) { + gl.render(scene, camera) + gizmoRef.current.render() + } + }) + + return null +} \ No newline at end of file diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 426e199..276f0bf 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -1,4 +1,3 @@ -import * as THREE from "three"; import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing"; import { BlendFunction } from "postprocessing"; import { @@ -6,16 +5,17 @@ import { useSelectedWallItem, useSelectedFloorItem, } from "../../../store/builder/store"; -import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; -import { useEffect } from "react"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useEffect } from "react"; +import { useBuilderStore } from "../../../store/builder/useBuilderStore"; export default function PostProcessing() { - const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); - const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); - const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { deletableFloorItem } = useDeletableFloorItem(); + const { selectedWallItem } = useSelectedWallItem(); + const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventSphere } = useSelectedEventSphere(); + const { selectedAisle } = useBuilderStore(); function flattenChildren(children: any[]) { const allChildren: any[] = []; @@ -28,79 +28,104 @@ export default function PostProcessing() { return allChildren; } + useEffect(() => { + // console.log('selectedFloorItem: ', selectedFloorItem); + }, [selectedFloorItem]) + + useEffect(() => { + // console.log('selectedFloorItem: ', deletableFloorItem); + }, [deletableFloorItem]) + + useEffect(() => { + // console.log('selectedAisle: ', selectedAisle); + }, [selectedAisle]) + return ( - <> - - + + {selectedAisle && ( + - {deletableFloorItem && ( - - )} - {selectedWallItem && ( - - )} - {selectedFloorItem && ( - - )} - {selectedEventSphere && ( - - )} - - + )} + {deletableFloorItem && ( + + )} + {selectedWallItem && ( + + )} + {selectedFloorItem && ( + + )} + {selectedEventSphere && ( + + )} + ); } diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index d8d3447..7777a30 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -1,6 +1,8 @@ import { useMemo } from "react"; import { Canvas } from "@react-three/fiber"; +import { Color } from "three"; import { KeyboardControls } from "@react-three/drei"; +import { SceneProvider } from "./sceneContext"; import Builder from "../builder/builder"; import Visualization from "../visualization/visualization"; @@ -8,7 +10,7 @@ import Setup from "./setup/setup"; import Simulation from "../simulation/simulation"; import Collaboration from "../collaboration/collaboration"; -export default function Scene() { +export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Comparison Layout' }) { const map = useMemo(() => [ { name: "forward", keys: ["ArrowUp", "w", "W"] }, { name: "backward", keys: ["ArrowDown", "s", "S"] }, @@ -17,24 +19,25 @@ export default function Scene() { ], []); return ( - - { - e.preventDefault(); - }} - > - - - - - - - - - - - + + + { + e.preventDefault(); + }} + onCreated={(e) => { + e.scene.background = new Color(0x19191d); + }} + > + + + + + + + + ); -} +} \ No newline at end of file diff --git a/app/src/modules/scene/sceneContext.tsx b/app/src/modules/scene/sceneContext.tsx new file mode 100644 index 0000000..0145b45 --- /dev/null +++ b/app/src/modules/scene/sceneContext.tsx @@ -0,0 +1,61 @@ +import { createContext, useContext, useMemo } from 'react'; +import { createMaterialStore, MaterialStoreType } from '../../store/simulation/useMaterialStore'; +import { createArmBotStore, ArmBotStoreType } from '../../store/simulation/useArmBotStore'; +import { createMachineStore, MachineStoreType } from '../../store/simulation/useMachineStore'; +import { createConveyorStore, ConveyorStoreType } from '../../store/simulation/useConveyorStore'; +import { createVehicleStore, VehicleStoreType } from '../../store/simulation/useVehicleStore'; +import { createStorageUnitStore, StorageUnitStoreType } from '../../store/simulation/useStorageUnitStore'; + +type SceneContextValue = { + materialStore: MaterialStoreType; + armBotStore: ArmBotStoreType; + machineStore: MachineStoreType; + conveyorStore: ConveyorStoreType; + vehicleStore: VehicleStoreType; + storageUnitStore: StorageUnitStoreType; + layout: 'Main Layout' | 'Comparison Layout'; +}; + +const SceneContext = createContext(null); + +export function SceneProvider({ + children, + layout +}: { + readonly children: React.ReactNode; + readonly layout: 'Main Layout' | 'Comparison Layout'; +}) { + const materialStore = useMemo(() => createMaterialStore(), []); + const armBotStore = useMemo(() => createArmBotStore(), []); + const machineStore = useMemo(() => createMachineStore(), []); + const conveyorStore = useMemo(() => createConveyorStore(), []); + const vehicleStore = useMemo(() => createVehicleStore(), []); + const storageUnitStore = useMemo(() => createStorageUnitStore(), []); + + const contextValue = useMemo(() => ( + { + materialStore, + armBotStore, + machineStore, + conveyorStore, + vehicleStore, + storageUnitStore, + layout + } + ), [materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout]); + + return ( + + {children} + + ); +} + +// Base hook to get the context +export function useSceneContext() { + const context = useContext(SceneContext); + if (!context) { + throw new Error('useSceneContext must be used within a SceneProvider'); + } + return context; +} \ No newline at end of file diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index a70dd8a..1da41b3 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -51,7 +51,13 @@ const MeasurementTool = () => { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" ); if (intersects.length > 0) { @@ -106,7 +112,13 @@ const MeasurementTool = () => { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" ); if (intersects.length > 0) { diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDefaultHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDefaultHandler.ts index ef8160e..b8d1207 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useDefaultHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDefaultHandler.ts @@ -1,8 +1,9 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; export function useDefaultHandler() { - const { getMaterialById } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { getMaterialById } = materialStore(); const defaultLogStatus = (materialUuid: string, status: string) => { echo.info(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts index e21936a..63fd0e5 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef } from "react"; import { useFrame } from "@react-three/fiber"; import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore, useAnimationPlaySpeed } from "../../../../../store/usePlayButtonStore"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; interface DelayInstance { initialDelay: number; @@ -19,7 +19,8 @@ export function useDelayHandler() { const { isPaused } = usePauseButtonStore(); const { isReset } = useResetButtonStore(); const { speed } = useAnimationPlaySpeed(); - const { setIsPaused } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { setIsPaused } = materialStore(); const activeDelays = useRef>(new Map()); const lastUpdateTimeRef = useRef(performance.now()); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts index 48cc3e7..2175a97 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts @@ -1,8 +1,9 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; export function useDespawnHandler() { - const { getMaterialById, removeMaterial, setEndTime } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { getMaterialById, removeMaterial, setEndTime } = materialStore(); const deSpawnLogStatus = (materialUuid: string, status: string) => { echo.info(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index 4abfcd3..d546b12 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -1,11 +1,10 @@ import { useCallback, useEffect, useState } from "react"; import * as THREE from 'three'; import { useFrame } from "@react-three/fiber"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; -import { useConveyorStore } from "../../../../../store/simulation/useConveyorStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; interface SpawnInstance { lastSpawnTime: number | null; @@ -23,14 +22,16 @@ interface SpawnInstance { } export function useSpawnHandler() { - const { addMaterial } = useMaterialStore(); - const { getConveyorById } = useConveyorStore(); + const { materialStore, conveyorStore } = useSceneContext(); + const { addMaterial } = materialStore(); + const { getConveyorById } = conveyorStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); const { speed } = useAnimationPlaySpeed(); const { isReset } = useResetButtonStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const [activeSpawns, setActiveSpawns] = useState>(new Map()); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts index 0f9af26..432293c 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts @@ -1,8 +1,9 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; export function useSwapHandler() { - const { getMaterialById, setMaterial } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { getMaterialById, setMaterial } = materialStore(); const swapLogStatus = (materialUuid: string, status: string) => { echo.info(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/actions/machine/actionHandler/useProcessHandler.ts b/app/src/modules/simulation/actions/machine/actionHandler/useProcessHandler.ts index cfd0c5a..378975a 100644 --- a/app/src/modules/simulation/actions/machine/actionHandler/useProcessHandler.ts +++ b/app/src/modules/simulation/actions/machine/actionHandler/useProcessHandler.ts @@ -1,14 +1,15 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; -import { useMachineStore } from "../../../../../store/simulation/useMachineStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; export function useProcessHandler() { - const { getMaterialById, setMaterial } = useMaterialStore(); - const { addCurrentAction } = useMachineStore(); + const { materialStore, machineStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { getMaterialById, setMaterial } = materialStore(); + const { addCurrentAction } = machineStore(); const { getModelUuidByActionUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const processLogStatus = (materialUuid: string, status: string) => { echo.log(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/actions/roboticArm/actionHandler/usePickAndPlaceHandler.ts b/app/src/modules/simulation/actions/roboticArm/actionHandler/usePickAndPlaceHandler.ts index b134d78..cfa62f9 100644 --- a/app/src/modules/simulation/actions/roboticArm/actionHandler/usePickAndPlaceHandler.ts +++ b/app/src/modules/simulation/actions/roboticArm/actionHandler/usePickAndPlaceHandler.ts @@ -1,14 +1,15 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; -import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; export function usePickAndPlaceHandler() { - const { getMaterialById } = useMaterialStore(); - const { addCurrentAction } = useArmBotStore(); + const { materialStore, armBotStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { getMaterialById } = materialStore(); + const { addCurrentAction } = armBotStore(); const { getModelUuidByActionUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const pickAndPlaceLogStatus = (materialUuid: string, status: string) => { echo.warn(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts index 3eb79af..5d4ea93 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -1,20 +1,19 @@ import { useCallback, useState, useEffect, useRef } from "react"; import { useFrame } from "@react-three/fiber"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; -import { useStorageUnitStore } from "../../../../../store/simulation/useStorageUnitStore"; -import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore"; -import { useVehicleStore } from "../../../../../store/simulation/useVehicleStore"; import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore, useAnimationPlaySpeed } from "../../../../../store/usePlayButtonStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; export function useRetrieveHandler() { - const { addMaterial } = useMaterialStore(); + const { materialStore, armBotStore, vehicleStore, storageUnitStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { addMaterial } = materialStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = useProductStore(); - const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = useStorageUnitStore(); - const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = useVehicleStore(); - const { selectedProduct } = useSelectedProduct(); - const { getArmBotById, addCurrentAction } = useArmBotStore(); + const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = storageUnitStore(); + const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore(); + const { selectedProduct } = selectedProductStore(); + const { getArmBotById, addCurrentAction } = armBotStore(); const { isPlaying } = usePlayButtonStore(); const { speed } = useAnimationPlaySpeed(); const { isPaused } = usePauseButtonStore(); diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts index 0e8a920..2656457 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts @@ -1,14 +1,15 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; -import { useStorageUnitStore } from "../../../../../store/simulation/useStorageUnitStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; export function useStoreHandler() { - const { getMaterialById, removeMaterial, setEndTime } = useMaterialStore(); - const { addCurrentMaterial, updateCurrentLoad } = useStorageUnitStore(); + const { materialStore, storageUnitStore } = useSceneContext(); + const { getMaterialById, removeMaterial, setEndTime } = materialStore(); + const { addCurrentMaterial, updateCurrentLoad } = storageUnitStore(); const { getModelUuidByActionUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const storeLogStatus = (materialUuid: string, status: string) => { echo.info(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/actions/vehicle/actionHandler/useTravelHandler.ts b/app/src/modules/simulation/actions/vehicle/actionHandler/useTravelHandler.ts index c661883..fe3dd4a 100644 --- a/app/src/modules/simulation/actions/vehicle/actionHandler/useTravelHandler.ts +++ b/app/src/modules/simulation/actions/vehicle/actionHandler/useTravelHandler.ts @@ -1,14 +1,15 @@ import { useCallback } from "react"; -import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; -import { useVehicleStore } from "../../../../../store/simulation/useVehicleStore"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; export function useTravelHandler() { - const { getMaterialById } = useMaterialStore(); + const { materialStore, vehicleStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { getMaterialById } = materialStore(); const { getModelUuidByActionUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); - const { incrementVehicleLoad, addCurrentMaterial } = useVehicleStore(); + const { selectedProduct } = selectedProductStore(); + const { incrementVehicleLoad, addCurrentMaterial } = vehicleStore(); const travelLogStatus = (materialUuid: string, status: string) => { echo.info(`${materialUuid}, ${status}`); diff --git a/app/src/modules/simulation/analysis/ROI/roiData.tsx b/app/src/modules/simulation/analysis/ROI/roiData.tsx index f1ddda5..4d6444d 100644 --- a/app/src/modules/simulation/analysis/ROI/roiData.tsx +++ b/app/src/modules/simulation/analysis/ROI/roiData.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState } from 'react' import { useInputValues, useProductionCapacityData, useROISummaryData } from '../../../../store/builder/store'; -import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { usePlayButtonStore } from '../../../../store/usePlayButtonStore'; +import { useProductContext } from '../../products/productContext'; export default function ROIData() { + const { selectedProductStore } = useProductContext(); const { inputValues } = useInputValues(); const { productionCapacityData } = useProductionCapacityData() - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const { isPlaying } = usePlayButtonStore(); const { setRoiSummaryData } = useROISummaryData(); useEffect(() => { diff --git a/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx b/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx index 19d8fd8..6b9062f 100644 --- a/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx +++ b/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx @@ -1,25 +1,22 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { useEffect } from 'react'; import { useProductStore } from '../../../../store/simulation/useProductStore'; import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences'; -import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; import { useMachineCount, useMachineUptime, useMaterialCycle, useProcessBar, useThroughPutData } from '../../../../store/builder/store'; -import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; -import { useMachineStore } from '../../../../store/simulation/useMachineStore'; -import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; -import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore'; -import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; -import { usePauseButtonStore, usePlayButtonStore } from '../../../../store/usePlayButtonStore'; +import { usePlayButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useProductContext } from '../../products/productContext'; export default function ThroughPutData() { - const { selectedProduct } = useSelectedProduct(); + const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { products, getProductById } = useProductStore(); - const { armBots } = useArmBotStore(); - const { vehicles } = useVehicleStore(); - const { machines } = useMachineStore(); - const { conveyors } = useConveyorStore(); - const { storageUnits } = useStorageUnitStore(); - const { materialHistory } = useMaterialStore(); + const { armBots } = armBotStore(); + const { vehicles } = vehicleStore(); + const { machines } = machineStore(); + const { conveyors } = conveyorStore(); + const { storageUnits } = storageUnitStore(); + const { materialHistory } = materialStore(); const { machineCount, setMachineCount } = useMachineCount(); const { machineActiveTime, setMachineActiveTime } = useMachineUptime(); const { materialCycleTime, setMaterialCycleTime } = useMaterialCycle(); diff --git a/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts b/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts index 4870613..34407c9 100644 --- a/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts +++ b/app/src/modules/simulation/conveyor/eventManager/useConveyorEventManager.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; import { useFrame } from '@react-three/fiber'; -import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; type ConveyorCallback = { conveyorId: string; @@ -9,7 +9,8 @@ type ConveyorCallback = { }; export function useConveyorEventManager() { - const { getConveyorById } = useConveyorStore(); + const { conveyorStore } = useSceneContext(); + const { getConveyorById } = conveyorStore(); const callbacksRef = useRef([]); const isMonitoringRef = useRef(false); const { isPlaying } = usePlayButtonStore(); diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx index 76277f0..67db5b9 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -1,17 +1,18 @@ import { useEffect } from 'react' -import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; -import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; import { useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useProductContext } from '../../../products/productContext'; // import { findConveyorSubsequence } from '../../../simulator/functions/getConveyorSequencesInProduct'; -function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) { +function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) { const { getProductById } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); - const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { materialStore, conveyorStore } = useSceneContext(); + const { getMaterialsByCurrentModelUuid, materials } = materialStore(); const { isReset } = useResetButtonStore(); - const { setConveyorPaused } = useConveyorStore(); + const { setConveyorPaused } = conveyorStore(); useEffect(() => { const product = getProductById(selectedProduct.productId); diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx index 4bc4252..75557ed 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx @@ -1,10 +1,11 @@ import React from 'react' import ConveyorInstance from './conveyorInstance/conveyorInstance' -import { useConveyorStore } from '../../../../store/simulation/useConveyorStore' +import { useSceneContext } from '../../../scene/sceneContext'; function ConveyorInstances() { - const { conveyors } = useConveyorStore(); + const { conveyorStore } = useSceneContext(); + const { conveyors } = conveyorStore(); return ( <> diff --git a/app/src/modules/simulation/events/arrows/arrows.tsx b/app/src/modules/simulation/events/arrows/arrows.tsx index 943b614..951435e 100644 --- a/app/src/modules/simulation/events/arrows/arrows.tsx +++ b/app/src/modules/simulation/events/arrows/arrows.tsx @@ -10,7 +10,7 @@ interface ConnectionLine { trigger: TriggerSchema; } -export function Arrows({ connections }: { connections: ConnectionLine[] }) { +export function Arrows({ connections }: { readonly connections: ConnectionLine[] }) { const [hoveredLineKey, setHoveredLineKey] = useState(null); const groupRef = useRef(null); const { scene } = useThree(); diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index c1b7d23..89513ec 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -2,7 +2,6 @@ import { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; @@ -10,13 +9,15 @@ import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../st import { useThree } from "@react-three/fiber"; import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; +import { useProductContext } from "../../../products/productContext"; function PointsCreator() { const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); + const { selectedProductStore } = useProductContext(); const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getEventByModelUuid: getEventByModelUuidFromProduct2, getPointByUuid: getPointByUuidFromProduct } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const { activeModule } = useModuleStore(); const transformRef = useRef(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); diff --git a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx index 2a420ae..09048b4 100644 --- a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx +++ b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx @@ -5,13 +5,13 @@ import { useSubModuleStore } from "../../../../store/useModuleStore"; import { useSelectedAction, useSelectedAsset } from "../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; -import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { handleAddEventToProduct } from "../points/functions/handleAddEventToProduct"; import { QuadraticBezierLine } from "@react-three/drei"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { useDeleteTool } from "../../../../store/builder/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import { ArrowOnQuadraticBezier, Arrows } from "../arrows/arrows"; +import { useProductContext } from "../../products/productContext"; interface ConnectionLine { id: string; @@ -23,9 +23,10 @@ interface ConnectionLine { function TriggerConnector() { const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); + const { selectedProductStore } = useProductContext(); const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, removeTrigger, addEvent, getEventByModelUuid, getPointUuidByActionUuid, getProductById } = useProductStore(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const [hoveredLineKey, setHoveredLineKey] = useState(null); const groupRefs = useRef>({}); const [helperlineColor, setHelperLineColor] = useState("red"); @@ -367,12 +368,18 @@ function TriggerConnector() { 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.name.includes("agv-collider") && + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" && !intersect.object.name.includes("ArrowWithTube") && !intersect.object.parent?.name.includes("Zone") && - !(intersect.object.type === "GridHelper") && - !(intersect.object.type === "Line2") + intersect.object.type !== "Line2" ); let point: THREE.Vector3 | null = null; diff --git a/app/src/modules/simulation/machine/eventManager/useMachineEventManager.ts b/app/src/modules/simulation/machine/eventManager/useMachineEventManager.ts index 35c6418..66dda67 100644 --- a/app/src/modules/simulation/machine/eventManager/useMachineEventManager.ts +++ b/app/src/modules/simulation/machine/eventManager/useMachineEventManager.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; import { useFrame } from '@react-three/fiber'; -import { useMachineStore } from '../../../../store/simulation/useMachineStore'; import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; type MachineCallback = { machineId: string; @@ -9,7 +9,8 @@ type MachineCallback = { }; export function useMachineEventManager() { - const { getMachineById } = useMachineStore(); + const { machineStore } = useSceneContext(); + const { getMachineById } = machineStore(); const callbacksRef = useRef([]); const isMonitoringRef = useRef(false); const { isPlaying } = usePlayButtonStore(); diff --git a/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx b/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx index 8f1505f..d510784 100644 --- a/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx +++ b/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx @@ -1,7 +1,6 @@ import { useEffect, useRef } from 'react'; -import { useMachineStore } from '../../../../../store/simulation/useMachineStore'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; - +import { useSceneContext } from '../../../../scene/sceneContext'; interface MachineAnimatorProps { currentPhase: string; @@ -19,7 +18,8 @@ const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machine const animationFrameId = useRef(null); const pauseTimeRef = useRef(null); const { isPaused } = usePauseButtonStore(); - const { removeCurrentAction } = useMachineStore(); + const { machineStore } = useSceneContext(); + const { removeCurrentAction } = machineStore(); const { isReset } = useResetButtonStore(); const { isPlaying } = usePlayButtonStore(); const { speed } = useAnimationPlaySpeed(); diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx index ae8c97f..2356e76 100644 --- a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx @@ -1,12 +1,12 @@ import { useEffect, useRef, useState } from 'react' -import { useMachineStore } from '../../../../../store/simulation/useMachineStore'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import MachineAnimator from '../animator/machineAnimator'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; -import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useProductContext } from '../../../products/productContext'; -function MachineInstance({ machineDetail }: { machineDetail: MachineStatus }) { +function MachineInstance({ machineDetail }: { readonly machineDetail: MachineStatus }) { const [currentPhase, setCurrentPhase] = useState('idle'); let isIncrememtable = useRef(true); const idleTimeRef = useRef(0); @@ -16,8 +16,10 @@ function MachineInstance({ machineDetail }: { machineDetail: MachineStatus }) { const isSpeedRef = useRef(0); const isPausedRef = useRef(false); const { isPlaying } = usePlayButtonStore(); - const { machines, setMachineState, setMachineActive, incrementIdleTime, incrementActiveTime, resetTime } = useMachineStore(); - const { selectedProduct } = useSelectedProduct(); + const { machineStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { machines, setMachineState, setMachineActive, incrementIdleTime, incrementActiveTime, resetTime } = machineStore(); + const { selectedProduct } = selectedProductStore(); const { getActionByUuid } = useProductStore(); const { triggerPointActions } = useTriggerHandler(); const { speed } = useAnimationPlaySpeed(); diff --git a/app/src/modules/simulation/machine/instances/machineInstances.tsx b/app/src/modules/simulation/machine/instances/machineInstances.tsx index e228b01..eac048f 100644 --- a/app/src/modules/simulation/machine/instances/machineInstances.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstances.tsx @@ -1,10 +1,11 @@ import React from "react"; import MachineInstance from "./machineInstance/machineInstance"; -import { useMachineStore } from "../../../../store/simulation/useMachineStore"; import MachineContentUi from "../../ui3d/MachineContentUi"; +import { useSceneContext } from "../../../scene/sceneContext"; function MachineInstances() { - const { machines } = useMachineStore(); + const { machineStore } = useSceneContext(); + const { machines } = machineStore(); return ( <> {machines.map((machine: MachineStatus) => ( diff --git a/app/src/modules/simulation/materials/collisionDetection/materialCollitionDetector.tsx b/app/src/modules/simulation/materials/collisionDetection/materialCollitionDetector.tsx index 68fbcc2..a5cf242 100644 --- a/app/src/modules/simulation/materials/collisionDetection/materialCollitionDetector.tsx +++ b/app/src/modules/simulation/materials/collisionDetection/materialCollitionDetector.tsx @@ -1,14 +1,15 @@ import { useEffect, useRef } from 'react'; import * as THREE from 'three'; -import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; import { useThree, useFrame } from '@react-three/fiber'; import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; import { CameraControls } from '@react-three/drei'; +import { useSceneContext } from '../../../scene/sceneContext'; const collisionWorker = new Worker(new URL('../../../../services/simulation/webWorkers/collisionWorker.ts', import.meta.url)); export default function MaterialCollisionDetector() { - const { materials } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { materials } = materialStore(); const { scene, controls, camera } = useThree(); const positionsRef = useRef>({}); const sizesRef = useRef>({}); @@ -99,6 +100,7 @@ export default function MaterialCollisionDetector() { const currentPos = new THREE.Vector3().copy(camera.position); const target = new THREE.Vector3(); + if (!controls) return; (controls as CameraControls).getTarget(target); const direction = new THREE.Vector3().subVectors(target, currentPos).normalize(); diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx index 4afe359..5cf20c7 100644 --- a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react'; import * as THREE from 'three'; import { useFrame, useThree } from '@react-three/fiber'; import { usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; -import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; interface MaterialAnimatorProps { matRef: React.RefObject; @@ -20,7 +20,8 @@ function MaterialAnimator({ const { scene } = useThree(); const [targetPosition, setTargetPosition] = useState(null); const [isAnimating, setIsAnimating] = useState(false); - const { getConveyorById } = useConveyorStore(); + const { conveyorStore } = useSceneContext(); + const { getConveyorById } = conveyorStore(); const animationState = useRef({ startTime: 0, startPosition: new THREE.Vector3(), diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index fce425a..9826ad9 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -1,18 +1,19 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' +import { useMemo, useRef } from 'react' import * as THREE from 'three'; import MaterialAnimator from '../animator/materialAnimator'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; -import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { MaterialModel } from '../material/materialModel'; import { useThree } from '@react-three/fiber'; import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; +import { useProductContext } from '../../../products/productContext'; -function MaterialInstance({ material }: { material: MaterialSchema }) { +function MaterialInstance({ material }: { readonly material: MaterialSchema }) { const matRef: any = useRef(); const { scene } = useThree(); + const { selectedProductStore } = useProductContext(); const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid, getTriggerByUuid, getActionByPointUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const { speed } = useAnimationPlaySpeed(); const { triggerPointActions } = useTriggerHandler(); diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx index 5ad4913..88b127f 100644 --- a/app/src/modules/simulation/materials/instances/materialInstances.tsx +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -1,9 +1,10 @@ import React, { useEffect } from 'react' import MaterialInstance from './instance/materialInstance' -import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; +import { useSceneContext } from '../../../scene/sceneContext'; function MaterialInstances() { - const { materials, materialHistory } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { materials, materialHistory } = materialStore(); useEffect(() => { // console.log('materials: ', materials); diff --git a/app/src/modules/simulation/materials/materials.tsx b/app/src/modules/simulation/materials/materials.tsx index dfcfeb2..0adc327 100644 --- a/app/src/modules/simulation/materials/materials.tsx +++ b/app/src/modules/simulation/materials/materials.tsx @@ -1,11 +1,12 @@ import { useEffect } from 'react' import MaterialInstances from './instances/materialInstances' import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; -import { useMaterialStore } from '../../../store/simulation/useMaterialStore'; import MaterialCollisionDetector from './collisionDetection/materialCollitionDetector'; +import { useSceneContext } from '../../scene/sceneContext'; function Materials() { - const { clearMaterials } = useMaterialStore(); + const { materialStore } = useSceneContext(); + const { clearMaterials } = materialStore(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); diff --git a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx deleted file mode 100644 index 715aa37..0000000 --- a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { useThree } from '@react-three/fiber' -import { useEffect } from 'react' -import { Object3D } from 'three'; -import { useSubModuleStore } from '../../../../store/useModuleStore'; -import { useLeftData, useTopData } from '../../../../store/visualization/useZone3DWidgetStore'; -import { useEventsStore } from '../../../../store/simulation/useEventsStore'; -import { useProductStore } from '../../../../store/simulation/useProductStore'; -import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; -import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore'; - -function AddOrRemoveEventsInProducts() { - const { gl, raycaster, scene } = useThree(); - const { subModule } = useSubModuleStore(); - const { setTop } = useTopData(); - const { setLeft } = useLeftData(); - const { getIsEventInProduct } = useProductStore(); - const { getEventByModelUuid } = useEventsStore(); - const { selectedProduct } = useSelectedProduct(); - const { selectedAsset, setSelectedAsset, clearSelectedAsset } = useSelectedAsset(); - - useEffect(() => { - - const canvasElement = gl.domElement; - - let drag = false; - let isRightMouseDown = false; - - const onMouseDown = (evt: MouseEvent) => { - if (selectedAsset) { - clearSelectedAsset(); - } - if (evt.button === 2) { - isRightMouseDown = true; - drag = false; - } - }; - - const onMouseUp = (evt: MouseEvent) => { - if (evt.button === 2) { - isRightMouseDown = false; - } - } - - const onMouseMove = () => { - if (isRightMouseDown) { - drag = true; - } - }; - - const handleRightClick = (evt: MouseEvent) => { - if (drag) return; - evt.preventDefault(); - const canvasElement = gl.domElement; - if (!canvasElement) return; - - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") && - !(intersect.object?.parent?.name.includes('zones')) && - !(intersect.object?.parent?.name.includes('Zone')) - ); - if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { - let currentObject = intersects[0].object; - - while (currentObject) { - if (currentObject.name === "Scene") { - break; - } - currentObject = currentObject.parent as Object3D; - } - if (currentObject) { - const isInProduct = getIsEventInProduct(selectedProduct.productId, currentObject.uuid); - - if (isInProduct) { - const event = getEventByModelUuid(currentObject.uuid); - if (event) { - setSelectedAsset(event) - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = evt.clientX - canvasRect.left; - const relativeY = evt.clientY - canvasRect.top; - - setTop(relativeY); - setLeft(relativeX); - } else { - clearSelectedAsset() - } - } else { - const event = getEventByModelUuid(currentObject.uuid); - if (event) { - setSelectedAsset(event) - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = evt.clientX - canvasRect.left; - const relativeY = evt.clientY - canvasRect.top; - - setTop(relativeY); - setLeft(relativeX); - } else { - clearSelectedAsset() - } - } - - } - } else { - clearSelectedAsset() - } - - }; - - if (subModule === 'simulations') { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener('contextmenu', handleRightClick); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener('contextmenu', handleRightClick); - }; - - }, [gl, subModule, selectedProduct, selectedAsset]); - - return ( - <> - ) -} - -export default AddOrRemoveEventsInProducts \ No newline at end of file diff --git a/app/src/modules/simulation/products/productContext.tsx b/app/src/modules/simulation/products/productContext.tsx new file mode 100644 index 0000000..ba95435 --- /dev/null +++ b/app/src/modules/simulation/products/productContext.tsx @@ -0,0 +1,37 @@ +import { createContext, useContext, useMemo } from 'react'; +import { createSelectedProductStore, SelectedProductType } from '../../../store/simulation/useSimulationStore'; + +type ProductContextValue = { + selectedProductStore: SelectedProductType, +}; + +const ProductContext = createContext(null); + +export function ProductProvider({ + children, +}: { + readonly children: React.ReactNode; +}) { + const selectedProductStore = useMemo(() => createSelectedProductStore(), []); + + const contextValue = useMemo(() => ( + { + selectedProductStore + } + ), [selectedProductStore]); + + return ( + + {children} + + ); +} + +// Base hook to get the context +export function useProductContext() { + const context = useContext(ProductContext); + if (!context) { + throw new Error('useProductContext must be used within a ProductProvider'); + } + return context; +} \ No newline at end of file diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index f0fd1f4..7e019a5 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -1,27 +1,39 @@ import * as THREE from 'three'; import { useEffect } from 'react'; import { useProductStore } from '../../../store/simulation/useProductStore'; -import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; -import AddOrRemoveEventsInProducts from './events/addOrRemoveEventsInProducts'; import { upsertProductOrEventApi } from '../../../services/simulation/products/UpsertProductOrEventApi'; import { getAllProductsApi } from '../../../services/simulation/products/getallProductsApi'; -import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; -import { useArmBotStore } from '../../../store/simulation/useArmBotStore'; -import { useConveyorStore } from '../../../store/simulation/useConveyorStore'; -import { useMachineStore } from '../../../store/simulation/useMachineStore'; -import { useStorageUnitStore } from '../../../store/simulation/useStorageUnitStore'; import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../scene/sceneContext'; +import { useProductContext } from './productContext'; +import { useComparisonProduct, useMainProduct } from '../../../store/simulation/useSimulationStore'; function Products() { + const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout } = useSceneContext(); const { products, getProductById, addProduct, setProducts } = useProductStore(); - const { selectedProduct, setSelectedProduct } = useSelectedProduct(); - const { addVehicle, clearvehicles } = useVehicleStore(); - const { addArmBot, clearArmBots } = useArmBotStore(); - const { addMachine, clearMachines } = useMachineStore(); - const { addConveyor, clearConveyors } = useConveyorStore(); - const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = useStorageUnitStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct, setSelectedProduct } = selectedProductStore(); + const { addVehicle, clearvehicles } = vehicleStore(); + const { addArmBot, clearArmBots } = armBotStore(); + const { addMachine, clearMachines } = machineStore(); + const { addConveyor, clearConveyors } = conveyorStore(); + const { setCurrentMaterials, clearStorageUnits, updateCurrentLoad, addStorageUnit } = storageUnitStore(); const { isReset } = useResetButtonStore(); const { isPlaying } = usePlayButtonStore(); + const { mainProduct } = useMainProduct(); + const { comparisonProduct } = useComparisonProduct(); + + useEffect(() => { + if (layout === 'Main Layout' && mainProduct) { + setSelectedProduct(mainProduct.productId, mainProduct.productName); + } + }, [mainProduct]) + + useEffect(() => { + if (layout === 'Comparison Layout' && comparisonProduct) { + setSelectedProduct(comparisonProduct.productId, comparisonProduct.productName); + } + }, [comparisonProduct]) useEffect(() => { const email = localStorage.getItem('email') @@ -32,10 +44,14 @@ function Products() { const name = 'Product 1'; addProduct(name, id); upsertProductOrEventApi({ productName: name, productId: id, organization: organization }) - setSelectedProduct(id, name); + if (layout === 'Main Layout') { + setSelectedProduct(id, name); + } } else { setProducts(data); - setSelectedProduct(data[0].productId, data[0].productName); + if (layout === 'Main Layout') { + setSelectedProduct(data[0].productId, data[0].productName); + } } }) }, []) @@ -106,7 +122,7 @@ function Products() { addStorageUnit(selectedProduct.productId, event); if (event.point.action.actionType === 'retrieve') { - const storageAction = event.point.action as StorageAction; + const storageAction = event.point.action; const materials = Array.from({ length: storageAction.storageCapacity }, () => ({ materialType: storageAction.materialType || 'Default material', materialId: THREE.MathUtils.generateUUID() @@ -127,8 +143,6 @@ function Products() { return ( <> - - ) } diff --git a/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts b/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts index 59004fa..3f07ea9 100644 --- a/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts +++ b/app/src/modules/simulation/roboticArm/eventManager/useArmBotEventManager.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; import { useFrame } from '@react-three/fiber'; -import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; type ArmBotCallback = { armBotId: string; @@ -9,7 +9,8 @@ type ArmBotCallback = { }; export function useArmBotEventManager() { - const { getArmBotById } = useArmBotStore(); + const {armBotStore} = useSceneContext(); + const { getArmBotById } = armBotStore(); const callbacksRef = useRef([]); const isMonitoringRef = useRef(false); const { isPlaying } = usePlayButtonStore(); diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 52213fe..1ad53de 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useFrame, useThree } from '@react-three/fiber'; import * as THREE from 'three'; import { Line, Text } from '@react-three/drei'; diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index ce5a280..40cd0f1 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import * as THREE from "three"; import { useThree } from "@react-three/fiber"; import IKInstance from '../ikInstance/ikInstance'; @@ -6,15 +6,12 @@ import RoboticArmAnimator from '../animator/roboticArmAnimator'; import MaterialAnimator from '../animator/materialAnimator'; import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb"; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; -import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; -import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; -import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; -import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useProductContext } from '../../../products/productContext'; -function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { +function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) { const [currentPhase, setCurrentPhase] = useState<(string)>("init"); const [path, setPath] = useState<[number, number, number][]>([]); @@ -28,11 +25,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const isSpeedRef = useRef(null); let startTime: number; - const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore(); - const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); - const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore(); - const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { materialStore, armBotStore, vehicleStore, storageUnitStore } = useSceneContext(); + const { setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = armBotStore(); + const { decrementVehicleLoad, removeLastMaterial } = vehicleStore(); + const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = storageUnitStore(); + const { getMaterialById, setIsVisible, setIsPaused } = materialStore(); + const { selectedProduct } = selectedProductStore(); const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore(); const { triggerPointActions } = useTriggerHandler(); const { isPlaying } = usePlayButtonStore(); diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx index f57fbcd..b1295a3 100644 --- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -1,10 +1,11 @@ +import { useSceneContext } from "../../../scene/sceneContext"; import RoboticArmInstance from "./armInstance/roboticArmInstance"; -import { useArmBotStore } from "../../../../store/simulation/useArmBotStore"; // import RoboticArmContentUi from "../../ui3d/RoboticArmContentUi"; import React from "react"; function RoboticArmInstances() { - const { armBots } = useArmBotStore(); + const {armBotStore} = useSceneContext(); + const { armBots } = armBotStore(); return ( <> diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index ffdb312..1946fce 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -1,12 +1,13 @@ import { useEffect, useState } from "react"; -import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import RoboticArmInstances from "./instances/roboticArmInstances"; import ArmBotUI from "../spatialUI/arm/armBotUI"; +import { useSceneContext } from "../../scene/sceneContext"; function RoboticArm() { - const { getArmBotById } = useArmBotStore(); + const {armBotStore} = useSceneContext(); + const { getArmBotById } = armBotStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { isPlaying } = usePlayButtonStore(); const [isArmBotSelected, setIsArmBotSelected] = useState(false); diff --git a/app/src/modules/simulation/simulator/functions/checkActiveRoboticArmsInSubsequence.ts b/app/src/modules/simulation/simulator/functions/checkActiveRoboticArmsInSubsequence.ts index 797f836..bfa8bfa 100644 --- a/app/src/modules/simulation/simulator/functions/checkActiveRoboticArmsInSubsequence.ts +++ b/app/src/modules/simulation/simulator/functions/checkActiveRoboticArmsInSubsequence.ts @@ -1,5 +1,5 @@ +import { useSceneContext } from "../../../scene/sceneContext"; import { extractTriggersFromPoint } from "./extractTriggersFromPoint"; -import { useArmBotStore } from "../../../../store/simulation/useArmBotStore"; export function getRoboticArmSequencesInProduct( product: { @@ -80,7 +80,8 @@ export function findRoboticArmSubsequence( // React component/hook that uses the pure functions export function useCheckActiveRoboticArmsInSubsequence() { - const { getArmBotById } = useArmBotStore(); + const {armBotStore} = useSceneContext(); + const { getArmBotById } = armBotStore(); return function (product: { productName: string; diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index aa0b9f6..ecd50b2 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -3,12 +3,13 @@ import { useProductStore } from '../../../store/simulation/useProductStore'; import { useActionHandler } from '../actions/useActionHandler'; import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; import { determineExecutionOrder } from './functions/determineExecutionOrder'; -import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; +import { useProductContext } from '../products/productContext'; function Simulator() { + const { selectedProductStore } = useProductContext(); const { products, getProductById } = useProductStore(); const { handleAction } = useActionHandler(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); diff --git a/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx index 0775157..5624582 100644 --- a/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx +++ b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx @@ -1,9 +1,8 @@ import React, { useEffect, useState } from 'react'; -import { useSelectedAction, useSelectedEventSphere, useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { useSelectedAction, useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore'; import { useGLTF } from '@react-three/drei'; import { useThree } from '@react-three/fiber'; import { useProductStore } from '../../../../store/simulation/useProductStore'; -import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; import PickDropPoints from './PickDropPoints'; import useDraggableGLTF from './useDraggableGLTF'; import * as THREE from 'three'; @@ -11,6 +10,8 @@ import * as THREE from 'three'; import armPick from "../../../../assets/gltf-glb/ui/arm_ui_pick.glb"; import armDrop from "../../../../assets/gltf-glb/ui/arm_ui_drop.glb"; import { upsertProductOrEventApi } from '../../../../services/simulation/products/UpsertProductOrEventApi'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useProductContext } from '../../products/productContext'; type Positions = { pick: [number, number, number]; @@ -21,10 +22,12 @@ type Positions = { const ArmBotUI = () => { const { getEventByModelUuid, updateAction, getActionByUuid } = useProductStore(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { scene } = useThree(); const { selectedAction } = useSelectedAction(); - const { armBots } = useArmBotStore(); + const { armBotStore } = useSceneContext(); + const { armBots } = armBotStore(); const armUiPick = useGLTF(armPick) as any; const armUiDrop = useGLTF(armDrop) as any; diff --git a/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts b/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts index e1f7fbf..8794ffd 100644 --- a/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts @@ -4,21 +4,19 @@ import { ThreeEvent, useThree } from "@react-three/fiber"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedEventData, - useSelectedProduct, } from "../../../../store/simulation/useSimulationStore"; +import { useProductContext } from "../../products/productContext"; type OnUpdateCallback = (object: THREE.Object3D) => void; export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { - const { getEventByModelUuid, updateAction, getActionByUuid } = - useProductStore(); + const { getEventByModelUuid } = useProductStore(); const { selectedEventData } = useSelectedEventData(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); const { camera, gl, controls } = useThree(); const activeObjRef = useRef(null); - const planeRef = useRef( - new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) - ); + const planeRef = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); const offsetRef = useRef(new THREE.Vector3()); const initialPositionRef = useRef(new THREE.Vector3()); diff --git a/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx b/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx index db55344..8765d14 100644 --- a/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx +++ b/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx @@ -5,16 +5,16 @@ import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedEventSphere, useIsDragging, - useSelectedProduct, useIsRotating, } from "../../../../store/simulation/useSimulationStore"; -import { useVehicleStore } from "../../../../store/simulation/useVehicleStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { DoubleSide, Group, Plane, Vector3 } from "three"; import startPoint from "../../../../assets/gltf-glb/ui/arrow_green.glb"; import startEnd from "../../../../assets/gltf-glb/ui/arrow_red.glb"; +import { useSceneContext } from "../../../scene/sceneContext"; +import { useProductContext } from "../../products/productContext"; const VehicleUI = () => { const { scene: startScene } = useGLTF(startPoint) as any; @@ -23,8 +23,10 @@ const VehicleUI = () => { const endMarker = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedProduct } = useSelectedProduct(); - const { vehicles, getVehicleById } = useVehicleStore(); + const { selectedProductStore } = useProductContext(); + const { vehicleStore } = useSceneContext(); + const { selectedProduct } = selectedProductStore(); + const { vehicles, getVehicleById } = vehicleStore(); const { updateEvent } = useProductStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([ 0, 1, 0, diff --git a/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx index 29b6ea0..72ad0e9 100644 --- a/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx +++ b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx @@ -1,17 +1,18 @@ import React from 'react' import StorageUnitInstance from './storageUnitInstance/storageUnitInstance' -import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore' import StorageContentUi from '../../ui3d/StorageContentUi'; +import { useSceneContext } from '../../../scene/sceneContext'; function StorageUnitInstances() { - const { storageUnits } = useStorageUnitStore(); + const { storageUnitStore } = useSceneContext(); + const { storageUnits } = storageUnitStore(); return ( <> {storageUnits.map((storageUnit: StorageUnitStatus) => ( - + ))} diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 57fc38a..e349880 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -1,32 +1,29 @@ import { useCallback } from 'react'; import { useActionHandler } from '../../actions/useActionHandler'; import { useProductStore } from '../../../../store/simulation/useProductStore'; -import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; -import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; -import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; -import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; -import { useMachineStore } from '../../../../store/simulation/useMachineStore'; -import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore'; import { useArmBotEventManager } from '../../roboticArm/eventManager/useArmBotEventManager'; -import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; import { useConveyorEventManager } from '../../conveyor/eventManager/useConveyorEventManager'; import { useVehicleEventManager } from '../../vehicle/eventManager/useVehicleEventManager'; import { useMachineEventManager } from '../../machine/eventManager/useMachineEventManager'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useProductContext } from '../../products/productContext'; export function useTriggerHandler() { + const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); const { handleAction } = useActionHandler(); - const { selectedProduct } = useSelectedProduct(); + const { selectedProduct } = selectedProductStore(); const { getEventByTriggerUuid, getEventByModelUuid, getActionByUuid, getModelUuidByActionUuid } = useProductStore(); - const { getArmBotById } = useArmBotStore(); - const { getConveyorById } = useConveyorStore(); + const { getArmBotById } = armBotStore(); + const { getConveyorById } = conveyorStore(); const { addArmBotToMonitor } = useArmBotEventManager(); const { addConveyorToMonitor } = useConveyorEventManager(); const { addVehicleToMonitor } = useVehicleEventManager(); const { addMachineToMonitor } = useMachineEventManager(); - const { getVehicleById } = useVehicleStore(); - const { getMachineById } = useMachineStore(); - const { getStorageUnitById } = useStorageUnitStore(); - const { setCurrentLocation, setNextLocation, setPreviousLocation, getMaterialById, setIsPaused, setIsVisible, setEndTime } = useMaterialStore(); + const { getVehicleById } = vehicleStore(); + const { getMachineById } = machineStore(); + const { getStorageUnitById } = storageUnitStore(); + const { getMaterialById, setCurrentLocation, setNextLocation, setPreviousLocation, setIsPaused, setIsVisible, setEndTime } = materialStore(); const handleTrigger = (trigger: TriggerSchema, action: Action, materialId?: string) => { diff --git a/app/src/modules/simulation/vehicle/eventManager/useVehicleEventManager.ts b/app/src/modules/simulation/vehicle/eventManager/useVehicleEventManager.ts index 7b2bb08..625beff 100644 --- a/app/src/modules/simulation/vehicle/eventManager/useVehicleEventManager.ts +++ b/app/src/modules/simulation/vehicle/eventManager/useVehicleEventManager.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react'; import { useFrame } from '@react-three/fiber'; -import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; type VehicleCallback = { vehicleId: string; @@ -9,7 +9,8 @@ type VehicleCallback = { }; export function useVehicleEventManager() { - const { getVehicleById } = useVehicleStore(); + const { vehicleStore } = useSceneContext(); + const { getVehicleById } = vehicleStore(); const callbacksRef = useRef([]); const isMonitoringRef = useRef(false); const { isPlaying } = usePlayButtonStore(); diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 9d51a8d..64501c1 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -3,7 +3,7 @@ import { useFrame, useThree } from '@react-three/fiber'; import * as THREE from 'three'; import { Line } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; interface VehicleAnimatorProps { path: [number, number, number][]; @@ -15,8 +15,9 @@ interface VehicleAnimatorProps { agvDetail: VehicleStatus; } -function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset, startUnloadingProcess }: VehicleAnimatorProps) { - const { getVehicleById } = useVehicleStore(); +function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset, startUnloadingProcess }: Readonly) { + const { vehicleStore } = useSceneContext(); + const { getVehicleById } = vehicleStore(); const { isPaused } = usePauseButtonStore(); const { isPlaying } = usePlayButtonStore(); const { speed } = useAnimationPlaySpeed(); diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 7c7de03..38d38a8 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -1,30 +1,28 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import VehicleAnimator from '../animator/vehicleAnimator'; import * as THREE from 'three'; import { NavMeshQuery } from '@recast-navigation/core'; import { useNavMesh } from '../../../../../store/builder/store'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; -import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; -import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; -import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; -import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; -import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; -import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; import MaterialAnimator from '../animator/materialAnimator'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useProductContext } from '../../../products/productContext'; function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) { const { navMesh } = useNavMesh(); const { isPlaying } = usePlayButtonStore(); - const { removeMaterial, setEndTime } = useMaterialStore(); - const { getStorageUnitById } = useStorageUnitStore(); - const { getArmBotById } = useArmBotStore(); - const { getConveyorById } = useConveyorStore(); + const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore } = useSceneContext(); + const { removeMaterial, setEndTime } = materialStore(); + const { getStorageUnitById } = storageUnitStore(); + const { getArmBotById } = armBotStore(); + const { getConveyorById } = conveyorStore(); const { triggerPointActions } = useTriggerHandler(); const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); - const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = useVehicleStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = vehicleStore(); const [currentPhase, setCurrentPhase] = useState('stationed'); const [path, setPath] = useState<[number, number, number][]>([]); diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 1c7cd51..708a4e1 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -1,10 +1,11 @@ import React from "react"; import VehicleInstance from "./instance/vehicleInstance"; -import { useVehicleStore } from "../../../../store/simulation/useVehicleStore"; import VehicleContentUi from "../../ui3d/VehicleContentUi"; +import { useSceneContext } from "../../../scene/sceneContext"; function VehicleInstances() { - const { vehicles } = useVehicleStore(); + const { vehicleStore } = useSceneContext(); + const { vehicles } = vehicleStore(); return ( <> diff --git a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx index a1bbfb0..54f669b 100644 --- a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx @@ -44,7 +44,7 @@ export default function PolygonGenerator({ }); const polygons = turf.polygonize(turf.featureCollection(validLineFeatures)); - + renderWallGeometry(wallPoints); if (polygons.features.length > 0) { @@ -115,6 +115,7 @@ export default function PolygonGenerator({ const quaternion = new THREE.Quaternion(); quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction); wallMesh.quaternion.copy(quaternion); + wallMesh.name = "agv-collider"; groupRef.current?.add(wallMesh); } diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index b9293cd..9e594a5 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -1,12 +1,13 @@ import { useEffect, useState } from "react"; -import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import VehicleInstances from "./instances/vehicleInstances"; import VehicleUI from "../spatialUI/vehicle/vehicleUI"; +import { useSceneContext } from "../../scene/sceneContext"; function Vehicles() { - const { getVehicleById } = useVehicleStore(); + const { vehicleStore } = useSceneContext(); + const { getVehicleById } = vehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { isPlaying } = usePlayButtonStore(); const [isVehicleSelected, setIsVehicleSelected] = useState(false); diff --git a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx index ccb88c4..601309a 100644 --- a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx +++ b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx @@ -138,9 +138,15 @@ export default function Dropped3dWidgets() { .filter( (intersect) => !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !(intersect.object.type === "GridHelper") + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" ); if (intersects.length > 0) { @@ -174,9 +180,15 @@ export default function Dropped3dWidgets() { .filter( (intersect) => !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !(intersect.object.type === "GridHelper") + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("zonePlane") && + !intersect.object.name.includes("SelectionGroup") && + !intersect.object.name.includes("selectionAssetGroup") && + !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && + !intersect.object.name.includes("SelectionGroupBoundingBox") && + !intersect.object.name.includes("SelectionGroupBoundingLine") && + intersect.object.type !== "GridHelper" ); // Update widget's position in memory if (intersects.length > 0) { @@ -412,7 +424,7 @@ export default function Dropped3dWidgets() { } } }; - + const handleMouseMove = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; @@ -477,9 +489,9 @@ export default function Dropped3dWidgets() { // // planeIntersect.current // // ); // // console.log('intersect: ', intersect); - + // let intersect = event.clientY - + // if (intersect && typeof intersectcontextmenu === "number") { // console.log('intersect: ', intersect); // const diff = intersect - intersectcontextmenu; @@ -504,21 +516,21 @@ export default function Dropped3dWidgets() { lastClientY.current = event.clientY; return; } - + const diff = lastClientY.current - event.clientY; // dragging up = increase Y const scaleFactor = 0.05; // tune this based on your scene scale - + const unclampedY = selectedWidget.position[1] + diff * scaleFactor; const newY = Math.max(0, unclampedY); - + lastClientY.current = event.clientY; - + const newPosition: [number, number, number] = [ selectedWidget.position[0], newY, selectedWidget.position[2], ]; - + updateWidgetPosition(selectedZoneId, rightClickSelected, newPosition); } diff --git a/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx b/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx index a0620e9..a0df039 100644 --- a/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx +++ b/app/src/modules/visualization/widgets/3d/cards/ProductionCapacity.tsx @@ -197,7 +197,7 @@ const ProductionCapacity: React.FC = ({ } }, [chartMeasurements, chartDuration, widgetName]); - useEffect(() => { }, [rotation]); + useEffect(() => {}, [rotation]); return ( <> diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 3b75365..cca83af 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -1,59 +1,39 @@ -import React, { useEffect, useState } from "react"; -import ModuleToggle from "../components/ui/ModuleToggle"; -import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft"; -import SideBarRight from "../components/layout/sidebarRight/SideBarRight"; -import useModuleStore, { useThreeDStore } from "../store/useModuleStore"; -import RealTimeVisulization from "../modules/visualization/RealTimeVisulization"; -import Tools from "../components/ui/Tools"; +import React, { useEffect } from "react"; +import useModuleStore from "../store/useModuleStore"; import { useSocketStore, - useFloorItems, useOrganization, useUserName, useWallItems, useZones, - useLoadingProgress, - useWidgetSubOption, useSaveVersion, } from "../store/builder/store"; import { useNavigate } from "react-router-dom"; -import { usePlayButtonStore } from "../store/usePlayButtonStore"; -import MarketPlace from "../modules/market/MarketPlace"; -import LoadingPage from "../components/templates/LoadingPage"; -import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys"; import { useSelectedUserStore } from "../store/collaboration/useCollabStore"; import FollowPerson from "../components/templates/FollowPerson"; -import Scene from "../modules/scene/scene"; -import { createHandleDrop } from "../modules/visualization/functions/handleUiDrop"; -import { useSelectedZoneStore } from "../store/visualization/useZoneStore"; -import { useFloatingWidget } from "../store/visualization/useDroppedObjectsStore"; import { useLogger } from "../components/ui/log/LoggerContext"; import RenderOverlay from "../components/templates/Overlay"; import LogList from "../components/ui/log/LogList"; import Footer from "../components/footer/Footer"; -import SelectFloorPlan from "../components/temporary/SelectFloorPlan"; -import ControlsPlayer from "../components/layout/controls/ControlsPlayer"; -import CompareLayOut from "../components/ui/compareVersion/CompareLayOut"; import { useToggleStore } from "../store/useUIToggleStore"; -import RegularDropDown from "../components/ui/inputs/RegularDropDown"; import VersionSaved from "../components/layout/sidebarRight/versionHisory/VersionSaved"; -import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; -import { useProductStore } from "../store/simulation/useProductStore"; +import { useAssetsStore } from "../store/builder/useAssetStore"; +import ComparisonSceneProvider from "../components/layout/scenes/ComparisonSceneProvider"; +import MainSceneProvider from "../components/layout/scenes/MainSceneProvider"; const Project: React.FC = () => { let navigate = useNavigate(); const echo = useLogger(); const { setToggleUI } = useToggleStore(); - const { activeModule, setActiveModule } = useModuleStore(); - const { loadingProgress } = useLoadingProgress(); + const { setAssets } = useAssetsStore(); const { setUserName } = useUserName(); const { setOrganization } = useOrganization(); - const { setFloorItems } = useFloorItems(); const { setWallItems } = useWallItems(); const { setZones } = useZones(); const { isVersionSaved } = useSaveVersion(); - const { products } = useProductStore(); + const { selectedUser } = useSelectedUserStore(); + const { isLogListVisible } = useLogger(); useEffect(() => { if (!isVersionSaved) { @@ -63,7 +43,7 @@ const Project: React.FC = () => { }, [isVersionSaved]); useEffect(() => { - setFloorItems([]); + setAssets([]); setWallItems([]); setZones([]); setActiveModule("builder"); @@ -83,84 +63,10 @@ const Project: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // global store - const { toggleThreeD } = useThreeDStore(); - - // simulation store - const { isPlaying } = usePlayButtonStore(); - - // collaboration store - const { selectedUser } = useSelectedUserStore(); - const { isLogListVisible } = useLogger(); - - // real-time visualization store - const { widgetSubOption } = useWidgetSubOption(); - const { visualizationSocket } = useSocketStore(); - const { selectedZone } = useSelectedZoneStore(); - const { setFloatingWidget } = useFloatingWidget(); - - const [selectedLayout, setSelectedLayout] = useState(null); // Track selected layout - - const dummyLayouts = [ - { id: 1, name: "Layout 1" }, - { id: 2, name: "Layout 2" }, - { id: 3, name: "Layout 3" }, - { id: 4, name: "Layout 4" }, - ]; - - const handleSelectLayout = (option: string) => { - setSelectedLayout(option); // Set selected layout - console.log("Selected layout:", option); - }; return (
- {!selectedUser && ( - <> - - {loadingProgress > 0 && } - {!isPlaying && ( - <> - {toggleThreeD && } - - - - )} - - {activeModule === "market" && } - {activeModule !== "market" && !isPlaying && !isVersionSaved && ( - - )} - {isPlaying && activeModule === "simulation" && } - {isPlaying && activeModule !== "simulation" && } - - {/* remove this later */} - {activeModule === "builder" && !toggleThreeD && } - - )} -
- createHandleDrop({ - widgetSubOption, - visualizationSocket, - selectedZone, - setFloatingWidget, - event, - }) - } - onDragOver={(event) => event.preventDefault()} - > - -
+ + {selectedUser && } {isLogListVisible && ( @@ -168,19 +74,6 @@ const Project: React.FC = () => { )} {activeModule !== "market" && !selectedUser &&
} - {isVersionSaved && activeModule === "simulation" && ( - <> -
- l.productName)} // Pass layout names as options - onSelect={handleSelectLayout} - search={false} - /> -
- - - )}
); diff --git a/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js b/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js deleted file mode 100644 index 50dac3f..0000000 --- a/app/src/services/factoryBuilder/webWorkers/assetManagerWorker.js +++ /dev/null @@ -1,51 +0,0 @@ -import * as THREE from 'three'; -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; - -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); - -onmessage = (event) => { - const { floorItems, cameraPosition, uuids, renderDistance } = event.data; - if (!floorItems) return - - const toAdd = []; - const toRemove = []; - - const cameraPos = new THREE.Vector3(cameraPosition.x, cameraPosition.y, cameraPosition.z); - - // Check for items to be added - floorItems.forEach((item) => { - const itemPosition = new THREE.Vector3(...item.position); - const distance = cameraPos.distanceTo(itemPosition); - - if (distance <= renderDistance && !uuids.includes(item.modelUuid)) { - toAdd.push(item); - } - }); - - // Sort the toAdd array based on distance (closest first) - toAdd.sort((a, b) => { - const aDistance = cameraPos.distanceTo(new THREE.Vector3(...a.position)); - const bDistance = cameraPos.distanceTo(new THREE.Vector3(...b.position)); - return aDistance - bDistance; - }); - - // Check for items to be removed - uuids.forEach((uuid) => { - const floorItem = floorItems.find((item) => item.modelUuid === uuid); - if (floorItem) { - const itemPosition = new THREE.Vector3(...floorItem.position); - const distance = cameraPos.distanceTo(itemPosition); - - if (distance > renderDistance) { - toRemove.push(uuid); - } - } - }); - - // Send the result back to the main thread - postMessage({ toAdd, toRemove }); -}; diff --git a/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js b/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js index 9e7397d..f98ad9b 100644 --- a/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js +++ b/app/src/services/factoryBuilder/webWorkers/gltfLoaderWorker.js @@ -17,6 +17,9 @@ onmessage = async (event) => { ); for (const item of uniqueItems) { + if(item.modelfileID === null || item.modelfileID === undefined) { + continue; // Skip items without a valid modelfileID + } const modelID = item.modelfileID; const indexedDBModel = await retrieveGLTF(modelID); diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index adaab07..267516b 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -155,15 +155,6 @@ export const useDeletePointOrLine = create((set: any) => ({ setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })), })); -export const useFloorItems = create((set: any) => ({ - floorItems: null, - setFloorItems: (callback: any) => - set((state: any) => ({ - floorItems: - typeof callback === "function" ? callback(state.floorItems) : callback, - })), -})); - export const useWallItems = create((set: any) => ({ wallItems: [], setWallItems: (callback: any) => diff --git a/app/src/store/builder/useAisleStore.ts b/app/src/store/builder/useAisleStore.ts new file mode 100644 index 0000000..71197c2 --- /dev/null +++ b/app/src/store/builder/useAisleStore.ts @@ -0,0 +1,205 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface AisleStore { + aisles: Aisles; + setAisles: (aisles: Aisles) => void; + addAisle: (aisle: Aisle) => void; + updateAisle: (uuid: string, updated: Partial) => void; + removeAisle: (uuid: string) => void; + removePoint: (uuid: string) => Aisles; + setPosition: (pointUuid: string, position: [number, number, number]) => void; + setLayer: (pointUuid: string, layer: number) => void; + setColor: (aisleUuid: string, color: AisleColors) => void; + + // Type-specific setters + setSolidAisleWidth: (aisleUuid: string, width: number) => void; + setDashedAisleProperties: ( + aisleUuid: string, + props: { aisleWidth?: number; dashLength?: number; gapLength?: number } + ) => void; + setStrippedAisleWidth: (aisleUuid: string, width: number) => void; + setDottedAisleProperties: ( + aisleUuid: string, + props: { dotRadius?: number; gapLength?: number } + ) => void; + setArrowAisleWidth: (aisleUuid: string, width: number) => void; + setArrowsAisleProperties: ( + aisleUuid: string, + props: { aisleWidth?: number; aisleLength?: number; gapLength?: number } + ) => void; + setArcAisleWidth: (aisleUuid: string, width: number) => void; + setCircleAisleWidth: (aisleUuid: string, width: number) => void; + setJunctionAisleWidth: (aisleUuid: string, width: number) => void; + + getAisleById: (uuid: string) => Aisle | undefined; + getAislePointById: (uuid: string) => Point | undefined; + getConnectedPoints: (uuid: string) => Point[] | []; + getAisleType: (uuid: string) => T | undefined; +} + +export const useAisleStore = create()( + immer((set, get) => ({ + aisles: [], + + setAisles: (aisles) => set((state) => { + state.aisles = aisles; + }), + + addAisle: (aisle) => set((state) => { + state.aisles.push(aisle); + }), + + updateAisle: (uuid, updated) => set((state) => { + const aisle = state.aisles.find((a) => a.uuid === uuid); + if (aisle) { + Object.assign(aisle, updated); + } + }), + + removeAisle: (uuid) => set((state) => { + state.aisles = state.aisles.filter((a) => a.uuid !== uuid); + }), + + removePoint: (uuid) => { + const removedAisles: Aisle[] = []; + set((state) => { + state.aisles = state.aisles.filter((aisle) => { + const hasPoint = aisle.points.some((point) => point.uuid === uuid); + if (hasPoint) { + removedAisles.push(JSON.parse(JSON.stringify(aisle))); + return false; + } + return true; + }); + }); + console.log('removedAisles: ', removedAisles); + return removedAisles; + }, + + setPosition: (pointUuid, position) => set((state) => { + for (const aisle of state.aisles) { + const point = aisle.points.find(p => p.uuid === pointUuid); + if (point) { + point.position = position; + } + } + }), + + setLayer: (pointUuid, layer) => set((state) => { + for (const aisle of state.aisles) { + const point = aisle.points.find(p => p.uuid === pointUuid); + if (point) { + point.layer = layer; + } + } + }), + + setColor: (aisleUuid, color) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle) { + aisle.type.aisleColor = color; + } + }), + + // Type-specific property setters + setSolidAisleWidth: (aisleUuid, width) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'solid-aisle') { + aisle.type.aisleWidth = width; + } + }), + + setDashedAisleProperties: (aisleUuid, props) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'dashed-aisle') { + if (props.aisleWidth !== undefined) aisle.type.aisleWidth = props.aisleWidth; + if (props.dashLength !== undefined) aisle.type.dashLength = props.dashLength; + if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength; + } + }), + + setStrippedAisleWidth: (aisleUuid, width) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'stripped-aisle') { + aisle.type.aisleWidth = width; + } + }), + + setDottedAisleProperties: (aisleUuid, props) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'dotted-aisle') { + if (props.dotRadius !== undefined) aisle.type.dotRadius = props.dotRadius; + if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength; + } + }), + + setArrowAisleWidth: (aisleUuid, width) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'arrow-aisle') { + aisle.type.aisleWidth = width; + } + }), + + setArrowsAisleProperties: (aisleUuid, props) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'arrows-aisle') { + if (props.aisleWidth !== undefined) aisle.type.aisleWidth = props.aisleWidth; + if (props.aisleLength !== undefined) aisle.type.aisleLength = props.aisleLength; + if (props.gapLength !== undefined) aisle.type.gapLength = props.gapLength; + } + }), + + setArcAisleWidth: (aisleUuid, width) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'arc-aisle') { + aisle.type.aisleWidth = width; + } + }), + + setCircleAisleWidth: (aisleUuid, width) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'circle-aisle') { + aisle.type.aisleWidth = width; + } + }), + + setJunctionAisleWidth: (aisleUuid, width) => set((state) => { + const aisle = state.aisles.find(a => a.uuid === aisleUuid); + if (aisle && aisle.type.aisleType === 'junction-aisle') { + aisle.type.aisleWidth = width; + } + }), + + getAisleById: (uuid) => { + return get().aisles.find((a) => a.uuid === uuid); + }, + + getAislePointById: (uuid) => { + for (const aisle of get().aisles) { + const point = aisle.points.find(p => p.uuid === uuid); + if (point) { + return point; + } + } + return undefined; + }, + + getConnectedPoints: (uuid) => { + const connected: Point[] = []; + for (const aisle of get().aisles) { + for (const point of aisle.points) { + if (point.uuid === uuid) { + connected.push(...aisle.points.filter(p => p.uuid !== uuid)); + } + } + } + return connected; + }, + + getAisleType: (uuid: string) => { + const aisle = get().aisles.find(a => a.uuid === uuid); + return aisle?.type as T | undefined; + }, + })) +); \ No newline at end of file diff --git a/app/src/store/builder/useAssetStore.ts b/app/src/store/builder/useAssetStore.ts index 326a877..ffa1d35 100644 --- a/app/src/store/builder/useAssetStore.ts +++ b/app/src/store/builder/useAssetStore.ts @@ -11,6 +11,7 @@ interface AssetsStore { setAssets: (assets: Assets) => void; // Asset properties + setName: (modelUuid: string, newName: string) => void; setPosition: (modelUuid: string, position: [number, number, number]) => void; setRotation: (modelUuid: string, rotation: [number, number, number]) => void; setLock: (modelUuid: string, isLocked: boolean) => void; @@ -70,6 +71,15 @@ export const useAssetsStore = create()( }, // Asset properties + setName: (modelUuid, newName) => { + set((state) => { + const asset = state.assets.find(a => a.modelUuid === modelUuid); + if (asset) { + asset.modelName = newName; + } + }); + }, + setPosition: (modelUuid, position) => { set((state) => { const asset = state.assets.find(a => a.modelUuid === modelUuid); diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts new file mode 100644 index 0000000..dc3524e --- /dev/null +++ b/app/src/store/builder/useBuilderStore.ts @@ -0,0 +1,170 @@ +import { Object3D } from 'three'; +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +interface BuilderState { + // Common properties + + hoveredPoint: Point | null; + snappedPoint: Point | null; + snappedPosition: [number, number, number] | null; + + selectedAisle: Object3D | null; + + aisleType: AisleTypes; + aisleWidth: number; + aisleColor: AisleColors; + + // Dashed aisle properties + dashLength: number; + gapLength: number; + + // Dotted aisle properties + dotRadius: number; + + // Arrows aisle properties + aisleLength: number; + + // Setters for common properties + + setHoveredPoint: (point: Point | null) => void; + setSnappedPoint: (point: Point | null) => void; + setSnappedPosition: (position: [number, number, number] | null) => void; + + setSelectedAisle: (aisle: Object3D | null) => void; + + setAisleType: (type: AisleTypes) => void; + setAisleWidth: (width: number) => void; + setAisleColor: (color: AisleColors) => void; + + // Setters for dashed aisle + setDashLength: (length: number) => void; + setGapLength: (length: number) => void; + + // Setters for dotted aisle + setDotRadius: (radius: number) => void; + + // Setters for arrows aisle + setAisleLength: (length: number) => void; + + // Batch setters + setDashedAisleProperties: (width: number, dashLength: number, gapLength: number) => void; + setDottedAisleProperties: (width: number, dotRadius: number, gapLength: number) => void; + setArrowsAisleProperties: (width: number, aisleLength: number, gapLength: number) => void; + setAisleProperties: (type: AisleTypes, width: number, color: AisleColors) => void; +} + +export const useBuilderStore = create()( + immer((set) => ({ + // Default values + + hoveredPoint: null, + snappedPoint: null, + snappedPosition: null, + + selectedAisle: null, + + aisleType: 'solid-aisle', + aisleWidth: 0.1, + aisleColor: 'yellow', + dashLength: 0.5, + gapLength: 0.3, + dotRadius: 0.1, + aisleLength: 0.6, + + // Individual setters + + setHoveredPoint: (point: Point | null) => { + set((state) => { + state.hoveredPoint = point; + }); + }, + + setSnappedPoint: (point: Point | null) => { + set((state) => { + state.snappedPoint = point; + }); + }, + + setSnappedPosition: (position: [number, number, number] | null) => { + set((state) => { + state.snappedPosition = position; + }); + }, + + setSelectedAisle: (aisle: Object3D | null) => { + set((state) => { + state.selectedAisle = aisle; + }); + }, + + setAisleType: (type) => { + set((state) => { + state.aisleType = type; + }); + }, + setAisleWidth: (width) => { + set((state) => { + state.aisleWidth = width; + }); + }, + setAisleColor: (color) => { + set((state) => { + state.aisleColor = color; + }); + }, + setDashLength: (length) => { + set((state) => { + state.dashLength = length; + }); + }, + setGapLength: (length) => { + set((state) => { + state.gapLength = length; + }); + }, + setDotRadius: (radius) => { + set((state) => { + state.dotRadius = radius; + }); + }, + setAisleLength: (length) => { + set((state) => { + state.aisleLength = length; + }); + }, + + // Batch setters + setDashedAisleProperties: (width, dashLength, gapLength) => { + set((state) => { + state.aisleType = 'dashed-aisle'; + state.aisleWidth = width; + state.dashLength = dashLength; + state.gapLength = gapLength; + }); + }, + setDottedAisleProperties: (width, dotRadius, gapLength) => { + set((state) => { + state.aisleType = 'dotted-aisle'; + state.aisleWidth = width; + state.dotRadius = dotRadius; + state.gapLength = gapLength; + }); + }, + setArrowsAisleProperties: (width, aisleLength, gapLength) => { + set((state) => { + state.aisleType = 'arrows-aisle'; + state.aisleWidth = width; + state.aisleLength = aisleLength; + state.gapLength = gapLength; + }); + }, + setAisleProperties: (type, width, color) => { + set((state) => { + state.aisleType = type; + state.aisleWidth = width; + state.aisleColor = color; + }); + } + })) +); \ No newline at end of file diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 7c4d38b..e05ee2b 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -34,173 +34,177 @@ interface ArmBotStore { getArmBotsByCurrentAction: (actionUuid: string) => ArmBotStatus[]; } -export const useArmBotStore = create()( - immer((set, get) => ({ - armBots: [], +export const createArmBotStore = () => { + return create()( + immer((set, get) => ({ + armBots: [], - addArmBot: (productId, event) => { - set((state) => { - const exists = state.armBots.some(a => a.modelUuid === event.modelUuid); - if (!exists) { - state.armBots.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); - } - }); - }, - - removeArmBot: (modelUuid) => { - set((state) => { - state.armBots = state.armBots.filter(a => a.modelUuid !== modelUuid); - }); - }, - - updateArmBot: (modelUuid, updates) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - Object.assign(armBot, updates); - } - }); - }, - - clearArmBots: () => { - set((state) => { - state.armBots = []; - }); - }, - - addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); - if (action) { - armBot.currentAction = { - actionUuid: action.actionUuid, - actionName: action.actionName, - materialType: materialType, - materialId: materialId - }; + addArmBot: (productId, event) => { + set((state) => { + const exists = state.armBots.some(a => a.modelUuid === event.modelUuid); + if (!exists) { + state.armBots.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); } - } - }); - }, + }); + }, - removeCurrentAction: (modelUuid) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.currentAction = undefined; - } - }); - }, + removeArmBot: (modelUuid) => { + set((state) => { + state.armBots = state.armBots.filter(a => a.modelUuid !== modelUuid); + }); + }, - addAction: (modelUuid, action) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.point.actions.push(action); - } - }); - }, - - removeAction: (modelUuid, actionUuid) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.point.actions = armBot.point.actions.filter(a => a.actionUuid !== actionUuid); - } - }); - }, - - updateStartPoint: (modelUuid, actionUuid, startPoint) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); - if (action) { - action.process.startPoint = startPoint; + updateArmBot: (modelUuid, updates) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + Object.assign(armBot, updates); } - } - }); - }, + }); + }, - updateEndPoint: (modelUuid, actionUuid, endPoint) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); - if (action) { - action.process.endPoint = endPoint; + clearArmBots: () => { + set((state) => { + state.armBots = []; + }); + }, + + addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + armBot.currentAction = { + actionUuid: action.actionUuid, + actionName: action.actionName, + materialType: materialType, + materialId: materialId + }; + } } - } - }); - }, + }); + }, - setArmBotActive: (modelUuid, isActive) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.isActive = isActive; - } - }); - }, + removeCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.currentAction = undefined; + } + }); + }, - setArmBotState: (modelUuid, newState) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.state = newState; - } - }); - }, + addAction: (modelUuid, action) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.point.actions.push(action); + } + }); + }, - incrementActiveTime: (modelUuid, incrementBy) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.activeTime += incrementBy; - } - }); - }, + removeAction: (modelUuid, actionUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.point.actions = armBot.point.actions.filter(a => a.actionUuid !== actionUuid); + } + }); + }, - incrementIdleTime: (modelUuid, incrementBy) => { - set((state) => { - const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot) { - armBot.idleTime += incrementBy; - } - }); - }, + updateStartPoint: (modelUuid, actionUuid, startPoint) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + action.process.startPoint = startPoint; + } + } + }); + }, - getArmBotById: (modelUuid) => { - return get().armBots.find(a => a.modelUuid === modelUuid); - }, + updateEndPoint: (modelUuid, actionUuid, endPoint) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + action.process.endPoint = endPoint; + } + } + }); + }, - getArmBotsByProduct: (productId) => { - return get().armBots.filter(a => a.productId === productId); - }, + setArmBotActive: (modelUuid, isActive) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.isActive = isActive; + } + }); + }, - getArmBotsByState: (state) => { - return get().armBots.filter(a => a.state === state); - }, + setArmBotState: (modelUuid, newState) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.state = newState; + } + }); + }, - getActiveArmBots: () => { - return get().armBots.filter(a => a.isActive); - }, + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.activeTime += incrementBy; + } + }); + }, - getIdleArmBots: () => { - return get().armBots.filter(a => !a.isActive && a.state === 'idle'); - }, + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.idleTime += incrementBy; + } + }); + }, - getArmBotsByCurrentAction: (actionUuid) => { - return get().armBots.filter(a => a.currentAction?.actionUuid === actionUuid); - } - })) -); + getArmBotById: (modelUuid) => { + return get().armBots.find(a => a.modelUuid === modelUuid); + }, + + getArmBotsByProduct: (productId) => { + return get().armBots.filter(a => a.productId === productId); + }, + + getArmBotsByState: (state) => { + return get().armBots.filter(a => a.state === state); + }, + + getActiveArmBots: () => { + return get().armBots.filter(a => a.isActive); + }, + + getIdleArmBots: () => { + return get().armBots.filter(a => !a.isActive && a.state === 'idle'); + }, + + getArmBotsByCurrentAction: (actionUuid) => { + return get().armBots.filter(a => a.currentAction?.actionUuid === actionUuid); + } + })) + ) +} + +export type ArmBotStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 6d920eb..40dfab4 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -26,111 +26,115 @@ interface ConveyorStore { getIdleConveyors: () => ConveyorStatus[]; } -export const useConveyorStore = create()( - immer((set, get) => ({ - conveyors: [], +export const createConveyorStore = () => { + return create()( + immer((set, get) => ({ + conveyors: [], - addConveyor: (productId, event) => { - set((state) => { - const exists = state.conveyors.some(c => c.modelUuid === event.modelUuid); - if (!exists) { - state.conveyors.push({ - ...event, - productId, - isActive: false, - isPaused: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); - } - }); - }, + addConveyor: (productId, event) => { + set((state) => { + const exists = state.conveyors.some(c => c.modelUuid === event.modelUuid); + if (!exists) { + state.conveyors.push({ + ...event, + productId, + isActive: false, + isPaused: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } + }); + }, - removeConveyor: (modelUuid) => { - set((state) => { - state.conveyors = state.conveyors.filter(c => c.modelUuid !== modelUuid); - }); - }, + removeConveyor: (modelUuid) => { + set((state) => { + state.conveyors = state.conveyors.filter(c => c.modelUuid !== modelUuid); + }); + }, - updateConveyor: (modelUuid, updates) => { - set((state) => { - const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); - if (conveyor) { - Object.assign(conveyor, updates); - } - }); - }, + updateConveyor: (modelUuid, updates) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + Object.assign(conveyor, updates); + } + }); + }, - clearConveyors: () => { - set((state) => { - state.conveyors = []; - }); - }, + clearConveyors: () => { + set((state) => { + state.conveyors = []; + }); + }, - setConveyorActive: (modelUuid, isActive) => { - set((state) => { - const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); - if (conveyor) { - conveyor.isActive = isActive; - } - }); - }, + setConveyorActive: (modelUuid, isActive) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.isActive = isActive; + } + }); + }, - setConveyorState: (modelUuid, newState) => { - set((state) => { - const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); - if (conveyor) { - conveyor.state = newState; - } - }); - }, + setConveyorState: (modelUuid, newState) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.state = newState; + } + }); + }, - setConveyorPaused: (modelUuid, isPaused) => { - set((state) => { - const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); - if (conveyor) { - conveyor.isPaused = isPaused; - } - }); - }, + setConveyorPaused: (modelUuid, isPaused) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.isPaused = isPaused; + } + }); + }, - incrementActiveTime: (modelUuid, incrementBy) => { - set((state) => { - const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); - if (conveyor) { - conveyor.activeTime += incrementBy; - } - }); - }, + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.activeTime += incrementBy; + } + }); + }, - incrementIdleTime: (modelUuid, incrementBy) => { - set((state) => { - const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); - if (conveyor) { - conveyor.idleTime += incrementBy; - } - }); - }, + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.idleTime += incrementBy; + } + }); + }, - getConveyorById: (modelUuid) => { - return get().conveyors.find(c => c.modelUuid === modelUuid); - }, + getConveyorById: (modelUuid) => { + return get().conveyors.find(c => c.modelUuid === modelUuid); + }, - getConveyorsByProduct: (productId) => { - return get().conveyors.filter(c => c.productId === productId); - }, + getConveyorsByProduct: (productId) => { + return get().conveyors.filter(c => c.productId === productId); + }, - getConveyorsByState: (state) => { - return get().conveyors.filter(c => c.state === state); - }, + getConveyorsByState: (state) => { + return get().conveyors.filter(c => c.state === state); + }, - getActiveConveyors: () => { - return get().conveyors.filter(c => c.isActive); - }, + getActiveConveyors: () => { + return get().conveyors.filter(c => c.isActive); + }, - getIdleConveyors: () => { - return get().conveyors.filter(c => !c.isActive && c.state === 'idle'); - }, - })) -); + getIdleConveyors: () => { + return get().conveyors.filter(c => !c.isActive && c.state === 'idle'); + }, + })) + ) +} + +export type ConveyorStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts index df480e0..8fb5b06 100644 --- a/app/src/store/simulation/useMachineStore.ts +++ b/app/src/store/simulation/useMachineStore.ts @@ -37,140 +37,144 @@ interface MachineStore { getIdleMachines: () => MachineStatus[]; } -export const useMachineStore = create()( - immer((set, get) => ({ - machines: [], +export const createMachineStore = () => { + return create()( + immer((set, get) => ({ + machines: [], - addMachine: (productId, machine) => { - set((state) => { - const exists = state.machines.some( - (m) => m.modelUuid === machine.modelUuid - ); - if (!exists) { - state.machines.push({ - ...machine, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: "idle", - }); - } - }); - }, - - removeMachine: (modelUuid) => { - set((state) => { - state.machines = state.machines.filter( - (m) => m.modelUuid !== modelUuid - ); - }); - }, - - updateMachine: (modelUuid, updates) => { - set((state) => { - const machine = state.machines.find((m) => m.modelUuid === modelUuid); - if (machine) { - Object.assign(machine, updates); - } - }); - }, - - clearMachines: () => { - set((state) => { - state.machines = []; - }); - }, - - addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => { - set((state) => { - const armBot = state.machines.find((a) => a.modelUuid === modelUuid); - if (armBot) { - const action = armBot.point.action; - if (action) { - armBot.currentAction = { - actionUuid: actionUuid, - actionName: action.actionName, - materialType: materialType, - materialId: materialId, - }; + addMachine: (productId, machine) => { + set((state) => { + const exists = state.machines.some( + (m) => m.modelUuid === machine.modelUuid + ); + if (!exists) { + state.machines.push({ + ...machine, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: "idle", + }); } - } - }); - }, + }); + }, - removeCurrentAction: (modelUuid) => { - set((state) => { - const armBot = state.machines.find((a) => a.modelUuid === modelUuid); - if (armBot) { - armBot.currentAction = undefined; - } - }); - }, + removeMachine: (modelUuid) => { + set((state) => { + state.machines = state.machines.filter( + (m) => m.modelUuid !== modelUuid + ); + }); + }, - setMachineActive: (modelUuid, isActive) => { - set((state) => { - const machine = state.machines.find((m) => m.modelUuid === modelUuid); - if (machine) { - machine.isActive = isActive; - } - }); - }, + updateMachine: (modelUuid, updates) => { + set((state) => { + const machine = state.machines.find((m) => m.modelUuid === modelUuid); + if (machine) { + Object.assign(machine, updates); + } + }); + }, - setMachineState: (modelUuid, newState) => { - set((state) => { - const machine = state.machines.find((m) => m.modelUuid === modelUuid); - if (machine) { - machine.state = newState; - } - }); - }, + clearMachines: () => { + set((state) => { + state.machines = []; + }); + }, - incrementActiveTime: (modelUuid, incrementBy) => { - set((state) => { - const machine = state.machines.find((m) => m.modelUuid === modelUuid); - if (machine) { - machine.activeTime += incrementBy; - } - }); - }, + addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => { + set((state) => { + const armBot = state.machines.find((a) => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.action; + if (action) { + armBot.currentAction = { + actionUuid: actionUuid, + actionName: action.actionName, + materialType: materialType, + materialId: materialId, + }; + } + } + }); + }, - incrementIdleTime: (modelUuid, incrementBy) => { - set((state) => { - const machine = state.machines.find((m) => m.modelUuid === modelUuid); - if (machine) { - machine.idleTime += incrementBy; - } - }); - }, - resetTime: (modelUuid) => { - set((state) => { - const machine = state.machines.find((m) => m.modelUuid === modelUuid); - if (machine) { - machine.activeTime = 0; - machine.idleTime = 0; - } - }); - }, + removeCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.machines.find((a) => a.modelUuid === modelUuid); + if (armBot) { + armBot.currentAction = undefined; + } + }); + }, - getMachineById: (modelUuid) => { - return get().machines.find((m) => m.modelUuid === modelUuid); - }, + setMachineActive: (modelUuid, isActive) => { + set((state) => { + const machine = state.machines.find((m) => m.modelUuid === modelUuid); + if (machine) { + machine.isActive = isActive; + } + }); + }, - getMachinesByProduct: (productId) => { - return get().machines.filter((m) => m.productId === productId); - }, + setMachineState: (modelUuid, newState) => { + set((state) => { + const machine = state.machines.find((m) => m.modelUuid === modelUuid); + if (machine) { + machine.state = newState; + } + }); + }, - getMachinesBystate: (state) => { - return get().machines.filter((m) => m.state === state); - }, + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const machine = state.machines.find((m) => m.modelUuid === modelUuid); + if (machine) { + machine.activeTime += incrementBy; + } + }); + }, - getActiveMachines: () => { - return get().machines.filter((m) => m.isActive); - }, + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const machine = state.machines.find((m) => m.modelUuid === modelUuid); + if (machine) { + machine.idleTime += incrementBy; + } + }); + }, + resetTime: (modelUuid) => { + set((state) => { + const machine = state.machines.find((m) => m.modelUuid === modelUuid); + if (machine) { + machine.activeTime = 0; + machine.idleTime = 0; + } + }); + }, - getIdleMachines: () => { - return get().machines.filter((m) => !m.isActive && m.state === "idle"); - }, - })) -); + getMachineById: (modelUuid) => { + return get().machines.find((m) => m.modelUuid === modelUuid); + }, + + getMachinesByProduct: (productId) => { + return get().machines.filter((m) => m.productId === productId); + }, + + getMachinesBystate: (state) => { + return get().machines.filter((m) => m.state === state); + }, + + getActiveMachines: () => { + return get().machines.filter((m) => m.isActive); + }, + + getIdleMachines: () => { + return get().machines.filter((m) => !m.isActive && m.state === "idle"); + }, + })) + ) +} + +export type MachineStoreType = ReturnType; diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index 31f5726..123ebb0 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -54,226 +54,231 @@ type MaterialsStore = { getMaterialHistory: () => MaterialHistorySchema; }; -export const useMaterialStore = create()( - immer((set, get) => ({ - materials: [], - materialHistory: [], +export const createMaterialStore = () => { + return create()( + immer((set, get) => ({ + materials: [], + materialHistory: [], - addMaterial: (material) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - state.materials.push(material); - }); - return updatedMaterial; - }, + addMaterial: (material) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + state.materials.push(material); + }); + return updatedMaterial; + }, - removeMaterial: (materialId) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - state.materials = state.materials.filter(m => m.materialId !== materialId); - updatedMaterial = JSON.parse(JSON.stringify(material)); + removeMaterial: (materialId) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + state.materials = state.materials.filter(m => m.materialId !== materialId); + updatedMaterial = JSON.parse(JSON.stringify(material)); - state.materialHistory.push({ - material, - removedAt: new Date().toISOString() - }); - } - }); - return updatedMaterial; - }, + state.materialHistory.push({ + material, + removedAt: new Date().toISOString() + }); + } + }); + return updatedMaterial; + }, - clearMaterials: () => { - set((state) => { - state.materials = []; - }); - }, + clearMaterials: () => { + set((state) => { + state.materials = []; + }); + }, - updateMaterial: (materialId, updates) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - Object.assign(material, updates); - updatedMaterial = JSON.parse(JSON.stringify(material)); - } - }); - return updatedMaterial; - }, + updateMaterial: (materialId, updates) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + Object.assign(material, updates); + updatedMaterial = JSON.parse(JSON.stringify(material)); + } + }); + return updatedMaterial; + }, - setPreviousLocation: (materialId, location) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.previous = location; - updatedMaterial = JSON.parse(JSON.stringify(material)); - } - }); - return updatedMaterial; - }, + setPreviousLocation: (materialId, location) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.previous = location; + updatedMaterial = JSON.parse(JSON.stringify(material)); + } + }); + return updatedMaterial; + }, - setCurrentLocation: (materialId, location) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.current = location; - updatedMaterial = JSON.parse(JSON.stringify(material)); - } - }); - return updatedMaterial; - }, + setCurrentLocation: (materialId, location) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.current = location; + updatedMaterial = JSON.parse(JSON.stringify(material)); + } + }); + return updatedMaterial; + }, - setNextLocation: (materialId, location) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.next = location; - updatedMaterial = JSON.parse(JSON.stringify(material)); - } - }); - return updatedMaterial; - }, + setNextLocation: (materialId, location) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.next = location; + updatedMaterial = JSON.parse(JSON.stringify(material)); + } + }); + return updatedMaterial; + }, - setMaterial: (materialId, materialType) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.materialType = materialType; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setMaterial: (materialId, materialType) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.materialType = materialType; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setStartTime: (materialId, startTime) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.startTime = startTime - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setStartTime: (materialId, startTime) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.startTime = startTime + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setEndTime: (materialId, endTime) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.endTime = endTime; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setEndTime: (materialId, endTime) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.endTime = endTime; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setCost: (materialId, cost) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.cost = cost; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setCost: (materialId, cost) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.cost = cost; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setWeight: (materialId, weight) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.weight = weight; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setWeight: (materialId, weight) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.weight = weight; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setIsActive: (materialId, isActive) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.isActive = isActive; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setIsActive: (materialId, isActive) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isActive = isActive; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setIsVisible: (materialId, isVisible) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.isVisible = isVisible; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setIsVisible: (materialId, isVisible) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isVisible = isVisible; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setIsPaused: (materialId, isPaused) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.isPaused = isPaused; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setIsPaused: (materialId, isPaused) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isPaused = isPaused; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - setIsRendered: (materialId, isRendered) => { - let updatedMaterial: MaterialSchema | undefined; - set((state) => { - const material = state.materials.find(m => m.materialId === materialId); - if (material) { - material.isRendered = isRendered; - updatedMaterial = JSON.parse(JSON.stringify(material)); - }; - }); - return updatedMaterial; - }, + setIsRendered: (materialId, isRendered) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isRendered = isRendered; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, - getMaterialById: (materialId) => { - return get().materials.find(m => m.materialId === materialId); - }, + getMaterialById: (materialId) => { + return get().materials.find(m => m.materialId === materialId); + }, - getMaterialsByCurrentModelUuid: (currentModelUuid) => { - return get().materials.filter(m => m.current?.modelUuid === currentModelUuid); - }, + getMaterialsByCurrentModelUuid: (currentModelUuid) => { + return get().materials.filter(m => m.current?.modelUuid === currentModelUuid); + }, - getMaterialByCurrentPointUuid: (currentPointlUuid) => { - return get().materials.find(m => m.current?.pointUuid === currentPointlUuid); - }, + getMaterialByCurrentPointUuid: (currentPointlUuid) => { + return get().materials.find(m => m.current?.pointUuid === currentPointlUuid); + }, - getMaterialsByPoint: (pointUuid) => { - return get().materials.filter(m => - m.current?.pointUuid === pointUuid || - m.next?.pointUuid === pointUuid - ); - }, + getMaterialsByPoint: (pointUuid) => { + return get().materials.filter(m => + m.current?.pointUuid === pointUuid || + m.next?.pointUuid === pointUuid + ); + }, - getMaterialsByModel: (modelUuid) => { - return get().materials.filter(m => - m.current?.modelUuid === modelUuid || - m.next?.modelUuid === modelUuid - ); - }, + getMaterialsByModel: (modelUuid) => { + return get().materials.filter(m => + m.current?.modelUuid === modelUuid || + m.next?.modelUuid === modelUuid + ); + }, - getMaterialHistory: () => { - return get().materialHistory; - }, - })) -); \ No newline at end of file + getMaterialHistory: () => { + return get().materialHistory; + }, + })) + ) +} + + +export type MaterialStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts index b3d3c78..9d9df4b 100644 --- a/app/src/store/simulation/useSimulationStore.ts +++ b/app/src/store/simulation/useSimulationStore.ts @@ -74,23 +74,27 @@ interface SelectedProductState { clearSelectedProduct: () => void; } -export const useSelectedProduct = create()( - immer((set) => ({ - selectedProduct: { productId: '', productName: '' }, - setSelectedProduct: (productId, productName) => { - set((state) => { - state.selectedProduct.productId = productId; - state.selectedProduct.productName = productName; - }); - }, - clearSelectedProduct: () => { - set((state) => { - state.selectedProduct.productId = ''; - state.selectedProduct.productName = ''; - }); - }, - })) -); +export const createSelectedProductStore = () => { + return create()( + immer((set) => ({ + selectedProduct: { productId: '', productName: '' }, + setSelectedProduct: (productId, productName) => { + set((state) => { + state.selectedProduct.productId = productId; + state.selectedProduct.productName = productName; + }); + }, + clearSelectedProduct: () => { + set((state) => { + state.selectedProduct.productId = ''; + state.selectedProduct.productName = ''; + }); + }, + })) + ) +} + +export type SelectedProductType = ReturnType; interface SelectedActionState { selectedAction: { actionId: string | null; actionName: string | null }; @@ -146,4 +150,48 @@ export const useIsRotating = create()( }); }, })) +); + +interface MainProductState { + mainProduct: { productId: string; productName: string } | null; + setMainProduct: (productId: string, productName: string) => void; + clearMainProduct: () => void; +} + +export const useMainProduct = create()( + immer((set) => ({ + mainProduct: null, + setMainProduct: (productId: string, productName: string) => { + set((state) => { + state.mainProduct = { productId, productName }; + }); + }, + clearMainProduct: () => { + set((state) => { + state.mainProduct = null; + }); + }, + })) +); + +interface ComparisonProductState { + comparisonProduct: { productId: string; productName: string } | null; + setComparisonProduct: (productId: string, productName: string) => void; + clearComparisonProduct: () => void; +} + +export const useComparisonProduct = create()( + immer((set) => ({ + comparisonProduct: null, + setComparisonProduct: (productId: string, productName: string) => { + set((state) => { + state.comparisonProduct = { productId, productName }; + }); + }, + clearComparisonProduct: () => { + set((state) => { + state.comparisonProduct = null; + }); + }, + })) ); \ No newline at end of file diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts index 171f674..a71af3b 100644 --- a/app/src/store/simulation/useStorageUnitStore.ts +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -35,181 +35,185 @@ interface StorageUnitStore { getEmptyStorageUnits: () => StorageUnitStatus[]; } -export const useStorageUnitStore = create()( - immer((set, get) => ({ - storageUnits: [], +export const createStorageUnitStore = () => { + return create()( + immer((set, get) => ({ + storageUnits: [], - addStorageUnit: (productId, storageUnit) => { - set((state) => { - const exists = state.storageUnits.some(s => s.modelUuid === storageUnit.modelUuid); - if (!exists) { - state.storageUnits.push({ - ...storageUnit, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - currentLoad: 0, - currentMaterials: [], - state: 'idle' - }); - } - }); - }, + addStorageUnit: (productId, storageUnit) => { + set((state) => { + const exists = state.storageUnits.some(s => s.modelUuid === storageUnit.modelUuid); + if (!exists) { + state.storageUnits.push({ + ...storageUnit, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + currentMaterials: [], + state: 'idle' + }); + } + }); + }, - removeStorageUnit: (modelUuid) => { - set((state) => { - state.storageUnits = state.storageUnits.filter(s => s.modelUuid !== modelUuid); - }); - }, + removeStorageUnit: (modelUuid) => { + set((state) => { + state.storageUnits = state.storageUnits.filter(s => s.modelUuid !== modelUuid); + }); + }, - updateStorageUnit: (modelUuid, updates) => { - set((state) => { - const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); - if (unit) { - Object.assign(unit, updates); - } - }); - }, + updateStorageUnit: (modelUuid, updates) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + Object.assign(unit, updates); + } + }); + }, - clearStorageUnits: () => { - set(() => ({ - storageUnits: [], - })); - }, + clearStorageUnits: () => { + set(() => ({ + storageUnits: [], + })); + }, - setStorageUnitActive: (modelUuid, isActive) => { - set((state) => { - const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); - if (unit) { - unit.isActive = isActive; - } - }); - }, + setStorageUnitActive: (modelUuid, isActive) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.isActive = isActive; + } + }); + }, - setStorageUnitState: (modelUuid, newState) => { - set((state) => { - const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); - if (unit) { - unit.state = newState; - } - }); - }, + setStorageUnitState: (modelUuid, newState) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.state = newState; + } + }); + }, - updateCurrentLoad: (modelUuid, incrementBy) => { - set((state) => { - const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); - if (unit) { - unit.currentLoad += incrementBy; - } - }); - }, + updateCurrentLoad: (modelUuid, incrementBy) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.currentLoad += incrementBy; + } + }); + }, - incrementActiveTime: (modelUuid, incrementBy) => { - set((state) => { - const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); - if (unit) { - unit.activeTime += incrementBy; - } - }); - }, + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.activeTime += incrementBy; + } + }); + }, - incrementIdleTime: (modelUuid, incrementBy) => { - set((state) => { - const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); - if (unit) { - unit.idleTime += incrementBy; - } - }); - }, + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); + if (unit) { + unit.idleTime += incrementBy; + } + }); + }, - addCurrentMaterial: (modelUuid, materialType, materialId) => { - set((state) => { - const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); - if (storage) { - storage.currentMaterials.push({ materialType, materialId }); - } - }); - }, + addCurrentMaterial: (modelUuid, materialType, materialId) => { + set((state) => { + const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); + if (storage) { + storage.currentMaterials.push({ materialType, materialId }); + } + }); + }, - setCurrentMaterials: (modelUuid, materials) => { - set((state) => { - const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); - if (storage) { - storage.currentMaterials = materials; - } - }); - }, + setCurrentMaterials: (modelUuid, materials) => { + set((state) => { + const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); + if (storage) { + storage.currentMaterials = materials; + } + }); + }, - getLastMaterial: (modelUuid) => { - let removedMaterial: { materialId: string; materialType: string; } | undefined; - set((state) => { - const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); - if (storage) { - if (storage.currentMaterials.length > 0) { - const material = storage.currentMaterials[storage.currentMaterials.length - 1]; - if (material) { - removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + getLastMaterial: (modelUuid) => { + let removedMaterial: { materialId: string; materialType: string; } | undefined; + set((state) => { + const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); + if (storage) { + if (storage.currentMaterials.length > 0) { + const material = storage.currentMaterials[storage.currentMaterials.length - 1]; + if (material) { + removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + } } } - } - }); - return removedMaterial; - }, + }); + return removedMaterial; + }, - removeLastMaterial: (modelUuid) => { - let removedMaterial: { materialId: string; materialType: string; } | undefined; - set((state) => { - const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); - if (storage) { - if (storage.currentMaterials.length > 0) { - const material = storage.currentMaterials.pop(); - if (material) { - removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + removeLastMaterial: (modelUuid) => { + let removedMaterial: { materialId: string; materialType: string; } | undefined; + set((state) => { + const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); + if (storage) { + if (storage.currentMaterials.length > 0) { + const material = storage.currentMaterials.pop(); + if (material) { + removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + } } } - } - }); - return removedMaterial; - }, + }); + return removedMaterial; + }, - clearCurrentMaterials: (modelUuid) => { - set((state) => { - const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); - if (storage) { - storage.currentMaterials = []; - } - }); - }, + clearCurrentMaterials: (modelUuid) => { + set((state) => { + const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); + if (storage) { + storage.currentMaterials = []; + } + }); + }, - getStorageUnitById: (modelUuid) => { - return get().storageUnits.find(s => s.modelUuid === modelUuid); - }, + getStorageUnitById: (modelUuid) => { + return get().storageUnits.find(s => s.modelUuid === modelUuid); + }, - getStorageUnitsByProduct: (productId) => { - return get().storageUnits.filter(s => s.productId === productId); - }, + getStorageUnitsByProduct: (productId) => { + return get().storageUnits.filter(s => s.productId === productId); + }, - getStorageUnitsBystate: (state) => { - return get().storageUnits.filter(s => s.state === state); - }, + getStorageUnitsBystate: (state) => { + return get().storageUnits.filter(s => s.state === state); + }, - getActiveStorageUnits: () => { - return get().storageUnits.filter(s => s.isActive); - }, + getActiveStorageUnits: () => { + return get().storageUnits.filter(s => s.isActive); + }, - getIdleStorageUnits: () => { - return get().storageUnits.filter(s => !s.isActive && s.state === 'idle'); - }, + getIdleStorageUnits: () => { + return get().storageUnits.filter(s => !s.isActive && s.state === 'idle'); + }, - getFullStorageUnits: () => { - return get().storageUnits.filter( - s => s.currentLoad >= s.point.action.storageCapacity - ); - }, + getFullStorageUnits: () => { + return get().storageUnits.filter( + s => s.currentLoad >= s.point.action.storageCapacity + ); + }, - getEmptyStorageUnits: () => { - return get().storageUnits.filter(s => s.currentLoad === 0); - }, - })) -); + getEmptyStorageUnits: () => { + return get().storageUnits.filter(s => s.currentLoad === 0); + }, + })) + ) +} + +export type StorageUnitStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index d1ce04f..c41bb7c 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -37,217 +37,221 @@ interface VehiclesStore { getActiveVehicles: () => VehicleStatus[]; } -export const useVehicleStore = create()( - immer((set, get) => ({ - vehicles: [], +export const createVehicleStore = () => { + return create()( + immer((set, get) => ({ + vehicles: [], - addVehicle: (productId, event) => { - set((state) => { - const exists = state.vehicles.some((v) => v.modelUuid === event.modelUuid); - if (!exists) { - state.vehicles.push({ - ...event, - productId, - isActive: false, - isPicking: false, - idleTime: 0, - activeTime: 0, - currentLoad: 0, - currentMaterials: [], - distanceTraveled: 0, - state: 'idle' - }); - } - }); - }, + addVehicle: (productId, event) => { + set((state) => { + const exists = state.vehicles.some((v) => v.modelUuid === event.modelUuid); + if (!exists) { + state.vehicles.push({ + ...event, + productId, + isActive: false, + isPicking: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + currentMaterials: [], + distanceTraveled: 0, + state: 'idle' + }); + } + }); + }, - removeVehicle: (modelUuid) => { - set((state) => { - state.vehicles = state.vehicles.filter( - (v) => v.modelUuid !== modelUuid - ); - }); - }, + removeVehicle: (modelUuid) => { + set((state) => { + state.vehicles = state.vehicles.filter( + (v) => v.modelUuid !== modelUuid + ); + }); + }, - updateVehicle: (modelUuid, updates) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - Object.assign(vehicle, updates); - } - }); - }, + updateVehicle: (modelUuid, updates) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + Object.assign(vehicle, updates); + } + }); + }, - clearvehicles: () => { - set((state) => { - state.vehicles = []; - }); - }, + clearvehicles: () => { + set((state) => { + state.vehicles = []; + }); + }, - setVehicleActive: (modelUuid, isActive) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.isActive = isActive; - } - }); - }, + setVehicleActive: (modelUuid, isActive) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.isActive = isActive; + } + }); + }, - setVehiclePicking: (modelUuid, isPicking) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.isPicking = isPicking; - } - }); - }, + setVehiclePicking: (modelUuid, isPicking) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.isPicking = isPicking; + } + }); + }, - updateSteeringAngle: (modelUuid, steeringAngle) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.point.action.steeringAngle = steeringAngle; - } - }); - }, + updateSteeringAngle: (modelUuid, steeringAngle) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.point.action.steeringAngle = steeringAngle; + } + }); + }, - incrementVehicleLoad: (modelUuid, incrementBy) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.currentLoad += incrementBy; - } - }); - }, + incrementVehicleLoad: (modelUuid, incrementBy) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad += incrementBy; + } + }); + }, - decrementVehicleLoad: (modelUuid, decrementBy) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.currentLoad -= decrementBy; - } - }); - }, + decrementVehicleLoad: (modelUuid, decrementBy) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad -= decrementBy; + } + }); + }, - setVehicleLoad: (modelUuid, load) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.currentLoad = load; - } - }); - }, + setVehicleLoad: (modelUuid, load) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad = load; + } + }); + }, - setVehicleState: (modelUuid, newState) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.state = newState; - } - }); - }, + setVehicleState: (modelUuid, newState) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.state = newState; + } + }); + }, - addCurrentMaterial: (modelUuid, materialType, materialId) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.currentMaterials.push({ materialType, materialId }); - } - }); - }, + addCurrentMaterial: (modelUuid, materialType, materialId) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentMaterials.push({ materialType, materialId }); + } + }); + }, - setCurrentMaterials: (modelUuid, materials) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.currentMaterials = materials; - } - }); - }, + setCurrentMaterials: (modelUuid, materials) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentMaterials = materials; + } + }); + }, - removeLastMaterial: (modelUuid) => { - let removedMaterial: { materialId: string; materialType: string; } | undefined; - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - if (vehicle.currentMaterials.length > 0) { - const material = vehicle.currentMaterials.pop(); - if (material) { - removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + removeLastMaterial: (modelUuid) => { + let removedMaterial: { materialId: string; materialType: string; } | undefined; + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + if (vehicle.currentMaterials.length > 0) { + const material = vehicle.currentMaterials.pop(); + if (material) { + removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + } } } - } - }); - return removedMaterial; - }, + }); + return removedMaterial; + }, - getLastMaterial: (modelUuid) => { - let removedMaterial: { materialId: string; materialType: string; } | undefined; - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - if (vehicle.currentMaterials.length > 0) { - removedMaterial = { - materialId: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialId, - materialType: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialType - }; + getLastMaterial: (modelUuid) => { + let removedMaterial: { materialId: string; materialType: string; } | undefined; + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + if (vehicle.currentMaterials.length > 0) { + removedMaterial = { + materialId: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialId, + materialType: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialType + }; + } } - } - }); - return removedMaterial; - }, + }); + return removedMaterial; + }, - clearCurrentMaterials: (modelUuid) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.currentMaterials = []; - } - }); - }, + clearCurrentMaterials: (modelUuid) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentMaterials = []; + } + }); + }, - incrementActiveTime: (modelUuid, incrementBy) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.activeTime += incrementBy; - } - }); - }, + incrementActiveTime: (modelUuid, incrementBy) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.activeTime += incrementBy; + } + }); + }, - incrementIdleTime: (modelUuid, incrementBy) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.idleTime += incrementBy; - } - }); - }, + incrementIdleTime: (modelUuid, incrementBy) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.idleTime += incrementBy; + } + }); + }, - resetTime: (modelUuid) => { - set((state) => { - const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); - if (vehicle) { - vehicle.activeTime = 0; - vehicle.idleTime = 0; - } - }); - }, + resetTime: (modelUuid) => { + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.activeTime = 0; + vehicle.idleTime = 0; + } + }); + }, - getVehicleById: (modelUuid) => { - return get().vehicles.find((v) => v.modelUuid === modelUuid); - }, + getVehicleById: (modelUuid) => { + return get().vehicles.find((v) => v.modelUuid === modelUuid); + }, - getVehiclesByProduct: (productId) => { - return get().vehicles.filter((v) => v.productId === productId); - }, + getVehiclesByProduct: (productId) => { + return get().vehicles.filter((v) => v.productId === productId); + }, - getVehiclesByState: (state) => { - return get().vehicles.filter((v) => v.state === state); - }, + getVehiclesByState: (state) => { + return get().vehicles.filter((v) => v.state === state); + }, - getActiveVehicles: () => { - return get().vehicles.filter((v) => v.isActive); - }, - })) -); + getActiveVehicles: () => { + return get().vehicles.filter((v) => v.isActive); + }, + })) + ) +} + +export type VehicleStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/styles/layout/compareLayout.scss b/app/src/styles/layout/compareLayout.scss index bee1ab8..e7d54c6 100644 --- a/app/src/styles/layout/compareLayout.scss +++ b/app/src/styles/layout/compareLayout.scss @@ -4,7 +4,18 @@ .initial-selectLayout-wrapper { position: fixed; top: 100px; + right: 40px; + z-index: 10; + + .regularDropdown-container { + background: var(--background-color); + } +} + +.selectLayout-wrapper { + position: fixed; left: 40px; + top: 100px; z-index: 10; .regularDropdown-container { @@ -26,16 +37,22 @@ animation: slideInFromRight 0.4s ease-out forwards; user-select: none; - - .selectLayout-wrapper { - + .resizer { + width: 32px; + height: 32px; + @include flex-center; + padding: 6px; position: absolute; - top: 100px; - right: 40px; - - .regularDropdown-container { - background: var(--background-color); - } + top: 50%; + left: 0; + transform: translate(-50%, -50%); + background: var(--background-color); + backdrop-filter: blur(20px); + box-shadow: $box-shadow-heavy; + border-radius: 50%; + cursor: ew-resize; + transition: transform 0.1s ease; + z-index: 10; } .chooseLayout-container { @@ -45,26 +62,14 @@ justify-content: center; align-items: center; position: relative; + overflow: hidden; - .resizer { - width: 32px; - height: 32px; - @include flex-center; - padding: 6px; + .compare-layout-canvas-container { position: absolute; - top: 50%; - left: 0; - transform: translate(-50%, -50%); - background: var(--background-color); - backdrop-filter: blur(20px); - box-shadow: $box-shadow-heavy; - border-radius: 50%; - cursor: ew-resize; - transition: transform 0.1s ease; - z-index: 10; - - - + height: 100vh; + width: 100vw; + top: 0; + right: 0; } .chooseLayout-wrapper { @@ -83,10 +88,8 @@ text-align: center; svg { - width: 100%; } - } .value { @@ -108,12 +111,8 @@ transition: all 0.2s ease; &:hover { - transform: translateY(-1px); } - - - } .displayLayouts-container { @@ -149,7 +148,6 @@ .layouts-container { .layout { - padding: 6px 0; } @@ -163,26 +161,20 @@ width: 100%; &:hover { - background-color: var(--highlight-text-color) !important; + background-color: var( + --highlight-text-color + ) !important; border-radius: 4px; .layout { color: var(--text-button-color) !important; - } svg { - path { - fill: var(--text-button-color) !important; } } - - .layout { - - color: var(--background-color-accent); - } } } } @@ -191,6 +183,226 @@ } } +.compare-result-container { + display: flex; + flex-direction: column; + gap: 6px; + position: fixed; + bottom: 40px; + width: 100%; + min-height: 200px; + z-index: 10; + background: var(--background-color-secondary); + backdrop-filter: blur(20px); + padding: 18px 8px; + + .header { + width: fit-content; + background-color: var(--background-color-solid); + color: var(--background-color-accent); + padding: 6px 10px; + border-radius: 6px; + } + + .compare-result-wrapper { + display: flex; + gap: 12px; + + .comparisionCard { + position: relative; + flex: 1; + width: auto; + max-height: 200px; + background: var(--background-color); + outline: 1px solid var(--border-color); + outline-offset: -1px; + border-radius: 12px; + padding: 8px 12px; + overflow: hidden; + } + + .performanceResult-wrapper { + min-width: 328px; + flex: 0; + position: relative; + padding-right: 65px; + + .header { + display: flex; + gap: 12px; + align-items: center; + } + + .metrics-container { + display: flex; + gap: 12px; + height: 100%; + + .metrics-left { + display: flex; + flex-direction: column; + justify-content: space-around; + height: 100%; + + .metric { + .metric-label { + display: flex; + align-items: center; + gap: 6px; + + span { + display: flex; + } + } + .metric-value { + padding-top: 6px; + font-size: var(--font-size-xlarge); + color: var(--background-color-accent); + font-weight: 600; + } + } + + .label { + padding-bottom: 68px; + } + } + + .metrics-right { + height: fit-content; + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(2, 1fr); + + gap: 2px; + overflow: visible; + + margin: auto 0; + + .metric-wrapper { + position: relative; + width: 64px; + height: 50px; + overflow: visible; // allow content like labels to overflow + + &:nth-child(1) { + .metric-label { + top: -57%; + left: 220%; + } + &::after { + content: ""; + position: absolute; + + top: -100%; + left: 50%; + width: 100%; // Required for visible shape + height: 40px; + background-color: #b7b7c6; + + // Custom polygon shape (adjust if needed) + clip-path: polygon( + 96% 52%, + 96% 54%, + 45% 53%, + 3% 100%, + 0 100%, + 42% 52% + ); + + z-index: 0; // Behind any inner content + } + } + + // Optional: content above the shape + > * { + position: relative; + z-index: 1; + } + + &:nth-child(2) { + grid-column-start: 1; + grid-row-start: 2; + .metric-label { + white-space: normal; + width: 50px; + left: 230%; + } + } + &:nth-child(3) { + grid-row: span 2 / span 2; + grid-column-start: 2; + grid-row-start: 1; + margin-top: 40%; + left: -16px; + position: relative; + } + } + + .metric-label { + position: absolute; + top: 0px; + left: 0%; + white-space: nowrap; + + transform: translate(-50%, -50%); + + font-size: 10px; + z-index: 1; + } + + .metric { + width: 100%; + height: 100%; + position: relative; + display: flex; + justify-content: center; + align-items: center; + + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + + background: var(--background-color, wheat); + clip-path: polygon( + 25% 0%, + 75% 0%, + 100% 50%, + 75% 100%, + 25% 100%, + 0% 50% + ); + filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.25)); + + z-index: 0; + } + + // Content stays above the shape + > * { + position: relative; + z-index: 1; + } + } + } + } + + .simulation-tag { + background: var(--background-color-button); + + color: var(--icon-default-color-active); + position: absolute; + bottom: 0; + right: 0; + padding: 10px 5px; + border-radius: 12px 0 0 0; + } + } + } +} + @keyframes slideInFromRight { from { transform: translateX(100%); @@ -203,11 +415,175 @@ } } +.energy-usage { + position: relative; -// body.compare-layout-open { -// main { -// padding-right: 10px; + .energy-usage-wrapper { + h4 { + font-weight: 600; + } -// transition: padding 0.3s ease; -// } -// } \ No newline at end of file + .value { + padding-top: 25px; + font-size: var(--font-size-xxxlarge); + color: var(--background-color-accent); + } + } + + .simulation-details { + position: absolute; + bottom: 12px; + right: 12px; + + .simulation-wrapper { + display: flex; + align-items: center; + gap: 6px; + + .icon { + width: 20px; + height: 20px; + border-radius: 50%; + background-color: var(--background-color-accent); + } + } + } + + .chart { + width: 90%; + position: absolute; + top: 10px; + left: 0; + } +} + +.throughPutCard-container { + .layers-wrapper { + padding: 20px 10px; + height: 100%; + width: 100%; + display: flex; + justify-content: space-between; + + .layer-wrapper { + display: flex; + flex-direction: column; + + &:last-child { + justify-content: end; + } + } + } + + .chart { + height: 90%; + position: absolute; + bottom: 0; + left: 0; + } +} + +.cycle-time-container { + .cycle-main { + display: flex; + justify-content: space-between; + height: 100%; + + .layers-wrapper { + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + + .layers { + display: flex; + flex-direction: column; + gap: 4px; + + .layer-name { + color: var(--background-color-accent); + } + + .layer-time { + font-size: var(--font-size-large); + } + + .layer-profit { + color: #14ca44; + text-align: end; + + span { + color: #14ca44; + } + } + } + } + } +} + +.overallDowntime-container { + .totalDownTime-wrapper { + display: flex; + + .totalDownTime { + width: 70%; + background: var(--background-color-secondary); + backdrop-filter: blur(20px); + border-radius: 12px; + + display: flex; + justify-content: space-between; + align-items: center; + gap: 20px; + padding: 8px 10px; + margin: 44px 0; + + .totalDownTime-right { + display: flex; + flex-direction: column; + gap: 6px; + } + + .totalDownTime-left { + display: flex; + align-items: center; + gap: 6px; + + .value { + font-size: var(--font-size-xlarge); + color: var(--background-color-button); + } + } + } + + .chart { + width: 30%; + position: relative; + } + } +} + +.overallScrapRate { + .overallScrapRate-wrapper { + display: flex; + + .overallScrapRate-value { + width: 50%; + display: flex; + flex-direction: column; + gap: 6px; + margin: 40px 0; + + .overallScrapRate-key { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .chart { + width: 50%; + position: relative; + } + } +} diff --git a/app/src/styles/layout/compareLayoutPopUp.scss b/app/src/styles/layout/compareLayoutPopUp.scss index 5182cb6..48820a1 100644 --- a/app/src/styles/layout/compareLayoutPopUp.scss +++ b/app/src/styles/layout/compareLayoutPopUp.scss @@ -202,6 +202,8 @@ gap: 6px; } } + + } diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 9d114e0..80125e3 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -221,7 +221,7 @@ padding: 13px 5px; background: var(--background-color-secondary); border-radius: #{$border-radius-medium}; - + box-shadow:var(--box-shadow-light); display: flex; justify-content: space-between; @@ -486,9 +486,6 @@ .add-icon { transform: scale(1.1); - - - } .kebab-icon { @@ -539,7 +536,7 @@ gap: 4px; .saved-history-count { - font-size: var(--font-size-tiny) + font-size: var(--font-size-tiny); } } } @@ -556,7 +553,6 @@ gap: 12px; .version-name { - background: var(--background-color); border: 1px solid var(--border-color); color: var(--text-color); @@ -588,7 +584,6 @@ display: flex; flex-direction: column; gap: 6px; - } .saved-by { @@ -597,7 +592,6 @@ gap: 6px; .user-profile { - background: var(--background-color-accent); color: var(--text-button-color); width: 20px; @@ -612,7 +606,6 @@ .user-name { text-transform: capitalize; - } } @@ -625,8 +618,6 @@ } } } - - } .no-event-selected { @@ -665,7 +656,7 @@ path { stroke: var(--text-button-color); - strokeWidth: 1.3; + stroke-width: 1.3; } } } @@ -686,7 +677,6 @@ max-height: 60vh; .sidebar-right-content-container { - .dataSideBar { .inputs-wrapper { display: flex; @@ -922,6 +912,7 @@ .display-element { width: 100%; height: 150px; + @include flex-center; background: var(--background-color); backdrop-filter: blur(20px); border-radius: 5px; @@ -1031,7 +1022,7 @@ path { stroke: var(--accent-color); - strokeWidth: 1.5px; + stroke-width: 1.5px; } &:hover { @@ -1366,15 +1357,105 @@ } } + .aisle-properties-container { + max-height: 65vh; + overflow: auto; + .aisle-texture-container { + max-height: 40vh; + overflow: auto; + .aisle-list { + width: calc(100% - 8px); + text-align: start; + padding: 4px 6px; + display: flex; + align-items: center; + gap: 6px; + border-radius: #{$border-radius-large}; + margin: 2px 6px; + .texture-display { + height: 34px; + width: 34px; + background: #7e7e7e86; + border-radius: #{$border-radius-large}; + margin-right: 4px; + overflow: hidden; + } + .aisle-color { + text-transform: capitalize; + } + .aisle-brief { + font-size: var(--font-size-small); + color: var(--input-text-color); + } + &.selected { + background: var(--background-color-accent); + color: var(--text-button-color); + &:hover { + background: var(--background-color-accent); + } + } + &:hover { + background: var(--background-color-secondary); + } + } + } + .value-field-container { + margin: 0; + } + .presets-list-container { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 6px; + padding-left: 7px; + .preset-list { + background: #444; + height: 90px; + width: 90px; + border-radius: #{$border-radius-large}; + overflow: hidden; + .thumbnail { + height: 100%; + width: 100%; + border-radius: #{$border-radius-large}; + outline-offset: -1px; + img { + height: 100%; + width: 100%; + object-fit: cover; + transition: all 0.2s; + } + &.selected { + outline: 2px solid var(--border-color-accent); + outline-offset: -2px; + &:hover { + outline: 2px solid var(--border-color-accent); + img { + transform: scale(1); + } + } + } + &:hover { + outline: 1px solid var(--border-color); + img { + transform: scale(1.1); + } + } + } + } + } + } + .global-properties-container, .analysis-main-container, .asset-properties-container, - .zone-properties-container { + .zone-properties-container, + .aisle-properties-container { .header { @include flex-space-between; padding: 10px 12px; color: var(--text-color); - + width: 100%; .input-value { color: inherit; } @@ -1756,9 +1837,11 @@ width: 100%; height: 100%; font-size: var(--font-size-regular); - background: linear-gradient(0deg, - rgba(37, 24, 51, 0) 0%, - rgba(52, 41, 61, 0.5) 100%); + background: linear-gradient( + 0deg, + rgba(37, 24, 51, 0) 0%, + rgba(52, 41, 61, 0.5) 100% + ); pointer-events: none; backdrop-filter: blur(8px); opacity: 0; @@ -1835,9 +1918,6 @@ } } - - - .versionSaved { min-width: 449px; position: fixed; @@ -1897,7 +1977,6 @@ } button { - font-size: var(--font-size-small); display: flex; justify-content: center; @@ -1909,10 +1988,7 @@ cursor: pointer; } } - - } - } .dismissing { @@ -1936,7 +2012,6 @@ border-radius: #{$border-radius-large}; backdrop-filter: blur(15px); outline: 1px solid var(--border-color); - display: flex; flex-direction: column; gap: 30px; @@ -1955,7 +2030,6 @@ .version-name, .version-description { - background: var(--background-color); backdrop-filter: blur(20px); border-radius: 20px; @@ -1971,7 +2045,6 @@ right: 8px; font-size: var(--font-size-tiny); color: var(--text-disabled); - } input { @@ -1984,7 +2057,6 @@ .version-description { textarea { - padding: 4px 8px; width: 100%; min-height: 101px; @@ -2001,7 +2073,6 @@ gap: 20px; .save { - display: flex; justify-content: center; align-items: center; @@ -2028,4 +2099,4 @@ text-transform: capitalize; } } -} \ No newline at end of file +} diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 53e3193..e8628ec 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -69,7 +69,7 @@ pointer-events: all; transition: all 0.3s linear; - &.bottom{ + &.bottom { bottom: var(--bottomWidth); } @@ -121,7 +121,8 @@ .zone-container.visualization-playing { bottom: 74px; - &.bottom{ + + &.bottom { bottom: var(--bottomWidth); } } @@ -612,7 +613,9 @@ top: 18px; right: 5px; transform: translate(0px, 0); + overflow: hidden; background: var(--background-color); + backdrop-filter: blur(20px); z-index: 10; display: flex; diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index adfdc78..b9f7616 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -28,4 +28,88 @@ interface Asset { } }; -type Assets = Asset[]; \ No newline at end of file +type Assets = Asset[]; + + +type PointTypes = 'Aisle' | 'Wall' | 'Floor' | 'Zone'; + +interface Point { + uuid: string; + pointType: PointTypes; + position: [number, number, number]; + layer: number; +} + + +type AisleTypes = 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle'; + +type AisleColors = 'gray' | 'yellow' | 'green' | 'orange' | 'blue' | 'purple' | 'red' | 'bright green' | 'yellow-black' | 'white-black' + +interface SolidAisle { + aisleType: 'solid-aisle'; + aisleColor: AisleColors; + aisleWidth: number; +} + +interface DashedAisle { + aisleType: 'dashed-aisle'; + aisleColor: AisleColors; + aisleWidth: number; + dashLength: number; + gapLength: number; +} + +interface StrippedAisle { + aisleType: 'stripped-aisle'; + aisleColor: AisleColors; + aisleWidth: number; +} + +interface DottedAisle { + aisleType: 'dotted-aisle'; + aisleColor: AisleColors; + dotRadius: number; + gapLength: number; +} + +interface ArrowAisle { + aisleType: 'arrow-aisle'; + aisleColor: AisleColors; + aisleWidth: number; +} + +interface ArrowsAisle { + aisleType: 'arrows-aisle'; + aisleColor: AisleColors; + aisleWidth: number; + aisleLength: number; + gapLength: number; +} + +interface ArcAisle { + aisleType: 'arc-aisle'; + aisleColor: AisleColors; + aisleWidth: number; +} + +interface CircleAisle { + aisleType: 'circle-aisle'; + aisleColor: AisleColors; + aisleWidth: number; +} + +interface JunctionAisle { + aisleType: 'junction-aisle'; + aisleColor: AisleColors; + aisleWidth: number; +} + +type AisleType = SolidAisle | DashedAisle | StrippedAisle | DottedAisle | ArrowAisle | ArrowsAisle | ArcAisle | CircleAisle | JunctionAisle; + +interface Aisle { + uuid: string; + points: [Point, Point]; + type: AisleType; +} + +type Aisles = Aisle[]; \ No newline at end of file diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 5193ee1..c2cb62d 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -158,7 +158,7 @@ export type RoofConfig = { export type AisleConfig = { width: number; height: number; - defaultColor: number; + defaultColor: string; }; export type ZoneConfig = { @@ -345,7 +345,7 @@ export const roofConfig: RoofConfig = { export const aisleConfig: AisleConfig = { width: 0.1, // Width of the aisles height: 0.01, // Height of the aisles - defaultColor: 0xE2AC09, // Default color of the aisles + defaultColor: '#E2AC09', // Default color of the aisles }; export const zoneConfig: ZoneConfig = {