From 2c6708117339f6a508ef076a4126437584b8b6aa Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 13 May 2025 17:21:52 +0530 Subject: [PATCH] Refactor success notifications to use echo instead of toast; update selection controls and duplication logic - Changed success notifications from `toast.success` to `echo.success` in copy, duplication, move, rotate, and selection controls. - Updated selection logic in `PostProcessing` to use `flattenChildren` for better performance. - Enhanced `Simulator` component to handle selected product state and execution order more effectively. - Modified `useSelectedItem` state to include `category` and `subCategory` properties for better item classification. - Adjusted `WallItem` interface to standardize type values and added `modelfileID` for improved asset management. --- .../components/layout/sidebarLeft/Assets.tsx | 20 +- .../IntialLoad/loadInitialFloorItems.ts | 5 +- .../IntialLoad/loadInitialWallItems.ts | 118 +- app/src/modules/builder/builder.tsx | 28 - app/src/modules/builder/csg/csg.tsx | 11 +- .../builder/eventFunctions/handleMeshDown.ts | 39 +- .../geomentries/assets/addAssetModel.ts | 4 +- .../geomentries/assets/deleteFloorItems.ts | 2 +- .../builder/geomentries/layers/deleteLayer.ts | 2 +- .../builder/geomentries/lines/deleteLine.ts | 2 +- .../geomentries/pillars/deletePillar.ts | 2 +- .../builder/geomentries/points/deletePoint.ts | 2 +- .../builder/geomentries/walls/addWallItems.ts | 204 +- .../geomentries/walls/deleteWallItems.ts | 9 +- .../builder/groups/floorItemsGroup.tsx | 2 +- .../modules/builder/groups/wallItemsGroup.tsx | 52 +- .../builder/groups/wallsAndWallItems.tsx | 3 - app/src/modules/builder/groups/wallsMesh.tsx | 2 +- .../socket/socketResponses.dev.tsx | 1911 +++++++++-------- .../selectionControls/copyPasteControls.tsx | 2 +- .../selectionControls/duplicationControls.tsx | 2 +- .../selectionControls/moveControls.tsx | 2 +- .../selectionControls/rotateControls.tsx | 2 +- .../selectionControls/selectionControls.tsx | 2 +- .../scene/postProcessing/postProcessing.tsx | 4 +- .../simulation/simulator/simulator.tsx | 13 +- app/src/store/store.ts | 2 +- app/src/types/world/worldTypes.d.ts | 17 +- 28 files changed, 1257 insertions(+), 1207 deletions(-) diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index dd2c2a2..e693684 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -140,16 +140,14 @@ const Assets: React.FC = () => { alt={asset.filename} className="asset-image" onPointerDown={() => { - if (asset.category !== 'Feneration') { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: - asset.type === "undefined" - ? undefined - : asset.type, - }); - } + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type + }); }} /> @@ -206,6 +204,8 @@ const Assets: React.FC = () => { asset.type === "undefined" ? undefined : asset.type, + category: asset.category, + subCategory: asset.subCategory }); }} /> diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index e625116..0d96c8d 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -71,7 +71,6 @@ async function loadInitialFloorItems( // 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); @@ -81,7 +80,6 @@ async function loadInitialFloorItems( // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { - // const blobUrl = URL.createObjectURL(indexedDBModel); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); @@ -102,7 +100,6 @@ async function loadInitialFloorItems( } // 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()); @@ -338,7 +335,7 @@ function checkLoadingCompletion( resolve: () => void ) { if (modelsLoaded === modelsToLoad) { - toast.success("Models Loaded!"); + echo.success("Models Loaded!"); dracoLoader.dispose(); } resolve(); diff --git a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts index f9799a8..2258f3b 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts @@ -1,54 +1,102 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; - +import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; +import * as THREE from 'three'; import * as Types from "../../../types/world/worldTypes"; import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi'; - -////////// Load the Wall Items's intially of there is any ////////// +import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; async function loadInitialWallItems( setWallItems: Types.setWallItemSetState, - AssetConfigurations: Types.AssetConfigurations ): Promise { + try { + const email = localStorage.getItem('email'); + if (!email) { + throw new Error('No email found in localStorage'); + } - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const organization = email.split("@")[1].split(".")[0]; + const items = await getWallItems(organization); - const items = await getWallItems(organization); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - localStorage.setItem("WallItems", JSON.stringify(items)); - if (items.length > 0) { - const storedWallItems: Types.wallItems = items; + if (!items || items.length === 0) { + localStorage.removeItem("WallItems"); + return; + } - const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { - const loader = new GLTFLoader(); + localStorage.setItem("WallItems", JSON.stringify(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); + + const loadedWallItems = await Promise.all(items.map(async (item: Types.WallItem) => { + // Check THREE.js cache first + const cachedModel = THREE.Cache.get(item.modelName!); + if (cachedModel) { + return processModel(cachedModel, item); + } + + // Check IndexedDB cache + const cachedModelBlob = await retrieveGLTF(item.modelfileID!); + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + return new Promise((resolve) => { + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.add(item.modelName!, gltf); + resolve(processModel(gltf, item)); + }); + }); + } + + // Load from original URL if not cached + const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; return new Promise((resolve) => { - loader.load(AssetConfigurations[item.modelName!].modelUrl, (gltf) => { - const model = gltf.scene; - model.uuid = item.modelUuid!; - - model.children[0].children.forEach((child: any) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } + loader.load(modelUrl, async (gltf) => { + try { + // Cache the model + const modelBlob = await fetch(modelUrl).then((res) => res.blob()); + await storeGLTF(item.modelName!, modelBlob); + THREE.Cache.add(item.modelName!, gltf); + resolve(processModel(gltf, item)); + } catch (error) { + console.error('Failed to cache model:', error); + resolve(processModel(gltf, item)); + } }); - - resolve({ - type: item.type, - model: model, - modelName: item.modelName, - scale: item.scale, - csgscale: item.csgscale, - csgposition: item.csgposition, - position: item.position, - quaternion: item.quaternion, - }); - }); }); })); setWallItems(loadedWallItems); + } catch (error) { + console.error('Failed to load wall items:', error); } } -export default loadInitialWallItems; +function processModel(gltf: GLTF, item: Types.WallItem): Types.WallItem { + const model = gltf.scene.clone(); + model.uuid = item.modelUuid!; + + model.children[0]?.children?.forEach((child: THREE.Object3D) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + return { + type: item.type, + model: model, + modelName: item.modelName, + modelfileID: item.modelfileID, + scale: item.scale, + csgscale: item.csgscale, + csgposition: item.csgposition, + position: item.position, + quaternion: item.quaternion, + }; +} + +export default loadInitialWallItems; \ No newline at end of file diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index f43cf3d..993bc8f 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -126,32 +126,6 @@ export default function Builder() { // dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); // loader.setDRACOLoader(dracoLoader); - ////////// Assest Configuration Values ////////// - - const AssetConfigurations: Types.AssetConfigurations = { - arch: { - modelUrl: arch, - scale: [0.75, 0.75, 0.75], - csgscale: [2, 4, 0.5], - csgposition: [0, 2, 0], - type: "Fixed-Move", - }, - door: { - modelUrl: door, - scale: [0.75, 0.75, 0.75], - csgscale: [2, 4, 0.5], - csgposition: [0, 2, 0], - type: "Fixed-Move", - }, - window: { - modelUrl: Window, - scale: [0.75, 0.75, 0.75], - csgscale: [5, 3, 0.5], - csgposition: [0, 1.5, 0], - type: "Free-Move", - }, - }; - ////////// All Toggle's ////////// useEffect(() => { @@ -247,7 +221,6 @@ export default function Builder() { floorGroupAisle={floorGroupAisle} scene={scene} onlyFloorlines={onlyFloorlines} - AssetConfigurations={AssetConfigurations} itemsGroup={itemsGroup} isTempLoader={isTempLoader} tempLoader={tempLoader} @@ -260,7 +233,6 @@ export default function Builder() { = (props) => { } }); } - props.hoveredDeletableWallItem.current = hovered ? object : null; + let currentObject = object; + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Mesh; + } + if (currentObject) { + props.hoveredDeletableWallItem.current = hovered ? currentObject : null; + } }; return ( diff --git a/app/src/modules/builder/eventFunctions/handleMeshDown.ts b/app/src/modules/builder/eventFunctions/handleMeshDown.ts index 1f00038..9fb7839 100644 --- a/app/src/modules/builder/eventFunctions/handleMeshDown.ts +++ b/app/src/modules/builder/eventFunctions/handleMeshDown.ts @@ -35,29 +35,26 @@ function handleMeshDown( } if (event.intersections.length > 0) { - const clickedIndex = wallItems.findIndex((item) => item.model === event.intersections[0]?.object?.parent?.parent); - if (clickedIndex !== -1) { - setSelectedItemsIndex(clickedIndex); - const wallItemModel = wallItems[clickedIndex]?.model; - if (wallItemModel && wallItemModel.parent && wallItemModel.parent.parent) { - currentWallItem.current = (wallItemModel.parent.parent.children[0]?.children[1]?.children[0] as Types.Mesh) || null; - setSelectedWallItem(wallItemModel.parent); - // currentWallItem.current?.children.forEach((child) => { - // if ((child as THREE.Mesh).isMesh && child.name !== "CSG_REF") { - // const material = (child as THREE.Mesh).material; - // if (Array.isArray(material)) { - // material.forEach(mat => { - // if (mat instanceof THREE.MeshStandardMaterial) { - // mat.emissive = new THREE.Color("green"); - // } - // }); - // } else if (material instanceof THREE.MeshStandardMaterial) { - // material.emissive = new THREE.Color("green"); - // } - // } - // }); + if (event.object) { + const wallItemModel = event.object; + let currentObject = wallItemModel as THREE.Object3D; + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + if (!currentObject) return; + const clickedIndex = wallItems.findIndex((item) => item.model?.uuid === currentObject.uuid); + if (clickedIndex !== -1) { + setSelectedItemsIndex(clickedIndex); + const wallItemModel = wallItems[clickedIndex]?.model; + if (wallItemModel) { + setSelectedWallItem(wallItemModel); + } } } + } } } diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 6023422..692664e 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -397,7 +397,7 @@ async function handleModelLoad( 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: () => { toast.success("Model Added!"); } }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } }); } else { const data = { @@ -421,7 +421,7 @@ async function handleModelLoad( socket.emit("v2:model-asset:add", data); gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } }); } } diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index f13ebee..5b234b4 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -80,7 +80,7 @@ async function DeleteFloorItems( } setFloorItems(updatedItems); - toast.success("Model Removed!"); + echo.success("Model Removed!"); } } } diff --git a/app/src/modules/builder/geomentries/layers/deleteLayer.ts b/app/src/modules/builder/geomentries/layers/deleteLayer.ts index 41afa6e..4fe2a28 100644 --- a/app/src/modules/builder/geomentries/layers/deleteLayer.ts +++ b/app/src/modules/builder/geomentries/layers/deleteLayer.ts @@ -83,7 +83,7 @@ async function DeleteLayer( floorGroup.current?.remove(meshToRemove); } - toast.success("Layer Removed!"); + echo.success("Layer Removed!"); setRemovedLayer(null); } export default DeleteLayer; diff --git a/app/src/modules/builder/geomentries/lines/deleteLine.ts b/app/src/modules/builder/geomentries/lines/deleteLine.ts index 14a5e27..42b32b3 100644 --- a/app/src/modules/builder/geomentries/lines/deleteLine.ts +++ b/app/src/modules/builder/geomentries/lines/deleteLine.ts @@ -82,7 +82,7 @@ function deleteLine( } }); - toast.success("Line Removed!"); + echo.success("Line Removed!"); } export default deleteLine; diff --git a/app/src/modules/builder/geomentries/pillars/deletePillar.ts b/app/src/modules/builder/geomentries/pillars/deletePillar.ts index b735c81..39e0b28 100644 --- a/app/src/modules/builder/geomentries/pillars/deletePillar.ts +++ b/app/src/modules/builder/geomentries/pillars/deletePillar.ts @@ -13,7 +13,7 @@ function DeletePillar( (hoveredDeletablePillar.current.material).dispose(); (hoveredDeletablePillar.current.geometry).dispose(); floorGroup.current.remove(hoveredDeletablePillar.current); - toast.success("Pillar Removed!"); + echo.success("Pillar Removed!"); hoveredDeletablePillar.current = undefined; } } diff --git a/app/src/modules/builder/geomentries/points/deletePoint.ts b/app/src/modules/builder/geomentries/points/deletePoint.ts index 937e57e..827818f 100644 --- a/app/src/modules/builder/geomentries/points/deletePoint.ts +++ b/app/src/modules/builder/geomentries/points/deletePoint.ts @@ -51,7 +51,7 @@ function deletePoint( RemoveConnectedLines(DeletedPointUUID, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines); - toast.success("Point Removed!"); + echo.success("Point Removed!"); } export default deletePoint; diff --git a/app/src/modules/builder/geomentries/walls/addWallItems.ts b/app/src/modules/builder/geomentries/walls/addWallItems.ts index 8b472ec..415fd35 100644 --- a/app/src/modules/builder/geomentries/walls/addWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/addWallItems.ts @@ -1,108 +1,138 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { toast } from 'react-toastify'; - import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; import * as CONSTANTS from '../../../../types/world/worldConstants'; -// import { setWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/setWallItemApi'; import { Socket } from 'socket.io-client'; +import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; async function AddWallItems( - selected: Types.String, + selected: any, raycaster: THREE.Raycaster, CSGGroup: Types.RefMesh, - AssetConfigurations: Types.AssetConfigurations, setWallItems: Types.setWallItemSetState, socket: Socket ): Promise { - - ////////// Load Wall GLtf's and set the positions, rotation, type etc. in state and store in localstorage ////////// - let intersects = raycaster?.intersectObject(CSGGroup.current!, true); const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference")); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - if (wallRaycastIntersection) { - const intersectionPoint = wallRaycastIntersection; - const loader = new GLTFLoader(); - loader.load(AssetConfigurations[selected].modelUrl, async (gltf) => { - const model = gltf.scene; - model.userData = { wall: intersectionPoint.object.parent }; - model.children[0].children.forEach((child) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } + if (!wallRaycastIntersection) return; + + const intersectionPoint = wallRaycastIntersection; + 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); + + // Check THREE.js cache first + const cachedModel = THREE.Cache.get(selected.id); + if (cachedModel) { + handleModelLoad(cachedModel); + return; + } + + // Check IndexedDB cache + const cachedModelBlob = await retrieveGLTF(selected.id); + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(selected.id, gltf); + handleModelLoad(gltf); + }); + return; + } + + // Load from backend if not in any cache + loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`, async (gltf) => { + try { + const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`).then((res) => res.blob()); + await storeGLTF(selected.id, modelBlob); + THREE.Cache.add(selected.id, gltf); + await handleModelLoad(gltf); + } catch (error) { + console.error('Failed to cache model:', error); + handleModelLoad(gltf); + } + }); + + async function handleModelLoad(gltf: GLTF) { + const model = gltf.scene.clone(); + model.userData = { wall: intersectionPoint.object.parent }; + + model.children[0].children.forEach((child) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + const boundingBox = new THREE.Box3().setFromObject(model); + const size = new THREE.Vector3(); + boundingBox.getSize(size); + + const csgscale = [size.x, size.y, size.z] as [number, number, number]; + + const center = new THREE.Vector3(); + boundingBox.getCenter(center); + const csgposition = [center.x, center.y, center.z] as [number, number, number]; + + let positionY = selected.subCategory === 'fixed-move' ? 0 : intersectionPoint.point.y; + if (positionY === 0) { + positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height; + } + + const newWallItem = { + type: selected.subCategory, + model: model, + modelName: selected.name, + modelfileID: selected.id, + scale: [1, 1, 1] as [number, number, number], + csgscale: csgscale, + csgposition: csgposition, + position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number], + quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType + }; + + const email = localStorage.getItem('email'); + const organization = email ? (email.split("@")[1]).split(".")[0] : 'default'; + + const data = { + organization: organization, + modelUuid: model.uuid, + modelName: newWallItem.modelName, + modelfileID: selected.id, + type: selected.subCategory, + csgposition: newWallItem.csgposition, + csgscale: newWallItem.csgscale, + position: newWallItem.position, + quaternion: newWallItem.quaternion, + scale: newWallItem.scale, + socketId: socket.id + }; + + socket.emit('v1:wallItems:set', data); + + setWallItems((prevItems) => { + const updatedItems = [...prevItems, newWallItem]; + + const WallItemsForStorage = updatedItems.map(item => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; }); - const config = AssetConfigurations[selected]; - let positionY = config.type === 'Fixed-Move' ? 0 : intersectionPoint.point.y; - if (positionY === 0) { - positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height; - } - - const newWallItem = { - type: config.type, - model: model, - modelName: selected, - scale: config.scale, - csgscale: config.csgscale, - csgposition: config.csgposition, - position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number], - quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType - }; - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - //REST - - // await setWallItem( - // organization, - // model.uuid, - // newWallItem.modelName, - // newWallItem.type!, - // newWallItem.csgposition!, - // newWallItem.csgscale!, - // newWallItem.position, - // newWallItem.quaternion, - // newWallItem.scale!, - // ) - - //SOCKET - - const data = { - organization: organization, - modelUuid: model.uuid, - modelName: newWallItem.modelName, - type: newWallItem.type!, - csgposition: newWallItem.csgposition!, - csgscale: newWallItem.csgscale!, - position: newWallItem.position, - quaternion: newWallItem.quaternion, - scale: newWallItem.scale!, - socketId: socket.id - } - - socket.emit('v1:wallItems:set', data); - - setWallItems((prevItems) => { - const updatedItems = [...prevItems, newWallItem]; - - const WallItemsForStorage = updatedItems.map(item => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Added!"); - - return updatedItems; - }); + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + echo.success("Model Added!"); + return updatedItems; }); } } -export default AddWallItems; +export default AddWallItems; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts index b5d40f4..80bc75f 100644 --- a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts @@ -1,5 +1,3 @@ -import { toast } from 'react-toastify'; - import * as Types from "../../../../types/world/worldTypes"; // import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi'; import { Socket } from 'socket.io-client'; @@ -13,11 +11,11 @@ function DeleteWallItems( ////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage ////////// - if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current.parent) { + if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) { setWallItems([]); let WallItemsRef = wallItems; - const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.parent?.uuid); - const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.parent?.uuid); + const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.uuid); + const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.uuid); setTimeout(async () => { WallItemsRef = Items; @@ -50,7 +48,6 @@ function DeleteWallItems( }); localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Removed!"); hoveredDeletableWallItem.current = null; }, 50); } diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index e58530b..8bd52f9 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -362,7 +362,7 @@ const FloorItemsGroup = ({ const onDrop = (event: any) => { if (!event.dataTransfer?.files[0]) return; - if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { + if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { addAssetModel( raycaster, state.camera, diff --git a/app/src/modules/builder/groups/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index faae06d..7bdb9e7 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -7,6 +7,7 @@ import { useSelectedWallItem, useSocketStore, useWallItems, + useSelectedItem, } from "../../../store/store"; import { Csg } from "../csg/csg"; import * as Types from "../../../types/world/worldTypes"; @@ -21,7 +22,6 @@ import useModuleStore from "../../../store/useModuleStore"; const WallItemsGroup = ({ currentWallItem, - AssetConfigurations, hoveredDeletableWallItem, selectedItemsIndex, setSelectedItemsIndex, @@ -37,34 +37,22 @@ const WallItemsGroup = ({ const { deletePointOrLine } = useDeletePointOrLine(); const { setSelectedWallItem } = useSelectedWallItem(); const { activeModule } = useModuleStore(); + const { selectedItem, setSelectedItem } = useSelectedItem(); useEffect(() => { // Load Wall Items from the backend - loadInitialWallItems(setWallItems, AssetConfigurations); + loadInitialWallItems(setWallItems); }, []); - ////////// Update the Scale value changes in thewallItems State ////////// - ////////// Update the Position value changes in the selected item ////////// - ////////// Update the Rotation value changes in the selected item ////////// - useEffect(() => { const canvasElement = state.gl.domElement; function handlePointerMove(e: any) { - if ( - selectedItemsIndex !== null && - !deletePointOrLine && - e.buttons === 1 - ) { + if (selectedItemsIndex !== null && !deletePointOrLine && e.buttons === 1) { const Raycaster = state.raycaster; - const intersects = Raycaster.intersectObjects( - CSGGroup.current?.children[0].children!, - true - ); - const Object = intersects.find((child) => - child.object.name.includes("WallRaycastReference") - ); + const intersects = Raycaster.intersectObjects(CSGGroup.current?.children[0].children!, true); + const Object = intersects.find((child) => child.object.name.includes("WallRaycastReference")); if (Object) { (state.controls as any)!.enabled = false; @@ -72,14 +60,14 @@ const WallItemsGroup = ({ const updatedItems = [...prevItems]; let position: [number, number, number] = [0, 0, 0]; - if (updatedItems[selectedItemsIndex].type === "Fixed-Move") { + if (updatedItems[selectedItemsIndex].type === "fixed-move") { position = [ Object!.point.x, Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height, Object!.point.z, ]; - } else if (updatedItems[selectedItemsIndex].type === "Free-Move") { + } else if (updatedItems[selectedItemsIndex].type === "free-move") { position = [Object!.point.x, Object!.point.y, Object!.point.z]; } @@ -95,8 +83,7 @@ const WallItemsGroup = ({ updatedItems[selectedItemsIndex] = { ...updatedItems[selectedItemsIndex], position: position, - quaternion: - Object!.object.quaternion.clone() as Types.QuaternionType, + quaternion: Object!.object.quaternion.clone() as Types.QuaternionType, }; return updatedItems; @@ -128,10 +115,7 @@ const WallItemsGroup = ({ }); currentItem = updatedItems[selectedItemsIndex]; - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); return updatedItems; }); @@ -145,6 +129,7 @@ const WallItemsGroup = ({ // organization, // currentItem?.model?.uuid, // currentItem.modelName, + // currentItem.modelfileID, // currentItem.type!, // currentItem.csgposition!, // currentItem.csgscale!, @@ -158,6 +143,7 @@ const WallItemsGroup = ({ const data = { organization: organization, modelUuid: currentItem.model?.uuid!, + modelfileID: currentItem.modelfileID, modelName: currentItem.modelName!, type: currentItem.type!, csgposition: currentItem.csgposition!, @@ -217,21 +203,19 @@ const WallItemsGroup = ({ }; const onDrop = (event: any) => { - if (!event.dataTransfer?.files[0]) return; + if (selectedItem.category !== 'Fenestration') return; pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + raycaster.setFromCamera(pointer, camera); - if (AssetConfigurations[event.dataTransfer.files[0].name.split(".")[0]]) { - const selected = event.dataTransfer.files[0].name.split(".")[0]; - - if (AssetConfigurations[selected]?.type) { + if (selectedItem.id) { + if (selectedItem.subCategory) { AddWallItems( - selected, + selectedItem, raycaster, CSGGroup, - AssetConfigurations, setWallItems, socket ); @@ -257,7 +241,7 @@ const WallItemsGroup = ({ canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("dragover", onDragOver); }; - }, [deleteTool, wallItems]); + }, [deleteTool, wallItems, selectedItem, camera]); useEffect(() => { if (deleteTool && activeModule === "builder") { diff --git a/app/src/modules/builder/groups/wallsAndWallItems.tsx b/app/src/modules/builder/groups/wallsAndWallItems.tsx index 19d5833..320a3bf 100644 --- a/app/src/modules/builder/groups/wallsAndWallItems.tsx +++ b/app/src/modules/builder/groups/wallsAndWallItems.tsx @@ -10,11 +10,9 @@ import handleMeshDown from "../eventFunctions/handleMeshDown"; import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import WallsMesh from "./wallsMesh"; import WallItemsGroup from "./wallItemsGroup"; -import { useEffect } from "react"; const WallsAndWallItems = ({ CSGGroup, - AssetConfigurations, setSelectedItemsIndex, selectedItemsIndex, currentWallItem, @@ -63,7 +61,6 @@ const WallsAndWallItems = ({ { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - if (!socket) return; + if (!socket) return; - socket.on("cameraCreateResponse", (data: any) => { - // console.log('data: ', data); - }); + socket.on("cameraCreateResponse", (data: any) => { + // console.log('data: ', data); + }); - socket.on("userConnectRespones", (data: any) => { - // console.log('data: ', data); - }); + socket.on("userConnectRespones", (data: any) => { + // console.log('data: ', data); + }); - socket.on("userDisConnectRespones", (data: any) => { - // console.log('data: ', data); - }); + socket.on("userDisConnectRespones", (data: any) => { + // console.log('data: ', data); + }); - socket.on("cameraUpdateResponse", (data: any) => { - // console.log('data: ', data); - }); + socket.on("cameraUpdateResponse", (data: any) => { + // console.log('data: ', data); + }); - socket.on("EnvironmentUpdateResponse", (data: any) => { - // console.log('data: ', data); - }); + socket.on("EnvironmentUpdateResponse", (data: any) => { + // console.log('data: ', data); + }); - socket.on("model-asset:response:updates", async (data: any) => { - // console.log('data: ', data); - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "Model created successfully") { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath( - "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" - ); - loader.setDRACOLoader(dracoLoader); - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - - try { - isTempLoader.current = true; - const cachedModel = THREE.Cache.get(data.data.modelName); - let url; - if (cachedModel) { - // console.log(`Getting ${data.data.modelName} from cache`); - const model = cachedModel.scene.clone(); - model.uuid = data.data.modelUuid; - model.userData = { - name: data.data.modelName, - modelId: data.data.modelfileID, - modelUuid: data.data.modelUuid, - }; - model.position.set( - ...(data.data.position as [number, number, number]) - ); - model.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; + socket.on("model-asset:response:updates", async (data: any) => { + // console.log('data: ', data); + if (socket.id === data.socketId) { + return; } - - const newFloorItem: Types.FloorItemType = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - modelfileID: data.data.modelfileID, - position: [...(data.data.position as [number, number, number])], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - gsap.to(model.position, { - y: data.data.position[1], - duration: 1.5, - ease: "power2.out", - }); - gsap.to(model.scale, { - x: 1, - y: 1, - z: 1, - duration: 1.5, - ease: "power2.out", - onComplete: () => { - toast.success("Model Added!"); - }, - }); - } else { - const indexedDBModel = await retrieveGLTF(data.data.modelName); - if (indexedDBModel) { - // console.log(`Getting ${data.data.modelName} from IndexedDB`); - url = URL.createObjectURL(indexedDBModel); - } else { - // console.log(`Getting ${data.data.modelName} from Backend`); - url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; - const modelBlob = await fetch(url).then((res) => res.blob()); - await storeGLTF(data.data.modelfileID, modelBlob); + if (organization !== data.organization) { + return; } - } + if (data.message === "Model created successfully") { + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); - if (url) { - loadModel(url); - } - } 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) + dracoLoader.setDecoderPath( + "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" ); - return updatedItems; - }); + loader.setDRACOLoader(dracoLoader); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - gsap.to(model.position, { - y: data.data.position[1], - duration: 1.5, - ease: "power2.out", - }); - gsap.to(model.scale, { - x: 1, - y: 1, - z: 1, - duration: 1.5, - ease: "power2.out", - onComplete: () => { - toast.success("Model Added!"); - }, - }); + 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); - THREE.Cache.add(data.data.modelName, gltf); - }, - () => { - TempLoader( - new THREE.Vector3(...data.data.position), - isTempLoader, - tempLoader, - itemsGroup - ); + 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); + } + } + + if (url) { + loadModel(url); + } + } 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 + ); + } + ); + } + } else if (data.message === "Model updated successfully") { + itemsGroup.current?.children.forEach((item: THREE.Group) => { + if (item.uuid === data.data.modelUuid) { + item.position.set( + ...(data.data.position as [number, number, number]) + ); + item.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); + } + }); + + setFloorItems((prevItems: Types.FloorItems) => { + if (!prevItems) { + return; + } + let updatedItem: any = null; + const updatedItems = prevItems.map((item) => { + if (item.modelUuid === data.data.modelUuid) { + updatedItem = { + ...item, + position: [...data.data.position] as [number, number, number], + rotation: { + x: data.data.rotation.x, + y: data.data.rotation.y, + z: data.data.rotation.z, + }, + }; + return updatedItem; + } + return item; + }); + return updatedItems; + }); } - ); - } - } else if (data.message === "Model updated successfully") { - itemsGroup.current?.children.forEach((item: THREE.Group) => { - if (item.uuid === data.data.modelUuid) { - item.position.set( - ...(data.data.position as [number, number, number]) - ); - item.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - } }); - setFloorItems((prevItems: Types.FloorItems) => { - if (!prevItems) { - return; - } - let updatedItem: any = null; - const updatedItems = prevItems.map((item) => { - if (item.modelUuid === data.data.modelUuid) { - updatedItem = { - ...item, - position: [...data.data.position] as [number, number, number], - rotation: { - x: data.data.rotation.x, - y: data.data.rotation.y, - z: data.data.rotation.z, - }, - }; - return updatedItem; + socket.on("model-asset:response:updates", (data: any) => { + if (socket.id === data.socketId) { + return; } - return item; - }); - return updatedItems; - }); - } - }); - - socket.on("model-asset:response:updates", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "Model deleted successfully") { - const deletedUUID = data.data.modelUuid; - let items = JSON.parse(localStorage.getItem("FloorItems")!); - - const updatedItems = items.filter( - (item: { modelUuid: string }) => item.modelUuid !== deletedUUID - ); - - const storedItems = JSON.parse( - localStorage.getItem("FloorItems") || "[]" - ); - const updatedStoredItems = storedItems.filter( - (item: { modelUuid: string }) => item.modelUuid !== deletedUUID - ); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - itemsGroup.current.children.forEach((item: any) => { - if (item.uuid === deletedUUID) { - itemsGroup.current.remove(item); - } - }); - setFloorItems(updatedItems); - toast.success("Model Removed!"); - } - }); - - socket.on("Line:response:update", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "line updated") { - const DraggedUUID = data.data.uuid; - const DraggedPosition = new THREE.Vector3( - data.data.position.x, - data.data.position.y, - data.data.position.z - ); - - const point = floorPlanGroupPoint.current.getObjectByProperty( - "uuid", - DraggedUUID - ); - point.position.set( - DraggedPosition.x, - DraggedPosition.y, - DraggedPosition.z - ); - const affectedLines = updateLinesPositions( - { uuid: DraggedUUID, position: DraggedPosition }, - lines - ); - - updateLines(floorPlanGroupLine, affectedLines); - updateDistanceText(scene, floorPlanGroupLine, affectedLines); - updateFloorLines(onlyFloorlines, { - uuid: DraggedUUID, - position: DraggedPosition, - }); - - loadWalls(lines, setWalls); - setUpdateScene(true); - } - }); - - socket.on("Line:response:delete", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "line deleted") { - const line = objectLineToArray(data.data); - const linePoints = line; - const connectedpoints = [linePoints[0][1], linePoints[1][1]]; - - onlyFloorlines.current = onlyFloorlines.current - .map((floorline: any) => - floorline.filter( - (line: any) => - line[0][1] !== connectedpoints[0] && - line[1][1] !== connectedpoints[1] - ) - ) - .filter((floorline: any) => floorline.length > 0); - - const removedLine = lines.current.find( - (item: any) => - (item[0][1] === linePoints[0][1] && - item[1][1] === linePoints[1][1]) || - (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]) - ); - lines.current = lines.current.filter( - (item: any) => item !== removedLine - ); - - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints as [ - number, - string, - number - ][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if ( - (uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1]) || - (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]) - ) { - line.material.dispose(); - line.geometry.dispose(); - floorPlanGroupLine.current.remove(line); - setDeletedLines([line.userData.linePoints]); - } - }); - - connectedpoints.forEach((pointUUID) => { - let isConnected = false; - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - if (uuid1 === pointUUID || uuid2 === pointUUID) { - isConnected = true; + if (organization !== data.organization) { + return; } - }); + if (data.message === "Model deleted successfully") { + const deletedUUID = data.data.modelUuid; + let items = JSON.parse(localStorage.getItem("FloorItems")!); - if (!isConnected) { - floorPlanGroupPoint.current.children.forEach((point: any) => { - if (point.uuid === pointUUID) { + const updatedItems = items.filter( + (item: { modelUuid: string }) => item.modelUuid !== deletedUUID + ); + + const storedItems = JSON.parse( + localStorage.getItem("FloorItems") || "[]" + ); + const updatedStoredItems = storedItems.filter( + (item: { modelUuid: string }) => item.modelUuid !== deletedUUID + ); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + itemsGroup.current.children.forEach((item: any) => { + if (item.uuid === deletedUUID) { + itemsGroup.current.remove(item); + } + }); + setFloorItems(updatedItems); + echo.success("Model Removed!"); + } + }); + + socket.on("Line:response:update", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "line updated") { + const DraggedUUID = data.data.uuid; + const DraggedPosition = new THREE.Vector3( + data.data.position.x, + data.data.position.y, + data.data.position.z + ); + + const point = floorPlanGroupPoint.current.getObjectByProperty( + "uuid", + DraggedUUID + ); + point.position.set( + DraggedPosition.x, + DraggedPosition.y, + DraggedPosition.z + ); + const affectedLines = updateLinesPositions( + { uuid: DraggedUUID, position: DraggedPosition }, + lines + ); + + updateLines(floorPlanGroupLine, affectedLines); + updateDistanceText(scene, floorPlanGroupLine, affectedLines); + updateFloorLines(onlyFloorlines, { + uuid: DraggedUUID, + position: DraggedPosition, + }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + } + }); + + socket.on("Line:response:delete", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "line deleted") { + const line = objectLineToArray(data.data); + const linePoints = line; + const connectedpoints = [linePoints[0][1], linePoints[1][1]]; + + onlyFloorlines.current = onlyFloorlines.current + .map((floorline: any) => + floorline.filter( + (line: any) => + line[0][1] !== connectedpoints[0] && + line[1][1] !== connectedpoints[1] + ) + ) + .filter((floorline: any) => floorline.length > 0); + + const removedLine = lines.current.find( + (item: any) => + (item[0][1] === linePoints[0][1] && + item[1][1] === linePoints[1][1]) || + (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]) + ); + lines.current = lines.current.filter( + (item: any) => item !== removedLine + ); + + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints as [ + number, + string, + number + ][]; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + + if ( + (uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1]) || + (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]) + ) { + line.material.dispose(); + line.geometry.dispose(); + floorPlanGroupLine.current.remove(line); + setDeletedLines([line.userData.linePoints]); + } + }); + + connectedpoints.forEach((pointUUID) => { + let isConnected = false; + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + if (uuid1 === pointUUID || uuid2 === pointUUID) { + isConnected = true; + } + }); + + if (!isConnected) { + floorPlanGroupPoint.current.children.forEach((point: any) => { + if (point.uuid === pointUUID) { + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point); + } + }); + } + }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + echo.success("Line Removed!"); + } + }); + + socket.on("Line:response:delete:point", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "point deleted") { + const point = floorPlanGroupPoint.current?.getObjectByProperty( + "uuid", + data.data + ); point.material.dispose(); point.geometry.dispose(); floorPlanGroupPoint.current.remove(point); - } - }); - } - }); - loadWalls(lines, setWalls); - setUpdateScene(true); + onlyFloorlines.current = onlyFloorlines.current + .map((floorline: any) => + floorline.filter( + (line: any) => + line[0][1] !== data.data && line[1][1] !== data.data + ) + ) + .filter((floorline: any) => floorline.length > 0); - toast.success("Line Removed!"); - } - }); - - socket.on("Line:response:delete:point", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "point deleted") { - const point = floorPlanGroupPoint.current?.getObjectByProperty( - "uuid", - data.data - ); - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point); - - onlyFloorlines.current = onlyFloorlines.current - .map((floorline: any) => - floorline.filter( - (line: any) => - line[0][1] !== data.data && line[1][1] !== data.data - ) - ) - .filter((floorline: any) => floorline.length > 0); - - RemoveConnectedLines( - data.data, - floorPlanGroupLine, - floorPlanGroupPoint, - setDeletedLines, - lines - ); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Point Removed!"); - } - }); - - socket.on("Line:response:delete:layer", async (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "layer deleted") { - setActiveLayer(1); - const removedLayer = data.data; - const removedLines: Types.Lines = lines.current.filter( - (line: any) => line[0][2] === removedLayer - ); - - ////////// Remove Points and lines from the removed layer ////////// - - removedLines.forEach(async (line) => { - line.forEach(async (removedPoint) => { - const removableLines: THREE.Mesh[] = []; - const connectedpoints: string[] = []; - - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints as [ - number, - string, - number - ][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { - connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); - removableLines.push(line as THREE.Mesh); - } - }); - - if (removableLines.length > 0) { - removableLines.forEach((line: any) => { - lines.current = lines.current.filter( - (item: any) => - JSON.stringify(item) !== - JSON.stringify(line.userData.linePoints) + RemoveConnectedLines( + data.data, + floorPlanGroupLine, + floorPlanGroupPoint, + setDeletedLines, + lines ); - line.material.dispose(); - line.geometry.dispose(); - floorPlanGroupLine.current.remove(line); - }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + echo.success("Point Removed!"); } + }); - const point = floorPlanGroupPoint.current.getObjectByProperty( - "uuid", - removedPoint[1] - ); - if (point) { - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point); + socket.on("Line:response:delete:layer", async (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "layer deleted") { + setActiveLayer(1); + const removedLayer = data.data; + const removedLines: Types.Lines = lines.current.filter( + (line: any) => line[0][2] === removedLayer + ); + + ////////// Remove Points and lines from the removed layer ////////// + + removedLines.forEach(async (line) => { + line.forEach(async (removedPoint) => { + const removableLines: THREE.Mesh[] = []; + const connectedpoints: string[] = []; + + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints as [ + number, + string, + number + ][]; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + + if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { + connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); + removableLines.push(line as THREE.Mesh); + } + }); + + if (removableLines.length > 0) { + removableLines.forEach((line: any) => { + lines.current = lines.current.filter( + (item: any) => + JSON.stringify(item) !== + JSON.stringify(line.userData.linePoints) + ); + line.material.dispose(); + line.geometry.dispose(); + floorPlanGroupLine.current.remove(line); + }); + } + + const point = floorPlanGroupPoint.current.getObjectByProperty( + "uuid", + removedPoint[1] + ); + if (point) { + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point); + } + }); + }); + + ////////// Update the remaining lines layer values in the userData and in lines.current ////////// + + let remaining = lines.current.filter( + (line: any) => line[0][2] !== removedLayer + ); + let updatedLines: Types.Lines = []; + remaining.forEach((line: any) => { + let newLines = JSON.parse(JSON.stringify(line)); + if (newLines[0][2] > removedLayer) { + newLines[0][2] -= 1; + newLines[1][2] -= 1; + } + + const matchingLine = floorPlanGroupLine.current.children.find( + (l: any) => + l.userData.linePoints[0][1] === line[0][1] && + l.userData.linePoints[1][1] === line[1][1] + ); + if (matchingLine) { + const updatedUserData = JSON.parse( + JSON.stringify(matchingLine.userData) + ); + updatedUserData.linePoints[0][2] = newLines[0][2]; + updatedUserData.linePoints[1][2] = newLines[1][2]; + matchingLine.userData = updatedUserData; + } + updatedLines.push(newLines); + }); + + lines.current = updatedLines; + localStorage.setItem("Lines", JSON.stringify(lines.current)); + + ////////// Also remove OnlyFloorLines and update it in localstorage ////////// + + onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { + return floor[0][0][2] !== removedLayer; + }); + const meshToRemove = floorGroup.current?.children.find( + (mesh: any) => mesh.name === `Only_Floor_Line_${removedLayer}` + ); + if (meshToRemove) { + meshToRemove.geometry.dispose(); + meshToRemove.material.dispose(); + floorGroup.current?.remove(meshToRemove); + } + + const zonesData = await getZonesApi(organization); + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + zonesData.data.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); + + setLayers(highestLayer); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + echo.success("Layer Removed!"); + } + }); + }, [socket]); + + useEffect(() => { + if (!socket) return; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + + socket.on("wallItemsDeleteResponse", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "wallitem deleted") { + const deletedUUID = data.data.modelUuid; + let WallItemsRef = wallItems; + const Items = WallItemsRef.filter((item: any) => item.model?.uuid !== deletedUUID); + + setWallItems([]); + setTimeout(async () => { + WallItemsRef = Items; + setWallItems(WallItemsRef); + const WallItemsForStorage = WallItemsRef.map((item: any) => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + echo.success("Model Removed!"); + }, 50); } - }); }); - ////////// Update the remaining lines layer values in the userData and in lines.current ////////// + socket.on("wallItemsUpdateResponse", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "wallIitem created") { + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); - let remaining = lines.current.filter( - (line: any) => line[0][2] !== removedLayer - ); - let updatedLines: Types.Lines = []; - remaining.forEach((line: any) => { - let newLines = JSON.parse(JSON.stringify(line)); - if (newLines[0][2] > removedLayer) { - newLines[0][2] -= 1; - newLines[1][2] -= 1; - } + dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + loader.setDRACOLoader(dracoLoader); - const matchingLine = floorPlanGroupLine.current.children.find( - (l: any) => - l.userData.linePoints[0][1] === line[0][1] && - l.userData.linePoints[1][1] === line[1][1] - ); - if (matchingLine) { - const updatedUserData = JSON.parse( - JSON.stringify(matchingLine.userData) - ); - updatedUserData.linePoints[0][2] = newLines[0][2]; - updatedUserData.linePoints[1][2] = newLines[1][2]; - matchingLine.userData = updatedUserData; - } - updatedLines.push(newLines); + // Check THREE.js cache first + const cachedModel = THREE.Cache.get(data.data.modelfileID); + if (cachedModel) { + handleModelLoad(cachedModel); + return; + } + + // Check IndexedDB cache + retrieveGLTF(data.data.modelfileID).then((cachedModelBlob) => { + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(data.data.modelfileID, gltf); + handleModelLoad(gltf); + }); + return; + } + }) + + // Load from backend if not in any cache + loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`, async (gltf) => { + try { + const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`).then((res) => res.blob()); + await storeGLTF(data.data.modelfileID, modelBlob); + THREE.Cache.add(data.data.modelfileID, gltf); + await handleModelLoad(gltf); + } catch (error) { + console.error('Failed to cache model:', error); + handleModelLoad(gltf); + } + }); + + async function handleModelLoad(gltf: GLTF) { + const model = gltf.scene.clone(); + model.uuid = data.data.modelUuid; + + model.children[0].children.forEach((child) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + const newWallItem = { + type: data.data.type, + model: model, + modelName: data.data.modelName, + modelfileID: data.data.modelfileID, + scale: data.data.scale, + csgscale: data.data.csgscale, + csgposition: data.data.csgposition, + position: data.data.position, + quaternion: data.data.quaternion, + }; + + setWallItems((prevItems: Types.wallItems) => { + const updatedItems = [...prevItems, newWallItem]; + + const WallItemsForStorage = updatedItems.map(item => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + echo.success("Model Added!"); + return updatedItems; + }); + } + + } else if (data.message === "wallIitem updated") { + const updatedUUID = data.data.modelUuid; + + setWallItems((prevItems: any) => { + const updatedItems = prevItems.map((item: any) => { + if (item.model.uuid === updatedUUID) { + return { + ...item, + position: data.data.position, + quaternion: data.data.quaternion, + scale: data.data.scale, + csgscale: data.data.csgscale, + csgposition: data.data.csgposition, + }; + } + return item; + }); + + const WallItemsForStorage = updatedItems.map((item: any) => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; + }); + + localStorage.setItem( + "WallItems", + JSON.stringify(WallItemsForStorage) + ); + echo.success("Model Updated!"); + + return updatedItems; + }); + } }); - lines.current = updatedLines; - localStorage.setItem("Lines", JSON.stringify(lines.current)); + return () => { + socket.off("wallItemsDeleteResponse"); + socket.off("wallItemsUpdateResponse"); + }; + }, [wallItems]); - ////////// Also remove OnlyFloorLines and update it in localstorage ////////// - - onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { - return floor[0][0][2] !== removedLayer; - }); - const meshToRemove = floorGroup.current?.children.find( - (mesh: any) => mesh.name === `Only_Floor_Line_${removedLayer}` - ); - if (meshToRemove) { - meshToRemove.geometry.dispose(); - meshToRemove.material.dispose(); - floorGroup.current?.remove(meshToRemove); + function getPointColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: + return CONSTANTS.pointConfig.wallOuterColor; + case CONSTANTS.lineConfig.floorName: + return CONSTANTS.pointConfig.floorOuterColor; + case CONSTANTS.lineConfig.aisleName: + return CONSTANTS.pointConfig.aisleOuterColor; + default: + return CONSTANTS.pointConfig.defaultOuterColor; } + } - const zonesData = await getZonesApi(organization); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - zonesData.data.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); + function getLineColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: + return CONSTANTS.lineConfig.wallColor; + case CONSTANTS.lineConfig.floorName: + return CONSTANTS.lineConfig.floorColor; + case CONSTANTS.lineConfig.aisleName: + return CONSTANTS.lineConfig.aisleColor; + default: + return CONSTANTS.lineConfig.defaultColor; + } + } - setLayers(highestLayer); + useEffect(() => { + if (!socket) return; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Layer Removed!"); - } - }); - }, [socket]); - - useEffect(() => { - if (!socket) return; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - socket.on("wallItemsDeleteResponse", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "wallitem deleted") { - const deletedUUID = data.data.modelUuid; - let WallItemsRef = wallItems; - const Items = WallItemsRef.filter( - (item: any) => item.model?.uuid !== deletedUUID - ); - - setWallItems([]); - setTimeout(async () => { - WallItemsRef = Items; - setWallItems(WallItemsRef); - const WallItemsForStorage = WallItemsRef.map((item: any) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); - toast.success("Model Removed!"); - }, 50); - } - }); - - socket.on("wallItemsUpdateResponse", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "wallIitem created") { - const loader = new GLTFLoader(); - loader.load( - AssetConfigurations[data.data.modelName].modelUrl, - async (gltf) => { - const model = gltf.scene; - model.uuid = data.data.modelUuid; - model.children[0].children.forEach((child) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } - }); - - const newWallItem = { - type: data.data.type, - model: model, - modelName: data.data.modelName, - scale: data.data.scale, - csgscale: data.data.csgscale, - csgposition: data.data.csgposition, - position: data.data.position, - quaternion: data.data.quaternion, - }; - - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems, newWallItem]; - - const WallItemsForStorage = updatedItems.map((item) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); - toast.success("Model Added!"); - - return updatedItems; - }); - } - ); - } else if (data.message === "wallIitem updated") { - const updatedUUID = data.data.modelUuid; - - setWallItems((prevItems: any) => { - const updatedItems = prevItems.map((item: any) => { - if (item.model.uuid === updatedUUID) { - return { - ...item, - position: data.data.position, - quaternion: data.data.quaternion, - scale: data.data.scale, - csgscale: data.data.csgscale, - csgposition: data.data.csgposition, - }; + socket.on("Line:response:create", async (data: any) => { + if (socket.id === data.socketId) { + return; } - return item; - }); + if (organization !== data.organization) { + return; + } + if (data.message === "line create") { + const line: Types.Line = objectLineToArray(data.data); + const type = line[0][3]; + const pointColour = getPointColor(type); + const lineColour = getLineColor(type); + setNewLines([line]); - const WallItemsForStorage = updatedItems.map((item: any) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); - toast.success("Model Updated!"); - - return updatedItems; - }); - } - }); - - return () => { - socket.off("wallItemsDeleteResponse"); - socket.off("wallItemsUpdateResponse"); - }; - }, [wallItems]); - - function getPointColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: - return CONSTANTS.pointConfig.wallOuterColor; - case CONSTANTS.lineConfig.floorName: - return CONSTANTS.pointConfig.floorOuterColor; - case CONSTANTS.lineConfig.aisleName: - return CONSTANTS.pointConfig.aisleOuterColor; - default: - return CONSTANTS.pointConfig.defaultOuterColor; - } - } - - function getLineColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: - return CONSTANTS.lineConfig.wallColor; - case CONSTANTS.lineConfig.floorName: - return CONSTANTS.lineConfig.floorColor; - case CONSTANTS.lineConfig.aisleName: - return CONSTANTS.lineConfig.aisleColor; - default: - return CONSTANTS.lineConfig.defaultColor; - } - } - - useEffect(() => { - if (!socket) return; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - socket.on("Line:response:create", async (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "line create") { - const line: Types.Line = objectLineToArray(data.data); - const type = line[0][3]; - const pointColour = getPointColor(type); - const lineColour = getLineColor(type); - setNewLines([line]); - - line.forEach((line) => { - const existingPoint = - floorPlanGroupPoint.current?.getObjectByProperty("uuid", line[1]); - if (existingPoint) { - return; - } - const geometry = new THREE.BoxGeometry( - ...CONSTANTS.pointConfig.boxScale - ); - const material = new THREE.ShaderMaterial({ - uniforms: { - uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border - uInnerColor: { - value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor), - }, // White color for the inner square - }, - vertexShader: ` + line.forEach((line) => { + const existingPoint = + floorPlanGroupPoint.current?.getObjectByProperty("uuid", line[1]); + if (existingPoint) { + return; + } + const geometry = new THREE.BoxGeometry( + ...CONSTANTS.pointConfig.boxScale + ); + const material = new THREE.ShaderMaterial({ + uniforms: { + uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border + uInnerColor: { + value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor), + }, // White color for the inner square + }, + vertexShader: ` varying vec2 vUv; void main() { @@ -870,7 +899,7 @@ export default function SocketResponses({ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, - fragmentShader: ` + fragmentShader: ` varying vec2 vUv; uniform vec3 uColor; uniform vec3 uInnerColor; @@ -886,162 +915,162 @@ export default function SocketResponses({ } } `, - }); - const point = new THREE.Mesh(geometry, material); - point.name = "point"; - point.uuid = line[1]; - point.userData = { type: type, color: pointColour }; - point.position.set(line[0].x, line[0].y, line[0].z); - currentLayerPoint.current.push(point); + }); + const point = new THREE.Mesh(geometry, material); + point.name = "point"; + point.uuid = line[1]; + point.userData = { type: type, color: pointColour }; + point.position.set(line[0].x, line[0].y, line[0].z); + currentLayerPoint.current.push(point); - floorPlanGroupPoint.current?.add(point); + floorPlanGroupPoint.current?.add(point); + }); + if (dragPointControls.current) { + dragPointControls.current!.objects = currentLayerPoint.current; + } + addLineToScene( + new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), + new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), + lineColour, + line, + floorPlanGroupLine + ); + lines.current.push(line); + + const zonesData = await getZonesApi(organization); + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + zonesData.data.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); + + setLayers(highestLayer); + + Layer2DVisibility( + activeLayer, + floorPlanGroup, + floorPlanGroupLine, + floorPlanGroupPoint, + currentLayerPoint, + dragPointControls + ); + + loadWalls(lines, setWalls); + setUpdateScene(true); + } }); - if (dragPointControls.current) { - dragPointControls.current!.objects = currentLayerPoint.current; - } - addLineToScene( - new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), - new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), - lineColour, - line, - floorPlanGroupLine - ); - lines.current.push(line); - const zonesData = await getZonesApi(organization); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - zonesData.data.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); + return () => { + socket.off("Line:response:create"); + }; + }, [socket, activeLayer]); - setLayers(highestLayer); + useEffect(() => { + if (!socket) return; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - Layer2DVisibility( - activeLayer, - floorPlanGroup, - floorPlanGroupLine, - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls - ); + socket.on("zone:response:updates", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } - loadWalls(lines, setWalls); - setUpdateScene(true); - } - }); + if (data.message === "zone created") { + const pointsArray: [number, number, number][] = data.data.points; + const vector3Array = pointsArray.map( + ([x, y, z]) => new THREE.Vector3(x, y, z) + ); + const newZones = [...zones, data.data]; + setZones(newZones); + const updatedZonePoints = [...zonePoints, ...vector3Array]; + setZonePoints(updatedZonePoints); - return () => { - socket.off("Line:response:create"); - }; - }, [socket, activeLayer]); + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + newZones.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); - useEffect(() => { - if (!socket) return; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + setLayers(highestLayer); + setUpdateScene(true); + } - socket.on("zone:response:updates", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } + if (data.message === "zone updated") { + const updatedZones = zones.map((zone: any) => + zone.zoneId === data.data.zoneId ? data.data : zone + ); + setZones(updatedZones); + setUpdateScene(true); + } + }); - if (data.message === "zone created") { - const pointsArray: [number, number, number][] = data.data.points; - const vector3Array = pointsArray.map( - ([x, y, z]) => new THREE.Vector3(x, y, z) - ); - const newZones = [...zones, data.data]; - setZones(newZones); - const updatedZonePoints = [...zonePoints, ...vector3Array]; - setZonePoints(updatedZonePoints); + socket.on("zone:response:delete", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "zone deleted") { + const updatedZones = zones.filter( + (zone: any) => zone.zoneId !== data.data.zoneId + ); + setZones(updatedZones); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - newZones.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); + const zoneIndex = zones.findIndex( + (zone: any) => zone.zoneId === data.data.zoneId + ); + if (zoneIndex !== -1) { + const updatedzonePoints = zonePoints.filter( + (_: any, index: any) => + index < zoneIndex * 4 || index >= zoneIndex * 4 + 4 + ); + setZonePoints(updatedzonePoints); + } - setLayers(highestLayer); - setUpdateScene(true); - } + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + updatedZones.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); - if (data.message === "zone updated") { - const updatedZones = zones.map((zone: any) => - zone.zoneId === data.data.zoneId ? data.data : zone - ); - setZones(updatedZones); - setUpdateScene(true); - } - }); + setLayers(highestLayer); + setUpdateScene(true); + } + }); - socket.on("zone:response:delete", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "zone deleted") { - const updatedZones = zones.filter( - (zone: any) => zone.zoneId !== data.data.zoneId - ); - setZones(updatedZones); + return () => { + socket.off("zone:response:updates"); + socket.off("zone:response:delete"); + }; + }, [socket, zones, zonePoints]); - const zoneIndex = zones.findIndex( - (zone: any) => zone.zoneId === data.data.zoneId - ); - if (zoneIndex !== -1) { - const updatedzonePoints = zonePoints.filter( - (_: any, index: any) => - index < zoneIndex * 4 || index >= zoneIndex * 4 + 4 - ); - setZonePoints(updatedzonePoints); - } - - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - updatedZones.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); - - setLayers(highestLayer); - setUpdateScene(true); - } - }); - - return () => { - socket.off("zone:response:updates"); - socket.off("zone:response:delete"); - }; - }, [socket, zones, zonePoints]); - - return <>; + return <>; } diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index 82f21ab..4e2dac3 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -415,7 +415,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas } }); - toast.success("Object added!"); + echo.success("Object added!"); clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index 3e951a3..83764a3 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -392,7 +392,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb } }); - toast.success("Object duplicated!"); + echo.success("Object duplicated!"); clearSelection(); } diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 5398fe6..8029d66 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -326,7 +326,7 @@ function MoveControls({ itemsGroupRef.current.add(obj); } }); - toast.success("Object moved!"); + 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 31ba8c9..057db06 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -265,7 +265,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo itemsGroupRef.current.add(obj); } }); - toast.success("Object rotated!"); + 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 5041434..67775a9 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -251,7 +251,7 @@ const SelectionControls: React.FC = () => { const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); setFloorItems(updatedItems); } - toast.success("Selected models removed!"); + echo.success("Selected models removed!"); clearSelection(); }; diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index dd5607a..b22c7bf 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -57,9 +57,7 @@ export default function PostProcessing() { )} {selectedWallItem && ( child.name !== "CSG_REF" - )} + selection={flattenChildren(selectedWallItem.children)} selectionLayer={10} width={3000} blendFunction={BlendFunction.ALPHA} diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index d6ff2fa..aa0b9f6 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -3,22 +3,27 @@ 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'; function Simulator() { - const { products } = useProductStore(); + const { products, getProductById } = useProductStore(); const { handleAction } = useActionHandler(); + const { selectedProduct } = useSelectedProduct(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); useEffect(() => { - if (!isPlaying || isReset) return; + if (!isPlaying || isReset || !selectedProduct.productId) return; - const executionOrder = determineExecutionOrder(products); + const product = getProductById(selectedProduct.productId); + if (!product) return; + + const executionOrder = determineExecutionOrder([product]); executionOrder.forEach(point => { const action = 'actions' in point ? point.actions[0] : point.action; handleAction(action); }); - }, [products, isPlaying, isReset]); + }, [products, isPlaying, isReset, selectedProduct]); return ( diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 4218a4e..4270735 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -90,7 +90,7 @@ export const useZonePoints = create((set) => ({ })); export const useSelectedItem = create((set: any) => ({ - selectedItem: { name: "", id: "", type: undefined }, + selectedItem: { name: "", id: "", type: undefined, category: '', subCatergory: '' }, setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), })); diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index 3d6115a..a84eb5e 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -217,27 +217,14 @@ export type FloorItems = Array; // Dispatch type for setting floor item state in React export type setFloorItemSetState = React.Dispatch>; -/** Asset Configuration for Loading and Positioning **/ - -// Configuration for assets, allowing model URLs, scaling, positioning, and types -interface AssetConfiguration { - modelUrl: string; - scale?: [number, number, number]; - csgscale?: [number, number, number]; - csgposition?: [number, number, number]; - type?: "Fixed-Move" | "Free-Move"; -} - -// Collection of asset configurations, keyed by unique identifiers -export type AssetConfigurations = { [key: string]: AssetConfiguration; }; - /** Wall Item Configuration **/ // Configuration for wall items, including model, scale, position, and rotation interface WallItem { - type: "Fixed-Move" | "Free-Move" | undefined; + type: "fixed-move" | "free-move" | undefined; model?: THREE.Group; modelUuid?: string; + modelfileID: string; modelName?: string; scale?: [number, number, number]; csgscale?: [number, number, number];